]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/cmd/link/internal/ld/lib.go
cmd/internal/link: merge .pdata and .xdata sections from host object files
[gostls13.git] / src / cmd / link / internal / ld / lib.go
index 644faeb2fbc2fd039b65681621a296cc700d2e53..b603fba6c7181fa83e6723f6ed3614322076cb3e 100644 (file)
@@ -32,33 +32,33 @@ package ld
 
 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.
@@ -120,6 +120,10 @@ type ArchSyms struct {
        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.
@@ -128,7 +132,7 @@ func (ctxt *Link) mkArchSym(name string, ver int, ls *loader.Sym) {
        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)
@@ -144,21 +148,14 @@ func (ctxt *Link) setArchSyms() {
        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)
@@ -189,14 +186,6 @@ type Arch struct {
        // 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
@@ -216,7 +205,7 @@ type Arch struct {
        // 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,
@@ -251,9 +240,6 @@ type Arch struct {
        // 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.
@@ -274,6 +260,9 @@ type Arch struct {
 
        // 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 (
@@ -288,6 +277,10 @@ const (
        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 {
@@ -323,7 +316,7 @@ var (
        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
@@ -336,8 +329,10 @@ var (
        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"
@@ -348,6 +343,17 @@ var (
        // to support internal linking mode.
        externalobj = false
 
+       // 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
@@ -388,9 +394,14 @@ func libinit(ctxt *Link) {
        } 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()
 
@@ -459,28 +470,40 @@ func loadinternal(ctxt *Link, name string) *sym.Library {
                }
        }
 
+       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)
@@ -506,12 +529,7 @@ func (ctxt *Link) loadlib() {
        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)
        }
@@ -535,6 +553,9 @@ func (ctxt *Link) loadlib() {
        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]
@@ -597,9 +618,14 @@ func (ctxt *Link) loadlib() {
                // 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 == "" {
@@ -612,28 +638,35 @@ func (ctxt *Link) loadlib() {
                                *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)
                        }
                }
        }
@@ -641,11 +674,87 @@ func (ctxt *Link) loadlib() {
        // 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() {
@@ -697,7 +806,9 @@ func (ctxt *Link) linksetup() {
                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
        }
 
@@ -773,7 +884,7 @@ func (ctxt *Link) linksetup() {
                // 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)
@@ -869,26 +980,43 @@ func (ctxt *Link) mangleTypeSym() {
 
 // 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.") {
+       if strings.HasPrefix(name, "type:runtime.") {
+               return name
+       }
+       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])
        }
-       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)
+       }
+       hash := notsha256.Sum256([]byte(name[i+1 : j]))
+       return name[:i+1] + base64.StdEncoding.EncodeToString(hash[:6]) + name[j:]
 }
 
 /*
@@ -984,6 +1112,17 @@ func loadobjfile(ctxt *Link, lib *sym.Library) {
                        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.
@@ -1013,12 +1152,17 @@ var hostobj []Hostobj
 // 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 {
@@ -1069,13 +1213,15 @@ func hostobjs(ctxt *Link) {
                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()
        }
 }
@@ -1093,14 +1239,13 @@ func hostlinksetup(ctxt *Link) {
 
        // 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)
                })
        }
@@ -1119,13 +1264,16 @@ func hostlinksetup(ctxt *Link) {
 
 // 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() {
@@ -1175,7 +1323,7 @@ func writeGDBLinkerScript() string {
 }
 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)
        }
@@ -1208,7 +1356,7 @@ func (ctxt *Link) archive() {
        }
        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, " "))
@@ -1240,14 +1388,14 @@ func (ctxt *Link) hostlink() {
        }
 
        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")
                }
@@ -1255,7 +1403,7 @@ func (ctxt *Link) hostlink() {
 
        // 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:
@@ -1267,14 +1415,29 @@ func (ctxt *Link) hostlink() {
                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")
@@ -1303,13 +1466,62 @@ func (ctxt *Link) hostlink() {
                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
        }
@@ -1319,14 +1531,26 @@ func (ctxt *Link) hostlink() {
                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() {
@@ -1343,9 +1567,7 @@ func (ctxt *Link) hostlink() {
                        }
                        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.
@@ -1375,36 +1597,35 @@ func (ctxt *Link) hostlink() {
                // 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)
                                }
                        }
                }
@@ -1414,7 +1635,9 @@ func (ctxt *Link) hostlink() {
                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")
@@ -1456,7 +1679,16 @@ func (ctxt *Link) hostlink() {
 
        // 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)
@@ -1468,13 +1700,24 @@ func (ctxt *Link) hostlink() {
                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.
@@ -1482,17 +1725,29 @@ func (ctxt *Link) hostlink() {
                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 {
@@ -1567,14 +1822,18 @@ func (ctxt *Link) hostlink() {
                }
        }
 
-       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 ")) {
@@ -1588,12 +1847,19 @@ func (ctxt *Link) hostlink() {
                        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 {
@@ -1635,17 +1901,78 @@ func (ctxt *Link) hostlink() {
        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.
@@ -1679,16 +2006,80 @@ func (ctxt *Link) hostlink() {
        }
 }
 
+// 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",
@@ -1715,11 +2106,10 @@ func linkerFlagSupported(arch *sys.Arch, linker, altLinker, flag string) bool {
                "-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
@@ -1740,19 +2130,7 @@ func linkerFlagSupported(arch *sys.Arch, linker, altLinker, flag string) bool {
                        }
                }
        }
-
-       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
@@ -1774,6 +2152,8 @@ func hostlinkArchArgs(arch *sys.Arch) []string {
                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:
@@ -1840,15 +2220,21 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
                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)
        }
@@ -1956,6 +2342,64 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
        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)
@@ -2106,12 +2550,12 @@ func ldshlibsyms(ctxt *Link, shlib string) {
                        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") {
@@ -2144,7 +2588,7 @@ func ldshlibsyms(ctxt *Link, shlib string) {
                        // 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))
                        }
                }
@@ -2152,19 +2596,6 @@ func ldshlibsyms(ctxt *Link, shlib string) {
                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})
 }
@@ -2179,227 +2610,6 @@ func addsection(ldr *loader.Loader, arch *sys.Arch, seg *sym.Segment, name strin
        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)
@@ -2456,6 +2666,10 @@ func Entryvalue(ctxt *Link) int64 {
        }
        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
@@ -2480,7 +2694,7 @@ func (ctxt *Link) callgraph() {
                        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))
                        }
                }
@@ -2587,3 +2801,46 @@ func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym,
                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)
+}