import (
"bytes"
- "cmd/internal/bio"
- "cmd/internal/goobj"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/sys"
- "cmd/link/internal/loadelf"
- "cmd/link/internal/loader"
- "cmd/link/internal/loadmacho"
- "cmd/link/internal/loadpe"
- "cmd/link/internal/loadxcoff"
- "cmd/link/internal/sym"
- "crypto/sha1"
"debug/elf"
"debug/macho"
"encoding/base64"
"encoding/binary"
"fmt"
"internal/buildcfg"
- exec "internal/execabs"
"io"
- "io/ioutil"
"log"
"os"
+ "os/exec"
"path/filepath"
"runtime"
+ "sort"
"strings"
"sync"
+
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/notsha256"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loadelf"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/loadmacho"
+ "cmd/link/internal/loadpe"
+ "cmd/link/internal/loadxcoff"
+ "cmd/link/internal/sym"
)
// Data layout and relocation.
DynStr loader.Sym
unreachableMethod loader.Sym
+
+ // Symbol containing a list of all the inittasks that need
+ // to be run at startup.
+ mainInittasks loader.Sym
}
// mkArchSym is a helper for setArchSyms, to set up a special symbol.
ctxt.loader.SetAttrReachable(*ls, true)
}
-// mkArchVecSym is similar to setArchSyms, but operates on elements within
+// mkArchSymVec is similar to setArchSyms, but operates on elements within
// a slice, where each element corresponds to some symbol version.
func (ctxt *Link) mkArchSymVec(name string, ver int, ls []loader.Sym) {
ls[ver] = ctxt.loader.LookupOrCreateSym(name, ver)
ctxt.mkArchSym(".dynamic", 0, &ctxt.Dynamic)
ctxt.mkArchSym(".dynsym", 0, &ctxt.DynSym)
ctxt.mkArchSym(".dynstr", 0, &ctxt.DynStr)
- ctxt.mkArchSym("runtime.unreachableMethod", sym.SymVerABIInternal, &ctxt.unreachableMethod)
+ ctxt.mkArchSym("runtime.unreachableMethod", abiInternalVer, &ctxt.unreachableMethod)
if ctxt.IsPPC64() {
ctxt.mkArchSym("TOC", 0, &ctxt.TOC)
- // NB: note the +2 below for DotTOC2 compared to the +1 for
- // DocTOC. This is because loadlibfull() creates an additional
- // syms version during conversion of loader.Sym symbols to
- // *sym.Symbol symbols. Symbols that are assigned this final
- // version are not going to have TOC references, so it should
- // be ok for them to inherit an invalid .TOC. symbol.
- // TODO: revisit the +2, now that loadlibfull is gone.
- ctxt.DotTOC = make([]loader.Sym, ctxt.MaxVersion()+2)
+ ctxt.DotTOC = make([]loader.Sym, ctxt.MaxVersion()+1)
for i := 0; i <= ctxt.MaxVersion(); i++ {
- if i >= 2 && i < sym.SymVerStatic { // these versions are not used currently
+ if i >= sym.SymVerABICount && i < sym.SymVerStatic { // these versions are not used currently
continue
}
ctxt.mkArchSymVec(".TOC.", i, ctxt.DotTOC)
// We leave some room for extra stuff like PLT stubs.
TrampLimit uint64
- Androiddynld string
- Linuxdynld string
- Freebsddynld string
- Netbsddynld string
- Openbsddynld string
- Dragonflydynld string
- Solarisdynld string
-
// Empty spaces between codeblocks will be padded with this value.
// For example an architecture might want to pad with a trap instruction to
// catch wayward programs. Architectures that do not define a padding value
// 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), number of external _host_ relocations needed (i.e.
- // ELF/Mach-O/etc. relocations, not Go relocations, this must match Elfreloc1,
+ // ELF/Mach-O/etc. relocations, not Go relocations, this must match ELF.Reloc1,
// etc.), and a boolean indicating success/failure (a failing value indicates
// a fatal error).
Archreloc func(*Target, *loader.Loader, *ArchSyms, loader.Reloc, loader.Sym,
// needed.
Extreloc func(*Target, *loader.Loader, loader.Reloc, loader.Sym) (loader.ExtReloc, bool)
- Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool
- ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1.
- Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
Gentext func(*Link, *loader.Loader) // Generate text before addressing has been performed.
Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
MachorelocSize uint32 // size of an Mach-O relocation record, must match Machoreloc1.
// optional override for assignAddress
AssignAddress func(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64)
+
+ // ELF specific information.
+ ELF ELFArch
}
var (
MINFUNC = 16 // minimum size for a function
)
+// Symbol version of ABIInternal symbols. It is sym.SymVerABIInternal if ABI wrappers
+// are used, 0 otherwise.
+var abiInternalVer = sym.SymVerABIInternal
+
// DynlinkingGo reports whether we are producing Go code that can live
// in separate shared libraries linked together at runtime.
func (ctxt *Link) DynlinkingGo() bool {
HEADR int32
nerrors int
- liveness int64
+ liveness int64 // size of liveness data (funcdata), printed if -v
// See -strictdups command line flag.
checkStrictDups int // 0=off 1=warning 2=error
Segrelrodata sym.Segment
Segdata sym.Segment
Segdwarf sym.Segment
+ Segpdata sym.Segment // windows-only
+ Segxdata sym.Segment // windows-only
- Segments = []*sym.Segment{&Segtext, &Segrodata, &Segrelrodata, &Segdata, &Segdwarf}
+ Segments = []*sym.Segment{&Segtext, &Segrodata, &Segrelrodata, &Segdata, &Segdwarf, &Segpdata, &Segxdata}
)
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 is set to true 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
+
+ // dynimportfail is a list of packages for which generating
+ // the dynimport file, _cgo_import.go, failed. If there are
+ // any of these objects, we must link externally. Issue 52863.
+ dynimportfail []string
+
+ // preferlinkext is a list of packages for which the Go command
+ // noticed use of peculiar C flags. If we see any of these,
+ // default to linking externally unless overridden by the
+ // user. See issues #58619, #58620, and #58848.
+ preferlinkext []string
+
+ // unknownObjFormat is set to true if we see an object whose
+ // format we don't recognize.
+ unknownObjFormat = false
+
+ theline string
)
func Lflag(ctxt *Link, arg string) {
} else if *flagMsan {
suffixsep = "_"
suffix = "msan"
+ } else if *flagAsan {
+ suffixsep = "_"
+ suffix = "asan"
}
- Lflag(ctxt, filepath.Join(buildcfg.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", buildcfg.GOOS, buildcfg.GOARCH, suffixsep, suffix)))
+ if buildcfg.GOROOT != "" {
+ Lflag(ctxt, filepath.Join(buildcfg.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", buildcfg.GOOS, buildcfg.GOARCH, suffixsep, suffix)))
+ }
mayberemoveoutfile()
}
}
+ if name == "runtime" {
+ Exitf("error: unable to find runtime.a")
+ }
ctxt.Logf("warning: unable to find %s.a\n", name)
return nil
}
// extld returns the current external linker.
-func (ctxt *Link) extld() string {
- if *flagExtld == "" {
- *flagExtld = "gcc"
+func (ctxt *Link) extld() []string {
+ if len(flagExtld) == 0 {
+ // Return the default external linker for the platform.
+ // This only matters when link tool is called directly without explicit -extld,
+ // go tool already passes the correct linker in other cases.
+ switch buildcfg.GOOS {
+ case "darwin", "freebsd", "openbsd":
+ flagExtld = []string{"clang"}
+ default:
+ flagExtld = []string{"gcc"}
+ }
}
- return *flagExtld
+ return flagExtld
}
// 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 {
extld := ctxt.extld()
- args := hostlinkArchArgs(ctxt.Arch)
+ name, args := extld[0], extld[1:]
+ args = append(args, hostlinkArchArgs(ctxt.Arch)...)
args = append(args, cmd)
if ctxt.Debugvlog != 0 {
ctxt.Logf("%s %v\n", extld, args)
}
- out, err := exec.Command(extld, args...).Output()
+ out, err := exec.Command(name, 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)
default:
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
}
- if !buildcfg.Experiment.RegabiWrappers {
- // Use ABI aliases if ABI wrappers are not used.
- flags |= loader.FlagUseABIAlias
- }
- elfsetstring1 := func(str string, off int) { elfsetstring(ctxt, 0, str, off) }
- ctxt.loader = loader.NewLoader(flags, elfsetstring1, &ctxt.ErrorReporter.ErrorReporter)
+ ctxt.loader = loader.NewLoader(flags, &ctxt.ErrorReporter.ErrorReporter)
ctxt.ErrorReporter.SymName = func(s loader.Sym) string {
return ctxt.loader.SymName(s)
}
if *flagMsan {
loadinternal(ctxt, "runtime/msan")
}
+ if *flagAsan {
+ loadinternal(ctxt, "runtime/asan")
+ }
loadinternal(ctxt, "runtime")
for ; i < len(ctxt.Library); i++ {
lib := ctxt.Library[i]
// If we have any undefined symbols in external
// objects, try to read them from the libgcc file.
any := false
- undefs := ctxt.loader.UndefinedRelocTargets(1)
+ undefs, froms := ctxt.loader.UndefinedRelocTargets(1)
if len(undefs) > 0 {
any = true
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("loadlib: first unresolved is %s [%d] from %s [%d]\n",
+ ctxt.loader.SymName(undefs[0]), undefs[0],
+ ctxt.loader.SymName(froms[0]), froms[0])
+ }
}
if any {
if *flagLibGCC == "" {
*flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
}
if ctxt.HeadType == objabi.Hwindows {
- if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
- hostArchive(ctxt, p)
- }
- if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
+ loadWindowsHostArchives(ctxt)
+ }
+ if *flagLibGCC != "none" {
+ hostArchive(ctxt, *flagLibGCC)
+ }
+ // For glibc systems, the linker setup used by GCC
+ // looks like
+ //
+ // GROUP ( /lib/x86_64-linux-gnu/libc.so.6
+ // /usr/lib/x86_64-linux-gnu/libc_nonshared.a
+ // AS_NEEDED ( /lib64/ld-linux-x86-64.so.2 ) )
+ //
+ // where libc_nonshared.a contains a small set of
+ // symbols including "__stack_chk_fail_local" and a
+ // few others. Thus if we are doing internal linking
+ // and "__stack_chk_fail_local" is unresolved (most
+ // likely due to the use of -fstack-protector), try
+ // loading libc_nonshared.a to resolve it.
+ //
+ // On Alpine Linux (musl-based), the library providing
+ // this symbol is called libssp_nonshared.a.
+ isunresolved := symbolsAreUnresolved(ctxt, []string{"__stack_chk_fail_local"})
+ if isunresolved[0] {
+ if p := ctxt.findLibPath("libc_nonshared.a"); p != "none" {
hostArchive(ctxt, p)
}
- // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol
- // (see https://golang.org/issue/23649 for details).
- if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" {
+ if p := ctxt.findLibPath("libssp_nonshared.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
- */
- }
- if *flagLibGCC != "none" {
- hostArchive(ctxt, *flagLibGCC)
}
}
}
// We've loaded all the code now.
ctxt.Loaded = true
- importcycles()
-
strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
}
+// loadWindowsHostArchives loads in host archives and objects when
+// doing internal linking on windows. Older toolchains seem to require
+// just a single pass through the various archives, but some modern
+// toolchains when linking a C program with mingw pass library paths
+// multiple times to the linker, e.g. "... -lmingwex -lmingw32 ...
+// -lmingwex -lmingw32 ...". To accommodate this behavior, we make two
+// passes over the host archives below.
+func loadWindowsHostArchives(ctxt *Link) {
+ any := true
+ for i := 0; any && i < 2; i++ {
+ // Link crt2.o (if present) to resolve "atexit" when
+ // using LLVM-based compilers.
+ isunresolved := symbolsAreUnresolved(ctxt, []string{"atexit"})
+ if isunresolved[0] {
+ if p := ctxt.findLibPath("crt2.o"); p != "none" {
+ hostObject(ctxt, "crt2", p)
+ }
+ }
+ if *flagRace {
+ if p := ctxt.findLibPath("libsynchronization.a"); p != "none" {
+ hostArchive(ctxt, p)
+ }
+ }
+ if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
+ hostArchive(ctxt, p)
+ }
+ if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
+ hostArchive(ctxt, p)
+ }
+ // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol
+ // (see https://golang.org/issue/23649 for details).
+ if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" {
+ hostArchive(ctxt, p)
+ }
+ any = false
+ undefs, froms := ctxt.loader.UndefinedRelocTargets(1)
+ if len(undefs) > 0 {
+ any = true
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("loadWindowsHostArchives: remaining unresolved is %s [%d] from %s [%d]\n",
+ ctxt.loader.SymName(undefs[0]), undefs[0],
+ ctxt.loader.SymName(froms[0]), froms[0])
+ }
+ }
+ }
+ // If needed, create the __CTOR_LIST__ and __DTOR_LIST__
+ // symbols (referenced by some of the mingw support library
+ // routines). Creation of these symbols is normally done by the
+ // linker if not already present.
+ want := []string{"__CTOR_LIST__", "__DTOR_LIST__"}
+ isunresolved := symbolsAreUnresolved(ctxt, want)
+ for k, w := range want {
+ if isunresolved[k] {
+ sb := ctxt.loader.CreateSymForUpdate(w, 0)
+ sb.SetType(sym.SDATA)
+ sb.AddUint64(ctxt.Arch, 0)
+ sb.SetReachable(true)
+ ctxt.loader.SetAttrSpecial(sb.Sym(), true)
+ }
+ }
+
+ // Fix up references to DLL import symbols now that we're done
+ // pulling in new objects.
+ if err := loadpe.PostProcessImports(); err != nil {
+ Errorf(nil, "%v", err)
+ }
+
+ // 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
+ */
+}
+
// loadcgodirectives reads the previously discovered cgo directives, creating
// symbols in preparation for host object loading or use later in the link.
func (ctxt *Link) loadcgodirectives() {
Peinit(ctxt)
}
- if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
+ if ctxt.LinkMode == LinkExternal {
+ // When external linking, we are creating an object file. The
+ // absolute address is irrelevant.
*FlagTextAddr = 0
}
// Set runtime.disableMemoryProfiling bool if
// runtime.MemProfile is not retained in the binary after
// deadcode (and we're not dynamically linking).
- memProfile := ctxt.loader.Lookup("runtime.MemProfile", sym.SymVerABIInternal)
+ memProfile := ctxt.loader.Lookup("runtime.MemProfile", abiInternalVer)
if memProfile != 0 && !ctxt.loader.AttrReachable(memProfile) && !ctxt.DynlinkingGo() {
memProfSym := ctxt.loader.LookupOrCreateSym("runtime.disableMemoryProfiling", 0)
sb := ctxt.loader.MakeSymbolUpdater(memProfSym)
// typeSymbolMangle mangles the given symbol name into something shorter.
//
-// Keep the type.. prefix, which parts of the linker (like the
+// 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
+// Leave type:runtime. symbols alone, because other parts of
// the linker manipulates them.
func typeSymbolMangle(name string) string {
- if !strings.HasPrefix(name, "type.") {
+ isType := strings.HasPrefix(name, "type:")
+ if !isType && !strings.Contains(name, "@") {
+ // Issue 58800: instantiated symbols may include a type name, which may contain "@"
+ return name
+ }
+ if strings.HasPrefix(name, "type:runtime.") {
return name
}
- if strings.HasPrefix(name, "type.runtime.") {
+ if strings.HasPrefix(name, "go:string.") {
+ // String symbols will be grouped to a single go:string.* symbol.
+ // No need to mangle individual symbol names.
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.."
+ if isType {
+ hash := notsha256.Sum256([]byte(name[5:]))
+ prefix := "type:"
+ if name[5] == '.' {
+ prefix = "type:."
+ }
+ return prefix + base64.StdEncoding.EncodeToString(hash[:6])
+ }
+ // instantiated symbol, replace type name in []
+ i := strings.IndexByte(name, '[')
+ j := strings.LastIndexByte(name, ']')
+ if j == -1 || j <= i {
+ j = len(name)
}
- return prefix + base64.StdEncoding.EncodeToString(hash[:6])
+ hash := notsha256.Sum256([]byte(name[i+1 : j]))
+ return name[:i+1] + base64.StdEncoding.EncodeToString(hash[:6]) + name[j:]
}
/*
continue
}
+ if arhdr.name == "dynimportfail" {
+ dynimportfail = append(dynimportfail, lib.Pkg)
+ }
+ if arhdr.name == "preferlinkext" {
+ // Ignore this directive if -linkmode has been
+ // set explicitly.
+ if ctxt.LinkMode == LinkAuto {
+ preferlinkext = append(preferlinkext, lib.Pkg)
+ }
+ }
+
// 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.
// These packages can use internal linking mode.
// Others trigger external mode.
var internalpkg = []string{
+ "crypto/internal/boring",
+ "crypto/internal/boring/syso",
"crypto/x509",
"net",
"os/user",
"runtime/cgo",
"runtime/race",
+ "runtime/race/internal/amd64v1",
+ "runtime/race/internal/amd64v3",
"runtime/msan",
+ "runtime/asan",
}
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 {
if err != nil {
Exitf("cannot reopen %s: %v", h.pn, err)
}
-
f.MustSeek(h.off, 0)
+ if h.ld == nil {
+ Errorf(nil, "%s: unrecognized object file format", h.pn)
+ continue
+ }
h.ld(ctxt, f, h.pkg, h.length, h.pn)
+ if *flagCaptureHostObjs != "" {
+ captureHostObj(h)
+ }
f.Close()
}
}
// create temporary directory and arrange cleanup
if *flagTmpdir == "" {
- dir, err := ioutil.TempDir("", "go-link-")
+ dir, err := os.MkdirTemp("", "go-link-")
if err != nil {
log.Fatal(err)
}
*flagTmpdir = dir
ownTmpDir = true
AtExit(func() {
- ctxt.Out.Close()
os.RemoveAll(*flagTmpdir)
})
}
// hostobjCopy creates a copy of the object files in hostobj in a
// temporary directory.
-func hostobjCopy() (paths []string) {
+func (ctxt *Link) 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)
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("host obj copy: %s from pkg %s -> %s\n", h.pn, h.pkg, dst)
+ }
wg.Add(1)
go func() {
}
INSERT AFTER .debug_types;
`
- err := ioutil.WriteFile(path, []byte(src), 0666)
+ err := os.WriteFile(path, []byte(src), 0666)
if err != nil {
Errorf(nil, "WriteFile %s failed: %v", name, err)
}
}
argv = append(argv, *flagOutfile)
argv = append(argv, filepath.Join(*flagTmpdir, "go.o"))
- argv = append(argv, hostobjCopy()...)
+ argv = append(argv, ctxt.hostobjCopy()...)
if ctxt.Debugvlog != 0 {
ctxt.Logf("archive: %s\n", strings.Join(argv, " "))
}
var argv []string
- argv = append(argv, ctxt.extld())
+ argv = append(argv, ctxt.extld()...)
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.
+ // so do not pass any arguments (but we strip symbols below).
} else {
argv = append(argv, "-s")
}
// On darwin, whether to combine DWARF into executable.
// Only macOS supports unmapped segments such as our __DWARF segment.
- combineDwarf := ctxt.IsDarwin() && !*FlagS && !*FlagW && !debug_s && machoPlatform == PLATFORM_MACOS
+ combineDwarf := ctxt.IsDarwin() && !*FlagW && machoPlatform == PLATFORM_MACOS
switch ctxt.HeadType {
case objabi.Hdarwin:
if ctxt.DynlinkingGo() && buildcfg.GOOS != "ios" {
// -flat_namespace is deprecated on iOS.
// It is useful for supporting plugins. We don't support plugins on iOS.
- argv = append(argv, "-Wl,-flat_namespace")
+ // -flat_namespace may cause the dynamic linker to hang at forkExec when
+ // resolving a lazy binding. See issue 38824.
+ // Force eager resolution to work around.
+ argv = append(argv, "-Wl,-flat_namespace", "-Wl,-bind_at_load")
}
if !combineDwarf {
argv = append(argv, "-Wl,-S") // suppress STAB (symbolic debugging) symbols
+ if debug_s {
+ // We are generating a binary with symbol table suppressed.
+ // Suppress local symbols. We need to keep dynamically exported
+ // and referenced symbols so the dynamic linker can resolve them.
+ argv = append(argv, "-Wl,-x")
+ }
}
case objabi.Hopenbsd:
argv = append(argv, "-Wl,-nopie")
argv = append(argv, "-pthread")
+ if ctxt.Arch.InFamily(sys.ARM64) {
+ // Disable execute-only on openbsd/arm64 - the Go arm64 assembler
+ // currently stores constants in the text section rather than in rodata.
+ // See issue #59615.
+ argv = append(argv, "-Wl,--no-execute-only")
+ }
case objabi.Hwindows:
if windowsgui {
argv = append(argv, "-mwindows")
argv = append(argv, "-Wl,-bbigtoc")
}
- // Enable ASLR on Windows.
- addASLRargs := func(argv []string) []string {
- // Enable ASLR.
- argv = append(argv, "-Wl,--dynamicbase")
+ // On PPC64, verify the external toolchain supports Power10. This is needed when
+ // PC relative relocations might be generated by Go. Only targets compiling ELF
+ // binaries might generate these relocations.
+ if ctxt.IsPPC64() && ctxt.IsElf() && buildcfg.GOPPC64 >= 10 {
+ if !linkerFlagSupported(ctxt.Arch, argv[0], "", "-mcpu=power10") {
+ Exitf("The external toolchain does not support -mcpu=power10. " +
+ " This is required to externally link GOPPC64 >= power10")
+ }
+ }
+
+ // Enable/disable ASLR on Windows.
+ addASLRargs := func(argv []string, val bool) []string {
+ // Old/ancient versions of GCC support "--dynamicbase" and
+ // "--high-entropy-va" but don't enable it by default. In
+ // addition, they don't accept "--disable-dynamicbase" or
+ // "--no-dynamicbase", so the only way to disable ASLR is to
+ // not pass any flags at all.
+ //
+ // More modern versions of GCC (and also clang) enable ASLR
+ // by default. With these compilers, however you can turn it
+ // off if you want using "--disable-dynamicbase" or
+ // "--no-dynamicbase".
+ //
+ // The strategy below is to try using "--disable-dynamicbase";
+ // if this succeeds, then assume we're working with more
+ // modern compilers and act accordingly. If it fails, assume
+ // an ancient compiler with ancient defaults.
+ var dbopt string
+ var heopt string
+ dbon := "--dynamicbase"
+ heon := "--high-entropy-va"
+ dboff := "--disable-dynamicbase"
+ heoff := "--disable-high-entropy-va"
+ if val {
+ dbopt = dbon
+ heopt = heon
+ } else {
+ // Test to see whether "--disable-dynamicbase" works.
+ newer := linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,"+dboff)
+ if newer {
+ // Newer compiler, which supports both on/off options.
+ dbopt = dboff
+ heopt = heoff
+ } else {
+ // older toolchain: we have to say nothing in order to
+ // get a no-ASLR binary.
+ dbopt = ""
+ heopt = ""
+ }
+ }
+ if dbopt != "" {
+ argv = append(argv, "-Wl,"+dbopt)
+ }
// enable high-entropy ASLR on 64-bit.
- if ctxt.Arch.PtrSize >= 8 {
- argv = append(argv, "-Wl,--high-entropy-va")
+ if ctxt.Arch.PtrSize >= 8 && heopt != "" {
+ argv = append(argv, "-Wl,"+heopt)
}
return argv
}
if ctxt.HeadType == objabi.Hdarwin {
if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() {
argv = append(argv, "-Wl,-no_pie")
- argv = append(argv, "-Wl,-pagezero_size,4000000")
}
}
+ if *flagRace && ctxt.HeadType == objabi.Hwindows {
+ // Current windows/amd64 race detector tsan support
+ // library can't handle PIE mode (see #53539 for more details).
+ // For now, explicitly disable PIE (since some compilers
+ // default to it) if -race is in effect.
+ argv = addASLRargs(argv, false)
+ }
case BuildModePIE:
switch ctxt.HeadType {
case objabi.Hdarwin, objabi.Haix:
case objabi.Hwindows:
- argv = addASLRargs(argv)
+ if *flagAslr && *flagRace {
+ // Current windows/amd64 race detector tsan support
+ // library can't handle PIE mode (see #53539 for more details).
+ // Disable alsr if -race in effect.
+ *flagAslr = false
+ }
+ argv = addASLRargs(argv, *flagAslr)
default:
// ELF.
if ctxt.UseRelro() {
}
argv = append(argv, "-shared")
if ctxt.HeadType == objabi.Hwindows {
- if *flagAslr {
- argv = addASLRargs(argv)
- }
+ argv = addASLRargs(argv, *flagAslr)
} else {
// Pass -z nodelete to mark the shared library as
// non-closeable: a dlclose will do nothing.
// 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")
+ argv = append(argv, "-Wl,-z,now")
// 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")
+ argv = append(argv, "-Wl,-z,nocopyreloc")
if buildcfg.GOOS == "android" {
// Use lld to avoid errors from default linker (issue #38838)
altLinker = "lld"
}
- if ctxt.Arch.InFamily(sys.ARM, sys.ARM64) && buildcfg.GOOS == "linux" {
- // On ARM, the GNU linker will generate COPY relocations
- // even with -znocopyreloc set.
+ if ctxt.Arch.InFamily(sys.ARM64) && buildcfg.GOOS == "linux" {
+ // On ARM64, the GNU linker will fail with
+ // -znocopyreloc if it thinks a COPY relocation is
+ // required. Switch to gold.
// 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.
+ // https://go.dev/issue/22040
altLinker = "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")
+ name, args := flagExtld[0], flagExtld[1:]
+ args = append(args, "-fuse-ld=gold", "-Wl,--version")
+ cmd := exec.Command(name, args...)
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)
+ log.Fatalf("ARM64 external linker must be gold (issue #15696, 22040), but is not: %s", out)
}
}
}
altLinker = "bfd"
// Provide a useful error if ld.bfd is missing.
- cmd := exec.Command(*flagExtld, "-fuse-ld=bfd", "-Wl,--version")
+ name, args := flagExtld[0], flagExtld[1:]
+ args = append(args, "-fuse-ld=bfd", "-Wl,--version")
+ cmd := exec.Command(name, args...)
if out, err := cmd.CombinedOutput(); err == nil {
if !bytes.Contains(out, []byte("GNU ld")) {
log.Fatalf("ARM64 external linker must be ld.bfd (issue #35197), please install devel/binutils")
// Force global symbols to be exported for dlopen, etc.
if ctxt.IsELF {
- argv = append(argv, "-rdynamic")
+ if ctxt.DynlinkingGo() || ctxt.BuildMode == BuildModeCShared || !linkerFlagSupported(ctxt.Arch, argv[0], altLinker, "-Wl,--export-dynamic-symbol=main") {
+ argv = append(argv, "-rdynamic")
+ } else {
+ var exports []string
+ ctxt.loader.ForAllCgoExportDynamic(func(s loader.Sym) {
+ exports = append(exports, "-Wl,--export-dynamic-symbol="+ctxt.loader.SymExtname(s))
+ })
+ sort.Strings(exports)
+ argv = append(argv, exports...)
+ }
}
if ctxt.HeadType == objabi.Haix {
fileName := xcoffCreateExportFile(ctxt)
argv = append(argv, unusedArguments)
}
- const compressDWARF = "-Wl,--compress-debug-sections=zlib-gnu"
+ if ctxt.IsWindows() {
+ // Suppress generation of the PE file header timestamp,
+ // so as to avoid spurious build ID differences between
+ // linked binaries that are otherwise identical other than
+ // the date/time they were linked.
+ const noTimeStamp = "-Wl,--no-insert-timestamp"
+ if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, noTimeStamp) {
+ argv = append(argv, noTimeStamp)
+ }
+ }
+
+ const compressDWARF = "-Wl,--compress-debug-sections=zlib"
if ctxt.compressDWARF && linkerFlagSupported(ctxt.Arch, argv[0], altLinker, compressDWARF) {
argv = append(argv, compressDWARF)
}
argv = append(argv, filepath.Join(*flagTmpdir, "go.o"))
- argv = append(argv, hostobjCopy()...)
+ argv = append(argv, ctxt.hostobjCopy()...)
if ctxt.HeadType == objabi.Haix {
// We want to have C files after Go files to remove
// trampolines csects made by ld.
argv = append(argv, "/lib/crt0_64.o")
extld := ctxt.extld()
+ name, args := extld[0], extld[1:]
// Get starting files.
getPathFile := func(file string) string {
- args := []string{"-maix64", "--print-file-name=" + file}
- out, err := exec.Command(extld, args...).CombinedOutput()
+ args := append(args, "-maix64", "--print-file-name="+file)
+ out, err := exec.Command(name, args...).CombinedOutput()
if err != nil {
log.Fatalf("running %s failed: %v\n%s", extld, err, out)
}
return strings.Trim(string(out), "\n")
}
- argv = append(argv, getPathFile("crtcxa.o"))
- argv = append(argv, getPathFile("crtdbase.o"))
+ // Since GCC version 11, the 64-bit version of GCC starting files
+ // are now suffixed by "_64". Even under "-maix64" multilib directory
+ // "crtcxa.o" is 32-bit.
+ crtcxa := getPathFile("crtcxa_64.o")
+ if !filepath.IsAbs(crtcxa) {
+ crtcxa = getPathFile("crtcxa.o")
+ }
+ crtdbase := getPathFile("crtdbase_64.o")
+ if !filepath.IsAbs(crtdbase) {
+ crtdbase = getPathFile("crtdbase.o")
+ }
+ argv = append(argv, crtcxa)
+ argv = append(argv, crtdbase)
}
if ctxt.linkShared {
}
}
- for _, p := range strings.Fields(*flagExtldflags) {
+ for _, p := range flagExtldflags {
argv = append(argv, p)
checkStatic(p)
}
if ctxt.HeadType == objabi.Hwindows {
// Determine which linker we're using. Add in the extldflags in
// case used has specified "-fuse-ld=...".
- cmd := exec.Command(*flagExtld, *flagExtldflags, "-Wl,--version")
+ extld := ctxt.extld()
+ name, args := extld[0], extld[1:]
+ args = append(args, trimLinkerArgv(flagExtldflags)...)
+ args = append(args, "-Wl,--version")
+ cmd := exec.Command(name, args...)
usingLLD := false
if out, err := cmd.CombinedOutput(); err == nil {
if bytes.Contains(out, []byte("LLD ")) {
p := writeGDBLinkerScript()
argv = append(argv, "-Wl,-T,"+p)
}
+ if *flagRace {
+ if p := ctxt.findLibPath("libsynchronization.a"); p != "libsynchronization.a" {
+ argv = append(argv, "-lsynchronization")
+ }
+ }
// 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()...)
}
+ argv = ctxt.passLongArgsInResponseFile(argv, altLinker)
+
if ctxt.Debugvlog != 0 {
ctxt.Logf("host link:")
for _, v := range argv {
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).
+ if ctxt.IsDarwin() && ctxt.IsAMD64() {
+ const noPieWarning = "ld: warning: -no_pie is deprecated when targeting new OS versions\n"
+ if i := bytes.Index(out, []byte(noPieWarning)); i >= 0 {
+ // swallow -no_pie deprecation warning, issue 54482
+ out = append(out[:i], out[i+len(noPieWarning):]...)
+ }
+ }
+ if ctxt.IsDarwin() {
+ const bindAtLoadWarning = "ld: warning: -bind_at_load is deprecated on macOS\n"
+ if i := bytes.Index(out, []byte(bindAtLoadWarning)); i >= 0 {
+ // -bind_at_load is deprecated with ld-prime, but needed for
+ // correctness with older versions of ld64. Swallow the warning.
+ // TODO: maybe pass -bind_at_load conditionally based on C
+ // linker version.
+ out = append(out[:i], out[i+len(bindAtLoadWarning):]...)
+ }
+ }
ctxt.Logf("%s", out)
}
if combineDwarf {
+ // Find "dsymutils" and "strip" tools using CC --print-prog-name.
+ var cc []string
+ cc = append(cc, ctxt.extld()...)
+ cc = append(cc, hostlinkArchArgs(ctxt.Arch)...)
+ cc = append(cc, "--print-prog-name", "dsymutil")
+ out, err := exec.Command(cc[0], cc[1:]...).CombinedOutput()
+ if err != nil {
+ Exitf("%s: finding dsymutil failed: %v\n%s", os.Args[0], err, out)
+ }
+ dsymutilCmd := strings.TrimSuffix(string(out), "\n")
+
+ cc[len(cc)-1] = "strip"
+ out, err = exec.Command(cc[0], cc[1:]...).CombinedOutput()
+ if err != nil {
+ Exitf("%s: finding strip failed: %v\n%s", os.Args[0], err, out)
+ }
+ stripCmd := strings.TrimSuffix(string(out), "\n")
+
dsym := filepath.Join(*flagTmpdir, "go.dwarf")
- if out, err := exec.Command("xcrun", "dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil {
+ cmd := exec.Command(dsymutilCmd, "-f", *flagOutfile, "-o", dsym)
+ // dsymutil may not clean up its temp directory at exit.
+ // Set DSYMUTIL_REPRODUCER_PATH to work around. see issue 59026.
+ cmd.Env = append(os.Environ(), "DSYMUTIL_REPRODUCER_PATH="+*flagTmpdir)
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("host link dsymutil:")
+ for _, v := range cmd.Args {
+ ctxt.Logf(" %q", v)
+ }
+ ctxt.Logf("\n")
+ }
+ if out, err := cmd.CombinedOutput(); err != nil {
Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
}
// Remove STAB (symbolic debugging) symbols after we are done with them (by dsymutil).
// They contain temporary file paths and make the build not reproducible.
- if out, err := exec.Command("xcrun", "strip", "-S", *flagOutfile).CombinedOutput(); err != nil {
+ var stripArgs = []string{"-S"}
+ if debug_s {
+ // We are generating a binary with symbol table suppressed.
+ // Suppress local symbols. We need to keep dynamically exported
+ // and referenced symbols so the dynamic linker can resolve them.
+ stripArgs = append(stripArgs, "-x")
+ }
+ stripArgs = append(stripArgs, *flagOutfile)
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("host link strip: %q", stripCmd)
+ for _, v := range stripArgs {
+ ctxt.Logf(" %q", v)
+ }
+ ctxt.Logf("\n")
+ }
+ if out, err := exec.Command(stripCmd, stripArgs...).CombinedOutput(); err != nil {
Exitf("%s: running strip failed: %v\n%s", os.Args[0], err, out)
}
// Skip combining if `dsymutil` didn't generate a file. See #11994.
}
}
+// passLongArgsInResponseFile writes the arguments into a file if they
+// are very long.
+func (ctxt *Link) passLongArgsInResponseFile(argv []string, altLinker string) []string {
+ c := 0
+ for _, arg := range argv {
+ c += len(arg)
+ }
+
+ if c < sys.ExecArgLengthLimit {
+ return argv
+ }
+
+ // Only use response files if they are supported.
+ response := filepath.Join(*flagTmpdir, "response")
+ if err := os.WriteFile(response, nil, 0644); err != nil {
+ log.Fatalf("failed while testing response file: %v", err)
+ }
+ if !linkerFlagSupported(ctxt.Arch, argv[0], altLinker, "@"+response) {
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("not using response file because linker does not support one")
+ }
+ return argv
+ }
+
+ var buf bytes.Buffer
+ for _, arg := range argv[1:] {
+ // The external linker response file supports quoted strings.
+ fmt.Fprintf(&buf, "%q\n", arg)
+ }
+ if err := os.WriteFile(response, buf.Bytes(), 0644); err != nil {
+ log.Fatalf("failed while writing response file: %v", err)
+ }
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("response file %s contents:\n%s", response, buf.Bytes())
+ }
+ return []string{
+ argv[0],
+ "@" + response,
+ }
+}
+
var createTrivialCOnce sync.Once
func linkerFlagSupported(arch *sys.Arch, linker, altLinker, 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 {
+ if err := os.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil {
Errorf(nil, "WriteFile trivial.c failed: %v", err)
}
})
+ flags := hostlinkArchArgs(arch)
+
+ moreFlags := trimLinkerArgv(append(flagExtldflags, ldflag...))
+ flags = append(flags, moreFlags...)
+
+ if altLinker != "" {
+ flags = append(flags, "-fuse-ld="+altLinker)
+ }
+ trivialPath := filepath.Join(*flagTmpdir, "trivial.c")
+ outPath := filepath.Join(*flagTmpdir, "a.out")
+ flags = append(flags, "-o", outPath, flag, trivialPath)
+
+ cmd := exec.Command(linker, flags...)
+ 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"))
+}
+
+// trimLinkerArgv returns a new copy of argv that does not include flags
+// that are not relevant for testing whether some linker option works.
+func trimLinkerArgv(argv []string) []string {
flagsWithNextArgSkip := []string{
"-F",
"-l",
"-target",
}
- flags := hostlinkArchArgs(arch)
+ var flags []string
keep := false
skip := false
- extldflags := strings.Fields(*flagExtldflags)
- for _, f := range append(extldflags, ldflag...) {
+ for _, f := range argv {
if keep {
flags = append(flags, f)
keep = false
}
}
}
-
- if altLinker != "" {
- flags = append(flags, "-fuse-ld="+altLinker)
- }
- 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"))
+ return flags
}
// hostlinkArchArgs returns arguments to pass to the external linker
if buildcfg.GOOS == "darwin" {
return []string{"-arch", "arm64"}
}
+ case sys.Loong64:
+ return []string{"-mabi=lp64d"}
case sys.MIPS64:
return []string{"-mabi=64"}
case sys.MIPS:
0xc401, // arm
0x64aa: // arm64
ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
- textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
+ ls, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
- if len(rsrc) != 0 {
- setpersrc(ctxt, rsrc)
+ if len(ls.Resources) != 0 {
+ setpersrc(ctxt, ls.Resources)
}
- ctxt.Textp = append(ctxt.Textp, textp...)
+ if ls.PData != 0 {
+ sehp.pdata = append(sehp.pdata, ls.PData)
+ }
+ if ls.XData != 0 {
+ sehp.xdata = append(sehp.xdata, ls.XData)
+ }
+ ctxt.Textp = append(ctxt.Textp, ls.Textp...)
}
return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
+ if c1 != 'g' || c2 != 'o' || c3 != ' ' || c4 != 'o' {
+ // An unrecognized object is just passed to the external linker.
+ // If we try to read symbols from this object, we will
+ // report an error at that time.
+ unknownObjFormat = true
+ return ldhostobj(nil, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
/* check the header */
line, err := f.ReadString('\n')
if err != nil {
return nil
}
- Errorf(nil, "%s: not an object file: @%d %02x%02x%02x%02x", pn, start, c1, c2, c3, c4)
+ Errorf(nil, "%s: not an object file: @%d %q", pn, start, line)
return nil
}
return nil
}
+// symbolsAreUnresolved scans through the loader's list of unresolved
+// symbols and checks to see whether any of them match the names of the
+// symbols in 'want'. Return value is a list of bools, with list[K] set
+// to true if there is an unresolved reference to the symbol in want[K].
+func symbolsAreUnresolved(ctxt *Link, want []string) []bool {
+ returnAllUndefs := -1
+ undefs, _ := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
+ seen := make(map[loader.Sym]struct{})
+ rval := make([]bool, len(want))
+ wantm := make(map[string]int)
+ for k, w := range want {
+ wantm[w] = k
+ }
+ count := 0
+ for _, s := range undefs {
+ if _, ok := seen[s]; ok {
+ continue
+ }
+ seen[s] = struct{}{}
+ if k, ok := wantm[ctxt.loader.SymName(s)]; ok {
+ rval[k] = true
+ count++
+ if count == len(want) {
+ return rval
+ }
+ }
+ }
+ return rval
+}
+
+// hostObject reads a single host object file (compare to "hostArchive").
+// This is used as part of internal linking when we need to pull in
+// files such as "crt?.o".
+func hostObject(ctxt *Link, objname string, path string) {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("hostObject(%s)\n", path)
+ }
+ objlib := sym.Library{
+ Pkg: objname,
+ }
+ f, err := bio.Open(path)
+ if err != nil {
+ Exitf("cannot open host object %q file %s: %v", objname, path, err)
+ }
+ defer f.Close()
+ h := ldobj(ctxt, f, &objlib, 0, path, path)
+ if h.ld == nil {
+ Exitf("unrecognized object file format in %s", path)
+ }
+ h.file = path
+ h.length = f.MustSeek(0, 2)
+ f.MustSeek(h.off, 0)
+ h.ld(ctxt, f, h.pkg, h.length, h.pn)
+ if *flagCaptureHostObjs != "" {
+ captureHostObj(h)
+ }
+}
+
func checkFingerprint(lib *sym.Library, libfp goobj.FingerprintType, src string, srcfp goobj.FingerprintType) {
if libfp != srcfp {
Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp)
continue
}
- // Symbols whose names start with "type." are compiler
- // generated, so make functions with that prefix internal.
+ // Symbols whose names start with "type:" are compiler generated,
+ // so make functions with that prefix internal.
ver := 0
symname := elfsym.Name // (unmangled) symbol name
- if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type.") {
- ver = sym.SymVerABIInternal
+ if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type:") {
+ ver = abiInternalVer
} else if buildcfg.Experiment.RegabiWrappers && elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC {
// Demangle the ABI name. Keep in sync with symtab.go:mangleABIName.
if strings.HasSuffix(elfsym.Name, ".abiinternal") {
// The decodetype_* functions in decodetype.go need access to
// the type data.
sname := l.SymName(s)
- if strings.HasPrefix(sname, "type.") && !strings.HasPrefix(sname, "type..") {
+ if strings.HasPrefix(sname, "type:") && !strings.HasPrefix(sname, "type:.") {
su.SetData(readelfsymboldata(ctxt, f, &elfsym))
}
}
if symname != elfsym.Name {
l.SetSymExtname(s, elfsym.Name)
}
-
- // For function symbols, if ABI wrappers are not used, we don't
- // know what ABI is available, so alias it under both ABIs.
- if !buildcfg.Experiment.RegabiWrappers && elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
- alias := ctxt.loader.LookupOrCreateSym(symname, sym.SymVerABIInternal)
- if l.SymType(alias) != 0 {
- continue
- }
- su := l.MakeSymbolUpdater(alias)
- su.SetType(sym.SABIALIAS)
- r, _ := su.AddRel(0) // type doesn't matter
- r.SetSym(s)
- }
}
ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
}
return sect
}
-type chain struct {
- sym loader.Sym
- up *chain
- limit int // limit on entry to sym
-}
-
-func haslinkregister(ctxt *Link) bool {
- return ctxt.FixedFrameSize() != 0
-}
-
-func callsize(ctxt *Link) int {
- if haslinkregister(ctxt) {
- return 0
- }
- return ctxt.Arch.RegSize
-}
-
-type stkChk struct {
- ldr *loader.Loader
- ctxt *Link
- morestack loader.Sym
- done loader.Bitmap
-}
-
-// Walk the call tree and check that there is always enough stack space
-// for the call frames, especially for a chain of nosplit functions.
-func (ctxt *Link) dostkcheck() {
- ldr := ctxt.loader
- sc := stkChk{
- ldr: ldr,
- ctxt: ctxt,
- morestack: ldr.Lookup("runtime.morestack", 0),
- done: loader.MakeBitmap(ldr.NSym()),
- }
-
- // 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.
- var ch chain
- ch.limit = objabi.StackLimit - callsize(ctxt)
- if buildcfg.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 {
- if ldr.IsNoSplit(s) {
- ch.sym = s
- sc.check(&ch, 0)
- }
- }
-
- for _, s := range ctxt.Textp {
- if !ldr.IsNoSplit(s) {
- ch.sym = s
- sc.check(&ch, 0)
- }
- }
-}
-
-func (sc *stkChk) check(up *chain, depth int) int {
- limit := up.limit
- s := up.sym
- ldr := sc.ldr
- ctxt := sc.ctxt
-
- // 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 sc.done.Has(s) {
- return 0
- }
- sc.done.Set(s)
- }
-
- if depth > 500 {
- sc.ctxt.Errorf(s, "nosplit stack check too deep")
- sc.broke(up, 0)
- return -1
- }
-
- if ldr.AttrExternal(s) {
- // 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 && ldr.SymType(s) != sym.SXREF && !ctxt.DynlinkingGo() &&
- // ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin {
- // Errorf(s, "call to external function")
- //}
- return -1
- }
- info := ldr.FuncInfo(s)
- if !info.Valid() { // external function. see above.
- return -1
- }
-
- if limit < 0 {
- sc.broke(up, limit)
- return -1
- }
-
- // morestack looks like it calls functions,
- // but it switches the stack pointer first.
- if s == sc.morestack {
- return 0
- }
-
- var ch chain
- ch.up = up
-
- if !ldr.IsNoSplit(s) {
- // Ensure we have enough stack to call morestack.
- ch.limit = limit - callsize(ctxt)
- ch.sym = sc.morestack
- if sc.check(&ch, depth+1) < 0 {
- return -1
- }
- if !top {
- return 0
- }
- // Raise limit to allow frame.
- locals := info.Locals()
- limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize())
- }
-
- // Walk through sp adjustments in function, consuming relocs.
- relocs := ldr.Relocs(s)
- var ch1 chain
- pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- ri := 0
- for pcsp.Init(ldr.Data(info.Pcsp())); !pcsp.Done; pcsp.Next() {
- // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
-
- // Check stack size in effect for this span.
- if int32(limit)-pcsp.Value < 0 {
- sc.broke(up, int(int32(limit)-pcsp.Value))
- return -1
- }
-
- // Process calls in this span.
- for ; ri < relocs.Count(); ri++ {
- r := relocs.At(ri)
- if uint32(r.Off()) >= pcsp.NextPC {
- break
- }
- t := r.Type()
- switch {
- case t.IsDirectCall():
- ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
- ch.sym = r.Sym()
- if sc.check(&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 t == objabi.R_CALLIND:
- ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
- ch.sym = 0
- ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue
- ch1.up = &ch
- ch1.sym = sc.morestack
- if sc.check(&ch1, depth+2) < 0 {
- return -1
- }
- }
- }
- }
-
- return 0
-}
-
-func (sc *stkChk) broke(ch *chain, limit int) {
- sc.ctxt.Errorf(ch.sym, "nosplit stack overflow")
- sc.print(ch, limit)
-}
-
-func (sc *stkChk) print(ch *chain, limit int) {
- ldr := sc.ldr
- ctxt := sc.ctxt
- var name string
- if ch.sym != 0 {
- name = fmt.Sprintf("%s<%d>", ldr.SymName(ch.sym), ldr.SymVersion(ch.sym))
- if ldr.IsNoSplit(ch.sym) {
- name += " (nosplit)"
- }
- } else {
- name = "function pointer"
- }
-
- if ch.up == nil {
- // top of chain. ch.sym != 0.
- if ldr.IsNoSplit(ch.sym) {
- 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 {
- sc.print(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)
}
ldr := ctxt.loader
s := ldr.Lookup(a, 0)
+ if s == 0 {
+ Errorf(nil, "missing entry symbol %q", a)
+ return 0
+ }
st := ldr.SymType(s)
if st == 0 {
return *FlagTextAddr
if rs == 0 {
continue
}
- if r.Type().IsDirectCall() && (ldr.SymType(rs) == sym.STEXT || ldr.SymType(rs) == sym.SABIALIAS) {
+ if r.Type().IsDirectCall() && ldr.SymType(rs) == sym.STEXT {
ctxt.Logf("%s calls %s\n", ldr.SymName(s), ldr.SymName(rs))
}
}
ldr.Errorf(s, "addgotsym: unsupported binary format")
}
}
+
+var hostobjcounter int
+
+// captureHostObj writes out the content of a host object (pulled from
+// an archive or loaded from a *.o file directly) to a directory
+// specified via the linker's "-capturehostobjs" debugging flag. This
+// is intended to make it easier for a developer to inspect the actual
+// object feeding into "CGO internal" link step.
+func captureHostObj(h *Hostobj) {
+ // Form paths for info file and obj file.
+ ofile := fmt.Sprintf("captured-obj-%d.o", hostobjcounter)
+ ifile := fmt.Sprintf("captured-obj-%d.txt", hostobjcounter)
+ hostobjcounter++
+ opath := filepath.Join(*flagCaptureHostObjs, ofile)
+ ipath := filepath.Join(*flagCaptureHostObjs, ifile)
+
+ // Write the info file.
+ info := fmt.Sprintf("pkg: %s\npn: %s\nfile: %s\noff: %d\nlen: %d\n",
+ h.pkg, h.pn, h.file, h.off, h.length)
+ if err := os.WriteFile(ipath, []byte(info), 0666); err != nil {
+ log.Fatalf("error writing captured host obj info %s: %v", ipath, err)
+ }
+
+ readObjData := func() []byte {
+ inf, err := os.Open(h.file)
+ if err != nil {
+ log.Fatalf("capturing host obj: open failed on %s: %v", h.pn, err)
+ }
+ res := make([]byte, h.length)
+ if n, err := inf.ReadAt(res, h.off); err != nil || n != int(h.length) {
+ log.Fatalf("capturing host obj: readat failed on %s: %v", h.pn, err)
+ }
+ return res
+ }
+
+ // Write the object file.
+ if err := os.WriteFile(opath, readObjData(), 0666); err != nil {
+ log.Fatalf("error writing captured host object %s: %v", opath, err)
+ }
+
+ fmt.Fprintf(os.Stderr, "link: info: captured host object %s to %s\n",
+ h.file, opath)
+}