| // Inferno utils/8l/asm.c | |
| // https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/asm.c | |
| // | |
| // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. | |
| // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) | |
| // Portions Copyright © 1997-1999 Vita Nuova Limited | |
| // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) | |
| // Portions Copyright © 2004,2006 Bruce Ellis | |
| // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) | |
| // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | |
| // Portions Copyright © 2009 The Go Authors. All rights reserved. | |
| // | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |
| // of this software and associated documentation files (the "Software"), to deal | |
| // in the Software without restriction, including without limitation the rights | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| // copies of the Software, and to permit persons to whom the Software is | |
| // furnished to do so, subject to the following conditions: | |
| // | |
| // The above copyright notice and this permission notice shall be included in | |
| // all copies or substantial portions of the Software. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| // THE SOFTWARE. | |
| package ld | |
| import ( | |
| "bufio" | |
| "bytes" | |
| "cmd/internal/bio" | |
| "cmd/internal/objabi" | |
| "cmd/internal/sys" | |
| "cmd/link/internal/loadelf" | |
| "cmd/link/internal/loadmacho" | |
| "cmd/link/internal/loadpe" | |
| "cmd/link/internal/loadxcoff" | |
| "cmd/link/internal/objfile" | |
| "cmd/link/internal/sym" | |
| "crypto/sha1" | |
| "debug/elf" | |
| "encoding/base64" | |
| "encoding/binary" | |
| "encoding/hex" | |
| "fmt" | |
| "io" | |
| "io/ioutil" | |
| "log" | |
| "os" | |
| "os/exec" | |
| "path/filepath" | |
| "runtime" | |
| "strings" | |
| "sync" | |
| ) | |
| // Data layout and relocation. | |
| // Derived from Inferno utils/6l/l.h | |
| // https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h | |
| // | |
| // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. | |
| // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) | |
| // Portions Copyright © 1997-1999 Vita Nuova Limited | |
| // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) | |
| // Portions Copyright © 2004,2006 Bruce Ellis | |
| // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) | |
| // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | |
| // Portions Copyright © 2009 The Go Authors. All rights reserved. | |
| // | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |
| // of this software and associated documentation files (the "Software"), to deal | |
| // in the Software without restriction, including without limitation the rights | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| // copies of the Software, and to permit persons to whom the Software is | |
| // furnished to do so, subject to the following conditions: | |
| // | |
| // The above copyright notice and this permission notice shall be included in | |
| // all copies or substantial portions of the Software. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| // THE SOFTWARE. | |
| type Arch struct { | |
| Funcalign int | |
| Maxalign int | |
| Minalign int | |
| Dwarfregsp int | |
| Dwarfreglr int | |
| Linuxdynld string | |
| Freebsddynld string | |
| Netbsddynld string | |
| Openbsddynld string | |
| Dragonflydynld string | |
| Solarisdynld string | |
| Adddynrel func(*Link, *sym.Symbol, *sym.Reloc) bool | |
| Archinit func(*Link) | |
| // Archreloc is an arch-specific hook that assists in | |
| // relocation processing (invoked by 'relocsym'); it handles | |
| // target-specific relocation tasks. Here "rel" is the current | |
| // relocation being examined, "sym" is the symbol containing the | |
| // chunk of data to which the relocation applies, and "off" is the | |
| // contents of the to-be-relocated data item (from sym.P). Return | |
| // value is the appropriately relocated value (to be written back | |
| // to the same spot in sym.P) and a boolean indicating | |
| // success/failure (a failing value indicates a fatal error). | |
| Archreloc func(link *Link, rel *sym.Reloc, sym *sym.Symbol, | |
| offset int64) (relocatedOffset int64, success bool) | |
| // Archrelocvariant is a second arch-specific hook used for | |
| // relocation processing; it handles relocations where r.Type is | |
| // insufficient to describe the relocation (r.Variant != | |
| // sym.RV_NONE). Here "rel" is the relocation being applied, "sym" | |
| // is the symbol containing the chunk of data to which the | |
| // relocation applies, and "off" is the contents of the | |
| // to-be-relocated data item (from sym.P). Return is an updated | |
| // offset value. | |
| Archrelocvariant func(link *Link, rel *sym.Reloc, sym *sym.Symbol, | |
| offset int64) (relocatedOffset int64) | |
| Trampoline func(*Link, *sym.Reloc, *sym.Symbol) | |
| Asmb func(*Link) | |
| Elfreloc1 func(*Link, *sym.Reloc, int64) bool | |
| Elfsetupplt func(*Link) | |
| Gentext func(*Link) | |
| Machoreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool | |
| PEreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool | |
| // TLSIEtoLE converts a TLS Initial Executable relocation to | |
| // a TLS Local Executable relocation. | |
| // | |
| // This is possible when a TLS IE relocation refers to a local | |
| // symbol in an executable, which is typical when internally | |
| // linking PIE binaries. | |
| TLSIEtoLE func(s *sym.Symbol, off, size int) | |
| // optional override for assignAddress | |
| AssignAddress func(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) | |
| } | |
| var ( | |
| thearch Arch | |
| Lcsize int32 | |
| rpath Rpath | |
| Spsize int32 | |
| Symsize int32 | |
| ) | |
| const ( | |
| MINFUNC = 16 // minimum size for a function | |
| ) | |
| // DynlinkingGo reports whether we are producing Go code that can live | |
| // in separate shared libraries linked together at runtime. | |
| func (ctxt *Link) DynlinkingGo() bool { | |
| if !ctxt.Loaded { | |
| panic("DynlinkingGo called before all symbols loaded") | |
| } | |
| return ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() | |
| } | |
| // CanUsePlugins reports whether a plugins can be used | |
| func (ctxt *Link) CanUsePlugins() bool { | |
| return ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil | |
| } | |
| // UseRelro reports whether to make use of "read only relocations" aka | |
| // relro. | |
| func (ctxt *Link) UseRelro() bool { | |
| switch ctxt.BuildMode { | |
| case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin: | |
| return ctxt.IsELF | |
| default: | |
| return ctxt.linkShared | |
| } | |
| } | |
| var ( | |
| dynexp []*sym.Symbol | |
| dynlib []string | |
| ldflag []string | |
| havedynamic int | |
| Funcalign int | |
| iscgo bool | |
| elfglobalsymndx int | |
| interpreter string | |
| debug_s bool // backup old value of debug['s'] | |
| HEADR int32 | |
| nerrors int | |
| liveness int64 | |
| ) | |
| var ( | |
| Segtext sym.Segment | |
| Segrodata sym.Segment | |
| Segrelrodata sym.Segment | |
| Segdata sym.Segment | |
| Segdwarf sym.Segment | |
| ) | |
| const pkgdef = "__.PKGDEF" | |
| var ( | |
| // Set if we see an object compiled by the host compiler that is not | |
| // from a package that is known to support internal linking mode. | |
| externalobj = false | |
| theline string | |
| ) | |
| func Lflag(ctxt *Link, arg string) { | |
| ctxt.Libdir = append(ctxt.Libdir, arg) | |
| } | |
| /* | |
| * Unix doesn't like it when we write to a running (or, sometimes, | |
| * recently run) binary, so remove the output file before writing it. | |
| * On Windows 7, remove() can force a subsequent create() to fail. | |
| * S_ISREG() does not exist on Plan 9. | |
| */ | |
| func mayberemoveoutfile() { | |
| if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() { | |
| return | |
| } | |
| os.Remove(*flagOutfile) | |
| } | |
| func libinit(ctxt *Link) { | |
| Funcalign = thearch.Funcalign | |
| // add goroot to the end of the libdir list. | |
| suffix := "" | |
| suffixsep := "" | |
| if *flagInstallSuffix != "" { | |
| suffixsep = "_" | |
| suffix = *flagInstallSuffix | |
| } else if *flagRace { | |
| suffixsep = "_" | |
| suffix = "race" | |
| } else if *flagMsan { | |
| suffixsep = "_" | |
| suffix = "msan" | |
| } | |
| Lflag(ctxt, filepath.Join(objabi.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", objabi.GOOS, objabi.GOARCH, suffixsep, suffix))) | |
| mayberemoveoutfile() | |
| f, err := os.OpenFile(*flagOutfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775) | |
| if err != nil { | |
| Exitf("cannot create %s: %v", *flagOutfile, err) | |
| } | |
| ctxt.Out.w = bufio.NewWriter(f) | |
| ctxt.Out.f = f | |
| if *flagEntrySymbol == "" { | |
| switch ctxt.BuildMode { | |
| case BuildModeCShared, BuildModeCArchive: | |
| *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", objabi.GOARCH, objabi.GOOS) | |
| case BuildModeExe, BuildModePIE: | |
| *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", objabi.GOARCH, objabi.GOOS) | |
| case BuildModeShared, BuildModePlugin: | |
| // No *flagEntrySymbol for -buildmode=shared and plugin | |
| default: | |
| Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode) | |
| } | |
| } | |
| } | |
| func errorexit() { | |
| if nerrors != 0 { | |
| Exit(2) | |
| } | |
| Exit(0) | |
| } | |
| func loadinternal(ctxt *Link, name string) *sym.Library { | |
| if ctxt.linkShared && ctxt.PackageShlib != nil { | |
| if shlib := ctxt.PackageShlib[name]; shlib != "" { | |
| return addlibpath(ctxt, "internal", "internal", "", name, shlib) | |
| } | |
| } | |
| if ctxt.PackageFile != nil { | |
| if pname := ctxt.PackageFile[name]; pname != "" { | |
| return addlibpath(ctxt, "internal", "internal", pname, name, "") | |
| } | |
| ctxt.Logf("loadinternal: cannot find %s\n", name) | |
| return nil | |
| } | |
| for _, libdir := range ctxt.Libdir { | |
| if ctxt.linkShared { | |
| shlibname := filepath.Join(libdir, name+".shlibname") | |
| if ctxt.Debugvlog != 0 { | |
| ctxt.Logf("searching for %s.a in %s\n", name, shlibname) | |
| } | |
| if _, err := os.Stat(shlibname); err == nil { | |
| return addlibpath(ctxt, "internal", "internal", "", name, shlibname) | |
| } | |
| } | |
| pname := filepath.Join(libdir, name+".a") | |
| if ctxt.Debugvlog != 0 { | |
| ctxt.Logf("searching for %s.a in %s\n", name, pname) | |
| } | |
| if _, err := os.Stat(pname); err == nil { | |
| return addlibpath(ctxt, "internal", "internal", pname, name, "") | |
| } | |
| } | |
| ctxt.Logf("warning: unable to find %s.a\n", name) | |
| return nil | |
| } | |
| // findLibPathCmd uses cmd command to find gcc library libname. | |
| // It returns library full path if found, or "none" if not found. | |
| func (ctxt *Link) findLibPathCmd(cmd, libname string) string { | |
| if *flagExtld == "" { | |
| *flagExtld = "gcc" | |
| } | |
| args := hostlinkArchArgs(ctxt.Arch) | |
| args = append(args, cmd) | |
| if ctxt.Debugvlog != 0 { | |
| ctxt.Logf("%s %v\n", *flagExtld, args) | |
| } | |
| out, err := exec.Command(*flagExtld, args...).Output() | |
| if err != nil { | |
| if ctxt.Debugvlog != 0 { | |
| ctxt.Logf("not using a %s file because compiler failed\n%v\n%s\n", libname, err, out) | |
| } | |
| return "none" | |
| } | |
| return strings.TrimSpace(string(out)) | |
| } | |
| // findLibPath searches for library libname. | |
| // It returns library full path if found, or "none" if not found. | |
| func (ctxt *Link) findLibPath(libname string) string { | |
| return ctxt.findLibPathCmd("--print-file-name="+libname, libname) | |
| } | |
| func (ctxt *Link) loadlib() { | |
| switch ctxt.BuildMode { | |
| case BuildModeCShared, BuildModePlugin: | |
| s := ctxt.Syms.Lookup("runtime.islibrary", 0) | |
| s.Attr |= sym.AttrDuplicateOK | |
| s.AddUint8(1) | |
| case BuildModeCArchive: | |
| s := ctxt.Syms.Lookup("runtime.isarchive", 0) | |
| s.Attr |= sym.AttrDuplicateOK | |
| s.AddUint8(1) | |
| } | |
| loadinternal(ctxt, "runtime") | |
| if ctxt.Arch.Family == sys.ARM { | |
| loadinternal(ctxt, "math") | |
| } | |
| if *flagRace { | |
| loadinternal(ctxt, "runtime/race") | |
| } | |
| if *flagMsan { | |
| loadinternal(ctxt, "runtime/msan") | |
| } | |
| // ctxt.Library grows during the loop, so not a range loop. | |
| for i := 0; i < len(ctxt.Library); i++ { | |
| lib := ctxt.Library[i] | |
| if lib.Shlib == "" { | |
| if ctxt.Debugvlog > 1 { | |
| ctxt.Logf("%5.2f autolib: %s (from %s)\n", Cputime(), lib.File, lib.Objref) | |
| } | |
| loadobjfile(ctxt, lib) | |
| } | |
| } | |
| for _, lib := range ctxt.Library { | |
| if lib.Shlib != "" { | |
| if ctxt.Debugvlog > 1 { | |
| ctxt.Logf("%5.2f autolib: %s (from %s)\n", Cputime(), lib.Shlib, lib.Objref) | |
| } | |
| ldshlibsyms(ctxt, lib.Shlib) | |
| } | |
| } | |
| iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil | |
| // We now have enough information to determine the link mode. | |
| determineLinkMode(ctxt) | |
| // Recalculate pe parameters now that we have ctxt.LinkMode set. | |
| if ctxt.HeadType == objabi.Hwindows { | |
| Peinit(ctxt) | |
| } | |
| if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal { | |
| *FlagTextAddr = 0 | |
| } | |
| if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 { | |
| toc := ctxt.Syms.Lookup(".TOC.", 0) | |
| toc.Type = sym.SDYNIMPORT | |
| } | |
| if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) { | |
| // This indicates a user requested -linkmode=external. | |
| // The startup code uses an import of runtime/cgo to decide | |
| // whether to initialize the TLS. So give it one. This could | |
| // be handled differently but it's an unusual case. | |
| if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil { | |
| if lib.Shlib != "" { | |
| ldshlibsyms(ctxt, lib.Shlib) | |
| } else { | |
| if ctxt.BuildMode == BuildModeShared || ctxt.linkShared { | |
| Exitf("cannot implicitly include runtime/cgo in a shared library") | |
| } | |
| loadobjfile(ctxt, lib) | |
| } | |
| } | |
| } | |
| if ctxt.LinkMode == LinkInternal { | |
| // Drop all the cgo_import_static declarations. | |
| // Turns out we won't be needing them. | |
| for _, s := range ctxt.Syms.Allsym { | |
| if s.Type == sym.SHOSTOBJ { | |
| // If a symbol was marked both | |
| // cgo_import_static and cgo_import_dynamic, | |
| // then we want to make it cgo_import_dynamic | |
| // now. | |
| if s.Extname() != "" && s.Dynimplib() != "" && !s.Attr.CgoExport() { | |
| s.Type = sym.SDYNIMPORT | |
| } else { | |
| s.Type = 0 | |
| } | |
| } | |
| } | |
| } | |
| tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0) | |
| // runtime.tlsg is used for external linking on platforms that do not define | |
| // a variable to hold g in assembly (currently only intel). | |
| if tlsg.Type == 0 { | |
| tlsg.Type = sym.STLSBSS | |
| tlsg.Size = int64(ctxt.Arch.PtrSize) | |
| } else if tlsg.Type != sym.SDYNIMPORT { | |
| Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type) | |
| } | |
| tlsg.Attr |= sym.AttrReachable | |
| ctxt.Tlsg = tlsg | |
| var moduledata *sym.Symbol | |
| if ctxt.BuildMode == BuildModePlugin { | |
| moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0) | |
| moduledata.Attr |= sym.AttrLocal | |
| } else { | |
| moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0) | |
| } | |
| if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT { | |
| // If the module (toolchain-speak for "executable or shared | |
| // library") we are linking contains the runtime package, it | |
| // will define the runtime.firstmoduledata symbol and we | |
| // truncate it back to 0 bytes so we can define its entire | |
| // contents in symtab.go:symtab(). | |
| moduledata.Size = 0 | |
| // In addition, on ARM, the runtime depends on the linker | |
| // recording the value of GOARM. | |
| if ctxt.Arch.Family == sys.ARM { | |
| s := ctxt.Syms.Lookup("runtime.goarm", 0) | |
| s.Type = sym.SDATA | |
| s.Size = 0 | |
| s.AddUint8(uint8(objabi.GOARM)) | |
| } | |
| if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { | |
| s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0) | |
| s.Type = sym.SDATA | |
| s.Size = 0 | |
| s.AddUint8(1) | |
| } | |
| } else { | |
| // If OTOH the module does not contain the runtime package, | |
| // create a local symbol for the moduledata. | |
| moduledata = ctxt.Syms.Lookup("local.moduledata", 0) | |
| moduledata.Attr |= sym.AttrLocal | |
| } | |
| // In all cases way we mark the moduledata as noptrdata to hide it from | |
| // the GC. | |
| moduledata.Type = sym.SNOPTRDATA | |
| moduledata.Attr |= sym.AttrReachable | |
| ctxt.Moduledata = moduledata | |
| // Now that we know the link mode, trim the dynexp list. | |
| x := sym.AttrCgoExportDynamic | |
| if ctxt.LinkMode == LinkExternal { | |
| x = sym.AttrCgoExportStatic | |
| } | |
| w := 0 | |
| for i := range dynexp { | |
| if dynexp[i].Attr&x != 0 { | |
| dynexp[w] = dynexp[i] | |
| w++ | |
| } | |
| } | |
| dynexp = dynexp[:w] | |
| // In internal link mode, read the host object files. | |
| if ctxt.LinkMode == LinkInternal { | |
| hostobjs(ctxt) | |
| // If we have any undefined symbols in external | |
| // objects, try to read them from the libgcc file. | |
| any := false | |
| for _, s := range ctxt.Syms.Allsym { | |
| for i := range s.R { | |
| r := &s.R[i] // Copying sym.Reloc has measurable impact on performance | |
| if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" { | |
| any = true | |
| break | |
| } | |
| } | |
| } | |
| if any { | |
| if *flagLibGCC == "" { | |
| *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc") | |
| } | |
| if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" { | |
| // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a". | |
| // In this case we fail to load libgcc.a and can encounter link | |
| // errors - see if we can find libcompiler_rt.a instead. | |
| *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") | |
| } | |
| if *flagLibGCC != "none" { | |
| hostArchive(ctxt, *flagLibGCC) | |
| } | |
| if ctxt.HeadType == objabi.Hwindows { | |
| if p := ctxt.findLibPath("libmingwex.a"); p != "none" { | |
| hostArchive(ctxt, p) | |
| } | |
| if p := ctxt.findLibPath("libmingw32.a"); p != "none" { | |
| hostArchive(ctxt, p) | |
| } | |
| // TODO: maybe do something similar to peimporteddlls to collect all lib names | |
| // and try link them all to final exe just like libmingwex.a and libmingw32.a: | |
| /* | |
| for: | |
| #cgo windows LDFLAGS: -lmsvcrt -lm | |
| import: | |
| libmsvcrt.a libm.a | |
| */ | |
| } | |
| } | |
| } else { | |
| hostlinksetup(ctxt) | |
| } | |
| // We've loaded all the code now. | |
| ctxt.Loaded = true | |
| // If there are no dynamic libraries needed, gcc disables dynamic linking. | |
| // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) | |
| // assumes that a dynamic binary always refers to at least one dynamic library. | |
| // Rather than be a source of test cases for glibc, disable dynamic linking | |
| // the same way that gcc would. | |
| // | |
| // Exception: on OS X, programs such as Shark only work with dynamic | |
| // binaries, so leave it enabled on OS X (Mach-O) binaries. | |
| // Also leave it enabled on Solaris which doesn't support | |
| // statically linked binaries. | |
| if ctxt.BuildMode == BuildModeExe { | |
| if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris { | |
| *FlagD = true | |
| } | |
| } | |
| // If package versioning is required, generate a hash of the | |
| // packages used in the link. | |
| if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() { | |
| for _, lib := range ctxt.Library { | |
| if lib.Shlib == "" { | |
| genhash(ctxt, lib) | |
| } | |
| } | |
| } | |
| if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows { | |
| if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() { | |
| got := ctxt.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0) | |
| got.Type = sym.SDYNIMPORT | |
| got.Attr |= sym.AttrReachable | |
| } | |
| } | |
| importcycles() | |
| // put symbols into Textp | |
| // do it in postorder so that packages are laid down in dependency order | |
| // internal first, then everything else | |
| ctxt.Library = postorder(ctxt.Library) | |
| for _, doInternal := range [2]bool{true, false} { | |
| for _, lib := range ctxt.Library { | |
| if isRuntimeDepPkg(lib.Pkg) != doInternal { | |
| continue | |
| } | |
| ctxt.Textp = append(ctxt.Textp, lib.Textp...) | |
| for _, s := range lib.DupTextSyms { | |
| if !s.Attr.OnList() { | |
| ctxt.Textp = append(ctxt.Textp, s) | |
| s.Attr |= sym.AttrOnList | |
| // dupok symbols may be defined in multiple packages. its | |
| // associated package is chosen sort of arbitrarily (the | |
| // first containing package that the linker loads). canonicalize | |
| // it here to the package with which it will be laid down | |
| // in text. | |
| s.File = objabi.PathToPrefix(lib.Pkg) | |
| } | |
| } | |
| } | |
| } | |
| if len(ctxt.Shlibs) > 0 { | |
| // We might have overwritten some functions above (this tends to happen for the | |
| // autogenerated type equality/hashing functions) and we don't want to generated | |
| // pcln table entries for these any more so remove them from Textp. | |
| textp := make([]*sym.Symbol, 0, len(ctxt.Textp)) | |
| for _, s := range ctxt.Textp { | |
| if s.Type != sym.SDYNIMPORT { | |
| textp = append(textp, s) | |
| } | |
| } | |
| ctxt.Textp = textp | |
| } | |
| // Resolve ABI aliases in the list of cgo-exported functions. | |
| // This is necessary because we load the ABI0 symbol for all | |
| // cgo exports. | |
| for i, s := range dynexp { | |
| if s.Type != sym.SABIALIAS { | |
| continue | |
| } | |
| t := resolveABIAlias(s) | |
| t.Attr |= s.Attr | |
| t.SetExtname(s.Extname()) | |
| dynexp[i] = t | |
| } | |
| } | |
| // mangleTypeSym shortens the names of symbols that represent Go types | |
| // if they are visible in the symbol table. | |
| // | |
| // As the names of these symbols are derived from the string of | |
| // the type, they can run to many kilobytes long. So we shorten | |
| // them using a SHA-1 when the name appears in the final binary. | |
| // This also removes characters that upset external linkers. | |
| // | |
| // These are the symbols that begin with the prefix 'type.' and | |
| // contain run-time type information used by the runtime and reflect | |
| // packages. All Go binaries contain these symbols, but only | |
| // those programs loaded dynamically in multiple parts need these | |
| // symbols to have entries in the symbol table. | |
| func (ctxt *Link) mangleTypeSym() { | |
| if ctxt.BuildMode != BuildModeShared && !ctxt.linkShared && ctxt.BuildMode != BuildModePlugin && !ctxt.CanUsePlugins() { | |
| return | |
| } | |
| for _, s := range ctxt.Syms.Allsym { | |
| newName := typeSymbolMangle(s.Name) | |
| if newName != s.Name { | |
| ctxt.Syms.Rename(s.Name, newName, int(s.Version), ctxt.Reachparent) | |
| } | |
| } | |
| } | |
| // typeSymbolMangle mangles the given symbol name into something shorter. | |
| // | |
| // Keep the type.. prefix, which parts of the linker (like the | |
| // DWARF generator) know means the symbol is not decodable. | |
| // Leave type.runtime. symbols alone, because other parts of | |
| // the linker manipulates them. | |
| func typeSymbolMangle(name string) string { | |
| if !strings.HasPrefix(name, "type.") { | |
| return name | |
| } | |
| if strings.HasPrefix(name, "type.runtime.") { | |
| return name | |
| } | |
| if len(name) <= 14 && !strings.Contains(name, "@") { // Issue 19529 | |
| return name | |
| } | |
| hash := sha1.Sum([]byte(name)) | |
| prefix := "type." | |
| if name[5] == '.' { | |
| prefix = "type.." | |
| } | |
| return prefix + base64.StdEncoding.EncodeToString(hash[:6]) | |
| } | |
| /* | |
| * look for the next file in an archive. | |
| * adapted from libmach. | |
| */ | |
| func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 { | |
| if off&1 != 0 { | |
| off++ | |
| } | |
| bp.Seek(off, 0) | |
| var buf [SAR_HDR]byte | |
| if n, err := io.ReadFull(bp, buf[:]); err != nil { | |
| if n == 0 && err != io.EOF { | |
| return -1 | |
| } | |
| return 0 | |
| } | |
| a.name = artrim(buf[0:16]) | |
| a.date = artrim(buf[16:28]) | |
| a.uid = artrim(buf[28:34]) | |
| a.gid = artrim(buf[34:40]) | |
| a.mode = artrim(buf[40:48]) | |
| a.size = artrim(buf[48:58]) | |
| a.fmag = artrim(buf[58:60]) | |
| arsize := atolwhex(a.size) | |
| if arsize&1 != 0 { | |
| arsize++ | |
| } | |
| return arsize + SAR_HDR | |
| } | |
| func genhash(ctxt *Link, lib *sym.Library) { | |
| f, err := bio.Open(lib.File) | |
| if err != nil { | |
| Errorf(nil, "cannot open file %s for hash generation: %v", lib.File, err) | |
| return | |
| } | |
| defer f.Close() | |
| var magbuf [len(ARMAG)]byte | |
| if _, err := io.ReadFull(f, magbuf[:]); err != nil { | |
| Exitf("file %s too short", lib.File) | |
| } | |
| if string(magbuf[:]) != ARMAG { | |
| Exitf("%s is not an archive file", lib.File) | |
| } | |
| var arhdr ArHdr | |
| l := nextar(f, f.Offset(), &arhdr) | |
| if l <= 0 { | |
| Errorf(nil, "%s: short read on archive file symbol header", lib.File) | |
| return | |
| } | |
| if arhdr.name != pkgdef { | |
| Errorf(nil, "%s: missing package data entry", lib.File) | |
| return | |
| } | |
| h := sha1.New() | |
| // To compute the hash of a package, we hash the first line of | |
| // __.PKGDEF (which contains the toolchain version and any | |
| // GOEXPERIMENT flags) and the export data (which is between | |
| // the first two occurrences of "\n$$"). | |
| pkgDefBytes := make([]byte, atolwhex(arhdr.size)) | |
| _, err = io.ReadFull(f, pkgDefBytes) | |
| if err != nil { | |
| Errorf(nil, "%s: error reading package data: %v", lib.File, err) | |
| return | |
| } | |
| firstEOL := bytes.IndexByte(pkgDefBytes, '\n') | |
| if firstEOL < 0 { | |
| Errorf(nil, "cannot parse package data of %s for hash generation, no newline found", lib.File) | |
| return | |
| } | |
| firstDoubleDollar := bytes.Index(pkgDefBytes, []byte("\n$$")) | |
| if firstDoubleDollar < 0 { | |
| Errorf(nil, "cannot parse package data of %s for hash generation, no \\n$$ found", lib.File) | |
| return | |
| } | |
| secondDoubleDollar := bytes.Index(pkgDefBytes[firstDoubleDollar+1:], []byte("\n$$")) | |
| if secondDoubleDollar < 0 { | |
| Errorf(nil, "cannot parse package data of %s for hash generation, only one \\n$$ found", lib.File) | |
| return | |
| } | |
| h.Write(pkgDefBytes[0:firstEOL]) | |
| h.Write(pkgDefBytes[firstDoubleDollar : firstDoubleDollar+secondDoubleDollar]) | |
| lib.Hash = hex.EncodeToString(h.Sum(nil)) | |
| } | |
| func loadobjfile(ctxt *Link, lib *sym.Library) { | |
| pkg := objabi.PathToPrefix(lib.Pkg) | |
| if ctxt.Debugvlog > 1 { | |
| ctxt.Logf("%5.2f ldobj: %s (%s)\n", Cputime(), lib.File, pkg) | |
| } | |
| f, err := bio.Open(lib.File) | |
| if err != nil { | |
| Exitf("cannot open file %s: %v", lib.File, err) | |
| } | |
| defer f.Close() | |
| defer func() { | |
| if pkg == "main" && !lib.Main { | |
| Exitf("%s: not package main", lib.File) | |
| } | |
| // Ideally, we'd check that *all* object files within | |
| // the archive were marked safe, but here we settle | |
| // for *any*. | |
| // | |
| // Historically, cmd/link only checked the __.PKGDEF | |
| // file, which in turn came from the first object | |
| // file, typically produced by cmd/compile. The | |
| // remaining object files are normally produced by | |
| // cmd/asm, which doesn't support marking files as | |
| // safe anyway. So at least in practice, this matches | |
| // how safe mode has always worked. | |
| if *flagU && !lib.Safe { | |
| Exitf("%s: load of unsafe package %s", lib.File, pkg) | |
| } | |
| }() | |
| for i := 0; i < len(ARMAG); i++ { | |
| if c, err := f.ReadByte(); err == nil && c == ARMAG[i] { | |
| continue | |
| } | |
| /* load it as a regular file */ | |
| l := f.Seek(0, 2) | |
| f.Seek(0, 0) | |
| ldobj(ctxt, f, lib, l, lib.File, lib.File) | |
| return | |
| } | |
| /* | |
| * load all the object files from the archive now. | |
| * this gives us sequential file access and keeps us | |
| * from needing to come back later to pick up more | |
| * objects. it breaks the usual C archive model, but | |
| * this is Go, not C. the common case in Go is that | |
| * we need to load all the objects, and then we throw away | |
| * the individual symbols that are unused. | |
| * | |
| * loading every object will also make it possible to | |
| * load foreign objects not referenced by __.PKGDEF. | |
| */ | |
| var arhdr ArHdr | |
| off := f.Offset() | |
| for { | |
| l := nextar(f, off, &arhdr) | |
| if l == 0 { | |
| break | |
| } | |
| if l < 0 { | |
| Exitf("%s: malformed archive", lib.File) | |
| } | |
| off += l | |
| // __.PKGDEF isn't a real Go object file, and it's | |
| // absent in -linkobj builds anyway. Skipping it | |
| // ensures consistency between -linkobj and normal | |
| // build modes. | |
| if arhdr.name == pkgdef { | |
| continue | |
| } | |
| // Skip other special (non-object-file) sections that | |
| // build tools may have added. Such sections must have | |
| // short names so that the suffix is not truncated. | |
| if len(arhdr.name) < 16 { | |
| if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" { | |
| continue | |
| } | |
| } | |
| pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name) | |
| l = atolwhex(arhdr.size) | |
| ldobj(ctxt, f, lib, l, pname, lib.File) | |
| } | |
| } | |
| type Hostobj struct { | |
| ld func(*Link, *bio.Reader, string, int64, string) | |
| pkg string | |
| pn string | |
| file string | |
| off int64 | |
| length int64 | |
| } | |
| var hostobj []Hostobj | |
| // These packages can use internal linking mode. | |
| // Others trigger external mode. | |
| var internalpkg = []string{ | |
| "crypto/x509", | |
| "net", | |
| "os/user", | |
| "runtime/cgo", | |
| "runtime/race", | |
| "runtime/msan", | |
| } | |
| func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj { | |
| isinternal := false | |
| for _, intpkg := range internalpkg { | |
| if pkg == intpkg { | |
| isinternal = true | |
| break | |
| } | |
| } | |
| // DragonFly declares errno with __thread, which results in a symbol | |
| // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not | |
| // currently know how to handle TLS relocations, hence we have to | |
| // force external linking for any libraries that link in code that | |
| // uses errno. This can be removed if the Go linker ever supports | |
| // these relocation types. | |
| if headType == objabi.Hdragonfly { | |
| if pkg == "net" || pkg == "os/user" { | |
| isinternal = false | |
| } | |
| } | |
| if !isinternal { | |
| externalobj = true | |
| } | |
| hostobj = append(hostobj, Hostobj{}) | |
| h := &hostobj[len(hostobj)-1] | |
| h.ld = ld | |
| h.pkg = pkg | |
| h.pn = pn | |
| h.file = file | |
| h.off = f.Offset() | |
| h.length = length | |
| return h | |
| } | |
| func hostobjs(ctxt *Link) { | |
| var h *Hostobj | |
| for i := 0; i < len(hostobj); i++ { | |
| h = &hostobj[i] | |
| f, err := bio.Open(h.file) | |
| if err != nil { | |
| Exitf("cannot reopen %s: %v", h.pn, err) | |
| } | |
| f.Seek(h.off, 0) | |
| h.ld(ctxt, f, h.pkg, h.length, h.pn) | |
| f.Close() | |
| } | |
| } | |
| func hostlinksetup(ctxt *Link) { | |
| if ctxt.LinkMode != LinkExternal { | |
| return | |
| } | |
| // For external link, record that we need to tell the external linker -s, | |
| // and turn off -s internally: the external linker needs the symbol | |
| // information for its final link. | |
| debug_s = *FlagS | |
| *FlagS = false | |
| // create temporary directory and arrange cleanup | |
| if *flagTmpdir == "" { | |
| dir, err := ioutil.TempDir("", "go-link-") | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| *flagTmpdir = dir | |
| AtExit(func() { | |
| ctxt.Out.f.Close() | |
| os.RemoveAll(*flagTmpdir) | |
| }) | |
| } | |
| // change our output to temporary object file | |
| ctxt.Out.f.Close() | |
| mayberemoveoutfile() | |
| p := filepath.Join(*flagTmpdir, "go.o") | |
| var err error | |
| f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775) | |
| if err != nil { | |
| Exitf("cannot create %s: %v", p, err) | |
| } | |
| ctxt.Out.w = bufio.NewWriter(f) | |
| ctxt.Out.f = f | |
| ctxt.Out.off = 0 | |
| } | |
| // hostobjCopy creates a copy of the object files in hostobj in a | |
| // temporary directory. | |
| func hostobjCopy() (paths []string) { | |
| var wg sync.WaitGroup | |
| sema := make(chan struct{}, runtime.NumCPU()) // limit open file descriptors | |
| for i, h := range hostobj { | |
| h := h | |
| dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i)) | |
| paths = append(paths, dst) | |
| wg.Add(1) | |
| go func() { | |
| sema <- struct{}{} | |
| defer func() { | |
| <-sema | |
| wg.Done() | |
| }() | |
| f, err := os.Open(h.file) | |
| if err != nil { | |
| Exitf("cannot reopen %s: %v", h.pn, err) | |
| } | |
| defer f.Close() | |
| if _, err := f.Seek(h.off, 0); err != nil { | |
| Exitf("cannot seek %s: %v", h.pn, err) | |
| } | |
| w, err := os.Create(dst) | |
| if err != nil { | |
| Exitf("cannot create %s: %v", dst, err) | |
| } | |
| if _, err := io.CopyN(w, f, h.length); err != nil { | |
| Exitf("cannot write %s: %v", dst, err) | |
| } | |
| if err := w.Close(); err != nil { | |
| Exitf("cannot close %s: %v", dst, err) | |
| } | |
| }() | |
| } | |
| wg.Wait() | |
| return paths | |
| } | |
| // writeGDBLinkerScript creates gcc linker script file in temp | |
| // directory. writeGDBLinkerScript returns created file path. | |
| // The script is used to work around gcc bug | |
| // (see https://golang.org/issue/20183 for details). | |
| func writeGDBLinkerScript() string { | |
| name := "fix_debug_gdb_scripts.ld" | |
| path := filepath.Join(*flagTmpdir, name) | |
| src := `SECTIONS | |
| { | |
| .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : | |
| { | |
| *(.debug_gdb_scripts) | |
| } | |
| } | |
| INSERT AFTER .debug_types; | |
| ` | |
| err := ioutil.WriteFile(path, []byte(src), 0666) | |
| if err != nil { | |
| Errorf(nil, "WriteFile %s failed: %v", name, err) | |
| } | |
| return path | |
| } | |
| // archive builds a .a archive from the hostobj object files. | |
| func (ctxt *Link) archive() { | |
| if ctxt.BuildMode != BuildModeCArchive { | |
| return | |
| } | |
| if *flagExtar == "" { | |
| *flagExtar = "ar" | |
| } | |
| mayberemoveoutfile() | |
| // Force the buffer to flush here so that external | |
| // tools will see a complete file. | |
| ctxt.Out.Flush() | |
| if err := ctxt.Out.f.Close(); err != nil { | |
| Exitf("close: %v", err) | |
| } | |
| ctxt.Out.f = nil | |
| argv := []string{*flagExtar, "-q", "-c", "-s", *flagOutfile} | |
| argv = append(argv, filepath.Join(*flagTmpdir, "go.o")) | |
| argv = append(argv, hostobjCopy()...) | |
| if ctxt.Debugvlog != 0 { | |
| ctxt.Logf("archive: %s\n", strings.Join(argv, " ")) | |
| } | |
| if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { | |
| Exitf("running %s failed: %v\n%s", argv[0], err, out) | |
| } | |
| } | |
| func (ctxt *Link) hostlink() { | |
| if ctxt.LinkMode != LinkExternal || nerrors > 0 { | |
| return | |
| } | |
| if ctxt.BuildMode == BuildModeCArchive { | |
| return | |
| } | |
| if *flagExtld == "" { | |
| *flagExtld = "gcc" | |
| } | |
| var argv []string | |
| argv = append(argv, *flagExtld) | |
| argv = append(argv, hostlinkArchArgs(ctxt.Arch)...) | |
| if *FlagS || debug_s { | |
| if ctxt.HeadType == objabi.Hdarwin { | |
| // Recent versions of macOS print | |
| // ld: warning: option -s is obsolete and being ignored | |
| // so do not pass any arguments. | |
| } else { | |
| argv = append(argv, "-s") | |
| } | |
| } | |
| switch ctxt.HeadType { | |
| case objabi.Hdarwin: | |
| argv = append(argv, "-Wl,-headerpad,1144") | |
| if ctxt.DynlinkingGo() && !ctxt.Arch.InFamily(sys.ARM, sys.ARM64) { | |
| argv = append(argv, "-Wl,-flat_namespace") | |
| } | |
| if ctxt.BuildMode == BuildModeExe && !ctxt.Arch.InFamily(sys.ARM64) { | |
| argv = append(argv, "-Wl,-no_pie") | |
| } | |
| case objabi.Hopenbsd: | |
| argv = append(argv, "-Wl,-nopie") | |
| case objabi.Hwindows: | |
| if windowsgui { | |
| argv = append(argv, "-mwindows") | |
| } else { | |
| argv = append(argv, "-mconsole") | |
| } | |
| } | |
| switch ctxt.BuildMode { | |
| case BuildModeExe: | |
| if ctxt.HeadType == objabi.Hdarwin { | |
| if ctxt.Arch.Family == sys.ARM64 { | |
| // __PAGEZERO segment size determined empirically. | |
| // XCode 9.0.1 successfully uploads an iOS app with this value. | |
| argv = append(argv, "-Wl,-pagezero_size,100000000") | |
| } else { | |
| argv = append(argv, "-Wl,-pagezero_size,4000000") | |
| } | |
| } | |
| case BuildModePIE: | |
| // ELF. | |
| if ctxt.HeadType != objabi.Hdarwin { | |
| if ctxt.UseRelro() { | |
| argv = append(argv, "-Wl,-z,relro") | |
| } | |
| argv = append(argv, "-pie") | |
| } | |
| case BuildModeCShared: | |
| if ctxt.HeadType == objabi.Hdarwin { | |
| argv = append(argv, "-dynamiclib") | |
| if ctxt.Arch.Family != sys.AMD64 { | |
| argv = append(argv, "-Wl,-read_only_relocs,suppress") | |
| } | |
| } else { | |
| // ELF. | |
| argv = append(argv, "-Wl,-Bsymbolic") | |
| if ctxt.UseRelro() { | |
| argv = append(argv, "-Wl,-z,relro") | |
| } | |
| argv = append(argv, "-shared") | |
| if ctxt.HeadType != objabi.Hwindows { | |
| // Pass -z nodelete to mark the shared library as | |
| // non-closeable: a dlclose will do nothing. | |
| argv = append(argv, "-Wl,-z,nodelete") | |
| } | |
| } | |
| case BuildModeShared: | |
| if ctxt.UseRelro() { | |
| argv = append(argv, "-Wl,-z,relro") | |
| } | |
| argv = append(argv, "-shared") | |
| case BuildModePlugin: | |
| if ctxt.HeadType == objabi.Hdarwin { | |
| argv = append(argv, "-dynamiclib") | |
| } else { | |
| if ctxt.UseRelro() { | |
| argv = append(argv, "-Wl,-z,relro") | |
| } | |
| argv = append(argv, "-shared") | |
| } | |
| } | |
| if ctxt.IsELF && ctxt.DynlinkingGo() { | |
| // We force all symbol resolution to be done at program startup | |
| // because lazy PLT resolution can use large amounts of stack at | |
| // times we cannot allow it to do so. | |
| argv = append(argv, "-Wl,-znow") | |
| // Do not let the host linker generate COPY relocations. These | |
| // can move symbols out of sections that rely on stable offsets | |
| // from the beginning of the section (like sym.STYPE). | |
| argv = append(argv, "-Wl,-znocopyreloc") | |
| if ctxt.Arch.InFamily(sys.ARM, sys.ARM64) { | |
| // On ARM, the GNU linker will generate COPY relocations | |
| // even with -znocopyreloc set. | |
| // https://sourceware.org/bugzilla/show_bug.cgi?id=19962 | |
| // | |
| // On ARM64, the GNU linker will fail instead of | |
| // generating COPY relocations. | |
| // | |
| // In both cases, switch to gold. | |
| argv = append(argv, "-fuse-ld=gold") | |
| // If gold is not installed, gcc will silently switch | |
| // back to ld.bfd. So we parse the version information | |
| // and provide a useful error if gold is missing. | |
| cmd := exec.Command(*flagExtld, "-fuse-ld=gold", "-Wl,--version") | |
| if out, err := cmd.CombinedOutput(); err == nil { | |
| if !bytes.Contains(out, []byte("GNU gold")) { | |
| log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out) | |
| } | |
| } | |
| } | |
| } | |
| if ctxt.IsELF && len(buildinfo) > 0 { | |
| argv = append(argv, fmt.Sprintf("-Wl,--build-id=0x%x", buildinfo)) | |
| } | |
| // On Windows, given -o foo, GCC will append ".exe" to produce | |
| // "foo.exe". We have decided that we want to honor the -o | |
| // option. To make this work, we append a '.' so that GCC | |
| // will decide that the file already has an extension. We | |
| // only want to do this when producing a Windows output file | |
| // on a Windows host. | |
| outopt := *flagOutfile | |
| if objabi.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" { | |
| outopt += "." | |
| } | |
| argv = append(argv, "-o") | |
| argv = append(argv, outopt) | |
| if rpath.val != "" { | |
| argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val)) | |
| } | |
| // Force global symbols to be exported for dlopen, etc. | |
| if ctxt.IsELF { | |
| argv = append(argv, "-rdynamic") | |
| } | |
| if strings.Contains(argv[0], "clang") { | |
| argv = append(argv, "-Qunused-arguments") | |
| } | |
| const compressDWARF = "-Wl,--compress-debug-sections=zlib-gnu" | |
| if ctxt.compressDWARF && linkerFlagSupported(argv[0], compressDWARF) { | |
| argv = append(argv, compressDWARF) | |
| } | |
| argv = append(argv, filepath.Join(*flagTmpdir, "go.o")) | |
| argv = append(argv, hostobjCopy()...) | |
| if ctxt.linkShared { | |
| seenDirs := make(map[string]bool) | |
| seenLibs := make(map[string]bool) | |
| addshlib := func(path string) { | |
| dir, base := filepath.Split(path) | |
| if !seenDirs[dir] { | |
| argv = append(argv, "-L"+dir) | |
| if !rpath.set { | |
| argv = append(argv, "-Wl,-rpath="+dir) | |
| } | |
| seenDirs[dir] = true | |
| } | |
| base = strings.TrimSuffix(base, ".so") | |
| base = strings.TrimPrefix(base, "lib") | |
| if !seenLibs[base] { | |
| argv = append(argv, "-l"+base) | |
| seenLibs[base] = true | |
| } | |
| } | |
| for _, shlib := range ctxt.Shlibs { | |
| addshlib(shlib.Path) | |
| for _, dep := range shlib.Deps { | |
| if dep == "" { | |
| continue | |
| } | |
| libpath := findshlib(ctxt, dep) | |
| if libpath != "" { | |
| addshlib(libpath) | |
| } | |
| } | |
| } | |
| } | |
| // clang, unlike GCC, passes -rdynamic to the linker | |
| // even when linking with -static, causing a linker | |
| // error when using GNU ld. So take out -rdynamic if | |
| // we added it. We do it in this order, rather than | |
| // only adding -rdynamic later, so that -*extldflags | |
| // can override -rdynamic without using -static. | |
| checkStatic := func(arg string) { | |
| if ctxt.IsELF && arg == "-static" { | |
| for i := range argv { | |
| if argv[i] == "-rdynamic" { | |
| argv[i] = "-static" | |
| } | |
| } | |
| } | |
| } | |
| for _, p := range ldflag { | |
| argv = append(argv, p) | |
| checkStatic(p) | |
| } | |
| // When building a program with the default -buildmode=exe the | |
| // gc compiler generates code requires DT_TEXTREL in a | |
| // position independent executable (PIE). On systems where the | |
| // toolchain creates PIEs by default, and where DT_TEXTREL | |
| // does not work, the resulting programs will not run. See | |
| // issue #17847. To avoid this problem pass -no-pie to the | |
| // toolchain if it is supported. | |
| if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared { | |
| // GCC uses -no-pie, clang uses -nopie. | |
| for _, nopie := range []string{"-no-pie", "-nopie"} { | |
| if linkerFlagSupported(argv[0], nopie) { | |
| argv = append(argv, nopie) | |
| break | |
| } | |
| } | |
| } | |
| for _, p := range strings.Fields(*flagExtldflags) { | |
| argv = append(argv, p) | |
| checkStatic(p) | |
| } | |
| if ctxt.HeadType == objabi.Hwindows { | |
| // use gcc linker script to work around gcc bug | |
| // (see https://golang.org/issue/20183 for details). | |
| p := writeGDBLinkerScript() | |
| argv = append(argv, "-Wl,-T,"+p) | |
| // libmingw32 and libmingwex have some inter-dependencies, | |
| // so must use linker groups. | |
| argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group") | |
| argv = append(argv, peimporteddlls()...) | |
| } | |
| if ctxt.Debugvlog != 0 { | |
| ctxt.Logf("%5.2f host link:", Cputime()) | |
| for _, v := range argv { | |
| ctxt.Logf(" %q", v) | |
| } | |
| ctxt.Logf("\n") | |
| } | |
| out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput() | |
| if err != nil { | |
| Exitf("running %s failed: %v\n%s", argv[0], err, out) | |
| } | |
| // Filter out useless linker warnings caused by bugs outside Go. | |
| // See also cmd/go/internal/work/exec.go's gccld method. | |
| var save [][]byte | |
| for _, line := range bytes.SplitAfter(out, []byte("\n")) { | |
| // golang.org/issue/26073 - Apple Xcode bug | |
| if bytes.Contains(line, []byte("ld: warning: text-based stub file")) { | |
| continue | |
| } | |
| save = append(save, line) | |
| } | |
| out = bytes.Join(save, nil) | |
| if len(out) > 0 { | |
| // always print external output even if the command is successful, so that we don't | |
| // swallow linker warnings (see https://golang.org/issue/17935). | |
| ctxt.Logf("%s", out) | |
| } | |
| if !*FlagS && !*FlagW && !debug_s && ctxt.HeadType == objabi.Hdarwin { | |
| dsym := filepath.Join(*flagTmpdir, "go.dwarf") | |
| if out, err := exec.Command("dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil { | |
| Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out) | |
| } | |
| // Skip combining if `dsymutil` didn't generate a file. See #11994. | |
| if _, err := os.Stat(dsym); os.IsNotExist(err) { | |
| return | |
| } | |
| // For os.Rename to work reliably, must be in same directory as outfile. | |
| combinedOutput := *flagOutfile + "~" | |
| isIOS, err := machoCombineDwarf(ctxt, *flagOutfile, dsym, combinedOutput) | |
| if err != nil { | |
| Exitf("%s: combining dwarf failed: %v", os.Args[0], err) | |
| } | |
| if !isIOS { | |
| os.Remove(*flagOutfile) | |
| if err := os.Rename(combinedOutput, *flagOutfile); err != nil { | |
| Exitf("%s: %v", os.Args[0], err) | |
| } | |
| } | |
| } | |
| } | |
| var createTrivialCOnce sync.Once | |
| func linkerFlagSupported(linker, flag string) bool { | |
| createTrivialCOnce.Do(func() { | |
| src := filepath.Join(*flagTmpdir, "trivial.c") | |
| if err := ioutil.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil { | |
| Errorf(nil, "WriteFile trivial.c failed: %v", err) | |
| } | |
| }) | |
| flagsWithNextArgSkip := []string{ | |
| "-F", | |
| "-l", | |
| "-L", | |
| "-framework", | |
| "-Wl,-framework", | |
| "-Wl,-rpath", | |
| "-Wl,-undefined", | |
| } | |
| flagsWithNextArgKeep := []string{ | |
| "-arch", | |
| "-isysroot", | |
| "--sysroot", | |
| "-target", | |
| } | |
| prefixesToKeep := []string{ | |
| "-f", | |
| "-m", | |
| "-p", | |
| "-Wl,", | |
| "-arch", | |
| "-isysroot", | |
| "--sysroot", | |
| "-target", | |
| } | |
| var flags []string | |
| keep := false | |
| skip := false | |
| extldflags := strings.Fields(*flagExtldflags) | |
| for _, f := range append(extldflags, ldflag...) { | |
| if keep { | |
| flags = append(flags, f) | |
| keep = false | |
| } else if skip { | |
| skip = false | |
| } else if f == "" || f[0] != '-' { | |
| } else if contains(flagsWithNextArgSkip, f) { | |
| skip = true | |
| } else if contains(flagsWithNextArgKeep, f) { | |
| flags = append(flags, f) | |
| keep = true | |
| } else { | |
| for _, p := range prefixesToKeep { | |
| if strings.HasPrefix(f, p) { | |
| flags = append(flags, f) | |
| break | |
| } | |
| } | |
| } | |
| } | |
| flags = append(flags, flag, "trivial.c") | |
| cmd := exec.Command(linker, flags...) | |
| cmd.Dir = *flagTmpdir | |
| cmd.Env = append([]string{"LC_ALL=C"}, os.Environ()...) | |
| out, err := cmd.CombinedOutput() | |
| // GCC says "unrecognized command line option ‘-no-pie’" | |
| // clang says "unknown argument: '-no-pie'" | |
| return err == nil && !bytes.Contains(out, []byte("unrecognized")) && !bytes.Contains(out, []byte("unknown")) | |
| } | |
| // hostlinkArchArgs returns arguments to pass to the external linker | |
| // based on the architecture. | |
| func hostlinkArchArgs(arch *sys.Arch) []string { | |
| switch arch.Family { | |
| case sys.I386: | |
| return []string{"-m32"} | |
| case sys.AMD64, sys.PPC64, sys.S390X: | |
| return []string{"-m64"} | |
| case sys.ARM: | |
| return []string{"-marm"} | |
| case sys.ARM64: | |
| // nothing needed | |
| case sys.MIPS64: | |
| return []string{"-mabi=64"} | |
| case sys.MIPS: | |
| return []string{"-mabi=32"} | |
| } | |
| return nil | |
| } | |
| // ldobj loads an input object. If it is a host object (an object | |
| // compiled by a non-Go compiler) it returns the Hostobj pointer. If | |
| // it is a Go object, it returns nil. | |
| func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj { | |
| pkg := objabi.PathToPrefix(lib.Pkg) | |
| eof := f.Offset() + length | |
| start := f.Offset() | |
| c1 := bgetc(f) | |
| c2 := bgetc(f) | |
| c3 := bgetc(f) | |
| c4 := bgetc(f) | |
| f.Seek(start, 0) | |
| magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) | |
| if magic == 0x7f454c46 { // \x7F E L F | |
| ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { | |
| textp, flags, err := loadelf.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags) | |
| if err != nil { | |
| Errorf(nil, "%v", err) | |
| return | |
| } | |
| ehdr.flags = flags | |
| ctxt.Textp = append(ctxt.Textp, textp...) | |
| } | |
| return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) | |
| } | |
| if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { | |
| ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { | |
| textp, err := loadmacho.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) | |
| if err != nil { | |
| Errorf(nil, "%v", err) | |
| return | |
| } | |
| ctxt.Textp = append(ctxt.Textp, textp...) | |
| } | |
| return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) | |
| } | |
| if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 { | |
| ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { | |
| textp, rsrc, err := loadpe.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) | |
| if err != nil { | |
| Errorf(nil, "%v", err) | |
| return | |
| } | |
| if rsrc != nil { | |
| setpersrc(ctxt, rsrc) | |
| } | |
| ctxt.Textp = append(ctxt.Textp, textp...) | |
| } | |
| return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) | |
| } | |
| if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) { | |
| ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { | |
| textp, err := loadxcoff.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) | |
| if err != nil { | |
| Errorf(nil, "%v", err) | |
| return | |
| } | |
| ctxt.Textp = append(ctxt.Textp, textp...) | |
| } | |
| return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) | |
| } | |
| /* check the header */ | |
| line, err := f.ReadString('\n') | |
| if err != nil { | |
| Errorf(nil, "truncated object file: %s: %v", pn, err) | |
| return nil | |
| } | |
| if !strings.HasPrefix(line, "go object ") { | |
| if strings.HasSuffix(pn, ".go") { | |
| Exitf("%s: uncompiled .go source file", pn) | |
| return nil | |
| } | |
| if line == ctxt.Arch.Name { | |
| // old header format: just $GOOS | |
| Errorf(nil, "%s: stale object file", pn) | |
| return nil | |
| } | |
| Errorf(nil, "%s: not an object file", pn) | |
| return nil | |
| } | |
| // First, check that the basic GOOS, GOARCH, and Version match. | |
| t := fmt.Sprintf("%s %s %s ", objabi.GOOS, objabi.GOARCH, objabi.Version) | |
| line = strings.TrimRight(line, "\n") | |
| if !strings.HasPrefix(line[10:]+" ", t) && !*flagF { | |
| Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], t) | |
| return nil | |
| } | |
| // Second, check that longer lines match each other exactly, | |
| // so that the Go compiler and write additional information | |
| // that must be the same from run to run. | |
| if len(line) >= len(t)+10 { | |
| if theline == "" { | |
| theline = line[10:] | |
| } else if theline != line[10:] { | |
| Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], theline) | |
| return nil | |
| } | |
| } | |
| // Skip over exports and other info -- ends with \n!\n. | |
| // | |
| // Note: It's possible for "\n!\n" to appear within the binary | |
| // package export data format. To avoid truncating the package | |
| // definition prematurely (issue 21703), we keep track of | |
| // how many "$$" delimiters we've seen. | |
| import0 := f.Offset() | |
| c1 = '\n' // the last line ended in \n | |
| c2 = bgetc(f) | |
| c3 = bgetc(f) | |
| markers := 0 | |
| for { | |
| if c1 == '\n' { | |
| if markers%2 == 0 && c2 == '!' && c3 == '\n' { | |
| break | |
| } | |
| if c2 == '$' && c3 == '$' { | |
| markers++ | |
| } | |
| } | |
| c1 = c2 | |
| c2 = c3 | |
| c3 = bgetc(f) | |
| if c3 == -1 { | |
| Errorf(nil, "truncated object file: %s", pn) | |
| return nil | |
| } | |
| } | |
| import1 := f.Offset() | |
| f.Seek(import0, 0) | |
| ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n | |
| f.Seek(import1, 0) | |
| objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, eof-f.Offset(), pn) | |
| addImports(ctxt, lib, pn) | |
| return nil | |
| } | |
| func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte { | |
| data := make([]byte, sym.Size) | |
| sect := f.Sections[sym.Section] | |
| if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE { | |
| Errorf(nil, "reading %s from non-data section", sym.Name) | |
| } | |
| n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr)) | |
| if uint64(n) != sym.Size { | |
| Errorf(nil, "reading contents of %s: %v", sym.Name, err) | |
| } | |
| return data | |
| } | |
| func readwithpad(r io.Reader, sz int32) ([]byte, error) { | |
| data := make([]byte, Rnd(int64(sz), 4)) | |
| _, err := io.ReadFull(r, data) | |
| if err != nil { | |
| return nil, err | |
| } | |
| data = data[:sz] | |
| return data, nil | |
| } | |
| func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) { | |
| for _, sect := range f.Sections { | |
| if sect.Type != elf.SHT_NOTE { | |
| continue | |
| } | |
| r := sect.Open() | |
| for { | |
| var namesize, descsize, noteType int32 | |
| err := binary.Read(r, f.ByteOrder, &namesize) | |
| if err != nil { | |
| if err == io.EOF { | |
| break | |
| } | |
| return nil, fmt.Errorf("read namesize failed: %v", err) | |
| } | |
| err = binary.Read(r, f.ByteOrder, &descsize) | |
| if err != nil { | |
| return nil, fmt.Errorf("read descsize failed: %v", err) | |
| } | |
| err = binary.Read(r, f.ByteOrder, ¬eType) | |
| if err != nil { | |
| return nil, fmt.Errorf("read type failed: %v", err) | |
| } | |
| noteName, err := readwithpad(r, namesize) | |
| if err != nil { | |
| return nil, fmt.Errorf("read name failed: %v", err) | |
| } | |
| desc, err := readwithpad(r, descsize) | |
| if err != nil { | |
| return nil, fmt.Errorf("read desc failed: %v", err) | |
| } | |
| if string(name) == string(noteName) && typ == noteType { | |
| return desc, nil | |
| } | |
| } | |
| } | |
| return nil, nil | |
| } | |
| func findshlib(ctxt *Link, shlib string) string { | |
| if filepath.IsAbs(shlib) { | |
| return shlib | |
| } | |
| for _, libdir := range ctxt.Libdir { | |
| libpath := filepath.Join(libdir, shlib) | |
| if _, err := os.Stat(libpath); err == nil { | |
| return libpath | |
| } | |
| } | |
| Errorf(nil, "cannot find shared library: %s", shlib) | |
| return "" | |
| } | |
| func ldshlibsyms(ctxt *Link, shlib string) { | |
| var libpath string | |
| if filepath.IsAbs(shlib) { | |
| libpath = shlib | |
| shlib = filepath.Base(shlib) | |
| } else { | |
| libpath = findshlib(ctxt, shlib) | |
| if libpath == "" { | |
| return | |
| } | |
| } | |
| for _, processedlib := range ctxt.Shlibs { | |
| if processedlib.Path == libpath { | |
| return | |
| } | |
| } | |
| if ctxt.Debugvlog > 1 { | |
| ctxt.Logf("%5.2f ldshlibsyms: found library with name %s at %s\n", Cputime(), shlib, libpath) | |
| } | |
| f, err := elf.Open(libpath) | |
| if err != nil { | |
| Errorf(nil, "cannot open shared library: %s", libpath) | |
| return | |
| } | |
| defer f.Close() | |
| hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) | |
| if err != nil { | |
| Errorf(nil, "cannot read ABI hash from shared library %s: %v", libpath, err) | |
| return | |
| } | |
| depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG) | |
| if err != nil { | |
| Errorf(nil, "cannot read dep list from shared library %s: %v", libpath, err) | |
| return | |
| } | |
| var deps []string | |
| for _, dep := range strings.Split(string(depsbytes), "\n") { | |
| if dep == "" { | |
| continue | |
| } | |
| if !filepath.IsAbs(dep) { | |
| // If the dep can be interpreted as a path relative to the shlib | |
| // in which it was found, do that. Otherwise, we will leave it | |
| // to be resolved by libdir lookup. | |
| abs := filepath.Join(filepath.Dir(libpath), dep) | |
| if _, err := os.Stat(abs); err == nil { | |
| dep = abs | |
| } | |
| } | |
| deps = append(deps, dep) | |
| } | |
| syms, err := f.DynamicSymbols() | |
| if err != nil { | |
| Errorf(nil, "cannot read symbols from shared library: %s", libpath) | |
| return | |
| } | |
| gcdataLocations := make(map[uint64]*sym.Symbol) | |
| for _, elfsym := range syms { | |
| if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION { | |
| continue | |
| } | |
| lsym := ctxt.Syms.Lookup(elfsym.Name, 0) | |
| // Because loadlib above loads all .a files before loading any shared | |
| // libraries, any non-dynimport symbols we find that duplicate symbols | |
| // already loaded should be ignored (the symbols from the .a files | |
| // "win"). | |
| if lsym.Type != 0 && lsym.Type != sym.SDYNIMPORT { | |
| continue | |
| } | |
| lsym.Type = sym.SDYNIMPORT | |
| lsym.SetElfType(elf.ST_TYPE(elfsym.Info)) | |
| lsym.Size = int64(elfsym.Size) | |
| if elfsym.Section != elf.SHN_UNDEF { | |
| // Set .File for the library that actually defines the symbol. | |
| lsym.File = libpath | |
| // The decodetype_* functions in decodetype.go need access to | |
| // the type data. | |
| if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") { | |
| lsym.P = readelfsymboldata(ctxt, f, &elfsym) | |
| gcdataLocations[elfsym.Value+2*uint64(ctxt.Arch.PtrSize)+8+1*uint64(ctxt.Arch.PtrSize)] = lsym | |
| } | |
| } | |
| // For function symbols, we don't know what ABI is | |
| // available, so alias it under both ABIs. | |
| // | |
| // TODO(austin): This is almost certainly wrong once | |
| // the ABIs are actually different. We might have to | |
| // mangle Go function names in the .so to include the | |
| // ABI. | |
| if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC { | |
| alias := ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal) | |
| if alias.Type != 0 { | |
| continue | |
| } | |
| alias.Type = sym.SABIALIAS | |
| alias.R = []sym.Reloc{{Sym: lsym}} | |
| } | |
| } | |
| gcdataAddresses := make(map[*sym.Symbol]uint64) | |
| if ctxt.Arch.Family == sys.ARM64 { | |
| for _, sect := range f.Sections { | |
| if sect.Type == elf.SHT_RELA { | |
| var rela elf.Rela64 | |
| rdr := sect.Open() | |
| for { | |
| err := binary.Read(rdr, f.ByteOrder, &rela) | |
| if err == io.EOF { | |
| break | |
| } else if err != nil { | |
| Errorf(nil, "reading relocation failed %v", err) | |
| return | |
| } | |
| t := elf.R_AARCH64(rela.Info & 0xffff) | |
| if t != elf.R_AARCH64_RELATIVE { | |
| continue | |
| } | |
| if lsym, ok := gcdataLocations[rela.Off]; ok { | |
| gcdataAddresses[lsym] = uint64(rela.Addend) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdataAddresses: gcdataAddresses}) | |
| } | |
| func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section { | |
| sect := new(sym.Section) | |
| sect.Rwx = uint8(rwx) | |
| sect.Name = name | |
| sect.Seg = seg | |
| sect.Align = int32(arch.PtrSize) // everything is at least pointer-aligned | |
| seg.Sections = append(seg.Sections, sect) | |
| return sect | |
| } | |
| type chain struct { | |
| sym *sym.Symbol | |
| up *chain | |
| limit int // limit on entry to sym | |
| } | |
| var morestack *sym.Symbol | |
| // TODO: Record enough information in new object files to | |
| // allow stack checks here. | |
| func haslinkregister(ctxt *Link) bool { | |
| return ctxt.FixedFrameSize() != 0 | |
| } | |
| func callsize(ctxt *Link) int { | |
| if haslinkregister(ctxt) { | |
| return 0 | |
| } | |
| return ctxt.Arch.RegSize | |
| } | |
| func (ctxt *Link) dostkcheck() { | |
| var ch chain | |
| morestack = ctxt.Syms.Lookup("runtime.morestack", 0) | |
| // Every splitting function ensures that there are at least StackLimit | |
| // bytes available below SP when the splitting prologue finishes. | |
| // If the splitting function calls F, then F begins execution with | |
| // at least StackLimit - callsize() bytes available. | |
| // Check that every function behaves correctly with this amount | |
| // of stack, following direct calls in order to piece together chains | |
| // of non-splitting functions. | |
| ch.up = nil | |
| ch.limit = objabi.StackLimit - callsize(ctxt) | |
| if objabi.GOARCH == "arm64" { | |
| // need extra 8 bytes below SP to save FP | |
| ch.limit -= 8 | |
| } | |
| // Check every function, but do the nosplit functions in a first pass, | |
| // to make the printed failure chains as short as possible. | |
| for _, s := range ctxt.Textp { | |
| // runtime.racesymbolizethunk is called from gcc-compiled C | |
| // code running on the operating system thread stack. | |
| // It uses more than the usual amount of stack but that's okay. | |
| if s.Name == "runtime.racesymbolizethunk" { | |
| continue | |
| } | |
| if s.Attr.NoSplit() { | |
| ch.sym = s | |
| stkcheck(ctxt, &ch, 0) | |
| } | |
| } | |
| for _, s := range ctxt.Textp { | |
| if !s.Attr.NoSplit() { | |
| ch.sym = s | |
| stkcheck(ctxt, &ch, 0) | |
| } | |
| } | |
| } | |
| func stkcheck(ctxt *Link, up *chain, depth int) int { | |
| limit := up.limit | |
| s := up.sym | |
| // Don't duplicate work: only need to consider each | |
| // function at top of safe zone once. | |
| top := limit == objabi.StackLimit-callsize(ctxt) | |
| if top { | |
| if s.Attr.StackCheck() { | |
| return 0 | |
| } | |
| s.Attr |= sym.AttrStackCheck | |
| } | |
| if depth > 100 { | |
| Errorf(s, "nosplit stack check too deep") | |
| stkbroke(ctxt, up, 0) | |
| return -1 | |
| } | |
| if s.Attr.External() || s.FuncInfo == nil { | |
| // external function. | |
| // should never be called directly. | |
| // onlyctxt.Diagnose the direct caller. | |
| // TODO(mwhudson): actually think about this. | |
| // TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack. | |
| // See the trampolines in src/runtime/sys_darwin_$ARCH.go. | |
| if depth == 1 && s.Type != sym.SXREF && !ctxt.DynlinkingGo() && | |
| ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin { | |
| //Errorf(s, "call to external function") | |
| } | |
| return -1 | |
| } | |
| if limit < 0 { | |
| stkbroke(ctxt, up, limit) | |
| return -1 | |
| } | |
| // morestack looks like it calls functions, | |
| // but it switches the stack pointer first. | |
| if s == morestack { | |
| return 0 | |
| } | |
| var ch chain | |
| ch.up = up | |
| if !s.Attr.NoSplit() { | |
| // Ensure we have enough stack to call morestack. | |
| ch.limit = limit - callsize(ctxt) | |
| ch.sym = morestack | |
| if stkcheck(ctxt, &ch, depth+1) < 0 { | |
| return -1 | |
| } | |
| if !top { | |
| return 0 | |
| } | |
| // Raise limit to allow frame. | |
| locals := int32(0) | |
| if s.FuncInfo != nil { | |
| locals = s.FuncInfo.Locals | |
| } | |
| limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize()) | |
| } | |
| // Walk through sp adjustments in function, consuming relocs. | |
| ri := 0 | |
| endr := len(s.R) | |
| var ch1 chain | |
| var pcsp Pciter | |
| var r *sym.Reloc | |
| for pciterinit(ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) { | |
| // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). | |
| // Check stack size in effect for this span. | |
| if int32(limit)-pcsp.value < 0 { | |
| stkbroke(ctxt, up, int(int32(limit)-pcsp.value)) | |
| return -1 | |
| } | |
| // Process calls in this span. | |
| for ; ri < endr && uint32(s.R[ri].Off) < pcsp.nextpc; ri++ { | |
| r = &s.R[ri] | |
| switch r.Type { | |
| // Direct call. | |
| case objabi.R_CALL, objabi.R_CALLARM, objabi.R_CALLARM64, objabi.R_CALLPOWER, objabi.R_CALLMIPS: | |
| ch.limit = int(int32(limit) - pcsp.value - int32(callsize(ctxt))) | |
| ch.sym = r.Sym | |
| if stkcheck(ctxt, &ch, depth+1) < 0 { | |
| return -1 | |
| } | |
| // Indirect call. Assume it is a call to a splitting function, | |
| // so we have to make sure it can call morestack. | |
| // Arrange the data structures to report both calls, so that | |
| // if there is an error, stkprint shows all the steps involved. | |
| case objabi.R_CALLIND: | |
| ch.limit = int(int32(limit) - pcsp.value - int32(callsize(ctxt))) | |
| ch.sym = nil | |
| ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue | |
| ch1.up = &ch | |
| ch1.sym = morestack | |
| if stkcheck(ctxt, &ch1, depth+2) < 0 { | |
| return -1 | |
| } | |
| } | |
| } | |
| } | |
| return 0 | |
| } | |
| func stkbroke(ctxt *Link, ch *chain, limit int) { | |
| Errorf(ch.sym, "nosplit stack overflow") | |
| stkprint(ctxt, ch, limit) | |
| } | |
| func stkprint(ctxt *Link, ch *chain, limit int) { | |
| var name string | |
| if ch.sym != nil { | |
| name = ch.sym.Name | |
| if ch.sym.Attr.NoSplit() { | |
| name += " (nosplit)" | |
| } | |
| } else { | |
| name = "function pointer" | |
| } | |
| if ch.up == nil { | |
| // top of chain. ch->sym != nil. | |
| if ch.sym.Attr.NoSplit() { | |
| fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name) | |
| } else { | |
| fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name) | |
| } | |
| } else { | |
| stkprint(ctxt, ch.up, ch.limit+callsize(ctxt)) | |
| if !haslinkregister(ctxt) { | |
| fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name) | |
| } | |
| } | |
| if ch.limit != limit { | |
| fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit) | |
| } | |
| } | |
| func usage() { | |
| fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n") | |
| objabi.Flagprint(os.Stderr) | |
| Exit(2) | |
| } | |
| type SymbolType int8 | |
| const ( | |
| // see also https://9p.io/magic/man2html/1/nm | |
| TextSym SymbolType = 'T' | |
| DataSym SymbolType = 'D' | |
| BSSSym SymbolType = 'B' | |
| UndefinedSym SymbolType = 'U' | |
| TLSSym SymbolType = 't' | |
| FrameSym SymbolType = 'm' | |
| ParamSym SymbolType = 'p' | |
| AutoSym SymbolType = 'a' | |
| // Deleted auto (not a real sym, just placeholder for type) | |
| DeletedAutoSym = 'x' | |
| ) | |
| func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int64, *sym.Symbol)) { | |
| // These symbols won't show up in the first loop below because we | |
| // skip sym.STEXT symbols. Normal sym.STEXT symbols are emitted by walking textp. | |
| s := ctxt.Syms.Lookup("runtime.text", 0) | |
| if s.Type == sym.STEXT { | |
| // We've already included this symbol in ctxt.Textp | |
| // if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin. | |
| // See data.go:/textaddress | |
| if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) { | |
| put(ctxt, s, s.Name, TextSym, s.Value, nil) | |
| } | |
| } | |
| n := 0 | |
| // Generate base addresses for all text sections if there are multiple | |
| for _, sect := range Segtext.Sections { | |
| if n == 0 { | |
| n++ | |
| continue | |
| } | |
| if sect.Name != ".text" { | |
| break | |
| } | |
| s = ctxt.Syms.ROLookup(fmt.Sprintf("runtime.text.%d", n), 0) | |
| if s == nil { | |
| break | |
| } | |
| if s.Type == sym.STEXT { | |
| put(ctxt, s, s.Name, TextSym, s.Value, nil) | |
| } | |
| n++ | |
| } | |
| s = ctxt.Syms.Lookup("runtime.etext", 0) | |
| if s.Type == sym.STEXT { | |
| // We've already included this symbol in ctxt.Textp | |
| // if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin. | |
| // See data.go:/textaddress | |
| if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) { | |
| put(ctxt, s, s.Name, TextSym, s.Value, nil) | |
| } | |
| } | |
| for _, s := range ctxt.Syms.Allsym { | |
| if s.Attr.NotInSymbolTable() { | |
| continue | |
| } | |
| if (s.Name == "" || s.Name[0] == '.') && !s.IsFileLocal() && s.Name != ".rathole" && s.Name != ".TOC." { | |
| continue | |
| } | |
| switch s.Type { | |
| case sym.SCONST, | |
| sym.SRODATA, | |
| sym.SSYMTAB, | |
| sym.SPCLNTAB, | |
| sym.SINITARR, | |
| sym.SDATA, | |
| sym.SNOPTRDATA, | |
| sym.SELFROSECT, | |
| sym.SMACHOGOT, | |
| sym.STYPE, | |
| sym.SSTRING, | |
| sym.SGOSTRING, | |
| sym.SGOFUNC, | |
| sym.SGCBITS, | |
| sym.STYPERELRO, | |
| sym.SSTRINGRELRO, | |
| sym.SGOSTRINGRELRO, | |
| sym.SGOFUNCRELRO, | |
| sym.SGCBITSRELRO, | |
| sym.SRODATARELRO, | |
| sym.STYPELINK, | |
| sym.SITABLINK, | |
| sym.SWINDOWS: | |
| if !s.Attr.Reachable() { | |
| continue | |
| } | |
| put(ctxt, s, s.Name, DataSym, Symaddr(s), s.Gotype) | |
| case sym.SBSS, sym.SNOPTRBSS: | |
| if !s.Attr.Reachable() { | |
| continue | |
| } | |
| if len(s.P) > 0 { | |
| Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(s.P), s.Type, s.Attr.Special()) | |
| } | |
| put(ctxt, s, s.Name, BSSSym, Symaddr(s), s.Gotype) | |
| case sym.SHOSTOBJ: | |
| if ctxt.HeadType == objabi.Hwindows || ctxt.IsELF { | |
| put(ctxt, s, s.Name, UndefinedSym, s.Value, nil) | |
| } | |
| case sym.SDYNIMPORT: | |
| if !s.Attr.Reachable() { | |
| continue | |
| } | |
| put(ctxt, s, s.Extname(), UndefinedSym, 0, nil) | |
| case sym.STLSBSS: | |
| if ctxt.LinkMode == LinkExternal { | |
| put(ctxt, s, s.Name, TLSSym, Symaddr(s), s.Gotype) | |
| } | |
| } | |
| } | |
| var off int32 | |
| for _, s := range ctxt.Textp { | |
| put(ctxt, s, s.Name, TextSym, s.Value, s.Gotype) | |
| locals := int32(0) | |
| if s.FuncInfo != nil { | |
| locals = s.FuncInfo.Locals | |
| } | |
| // NOTE(ality): acid can't produce a stack trace without .frame symbols | |
| put(ctxt, nil, ".frame", FrameSym, int64(locals)+int64(ctxt.Arch.PtrSize), nil) | |
| if s.FuncInfo == nil { | |
| continue | |
| } | |
| for _, a := range s.FuncInfo.Autom { | |
| if a.Name == objabi.A_DELETED_AUTO { | |
| put(ctxt, nil, "", DeletedAutoSym, 0, a.Gotype) | |
| continue | |
| } | |
| // Emit a or p according to actual offset, even if label is wrong. | |
| // This avoids negative offsets, which cannot be encoded. | |
| if a.Name != objabi.A_AUTO && a.Name != objabi.A_PARAM { | |
| continue | |
| } | |
| // compute offset relative to FP | |
| if a.Name == objabi.A_PARAM { | |
| off = a.Aoffset | |
| } else { | |
| off = a.Aoffset - int32(ctxt.Arch.PtrSize) | |
| } | |
| // FP | |
| if off >= 0 { | |
| put(ctxt, nil, a.Asym.Name, ParamSym, int64(off), a.Gotype) | |
| continue | |
| } | |
| // SP | |
| if off <= int32(-ctxt.Arch.PtrSize) { | |
| put(ctxt, nil, a.Asym.Name, AutoSym, -(int64(off) + int64(ctxt.Arch.PtrSize)), a.Gotype) | |
| continue | |
| } | |
| // Otherwise, off is addressing the saved program counter. | |
| // Something underhanded is going on. Say nothing. | |
| } | |
| } | |
| if ctxt.Debugvlog != 0 || *flagN { | |
| ctxt.Logf("%5.2f symsize = %d\n", Cputime(), uint32(Symsize)) | |
| } | |
| } | |
| func Symaddr(s *sym.Symbol) int64 { | |
| if !s.Attr.Reachable() { | |
| Errorf(s, "unreachable symbol in symaddr") | |
| } | |
| return s.Value | |
| } | |
| func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) { | |
| s := ctxt.Syms.Lookup(p, 0) | |
| s.Type = t | |
| s.Value = v | |
| s.Attr |= sym.AttrReachable | |
| s.Attr |= sym.AttrSpecial | |
| s.Attr |= sym.AttrLocal | |
| } | |
| func datoff(s *sym.Symbol, addr int64) int64 { | |
| if uint64(addr) >= Segdata.Vaddr { | |
| return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff) | |
| } | |
| if uint64(addr) >= Segtext.Vaddr { | |
| return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff) | |
| } | |
| Errorf(s, "invalid datoff %#x", addr) | |
| return 0 | |
| } | |
| func Entryvalue(ctxt *Link) int64 { | |
| a := *flagEntrySymbol | |
| if a[0] >= '0' && a[0] <= '9' { | |
| return atolwhex(a) | |
| } | |
| s := ctxt.Syms.Lookup(a, 0) | |
| if s.Type == 0 { | |
| return *FlagTextAddr | |
| } | |
| if ctxt.HeadType != objabi.Haix && s.Type != sym.STEXT { | |
| Errorf(s, "entry not text") | |
| } | |
| return s.Value | |
| } | |
| func undefsym(ctxt *Link, s *sym.Symbol) { | |
| var r *sym.Reloc | |
| for i := 0; i < len(s.R); i++ { | |
| r = &s.R[i] | |
| if r.Sym == nil { // happens for some external ARM relocs | |
| continue | |
| } | |
| // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make | |
| // sense and should be removed when someone has thought about it properly. | |
| if (r.Sym.Type == sym.Sxxx || r.Sym.Type == sym.SXREF) && !r.Sym.Attr.VisibilityHidden() { | |
| Errorf(s, "undefined: %q", r.Sym.Name) | |
| } | |
| if !r.Sym.Attr.Reachable() && r.Type != objabi.R_WEAKADDROFF { | |
| Errorf(s, "relocation target %q", r.Sym.Name) | |
| } | |
| } | |
| } | |
| func (ctxt *Link) undef() { | |
| // undefsym performs checks (almost) identical to checks | |
| // that report undefined relocations in relocsym. | |
| // Both undefsym and relocsym can report same symbol as undefined, | |
| // which results in error message duplication (see #10978). | |
| // | |
| // The undef is run after Arch.Asmb and could detect some | |
| // programming errors there, but if object being linked is already | |
| // failed with errors, it is better to avoid duplicated errors. | |
| if nerrors > 0 { | |
| return | |
| } | |
| for _, s := range ctxt.Textp { | |
| undefsym(ctxt, s) | |
| } | |
| for _, s := range datap { | |
| undefsym(ctxt, s) | |
| } | |
| if nerrors > 0 { | |
| errorexit() | |
| } | |
| } | |
| func (ctxt *Link) callgraph() { | |
| if !*FlagC { | |
| return | |
| } | |
| var i int | |
| var r *sym.Reloc | |
| for _, s := range ctxt.Textp { | |
| for i = 0; i < len(s.R); i++ { | |
| r = &s.R[i] | |
| if r.Sym == nil { | |
| continue | |
| } | |
| if (r.Type == objabi.R_CALL || r.Type == objabi.R_CALLARM || r.Type == objabi.R_CALLPOWER || r.Type == objabi.R_CALLMIPS) && r.Sym.Type == sym.STEXT { | |
| ctxt.Logf("%s calls %s\n", s.Name, r.Sym.Name) | |
| } | |
| } | |
| } | |
| } | |
| func Rnd(v int64, r int64) int64 { | |
| if r <= 0 { | |
| return v | |
| } | |
| v += r - 1 | |
| c := v % r | |
| if c < 0 { | |
| c += r | |
| } | |
| v -= c | |
| return v | |
| } | |
| func bgetc(r *bio.Reader) int { | |
| c, err := r.ReadByte() | |
| if err != nil { | |
| if err != io.EOF { | |
| log.Fatalf("reading input: %v", err) | |
| } | |
| return -1 | |
| } | |
| return int(c) | |
| } | |
| type markKind uint8 // for postorder traversal | |
| const ( | |
| _ markKind = iota | |
| visiting | |
| visited | |
| ) | |
| func postorder(libs []*sym.Library) []*sym.Library { | |
| order := make([]*sym.Library, 0, len(libs)) // hold the result | |
| mark := make(map[*sym.Library]markKind, len(libs)) | |
| for _, lib := range libs { | |
| dfs(lib, mark, &order) | |
| } | |
| return order | |
| } | |
| func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library) { | |
| if mark[lib] == visited { | |
| return | |
| } | |
| if mark[lib] == visiting { | |
| panic("found import cycle while visiting " + lib.Pkg) | |
| } | |
| mark[lib] = visiting | |
| for _, i := range lib.Imports { | |
| dfs(i, mark, order) | |
| } | |
| mark[lib] = visited | |
| *order = append(*order, lib) | |
| } |