summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal/ld
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal/ld')
-rw-r--r--src/cmd/link/internal/ld/ar.go247
-rw-r--r--src/cmd/link/internal/ld/asmb.go208
-rw-r--r--src/cmd/link/internal/ld/config.go307
-rw-r--r--src/cmd/link/internal/ld/data.go2956
-rw-r--r--src/cmd/link/internal/ld/data_test.go93
-rw-r--r--src/cmd/link/internal/ld/deadcode.go469
-rw-r--r--src/cmd/link/internal/ld/deadcode_test.go50
-rw-r--r--src/cmd/link/internal/ld/decodesym.go310
-rw-r--r--src/cmd/link/internal/ld/dwarf.go2328
-rw-r--r--src/cmd/link/internal/ld/dwarf_test.go1971
-rw-r--r--src/cmd/link/internal/ld/elf.go2395
-rw-r--r--src/cmd/link/internal/ld/elf_test.go127
-rw-r--r--src/cmd/link/internal/ld/errors.go67
-rw-r--r--src/cmd/link/internal/ld/execarchive.go38
-rw-r--r--src/cmd/link/internal/ld/execarchive_noexec.go14
-rw-r--r--src/cmd/link/internal/ld/fallocate_test.go65
-rw-r--r--src/cmd/link/internal/ld/go.go499
-rw-r--r--src/cmd/link/internal/ld/go_test.go116
-rw-r--r--src/cmd/link/internal/ld/heap.go54
-rw-r--r--src/cmd/link/internal/ld/heap_test.go90
-rw-r--r--src/cmd/link/internal/ld/issue33808_test.go49
-rw-r--r--src/cmd/link/internal/ld/ld.go256
-rw-r--r--src/cmd/link/internal/ld/ld_test.go343
-rw-r--r--src/cmd/link/internal/ld/lib.go2696
-rw-r--r--src/cmd/link/internal/ld/link.go161
-rw-r--r--src/cmd/link/internal/ld/macho.go1568
-rw-r--r--src/cmd/link/internal/ld/macho_combine_dwarf.go438
-rw-r--r--src/cmd/link/internal/ld/main.go444
-rw-r--r--src/cmd/link/internal/ld/msync_darwin_libc.go12
-rw-r--r--src/cmd/link/internal/ld/msync_darwin_syscall.go24
-rw-r--r--src/cmd/link/internal/ld/nooptcgolink_test.go29
-rw-r--r--src/cmd/link/internal/ld/outbuf.go325
-rw-r--r--src/cmd/link/internal/ld/outbuf_darwin.go48
-rw-r--r--src/cmd/link/internal/ld/outbuf_linux.go11
-rw-r--r--src/cmd/link/internal/ld/outbuf_mmap.go59
-rw-r--r--src/cmd/link/internal/ld/outbuf_nofallocate.go11
-rw-r--r--src/cmd/link/internal/ld/outbuf_nommap.go22
-rw-r--r--src/cmd/link/internal/ld/outbuf_notdarwin.go10
-rw-r--r--src/cmd/link/internal/ld/outbuf_test.go93
-rw-r--r--src/cmd/link/internal/ld/outbuf_windows.go79
-rw-r--r--src/cmd/link/internal/ld/pcln.go935
-rw-r--r--src/cmd/link/internal/ld/pe.go1709
-rw-r--r--src/cmd/link/internal/ld/stackcheck.go421
-rw-r--r--src/cmd/link/internal/ld/stackcheck_test.go87
-rw-r--r--src/cmd/link/internal/ld/sym.go117
-rw-r--r--src/cmd/link/internal/ld/symtab.go890
-rw-r--r--src/cmd/link/internal/ld/target.go206
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go30
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go22
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go29
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go25
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go24
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/typedesc.go16
-rw-r--r--src/cmd/link/internal/ld/testdata/httptest/main/main.go22
-rw-r--r--src/cmd/link/internal/ld/testdata/issue10978/main.go27
-rw-r--r--src/cmd/link/internal/ld/testdata/issue10978/main.s1
-rw-r--r--src/cmd/link/internal/ld/testdata/issue25459/a/a.go27
-rw-r--r--src/cmd/link/internal/ld/testdata/issue25459/main/main.go10
-rw-r--r--src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go16
-rw-r--r--src/cmd/link/internal/ld/testdata/issue26237/main/main.go16
-rw-r--r--src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m16
-rw-r--r--src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go19
-rw-r--r--src/cmd/link/internal/ld/testdata/issue32233/main/main.go11
-rw-r--r--src/cmd/link/internal/ld/testdata/issue38192/main.go11
-rw-r--r--src/cmd/link/internal/ld/testdata/issue38192/oneline.s8
-rw-r--r--src/cmd/link/internal/ld/testdata/issue39256/x.go22
-rw-r--r--src/cmd/link/internal/ld/testdata/issue39256/x.s10
-rw-r--r--src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go15
-rw-r--r--src/cmd/link/internal/ld/testdata/issue42484/main.go16
-rw-r--r--src/cmd/link/internal/ld/testdata/stackcheck/main.go20
-rw-r--r--src/cmd/link/internal/ld/testdata/stackcheck/main.s40
-rw-r--r--src/cmd/link/internal/ld/typelink.go72
-rw-r--r--src/cmd/link/internal/ld/util.go114
-rw-r--r--src/cmd/link/internal/ld/xcoff.go1815
74 files changed, 25901 insertions, 0 deletions
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
new file mode 100644
index 0000000..73c1cd3
--- /dev/null
+++ b/src/cmd/link/internal/ld/ar.go
@@ -0,0 +1,247 @@
+// Inferno utils/include/ar.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/include/ar.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "cmd/internal/bio"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "encoding/binary"
+ "fmt"
+ "internal/buildcfg"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+const (
+ SARMAG = 8
+ SAR_HDR = 16 + 44
+)
+
+const (
+ ARMAG = "!<arch>\n"
+)
+
+type ArHdr struct {
+ name string
+ date string
+ uid string
+ gid string
+ mode string
+ size string
+ fmag string
+}
+
+// pruneUndefsForWindows trims the list "undefs" of currently
+// outstanding unresolved symbols to remove references to DLL import
+// symbols (e.g. "__imp_XXX"). In older versions of the linker, we
+// would just immediately forward references from the import sym
+// (__imp_XXX) to the DLL sym (XXX), but with newer compilers this
+// strategy falls down in certain cases. We instead now do this
+// forwarding later on as a post-processing step, and meaning that
+// during the middle part of host object loading we can see a lot of
+// unresolved (SXREF) import symbols. We do not, however, want to
+// trigger the inclusion of an object from a host archive if the
+// reference is going to be eventually forwarded to the corresponding
+// SDYNIMPORT symbol, so here we strip out such refs from the undefs
+// list.
+func pruneUndefsForWindows(ldr *loader.Loader, undefs, froms []loader.Sym) ([]loader.Sym, []loader.Sym) {
+ var newundefs []loader.Sym
+ var newfroms []loader.Sym
+ for _, s := range undefs {
+ sname := ldr.SymName(s)
+ if strings.HasPrefix(sname, "__imp_") {
+ dname := sname[len("__imp_"):]
+ ds := ldr.Lookup(dname, 0)
+ if ds != 0 && ldr.SymType(ds) == sym.SDYNIMPORT {
+ // Don't try to pull things out of a host archive to
+ // satisfy this symbol.
+ continue
+ }
+ }
+ newundefs = append(newundefs, s)
+ newfroms = append(newfroms, s)
+ }
+ return newundefs, newfroms
+}
+
+// hostArchive reads an archive file holding host objects and links in
+// required objects. The general format is the same as a Go archive
+// file, but it has an armap listing symbols and the objects that
+// define them. This is used for the compiler support library
+// libgcc.a.
+func hostArchive(ctxt *Link, name string) {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("hostArchive(%s)\n", name)
+ }
+ f, err := bio.Open(name)
+ if err != nil {
+ if os.IsNotExist(err) {
+ // It's OK if we don't have a libgcc file at all.
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("skipping libgcc file: %v\n", err)
+ }
+ return
+ }
+ Exitf("cannot open file %s: %v", name, err)
+ }
+ defer f.Close()
+
+ var magbuf [len(ARMAG)]byte
+ if _, err := io.ReadFull(f, magbuf[:]); err != nil {
+ Exitf("file %s too short", name)
+ }
+
+ if string(magbuf[:]) != ARMAG {
+ Exitf("%s is not an archive file", name)
+ }
+
+ var arhdr ArHdr
+ l := nextar(f, f.Offset(), &arhdr)
+ if l <= 0 {
+ Exitf("%s missing armap", name)
+ }
+
+ var armap archiveMap
+ if arhdr.name == "/" || arhdr.name == "/SYM64/" {
+ armap = readArmap(name, f, arhdr)
+ } else {
+ Exitf("%s missing armap", name)
+ }
+
+ loaded := make(map[uint64]bool)
+ any := true
+ for any {
+ var load []uint64
+ returnAllUndefs := -1
+ undefs, froms := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
+ if buildcfg.GOOS == "windows" {
+ undefs, froms = pruneUndefsForWindows(ctxt.loader, undefs, froms)
+ }
+ for k, symIdx := range undefs {
+ sname := ctxt.loader.SymName(symIdx)
+ if off := armap[sname]; off != 0 && !loaded[off] {
+ load = append(load, off)
+ loaded[off] = true
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("hostArchive(%s): selecting object at offset %x to resolve %s [%d] reference from %s [%d]\n", name, off, sname, symIdx, ctxt.loader.SymName(froms[k]), froms[k])
+ }
+ }
+ }
+
+ for _, off := range load {
+ l := nextar(f, int64(off), &arhdr)
+ if l <= 0 {
+ Exitf("%s missing archive entry at offset %d", name, off)
+ }
+ pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
+ l = atolwhex(arhdr.size)
+
+ pkname := filepath.Base(name)
+ if i := strings.LastIndex(pkname, ".a"); i >= 0 {
+ pkname = pkname[:i]
+ }
+ libar := sym.Library{Pkg: pkname}
+ h := ldobj(ctxt, f, &libar, l, pname, name)
+ if h.ld == nil {
+ Errorf(nil, "%s unrecognized object file at offset %d", name, off)
+ continue
+ }
+ f.MustSeek(h.off, 0)
+ h.ld(ctxt, f, h.pkg, h.length, h.pn)
+ if *flagCaptureHostObjs != "" {
+ captureHostObj(h)
+ }
+ }
+
+ any = len(load) > 0
+ }
+}
+
+// archiveMap is an archive symbol map: a mapping from symbol name to
+// offset within the archive file.
+type archiveMap map[string]uint64
+
+// readArmap reads the archive symbol map.
+func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap {
+ is64 := arhdr.name == "/SYM64/"
+ wordSize := 4
+ if is64 {
+ wordSize = 8
+ }
+
+ contents := make([]byte, atolwhex(arhdr.size))
+ if _, err := io.ReadFull(f, contents); err != nil {
+ Exitf("short read from %s", filename)
+ }
+
+ var c uint64
+ if is64 {
+ c = binary.BigEndian.Uint64(contents)
+ } else {
+ c = uint64(binary.BigEndian.Uint32(contents))
+ }
+ contents = contents[wordSize:]
+
+ ret := make(archiveMap)
+
+ names := contents[c*uint64(wordSize):]
+ for i := uint64(0); i < c; i++ {
+ n := 0
+ for names[n] != 0 {
+ n++
+ }
+ name := string(names[:n])
+ names = names[n+1:]
+
+ // For Mach-O and PE/386 files we strip a leading
+ // underscore from the symbol name.
+ if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" || (buildcfg.GOOS == "windows" && buildcfg.GOARCH == "386") {
+ if name[0] == '_' && len(name) > 1 {
+ name = name[1:]
+ }
+ }
+
+ var off uint64
+ if is64 {
+ off = binary.BigEndian.Uint64(contents)
+ } else {
+ off = uint64(binary.BigEndian.Uint32(contents))
+ }
+ contents = contents[wordSize:]
+
+ ret[name] = off
+ }
+
+ return ret
+}
diff --git a/src/cmd/link/internal/ld/asmb.go b/src/cmd/link/internal/ld/asmb.go
new file mode 100644
index 0000000..cd8927b
--- /dev/null
+++ b/src/cmd/link/internal/ld/asmb.go
@@ -0,0 +1,208 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "runtime"
+ "sync"
+)
+
+// Assembling the binary is broken into two steps:
+// - writing out the code/data/dwarf Segments, applying relocations on the fly
+// - writing out the architecture specific pieces.
+//
+// This function handles the first part.
+func asmb(ctxt *Link) {
+ // TODO(jfaller): delete me.
+ if thearch.Asmb != nil {
+ thearch.Asmb(ctxt, ctxt.loader)
+ return
+ }
+
+ if ctxt.IsELF {
+ Asmbelfsetup()
+ }
+
+ var wg sync.WaitGroup
+ f := func(ctxt *Link, out *OutBuf, start, length int64) {
+ pad := thearch.CodePad
+ if pad == nil {
+ pad = zeros[:]
+ }
+ CodeblkPad(ctxt, out, start, length, pad)
+ }
+
+ for _, sect := range Segtext.Sections {
+ offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff
+ // Handle text sections with Codeblk
+ if sect.Name == ".text" {
+ writeParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length)
+ } else {
+ writeParallel(&wg, datblk, ctxt, offset, sect.Vaddr, sect.Length)
+ }
+ }
+
+ if Segrodata.Filelen > 0 {
+ writeParallel(&wg, datblk, ctxt, Segrodata.Fileoff, Segrodata.Vaddr, Segrodata.Filelen)
+ }
+
+ if Segrelrodata.Filelen > 0 {
+ writeParallel(&wg, datblk, ctxt, Segrelrodata.Fileoff, Segrelrodata.Vaddr, Segrelrodata.Filelen)
+ }
+
+ writeParallel(&wg, datblk, ctxt, Segdata.Fileoff, Segdata.Vaddr, Segdata.Filelen)
+
+ writeParallel(&wg, dwarfblk, ctxt, Segdwarf.Fileoff, Segdwarf.Vaddr, Segdwarf.Filelen)
+
+ wg.Wait()
+}
+
+// Assembling the binary is broken into two steps:
+// - writing out the code/data/dwarf Segments
+// - writing out the architecture specific pieces.
+//
+// This function handles the second part.
+func asmb2(ctxt *Link) {
+ if thearch.Asmb2 != nil {
+ thearch.Asmb2(ctxt, ctxt.loader)
+ return
+ }
+
+ symSize = 0
+ spSize = 0
+ lcSize = 0
+
+ switch ctxt.HeadType {
+ default:
+ panic("unknown platform")
+
+ // Macho
+ case objabi.Hdarwin:
+ asmbMacho(ctxt)
+
+ // Plan9
+ case objabi.Hplan9:
+ asmbPlan9(ctxt)
+
+ // PE
+ case objabi.Hwindows:
+ asmbPe(ctxt)
+
+ // Xcoff
+ case objabi.Haix:
+ asmbXcoff(ctxt)
+
+ // Elf
+ case objabi.Hdragonfly,
+ objabi.Hfreebsd,
+ objabi.Hlinux,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd,
+ objabi.Hsolaris:
+ asmbElf(ctxt)
+ }
+
+ if *FlagC {
+ fmt.Printf("textsize=%d\n", Segtext.Filelen)
+ fmt.Printf("datsize=%d\n", Segdata.Filelen)
+ fmt.Printf("bsssize=%d\n", Segdata.Length-Segdata.Filelen)
+ fmt.Printf("symsize=%d\n", symSize)
+ fmt.Printf("lcsize=%d\n", lcSize)
+ fmt.Printf("total=%d\n", Segtext.Filelen+Segdata.Length+uint64(symSize)+uint64(lcSize))
+ }
+}
+
+// writePlan9Header writes out the plan9 header at the present position in the OutBuf.
+func writePlan9Header(buf *OutBuf, magic uint32, entry int64, is64Bit bool) {
+ if is64Bit {
+ magic |= 0x00008000
+ }
+ buf.Write32b(magic)
+ buf.Write32b(uint32(Segtext.Filelen))
+ buf.Write32b(uint32(Segdata.Filelen))
+ buf.Write32b(uint32(Segdata.Length - Segdata.Filelen))
+ buf.Write32b(uint32(symSize))
+ if is64Bit {
+ buf.Write32b(uint32(entry &^ 0x80000000))
+ } else {
+ buf.Write32b(uint32(entry))
+ }
+ buf.Write32b(uint32(spSize))
+ buf.Write32b(uint32(lcSize))
+ // amd64 includes the entry at the beginning of the symbol table.
+ if is64Bit {
+ buf.Write64b(uint64(entry))
+ }
+}
+
+// asmbPlan9 assembles a plan 9 binary.
+func asmbPlan9(ctxt *Link) {
+ if !*FlagS {
+ *FlagS = true
+ symo := int64(Segdata.Fileoff + Segdata.Filelen)
+ ctxt.Out.SeekSet(symo)
+ asmbPlan9Sym(ctxt)
+ }
+ ctxt.Out.SeekSet(0)
+ writePlan9Header(ctxt.Out, thearch.Plan9Magic, Entryvalue(ctxt), thearch.Plan9_64Bit)
+}
+
+// sizeExtRelocs precomputes the size needed for the reloc records,
+// sets the size and offset for relocation records in each section,
+// and mmap the output buffer with the proper size.
+func sizeExtRelocs(ctxt *Link, relsize uint32) {
+ if relsize == 0 {
+ panic("sizeExtRelocs: relocation size not set")
+ }
+ var sz int64
+ for _, seg := range Segments {
+ for _, sect := range seg.Sections {
+ sect.Reloff = uint64(ctxt.Out.Offset() + sz)
+ sect.Rellen = uint64(relsize * sect.Relcount)
+ sz += int64(sect.Rellen)
+ }
+ }
+ filesz := ctxt.Out.Offset() + sz
+ err := ctxt.Out.Mmap(uint64(filesz))
+ if err != nil {
+ Exitf("mapping output file failed: %v", err)
+ }
+}
+
+// relocSectFn wraps the function writing relocations of a section
+// for parallel execution. Returns the wrapped function and a wait
+// group for which the caller should wait.
+func relocSectFn(ctxt *Link, relocSect func(*Link, *OutBuf, *sym.Section, []loader.Sym)) (func(*Link, *sym.Section, []loader.Sym), *sync.WaitGroup) {
+ var fn func(ctxt *Link, sect *sym.Section, syms []loader.Sym)
+ var wg sync.WaitGroup
+ var sem chan int
+ if ctxt.Out.isMmapped() {
+ // Write sections in parallel.
+ sem = make(chan int, 2*runtime.GOMAXPROCS(0))
+ fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) {
+ wg.Add(1)
+ sem <- 1
+ out, err := ctxt.Out.View(sect.Reloff)
+ if err != nil {
+ panic(err)
+ }
+ go func() {
+ relocSect(ctxt, out, sect, syms)
+ wg.Done()
+ <-sem
+ }()
+ }
+ } else {
+ // We cannot Mmap. Write sequentially.
+ fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) {
+ relocSect(ctxt, ctxt.Out, sect, syms)
+ }
+ }
+ return fn, &wg
+}
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
new file mode 100644
index 0000000..ba74b6f
--- /dev/null
+++ b/src/cmd/link/internal/ld/config.go
@@ -0,0 +1,307 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/sys"
+ "fmt"
+ "internal/buildcfg"
+ "internal/platform"
+)
+
+// A BuildMode indicates the sort of object we are building.
+//
+// Possible build modes are the same as those for the -buildmode flag
+// in cmd/go, and are documented in 'go help buildmode'.
+type BuildMode uint8
+
+const (
+ BuildModeUnset BuildMode = iota
+ BuildModeExe
+ BuildModePIE
+ BuildModeCArchive
+ BuildModeCShared
+ BuildModeShared
+ BuildModePlugin
+)
+
+func (mode *BuildMode) Set(s string) error {
+ badmode := func() error {
+ return fmt.Errorf("buildmode %s not supported on %s/%s", s, buildcfg.GOOS, buildcfg.GOARCH)
+ }
+ switch s {
+ default:
+ return fmt.Errorf("invalid buildmode: %q", s)
+ case "exe":
+ switch buildcfg.GOOS + "/" + buildcfg.GOARCH {
+ case "darwin/arm64", "windows/arm", "windows/arm64": // On these platforms, everything is PIE
+ *mode = BuildModePIE
+ default:
+ *mode = BuildModeExe
+ }
+ case "pie":
+ switch buildcfg.GOOS {
+ case "aix", "android", "linux", "windows", "darwin", "ios":
+ case "freebsd":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModePIE
+ case "c-archive":
+ switch buildcfg.GOOS {
+ case "aix", "darwin", "ios", "linux":
+ case "freebsd":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ default:
+ return badmode()
+ }
+ case "windows":
+ switch buildcfg.GOARCH {
+ case "amd64", "386", "arm", "arm64":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModeCArchive
+ case "c-shared":
+ switch buildcfg.GOARCH {
+ case "386", "amd64", "arm", "arm64", "ppc64le", "riscv64", "s390x":
+ default:
+ return badmode()
+ }
+ *mode = BuildModeCShared
+ case "shared":
+ switch buildcfg.GOOS {
+ case "linux":
+ switch buildcfg.GOARCH {
+ case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModeShared
+ case "plugin":
+ switch buildcfg.GOOS {
+ case "linux":
+ switch buildcfg.GOARCH {
+ case "386", "amd64", "arm", "arm64", "s390x", "ppc64le":
+ default:
+ return badmode()
+ }
+ case "darwin":
+ switch buildcfg.GOARCH {
+ case "amd64", "arm64":
+ default:
+ return badmode()
+ }
+ case "freebsd":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModePlugin
+ }
+ return nil
+}
+
+func (mode *BuildMode) String() string {
+ switch *mode {
+ case BuildModeUnset:
+ return "" // avoid showing a default in usage message
+ case BuildModeExe:
+ return "exe"
+ case BuildModePIE:
+ return "pie"
+ case BuildModeCArchive:
+ return "c-archive"
+ case BuildModeCShared:
+ return "c-shared"
+ case BuildModeShared:
+ return "shared"
+ case BuildModePlugin:
+ return "plugin"
+ }
+ return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
+}
+
+// LinkMode indicates whether an external linker is used for the final link.
+type LinkMode uint8
+
+const (
+ LinkAuto LinkMode = iota
+ LinkInternal
+ LinkExternal
+)
+
+func (mode *LinkMode) Set(s string) error {
+ switch s {
+ default:
+ return fmt.Errorf("invalid linkmode: %q", s)
+ case "auto":
+ *mode = LinkAuto
+ case "internal":
+ *mode = LinkInternal
+ case "external":
+ *mode = LinkExternal
+ }
+ return nil
+}
+
+func (mode *LinkMode) String() string {
+ switch *mode {
+ case LinkAuto:
+ return "auto"
+ case LinkInternal:
+ return "internal"
+ case LinkExternal:
+ return "external"
+ }
+ return fmt.Sprintf("LinkMode(%d)", uint8(*mode))
+}
+
+// mustLinkExternal reports whether the program being linked requires
+// the external linker be used to complete the link.
+func mustLinkExternal(ctxt *Link) (res bool, reason string) {
+ if ctxt.Debugvlog > 1 {
+ defer func() {
+ if res {
+ ctxt.Logf("external linking is forced by: %s\n", reason)
+ }
+ }()
+ }
+
+ if platform.MustLinkExternal(buildcfg.GOOS, buildcfg.GOARCH) {
+ return true, fmt.Sprintf("%s/%s requires external linking", buildcfg.GOOS, buildcfg.GOARCH)
+ }
+
+ if *flagMsan {
+ return true, "msan"
+ }
+
+ if *flagAsan {
+ return true, "asan"
+ }
+
+ // Internally linking cgo is incomplete on some architectures.
+ // https://golang.org/issue/14449
+ if iscgo && ctxt.Arch.InFamily(sys.Loong64, sys.MIPS64, sys.MIPS, sys.RISCV64) {
+ return true, buildcfg.GOARCH + " does not support internal cgo"
+ }
+ if iscgo && (buildcfg.GOOS == "android" || buildcfg.GOOS == "dragonfly") {
+ // It seems that on Dragonfly thread local storage is
+ // set up by the dynamic linker, so internal cgo linking
+ // doesn't work. Test case is "go test runtime/cgo".
+ return true, buildcfg.GOOS + " does not support internal cgo"
+ }
+ if iscgo && buildcfg.GOOS == "windows" && buildcfg.GOARCH == "arm64" {
+ // windows/arm64 internal linking is not implemented.
+ return true, buildcfg.GOOS + "/" + buildcfg.GOARCH + " does not support internal cgo"
+ }
+ if iscgo && ctxt.Arch == sys.ArchPPC64 {
+ // Big Endian PPC64 cgo internal linking is not implemented for aix or linux.
+ return true, buildcfg.GOOS + " does not support internal cgo"
+ }
+
+ // Some build modes require work the internal linker cannot do (yet).
+ switch ctxt.BuildMode {
+ case BuildModeCArchive:
+ return true, "buildmode=c-archive"
+ case BuildModeCShared:
+ return true, "buildmode=c-shared"
+ case BuildModePIE:
+ switch buildcfg.GOOS + "/" + buildcfg.GOARCH {
+ case "android/arm64":
+ case "linux/amd64", "linux/arm64", "linux/ppc64le":
+ case "windows/386", "windows/amd64", "windows/arm", "windows/arm64":
+ case "darwin/amd64", "darwin/arm64":
+ default:
+ // Internal linking does not support TLS_IE.
+ return true, "buildmode=pie"
+ }
+ case BuildModePlugin:
+ return true, "buildmode=plugin"
+ case BuildModeShared:
+ return true, "buildmode=shared"
+ }
+ if ctxt.linkShared {
+ return true, "dynamically linking with a shared library"
+ }
+
+ if unknownObjFormat {
+ return true, "some input objects have an unrecognized file format"
+ }
+
+ if len(dynimportfail) > 0 {
+ // This error means that we were unable to generate
+ // the _cgo_import.go file for some packages.
+ // This typically means that there are some dependencies
+ // that the cgo tool could not figure out.
+ // See issue #52863.
+ return true, fmt.Sprintf("some packages could not be built to support internal linking (%v)", dynimportfail)
+ }
+
+ return false, ""
+}
+
+// determineLinkMode sets ctxt.LinkMode.
+//
+// It is called after flags are processed and inputs are processed,
+// so the ctxt.LinkMode variable has an initial value from the -linkmode
+// flag and the iscgo, externalobj, and unknownObjFormat variables are set.
+func determineLinkMode(ctxt *Link) {
+ extNeeded, extReason := mustLinkExternal(ctxt)
+ via := ""
+
+ if ctxt.LinkMode == LinkAuto {
+ // The environment variable GO_EXTLINK_ENABLED controls the
+ // default value of -linkmode. If it is not set when the
+ // linker is called we take the value it was set to when
+ // cmd/link was compiled. (See make.bash.)
+ switch buildcfg.Getgoextlinkenabled() {
+ case "0":
+ ctxt.LinkMode = LinkInternal
+ via = "via GO_EXTLINK_ENABLED "
+ case "1":
+ ctxt.LinkMode = LinkExternal
+ via = "via GO_EXTLINK_ENABLED "
+ default:
+ preferExternal := len(preferlinkext) != 0
+ if preferExternal && ctxt.Debugvlog > 0 {
+ ctxt.Logf("external linking prefer list is %v\n", preferlinkext)
+ }
+ if extNeeded || (iscgo && (externalobj || preferExternal)) {
+ ctxt.LinkMode = LinkExternal
+ } else {
+ ctxt.LinkMode = LinkInternal
+ }
+ }
+ }
+
+ switch ctxt.LinkMode {
+ case LinkInternal:
+ if extNeeded {
+ Exitf("internal linking requested %sbut external linking required: %s", via, extReason)
+ }
+ case LinkExternal:
+ switch {
+ case buildcfg.GOARCH == "ppc64" && buildcfg.GOOS != "aix":
+ Exitf("external linking not supported for %s/ppc64", buildcfg.GOOS)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
new file mode 100644
index 0000000..afba7d3
--- /dev/null
+++ b/src/cmd/link/internal/ld/data.go
@@ -0,0 +1,2956 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "bytes"
+ "cmd/internal/gcprog"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/loadpe"
+ "cmd/link/internal/sym"
+ "compress/zlib"
+ "debug/elf"
+ "encoding/binary"
+ "fmt"
+ "log"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+)
+
+// isRuntimeDepPkg reports whether pkg is the runtime package or its dependency.
+func isRuntimeDepPkg(pkg string) bool {
+ switch pkg {
+ case "runtime",
+ "sync/atomic", // runtime may call to sync/atomic, due to go:linkname
+ "internal/abi", // used by reflectcall (and maybe more)
+ "internal/bytealg", // for IndexByte
+ "internal/cpu": // for cpu features
+ return true
+ }
+ return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test")
+}
+
+// Estimate the max size needed to hold any new trampolines created for this function. This
+// is used to determine when the section can be split if it becomes too large, to ensure that
+// the trampolines are in the same section as the function that uses them.
+func maxSizeTrampolines(ctxt *Link, ldr *loader.Loader, s loader.Sym, isTramp bool) uint64 {
+ // If thearch.Trampoline is nil, then trampoline support is not available on this arch.
+ // A trampoline does not need any dependent trampolines.
+ if thearch.Trampoline == nil || isTramp {
+ return 0
+ }
+
+ n := uint64(0)
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Type().IsDirectCallOrJump() {
+ n++
+ }
+ }
+
+ if ctxt.IsARM() {
+ return n * 20 // Trampolines in ARM range from 3 to 5 instructions.
+ }
+ if ctxt.IsPPC64() {
+ return n * 16 // Trampolines in PPC64 are 4 instructions.
+ }
+ if ctxt.IsARM64() {
+ return n * 12 // Trampolines in ARM64 are 3 instructions.
+ }
+ panic("unreachable")
+}
+
+// Detect too-far jumps in function s, and add trampolines if necessary.
+// ARM, PPC64, PPC64LE and RISCV64 support trampoline insertion for internal
+// and external linking. On PPC64 and PPC64LE the text sections might be split
+// but will still insert trampolines where necessary.
+func trampoline(ctxt *Link, s loader.Sym) {
+ if thearch.Trampoline == nil {
+ return // no need or no support of trampolines on this arch
+ }
+
+ ldr := ctxt.loader
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rt := r.Type()
+ if !rt.IsDirectCallOrJump() && !isPLTCall(rt) {
+ continue
+ }
+ rs := r.Sym()
+ if !ldr.AttrReachable(rs) || ldr.SymType(rs) == sym.Sxxx {
+ continue // something is wrong. skip it here and we'll emit a better error later
+ }
+
+ // RISC-V is only able to reach +/-1MiB via a JAL instruction,
+ // which we can readily exceed in the same package. As such, we
+ // need to generate trampolines when the address is unknown.
+ if ldr.SymValue(rs) == 0 && !ctxt.Target.IsRISCV64() && ldr.SymType(rs) != sym.SDYNIMPORT && ldr.SymType(rs) != sym.SUNDEFEXT {
+ if ldr.SymPkg(s) != "" && ldr.SymPkg(rs) == ldr.SymPkg(s) {
+ // Symbols in the same package are laid out together.
+ // Except that if SymPkg(s) == "", it is a host object symbol
+ // which may call an external symbol via PLT.
+ continue
+ }
+ if isRuntimeDepPkg(ldr.SymPkg(s)) && isRuntimeDepPkg(ldr.SymPkg(rs)) {
+ continue // runtime packages are laid out together
+ }
+ }
+ thearch.Trampoline(ctxt, ldr, ri, rs, s)
+ }
+}
+
+// whether rt is a (host object) relocation that will be turned into
+// a call to PLT.
+func isPLTCall(rt objabi.RelocType) bool {
+ const pcrel = 1
+ switch rt {
+ // ARM64
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26),
+ objabi.MachoRelocOffset + MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
+ return true
+
+ // ARM
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24):
+ return true
+ }
+ // TODO: other architectures.
+ return false
+}
+
+// FoldSubSymbolOffset computes the offset of symbol s to its top-level outer
+// symbol. Returns the top-level symbol and the offset.
+// This is used in generating external relocations.
+func FoldSubSymbolOffset(ldr *loader.Loader, s loader.Sym) (loader.Sym, int64) {
+ outer := ldr.OuterSym(s)
+ off := int64(0)
+ if outer != 0 {
+ off += ldr.SymValue(s) - ldr.SymValue(outer)
+ s = outer
+ }
+ return s, off
+}
+
+// relocsym resolve relocations in "s", updating the symbol's content
+// in "P".
+// The main loop walks through the list of relocations attached to "s"
+// and resolves them where applicable. Relocations are often
+// architecture-specific, requiring calls into the 'archreloc' and/or
+// 'archrelocvariant' functions for the architecture. When external
+// linking is in effect, it may not be possible to completely resolve
+// the address/offset for a symbol, in which case the goal is to lay
+// the groundwork for turning a given relocation into an external reloc
+// (to be applied by the external linker). For more on how relocations
+// work in general, see
+//
+// "Linkers and Loaders", by John R. Levine (Morgan Kaufmann, 1999), ch. 7
+//
+// This is a performance-critical function for the linker; be careful
+// to avoid introducing unnecessary allocations in the main loop.
+func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
+ ldr := st.ldr
+ relocs := ldr.Relocs(s)
+ if relocs.Count() == 0 {
+ return
+ }
+ target := st.target
+ syms := st.syms
+ nExtReloc := 0 // number of external relocations
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ off := r.Off()
+ siz := int32(r.Siz())
+ rs := r.Sym()
+ rt := r.Type()
+ weak := r.Weak()
+ if off < 0 || off+siz > int32(len(P)) {
+ rname := ""
+ if rs != 0 {
+ rname = ldr.SymName(rs)
+ }
+ st.err.Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(P))
+ continue
+ }
+ if siz == 0 { // informational relocation - no work to do
+ continue
+ }
+
+ var rst sym.SymKind
+ if rs != 0 {
+ rst = ldr.SymType(rs)
+ }
+
+ if rs != 0 && (rst == sym.Sxxx || rst == sym.SXREF) {
+ // When putting the runtime but not main into a shared library
+ // these symbols are undefined and that's OK.
+ if target.IsShared() || target.IsPlugin() {
+ if ldr.SymName(rs) == "main.main" || (!target.IsPlugin() && ldr.SymName(rs) == "main..inittask") {
+ sb := ldr.MakeSymbolUpdater(rs)
+ sb.SetType(sym.SDYNIMPORT)
+ } else if strings.HasPrefix(ldr.SymName(rs), "go:info.") {
+ // Skip go.info symbols. They are only needed to communicate
+ // DWARF info between the compiler and linker.
+ continue
+ }
+ } else if target.IsPPC64() && ldr.SymName(rs) == ".TOC." {
+ // TOC symbol doesn't have a type but we do assign a value
+ // (see the address pass) and we can resolve it.
+ // TODO: give it a type.
+ } else {
+ st.err.errorUnresolved(ldr, s, rs)
+ continue
+ }
+ }
+
+ if rt >= objabi.ElfRelocOffset {
+ continue
+ }
+
+ // We need to be able to reference dynimport symbols when linking against
+ // shared libraries, and AIX, Darwin, OpenBSD and Solaris always need it.
+ if !target.IsAIX() && !target.IsDarwin() && !target.IsSolaris() && !target.IsOpenbsd() && rs != 0 && rst == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !ldr.AttrSubSymbol(rs) {
+ if !(target.IsPPC64() && target.IsExternal() && ldr.SymName(rs) == ".TOC.") {
+ st.err.Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", ldr.SymName(rs), rst, rst, rt, sym.RelocName(target.Arch, rt))
+ }
+ }
+ if rs != 0 && rst != sym.STLSBSS && !weak && rt != objabi.R_METHODOFF && !ldr.AttrReachable(rs) {
+ st.err.Errorf(s, "unreachable sym in relocation: %s", ldr.SymName(rs))
+ }
+
+ var rv sym.RelocVariant
+ if target.IsPPC64() || target.IsS390X() {
+ rv = ldr.RelocVariant(s, ri)
+ }
+
+ // TODO(mundaym): remove this special case - see issue 14218.
+ if target.IsS390X() {
+ switch rt {
+ case objabi.R_PCRELDBL:
+ rt = objabi.R_PCREL
+ rv = sym.RV_390_DBL
+ case objabi.R_CALL:
+ rv = sym.RV_390_DBL
+ }
+ }
+
+ var o int64
+ switch rt {
+ default:
+ switch siz {
+ default:
+ st.err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs))
+ case 1:
+ o = int64(P[off])
+ case 2:
+ o = int64(target.Arch.ByteOrder.Uint16(P[off:]))
+ case 4:
+ o = int64(target.Arch.ByteOrder.Uint32(P[off:]))
+ case 8:
+ o = int64(target.Arch.ByteOrder.Uint64(P[off:]))
+ }
+ out, n, ok := thearch.Archreloc(target, ldr, syms, r, s, o)
+ if target.IsExternal() {
+ nExtReloc += n
+ }
+ if ok {
+ o = out
+ } else {
+ st.err.Errorf(s, "unknown reloc to %v: %d (%s)", ldr.SymName(rs), rt, sym.RelocName(target.Arch, rt))
+ }
+ case objabi.R_TLS_LE:
+ if target.IsExternal() && target.IsElf() {
+ nExtReloc++
+ o = 0
+ if !target.IsAMD64() {
+ o = r.Add()
+ }
+ break
+ }
+
+ if target.IsElf() && target.IsARM() {
+ // On ELF ARM, the thread pointer is 8 bytes before
+ // the start of the thread-local data block, so add 8
+ // to the actual TLS offset (r->sym->value).
+ // This 8 seems to be a fundamental constant of
+ // ELF on ARM (or maybe Glibc on ARM); it is not
+ // related to the fact that our own TLS storage happens
+ // to take up 8 bytes.
+ o = 8 + ldr.SymValue(rs)
+ } else if target.IsElf() || target.IsPlan9() || target.IsDarwin() {
+ o = int64(syms.Tlsoffset) + r.Add()
+ } else if target.IsWindows() {
+ o = r.Add()
+ } else {
+ log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType)
+ }
+ case objabi.R_TLS_IE:
+ if target.IsExternal() && target.IsElf() {
+ nExtReloc++
+ o = 0
+ if !target.IsAMD64() {
+ o = r.Add()
+ }
+ if target.Is386() {
+ nExtReloc++ // need two ELF relocations on 386, see ../x86/asm.go:elfreloc1
+ }
+ break
+ }
+ if target.IsPIE() && target.IsElf() {
+ // We are linking the final executable, so we
+ // can optimize any TLS IE relocation to LE.
+ if thearch.TLSIEtoLE == nil {
+ log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family)
+ }
+ thearch.TLSIEtoLE(P, int(off), int(siz))
+ o = int64(syms.Tlsoffset)
+ } else {
+ log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s))
+ }
+ case objabi.R_ADDR:
+ if weak && !ldr.AttrReachable(rs) {
+ // Redirect it to runtime.unreachableMethod, which will throw if called.
+ rs = syms.unreachableMethod
+ }
+ if target.IsExternal() {
+ nExtReloc++
+
+ // set up addend for eventual relocation via outer symbol.
+ rs := rs
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ xadd := r.Add() + off
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil {
+ st.err.Errorf(s, "missing section for relocation target %s", ldr.SymName(rs))
+ }
+
+ o = xadd
+ if target.IsElf() {
+ if target.IsAMD64() {
+ o = 0
+ }
+ } else if target.IsDarwin() {
+ if ldr.SymType(rs) != sym.SHOSTOBJ {
+ o += ldr.SymValue(rs)
+ }
+ } else if target.IsWindows() {
+ // nothing to do
+ } else if target.IsAIX() {
+ o = ldr.SymValue(rs) + xadd
+ } else {
+ st.err.Errorf(s, "unhandled pcrel relocation to %s on %v", ldr.SymName(rs), target.HeadType)
+ }
+
+ break
+ }
+
+ // On AIX, a second relocation must be done by the loader,
+ // as section addresses can change once loaded.
+ // The "default" symbol address is still needed by the loader so
+ // the current relocation can't be skipped.
+ if target.IsAIX() && rst != sym.SDYNIMPORT {
+ // It's not possible to make a loader relocation in a
+ // symbol which is not inside .data section.
+ // FIXME: It should be forbidden to have R_ADDR from a
+ // symbol which isn't in .data. However, as .text has the
+ // same address once loaded, this is possible.
+ if ldr.SymSect(s).Seg == &Segdata {
+ Xcoffadddynrel(target, ldr, syms, s, r, ri)
+ }
+ }
+
+ o = ldr.SymValue(rs) + r.Add()
+
+ // On amd64, 4-byte offsets will be sign-extended, so it is impossible to
+ // access more than 2GB of static data; fail at link time is better than
+ // fail at runtime. See https://golang.org/issue/7980.
+ // Instead of special casing only amd64, we treat this as an error on all
+ // 64-bit architectures so as to be future-proof.
+ if int32(o) < 0 && target.Arch.PtrSize > 4 && siz == 4 {
+ st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", ldr.SymName(rs), uint64(o), ldr.SymValue(rs), r.Add())
+ errorexit()
+ }
+ case objabi.R_DWARFSECREF:
+ if ldr.SymSect(rs) == nil {
+ st.err.Errorf(s, "missing DWARF section for relocation target %s", ldr.SymName(rs))
+ }
+
+ if target.IsExternal() {
+ // On most platforms, the external linker needs to adjust DWARF references
+ // as it combines DWARF sections. However, on Darwin, dsymutil does the
+ // DWARF linking, and it understands how to follow section offsets.
+ // Leaving in the relocation records confuses it (see
+ // https://golang.org/issue/22068) so drop them for Darwin.
+ if !target.IsDarwin() {
+ nExtReloc++
+ }
+
+ xadd := r.Add() + ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr)
+
+ o = xadd
+ if target.IsElf() && target.IsAMD64() {
+ o = 0
+ }
+ break
+ }
+ o = ldr.SymValue(rs) + r.Add() - int64(ldr.SymSect(rs).Vaddr)
+ case objabi.R_METHODOFF:
+ if !ldr.AttrReachable(rs) {
+ // Set it to a sentinel value. The runtime knows this is not pointing to
+ // anything valid.
+ o = -1
+ break
+ }
+ fallthrough
+ case objabi.R_ADDROFF:
+ if weak && !ldr.AttrReachable(rs) {
+ continue
+ }
+ if ldr.SymSect(rs) == nil {
+ st.err.Errorf(s, "unreachable sym in relocation: %s", ldr.SymName(rs))
+ continue
+ }
+
+ // The method offset tables using this relocation expect the offset to be relative
+ // to the start of the first text section, even if there are multiple.
+ if ldr.SymSect(rs).Name == ".text" {
+ o = ldr.SymValue(rs) - int64(Segtext.Sections[0].Vaddr) + r.Add()
+ } else {
+ o = ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + r.Add()
+ }
+
+ case objabi.R_ADDRCUOFF:
+ // debug_range and debug_loc elements use this relocation type to get an
+ // offset from the start of the compile unit.
+ o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(loader.Sym(ldr.SymUnit(rs).Textp[0]))
+
+ // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call.
+ case objabi.R_GOTPCREL:
+ if target.IsDynlinkingGo() && target.IsDarwin() && rs != 0 {
+ nExtReloc++
+ o = r.Add()
+ break
+ }
+ if target.Is386() && target.IsExternal() && target.IsELF {
+ nExtReloc++ // need two ELF relocations on 386, see ../x86/asm.go:elfreloc1
+ }
+ fallthrough
+ case objabi.R_CALL, objabi.R_PCREL:
+ if target.IsExternal() && rs != 0 && rst == sym.SUNDEFEXT {
+ // pass through to the external linker.
+ nExtReloc++
+ o = 0
+ break
+ }
+ if target.IsExternal() && rs != 0 && (ldr.SymSect(rs) != ldr.SymSect(s) || rt == objabi.R_GOTPCREL) {
+ nExtReloc++
+
+ // set up addend for eventual relocation via outer symbol.
+ rs := rs
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ xadd := r.Add() + off - int64(siz) // relative to address after the relocated chunk
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil {
+ st.err.Errorf(s, "missing section for relocation target %s", ldr.SymName(rs))
+ }
+
+ o = xadd
+ if target.IsElf() {
+ if target.IsAMD64() {
+ o = 0
+ }
+ } else if target.IsDarwin() {
+ if rt == objabi.R_CALL {
+ if target.IsExternal() && rst == sym.SDYNIMPORT {
+ if target.IsAMD64() {
+ // AMD64 dynamic relocations are relative to the end of the relocation.
+ o += int64(siz)
+ }
+ } else {
+ if rst != sym.SHOSTOBJ {
+ o += int64(uint64(ldr.SymValue(rs)) - ldr.SymSect(rs).Vaddr)
+ }
+ o -= int64(off) // relative to section offset, not symbol
+ }
+ } else {
+ o += int64(siz)
+ }
+ } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL
+ // PE/COFF's PC32 relocation uses the address after the relocated
+ // bytes as the base. Compensate by skewing the addend.
+ o += int64(siz)
+ } else {
+ st.err.Errorf(s, "unhandled pcrel relocation to %s on %v", ldr.SymName(rs), target.HeadType)
+ }
+
+ break
+ }
+
+ o = 0
+ if rs != 0 {
+ o = ldr.SymValue(rs)
+ }
+
+ o += r.Add() - (ldr.SymValue(s) + int64(off) + int64(siz))
+ case objabi.R_SIZE:
+ o = ldr.SymSize(rs) + r.Add()
+
+ case objabi.R_XCOFFREF:
+ if !target.IsAIX() {
+ st.err.Errorf(s, "find XCOFF R_REF on non-XCOFF files")
+ }
+ if !target.IsExternal() {
+ st.err.Errorf(s, "find XCOFF R_REF with internal linking")
+ }
+ nExtReloc++
+ continue
+
+ case objabi.R_DWARFFILEREF:
+ // We don't renumber files in dwarf.go:writelines anymore.
+ continue
+
+ case objabi.R_CONST:
+ o = r.Add()
+
+ case objabi.R_GOTOFF:
+ o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(syms.GOT)
+ }
+
+ if target.IsPPC64() || target.IsS390X() {
+ if rv != sym.RV_NONE {
+ o = thearch.Archrelocvariant(target, ldr, r, rv, s, o, P)
+ }
+ }
+
+ switch siz {
+ default:
+ st.err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs))
+ case 1:
+ P[off] = byte(int8(o))
+ case 2:
+ if o != int64(int16(o)) {
+ st.err.Errorf(s, "relocation address for %s is too big: %#x", ldr.SymName(rs), o)
+ }
+ target.Arch.ByteOrder.PutUint16(P[off:], uint16(o))
+ case 4:
+ if rt == objabi.R_PCREL || rt == objabi.R_CALL {
+ if o != int64(int32(o)) {
+ st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
+ }
+ } else {
+ if o != int64(int32(o)) && o != int64(uint32(o)) {
+ st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
+ }
+ }
+ target.Arch.ByteOrder.PutUint32(P[off:], uint32(o))
+ case 8:
+ target.Arch.ByteOrder.PutUint64(P[off:], uint64(o))
+ }
+ }
+ if target.IsExternal() {
+ // We'll stream out the external relocations in asmb2 (e.g. elfrelocsect)
+ // and we only need the count here.
+ atomic.AddUint32(&ldr.SymSect(s).Relcount, uint32(nExtReloc))
+ }
+}
+
+// Convert a Go relocation to an external relocation.
+func extreloc(ctxt *Link, ldr *loader.Loader, s loader.Sym, r loader.Reloc) (loader.ExtReloc, bool) {
+ var rr loader.ExtReloc
+ target := &ctxt.Target
+ siz := int32(r.Siz())
+ if siz == 0 { // informational relocation - no work to do
+ return rr, false
+ }
+
+ rt := r.Type()
+ if rt >= objabi.ElfRelocOffset {
+ return rr, false
+ }
+ rr.Type = rt
+ rr.Size = uint8(siz)
+
+ // TODO(mundaym): remove this special case - see issue 14218.
+ if target.IsS390X() {
+ switch rt {
+ case objabi.R_PCRELDBL:
+ rt = objabi.R_PCREL
+ }
+ }
+
+ switch rt {
+ default:
+ return thearch.Extreloc(target, ldr, r, s)
+
+ case objabi.R_TLS_LE, objabi.R_TLS_IE:
+ if target.IsElf() {
+ rs := r.Sym()
+ rr.Xsym = rs
+ if rr.Xsym == 0 {
+ rr.Xsym = ctxt.Tlsg
+ }
+ rr.Xadd = r.Add()
+ break
+ }
+ return rr, false
+
+ case objabi.R_ADDR:
+ // set up addend for eventual relocation via outer symbol.
+ rs := r.Sym()
+ if r.Weak() && !ldr.AttrReachable(rs) {
+ rs = ctxt.ArchSyms.unreachableMethod
+ }
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ rr.Xadd = r.Add() + off
+ rr.Xsym = rs
+
+ case objabi.R_DWARFSECREF:
+ // On most platforms, the external linker needs to adjust DWARF references
+ // as it combines DWARF sections. However, on Darwin, dsymutil does the
+ // DWARF linking, and it understands how to follow section offsets.
+ // Leaving in the relocation records confuses it (see
+ // https://golang.org/issue/22068) so drop them for Darwin.
+ if target.IsDarwin() {
+ return rr, false
+ }
+ rs := r.Sym()
+ rr.Xsym = loader.Sym(ldr.SymSect(rs).Sym)
+ rr.Xadd = r.Add() + ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr)
+
+ // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call.
+ case objabi.R_GOTPCREL, objabi.R_CALL, objabi.R_PCREL:
+ rs := r.Sym()
+ if rt == objabi.R_GOTPCREL && target.IsDynlinkingGo() && target.IsDarwin() && rs != 0 {
+ rr.Xadd = r.Add()
+ rr.Xadd -= int64(siz) // relative to address after the relocated chunk
+ rr.Xsym = rs
+ break
+ }
+ if rs != 0 && ldr.SymType(rs) == sym.SUNDEFEXT {
+ // pass through to the external linker.
+ rr.Xadd = 0
+ if target.IsElf() {
+ rr.Xadd -= int64(siz)
+ }
+ rr.Xsym = rs
+ break
+ }
+ if rs != 0 && (ldr.SymSect(rs) != ldr.SymSect(s) || rt == objabi.R_GOTPCREL) {
+ // set up addend for eventual relocation via outer symbol.
+ rs := rs
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ rr.Xadd = r.Add() + off
+ rr.Xadd -= int64(siz) // relative to address after the relocated chunk
+ rr.Xsym = rs
+ break
+ }
+ return rr, false
+
+ case objabi.R_XCOFFREF:
+ return ExtrelocSimple(ldr, r), true
+
+ // These reloc types don't need external relocations.
+ case objabi.R_ADDROFF, objabi.R_METHODOFF, objabi.R_ADDRCUOFF,
+ objabi.R_SIZE, objabi.R_CONST, objabi.R_GOTOFF:
+ return rr, false
+ }
+ return rr, true
+}
+
+// ExtrelocSimple creates a simple external relocation from r, with the same
+// symbol and addend.
+func ExtrelocSimple(ldr *loader.Loader, r loader.Reloc) loader.ExtReloc {
+ var rr loader.ExtReloc
+ rs := r.Sym()
+ rr.Xsym = rs
+ rr.Xadd = r.Add()
+ rr.Type = r.Type()
+ rr.Size = r.Siz()
+ return rr
+}
+
+// ExtrelocViaOuterSym creates an external relocation from r targeting the
+// outer symbol and folding the subsymbol's offset into the addend.
+func ExtrelocViaOuterSym(ldr *loader.Loader, r loader.Reloc, s loader.Sym) loader.ExtReloc {
+ // set up addend for eventual relocation via outer symbol.
+ var rr loader.ExtReloc
+ rs := r.Sym()
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ rr.Xadd = r.Add() + off
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil {
+ ldr.Errorf(s, "missing section for %s", ldr.SymName(rs))
+ }
+ rr.Xsym = rs
+ rr.Type = r.Type()
+ rr.Size = r.Siz()
+ return rr
+}
+
+// relocSymState hold state information needed when making a series of
+// successive calls to relocsym(). The items here are invariant
+// (meaning that they are set up once initially and then don't change
+// during the execution of relocsym), with the exception of a slice
+// used to facilitate batch allocation of external relocations. Calls
+// to relocsym happen in parallel; the assumption is that each
+// parallel thread will have its own state object.
+type relocSymState struct {
+ target *Target
+ ldr *loader.Loader
+ err *ErrorReporter
+ syms *ArchSyms
+}
+
+// makeRelocSymState creates a relocSymState container object to
+// pass to relocsym(). If relocsym() calls happen in parallel,
+// each parallel thread should have its own state object.
+func (ctxt *Link) makeRelocSymState() *relocSymState {
+ return &relocSymState{
+ target: &ctxt.Target,
+ ldr: ctxt.loader,
+ err: &ctxt.ErrorReporter,
+ syms: &ctxt.ArchSyms,
+ }
+}
+
+// windynrelocsym examines a text symbol 's' and looks for relocations
+// from it that correspond to references to symbols defined in DLLs,
+// then fixes up those relocations as needed. A reference to a symbol
+// XYZ from some DLL will fall into one of two categories: an indirect
+// ref via "__imp_XYZ", or a direct ref to "XYZ". Here's an example of
+// an indirect ref (this is an excerpt from objdump -ldr):
+//
+// 1c1: 48 89 c6 movq %rax, %rsi
+// 1c4: ff 15 00 00 00 00 callq *(%rip)
+// 00000000000001c6: IMAGE_REL_AMD64_REL32 __imp__errno
+//
+// In the assembly above, the code loads up the value of __imp_errno
+// and then does an indirect call to that value.
+//
+// Here is what a direct reference might look like:
+//
+// 137: e9 20 06 00 00 jmp 0x75c <pow+0x75c>
+// 13c: e8 00 00 00 00 callq 0x141 <pow+0x141>
+// 000000000000013d: IMAGE_REL_AMD64_REL32 _errno
+//
+// The assembly below dispenses with the import symbol and just makes
+// a direct call to _errno.
+//
+// The code below handles indirect refs by redirecting the target of
+// the relocation from "__imp_XYZ" to "XYZ" (since the latter symbol
+// is what the Windows loader is expected to resolve). For direct refs
+// the call is redirected to a stub, where the stub first loads the
+// symbol and then direct an indirect call to that value.
+//
+// Note that for a given symbol (as above) it is perfectly legal to
+// have both direct and indirect references.
+func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) error {
+ var su *loader.SymbolBuilder
+ relocs := ctxt.loader.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.IsMarker() {
+ continue // skip marker relocations
+ }
+ targ := r.Sym()
+ if targ == 0 {
+ continue
+ }
+ if !ctxt.loader.AttrReachable(targ) {
+ if r.Weak() {
+ continue
+ }
+ return fmt.Errorf("dynamic relocation to unreachable symbol %s",
+ ctxt.loader.SymName(targ))
+ }
+ tgot := ctxt.loader.SymGot(targ)
+ if tgot == loadpe.RedirectToDynImportGotToken {
+
+ // Consistency check: name should be __imp_X
+ sname := ctxt.loader.SymName(targ)
+ if !strings.HasPrefix(sname, "__imp_") {
+ return fmt.Errorf("internal error in windynrelocsym: redirect GOT token applied to non-import symbol %s", sname)
+ }
+
+ // Locate underlying symbol (which originally had type
+ // SDYNIMPORT but has since been retyped to SWINDOWS).
+ ds, err := loadpe.LookupBaseFromImport(targ, ctxt.loader, ctxt.Arch)
+ if err != nil {
+ return err
+ }
+ dstyp := ctxt.loader.SymType(ds)
+ if dstyp != sym.SWINDOWS {
+ return fmt.Errorf("internal error in windynrelocsym: underlying sym for %q has wrong type %s", sname, dstyp.String())
+ }
+
+ // Redirect relocation to the dynimport.
+ r.SetSym(ds)
+ continue
+ }
+
+ tplt := ctxt.loader.SymPlt(targ)
+ if tplt == loadpe.CreateImportStubPltToken {
+
+ // Consistency check: don't want to see both PLT and GOT tokens.
+ if tgot != -1 {
+ return fmt.Errorf("internal error in windynrelocsym: invalid GOT setting %d for reloc to %s", tgot, ctxt.loader.SymName(targ))
+ }
+
+ // make dynimport JMP table for PE object files.
+ tplt := int32(rel.Size())
+ ctxt.loader.SetPlt(targ, tplt)
+
+ if su == nil {
+ su = ctxt.loader.MakeSymbolUpdater(s)
+ }
+ r.SetSym(rel.Sym())
+ r.SetAdd(int64(tplt))
+
+ // jmp *addr
+ switch ctxt.Arch.Family {
+ default:
+ return fmt.Errorf("internal error in windynrelocsym: unsupported arch %v", ctxt.Arch.Family)
+ case sys.I386:
+ rel.AddUint8(0xff)
+ rel.AddUint8(0x25)
+ rel.AddAddrPlus(ctxt.Arch, targ, 0)
+ rel.AddUint8(0x90)
+ rel.AddUint8(0x90)
+ case sys.AMD64:
+ rel.AddUint8(0xff)
+ rel.AddUint8(0x24)
+ rel.AddUint8(0x25)
+ rel.AddAddrPlus4(ctxt.Arch, targ, 0)
+ rel.AddUint8(0x90)
+ }
+ } else if tplt >= 0 {
+ if su == nil {
+ su = ctxt.loader.MakeSymbolUpdater(s)
+ }
+ r.SetSym(rel.Sym())
+ r.SetAdd(int64(tplt))
+ }
+ }
+ return nil
+}
+
+// windynrelocsyms generates jump table to C library functions that will be
+// added later. windynrelocsyms writes the table into .rel symbol.
+func (ctxt *Link) windynrelocsyms() {
+ if !(ctxt.IsWindows() && iscgo && ctxt.IsInternal()) {
+ return
+ }
+
+ rel := ctxt.loader.CreateSymForUpdate(".rel", 0)
+ rel.SetType(sym.STEXT)
+
+ for _, s := range ctxt.Textp {
+ if err := windynrelocsym(ctxt, rel, s); err != nil {
+ ctxt.Errorf(s, "%v", err)
+ }
+ }
+
+ ctxt.Textp = append(ctxt.Textp, rel.Sym())
+}
+
+func dynrelocsym(ctxt *Link, s loader.Sym) {
+ target := &ctxt.Target
+ ldr := ctxt.loader
+ syms := &ctxt.ArchSyms
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.IsMarker() {
+ continue // skip marker relocations
+ }
+ rSym := r.Sym()
+ if r.Weak() && !ldr.AttrReachable(rSym) {
+ continue
+ }
+ if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal {
+ // It's expected that some relocations will be done
+ // later by relocsym (R_TLS_LE, R_ADDROFF), so
+ // don't worry if Adddynrel returns false.
+ thearch.Adddynrel(target, ldr, syms, s, r, ri)
+ continue
+ }
+
+ if rSym != 0 && ldr.SymType(rSym) == sym.SDYNIMPORT || r.Type() >= objabi.ElfRelocOffset {
+ if rSym != 0 && !ldr.AttrReachable(rSym) {
+ ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s", ldr.SymName(rSym))
+ }
+ if !thearch.Adddynrel(target, ldr, syms, s, r, ri) {
+ ctxt.Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", ldr.SymName(rSym), r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymType(rSym), ldr.SymType(rSym))
+ }
+ }
+ }
+}
+
+func (state *dodataState) dynreloc(ctxt *Link) {
+ if ctxt.HeadType == objabi.Hwindows {
+ return
+ }
+ // -d suppresses dynamic loader format, so we may as well not
+ // compute these sections or mark their symbols as reachable.
+ if *FlagD {
+ return
+ }
+
+ for _, s := range ctxt.Textp {
+ dynrelocsym(ctxt, s)
+ }
+ for _, syms := range state.data {
+ for _, s := range syms {
+ dynrelocsym(ctxt, s)
+ }
+ }
+ if ctxt.IsELF {
+ elfdynhash(ctxt)
+ }
+}
+
+func CodeblkPad(ctxt *Link, out *OutBuf, addr int64, size int64, pad []byte) {
+ writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, ctxt.Textp, addr, size, pad)
+}
+
+const blockSize = 1 << 20 // 1MB chunks written at a time.
+
+// writeBlocks writes a specified chunk of symbols to the output buffer. It
+// breaks the write up into ≥blockSize chunks to write them out, and schedules
+// as many goroutines as necessary to accomplish this task. This call then
+// blocks, waiting on the writes to complete. Note that we use the sem parameter
+// to limit the number of concurrent writes taking place.
+func writeBlocks(ctxt *Link, out *OutBuf, sem chan int, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) {
+ for i, s := range syms {
+ if ldr.SymValue(s) >= addr && !ldr.AttrSubSymbol(s) {
+ syms = syms[i:]
+ break
+ }
+ }
+
+ var wg sync.WaitGroup
+ max, lastAddr, written := int64(blockSize), addr+size, int64(0)
+ for addr < lastAddr {
+ // Find the last symbol we'd write.
+ idx := -1
+ for i, s := range syms {
+ if ldr.AttrSubSymbol(s) {
+ continue
+ }
+
+ // If the next symbol's size would put us out of bounds on the total length,
+ // stop looking.
+ end := ldr.SymValue(s) + ldr.SymSize(s)
+ if end > lastAddr {
+ break
+ }
+
+ // We're gonna write this symbol.
+ idx = i
+
+ // If we cross over the max size, we've got enough symbols.
+ if end > addr+max {
+ break
+ }
+ }
+
+ // If we didn't find any symbols to write, we're done here.
+ if idx < 0 {
+ break
+ }
+
+ // Compute the length to write, including padding.
+ // We need to write to the end address (lastAddr), or the next symbol's
+ // start address, whichever comes first. If there is no more symbols,
+ // just write to lastAddr. This ensures we don't leave holes between the
+ // blocks or at the end.
+ length := int64(0)
+ if idx+1 < len(syms) {
+ // Find the next top-level symbol.
+ // Skip over sub symbols so we won't split a container symbol
+ // into two blocks.
+ next := syms[idx+1]
+ for ldr.AttrSubSymbol(next) {
+ idx++
+ next = syms[idx+1]
+ }
+ length = ldr.SymValue(next) - addr
+ }
+ if length == 0 || length > lastAddr-addr {
+ length = lastAddr - addr
+ }
+
+ // Start the block output operator.
+ if o, err := out.View(uint64(out.Offset() + written)); err == nil {
+ sem <- 1
+ wg.Add(1)
+ go func(o *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) {
+ writeBlock(ctxt, o, ldr, syms, addr, size, pad)
+ wg.Done()
+ <-sem
+ }(o, ldr, syms, addr, length, pad)
+ } else { // output not mmaped, don't parallelize.
+ writeBlock(ctxt, out, ldr, syms, addr, length, pad)
+ }
+
+ // Prepare for the next loop.
+ if idx != -1 {
+ syms = syms[idx+1:]
+ }
+ written += length
+ addr += length
+ }
+ wg.Wait()
+}
+
+func writeBlock(ctxt *Link, out *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) {
+
+ st := ctxt.makeRelocSymState()
+
+ // This doesn't distinguish the memory size from the file
+ // size, and it lays out the file based on Symbol.Value, which
+ // is the virtual address. DWARF compression changes file sizes,
+ // so dwarfcompress will fix this up later if necessary.
+ eaddr := addr + size
+ for _, s := range syms {
+ if ldr.AttrSubSymbol(s) {
+ continue
+ }
+ val := ldr.SymValue(s)
+ if val >= eaddr {
+ break
+ }
+ if val < addr {
+ ldr.Errorf(s, "phase error: addr=%#x but sym=%#x type=%v sect=%v", addr, val, ldr.SymType(s), ldr.SymSect(s).Name)
+ errorexit()
+ }
+ if addr < val {
+ out.WriteStringPad("", int(val-addr), pad)
+ addr = val
+ }
+ P := out.WriteSym(ldr, s)
+ st.relocsym(s, P)
+ if f, ok := ctxt.generatorSyms[s]; ok {
+ f(ctxt, s)
+ }
+ addr += int64(len(P))
+ siz := ldr.SymSize(s)
+ if addr < val+siz {
+ out.WriteStringPad("", int(val+siz-addr), pad)
+ addr = val + siz
+ }
+ if addr != val+siz {
+ ldr.Errorf(s, "phase error: addr=%#x value+size=%#x", addr, val+siz)
+ errorexit()
+ }
+ if val+siz >= eaddr {
+ break
+ }
+ }
+
+ if addr < eaddr {
+ out.WriteStringPad("", int(eaddr-addr), pad)
+ }
+}
+
+type writeFn func(*Link, *OutBuf, int64, int64)
+
+// writeParallel handles scheduling parallel execution of data write functions.
+func writeParallel(wg *sync.WaitGroup, fn writeFn, ctxt *Link, seek, vaddr, length uint64) {
+ if out, err := ctxt.Out.View(seek); err != nil {
+ ctxt.Out.SeekSet(int64(seek))
+ fn(ctxt, ctxt.Out, int64(vaddr), int64(length))
+ } else {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ fn(ctxt, out, int64(vaddr), int64(length))
+ }()
+ }
+}
+
+func datblk(ctxt *Link, out *OutBuf, addr, size int64) {
+ writeDatblkToOutBuf(ctxt, out, addr, size)
+}
+
+// Used only on Wasm for now.
+func DatblkBytes(ctxt *Link, addr int64, size int64) []byte {
+ buf := make([]byte, size)
+ out := &OutBuf{heap: buf}
+ writeDatblkToOutBuf(ctxt, out, addr, size)
+ return buf
+}
+
+func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) {
+ writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, ctxt.datap, addr, size, zeros[:])
+}
+
+func dwarfblk(ctxt *Link, out *OutBuf, addr int64, size int64) {
+ // Concatenate the section symbol lists into a single list to pass
+ // to writeBlocks.
+ //
+ // NB: ideally we would do a separate writeBlocks call for each
+ // section, but this would run the risk of undoing any file offset
+ // adjustments made during layout.
+ n := 0
+ for i := range dwarfp {
+ n += len(dwarfp[i].syms)
+ }
+ syms := make([]loader.Sym, 0, n)
+ for i := range dwarfp {
+ syms = append(syms, dwarfp[i].syms...)
+ }
+ writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, syms, addr, size, zeros[:])
+}
+
+var covCounterDataStartOff, covCounterDataLen uint64
+
+var zeros [512]byte
+
+var (
+ strdata = make(map[string]string)
+ strnames []string
+)
+
+func addstrdata1(ctxt *Link, arg string) {
+ eq := strings.Index(arg, "=")
+ dot := strings.LastIndex(arg[:eq+1], ".")
+ if eq < 0 || dot < 0 {
+ Exitf("-X flag requires argument of the form importpath.name=value")
+ }
+ pkg := arg[:dot]
+ if ctxt.BuildMode == BuildModePlugin && pkg == "main" {
+ pkg = *flagPluginPath
+ }
+ pkg = objabi.PathToPrefix(pkg)
+ name := pkg + arg[dot:eq]
+ value := arg[eq+1:]
+ if _, ok := strdata[name]; !ok {
+ strnames = append(strnames, name)
+ }
+ strdata[name] = value
+}
+
+// addstrdata sets the initial value of the string variable name to value.
+func addstrdata(arch *sys.Arch, l *loader.Loader, name, value string) {
+ s := l.Lookup(name, 0)
+ if s == 0 {
+ return
+ }
+ if goType := l.SymGoType(s); goType == 0 {
+ return
+ } else if typeName := l.SymName(goType); typeName != "type:string" {
+ Errorf(nil, "%s: cannot set with -X: not a var of type string (%s)", name, typeName)
+ return
+ }
+ if !l.AttrReachable(s) {
+ return // don't bother setting unreachable variable
+ }
+ bld := l.MakeSymbolUpdater(s)
+ if bld.Type() == sym.SBSS {
+ bld.SetType(sym.SDATA)
+ }
+
+ p := fmt.Sprintf("%s.str", name)
+ sbld := l.CreateSymForUpdate(p, 0)
+ sbld.Addstring(value)
+ sbld.SetType(sym.SRODATA)
+
+ // Don't reset the variable's size. String variable usually has size of
+ // 2*PtrSize, but in ASAN build it can be larger due to red zone.
+ // (See issue 56175.)
+ bld.SetData(make([]byte, arch.PtrSize*2))
+ bld.SetReadOnly(false)
+ bld.ResetRelocs()
+ bld.SetAddrPlus(arch, 0, sbld.Sym(), 0)
+ bld.SetUint(arch, int64(arch.PtrSize), uint64(len(value)))
+}
+
+func (ctxt *Link) dostrdata() {
+ for _, name := range strnames {
+ addstrdata(ctxt.Arch, ctxt.loader, name, strdata[name])
+ }
+}
+
+// addgostring adds str, as a Go string value, to s. symname is the name of the
+// symbol used to define the string data and must be unique per linked object.
+func addgostring(ctxt *Link, ldr *loader.Loader, s *loader.SymbolBuilder, symname, str string) {
+ sdata := ldr.CreateSymForUpdate(symname, 0)
+ if sdata.Type() != sym.Sxxx {
+ ctxt.Errorf(s.Sym(), "duplicate symname in addgostring: %s", symname)
+ }
+ sdata.SetLocal(true)
+ sdata.SetType(sym.SRODATA)
+ sdata.SetSize(int64(len(str)))
+ sdata.SetData([]byte(str))
+ s.AddAddr(ctxt.Arch, sdata.Sym())
+ s.AddUint(ctxt.Arch, uint64(len(str)))
+}
+
+func addinitarrdata(ctxt *Link, ldr *loader.Loader, s loader.Sym) {
+ p := ldr.SymName(s) + ".ptr"
+ sp := ldr.CreateSymForUpdate(p, 0)
+ sp.SetType(sym.SINITARR)
+ sp.SetSize(0)
+ sp.SetDuplicateOK(true)
+ sp.AddAddr(ctxt.Arch, s)
+}
+
+// symalign returns the required alignment for the given symbol s.
+func symalign(ldr *loader.Loader, s loader.Sym) int32 {
+ min := int32(thearch.Minalign)
+ align := ldr.SymAlign(s)
+ if align >= min {
+ return align
+ } else if align != 0 {
+ return min
+ }
+ align = int32(thearch.Maxalign)
+ ssz := ldr.SymSize(s)
+ for int64(align) > ssz && align > min {
+ align >>= 1
+ }
+ ldr.SetSymAlign(s, align)
+ return align
+}
+
+func aligndatsize(state *dodataState, datsize int64, s loader.Sym) int64 {
+ return Rnd(datsize, int64(symalign(state.ctxt.loader, s)))
+}
+
+const debugGCProg = false
+
+type GCProg struct {
+ ctxt *Link
+ sym *loader.SymbolBuilder
+ w gcprog.Writer
+}
+
+func (p *GCProg) Init(ctxt *Link, name string) {
+ p.ctxt = ctxt
+ p.sym = ctxt.loader.CreateSymForUpdate(name, 0)
+ p.w.Init(p.writeByte())
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name)
+ p.w.Debug(os.Stderr)
+ }
+}
+
+func (p *GCProg) writeByte() func(x byte) {
+ return func(x byte) {
+ p.sym.AddUint8(x)
+ }
+}
+
+func (p *GCProg) End(size int64) {
+ p.w.ZeroUntil(size / int64(p.ctxt.Arch.PtrSize))
+ p.w.End()
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "ld: end GCProg\n")
+ }
+}
+
+func (p *GCProg) AddSym(s loader.Sym) {
+ ldr := p.ctxt.loader
+ typ := ldr.SymGoType(s)
+
+ // Things without pointers should be in sym.SNOPTRDATA or sym.SNOPTRBSS;
+ // everything we see should have pointers and should therefore have a type.
+ if typ == 0 {
+ switch ldr.SymName(s) {
+ case "runtime.data", "runtime.edata", "runtime.bss", "runtime.ebss":
+ // Ignore special symbols that are sometimes laid out
+ // as real symbols. See comment about dyld on darwin in
+ // the address function.
+ return
+ }
+ p.ctxt.Errorf(p.sym.Sym(), "missing Go type information for global symbol %s: size %d", ldr.SymName(s), ldr.SymSize(s))
+ return
+ }
+
+ ptrsize := int64(p.ctxt.Arch.PtrSize)
+ typData := ldr.Data(typ)
+ nptr := decodetypePtrdata(p.ctxt.Arch, typData) / ptrsize
+
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", ldr.SymName(s), ldr.SymValue(s), ldr.SymValue(s)/ptrsize, nptr)
+ }
+
+ sval := ldr.SymValue(s)
+ if decodetypeUsegcprog(p.ctxt.Arch, typData) == 0 {
+ // Copy pointers from mask into program.
+ mask := decodetypeGcmask(p.ctxt, typ)
+ for i := int64(0); i < nptr; i++ {
+ if (mask[i/8]>>uint(i%8))&1 != 0 {
+ p.w.Ptr(sval/ptrsize + i)
+ }
+ }
+ return
+ }
+
+ // Copy program.
+ prog := decodetypeGcprog(p.ctxt, typ)
+ p.w.ZeroUntil(sval / ptrsize)
+ p.w.Append(prog[4:], nptr)
+}
+
+// cutoff is the maximum data section size permitted by the linker
+// (see issue #9862).
+const cutoff = 2e9 // 2 GB (or so; looks better in errors than 2^31)
+
+func (state *dodataState) checkdatsize(symn sym.SymKind) {
+ if state.datsize > cutoff {
+ Errorf(nil, "too much data in section %v (over %v bytes)", symn, cutoff)
+ }
+}
+
+// fixZeroSizedSymbols gives a few special symbols with zero size some space.
+func fixZeroSizedSymbols(ctxt *Link) {
+ // The values in moduledata are filled out by relocations
+ // pointing to the addresses of these special symbols.
+ // Typically these symbols have no size and are not laid
+ // out with their matching section.
+ //
+ // However on darwin, dyld will find the special symbol
+ // in the first loaded module, even though it is local.
+ //
+ // (An hypothesis, formed without looking in the dyld sources:
+ // these special symbols have no size, so their address
+ // matches a real symbol. The dynamic linker assumes we
+ // want the normal symbol with the same address and finds
+ // it in the other module.)
+ //
+ // To work around this we lay out the symbls whose
+ // addresses are vital for multi-module programs to work
+ // as normal symbols, and give them a little size.
+ //
+ // On AIX, as all DATA sections are merged together, ld might not put
+ // these symbols at the beginning of their respective section if there
+ // aren't real symbols, their alignment might not match the
+ // first symbol alignment. Therefore, there are explicitly put at the
+ // beginning of their section with the same alignment.
+ if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+ return
+ }
+
+ ldr := ctxt.loader
+ bss := ldr.CreateSymForUpdate("runtime.bss", 0)
+ bss.SetSize(8)
+ ldr.SetAttrSpecial(bss.Sym(), false)
+
+ ebss := ldr.CreateSymForUpdate("runtime.ebss", 0)
+ ldr.SetAttrSpecial(ebss.Sym(), false)
+
+ data := ldr.CreateSymForUpdate("runtime.data", 0)
+ data.SetSize(8)
+ ldr.SetAttrSpecial(data.Sym(), false)
+
+ edata := ldr.CreateSymForUpdate("runtime.edata", 0)
+ ldr.SetAttrSpecial(edata.Sym(), false)
+
+ if ctxt.HeadType == objabi.Haix {
+ // XCOFFTOC symbols are part of .data section.
+ edata.SetType(sym.SXCOFFTOC)
+ }
+
+ types := ldr.CreateSymForUpdate("runtime.types", 0)
+ types.SetType(sym.STYPE)
+ types.SetSize(8)
+ ldr.SetAttrSpecial(types.Sym(), false)
+
+ etypes := ldr.CreateSymForUpdate("runtime.etypes", 0)
+ etypes.SetType(sym.SFUNCTAB)
+ ldr.SetAttrSpecial(etypes.Sym(), false)
+
+ if ctxt.HeadType == objabi.Haix {
+ rodata := ldr.CreateSymForUpdate("runtime.rodata", 0)
+ rodata.SetType(sym.SSTRING)
+ rodata.SetSize(8)
+ ldr.SetAttrSpecial(rodata.Sym(), false)
+
+ erodata := ldr.CreateSymForUpdate("runtime.erodata", 0)
+ ldr.SetAttrSpecial(erodata.Sym(), false)
+ }
+}
+
+// makeRelroForSharedLib creates a section of readonly data if necessary.
+func (state *dodataState) makeRelroForSharedLib(target *Link) {
+ if !target.UseRelro() {
+ return
+ }
+
+ // "read only" data with relocations needs to go in its own section
+ // when building a shared library. We do this by boosting objects of
+ // type SXXX with relocations to type SXXXRELRO.
+ ldr := target.loader
+ for _, symnro := range sym.ReadOnly {
+ symnrelro := sym.RelROMap[symnro]
+
+ ro := []loader.Sym{}
+ relro := state.data[symnrelro]
+
+ for _, s := range state.data[symnro] {
+ relocs := ldr.Relocs(s)
+ isRelro := relocs.Count() > 0
+ switch state.symType(s) {
+ case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO:
+ // Symbols are not sorted yet, so it is possible
+ // that an Outer symbol has been changed to a
+ // relro Type before it reaches here.
+ isRelro = true
+ case sym.SFUNCTAB:
+ if ldr.SymName(s) == "runtime.etypes" {
+ // runtime.etypes must be at the end of
+ // the relro data.
+ isRelro = true
+ }
+ case sym.SGOFUNC:
+ // The only SGOFUNC symbols that contain relocations are .stkobj,
+ // and their relocations are of type objabi.R_ADDROFF,
+ // which always get resolved during linking.
+ isRelro = false
+ }
+ if isRelro {
+ state.setSymType(s, symnrelro)
+ if outer := ldr.OuterSym(s); outer != 0 {
+ state.setSymType(outer, symnrelro)
+ }
+ relro = append(relro, s)
+ } else {
+ ro = append(ro, s)
+ }
+ }
+
+ // Check that we haven't made two symbols with the same .Outer into
+ // different types (because references two symbols with non-nil Outer
+ // become references to the outer symbol + offset it's vital that the
+ // symbol and the outer end up in the same section).
+ for _, s := range relro {
+ if outer := ldr.OuterSym(s); outer != 0 {
+ st := state.symType(s)
+ ost := state.symType(outer)
+ if st != ost {
+ state.ctxt.Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)",
+ ldr.SymName(outer), st, ost)
+ }
+ }
+ }
+
+ state.data[symnro] = ro
+ state.data[symnrelro] = relro
+ }
+}
+
+// dodataState holds bits of state information needed by dodata() and the
+// various helpers it calls. The lifetime of these items should not extend
+// past the end of dodata().
+type dodataState struct {
+ // Link context
+ ctxt *Link
+ // Data symbols bucketed by type.
+ data [sym.SXREF][]loader.Sym
+ // Max alignment for each flavor of data symbol.
+ dataMaxAlign [sym.SXREF]int32
+ // Overridden sym type
+ symGroupType []sym.SymKind
+ // Current data size so far.
+ datsize int64
+}
+
+// A note on symType/setSymType below:
+//
+// In the legacy linker, the types of symbols (notably data symbols) are
+// changed during the symtab() phase so as to insure that similar symbols
+// are bucketed together, then their types are changed back again during
+// dodata. Symbol to section assignment also plays tricks along these lines
+// in the case where a relro segment is needed.
+//
+// The value returned from setType() below reflects the effects of
+// any overrides made by symtab and/or dodata.
+
+// symType returns the (possibly overridden) type of 's'.
+func (state *dodataState) symType(s loader.Sym) sym.SymKind {
+ if int(s) < len(state.symGroupType) {
+ if override := state.symGroupType[s]; override != 0 {
+ return override
+ }
+ }
+ return state.ctxt.loader.SymType(s)
+}
+
+// setSymType sets a new override type for 's'.
+func (state *dodataState) setSymType(s loader.Sym, kind sym.SymKind) {
+ if s == 0 {
+ panic("bad")
+ }
+ if int(s) < len(state.symGroupType) {
+ state.symGroupType[s] = kind
+ } else {
+ su := state.ctxt.loader.MakeSymbolUpdater(s)
+ su.SetType(kind)
+ }
+}
+
+func (ctxt *Link) dodata(symGroupType []sym.SymKind) {
+
+ // Give zeros sized symbols space if necessary.
+ fixZeroSizedSymbols(ctxt)
+
+ // Collect data symbols by type into data.
+ state := dodataState{ctxt: ctxt, symGroupType: symGroupType}
+ ldr := ctxt.loader
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || ldr.AttrSubSymbol(s) ||
+ !ldr.TopLevelSym(s) {
+ continue
+ }
+
+ st := state.symType(s)
+
+ if st <= sym.STEXT || st >= sym.SXREF {
+ continue
+ }
+ state.data[st] = append(state.data[st], s)
+
+ // Similarly with checking the onlist attr.
+ if ldr.AttrOnList(s) {
+ log.Fatalf("symbol %s listed multiple times", ldr.SymName(s))
+ }
+ ldr.SetAttrOnList(s, true)
+ }
+
+ // Now that we have the data symbols, but before we start
+ // to assign addresses, record all the necessary
+ // dynamic relocations. These will grow the relocation
+ // symbol, which is itself data.
+ //
+ // On darwin, we need the symbol table numbers for dynreloc.
+ if ctxt.HeadType == objabi.Hdarwin {
+ machosymorder(ctxt)
+ }
+ state.dynreloc(ctxt)
+
+ // Move any RO data with relocations to a separate section.
+ state.makeRelroForSharedLib(ctxt)
+
+ // Set alignment for the symbol with the largest known index,
+ // so as to trigger allocation of the loader's internal
+ // alignment array. This will avoid data races in the parallel
+ // section below.
+ lastSym := loader.Sym(ldr.NSym() - 1)
+ ldr.SetSymAlign(lastSym, ldr.SymAlign(lastSym))
+
+ // Sort symbols.
+ var wg sync.WaitGroup
+ for symn := range state.data {
+ symn := sym.SymKind(symn)
+ wg.Add(1)
+ go func() {
+ state.data[symn], state.dataMaxAlign[symn] = state.dodataSect(ctxt, symn, state.data[symn])
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+
+ if ctxt.IsELF {
+ // Make .rela and .rela.plt contiguous, the ELF ABI requires this
+ // and Solaris actually cares.
+ syms := state.data[sym.SELFROSECT]
+ reli, plti := -1, -1
+ for i, s := range syms {
+ switch ldr.SymName(s) {
+ case ".rel.plt", ".rela.plt":
+ plti = i
+ case ".rel", ".rela":
+ reli = i
+ }
+ }
+ if reli >= 0 && plti >= 0 && plti != reli+1 {
+ var first, second int
+ if plti > reli {
+ first, second = reli, plti
+ } else {
+ first, second = plti, reli
+ }
+ rel, plt := syms[reli], syms[plti]
+ copy(syms[first+2:], syms[first+1:second])
+ syms[first+0] = rel
+ syms[first+1] = plt
+
+ // Make sure alignment doesn't introduce a gap.
+ // Setting the alignment explicitly prevents
+ // symalign from basing it on the size and
+ // getting it wrong.
+ ldr.SetSymAlign(rel, int32(ctxt.Arch.RegSize))
+ ldr.SetSymAlign(plt, int32(ctxt.Arch.RegSize))
+ }
+ state.data[sym.SELFROSECT] = syms
+ }
+
+ if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal {
+ // These symbols must have the same alignment as their section.
+ // Otherwise, ld might change the layout of Go sections.
+ ldr.SetSymAlign(ldr.Lookup("runtime.data", 0), state.dataMaxAlign[sym.SDATA])
+ ldr.SetSymAlign(ldr.Lookup("runtime.bss", 0), state.dataMaxAlign[sym.SBSS])
+ }
+
+ // Create *sym.Section objects and assign symbols to sections for
+ // data/rodata (and related) symbols.
+ state.allocateDataSections(ctxt)
+
+ // Create *sym.Section objects and assign symbols to sections for
+ // DWARF symbols.
+ state.allocateDwarfSections(ctxt)
+
+ /* number the sections */
+ n := int16(1)
+
+ for _, sect := range Segtext.Sections {
+ sect.Extnum = n
+ n++
+ }
+ for _, sect := range Segrodata.Sections {
+ sect.Extnum = n
+ n++
+ }
+ for _, sect := range Segrelrodata.Sections {
+ sect.Extnum = n
+ n++
+ }
+ for _, sect := range Segdata.Sections {
+ sect.Extnum = n
+ n++
+ }
+ for _, sect := range Segdwarf.Sections {
+ sect.Extnum = n
+ n++
+ }
+}
+
+// allocateDataSectionForSym creates a new sym.Section into which a a
+// single symbol will be placed. Here "seg" is the segment into which
+// the section will go, "s" is the symbol to be placed into the new
+// section, and "rwx" contains permissions for the section.
+func (state *dodataState) allocateDataSectionForSym(seg *sym.Segment, s loader.Sym, rwx int) *sym.Section {
+ ldr := state.ctxt.loader
+ sname := ldr.SymName(s)
+ if strings.HasPrefix(sname, "go:") {
+ sname = ".go." + sname[len("go:"):]
+ }
+ sect := addsection(ldr, state.ctxt.Arch, seg, sname, rwx)
+ sect.Align = symalign(ldr, s)
+ state.datsize = Rnd(state.datsize, int64(sect.Align))
+ sect.Vaddr = uint64(state.datsize)
+ return sect
+}
+
+// allocateNamedDataSection creates a new sym.Section for a category
+// of data symbols. Here "seg" is the segment into which the section
+// will go, "sName" is the name to give to the section, "types" is a
+// range of symbol types to be put into the section, and "rwx"
+// contains permissions for the section.
+func (state *dodataState) allocateNamedDataSection(seg *sym.Segment, sName string, types []sym.SymKind, rwx int) *sym.Section {
+ sect := addsection(state.ctxt.loader, state.ctxt.Arch, seg, sName, rwx)
+ if len(types) == 0 {
+ sect.Align = 1
+ } else if len(types) == 1 {
+ sect.Align = state.dataMaxAlign[types[0]]
+ } else {
+ for _, symn := range types {
+ align := state.dataMaxAlign[symn]
+ if sect.Align < align {
+ sect.Align = align
+ }
+ }
+ }
+ state.datsize = Rnd(state.datsize, int64(sect.Align))
+ sect.Vaddr = uint64(state.datsize)
+ return sect
+}
+
+// assignDsymsToSection assigns a collection of data symbols to a
+// newly created section. "sect" is the section into which to place
+// the symbols, "syms" holds the list of symbols to assign,
+// "forceType" (if non-zero) contains a new sym type to apply to each
+// sym during the assignment, and "aligner" is a hook to call to
+// handle alignment during the assignment process.
+func (state *dodataState) assignDsymsToSection(sect *sym.Section, syms []loader.Sym, forceType sym.SymKind, aligner func(state *dodataState, datsize int64, s loader.Sym) int64) {
+ ldr := state.ctxt.loader
+ for _, s := range syms {
+ state.datsize = aligner(state, state.datsize, s)
+ ldr.SetSymSect(s, sect)
+ if forceType != sym.Sxxx {
+ state.setSymType(s, forceType)
+ }
+ ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr))
+ state.datsize += ldr.SymSize(s)
+ }
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+}
+
+func (state *dodataState) assignToSection(sect *sym.Section, symn sym.SymKind, forceType sym.SymKind) {
+ state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize)
+ state.checkdatsize(symn)
+}
+
+// allocateSingleSymSections walks through the bucketed data symbols
+// with type 'symn', creates a new section for each sym, and assigns
+// the sym to a newly created section. Section name is set from the
+// symbol name. "Seg" is the segment into which to place the new
+// section, "forceType" is the new sym.SymKind to assign to the symbol
+// within the section, and "rwx" holds section permissions.
+func (state *dodataState) allocateSingleSymSections(seg *sym.Segment, symn sym.SymKind, forceType sym.SymKind, rwx int) {
+ ldr := state.ctxt.loader
+ for _, s := range state.data[symn] {
+ sect := state.allocateDataSectionForSym(seg, s, rwx)
+ ldr.SetSymSect(s, sect)
+ state.setSymType(s, forceType)
+ ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr))
+ state.datsize += ldr.SymSize(s)
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+ }
+ state.checkdatsize(symn)
+}
+
+// allocateNamedSectionAndAssignSyms creates a new section with the
+// specified name, then walks through the bucketed data symbols with
+// type 'symn' and assigns each of them to this new section. "Seg" is
+// the segment into which to place the new section, "secName" is the
+// name to give to the new section, "forceType" (if non-zero) contains
+// a new sym type to apply to each sym during the assignment, and
+// "rwx" holds section permissions.
+func (state *dodataState) allocateNamedSectionAndAssignSyms(seg *sym.Segment, secName string, symn sym.SymKind, forceType sym.SymKind, rwx int) *sym.Section {
+
+ sect := state.allocateNamedDataSection(seg, secName, []sym.SymKind{symn}, rwx)
+ state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize)
+ return sect
+}
+
+// allocateDataSections allocates sym.Section objects for data/rodata
+// (and related) symbols, and then assigns symbols to those sections.
+func (state *dodataState) allocateDataSections(ctxt *Link) {
+ // Allocate sections.
+ // Data is processed before segtext, because we need
+ // to see all symbols in the .data and .bss sections in order
+ // to generate garbage collection information.
+
+ // Writable data sections that do not need any specialized handling.
+ writable := []sym.SymKind{
+ sym.SBUILDINFO,
+ sym.SELFSECT,
+ sym.SMACHO,
+ sym.SMACHOGOT,
+ sym.SWINDOWS,
+ }
+ for _, symn := range writable {
+ state.allocateSingleSymSections(&Segdata, symn, sym.SDATA, 06)
+ }
+ ldr := ctxt.loader
+
+ // .got
+ if len(state.data[sym.SELFGOT]) > 0 {
+ state.allocateNamedSectionAndAssignSyms(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06)
+ }
+
+ /* pointer-free data */
+ sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrdata", sym.SNOPTRDATA, sym.SDATA, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrdata", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrdata", 0), sect)
+
+ hasinitarr := ctxt.linkShared
+
+ /* shared library initializer */
+ switch ctxt.BuildMode {
+ case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin:
+ hasinitarr = true
+ }
+
+ if ctxt.HeadType == objabi.Haix {
+ if len(state.data[sym.SINITARR]) > 0 {
+ Errorf(nil, "XCOFF format doesn't allow .init_array section")
+ }
+ }
+
+ if hasinitarr && len(state.data[sym.SINITARR]) > 0 {
+ state.allocateNamedSectionAndAssignSyms(&Segdata, ".init_array", sym.SINITARR, sym.Sxxx, 06)
+ }
+
+ /* data */
+ sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".data", sym.SDATA, sym.SDATA, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.data", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.edata", 0), sect)
+ dataGcEnd := state.datsize - int64(sect.Vaddr)
+
+ // On AIX, TOC entries must be the last of .data
+ // These aren't part of gc as they won't change during the runtime.
+ state.assignToSection(sect, sym.SXCOFFTOC, sym.SDATA)
+ state.checkdatsize(sym.SDATA)
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+
+ /* bss */
+ sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".bss", sym.SBSS, sym.Sxxx, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.bss", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ebss", 0), sect)
+ bssGcEnd := state.datsize - int64(sect.Vaddr)
+
+ // Emit gcdata for bss symbols now that symbol values have been assigned.
+ gcsToEmit := []struct {
+ symName string
+ symKind sym.SymKind
+ gcEnd int64
+ }{
+ {"runtime.gcdata", sym.SDATA, dataGcEnd},
+ {"runtime.gcbss", sym.SBSS, bssGcEnd},
+ }
+ for _, g := range gcsToEmit {
+ var gc GCProg
+ gc.Init(ctxt, g.symName)
+ for _, s := range state.data[g.symKind] {
+ gc.AddSym(s)
+ }
+ gc.End(g.gcEnd)
+ }
+
+ /* pointer-free bss */
+ sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrbss", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrbss", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.end", 0), sect)
+
+ // Code coverage counters are assigned to the .noptrbss section.
+ // We assign them in a separate pass so that they stay aggregated
+ // together in a single blob (coverage runtime depends on this).
+ covCounterDataStartOff = sect.Length
+ state.assignToSection(sect, sym.SCOVERAGE_COUNTER, sym.SNOPTRBSS)
+ covCounterDataLen = sect.Length - covCounterDataStartOff
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.covctrs", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ecovctrs", 0), sect)
+
+ // Coverage instrumentation counters for libfuzzer.
+ if len(state.data[sym.SLIBFUZZER_8BIT_COUNTER]) > 0 {
+ sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".go.fuzzcntrs", sym.SLIBFUZZER_8BIT_COUNTER, sym.Sxxx, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.__start___sancov_cntrs", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.__stop___sancov_cntrs", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._counters", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._ecounters", 0), sect)
+ }
+
+ if len(state.data[sym.STLSBSS]) > 0 {
+ var sect *sym.Section
+ // FIXME: not clear why it is sometimes necessary to suppress .tbss section creation.
+ if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) {
+ sect = addsection(ldr, ctxt.Arch, &Segdata, ".tbss", 06)
+ sect.Align = int32(ctxt.Arch.PtrSize)
+ // FIXME: why does this need to be set to zero?
+ sect.Vaddr = 0
+ }
+ state.datsize = 0
+
+ for _, s := range state.data[sym.STLSBSS] {
+ state.datsize = aligndatsize(state, state.datsize, s)
+ if sect != nil {
+ ldr.SetSymSect(s, sect)
+ }
+ ldr.SetSymValue(s, state.datsize)
+ state.datsize += ldr.SymSize(s)
+ }
+ state.checkdatsize(sym.STLSBSS)
+
+ if sect != nil {
+ sect.Length = uint64(state.datsize)
+ }
+ }
+
+ /*
+ * We finished data, begin read-only data.
+ * Not all systems support a separate read-only non-executable data section.
+ * ELF and Windows PE systems do.
+ * OS X and Plan 9 do not.
+ * And if we're using external linking mode, the point is moot,
+ * since it's not our decision; that code expects the sections in
+ * segtext.
+ */
+ var segro *sym.Segment
+ if ctxt.IsELF && ctxt.LinkMode == LinkInternal {
+ segro = &Segrodata
+ } else if ctxt.HeadType == objabi.Hwindows {
+ segro = &Segrodata
+ } else {
+ segro = &Segtext
+ }
+
+ state.datsize = 0
+
+ /* read-only executable ELF, Mach-O sections */
+ if len(state.data[sym.STEXT]) != 0 {
+ culprit := ldr.SymName(state.data[sym.STEXT][0])
+ Errorf(nil, "dodata found an sym.STEXT symbol: %s", culprit)
+ }
+ state.allocateSingleSymSections(&Segtext, sym.SELFRXSECT, sym.SRODATA, 05)
+ state.allocateSingleSymSections(&Segtext, sym.SMACHOPLT, sym.SRODATA, 05)
+
+ /* read-only data */
+ sect = state.allocateNamedDataSection(segro, ".rodata", sym.ReadOnly, 04)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.rodata", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.erodata", 0), sect)
+ if !ctxt.UseRelro() {
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect)
+ }
+ for _, symn := range sym.ReadOnly {
+ symnStartValue := state.datsize
+ if len(state.data[symn]) != 0 {
+ symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0])
+ }
+ state.assignToSection(sect, symn, sym.SRODATA)
+ setCarrierSize(symn, state.datsize-symnStartValue)
+ if ctxt.HeadType == objabi.Haix {
+ // Read-only symbols might be wrapped inside their outer
+ // symbol.
+ // XCOFF symbol table needs to know the size of
+ // these outer symbols.
+ xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn)
+ }
+ }
+
+ /* read-only ELF, Mach-O sections */
+ state.allocateSingleSymSections(segro, sym.SELFROSECT, sym.SRODATA, 04)
+
+ // There is some data that are conceptually read-only but are written to by
+ // relocations. On GNU systems, we can arrange for the dynamic linker to
+ // mprotect sections after relocations are applied by giving them write
+ // permissions in the object file and calling them ".data.rel.ro.FOO". We
+ // divide the .rodata section between actual .rodata and .data.rel.ro.rodata,
+ // but for the other sections that this applies to, we just write a read-only
+ // .FOO section or a read-write .data.rel.ro.FOO section depending on the
+ // situation.
+ // TODO(mwhudson): It would make sense to do this more widely, but it makes
+ // the system linker segfault on darwin.
+ const relroPerm = 06
+ const fallbackPerm = 04
+ relroSecPerm := fallbackPerm
+ genrelrosecname := func(suffix string) string {
+ if suffix == "" {
+ return ".rodata"
+ }
+ return suffix
+ }
+ seg := segro
+
+ if ctxt.UseRelro() {
+ segrelro := &Segrelrodata
+ if ctxt.LinkMode == LinkExternal && !ctxt.IsAIX() && !ctxt.IsDarwin() {
+ // Using a separate segment with an external
+ // linker results in some programs moving
+ // their data sections unexpectedly, which
+ // corrupts the moduledata. So we use the
+ // rodata segment and let the external linker
+ // sort out a rel.ro segment.
+ segrelro = segro
+ } else {
+ // Reset datsize for new segment.
+ state.datsize = 0
+ }
+
+ if !ctxt.IsDarwin() { // We don't need the special names on darwin.
+ genrelrosecname = func(suffix string) string {
+ return ".data.rel.ro" + suffix
+ }
+ }
+
+ relroReadOnly := []sym.SymKind{}
+ for _, symnro := range sym.ReadOnly {
+ symn := sym.RelROMap[symnro]
+ relroReadOnly = append(relroReadOnly, symn)
+ }
+ seg = segrelro
+ relroSecPerm = relroPerm
+
+ /* data only written by relocations */
+ sect = state.allocateNamedDataSection(segrelro, genrelrosecname(""), relroReadOnly, relroSecPerm)
+
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect)
+
+ for i, symnro := range sym.ReadOnly {
+ if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix {
+ // Skip forward so that no type
+ // reference uses a zero offset.
+ // This is unlikely but possible in small
+ // programs with no other read-only data.
+ state.datsize++
+ }
+
+ symn := sym.RelROMap[symnro]
+ symnStartValue := state.datsize
+ if len(state.data[symn]) != 0 {
+ symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0])
+ }
+
+ for _, s := range state.data[symn] {
+ outer := ldr.OuterSym(s)
+ if s != 0 && ldr.SymSect(outer) != nil && ldr.SymSect(outer) != sect {
+ ctxt.Errorf(s, "s.Outer (%s) in different section from s, %s != %s", ldr.SymName(outer), ldr.SymSect(outer).Name, sect.Name)
+ }
+ }
+ state.assignToSection(sect, symn, sym.SRODATA)
+ setCarrierSize(symn, state.datsize-symnStartValue)
+ if ctxt.HeadType == objabi.Haix {
+ // Read-only symbols might be wrapped inside their outer
+ // symbol.
+ // XCOFF symbol table needs to know the size of
+ // these outer symbols.
+ xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn)
+ }
+ }
+
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+ }
+
+ /* typelink */
+ sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm)
+
+ typelink := ldr.CreateSymForUpdate("runtime.typelink", 0)
+ ldr.SetSymSect(typelink.Sym(), sect)
+ typelink.SetType(sym.SRODATA)
+ state.datsize += typelink.Size()
+ state.checkdatsize(sym.STYPELINK)
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+
+ /* itablink */
+ sect = state.allocateNamedDataSection(seg, genrelrosecname(".itablink"), []sym.SymKind{sym.SITABLINK}, relroSecPerm)
+
+ itablink := ldr.CreateSymForUpdate("runtime.itablink", 0)
+ ldr.SetSymSect(itablink.Sym(), sect)
+ itablink.SetType(sym.SRODATA)
+ state.datsize += itablink.Size()
+ state.checkdatsize(sym.SITABLINK)
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+
+ /* gosymtab */
+ sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gosymtab"), sym.SSYMTAB, sym.SRODATA, relroSecPerm)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.symtab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.esymtab", 0), sect)
+
+ /* gopclntab */
+ sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
+ setCarrierSize(sym.SPCLNTAB, int64(sect.Length))
+ if ctxt.HeadType == objabi.Haix {
+ xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB)
+ }
+
+ // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits.
+ if state.datsize != int64(uint32(state.datsize)) {
+ Errorf(nil, "read-only data segment too large: %d", state.datsize)
+ }
+
+ siz := 0
+ for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ {
+ siz += len(state.data[symn])
+ }
+ ctxt.datap = make([]loader.Sym, 0, siz)
+ for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ {
+ ctxt.datap = append(ctxt.datap, state.data[symn]...)
+ }
+}
+
+// allocateDwarfSections allocates sym.Section objects for DWARF
+// symbols, and assigns symbols to sections.
+func (state *dodataState) allocateDwarfSections(ctxt *Link) {
+
+ alignOne := func(state *dodataState, datsize int64, s loader.Sym) int64 { return datsize }
+
+ ldr := ctxt.loader
+ for i := 0; i < len(dwarfp); i++ {
+ // First the section symbol.
+ s := dwarfp[i].secSym()
+ sect := state.allocateNamedDataSection(&Segdwarf, ldr.SymName(s), []sym.SymKind{}, 04)
+ ldr.SetSymSect(s, sect)
+ sect.Sym = sym.LoaderSym(s)
+ curType := ldr.SymType(s)
+ state.setSymType(s, sym.SRODATA)
+ ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr))
+ state.datsize += ldr.SymSize(s)
+
+ // Then any sub-symbols for the section symbol.
+ subSyms := dwarfp[i].subSyms()
+ state.assignDsymsToSection(sect, subSyms, sym.SRODATA, alignOne)
+
+ for j := 0; j < len(subSyms); j++ {
+ s := subSyms[j]
+ if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC {
+ // Update the size of .debug_loc for this symbol's
+ // package.
+ addDwsectCUSize(".debug_loc", ldr.SymPkg(s), uint64(ldr.SymSize(s)))
+ }
+ }
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+ state.checkdatsize(curType)
+ }
+}
+
+type symNameSize struct {
+ name string
+ sz int64
+ val int64
+ sym loader.Sym
+}
+
+func (state *dodataState) dodataSect(ctxt *Link, symn sym.SymKind, syms []loader.Sym) (result []loader.Sym, maxAlign int32) {
+ var head, tail loader.Sym
+ ldr := ctxt.loader
+ sl := make([]symNameSize, len(syms))
+ for k, s := range syms {
+ ss := ldr.SymSize(s)
+ sl[k] = symNameSize{name: ldr.SymName(s), sz: ss, sym: s}
+ ds := int64(len(ldr.Data(s)))
+ switch {
+ case ss < ds:
+ ctxt.Errorf(s, "initialize bounds (%d < %d)", ss, ds)
+ case ss < 0:
+ ctxt.Errorf(s, "negative size (%d bytes)", ss)
+ case ss > cutoff:
+ ctxt.Errorf(s, "symbol too large (%d bytes)", ss)
+ }
+
+ // If the usually-special section-marker symbols are being laid
+ // out as regular symbols, put them either at the beginning or
+ // end of their section.
+ if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+ switch ldr.SymName(s) {
+ case "runtime.text", "runtime.bss", "runtime.data", "runtime.types", "runtime.rodata":
+ head = s
+ continue
+ case "runtime.etext", "runtime.ebss", "runtime.edata", "runtime.etypes", "runtime.erodata":
+ tail = s
+ continue
+ }
+ }
+ }
+
+ // For ppc64, we want to interleave the .got and .toc sections
+ // from input files. Both are type sym.SELFGOT, so in that case
+ // we skip size comparison and fall through to the name
+ // comparison (conveniently, .got sorts before .toc).
+ checkSize := symn != sym.SELFGOT
+
+ // Perform the sort.
+ if symn != sym.SPCLNTAB {
+ sort.Slice(sl, func(i, j int) bool {
+ si, sj := sl[i].sym, sl[j].sym
+ switch {
+ case si == head, sj == tail:
+ return true
+ case sj == head, si == tail:
+ return false
+ }
+ if checkSize {
+ isz := sl[i].sz
+ jsz := sl[j].sz
+ if isz != jsz {
+ return isz < jsz
+ }
+ }
+ iname := sl[i].name
+ jname := sl[j].name
+ if iname != jname {
+ return iname < jname
+ }
+ return si < sj
+ })
+ } else {
+ // PCLNTAB was built internally, and already has the proper order.
+ }
+
+ // Set alignment, construct result
+ syms = syms[:0]
+ for k := range sl {
+ s := sl[k].sym
+ if s != head && s != tail {
+ align := symalign(ldr, s)
+ if maxAlign < align {
+ maxAlign = align
+ }
+ }
+ syms = append(syms, s)
+ }
+
+ return syms, maxAlign
+}
+
+// Add buildid to beginning of text segment, on non-ELF systems.
+// Non-ELF binary formats are not always flexible enough to
+// give us a place to put the Go build ID. On those systems, we put it
+// at the very beginning of the text segment.
+// This “header” is read by cmd/go.
+func (ctxt *Link) textbuildid() {
+ if ctxt.IsELF || ctxt.BuildMode == BuildModePlugin || *flagBuildid == "" {
+ return
+ }
+
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate("go:buildid", 0)
+ // The \xff is invalid UTF-8, meant to make it less likely
+ // to find one of these accidentally.
+ data := "\xff Go build ID: " + strconv.Quote(*flagBuildid) + "\n \xff"
+ s.SetType(sym.STEXT)
+ s.SetData([]byte(data))
+ s.SetSize(int64(len(data)))
+
+ ctxt.Textp = append(ctxt.Textp, 0)
+ copy(ctxt.Textp[1:], ctxt.Textp)
+ ctxt.Textp[0] = s.Sym()
+}
+
+func (ctxt *Link) buildinfo() {
+ if ctxt.linkShared || ctxt.BuildMode == BuildModePlugin {
+ // -linkshared and -buildmode=plugin get confused
+ // about the relocations in go.buildinfo
+ // pointing at the other data sections.
+ // The version information is only available in executables.
+ return
+ }
+
+ // Write the buildinfo symbol, which go version looks for.
+ // The code reading this data is in package debug/buildinfo.
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate("go:buildinfo", 0)
+ s.SetType(sym.SBUILDINFO)
+ s.SetAlign(16)
+ // The \xff is invalid UTF-8, meant to make it less likely
+ // to find one of these accidentally.
+ const prefix = "\xff Go buildinf:" // 14 bytes, plus 2 data bytes filled in below
+ data := make([]byte, 32)
+ copy(data, prefix)
+ data[len(prefix)] = byte(ctxt.Arch.PtrSize)
+ data[len(prefix)+1] = 0
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ data[len(prefix)+1] = 1
+ }
+ data[len(prefix)+1] |= 2 // signals new pointer-free format
+ data = appendString(data, strdata["runtime.buildVersion"])
+ data = appendString(data, strdata["runtime.modinfo"])
+ // MacOS linker gets very upset if the size os not a multiple of alignment.
+ for len(data)%16 != 0 {
+ data = append(data, 0)
+ }
+ s.SetData(data)
+ s.SetSize(int64(len(data)))
+
+ // Add reference to go:buildinfo from the rodata section,
+ // so that external linking with -Wl,--gc-sections does not
+ // delete the build info.
+ sr := ldr.CreateSymForUpdate("go:buildinfo.ref", 0)
+ sr.SetType(sym.SRODATA)
+ sr.SetAlign(int32(ctxt.Arch.PtrSize))
+ sr.AddAddr(ctxt.Arch, s.Sym())
+}
+
+// appendString appends s to data, prefixed by its varint-encoded length.
+func appendString(data []byte, s string) []byte {
+ var v [binary.MaxVarintLen64]byte
+ n := binary.PutUvarint(v[:], uint64(len(s)))
+ data = append(data, v[:n]...)
+ data = append(data, s...)
+ return data
+}
+
+// assign addresses to text
+func (ctxt *Link) textaddress() {
+ addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05)
+
+ // Assign PCs in text segment.
+ // Could parallelize, by assigning to text
+ // and then letting threads copy down, but probably not worth it.
+ sect := Segtext.Sections[0]
+
+ sect.Align = int32(Funcalign)
+
+ ldr := ctxt.loader
+
+ text := ctxt.xdefine("runtime.text", sym.STEXT, 0)
+ etext := ctxt.xdefine("runtime.etext", sym.STEXT, 0)
+ ldr.SetSymSect(text, sect)
+ if ctxt.IsAIX() && ctxt.IsExternal() {
+ // Setting runtime.text has a real symbol prevents ld to
+ // change its base address resulting in wrong offsets for
+ // reflect methods.
+ u := ldr.MakeSymbolUpdater(text)
+ u.SetAlign(sect.Align)
+ u.SetSize(8)
+ }
+
+ if (ctxt.DynlinkingGo() && ctxt.IsDarwin()) || (ctxt.IsAIX() && ctxt.IsExternal()) {
+ ldr.SetSymSect(etext, sect)
+ ctxt.Textp = append(ctxt.Textp, etext, 0)
+ copy(ctxt.Textp[1:], ctxt.Textp)
+ ctxt.Textp[0] = text
+ }
+
+ start := uint64(Rnd(*FlagTextAddr, int64(Funcalign)))
+ va := start
+ n := 1
+ sect.Vaddr = va
+
+ limit := thearch.TrampLimit
+ if limit == 0 {
+ limit = 1 << 63 // unlimited
+ }
+ if *FlagDebugTextSize != 0 {
+ limit = uint64(*FlagDebugTextSize)
+ }
+ if *FlagDebugTramp > 1 {
+ limit = 1 // debug mode, force generating trampolines for everything
+ }
+
+ if ctxt.IsAIX() && ctxt.IsExternal() {
+ // On AIX, normally we won't generate direct calls to external symbols,
+ // except in one test, cmd/go/testdata/script/link_syso_issue33139.txt.
+ // That test doesn't make much sense, and I'm not sure it ever works.
+ // Just generate trampoline for now (which will turn a direct call to
+ // an indirect call, which at least builds).
+ limit = 1
+ }
+
+ // First pass: assign addresses assuming the program is small and
+ // don't generate trampolines.
+ big := false
+ for _, s := range ctxt.Textp {
+ sect, n, va = assignAddress(ctxt, sect, n, s, va, false, big)
+ if va-start >= limit {
+ big = true
+ break
+ }
+ }
+
+ // Second pass: only if it is too big, insert trampolines for too-far
+ // jumps and targets with unknown addresses.
+ if big {
+ // reset addresses
+ for _, s := range ctxt.Textp {
+ if ldr.OuterSym(s) != 0 || s == text {
+ continue
+ }
+ oldv := ldr.SymValue(s)
+ for sub := s; sub != 0; sub = ldr.SubSym(sub) {
+ ldr.SetSymValue(sub, ldr.SymValue(sub)-oldv)
+ }
+ }
+ va = start
+
+ ntramps := 0
+ for _, s := range ctxt.Textp {
+ sect, n, va = assignAddress(ctxt, sect, n, s, va, false, big)
+
+ trampoline(ctxt, s) // resolve jumps, may add trampolines if jump too far
+
+ // lay down trampolines after each function
+ for ; ntramps < len(ctxt.tramps); ntramps++ {
+ tramp := ctxt.tramps[ntramps]
+ if ctxt.IsAIX() && strings.HasPrefix(ldr.SymName(tramp), "runtime.text.") {
+ // Already set in assignAddress
+ continue
+ }
+ sect, n, va = assignAddress(ctxt, sect, n, tramp, va, true, big)
+ }
+ }
+
+ // merge tramps into Textp, keeping Textp in address order
+ if ntramps != 0 {
+ newtextp := make([]loader.Sym, 0, len(ctxt.Textp)+ntramps)
+ i := 0
+ for _, s := range ctxt.Textp {
+ for ; i < ntramps && ldr.SymValue(ctxt.tramps[i]) < ldr.SymValue(s); i++ {
+ newtextp = append(newtextp, ctxt.tramps[i])
+ }
+ newtextp = append(newtextp, s)
+ }
+ newtextp = append(newtextp, ctxt.tramps[i:ntramps]...)
+
+ ctxt.Textp = newtextp
+ }
+ }
+
+ sect.Length = va - sect.Vaddr
+ ldr.SetSymSect(etext, sect)
+ if ldr.SymValue(etext) == 0 {
+ // Set the address of the start/end symbols, if not already
+ // (i.e. not darwin+dynlink or AIX+external, see above).
+ ldr.SetSymValue(etext, int64(va))
+ ldr.SetSymValue(text, int64(Segtext.Sections[0].Vaddr))
+ }
+}
+
+// assigns address for a text symbol, returns (possibly new) section, its number, and the address.
+func assignAddress(ctxt *Link, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp, big bool) (*sym.Section, int, uint64) {
+ ldr := ctxt.loader
+ if thearch.AssignAddress != nil {
+ return thearch.AssignAddress(ldr, sect, n, s, va, isTramp)
+ }
+
+ ldr.SetSymSect(s, sect)
+ if ldr.AttrSubSymbol(s) {
+ return sect, n, va
+ }
+
+ align := ldr.SymAlign(s)
+ if align == 0 {
+ align = int32(Funcalign)
+ }
+ va = uint64(Rnd(int64(va), int64(align)))
+ if sect.Align < align {
+ sect.Align = align
+ }
+
+ funcsize := uint64(MINFUNC) // spacing required for findfunctab
+ if ldr.SymSize(s) > MINFUNC {
+ funcsize = uint64(ldr.SymSize(s))
+ }
+
+ // If we need to split text sections, and this function doesn't fit in the current
+ // section, then create a new one.
+ //
+ // Only break at outermost syms.
+ if big && splitTextSections(ctxt) && ldr.OuterSym(s) == 0 {
+ // For debugging purposes, allow text size limit to be cranked down,
+ // so as to stress test the code that handles multiple text sections.
+ var textSizelimit uint64 = thearch.TrampLimit
+ if *FlagDebugTextSize != 0 {
+ textSizelimit = uint64(*FlagDebugTextSize)
+ }
+
+ // Sanity check: make sure the limit is larger than any
+ // individual text symbol.
+ if funcsize > textSizelimit {
+ panic(fmt.Sprintf("error: text size limit %d less than text symbol %s size of %d", textSizelimit, ldr.SymName(s), funcsize))
+ }
+
+ if va-sect.Vaddr+funcsize+maxSizeTrampolines(ctxt, ldr, s, isTramp) > textSizelimit {
+ sectAlign := int32(thearch.Funcalign)
+ if ctxt.IsPPC64() {
+ // Align the next text section to the worst case function alignment likely
+ // to be encountered when processing function symbols. The start address
+ // is rounded against the final alignment of the text section later on in
+ // (*Link).address. This may happen due to usage of PCALIGN directives
+ // larger than Funcalign, or usage of ISA 3.1 prefixed instructions
+ // (see ISA 3.1 Book I 1.9).
+ const ppc64maxFuncalign = 64
+ sectAlign = ppc64maxFuncalign
+ va = uint64(Rnd(int64(va), ppc64maxFuncalign))
+ }
+
+ // Set the length for the previous text section
+ sect.Length = va - sect.Vaddr
+
+ // Create new section, set the starting Vaddr
+ sect = addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05)
+
+ sect.Vaddr = va
+ sect.Align = sectAlign
+ ldr.SetSymSect(s, sect)
+
+ // Create a symbol for the start of the secondary text sections
+ ntext := ldr.CreateSymForUpdate(fmt.Sprintf("runtime.text.%d", n), 0)
+ ntext.SetSect(sect)
+ if ctxt.IsAIX() {
+ // runtime.text.X must be a real symbol on AIX.
+ // Assign its address directly in order to be the
+ // first symbol of this new section.
+ ntext.SetType(sym.STEXT)
+ ntext.SetSize(int64(MINFUNC))
+ ntext.SetOnList(true)
+ ntext.SetAlign(sectAlign)
+ ctxt.tramps = append(ctxt.tramps, ntext.Sym())
+
+ ntext.SetValue(int64(va))
+ va += uint64(ntext.Size())
+
+ if align := ldr.SymAlign(s); align != 0 {
+ va = uint64(Rnd(int64(va), int64(align)))
+ } else {
+ va = uint64(Rnd(int64(va), int64(Funcalign)))
+ }
+ }
+ n++
+ }
+ }
+
+ ldr.SetSymValue(s, 0)
+ for sub := s; sub != 0; sub = ldr.SubSym(sub) {
+ ldr.SetSymValue(sub, ldr.SymValue(sub)+int64(va))
+ if ctxt.Debugvlog > 2 {
+ fmt.Println("assign text address:", ldr.SymName(sub), ldr.SymValue(sub))
+ }
+ }
+
+ va += funcsize
+
+ return sect, n, va
+}
+
+// Return whether we may need to split text sections.
+//
+// On PPC64x, when external linking, a text section should not be
+// larger than 2^25 bytes due to the size of call target offset field
+// in the 'bl' instruction. Splitting into smaller text sections
+// smaller than this limit allows the system linker to modify the long
+// calls appropriately. The limit allows for the space needed for
+// tables inserted by the linker.
+//
+// The same applies to Darwin/ARM64, with 2^27 byte threshold.
+//
+// Similarly for ARM, we split sections (at 2^25 bytes) to avoid
+// inconsistencies between the Go linker's reachability calculations
+// (e.g. will direct call from X to Y need a trampoline) and similar
+// machinery in the external linker; see #58425 for more on the
+// history here.
+func splitTextSections(ctxt *Link) bool {
+ return (ctxt.IsARM() || ctxt.IsPPC64() || (ctxt.IsARM64() && ctxt.IsDarwin())) && ctxt.IsExternal()
+}
+
+// On Wasm, we reserve 4096 bytes for zero page, then 8192 bytes for wasm_exec.js
+// to store command line args and environment variables.
+// Data sections starts from at least address 12288.
+// Keep in sync with wasm_exec.js.
+const wasmMinDataAddr = 4096 + 8192
+
+// address assigns virtual addresses to all segments and sections and
+// returns all segments in file order.
+func (ctxt *Link) address() []*sym.Segment {
+ var order []*sym.Segment // Layout order
+
+ va := uint64(*FlagTextAddr)
+ order = append(order, &Segtext)
+ Segtext.Rwx = 05
+ Segtext.Vaddr = va
+ for i, s := range Segtext.Sections {
+ va = uint64(Rnd(int64(va), int64(s.Align)))
+ s.Vaddr = va
+ va += s.Length
+
+ if ctxt.IsWasm() && i == 0 && va < wasmMinDataAddr {
+ va = wasmMinDataAddr
+ }
+ }
+
+ Segtext.Length = va - uint64(*FlagTextAddr)
+
+ if len(Segrodata.Sections) > 0 {
+ // align to page boundary so as not to mix
+ // rodata and executable text.
+ //
+ // Note: gold or GNU ld will reduce the size of the executable
+ // file by arranging for the relro segment to end at a page
+ // boundary, and overlap the end of the text segment with the
+ // start of the relro segment in the file. The PT_LOAD segments
+ // will be such that the last page of the text segment will be
+ // mapped twice, once r-x and once starting out rw- and, after
+ // relocation processing, changed to r--.
+ //
+ // Ideally the last page of the text segment would not be
+ // writable even for this short period.
+ va = uint64(Rnd(int64(va), int64(*FlagRound)))
+
+ order = append(order, &Segrodata)
+ Segrodata.Rwx = 04
+ Segrodata.Vaddr = va
+ for _, s := range Segrodata.Sections {
+ va = uint64(Rnd(int64(va), int64(s.Align)))
+ s.Vaddr = va
+ va += s.Length
+ }
+
+ Segrodata.Length = va - Segrodata.Vaddr
+ }
+ if len(Segrelrodata.Sections) > 0 {
+ // align to page boundary so as not to mix
+ // rodata, rel-ro data, and executable text.
+ va = uint64(Rnd(int64(va), int64(*FlagRound)))
+ if ctxt.HeadType == objabi.Haix {
+ // Relro data are inside data segment on AIX.
+ va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE)
+ }
+
+ order = append(order, &Segrelrodata)
+ Segrelrodata.Rwx = 06
+ Segrelrodata.Vaddr = va
+ for _, s := range Segrelrodata.Sections {
+ va = uint64(Rnd(int64(va), int64(s.Align)))
+ s.Vaddr = va
+ va += s.Length
+ }
+
+ Segrelrodata.Length = va - Segrelrodata.Vaddr
+ }
+
+ va = uint64(Rnd(int64(va), int64(*FlagRound)))
+ if ctxt.HeadType == objabi.Haix && len(Segrelrodata.Sections) == 0 {
+ // Data sections are moved to an unreachable segment
+ // to ensure that they are position-independent.
+ // Already done if relro sections exist.
+ va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE)
+ }
+ order = append(order, &Segdata)
+ Segdata.Rwx = 06
+ Segdata.Vaddr = va
+ var data *sym.Section
+ var noptr *sym.Section
+ var bss *sym.Section
+ var noptrbss *sym.Section
+ var fuzzCounters *sym.Section
+ for i, s := range Segdata.Sections {
+ if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && s.Name == ".tbss" {
+ continue
+ }
+ vlen := int64(s.Length)
+ if i+1 < len(Segdata.Sections) && !((ctxt.IsELF || ctxt.HeadType == objabi.Haix) && Segdata.Sections[i+1].Name == ".tbss") {
+ vlen = int64(Segdata.Sections[i+1].Vaddr - s.Vaddr)
+ }
+ s.Vaddr = va
+ va += uint64(vlen)
+ Segdata.Length = va - Segdata.Vaddr
+ switch s.Name {
+ case ".data":
+ data = s
+ case ".noptrdata":
+ noptr = s
+ case ".bss":
+ bss = s
+ case ".noptrbss":
+ noptrbss = s
+ case ".go.fuzzcntrs":
+ fuzzCounters = s
+ }
+ }
+
+ // Assign Segdata's Filelen omitting the BSS. We do this here
+ // simply because right now we know where the BSS starts.
+ Segdata.Filelen = bss.Vaddr - Segdata.Vaddr
+
+ va = uint64(Rnd(int64(va), int64(*FlagRound)))
+ order = append(order, &Segdwarf)
+ Segdwarf.Rwx = 06
+ Segdwarf.Vaddr = va
+ for i, s := range Segdwarf.Sections {
+ vlen := int64(s.Length)
+ if i+1 < len(Segdwarf.Sections) {
+ vlen = int64(Segdwarf.Sections[i+1].Vaddr - s.Vaddr)
+ }
+ s.Vaddr = va
+ va += uint64(vlen)
+ if ctxt.HeadType == objabi.Hwindows {
+ va = uint64(Rnd(int64(va), PEFILEALIGN))
+ }
+ Segdwarf.Length = va - Segdwarf.Vaddr
+ }
+
+ ldr := ctxt.loader
+ var (
+ rodata = ldr.SymSect(ldr.LookupOrCreateSym("runtime.rodata", 0))
+ symtab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.symtab", 0))
+ pclntab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0))
+ types = ldr.SymSect(ldr.LookupOrCreateSym("runtime.types", 0))
+ )
+
+ for _, s := range ctxt.datap {
+ if sect := ldr.SymSect(s); sect != nil {
+ ldr.AddToSymValue(s, int64(sect.Vaddr))
+ }
+ v := ldr.SymValue(s)
+ for sub := ldr.SubSym(s); sub != 0; sub = ldr.SubSym(sub) {
+ ldr.AddToSymValue(sub, v)
+ }
+ }
+
+ for _, si := range dwarfp {
+ for _, s := range si.syms {
+ if sect := ldr.SymSect(s); sect != nil {
+ ldr.AddToSymValue(s, int64(sect.Vaddr))
+ }
+ sub := ldr.SubSym(s)
+ if sub != 0 {
+ panic(fmt.Sprintf("unexpected sub-sym for %s %s", ldr.SymName(s), ldr.SymType(s).String()))
+ }
+ v := ldr.SymValue(s)
+ for ; sub != 0; sub = ldr.SubSym(sub) {
+ ldr.AddToSymValue(s, v)
+ }
+ }
+ }
+
+ if ctxt.BuildMode == BuildModeShared {
+ s := ldr.LookupOrCreateSym("go:link.abihashbytes", 0)
+ sect := ldr.SymSect(ldr.LookupOrCreateSym(".note.go.abihash", 0))
+ ldr.SetSymSect(s, sect)
+ ldr.SetSymValue(s, int64(sect.Vaddr+16))
+ }
+
+ // If there are multiple text sections, create runtime.text.n for
+ // their section Vaddr, using n for index
+ n := 1
+ for _, sect := range Segtext.Sections[1:] {
+ if sect.Name != ".text" {
+ break
+ }
+ symname := fmt.Sprintf("runtime.text.%d", n)
+ if ctxt.HeadType != objabi.Haix || ctxt.LinkMode != LinkExternal {
+ // Addresses are already set on AIX with external linker
+ // because these symbols are part of their sections.
+ ctxt.xdefine(symname, sym.STEXT, int64(sect.Vaddr))
+ }
+ n++
+ }
+
+ ctxt.xdefine("runtime.rodata", sym.SRODATA, int64(rodata.Vaddr))
+ ctxt.xdefine("runtime.erodata", sym.SRODATA, int64(rodata.Vaddr+rodata.Length))
+ ctxt.xdefine("runtime.types", sym.SRODATA, int64(types.Vaddr))
+ ctxt.xdefine("runtime.etypes", sym.SRODATA, int64(types.Vaddr+types.Length))
+
+ s := ldr.Lookup("runtime.gcdata", 0)
+ ldr.SetAttrLocal(s, true)
+ ctxt.xdefine("runtime.egcdata", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s))
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcdata", 0), ldr.SymSect(s))
+
+ s = ldr.LookupOrCreateSym("runtime.gcbss", 0)
+ ldr.SetAttrLocal(s, true)
+ ctxt.xdefine("runtime.egcbss", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s))
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcbss", 0), ldr.SymSect(s))
+
+ ctxt.xdefine("runtime.symtab", sym.SRODATA, int64(symtab.Vaddr))
+ ctxt.xdefine("runtime.esymtab", sym.SRODATA, int64(symtab.Vaddr+symtab.Length))
+ ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr))
+ ctxt.defineInternal("runtime.pcheader", sym.SRODATA)
+ ctxt.defineInternal("runtime.funcnametab", sym.SRODATA)
+ ctxt.defineInternal("runtime.cutab", sym.SRODATA)
+ ctxt.defineInternal("runtime.filetab", sym.SRODATA)
+ ctxt.defineInternal("runtime.pctab", sym.SRODATA)
+ ctxt.defineInternal("runtime.functab", sym.SRODATA)
+ ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
+ ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
+ ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length))
+ ctxt.xdefine("runtime.bss", sym.SBSS, int64(bss.Vaddr))
+ ctxt.xdefine("runtime.ebss", sym.SBSS, int64(bss.Vaddr+bss.Length))
+ ctxt.xdefine("runtime.data", sym.SDATA, int64(data.Vaddr))
+ ctxt.xdefine("runtime.edata", sym.SDATA, int64(data.Vaddr+data.Length))
+ ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr))
+ ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length))
+ ctxt.xdefine("runtime.covctrs", sym.SCOVERAGE_COUNTER, int64(noptrbss.Vaddr+covCounterDataStartOff))
+ ctxt.xdefine("runtime.ecovctrs", sym.SCOVERAGE_COUNTER, int64(noptrbss.Vaddr+covCounterDataStartOff+covCounterDataLen))
+ ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length))
+
+ if fuzzCounters != nil {
+ ctxt.xdefine("runtime.__start___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr))
+ ctxt.xdefine("runtime.__stop___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
+ ctxt.xdefine("internal/fuzz._counters", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr))
+ ctxt.xdefine("internal/fuzz._ecounters", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
+ }
+
+ if ctxt.IsSolaris() {
+ // On Solaris, in the runtime it sets the external names of the
+ // end symbols. Unset them and define separate symbols, so we
+ // keep both.
+ etext := ldr.Lookup("runtime.etext", 0)
+ edata := ldr.Lookup("runtime.edata", 0)
+ end := ldr.Lookup("runtime.end", 0)
+ ldr.SetSymExtname(etext, "runtime.etext")
+ ldr.SetSymExtname(edata, "runtime.edata")
+ ldr.SetSymExtname(end, "runtime.end")
+ ctxt.xdefine("_etext", ldr.SymType(etext), ldr.SymValue(etext))
+ ctxt.xdefine("_edata", ldr.SymType(edata), ldr.SymValue(edata))
+ ctxt.xdefine("_end", ldr.SymType(end), ldr.SymValue(end))
+ ldr.SetSymSect(ldr.Lookup("_etext", 0), ldr.SymSect(etext))
+ ldr.SetSymSect(ldr.Lookup("_edata", 0), ldr.SymSect(edata))
+ ldr.SetSymSect(ldr.Lookup("_end", 0), ldr.SymSect(end))
+ }
+
+ if ctxt.IsPPC64() && ctxt.IsElf() {
+ // Resolve .TOC. symbols for all objects. Only one TOC region is supported. If a
+ // GOT section is present, compute it as suggested by the ELFv2 ABI. Otherwise,
+ // choose a similar offset from the start of the data segment.
+ tocAddr := int64(Segdata.Vaddr) + 0x8000
+ if gotAddr := ldr.SymValue(ctxt.GOT); gotAddr != 0 {
+ tocAddr = gotAddr + 0x8000
+ }
+ for i := range ctxt.DotTOC {
+ if i >= sym.SymVerABICount && i < sym.SymVerStatic { // these versions are not used currently
+ continue
+ }
+ if toc := ldr.Lookup(".TOC.", i); toc != 0 {
+ ldr.SetSymValue(toc, tocAddr)
+ }
+ }
+ }
+
+ return order
+}
+
+// layout assigns file offsets and lengths to the segments in order.
+// Returns the file size containing all the segments.
+func (ctxt *Link) layout(order []*sym.Segment) uint64 {
+ var prev *sym.Segment
+ for _, seg := range order {
+ if prev == nil {
+ seg.Fileoff = uint64(HEADR)
+ } else {
+ switch ctxt.HeadType {
+ default:
+ // Assuming the previous segment was
+ // aligned, the following rounding
+ // should ensure that this segment's
+ // VA ≡ Fileoff mod FlagRound.
+ seg.Fileoff = uint64(Rnd(int64(prev.Fileoff+prev.Filelen), int64(*FlagRound)))
+ if seg.Vaddr%uint64(*FlagRound) != seg.Fileoff%uint64(*FlagRound) {
+ Exitf("bad segment rounding (Vaddr=%#x Fileoff=%#x FlagRound=%#x)", seg.Vaddr, seg.Fileoff, *FlagRound)
+ }
+ case objabi.Hwindows:
+ seg.Fileoff = prev.Fileoff + uint64(Rnd(int64(prev.Filelen), PEFILEALIGN))
+ case objabi.Hplan9:
+ seg.Fileoff = prev.Fileoff + prev.Filelen
+ }
+ }
+ if seg != &Segdata {
+ // Link.address already set Segdata.Filelen to
+ // account for BSS.
+ seg.Filelen = seg.Length
+ }
+ prev = seg
+ }
+ return prev.Fileoff + prev.Filelen
+}
+
+// add a trampoline with symbol s (to be laid down after the current function)
+func (ctxt *Link) AddTramp(s *loader.SymbolBuilder) {
+ s.SetType(sym.STEXT)
+ s.SetReachable(true)
+ s.SetOnList(true)
+ ctxt.tramps = append(ctxt.tramps, s.Sym())
+ if *FlagDebugTramp > 0 && ctxt.Debugvlog > 0 {
+ ctxt.Logf("trampoline %s inserted\n", s.Name())
+ }
+}
+
+// compressSyms compresses syms and returns the contents of the
+// compressed section. If the section would get larger, it returns nil.
+func compressSyms(ctxt *Link, syms []loader.Sym) []byte {
+ ldr := ctxt.loader
+ var total int64
+ for _, sym := range syms {
+ total += ldr.SymSize(sym)
+ }
+
+ var buf bytes.Buffer
+ if ctxt.IsELF {
+ switch ctxt.Arch.PtrSize {
+ case 8:
+ binary.Write(&buf, ctxt.Arch.ByteOrder, elf.Chdr64{
+ Type: uint32(elf.COMPRESS_ZLIB),
+ Size: uint64(total),
+ Addralign: uint64(ctxt.Arch.Alignment),
+ })
+ case 4:
+ binary.Write(&buf, ctxt.Arch.ByteOrder, elf.Chdr32{
+ Type: uint32(elf.COMPRESS_ZLIB),
+ Size: uint32(total),
+ Addralign: uint32(ctxt.Arch.Alignment),
+ })
+ default:
+ log.Fatalf("can't compress header size:%d", ctxt.Arch.PtrSize)
+ }
+ } else {
+ buf.Write([]byte("ZLIB"))
+ var sizeBytes [8]byte
+ binary.BigEndian.PutUint64(sizeBytes[:], uint64(total))
+ buf.Write(sizeBytes[:])
+ }
+
+ var relocbuf []byte // temporary buffer for applying relocations
+
+ // Using zlib.BestSpeed achieves very nearly the same
+ // compression levels of zlib.DefaultCompression, but takes
+ // substantially less time. This is important because DWARF
+ // compression can be a significant fraction of link time.
+ z, err := zlib.NewWriterLevel(&buf, zlib.BestSpeed)
+ if err != nil {
+ log.Fatalf("NewWriterLevel failed: %s", err)
+ }
+ st := ctxt.makeRelocSymState()
+ for _, s := range syms {
+ // Symbol data may be read-only. Apply relocations in a
+ // temporary buffer, and immediately write it out.
+ P := ldr.Data(s)
+ relocs := ldr.Relocs(s)
+ if relocs.Count() != 0 {
+ relocbuf = append(relocbuf[:0], P...)
+ P = relocbuf
+ st.relocsym(s, P)
+ }
+ if _, err := z.Write(P); err != nil {
+ log.Fatalf("compression failed: %s", err)
+ }
+ for i := ldr.SymSize(s) - int64(len(P)); i > 0; {
+ b := zeros[:]
+ if i < int64(len(b)) {
+ b = b[:i]
+ }
+ n, err := z.Write(b)
+ if err != nil {
+ log.Fatalf("compression failed: %s", err)
+ }
+ i -= int64(n)
+ }
+ }
+ if err := z.Close(); err != nil {
+ log.Fatalf("compression failed: %s", err)
+ }
+ if int64(buf.Len()) >= total {
+ // Compression didn't save any space.
+ return nil
+ }
+ return buf.Bytes()
+}
diff --git a/src/cmd/link/internal/ld/data_test.go b/src/cmd/link/internal/ld/data_test.go
new file mode 100644
index 0000000..f91493b
--- /dev/null
+++ b/src/cmd/link/internal/ld/data_test.go
@@ -0,0 +1,93 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "internal/buildcfg"
+ "testing"
+)
+
+func setUpContext(arch *sys.Arch, iself bool, ht objabi.HeadType, bm, lm string) *Link {
+ ctxt := linknew(arch)
+ edummy := func(str string, off int) {}
+ ctxt.HeadType = ht
+ er := loader.ErrorReporter{}
+ ctxt.loader = loader.NewLoader(0, edummy, &er)
+ ctxt.BuildMode.Set(bm)
+ ctxt.LinkMode.Set(lm)
+ ctxt.IsELF = iself
+ ctxt.mustSetHeadType()
+ ctxt.setArchSyms()
+ return ctxt
+}
+
+// Make sure the addgotsym properly increases the symbols.
+func TestAddGotSym(t *testing.T) {
+ tests := []struct {
+ arch *sys.Arch
+ ht objabi.HeadType
+ bm, lm string
+ rel string
+ relsize int
+ gotsize int
+ }{
+ {
+ arch: sys.Arch386,
+ ht: objabi.Hlinux,
+ bm: "pie",
+ lm: "internal",
+ rel: ".rel",
+ relsize: 2 * sys.Arch386.PtrSize,
+ gotsize: sys.Arch386.PtrSize,
+ },
+ {
+ arch: sys.ArchAMD64,
+ ht: objabi.Hlinux,
+ bm: "pie",
+ lm: "internal",
+ rel: ".rela",
+ relsize: 3 * sys.ArchAMD64.PtrSize,
+ gotsize: sys.ArchAMD64.PtrSize,
+ },
+ {
+ arch: sys.ArchAMD64,
+ ht: objabi.Hdarwin,
+ bm: "pie",
+ lm: "external",
+ gotsize: sys.ArchAMD64.PtrSize,
+ },
+ }
+
+ // Save the architecture as we're going to set it on each test run.
+ origArch := buildcfg.GOARCH
+ defer func() {
+ buildcfg.GOARCH = origArch
+ }()
+
+ for i, test := range tests {
+ iself := len(test.rel) != 0
+ buildcfg.GOARCH = test.arch.Name
+ ctxt := setUpContext(test.arch, iself, test.ht, test.bm, test.lm)
+ foo := ctxt.loader.CreateSymForUpdate("foo", 0)
+ ctxt.loader.CreateExtSym("bar", 0)
+ AddGotSym(&ctxt.Target, ctxt.loader, &ctxt.ArchSyms, foo.Sym(), 0)
+
+ if iself {
+ rel := ctxt.loader.Lookup(test.rel, 0)
+ if rel == 0 {
+ t.Fatalf("[%d] could not find symbol: %q", i, test.rel)
+ }
+ if s := ctxt.loader.SymSize(rel); s != int64(test.relsize) {
+ t.Fatalf("[%d] expected ldr.Size(%q) == %v, got %v", i, test.rel, test.relsize, s)
+ }
+ }
+ if s := ctxt.loader.SymSize(ctxt.loader.Lookup(".got", 0)); s != int64(test.gotsize) {
+ t.Fatalf(`[%d] expected ldr.Size(".got") == %v, got %v`, i, test.gotsize, s)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
new file mode 100644
index 0000000..0738a51
--- /dev/null
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -0,0 +1,469 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "internal/buildcfg"
+ "unicode"
+)
+
+var _ = fmt.Print
+
+type deadcodePass struct {
+ ctxt *Link
+ ldr *loader.Loader
+ wq heap // work queue, using min-heap for better locality
+
+ ifaceMethod map[methodsig]bool // methods called from reached interface call sites
+ genericIfaceMethod map[string]bool // names of methods called from reached generic interface call sites
+ markableMethods []methodref // methods of reached types
+ reflectSeen bool // whether we have seen a reflect method call
+ dynlink bool
+
+ methodsigstmp []methodsig // scratch buffer for decoding method signatures
+}
+
+func (d *deadcodePass) init() {
+ d.ldr.InitReachable()
+ d.ifaceMethod = make(map[methodsig]bool)
+ d.genericIfaceMethod = make(map[string]bool)
+ if buildcfg.Experiment.FieldTrack {
+ d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
+ }
+ d.dynlink = d.ctxt.DynlinkingGo()
+
+ if d.ctxt.BuildMode == BuildModeShared {
+ // Mark all symbols defined in this library as reachable when
+ // building a shared library.
+ n := d.ldr.NDef()
+ for i := 1; i < n; i++ {
+ s := loader.Sym(i)
+ d.mark(s, 0)
+ }
+ return
+ }
+
+ var names []string
+
+ // In a normal binary, start at main.main and the init
+ // functions and mark what is reachable from there.
+ if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+ names = append(names, "main.main", "main..inittask")
+ } else {
+ // The external linker refers main symbol directly.
+ if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+ if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
+ *flagEntrySymbol = "_main"
+ } else {
+ *flagEntrySymbol = "main"
+ }
+ }
+ names = append(names, *flagEntrySymbol)
+ }
+ // runtime.unreachableMethod is a function that will throw if called.
+ // We redirect unreachable methods to it.
+ names = append(names, "runtime.unreachableMethod")
+ if d.ctxt.BuildMode == BuildModePlugin {
+ names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go:plugin.tabs")
+
+ // We don't keep the go.plugin.exports symbol,
+ // but we do keep the symbols it refers to.
+ exportsIdx := d.ldr.Lookup("go:plugin.exports", 0)
+ if exportsIdx != 0 {
+ relocs := d.ldr.Relocs(exportsIdx)
+ for i := 0; i < relocs.Count(); i++ {
+ d.mark(relocs.At(i).Sym(), 0)
+ }
+ }
+ }
+
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("deadcode start names: %v\n", names)
+ }
+
+ for _, name := range names {
+ // Mark symbol as a data/ABI0 symbol.
+ d.mark(d.ldr.Lookup(name, 0), 0)
+ if abiInternalVer != 0 {
+ // Also mark any Go functions (internal ABI).
+ d.mark(d.ldr.Lookup(name, abiInternalVer), 0)
+ }
+ }
+
+ // All dynamic exports are roots.
+ for _, s := range d.ctxt.dynexp {
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("deadcode start dynexp: %s<%d>\n", d.ldr.SymName(s), d.ldr.SymVersion(s))
+ }
+ d.mark(s, 0)
+ }
+}
+
+func (d *deadcodePass) flood() {
+ var methods []methodref
+ for !d.wq.empty() {
+ symIdx := d.wq.pop()
+
+ d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
+
+ isgotype := d.ldr.IsGoType(symIdx)
+ relocs := d.ldr.Relocs(symIdx)
+ var usedInIface bool
+
+ if isgotype {
+ if d.dynlink {
+ // When dynamic linking, a type may be passed across DSO
+ // boundary and get converted to interface at the other side.
+ d.ldr.SetAttrUsedInIface(symIdx, true)
+ }
+ usedInIface = d.ldr.AttrUsedInIface(symIdx)
+ }
+
+ methods = methods[:0]
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ // When build with "-linkshared", we can't tell if the interface
+ // method in itab will be used or not. Ignore the weak attribute.
+ if r.Weak() && !(d.ctxt.linkShared && d.ldr.IsItab(symIdx)) {
+ continue
+ }
+ t := r.Type()
+ switch t {
+ case objabi.R_METHODOFF:
+ if i+2 >= relocs.Count() {
+ panic("expect three consecutive R_METHODOFF relocs")
+ }
+ if usedInIface {
+ methods = append(methods, methodref{src: symIdx, r: i})
+ // The method descriptor is itself a type descriptor, and
+ // it can be used to reach other types, e.g. by using
+ // reflect.Type.Method(i).Type.In(j). We need to traverse
+ // its child types with UsedInIface set. (See also the
+ // comment below.)
+ rs := r.Sym()
+ if !d.ldr.AttrUsedInIface(rs) {
+ d.ldr.SetAttrUsedInIface(rs, true)
+ if d.ldr.AttrReachable(rs) {
+ d.ldr.SetAttrReachable(rs, false)
+ d.mark(rs, symIdx)
+ }
+ }
+ }
+ i += 2
+ continue
+ case objabi.R_USETYPE:
+ // type symbol used for DWARF. we need to load the symbol but it may not
+ // be otherwise reachable in the program.
+ // do nothing for now as we still load all type symbols.
+ continue
+ case objabi.R_USEIFACE:
+ // R_USEIFACE is a marker relocation that tells the linker the type is
+ // converted to an interface, i.e. should have UsedInIface set. See the
+ // comment below for why we need to unset the Reachable bit and re-mark it.
+ rs := r.Sym()
+ if !d.ldr.AttrUsedInIface(rs) {
+ d.ldr.SetAttrUsedInIface(rs, true)
+ if d.ldr.AttrReachable(rs) {
+ d.ldr.SetAttrReachable(rs, false)
+ d.mark(rs, symIdx)
+ }
+ }
+ continue
+ case objabi.R_USEIFACEMETHOD:
+ // R_USEIFACEMETHOD is a marker relocation that marks an interface
+ // method as used.
+ rs := r.Sym()
+ if d.ctxt.linkShared && (d.ldr.SymType(rs) == sym.SDYNIMPORT || d.ldr.SymType(rs) == sym.Sxxx) {
+ // Don't decode symbol from shared library (we'll mark all exported methods anyway).
+ // We check for both SDYNIMPORT and Sxxx because name-mangled symbols haven't
+ // been resolved at this point.
+ continue
+ }
+ m := d.decodeIfaceMethod(d.ldr, d.ctxt.Arch, rs, r.Add())
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("reached iface method: %v\n", m)
+ }
+ d.ifaceMethod[m] = true
+ continue
+ case objabi.R_USEGENERICIFACEMETHOD:
+ name := d.decodeGenericIfaceMethod(d.ldr, r.Sym())
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("reached generic iface method: %s\n", name)
+ }
+ d.genericIfaceMethod[name] = true
+ continue // don't mark referenced symbol - it is not needed in the final binary.
+ }
+ rs := r.Sym()
+ if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) {
+ // If a type is converted to an interface, it is possible to obtain an
+ // interface with a "child" type of it using reflection (e.g. obtain an
+ // interface of T from []chan T). We need to traverse its "child" types
+ // with UsedInIface attribute set.
+ // When visiting the child type (chan T in the example above), it will
+ // have UsedInIface set, so it in turn will mark and (re)visit its children
+ // (e.g. T above).
+ // We unset the reachable bit here, so if the child type is already visited,
+ // it will be visited again.
+ // Note that a type symbol can be visited at most twice, one without
+ // UsedInIface and one with. So termination is still guaranteed.
+ d.ldr.SetAttrUsedInIface(rs, true)
+ d.ldr.SetAttrReachable(rs, false)
+ }
+ d.mark(rs, symIdx)
+ }
+ naux := d.ldr.NAux(symIdx)
+ for i := 0; i < naux; i++ {
+ a := d.ldr.Aux(symIdx, i)
+ if a.Type() == goobj.AuxGotype {
+ // A symbol being reachable doesn't imply we need its
+ // type descriptor. Don't mark it.
+ continue
+ }
+ d.mark(a.Sym(), symIdx)
+ }
+ // Some host object symbols have an outer object, which acts like a
+ // "carrier" symbol, or it holds all the symbols for a particular
+ // section. We need to mark all "referenced" symbols from that carrier,
+ // so we make sure we're pulling in all outer symbols, and their sub
+ // symbols. This is not ideal, and these carrier/section symbols could
+ // be removed.
+ if d.ldr.IsExternal(symIdx) {
+ d.mark(d.ldr.OuterSym(symIdx), symIdx)
+ d.mark(d.ldr.SubSym(symIdx), symIdx)
+ }
+
+ if len(methods) != 0 {
+ if !isgotype {
+ panic("method found on non-type symbol")
+ }
+ // Decode runtime type information for type methods
+ // to help work out which methods can be called
+ // dynamically via interfaces.
+ methodsigs := d.decodetypeMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs)
+ if len(methods) != len(methodsigs) {
+ panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
+ }
+ for i, m := range methodsigs {
+ methods[i].m = m
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("markable method: %v of sym %v %s\n", m, symIdx, d.ldr.SymName(symIdx))
+ }
+ }
+ d.markableMethods = append(d.markableMethods, methods...)
+ }
+ }
+}
+
+func (d *deadcodePass) mark(symIdx, parent loader.Sym) {
+ if symIdx != 0 && !d.ldr.AttrReachable(symIdx) {
+ d.wq.push(symIdx)
+ d.ldr.SetAttrReachable(symIdx, true)
+ if buildcfg.Experiment.FieldTrack && d.ldr.Reachparent[symIdx] == 0 {
+ d.ldr.Reachparent[symIdx] = parent
+ }
+ if *flagDumpDep {
+ to := d.ldr.SymName(symIdx)
+ if to != "" {
+ if d.ldr.AttrUsedInIface(symIdx) {
+ to += " <UsedInIface>"
+ }
+ from := "_"
+ if parent != 0 {
+ from = d.ldr.SymName(parent)
+ if d.ldr.AttrUsedInIface(parent) {
+ from += " <UsedInIface>"
+ }
+ }
+ fmt.Printf("%s -> %s\n", from, to)
+ }
+ }
+ }
+}
+
+func (d *deadcodePass) markMethod(m methodref) {
+ relocs := d.ldr.Relocs(m.src)
+ d.mark(relocs.At(m.r).Sym(), m.src)
+ d.mark(relocs.At(m.r+1).Sym(), m.src)
+ d.mark(relocs.At(m.r+2).Sym(), m.src)
+}
+
+// deadcode marks all reachable symbols.
+//
+// The basis of the dead code elimination is a flood fill of symbols,
+// following their relocations, beginning at *flagEntrySymbol.
+//
+// This flood fill is wrapped in logic for pruning unused methods.
+// All methods are mentioned by relocations on their receiver's *rtype.
+// These relocations are specially defined as R_METHODOFF by the compiler
+// so we can detect and manipulated them here.
+//
+// There are three ways a method of a reachable type can be invoked:
+//
+// 1. direct call
+// 2. through a reachable interface type
+// 3. reflect.Value.Method (or MethodByName), or reflect.Type.Method
+// (or MethodByName)
+//
+// The first case is handled by the flood fill, a directly called method
+// is marked as reachable.
+//
+// The second case is handled by decomposing all reachable interface
+// types into method signatures. Each encountered method is compared
+// against the interface method signatures, if it matches it is marked
+// as reachable. This is extremely conservative, but easy and correct.
+//
+// The third case is handled by looking to see if any of:
+// - reflect.Value.Method or MethodByName is reachable
+// - reflect.Type.Method or MethodByName is called (through the
+// REFLECTMETHOD attribute marked by the compiler).
+//
+// If any of these happen, all bets are off and all exported methods
+// of reachable types are marked reachable.
+//
+// Any unreached text symbols are removed from ctxt.Textp.
+func deadcode(ctxt *Link) {
+ ldr := ctxt.loader
+ d := deadcodePass{ctxt: ctxt, ldr: ldr}
+ d.init()
+ d.flood()
+
+ methSym := ldr.Lookup("reflect.Value.Method", abiInternalVer)
+ methByNameSym := ldr.Lookup("reflect.Value.MethodByName", abiInternalVer)
+
+ if ctxt.DynlinkingGo() {
+ // Exported methods may satisfy interfaces we don't know
+ // about yet when dynamically linking.
+ d.reflectSeen = true
+ }
+
+ for {
+ // Methods might be called via reflection. Give up on
+ // static analysis, mark all exported methods of
+ // all reachable types as reachable.
+ d.reflectSeen = d.reflectSeen || (methSym != 0 && ldr.AttrReachable(methSym)) || (methByNameSym != 0 && ldr.AttrReachable(methByNameSym))
+
+ // Mark all methods that could satisfy a discovered
+ // interface as reachable. We recheck old marked interfaces
+ // as new types (with new methods) may have been discovered
+ // in the last pass.
+ rem := d.markableMethods[:0]
+ for _, m := range d.markableMethods {
+ if (d.reflectSeen && (m.isExported() || d.dynlink)) || d.ifaceMethod[m.m] || d.genericIfaceMethod[m.m.name] {
+ d.markMethod(m)
+ } else {
+ rem = append(rem, m)
+ }
+ }
+ d.markableMethods = rem
+
+ if d.wq.empty() {
+ // No new work was discovered. Done.
+ break
+ }
+ d.flood()
+ }
+}
+
+// methodsig is a typed method signature (name + type).
+type methodsig struct {
+ name string
+ typ loader.Sym // type descriptor symbol of the function
+}
+
+// methodref holds the relocations from a receiver type symbol to its
+// method. There are three relocations, one for each of the fields in
+// the reflect.method struct: mtyp, ifn, and tfn.
+type methodref struct {
+ m methodsig
+ src loader.Sym // receiver type symbol
+ r int // the index of R_METHODOFF relocations
+}
+
+func (m methodref) isExported() bool {
+ for _, r := range m.m.name {
+ return unicode.IsUpper(r)
+ }
+ panic("methodref has no signature")
+}
+
+// decodeMethodSig decodes an array of method signature information.
+// Each element of the array is size bytes. The first 4 bytes is a
+// nameOff for the method name, and the next 4 bytes is a typeOff for
+// the function type.
+//
+// Conveniently this is the layout of both runtime.method and runtime.imethod.
+func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig {
+ if cap(d.methodsigstmp) < count {
+ d.methodsigstmp = append(d.methodsigstmp[:0], make([]methodsig, count)...)
+ }
+ var methods = d.methodsigstmp[:count]
+ for i := 0; i < count; i++ {
+ methods[i].name = decodetypeName(ldr, symIdx, relocs, off)
+ methods[i].typ = decodeRelocSym(ldr, symIdx, relocs, int32(off+4))
+ off += size
+ }
+ return methods
+}
+
+// Decode the method of interface type symbol symIdx at offset off.
+func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig {
+ p := ldr.Data(symIdx)
+ if p == nil {
+ panic(fmt.Sprintf("missing symbol %q", ldr.SymName(symIdx)))
+ }
+ if decodetypeKind(arch, p)&kindMask != kindInterface {
+ panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
+ }
+ relocs := ldr.Relocs(symIdx)
+ var m methodsig
+ m.name = decodetypeName(ldr, symIdx, &relocs, int(off))
+ m.typ = decodeRelocSym(ldr, symIdx, &relocs, int32(off+4))
+ return m
+}
+
+// Decode the method name stored in symbol symIdx. The symbol should contain just the bytes of a method name.
+func (d *deadcodePass) decodeGenericIfaceMethod(ldr *loader.Loader, symIdx loader.Sym) string {
+ return string(ldr.Data(symIdx))
+}
+
+func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
+ p := ldr.Data(symIdx)
+ if !decodetypeHasUncommon(arch, p) {
+ panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
+ }
+ off := commonsize(arch) // reflect.rtype
+ switch decodetypeKind(arch, p) & kindMask {
+ case kindStruct: // reflect.structType
+ off += 4 * arch.PtrSize
+ case kindPtr: // reflect.ptrType
+ off += arch.PtrSize
+ case kindFunc: // reflect.funcType
+ off += arch.PtrSize // 4 bytes, pointer aligned
+ case kindSlice: // reflect.sliceType
+ off += arch.PtrSize
+ case kindArray: // reflect.arrayType
+ off += 3 * arch.PtrSize
+ case kindChan: // reflect.chanType
+ off += 2 * arch.PtrSize
+ case kindMap: // reflect.mapType
+ off += 4*arch.PtrSize + 8
+ case kindInterface: // reflect.interfaceType
+ off += 3 * arch.PtrSize
+ default:
+ // just Sizeof(rtype)
+ }
+
+ mcount := int(decodeInuxi(arch, p[off+4:], 2))
+ moff := int(decodeInuxi(arch, p[off+4+2+2:], 4))
+ off += moff // offset to array of reflect.method values
+ const sizeofMethod = 4 * 4 // sizeof reflect.method in program
+ return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
+}
diff --git a/src/cmd/link/internal/ld/deadcode_test.go b/src/cmd/link/internal/ld/deadcode_test.go
new file mode 100644
index 0000000..573bff3
--- /dev/null
+++ b/src/cmd/link/internal/ld/deadcode_test.go
@@ -0,0 +1,50 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "bytes"
+ "internal/testenv"
+ "path/filepath"
+ "testing"
+)
+
+func TestDeadcode(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tmpdir := t.TempDir()
+
+ tests := []struct {
+ src string
+ pos, neg string // positive and negative patterns
+ }{
+ {"reflectcall", "", "main.T.M"},
+ {"typedesc", "", "type:main.T"},
+ {"ifacemethod", "", "main.T.M"},
+ {"ifacemethod2", "main.T.M", ""},
+ {"ifacemethod3", "main.S.M", ""},
+ {"ifacemethod4", "", "main.T.M"},
+ }
+ for _, test := range tests {
+ test := test
+ t.Run(test.src, func(t *testing.T) {
+ t.Parallel()
+ src := filepath.Join("testdata", "deadcode", test.src+".go")
+ exe := filepath.Join(tmpdir, test.src+".exe")
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-dumpdep", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+ }
+ if test.pos != "" && !bytes.Contains(out, []byte(test.pos+"\n")) {
+ t.Errorf("%s should be reachable. Output:\n%s", test.pos, out)
+ }
+ if test.neg != "" && bytes.Contains(out, []byte(test.neg+"\n")) {
+ t.Errorf("%s should not be reachable. Output:\n%s", test.neg, out)
+ }
+ })
+ }
+}
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
new file mode 100644
index 0000000..b0f4b87
--- /dev/null
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -0,0 +1,310 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "log"
+)
+
+// Decoding the type.* symbols. This has to be in sync with
+// ../../runtime/type.go, or more specifically, with what
+// cmd/compile/internal/reflectdata/reflect.go stuffs in these.
+
+// tflag is documented in reflect/type.go.
+//
+// tflag values must be kept in sync with copies in:
+//
+// cmd/compile/internal/reflectdata/reflect.go
+// cmd/link/internal/ld/decodesym.go
+// reflect/type.go
+// runtime/type.go
+const (
+ tflagUncommon = 1 << 0
+ tflagExtraStar = 1 << 1
+)
+
+func decodeInuxi(arch *sys.Arch, p []byte, sz int) uint64 {
+ switch sz {
+ case 2:
+ return uint64(arch.ByteOrder.Uint16(p))
+ case 4:
+ return uint64(arch.ByteOrder.Uint32(p))
+ case 8:
+ return arch.ByteOrder.Uint64(p)
+ default:
+ Exitf("dwarf: decode inuxi %d", sz)
+ panic("unreachable")
+ }
+}
+
+func commonsize(arch *sys.Arch) int { return 4*arch.PtrSize + 8 + 8 } // runtime._type
+func structfieldSize(arch *sys.Arch) int { return 3 * arch.PtrSize } // runtime.structfield
+func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype
+
+// Type.commonType.kind
+func decodetypeKind(arch *sys.Arch, p []byte) uint8 {
+ return p[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f
+}
+
+// Type.commonType.kind
+func decodetypeUsegcprog(arch *sys.Arch, p []byte) uint8 {
+ return p[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f
+}
+
+// Type.commonType.size
+func decodetypeSize(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p, arch.PtrSize)) // 0x8 / 0x10
+}
+
+// Type.commonType.ptrdata
+func decodetypePtrdata(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10
+}
+
+// Type.commonType.tflag
+func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool {
+ return p[2*arch.PtrSize+4]&tflagUncommon != 0
+}
+
+// Type.FuncType.dotdotdot
+func decodetypeFuncDotdotdot(arch *sys.Arch, p []byte) bool {
+ return uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2))&(1<<15) != 0
+}
+
+// Type.FuncType.inCount
+func decodetypeFuncInCount(arch *sys.Arch, p []byte) int {
+ return int(decodeInuxi(arch, p[commonsize(arch):], 2))
+}
+
+func decodetypeFuncOutCount(arch *sys.Arch, p []byte) int {
+ return int(uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2)) & (1<<15 - 1))
+}
+
+// InterfaceType.methods.length
+func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+// Matches runtime/typekind.go and reflect.Kind.
+const (
+ kindArray = 17
+ kindChan = 18
+ kindFunc = 19
+ kindInterface = 20
+ kindMap = 21
+ kindPtr = 22
+ kindSlice = 23
+ kindStruct = 25
+ kindMask = (1 << 5) - 1
+)
+
+func decodeReloc(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc {
+ for j := 0; j < relocs.Count(); j++ {
+ rel := relocs.At(j)
+ if rel.Off() == off {
+ return rel
+ }
+ }
+ return loader.Reloc{}
+}
+
+func decodeRelocSym(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym {
+ return decodeReloc(ldr, symIdx, relocs, off).Sym()
+}
+
+// decodetypeName decodes the name from a reflect.name.
+func decodetypeName(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string {
+ r := decodeRelocSym(ldr, symIdx, relocs, int32(off))
+ if r == 0 {
+ return ""
+ }
+
+ data := ldr.Data(r)
+ nameLen, nameLenLen := binary.Uvarint(data[1:])
+ return string(data[1+nameLenLen : 1+nameLenLen+int(nameLen)])
+}
+
+func decodetypeNameEmbedded(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) bool {
+ r := decodeRelocSym(ldr, symIdx, relocs, int32(off))
+ if r == 0 {
+ return false
+ }
+ data := ldr.Data(r)
+ return data[0]&(1<<3) != 0
+}
+
+func decodetypeFuncInType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
+ uadd := commonsize(arch) + 4
+ if arch.PtrSize == 8 {
+ uadd += 4
+ }
+ if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
+ uadd += uncommonSize()
+ }
+ return decodeRelocSym(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize))
+}
+
+func decodetypeFuncOutType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
+ return decodetypeFuncInType(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
+}
+
+func decodetypeArrayElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeArrayLen(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int64 {
+ data := ldr.Data(symIdx)
+ return int64(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeChanElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapKey(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapValue(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38
+}
+
+func decodetypePtrElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeStructFieldCount(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int {
+ data := ldr.Data(symIdx)
+ return int(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeStructFieldArrayOff(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int {
+ data := ldr.Data(symIdx)
+ off := commonsize(arch) + 4*arch.PtrSize
+ if decodetypeHasUncommon(arch, data) {
+ off += uncommonSize()
+ }
+ off += i * structfieldSize(arch)
+ return off
+}
+
+func decodetypeStructFieldName(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) string {
+ off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
+ relocs := ldr.Relocs(symIdx)
+ return decodetypeName(ldr, symIdx, &relocs, off)
+}
+
+func decodetypeStructFieldType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym {
+ off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(off+arch.PtrSize))
+}
+
+func decodetypeStructFieldOffset(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 {
+ off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
+ data := ldr.Data(symIdx)
+ return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeStructFieldEmbedded(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) bool {
+ off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
+ relocs := ldr.Relocs(symIdx)
+ return decodetypeNameEmbedded(ldr, symIdx, &relocs, off)
+}
+
+// decodetypeStr returns the contents of an rtype's str field (a nameOff).
+func decodetypeStr(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) string {
+ relocs := ldr.Relocs(symIdx)
+ str := decodetypeName(ldr, symIdx, &relocs, 4*arch.PtrSize+8)
+ data := ldr.Data(symIdx)
+ if data[2*arch.PtrSize+4]&tflagExtraStar != 0 {
+ return str[1:]
+ }
+ return str
+}
+
+func decodetypeGcmask(ctxt *Link, s loader.Sym) []byte {
+ if ctxt.loader.SymType(s) == sym.SDYNIMPORT {
+ symData := ctxt.loader.Data(s)
+ addr := decodetypeGcprogShlib(ctxt, symData)
+ ptrdata := decodetypePtrdata(ctxt.Arch, symData)
+ sect := findShlibSection(ctxt, ctxt.loader.SymPkg(s), addr)
+ if sect != nil {
+ bits := ptrdata / int64(ctxt.Arch.PtrSize)
+ r := make([]byte, (bits+7)/8)
+ // ldshlibsyms avoids closing the ELF file so sect.ReadAt works.
+ // If we remove this read (and the ones in decodetypeGcprog), we
+ // can close the file.
+ _, err := sect.ReadAt(r, int64(addr-sect.Addr))
+ if err != nil {
+ log.Fatal(err)
+ }
+ return r
+ }
+ Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s))
+ return nil
+ }
+ relocs := ctxt.loader.Relocs(s)
+ mask := decodeRelocSym(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize))
+ return ctxt.loader.Data(mask)
+}
+
+// Type.commonType.gc
+func decodetypeGcprog(ctxt *Link, s loader.Sym) []byte {
+ if ctxt.loader.SymType(s) == sym.SDYNIMPORT {
+ symData := ctxt.loader.Data(s)
+ addr := decodetypeGcprogShlib(ctxt, symData)
+ sect := findShlibSection(ctxt, ctxt.loader.SymPkg(s), addr)
+ if sect != nil {
+ // A gcprog is a 4-byte uint32 indicating length, followed by
+ // the actual program.
+ progsize := make([]byte, 4)
+ _, err := sect.ReadAt(progsize, int64(addr-sect.Addr))
+ if err != nil {
+ log.Fatal(err)
+ }
+ progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize))
+ _, err = sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
+ if err != nil {
+ log.Fatal(err)
+ }
+ return append(progsize, progbytes...)
+ }
+ Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s))
+ return nil
+ }
+ relocs := ctxt.loader.Relocs(s)
+ rs := decodeRelocSym(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize))
+ return ctxt.loader.Data(rs)
+}
+
+// Find the elf.Section of a given shared library that contains a given address.
+func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section {
+ for _, shlib := range ctxt.Shlibs {
+ if shlib.Path == path {
+ for _, sect := range shlib.File.Sections[1:] { // skip the NULL section
+ if sect.Addr <= addr && addr < sect.Addr+sect.Size {
+ return sect
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func decodetypeGcprogShlib(ctxt *Link, data []byte) uint64 {
+ return decodeInuxi(ctxt.Arch, data[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
+}
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
new file mode 100644
index 0000000..feea864
--- /dev/null
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -0,0 +1,2328 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO/NICETOHAVE:
+// - eliminate DW_CLS_ if not used
+// - package info in compilation units
+// - assign types to their packages
+// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg
+// ptype struct '[]uint8' and qualifiers need to be quoted away
+// - file:line info for variables
+// - make strings a typedef so prettyprinters can see the underlying string type
+
+package ld
+
+import (
+ "cmd/internal/dwarf"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "internal/buildcfg"
+ "log"
+ "path"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+)
+
+// dwctxt is a wrapper intended to satisfy the method set of
+// dwarf.Context, so that functions like dwarf.PutAttrs will work with
+// DIEs that use loader.Sym as opposed to *sym.Symbol. It is also
+// being used as a place to store tables/maps that are useful as part
+// of type conversion (this is just a convenience; it would be easy to
+// split these things out into another type if need be).
+type dwctxt struct {
+ linkctxt *Link
+ ldr *loader.Loader
+ arch *sys.Arch
+
+ // This maps type name string (e.g. "uintptr") to loader symbol for
+ // the DWARF DIE for that type (e.g. "go:info.type.uintptr")
+ tmap map[string]loader.Sym
+
+ // This maps loader symbol for the DWARF DIE symbol generated for
+ // a type (e.g. "go:info.uintptr") to the type symbol itself
+ // ("type:uintptr").
+ // FIXME: try converting this map (and the next one) to a single
+ // array indexed by loader.Sym -- this may perform better.
+ rtmap map[loader.Sym]loader.Sym
+
+ // This maps Go type symbol (e.g. "type:XXX") to loader symbol for
+ // the typedef DIE for that type (e.g. "go:info.XXX..def")
+ tdmap map[loader.Sym]loader.Sym
+
+ // Cache these type symbols, so as to avoid repeatedly looking them up
+ typeRuntimeEface loader.Sym
+ typeRuntimeIface loader.Sym
+ uintptrInfoSym loader.Sym
+
+ // Used at various points in that parallel portion of DWARF gen to
+ // protect against conflicting updates to globals (such as "gdbscript")
+ dwmu *sync.Mutex
+}
+
+// dwSym wraps a loader.Sym; this type is meant to obey the interface
+// rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and
+// DwAttr objects contain references to symbols via this type.
+type dwSym loader.Sym
+
+func (s dwSym) Length(dwarfContext interface{}) int64 {
+ l := dwarfContext.(dwctxt).ldr
+ return int64(len(l.Data(loader.Sym(s))))
+}
+
+func (c dwctxt) PtrSize() int {
+ return c.arch.PtrSize
+}
+
+func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.AddUintXX(c.arch, uint64(i), size)
+}
+
+func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.AddBytes(b)
+}
+
+func (c dwctxt) AddString(s dwarf.Sym, v string) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.Addstring(v)
+}
+
+func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ if value != 0 {
+ value -= dsu.Value()
+ }
+ tgtds := loader.Sym(data.(dwSym))
+ dsu.AddAddrPlus(c.arch, tgtds, value)
+}
+
+func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ if value != 0 {
+ value -= dsu.Value()
+ }
+ tgtds := loader.Sym(data.(dwSym))
+ dsu.AddCURelativeAddrPlus(c.arch, tgtds, value)
+}
+
+func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ tds := loader.Sym(t.(dwSym))
+ switch size {
+ default:
+ c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size)
+ case c.arch.PtrSize, 4:
+ }
+ dsu.AddSymRef(c.arch, tds, ofs, objabi.R_ADDROFF, size)
+}
+
+func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
+ size := 4
+ if isDwarf64(c.linkctxt) {
+ size = 8
+ }
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ tds := loader.Sym(t.(dwSym))
+ switch size {
+ default:
+ c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size)
+ case c.arch.PtrSize, 4:
+ }
+ dsu.AddSymRef(c.arch, tds, ofs, objabi.R_DWARFSECREF, size)
+}
+
+func (c dwctxt) Logf(format string, args ...interface{}) {
+ c.linkctxt.Logf(format, args...)
+}
+
+// At the moment these interfaces are only used in the compiler.
+
+func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) {
+ panic("should be used only in the compiler")
+}
+
+func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 {
+ panic("should be used only in the compiler")
+}
+
+func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) {
+ panic("should be used only in the compiler")
+}
+
+func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
+ panic("should be used only in the compiler")
+}
+
+func isDwarf64(ctxt *Link) bool {
+ return ctxt.HeadType == objabi.Haix
+}
+
+// https://sourceware.org/gdb/onlinedocs/gdb/dotdebug_005fgdb_005fscripts-section.html
+// Each entry inside .debug_gdb_scripts section begins with a non-null prefix
+// byte that specifies the kind of entry. The following entries are supported:
+const (
+ GdbScriptPythonFileId = 1
+ GdbScriptSchemeFileId = 3
+ GdbScriptPythonTextId = 4
+ GdbScriptSchemeTextId = 6
+)
+
+var gdbscript string
+
+// dwarfSecInfo holds information about a DWARF output section,
+// specifically a section symbol and a list of symbols contained in
+// that section. On the syms list, the first symbol will always be the
+// section symbol, then any remaining symbols (if any) will be
+// sub-symbols in that section. Note that for some sections (eg:
+// .debug_abbrev), the section symbol is all there is (all content is
+// contained in it). For other sections (eg: .debug_info), the section
+// symbol is empty and all the content is in the sub-symbols. Finally
+// there are some sections (eg: .debug_ranges) where it is a mix (both
+// the section symbol and the sub-symbols have content)
+type dwarfSecInfo struct {
+ syms []loader.Sym
+}
+
+// secSym returns the section symbol for the section.
+func (dsi *dwarfSecInfo) secSym() loader.Sym {
+ if len(dsi.syms) == 0 {
+ return 0
+ }
+ return dsi.syms[0]
+}
+
+// subSyms returns a list of sub-symbols for the section.
+func (dsi *dwarfSecInfo) subSyms() []loader.Sym {
+ if len(dsi.syms) == 0 {
+ return []loader.Sym{}
+ }
+ return dsi.syms[1:]
+}
+
+// dwarfp stores the collected DWARF symbols created during
+// dwarf generation.
+var dwarfp []dwarfSecInfo
+
+func (d *dwctxt) writeabbrev() dwarfSecInfo {
+ abrvs := d.ldr.CreateSymForUpdate(".debug_abbrev", 0)
+ abrvs.SetType(sym.SDWARFSECT)
+ abrvs.AddBytes(dwarf.GetAbbrev())
+ return dwarfSecInfo{syms: []loader.Sym{abrvs.Sym()}}
+}
+
+var dwtypes dwarf.DWDie
+
+// newattr attaches a new attribute to the specified DIE.
+//
+// FIXME: at the moment attributes are stored in a linked list in a
+// fairly space-inefficient way -- it might be better to instead look
+// up all attrs in a single large table, then store indices into the
+// table in the DIE. This would allow us to common up storage for
+// attributes that are shared by many DIEs (ex: byte size of N).
+func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) {
+ a := new(dwarf.DWAttr)
+ a.Link = die.Attr
+ die.Attr = a
+ a.Atr = attr
+ a.Cls = uint8(cls)
+ a.Value = value
+ a.Data = data
+}
+
+// Each DIE (except the root ones) has at least 1 attribute: its
+// name. getattr moves the desired one to the front so
+// frequently searched ones are found faster.
+func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr {
+ if die.Attr.Atr == attr {
+ return die.Attr
+ }
+
+ a := die.Attr
+ b := a.Link
+ for b != nil {
+ if b.Atr == attr {
+ a.Link = b.Link
+ b.Link = die.Attr
+ die.Attr = b
+ return b
+ }
+
+ a = b
+ b = b.Link
+ }
+
+ return nil
+}
+
+// Every DIE manufactured by the linker has at least an AT_name
+// attribute (but it will only be written out if it is listed in the abbrev).
+// The compiler does create nameless DWARF DIEs (ex: concrete subprogram
+// instance).
+// FIXME: it would be more efficient to bulk-allocate DIEs.
+func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string) *dwarf.DWDie {
+ die := new(dwarf.DWDie)
+ die.Abbrev = abbrev
+ die.Link = parent.Child
+ parent.Child = die
+
+ newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name)
+
+ // Sanity check: all DIEs created in the linker should be named.
+ if name == "" {
+ panic("nameless DWARF DIE")
+ }
+
+ var st sym.SymKind
+ switch abbrev {
+ case dwarf.DW_ABRV_FUNCTYPEPARAM, dwarf.DW_ABRV_DOTDOTDOT, dwarf.DW_ABRV_STRUCTFIELD, dwarf.DW_ABRV_ARRAYRANGE:
+ // There are no relocations against these dies, and their names
+ // are not unique, so don't create a symbol.
+ return die
+ case dwarf.DW_ABRV_COMPUNIT, dwarf.DW_ABRV_COMPUNIT_TEXTLESS:
+ // Avoid collisions with "real" symbol names.
+ name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits))
+ st = sym.SDWARFCUINFO
+ case dwarf.DW_ABRV_VARIABLE:
+ st = sym.SDWARFVAR
+ default:
+ // Everything else is assigned a type of SDWARFTYPE. that
+ // this also includes loose ends such as STRUCT_FIELD.
+ st = sym.SDWARFTYPE
+ }
+ ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, 0)
+ dsu := d.ldr.MakeSymbolUpdater(ds)
+ dsu.SetType(st)
+ d.ldr.SetAttrNotInSymbolTable(ds, true)
+ d.ldr.SetAttrReachable(ds, true)
+ die.Sym = dwSym(ds)
+ if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL {
+ d.tmap[name] = ds
+ }
+
+ return die
+}
+
+func walktypedef(die *dwarf.DWDie) *dwarf.DWDie {
+ if die == nil {
+ return nil
+ }
+ // Resolve typedef if present.
+ if die.Abbrev == dwarf.DW_ABRV_TYPEDECL {
+ for attr := die.Attr; attr != nil; attr = attr.Link {
+ if attr.Atr == dwarf.DW_AT_type && attr.Cls == dwarf.DW_CLS_REFERENCE && attr.Data != nil {
+ return attr.Data.(*dwarf.DWDie)
+ }
+ }
+ }
+
+ return die
+}
+
+func (d *dwctxt) walksymtypedef(symIdx loader.Sym) loader.Sym {
+
+ // We're being given the loader symbol for the type DIE, e.g.
+ // "go:info.type.uintptr". Map that first to the type symbol (e.g.
+ // "type:uintptr") and then to the typedef DIE for the type.
+ // FIXME: this seems clunky, maybe there is a better way to do this.
+
+ if ts, ok := d.rtmap[symIdx]; ok {
+ if def, ok := d.tdmap[ts]; ok {
+ return def
+ }
+ d.linkctxt.Errorf(ts, "internal error: no entry for sym %d in tdmap\n", ts)
+ return 0
+ }
+ d.linkctxt.Errorf(symIdx, "internal error: no entry for sym %d in rtmap\n", symIdx)
+ return 0
+}
+
+// Find child by AT_name using hashtable if available or linear scan
+// if not.
+func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie {
+ var prev *dwarf.DWDie
+ for ; die != prev; prev, die = die, walktypedef(die) {
+ for a := die.Child; a != nil; a = a.Link {
+ if name == getattr(a, dwarf.DW_AT_name).Data {
+ return a
+ }
+ }
+ continue
+ }
+ return nil
+}
+
+// find looks up the loader symbol for the DWARF DIE generated for the
+// type with the specified name.
+func (d *dwctxt) find(name string) loader.Sym {
+ return d.tmap[name]
+}
+
+func (d *dwctxt) mustFind(name string) loader.Sym {
+ r := d.find(name)
+ if r == 0 {
+ Exitf("dwarf find: cannot find %s", name)
+ }
+ return r
+}
+
+func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) {
+ switch size {
+ default:
+ d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size)
+ case d.arch.PtrSize, 4:
+ }
+ sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size)
+}
+
+func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) {
+ if ref == 0 {
+ return
+ }
+ newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref))
+}
+
+func (d *dwctxt) dtolsym(s dwarf.Sym) loader.Sym {
+ if s == nil {
+ return 0
+ }
+ dws := loader.Sym(s.(dwSym))
+ return dws
+}
+
+func (d *dwctxt) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym {
+ s := d.dtolsym(die.Sym)
+ if s == 0 {
+ s = syms[len(syms)-1]
+ } else {
+ syms = append(syms, s)
+ }
+ sDwsym := dwSym(s)
+ dwarf.Uleb128put(d, sDwsym, int64(die.Abbrev))
+ dwarf.PutAttrs(d, sDwsym, die.Abbrev, die.Attr)
+ if dwarf.HasChildren(die) {
+ for die := die.Child; die != nil; die = die.Link {
+ syms = d.putdie(syms, die)
+ }
+ dsu := d.ldr.MakeSymbolUpdater(syms[len(syms)-1])
+ dsu.AddUint8(0)
+ }
+ return syms
+}
+
+func reverselist(list **dwarf.DWDie) {
+ curr := *list
+ var prev *dwarf.DWDie
+ for curr != nil {
+ next := curr.Link
+ curr.Link = prev
+ prev = curr
+ curr = next
+ }
+
+ *list = prev
+}
+
+func reversetree(list **dwarf.DWDie) {
+ reverselist(list)
+ for die := *list; die != nil; die = die.Link {
+ if dwarf.HasChildren(die) {
+ reversetree(&die.Child)
+ }
+ }
+}
+
+func newmemberoffsetattr(die *dwarf.DWDie, offs int32) {
+ newattr(die, dwarf.DW_AT_data_member_location, dwarf.DW_CLS_CONSTANT, int64(offs), nil)
+}
+
+func (d *dwctxt) lookupOrDiag(n string) loader.Sym {
+ symIdx := d.ldr.Lookup(n, 0)
+ if symIdx == 0 {
+ Exitf("dwarf: missing type: %s", n)
+ }
+ if len(d.ldr.Data(symIdx)) == 0 {
+ Exitf("dwarf: missing type (no data): %s", n)
+ }
+
+ return symIdx
+}
+
+func (d *dwctxt) dotypedef(parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie {
+ // Only emit typedefs for real names.
+ if strings.HasPrefix(name, "map[") {
+ return nil
+ }
+ if strings.HasPrefix(name, "struct {") {
+ return nil
+ }
+ // cmd/compile uses "noalg.struct {...}" as type name when hash and eq algorithm generation of
+ // this struct type is suppressed.
+ if strings.HasPrefix(name, "noalg.struct {") {
+ return nil
+ }
+ if strings.HasPrefix(name, "chan ") {
+ return nil
+ }
+ if name[0] == '[' || name[0] == '*' {
+ return nil
+ }
+ if def == nil {
+ Errorf(nil, "dwarf: bad def in dotypedef")
+ }
+
+ // Create a new loader symbol for the typedef. We no longer
+ // do lookups of typedef symbols by name, so this is going
+ // to be an anonymous symbol (we want this for perf reasons).
+ tds := d.ldr.CreateExtSym("", 0)
+ tdsu := d.ldr.MakeSymbolUpdater(tds)
+ tdsu.SetType(sym.SDWARFTYPE)
+ def.Sym = dwSym(tds)
+ d.ldr.SetAttrNotInSymbolTable(tds, true)
+ d.ldr.SetAttrReachable(tds, true)
+
+ // The typedef entry must be created after the def,
+ // so that future lookups will find the typedef instead
+ // of the real definition. This hooks the typedef into any
+ // circular definition loops, so that gdb can understand them.
+ die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name)
+
+ d.newrefattr(die, dwarf.DW_AT_type, tds)
+
+ return die
+}
+
+// Define gotype, for composite ones recurse into constituents.
+func (d *dwctxt) defgotype(gotype loader.Sym) loader.Sym {
+ if gotype == 0 {
+ return d.mustFind("<unspecified>")
+ }
+
+ // If we already have a tdmap entry for the gotype, return it.
+ if ds, ok := d.tdmap[gotype]; ok {
+ return ds
+ }
+
+ sn := d.ldr.SymName(gotype)
+ if !strings.HasPrefix(sn, "type:") {
+ d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type:\"")
+ return d.mustFind("<unspecified>")
+ }
+ name := sn[5:] // could also decode from Type.string
+
+ sdie := d.find(name)
+ if sdie != 0 {
+ return sdie
+ }
+
+ gtdwSym := d.newtype(gotype)
+ d.tdmap[gotype] = loader.Sym(gtdwSym.Sym.(dwSym))
+ return loader.Sym(gtdwSym.Sym.(dwSym))
+}
+
+func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie {
+ sn := d.ldr.SymName(gotype)
+ name := sn[5:] // could also decode from Type.string
+ tdata := d.ldr.Data(gotype)
+ if len(tdata) == 0 {
+ d.linkctxt.Errorf(gotype, "missing type")
+ }
+ kind := decodetypeKind(d.arch, tdata)
+ bytesize := decodetypeSize(d.arch, tdata)
+
+ var die, typedefdie *dwarf.DWDie
+ switch kind {
+ case objabi.KindBool:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindInt,
+ objabi.KindInt8,
+ objabi.KindInt16,
+ objabi.KindInt32,
+ objabi.KindInt64:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindUint,
+ objabi.KindUint8,
+ objabi.KindUint16,
+ objabi.KindUint32,
+ objabi.KindUint64,
+ objabi.KindUintptr:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindFloat32,
+ objabi.KindFloat64:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindComplex64,
+ objabi.KindComplex128:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindArray:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ s := decodetypeArrayElem(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
+ fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range")
+
+ // use actual length not upper bound; correct for 0-length arrays.
+ newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(d.ldr, d.arch, gotype), 0)
+
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+
+ case objabi.KindChan:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name)
+ s := decodetypeChanElem(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
+ // Save elem type for synthesizechantypes. We could synthesize here
+ // but that would change the order of DIEs we output.
+ d.newrefattr(die, dwarf.DW_AT_type, s)
+
+ case objabi.KindFunc:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ data := d.ldr.Data(gotype)
+ // FIXME: add caching or reuse reloc slice.
+ relocs := d.ldr.Relocs(gotype)
+ nfields := decodetypeFuncInCount(d.arch, data)
+ for i := 0; i < nfields; i++ {
+ s := decodetypeFuncInType(d.ldr, d.arch, gotype, &relocs, i)
+ sn := d.ldr.SymName(s)
+ fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:])
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
+ }
+
+ if decodetypeFuncDotdotdot(d.arch, data) {
+ d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...")
+ }
+ nfields = decodetypeFuncOutCount(d.arch, data)
+ for i := 0; i < nfields; i++ {
+ s := decodetypeFuncOutType(d.ldr, d.arch, gotype, &relocs, i)
+ sn := d.ldr.SymName(s)
+ fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:])
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s)))
+ }
+
+ case objabi.KindInterface:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ data := d.ldr.Data(gotype)
+ nfields := int(decodetypeIfaceMethodCount(d.arch, data))
+ var s loader.Sym
+ if nfields == 0 {
+ s = d.typeRuntimeEface
+ } else {
+ s = d.typeRuntimeIface
+ }
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
+
+ case objabi.KindMap:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name)
+ s := decodetypeMapKey(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s))
+ s = decodetypeMapValue(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
+ // Save gotype for use in synthesizemaptypes. We could synthesize here,
+ // but that would change the order of the DIEs.
+ d.newrefattr(die, dwarf.DW_AT_type, gotype)
+
+ case objabi.KindPtr:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ s := decodetypePtrElem(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
+
+ case objabi.KindSlice:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ s := decodetypeArrayElem(d.ldr, d.arch, gotype)
+ elem := d.defgotype(s)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, elem)
+
+ case objabi.KindString:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindStruct:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ nfields := decodetypeStructFieldCount(d.ldr, d.arch, gotype)
+ for i := 0; i < nfields; i++ {
+ f := decodetypeStructFieldName(d.ldr, d.arch, gotype, i)
+ s := decodetypeStructFieldType(d.ldr, d.arch, gotype, i)
+ if f == "" {
+ sn := d.ldr.SymName(s)
+ f = sn[5:] // skip "type:"
+ }
+ fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
+ offset := decodetypeStructFieldOffset(d.ldr, d.arch, gotype, i)
+ newmemberoffsetattr(fld, int32(offset))
+ if decodetypeStructFieldEmbedded(d.ldr, d.arch, gotype, i) {
+ newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0)
+ }
+ }
+
+ case objabi.KindUnsafePointer:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name)
+
+ default:
+ d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name)
+ d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("<unspecified>"))
+ }
+
+ newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0)
+
+ if d.ldr.AttrReachable(gotype) {
+ newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gotype))
+ }
+
+ // Sanity check.
+ if _, ok := d.rtmap[gotype]; ok {
+ log.Fatalf("internal error: rtmap entry already installed\n")
+ }
+
+ ds := loader.Sym(die.Sym.(dwSym))
+ if typedefdie != nil {
+ ds = loader.Sym(typedefdie.Sym.(dwSym))
+ }
+ d.rtmap[ds] = gotype
+
+ if _, ok := prototypedies[sn]; ok {
+ prototypedies[sn] = die
+ }
+
+ if typedefdie != nil {
+ return typedefdie
+ }
+ return die
+}
+
+func (d *dwctxt) nameFromDIESym(dwtypeDIESym loader.Sym) string {
+ sn := d.ldr.SymName(dwtypeDIESym)
+ return sn[len(dwarf.InfoPrefix):]
+}
+
+func (d *dwctxt) defptrto(dwtype loader.Sym) loader.Sym {
+
+ // FIXME: it would be nice if the compiler attached an aux symbol
+ // ref from the element type to the pointer type -- it would be
+ // more efficient to do it this way as opposed to via name lookups.
+
+ ptrname := "*" + d.nameFromDIESym(dwtype)
+ if die := d.find(ptrname); die != 0 {
+ return die
+ }
+
+ pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname)
+ d.newrefattr(pdie, dwarf.DW_AT_type, dwtype)
+
+ // The DWARF info synthesizes pointer types that don't exist at the
+ // language level, like *hash<...> and *bucket<...>, and the data
+ // pointers of slices. Link to the ones we can find.
+ gts := d.ldr.Lookup("type:"+ptrname, 0)
+ if gts != 0 && d.ldr.AttrReachable(gts) {
+ newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts))
+ }
+
+ if gts != 0 {
+ ds := loader.Sym(pdie.Sym.(dwSym))
+ d.rtmap[ds] = gts
+ d.tdmap[gts] = ds
+ }
+
+ return d.dtolsym(pdie.Sym)
+}
+
+// Copies src's children into dst. Copies attributes by value.
+// DWAttr.data is copied as pointer only. If except is one of
+// the top-level children, it will not be copied.
+func (d *dwctxt) copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) {
+ for src = src.Child; src != nil; src = src.Link {
+ if src == except {
+ continue
+ }
+ c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string))
+ for a := src.Attr; a != nil; a = a.Link {
+ newattr(c, a.Atr, int(a.Cls), a.Value, a.Data)
+ }
+ d.copychildrenexcept(ctxt, c, src, nil)
+ }
+
+ reverselist(&dst.Child)
+}
+
+func (d *dwctxt) copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) {
+ d.copychildrenexcept(ctxt, dst, src, nil)
+}
+
+// Search children (assumed to have TAG_member) for the one named
+// field and set its AT_type to dwtype
+func (d *dwctxt) substitutetype(structdie *dwarf.DWDie, field string, dwtype loader.Sym) {
+ child := findchild(structdie, field)
+ if child == nil {
+ Exitf("dwarf substitutetype: %s does not have member %s",
+ getattr(structdie, dwarf.DW_AT_name).Data, field)
+ return
+ }
+
+ a := getattr(child, dwarf.DW_AT_type)
+ if a != nil {
+ a.Data = dwSym(dwtype)
+ } else {
+ d.newrefattr(child, dwarf.DW_AT_type, dwtype)
+ }
+}
+
+func (d *dwctxt) findprotodie(ctxt *Link, name string) *dwarf.DWDie {
+ die, ok := prototypedies[name]
+ if ok && die == nil {
+ d.defgotype(d.lookupOrDiag(name))
+ die = prototypedies[name]
+ }
+ if die == nil {
+ log.Fatalf("internal error: DIE generation failed for %s\n", name)
+ }
+ return die
+}
+
+func (d *dwctxt) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
+ prototype := walktypedef(d.findprotodie(ctxt, "type:runtime.stringStructDWARF"))
+ if prototype == nil {
+ return
+ }
+
+ for ; die != nil; die = die.Link {
+ if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE {
+ continue
+ }
+ d.copychildren(ctxt, die, prototype)
+ }
+}
+
+func (d *dwctxt) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
+ prototype := walktypedef(d.findprotodie(ctxt, "type:runtime.slice"))
+ if prototype == nil {
+ return
+ }
+
+ for ; die != nil; die = die.Link {
+ if die.Abbrev != dwarf.DW_ABRV_SLICETYPE {
+ continue
+ }
+ d.copychildren(ctxt, die, prototype)
+ elem := loader.Sym(getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym))
+ d.substitutetype(die, "array", d.defptrto(elem))
+ }
+}
+
+func mkinternaltypename(base string, arg1 string, arg2 string) string {
+ if arg2 == "" {
+ return fmt.Sprintf("%s<%s>", base, arg1)
+ }
+ return fmt.Sprintf("%s<%s,%s>", base, arg1, arg2)
+}
+
+// synthesizemaptypes is way too closely married to runtime/hashmap.c
+const (
+ MaxKeySize = 128
+ MaxValSize = 128
+ BucketSize = 8
+)
+
+func (d *dwctxt) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) loader.Sym {
+ name := mkinternaltypename(typename, keyname, valname)
+ symname := dwarf.InfoPrefix + name
+ s := d.ldr.Lookup(symname, 0)
+ if s != 0 && d.ldr.SymType(s) == sym.SDWARFTYPE {
+ return s
+ }
+ die := d.newdie(&dwtypes, abbrev, name)
+ f(die)
+ return d.dtolsym(die.Sym)
+}
+
+func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
+ hash := walktypedef(d.findprotodie(ctxt, "type:runtime.hmap"))
+ bucket := walktypedef(d.findprotodie(ctxt, "type:runtime.bmap"))
+
+ if hash == nil {
+ return
+ }
+
+ for ; die != nil; die = die.Link {
+ if die.Abbrev != dwarf.DW_ABRV_MAPTYPE {
+ continue
+ }
+ gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
+ keytype := decodetypeMapKey(d.ldr, d.arch, gotype)
+ valtype := decodetypeMapValue(d.ldr, d.arch, gotype)
+ keydata := d.ldr.Data(keytype)
+ valdata := d.ldr.Data(valtype)
+ keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata)
+ keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype))
+
+ // compute size info like hashmap.c does.
+ indirectKey, indirectVal := false, false
+ if keysize > MaxKeySize {
+ keysize = int64(d.arch.PtrSize)
+ indirectKey = true
+ }
+ if valsize > MaxValSize {
+ valsize = int64(d.arch.PtrSize)
+ indirectVal = true
+ }
+
+ // Construct type to represent an array of BucketSize keys
+ keyname := d.nameFromDIESym(keytype)
+ dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) {
+ newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0)
+ t := keytype
+ if indirectKey {
+ t = d.defptrto(keytype)
+ }
+ d.newrefattr(dwhk, dwarf.DW_AT_type, t)
+ fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size")
+ newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+ })
+
+ // Construct type to represent an array of BucketSize values
+ valname := d.nameFromDIESym(valtype)
+ dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) {
+ newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0)
+ t := valtype
+ if indirectVal {
+ t = d.defptrto(valtype)
+ }
+ d.newrefattr(dwhv, dwarf.DW_AT_type, t)
+ fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size")
+ newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+ })
+
+ // Construct bucket<K,V>
+ dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
+ // Copy over all fields except the field "data" from the generic
+ // bucket. "data" will be replaced with keys/values below.
+ d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
+
+ fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys")
+ d.newrefattr(fld, dwarf.DW_AT_type, dwhks)
+ newmemberoffsetattr(fld, BucketSize)
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values")
+ d.newrefattr(fld, dwarf.DW_AT_type, dwhvs)
+ newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow")
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym)))
+ newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize)))
+ if d.arch.RegSize > d.arch.PtrSize {
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad")
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+ newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize))
+ }
+
+ newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(d.arch.RegSize), 0)
+ })
+
+ // Construct hash<K,V>
+ dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
+ d.copychildren(ctxt, dwh, hash)
+ d.substitutetype(dwh, "buckets", d.defptrto(dwhbs))
+ d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs))
+ newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil)
+ })
+
+ // make map type a pointer to hash<K,V>
+ d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
+ }
+}
+
+func (d *dwctxt) synthesizechantypes(ctxt *Link, die *dwarf.DWDie) {
+ sudog := walktypedef(d.findprotodie(ctxt, "type:runtime.sudog"))
+ waitq := walktypedef(d.findprotodie(ctxt, "type:runtime.waitq"))
+ hchan := walktypedef(d.findprotodie(ctxt, "type:runtime.hchan"))
+ if sudog == nil || waitq == nil || hchan == nil {
+ return
+ }
+
+ sudogsize := int(getattr(sudog, dwarf.DW_AT_byte_size).Value)
+
+ for ; die != nil; die = die.Link {
+ if die.Abbrev != dwarf.DW_ABRV_CHANTYPE {
+ continue
+ }
+ elemgotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
+ tname := d.ldr.SymName(elemgotype)
+ elemname := tname[5:]
+ elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname)))
+
+ // sudog<T>
+ dwss := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) {
+ d.copychildren(ctxt, dws, sudog)
+ d.substitutetype(dws, "elem", d.defptrto(elemtype))
+ newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil)
+ })
+
+ // waitq<T>
+ dwws := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) {
+
+ d.copychildren(ctxt, dww, waitq)
+ d.substitutetype(dww, "first", d.defptrto(dwss))
+ d.substitutetype(dww, "last", d.defptrto(dwss))
+ newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil)
+ })
+
+ // hchan<T>
+ dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) {
+ d.copychildren(ctxt, dwh, hchan)
+ d.substitutetype(dwh, "recvq", dwws)
+ d.substitutetype(dwh, "sendq", dwws)
+ newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil)
+ })
+
+ d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
+ }
+}
+
+// createUnitLength creates the initial length field with value v and update
+// offset of unit_length if needed.
+func (d *dwctxt) createUnitLength(su *loader.SymbolBuilder, v uint64) {
+ if isDwarf64(d.linkctxt) {
+ su.AddUint32(d.arch, 0xFFFFFFFF)
+ }
+ d.addDwarfAddrField(su, v)
+}
+
+// addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits.
+func (d *dwctxt) addDwarfAddrField(sb *loader.SymbolBuilder, v uint64) {
+ if isDwarf64(d.linkctxt) {
+ sb.AddUint(d.arch, v)
+ } else {
+ sb.AddUint32(d.arch, uint32(v))
+ }
+}
+
+// addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits.
+func (d *dwctxt) addDwarfAddrRef(sb *loader.SymbolBuilder, t loader.Sym) {
+ if isDwarf64(d.linkctxt) {
+ d.adddwarfref(sb, t, 8)
+ } else {
+ d.adddwarfref(sb, t, 4)
+ }
+}
+
+// calcCompUnitRanges calculates the PC ranges of the compilation units.
+func (d *dwctxt) calcCompUnitRanges() {
+ var prevUnit *sym.CompilationUnit
+ for _, s := range d.linkctxt.Textp {
+ sym := loader.Sym(s)
+
+ fi := d.ldr.FuncInfo(sym)
+ if !fi.Valid() {
+ continue
+ }
+
+ // Skip linker-created functions (ex: runtime.addmoduledata), since they
+ // don't have DWARF to begin with.
+ unit := d.ldr.SymUnit(sym)
+ if unit == nil {
+ continue
+ }
+
+ // Update PC ranges.
+ //
+ // We don't simply compare the end of the previous
+ // symbol with the start of the next because there's
+ // often a little padding between them. Instead, we
+ // only create boundaries between symbols from
+ // different units.
+ sval := d.ldr.SymValue(sym)
+ u0val := d.ldr.SymValue(loader.Sym(unit.Textp[0]))
+ if prevUnit != unit {
+ unit.PCs = append(unit.PCs, dwarf.Range{Start: sval - u0val})
+ prevUnit = unit
+ }
+ unit.PCs[len(unit.PCs)-1].End = sval - u0val + int64(len(d.ldr.Data(sym)))
+ }
+}
+
+func movetomodule(ctxt *Link, parent *dwarf.DWDie) {
+ die := ctxt.runtimeCU.DWInfo.Child
+ if die == nil {
+ ctxt.runtimeCU.DWInfo.Child = parent.Child
+ return
+ }
+ for die.Link != nil {
+ die = die.Link
+ }
+ die.Link = parent.Child
+}
+
+/*
+ * Generate a sequence of opcodes that is as short as possible.
+ * See section 6.2.5
+ */
+const (
+ LINE_BASE = -4
+ LINE_RANGE = 10
+ PC_RANGE = (255 - OPCODE_BASE) / LINE_RANGE
+ OPCODE_BASE = 11
+)
+
+/*
+ * Walk prog table, emit line program and build DIE tree.
+ */
+
+func getCompilationDir() string {
+ // OSX requires this be set to something, but it's not easy to choose
+ // a value. Linking takes place in a temporary directory, so there's
+ // no point including it here. Paths in the file table are usually
+ // absolute, in which case debuggers will ignore this value. -trimpath
+ // produces relative paths, but we don't know where they start, so
+ // all we can do here is try not to make things worse.
+ return "."
+}
+
+func (d *dwctxt) importInfoSymbol(dsym loader.Sym) {
+ d.ldr.SetAttrReachable(dsym, true)
+ d.ldr.SetAttrNotInSymbolTable(dsym, true)
+ dst := d.ldr.SymType(dsym)
+ if dst != sym.SDWARFCONST && dst != sym.SDWARFABSFCN {
+ log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String())
+ }
+ relocs := d.ldr.Relocs(dsym)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ if r.Type() != objabi.R_DWARFSECREF {
+ continue
+ }
+ rsym := r.Sym()
+ // If there is an entry for the symbol in our rtmap, then it
+ // means we've processed the type already, and can skip this one.
+ if _, ok := d.rtmap[rsym]; ok {
+ // type already generated
+ continue
+ }
+ // FIXME: is there a way we could avoid materializing the
+ // symbol name here?
+ sn := d.ldr.SymName(rsym)
+ tn := sn[len(dwarf.InfoPrefix):]
+ ts := d.ldr.Lookup("type:"+tn, 0)
+ d.defgotype(ts)
+ }
+}
+
+func expandFile(fname string) string {
+ if strings.HasPrefix(fname, src.FileSymPrefix) {
+ fname = fname[len(src.FileSymPrefix):]
+ }
+ return expandGoroot(fname)
+}
+
+// writeDirFileTables emits the portion of the DWARF line table
+// prologue containing the include directories and file names,
+// described in section 6.2.4 of the DWARF 4 standard. It walks the
+// filepaths for the unit to discover any common directories, which
+// are emitted to the directory table first, then the file table is
+// emitted after that.
+func (d *dwctxt) writeDirFileTables(unit *sym.CompilationUnit, lsu *loader.SymbolBuilder) {
+ type fileDir struct {
+ base string
+ dir int
+ }
+ dirNums := make(map[string]int)
+ dirs := []string{""}
+ files := []fileDir{}
+
+ // Preprocess files to collect directories. This assumes that the
+ // file table is already de-duped.
+ for i, name := range unit.FileTable {
+ name := expandFile(name)
+ if len(name) == 0 {
+ // Can't have empty filenames, and having a unique
+ // filename is quite useful for debugging.
+ name = fmt.Sprintf("<missing>_%d", i)
+ }
+ // Note the use of "path" here and not "filepath". The compiler
+ // hard-codes to use "/" in DWARF paths (even for Windows), so we
+ // want to maintain that here.
+ file := path.Base(name)
+ dir := path.Dir(name)
+ dirIdx, ok := dirNums[dir]
+ if !ok && dir != "." {
+ dirIdx = len(dirNums) + 1
+ dirNums[dir] = dirIdx
+ dirs = append(dirs, dir)
+ }
+ files = append(files, fileDir{base: file, dir: dirIdx})
+
+ // We can't use something that may be dead-code
+ // eliminated from a binary here. proc.go contains
+ // main and the scheduler, so it's not going anywhere.
+ if i := strings.Index(name, "runtime/proc.go"); i >= 0 && unit.Lib.Pkg == "runtime" {
+ d.dwmu.Lock()
+ if gdbscript == "" {
+ k := strings.Index(name, "runtime/proc.go")
+ gdbscript = name[:k] + "runtime/runtime-gdb.py"
+ }
+ d.dwmu.Unlock()
+ }
+ }
+
+ // Emit directory section. This is a series of nul terminated
+ // strings, followed by a single zero byte.
+ lsDwsym := dwSym(lsu.Sym())
+ for k := 1; k < len(dirs); k++ {
+ d.AddString(lsDwsym, dirs[k])
+ }
+ lsu.AddUint8(0) // terminator
+
+ // Emit file section.
+ for k := 0; k < len(files); k++ {
+ d.AddString(lsDwsym, files[k].base)
+ dwarf.Uleb128put(d, lsDwsym, int64(files[k].dir))
+ lsu.AddUint8(0) // mtime
+ lsu.AddUint8(0) // length
+ }
+ lsu.AddUint8(0) // terminator
+}
+
+// writelines collects up and chains together the symbols needed to
+// form the DWARF line table for the specified compilation unit,
+// returning a list of symbols. The returned list will include an
+// initial symbol containing the line table header and prologue (with
+// file table), then a series of compiler-emitted line table symbols
+// (one per live function), and finally an epilog symbol containing an
+// end-of-sequence operator. The prologue and epilog symbols are passed
+// in (having been created earlier); here we add content to them.
+func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []loader.Sym {
+ is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles.
+
+ unitstart := int64(-1)
+ headerstart := int64(-1)
+ headerend := int64(-1)
+
+ syms := make([]loader.Sym, 0, len(unit.Textp)+2)
+ syms = append(syms, lineProlog)
+ lsu := d.ldr.MakeSymbolUpdater(lineProlog)
+ lsDwsym := dwSym(lineProlog)
+ newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, lsDwsym)
+
+ // Write .debug_line Line Number Program Header (sec 6.2.4)
+ // Fields marked with (*) must be changed for 64-bit dwarf
+ unitLengthOffset := lsu.Size()
+ d.createUnitLength(lsu, 0) // unit_length (*), filled in at end
+ unitstart = lsu.Size()
+ lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05
+ headerLengthOffset := lsu.Size()
+ d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end
+ headerstart = lsu.Size()
+
+ // cpos == unitstart + 4 + 2 + 4
+ lsu.AddUint8(1) // minimum_instruction_length
+ lsu.AddUint8(is_stmt) // default_is_stmt
+ lsu.AddUint8(LINE_BASE & 0xFF) // line_base
+ lsu.AddUint8(LINE_RANGE) // line_range
+ lsu.AddUint8(OPCODE_BASE) // opcode_base
+ lsu.AddUint8(0) // standard_opcode_lengths[1]
+ lsu.AddUint8(1) // standard_opcode_lengths[2]
+ lsu.AddUint8(1) // standard_opcode_lengths[3]
+ lsu.AddUint8(1) // standard_opcode_lengths[4]
+ lsu.AddUint8(1) // standard_opcode_lengths[5]
+ lsu.AddUint8(0) // standard_opcode_lengths[6]
+ lsu.AddUint8(0) // standard_opcode_lengths[7]
+ lsu.AddUint8(0) // standard_opcode_lengths[8]
+ lsu.AddUint8(1) // standard_opcode_lengths[9]
+ lsu.AddUint8(0) // standard_opcode_lengths[10]
+
+ // Call helper to emit dir and file sections.
+ d.writeDirFileTables(unit, lsu)
+
+ // capture length at end of file names.
+ headerend = lsu.Size()
+ unitlen := lsu.Size() - unitstart
+
+ // Output the state machine for each function remaining.
+ for _, s := range unit.Textp {
+ fnSym := loader.Sym(s)
+ _, _, _, lines := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+
+ // Chain the line symbol onto the list.
+ if lines != 0 {
+ syms = append(syms, lines)
+ unitlen += int64(len(d.ldr.Data(lines)))
+ }
+ }
+
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(unitlen))
+ }
+
+ if isDwarf64(d.linkctxt) {
+ lsu.SetUint(d.arch, unitLengthOffset+4, uint64(unitlen)) // +4 because of 0xFFFFFFFF
+ lsu.SetUint(d.arch, headerLengthOffset, uint64(headerend-headerstart))
+ } else {
+ lsu.SetUint32(d.arch, unitLengthOffset, uint32(unitlen))
+ lsu.SetUint32(d.arch, headerLengthOffset, uint32(headerend-headerstart))
+ }
+
+ return syms
+}
+
+// writepcranges generates the DW_AT_ranges table for compilation unit
+// "unit", and returns a collection of ranges symbols (one for the
+// compilation unit DIE itself and the remainder from functions in the unit).
+func (d *dwctxt) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, rangeProlog loader.Sym) []loader.Sym {
+
+ syms := make([]loader.Sym, 0, len(unit.RangeSyms)+1)
+ syms = append(syms, rangeProlog)
+ rsu := d.ldr.MakeSymbolUpdater(rangeProlog)
+ rDwSym := dwSym(rangeProlog)
+
+ // Create PC ranges for the compilation unit DIE.
+ newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym)
+ newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base))
+ dwarf.PutBasedRanges(d, rDwSym, pcs)
+
+ // Collect up the ranges for functions in the unit.
+ rsize := uint64(rsu.Size())
+ for _, ls := range unit.RangeSyms {
+ s := loader.Sym(ls)
+ syms = append(syms, s)
+ rsize += uint64(d.ldr.SymSize(s))
+ }
+
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, rsize)
+ }
+
+ return syms
+}
+
+/*
+ * Emit .debug_frame
+ */
+const (
+ dataAlignmentFactor = -4
+)
+
+// appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice.
+func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte {
+ b = append(b, dwarf.DW_CFA_def_cfa_offset_sf)
+ b = dwarf.AppendSleb128(b, cfa/dataAlignmentFactor)
+
+ switch {
+ case deltapc < 0x40:
+ b = append(b, uint8(dwarf.DW_CFA_advance_loc+deltapc))
+ case deltapc < 0x100:
+ b = append(b, dwarf.DW_CFA_advance_loc1)
+ b = append(b, uint8(deltapc))
+ case deltapc < 0x10000:
+ b = append(b, dwarf.DW_CFA_advance_loc2, 0, 0)
+ arch.ByteOrder.PutUint16(b[len(b)-2:], uint16(deltapc))
+ default:
+ b = append(b, dwarf.DW_CFA_advance_loc4, 0, 0, 0, 0)
+ arch.ByteOrder.PutUint32(b[len(b)-4:], uint32(deltapc))
+ }
+ return b
+}
+
+func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo {
+ fsd := dwSym(fs)
+ fsu := d.ldr.MakeSymbolUpdater(fs)
+ fsu.SetType(sym.SDWARFSECT)
+ isdw64 := isDwarf64(d.linkctxt)
+ haslr := d.linkctxt.Arch.HasLR
+
+ // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64
+ lengthFieldSize := int64(4)
+ if isdw64 {
+ lengthFieldSize += 8
+ }
+
+ // Emit the CIE, Section 6.4.1
+ cieReserve := uint32(16)
+ if haslr {
+ cieReserve = 32
+ }
+ if isdw64 {
+ cieReserve += 4 // 4 bytes added for cid
+ }
+ d.createUnitLength(fsu, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize
+ d.addDwarfAddrField(fsu, ^uint64(0)) // cid
+ fsu.AddUint8(3) // dwarf version (appendix F)
+ fsu.AddUint8(0) // augmentation ""
+ dwarf.Uleb128put(d, fsd, 1) // code_alignment_factor
+ dwarf.Sleb128put(d, fsd, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // return_address_register
+
+ fsu.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address..
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
+ if haslr {
+ dwarf.Uleb128put(d, fsd, int64(0)) // ...plus a 0 offset.
+
+ fsu.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr))
+
+ fsu.AddUint8(dwarf.DW_CFA_val_offset) // The previous value...
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...of the platform's SP register...
+ dwarf.Uleb128put(d, fsd, int64(0)) // ...is CFA+0.
+ } else {
+ dwarf.Uleb128put(d, fsd, int64(d.arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
+
+ fsu.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value...
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // ...of the return address...
+ dwarf.Uleb128put(d, fsd, int64(-d.arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
+ }
+
+ pad := int64(cieReserve) + lengthFieldSize - int64(len(d.ldr.Data(fs)))
+
+ if pad < 0 {
+ Exitf("dwarf: cieReserve too small by %d bytes.", -pad)
+ }
+
+ internalExec := d.linkctxt.BuildMode == BuildModeExe && d.linkctxt.IsInternal()
+ addAddrPlus := loader.GenAddAddrPlusFunc(internalExec)
+
+ fsu.AddBytes(zeros[:pad])
+
+ var deltaBuf []byte
+ pcsp := obj.NewPCIter(uint32(d.arch.MinLC))
+ for _, s := range d.linkctxt.Textp {
+ fn := loader.Sym(s)
+ fi := d.ldr.FuncInfo(fn)
+ if !fi.Valid() {
+ continue
+ }
+ fpcsp := d.ldr.Pcsp(s)
+
+ // Emit a FDE, Section 6.4.1.
+ // First build the section contents into a byte buffer.
+ deltaBuf = deltaBuf[:0]
+ if haslr && fi.TopFrame() {
+ // Mark the link register as having an undefined value.
+ // This stops call stack unwinders progressing any further.
+ // TODO: similar mark on non-LR architectures.
+ deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined)
+ deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
+ }
+
+ for pcsp.Init(d.linkctxt.loader.Data(fpcsp)); !pcsp.Done; pcsp.Next() {
+ nextpc := pcsp.NextPC
+
+ // pciterinit goes up to the end of the function,
+ // but DWARF expects us to stop just before the end.
+ if int64(nextpc) == int64(len(d.ldr.Data(fn))) {
+ nextpc--
+ if nextpc < pcsp.PC {
+ continue
+ }
+ }
+
+ spdelta := int64(pcsp.Value)
+ if !haslr {
+ // Return address has been pushed onto stack.
+ spdelta += int64(d.arch.PtrSize)
+ }
+
+ if haslr && !fi.TopFrame() {
+ // TODO(bryanpkc): This is imprecise. In general, the instruction
+ // that stores the return address to the stack frame is not the
+ // same one that allocates the frame.
+ if pcsp.Value > 0 {
+ // The return address is preserved at (CFA-frame_size)
+ // after a stack frame has been allocated.
+ deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf)
+ deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
+ deltaBuf = dwarf.AppendSleb128(deltaBuf, -spdelta/dataAlignmentFactor)
+ } else {
+ // The return address is restored into the link register
+ // when a stack frame has been de-allocated.
+ deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value)
+ deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
+ }
+ }
+
+ deltaBuf = appendPCDeltaCFA(d.arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta)
+ }
+ pad := int(Rnd(int64(len(deltaBuf)), int64(d.arch.PtrSize))) - len(deltaBuf)
+ deltaBuf = append(deltaBuf, zeros[:pad]...)
+
+ // Emit the FDE header, Section 6.4.1.
+ // 4 bytes: length, must be multiple of thearch.ptrsize
+ // 4/8 bytes: Pointer to the CIE above, at offset 0
+ // ptrsize: initial location
+ // ptrsize: address range
+
+ fdeLength := uint64(4 + 2*d.arch.PtrSize + len(deltaBuf))
+ if isdw64 {
+ fdeLength += 4 // 4 bytes added for CIE pointer
+ }
+ d.createUnitLength(fsu, fdeLength)
+
+ if d.linkctxt.LinkMode == LinkExternal {
+ d.addDwarfAddrRef(fsu, fs)
+ } else {
+ d.addDwarfAddrField(fsu, 0) // CIE offset
+ }
+ addAddrPlus(fsu, d.arch, s, 0)
+ fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range
+ fsu.AddBytes(deltaBuf)
+
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_frame", d.ldr.SymPkg(fn), fdeLength+uint64(lengthFieldSize))
+ }
+ }
+
+ return dwarfSecInfo{syms: []loader.Sym{fs}}
+}
+
+/*
+ * Walk DWarfDebugInfoEntries, and emit .debug_info
+ */
+
+const (
+ COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
+)
+
+// appendSyms appends the syms from 'src' into 'syms' and returns the
+// result. This can go away once we do away with sym.LoaderSym
+// entirely.
+func appendSyms(syms []loader.Sym, src []sym.LoaderSym) []loader.Sym {
+ for _, s := range src {
+ syms = append(syms, loader.Sym(s))
+ }
+ return syms
+}
+
+func (d *dwctxt) writeUnitInfo(u *sym.CompilationUnit, abbrevsym loader.Sym, infoEpilog loader.Sym) []loader.Sym {
+ syms := []loader.Sym{}
+ if len(u.Textp) == 0 && u.DWInfo.Child == nil && len(u.VarDIEs) == 0 {
+ return syms
+ }
+
+ compunit := u.DWInfo
+ s := d.dtolsym(compunit.Sym)
+ su := d.ldr.MakeSymbolUpdater(s)
+
+ // Write .debug_info Compilation Unit Header (sec 7.5.1)
+ // Fields marked with (*) must be changed for 64-bit dwarf
+ // This must match COMPUNITHEADERSIZE above.
+ d.createUnitLength(su, 0) // unit_length (*), will be filled in later.
+ su.AddUint16(d.arch, 4) // dwarf version (appendix F)
+
+ // debug_abbrev_offset (*)
+ d.addDwarfAddrRef(su, abbrevsym)
+
+ su.AddUint8(uint8(d.arch.PtrSize)) // address_size
+
+ ds := dwSym(s)
+ dwarf.Uleb128put(d, ds, int64(compunit.Abbrev))
+ dwarf.PutAttrs(d, ds, compunit.Abbrev, compunit.Attr)
+
+ // This is an under-estimate; more will be needed for type DIEs.
+ cu := make([]loader.Sym, 0, len(u.AbsFnDIEs)+len(u.FuncDIEs))
+ cu = append(cu, s)
+ cu = appendSyms(cu, u.AbsFnDIEs)
+ cu = appendSyms(cu, u.FuncDIEs)
+ if u.Consts != 0 {
+ cu = append(cu, loader.Sym(u.Consts))
+ }
+ cu = appendSyms(cu, u.VarDIEs)
+ var cusize int64
+ for _, child := range cu {
+ cusize += int64(len(d.ldr.Data(child)))
+ }
+
+ for die := compunit.Child; die != nil; die = die.Link {
+ l := len(cu)
+ lastSymSz := int64(len(d.ldr.Data(cu[l-1])))
+ cu = d.putdie(cu, die)
+ if lastSymSz != int64(len(d.ldr.Data(cu[l-1]))) {
+ // putdie will sometimes append directly to the last symbol of the list
+ cusize = cusize - lastSymSz + int64(len(d.ldr.Data(cu[l-1])))
+ }
+ for _, child := range cu[l:] {
+ cusize += int64(len(d.ldr.Data(child)))
+ }
+ }
+
+ culu := d.ldr.MakeSymbolUpdater(infoEpilog)
+ culu.AddUint8(0) // closes compilation unit DIE
+ cu = append(cu, infoEpilog)
+ cusize++
+
+ // Save size for AIX symbol table.
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_info", d.getPkgFromCUSym(s), uint64(cusize))
+ }
+ if isDwarf64(d.linkctxt) {
+ cusize -= 12 // exclude the length field.
+ su.SetUint(d.arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF
+ } else {
+ cusize -= 4 // exclude the length field.
+ su.SetUint32(d.arch, 0, uint32(cusize))
+ }
+ return append(syms, cu...)
+}
+
+func (d *dwctxt) writegdbscript() dwarfSecInfo {
+ // TODO (aix): make it available
+ if d.linkctxt.HeadType == objabi.Haix {
+ return dwarfSecInfo{}
+ }
+ if d.linkctxt.LinkMode == LinkExternal && d.linkctxt.HeadType == objabi.Hwindows && d.linkctxt.BuildMode == BuildModeCArchive {
+ // gcc on Windows places .debug_gdb_scripts in the wrong location, which
+ // causes the program not to run. See https://golang.org/issue/20183
+ // Non c-archives can avoid this issue via a linker script
+ // (see fix near writeGDBLinkerScript).
+ // c-archive users would need to specify the linker script manually.
+ // For UX it's better not to deal with this.
+ return dwarfSecInfo{}
+ }
+ if gdbscript == "" {
+ return dwarfSecInfo{}
+ }
+
+ gs := d.ldr.CreateSymForUpdate(".debug_gdb_scripts", 0)
+ gs.SetType(sym.SDWARFSECT)
+
+ gs.AddUint8(GdbScriptPythonFileId)
+ gs.Addstring(gdbscript)
+ return dwarfSecInfo{syms: []loader.Sym{gs.Sym()}}
+}
+
+// FIXME: might be worth looking replacing this map with a function
+// that switches based on symbol instead.
+
+var prototypedies map[string]*dwarf.DWDie
+
+func dwarfEnabled(ctxt *Link) bool {
+ if *FlagW { // disable dwarf
+ return false
+ }
+ if *FlagS && ctxt.HeadType != objabi.Hdarwin {
+ return false
+ }
+ if ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hjs {
+ return false
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ switch {
+ case ctxt.IsELF:
+ case ctxt.HeadType == objabi.Hdarwin:
+ case ctxt.HeadType == objabi.Hwindows:
+ case ctxt.HeadType == objabi.Haix:
+ res, err := dwarf.IsDWARFEnabledOnAIXLd(ctxt.extld())
+ if err != nil {
+ Exitf("%v", err)
+ }
+ return res
+ default:
+ return false
+ }
+ }
+
+ return true
+}
+
+// mkBuiltinType populates the dwctxt2 sym lookup maps for the
+// newly created builtin type DIE 'typeDie'.
+func (d *dwctxt) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie {
+ // create type DIE
+ die := d.newdie(&dwtypes, abrv, tname)
+
+ // Look up type symbol.
+ gotype := d.lookupOrDiag("type:" + tname)
+
+ // Map from die sym to type sym
+ ds := loader.Sym(die.Sym.(dwSym))
+ d.rtmap[ds] = gotype
+
+ // Map from type to def sym
+ d.tdmap[gotype] = ds
+
+ return die
+}
+
+// dwarfVisitFunction takes a function (text) symbol and processes the
+// subprogram DIE for the function and picks up any other DIEs
+// (absfns, types) that it references.
+func (d *dwctxt) dwarfVisitFunction(fnSym loader.Sym, unit *sym.CompilationUnit) {
+ // The DWARF subprogram DIE symbol is listed as an aux sym
+ // of the text (fcn) symbol, so ask the loader to retrieve it,
+ // as well as the associated range symbol.
+ infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+ if infosym == 0 {
+ return
+ }
+ d.ldr.SetAttrNotInSymbolTable(infosym, true)
+ d.ldr.SetAttrReachable(infosym, true)
+ unit.FuncDIEs = append(unit.FuncDIEs, sym.LoaderSym(infosym))
+ if rangesym != 0 {
+ d.ldr.SetAttrNotInSymbolTable(rangesym, true)
+ d.ldr.SetAttrReachable(rangesym, true)
+ unit.RangeSyms = append(unit.RangeSyms, sym.LoaderSym(rangesym))
+ }
+
+ // Walk the relocations of the subprogram DIE symbol to discover
+ // references to abstract function DIEs, Go type DIES, and
+ // (via R_USETYPE relocs) types that were originally assigned to
+ // locals/params but were optimized away.
+ drelocs := d.ldr.Relocs(infosym)
+ for ri := 0; ri < drelocs.Count(); ri++ {
+ r := drelocs.At(ri)
+ // Look for "use type" relocs.
+ if r.Type() == objabi.R_USETYPE {
+ d.defgotype(r.Sym())
+ continue
+ }
+ if r.Type() != objabi.R_DWARFSECREF {
+ continue
+ }
+
+ rsym := r.Sym()
+ rst := d.ldr.SymType(rsym)
+
+ // Look for abstract function references.
+ if rst == sym.SDWARFABSFCN {
+ if !d.ldr.AttrOnList(rsym) {
+ // abstract function
+ d.ldr.SetAttrOnList(rsym, true)
+ unit.AbsFnDIEs = append(unit.AbsFnDIEs, sym.LoaderSym(rsym))
+ d.importInfoSymbol(rsym)
+ }
+ continue
+ }
+
+ // Look for type references.
+ if rst != sym.SDWARFTYPE && rst != sym.Sxxx {
+ continue
+ }
+ if _, ok := d.rtmap[rsym]; ok {
+ // type already generated
+ continue
+ }
+
+ rsn := d.ldr.SymName(rsym)
+ tn := rsn[len(dwarf.InfoPrefix):]
+ ts := d.ldr.Lookup("type:"+tn, 0)
+ d.defgotype(ts)
+ }
+}
+
+// dwarfGenerateDebugInfo generated debug info entries for all types,
+// variables and functions in the program.
+// Along with dwarfGenerateDebugSyms they are the two main entry points into
+// dwarf generation: dwarfGenerateDebugInfo does all the work that should be
+// done before symbol names are mangled while dwarfGenerateDebugSyms does
+// all the work that can only be done after addresses have been assigned to
+// text symbols.
+func dwarfGenerateDebugInfo(ctxt *Link) {
+ if !dwarfEnabled(ctxt) {
+ return
+ }
+
+ d := &dwctxt{
+ linkctxt: ctxt,
+ ldr: ctxt.loader,
+ arch: ctxt.Arch,
+ tmap: make(map[string]loader.Sym),
+ tdmap: make(map[loader.Sym]loader.Sym),
+ rtmap: make(map[loader.Sym]loader.Sym),
+ }
+ d.typeRuntimeEface = d.lookupOrDiag("type:runtime.eface")
+ d.typeRuntimeIface = d.lookupOrDiag("type:runtime.iface")
+
+ if ctxt.HeadType == objabi.Haix {
+ // Initial map used to store package size for each DWARF section.
+ dwsectCUSize = make(map[string]uint64)
+ }
+
+ // For ctxt.Diagnostic messages.
+ newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes")
+
+ // Unspecified type. There are no references to this in the symbol table.
+ d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>")
+
+ // Some types that must exist to define other ones (uintptr in particular
+ // is needed for array size)
+ d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer")
+ die := d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BASETYPE, "uintptr")
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(d.arch.PtrSize), 0)
+ newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0)
+ newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, dwSym(d.lookupOrDiag("type:uintptr")))
+
+ d.uintptrInfoSym = d.mustFind("uintptr")
+
+ // Prototypes needed for type synthesis.
+ prototypedies = map[string]*dwarf.DWDie{
+ "type:runtime.stringStructDWARF": nil,
+ "type:runtime.slice": nil,
+ "type:runtime.hmap": nil,
+ "type:runtime.bmap": nil,
+ "type:runtime.sudog": nil,
+ "type:runtime.waitq": nil,
+ "type:runtime.hchan": nil,
+ }
+
+ // Needed by the prettyprinter code for interface inspection.
+ for _, typ := range []string{
+ "type:runtime._type",
+ "type:runtime.arraytype",
+ "type:runtime.chantype",
+ "type:runtime.functype",
+ "type:runtime.maptype",
+ "type:runtime.ptrtype",
+ "type:runtime.slicetype",
+ "type:runtime.structtype",
+ "type:runtime.interfacetype",
+ "type:runtime.itab",
+ "type:runtime.imethod"} {
+ d.defgotype(d.lookupOrDiag(typ))
+ }
+
+ // fake root DIE for compile unit DIEs
+ var dwroot dwarf.DWDie
+ flagVariants := make(map[string]bool)
+
+ for _, lib := range ctxt.Library {
+
+ consts := d.ldr.Lookup(dwarf.ConstInfoPrefix+lib.Pkg, 0)
+ for _, unit := range lib.Units {
+ // We drop the constants into the first CU.
+ if consts != 0 {
+ unit.Consts = sym.LoaderSym(consts)
+ d.importInfoSymbol(consts)
+ consts = 0
+ }
+ ctxt.compUnits = append(ctxt.compUnits, unit)
+
+ // We need at least one runtime unit.
+ if unit.Lib.Pkg == "runtime" {
+ ctxt.runtimeCU = unit
+ }
+
+ cuabrv := dwarf.DW_ABRV_COMPUNIT
+ if len(unit.Textp) == 0 {
+ cuabrv = dwarf.DW_ABRV_COMPUNIT_TEXTLESS
+ }
+ unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg)
+ newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0)
+ // OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
+ compDir := getCompilationDir()
+ // TODO: Make this be the actual compilation directory, not
+ // the linker directory. If we move CU construction into the
+ // compiler, this should happen naturally.
+ newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir)
+
+ var peData []byte
+ if producerExtra := d.ldr.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0); producerExtra != 0 {
+ peData = d.ldr.Data(producerExtra)
+ }
+ producer := "Go cmd/compile " + buildcfg.Version
+ if len(peData) > 0 {
+ // We put a semicolon before the flags to clearly
+ // separate them from the version, which can be long
+ // and have lots of weird things in it in development
+ // versions. We promise not to put a semicolon in the
+ // version, so it should be safe for readers to scan
+ // forward to the semicolon.
+ producer += "; " + string(peData)
+ flagVariants[string(peData)] = true
+ } else {
+ flagVariants[""] = true
+ }
+
+ newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer)
+
+ var pkgname string
+ if pnSymIdx := d.ldr.Lookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); pnSymIdx != 0 {
+ pnsData := d.ldr.Data(pnSymIdx)
+ pkgname = string(pnsData)
+ }
+ newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname)
+
+ // Scan all functions in this compilation unit, create
+ // DIEs for all referenced types, find all referenced
+ // abstract functions, visit range symbols. Note that
+ // Textp has been dead-code-eliminated already.
+ for _, s := range unit.Textp {
+ d.dwarfVisitFunction(loader.Sym(s), unit)
+ }
+ }
+ }
+
+ // Fix for 31034: if the objects feeding into this link were compiled
+ // with different sets of flags, then don't issue an error if
+ // the -strictdups checks fail.
+ if checkStrictDups > 1 && len(flagVariants) > 1 {
+ checkStrictDups = 1
+ }
+
+ // Make a pass through all data symbols, looking for those
+ // corresponding to reachable, Go-generated, user-visible
+ // global variables. For each global of this sort, locate
+ // the corresponding compiler-generated DIE symbol and tack
+ // it onto the list associated with the unit.
+ // Also looks for dictionary symbols and generates DIE symbols for each
+ // type they reference.
+ for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ {
+ if !d.ldr.AttrReachable(idx) ||
+ d.ldr.AttrNotInSymbolTable(idx) ||
+ d.ldr.SymVersion(idx) >= sym.SymVerStatic {
+ continue
+ }
+ t := d.ldr.SymType(idx)
+ switch t {
+ case sym.SRODATA, sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS:
+ // ok
+ default:
+ continue
+ }
+ // Skip things with no type, unless it's a dictionary
+ gt := d.ldr.SymGoType(idx)
+ if gt == 0 {
+ if t == sym.SRODATA {
+ if d.ldr.IsDict(idx) {
+ // This is a dictionary, make sure that all types referenced by this dictionary are reachable
+ relocs := d.ldr.Relocs(idx)
+ for i := 0; i < relocs.Count(); i++ {
+ reloc := relocs.At(i)
+ if reloc.Type() == objabi.R_USEIFACE {
+ d.defgotype(reloc.Sym())
+ }
+ }
+ }
+ }
+ continue
+ }
+ // Skip file local symbols (this includes static tmps, stack
+ // object symbols, and local symbols in assembler src files).
+ if d.ldr.IsFileLocal(idx) {
+ continue
+ }
+ sn := d.ldr.SymName(idx)
+ if sn == "" {
+ // skip aux symbols
+ continue
+ }
+
+ // Find compiler-generated DWARF info sym for global in question,
+ // and tack it onto the appropriate unit. Note that there are
+ // circumstances under which we can't find the compiler-generated
+ // symbol-- this typically happens as a result of compiler options
+ // (e.g. compile package X with "-dwarf=0").
+
+ // FIXME: use an aux sym or a relocation here instead of a
+ // name lookup.
+ varDIE := d.ldr.Lookup(dwarf.InfoPrefix+sn, 0)
+ if varDIE != 0 {
+ unit := d.ldr.SymUnit(idx)
+ d.defgotype(gt)
+ unit.VarDIEs = append(unit.VarDIEs, sym.LoaderSym(varDIE))
+ }
+ }
+
+ d.synthesizestringtypes(ctxt, dwtypes.Child)
+ d.synthesizeslicetypes(ctxt, dwtypes.Child)
+ d.synthesizemaptypes(ctxt, dwtypes.Child)
+ d.synthesizechantypes(ctxt, dwtypes.Child)
+}
+
+// dwarfGenerateDebugSyms constructs debug_line, debug_frame, and
+// debug_loc. It also writes out the debug_info section using symbols
+// generated in dwarfGenerateDebugInfo2.
+func dwarfGenerateDebugSyms(ctxt *Link) {
+ if !dwarfEnabled(ctxt) {
+ return
+ }
+ d := &dwctxt{
+ linkctxt: ctxt,
+ ldr: ctxt.loader,
+ arch: ctxt.Arch,
+ dwmu: new(sync.Mutex),
+ }
+ d.dwarfGenerateDebugSyms()
+}
+
+// dwUnitSyms stores input and output symbols for DWARF generation
+// for a given compilation unit.
+type dwUnitSyms struct {
+ // Inputs for a given unit.
+ lineProlog loader.Sym
+ rangeProlog loader.Sym
+ infoEpilog loader.Sym
+
+ // Outputs for a given unit.
+ linesyms []loader.Sym
+ infosyms []loader.Sym
+ locsyms []loader.Sym
+ rangessyms []loader.Sym
+}
+
+// dwUnitPortion assembles the DWARF content for a given compilation
+// unit: debug_info, debug_lines, debug_ranges, debug_loc (debug_frame
+// is handled elsewere). Order is important; the calls to writelines
+// and writepcranges below make updates to the compilation unit DIE,
+// hence they have to happen before the call to writeUnitInfo.
+func (d *dwctxt) dwUnitPortion(u *sym.CompilationUnit, abbrevsym loader.Sym, us *dwUnitSyms) {
+ if u.DWInfo.Abbrev != dwarf.DW_ABRV_COMPUNIT_TEXTLESS {
+ us.linesyms = d.writelines(u, us.lineProlog)
+ base := loader.Sym(u.Textp[0])
+ us.rangessyms = d.writepcranges(u, base, u.PCs, us.rangeProlog)
+ us.locsyms = d.collectUnitLocs(u)
+ }
+ us.infosyms = d.writeUnitInfo(u, abbrevsym, us.infoEpilog)
+}
+
+func (d *dwctxt) dwarfGenerateDebugSyms() {
+ abbrevSec := d.writeabbrev()
+ dwarfp = append(dwarfp, abbrevSec)
+ d.calcCompUnitRanges()
+ sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits))
+
+ // newdie adds DIEs to the *beginning* of the parent's DIE list.
+ // Now that we're done creating DIEs, reverse the trees so DIEs
+ // appear in the order they were created.
+ for _, u := range d.linkctxt.compUnits {
+ reversetree(&u.DWInfo.Child)
+ }
+ reversetree(&dwtypes.Child)
+ movetomodule(d.linkctxt, &dwtypes)
+
+ mkSecSym := func(name string) loader.Sym {
+ s := d.ldr.CreateSymForUpdate(name, 0)
+ s.SetType(sym.SDWARFSECT)
+ s.SetReachable(true)
+ return s.Sym()
+ }
+ mkAnonSym := func(kind sym.SymKind) loader.Sym {
+ s := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0))
+ s.SetType(kind)
+ s.SetReachable(true)
+ return s.Sym()
+ }
+
+ // Create the section symbols.
+ frameSym := mkSecSym(".debug_frame")
+ locSym := mkSecSym(".debug_loc")
+ lineSym := mkSecSym(".debug_line")
+ rangesSym := mkSecSym(".debug_ranges")
+ infoSym := mkSecSym(".debug_info")
+
+ // Create the section objects
+ lineSec := dwarfSecInfo{syms: []loader.Sym{lineSym}}
+ locSec := dwarfSecInfo{syms: []loader.Sym{locSym}}
+ rangesSec := dwarfSecInfo{syms: []loader.Sym{rangesSym}}
+ frameSec := dwarfSecInfo{syms: []loader.Sym{frameSym}}
+ infoSec := dwarfSecInfo{syms: []loader.Sym{infoSym}}
+
+ // Create any new symbols that will be needed during the
+ // parallel portion below.
+ ncu := len(d.linkctxt.compUnits)
+ unitSyms := make([]dwUnitSyms, ncu)
+ for i := 0; i < ncu; i++ {
+ us := &unitSyms[i]
+ us.lineProlog = mkAnonSym(sym.SDWARFLINES)
+ us.rangeProlog = mkAnonSym(sym.SDWARFRANGE)
+ us.infoEpilog = mkAnonSym(sym.SDWARFFCN)
+ }
+
+ var wg sync.WaitGroup
+ sema := make(chan struct{}, runtime.GOMAXPROCS(0))
+
+ // Kick off generation of .debug_frame, since it doesn't have
+ // any entanglements and can be started right away.
+ wg.Add(1)
+ go func() {
+ sema <- struct{}{}
+ defer func() {
+ <-sema
+ wg.Done()
+ }()
+ frameSec = d.writeframes(frameSym)
+ }()
+
+ // Create a goroutine per comp unit to handle the generation that
+ // unit's portion of .debug_line, .debug_loc, .debug_ranges, and
+ // .debug_info.
+ wg.Add(len(d.linkctxt.compUnits))
+ for i := 0; i < ncu; i++ {
+ go func(u *sym.CompilationUnit, us *dwUnitSyms) {
+ sema <- struct{}{}
+ defer func() {
+ <-sema
+ wg.Done()
+ }()
+ d.dwUnitPortion(u, abbrevSec.secSym(), us)
+ }(d.linkctxt.compUnits[i], &unitSyms[i])
+ }
+ wg.Wait()
+
+ markReachable := func(syms []loader.Sym) []loader.Sym {
+ for _, s := range syms {
+ d.ldr.SetAttrNotInSymbolTable(s, true)
+ d.ldr.SetAttrReachable(s, true)
+ }
+ return syms
+ }
+
+ // Stitch together the results.
+ for i := 0; i < ncu; i++ {
+ r := &unitSyms[i]
+ lineSec.syms = append(lineSec.syms, markReachable(r.linesyms)...)
+ infoSec.syms = append(infoSec.syms, markReachable(r.infosyms)...)
+ locSec.syms = append(locSec.syms, markReachable(r.locsyms)...)
+ rangesSec.syms = append(rangesSec.syms, markReachable(r.rangessyms)...)
+ }
+ dwarfp = append(dwarfp, lineSec)
+ dwarfp = append(dwarfp, frameSec)
+ gdbScriptSec := d.writegdbscript()
+ if gdbScriptSec.secSym() != 0 {
+ dwarfp = append(dwarfp, gdbScriptSec)
+ }
+ dwarfp = append(dwarfp, infoSec)
+ if len(locSec.syms) > 1 {
+ dwarfp = append(dwarfp, locSec)
+ }
+ dwarfp = append(dwarfp, rangesSec)
+
+ // Check to make sure we haven't listed any symbols more than once
+ // in the info section. This used to be done by setting and
+ // checking the OnList attribute in "putdie", but that strategy
+ // was not friendly for concurrency.
+ seen := loader.MakeBitmap(d.ldr.NSym())
+ for _, s := range infoSec.syms {
+ if seen.Has(s) {
+ log.Fatalf("symbol %s listed multiple times", d.ldr.SymName(s))
+ }
+ seen.Set(s)
+ }
+}
+
+func (d *dwctxt) collectUnitLocs(u *sym.CompilationUnit) []loader.Sym {
+ syms := []loader.Sym{}
+ for _, fn := range u.FuncDIEs {
+ relocs := d.ldr.Relocs(loader.Sym(fn))
+ for i := 0; i < relocs.Count(); i++ {
+ reloc := relocs.At(i)
+ if reloc.Type() != objabi.R_DWARFSECREF {
+ continue
+ }
+ rsym := reloc.Sym()
+ if d.ldr.SymType(rsym) == sym.SDWARFLOC {
+ syms = append(syms, rsym)
+ // One location list entry per function, but many relocations to it. Don't duplicate.
+ break
+ }
+ }
+ }
+ return syms
+}
+
+/*
+ * Elf.
+ */
+func dwarfaddshstrings(ctxt *Link, shstrtab *loader.SymbolBuilder) {
+ if *FlagW { // disable dwarf
+ return
+ }
+
+ secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts", "ranges"}
+ for _, sec := range secs {
+ shstrtab.Addstring(".debug_" + sec)
+ if ctxt.IsExternal() {
+ shstrtab.Addstring(elfRelType + ".debug_" + sec)
+ } else {
+ shstrtab.Addstring(".zdebug_" + sec)
+ }
+ }
+}
+
+func dwarfaddelfsectionsyms(ctxt *Link) {
+ if *FlagW { // disable dwarf
+ return
+ }
+ if ctxt.LinkMode != LinkExternal {
+ return
+ }
+
+ ldr := ctxt.loader
+ for _, si := range dwarfp {
+ s := si.secSym()
+ sect := ldr.SymSect(si.secSym())
+ putelfsectionsym(ctxt, ctxt.Out, s, sect.Elfsect.(*ElfShdr).shnum)
+ }
+}
+
+// dwarfcompress compresses the DWARF sections. Relocations are applied
+// on the fly. After this, dwarfp will contain a different (new) set of
+// symbols, and sections may have been replaced.
+func dwarfcompress(ctxt *Link) {
+ // compressedSect is a helper type for parallelizing compression.
+ type compressedSect struct {
+ index int
+ compressed []byte
+ syms []loader.Sym
+ }
+
+ supported := ctxt.IsELF || ctxt.IsWindows() || ctxt.IsDarwin()
+ if !ctxt.compressDWARF || !supported || ctxt.IsExternal() {
+ return
+ }
+
+ var compressedCount int
+ resChannel := make(chan compressedSect)
+ for i := range dwarfp {
+ go func(resIndex int, syms []loader.Sym) {
+ resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms}
+ }(compressedCount, dwarfp[i].syms)
+ compressedCount++
+ }
+ res := make([]compressedSect, compressedCount)
+ for ; compressedCount > 0; compressedCount-- {
+ r := <-resChannel
+ res[r.index] = r
+ }
+
+ ldr := ctxt.loader
+ var newDwarfp []dwarfSecInfo
+ Segdwarf.Sections = Segdwarf.Sections[:0]
+ for _, z := range res {
+ s := z.syms[0]
+ if z.compressed == nil {
+ // Compression didn't help.
+ ds := dwarfSecInfo{syms: z.syms}
+ newDwarfp = append(newDwarfp, ds)
+ Segdwarf.Sections = append(Segdwarf.Sections, ldr.SymSect(s))
+ } else {
+ var compressedSegName string
+ if ctxt.IsELF {
+ compressedSegName = ldr.SymSect(s).Name
+ } else {
+ compressedSegName = ".zdebug_" + ldr.SymSect(s).Name[len(".debug_"):]
+ }
+ sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04)
+ sect.Align = int32(ctxt.Arch.Alignment)
+ sect.Length = uint64(len(z.compressed))
+ sect.Compressed = true
+ newSym := ldr.MakeSymbolBuilder(compressedSegName)
+ ldr.SetAttrReachable(s, true)
+ newSym.SetData(z.compressed)
+ newSym.SetSize(int64(len(z.compressed)))
+ ldr.SetSymSect(newSym.Sym(), sect)
+ ds := dwarfSecInfo{syms: []loader.Sym{newSym.Sym()}}
+ newDwarfp = append(newDwarfp, ds)
+
+ // compressed symbols are no longer needed.
+ for _, s := range z.syms {
+ ldr.SetAttrReachable(s, false)
+ ldr.FreeSym(s)
+ }
+ }
+ }
+ dwarfp = newDwarfp
+
+ // Re-compute the locations of the compressed DWARF symbols
+ // and sections, since the layout of these within the file is
+ // based on Section.Vaddr and Symbol.Value.
+ pos := Segdwarf.Vaddr
+ var prevSect *sym.Section
+ for _, si := range dwarfp {
+ for _, s := range si.syms {
+ ldr.SetSymValue(s, int64(pos))
+ sect := ldr.SymSect(s)
+ if sect != prevSect {
+ sect.Vaddr = uint64(pos)
+ prevSect = sect
+ }
+ if ldr.SubSym(s) != 0 {
+ log.Fatalf("%s: unexpected sub-symbols", ldr.SymName(s))
+ }
+ pos += uint64(ldr.SymSize(s))
+ if ctxt.IsWindows() {
+ pos = uint64(Rnd(int64(pos), PEFILEALIGN))
+ }
+ }
+ }
+ Segdwarf.Length = pos - Segdwarf.Vaddr
+}
+
+type compilationUnitByStartPC []*sym.CompilationUnit
+
+func (v compilationUnitByStartPC) Len() int { return len(v) }
+func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
+
+func (v compilationUnitByStartPC) Less(i, j int) bool {
+ switch {
+ case len(v[i].Textp) == 0 && len(v[j].Textp) == 0:
+ return v[i].Lib.Pkg < v[j].Lib.Pkg
+ case len(v[i].Textp) != 0 && len(v[j].Textp) == 0:
+ return true
+ case len(v[i].Textp) == 0 && len(v[j].Textp) != 0:
+ return false
+ default:
+ return v[i].PCs[0].Start < v[j].PCs[0].Start
+ }
+}
+
+// getPkgFromCUSym returns the package name for the compilation unit
+// represented by s.
+// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get
+// the package name.
+func (d *dwctxt) getPkgFromCUSym(s loader.Sym) string {
+ return strings.TrimPrefix(d.ldr.SymName(s), dwarf.InfoPrefix+".pkg.")
+}
+
+// On AIX, the symbol table needs to know where are the compilation units parts
+// for a specific package in each .dw section.
+// dwsectCUSize map will save the size of a compilation unit for
+// the corresponding .dw section.
+// This size can later be retrieved with the index "sectionName.pkgName".
+var dwsectCUSizeMu sync.Mutex
+var dwsectCUSize map[string]uint64
+
+// getDwsectCUSize retrieves the corresponding package size inside the current section.
+func getDwsectCUSize(sname string, pkgname string) uint64 {
+ return dwsectCUSize[sname+"."+pkgname]
+}
+
+func addDwsectCUSize(sname string, pkgname string, size uint64) {
+ dwsectCUSizeMu.Lock()
+ defer dwsectCUSizeMu.Unlock()
+ dwsectCUSize[sname+"."+pkgname] += size
+}
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
new file mode 100644
index 0000000..a3db4a9
--- /dev/null
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -0,0 +1,1971 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "debug/dwarf"
+ "debug/pe"
+ "fmt"
+ "internal/testenv"
+ "io"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "testing"
+
+ intdwarf "cmd/internal/dwarf"
+ objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function
+ "cmd/link/internal/dwtest"
+)
+
+const (
+ DefaultOpt = "-gcflags="
+ NoOpt = "-gcflags=-l -N"
+ OptInl4 = "-gcflags=-l=4"
+ OptAllInl4 = "-gcflags=all=-l=4"
+)
+
+func TestRuntimeTypesPresent(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, `package main; func main() { }`, NoOpt)
+ defer f.Close()
+
+ dwarf, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ want := map[string]bool{
+ "runtime._type": true,
+ "runtime.arraytype": true,
+ "runtime.chantype": true,
+ "runtime.functype": true,
+ "runtime.maptype": true,
+ "runtime.ptrtype": true,
+ "runtime.slicetype": true,
+ "runtime.structtype": true,
+ "runtime.interfacetype": true,
+ "runtime.itab": true,
+ "runtime.imethod": true,
+ }
+
+ found := findTypes(t, dwarf, want)
+ if len(found) != len(want) {
+ t.Errorf("found %v, want %v", found, want)
+ }
+}
+
+func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) {
+ found = make(map[string]bool)
+ rdr := dw.Reader()
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ switch entry.Tag {
+ case dwarf.TagTypedef:
+ if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] {
+ found[name] = true
+ }
+ }
+ }
+ return
+}
+
+type builtFile struct {
+ *objfilepkg.File
+ path string
+}
+
+func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile {
+ src := filepath.Join(dir, "test.go")
+ dst := filepath.Join(dir, "out.exe")
+
+ if err := os.WriteFile(src, []byte(testfile), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", gcflags, "-o", dst, src)
+ b, err := cmd.CombinedOutput()
+ if len(b) != 0 {
+ t.Logf("## build output:\n%s", b)
+ }
+ if err != nil {
+ t.Fatalf("build error: %v", err)
+ }
+
+ f, err := objfilepkg.Open(dst)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return &builtFile{f, dst}
+}
+
+// Similar to gobuild() above, but uses a main package instead of a test.go file.
+
+func gobuildTestdata(t *testing.T, tdir string, pkgDir string, gcflags string) *builtFile {
+ dst := filepath.Join(tdir, "out.exe")
+
+ // Run a build with an updated GOPATH
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", gcflags, "-o", dst)
+ cmd.Dir = pkgDir
+ if b, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("build: %s\n", b)
+ t.Fatalf("build error: %v", err)
+ }
+
+ f, err := objfilepkg.Open(dst)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return &builtFile{f, dst}
+}
+
+func TestEmbeddedStructMarker(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ const prog = `
+package main
+
+import "fmt"
+
+type Foo struct { v int }
+type Bar struct {
+ Foo
+ name string
+}
+type Baz struct {
+ *Foo
+ name string
+}
+
+func main() {
+ bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"}
+ baz := Baz{ Foo: &bar.Foo, name: "123" }
+ fmt.Println(bar, baz)
+}`
+
+ want := map[string]map[string]bool{
+ "main.Foo": {"v": false},
+ "main.Bar": {"Foo": true, "name": false},
+ "main.Baz": {"Foo": true, "name": false},
+ }
+
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ switch entry.Tag {
+ case dwarf.TagStructType:
+ name := entry.Val(dwarf.AttrName).(string)
+ wantMembers := want[name]
+ if wantMembers == nil {
+ continue
+ }
+ gotMembers, err := findMembers(rdr)
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ if !reflect.DeepEqual(gotMembers, wantMembers) {
+ t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers)
+ }
+ delete(want, name)
+ }
+ }
+ if len(want) != 0 {
+ t.Errorf("failed to check all expected types: missing types = %+v", want)
+ }
+}
+
+func findMembers(rdr *dwarf.Reader) (map[string]bool, error) {
+ memberEmbedded := map[string]bool{}
+ // TODO(hyangah): define in debug/dwarf package
+ const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field)
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ return nil, err
+ }
+ switch entry.Tag {
+ case dwarf.TagMember:
+ name := entry.Val(dwarf.AttrName).(string)
+ embedded := entry.Val(goEmbeddedStruct).(bool)
+ memberEmbedded[name] = embedded
+ case 0:
+ return memberEmbedded, nil
+ }
+ }
+ return memberEmbedded, nil
+}
+
+func TestSizes(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ // External linking may bring in C symbols with unknown size. Skip.
+ testenv.MustInternalLink(t)
+
+ t.Parallel()
+
+ // DWARF sizes should never be -1.
+ // See issue #21097
+ const prog = `
+package main
+var x func()
+var y [4]func()
+func main() {
+ x = nil
+ y[0] = nil
+}
+`
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ rdr := d.Reader()
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ switch entry.Tag {
+ case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
+ default:
+ continue
+ }
+ typ, err := d.Type(entry.Offset)
+ if err != nil {
+ t.Fatalf("can't read type: %v", err)
+ }
+ if typ.Size() < 0 {
+ t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ)
+ }
+ }
+}
+
+func TestFieldOverlap(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test grew out of issue 21094, where specific sudog<T> DWARF types
+ // had elem fields set to values instead of pointers.
+ const prog = `
+package main
+
+var c chan string
+
+func main() {
+ c <- "foo"
+}
+`
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if entry.Tag != dwarf.TagStructType {
+ continue
+ }
+ typ, err := d.Type(entry.Offset)
+ if err != nil {
+ t.Fatalf("can't read type: %v", err)
+ }
+ s := typ.(*dwarf.StructType)
+ for i := 0; i < len(s.Field); i++ {
+ end := s.Field[i].ByteOffset + s.Field[i].Type.Size()
+ var limit int64
+ if i == len(s.Field)-1 {
+ limit = s.Size()
+ } else {
+ limit = s.Field[i+1].ByteOffset
+ }
+ if end > limit {
+ name := entry.Val(dwarf.AttrName).(string)
+ t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name)
+ }
+ }
+ }
+}
+
+func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) {
+ t.Parallel()
+
+ prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive)
+
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.main DIE
+ mains := ex.Named("main.main")
+ if len(mains) == 0 {
+ t.Fatalf("unable to locate DIE for main.main")
+ }
+ if len(mains) != 1 {
+ t.Fatalf("more than one main.main DIE")
+ }
+ maindie := mains[0]
+
+ // Vet the main.main DIE
+ if maindie.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
+ }
+
+ // Walk main's children and select variable "i".
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
+ childDies := ex.Children(mainIdx)
+ var iEntry *dwarf.Entry
+ for _, child := range childDies {
+ if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" {
+ iEntry = child
+ break
+ }
+ }
+ if iEntry == nil {
+ t.Fatalf("didn't find DW_TAG_variable for i in main.main")
+ }
+
+ // Verify line/file attributes.
+ line := iEntry.Val(dwarf.AttrDeclLine)
+ if line == nil || line.(int64) != int64(expectLine) {
+ t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine)
+ }
+
+ fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64)
+ if !fileIdxOK {
+ t.Errorf("missing or invalid DW_AT_decl_file for main")
+ }
+ file, err := ex.FileRef(d, mainIdx, fileIdx)
+ if err != nil {
+ t.Fatalf("FileRef: %v", err)
+ }
+ base := filepath.Base(file)
+ if base != expectFile {
+ t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile)
+ }
+}
+
+func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", "test.go", 5, "")
+}
+
+func TestVarDeclCoordsWithLineDirective(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective",
+ "foobar.go", 202, "//line /foobar.go:200")
+}
+
+func TestInlinedRoutineRecords(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ const prog = `
+package main
+
+var G int
+
+func noinline(x int) int {
+ defer func() { G += x }()
+ return x
+}
+
+func cand(x, y int) int {
+ return noinline(x+y) ^ (y - x)
+}
+
+func main() {
+ x := cand(G*G,G|7%G)
+ G = x
+}
+`
+ dir := t.TempDir()
+
+ // Note: this is a build with "-l=4", as opposed to "-l -N". The
+ // test is intended to verify DWARF that is only generated when
+ // the inliner is active. We're only going to look at the DWARF for
+ // main.main, however, hence we build with "-gcflags=-l=4" as opposed
+ // to "-gcflags=all=-l=4".
+ f := gobuild(t, dir, prog, OptInl4)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // The inlined subroutines we expect to visit
+ expectedInl := []string{"main.cand"}
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.main DIE
+ mains := ex.Named("main.main")
+ if len(mains) == 0 {
+ t.Fatalf("unable to locate DIE for main.main")
+ }
+ if len(mains) != 1 {
+ t.Fatalf("more than one main.main DIE")
+ }
+ maindie := mains[0]
+
+ // Vet the main.main DIE
+ if maindie.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
+ }
+
+ // Walk main's children and pick out the inlined subroutines
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
+ childDies := ex.Children(mainIdx)
+ exCount := 0
+ for _, child := range childDies {
+ if child.Tag == dwarf.TagInlinedSubroutine {
+ // Found an inlined subroutine, locate abstract origin.
+ ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+ if !originOK {
+ t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
+ }
+ originDIE := ex.EntryFromOffset(ooff)
+ if originDIE == nil {
+ t.Fatalf("can't locate origin DIE at off %v", ooff)
+ }
+
+ // Walk the children of the abstract subroutine. We expect
+ // to see child variables there, even if (perhaps due to
+ // optimization) there are no references to them from the
+ // inlined subroutine DIE.
+ absFcnIdx := ex.IdxFromOffset(ooff)
+ absFcnChildDies := ex.Children(absFcnIdx)
+ if len(absFcnChildDies) != 2 {
+ t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies))
+ }
+ formalCount := 0
+ for _, absChild := range absFcnChildDies {
+ if absChild.Tag == dwarf.TagFormalParameter {
+ formalCount += 1
+ continue
+ }
+ t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag)
+ }
+ if formalCount != 2 {
+ t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount)
+ }
+
+ if exCount >= len(expectedInl) {
+ t.Fatalf("too many inlined subroutines found in main.main")
+ }
+
+ // Name should check out.
+ expected := expectedInl[exCount]
+ if name, ok := originDIE.Val(dwarf.AttrName).(string); ok {
+ if name != expected {
+ t.Fatalf("expected inlined routine %s got %s", name, expected)
+ }
+ }
+ exCount++
+
+ // Verify that the call_file attribute for the inlined
+ // instance is ok. In this case it should match the file
+ // for the main routine. To do this we need to locate the
+ // compilation unit DIE that encloses what we're looking
+ // at; this can be done with the examiner.
+ cf, cfOK := child.Val(dwarf.AttrCallFile).(int64)
+ if !cfOK {
+ t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset)
+ }
+ file, err := ex.FileRef(d, mainIdx, cf)
+ if err != nil {
+ t.Errorf("FileRef: %v", err)
+ continue
+ }
+ base := filepath.Base(file)
+ if base != "test.go" {
+ t.Errorf("bad call_file attribute, found '%s', want '%s'",
+ file, "test.go")
+ }
+
+ omap := make(map[dwarf.Offset]bool)
+
+ // Walk the child variables of the inlined routine. Each
+ // of them should have a distinct abstract origin-- if two
+ // vars point to the same origin things are definitely broken.
+ inlIdx := ex.IdxFromOffset(child.Offset)
+ inlChildDies := ex.Children(inlIdx)
+ for _, k := range inlChildDies {
+ ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+ if !originOK {
+ t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset)
+ }
+ if _, found := omap[ooff]; found {
+ t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset)
+ }
+ omap[ooff] = true
+ }
+ }
+ }
+ if exCount != len(expectedInl) {
+ t.Fatalf("not enough inlined subroutines found in main.main")
+ }
+}
+
+func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ // Build with inlining, to exercise DWARF inlining support.
+ f := gobuildTestdata(t, dir, filepath.Join(pkgDir, "main"), flags)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Make a pass through all DIEs looking for abstract origin
+ // references.
+ abscount := 0
+ for i, die := range ex.DIEs() {
+ // Does it have an abstract origin?
+ ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+ if !originOK {
+ continue
+ }
+
+ // All abstract origin references should be resolvable.
+ abscount += 1
+ originDIE := ex.EntryFromOffset(ooff)
+ if originDIE == nil {
+ ex.DumpEntry(i, false, 0)
+ t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset)
+ }
+
+ // Suppose that DIE X has parameter/variable children {K1,
+ // K2, ... KN}. If X has an abstract origin of A, then for
+ // each KJ, the abstract origin of KJ should be a child of A.
+ // Note that this same rule doesn't hold for non-variable DIEs.
+ pidx := ex.IdxFromOffset(die.Offset)
+ if pidx < 0 {
+ t.Fatalf("can't locate DIE id")
+ }
+ kids := ex.Children(pidx)
+ for _, kid := range kids {
+ if kid.Tag != dwarf.TagVariable &&
+ kid.Tag != dwarf.TagFormalParameter {
+ continue
+ }
+ kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+ if !originOK {
+ continue
+ }
+ childOriginDIE := ex.EntryFromOffset(kooff)
+ if childOriginDIE == nil {
+ ex.DumpEntry(i, false, 0)
+ t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset)
+ }
+ coidx := ex.IdxFromOffset(childOriginDIE.Offset)
+ childOriginParent := ex.Parent(coidx)
+ if childOriginParent != originDIE {
+ ex.DumpEntry(i, false, 0)
+ t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset)
+ }
+ }
+ }
+ if abscount == 0 {
+ t.Fatalf("no abstract origin refs found, something is wrong")
+ }
+}
+
+func TestAbstractOriginSanity(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ if wd, err := os.Getwd(); err == nil {
+ gopathdir := filepath.Join(wd, "testdata", "httptest")
+ abstractOriginSanity(t, gopathdir, OptAllInl4)
+ } else {
+ t.Fatalf("os.Getwd() failed %v", err)
+ }
+}
+
+func TestAbstractOriginSanityIssue25459(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ if runtime.GOARCH != "amd64" && runtime.GOARCH != "386" {
+ t.Skip("skipping on not-amd64 not-386; location lists not supported")
+ }
+
+ if wd, err := os.Getwd(); err == nil {
+ gopathdir := filepath.Join(wd, "testdata", "issue25459")
+ abstractOriginSanity(t, gopathdir, DefaultOpt)
+ } else {
+ t.Fatalf("os.Getwd() failed %v", err)
+ }
+}
+
+func TestAbstractOriginSanityIssue26237(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ if wd, err := os.Getwd(); err == nil {
+ gopathdir := filepath.Join(wd, "testdata", "issue26237")
+ abstractOriginSanity(t, gopathdir, DefaultOpt)
+ } else {
+ t.Fatalf("os.Getwd() failed %v", err)
+ }
+}
+
+func TestRuntimeTypeAttrInternal(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustInternalLink(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ if runtime.GOOS == "windows" {
+ t.Skip("skipping on windows; test is incompatible with relocatable binaries")
+ }
+
+ testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal")
+}
+
+// External linking requires a host linker (https://golang.org/src/cmd/cgo/doc.go l.732)
+func TestRuntimeTypeAttrExternal(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ // Explicitly test external linking, for dsymutil compatibility on Darwin.
+ if runtime.GOARCH == "ppc64" {
+ t.Skip("-linkmode=external not supported on ppc64")
+ }
+
+ if runtime.GOOS == "windows" {
+ t.Skip("skipping on windows; test is incompatible with relocatable binaries")
+ }
+
+ testRuntimeTypeAttr(t, "-ldflags=-linkmode=external")
+}
+
+func testRuntimeTypeAttr(t *testing.T, flags string) {
+ t.Parallel()
+
+ const prog = `
+package main
+
+import "unsafe"
+
+type X struct{ _ int }
+
+func main() {
+ var x interface{} = &X{}
+ p := *(*uintptr)(unsafe.Pointer(&x))
+ print(p)
+}
+`
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, flags)
+ defer f.Close()
+
+ out, err := testenv.Command(t, f.path).CombinedOutput()
+ if err != nil {
+ t.Fatalf("could not run test program: %v", err)
+ }
+ addr, err := strconv.ParseUint(string(out), 10, 64)
+ if err != nil {
+ t.Fatalf("could not parse type address from program output %q: %v", out, err)
+ }
+
+ symbols, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("error reading symbols: %v", err)
+ }
+ var types *objfilepkg.Sym
+ for _, sym := range symbols {
+ if sym.Name == "runtime.types" {
+ types = &sym
+ break
+ }
+ }
+ if types == nil {
+ t.Fatal("couldn't find runtime.types in symbols")
+ }
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ dies := ex.Named("*main.X")
+ if len(dies) != 1 {
+ t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
+ }
+ rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type)
+ if rtAttr == nil {
+ t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
+ }
+
+ if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
+ return // everything is PIE on ARM64, addresses are relocated
+ }
+ if rtAttr.(uint64)+types.Addr != addr {
+ t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
+ }
+}
+
+func TestIssue27614(t *testing.T) {
+ // Type references in debug_info should always use the DW_TAG_typedef_type
+ // for the type, when that's generated.
+
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ const prog = `package main
+
+import "fmt"
+
+type astruct struct {
+ X int
+}
+
+type bstruct struct {
+ X float32
+}
+
+var globalptr *astruct
+var globalvar astruct
+var bvar0, bvar1, bvar2 bstruct
+
+func main() {
+ fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
+}
+`
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ defer f.Close()
+
+ data, err := f.DWARF()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rdr := data.Reader()
+
+ var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
+ var globalptrDIE, globalvarDIE *dwarf.Entry
+ var bvarDIE [3]*dwarf.Entry
+
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if e == nil {
+ break
+ }
+
+ name, _ := e.Val(dwarf.AttrName).(string)
+
+ switch e.Tag {
+ case dwarf.TagTypedef:
+ switch name {
+ case "main.astruct":
+ astructTypeDIE = e
+ case "main.bstruct":
+ bstructTypeDIE = e
+ }
+ case dwarf.TagPointerType:
+ if name == "*main.astruct" {
+ ptrastructTypeDIE = e
+ }
+ case dwarf.TagVariable:
+ switch name {
+ case "main.globalptr":
+ globalptrDIE = e
+ case "main.globalvar":
+ globalvarDIE = e
+ default:
+ const bvarprefix = "main.bvar"
+ if strings.HasPrefix(name, bvarprefix) {
+ i, _ := strconv.Atoi(name[len(bvarprefix):])
+ bvarDIE[i] = e
+ }
+ }
+ }
+ }
+
+ typedieof := func(e *dwarf.Entry) dwarf.Offset {
+ return e.Val(dwarf.AttrType).(dwarf.Offset)
+ }
+
+ if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
+ t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
+ }
+
+ if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
+ t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
+ }
+
+ if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
+ t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
+ }
+
+ for i := range bvarDIE {
+ if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
+ t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
+ }
+ }
+}
+
+func TestStaticTmp(t *testing.T) {
+ // Checks that statictmp variables do not appear in debug_info or the
+ // symbol table.
+ // Also checks that statictmp variables do not collide with user defined
+ // variables (issue #25113)
+
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ const prog = `package main
+
+var stmp_0 string
+var a []int
+
+func init() {
+ a = []int{ 7 }
+}
+
+func main() {
+ println(a[0])
+}
+`
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagVariable {
+ continue
+ }
+ name, ok := e.Val(dwarf.AttrName).(string)
+ if !ok {
+ continue
+ }
+ if strings.Contains(name, "stmp") {
+ t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset)
+ }
+ }
+
+ // When external linking, we put all symbols in the symbol table (so the
+ // external linker can find them). Skip the symbol table check.
+ // TODO: maybe there is some way to tell the external linker not to put
+ // those symbols in the executable's symbol table? Prefix the symbol name
+ // with "." or "L" to pretend it is a label?
+ if !testenv.CanInternalLink() {
+ return
+ }
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("error reading symbols: %v", err)
+ }
+ for _, sym := range syms {
+ if strings.Contains(sym.Name, "stmp") {
+ t.Errorf("statictmp variable found in symbol table: %s", sym.Name)
+ }
+ }
+}
+
+func TestPackageNameAttr(t *testing.T) {
+ const dwarfAttrGoPackageName = dwarf.Attr(0x2905)
+ const dwarfGoLanguage = 22
+
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n"
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ runtimeUnitSeen := false
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage {
+ continue
+ }
+
+ pn, ok := e.Val(dwarfAttrGoPackageName).(string)
+ if !ok {
+ name, _ := e.Val(dwarf.AttrName).(string)
+ t.Errorf("found compile unit without package name: %s", name)
+
+ }
+ if pn == "" {
+ name, _ := e.Val(dwarf.AttrName).(string)
+ t.Errorf("found compile unit with empty package name: %s", name)
+ } else {
+ if pn == "runtime" {
+ runtimeUnitSeen = true
+ }
+ }
+ }
+
+ // Something is wrong if there's no runtime compilation unit.
+ if !runtimeUnitSeen {
+ t.Errorf("no package name for runtime unit")
+ }
+}
+
+func TestMachoIssue32233(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ if runtime.GOOS != "darwin" {
+ t.Skip("skipping; test only interesting on darwin")
+ }
+
+ tmpdir := t.TempDir()
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue32233", "main")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+ f.Close()
+}
+
+func TestWindowsIssue36495(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ if runtime.GOOS != "windows" {
+ t.Skip("skipping: test only on windows")
+ }
+
+ dir := t.TempDir()
+
+ prog := `
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello World")
+}`
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+ exe, err := pe.Open(f.path)
+ if err != nil {
+ t.Fatalf("error opening pe file: %v", err)
+ }
+ defer exe.Close()
+ dw, err := exe.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ lnrdr, err := dw.LineReader(e)
+ if err != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr != nil {
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if strings.Contains(lne.File.Name, `\`) {
+ t.Errorf("filename should not contain backslash: %v", lne.File.Name)
+ }
+ }
+ }
+ rdr.SkipChildren()
+ }
+}
+
+func TestIssue38192(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ // Build a test program that contains a translation unit whose
+ // text (from am assembly source) contains only a single instruction.
+ tmpdir := t.TempDir()
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue38192")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+ defer f.Close()
+
+ // Open the resulting binary and examine the DWARF it contains.
+ // Look for the function of interest ("main.singleInstruction")
+ // and verify that the line table has an entry not just for the
+ // single instruction but also a dummy instruction following it,
+ // so as to test that whoever is emitting the DWARF doesn't
+ // emit an end-sequence op immediately after the last instruction
+ // in the translation unit.
+ //
+ // NB: another way to write this test would have been to run the
+ // resulting executable under GDB, set a breakpoint in
+ // "main.singleInstruction", then verify that GDB displays the
+ // correct line/file information. Given the headache and flakiness
+ // associated with GDB-based tests these days, a direct read of
+ // the line table seems more desirable.
+ rows := []dwarf.LineEntry{}
+ dw, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ // NB: there can be multiple compile units named "main".
+ name := e.Val(dwarf.AttrName).(string)
+ if name != "main" {
+ continue
+ }
+ lnrdr, err := dw.LineReader(e)
+ if err != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr != nil {
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") {
+ continue
+ }
+ rows = append(rows, lne)
+ }
+ }
+ rdr.SkipChildren()
+ }
+ f.Close()
+
+ // Make sure that:
+ // - main.singleInstruction appears in the line table
+ // - more than one PC value appears the line table for
+ // that compilation unit.
+ // - at least one row has the correct line number (8)
+ pcs := make(map[uint64]bool)
+ line8seen := false
+ for _, r := range rows {
+ pcs[r.Address] = true
+ if r.Line == 8 {
+ line8seen = true
+ }
+ }
+ failed := false
+ if len(pcs) < 2 {
+ failed = true
+ t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs))
+ }
+ if !line8seen {
+ failed = true
+ t.Errorf("line table does not contain correct line for main.singleInstruction")
+ }
+ if !failed {
+ return
+ }
+ for i, r := range rows {
+ t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
+ }
+}
+
+func TestIssue39757(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ // In this bug the DWARF line table contents for the last couple of
+ // instructions in a function were incorrect (bad file/line). This
+ // test verifies that all of the line table rows for a function
+ // of interest have the same file (no "autogenerated").
+ //
+ // Note: the function in this test was written with an eye towards
+ // ensuring that there are no inlined routines from other packages
+ // (which could introduce other source files into the DWARF); it's
+ // possible that at some point things could evolve in the
+ // compiler/runtime in ways that aren't happening now, so this
+ // might be something to check for if it does start failing.
+
+ tmpdir := t.TempDir()
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue39757")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+ defer f.Close()
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var addr uint64
+ for _, sym := range syms {
+ if sym.Name == "main.main" {
+ addr = sym.Addr
+ break
+ }
+ }
+ if addr == 0 {
+ t.Fatal("cannot find main.main in symbols")
+ }
+
+ // Open the resulting binary and examine the DWARF it contains.
+ // Look for the function of interest ("main.main")
+ // and verify that all line table entries show the same source
+ // file.
+ dw, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.main DIE
+ mains := ex.Named("main.main")
+ if len(mains) == 0 {
+ t.Fatalf("unable to locate DIE for main.main")
+ }
+ if len(mains) != 1 {
+ t.Fatalf("more than one main.main DIE")
+ }
+ maindie := mains[0]
+
+ // Collect the start/end PC for main.main
+ lowpc := maindie.Val(dwarf.AttrLowpc).(uint64)
+ highpc := maindie.Val(dwarf.AttrHighpc).(uint64)
+
+ // Now read the line table for the 'main' compilation unit.
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
+ cuentry := ex.Parent(mainIdx)
+ if cuentry == nil {
+ t.Fatalf("main.main DIE appears orphaned")
+ }
+ lnrdr, lerr := dw.LineReader(cuentry)
+ if lerr != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr == nil {
+ t.Fatalf("no line table for main.main compilation unit")
+ }
+ rows := []dwarf.LineEntry{}
+ mainrows := 0
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ rows = append(rows, lne)
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if lne.Address < lowpc || lne.Address > highpc {
+ continue
+ }
+ if !strings.HasSuffix(lne.File.Name, "issue39757main.go") {
+ t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name)
+ }
+ mainrows++
+ }
+ f.Close()
+
+ // Make sure we saw a few rows.
+ if mainrows < 3 {
+ t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows)
+ for i, r := range rows {
+ t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
+ }
+ }
+}
+
+func TestIssue42484(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ tmpdir, err := os.MkdirTemp("", "TestIssue42484")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue42484")
+ f := gobuildTestdata(t, tmpdir, pdir, NoOpt)
+
+ var lastAddr uint64
+ var lastFile string
+ var lastLine int
+
+ dw, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ lnrdr, err := dw.LineReader(e)
+ if err != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr != nil {
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if lne.EndSequence {
+ continue
+ }
+ if lne.Address == lastAddr && (lne.File.Name != lastFile || lne.Line != lastLine) {
+ t.Errorf("address %#x is assigned to both %s:%d and %s:%d", lastAddr, lastFile, lastLine, lne.File.Name, lne.Line)
+ }
+ lastAddr = lne.Address
+ lastFile = lne.File.Name
+ lastLine = lne.Line
+ }
+ }
+ rdr.SkipChildren()
+ }
+ f.Close()
+}
+
+// processParams examines the formal parameter children of subprogram
+// DIE "die" using the explorer "ex" and returns a string that
+// captures the name, order, and classification of the subprogram's
+// input and output parameters. For example, for the go function
+//
+// func foo(i1 int, f1 float64) (string, bool) {
+//
+// this function would return a string something like
+//
+// i1:0:1 f1:1:1 ~r0:2:2 ~r1:3:2
+//
+// where each chunk above is of the form NAME:ORDER:INOUTCLASSIFICATION
+func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string {
+ // Values in the returned map are of the form <order>:<varparam>
+ // where order is the order within the child DIE list of the
+ // param, and <varparam> is an integer:
+ //
+ // -1: varparm attr not found
+ // 1: varparm found with value false
+ // 2: varparm found with value true
+ //
+ foundParams := make(map[string]string)
+
+ // Walk the subprogram DIE's children looking for params.
+ pIdx := ex.IdxFromOffset(die.Offset)
+ childDies := ex.Children(pIdx)
+ idx := 0
+ for _, child := range childDies {
+ if child.Tag == dwarf.TagFormalParameter {
+ // NB: a setting of DW_AT_variable_parameter indicates
+ // that the param in question is an output parameter; we
+ // want to see this attribute set to TRUE for all Go
+ // return params. It would be OK to have it missing for
+ // input parameters, but for the moment we verify that the
+ // attr is present but set to false.
+ st := -1
+ if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok {
+ if vp {
+ st = 2
+ } else {
+ st = 1
+ }
+ }
+ if name, ok := child.Val(dwarf.AttrName).(string); ok {
+ foundParams[name] = fmt.Sprintf("%d:%d", idx, st)
+ idx++
+ }
+ }
+ }
+
+ found := make([]string, 0, len(foundParams))
+ for k, v := range foundParams {
+ found = append(found, fmt.Sprintf("%s:%s", k, v))
+ }
+ sort.Strings(found)
+
+ return fmt.Sprintf("%+v", found)
+}
+
+func TestOutputParamAbbrevAndAttr(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test verifies that the compiler is selecting the correct
+ // DWARF abbreviation for output parameters, and that the
+ // variable parameter attribute is correct for in-params and
+ // out-params.
+
+ const prog = `
+package main
+
+//go:noinline
+func ABC(c1, c2, c3 int, d1, d2, d3, d4 string, f1, f2, f3 float32, g1 [1024]int) (r1 int, r2 int, r3 [1024]int, r4 byte, r5 string, r6 float32) {
+ g1[0] = 6
+ r1, r2, r3, r4, r5, r6 = c3, c2+c1, g1, 'a', d1+d2+d3+d4, f1+f2+f3
+ return
+}
+
+func main() {
+ a := [1024]int{}
+ v1, v2, v3, v4, v5, v6 := ABC(1, 2, 3, "a", "b", "c", "d", 1.0, 2.0, 1.0, a)
+ println(v1, v2, v3[0], v4, v5, v6)
+}
+`
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.ABC DIE
+ abcs := ex.Named("main.ABC")
+ if len(abcs) == 0 {
+ t.Fatalf("unable to locate DIE for main.ABC")
+ }
+ if len(abcs) != 1 {
+ t.Fatalf("more than one main.ABC DIE")
+ }
+ abcdie := abcs[0]
+
+ // Vet the DIE
+ if abcdie.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on main.ABC DIE", abcdie.Tag)
+ }
+
+ // Call a helper to collect param info.
+ found := processParams(abcdie, &ex)
+
+ // Make sure we see all of the expected params in the proper
+ // order, that they have the varparam attr, and the varparam is
+ // set for the returns.
+ expected := "[c1:0:1 c2:1:1 c3:2:1 d1:3:1 d2:4:1 d3:5:1 d4:6:1 f1:7:1 f2:8:1 f3:9:1 g1:10:1 r1:11:2 r2:12:2 r3:13:2 r4:14:2 r5:15:2 r6:16:2]"
+ if found != expected {
+ t.Errorf("param check failed, wanted:\n%s\ngot:\n%s\n",
+ expected, found)
+ }
+}
+
+func TestDictIndex(t *testing.T) {
+ // Check that variables with a parametric type have a dictionary index
+ // attribute and that types that are only referenced through dictionaries
+ // have DIEs.
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ const prog = `
+package main
+
+import "fmt"
+
+type CustomInt int
+
+func testfn[T any](arg T) {
+ var mapvar = make(map[int]T)
+ mapvar[0] = arg
+ fmt.Println(arg, mapvar)
+}
+
+func main() {
+ testfn(CustomInt(3))
+}
+`
+
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ found := false
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ name, _ := entry.Val(dwarf.AttrName).(string)
+ if strings.HasPrefix(name, "main.testfn") {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Fatalf("could not find main.testfn")
+ }
+
+ offs := []dwarf.Offset{}
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if entry.Tag == 0 {
+ break
+ }
+ name, _ := entry.Val(dwarf.AttrName).(string)
+ switch name {
+ case "arg", "mapvar":
+ offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset))
+ }
+ }
+ if len(offs) != 2 {
+ t.Errorf("wrong number of variables found in main.testfn %d", len(offs))
+ }
+ for _, off := range offs {
+ rdr.Seek(off)
+ entry, err := rdr.Next()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok {
+ t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index))
+ }
+ }
+
+ rdr.Seek(0)
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} {
+ dies := ex.Named(typeName)
+ if len(dies) != 1 {
+ t.Errorf("wanted 1 DIE named %s, found %v", typeName, len(dies))
+ }
+ if dies[0].Val(intdwarf.DW_AT_go_runtime_type).(uint64) == 0 {
+ t.Errorf("type %s does not have DW_AT_go_runtime_type", typeName)
+ }
+ }
+}
+
+func TestOptimizedOutParamHandling(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test is intended to verify that the compiler emits DWARF
+ // DIE entries for all input and output parameters, and that:
+ //
+ // - attributes are set correctly for output params,
+ // - things appear in the proper order
+ // - things work properly for both register-resident
+ // params and params passed on the stack
+ // - things work for both referenced and unreferenced params
+ // - things work for named return values un-named return vals
+ //
+ // The scenarios below don't cover all possible permutations and
+ // combinations, but they hit a bunch of the high points.
+
+ const prog = `
+package main
+
+// First testcase. All input params in registers, all params used.
+
+//go:noinline
+func tc1(p1, p2 int, p3 string) (int, string) {
+ return p1 + p2, p3 + "foo"
+}
+
+// Second testcase. Some params in registers, some on stack.
+
+//go:noinline
+func tc2(p1 int, p2 [128]int, p3 string) (int, string, [128]int) {
+ return p1 + p2[p1], p3 + "foo", [128]int{p1}
+}
+
+// Third testcase. Named return params.
+
+//go:noinline
+func tc3(p1 int, p2 [128]int, p3 string) (r1 int, r2 bool, r3 string, r4 [128]int) {
+ if p1 == 101 {
+ r1 = p1 + p2[p1]
+ r2 = p3 == "foo"
+ r4 = [128]int{p1}
+ return
+ } else {
+ return p1 - p2[p1+3], false, "bar", [128]int{p1 + 2}
+ }
+}
+
+// Fourth testcase. Some thing are used, some are unused.
+
+//go:noinline
+func tc4(p1, p1un int, p2, p2un [128]int, p3, p3un string) (r1 int, r1un int, r2 bool, r3 string, r4, r4un [128]int) {
+ if p1 == 101 {
+ r1 = p1 + p2[p2[0]]
+ r2 = p3 == "foo"
+ r4 = [128]int{p1}
+ return
+ } else {
+ return p1, -1, true, "plex", [128]int{p1 + 2}, [128]int{-1}
+ }
+}
+
+func main() {
+ {
+ r1, r2 := tc1(3, 4, "five")
+ println(r1, r2)
+ }
+ {
+ x := [128]int{9}
+ r1, r2, r3 := tc2(3, x, "five")
+ println(r1, r2, r3[0])
+ }
+ {
+ x := [128]int{9}
+ r1, r2, r3, r4 := tc3(3, x, "five")
+ println(r1, r2, r3, r4[0])
+ }
+ {
+ x := [128]int{3}
+ y := [128]int{7}
+ r1, r1u, r2, r3, r4, r4u := tc4(0, 1, x, y, "a", "b")
+ println(r1, r1u, r2, r3, r4[0], r4u[1])
+ }
+
+}
+`
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, DefaultOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ testcases := []struct {
+ tag string
+ expected string
+ }{
+ {
+ tag: "tc1",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2]",
+ },
+ {
+ tag: "tc2",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2 ~r2:5:2]",
+ },
+ {
+ tag: "tc3",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 r1:3:2 r2:4:2 r3:5:2 r4:6:2]",
+ },
+ {
+ tag: "tc4",
+ expected: "[p1:0:1 p1un:1:1 p2:2:1 p2un:3:1 p3:4:1 p3un:5:1 r1:6:2 r1un:7:2 r2:8:2 r3:9:2 r4:10:2 r4un:11:2]",
+ },
+ }
+
+ for _, tc := range testcases {
+ // Locate the proper DIE
+ which := fmt.Sprintf("main.%s", tc.tag)
+ tcs := ex.Named(which)
+ if len(tcs) == 0 {
+ t.Fatalf("unable to locate DIE for " + which)
+ }
+ if len(tcs) != 1 {
+ t.Fatalf("more than one " + which + " DIE")
+ }
+ die := tcs[0]
+
+ // Vet the DIE
+ if die.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on "+which+" DIE", die.Tag)
+ }
+
+ // Examine params for this subprogram.
+ foundParams := processParams(die, &ex)
+ if foundParams != tc.expected {
+ t.Errorf("check failed for testcase %s -- wanted:\n%s\ngot:%s\n",
+ tc.tag, tc.expected, foundParams)
+ }
+ }
+}
+func TestIssue54320(t *testing.T) {
+ // Check that when trampolines are used, the DWARF LPT is correctly
+ // emitted in the final binary
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ const prog = `
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Printf("Hello world\n");
+}
+`
+
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2")
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ found := false
+ var entry *dwarf.Entry
+ for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if entry.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ name, _ := entry.Val(dwarf.AttrName).(string)
+ if name == "main" {
+ found = true
+ break
+ }
+ rdr.SkipChildren()
+ }
+
+ if !found {
+ t.Fatalf("could not find main compile unit")
+ }
+ lr, err := d.LineReader(entry)
+ if err != nil {
+ t.Fatalf("error obtaining linereader: %v", err)
+ }
+
+ var le dwarf.LineEntry
+ found = false
+ for {
+ if err := lr.Next(&le); err != nil {
+ if err == io.EOF {
+ break
+ }
+ t.Fatalf("error reading linentry: %v", err)
+ }
+ // check LE contains an entry to test.go
+ if le.File == nil {
+ continue
+ }
+ file := filepath.Base(le.File.Name)
+ if file == "test.go" {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("no LPT entries for test.go")
+ }
+}
+
+func TestZeroSizedVariable(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test verifies that the compiler emits DIEs for zero sized variables
+ // (for example variables of type 'struct {}').
+ // See go.dev/issues/54615.
+
+ const prog = `
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ zeroSizedVariable := struct{}{}
+ fmt.Println(zeroSizedVariable)
+}
+`
+
+ for _, opt := range []string{NoOpt, DefaultOpt} {
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, opt)
+ defer f.Close()
+ defer os.RemoveAll(dir)
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.zeroSizedVariable DIE
+ abcs := ex.Named("zeroSizedVariable")
+ if len(abcs) == 0 {
+ t.Fatalf("unable to locate DIE for zeroSizedVariable")
+ }
+ if len(abcs) != 1 {
+ t.Fatalf("more than one zeroSizedVariable DIE")
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
new file mode 100644
index 0000000..a1ae7ea
--- /dev/null
+++ b/src/cmd/link/internal/ld/elf.go
@@ -0,0 +1,2395 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/notsha256"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "internal/buildcfg"
+ "os"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+)
+
+/*
+ * Derived from:
+ * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $
+ * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $
+ * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $
+ * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $
+ * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $
+ * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $
+ *
+ * Copyright (c) 1996-1998 John D. Polstra. All rights reserved.
+ * Copyright (c) 2001 David E. O'Brien
+ * Portions Copyright 2009 The Go Authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * ELF definitions that are independent of architecture or word size.
+ */
+
+/*
+ * Note header. The ".note" section contains an array of notes. Each
+ * begins with this header, aligned to a word boundary. Immediately
+ * following the note header is n_namesz bytes of name, padded to the
+ * next word boundary. Then comes n_descsz bytes of descriptor, again
+ * padded to a word boundary. The values of n_namesz and n_descsz do
+ * not include the padding.
+ */
+type elfNote struct {
+ nNamesz uint32
+ nDescsz uint32
+ nType uint32
+}
+
+/* For accessing the fields of r_info. */
+
+/* For constructing r_info from field values. */
+
+/*
+ * Relocation types.
+ */
+const (
+ ARM_MAGIC_TRAMP_NUMBER = 0x5c000003
+)
+
+/*
+ * Symbol table entries.
+ */
+
+/* For accessing the fields of st_info. */
+
+/* For constructing st_info from field values. */
+
+/* For accessing the fields of st_other. */
+
+/*
+ * ELF header.
+ */
+type ElfEhdr elf.Header64
+
+/*
+ * Section header.
+ */
+type ElfShdr struct {
+ elf.Section64
+ shnum elf.SectionIndex
+}
+
+/*
+ * Program header.
+ */
+type ElfPhdr elf.ProgHeader
+
+/* For accessing the fields of r_info. */
+
+/* For constructing r_info from field values. */
+
+/*
+ * Symbol table entries.
+ */
+
+/* For accessing the fields of st_info. */
+
+/* For constructing st_info from field values. */
+
+/* For accessing the fields of st_other. */
+
+/*
+ * Go linker interface
+ */
+const (
+ ELF64HDRSIZE = 64
+ ELF64PHDRSIZE = 56
+ ELF64SHDRSIZE = 64
+ ELF64RELSIZE = 16
+ ELF64RELASIZE = 24
+ ELF64SYMSIZE = 24
+ ELF32HDRSIZE = 52
+ ELF32PHDRSIZE = 32
+ ELF32SHDRSIZE = 40
+ ELF32SYMSIZE = 16
+ ELF32RELSIZE = 8
+)
+
+/*
+ * The interface uses the 64-bit structures always,
+ * to avoid code duplication. The writers know how to
+ * marshal a 32-bit representation from the 64-bit structure.
+ */
+
+var Elfstrdat []byte
+
+/*
+ * Total amount of space to reserve at the start of the file
+ * for Header, PHeaders, SHeaders, and interp.
+ * May waste some.
+ * On FreeBSD, cannot be larger than a page.
+ */
+const (
+ ELFRESERVE = 4096
+)
+
+/*
+ * We use the 64-bit data structures on both 32- and 64-bit machines
+ * in order to write the code just once. The 64-bit data structure is
+ * written in the 32-bit format on the 32-bit machines.
+ */
+const (
+ NSECT = 400
+)
+
+var (
+ Nelfsym = 1
+
+ elf64 bool
+ // Either ".rel" or ".rela" depending on which type of relocation the
+ // target platform uses.
+ elfRelType string
+
+ ehdr ElfEhdr
+ phdr [NSECT]*ElfPhdr
+ shdr [NSECT]*ElfShdr
+
+ interp string
+)
+
+type Elfstring struct {
+ s string
+ off int
+}
+
+var elfstr [100]Elfstring
+
+var nelfstr int
+
+var buildinfo []byte
+
+/*
+Initialize the global variable that describes the ELF header. It will be updated as
+we write section and prog headers.
+*/
+func Elfinit(ctxt *Link) {
+ ctxt.IsELF = true
+
+ if ctxt.Arch.InFamily(sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) {
+ elfRelType = ".rela"
+ } else {
+ elfRelType = ".rel"
+ }
+
+ switch ctxt.Arch.Family {
+ // 64-bit architectures
+ case sys.PPC64, sys.S390X:
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ ehdr.Flags = 1 /* Version 1 ABI */
+ } else {
+ ehdr.Flags = 2 /* Version 2 ABI */
+ }
+ fallthrough
+ case sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.RISCV64:
+ if ctxt.Arch.Family == sys.MIPS64 {
+ ehdr.Flags = 0x20000004 /* MIPS 3 CPIC */
+ }
+ if ctxt.Arch.Family == sys.Loong64 {
+ ehdr.Flags = 0x3 /* LoongArch lp64d */
+ }
+ if ctxt.Arch.Family == sys.RISCV64 {
+ ehdr.Flags = 0x4 /* RISCV Float ABI Double */
+ }
+ elf64 = true
+
+ ehdr.Phoff = ELF64HDRSIZE /* Must be ELF64HDRSIZE: first PHdr must follow ELF header */
+ ehdr.Shoff = ELF64HDRSIZE /* Will move as we add PHeaders */
+ ehdr.Ehsize = ELF64HDRSIZE /* Must be ELF64HDRSIZE */
+ ehdr.Phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */
+ ehdr.Shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */
+
+ // 32-bit architectures
+ case sys.ARM, sys.MIPS:
+ if ctxt.Arch.Family == sys.ARM {
+ // we use EABI on linux/arm, freebsd/arm, netbsd/arm.
+ if ctxt.HeadType == objabi.Hlinux || ctxt.HeadType == objabi.Hfreebsd || ctxt.HeadType == objabi.Hnetbsd {
+ // We set a value here that makes no indication of which
+ // float ABI the object uses, because this is information
+ // used by the dynamic linker to compare executables and
+ // shared libraries -- so it only matters for cgo calls, and
+ // the information properly comes from the object files
+ // produced by the host C compiler. parseArmAttributes in
+ // ldelf.go reads that information and updates this field as
+ // appropriate.
+ ehdr.Flags = 0x5000002 // has entry point, Version5 EABI
+ }
+ } else if ctxt.Arch.Family == sys.MIPS {
+ ehdr.Flags = 0x50001004 /* MIPS 32 CPIC O32*/
+ }
+ fallthrough
+ default:
+ ehdr.Phoff = ELF32HDRSIZE
+ /* Must be ELF32HDRSIZE: first PHdr must follow ELF header */
+ ehdr.Shoff = ELF32HDRSIZE /* Will move as we add PHeaders */
+ ehdr.Ehsize = ELF32HDRSIZE /* Must be ELF32HDRSIZE */
+ ehdr.Phentsize = ELF32PHDRSIZE /* Must be ELF32PHDRSIZE */
+ ehdr.Shentsize = ELF32SHDRSIZE /* Must be ELF32SHDRSIZE */
+ }
+}
+
+// Make sure PT_LOAD is aligned properly and
+// that there is no gap,
+// correct ELF loaders will do this implicitly,
+// but buggy ELF loaders like the one in some
+// versions of QEMU and UPX won't.
+func fixElfPhdr(e *ElfPhdr) {
+ frag := int(e.Vaddr & (e.Align - 1))
+
+ e.Off -= uint64(frag)
+ e.Vaddr -= uint64(frag)
+ e.Paddr -= uint64(frag)
+ e.Filesz += uint64(frag)
+ e.Memsz += uint64(frag)
+}
+
+func elf64phdr(out *OutBuf, e *ElfPhdr) {
+ if e.Type == elf.PT_LOAD {
+ fixElfPhdr(e)
+ }
+
+ out.Write32(uint32(e.Type))
+ out.Write32(uint32(e.Flags))
+ out.Write64(e.Off)
+ out.Write64(e.Vaddr)
+ out.Write64(e.Paddr)
+ out.Write64(e.Filesz)
+ out.Write64(e.Memsz)
+ out.Write64(e.Align)
+}
+
+func elf32phdr(out *OutBuf, e *ElfPhdr) {
+ if e.Type == elf.PT_LOAD {
+ fixElfPhdr(e)
+ }
+
+ out.Write32(uint32(e.Type))
+ out.Write32(uint32(e.Off))
+ out.Write32(uint32(e.Vaddr))
+ out.Write32(uint32(e.Paddr))
+ out.Write32(uint32(e.Filesz))
+ out.Write32(uint32(e.Memsz))
+ out.Write32(uint32(e.Flags))
+ out.Write32(uint32(e.Align))
+}
+
+func elf64shdr(out *OutBuf, e *ElfShdr) {
+ out.Write32(e.Name)
+ out.Write32(uint32(e.Type))
+ out.Write64(uint64(e.Flags))
+ out.Write64(e.Addr)
+ out.Write64(e.Off)
+ out.Write64(e.Size)
+ out.Write32(e.Link)
+ out.Write32(e.Info)
+ out.Write64(e.Addralign)
+ out.Write64(e.Entsize)
+}
+
+func elf32shdr(out *OutBuf, e *ElfShdr) {
+ out.Write32(e.Name)
+ out.Write32(uint32(e.Type))
+ out.Write32(uint32(e.Flags))
+ out.Write32(uint32(e.Addr))
+ out.Write32(uint32(e.Off))
+ out.Write32(uint32(e.Size))
+ out.Write32(e.Link)
+ out.Write32(e.Info)
+ out.Write32(uint32(e.Addralign))
+ out.Write32(uint32(e.Entsize))
+}
+
+func elfwriteshdrs(out *OutBuf) uint32 {
+ if elf64 {
+ for i := 0; i < int(ehdr.Shnum); i++ {
+ elf64shdr(out, shdr[i])
+ }
+ return uint32(ehdr.Shnum) * ELF64SHDRSIZE
+ }
+
+ for i := 0; i < int(ehdr.Shnum); i++ {
+ elf32shdr(out, shdr[i])
+ }
+ return uint32(ehdr.Shnum) * ELF32SHDRSIZE
+}
+
+func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) {
+ if nelfstr >= len(elfstr) {
+ ctxt.Errorf(s, "too many elf strings")
+ errorexit()
+ }
+
+ elfstr[nelfstr].s = str
+ elfstr[nelfstr].off = off
+ nelfstr++
+}
+
+func elfwritephdrs(out *OutBuf) uint32 {
+ if elf64 {
+ for i := 0; i < int(ehdr.Phnum); i++ {
+ elf64phdr(out, phdr[i])
+ }
+ return uint32(ehdr.Phnum) * ELF64PHDRSIZE
+ }
+
+ for i := 0; i < int(ehdr.Phnum); i++ {
+ elf32phdr(out, phdr[i])
+ }
+ return uint32(ehdr.Phnum) * ELF32PHDRSIZE
+}
+
+func newElfPhdr() *ElfPhdr {
+ e := new(ElfPhdr)
+ if ehdr.Phnum >= NSECT {
+ Errorf(nil, "too many phdrs")
+ } else {
+ phdr[ehdr.Phnum] = e
+ ehdr.Phnum++
+ }
+ if elf64 {
+ ehdr.Shoff += ELF64PHDRSIZE
+ } else {
+ ehdr.Shoff += ELF32PHDRSIZE
+ }
+ return e
+}
+
+func newElfShdr(name int64) *ElfShdr {
+ e := new(ElfShdr)
+ e.Name = uint32(name)
+ e.shnum = elf.SectionIndex(ehdr.Shnum)
+ if ehdr.Shnum >= NSECT {
+ Errorf(nil, "too many shdrs")
+ } else {
+ shdr[ehdr.Shnum] = e
+ ehdr.Shnum++
+ }
+
+ return e
+}
+
+func getElfEhdr() *ElfEhdr {
+ return &ehdr
+}
+
+func elf64writehdr(out *OutBuf) uint32 {
+ out.Write(ehdr.Ident[:])
+ out.Write16(uint16(ehdr.Type))
+ out.Write16(uint16(ehdr.Machine))
+ out.Write32(uint32(ehdr.Version))
+ out.Write64(ehdr.Entry)
+ out.Write64(ehdr.Phoff)
+ out.Write64(ehdr.Shoff)
+ out.Write32(ehdr.Flags)
+ out.Write16(ehdr.Ehsize)
+ out.Write16(ehdr.Phentsize)
+ out.Write16(ehdr.Phnum)
+ out.Write16(ehdr.Shentsize)
+ out.Write16(ehdr.Shnum)
+ out.Write16(ehdr.Shstrndx)
+ return ELF64HDRSIZE
+}
+
+func elf32writehdr(out *OutBuf) uint32 {
+ out.Write(ehdr.Ident[:])
+ out.Write16(uint16(ehdr.Type))
+ out.Write16(uint16(ehdr.Machine))
+ out.Write32(uint32(ehdr.Version))
+ out.Write32(uint32(ehdr.Entry))
+ out.Write32(uint32(ehdr.Phoff))
+ out.Write32(uint32(ehdr.Shoff))
+ out.Write32(ehdr.Flags)
+ out.Write16(ehdr.Ehsize)
+ out.Write16(ehdr.Phentsize)
+ out.Write16(ehdr.Phnum)
+ out.Write16(ehdr.Shentsize)
+ out.Write16(ehdr.Shnum)
+ out.Write16(ehdr.Shstrndx)
+ return ELF32HDRSIZE
+}
+
+func elfwritehdr(out *OutBuf) uint32 {
+ if elf64 {
+ return elf64writehdr(out)
+ }
+ return elf32writehdr(out)
+}
+
+/* Taken directly from the definition document for ELF64. */
+func elfhash(name string) uint32 {
+ var h uint32
+ for i := 0; i < len(name); i++ {
+ h = (h << 4) + uint32(name[i])
+ if g := h & 0xf0000000; g != 0 {
+ h ^= g >> 24
+ }
+ h &= 0x0fffffff
+ }
+ return h
+}
+
+func elfWriteDynEntSym(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
+ Elfwritedynentsymplus(ctxt, s, tag, t, 0)
+}
+
+func Elfwritedynent(arch *sys.Arch, s *loader.SymbolBuilder, tag elf.DynTag, val uint64) {
+ if elf64 {
+ s.AddUint64(arch, uint64(tag))
+ s.AddUint64(arch, val)
+ } else {
+ s.AddUint32(arch, uint32(tag))
+ s.AddUint32(arch, uint32(val))
+ }
+}
+
+func Elfwritedynentsymplus(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym, add int64) {
+ if elf64 {
+ s.AddUint64(ctxt.Arch, uint64(tag))
+ } else {
+ s.AddUint32(ctxt.Arch, uint32(tag))
+ }
+ s.AddAddrPlus(ctxt.Arch, t, add)
+}
+
+func elfwritedynentsymsize(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
+ if elf64 {
+ s.AddUint64(ctxt.Arch, uint64(tag))
+ } else {
+ s.AddUint32(ctxt.Arch, uint32(tag))
+ }
+ s.AddSize(ctxt.Arch, t)
+}
+
+func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int {
+ interp = p
+ n := len(interp) + 1
+ sh.Addr = startva + resoff - uint64(n)
+ sh.Off = resoff - uint64(n)
+ sh.Size = uint64(n)
+
+ return n
+}
+
+func elfwriteinterp(out *OutBuf) int {
+ sh := elfshname(".interp")
+ out.SeekSet(int64(sh.Off))
+ out.WriteString(interp)
+ out.Write8(0)
+ return int(sh.Size)
+}
+
+// member of .gnu.attributes of MIPS for fpAbi
+const (
+ // No floating point is present in the module (default)
+ MIPS_FPABI_NONE = 0
+ // FP code in the module uses the FP32 ABI for a 32-bit ABI
+ MIPS_FPABI_ANY = 1
+ // FP code in the module only uses single precision ABI
+ MIPS_FPABI_SINGLE = 2
+ // FP code in the module uses soft-float ABI
+ MIPS_FPABI_SOFT = 3
+ // FP code in the module assumes an FPU with FR=1 and has 12
+ // callee-saved doubles. Historic, no longer supported.
+ MIPS_FPABI_HIST = 4
+ // FP code in the module uses the FPXX ABI
+ MIPS_FPABI_FPXX = 5
+ // FP code in the module uses the FP64 ABI
+ MIPS_FPABI_FP64 = 6
+ // FP code in the module uses the FP64A ABI
+ MIPS_FPABI_FP64A = 7
+)
+
+func elfMipsAbiFlags(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := 24
+ sh.Addr = startva + resoff - uint64(n)
+ sh.Off = resoff - uint64(n)
+ sh.Size = uint64(n)
+ sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+
+ return n
+}
+
+// Layout is given by this C definition:
+//
+// typedef struct
+// {
+// /* Version of flags structure. */
+// uint16_t version;
+// /* The level of the ISA: 1-5, 32, 64. */
+// uint8_t isa_level;
+// /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
+// uint8_t isa_rev;
+// /* The size of general purpose registers. */
+// uint8_t gpr_size;
+// /* The size of co-processor 1 registers. */
+// uint8_t cpr1_size;
+// /* The size of co-processor 2 registers. */
+// uint8_t cpr2_size;
+// /* The floating-point ABI. */
+// uint8_t fp_abi;
+// /* Processor-specific extension. */
+// uint32_t isa_ext;
+// /* Mask of ASEs used. */
+// uint32_t ases;
+// /* Mask of general flags. */
+// uint32_t flags1;
+// uint32_t flags2;
+// } Elf_Internal_ABIFlags_v0;
+func elfWriteMipsAbiFlags(ctxt *Link) int {
+ sh := elfshname(".MIPS.abiflags")
+ ctxt.Out.SeekSet(int64(sh.Off))
+ ctxt.Out.Write16(0) // version
+ ctxt.Out.Write8(32) // isaLevel
+ ctxt.Out.Write8(1) // isaRev
+ ctxt.Out.Write8(1) // gprSize
+ ctxt.Out.Write8(1) // cpr1Size
+ ctxt.Out.Write8(0) // cpr2Size
+ if buildcfg.GOMIPS == "softfloat" {
+ ctxt.Out.Write8(MIPS_FPABI_SOFT) // fpAbi
+ } else {
+ // Go cannot make sure non odd-number-fpr is used (ie, in load a double from memory).
+ // So, we mark the object is MIPS I style paired float/double register scheme,
+ // aka MIPS_FPABI_ANY. If we mark the object as FPXX, the kernel may use FR=1 mode,
+ // then we meet some problem.
+ // Note: MIPS_FPABI_ANY is bad naming: in fact it is MIPS I style FPR usage.
+ // It is not for 'ANY'.
+ // TODO: switch to FPXX after be sure that no odd-number-fpr is used.
+ ctxt.Out.Write8(MIPS_FPABI_ANY) // fpAbi
+ }
+ ctxt.Out.Write32(0) // isaExt
+ ctxt.Out.Write32(0) // ases
+ ctxt.Out.Write32(0) // flags1
+ ctxt.Out.Write32(0) // flags2
+ return int(sh.Size)
+}
+
+func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sizes ...int) int {
+ n := resoff % 4
+ // if section contains multiple notes (as is the case with FreeBSD signature),
+ // multiple note sizes can be specified
+ for _, sz := range sizes {
+ n += 3*4 + uint64(sz)
+ }
+
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 4
+ sh.Addr = startva + resoff - n
+ sh.Off = resoff - n
+ sh.Size = n - resoff%4
+
+ return int(n)
+}
+
+func elfwritenotehdr(out *OutBuf, str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr {
+ sh := elfshname(str)
+
+ // Write Elf_Note header.
+ out.SeekSet(int64(sh.Off))
+
+ out.Write32(namesz)
+ out.Write32(descsz)
+ out.Write32(tag)
+
+ return sh
+}
+
+// NetBSD Signature (as per sys/exec_elf.h)
+const (
+ ELF_NOTE_NETBSD_NAMESZ = 7
+ ELF_NOTE_NETBSD_DESCSZ = 4
+ ELF_NOTE_NETBSD_TAG = 1
+ ELF_NOTE_NETBSD_VERSION = 700000000 /* NetBSD 7.0 */
+)
+
+var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00")
+
+func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4))
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfwritenetbsdsig(out *OutBuf) int {
+ // Write Elf_Note header.
+ sh := elfwritenotehdr(out, ".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG)
+
+ if sh == nil {
+ return 0
+ }
+
+ // Followed by NetBSD string and version.
+ out.Write(ELF_NOTE_NETBSD_NAME)
+ out.Write8(0)
+ out.Write32(ELF_NOTE_NETBSD_VERSION)
+
+ return int(sh.Size)
+}
+
+// The race detector can't handle ASLR (address space layout randomization).
+// ASLR is on by default for NetBSD, so we turn the ASLR off explicitly
+// using a magic elf Note when building race binaries.
+
+func elfnetbsdpax(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := int(Rnd(4, 4) + Rnd(4, 4))
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfwritenetbsdpax(out *OutBuf) int {
+ sh := elfwritenotehdr(out, ".note.netbsd.pax", 4 /* length of PaX\x00 */, 4 /* length of flags */, 0x03 /* PaX type */)
+ if sh == nil {
+ return 0
+ }
+ out.Write([]byte("PaX\x00"))
+ out.Write32(0x20) // 0x20 = Force disable ASLR
+ return int(sh.Size)
+}
+
+// OpenBSD Signature
+const (
+ ELF_NOTE_OPENBSD_NAMESZ = 8
+ ELF_NOTE_OPENBSD_DESCSZ = 4
+ ELF_NOTE_OPENBSD_TAG = 1
+ ELF_NOTE_OPENBSD_VERSION = 0
+)
+
+var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00")
+
+func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfwriteopenbsdsig(out *OutBuf) int {
+ // Write Elf_Note header.
+ sh := elfwritenotehdr(out, ".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG)
+
+ if sh == nil {
+ return 0
+ }
+
+ // Followed by OpenBSD string and version.
+ out.Write(ELF_NOTE_OPENBSD_NAME)
+
+ out.Write32(ELF_NOTE_OPENBSD_VERSION)
+
+ return int(sh.Size)
+}
+
+// FreeBSD Signature (as per sys/elf_common.h)
+const (
+ ELF_NOTE_FREEBSD_NAMESZ = 8
+ ELF_NOTE_FREEBSD_DESCSZ = 4
+ ELF_NOTE_FREEBSD_ABI_TAG = 1
+ ELF_NOTE_FREEBSD_NOINIT_TAG = 2
+ ELF_NOTE_FREEBSD_FEATURE_CTL_TAG = 4
+ ELF_NOTE_FREEBSD_VERSION = 1203000 // 12.3-RELEASE
+ ELF_NOTE_FREEBSD_FCTL_ASLR_DISABLE = 0x1
+)
+
+const ELF_NOTE_FREEBSD_NAME = "FreeBSD\x00"
+
+func elffreebsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := ELF_NOTE_FREEBSD_NAMESZ + ELF_NOTE_FREEBSD_DESCSZ
+ // FreeBSD signature section contains 3 equally sized notes
+ return elfnote(sh, startva, resoff, n, n, n)
+}
+
+// elfwritefreebsdsig writes FreeBSD .note section.
+//
+// See https://www.netbsd.org/docs/kernel/elf-notes.html for the description of
+// a Note element format and
+// https://github.com/freebsd/freebsd-src/blob/main/sys/sys/elf_common.h#L790
+// for the FreeBSD-specific values.
+func elfwritefreebsdsig(out *OutBuf) int {
+ sh := elfshname(".note.tag")
+ if sh == nil {
+ return 0
+ }
+ out.SeekSet(int64(sh.Off))
+
+ // NT_FREEBSD_ABI_TAG
+ out.Write32(ELF_NOTE_FREEBSD_NAMESZ)
+ out.Write32(ELF_NOTE_FREEBSD_DESCSZ)
+ out.Write32(ELF_NOTE_FREEBSD_ABI_TAG)
+ out.WriteString(ELF_NOTE_FREEBSD_NAME)
+ out.Write32(ELF_NOTE_FREEBSD_VERSION)
+
+ // NT_FREEBSD_NOINIT_TAG
+ out.Write32(ELF_NOTE_FREEBSD_NAMESZ)
+ out.Write32(ELF_NOTE_FREEBSD_DESCSZ)
+ out.Write32(ELF_NOTE_FREEBSD_NOINIT_TAG)
+ out.WriteString(ELF_NOTE_FREEBSD_NAME)
+ out.Write32(0)
+
+ // NT_FREEBSD_FEATURE_CTL
+ out.Write32(ELF_NOTE_FREEBSD_NAMESZ)
+ out.Write32(ELF_NOTE_FREEBSD_DESCSZ)
+ out.Write32(ELF_NOTE_FREEBSD_FEATURE_CTL_TAG)
+ out.WriteString(ELF_NOTE_FREEBSD_NAME)
+ if *flagRace {
+ // The race detector can't handle ASLR, turn the ASLR off when compiling with -race.
+ out.Write32(ELF_NOTE_FREEBSD_FCTL_ASLR_DISABLE)
+ } else {
+ out.Write32(0)
+ }
+
+ return int(sh.Size)
+}
+
+func addbuildinfo(val string) {
+ if !strings.HasPrefix(val, "0x") {
+ Exitf("-B argument must start with 0x: %s", val)
+ }
+
+ ov := val
+ val = val[2:]
+
+ const maxLen = 32
+ if hex.DecodedLen(len(val)) > maxLen {
+ Exitf("-B option too long (max %d digits): %s", maxLen, ov)
+ }
+
+ b, err := hex.DecodeString(val)
+ if err != nil {
+ if err == hex.ErrLength {
+ Exitf("-B argument must have even number of digits: %s", ov)
+ }
+ if inv, ok := err.(hex.InvalidByteError); ok {
+ Exitf("-B argument contains invalid hex digit %c: %s", byte(inv), ov)
+ }
+ Exitf("-B argument contains invalid hex: %s", ov)
+ }
+
+ buildinfo = b
+}
+
+// Build info note
+const (
+ ELF_NOTE_BUILDINFO_NAMESZ = 4
+ ELF_NOTE_BUILDINFO_TAG = 3
+)
+
+var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00")
+
+func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4))
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(*flagBuildid)), 4))
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfwritebuildinfo(out *OutBuf) int {
+ sh := elfwritenotehdr(out, ".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG)
+ if sh == nil {
+ return 0
+ }
+
+ out.Write(ELF_NOTE_BUILDINFO_NAME)
+ out.Write(buildinfo)
+ var zero = make([]byte, 4)
+ out.Write(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))])
+
+ return int(sh.Size)
+}
+
+func elfwritegobuildid(out *OutBuf) int {
+ sh := elfwritenotehdr(out, ".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(*flagBuildid)), ELF_NOTE_GOBUILDID_TAG)
+ if sh == nil {
+ return 0
+ }
+
+ out.Write(ELF_NOTE_GO_NAME)
+ out.Write([]byte(*flagBuildid))
+ var zero = make([]byte, 4)
+ out.Write(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))])
+
+ return int(sh.Size)
+}
+
+// Go specific notes
+const (
+ ELF_NOTE_GOPKGLIST_TAG = 1
+ ELF_NOTE_GOABIHASH_TAG = 2
+ ELF_NOTE_GODEPS_TAG = 3
+ ELF_NOTE_GOBUILDID_TAG = 4
+)
+
+var ELF_NOTE_GO_NAME = []byte("Go\x00\x00")
+
+var elfverneed int
+
+type Elfaux struct {
+ next *Elfaux
+ num int
+ vers string
+}
+
+type Elflib struct {
+ next *Elflib
+ aux *Elfaux
+ file string
+}
+
+func addelflib(list **Elflib, file string, vers string) *Elfaux {
+ var lib *Elflib
+
+ for lib = *list; lib != nil; lib = lib.next {
+ if lib.file == file {
+ goto havelib
+ }
+ }
+ lib = new(Elflib)
+ lib.next = *list
+ lib.file = file
+ *list = lib
+
+havelib:
+ for aux := lib.aux; aux != nil; aux = aux.next {
+ if aux.vers == vers {
+ return aux
+ }
+ }
+ aux := new(Elfaux)
+ aux.next = lib.aux
+ aux.vers = vers
+ lib.aux = aux
+
+ return aux
+}
+
+func elfdynhash(ctxt *Link) {
+ if !ctxt.IsELF {
+ return
+ }
+
+ nsym := Nelfsym
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate(".hash", 0)
+ s.SetType(sym.SELFROSECT)
+
+ i := nsym
+ nbucket := 1
+ for i > 0 {
+ nbucket++
+ i >>= 1
+ }
+
+ var needlib *Elflib
+ need := make([]*Elfaux, nsym)
+ chain := make([]uint32, nsym)
+ buckets := make([]uint32, nbucket)
+
+ for _, sy := range ldr.DynidSyms() {
+
+ dynid := ldr.SymDynid(sy)
+ if ldr.SymDynimpvers(sy) != "" {
+ need[dynid] = addelflib(&needlib, ldr.SymDynimplib(sy), ldr.SymDynimpvers(sy))
+ }
+
+ name := ldr.SymExtname(sy)
+ hc := elfhash(name)
+
+ b := hc % uint32(nbucket)
+ chain[dynid] = buckets[b]
+ buckets[b] = uint32(dynid)
+ }
+
+ // s390x (ELF64) hash table entries are 8 bytes
+ if ctxt.Arch.Family == sys.S390X {
+ s.AddUint64(ctxt.Arch, uint64(nbucket))
+ s.AddUint64(ctxt.Arch, uint64(nsym))
+ for i := 0; i < nbucket; i++ {
+ s.AddUint64(ctxt.Arch, uint64(buckets[i]))
+ }
+ for i := 0; i < nsym; i++ {
+ s.AddUint64(ctxt.Arch, uint64(chain[i]))
+ }
+ } else {
+ s.AddUint32(ctxt.Arch, uint32(nbucket))
+ s.AddUint32(ctxt.Arch, uint32(nsym))
+ for i := 0; i < nbucket; i++ {
+ s.AddUint32(ctxt.Arch, buckets[i])
+ }
+ for i := 0; i < nsym; i++ {
+ s.AddUint32(ctxt.Arch, chain[i])
+ }
+ }
+
+ dynstr := ldr.CreateSymForUpdate(".dynstr", 0)
+
+ // version symbols
+ gnuVersionR := ldr.CreateSymForUpdate(".gnu.version_r", 0)
+ s = gnuVersionR
+ i = 2
+ nfile := 0
+ for l := needlib; l != nil; l = l.next {
+ nfile++
+
+ // header
+ s.AddUint16(ctxt.Arch, 1) // table version
+ j := 0
+ for x := l.aux; x != nil; x = x.next {
+ j++
+ }
+ s.AddUint16(ctxt.Arch, uint16(j)) // aux count
+ s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(l.file))) // file string offset
+ s.AddUint32(ctxt.Arch, 16) // offset from header to first aux
+ if l.next != nil {
+ s.AddUint32(ctxt.Arch, 16+uint32(j)*16) // offset from this header to next
+ } else {
+ s.AddUint32(ctxt.Arch, 0)
+ }
+
+ for x := l.aux; x != nil; x = x.next {
+ x.num = i
+ i++
+
+ // aux struct
+ s.AddUint32(ctxt.Arch, elfhash(x.vers)) // hash
+ s.AddUint16(ctxt.Arch, 0) // flags
+ s.AddUint16(ctxt.Arch, uint16(x.num)) // other - index we refer to this by
+ s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(x.vers))) // version string offset
+ if x.next != nil {
+ s.AddUint32(ctxt.Arch, 16) // offset from this aux to next
+ } else {
+ s.AddUint32(ctxt.Arch, 0)
+ }
+ }
+ }
+
+ // version references
+ gnuVersion := ldr.CreateSymForUpdate(".gnu.version", 0)
+ s = gnuVersion
+
+ for i := 0; i < nsym; i++ {
+ if i == 0 {
+ s.AddUint16(ctxt.Arch, 0) // first entry - no symbol
+ } else if need[i] == nil {
+ s.AddUint16(ctxt.Arch, 1) // global
+ } else {
+ s.AddUint16(ctxt.Arch, uint16(need[i].num))
+ }
+ }
+
+ s = ldr.CreateSymForUpdate(".dynamic", 0)
+ if ctxt.BuildMode == BuildModePIE {
+ // https://github.com/bminor/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/elf/elf.h#L986
+ const DTFLAGS_1_PIE = 0x08000000
+ Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(DTFLAGS_1_PIE))
+ }
+ elfverneed = nfile
+ if elfverneed != 0 {
+ elfWriteDynEntSym(ctxt, s, elf.DT_VERNEED, gnuVersionR.Sym())
+ Elfwritedynent(ctxt.Arch, s, elf.DT_VERNEEDNUM, uint64(nfile))
+ elfWriteDynEntSym(ctxt, s, elf.DT_VERSYM, gnuVersion.Sym())
+ }
+
+ sy := ldr.CreateSymForUpdate(elfRelType+".plt", 0)
+ if sy.Size() > 0 {
+ if elfRelType == ".rela" {
+ Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_RELA))
+ } else {
+ Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_REL))
+ }
+ elfwritedynentsymsize(ctxt, s, elf.DT_PLTRELSZ, sy.Sym())
+ elfWriteDynEntSym(ctxt, s, elf.DT_JMPREL, sy.Sym())
+ }
+
+ Elfwritedynent(ctxt.Arch, s, elf.DT_NULL, 0)
+}
+
+func elfphload(seg *sym.Segment) *ElfPhdr {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_LOAD
+ if seg.Rwx&4 != 0 {
+ ph.Flags |= elf.PF_R
+ }
+ if seg.Rwx&2 != 0 {
+ ph.Flags |= elf.PF_W
+ }
+ if seg.Rwx&1 != 0 {
+ ph.Flags |= elf.PF_X
+ }
+ ph.Vaddr = seg.Vaddr
+ ph.Paddr = seg.Vaddr
+ ph.Memsz = seg.Length
+ ph.Off = seg.Fileoff
+ ph.Filesz = seg.Filelen
+ ph.Align = uint64(*FlagRound)
+
+ return ph
+}
+
+func elfphrelro(seg *sym.Segment) {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_GNU_RELRO
+ ph.Vaddr = seg.Vaddr
+ ph.Paddr = seg.Vaddr
+ ph.Memsz = seg.Length
+ ph.Off = seg.Fileoff
+ ph.Filesz = seg.Filelen
+ ph.Align = uint64(*FlagRound)
+}
+
+func elfshname(name string) *ElfShdr {
+ for i := 0; i < nelfstr; i++ {
+ if name != elfstr[i].s {
+ continue
+ }
+ off := elfstr[i].off
+ for i = 0; i < int(ehdr.Shnum); i++ {
+ sh := shdr[i]
+ if sh.Name == uint32(off) {
+ return sh
+ }
+ }
+ return newElfShdr(int64(off))
+ }
+ Exitf("cannot find elf name %s", name)
+ return nil
+}
+
+// Create an ElfShdr for the section with name.
+// Create a duplicate if one already exists with that name.
+func elfshnamedup(name string) *ElfShdr {
+ for i := 0; i < nelfstr; i++ {
+ if name == elfstr[i].s {
+ off := elfstr[i].off
+ return newElfShdr(int64(off))
+ }
+ }
+
+ Errorf(nil, "cannot find elf name %s", name)
+ errorexit()
+ return nil
+}
+
+func elfshalloc(sect *sym.Section) *ElfShdr {
+ sh := elfshname(sect.Name)
+ sect.Elfsect = sh
+ return sh
+}
+
+func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr {
+ var sh *ElfShdr
+
+ if sect.Name == ".text" {
+ if sect.Elfsect == nil {
+ sect.Elfsect = elfshnamedup(sect.Name)
+ }
+ sh = sect.Elfsect.(*ElfShdr)
+ } else {
+ sh = elfshalloc(sect)
+ }
+
+ // If this section has already been set up as a note, we assume type_ and
+ // flags are already correct, but the other fields still need filling in.
+ if sh.Type == uint32(elf.SHT_NOTE) {
+ if linkmode != LinkExternal {
+ // TODO(mwhudson): the approach here will work OK when
+ // linking internally for notes that we want to be included
+ // in a loadable segment (e.g. the abihash note) but not for
+ // notes that we do not want to be mapped (e.g. the package
+ // list note). The real fix is probably to define new values
+ // for Symbol.Type corresponding to mapped and unmapped notes
+ // and handle them in dodata().
+ Errorf(nil, "sh.Type == SHT_NOTE in elfshbits when linking internally")
+ }
+ sh.Addralign = uint64(sect.Align)
+ sh.Size = sect.Length
+ sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+ return sh
+ }
+ if sh.Type > 0 {
+ return sh
+ }
+
+ if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
+ switch sect.Name {
+ case ".init_array":
+ sh.Type = uint32(elf.SHT_INIT_ARRAY)
+ default:
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ }
+ } else {
+ sh.Type = uint32(elf.SHT_NOBITS)
+ }
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ if sect.Rwx&1 != 0 {
+ sh.Flags |= uint64(elf.SHF_EXECINSTR)
+ }
+ if sect.Rwx&2 != 0 {
+ sh.Flags |= uint64(elf.SHF_WRITE)
+ }
+ if sect.Name == ".tbss" {
+ sh.Flags |= uint64(elf.SHF_TLS)
+ sh.Type = uint32(elf.SHT_NOBITS)
+ }
+ if linkmode != LinkExternal {
+ sh.Addr = sect.Vaddr
+ }
+
+ if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") {
+ sh.Flags = 0
+ sh.Addr = 0
+ if sect.Compressed {
+ sh.Flags |= uint64(elf.SHF_COMPRESSED)
+ }
+ }
+
+ sh.Addralign = uint64(sect.Align)
+ sh.Size = sect.Length
+ if sect.Name != ".tbss" {
+ sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+ }
+
+ return sh
+}
+
+func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr {
+ // If main section is SHT_NOBITS, nothing to relocate.
+ // Also nothing to relocate in .shstrtab or notes.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return nil
+ }
+ if sect.Name == ".shstrtab" || sect.Name == ".tbss" {
+ return nil
+ }
+ if sect.Elfsect.(*ElfShdr).Type == uint32(elf.SHT_NOTE) {
+ return nil
+ }
+
+ typ := elf.SHT_REL
+ if elfRelType == ".rela" {
+ typ = elf.SHT_RELA
+ }
+
+ sh := elfshname(elfRelType + sect.Name)
+ // There could be multiple text sections but each needs
+ // its own .rela.text.
+
+ if sect.Name == ".text" {
+ if sh.Info != 0 && sh.Info != uint32(sect.Elfsect.(*ElfShdr).shnum) {
+ sh = elfshnamedup(elfRelType + sect.Name)
+ }
+ }
+
+ sh.Type = uint32(typ)
+ sh.Entsize = uint64(arch.RegSize) * 2
+ if typ == elf.SHT_RELA {
+ sh.Entsize += uint64(arch.RegSize)
+ }
+ sh.Link = uint32(elfshname(".symtab").shnum)
+ sh.Info = uint32(sect.Elfsect.(*ElfShdr).shnum)
+ sh.Off = sect.Reloff
+ sh.Size = sect.Rellen
+ sh.Addralign = uint64(arch.RegSize)
+ return sh
+}
+
+func elfrelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) {
+ // If main section is SHT_NOBITS, nothing to relocate.
+ // Also nothing to relocate in .shstrtab.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return
+ }
+ if sect.Name == ".shstrtab" {
+ return
+ }
+
+ ldr := ctxt.loader
+ for i, s := range syms {
+ if !ldr.AttrReachable(s) {
+ panic("should never happen")
+ }
+ if uint64(ldr.SymValue(s)) >= sect.Vaddr {
+ syms = syms[i:]
+ break
+ }
+ }
+
+ eaddr := sect.Vaddr + sect.Length
+ for _, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymValue(s) >= int64(eaddr) {
+ break
+ }
+
+ // Compute external relocations on the go, and pass to Elfreloc1
+ // to stream out.
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rr, ok := extreloc(ctxt, ldr, s, r)
+ if !ok {
+ continue
+ }
+ if rr.Xsym == 0 {
+ ldr.Errorf(s, "missing xsym in relocation")
+ continue
+ }
+ esr := ElfSymForReloc(ctxt, rr.Xsym)
+ if esr == 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-elf symbol %s (outer=%s) %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymType(r.Sym()).String())
+ }
+ if !ldr.AttrReachable(rr.Xsym) {
+ ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym))
+ }
+ if !thearch.Elfreloc1(ctxt, out, ldr, s, rr, ri, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) {
+ ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ }
+ }
+
+ // sanity check
+ if uint64(out.Offset()) != sect.Reloff+sect.Rellen {
+ panic(fmt.Sprintf("elfrelocsect: size mismatch %d != %d + %d", out.Offset(), sect.Reloff, sect.Rellen))
+ }
+}
+
+func elfEmitReloc(ctxt *Link) {
+ for ctxt.Out.Offset()&7 != 0 {
+ ctxt.Out.Write8(0)
+ }
+
+ sizeExtRelocs(ctxt, thearch.ElfrelocSize)
+ relocSect, wg := relocSectFn(ctxt, elfrelocsect)
+
+ for _, sect := range Segtext.Sections {
+ if sect.Name == ".text" {
+ relocSect(ctxt, sect, ctxt.Textp)
+ } else {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ }
+
+ for _, sect := range Segrodata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for _, sect := range Segdata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for i := 0; i < len(Segdwarf.Sections); i++ {
+ sect := Segdwarf.Sections[i]
+ si := dwarfp[i]
+ if si.secSym() != loader.Sym(sect.Sym) ||
+ ctxt.loader.SymSect(si.secSym()) != sect {
+ panic("inconsistency between dwarfp and Segdwarf")
+ }
+ relocSect(ctxt, sect, si.syms)
+ }
+ wg.Wait()
+}
+
+func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate(sectionName, 0)
+ s.SetType(sym.SELFROSECT)
+ // namesz
+ s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME)))
+ // descsz
+ s.AddUint32(ctxt.Arch, uint32(len(desc)))
+ // tag
+ s.AddUint32(ctxt.Arch, tag)
+ // name + padding
+ s.AddBytes(ELF_NOTE_GO_NAME)
+ for len(s.Data())%4 != 0 {
+ s.AddUint8(0)
+ }
+ // desc + padding
+ s.AddBytes(desc)
+ for len(s.Data())%4 != 0 {
+ s.AddUint8(0)
+ }
+ s.SetSize(int64(len(s.Data())))
+ s.SetAlign(4)
+}
+
+func (ctxt *Link) doelf() {
+ ldr := ctxt.loader
+
+ /* predefine strings we need for section headers */
+ shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0)
+
+ shstrtab.SetType(sym.SELFROSECT)
+
+ shstrtab.Addstring("")
+ shstrtab.Addstring(".text")
+ shstrtab.Addstring(".noptrdata")
+ shstrtab.Addstring(".data")
+ shstrtab.Addstring(".bss")
+ shstrtab.Addstring(".noptrbss")
+ shstrtab.Addstring(".go.fuzzcntrs")
+ shstrtab.Addstring(".go.buildinfo")
+ if ctxt.IsMIPS() {
+ shstrtab.Addstring(".MIPS.abiflags")
+ shstrtab.Addstring(".gnu.attributes")
+ }
+
+ // generate .tbss section for dynamic internal linker or external
+ // linking, so that various binutils could correctly calculate
+ // PT_TLS size. See https://golang.org/issue/5200.
+ if !*FlagD || ctxt.IsExternal() {
+ shstrtab.Addstring(".tbss")
+ }
+ if ctxt.IsNetbsd() {
+ shstrtab.Addstring(".note.netbsd.ident")
+ if *flagRace {
+ shstrtab.Addstring(".note.netbsd.pax")
+ }
+ }
+ if ctxt.IsOpenbsd() {
+ shstrtab.Addstring(".note.openbsd.ident")
+ }
+ if ctxt.IsFreebsd() {
+ shstrtab.Addstring(".note.tag")
+ }
+ if len(buildinfo) > 0 {
+ shstrtab.Addstring(".note.gnu.build-id")
+ }
+ if *flagBuildid != "" {
+ shstrtab.Addstring(".note.go.buildid")
+ }
+ shstrtab.Addstring(".elfdata")
+ shstrtab.Addstring(".rodata")
+ // See the comment about data.rel.ro.FOO section names in data.go.
+ relro_prefix := ""
+ if ctxt.UseRelro() {
+ shstrtab.Addstring(".data.rel.ro")
+ relro_prefix = ".data.rel.ro"
+ }
+ shstrtab.Addstring(relro_prefix + ".typelink")
+ shstrtab.Addstring(relro_prefix + ".itablink")
+ shstrtab.Addstring(relro_prefix + ".gosymtab")
+ shstrtab.Addstring(relro_prefix + ".gopclntab")
+
+ if ctxt.IsExternal() {
+ *FlagD = true
+
+ shstrtab.Addstring(elfRelType + ".text")
+ shstrtab.Addstring(elfRelType + ".rodata")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".typelink")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".itablink")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".gosymtab")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".gopclntab")
+ shstrtab.Addstring(elfRelType + ".noptrdata")
+ shstrtab.Addstring(elfRelType + ".data")
+ if ctxt.UseRelro() {
+ shstrtab.Addstring(elfRelType + ".data.rel.ro")
+ }
+ shstrtab.Addstring(elfRelType + ".go.buildinfo")
+ if ctxt.IsMIPS() {
+ shstrtab.Addstring(elfRelType + ".MIPS.abiflags")
+ shstrtab.Addstring(elfRelType + ".gnu.attributes")
+ }
+
+ // add a .note.GNU-stack section to mark the stack as non-executable
+ shstrtab.Addstring(".note.GNU-stack")
+
+ if ctxt.IsShared() {
+ shstrtab.Addstring(".note.go.abihash")
+ shstrtab.Addstring(".note.go.pkg-list")
+ shstrtab.Addstring(".note.go.deps")
+ }
+ }
+
+ hasinitarr := ctxt.linkShared
+
+ /* shared library initializer */
+ switch ctxt.BuildMode {
+ case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin:
+ hasinitarr = true
+ }
+
+ if hasinitarr {
+ shstrtab.Addstring(".init_array")
+ shstrtab.Addstring(elfRelType + ".init_array")
+ }
+
+ if !*FlagS {
+ shstrtab.Addstring(".symtab")
+ shstrtab.Addstring(".strtab")
+ dwarfaddshstrings(ctxt, shstrtab)
+ }
+
+ shstrtab.Addstring(".shstrtab")
+
+ if !*FlagD { /* -d suppresses dynamic loader format */
+ shstrtab.Addstring(".interp")
+ shstrtab.Addstring(".hash")
+ shstrtab.Addstring(".got")
+ if ctxt.IsPPC64() {
+ shstrtab.Addstring(".glink")
+ }
+ shstrtab.Addstring(".got.plt")
+ shstrtab.Addstring(".dynamic")
+ shstrtab.Addstring(".dynsym")
+ shstrtab.Addstring(".dynstr")
+ shstrtab.Addstring(elfRelType)
+ shstrtab.Addstring(elfRelType + ".plt")
+
+ shstrtab.Addstring(".plt")
+ shstrtab.Addstring(".gnu.version")
+ shstrtab.Addstring(".gnu.version_r")
+
+ /* dynamic symbol table - first entry all zeros */
+ dynsym := ldr.CreateSymForUpdate(".dynsym", 0)
+
+ dynsym.SetType(sym.SELFROSECT)
+ if elf64 {
+ dynsym.SetSize(dynsym.Size() + ELF64SYMSIZE)
+ } else {
+ dynsym.SetSize(dynsym.Size() + ELF32SYMSIZE)
+ }
+
+ /* dynamic string table */
+ dynstr := ldr.CreateSymForUpdate(".dynstr", 0)
+
+ dynstr.SetType(sym.SELFROSECT)
+ if dynstr.Size() == 0 {
+ dynstr.Addstring("")
+ }
+
+ /* relocation table */
+ s := ldr.CreateSymForUpdate(elfRelType, 0)
+ s.SetType(sym.SELFROSECT)
+
+ /* global offset table */
+ got := ldr.CreateSymForUpdate(".got", 0)
+ got.SetType(sym.SELFGOT) // writable
+
+ /* ppc64 glink resolver */
+ if ctxt.IsPPC64() {
+ s := ldr.CreateSymForUpdate(".glink", 0)
+ s.SetType(sym.SELFRXSECT)
+ }
+
+ /* hash */
+ hash := ldr.CreateSymForUpdate(".hash", 0)
+ hash.SetType(sym.SELFROSECT)
+
+ gotplt := ldr.CreateSymForUpdate(".got.plt", 0)
+ gotplt.SetType(sym.SELFSECT) // writable
+
+ plt := ldr.CreateSymForUpdate(".plt", 0)
+ if ctxt.IsPPC64() {
+ // In the ppc64 ABI, .plt is a data section
+ // written by the dynamic linker.
+ plt.SetType(sym.SELFSECT)
+ } else {
+ plt.SetType(sym.SELFRXSECT)
+ }
+
+ s = ldr.CreateSymForUpdate(elfRelType+".plt", 0)
+ s.SetType(sym.SELFROSECT)
+
+ s = ldr.CreateSymForUpdate(".gnu.version", 0)
+ s.SetType(sym.SELFROSECT)
+
+ s = ldr.CreateSymForUpdate(".gnu.version_r", 0)
+ s.SetType(sym.SELFROSECT)
+
+ /* define dynamic elf table */
+ dynamic := ldr.CreateSymForUpdate(".dynamic", 0)
+ dynamic.SetType(sym.SELFSECT) // writable
+
+ if ctxt.IsS390X() {
+ // S390X uses .got instead of .got.plt
+ gotplt = got
+ }
+ thearch.Elfsetupplt(ctxt, plt, gotplt, dynamic.Sym())
+
+ /*
+ * .dynamic table
+ */
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_HASH, hash.Sym())
+
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_SYMTAB, dynsym.Sym())
+ if elf64 {
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF64SYMSIZE)
+ } else {
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF32SYMSIZE)
+ }
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_STRTAB, dynstr.Sym())
+ elfwritedynentsymsize(ctxt, dynamic, elf.DT_STRSZ, dynstr.Sym())
+ if elfRelType == ".rela" {
+ rela := ldr.LookupOrCreateSym(".rela", 0)
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_RELA, rela)
+ elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELASZ, rela)
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELAENT, ELF64RELASIZE)
+ } else {
+ rel := ldr.LookupOrCreateSym(".rel", 0)
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_REL, rel)
+ elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELSZ, rel)
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELENT, ELF32RELSIZE)
+ }
+
+ if rpath.val != "" {
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RUNPATH, uint64(dynstr.Addstring(rpath.val)))
+ }
+
+ if ctxt.IsPPC64() {
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_PLTGOT, plt.Sym())
+ } else {
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_PLTGOT, gotplt.Sym())
+ }
+
+ if ctxt.IsPPC64() {
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_PPC64_OPT, 0)
+ }
+
+ // Solaris dynamic linker can't handle an empty .rela.plt if
+ // DT_JMPREL is emitted so we have to defer generation of elf.DT_PLTREL,
+ // DT_PLTRELSZ, and elf.DT_JMPREL dynamic entries until after we know the
+ // size of .rel(a).plt section.
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_DEBUG, 0)
+ }
+
+ if ctxt.IsShared() {
+ // The go.link.abihashbytes symbol will be pointed at the appropriate
+ // part of the .note.go.abihash section in data.go:func address().
+ s := ldr.LookupOrCreateSym("go:link.abihashbytes", 0)
+ sb := ldr.MakeSymbolUpdater(s)
+ ldr.SetAttrLocal(s, true)
+ sb.SetType(sym.SRODATA)
+ ldr.SetAttrSpecial(s, true)
+ sb.SetReachable(true)
+ sb.SetSize(notsha256.Size)
+
+ sort.Sort(byPkg(ctxt.Library))
+ h := notsha256.New()
+ for _, l := range ctxt.Library {
+ h.Write(l.Fingerprint[:])
+ }
+ addgonote(ctxt, ".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{}))
+ addgonote(ctxt, ".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote)
+ var deplist []string
+ for _, shlib := range ctxt.Shlibs {
+ deplist = append(deplist, filepath.Base(shlib.Path))
+ }
+ addgonote(ctxt, ".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n")))
+ }
+
+ if ctxt.LinkMode == LinkExternal && *flagBuildid != "" {
+ addgonote(ctxt, ".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(*flagBuildid))
+ }
+
+ //type mipsGnuAttributes struct {
+ // version uint8 // 'A'
+ // length uint32 // 15 including itself
+ // gnu [4]byte // "gnu\0"
+ // tag uint8 // 1:file, 2: section, 3: symbol, 1 here
+ // taglen uint32 // tag length, including tag, 7 here
+ // tagfp uint8 // 4
+ // fpAbi uint8 // see .MIPS.abiflags
+ //}
+ if ctxt.IsMIPS() {
+ gnuattributes := ldr.CreateSymForUpdate(".gnu.attributes", 0)
+ gnuattributes.SetType(sym.SELFROSECT)
+ gnuattributes.SetReachable(true)
+ gnuattributes.AddUint8('A') // version 'A'
+ gnuattributes.AddUint32(ctxt.Arch, 15) // length 15 including itself
+ gnuattributes.AddBytes([]byte("gnu\x00")) // "gnu\0"
+ gnuattributes.AddUint8(1) // 1:file, 2: section, 3: symbol, 1 here
+ gnuattributes.AddUint32(ctxt.Arch, 7) // tag length, including tag, 7 here
+ gnuattributes.AddUint8(4) // 4 for FP, 8 for MSA
+ if buildcfg.GOMIPS == "softfloat" {
+ gnuattributes.AddUint8(MIPS_FPABI_SOFT)
+ } else {
+ // Note: MIPS_FPABI_ANY is bad naming: in fact it is MIPS I style FPR usage.
+ // It is not for 'ANY'.
+ // TODO: switch to FPXX after be sure that no odd-number-fpr is used.
+ gnuattributes.AddUint8(MIPS_FPABI_ANY)
+ }
+ }
+}
+
+// Do not write DT_NULL. elfdynhash will finish it.
+func shsym(sh *ElfShdr, ldr *loader.Loader, s loader.Sym) {
+ if s == 0 {
+ panic("bad symbol in shsym2")
+ }
+ addr := ldr.SymValue(s)
+ if sh.Flags&uint64(elf.SHF_ALLOC) != 0 {
+ sh.Addr = uint64(addr)
+ }
+ sh.Off = uint64(datoff(ldr, s, addr))
+ sh.Size = uint64(ldr.SymSize(s))
+}
+
+func phsh(ph *ElfPhdr, sh *ElfShdr) {
+ ph.Vaddr = sh.Addr
+ ph.Paddr = ph.Vaddr
+ ph.Off = sh.Off
+ ph.Filesz = sh.Size
+ ph.Memsz = sh.Size
+ ph.Align = sh.Addralign
+}
+
+func Asmbelfsetup() {
+ /* This null SHdr must appear before all others */
+ elfshname("")
+
+ for _, sect := range Segtext.Sections {
+ // There could be multiple .text sections. Instead check the Elfsect
+ // field to determine if already has an ElfShdr and if not, create one.
+ if sect.Name == ".text" {
+ if sect.Elfsect == nil {
+ sect.Elfsect = elfshnamedup(sect.Name)
+ }
+ } else {
+ elfshalloc(sect)
+ }
+ }
+ for _, sect := range Segrodata.Sections {
+ elfshalloc(sect)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ elfshalloc(sect)
+ }
+ for _, sect := range Segdata.Sections {
+ elfshalloc(sect)
+ }
+ for _, sect := range Segdwarf.Sections {
+ elfshalloc(sect)
+ }
+}
+
+func asmbElf(ctxt *Link) {
+ var symo int64
+ if !*FlagS {
+ symo = int64(Segdwarf.Fileoff + Segdwarf.Filelen)
+ symo = Rnd(symo, int64(ctxt.Arch.PtrSize))
+ ctxt.Out.SeekSet(symo)
+ asmElfSym(ctxt)
+ ctxt.Out.Write(Elfstrdat)
+ if ctxt.IsExternal() {
+ elfEmitReloc(ctxt)
+ }
+ }
+ ctxt.Out.SeekSet(0)
+
+ ldr := ctxt.loader
+ eh := getElfEhdr()
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown architecture in asmbelf: %v", ctxt.Arch.Family)
+ case sys.MIPS, sys.MIPS64:
+ eh.Machine = uint16(elf.EM_MIPS)
+ case sys.Loong64:
+ eh.Machine = uint16(elf.EM_LOONGARCH)
+ case sys.ARM:
+ eh.Machine = uint16(elf.EM_ARM)
+ case sys.AMD64:
+ eh.Machine = uint16(elf.EM_X86_64)
+ case sys.ARM64:
+ eh.Machine = uint16(elf.EM_AARCH64)
+ case sys.I386:
+ eh.Machine = uint16(elf.EM_386)
+ case sys.PPC64:
+ eh.Machine = uint16(elf.EM_PPC64)
+ case sys.RISCV64:
+ eh.Machine = uint16(elf.EM_RISCV)
+ case sys.S390X:
+ eh.Machine = uint16(elf.EM_S390)
+ }
+
+ elfreserve := int64(ELFRESERVE)
+
+ numtext := int64(0)
+ for _, sect := range Segtext.Sections {
+ if sect.Name == ".text" {
+ numtext++
+ }
+ }
+
+ // If there are multiple text sections, extra space is needed
+ // in the elfreserve for the additional .text and .rela.text
+ // section headers. It can handle 4 extra now. Headers are
+ // 64 bytes.
+
+ if numtext > 4 {
+ elfreserve += elfreserve + numtext*64*2
+ }
+
+ startva := *FlagTextAddr - int64(HEADR)
+ resoff := elfreserve
+
+ var pph *ElfPhdr
+ var pnote *ElfPhdr
+ getpnote := func() *ElfPhdr {
+ if pnote == nil {
+ pnote = newElfPhdr()
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
+ }
+ return pnote
+ }
+ if *flagRace && ctxt.IsNetbsd() {
+ sh := elfshname(".note.netbsd.pax")
+ resoff -= int64(elfnetbsdpax(sh, uint64(startva), uint64(resoff)))
+ phsh(getpnote(), sh)
+ }
+ if ctxt.LinkMode == LinkExternal {
+ /* skip program headers */
+ eh.Phoff = 0
+
+ eh.Phentsize = 0
+
+ if ctxt.BuildMode == BuildModeShared {
+ sh := elfshname(".note.go.pkg-list")
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh = elfshname(".note.go.abihash")
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh = elfshname(".note.go.deps")
+ sh.Type = uint32(elf.SHT_NOTE)
+ }
+
+ if *flagBuildid != "" {
+ sh := elfshname(".note.go.buildid")
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ }
+
+ goto elfobj
+ }
+
+ /* program header info */
+ pph = newElfPhdr()
+
+ pph.Type = elf.PT_PHDR
+ pph.Flags = elf.PF_R
+ pph.Off = uint64(eh.Ehsize)
+ pph.Vaddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off
+ pph.Paddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off
+ pph.Align = uint64(*FlagRound)
+
+ /*
+ * PHDR must be in a loaded segment. Adjust the text
+ * segment boundaries downwards to include it.
+ */
+ {
+ o := int64(Segtext.Vaddr - pph.Vaddr)
+ Segtext.Vaddr -= uint64(o)
+ Segtext.Length += uint64(o)
+ o = int64(Segtext.Fileoff - pph.Off)
+ Segtext.Fileoff -= uint64(o)
+ Segtext.Filelen += uint64(o)
+ }
+
+ if !*FlagD { /* -d suppresses dynamic loader format */
+ /* interpreter */
+ sh := elfshname(".interp")
+
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 1
+
+ if interpreter == "" && buildcfg.GOOS == runtime.GOOS && buildcfg.GOARCH == runtime.GOARCH && buildcfg.GO_LDSO != "" {
+ interpreter = buildcfg.GO_LDSO
+ }
+
+ if interpreter == "" {
+ switch ctxt.HeadType {
+ case objabi.Hlinux:
+ if buildcfg.GOOS == "android" {
+ interpreter = thearch.Androiddynld
+ if interpreter == "" {
+ Exitf("ELF interpreter not set")
+ }
+ } else {
+ interpreter = thearch.Linuxdynld
+ // If interpreter does not exist, try musl instead.
+ // This lets the same cmd/link binary work on
+ // both glibc-based and musl-based systems.
+ if _, err := os.Stat(interpreter); err != nil {
+ if musl := thearch.LinuxdynldMusl; musl != "" {
+ if _, err := os.Stat(musl); err == nil {
+ interpreter = musl
+ }
+ }
+ }
+ }
+
+ case objabi.Hfreebsd:
+ interpreter = thearch.Freebsddynld
+
+ case objabi.Hnetbsd:
+ interpreter = thearch.Netbsddynld
+
+ case objabi.Hopenbsd:
+ interpreter = thearch.Openbsddynld
+
+ case objabi.Hdragonfly:
+ interpreter = thearch.Dragonflydynld
+
+ case objabi.Hsolaris:
+ interpreter = thearch.Solarisdynld
+ }
+ }
+
+ resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter))
+
+ ph := newElfPhdr()
+ ph.Type = elf.PT_INTERP
+ ph.Flags = elf.PF_R
+ phsh(ph, sh)
+ }
+
+ if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd || ctxt.HeadType == objabi.Hfreebsd {
+ var sh *ElfShdr
+ switch ctxt.HeadType {
+ case objabi.Hnetbsd:
+ sh = elfshname(".note.netbsd.ident")
+ resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff)))
+
+ case objabi.Hopenbsd:
+ sh = elfshname(".note.openbsd.ident")
+ resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff)))
+
+ case objabi.Hfreebsd:
+ sh = elfshname(".note.tag")
+ resoff -= int64(elffreebsdsig(sh, uint64(startva), uint64(resoff)))
+ }
+ // NetBSD, OpenBSD and FreeBSD require ident in an independent segment.
+ pnotei := newElfPhdr()
+ pnotei.Type = elf.PT_NOTE
+ pnotei.Flags = elf.PF_R
+ phsh(pnotei, sh)
+ }
+
+ if len(buildinfo) > 0 {
+ sh := elfshname(".note.gnu.build-id")
+ resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff)))
+ phsh(getpnote(), sh)
+ }
+
+ if *flagBuildid != "" {
+ sh := elfshname(".note.go.buildid")
+ resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff)))
+ phsh(getpnote(), sh)
+ }
+
+ // Additions to the reserved area must be above this line.
+
+ elfphload(&Segtext)
+ if len(Segrodata.Sections) > 0 {
+ elfphload(&Segrodata)
+ }
+ if len(Segrelrodata.Sections) > 0 {
+ elfphload(&Segrelrodata)
+ elfphrelro(&Segrelrodata)
+ }
+ elfphload(&Segdata)
+
+ /* Dynamic linking sections */
+ if !*FlagD {
+ sh := elfshname(".dynsym")
+ sh.Type = uint32(elf.SHT_DYNSYM)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ if elf64 {
+ sh.Entsize = ELF64SYMSIZE
+ } else {
+ sh.Entsize = ELF32SYMSIZE
+ }
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".dynstr").shnum)
+
+ // sh.info is the index of first non-local symbol (number of local symbols)
+ s := ldr.Lookup(".dynsym", 0)
+ i := uint32(0)
+ for sub := s; sub != 0; sub = ldr.SubSym(sub) {
+ i++
+ if !ldr.AttrLocal(sub) {
+ break
+ }
+ }
+ sh.Info = i
+ shsym(sh, ldr, s)
+
+ sh = elfshname(".dynstr")
+ sh.Type = uint32(elf.SHT_STRTAB)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 1
+ shsym(sh, ldr, ldr.Lookup(".dynstr", 0))
+
+ if elfverneed != 0 {
+ sh := elfshname(".gnu.version")
+ sh.Type = uint32(elf.SHT_GNU_VERSYM)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 2
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.Entsize = 2
+ shsym(sh, ldr, ldr.Lookup(".gnu.version", 0))
+
+ sh = elfshname(".gnu.version_r")
+ sh.Type = uint32(elf.SHT_GNU_VERNEED)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Info = uint32(elfverneed)
+ sh.Link = uint32(elfshname(".dynstr").shnum)
+ shsym(sh, ldr, ldr.Lookup(".gnu.version_r", 0))
+ }
+
+ if elfRelType == ".rela" {
+ sh := elfshname(".rela.plt")
+ sh.Type = uint32(elf.SHT_RELA)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = ELF64RELASIZE
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.Info = uint32(elfshname(".plt").shnum)
+ shsym(sh, ldr, ldr.Lookup(".rela.plt", 0))
+
+ sh = elfshname(".rela")
+ sh.Type = uint32(elf.SHT_RELA)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = ELF64RELASIZE
+ sh.Addralign = 8
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ shsym(sh, ldr, ldr.Lookup(".rela", 0))
+ } else {
+ sh := elfshname(".rel.plt")
+ sh.Type = uint32(elf.SHT_REL)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = ELF32RELSIZE
+ sh.Addralign = 4
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ shsym(sh, ldr, ldr.Lookup(".rel.plt", 0))
+
+ sh = elfshname(".rel")
+ sh.Type = uint32(elf.SHT_REL)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = ELF32RELSIZE
+ sh.Addralign = 4
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ shsym(sh, ldr, ldr.Lookup(".rel", 0))
+ }
+
+ if elf.Machine(eh.Machine) == elf.EM_PPC64 {
+ sh := elfshname(".glink")
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR)
+ sh.Addralign = 4
+ shsym(sh, ldr, ldr.Lookup(".glink", 0))
+ }
+
+ sh = elfshname(".plt")
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR)
+ if elf.Machine(eh.Machine) == elf.EM_X86_64 {
+ sh.Entsize = 16
+ } else if elf.Machine(eh.Machine) == elf.EM_S390 {
+ sh.Entsize = 32
+ } else if elf.Machine(eh.Machine) == elf.EM_PPC64 {
+ // On ppc64, this is just a table of addresses
+ // filled by the dynamic linker
+ sh.Type = uint32(elf.SHT_NOBITS)
+
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = 8
+ } else {
+ sh.Entsize = 4
+ }
+ sh.Addralign = sh.Entsize
+ shsym(sh, ldr, ldr.Lookup(".plt", 0))
+
+ // On ppc64, .got comes from the input files, so don't
+ // create it here, and .got.plt is not used.
+ if elf.Machine(eh.Machine) != elf.EM_PPC64 {
+ sh := elfshname(".got")
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = uint64(ctxt.Arch.RegSize)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ shsym(sh, ldr, ldr.Lookup(".got", 0))
+
+ sh = elfshname(".got.plt")
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = uint64(ctxt.Arch.RegSize)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ shsym(sh, ldr, ldr.Lookup(".got.plt", 0))
+ }
+
+ sh = elfshname(".hash")
+ sh.Type = uint32(elf.SHT_HASH)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = 4
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ shsym(sh, ldr, ldr.Lookup(".hash", 0))
+
+ /* sh and elf.PT_DYNAMIC for .dynamic section */
+ sh = elfshname(".dynamic")
+
+ sh.Type = uint32(elf.SHT_DYNAMIC)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = 2 * uint64(ctxt.Arch.RegSize)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".dynstr").shnum)
+ shsym(sh, ldr, ldr.Lookup(".dynamic", 0))
+ ph := newElfPhdr()
+ ph.Type = elf.PT_DYNAMIC
+ ph.Flags = elf.PF_R + elf.PF_W
+ phsh(ph, sh)
+
+ /*
+ * Thread-local storage segment (really just size).
+ */
+ tlssize := uint64(0)
+ for _, sect := range Segdata.Sections {
+ if sect.Name == ".tbss" {
+ tlssize = sect.Length
+ }
+ }
+ if tlssize != 0 {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_TLS
+ ph.Flags = elf.PF_R
+ ph.Memsz = tlssize
+ ph.Align = uint64(ctxt.Arch.RegSize)
+ }
+ }
+
+ if ctxt.HeadType == objabi.Hlinux {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_GNU_STACK
+ ph.Flags = elf.PF_W + elf.PF_R
+ ph.Align = uint64(ctxt.Arch.RegSize)
+
+ ph = newElfPhdr()
+ ph.Type = elf.PT_PAX_FLAGS
+ ph.Flags = 0x2a00 // mprotect, randexec, emutramp disabled
+ ph.Align = uint64(ctxt.Arch.RegSize)
+ } else if ctxt.HeadType == objabi.Hsolaris {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_SUNWSTACK
+ ph.Flags = elf.PF_W + elf.PF_R
+ } else if ctxt.HeadType == objabi.Hfreebsd {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_GNU_STACK
+ ph.Flags = elf.PF_W + elf.PF_R
+ ph.Align = uint64(ctxt.Arch.RegSize)
+ }
+
+elfobj:
+ sh := elfshname(".shstrtab")
+ sh.Type = uint32(elf.SHT_STRTAB)
+ sh.Addralign = 1
+ shsym(sh, ldr, ldr.Lookup(".shstrtab", 0))
+ eh.Shstrndx = uint16(sh.shnum)
+
+ if ctxt.IsMIPS() {
+ sh = elfshname(".MIPS.abiflags")
+ sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 8
+ resoff -= int64(elfMipsAbiFlags(sh, uint64(startva), uint64(resoff)))
+
+ ph := newElfPhdr()
+ ph.Type = elf.PT_MIPS_ABIFLAGS
+ ph.Flags = elf.PF_R
+ phsh(ph, sh)
+
+ sh = elfshname(".gnu.attributes")
+ sh.Type = uint32(elf.SHT_GNU_ATTRIBUTES)
+ sh.Addralign = 1
+ ldr := ctxt.loader
+ shsym(sh, ldr, ldr.Lookup(".gnu.attributes", 0))
+ }
+
+ // put these sections early in the list
+ if !*FlagS {
+ elfshname(".symtab")
+ elfshname(".strtab")
+ }
+
+ for _, sect := range Segtext.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+ for _, sect := range Segrodata.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+ for _, sect := range Segdata.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+ for _, sect := range Segdwarf.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ for _, sect := range Segtext.Sections {
+ elfshreloc(ctxt.Arch, sect)
+ }
+ for _, sect := range Segrodata.Sections {
+ elfshreloc(ctxt.Arch, sect)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ elfshreloc(ctxt.Arch, sect)
+ }
+ for _, sect := range Segdata.Sections {
+ elfshreloc(ctxt.Arch, sect)
+ }
+ for _, si := range dwarfp {
+ sect := ldr.SymSect(si.secSym())
+ elfshreloc(ctxt.Arch, sect)
+ }
+ // add a .note.GNU-stack section to mark the stack as non-executable
+ sh := elfshname(".note.GNU-stack")
+
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Addralign = 1
+ sh.Flags = 0
+ }
+
+ if !*FlagS {
+ sh := elfshname(".symtab")
+ sh.Type = uint32(elf.SHT_SYMTAB)
+ sh.Off = uint64(symo)
+ sh.Size = uint64(symSize)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Entsize = 8 + 2*uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".strtab").shnum)
+ sh.Info = uint32(elfglobalsymndx)
+
+ sh = elfshname(".strtab")
+ sh.Type = uint32(elf.SHT_STRTAB)
+ sh.Off = uint64(symo) + uint64(symSize)
+ sh.Size = uint64(len(Elfstrdat))
+ sh.Addralign = 1
+ }
+
+ /* Main header */
+ copy(eh.Ident[:], elf.ELFMAG)
+
+ var osabi elf.OSABI
+ switch ctxt.HeadType {
+ case objabi.Hfreebsd:
+ osabi = elf.ELFOSABI_FREEBSD
+ case objabi.Hnetbsd:
+ osabi = elf.ELFOSABI_NETBSD
+ case objabi.Hopenbsd:
+ osabi = elf.ELFOSABI_OPENBSD
+ case objabi.Hdragonfly:
+ osabi = elf.ELFOSABI_NONE
+ }
+ eh.Ident[elf.EI_OSABI] = byte(osabi)
+
+ if elf64 {
+ eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS64)
+ } else {
+ eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS32)
+ }
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2MSB)
+ } else {
+ eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2LSB)
+ }
+ eh.Ident[elf.EI_VERSION] = byte(elf.EV_CURRENT)
+
+ if ctxt.LinkMode == LinkExternal {
+ eh.Type = uint16(elf.ET_REL)
+ } else if ctxt.BuildMode == BuildModePIE {
+ eh.Type = uint16(elf.ET_DYN)
+ } else {
+ eh.Type = uint16(elf.ET_EXEC)
+ }
+
+ if ctxt.LinkMode != LinkExternal {
+ eh.Entry = uint64(Entryvalue(ctxt))
+ }
+
+ eh.Version = uint32(elf.EV_CURRENT)
+
+ if pph != nil {
+ pph.Filesz = uint64(eh.Phnum) * uint64(eh.Phentsize)
+ pph.Memsz = pph.Filesz
+ }
+
+ ctxt.Out.SeekSet(0)
+ a := int64(0)
+ a += int64(elfwritehdr(ctxt.Out))
+ a += int64(elfwritephdrs(ctxt.Out))
+ a += int64(elfwriteshdrs(ctxt.Out))
+ if !*FlagD {
+ a += int64(elfwriteinterp(ctxt.Out))
+ }
+ if ctxt.IsMIPS() {
+ a += int64(elfWriteMipsAbiFlags(ctxt))
+ }
+
+ if ctxt.LinkMode != LinkExternal {
+ if ctxt.HeadType == objabi.Hnetbsd {
+ a += int64(elfwritenetbsdsig(ctxt.Out))
+ }
+ if ctxt.HeadType == objabi.Hopenbsd {
+ a += int64(elfwriteopenbsdsig(ctxt.Out))
+ }
+ if ctxt.HeadType == objabi.Hfreebsd {
+ a += int64(elfwritefreebsdsig(ctxt.Out))
+ }
+ if len(buildinfo) > 0 {
+ a += int64(elfwritebuildinfo(ctxt.Out))
+ }
+ if *flagBuildid != "" {
+ a += int64(elfwritegobuildid(ctxt.Out))
+ }
+ }
+ if *flagRace && ctxt.IsNetbsd() {
+ a += int64(elfwritenetbsdpax(ctxt.Out))
+ }
+
+ if a > elfreserve {
+ Errorf(nil, "ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext)
+ }
+
+ // Verify the amount of space allocated for the elf header is sufficient. The file offsets are
+ // already computed in layout, so we could spill into another section.
+ if a > int64(HEADR) {
+ Errorf(nil, "HEADR too small: %d > %d with %d text sections", a, HEADR, numtext)
+ }
+}
+
+func elfadddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
+ ldr.SetSymDynid(s, int32(Nelfsym))
+ Nelfsym++
+ d := ldr.MakeSymbolUpdater(syms.DynSym)
+ name := ldr.SymExtname(s)
+ dstru := ldr.MakeSymbolUpdater(syms.DynStr)
+ st := ldr.SymType(s)
+ cgoeStatic := ldr.AttrCgoExportStatic(s)
+ cgoeDynamic := ldr.AttrCgoExportDynamic(s)
+ cgoexp := (cgoeStatic || cgoeDynamic)
+
+ d.AddUint32(target.Arch, uint32(dstru.Addstring(name)))
+
+ if elf64 {
+
+ /* type */
+ var t uint8
+
+ if cgoexp && st == sym.STEXT {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
+ } else {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT)
+ }
+ d.AddUint8(t)
+
+ /* reserved */
+ d.AddUint8(0)
+
+ /* section where symbol is defined */
+ if st == sym.SDYNIMPORT {
+ d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF))
+ } else {
+ d.AddUint16(target.Arch, 1)
+ }
+
+ /* value */
+ if st == sym.SDYNIMPORT {
+ d.AddUint64(target.Arch, 0)
+ } else {
+ d.AddAddrPlus(target.Arch, s, 0)
+ }
+
+ /* size of object */
+ d.AddUint64(target.Arch, uint64(len(ldr.Data(s))))
+
+ dil := ldr.SymDynimplib(s)
+
+ if !cgoeDynamic && dil != "" && !seenlib[dil] {
+ du := ldr.MakeSymbolUpdater(syms.Dynamic)
+ Elfwritedynent(target.Arch, du, elf.DT_NEEDED, uint64(dstru.Addstring(dil)))
+ seenlib[dil] = true
+ }
+ } else {
+
+ /* value */
+ if st == sym.SDYNIMPORT {
+ d.AddUint32(target.Arch, 0)
+ } else {
+ d.AddAddrPlus(target.Arch, s, 0)
+ }
+
+ /* size of object */
+ d.AddUint32(target.Arch, uint32(len(ldr.Data(s))))
+
+ /* type */
+ var t uint8
+
+ // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386.
+ if target.Arch.Family == sys.I386 && cgoexp && st == sym.STEXT {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
+ } else if target.Arch.Family == sys.ARM && cgoeDynamic && st == sym.STEXT {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
+ } else {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT)
+ }
+ d.AddUint8(t)
+ d.AddUint8(0)
+
+ /* shndx */
+ if st == sym.SDYNIMPORT {
+ d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF))
+ } else {
+ d.AddUint16(target.Arch, 1)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/elf_test.go b/src/cmd/link/internal/ld/elf_test.go
new file mode 100644
index 0000000..8af0ca1
--- /dev/null
+++ b/src/cmd/link/internal/ld/elf_test.go
@@ -0,0 +1,127 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+// +build cgo
+
+package ld
+
+import (
+ "debug/elf"
+ "internal/testenv"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestDynSymShInfo(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ dir := t.TempDir()
+
+ const prog = `
+package main
+
+import "net"
+
+func main() {
+ net.Dial("", "")
+}
+`
+ src := filepath.Join(dir, "issue33358.go")
+ if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ binFile := filepath.Join(dir, "issue33358")
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+ }
+
+ fi, err := os.Open(binFile)
+ if err != nil {
+ t.Fatalf("failed to open built file: %v", err)
+ }
+ defer fi.Close()
+
+ elfFile, err := elf.NewFile(fi)
+ if err != nil {
+ t.Skip("The system may not support ELF, skipped.")
+ }
+
+ section := elfFile.Section(".dynsym")
+ if section == nil {
+ t.Fatal("no dynsym")
+ }
+
+ symbols, err := elfFile.DynamicSymbols()
+ if err != nil {
+ t.Fatalf("failed to get dynamic symbols: %v", err)
+ }
+
+ var numLocalSymbols uint32
+ for i, s := range symbols {
+ if elf.ST_BIND(s.Info) != elf.STB_LOCAL {
+ numLocalSymbols = uint32(i + 1)
+ break
+ }
+ }
+
+ if section.Info != numLocalSymbols {
+ t.Fatalf("Unexpected sh info, want greater than 0, got: %d", section.Info)
+ }
+}
+
+func TestNoDuplicateNeededEntries(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ // run this test on just a small set of platforms (no need to test it
+ // across the board given the nature of the test).
+ pair := runtime.GOOS + "-" + runtime.GOARCH
+ switch pair {
+ case "linux-amd64", "linux-arm64", "freebsd-amd64", "openbsd-amd64":
+ default:
+ t.Skip("no need for test on " + pair)
+ }
+
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("Failed to get working directory: %v", err)
+ }
+
+ path := filepath.Join(dir, "x")
+ argv := []string{"build", "-o", path, filepath.Join(wd, "testdata", "issue39256")}
+ out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
+ if err != nil {
+ t.Fatalf("Build failure: %s\n%s\n", err, string(out))
+ }
+
+ f, err := elf.Open(path)
+ if err != nil {
+ t.Fatalf("Failed to open ELF file: %v", err)
+ }
+ libs, err := f.ImportedLibraries()
+ if err != nil {
+ t.Fatalf("Failed to read imported libraries: %v", err)
+ }
+
+ var count int
+ for _, lib := range libs {
+ if lib == "libc.so" || strings.HasPrefix(lib, "libc.so.") {
+ count++
+ }
+ }
+
+ if got, want := count, 1; got != want {
+ t.Errorf("Got %d entries for `libc.so`, want %d", got, want)
+ }
+}
diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go
new file mode 100644
index 0000000..b553d68
--- /dev/null
+++ b/src/cmd/link/internal/ld/errors.go
@@ -0,0 +1,67 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/obj"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "sync"
+)
+
+type unresolvedSymKey struct {
+ from loader.Sym // Symbol that referenced unresolved "to"
+ to loader.Sym // Unresolved symbol referenced by "from"
+}
+
+type symNameFn func(s loader.Sym) string
+
+// ErrorReporter is used to make error reporting thread safe.
+type ErrorReporter struct {
+ loader.ErrorReporter
+ unresSyms map[unresolvedSymKey]bool
+ unresMutex sync.Mutex
+ SymName symNameFn
+}
+
+// errorUnresolved prints unresolved symbol error for rs that is referenced from s.
+func (reporter *ErrorReporter) errorUnresolved(ldr *loader.Loader, s, rs loader.Sym) {
+ reporter.unresMutex.Lock()
+ defer reporter.unresMutex.Unlock()
+
+ if reporter.unresSyms == nil {
+ reporter.unresSyms = make(map[unresolvedSymKey]bool)
+ }
+ k := unresolvedSymKey{from: s, to: rs}
+ if !reporter.unresSyms[k] {
+ reporter.unresSyms[k] = true
+ name := ldr.SymName(rs)
+
+ // Try to find symbol under another ABI.
+ var reqABI, haveABI obj.ABI
+ haveABI = ^obj.ABI(0)
+ reqABI, ok := sym.VersionToABI(ldr.SymVersion(rs))
+ if ok {
+ for abi := obj.ABI(0); abi < obj.ABICount; abi++ {
+ v := sym.ABIToVersion(abi)
+ if v == -1 {
+ continue
+ }
+ if rs1 := ldr.Lookup(name, v); rs1 != 0 && ldr.SymType(rs1) != sym.Sxxx && ldr.SymType(rs1) != sym.SXREF {
+ haveABI = abi
+ }
+ }
+ }
+
+ // Give a special error message for main symbol (see #24809).
+ if name == "main.main" {
+ reporter.Errorf(s, "function main is undeclared in the main package")
+ } else if haveABI != ^obj.ABI(0) {
+ reporter.Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", name, reqABI, haveABI)
+ } else {
+ reporter.Errorf(s, "relocation target %s not defined", name)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/execarchive.go b/src/cmd/link/internal/ld/execarchive.go
new file mode 100644
index 0000000..a9376e9
--- /dev/null
+++ b/src/cmd/link/internal/ld/execarchive.go
@@ -0,0 +1,38 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !wasm && !windows
+// +build !wasm,!windows
+
+package ld
+
+import (
+ "os"
+ "os/exec"
+ "path/filepath"
+ "syscall"
+)
+
+const syscallExecSupported = true
+
+// execArchive invokes the archiver tool with syscall.Exec(), with
+// the expectation that this is the last thing that takes place
+// in the linking operation.
+func (ctxt *Link) execArchive(argv []string) {
+ var err error
+ argv0 := argv[0]
+ if filepath.Base(argv0) == argv0 {
+ argv0, err = exec.LookPath(argv0)
+ if err != nil {
+ Exitf("cannot find %s: %v", argv[0], err)
+ }
+ }
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("invoking archiver with syscall.Exec()\n")
+ }
+ err = syscall.Exec(argv0, argv, os.Environ())
+ if err != nil {
+ Exitf("running %s failed: %v", argv[0], err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/execarchive_noexec.go b/src/cmd/link/internal/ld/execarchive_noexec.go
new file mode 100644
index 0000000..5e1f266
--- /dev/null
+++ b/src/cmd/link/internal/ld/execarchive_noexec.go
@@ -0,0 +1,14 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build wasm || windows
+// +build wasm windows
+
+package ld
+
+const syscallExecSupported = false
+
+func (ctxt *Link) execArchive(argv []string) {
+ panic("should never arrive here")
+}
diff --git a/src/cmd/link/internal/ld/fallocate_test.go b/src/cmd/link/internal/ld/fallocate_test.go
new file mode 100644
index 0000000..1ed0eb2
--- /dev/null
+++ b/src/cmd/link/internal/ld/fallocate_test.go
@@ -0,0 +1,65 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin || linux
+// +build darwin linux
+
+package ld
+
+import (
+ "os"
+ "path/filepath"
+ "syscall"
+ "testing"
+)
+
+func TestFallocate(t *testing.T) {
+ dir := t.TempDir()
+ filename := filepath.Join(dir, "a.out")
+ out := NewOutBuf(nil)
+ err := out.Open(filename)
+ if err != nil {
+ t.Fatalf("Open file failed: %v", err)
+ }
+ defer out.Close()
+
+ // Try fallocate first.
+ for {
+ err = out.fallocate(1 << 10)
+ if err == syscall.EOPNOTSUPP { // The underlying file system may not support fallocate
+ t.Skip("fallocate is not supported")
+ }
+ if err == syscall.EINTR {
+ continue // try again
+ }
+ if err != nil {
+ t.Fatalf("fallocate failed: %v", err)
+ }
+ break
+ }
+
+ // Mmap 1 MiB initially, and grow to 2 and 3 MiB.
+ // Check if the file size and disk usage is expected.
+ for _, sz := range []int64{1 << 20, 2 << 20, 3 << 20} {
+ err = out.Mmap(uint64(sz))
+ if err != nil {
+ t.Fatalf("Mmap failed: %v", err)
+ }
+ stat, err := os.Stat(filename)
+ if err != nil {
+ t.Fatalf("Stat failed: %v", err)
+ }
+ if got := stat.Size(); got != sz {
+ t.Errorf("unexpected file size: got %d, want %d", got, sz)
+ }
+ // The number of blocks must be enough for the requested size.
+ // We used to require an exact match, but it appears that
+ // some file systems allocate a few extra blocks in some cases.
+ // See issue #41127.
+ if got, want := stat.Sys().(*syscall.Stat_t).Blocks, (sz+511)/512; got < want {
+ t.Errorf("unexpected disk usage: got %d blocks, want at least %d", got, want)
+ }
+ out.munmap()
+ }
+}
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
new file mode 100644
index 0000000..9dfb0f7
--- /dev/null
+++ b/src/cmd/link/internal/ld/go.go
@@ -0,0 +1,499 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// go-specific code shared across loaders (5l, 6l, 8l).
+
+package ld
+
+import (
+ "cmd/internal/bio"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// go-specific code shared across loaders (5l, 6l, 8l).
+
+// TODO:
+// generate debugging section in binary.
+// once the dust settles, try to move some code to
+// libmach, so that other linkers and ar can share.
+
+func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) {
+ if *flagG {
+ return
+ }
+
+ if int64(int(length)) != length {
+ fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
+ return
+ }
+
+ bdata := make([]byte, length)
+ if _, err := io.ReadFull(f, bdata); err != nil {
+ fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
+ return
+ }
+ data := string(bdata)
+
+ // process header lines
+ for data != "" {
+ var line string
+ if i := strings.Index(data, "\n"); i >= 0 {
+ line, data = data[:i], data[i+1:]
+ } else {
+ line, data = data, ""
+ }
+ if line == "main" {
+ lib.Main = true
+ }
+ if line == "" {
+ break
+ }
+ }
+
+ // look for cgo section
+ p0 := strings.Index(data, "\n$$ // cgo")
+ var p1 int
+ if p0 >= 0 {
+ p0 += p1
+ i := strings.IndexByte(data[p0+1:], '\n')
+ if i < 0 {
+ fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
+ return
+ }
+ p0 += 1 + i
+
+ p1 = strings.Index(data[p0:], "\n$$")
+ if p1 < 0 {
+ p1 = strings.Index(data[p0:], "\n!\n")
+ }
+ if p1 < 0 {
+ fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
+ return
+ }
+ p1 += p0
+ loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
+ }
+}
+
+func loadcgo(ctxt *Link, file string, pkg string, p string) {
+ var directives [][]string
+ if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
+ nerrors++
+ return
+ }
+
+ // Record the directives. We'll process them later after Symbols are created.
+ ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
+}
+
+// Set symbol attributes or flags based on cgo directives.
+// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'.
+func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) {
+ l := ctxt.loader
+ for _, f := range directives {
+ switch f[0] {
+ case "cgo_import_dynamic":
+ if len(f) < 2 || len(f) > 4 {
+ break
+ }
+
+ local := f[1]
+ remote := local
+ if len(f) > 2 {
+ remote = f[2]
+ }
+ lib := ""
+ if len(f) > 3 {
+ lib = f[3]
+ }
+
+ if *FlagD {
+ fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
+ nerrors++
+ return
+ }
+
+ if local == "_" && remote == "_" {
+ // allow #pragma dynimport _ _ "foo.so"
+ // to force a link of foo.so.
+ havedynamic = 1
+
+ if ctxt.HeadType == objabi.Hdarwin {
+ machoadddynlib(lib, ctxt.LinkMode)
+ } else {
+ dynlib = append(dynlib, lib)
+ }
+ continue
+ }
+
+ q := ""
+ if i := strings.Index(remote, "#"); i >= 0 {
+ remote, q = remote[:i], remote[i+1:]
+ }
+ s := l.LookupOrCreateSym(local, 0)
+ st := l.SymType(s)
+ if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ {
+ l.SetSymDynimplib(s, lib)
+ l.SetSymExtname(s, remote)
+ l.SetSymDynimpvers(s, q)
+ if st != sym.SHOSTOBJ {
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SDYNIMPORT)
+ } else {
+ hostObjSyms[s] = struct{}{}
+ }
+ havedynamic = 1
+ if lib != "" && ctxt.IsDarwin() {
+ machoadddynlib(lib, ctxt.LinkMode)
+ }
+ }
+
+ continue
+
+ case "cgo_import_static":
+ if len(f) != 2 {
+ break
+ }
+ local := f[1]
+
+ s := l.LookupOrCreateSym(local, 0)
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SHOSTOBJ)
+ su.SetSize(0)
+ hostObjSyms[s] = struct{}{}
+ continue
+
+ case "cgo_export_static", "cgo_export_dynamic":
+ if len(f) < 2 || len(f) > 4 {
+ break
+ }
+ local := f[1]
+ remote := local
+ if len(f) > 2 {
+ remote = f[2]
+ }
+ // The compiler adds a fourth argument giving
+ // the definition ABI of function symbols.
+ abi := obj.ABI0
+ if len(f) > 3 {
+ var ok bool
+ abi, ok = obj.ParseABI(f[3])
+ if !ok {
+ fmt.Fprintf(os.Stderr, "%s: bad ABI in cgo_export directive %s\n", os.Args[0], f)
+ nerrors++
+ return
+ }
+ }
+
+ s := l.LookupOrCreateSym(local, sym.ABIToVersion(abi))
+
+ if l.SymType(s) == sym.SHOSTOBJ {
+ hostObjSyms[s] = struct{}{}
+ }
+
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
+ if s == l.Lookup("main", 0) {
+ continue
+ }
+ }
+
+ // export overrides import, for openbsd/cgo.
+ // see issue 4878.
+ if l.SymDynimplib(s) != "" {
+ l.SetSymDynimplib(s, "")
+ l.SetSymDynimpvers(s, "")
+ l.SetSymExtname(s, "")
+ var su *loader.SymbolBuilder
+ su = l.MakeSymbolUpdater(s)
+ su.SetType(0)
+ }
+
+ if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) {
+ l.SetSymExtname(s, remote)
+ } else if l.SymExtname(s) != remote {
+ fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote)
+ nerrors++
+ return
+ }
+
+ // Mark exported symbols and also add them to
+ // the lists used for roots in the deadcode pass.
+ if f[0] == "cgo_export_static" {
+ if ctxt.LinkMode == LinkExternal && !l.AttrCgoExportStatic(s) {
+ // Static cgo exports appear
+ // in the exported symbol table.
+ ctxt.dynexp = append(ctxt.dynexp, s)
+ }
+ if ctxt.LinkMode == LinkInternal {
+ // For internal linking, we're
+ // responsible for resolving
+ // relocations from host objects.
+ // Record the right Go symbol
+ // version to use.
+ l.AddCgoExport(s)
+ }
+ l.SetAttrCgoExportStatic(s, true)
+ } else {
+ if ctxt.LinkMode == LinkInternal && !l.AttrCgoExportDynamic(s) {
+ // Dynamic cgo exports appear
+ // in the exported symbol table.
+ ctxt.dynexp = append(ctxt.dynexp, s)
+ }
+ l.SetAttrCgoExportDynamic(s, true)
+ }
+
+ continue
+
+ case "cgo_dynamic_linker":
+ if len(f) != 2 {
+ break
+ }
+
+ if *flagInterpreter == "" {
+ if interpreter != "" && interpreter != f[1] {
+ fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
+ nerrors++
+ return
+ }
+
+ interpreter = f[1]
+ }
+ continue
+
+ case "cgo_ldflag":
+ if len(f) != 2 {
+ break
+ }
+ ldflag = append(ldflag, f[1])
+ continue
+ }
+
+ fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
+ nerrors++
+ }
+ return
+}
+
+// openbsdTrimLibVersion indicates whether a shared library is
+// versioned and if it is, returns the unversioned name. The
+// OpenBSD library naming scheme is lib<name>.so.<major>.<minor>
+func openbsdTrimLibVersion(lib string) (string, bool) {
+ parts := strings.Split(lib, ".")
+ if len(parts) != 4 {
+ return "", false
+ }
+ if parts[1] != "so" {
+ return "", false
+ }
+ if _, err := strconv.Atoi(parts[2]); err != nil {
+ return "", false
+ }
+ if _, err := strconv.Atoi(parts[3]); err != nil {
+ return "", false
+ }
+ return fmt.Sprintf("%s.%s", parts[0], parts[1]), true
+}
+
+// dedupLibrariesOpenBSD dedups a list of shared libraries, treating versioned
+// and unversioned libraries as equivalents. Versioned libraries are preferred
+// and retained over unversioned libraries. This avoids the situation where
+// the use of cgo results in a DT_NEEDED for a versioned library (for example,
+// libc.so.96.1), while a dynamic import specifies an unversioned library (for
+// example, libc.so) - this would otherwise result in two DT_NEEDED entries
+// for the same library, resulting in a failure when ld.so attempts to load
+// the Go binary.
+func dedupLibrariesOpenBSD(ctxt *Link, libs []string) []string {
+ libraries := make(map[string]string)
+ for _, lib := range libs {
+ if name, ok := openbsdTrimLibVersion(lib); ok {
+ // Record unversioned name as seen.
+ seenlib[name] = true
+ libraries[name] = lib
+ } else if _, ok := libraries[lib]; !ok {
+ libraries[lib] = lib
+ }
+ }
+
+ libs = nil
+ for _, lib := range libraries {
+ libs = append(libs, lib)
+ }
+ sort.Strings(libs)
+
+ return libs
+}
+
+func dedupLibraries(ctxt *Link, libs []string) []string {
+ if ctxt.Target.IsOpenbsd() {
+ return dedupLibrariesOpenBSD(ctxt, libs)
+ }
+ return libs
+}
+
+var seenlib = make(map[string]bool)
+
+func adddynlib(ctxt *Link, lib string) {
+ if seenlib[lib] || ctxt.LinkMode == LinkExternal {
+ return
+ }
+ seenlib[lib] = true
+
+ if ctxt.IsELF {
+ dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr)
+ if dsu.Size() == 0 {
+ dsu.Addstring("")
+ }
+ du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic)
+ Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib)))
+ } else {
+ Errorf(nil, "adddynlib: unsupported binary format")
+ }
+}
+
+func Adddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
+ if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal {
+ return
+ }
+
+ if target.IsELF {
+ elfadddynsym(ldr, target, syms, s)
+ } else if target.HeadType == objabi.Hdarwin {
+ ldr.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s))
+ } else if target.HeadType == objabi.Hwindows {
+ // already taken care of
+ } else {
+ ldr.Errorf(s, "adddynsym: unsupported binary format")
+ }
+}
+
+func fieldtrack(arch *sys.Arch, l *loader.Loader) {
+ var buf strings.Builder
+ for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ {
+ if name := l.SymName(i); strings.HasPrefix(name, "go:track.") {
+ if l.AttrReachable(i) {
+ l.SetAttrSpecial(i, true)
+ l.SetAttrNotInSymbolTable(i, true)
+ buf.WriteString(name[9:])
+ for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] {
+ buf.WriteString("\t")
+ buf.WriteString(l.SymName(p))
+ }
+ buf.WriteString("\n")
+ }
+ }
+ }
+ l.Reachparent = nil // we are done with it
+ if *flagFieldTrack == "" {
+ return
+ }
+ s := l.Lookup(*flagFieldTrack, 0)
+ if s == 0 || !l.AttrReachable(s) {
+ return
+ }
+ bld := l.MakeSymbolUpdater(s)
+ bld.SetType(sym.SDATA)
+ addstrdata(arch, l, *flagFieldTrack, buf.String())
+}
+
+func (ctxt *Link) addexport() {
+ // Track undefined external symbols during external link.
+ if ctxt.LinkMode == LinkExternal {
+ for _, s := range ctxt.Textp {
+ if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) {
+ continue
+ }
+ relocs := ctxt.loader.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ if rs := relocs.At(i).Sym(); rs != 0 {
+ if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) {
+ // sanity check
+ if len(ctxt.loader.Data(rs)) != 0 {
+ panic("expected no data on undef symbol")
+ }
+ su := ctxt.loader.MakeSymbolUpdater(rs)
+ su.SetType(sym.SUNDEFEXT)
+ }
+ }
+ }
+ }
+ }
+
+ // TODO(aix)
+ if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
+ return
+ }
+
+ // Add dynamic symbols.
+ for _, s := range ctxt.dynexp {
+ // Consistency check.
+ if !ctxt.loader.AttrReachable(s) {
+ panic("dynexp entry not reachable")
+ }
+
+ Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, s)
+ }
+
+ for _, lib := range dedupLibraries(ctxt, dynlib) {
+ adddynlib(ctxt, lib)
+ }
+}
+
+type Pkg struct {
+ mark bool
+ checked bool
+ path string
+ impby []*Pkg
+}
+
+var pkgall []*Pkg
+
+func (p *Pkg) cycle() *Pkg {
+ if p.checked {
+ return nil
+ }
+
+ if p.mark {
+ nerrors++
+ fmt.Printf("import cycle:\n")
+ fmt.Printf("\t%s\n", p.path)
+ return p
+ }
+
+ p.mark = true
+ for _, q := range p.impby {
+ if bad := q.cycle(); bad != nil {
+ p.mark = false
+ p.checked = true
+ fmt.Printf("\timports %s\n", p.path)
+ if bad == p {
+ return nil
+ }
+ return bad
+ }
+ }
+
+ p.checked = true
+ p.mark = false
+ return nil
+}
+
+func importcycles() {
+ for _, p := range pkgall {
+ p.cycle()
+ }
+}
diff --git a/src/cmd/link/internal/ld/go_test.go b/src/cmd/link/internal/ld/go_test.go
new file mode 100644
index 0000000..836731a
--- /dev/null
+++ b/src/cmd/link/internal/ld/go_test.go
@@ -0,0 +1,116 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "internal/testenv"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "testing"
+
+ "cmd/internal/objabi"
+)
+
+func TestDedupLibraries(t *testing.T) {
+ ctxt := &Link{}
+ ctxt.Target.HeadType = objabi.Hlinux
+
+ libs := []string{"libc.so", "libc.so.6"}
+
+ got := dedupLibraries(ctxt, libs)
+ if !reflect.DeepEqual(got, libs) {
+ t.Errorf("dedupLibraries(%v) = %v, want %v", libs, got, libs)
+ }
+}
+
+func TestDedupLibrariesOpenBSD(t *testing.T) {
+ ctxt := &Link{}
+ ctxt.Target.HeadType = objabi.Hopenbsd
+
+ tests := []struct {
+ libs []string
+ want []string
+ }{
+ {
+ libs: []string{"libc.so"},
+ want: []string{"libc.so"},
+ },
+ {
+ libs: []string{"libc.so", "libc.so.96.1"},
+ want: []string{"libc.so.96.1"},
+ },
+ {
+ libs: []string{"libc.so.96.1", "libc.so"},
+ want: []string{"libc.so.96.1"},
+ },
+ {
+ libs: []string{"libc.a", "libc.so.96.1"},
+ want: []string{"libc.a", "libc.so.96.1"},
+ },
+ {
+ libs: []string{"libpthread.so", "libc.so"},
+ want: []string{"libc.so", "libpthread.so"},
+ },
+ {
+ libs: []string{"libpthread.so.26.1", "libpthread.so", "libc.so.96.1", "libc.so"},
+ want: []string{"libc.so.96.1", "libpthread.so.26.1"},
+ },
+ {
+ libs: []string{"libpthread.so.26.1", "libpthread.so", "libc.so.96.1", "libc.so", "libfoo.so"},
+ want: []string{"libc.so.96.1", "libfoo.so", "libpthread.so.26.1"},
+ },
+ }
+
+ for _, test := range tests {
+ t.Run("dedup", func(t *testing.T) {
+ got := dedupLibraries(ctxt, test.libs)
+ if !reflect.DeepEqual(got, test.want) {
+ t.Errorf("dedupLibraries(%v) = %v, want %v", test.libs, got, test.want)
+ }
+ })
+ }
+}
+
+func TestDedupLibrariesOpenBSDLink(t *testing.T) {
+ // The behavior we're checking for is of interest only on OpenBSD.
+ if runtime.GOOS != "openbsd" {
+ t.Skip("test only useful on openbsd")
+ }
+
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ // cgo_import_dynamic both the unversioned libraries and pull in the
+ // net package to get a cgo package with a versioned library.
+ srcFile := filepath.Join(dir, "x.go")
+ src := `package main
+
+import (
+ _ "net"
+)
+
+//go:cgo_import_dynamic _ _ "libc.so"
+
+func main() {}`
+ if err := os.WriteFile(srcFile, []byte(src), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ exe := filepath.Join(dir, "deduped.exe")
+ out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, srcFile).CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failure: %s\n%s\n", err, string(out))
+ }
+
+ // Result should be runnable.
+ if _, err = testenv.Command(t, exe).CombinedOutput(); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/heap.go b/src/cmd/link/internal/ld/heap.go
new file mode 100644
index 0000000..ea2d772
--- /dev/null
+++ b/src/cmd/link/internal/ld/heap.go
@@ -0,0 +1,54 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import "cmd/link/internal/loader"
+
+// Min-heap implementation, for the deadcode pass.
+// Specialized for loader.Sym elements.
+
+type heap []loader.Sym
+
+func (h *heap) push(s loader.Sym) {
+ *h = append(*h, s)
+ // sift up
+ n := len(*h) - 1
+ for n > 0 {
+ p := (n - 1) / 2 // parent
+ if (*h)[p] <= (*h)[n] {
+ break
+ }
+ (*h)[n], (*h)[p] = (*h)[p], (*h)[n]
+ n = p
+ }
+}
+
+func (h *heap) pop() loader.Sym {
+ r := (*h)[0]
+ n := len(*h) - 1
+ (*h)[0] = (*h)[n]
+ *h = (*h)[:n]
+
+ // sift down
+ i := 0
+ for {
+ c := 2*i + 1 // left child
+ if c >= n {
+ break
+ }
+ if c1 := c + 1; c1 < n && (*h)[c1] < (*h)[c] {
+ c = c1 // right child
+ }
+ if (*h)[i] <= (*h)[c] {
+ break
+ }
+ (*h)[i], (*h)[c] = (*h)[c], (*h)[i]
+ i = c
+ }
+
+ return r
+}
+
+func (h *heap) empty() bool { return len(*h) == 0 }
diff --git a/src/cmd/link/internal/ld/heap_test.go b/src/cmd/link/internal/ld/heap_test.go
new file mode 100644
index 0000000..08c9030
--- /dev/null
+++ b/src/cmd/link/internal/ld/heap_test.go
@@ -0,0 +1,90 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/link/internal/loader"
+ "testing"
+)
+
+func TestHeap(t *testing.T) {
+ tests := [][]loader.Sym{
+ {10, 20, 30, 40, 50, 60, 70, 80, 90, 100},
+ {100, 90, 80, 70, 60, 50, 40, 30, 20, 10},
+ {30, 50, 80, 20, 60, 70, 10, 100, 90, 40},
+ }
+ for _, s := range tests {
+ h := heap{}
+ for _, i := range s {
+ h.push(i)
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ }
+ for j := 0; j < len(s); j++ {
+ x := h.pop()
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ // pop should return elements in ascending order.
+ if want := loader.Sym((j + 1) * 10); x != want {
+ t.Errorf("pop returns wrong element: want %d, got %d", want, x)
+ }
+ }
+ if !h.empty() {
+ t.Errorf("heap is not empty after all pops")
+ }
+ }
+
+ // Also check that mixed pushes and pops work correctly.
+ for _, s := range tests {
+ h := heap{}
+ for i := 0; i < len(s)/2; i++ {
+ // two pushes, one pop
+ h.push(s[2*i])
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ h.push(s[2*i+1])
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ h.pop()
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ }
+ for !h.empty() { // pop remaining elements
+ h.pop()
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ }
+ }
+}
+
+// recursively verify heap-ness, starting at element i.
+func verify(h *heap, i int) bool {
+ n := len(*h)
+ c1 := 2*i + 1 // left child
+ c2 := 2*i + 2 // right child
+ if c1 < n {
+ if (*h)[c1] < (*h)[i] {
+ return false
+ }
+ if !verify(h, c1) {
+ return false
+ }
+ }
+ if c2 < n {
+ if (*h)[c2] < (*h)[i] {
+ return false
+ }
+ if !verify(h, c2) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/cmd/link/internal/ld/issue33808_test.go b/src/cmd/link/internal/ld/issue33808_test.go
new file mode 100644
index 0000000..43f4540
--- /dev/null
+++ b/src/cmd/link/internal/ld/issue33808_test.go
@@ -0,0 +1,49 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "internal/testenv"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+const prog = `
+package main
+
+import "log"
+
+func main() {
+ log.Fatalf("HERE")
+}
+`
+
+func TestIssue33808(t *testing.T) {
+ if runtime.GOOS != "darwin" {
+ return
+ }
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, "-ldflags=-linkmode=external")
+ f.Close()
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("Error reading symbols: %v", err)
+ }
+
+ name := "log.Fatalf"
+ for _, sym := range syms {
+ if strings.Contains(sym.Name, name) {
+ return
+ }
+ }
+ t.Fatalf("Didn't find %v", name)
+}
diff --git a/src/cmd/link/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go
new file mode 100644
index 0000000..d416571
--- /dev/null
+++ b/src/cmd/link/internal/ld/ld.go
@@ -0,0 +1,256 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "cmd/internal/goobj"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+)
+
+func (ctxt *Link) readImportCfg(file string) {
+ ctxt.PackageFile = make(map[string]string)
+ ctxt.PackageShlib = make(map[string]string)
+ data, err := os.ReadFile(file)
+ if err != nil {
+ log.Fatalf("-importcfg: %v", err)
+ }
+
+ for lineNum, line := range strings.Split(string(data), "\n") {
+ lineNum++ // 1-based
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+
+ var verb, args string
+ if i := strings.Index(line, " "); i < 0 {
+ verb = line
+ } else {
+ verb, args = line[:i], strings.TrimSpace(line[i+1:])
+ }
+ var before, after string
+ if i := strings.Index(args, "="); i >= 0 {
+ before, after = args[:i], args[i+1:]
+ }
+ switch verb {
+ default:
+ log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
+ case "packagefile":
+ if before == "" || after == "" {
+ log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
+ }
+ ctxt.PackageFile[before] = after
+ case "packageshlib":
+ if before == "" || after == "" {
+ log.Fatalf(`%s:%d: invalid packageshlib: syntax is "packageshlib path=filename"`, file, lineNum)
+ }
+ ctxt.PackageShlib[before] = after
+ case "modinfo":
+ s, err := strconv.Unquote(args)
+ if err != nil {
+ log.Fatalf("%s:%d: invalid modinfo: %v", file, lineNum, err)
+ }
+ addstrdata1(ctxt, "runtime.modinfo="+s)
+ }
+ }
+}
+
+func pkgname(ctxt *Link, lib string) string {
+ return path.Clean(lib)
+}
+
+func findlib(ctxt *Link, lib string) (string, bool) {
+ name := path.Clean(lib)
+
+ var pname string
+ isshlib := false
+
+ if ctxt.linkShared && ctxt.PackageShlib[name] != "" {
+ pname = ctxt.PackageShlib[name]
+ isshlib = true
+ } else if ctxt.PackageFile != nil {
+ pname = ctxt.PackageFile[name]
+ if pname == "" {
+ ctxt.Logf("cannot find package %s (using -importcfg)\n", name)
+ return "", false
+ }
+ } else {
+ pkg := pkgname(ctxt, lib)
+
+ // search -L "libdir" directories
+ for _, dir := range ctxt.Libdir {
+ if ctxt.linkShared {
+ pname = filepath.Join(dir, pkg+".shlibname")
+ if _, err := os.Stat(pname); err == nil {
+ isshlib = true
+ break
+ }
+ }
+ pname = filepath.Join(dir, name+".a")
+ if _, err := os.Stat(pname); err == nil {
+ break
+ }
+ pname = filepath.Join(dir, name+".o")
+ if _, err := os.Stat(pname); err == nil {
+ break
+ }
+ }
+ pname = filepath.Clean(pname)
+ }
+
+ return pname, isshlib
+}
+
+func addlib(ctxt *Link, src, obj, lib string, fingerprint goobj.FingerprintType) *sym.Library {
+ pkg := pkgname(ctxt, lib)
+
+ // already loaded?
+ if l := ctxt.LibraryByPkg[pkg]; l != nil && !l.Fingerprint.IsZero() {
+ // Normally, packages are loaded in dependency order, and if l != nil
+ // l is already loaded with the actual fingerprint. In shared build mode,
+ // however, packages may be added not in dependency order, and it is
+ // possible that l's fingerprint is not yet loaded -- exclude it in
+ // checking.
+ checkFingerprint(l, l.Fingerprint, src, fingerprint)
+ return l
+ }
+
+ pname, isshlib := findlib(ctxt, lib)
+
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("addlib: %s %s pulls in %s isshlib %v\n", obj, src, pname, isshlib)
+ }
+
+ if isshlib {
+ return addlibpath(ctxt, src, obj, "", pkg, pname, fingerprint)
+ }
+ return addlibpath(ctxt, src, obj, pname, pkg, "", fingerprint)
+}
+
+/*
+ * add library to library list, return added library.
+ * srcref: src file referring to package
+ * objref: object file referring to package
+ * file: object file, e.g., /home/rsc/go/pkg/container/vector.a
+ * pkg: package import path, e.g. container/vector
+ * shlib: path to shared library, or .shlibname file holding path
+ * fingerprint: if not 0, expected fingerprint for import from srcref
+ * fingerprint is 0 if the library is not imported (e.g. main)
+ */
+func addlibpath(ctxt *Link, srcref, objref, file, pkg, shlib string, fingerprint goobj.FingerprintType) *sym.Library {
+ if l := ctxt.LibraryByPkg[pkg]; l != nil {
+ return l
+ }
+
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("addlibpath: srcref: %s objref: %s file: %s pkg: %s shlib: %s fingerprint: %x\n", srcref, objref, file, pkg, shlib, fingerprint)
+ }
+
+ l := &sym.Library{}
+ ctxt.LibraryByPkg[pkg] = l
+ ctxt.Library = append(ctxt.Library, l)
+ l.Objref = objref
+ l.Srcref = srcref
+ l.File = file
+ l.Pkg = pkg
+ l.Fingerprint = fingerprint
+ if shlib != "" {
+ if strings.HasSuffix(shlib, ".shlibname") {
+ data, err := os.ReadFile(shlib)
+ if err != nil {
+ Errorf(nil, "cannot read %s: %v", shlib, err)
+ }
+ shlib = strings.TrimSpace(string(data))
+ }
+ l.Shlib = shlib
+ }
+ return l
+}
+
+func atolwhex(s string) int64 {
+ n, _ := strconv.ParseInt(s, 0, 64)
+ return n
+}
+
+// PrepareAddmoduledata returns a symbol builder that target-specific
+// code can use to build up the linker-generated go.link.addmoduledata
+// function, along with the sym for runtime.addmoduledata itself. If
+// this function is not needed (for example in cases where we're
+// linking a module that contains the runtime) the returned builder
+// will be nil.
+func PrepareAddmoduledata(ctxt *Link) (*loader.SymbolBuilder, loader.Sym) {
+ if !ctxt.DynlinkingGo() {
+ return nil, 0
+ }
+ amd := ctxt.loader.LookupOrCreateSym("runtime.addmoduledata", 0)
+ if ctxt.loader.SymType(amd) == sym.STEXT && ctxt.BuildMode != BuildModePlugin {
+ // we're linking a module containing the runtime -> no need for
+ // an init function
+ return nil, 0
+ }
+ ctxt.loader.SetAttrReachable(amd, true)
+
+ // Create a new init func text symbol. Caller will populate this
+ // sym with arch-specific content.
+ ifs := ctxt.loader.LookupOrCreateSym("go:link.addmoduledata", 0)
+ initfunc := ctxt.loader.MakeSymbolUpdater(ifs)
+ ctxt.loader.SetAttrReachable(ifs, true)
+ ctxt.loader.SetAttrLocal(ifs, true)
+ initfunc.SetType(sym.STEXT)
+
+ // Add the init func and/or addmoduledata to Textp.
+ if ctxt.BuildMode == BuildModePlugin {
+ ctxt.Textp = append(ctxt.Textp, amd)
+ }
+ ctxt.Textp = append(ctxt.Textp, initfunc.Sym())
+
+ // Create an init array entry
+ amdi := ctxt.loader.LookupOrCreateSym("go:link.addmoduledatainit", 0)
+ initarray_entry := ctxt.loader.MakeSymbolUpdater(amdi)
+ ctxt.loader.SetAttrReachable(amdi, true)
+ ctxt.loader.SetAttrLocal(amdi, true)
+ initarray_entry.SetType(sym.SINITARR)
+ initarray_entry.AddAddr(ctxt.Arch, ifs)
+
+ return initfunc, amd
+}
diff --git a/src/cmd/link/internal/ld/ld_test.go b/src/cmd/link/internal/ld/ld_test.go
new file mode 100644
index 0000000..4d32dd6
--- /dev/null
+++ b/src/cmd/link/internal/ld/ld_test.go
@@ -0,0 +1,343 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "bytes"
+ "debug/pe"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestUndefinedRelocErrors(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // When external linking, symbols may be defined externally, so we allow
+ // undefined symbols and let external linker resolve. Skip the test.
+ testenv.MustInternalLink(t)
+
+ t.Parallel()
+
+ out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "./testdata/issue10978").CombinedOutput()
+ if err == nil {
+ t.Fatal("expected build to fail")
+ }
+
+ wantErrors := map[string]int{
+ // Main function has dedicated error message.
+ "function main is undeclared in the main package": 1,
+
+ // Single error reporting per each symbol.
+ // This way, duplicated messages are not reported for
+ // multiple relocations with a same name.
+ "main.defined1: relocation target main.undefined not defined": 1,
+ "main.defined2: relocation target main.undefined not defined": 1,
+ }
+ unexpectedErrors := map[string]int{}
+
+ for _, l := range strings.Split(string(out), "\n") {
+ if strings.HasPrefix(l, "#") || l == "" {
+ continue
+ }
+ matched := ""
+ for want := range wantErrors {
+ if strings.Contains(l, want) {
+ matched = want
+ break
+ }
+ }
+ if matched != "" {
+ wantErrors[matched]--
+ } else {
+ unexpectedErrors[l]++
+ }
+ }
+
+ for want, n := range wantErrors {
+ switch {
+ case n > 0:
+ t.Errorf("unmatched error: %s (x%d)", want, n)
+ case n < 0:
+ t.Errorf("extra errors: %s (x%d)", want, -n)
+ }
+ }
+ for unexpected, n := range unexpectedErrors {
+ t.Errorf("unexpected error: %s (x%d)", unexpected, n)
+ }
+}
+
+const carchiveSrcText = `
+package main
+
+//export GoFunc
+func GoFunc() {
+ println(42)
+}
+
+func main() {
+}
+`
+
+func TestArchiveBuildInvokeWithExec(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ // run this test on just a small set of platforms (no need to test it
+ // across the board given the nature of the test).
+ pair := runtime.GOOS + "-" + runtime.GOARCH
+ switch pair {
+ case "darwin-amd64", "darwin-arm64", "linux-amd64", "freebsd-amd64":
+ default:
+ t.Skip("no need for test on " + pair)
+ }
+ switch runtime.GOOS {
+ case "openbsd", "windows":
+ t.Skip("c-archive unsupported")
+ }
+ dir := t.TempDir()
+
+ srcfile := filepath.Join(dir, "test.go")
+ arfile := filepath.Join(dir, "test.a")
+ if err := os.WriteFile(srcfile, []byte(carchiveSrcText), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ ldf := fmt.Sprintf("-ldflags=-v -tmpdir=%s", dir)
+ argv := []string{"build", "-buildmode=c-archive", "-o", arfile, ldf, srcfile}
+ out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failure: %s\n%s\n", err, string(out))
+ }
+
+ found := false
+ const want = "invoking archiver with syscall.Exec"
+ for _, l := range strings.Split(string(out), "\n") {
+ if strings.HasPrefix(l, want) {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("expected '%s' in -v output, got:\n%s\n", want, string(out))
+ }
+}
+
+func TestLargeTextSectionSplitting(t *testing.T) {
+ switch runtime.GOARCH {
+ case "ppc64", "ppc64le", "arm":
+ case "arm64":
+ if runtime.GOOS == "darwin" {
+ break
+ }
+ fallthrough
+ default:
+ t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
+
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+ dir := t.TempDir()
+
+ // NB: the use of -ldflags=-debugtextsize=1048576 tells the linker to
+ // split text sections at a size threshold of 1M instead of the
+ // architected limit of 67M or larger. The choice of building cmd/go
+ // is arbitrary; we just need something sufficiently large that uses
+ // external linking.
+ exe := filepath.Join(dir, "go.exe")
+ out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, "-ldflags=-linkmode=external -debugtextsize=1048576", "cmd/go").CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failure: %s\n%s\n", err, string(out))
+ }
+
+ // Check that we did split text sections.
+ out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
+ if err != nil {
+ t.Fatalf("nm failure: %s\n%s\n", err, string(out))
+ }
+ if !bytes.Contains(out, []byte("runtime.text.1")) {
+ t.Errorf("runtime.text.1 not found, text section not split?")
+ }
+
+ // Result should be runnable.
+ _, err = testenv.Command(t, exe, "version").CombinedOutput()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestWindowsBuildmodeCSharedASLR(t *testing.T) {
+ platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
+ switch platform {
+ case "windows/amd64", "windows/386":
+ default:
+ t.Skip("skipping windows amd64/386 only test")
+ }
+
+ testenv.MustHaveCGO(t)
+
+ t.Run("aslr", func(t *testing.T) {
+ testWindowsBuildmodeCSharedASLR(t, true)
+ })
+ t.Run("no-aslr", func(t *testing.T) {
+ testWindowsBuildmodeCSharedASLR(t, false)
+ })
+}
+
+func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+
+ dir := t.TempDir()
+
+ srcfile := filepath.Join(dir, "test.go")
+ objfile := filepath.Join(dir, "test.dll")
+ if err := os.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil {
+ t.Fatal(err)
+ }
+ argv := []string{"build", "-buildmode=c-shared"}
+ if !useASLR {
+ argv = append(argv, "-ldflags", "-aslr=false")
+ }
+ argv = append(argv, "-o", objfile, srcfile)
+ out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failure: %s\n%s\n", err, string(out))
+ }
+
+ f, err := pe.Open(objfile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ var dc uint16
+ switch oh := f.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ dc = oh.DllCharacteristics
+ case *pe.OptionalHeader64:
+ dc = oh.DllCharacteristics
+ hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0
+ if useASLR && !hasHEVA {
+ t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
+ } else if !useASLR && hasHEVA {
+ t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set")
+ }
+ default:
+ t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
+ }
+ hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0
+ if useASLR && !hasASLR {
+ t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
+ } else if !useASLR && hasASLR {
+ t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
+ }
+}
+
+// TestMemProfileCheck tests that cmd/link sets
+// runtime.disableMemoryProfiling if the runtime.MemProfile
+// symbol is unreachable after deadcode (and not dynlinking).
+// The runtime then uses that to set the default value of
+// runtime.MemProfileRate, which this test checks.
+func TestMemProfileCheck(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ prog string
+ wantOut string
+ }{
+ {
+ "no_memprofile",
+ `
+package main
+import "runtime"
+func main() {
+ println(runtime.MemProfileRate)
+}
+`,
+ "0",
+ },
+ {
+ "with_memprofile",
+ `
+package main
+import "runtime"
+func main() {
+ runtime.MemProfile(nil, false)
+ println(runtime.MemProfileRate)
+}
+`,
+ "524288",
+ },
+ {
+ "with_memprofile_indirect",
+ `
+package main
+import "runtime"
+var f = runtime.MemProfile
+func main() {
+ if f == nil {
+ panic("no f")
+ }
+ println(runtime.MemProfileRate)
+}
+`,
+ "524288",
+ },
+ {
+ "with_memprofile_runtime_pprof",
+ `
+package main
+import "runtime"
+import "runtime/pprof"
+func main() {
+ _ = pprof.Profiles()
+ println(runtime.MemProfileRate)
+}
+`,
+ "524288",
+ },
+ {
+ "with_memprofile_http_pprof",
+ `
+package main
+import "runtime"
+import _ "net/http/pprof"
+func main() {
+ println(runtime.MemProfileRate)
+}
+`,
+ "524288",
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ tempDir := t.TempDir()
+ src := filepath.Join(tempDir, "x.go")
+ if err := os.WriteFile(src, []byte(tt.prog), 0644); err != nil {
+ t.Fatal(err)
+ }
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatal(err)
+ }
+ got := strings.TrimSpace(string(out))
+ if got != tt.wantOut {
+ t.Errorf("got %q; want %q", got, tt.wantOut)
+ }
+ })
+ }
+}
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
new file mode 100644
index 0000000..6eae900
--- /dev/null
+++ b/src/cmd/link/internal/ld/lib.go
@@ -0,0 +1,2696 @@
+// Inferno utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "bytes"
+ "debug/elf"
+ "debug/macho"
+ "encoding/base64"
+ "encoding/binary"
+ "fmt"
+ "internal/buildcfg"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "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.
+
+// Derived from Inferno utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// ArchSyms holds a number of architecture specific symbols used during
+// relocation. Rather than allowing them universal access to all symbols,
+// we keep a subset for relocation application.
+type ArchSyms struct {
+ Rel loader.Sym
+ Rela loader.Sym
+ RelPLT loader.Sym
+ RelaPLT loader.Sym
+
+ LinkEditGOT loader.Sym
+ LinkEditPLT loader.Sym
+
+ TOC loader.Sym
+ DotTOC []loader.Sym // for each version
+
+ GOT loader.Sym
+ PLT loader.Sym
+ GOTPLT loader.Sym
+
+ Tlsg loader.Sym
+ Tlsoffset int
+
+ Dynamic loader.Sym
+ DynSym loader.Sym
+ DynStr loader.Sym
+
+ unreachableMethod loader.Sym
+}
+
+// mkArchSym is a helper for setArchSyms, to set up a special symbol.
+func (ctxt *Link) mkArchSym(name string, ver int, ls *loader.Sym) {
+ *ls = ctxt.loader.LookupOrCreateSym(name, ver)
+ ctxt.loader.SetAttrReachable(*ls, true)
+}
+
+// mkArchVecSym 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.loader.SetAttrReachable(ls[ver], true)
+}
+
+// setArchSyms sets up the ArchSyms structure, and must be called before
+// relocations are applied.
+func (ctxt *Link) setArchSyms() {
+ ctxt.mkArchSym(".got", 0, &ctxt.GOT)
+ ctxt.mkArchSym(".plt", 0, &ctxt.PLT)
+ ctxt.mkArchSym(".got.plt", 0, &ctxt.GOTPLT)
+ ctxt.mkArchSym(".dynamic", 0, &ctxt.Dynamic)
+ ctxt.mkArchSym(".dynsym", 0, &ctxt.DynSym)
+ ctxt.mkArchSym(".dynstr", 0, &ctxt.DynStr)
+ ctxt.mkArchSym("runtime.unreachableMethod", abiInternalVer, &ctxt.unreachableMethod)
+
+ if ctxt.IsPPC64() {
+ ctxt.mkArchSym("TOC", 0, &ctxt.TOC)
+
+ ctxt.DotTOC = make([]loader.Sym, ctxt.MaxVersion()+1)
+ for i := 0; i <= ctxt.MaxVersion(); i++ {
+ if i >= sym.SymVerABICount && i < sym.SymVerStatic { // these versions are not used currently
+ continue
+ }
+ ctxt.mkArchSymVec(".TOC.", i, ctxt.DotTOC)
+ }
+ }
+ if ctxt.IsElf() {
+ ctxt.mkArchSym(".rel", 0, &ctxt.Rel)
+ ctxt.mkArchSym(".rela", 0, &ctxt.Rela)
+ ctxt.mkArchSym(".rel.plt", 0, &ctxt.RelPLT)
+ ctxt.mkArchSym(".rela.plt", 0, &ctxt.RelaPLT)
+ }
+ if ctxt.IsDarwin() {
+ ctxt.mkArchSym(".linkedit.got", 0, &ctxt.LinkEditGOT)
+ ctxt.mkArchSym(".linkedit.plt", 0, &ctxt.LinkEditPLT)
+ }
+}
+
+type Arch struct {
+ Funcalign int
+ Maxalign int
+ Minalign int
+ Dwarfregsp int
+ Dwarfreglr int
+
+ // Threshold of total text size, used for trampoline insertion. If the total
+ // text size is smaller than TrampLimit, we won't need to insert trampolines.
+ // It is pretty close to the offset range of a direct CALL machine instruction.
+ // We leave some room for extra stuff like PLT stubs.
+ TrampLimit uint64
+
+ Androiddynld string
+ Linuxdynld string
+ LinuxdynldMusl 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
+ // are padded with zeros.
+ CodePad []byte
+
+ // Plan 9 variables.
+ Plan9Magic uint32
+ Plan9_64Bit bool
+
+ Adddynrel func(*Target, *loader.Loader, *ArchSyms, loader.Sym, loader.Reloc, int) bool
+ Archinit func(*Link)
+ // Archreloc is an arch-specific hook that assists in relocation processing
+ // (invoked by 'relocsym'); it handles target-specific relocation tasks.
+ // Here "rel" is the current relocation being examined, "sym" is the symbol
+ // containing the chunk of data to which the relocation applies, and "off"
+ // is the contents of the to-be-relocated data item (from sym.P). Return
+ // value is the appropriately relocated value (to be written back to the
+ // same spot in sym.P), number of external _host_ relocations needed (i.e.
+ // ELF/Mach-O/etc. relocations, not Go relocations, this must match Elfreloc1,
+ // etc.), and a boolean indicating success/failure (a failing value indicates
+ // a fatal error).
+ Archreloc func(*Target, *loader.Loader, *ArchSyms, loader.Reloc, loader.Sym,
+ int64) (relocatedOffset int64, nExtReloc int, ok bool)
+ // Archrelocvariant is a second arch-specific hook used for
+ // relocation processing; it handles relocations where r.Type is
+ // insufficient to describe the relocation (r.Variant !=
+ // sym.RV_NONE). Here "rel" is the relocation being applied, "sym"
+ // is the symbol containing the chunk of data to which the
+ // relocation applies, and "off" is the contents of the
+ // to-be-relocated data item (from sym.P). Return is an updated
+ // offset value.
+ Archrelocvariant func(target *Target, ldr *loader.Loader, rel loader.Reloc,
+ rv sym.RelocVariant, sym loader.Sym, offset int64, data []byte) (relocatedOffset int64)
+
+ // Generate a trampoline for a call from s to rs if necessary. ri is
+ // index of the relocation.
+ Trampoline func(ctxt *Link, ldr *loader.Loader, ri int, rs, s loader.Sym)
+
+ // Assembling the binary breaks into two phases, writing the code/data/
+ // dwarf information (which is rather generic), and some more architecture
+ // specific work like setting up the elf headers/dynamic relocations, etc.
+ // The phases are called "Asmb" and "Asmb2". Asmb2 needs to be defined for
+ // every architecture, but only if architecture has an Asmb function will
+ // it be used for assembly. Otherwise a generic assembly Asmb function is
+ // used.
+ Asmb func(*Link, *loader.Loader)
+ Asmb2 func(*Link, *loader.Loader)
+
+ // Extreloc is an arch-specific hook that converts a Go relocation to an
+ // external relocation. Return the external relocation and whether it is
+ // 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.
+ PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
+ Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
+
+ // Generate additional symbols for the native symbol table just prior to
+ // code generation.
+ GenSymsLate func(*Link, *loader.Loader)
+
+ // TLSIEtoLE converts a TLS Initial Executable relocation to
+ // a TLS Local Executable relocation.
+ //
+ // This is possible when a TLS IE relocation refers to a local
+ // symbol in an executable, which is typical when internally
+ // linking PIE binaries.
+ TLSIEtoLE func(P []byte, off, size int)
+
+ // 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)
+}
+
+var (
+ thearch Arch
+ lcSize int32
+ rpath Rpath
+ spSize int32
+ symSize int32
+)
+
+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 {
+ if !ctxt.Loaded {
+ panic("DynlinkingGo called before all symbols loaded")
+ }
+ return ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin || ctxt.canUsePlugins
+}
+
+// CanUsePlugins reports whether a plugins can be used
+func (ctxt *Link) CanUsePlugins() bool {
+ if !ctxt.Loaded {
+ panic("CanUsePlugins called before all symbols loaded")
+ }
+ return ctxt.canUsePlugins
+}
+
+// NeedCodeSign reports whether we need to code-sign the output binary.
+func (ctxt *Link) NeedCodeSign() bool {
+ return ctxt.IsDarwin() && ctxt.IsARM64()
+}
+
+var (
+ dynlib []string
+ ldflag []string
+ havedynamic int
+ Funcalign int
+ iscgo bool
+ elfglobalsymndx int
+ interpreter string
+
+ debug_s bool // backup old value of debug['s']
+ HEADR int32
+
+ nerrors int
+ liveness int64 // size of liveness data (funcdata), printed if -v
+
+ // See -strictdups command line flag.
+ checkStrictDups int // 0=off 1=warning 2=error
+ strictDupMsgCount int
+)
+
+var (
+ Segtext sym.Segment
+ Segrodata sym.Segment
+ Segrelrodata sym.Segment
+ Segdata sym.Segment
+ Segdwarf sym.Segment
+
+ Segments = []*sym.Segment{&Segtext, &Segrodata, &Segrelrodata, &Segdata, &Segdwarf}
+)
+
+const pkgdef = "__.PKGDEF"
+
+var (
+ // 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
+
+ // 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) {
+ ctxt.Libdir = append(ctxt.Libdir, arg)
+}
+
+/*
+ * Unix doesn't like it when we write to a running (or, sometimes,
+ * recently run) binary, so remove the output file before writing it.
+ * On Windows 7, remove() can force a subsequent create() to fail.
+ * S_ISREG() does not exist on Plan 9.
+ */
+func mayberemoveoutfile() {
+ if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() {
+ return
+ }
+ os.Remove(*flagOutfile)
+}
+
+func libinit(ctxt *Link) {
+ Funcalign = thearch.Funcalign
+
+ // add goroot to the end of the libdir list.
+ suffix := ""
+
+ suffixsep := ""
+ if *flagInstallSuffix != "" {
+ suffixsep = "_"
+ suffix = *flagInstallSuffix
+ } else if *flagRace {
+ suffixsep = "_"
+ suffix = "race"
+ } else if *flagMsan {
+ suffixsep = "_"
+ suffix = "msan"
+ } else if *flagAsan {
+ suffixsep = "_"
+ suffix = "asan"
+ }
+
+ if buildcfg.GOROOT != "" {
+ Lflag(ctxt, filepath.Join(buildcfg.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", buildcfg.GOOS, buildcfg.GOARCH, suffixsep, suffix)))
+ }
+
+ mayberemoveoutfile()
+
+ if err := ctxt.Out.Open(*flagOutfile); err != nil {
+ Exitf("cannot create %s: %v", *flagOutfile, err)
+ }
+
+ if *flagEntrySymbol == "" {
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModeCArchive:
+ *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", buildcfg.GOARCH, buildcfg.GOOS)
+ case BuildModeExe, BuildModePIE:
+ *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", buildcfg.GOARCH, buildcfg.GOOS)
+ case BuildModeShared, BuildModePlugin:
+ // No *flagEntrySymbol for -buildmode=shared and plugin
+ default:
+ Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode)
+ }
+ }
+}
+
+func exitIfErrors() {
+ if nerrors != 0 || checkStrictDups > 1 && strictDupMsgCount > 0 {
+ mayberemoveoutfile()
+ Exit(2)
+ }
+
+}
+
+func errorexit() {
+ exitIfErrors()
+ Exit(0)
+}
+
+func loadinternal(ctxt *Link, name string) *sym.Library {
+ zerofp := goobj.FingerprintType{}
+ if ctxt.linkShared && ctxt.PackageShlib != nil {
+ if shlib := ctxt.PackageShlib[name]; shlib != "" {
+ return addlibpath(ctxt, "internal", "internal", "", name, shlib, zerofp)
+ }
+ }
+ if ctxt.PackageFile != nil {
+ if pname := ctxt.PackageFile[name]; pname != "" {
+ return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp)
+ }
+ ctxt.Logf("loadinternal: cannot find %s\n", name)
+ return nil
+ }
+
+ for _, libdir := range ctxt.Libdir {
+ if ctxt.linkShared {
+ shlibname := filepath.Join(libdir, name+".shlibname")
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("searching for %s.a in %s\n", name, shlibname)
+ }
+ if _, err := os.Stat(shlibname); err == nil {
+ return addlibpath(ctxt, "internal", "internal", "", name, shlibname, zerofp)
+ }
+ }
+ pname := filepath.Join(libdir, name+".a")
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("searching for %s.a in %s\n", name, pname)
+ }
+ if _, err := os.Stat(pname); err == nil {
+ return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp)
+ }
+ }
+
+ 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 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
+}
+
+// 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()
+ 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(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)
+ }
+ return "none"
+ }
+ return strings.TrimSpace(string(out))
+}
+
+// findLibPath searches for library libname.
+// It returns library full path if found, or "none" if not found.
+func (ctxt *Link) findLibPath(libname string) string {
+ return ctxt.findLibPathCmd("--print-file-name="+libname, libname)
+}
+
+func (ctxt *Link) loadlib() {
+ var flags uint32
+ switch *FlagStrictDups {
+ case 0:
+ // nothing to do
+ case 1, 2:
+ flags |= loader.FlagStrictDups
+ default:
+ log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
+ }
+ elfsetstring1 := func(str string, off int) { elfsetstring(ctxt, 0, str, off) }
+ ctxt.loader = loader.NewLoader(flags, elfsetstring1, &ctxt.ErrorReporter.ErrorReporter)
+ ctxt.ErrorReporter.SymName = func(s loader.Sym) string {
+ return ctxt.loader.SymName(s)
+ }
+
+ // ctxt.Library grows during the loop, so not a range loop.
+ i := 0
+ for ; i < len(ctxt.Library); i++ {
+ lib := ctxt.Library[i]
+ if lib.Shlib == "" {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref)
+ }
+ loadobjfile(ctxt, lib)
+ }
+ }
+
+ // load internal packages, if not already
+ if *flagRace {
+ loadinternal(ctxt, "runtime/race")
+ }
+ 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 lib.Shlib == "" {
+ loadobjfile(ctxt, lib)
+ }
+ }
+ // At this point, the Go objects are "preloaded". Not all the symbols are
+ // added to the symbol table (only defined package symbols are). Looking
+ // up symbol by name may not get expected result.
+
+ iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil
+
+ // Plugins a require cgo support to function. Similarly, plugins may require additional
+ // internal linker support on some platforms which may not be implemented.
+ ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != nil && iscgo
+
+ // We now have enough information to determine the link mode.
+ determineLinkMode(ctxt)
+
+ if ctxt.LinkMode == LinkExternal && !iscgo && !(buildcfg.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && ctxt.Arch.Family == sys.AMD64) {
+ // This indicates a user requested -linkmode=external.
+ // The startup code uses an import of runtime/cgo to decide
+ // whether to initialize the TLS. So give it one. This could
+ // be handled differently but it's an unusual case.
+ if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" {
+ if ctxt.BuildMode == BuildModeShared || ctxt.linkShared {
+ Exitf("cannot implicitly include runtime/cgo in a shared library")
+ }
+ for ; i < len(ctxt.Library); i++ {
+ lib := ctxt.Library[i]
+ if lib.Shlib == "" {
+ loadobjfile(ctxt, lib)
+ }
+ }
+ }
+ }
+
+ // Add non-package symbols and references of externally defined symbols.
+ ctxt.loader.LoadSyms(ctxt.Arch)
+
+ // Load symbols from shared libraries, after all Go object symbols are loaded.
+ for _, lib := range ctxt.Library {
+ if lib.Shlib != "" {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref)
+ }
+ ldshlibsyms(ctxt, lib.Shlib)
+ }
+ }
+
+ // Process cgo directives (has to be done before host object loading).
+ ctxt.loadcgodirectives()
+
+ // Conditionally load host objects, or setup for external linking.
+ hostobjs(ctxt)
+ hostlinksetup(ctxt)
+
+ if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
+ // If we have any undefined symbols in external
+ // objects, try to read them from the libgcc file.
+ any := false
+ 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-libgcc-file-name", "libgcc")
+ }
+ if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" {
+ // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a".
+ // In this case we fail to load libgcc.a and can encounter link
+ // errors - see if we can find libcompiler_rt.a instead.
+ *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
+ }
+ if ctxt.HeadType == objabi.Hwindows {
+ loadWindowsHostArchives(ctxt)
+ }
+ 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() {
+ l := ctxt.loader
+ hostObjSyms := make(map[loader.Sym]struct{})
+ for _, d := range ctxt.cgodata {
+ setCgoAttr(ctxt, d.file, d.pkg, d.directives, hostObjSyms)
+ }
+ ctxt.cgodata = nil
+
+ if ctxt.LinkMode == LinkInternal {
+ // Drop all the cgo_import_static declarations.
+ // Turns out we won't be needing them.
+ for symIdx := range hostObjSyms {
+ if l.SymType(symIdx) == sym.SHOSTOBJ {
+ // If a symbol was marked both
+ // cgo_import_static and cgo_import_dynamic,
+ // then we want to make it cgo_import_dynamic
+ // now.
+ su := l.MakeSymbolUpdater(symIdx)
+ if l.SymExtname(symIdx) != "" && l.SymDynimplib(symIdx) != "" && !(l.AttrCgoExportStatic(symIdx) || l.AttrCgoExportDynamic(symIdx)) {
+ su.SetType(sym.SDYNIMPORT)
+ } else {
+ su.SetType(0)
+ }
+ }
+ }
+ }
+}
+
+// Set up flags and special symbols depending on the platform build mode.
+// This version works with loader.Loader.
+func (ctxt *Link) linksetup() {
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModePlugin:
+ symIdx := ctxt.loader.LookupOrCreateSym("runtime.islibrary", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(symIdx)
+ sb.SetType(sym.SNOPTRDATA)
+ sb.AddUint8(1)
+ case BuildModeCArchive:
+ symIdx := ctxt.loader.LookupOrCreateSym("runtime.isarchive", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(symIdx)
+ sb.SetType(sym.SNOPTRDATA)
+ sb.AddUint8(1)
+ }
+
+ // Recalculate pe parameters now that we have ctxt.LinkMode set.
+ if ctxt.HeadType == objabi.Hwindows {
+ Peinit(ctxt)
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ // When external linking, we are creating an object file. The
+ // absolute address is irrelevant.
+ *FlagTextAddr = 0
+ }
+
+ // If there are no dynamic libraries needed, gcc disables dynamic linking.
+ // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
+ // assumes that a dynamic binary always refers to at least one dynamic library.
+ // Rather than be a source of test cases for glibc, disable dynamic linking
+ // the same way that gcc would.
+ //
+ // Exception: on OS X, programs such as Shark only work with dynamic
+ // binaries, so leave it enabled on OS X (Mach-O) binaries.
+ // Also leave it enabled on Solaris which doesn't support
+ // statically linked binaries.
+ if ctxt.BuildMode == BuildModeExe {
+ if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris {
+ *FlagD = true
+ }
+ }
+
+ if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && buildcfg.GOOS != "aix" {
+ toc := ctxt.loader.LookupOrCreateSym(".TOC.", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(toc)
+ sb.SetType(sym.SDYNIMPORT)
+ }
+
+ // The Android Q linker started to complain about underalignment of the our TLS
+ // section. We don't actually use the section on android, so don't
+ // generate it.
+ if buildcfg.GOOS != "android" {
+ tlsg := ctxt.loader.LookupOrCreateSym("runtime.tlsg", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(tlsg)
+
+ // runtime.tlsg is used for external linking on platforms that do not define
+ // a variable to hold g in assembly (currently only intel).
+ if sb.Type() == 0 {
+ sb.SetType(sym.STLSBSS)
+ sb.SetSize(int64(ctxt.Arch.PtrSize))
+ } else if sb.Type() != sym.SDYNIMPORT {
+ Errorf(nil, "runtime declared tlsg variable %v", sb.Type())
+ }
+ ctxt.loader.SetAttrReachable(tlsg, true)
+ ctxt.Tlsg = tlsg
+ }
+
+ var moduledata loader.Sym
+ var mdsb *loader.SymbolBuilder
+ if ctxt.BuildMode == BuildModePlugin {
+ moduledata = ctxt.loader.LookupOrCreateSym("local.pluginmoduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+ ctxt.loader.SetAttrLocal(moduledata, true)
+ } else {
+ moduledata = ctxt.loader.LookupOrCreateSym("runtime.firstmoduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+ }
+ if mdsb.Type() != 0 && mdsb.Type() != sym.SDYNIMPORT {
+ // If the module (toolchain-speak for "executable or shared
+ // library") we are linking contains the runtime package, it
+ // will define the runtime.firstmoduledata symbol and we
+ // truncate it back to 0 bytes so we can define its entire
+ // contents in symtab.go:symtab().
+ mdsb.SetSize(0)
+
+ // In addition, on ARM, the runtime depends on the linker
+ // recording the value of GOARM.
+ if ctxt.Arch.Family == sys.ARM {
+ goarm := ctxt.loader.LookupOrCreateSym("runtime.goarm", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(goarm)
+ sb.SetType(sym.SDATA)
+ sb.SetSize(0)
+ sb.AddUint8(uint8(buildcfg.GOARM))
+ }
+
+ // 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", abiInternalVer)
+ if memProfile != 0 && !ctxt.loader.AttrReachable(memProfile) && !ctxt.DynlinkingGo() {
+ memProfSym := ctxt.loader.LookupOrCreateSym("runtime.disableMemoryProfiling", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(memProfSym)
+ sb.SetType(sym.SDATA)
+ sb.SetSize(0)
+ sb.AddUint8(1) // true bool
+ }
+ } else {
+ // If OTOH the module does not contain the runtime package,
+ // create a local symbol for the moduledata.
+ moduledata = ctxt.loader.LookupOrCreateSym("local.moduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+ ctxt.loader.SetAttrLocal(moduledata, true)
+ }
+ // In all cases way we mark the moduledata as noptrdata to hide it from
+ // the GC.
+ mdsb.SetType(sym.SNOPTRDATA)
+ ctxt.loader.SetAttrReachable(moduledata, true)
+ ctxt.Moduledata = moduledata
+
+ if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows {
+ if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() {
+ got := ctxt.loader.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(got)
+ sb.SetType(sym.SDYNIMPORT)
+ ctxt.loader.SetAttrReachable(got, true)
+ }
+ }
+
+ // DWARF-gen and other phases require that the unit Textp slices
+ // be populated, so that it can walk the functions in each unit.
+ // Call into the loader to do this (requires that we collect the
+ // set of internal libraries first). NB: might be simpler if we
+ // moved isRuntimeDepPkg to cmd/internal and then did the test in
+ // loader.AssignTextSymbolOrder.
+ ctxt.Library = postorder(ctxt.Library)
+ intlibs := []bool{}
+ for _, lib := range ctxt.Library {
+ intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg))
+ }
+ ctxt.Textp = ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs, ctxt.Textp)
+}
+
+// mangleTypeSym shortens the names of symbols that represent Go types
+// if they are visible in the symbol table.
+//
+// As the names of these symbols are derived from the string of
+// the type, they can run to many kilobytes long. So we shorten
+// them using a SHA-1 when the name appears in the final binary.
+// This also removes characters that upset external linkers.
+//
+// These are the symbols that begin with the prefix 'type.' and
+// contain run-time type information used by the runtime and reflect
+// packages. All Go binaries contain these symbols, but only
+// those programs loaded dynamically in multiple parts need these
+// symbols to have entries in the symbol table.
+func (ctxt *Link) mangleTypeSym() {
+ if ctxt.BuildMode != BuildModeShared && !ctxt.linkShared && ctxt.BuildMode != BuildModePlugin && !ctxt.CanUsePlugins() {
+ return
+ }
+
+ ldr := ctxt.loader
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) && !ctxt.linkShared {
+ // If -linkshared, the GCProg generation code may need to reach
+ // out to the shared library for the type descriptor's data, even
+ // the type descriptor itself is not actually needed at run time
+ // (therefore not reachable). We still need to mangle its name,
+ // so it is consistent with the one stored in the shared library.
+ continue
+ }
+ name := ldr.SymName(s)
+ newName := typeSymbolMangle(name)
+ if newName != name {
+ ldr.SetSymExtname(s, newName)
+
+ // When linking against a shared library, the Go object file may
+ // have reference to the original symbol name whereas the shared
+ // library provides a symbol with the mangled name. We need to
+ // copy the payload of mangled to original.
+ // XXX maybe there is a better way to do this.
+ dup := ldr.Lookup(newName, ldr.SymVersion(s))
+ if dup != 0 {
+ st := ldr.SymType(s)
+ dt := ldr.SymType(dup)
+ if st == sym.Sxxx && dt != sym.Sxxx {
+ ldr.CopySym(dup, s)
+ }
+ }
+ }
+ }
+}
+
+// typeSymbolMangle mangles the given symbol name into something shorter.
+//
+// Keep the type:. prefix, which parts of the linker (like the
+// DWARF generator) know means the symbol is not decodable.
+// Leave type:runtime. symbols alone, because other parts of
+// the linker manipulates them.
+func typeSymbolMangle(name string) string {
+ if !strings.HasPrefix(name, "type:") {
+ return name
+ }
+ if strings.HasPrefix(name, "type:runtime.") {
+ return name
+ }
+ if len(name) <= 14 && !strings.Contains(name, "@") { // Issue 19529
+ return name
+ }
+ hash := notsha256.Sum256([]byte(name))
+ prefix := "type:"
+ if name[5] == '.' {
+ prefix = "type:."
+ }
+ return prefix + base64.StdEncoding.EncodeToString(hash[:6])
+}
+
+/*
+ * look for the next file in an archive.
+ * adapted from libmach.
+ */
+func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 {
+ if off&1 != 0 {
+ off++
+ }
+ bp.MustSeek(off, 0)
+ var buf [SAR_HDR]byte
+ if n, err := io.ReadFull(bp, buf[:]); err != nil {
+ if n == 0 && err != io.EOF {
+ return -1
+ }
+ return 0
+ }
+
+ a.name = artrim(buf[0:16])
+ a.date = artrim(buf[16:28])
+ a.uid = artrim(buf[28:34])
+ a.gid = artrim(buf[34:40])
+ a.mode = artrim(buf[40:48])
+ a.size = artrim(buf[48:58])
+ a.fmag = artrim(buf[58:60])
+
+ arsize := atolwhex(a.size)
+ if arsize&1 != 0 {
+ arsize++
+ }
+ return arsize + SAR_HDR
+}
+
+func loadobjfile(ctxt *Link, lib *sym.Library) {
+ pkg := objabi.PathToPrefix(lib.Pkg)
+
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("ldobj: %s (%s)\n", lib.File, pkg)
+ }
+ f, err := bio.Open(lib.File)
+ if err != nil {
+ Exitf("cannot open file %s: %v", lib.File, err)
+ }
+ defer f.Close()
+ defer func() {
+ if pkg == "main" && !lib.Main {
+ Exitf("%s: not package main", lib.File)
+ }
+ }()
+
+ for i := 0; i < len(ARMAG); i++ {
+ if c, err := f.ReadByte(); err == nil && c == ARMAG[i] {
+ continue
+ }
+
+ /* load it as a regular file */
+ l := f.MustSeek(0, 2)
+ f.MustSeek(0, 0)
+ ldobj(ctxt, f, lib, l, lib.File, lib.File)
+ return
+ }
+
+ /*
+ * load all the object files from the archive now.
+ * this gives us sequential file access and keeps us
+ * from needing to come back later to pick up more
+ * objects. it breaks the usual C archive model, but
+ * this is Go, not C. the common case in Go is that
+ * we need to load all the objects, and then we throw away
+ * the individual symbols that are unused.
+ *
+ * loading every object will also make it possible to
+ * load foreign objects not referenced by __.PKGDEF.
+ */
+ var arhdr ArHdr
+ off := f.Offset()
+ for {
+ l := nextar(f, off, &arhdr)
+ if l == 0 {
+ break
+ }
+ if l < 0 {
+ Exitf("%s: malformed archive", lib.File)
+ }
+ off += l
+
+ // __.PKGDEF isn't a real Go object file, and it's
+ // absent in -linkobj builds anyway. Skipping it
+ // ensures consistency between -linkobj and normal
+ // build modes.
+ if arhdr.name == pkgdef {
+ continue
+ }
+
+ 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.
+ if len(arhdr.name) < 16 {
+ if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" {
+ continue
+ }
+ }
+
+ pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name)
+ l = atolwhex(arhdr.size)
+ ldobj(ctxt, f, lib, l, pname, lib.File)
+ }
+}
+
+type Hostobj struct {
+ ld func(*Link, *bio.Reader, string, int64, string)
+ pkg string
+ pn string
+ file string
+ off int64
+ length int64
+}
+
+var hostobj []Hostobj
+
+// These packages can use internal linking mode.
+// Others trigger external mode.
+var internalpkg = []string{
+ "crypto/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 {
+ isinternal := false
+ for _, intpkg := range internalpkg {
+ if pkg == intpkg {
+ isinternal = true
+ break
+ }
+ }
+
+ // DragonFly declares errno with __thread, which results in a symbol
+ // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not
+ // currently know how to handle TLS relocations, hence we have to
+ // force external linking for any libraries that link in code that
+ // uses errno. This can be removed if the Go linker ever supports
+ // these relocation types.
+ if headType == objabi.Hdragonfly {
+ if pkg == "net" || pkg == "os/user" {
+ isinternal = false
+ }
+ }
+
+ if !isinternal {
+ externalobj = true
+ }
+
+ hostobj = append(hostobj, Hostobj{})
+ h := &hostobj[len(hostobj)-1]
+ h.ld = ld
+ h.pkg = pkg
+ h.pn = pn
+ h.file = file
+ h.off = f.Offset()
+ h.length = length
+ return h
+}
+
+func hostobjs(ctxt *Link) {
+ if ctxt.LinkMode != LinkInternal {
+ return
+ }
+ var h *Hostobj
+
+ for i := 0; i < len(hostobj); i++ {
+ h = &hostobj[i]
+ f, err := bio.Open(h.file)
+ if err != nil {
+ Exitf("cannot reopen %s: %v", h.pn, err)
+ }
+ f.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()
+ }
+}
+
+func hostlinksetup(ctxt *Link) {
+ if ctxt.LinkMode != LinkExternal {
+ return
+ }
+
+ // For external link, record that we need to tell the external linker -s,
+ // and turn off -s internally: the external linker needs the symbol
+ // information for its final link.
+ debug_s = *FlagS
+ *FlagS = false
+
+ // create temporary directory and arrange cleanup
+ if *flagTmpdir == "" {
+ dir, err := os.MkdirTemp("", "go-link-")
+ if err != nil {
+ log.Fatal(err)
+ }
+ *flagTmpdir = dir
+ ownTmpDir = true
+ AtExit(func() {
+ os.RemoveAll(*flagTmpdir)
+ })
+ }
+
+ // change our output to temporary object file
+ if err := ctxt.Out.Close(); err != nil {
+ Exitf("error closing output file")
+ }
+ mayberemoveoutfile()
+
+ p := filepath.Join(*flagTmpdir, "go.o")
+ if err := ctxt.Out.Open(p); err != nil {
+ Exitf("cannot create %s: %v", p, err)
+ }
+}
+
+// hostobjCopy creates a copy of the object files in hostobj in a
+// temporary directory.
+func hostobjCopy() (paths []string) {
+ var wg sync.WaitGroup
+ sema := make(chan struct{}, runtime.NumCPU()) // limit open file descriptors
+ for i, h := range hostobj {
+ h := h
+ dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i))
+ paths = append(paths, dst)
+
+ wg.Add(1)
+ go func() {
+ sema <- struct{}{}
+ defer func() {
+ <-sema
+ wg.Done()
+ }()
+ f, err := os.Open(h.file)
+ if err != nil {
+ Exitf("cannot reopen %s: %v", h.pn, err)
+ }
+ defer f.Close()
+ if _, err := f.Seek(h.off, 0); err != nil {
+ Exitf("cannot seek %s: %v", h.pn, err)
+ }
+
+ w, err := os.Create(dst)
+ if err != nil {
+ Exitf("cannot create %s: %v", dst, err)
+ }
+ if _, err := io.CopyN(w, f, h.length); err != nil {
+ Exitf("cannot write %s: %v", dst, err)
+ }
+ if err := w.Close(); err != nil {
+ Exitf("cannot close %s: %v", dst, err)
+ }
+ }()
+ }
+ wg.Wait()
+ return paths
+}
+
+// writeGDBLinkerScript creates gcc linker script file in temp
+// directory. writeGDBLinkerScript returns created file path.
+// The script is used to work around gcc bug
+// (see https://golang.org/issue/20183 for details).
+func writeGDBLinkerScript() string {
+ name := "fix_debug_gdb_scripts.ld"
+ path := filepath.Join(*flagTmpdir, name)
+ src := `SECTIONS
+{
+ .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) :
+ {
+ *(.debug_gdb_scripts)
+ }
+}
+INSERT AFTER .debug_types;
+`
+ err := os.WriteFile(path, []byte(src), 0666)
+ if err != nil {
+ Errorf(nil, "WriteFile %s failed: %v", name, err)
+ }
+ return path
+}
+
+// archive builds a .a archive from the hostobj object files.
+func (ctxt *Link) archive() {
+ if ctxt.BuildMode != BuildModeCArchive {
+ return
+ }
+
+ exitIfErrors()
+
+ if *flagExtar == "" {
+ *flagExtar = "ar"
+ }
+
+ mayberemoveoutfile()
+
+ // Force the buffer to flush here so that external
+ // tools will see a complete file.
+ if err := ctxt.Out.Close(); err != nil {
+ Exitf("error closing %v", *flagOutfile)
+ }
+
+ argv := []string{*flagExtar, "-q", "-c", "-s"}
+ if ctxt.HeadType == objabi.Haix {
+ argv = append(argv, "-X64")
+ }
+ argv = append(argv, *flagOutfile)
+ argv = append(argv, filepath.Join(*flagTmpdir, "go.o"))
+ argv = append(argv, hostobjCopy()...)
+
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("archive: %s\n", strings.Join(argv, " "))
+ }
+
+ // If supported, use syscall.Exec() to invoke the archive command,
+ // which should be the final remaining step needed for the link.
+ // This will reduce peak RSS for the link (and speed up linking of
+ // large applications), since when the archive command runs we
+ // won't be holding onto all of the linker's live memory.
+ if syscallExecSupported && !ownTmpDir {
+ runAtExitFuncs()
+ ctxt.execArchive(argv)
+ panic("should not get here")
+ }
+
+ // Otherwise invoke 'ar' in the usual way (fork + exec).
+ if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
+ Exitf("running %s failed: %v\n%s", argv[0], err, out)
+ }
+}
+
+func (ctxt *Link) hostlink() {
+ if ctxt.LinkMode != LinkExternal || nerrors > 0 {
+ return
+ }
+ if ctxt.BuildMode == BuildModeCArchive {
+ return
+ }
+
+ var argv []string
+ 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.
+ } 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
+
+ switch ctxt.HeadType {
+ case objabi.Hdarwin:
+ if linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,-ld_classic") {
+ // Force old linker to work around bugs in Apple's new linker.
+ argv = append(argv, "-Wl,-ld_classic")
+ }
+ if combineDwarf {
+ // Leave room for DWARF combining.
+ // -headerpad is incompatible with -fembed-bitcode.
+ argv = append(argv, "-Wl,-headerpad,1144")
+ }
+ 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.
+ // -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
+ }
+ case objabi.Hopenbsd:
+ argv = append(argv, "-Wl,-nopie")
+ argv = append(argv, "-pthread")
+ case objabi.Hwindows:
+ if windowsgui {
+ argv = append(argv, "-mwindows")
+ } else {
+ argv = append(argv, "-mconsole")
+ }
+ // Mark as having awareness of terminal services, to avoid
+ // ancient compatibility hacks.
+ argv = append(argv, "-Wl,--tsaware")
+
+ // Enable DEP
+ argv = append(argv, "-Wl,--nxcompat")
+
+ argv = append(argv, fmt.Sprintf("-Wl,--major-os-version=%d", PeMinimumTargetMajorVersion))
+ argv = append(argv, fmt.Sprintf("-Wl,--minor-os-version=%d", PeMinimumTargetMinorVersion))
+ argv = append(argv, fmt.Sprintf("-Wl,--major-subsystem-version=%d", PeMinimumTargetMajorVersion))
+ argv = append(argv, fmt.Sprintf("-Wl,--minor-subsystem-version=%d", PeMinimumTargetMinorVersion))
+ case objabi.Haix:
+ argv = append(argv, "-pthread")
+ // prevent ld to reorder .text functions to keep the same
+ // first/last functions for moduledata.
+ argv = append(argv, "-Wl,-bnoobjreorder")
+ // mcmodel=large is needed for every gcc generated files, but
+ // ld still need -bbigtoc in order to allow larger TOC.
+ argv = append(argv, "-mcmodel=large")
+ argv = append(argv, "-Wl,-bbigtoc")
+ }
+
+ // 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 && heopt != "" {
+ argv = append(argv, "-Wl,"+heopt)
+ }
+ return argv
+ }
+
+ switch ctxt.BuildMode {
+ case BuildModeExe:
+ if ctxt.HeadType == objabi.Hdarwin {
+ if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() {
+ argv = append(argv, "-Wl,-no_pie")
+ }
+ }
+ 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:
+ 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, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-pie")
+ }
+ case BuildModeCShared:
+ if ctxt.HeadType == objabi.Hdarwin {
+ argv = append(argv, "-dynamiclib")
+ } else {
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ if ctxt.HeadType == objabi.Hwindows {
+ argv = addASLRargs(argv, *flagAslr)
+ } else {
+ // Pass -z nodelete to mark the shared library as
+ // non-closeable: a dlclose will do nothing.
+ argv = append(argv, "-Wl,-z,nodelete")
+ // Only pass Bsymbolic on non-Windows.
+ argv = append(argv, "-Wl,-Bsymbolic")
+ }
+ }
+ case BuildModeShared:
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ case BuildModePlugin:
+ if ctxt.HeadType == objabi.Hdarwin {
+ argv = append(argv, "-dynamiclib")
+ } else {
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ }
+ }
+
+ var altLinker string
+ if ctxt.IsELF && ctxt.DynlinkingGo() {
+ // We force all symbol resolution to be done at program startup
+ // because lazy PLT resolution can use large amounts of stack at
+ // times we cannot allow it to do so.
+ argv = append(argv, "-Wl,-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,-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.
+ // 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.
+ 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.
+ 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)
+ }
+ }
+ }
+ }
+ if ctxt.Arch.Family == sys.ARM64 && buildcfg.GOOS == "freebsd" {
+ // Switch to ld.bfd on freebsd/arm64.
+ altLinker = "bfd"
+
+ // Provide a useful error if ld.bfd is missing.
+ 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")
+ }
+ }
+ }
+ if altLinker != "" {
+ argv = append(argv, "-fuse-ld="+altLinker)
+ }
+
+ if ctxt.IsELF && len(buildinfo) > 0 {
+ argv = append(argv, fmt.Sprintf("-Wl,--build-id=0x%x", buildinfo))
+ }
+
+ // On Windows, given -o foo, GCC will append ".exe" to produce
+ // "foo.exe". We have decided that we want to honor the -o
+ // option. To make this work, we append a '.' so that GCC
+ // will decide that the file already has an extension. We
+ // only want to do this when producing a Windows output file
+ // on a Windows host.
+ outopt := *flagOutfile
+ if buildcfg.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" {
+ outopt += "."
+ }
+ argv = append(argv, "-o")
+ argv = append(argv, outopt)
+
+ if rpath.val != "" {
+ argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val))
+ }
+
+ if *flagInterpreter != "" {
+ // Many linkers support both -I and the --dynamic-linker flags
+ // to set the ELF interpreter, but lld only supports
+ // --dynamic-linker so prefer that (ld on very old Solaris only
+ // supports -I but that seems less important).
+ argv = append(argv, fmt.Sprintf("-Wl,--dynamic-linker,%s", *flagInterpreter))
+ }
+
+ // Force global symbols to be exported for dlopen, etc.
+ if ctxt.IsELF {
+ argv = append(argv, "-rdynamic")
+ }
+ if ctxt.HeadType == objabi.Haix {
+ fileName := xcoffCreateExportFile(ctxt)
+ argv = append(argv, "-Wl,-bE:"+fileName)
+ }
+
+ const unusedArguments = "-Qunused-arguments"
+ if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, unusedArguments) {
+ argv = append(argv, unusedArguments)
+ }
+
+ 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()...)
+ if ctxt.HeadType == objabi.Haix {
+ // We want to have C files after Go files to remove
+ // trampolines csects made by ld.
+ argv = append(argv, "-nostartfiles")
+ 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 := 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")
+ }
+ // 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 {
+ seenDirs := make(map[string]bool)
+ seenLibs := make(map[string]bool)
+ addshlib := func(path string) {
+ dir, base := filepath.Split(path)
+ if !seenDirs[dir] {
+ argv = append(argv, "-L"+dir)
+ if !rpath.set {
+ argv = append(argv, "-Wl,-rpath="+dir)
+ }
+ seenDirs[dir] = true
+ }
+ base = strings.TrimSuffix(base, ".so")
+ base = strings.TrimPrefix(base, "lib")
+ if !seenLibs[base] {
+ argv = append(argv, "-l"+base)
+ seenLibs[base] = true
+ }
+ }
+ for _, shlib := range ctxt.Shlibs {
+ addshlib(shlib.Path)
+ for _, dep := range shlib.Deps {
+ if dep == "" {
+ continue
+ }
+ libpath := findshlib(ctxt, dep)
+ if libpath != "" {
+ addshlib(libpath)
+ }
+ }
+ }
+ }
+
+ // clang, unlike GCC, passes -rdynamic to the linker
+ // even when linking with -static, causing a linker
+ // error when using GNU ld. So take out -rdynamic if
+ // we added it. We do it in this order, rather than
+ // only adding -rdynamic later, so that -extldflags
+ // can override -rdynamic without using -static.
+ // Similarly for -Wl,--dynamic-linker.
+ checkStatic := func(arg string) {
+ if ctxt.IsELF && arg == "-static" {
+ for i := range argv {
+ if argv[i] == "-rdynamic" || strings.HasPrefix(argv[i], "-Wl,--dynamic-linker,") {
+ argv[i] = "-static"
+ }
+ }
+ }
+ }
+
+ for _, p := range ldflag {
+ argv = append(argv, p)
+ checkStatic(p)
+ }
+
+ // When building a program with the default -buildmode=exe the
+ // gc compiler generates code requires DT_TEXTREL in a
+ // position independent executable (PIE). On systems where the
+ // toolchain creates PIEs by default, and where DT_TEXTREL
+ // does not work, the resulting programs will not run. See
+ // issue #17847. To avoid this problem pass -no-pie to the
+ // toolchain if it is supported.
+ if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared && !(ctxt.IsDarwin() && ctxt.IsARM64()) {
+ // GCC uses -no-pie, clang uses -nopie.
+ for _, nopie := range []string{"-no-pie", "-nopie"} {
+ if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, nopie) {
+ argv = append(argv, nopie)
+ break
+ }
+ }
+ }
+
+ 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=...".
+ extld := ctxt.extld()
+ name, args := extld[0], extld[1:]
+ args = append(args, 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 ")) {
+ usingLLD = true
+ }
+ }
+
+ // use gcc linker script to work around gcc bug
+ // (see https://golang.org/issue/20183 for details).
+ if !usingLLD {
+ 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()...)
+ }
+
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("host link:")
+ for _, v := range argv {
+ ctxt.Logf(" %q", v)
+ }
+ ctxt.Logf("\n")
+ }
+
+ out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput()
+ if err != nil {
+ Exitf("running %s failed: %v\n%s", argv[0], err, out)
+ }
+
+ // Filter out useless linker warnings caused by bugs outside Go.
+ // See also cmd/go/internal/work/exec.go's gccld method.
+ var save [][]byte
+ var skipLines int
+ for _, line := range bytes.SplitAfter(out, []byte("\n")) {
+ // golang.org/issue/26073 - Apple Xcode bug
+ if bytes.Contains(line, []byte("ld: warning: text-based stub file")) {
+ continue
+ }
+
+ if skipLines > 0 {
+ skipLines--
+ continue
+ }
+
+ // Remove TOC overflow warning on AIX.
+ if bytes.Contains(line, []byte("ld: 0711-783")) {
+ skipLines = 2
+ continue
+ }
+
+ save = append(save, line)
+ }
+ out = bytes.Join(save, nil)
+
+ if len(out) > 0 {
+ // always print external output even if the command is successful, so that we don't
+ // swallow linker warnings (see https://golang.org/issue/17935).
+ 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(dsymutilCmd, "-f", *flagOutfile, "-o", dsym).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(stripCmd, "-S", *flagOutfile).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.
+ if _, err := os.Stat(dsym); os.IsNotExist(err) {
+ return
+ }
+ // For os.Rename to work reliably, must be in same directory as outfile.
+ combinedOutput := *flagOutfile + "~"
+ exef, err := os.Open(*flagOutfile)
+ if err != nil {
+ Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
+ }
+ defer exef.Close()
+ exem, err := macho.NewFile(exef)
+ if err != nil {
+ Exitf("%s: parsing Mach-O header failed: %v", os.Args[0], err)
+ }
+ if err := machoCombineDwarf(ctxt, exef, exem, dsym, combinedOutput); err != nil {
+ Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
+ }
+ os.Remove(*flagOutfile)
+ if err := os.Rename(combinedOutput, *flagOutfile); err != nil {
+ Exitf("%s: %v", os.Args[0], err)
+ }
+ }
+ if ctxt.NeedCodeSign() {
+ err := machoCodeSign(ctxt, *flagOutfile)
+ if err != nil {
+ Exitf("%s: code signing failed: %v", os.Args[0], err)
+ }
+ }
+}
+
+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 := os.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil {
+ Errorf(nil, "WriteFile trivial.c failed: %v", err)
+ }
+ })
+
+ flagsWithNextArgSkip := []string{
+ "-F",
+ "-l",
+ "-L",
+ "-framework",
+ "-Wl,-framework",
+ "-Wl,-rpath",
+ "-Wl,-undefined",
+ }
+ flagsWithNextArgKeep := []string{
+ "-arch",
+ "-isysroot",
+ "--sysroot",
+ "-target",
+ }
+ prefixesToKeep := []string{
+ "-f",
+ "-m",
+ "-p",
+ "-Wl,",
+ "-arch",
+ "-isysroot",
+ "--sysroot",
+ "-target",
+ }
+
+ flags := hostlinkArchArgs(arch)
+ keep := false
+ skip := false
+ for _, f := range append(flagExtldflags, ldflag...) {
+ if keep {
+ flags = append(flags, f)
+ keep = false
+ } else if skip {
+ skip = false
+ } else if f == "" || f[0] != '-' {
+ } else if contains(flagsWithNextArgSkip, f) {
+ skip = true
+ } else if contains(flagsWithNextArgKeep, f) {
+ flags = append(flags, f)
+ keep = true
+ } else {
+ for _, p := range prefixesToKeep {
+ if strings.HasPrefix(f, p) {
+ flags = append(flags, f)
+ break
+ }
+ }
+ }
+ }
+
+ 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"))
+}
+
+// hostlinkArchArgs returns arguments to pass to the external linker
+// based on the architecture.
+func hostlinkArchArgs(arch *sys.Arch) []string {
+ switch arch.Family {
+ case sys.I386:
+ return []string{"-m32"}
+ case sys.AMD64:
+ if buildcfg.GOOS == "darwin" {
+ return []string{"-arch", "x86_64", "-m64"}
+ }
+ return []string{"-m64"}
+ case sys.S390X:
+ return []string{"-m64"}
+ case sys.ARM:
+ return []string{"-marm"}
+ case sys.ARM64:
+ 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:
+ return []string{"-mabi=32"}
+ case sys.PPC64:
+ if buildcfg.GOOS == "aix" {
+ return []string{"-maix64"}
+ } else {
+ return []string{"-m64"}
+ }
+
+ }
+ return nil
+}
+
+var wantHdr = objabi.HeaderString()
+
+// ldobj loads an input object. If it is a host object (an object
+// compiled by a non-Go compiler) it returns the Hostobj pointer. If
+// it is a Go object, it returns nil.
+func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj {
+ pkg := objabi.PathToPrefix(lib.Pkg)
+
+ eof := f.Offset() + length
+ start := f.Offset()
+ c1 := bgetc(f)
+ c2 := bgetc(f)
+ c3 := bgetc(f)
+ c4 := bgetc(f)
+ f.MustSeek(start, 0)
+
+ unit := &sym.CompilationUnit{Lib: lib}
+ lib.Units = append(lib.Units, unit)
+
+ magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
+ if magic == 0x7f454c46 { // \x7F E L F
+ ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn, ehdr.Flags)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ehdr.Flags = flags
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe {
+ ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ switch c1<<8 | c2 {
+ case 0x4c01, // 386
+ 0x6486, // amd64
+ 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)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ if len(rsrc) != 0 {
+ setpersrc(ctxt, rsrc)
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) {
+ ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ 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 {
+ Errorf(nil, "truncated object file: %s: %v", pn, err)
+ return nil
+ }
+
+ if !strings.HasPrefix(line, "go object ") {
+ if strings.HasSuffix(pn, ".go") {
+ Exitf("%s: uncompiled .go source file", pn)
+ return nil
+ }
+
+ if line == ctxt.Arch.Name {
+ // old header format: just $GOOS
+ Errorf(nil, "%s: stale object file", pn)
+ return nil
+ }
+
+ Errorf(nil, "%s: not an object file: @%d %q", pn, start, line)
+ return nil
+ }
+
+ // First, check that the basic GOOS, GOARCH, and Version match.
+ if line != wantHdr {
+ Errorf(nil, "%s: linked object header mismatch:\nhave %q\nwant %q\n", pn, line, wantHdr)
+ }
+
+ // Skip over exports and other info -- ends with \n!\n.
+ //
+ // Note: It's possible for "\n!\n" to appear within the binary
+ // package export data format. To avoid truncating the package
+ // definition prematurely (issue 21703), we keep track of
+ // how many "$$" delimiters we've seen.
+
+ import0 := f.Offset()
+
+ c1 = '\n' // the last line ended in \n
+ c2 = bgetc(f)
+ c3 = bgetc(f)
+ markers := 0
+ for {
+ if c1 == '\n' {
+ if markers%2 == 0 && c2 == '!' && c3 == '\n' {
+ break
+ }
+ if c2 == '$' && c3 == '$' {
+ markers++
+ }
+ }
+
+ c1 = c2
+ c2 = c3
+ c3 = bgetc(f)
+ if c3 == -1 {
+ Errorf(nil, "truncated object file: %s", pn)
+ return nil
+ }
+ }
+
+ import1 := f.Offset()
+
+ f.MustSeek(import0, 0)
+ ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n
+ f.MustSeek(import1, 0)
+
+ fingerprint := ctxt.loader.Preload(ctxt.IncVersion(), f, lib, unit, eof-f.Offset())
+ if !fingerprint.IsZero() { // Assembly objects don't have fingerprints. Ignore them.
+ // Check fingerprint, to ensure the importing and imported packages
+ // have consistent view of symbol indices.
+ // Normally the go command should ensure this. But in case something
+ // goes wrong, it could lead to obscure bugs like run-time crash.
+ // Check it here to be sure.
+ if lib.Fingerprint.IsZero() { // Not yet imported. Update its fingerprint.
+ lib.Fingerprint = fingerprint
+ }
+ checkFingerprint(lib, fingerprint, lib.Srcref, lib.Fingerprint)
+ }
+
+ addImports(ctxt, lib, pn)
+ 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)
+ }
+}
+
+func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte {
+ data := make([]byte, sym.Size)
+ sect := f.Sections[sym.Section]
+ if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE {
+ Errorf(nil, "reading %s from non-data section", sym.Name)
+ }
+ n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr))
+ if uint64(n) != sym.Size {
+ Errorf(nil, "reading contents of %s: %v", sym.Name, err)
+ }
+ return data
+}
+
+func readwithpad(r io.Reader, sz int32) ([]byte, error) {
+ data := make([]byte, Rnd(int64(sz), 4))
+ _, err := io.ReadFull(r, data)
+ if err != nil {
+ return nil, err
+ }
+ data = data[:sz]
+ return data, nil
+}
+
+func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) {
+ for _, sect := range f.Sections {
+ if sect.Type != elf.SHT_NOTE {
+ continue
+ }
+ r := sect.Open()
+ for {
+ var namesize, descsize, noteType int32
+ err := binary.Read(r, f.ByteOrder, &namesize)
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, fmt.Errorf("read namesize failed: %v", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read descsize failed: %v", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &noteType)
+ if err != nil {
+ return nil, fmt.Errorf("read type failed: %v", err)
+ }
+ noteName, err := readwithpad(r, namesize)
+ if err != nil {
+ return nil, fmt.Errorf("read name failed: %v", err)
+ }
+ desc, err := readwithpad(r, descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read desc failed: %v", err)
+ }
+ if string(name) == string(noteName) && typ == noteType {
+ return desc, nil
+ }
+ }
+ }
+ return nil, nil
+}
+
+func findshlib(ctxt *Link, shlib string) string {
+ if filepath.IsAbs(shlib) {
+ return shlib
+ }
+ for _, libdir := range ctxt.Libdir {
+ libpath := filepath.Join(libdir, shlib)
+ if _, err := os.Stat(libpath); err == nil {
+ return libpath
+ }
+ }
+ Errorf(nil, "cannot find shared library: %s", shlib)
+ return ""
+}
+
+func ldshlibsyms(ctxt *Link, shlib string) {
+ var libpath string
+ if filepath.IsAbs(shlib) {
+ libpath = shlib
+ shlib = filepath.Base(shlib)
+ } else {
+ libpath = findshlib(ctxt, shlib)
+ if libpath == "" {
+ return
+ }
+ }
+ for _, processedlib := range ctxt.Shlibs {
+ if processedlib.Path == libpath {
+ return
+ }
+ }
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("ldshlibsyms: found library with name %s at %s\n", shlib, libpath)
+ }
+
+ f, err := elf.Open(libpath)
+ if err != nil {
+ Errorf(nil, "cannot open shared library: %s", libpath)
+ return
+ }
+ // Keep the file open as decodetypeGcprog needs to read from it.
+ // TODO: fix. Maybe mmap the file.
+ //defer f.Close()
+
+ hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG)
+ if err != nil {
+ Errorf(nil, "cannot read ABI hash from shared library %s: %v", libpath, err)
+ return
+ }
+
+ depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG)
+ if err != nil {
+ Errorf(nil, "cannot read dep list from shared library %s: %v", libpath, err)
+ return
+ }
+ var deps []string
+ for _, dep := range strings.Split(string(depsbytes), "\n") {
+ if dep == "" {
+ continue
+ }
+ if !filepath.IsAbs(dep) {
+ // If the dep can be interpreted as a path relative to the shlib
+ // in which it was found, do that. Otherwise, we will leave it
+ // to be resolved by libdir lookup.
+ abs := filepath.Join(filepath.Dir(libpath), dep)
+ if _, err := os.Stat(abs); err == nil {
+ dep = abs
+ }
+ }
+ deps = append(deps, dep)
+ }
+
+ syms, err := f.DynamicSymbols()
+ if err != nil {
+ Errorf(nil, "cannot read symbols from shared library: %s", libpath)
+ return
+ }
+
+ for _, elfsym := range syms {
+ if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION {
+ continue
+ }
+
+ // 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 = 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") {
+ ver = sym.SymVerABIInternal
+ symname = strings.TrimSuffix(elfsym.Name, ".abiinternal")
+ } else if strings.HasSuffix(elfsym.Name, ".abi0") {
+ ver = 0
+ symname = strings.TrimSuffix(elfsym.Name, ".abi0")
+ }
+ }
+
+ l := ctxt.loader
+ s := l.LookupOrCreateSym(symname, ver)
+
+ // Because loadlib above loads all .a files before loading
+ // any shared libraries, any non-dynimport symbols we find
+ // that duplicate symbols already loaded should be ignored
+ // (the symbols from the .a files "win").
+ if l.SymType(s) != 0 && l.SymType(s) != sym.SDYNIMPORT {
+ continue
+ }
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SDYNIMPORT)
+ l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info))
+ su.SetSize(int64(elfsym.Size))
+ if elfsym.Section != elf.SHN_UNDEF {
+ // Set .File for the library that actually defines the symbol.
+ l.SetSymPkg(s, libpath)
+
+ // 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:.") {
+ su.SetData(readelfsymboldata(ctxt, f, &elfsym))
+ }
+ }
+
+ if symname != elfsym.Name {
+ l.SetSymExtname(s, elfsym.Name)
+ }
+ }
+ ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
+}
+
+func addsection(ldr *loader.Loader, arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section {
+ sect := ldr.NewSection()
+ sect.Rwx = uint8(rwx)
+ sect.Name = name
+ sect.Seg = seg
+ sect.Align = int32(arch.PtrSize) // everything is at least pointer-aligned
+ seg.Sections = append(seg.Sections, sect)
+ return sect
+}
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n")
+ objabi.Flagprint(os.Stderr)
+ Exit(2)
+}
+
+type SymbolType int8 // TODO: after genasmsym is gone, maybe rename to plan9typeChar or something
+
+const (
+ // see also https://9p.io/magic/man2html/1/nm
+ TextSym SymbolType = 'T'
+ DataSym SymbolType = 'D'
+ BSSSym SymbolType = 'B'
+ UndefinedSym SymbolType = 'U'
+ TLSSym SymbolType = 't'
+ FrameSym SymbolType = 'm'
+ ParamSym SymbolType = 'p'
+ AutoSym SymbolType = 'a'
+
+ // Deleted auto (not a real sym, just placeholder for type)
+ DeletedAutoSym = 'x'
+)
+
+// defineInternal defines a symbol used internally by the go runtime.
+func (ctxt *Link) defineInternal(p string, t sym.SymKind) loader.Sym {
+ s := ctxt.loader.CreateSymForUpdate(p, 0)
+ s.SetType(t)
+ s.SetSpecial(true)
+ s.SetLocal(true)
+ return s.Sym()
+}
+
+func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) loader.Sym {
+ s := ctxt.defineInternal(p, t)
+ ctxt.loader.SetSymValue(s, v)
+ return s
+}
+
+func datoff(ldr *loader.Loader, s loader.Sym, addr int64) int64 {
+ if uint64(addr) >= Segdata.Vaddr {
+ return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff)
+ }
+ if uint64(addr) >= Segtext.Vaddr {
+ return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff)
+ }
+ ldr.Errorf(s, "invalid datoff %#x", addr)
+ return 0
+}
+
+func Entryvalue(ctxt *Link) int64 {
+ a := *flagEntrySymbol
+ if a[0] >= '0' && a[0] <= '9' {
+ return atolwhex(a)
+ }
+ 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 !ctxt.IsAIX() && st != sym.STEXT {
+ ldr.Errorf(s, "entry not text")
+ }
+ return ldr.SymValue(s)
+}
+
+func (ctxt *Link) callgraph() {
+ if !*FlagC {
+ return
+ }
+
+ ldr := ctxt.loader
+ for _, s := range ctxt.Textp {
+ relocs := ldr.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ rs := r.Sym()
+ if rs == 0 {
+ continue
+ }
+ if r.Type().IsDirectCall() && ldr.SymType(rs) == sym.STEXT {
+ ctxt.Logf("%s calls %s\n", ldr.SymName(s), ldr.SymName(rs))
+ }
+ }
+ }
+}
+
+func Rnd(v int64, r int64) int64 {
+ if r <= 0 {
+ return v
+ }
+ v += r - 1
+ c := v % r
+ if c < 0 {
+ c += r
+ }
+ v -= c
+ return v
+}
+
+func bgetc(r *bio.Reader) int {
+ c, err := r.ReadByte()
+ if err != nil {
+ if err != io.EOF {
+ log.Fatalf("reading input: %v", err)
+ }
+ return -1
+ }
+ return int(c)
+}
+
+type markKind uint8 // for postorder traversal
+const (
+ _ markKind = iota
+ visiting
+ visited
+)
+
+func postorder(libs []*sym.Library) []*sym.Library {
+ order := make([]*sym.Library, 0, len(libs)) // hold the result
+ mark := make(map[*sym.Library]markKind, len(libs))
+ for _, lib := range libs {
+ dfs(lib, mark, &order)
+ }
+ return order
+}
+
+func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library) {
+ if mark[lib] == visited {
+ return
+ }
+ if mark[lib] == visiting {
+ panic("found import cycle while visiting " + lib.Pkg)
+ }
+ mark[lib] = visiting
+ for _, i := range lib.Imports {
+ dfs(i, mark, order)
+ }
+ mark[lib] = visited
+ *order = append(*order, lib)
+}
+
+func ElfSymForReloc(ctxt *Link, s loader.Sym) int32 {
+ // If putelfsym created a local version of this symbol, use that in all
+ // relocations.
+ les := ctxt.loader.SymLocalElfSym(s)
+ if les != 0 {
+ return les
+ } else {
+ return ctxt.loader.SymElfSym(s)
+ }
+}
+
+func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, elfRelocTyp uint32) {
+ if ldr.SymGot(s) >= 0 {
+ return
+ }
+
+ Adddynsym(ldr, target, syms, s)
+ got := ldr.MakeSymbolUpdater(syms.GOT)
+ ldr.SetGot(s, int32(got.Size()))
+ got.AddUint(target.Arch, 0)
+
+ if target.IsElf() {
+ if target.Arch.PtrSize == 8 {
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp))
+ rela.AddUint64(target.Arch, 0)
+ } else {
+ rel := ldr.MakeSymbolUpdater(syms.Rel)
+ rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), elfRelocTyp))
+ }
+ } else if target.IsDarwin() {
+ leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT)
+ leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s)))
+ if target.IsPIE() && target.IsInternal() {
+ // Mach-O relocations are a royal pain to lay out.
+ // They use a compact stateful bytecode representation.
+ // Here we record what are needed and encode them later.
+ MachoAddBind(int64(ldr.SymGot(s)), s)
+ }
+ } else {
+ 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)
+}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
new file mode 100644
index 0000000..34221df
--- /dev/null
+++ b/src/cmd/link/internal/ld/link.go
@@ -0,0 +1,161 @@
+// Derived from Inferno utils/6l/l.h and related files.
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "bufio"
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+)
+
+type Shlib struct {
+ Path string
+ Hash []byte
+ Deps []string
+ File *elf.File
+}
+
+// Link holds the context for writing object code from a compiler
+// or for reading that input into the linker.
+type Link struct {
+ Target
+ ErrorReporter
+ ArchSyms
+
+ outSem chan int // limits the number of output writers
+ Out *OutBuf
+
+ version int // current version number for static/file-local symbols
+
+ Debugvlog int
+ Bso *bufio.Writer
+
+ Loaded bool // set after all inputs have been loaded as symbols
+
+ compressDWARF bool
+
+ Libdir []string
+ Library []*sym.Library
+ LibraryByPkg map[string]*sym.Library
+ Shlibs []Shlib
+ Textp []loader.Sym
+ Moduledata loader.Sym
+
+ PackageFile map[string]string
+ PackageShlib map[string]string
+
+ tramps []loader.Sym // trampolines
+
+ compUnits []*sym.CompilationUnit // DWARF compilation units
+ runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen.
+
+ loader *loader.Loader
+ cgodata []cgodata // cgo directives to load, three strings are args for loadcgo
+
+ datap []loader.Sym
+ dynexp []loader.Sym
+
+ // Elf symtab variables.
+ numelfsym int // starts at 0, 1 is reserved
+
+ // These are symbols that created and written by the linker.
+ // Rather than creating a symbol, and writing all its data into the heap,
+ // you can create a symbol, and just a generation function will be called
+ // after the symbol's been created in the output mmap.
+ generatorSyms map[loader.Sym]generatorFunc
+}
+
+type cgodata struct {
+ file string
+ pkg string
+ directives [][]string
+}
+
+func (ctxt *Link) Logf(format string, args ...interface{}) {
+ fmt.Fprintf(ctxt.Bso, format, args...)
+ ctxt.Bso.Flush()
+}
+
+func addImports(ctxt *Link, l *sym.Library, pn string) {
+ pkg := objabi.PathToPrefix(l.Pkg)
+ for _, imp := range l.Autolib {
+ lib := addlib(ctxt, pkg, pn, imp.Pkg, imp.Fingerprint)
+ if lib != nil {
+ l.Imports = append(l.Imports, lib)
+ }
+ }
+ l.Autolib = nil
+}
+
+// Allocate a new version (i.e. symbol namespace).
+func (ctxt *Link) IncVersion() int {
+ ctxt.version++
+ return ctxt.version - 1
+}
+
+// returns the maximum version number
+func (ctxt *Link) MaxVersion() int {
+ return ctxt.version
+}
+
+// generatorFunc is a convenience type.
+// Some linker-created Symbols are large and shouldn't really live in the heap.
+// Such Symbols can define a generator function. Their bytes can be generated
+// directly in the output mmap.
+//
+// Relocations are applied prior to emitting generator Symbol contents.
+// Generator Symbols that require relocations can be written in two passes.
+// The first pass, at Symbol creation time, adds only relocations.
+// The second pass, at content generation time, adds the rest.
+// See generateFunctab for an example.
+//
+// Generator functions shouldn't grow the Symbol size.
+// Generator functions must be safe for concurrent use.
+//
+// Generator Symbols have their Data set to the mmapped area when the
+// generator is called.
+type generatorFunc func(*Link, loader.Sym)
+
+// createGeneratorSymbol is a convenience method for creating a generator
+// symbol.
+func (ctxt *Link) createGeneratorSymbol(name string, version int, t sym.SymKind, size int64, gen generatorFunc) loader.Sym {
+ ldr := ctxt.loader
+ s := ldr.LookupOrCreateSym(name, version)
+ ldr.SetIsGeneratedSym(s, true)
+ sb := ldr.MakeSymbolUpdater(s)
+ sb.SetType(t)
+ sb.SetSize(size)
+ ctxt.generatorSyms[s] = gen
+ return s
+}
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
new file mode 100644
index 0000000..d6c28e4
--- /dev/null
+++ b/src/cmd/link/internal/ld/macho.go
@@ -0,0 +1,1568 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "bytes"
+ "cmd/internal/codesign"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/macho"
+ "encoding/binary"
+ "fmt"
+ "internal/buildcfg"
+ "io"
+ "os"
+ "sort"
+ "strings"
+ "unsafe"
+)
+
+type MachoHdr struct {
+ cpu uint32
+ subcpu uint32
+}
+
+type MachoSect struct {
+ name string
+ segname string
+ addr uint64
+ size uint64
+ off uint32
+ align uint32
+ reloc uint32
+ nreloc uint32
+ flag uint32
+ res1 uint32
+ res2 uint32
+}
+
+type MachoSeg struct {
+ name string
+ vsize uint64
+ vaddr uint64
+ fileoffset uint64
+ filesize uint64
+ prot1 uint32
+ prot2 uint32
+ nsect uint32
+ msect uint32
+ sect []MachoSect
+ flag uint32
+}
+
+// MachoPlatformLoad represents a LC_VERSION_MIN_* or
+// LC_BUILD_VERSION load command.
+type MachoPlatformLoad struct {
+ platform MachoPlatform // One of PLATFORM_* constants.
+ cmd MachoLoad
+}
+
+type MachoLoad struct {
+ type_ uint32
+ data []uint32
+}
+
+type MachoPlatform int
+
+/*
+ * Total amount of space to reserve at the start of the file
+ * for Header, PHeaders, and SHeaders.
+ * May waste some.
+ */
+const (
+ INITIAL_MACHO_HEADR = 4 * 1024
+)
+
+const (
+ MACHO_CPU_AMD64 = 1<<24 | 7
+ MACHO_CPU_386 = 7
+ MACHO_SUBCPU_X86 = 3
+ MACHO_CPU_ARM = 12
+ MACHO_SUBCPU_ARM = 0
+ MACHO_SUBCPU_ARMV7 = 9
+ MACHO_CPU_ARM64 = 1<<24 | 12
+ MACHO_SUBCPU_ARM64_ALL = 0
+ MACHO_SUBCPU_ARM64_V8 = 1
+ MACHO_SUBCPU_ARM64E = 2
+ MACHO32SYMSIZE = 12
+ MACHO64SYMSIZE = 16
+ MACHO_X86_64_RELOC_UNSIGNED = 0
+ MACHO_X86_64_RELOC_SIGNED = 1
+ MACHO_X86_64_RELOC_BRANCH = 2
+ MACHO_X86_64_RELOC_GOT_LOAD = 3
+ MACHO_X86_64_RELOC_GOT = 4
+ MACHO_X86_64_RELOC_SUBTRACTOR = 5
+ MACHO_X86_64_RELOC_SIGNED_1 = 6
+ MACHO_X86_64_RELOC_SIGNED_2 = 7
+ MACHO_X86_64_RELOC_SIGNED_4 = 8
+ MACHO_ARM_RELOC_VANILLA = 0
+ MACHO_ARM_RELOC_PAIR = 1
+ MACHO_ARM_RELOC_SECTDIFF = 2
+ MACHO_ARM_RELOC_BR24 = 5
+ MACHO_ARM64_RELOC_UNSIGNED = 0
+ MACHO_ARM64_RELOC_BRANCH26 = 2
+ MACHO_ARM64_RELOC_PAGE21 = 3
+ MACHO_ARM64_RELOC_PAGEOFF12 = 4
+ MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5
+ MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6
+ MACHO_ARM64_RELOC_ADDEND = 10
+ MACHO_GENERIC_RELOC_VANILLA = 0
+ MACHO_FAKE_GOTPCREL = 100
+)
+
+const (
+ MH_MAGIC = 0xfeedface
+ MH_MAGIC_64 = 0xfeedfacf
+
+ MH_OBJECT = 0x1
+ MH_EXECUTE = 0x2
+
+ MH_NOUNDEFS = 0x1
+ MH_DYLDLINK = 0x4
+ MH_PIE = 0x200000
+)
+
+const (
+ LC_SEGMENT = 0x1
+ LC_SYMTAB = 0x2
+ LC_SYMSEG = 0x3
+ LC_THREAD = 0x4
+ LC_UNIXTHREAD = 0x5
+ LC_LOADFVMLIB = 0x6
+ LC_IDFVMLIB = 0x7
+ LC_IDENT = 0x8
+ LC_FVMFILE = 0x9
+ LC_PREPAGE = 0xa
+ LC_DYSYMTAB = 0xb
+ LC_LOAD_DYLIB = 0xc
+ LC_ID_DYLIB = 0xd
+ LC_LOAD_DYLINKER = 0xe
+ LC_ID_DYLINKER = 0xf
+ LC_PREBOUND_DYLIB = 0x10
+ LC_ROUTINES = 0x11
+ LC_SUB_FRAMEWORK = 0x12
+ LC_SUB_UMBRELLA = 0x13
+ LC_SUB_CLIENT = 0x14
+ LC_SUB_LIBRARY = 0x15
+ LC_TWOLEVEL_HINTS = 0x16
+ LC_PREBIND_CKSUM = 0x17
+ LC_LOAD_WEAK_DYLIB = 0x80000018
+ LC_SEGMENT_64 = 0x19
+ LC_ROUTINES_64 = 0x1a
+ LC_UUID = 0x1b
+ LC_RPATH = 0x8000001c
+ LC_CODE_SIGNATURE = 0x1d
+ LC_SEGMENT_SPLIT_INFO = 0x1e
+ LC_REEXPORT_DYLIB = 0x8000001f
+ LC_LAZY_LOAD_DYLIB = 0x20
+ LC_ENCRYPTION_INFO = 0x21
+ LC_DYLD_INFO = 0x22
+ LC_DYLD_INFO_ONLY = 0x80000022
+ LC_LOAD_UPWARD_DYLIB = 0x80000023
+ LC_VERSION_MIN_MACOSX = 0x24
+ LC_VERSION_MIN_IPHONEOS = 0x25
+ LC_FUNCTION_STARTS = 0x26
+ LC_DYLD_ENVIRONMENT = 0x27
+ LC_MAIN = 0x80000028
+ LC_DATA_IN_CODE = 0x29
+ LC_SOURCE_VERSION = 0x2A
+ LC_DYLIB_CODE_SIGN_DRS = 0x2B
+ LC_ENCRYPTION_INFO_64 = 0x2C
+ LC_LINKER_OPTION = 0x2D
+ LC_LINKER_OPTIMIZATION_HINT = 0x2E
+ LC_VERSION_MIN_TVOS = 0x2F
+ LC_VERSION_MIN_WATCHOS = 0x30
+ LC_VERSION_NOTE = 0x31
+ LC_BUILD_VERSION = 0x32
+ LC_DYLD_EXPORTS_TRIE = 0x80000033
+ LC_DYLD_CHAINED_FIXUPS = 0x80000034
+)
+
+const (
+ S_REGULAR = 0x0
+ S_ZEROFILL = 0x1
+ S_NON_LAZY_SYMBOL_POINTERS = 0x6
+ S_SYMBOL_STUBS = 0x8
+ S_MOD_INIT_FUNC_POINTERS = 0x9
+ S_ATTR_PURE_INSTRUCTIONS = 0x80000000
+ S_ATTR_DEBUG = 0x02000000
+ S_ATTR_SOME_INSTRUCTIONS = 0x00000400
+)
+
+const (
+ PLATFORM_MACOS MachoPlatform = 1
+ PLATFORM_IOS MachoPlatform = 2
+ PLATFORM_TVOS MachoPlatform = 3
+ PLATFORM_WATCHOS MachoPlatform = 4
+ PLATFORM_BRIDGEOS MachoPlatform = 5
+)
+
+// rebase table opcode
+const (
+ REBASE_TYPE_POINTER = 1
+ REBASE_TYPE_TEXT_ABSOLUTE32 = 2
+ REBASE_TYPE_TEXT_PCREL32 = 3
+
+ REBASE_OPCODE_MASK = 0xF0
+ REBASE_IMMEDIATE_MASK = 0x0F
+ REBASE_OPCODE_DONE = 0x00
+ REBASE_OPCODE_SET_TYPE_IMM = 0x10
+ REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20
+ REBASE_OPCODE_ADD_ADDR_ULEB = 0x30
+ REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40
+ REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50
+ REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60
+ REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70
+ REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80
+)
+
+// bind table opcode
+const (
+ BIND_TYPE_POINTER = 1
+ BIND_TYPE_TEXT_ABSOLUTE32 = 2
+ BIND_TYPE_TEXT_PCREL32 = 3
+
+ BIND_SPECIAL_DYLIB_SELF = 0
+ BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1
+ BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2
+ BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3
+
+ BIND_OPCODE_MASK = 0xF0
+ BIND_IMMEDIATE_MASK = 0x0F
+ BIND_OPCODE_DONE = 0x00
+ BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10
+ BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20
+ BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30
+ BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40
+ BIND_OPCODE_SET_TYPE_IMM = 0x50
+ BIND_OPCODE_SET_ADDEND_SLEB = 0x60
+ BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70
+ BIND_OPCODE_ADD_ADDR_ULEB = 0x80
+ BIND_OPCODE_DO_BIND = 0x90
+ BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0
+ BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0
+ BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0
+ BIND_OPCODE_THREADED = 0xD0
+ BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00
+ BIND_SUBOPCODE_THREADED_APPLY = 0x01
+)
+
+const machoHeaderSize64 = 8 * 4 // size of 64-bit Mach-O header
+
+// Mach-O file writing
+// https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
+
+var machohdr MachoHdr
+
+var load []MachoLoad
+
+var machoPlatform MachoPlatform
+
+var seg [16]MachoSeg
+
+var nseg int
+
+var ndebug int
+
+var nsect int
+
+const (
+ SymKindLocal = 0 + iota
+ SymKindExtdef
+ SymKindUndef
+ NumSymKind
+)
+
+var nkind [NumSymKind]int
+
+var sortsym []loader.Sym
+
+var nsortsym int
+
+// Amount of space left for adding load commands
+// that refer to dynamic libraries. Because these have
+// to go in the Mach-O header, we can't just pick a
+// "big enough" header size. The initial header is
+// one page, the non-dynamic library stuff takes
+// up about 1300 bytes; we overestimate that as 2k.
+var loadBudget = INITIAL_MACHO_HEADR - 2*1024
+
+func getMachoHdr() *MachoHdr {
+ return &machohdr
+}
+
+func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad {
+ if arch.PtrSize == 8 && (ndata&1 != 0) {
+ ndata++
+ }
+
+ load = append(load, MachoLoad{})
+ l := &load[len(load)-1]
+ l.type_ = type_
+ l.data = make([]uint32, ndata)
+ return l
+}
+
+func newMachoSeg(name string, msect int) *MachoSeg {
+ if nseg >= len(seg) {
+ Exitf("too many segs")
+ }
+
+ s := &seg[nseg]
+ nseg++
+ s.name = name
+ s.msect = uint32(msect)
+ s.sect = make([]MachoSect, msect)
+ return s
+}
+
+func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect {
+ if seg.nsect >= seg.msect {
+ Exitf("too many sects in segment %s", seg.name)
+ }
+
+ s := &seg.sect[seg.nsect]
+ seg.nsect++
+ s.name = name
+ s.segname = segname
+ nsect++
+ return s
+}
+
+// Generic linking code.
+
+var dylib []string
+
+var linkoff int64
+
+func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int {
+ o1 := out.Offset()
+
+ loadsize := 4 * 4 * ndebug
+ for i := range load {
+ loadsize += 4 * (len(load[i].data) + 2)
+ }
+ if arch.PtrSize == 8 {
+ loadsize += 18 * 4 * nseg
+ loadsize += 20 * 4 * nsect
+ } else {
+ loadsize += 14 * 4 * nseg
+ loadsize += 17 * 4 * nsect
+ }
+
+ if arch.PtrSize == 8 {
+ out.Write32(MH_MAGIC_64)
+ } else {
+ out.Write32(MH_MAGIC)
+ }
+ out.Write32(machohdr.cpu)
+ out.Write32(machohdr.subcpu)
+ if linkmode == LinkExternal {
+ out.Write32(MH_OBJECT) /* file type - mach object */
+ } else {
+ out.Write32(MH_EXECUTE) /* file type - mach executable */
+ }
+ out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug))
+ out.Write32(uint32(loadsize))
+ flags := uint32(0)
+ if nkind[SymKindUndef] == 0 {
+ flags |= MH_NOUNDEFS
+ }
+ if ctxt.IsPIE() && linkmode == LinkInternal {
+ flags |= MH_PIE | MH_DYLDLINK
+ }
+ out.Write32(flags) /* flags */
+ if arch.PtrSize == 8 {
+ out.Write32(0) /* reserved */
+ }
+
+ for i := 0; i < nseg; i++ {
+ s := &seg[i]
+ if arch.PtrSize == 8 {
+ out.Write32(LC_SEGMENT_64)
+ out.Write32(72 + 80*s.nsect)
+ out.WriteStringN(s.name, 16)
+ out.Write64(s.vaddr)
+ out.Write64(s.vsize)
+ out.Write64(s.fileoffset)
+ out.Write64(s.filesize)
+ out.Write32(s.prot1)
+ out.Write32(s.prot2)
+ out.Write32(s.nsect)
+ out.Write32(s.flag)
+ } else {
+ out.Write32(LC_SEGMENT)
+ out.Write32(56 + 68*s.nsect)
+ out.WriteStringN(s.name, 16)
+ out.Write32(uint32(s.vaddr))
+ out.Write32(uint32(s.vsize))
+ out.Write32(uint32(s.fileoffset))
+ out.Write32(uint32(s.filesize))
+ out.Write32(s.prot1)
+ out.Write32(s.prot2)
+ out.Write32(s.nsect)
+ out.Write32(s.flag)
+ }
+
+ for j := uint32(0); j < s.nsect; j++ {
+ t := &s.sect[j]
+ if arch.PtrSize == 8 {
+ out.WriteStringN(t.name, 16)
+ out.WriteStringN(t.segname, 16)
+ out.Write64(t.addr)
+ out.Write64(t.size)
+ out.Write32(t.off)
+ out.Write32(t.align)
+ out.Write32(t.reloc)
+ out.Write32(t.nreloc)
+ out.Write32(t.flag)
+ out.Write32(t.res1) /* reserved */
+ out.Write32(t.res2) /* reserved */
+ out.Write32(0) /* reserved */
+ } else {
+ out.WriteStringN(t.name, 16)
+ out.WriteStringN(t.segname, 16)
+ out.Write32(uint32(t.addr))
+ out.Write32(uint32(t.size))
+ out.Write32(t.off)
+ out.Write32(t.align)
+ out.Write32(t.reloc)
+ out.Write32(t.nreloc)
+ out.Write32(t.flag)
+ out.Write32(t.res1) /* reserved */
+ out.Write32(t.res2) /* reserved */
+ }
+ }
+ }
+
+ for i := range load {
+ l := &load[i]
+ out.Write32(l.type_)
+ out.Write32(4 * (uint32(len(l.data)) + 2))
+ for j := 0; j < len(l.data); j++ {
+ out.Write32(l.data[j])
+ }
+ }
+
+ return int(out.Offset() - o1)
+}
+
+func (ctxt *Link) domacho() {
+ if *FlagD {
+ return
+ }
+
+ // Copy platform load command.
+ for _, h := range hostobj {
+ load, err := hostobjMachoPlatform(&h)
+ if err != nil {
+ Exitf("%v", err)
+ }
+ if load != nil {
+ machoPlatform = load.platform
+ ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data)))
+ copy(ml.data, load.cmd.data)
+ break
+ }
+ }
+ if machoPlatform == 0 {
+ machoPlatform = PLATFORM_MACOS
+ if buildcfg.GOOS == "ios" {
+ machoPlatform = PLATFORM_IOS
+ }
+ if ctxt.LinkMode == LinkInternal && machoPlatform == PLATFORM_MACOS {
+ var version uint32
+ switch ctxt.Arch.Family {
+ case sys.AMD64:
+ // This must be fairly recent for Apple signing (go.dev/issue/30488).
+ // Having too old a version here was also implicated in some problems
+ // calling into macOS libraries (go.dev/issue/56784).
+ // In general this can be the most recent supported macOS version.
+ version = 10<<16 | 13<<8 | 0<<0 // 10.13.0
+ case sys.ARM64:
+ version = 11<<16 | 0<<8 | 0<<0 // 11.0.0
+ }
+ ml := newMachoLoad(ctxt.Arch, LC_BUILD_VERSION, 4)
+ ml.data[0] = uint32(machoPlatform)
+ ml.data[1] = version // OS version
+ ml.data[2] = version // SDK version
+ ml.data[3] = 0 // ntools
+ }
+ }
+
+ // empirically, string table must begin with " \x00".
+ s := ctxt.loader.LookupOrCreateSym(".machosymstr", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ sb.SetType(sym.SMACHOSYMSTR)
+ sb.SetReachable(true)
+ sb.AddUint8(' ')
+ sb.AddUint8('\x00')
+
+ s = ctxt.loader.LookupOrCreateSym(".machosymtab", 0)
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOSYMTAB)
+ sb.SetReachable(true)
+
+ if ctxt.IsInternal() {
+ s = ctxt.loader.LookupOrCreateSym(".plt", 0) // will be __symbol_stub
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOPLT)
+ sb.SetReachable(true)
+
+ s = ctxt.loader.LookupOrCreateSym(".got", 0) // will be __nl_symbol_ptr
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOGOT)
+ sb.SetReachable(true)
+ sb.SetAlign(4)
+
+ s = ctxt.loader.LookupOrCreateSym(".linkedit.plt", 0) // indirect table for .plt
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOINDIRECTPLT)
+ sb.SetReachable(true)
+
+ s = ctxt.loader.LookupOrCreateSym(".linkedit.got", 0) // indirect table for .got
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOINDIRECTGOT)
+ sb.SetReachable(true)
+ }
+
+ // Add a dummy symbol that will become the __asm marker section.
+ if ctxt.IsExternal() {
+ s = ctxt.loader.LookupOrCreateSym(".llvmasm", 0)
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHO)
+ sb.SetReachable(true)
+ sb.AddUint8(0)
+ }
+
+ // Un-export runtime symbols from plugins. Since the runtime
+ // is included in both the main binary and each plugin, these
+ // symbols appear in both images. If we leave them exported in
+ // the plugin, then the dynamic linker will resolve
+ // relocations to these functions in the plugin's functab to
+ // point to the main image, causing the runtime to think the
+ // plugin's functab is corrupted. By unexporting them, these
+ // become static references, which are resolved to the
+ // plugin's text.
+ //
+ // It would be better to omit the runtime from plugins. (Using
+ // relative PCs in the functab instead of relocations would
+ // also address this.)
+ //
+ // See issue #18190.
+ if ctxt.BuildMode == BuildModePlugin {
+ for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} {
+ // Most of these are data symbols or C
+ // symbols, so they have symbol version 0.
+ ver := 0
+ // _cgo_panic is a Go function, so it uses ABIInternal.
+ if name == "_cgo_panic" {
+ ver = abiInternalVer
+ }
+ s := ctxt.loader.Lookup(name, ver)
+ if s != 0 {
+ ctxt.loader.SetAttrCgoExportDynamic(s, false)
+ }
+ }
+ }
+}
+
+func machoadddynlib(lib string, linkmode LinkMode) {
+ if seenlib[lib] || linkmode == LinkExternal {
+ return
+ }
+ seenlib[lib] = true
+
+ // Will need to store the library name rounded up
+ // and 24 bytes of header metadata. If not enough
+ // space, grab another page of initial space at the
+ // beginning of the output file.
+ loadBudget -= (len(lib)+7)/8*8 + 24
+
+ if loadBudget < 0 {
+ HEADR += 4096
+ *FlagTextAddr += 4096
+ loadBudget += 4096
+ }
+
+ dylib = append(dylib, lib)
+}
+
+func machoshbits(ctxt *Link, mseg *MachoSeg, sect *sym.Section, segname string) {
+ buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1)
+
+ msect := newMachoSect(mseg, buf, segname)
+
+ if sect.Rellen > 0 {
+ msect.reloc = uint32(sect.Reloff)
+ msect.nreloc = uint32(sect.Rellen / 8)
+ }
+
+ for 1<<msect.align < sect.Align {
+ msect.align++
+ }
+ msect.addr = sect.Vaddr
+ msect.size = sect.Length
+
+ if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
+ // data in file
+ if sect.Length > sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr {
+ Errorf(nil, "macho cannot represent section %s crossing data and bss", sect.Name)
+ }
+ msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr)
+ } else {
+ msect.off = 0
+ msect.flag |= S_ZEROFILL
+ }
+
+ if sect.Rwx&1 != 0 {
+ msect.flag |= S_ATTR_SOME_INSTRUCTIONS
+ }
+
+ if sect.Name == ".text" {
+ msect.flag |= S_ATTR_PURE_INSTRUCTIONS
+ }
+
+ if sect.Name == ".plt" {
+ msect.name = "__symbol_stub1"
+ msect.flag = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS | S_SYMBOL_STUBS
+ msect.res1 = 0 //nkind[SymKindLocal];
+ msect.res2 = 6
+ }
+
+ if sect.Name == ".got" {
+ msect.name = "__nl_symbol_ptr"
+ msect.flag = S_NON_LAZY_SYMBOL_POINTERS
+ msect.res1 = uint32(ctxt.loader.SymSize(ctxt.ArchSyms.LinkEditPLT) / 4) /* offset into indirect symbol table */
+ }
+
+ if sect.Name == ".init_array" {
+ msect.name = "__mod_init_func"
+ msect.flag = S_MOD_INIT_FUNC_POINTERS
+ }
+
+ // Some platforms such as watchOS and tvOS require binaries with
+ // bitcode enabled. The Go toolchain can't output bitcode, so use
+ // a marker section in the __LLVM segment, "__asm", to tell the Apple
+ // toolchain that the Go text came from assembler and thus has no
+ // bitcode. This is not true, but Kotlin/Native, Rust and Flutter
+ // are also using this trick.
+ if sect.Name == ".llvmasm" {
+ msect.name = "__asm"
+ msect.segname = "__LLVM"
+ }
+
+ if segname == "__DWARF" {
+ msect.flag |= S_ATTR_DEBUG
+ }
+}
+
+func asmbMacho(ctxt *Link) {
+ machlink := doMachoLink(ctxt)
+ if !*FlagS && ctxt.IsExternal() {
+ symo := int64(Segdwarf.Fileoff + uint64(Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))) + uint64(machlink))
+ ctxt.Out.SeekSet(symo)
+ machoEmitReloc(ctxt)
+ }
+ ctxt.Out.SeekSet(0)
+
+ ldr := ctxt.loader
+
+ /* apple MACH */
+ va := *FlagTextAddr - int64(HEADR)
+
+ mh := getMachoHdr()
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown macho architecture: %v", ctxt.Arch.Family)
+
+ case sys.AMD64:
+ mh.cpu = MACHO_CPU_AMD64
+ mh.subcpu = MACHO_SUBCPU_X86
+
+ case sys.ARM64:
+ mh.cpu = MACHO_CPU_ARM64
+ mh.subcpu = MACHO_SUBCPU_ARM64_ALL
+ }
+
+ var ms *MachoSeg
+ if ctxt.LinkMode == LinkExternal {
+ /* segment for entire file */
+ ms = newMachoSeg("", 40)
+
+ ms.fileoffset = Segtext.Fileoff
+ ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
+ ms.vsize = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr
+ }
+
+ /* segment for zero page */
+ if ctxt.LinkMode != LinkExternal {
+ ms = newMachoSeg("__PAGEZERO", 0)
+ ms.vsize = uint64(va)
+ }
+
+ /* text */
+ v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound))
+
+ if ctxt.LinkMode != LinkExternal {
+ ms = newMachoSeg("__TEXT", 20)
+ ms.vaddr = uint64(va)
+ ms.vsize = uint64(v)
+ ms.fileoffset = 0
+ ms.filesize = uint64(v)
+ ms.prot1 = 7
+ ms.prot2 = 5
+ }
+
+ for _, sect := range Segtext.Sections {
+ machoshbits(ctxt, ms, sect, "__TEXT")
+ }
+
+ /* rodata */
+ if ctxt.LinkMode != LinkExternal && Segrelrodata.Length > 0 {
+ ms = newMachoSeg("__DATA_CONST", 20)
+ ms.vaddr = Segrelrodata.Vaddr
+ ms.vsize = Segrelrodata.Length
+ ms.fileoffset = Segrelrodata.Fileoff
+ ms.filesize = Segrelrodata.Filelen
+ ms.prot1 = 3
+ ms.prot2 = 3
+ ms.flag = 0x10 // SG_READ_ONLY
+ }
+
+ for _, sect := range Segrelrodata.Sections {
+ machoshbits(ctxt, ms, sect, "__DATA_CONST")
+ }
+
+ /* data */
+ if ctxt.LinkMode != LinkExternal {
+ ms = newMachoSeg("__DATA", 20)
+ ms.vaddr = Segdata.Vaddr
+ ms.vsize = Segdata.Length
+ ms.fileoffset = Segdata.Fileoff
+ ms.filesize = Segdata.Filelen
+ ms.prot1 = 3
+ ms.prot2 = 3
+ }
+
+ for _, sect := range Segdata.Sections {
+ machoshbits(ctxt, ms, sect, "__DATA")
+ }
+
+ /* dwarf */
+ if !*FlagW {
+ if ctxt.LinkMode != LinkExternal {
+ ms = newMachoSeg("__DWARF", 20)
+ ms.vaddr = Segdwarf.Vaddr
+ ms.vsize = 0
+ ms.fileoffset = Segdwarf.Fileoff
+ ms.filesize = Segdwarf.Filelen
+ }
+ for _, sect := range Segdwarf.Sections {
+ machoshbits(ctxt, ms, sect, "__DWARF")
+ }
+ }
+
+ if ctxt.LinkMode != LinkExternal {
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown macho architecture: %v", ctxt.Arch.Family)
+
+ case sys.AMD64:
+ ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 42+2)
+ ml.data[0] = 4 /* thread type */
+ ml.data[1] = 42 /* word count */
+ ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */
+ ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32)
+
+ case sys.ARM64:
+ ml := newMachoLoad(ctxt.Arch, LC_MAIN, 4)
+ ml.data[0] = uint32(uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR)))
+ ml.data[1] = uint32((uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) >> 32)
+ }
+ }
+
+ var codesigOff int64
+ if !*FlagD {
+ // must match doMachoLink below
+ s1 := ldr.SymSize(ldr.Lookup(".machorebase", 0))
+ s2 := ldr.SymSize(ldr.Lookup(".machobind", 0))
+ s3 := ldr.SymSize(ldr.Lookup(".machosymtab", 0))
+ s4 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT)
+ s5 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT)
+ s6 := ldr.SymSize(ldr.Lookup(".machosymstr", 0))
+ s7 := ldr.SymSize(ldr.Lookup(".machocodesig", 0))
+
+ if ctxt.LinkMode != LinkExternal {
+ ms := newMachoSeg("__LINKEDIT", 0)
+ ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound)))
+ ms.vsize = uint64(s1 + s2 + s3 + s4 + s5 + s6 + s7)
+ ms.fileoffset = uint64(linkoff)
+ ms.filesize = ms.vsize
+ ms.prot1 = 1
+ ms.prot2 = 1
+
+ codesigOff = linkoff + s1 + s2 + s3 + s4 + s5 + s6
+ }
+
+ if ctxt.LinkMode != LinkExternal && ctxt.IsPIE() {
+ ml := newMachoLoad(ctxt.Arch, LC_DYLD_INFO_ONLY, 10)
+ ml.data[0] = uint32(linkoff) // rebase off
+ ml.data[1] = uint32(s1) // rebase size
+ ml.data[2] = uint32(linkoff + s1) // bind off
+ ml.data[3] = uint32(s2) // bind size
+ ml.data[4] = 0 // weak bind off
+ ml.data[5] = 0 // weak bind size
+ ml.data[6] = 0 // lazy bind off
+ ml.data[7] = 0 // lazy bind size
+ ml.data[8] = 0 // export
+ ml.data[9] = 0 // export size
+ }
+
+ ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4)
+ ml.data[0] = uint32(linkoff + s1 + s2) /* symoff */
+ ml.data[1] = uint32(nsortsym) /* nsyms */
+ ml.data[2] = uint32(linkoff + s1 + s2 + s3 + s4 + s5) /* stroff */
+ ml.data[3] = uint32(s6) /* strsize */
+
+ machodysymtab(ctxt, linkoff+s1+s2)
+
+ if ctxt.LinkMode != LinkExternal {
+ ml := newMachoLoad(ctxt.Arch, LC_LOAD_DYLINKER, 6)
+ ml.data[0] = 12 /* offset to string */
+ stringtouint32(ml.data[1:], "/usr/lib/dyld")
+
+ for _, lib := range dylib {
+ ml = newMachoLoad(ctxt.Arch, LC_LOAD_DYLIB, 4+(uint32(len(lib))+1+7)/8*2)
+ ml.data[0] = 24 /* offset of string from beginning of load */
+ ml.data[1] = 0 /* time stamp */
+ ml.data[2] = 0 /* version */
+ ml.data[3] = 0 /* compatibility version */
+ stringtouint32(ml.data[4:], lib)
+ }
+ }
+
+ if ctxt.IsInternal() && ctxt.NeedCodeSign() {
+ ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2)
+ ml.data[0] = uint32(codesigOff)
+ ml.data[1] = uint32(s7)
+ }
+ }
+
+ a := machowrite(ctxt, ctxt.Arch, ctxt.Out, ctxt.LinkMode)
+ if int32(a) > HEADR {
+ Exitf("HEADR too small: %d > %d", a, HEADR)
+ }
+
+ // Now we have written everything. Compute the code signature (which
+ // is a hash of the file content, so it must be done at last.)
+ if ctxt.IsInternal() && ctxt.NeedCodeSign() {
+ cs := ldr.Lookup(".machocodesig", 0)
+ data := ctxt.Out.Data()
+ if int64(len(data)) != codesigOff {
+ panic("wrong size")
+ }
+ codesign.Sign(ldr.Data(cs), bytes.NewReader(data), "a.out", codesigOff, int64(Segtext.Fileoff), int64(Segtext.Filelen), ctxt.IsExe() || ctxt.IsPIE())
+ ctxt.Out.SeekSet(codesigOff)
+ ctxt.Out.Write(ldr.Data(cs))
+ }
+}
+
+func symkind(ldr *loader.Loader, s loader.Sym) int {
+ if ldr.SymType(s) == sym.SDYNIMPORT {
+ return SymKindUndef
+ }
+ if ldr.AttrCgoExport(s) {
+ return SymKindExtdef
+ }
+ return SymKindLocal
+}
+
+func collectmachosyms(ctxt *Link) {
+ ldr := ctxt.loader
+
+ addsym := func(s loader.Sym) {
+ sortsym = append(sortsym, s)
+ nkind[symkind(ldr, s)]++
+ }
+
+ // Add special runtime.text and runtime.etext symbols.
+ // We've already included this symbol in Textp on darwin if ctxt.DynlinkingGo().
+ // See data.go:/textaddress
+ if !ctxt.DynlinkingGo() {
+ s := ldr.Lookup("runtime.text", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ addsym(s)
+ }
+ for n := range Segtext.Sections[1:] {
+ s := ldr.Lookup(fmt.Sprintf("runtime.text.%d", n+1), 0)
+ if s != 0 {
+ addsym(s)
+ } else {
+ break
+ }
+ }
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ addsym(s)
+ }
+ }
+
+ // Add text symbols.
+ for _, s := range ctxt.Textp {
+ addsym(s)
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym) bool {
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ name := ldr.SymName(s) // TODO: try not to read the name
+ if name == "" || name[0] == '.' {
+ return false
+ }
+ return true
+ }
+
+ // Add data symbols and external references.
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ t := ldr.SymType(s)
+ if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata
+ if t == sym.STLSBSS {
+ // TLSBSS is not used on darwin. See data.go:allocateDataSections
+ continue
+ }
+ if !shouldBeInSymbolTable(s) {
+ continue
+ }
+ addsym(s)
+ }
+
+ switch t {
+ case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT:
+ addsym(s)
+ }
+
+ // Some 64-bit functions have a "$INODE64" or "$INODE64$UNIX2003" suffix.
+ if t == sym.SDYNIMPORT && ldr.SymDynimplib(s) == "/usr/lib/libSystem.B.dylib" {
+ // But only on macOS.
+ if machoPlatform == PLATFORM_MACOS {
+ switch n := ldr.SymExtname(s); n {
+ case "fdopendir":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ ldr.SetSymExtname(s, n+"$INODE64")
+ }
+ case "readdir_r", "getfsstat":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ ldr.SetSymExtname(s, n+"$INODE64")
+ }
+ }
+ }
+ }
+ }
+
+ nsortsym = len(sortsym)
+}
+
+func machosymorder(ctxt *Link) {
+ ldr := ctxt.loader
+
+ // On Mac OS X Mountain Lion, we must sort exported symbols
+ // So we sort them here and pre-allocate dynid for them
+ // See https://golang.org/issue/4029
+ for _, s := range ctxt.dynexp {
+ if !ldr.AttrReachable(s) {
+ panic("dynexp symbol is not reachable")
+ }
+ }
+ collectmachosyms(ctxt)
+ sort.Slice(sortsym[:nsortsym], func(i, j int) bool {
+ s1 := sortsym[i]
+ s2 := sortsym[j]
+ k1 := symkind(ldr, s1)
+ k2 := symkind(ldr, s2)
+ if k1 != k2 {
+ return k1 < k2
+ }
+ return ldr.SymExtname(s1) < ldr.SymExtname(s2) // Note: unnamed symbols are not added in collectmachosyms
+ })
+ for i, s := range sortsym {
+ ldr.SetSymDynid(s, int32(i))
+ }
+}
+
+// AddMachoSym adds s to Mach-O symbol table, used in GenSymLate.
+// Currently only used on ARM64 when external linking.
+func AddMachoSym(ldr *loader.Loader, s loader.Sym) {
+ ldr.SetSymDynid(s, int32(nsortsym))
+ sortsym = append(sortsym, s)
+ nsortsym++
+ nkind[symkind(ldr, s)]++
+}
+
+// machoShouldExport reports whether a symbol needs to be exported.
+//
+// When dynamically linking, all non-local variables and plugin-exported
+// symbols need to be exported.
+func machoShouldExport(ctxt *Link, ldr *loader.Loader, s loader.Sym) bool {
+ if !ctxt.DynlinkingGo() || ldr.AttrLocal(s) {
+ return false
+ }
+ if ctxt.BuildMode == BuildModePlugin && strings.HasPrefix(ldr.SymExtname(s), objabi.PathToPrefix(*flagPluginPath)) {
+ return true
+ }
+ name := ldr.SymName(s)
+ if strings.HasPrefix(name, "go:itab.") {
+ return true
+ }
+ if strings.HasPrefix(name, "type:") && !strings.HasPrefix(name, "type:.") {
+ // reduce runtime typemap pressure, but do not
+ // export alg functions (type:.*), as these
+ // appear in pclntable.
+ return true
+ }
+ if strings.HasPrefix(name, "go:link.pkghash") {
+ return true
+ }
+ return ldr.SymType(s) >= sym.SFirstWritable // only writable sections
+}
+
+func machosymtab(ctxt *Link) {
+ ldr := ctxt.loader
+ symtab := ldr.CreateSymForUpdate(".machosymtab", 0)
+ symstr := ldr.CreateSymForUpdate(".machosymstr", 0)
+
+ for _, s := range sortsym[:nsortsym] {
+ symtab.AddUint32(ctxt.Arch, uint32(symstr.Size()))
+
+ export := machoShouldExport(ctxt, ldr, s)
+
+ // Prefix symbol names with "_" to match the system toolchain.
+ // (We used to only prefix C symbols, which is all required for the build.
+ // But some tools don't recognize Go symbols as symbols, so we prefix them
+ // as well.)
+ symstr.AddUint8('_')
+
+ // replace "·" as ".", because DTrace cannot handle it.
+ name := strings.Replace(ldr.SymExtname(s), "·", ".", -1)
+
+ name = mangleABIName(ctxt, ldr, s, name)
+ symstr.Addstring(name)
+
+ if t := ldr.SymType(s); t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT {
+ symtab.AddUint8(0x01) // type N_EXT, external symbol
+ symtab.AddUint8(0) // no section
+ symtab.AddUint16(ctxt.Arch, 0) // desc
+ symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value
+ } else {
+ if export || ldr.AttrCgoExportDynamic(s) {
+ symtab.AddUint8(0x0f) // N_SECT | N_EXT
+ } else if ldr.AttrCgoExportStatic(s) {
+ // Only export statically, not dynamically. (N_PEXT is like hidden visibility)
+ symtab.AddUint8(0x1f) // N_SECT | N_EXT | N_PEXT
+ } else {
+ symtab.AddUint8(0x0e) // N_SECT
+ }
+ o := s
+ if outer := ldr.OuterSym(o); outer != 0 {
+ o = outer
+ }
+ if ldr.SymSect(o) == nil {
+ ldr.Errorf(s, "missing section for symbol")
+ symtab.AddUint8(0)
+ } else {
+ symtab.AddUint8(uint8(ldr.SymSect(o).Extnum))
+ }
+ symtab.AddUint16(ctxt.Arch, 0) // desc
+ symtab.AddUintXX(ctxt.Arch, uint64(ldr.SymAddr(s)), ctxt.Arch.PtrSize)
+ }
+ }
+}
+
+func machodysymtab(ctxt *Link, base int64) {
+ ml := newMachoLoad(ctxt.Arch, LC_DYSYMTAB, 18)
+
+ n := 0
+ ml.data[0] = uint32(n) /* ilocalsym */
+ ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */
+ n += nkind[SymKindLocal]
+
+ ml.data[2] = uint32(n) /* iextdefsym */
+ ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */
+ n += nkind[SymKindExtdef]
+
+ ml.data[4] = uint32(n) /* iundefsym */
+ ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */
+
+ ml.data[6] = 0 /* tocoffset */
+ ml.data[7] = 0 /* ntoc */
+ ml.data[8] = 0 /* modtaboff */
+ ml.data[9] = 0 /* nmodtab */
+ ml.data[10] = 0 /* extrefsymoff */
+ ml.data[11] = 0 /* nextrefsyms */
+
+ ldr := ctxt.loader
+
+ // must match domacholink below
+ s1 := ldr.SymSize(ldr.Lookup(".machosymtab", 0))
+ s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT)
+ s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT)
+ ml.data[12] = uint32(base + s1) /* indirectsymoff */
+ ml.data[13] = uint32((s2 + s3) / 4) /* nindirectsyms */
+
+ ml.data[14] = 0 /* extreloff */
+ ml.data[15] = 0 /* nextrel */
+ ml.data[16] = 0 /* locreloff */
+ ml.data[17] = 0 /* nlocrel */
+}
+
+func doMachoLink(ctxt *Link) int64 {
+ machosymtab(ctxt)
+ machoDyldInfo(ctxt)
+
+ ldr := ctxt.loader
+
+ // write data that will be linkedit section
+ s1 := ldr.Lookup(".machorebase", 0)
+ s2 := ldr.Lookup(".machobind", 0)
+ s3 := ldr.Lookup(".machosymtab", 0)
+ s4 := ctxt.ArchSyms.LinkEditPLT
+ s5 := ctxt.ArchSyms.LinkEditGOT
+ s6 := ldr.Lookup(".machosymstr", 0)
+
+ size := ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4) + ldr.SymSize(s5) + ldr.SymSize(s6)
+
+ // Force the linkedit section to end on a 16-byte
+ // boundary. This allows pure (non-cgo) Go binaries
+ // to be code signed correctly.
+ //
+ // Apple's codesign_allocate (a helper utility for
+ // the codesign utility) can do this fine itself if
+ // it is run on a dynamic Mach-O binary. However,
+ // when it is run on a pure (non-cgo) Go binary, where
+ // the linkedit section is mostly empty, it fails to
+ // account for the extra padding that it itself adds
+ // when adding the LC_CODE_SIGNATURE load command
+ // (which must be aligned on a 16-byte boundary).
+ //
+ // By forcing the linkedit section to end on a 16-byte
+ // boundary, codesign_allocate will not need to apply
+ // any alignment padding itself, working around the
+ // issue.
+ if size%16 != 0 {
+ n := 16 - size%16
+ s6b := ldr.MakeSymbolUpdater(s6)
+ s6b.Grow(s6b.Size() + n)
+ s6b.SetSize(s6b.Size() + n)
+ size += n
+ }
+
+ if size > 0 {
+ linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segrelrodata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
+ ctxt.Out.SeekSet(linkoff)
+
+ ctxt.Out.Write(ldr.Data(s1))
+ ctxt.Out.Write(ldr.Data(s2))
+ ctxt.Out.Write(ldr.Data(s3))
+ ctxt.Out.Write(ldr.Data(s4))
+ ctxt.Out.Write(ldr.Data(s5))
+ ctxt.Out.Write(ldr.Data(s6))
+
+ // Add code signature if necessary. This must be the last.
+ s7 := machoCodeSigSym(ctxt, linkoff+size)
+ size += ldr.SymSize(s7)
+ }
+
+ return Rnd(size, int64(*FlagRound))
+}
+
+func machorelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) {
+ // If main section has no bits, nothing to relocate.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return
+ }
+ ldr := ctxt.loader
+
+ for i, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if uint64(ldr.SymValue(s)) >= sect.Vaddr {
+ syms = syms[i:]
+ break
+ }
+ }
+
+ eaddr := sect.Vaddr + sect.Length
+ for _, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymValue(s) >= int64(eaddr) {
+ break
+ }
+
+ // Compute external relocations on the go, and pass to Machoreloc1
+ // to stream out.
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rr, ok := extreloc(ctxt, ldr, s, r)
+ if !ok {
+ continue
+ }
+ if rr.Xsym == 0 {
+ ldr.Errorf(s, "missing xsym in relocation")
+ continue
+ }
+ if !ldr.AttrReachable(rr.Xsym) {
+ ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym))
+ }
+ if !thearch.Machoreloc1(ctxt.Arch, out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) {
+ ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ }
+ }
+
+ // sanity check
+ if uint64(out.Offset()) != sect.Reloff+sect.Rellen {
+ panic("machorelocsect: size mismatch")
+ }
+}
+
+func machoEmitReloc(ctxt *Link) {
+ for ctxt.Out.Offset()&7 != 0 {
+ ctxt.Out.Write8(0)
+ }
+
+ sizeExtRelocs(ctxt, thearch.MachorelocSize)
+ relocSect, wg := relocSectFn(ctxt, machorelocsect)
+
+ relocSect(ctxt, Segtext.Sections[0], ctxt.Textp)
+ for _, sect := range Segtext.Sections[1:] {
+ if sect.Name == ".text" {
+ relocSect(ctxt, sect, ctxt.Textp)
+ } else {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ }
+ for _, sect := range Segrelrodata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for _, sect := range Segdata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for i := 0; i < len(Segdwarf.Sections); i++ {
+ sect := Segdwarf.Sections[i]
+ si := dwarfp[i]
+ if si.secSym() != loader.Sym(sect.Sym) ||
+ ctxt.loader.SymSect(si.secSym()) != sect {
+ panic("inconsistency between dwarfp and Segdwarf")
+ }
+ relocSect(ctxt, sect, si.syms)
+ }
+ wg.Wait()
+}
+
+// hostobjMachoPlatform returns the first platform load command found
+// in the host object, if any.
+func hostobjMachoPlatform(h *Hostobj) (*MachoPlatformLoad, error) {
+ f, err := os.Open(h.file)
+ if err != nil {
+ return nil, fmt.Errorf("%s: failed to open host object: %v\n", h.file, err)
+ }
+ defer f.Close()
+ sr := io.NewSectionReader(f, h.off, h.length)
+ m, err := macho.NewFile(sr)
+ if err != nil {
+ // Not a valid Mach-O file.
+ return nil, nil
+ }
+ return peekMachoPlatform(m)
+}
+
+// peekMachoPlatform returns the first LC_VERSION_MIN_* or LC_BUILD_VERSION
+// load command found in the Mach-O file, if any.
+func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) {
+ for _, cmd := range m.Loads {
+ raw := cmd.Raw()
+ ml := MachoLoad{
+ type_: m.ByteOrder.Uint32(raw),
+ }
+ // Skip the type and command length.
+ data := raw[8:]
+ var p MachoPlatform
+ switch ml.type_ {
+ case LC_VERSION_MIN_IPHONEOS:
+ p = PLATFORM_IOS
+ case LC_VERSION_MIN_MACOSX:
+ p = PLATFORM_MACOS
+ case LC_VERSION_MIN_WATCHOS:
+ p = PLATFORM_WATCHOS
+ case LC_VERSION_MIN_TVOS:
+ p = PLATFORM_TVOS
+ case LC_BUILD_VERSION:
+ p = MachoPlatform(m.ByteOrder.Uint32(data))
+ default:
+ continue
+ }
+ ml.data = make([]uint32, len(data)/4)
+ r := bytes.NewReader(data)
+ if err := binary.Read(r, m.ByteOrder, &ml.data); err != nil {
+ return nil, err
+ }
+ return &MachoPlatformLoad{
+ platform: p,
+ cmd: ml,
+ }, nil
+ }
+ return nil, nil
+}
+
+// A rebase entry tells the dynamic linker the data at sym+off needs to be
+// relocated when the in-memory image moves. (This is somewhat like, say,
+// ELF R_X86_64_RELATIVE).
+// For now, the only kind of entry we support is that the data is an absolute
+// address. That seems all we need.
+// In the binary it uses a compact stateful bytecode encoding. So we record
+// entries as we go and build the table at the end.
+type machoRebaseRecord struct {
+ sym loader.Sym
+ off int64
+}
+
+var machorebase []machoRebaseRecord
+
+func MachoAddRebase(s loader.Sym, off int64) {
+ machorebase = append(machorebase, machoRebaseRecord{s, off})
+}
+
+// A bind entry tells the dynamic linker the data at GOT+off should be bound
+// to the address of the target symbol, which is a dynamic import.
+// For now, the only kind of entry we support is that the data is an absolute
+// address, and the source symbol is always the GOT. That seems all we need.
+// In the binary it uses a compact stateful bytecode encoding. So we record
+// entries as we go and build the table at the end.
+type machoBindRecord struct {
+ off int64
+ targ loader.Sym
+}
+
+var machobind []machoBindRecord
+
+func MachoAddBind(off int64, targ loader.Sym) {
+ machobind = append(machobind, machoBindRecord{off, targ})
+}
+
+// Generate data for the dynamic linker, used in LC_DYLD_INFO_ONLY load command.
+// See mach-o/loader.h, struct dyld_info_command, for the encoding.
+// e.g. https://opensource.apple.com/source/xnu/xnu-6153.81.5/EXTERNAL_HEADERS/mach-o/loader.h
+func machoDyldInfo(ctxt *Link) {
+ ldr := ctxt.loader
+ rebase := ldr.CreateSymForUpdate(".machorebase", 0)
+ bind := ldr.CreateSymForUpdate(".machobind", 0)
+
+ if !(ctxt.IsPIE() && ctxt.IsInternal()) {
+ return
+ }
+
+ segId := func(seg *sym.Segment) uint8 {
+ switch seg {
+ case &Segtext:
+ return 1
+ case &Segrelrodata:
+ return 2
+ case &Segdata:
+ if Segrelrodata.Length > 0 {
+ return 3
+ }
+ return 2
+ }
+ panic("unknown segment")
+ }
+
+ dylibId := func(s loader.Sym) int {
+ slib := ldr.SymDynimplib(s)
+ for i, lib := range dylib {
+ if lib == slib {
+ return i + 1
+ }
+ }
+ return BIND_SPECIAL_DYLIB_FLAT_LOOKUP // don't know where it is from
+ }
+
+ // Rebase table.
+ // TODO: use more compact encoding. The encoding is stateful, and
+ // we can use delta encoding.
+ rebase.AddUint8(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER)
+ for _, r := range machorebase {
+ seg := ldr.SymSect(r.sym).Seg
+ off := uint64(ldr.SymValue(r.sym)+r.off) - seg.Vaddr
+ rebase.AddUint8(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg))
+ rebase.AddUleb(off)
+
+ rebase.AddUint8(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1)
+ }
+ rebase.AddUint8(REBASE_OPCODE_DONE)
+ sz := Rnd(rebase.Size(), 8)
+ rebase.Grow(sz)
+ rebase.SetSize(sz)
+
+ // Bind table.
+ // TODO: compact encoding, as above.
+ // TODO: lazy binding?
+ got := ctxt.GOT
+ seg := ldr.SymSect(got).Seg
+ gotAddr := ldr.SymValue(got)
+ bind.AddUint8(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER)
+ for _, r := range machobind {
+ off := uint64(gotAddr+r.off) - seg.Vaddr
+ bind.AddUint8(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg))
+ bind.AddUleb(off)
+
+ d := dylibId(r.targ)
+ if d > 0 && d < 128 {
+ bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | uint8(d)&0xf)
+ } else if d >= 128 {
+ bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB)
+ bind.AddUleb(uint64(d))
+ } else { // d <= 0
+ bind.AddUint8(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | uint8(d)&0xf)
+ }
+
+ bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
+ // target symbol name as a C string, with _ prefix
+ bind.AddUint8('_')
+ bind.Addstring(ldr.SymExtname(r.targ))
+
+ bind.AddUint8(BIND_OPCODE_DO_BIND)
+ }
+ bind.AddUint8(BIND_OPCODE_DONE)
+ sz = Rnd(bind.Size(), 16) // make it 16-byte aligned, see the comment in doMachoLink
+ bind.Grow(sz)
+ bind.SetSize(sz)
+
+ // TODO: export table.
+ // The symbols names are encoded as a trie. I'm really too lazy to do that
+ // for now.
+ // Without it, the symbols are not dynamically exported, so they cannot be
+ // e.g. dlsym'd. But internal linking is not the default in that case, so
+ // it is fine.
+}
+
+// machoCodeSigSym creates and returns a symbol for code signature.
+// The symbol context is left as zeros, which will be generated at the end
+// (as it depends on the rest of the file).
+func machoCodeSigSym(ctxt *Link, codeSize int64) loader.Sym {
+ ldr := ctxt.loader
+ cs := ldr.CreateSymForUpdate(".machocodesig", 0)
+ if !ctxt.NeedCodeSign() || ctxt.IsExternal() {
+ return cs.Sym()
+ }
+ sz := codesign.Size(codeSize, "a.out")
+ cs.Grow(sz)
+ cs.SetSize(sz)
+ return cs.Sym()
+}
+
+// machoCodeSign code-signs Mach-O file fname with an ad-hoc signature.
+// This is used for updating an external linker generated binary.
+func machoCodeSign(ctxt *Link, fname string) error {
+ f, err := os.OpenFile(fname, os.O_RDWR, 0)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ mf, err := macho.NewFile(f)
+ if err != nil {
+ return err
+ }
+ if mf.Magic != macho.Magic64 {
+ Exitf("not 64-bit Mach-O file: %s", fname)
+ }
+
+ // Find existing LC_CODE_SIGNATURE and __LINKEDIT segment
+ var sigOff, sigSz, csCmdOff, linkeditOff int64
+ var linkeditSeg, textSeg *macho.Segment
+ loadOff := int64(machoHeaderSize64)
+ get32 := mf.ByteOrder.Uint32
+ for _, l := range mf.Loads {
+ data := l.Raw()
+ cmd, sz := get32(data), get32(data[4:])
+ if cmd == LC_CODE_SIGNATURE {
+ sigOff = int64(get32(data[8:]))
+ sigSz = int64(get32(data[12:]))
+ csCmdOff = loadOff
+ }
+ if seg, ok := l.(*macho.Segment); ok {
+ switch seg.Name {
+ case "__LINKEDIT":
+ linkeditSeg = seg
+ linkeditOff = loadOff
+ case "__TEXT":
+ textSeg = seg
+ }
+ }
+ loadOff += int64(sz)
+ }
+
+ if sigOff == 0 {
+ // The C linker doesn't generate a signed binary, for some reason.
+ // Skip.
+ return nil
+ }
+
+ fi, err := f.Stat()
+ if err != nil {
+ return err
+ }
+ if sigOff+sigSz != fi.Size() {
+ // We don't expect anything after the signature (this will invalidate
+ // the signature anyway.)
+ return fmt.Errorf("unexpected content after code signature")
+ }
+
+ sz := codesign.Size(sigOff, "a.out")
+ if sz != sigSz {
+ // Update the load command,
+ var tmp [8]byte
+ mf.ByteOrder.PutUint32(tmp[:4], uint32(sz))
+ _, err = f.WriteAt(tmp[:4], csCmdOff+12)
+ if err != nil {
+ return err
+ }
+
+ // Uodate the __LINKEDIT segment.
+ segSz := sigOff + sz - int64(linkeditSeg.Offset)
+ mf.ByteOrder.PutUint64(tmp[:8], uint64(segSz))
+ _, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Memsz)))
+ if err != nil {
+ return err
+ }
+ _, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Filesz)))
+ if err != nil {
+ return err
+ }
+ }
+
+ cs := make([]byte, sz)
+ codesign.Sign(cs, f, "a.out", sigOff, int64(textSeg.Offset), int64(textSeg.Filesz), ctxt.IsExe() || ctxt.IsPIE())
+ _, err = f.WriteAt(cs, sigOff)
+ if err != nil {
+ return err
+ }
+ err = f.Truncate(sigOff + sz)
+ return err
+}
diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go
new file mode 100644
index 0000000..2ab7da9
--- /dev/null
+++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go
@@ -0,0 +1,438 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "bytes"
+ "compress/zlib"
+ "debug/macho"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "unsafe"
+)
+
+type loadCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+}
+
+type dyldInfoCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ RebaseOff, RebaseLen uint32
+ BindOff, BindLen uint32
+ WeakBindOff, WeakBindLen uint32
+ LazyBindOff, LazyBindLen uint32
+ ExportOff, ExportLen uint32
+}
+
+type linkEditDataCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ DataOff, DataLen uint32
+}
+
+type encryptionInfoCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ CryptOff, CryptLen uint32
+ CryptId uint32
+}
+
+type loadCmdReader struct {
+ offset, next int64
+ f *os.File
+ order binary.ByteOrder
+}
+
+func (r *loadCmdReader) Next() (loadCmd, error) {
+ var cmd loadCmd
+
+ r.offset = r.next
+ if _, err := r.f.Seek(r.offset, 0); err != nil {
+ return cmd, err
+ }
+ if err := binary.Read(r.f, r.order, &cmd); err != nil {
+ return cmd, err
+ }
+ r.next = r.offset + int64(cmd.Len)
+ return cmd, nil
+}
+
+func (r loadCmdReader) ReadAt(offset int64, data interface{}) error {
+ if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+ return err
+ }
+ return binary.Read(r.f, r.order, data)
+}
+
+func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
+ if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+ return err
+ }
+ return binary.Write(r.f, r.order, data)
+}
+
+// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable.
+//
+// With internal linking, DWARF is embedded into the executable, this lets us do the
+// same for external linking.
+// exef is the file of the executable with no DWARF. It must have enough room in the macho
+// header to add the DWARF sections. (Use ld's -headerpad option)
+// exem is the macho representation of exef.
+// dsym is the path to the macho file containing DWARF from dsymutil.
+// outexe is the path where the combined executable should be saved.
+func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe string) error {
+ dwarff, err := os.Open(dsym)
+ if err != nil {
+ return err
+ }
+ defer dwarff.Close()
+ outf, err := os.OpenFile(outexe, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
+ if err != nil {
+ return err
+ }
+ defer outf.Close()
+ dwarfm, err := macho.NewFile(dwarff)
+ if err != nil {
+ return err
+ }
+ defer dwarfm.Close()
+
+ // The string table needs to be the last thing in the file
+ // for code signing to work. So we'll need to move the
+ // linkedit section, but all the others can be copied directly.
+ linkseg := exem.Segment("__LINKEDIT")
+ if linkseg == nil {
+ return fmt.Errorf("missing __LINKEDIT segment")
+ }
+
+ if _, err := exef.Seek(0, 0); err != nil {
+ return err
+ }
+ if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil {
+ return err
+ }
+
+ realdwarf := dwarfm.Segment("__DWARF")
+ if realdwarf == nil {
+ return fmt.Errorf("missing __DWARF segment")
+ }
+
+ // Try to compress the DWARF sections. This includes some Apple
+ // proprietary sections like __apple_types.
+ compressedSects, compressedBytes, err := machoCompressSections(ctxt, dwarfm)
+ if err != nil {
+ return err
+ }
+
+ // Now copy the dwarf data into the output.
+ // Kernel requires all loaded segments to be page-aligned in the file,
+ // even though we mark this one as being 0 bytes of virtual address space.
+ dwarfstart := Rnd(int64(linkseg.Offset), int64(*FlagRound))
+ if _, err := outf.Seek(dwarfstart, 0); err != nil {
+ return err
+ }
+
+ if _, err := dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
+ return err
+ }
+
+ // Write out the compressed sections, or the originals if we gave up
+ // on compressing them.
+ var dwarfsize uint64
+ if compressedBytes != nil {
+ dwarfsize = uint64(len(compressedBytes))
+ if _, err := outf.Write(compressedBytes); err != nil {
+ return err
+ }
+ } else {
+ if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil {
+ return err
+ }
+ dwarfsize = realdwarf.Filesz
+ }
+
+ // And finally the linkedit section.
+ if _, err := exef.Seek(int64(linkseg.Offset), 0); err != nil {
+ return err
+ }
+ linkstart := Rnd(dwarfstart+int64(dwarfsize), int64(*FlagRound))
+ if _, err := outf.Seek(linkstart, 0); err != nil {
+ return err
+ }
+ if _, err := io.Copy(outf, exef); err != nil {
+ return err
+ }
+
+ // Now we need to update the headers.
+ textsect := exem.Section("__text")
+ if textsect == nil {
+ return fmt.Errorf("missing __text section")
+ }
+
+ cmdOffset := unsafe.Sizeof(exem.FileHeader)
+ if is64bit := exem.Magic == macho.Magic64; is64bit {
+ // mach_header_64 has one extra uint32.
+ cmdOffset += unsafe.Sizeof(exem.Magic)
+ }
+ dwarfCmdOffset := uint32(cmdOffset) + exem.FileHeader.Cmdsz
+ availablePadding := textsect.Offset - dwarfCmdOffset
+ if availablePadding < realdwarf.Len {
+ return fmt.Errorf("no room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding)
+ }
+ // First, copy the dwarf load command into the header. It will be
+ // updated later with new offsets and lengths as necessary.
+ if _, err := outf.Seek(int64(dwarfCmdOffset), 0); err != nil {
+ return err
+ }
+ if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil {
+ return err
+ }
+ if _, err := outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil {
+ return err
+ }
+ if err := binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil {
+ return err
+ }
+ if err := binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil {
+ return err
+ }
+
+ reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder}
+ for i := uint32(0); i < exem.Ncmd; i++ {
+ cmd, err := reader.Next()
+ if err != nil {
+ return err
+ }
+ linkoffset := uint64(linkstart) - linkseg.Offset
+ switch cmd.Cmd {
+ case macho.LoadCmdSegment64:
+ err = machoUpdateSegment(reader, linkseg, linkoffset)
+ case macho.LoadCmdSegment:
+ panic("unexpected 32-bit segment")
+ case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
+ case macho.LoadCmdSymtab:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.SymtabCmd{}, "Symoff", "Stroff")
+ case macho.LoadCmdDysymtab:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
+ case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS,
+ LC_DYLD_EXPORTS_TRIE, LC_DYLD_CHAINED_FIXUPS:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &linkEditDataCmd{}, "DataOff")
+ case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &encryptionInfoCmd{}, "CryptOff")
+ case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread,
+ LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION,
+ LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH, LC_ID_DYLIB,
+ LC_SYMSEG, LC_LOADFVMLIB, LC_IDFVMLIB, LC_IDENT, LC_FVMFILE, LC_PREPAGE, LC_ID_DYLINKER,
+ LC_ROUTINES, LC_SUB_FRAMEWORK, LC_SUB_UMBRELLA, LC_SUB_CLIENT, LC_SUB_LIBRARY, LC_TWOLEVEL_HINTS,
+ LC_PREBIND_CKSUM, LC_ROUTINES_64, LC_LAZY_LOAD_DYLIB, LC_LOAD_UPWARD_DYLIB, LC_DYLD_ENVIRONMENT,
+ LC_LINKER_OPTION, LC_LINKER_OPTIMIZATION_HINT, LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS,
+ LC_VERSION_NOTE, LC_BUILD_VERSION:
+ // Nothing to update
+ default:
+ err = fmt.Errorf("unknown load command 0x%x (%s)", int(cmd.Cmd), cmd.Cmd)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ // Do the final update of the DWARF segment's load command.
+ return machoUpdateDwarfHeader(&reader, compressedSects, dwarfsize, dwarfstart, realdwarf)
+}
+
+// machoCompressSections tries to compress the DWARF segments in dwarfm,
+// returning the updated sections and segment contents, nils if the sections
+// weren't compressed, or an error if there was a problem reading dwarfm.
+func machoCompressSections(ctxt *Link, dwarfm *macho.File) ([]*macho.Section, []byte, error) {
+ if !ctxt.compressDWARF {
+ return nil, nil, nil
+ }
+
+ dwarfseg := dwarfm.Segment("__DWARF")
+ var sects []*macho.Section
+ var buf bytes.Buffer
+
+ for _, sect := range dwarfm.Sections {
+ if sect.Seg != "__DWARF" {
+ continue
+ }
+
+ // As of writing, there are no relocations in dsymutil's output
+ // so there's no point in worrying about them. Bail out if that
+ // changes.
+ if sect.Nreloc != 0 {
+ return nil, nil, nil
+ }
+
+ data, err := sect.Data()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ compressed, contents, err := machoCompressSection(data)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ newSec := *sect
+ newSec.Offset = uint32(dwarfseg.Offset) + uint32(buf.Len())
+ newSec.Addr = dwarfseg.Addr + uint64(buf.Len())
+ if compressed {
+ newSec.Name = "__z" + sect.Name[2:]
+ newSec.Size = uint64(len(contents))
+ }
+ sects = append(sects, &newSec)
+ buf.Write(contents)
+ }
+ return sects, buf.Bytes(), nil
+}
+
+// machoCompressSection compresses secBytes if it results in less data.
+func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, err error) {
+ var buf bytes.Buffer
+ buf.WriteString("ZLIB")
+ var sizeBytes [8]byte
+ binary.BigEndian.PutUint64(sizeBytes[:], uint64(len(sectBytes)))
+ buf.Write(sizeBytes[:])
+
+ z := zlib.NewWriter(&buf)
+ if _, err := z.Write(sectBytes); err != nil {
+ return false, nil, err
+ }
+ if err := z.Close(); err != nil {
+ return false, nil, err
+ }
+ if buf.Len() >= len(sectBytes) {
+ return false, sectBytes, nil
+ }
+ return true, buf.Bytes(), nil
+}
+
+// machoUpdateSegment updates the load command for a moved segment.
+// Only the linkedit segment should move, and it should have 0 sections.
+func machoUpdateSegment(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64) error {
+ var seg macho.Segment64
+ if err := r.ReadAt(0, &seg); err != nil {
+ return err
+ }
+
+ // Only the linkedit segment moved, anything before that is fine.
+ if seg.Offset < linkseg.Offset {
+ return nil
+ }
+ seg.Offset += linkoffset
+ if err := r.WriteAt(0, &seg); err != nil {
+ return err
+ }
+ // There shouldn't be any sections, but just to make sure...
+ return machoUpdateSections(r, &seg, linkoffset, nil)
+}
+
+func machoUpdateSections(r loadCmdReader, seg *macho.Segment64, deltaOffset uint64, compressedSects []*macho.Section) error {
+ nsect := seg.Nsect
+ if nsect == 0 {
+ return nil
+ }
+ sectOffset := int64(unsafe.Sizeof(*seg))
+
+ var sect macho.Section64
+ sectSize := int64(unsafe.Sizeof(sect))
+ for i := uint32(0); i < nsect; i++ {
+ if err := r.ReadAt(sectOffset, &sect); err != nil {
+ return err
+ }
+ if compressedSects != nil {
+ cSect := compressedSects[i]
+ copy(sect.Name[:], cSect.Name)
+ sect.Size = cSect.Size
+ if cSect.Offset != 0 {
+ sect.Offset = cSect.Offset + uint32(deltaOffset)
+ }
+ if cSect.Addr != 0 {
+ sect.Addr = cSect.Addr
+ }
+ } else {
+ if sect.Offset != 0 {
+ sect.Offset += uint32(deltaOffset)
+ }
+ if sect.Reloff != 0 {
+ sect.Reloff += uint32(deltaOffset)
+ }
+ }
+ if err := r.WriteAt(sectOffset, &sect); err != nil {
+ return err
+ }
+ sectOffset += sectSize
+ }
+ return nil
+}
+
+// machoUpdateDwarfHeader updates the DWARF segment load command.
+func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, dwarfsize uint64, dwarfstart int64, realdwarf *macho.Segment) error {
+ cmd, err := r.Next()
+ if err != nil {
+ return err
+ }
+ if cmd.Cmd != macho.LoadCmdSegment64 {
+ panic("not a Segment64")
+ }
+ var seg macho.Segment64
+ if err := r.ReadAt(0, &seg); err != nil {
+ return err
+ }
+ seg.Offset = uint64(dwarfstart)
+
+ if compressedSects != nil {
+ var segSize uint64
+ for _, newSect := range compressedSects {
+ segSize += newSect.Size
+ }
+ seg.Filesz = segSize
+ } else {
+ seg.Filesz = dwarfsize
+ }
+
+ // We want the DWARF segment to be considered non-loadable, so
+ // force vmaddr and vmsize to zero. In addition, set the initial
+ // protection to zero so as to make the dynamic loader happy,
+ // since otherwise it may complain that the vm size and file
+ // size don't match for the segment. See issues 21647 and 32673
+ // for more context. Also useful to refer to the Apple dynamic
+ // loader source, specifically ImageLoaderMachO::sniffLoadCommands
+ // in ImageLoaderMachO.cpp (various versions can be found online, see
+ // https://opensource.apple.com/source/dyld/dyld-519.2.2/src/ImageLoaderMachO.cpp.auto.html
+ // as one example).
+ seg.Addr = 0
+ seg.Memsz = 0
+ seg.Prot = 0
+
+ if err := r.WriteAt(0, &seg); err != nil {
+ return err
+ }
+ return machoUpdateSections(*r, &seg, uint64(dwarfstart)-realdwarf.Offset, compressedSects)
+}
+
+func machoUpdateLoadCommand(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64, cmd interface{}, fields ...string) error {
+ if err := r.ReadAt(0, cmd); err != nil {
+ return err
+ }
+ value := reflect.Indirect(reflect.ValueOf(cmd))
+
+ for _, name := range fields {
+ field := value.FieldByName(name)
+ if fieldval := field.Uint(); fieldval >= linkseg.Offset {
+ field.SetUint(fieldval + linkoffset)
+ }
+ }
+ if err := r.WriteAt(0, cmd); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
new file mode 100644
index 0000000..34411d7
--- /dev/null
+++ b/src/cmd/link/internal/ld/main.go
@@ -0,0 +1,444 @@
+// Inferno utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "bufio"
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/quoted"
+ "cmd/internal/sys"
+ "cmd/link/internal/benchmark"
+ "flag"
+ "internal/buildcfg"
+ "log"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "strings"
+)
+
+var (
+ pkglistfornote []byte
+ windowsgui bool // writes a "GUI binary" instead of a "console binary"
+ ownTmpDir bool // set to true if tmp dir created by linker (e.g. no -tmpdir)
+)
+
+func init() {
+ flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...")
+ flag.Var(&flagExtld, "extld", "use `linker` when linking in external mode")
+ flag.Var(&flagExtldflags, "extldflags", "pass `flags` to external linker")
+}
+
+// Flags used by the linker. The exported flags are used by the architecture-specific packages.
+var (
+ flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
+
+ flagOutfile = flag.String("o", "", "write output to `file`")
+ flagPluginPath = flag.String("pluginpath", "", "full path name for plugin")
+
+ flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`")
+ flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph")
+ flagRace = flag.Bool("race", false, "enable race detector")
+ flagMsan = flag.Bool("msan", false, "enable MSan interface")
+ flagAsan = flag.Bool("asan", false, "enable ASan interface")
+ flagAslr = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows")
+
+ flagFieldTrack = flag.String("k", "", "set field tracking `symbol`")
+ flagLibGCC = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable")
+ flagTmpdir = flag.String("tmpdir", "", "use `directory` for temporary files")
+
+ flagExtld quoted.Flag
+ flagExtldflags quoted.Flag
+ flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive")
+
+ flagCaptureHostObjs = flag.String("capturehostobjs", "", "capture host object files loaded during internal linking to specified dir")
+
+ flagA = flag.Bool("a", false, "no-op (deprecated)")
+ FlagC = flag.Bool("c", false, "dump call graph")
+ FlagD = flag.Bool("d", false, "disable dynamic executable")
+ flagF = flag.Bool("f", false, "ignore version mismatch")
+ flagG = flag.Bool("g", false, "disable go package data checks")
+ flagH = flag.Bool("h", false, "halt on error")
+ flagN = flag.Bool("n", false, "dump symbol table")
+ FlagS = flag.Bool("s", false, "disable symbol table")
+ FlagW = flag.Bool("w", false, "disable DWARF generation")
+ flag8 bool // use 64-bit addresses in symbol table
+ flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
+ FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
+ FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size")
+ flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph")
+ FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
+ FlagRound = flag.Int("R", -1, "set address rounding `quantum`")
+ FlagTextAddr = flag.Int64("T", -1, "set text segment `address`")
+ flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
+ cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
+ memprofile = flag.String("memprofile", "", "write memory profile to `file`")
+ memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
+ benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
+ benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")
+)
+
+// Main is the main entry point for the linker code.
+func Main(arch *sys.Arch, theArch Arch) {
+ thearch = theArch
+ ctxt := linknew(arch)
+ ctxt.Bso = bufio.NewWriter(os.Stdout)
+
+ // For testing behavior of go command when tools crash silently.
+ // Undocumented, not in standard flag parser to avoid
+ // exposing in usage message.
+ for _, arg := range os.Args {
+ if arg == "-crash_for_testing" {
+ os.Exit(2)
+ }
+ }
+
+ if final := gorootFinal(); final == "$GOROOT" {
+ // cmd/go sets GOROOT_FINAL to the dummy value "$GOROOT" when -trimpath is set,
+ // but runtime.GOROOT() should return the empty string, not a bogus value.
+ // (See https://go.dev/issue/51461.)
+ } else {
+ addstrdata1(ctxt, "runtime.defaultGOROOT="+final)
+ }
+
+ buildVersion := buildcfg.Version
+ if goexperiment := buildcfg.Experiment.String(); goexperiment != "" {
+ buildVersion += " X:" + goexperiment
+ }
+ addstrdata1(ctxt, "runtime.buildVersion="+buildVersion)
+
+ // TODO(matloob): define these above and then check flag values here
+ if ctxt.Arch.Family == sys.AMD64 && buildcfg.GOOS == "plan9" {
+ flag.BoolVar(&flag8, "8", false, "use 64-bit addresses in symbol table")
+ }
+ flagHeadType := flag.String("H", "", "set header `type`")
+ flag.BoolVar(&ctxt.linkShared, "linkshared", false, "link against installed Go shared libraries")
+ flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
+ flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
+ flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
+ objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo)
+ objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
+ objabi.AddVersionFlag() // -V
+ objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
+ objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog)
+ objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg)
+
+ objabi.Flagparse(usage)
+
+ if ctxt.Debugvlog > 0 {
+ // dump symbol info on crash
+ defer func() { ctxt.loader.Dump() }()
+ }
+
+ switch *flagHeadType {
+ case "":
+ case "windowsgui":
+ ctxt.HeadType = objabi.Hwindows
+ windowsgui = true
+ default:
+ if err := ctxt.HeadType.Set(*flagHeadType); err != nil {
+ Errorf(nil, "%v", err)
+ usage()
+ }
+ }
+ if ctxt.HeadType == objabi.Hunknown {
+ ctxt.HeadType.Set(buildcfg.GOOS)
+ }
+
+ if !*flagAslr && ctxt.BuildMode != BuildModeCShared {
+ Errorf(nil, "-aslr=false is only allowed for -buildmode=c-shared")
+ usage()
+ }
+
+ if *FlagD && ctxt.UsesLibc() {
+ Exitf("dynamic linking required on %s; -d flag cannot be used", buildcfg.GOOS)
+ }
+
+ checkStrictDups = *FlagStrictDups
+
+ if ctxt.IsDarwin() && ctxt.BuildMode == BuildModeCShared {
+ *FlagW = true // default to -w in c-shared mode on darwin, see #61229
+ }
+
+ if !buildcfg.Experiment.RegabiWrappers {
+ abiInternalVer = 0
+ }
+
+ startProfile()
+ if ctxt.BuildMode == BuildModeUnset {
+ ctxt.BuildMode.Set("exe")
+ }
+
+ if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 {
+ usage()
+ }
+
+ if *flagOutfile == "" {
+ *flagOutfile = "a.out"
+ if ctxt.HeadType == objabi.Hwindows {
+ *flagOutfile += ".exe"
+ }
+ }
+
+ interpreter = *flagInterpreter
+
+ if *flagBuildid == "" && ctxt.Target.IsOpenbsd() {
+ // TODO(jsing): Remove once direct syscalls are no longer in use.
+ // OpenBSD 6.7 onwards will not permit direct syscalls from a
+ // dynamically linked binary unless it identifies the binary
+ // contains a .note.go.buildid ELF note. See issue #36435.
+ *flagBuildid = "go-openbsd"
+ }
+
+ // enable benchmarking
+ var bench *benchmark.Metrics
+ if len(*benchmarkFlag) != 0 {
+ if *benchmarkFlag == "mem" {
+ bench = benchmark.New(benchmark.GC, *benchmarkFileFlag)
+ } else if *benchmarkFlag == "cpu" {
+ bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag)
+ } else {
+ Errorf(nil, "unknown benchmark flag: %q", *benchmarkFlag)
+ usage()
+ }
+ }
+
+ bench.Start("libinit")
+ libinit(ctxt) // creates outfile
+ bench.Start("computeTLSOffset")
+ ctxt.computeTLSOffset()
+ bench.Start("Archinit")
+ thearch.Archinit(ctxt)
+
+ if ctxt.linkShared && !ctxt.IsELF {
+ Exitf("-linkshared can only be used on elf systems")
+ }
+
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("HEADER = -H%d -T0x%x -R0x%x\n", ctxt.HeadType, uint64(*FlagTextAddr), uint32(*FlagRound))
+ }
+
+ zerofp := goobj.FingerprintType{}
+ switch ctxt.BuildMode {
+ case BuildModeShared:
+ for i := 0; i < flag.NArg(); i++ {
+ arg := flag.Arg(i)
+ parts := strings.SplitN(arg, "=", 2)
+ var pkgpath, file string
+ if len(parts) == 1 {
+ pkgpath, file = "main", arg
+ } else {
+ pkgpath, file = parts[0], parts[1]
+ }
+ pkglistfornote = append(pkglistfornote, pkgpath...)
+ pkglistfornote = append(pkglistfornote, '\n')
+ addlibpath(ctxt, "command line", "command line", file, pkgpath, "", zerofp)
+ }
+ case BuildModePlugin:
+ addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "", zerofp)
+ default:
+ addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "", zerofp)
+ }
+ bench.Start("loadlib")
+ ctxt.loadlib()
+
+ bench.Start("deadcode")
+ deadcode(ctxt)
+
+ bench.Start("linksetup")
+ ctxt.linksetup()
+
+ bench.Start("dostrdata")
+ ctxt.dostrdata()
+ if buildcfg.Experiment.FieldTrack {
+ bench.Start("fieldtrack")
+ fieldtrack(ctxt.Arch, ctxt.loader)
+ }
+
+ bench.Start("dwarfGenerateDebugInfo")
+ dwarfGenerateDebugInfo(ctxt)
+
+ bench.Start("callgraph")
+ ctxt.callgraph()
+
+ bench.Start("doStackCheck")
+ ctxt.doStackCheck()
+
+ bench.Start("mangleTypeSym")
+ ctxt.mangleTypeSym()
+
+ if ctxt.IsELF {
+ bench.Start("doelf")
+ ctxt.doelf()
+ }
+ if ctxt.IsDarwin() {
+ bench.Start("domacho")
+ ctxt.domacho()
+ }
+ if ctxt.IsWindows() {
+ bench.Start("dope")
+ ctxt.dope()
+ bench.Start("windynrelocsyms")
+ ctxt.windynrelocsyms()
+ }
+ if ctxt.IsAIX() {
+ bench.Start("doxcoff")
+ ctxt.doxcoff()
+ }
+
+ bench.Start("textbuildid")
+ ctxt.textbuildid()
+ bench.Start("addexport")
+ ctxt.setArchSyms()
+ ctxt.addexport()
+ bench.Start("Gentext")
+ thearch.Gentext(ctxt, ctxt.loader) // trampolines, call stubs, etc.
+
+ bench.Start("textaddress")
+ ctxt.textaddress()
+ bench.Start("typelink")
+ ctxt.typelink()
+ bench.Start("buildinfo")
+ ctxt.buildinfo()
+ bench.Start("pclntab")
+ containers := ctxt.findContainerSyms()
+ pclnState := ctxt.pclntab(containers)
+ bench.Start("findfunctab")
+ ctxt.findfunctab(pclnState, containers)
+ bench.Start("dwarfGenerateDebugSyms")
+ dwarfGenerateDebugSyms(ctxt)
+ bench.Start("symtab")
+ symGroupType := ctxt.symtab(pclnState)
+ bench.Start("dodata")
+ ctxt.dodata(symGroupType)
+ bench.Start("address")
+ order := ctxt.address()
+ bench.Start("dwarfcompress")
+ dwarfcompress(ctxt)
+ bench.Start("layout")
+ filesize := ctxt.layout(order)
+
+ // Write out the output file.
+ // It is split into two parts (Asmb and Asmb2). The first
+ // part writes most of the content (sections and segments),
+ // for which we have computed the size and offset, in a
+ // mmap'd region. The second part writes more content, for
+ // which we don't know the size.
+ if ctxt.Arch.Family != sys.Wasm {
+ // Don't mmap if we're building for Wasm. Wasm file
+ // layout is very different so filesize is meaningless.
+ if err := ctxt.Out.Mmap(filesize); err != nil {
+ Exitf("mapping output file failed: %v", err)
+ }
+ }
+ // asmb will redirect symbols to the output file mmap, and relocations
+ // will be applied directly there.
+ bench.Start("Asmb")
+ asmb(ctxt)
+
+ exitIfErrors()
+
+ // Generate additional symbols for the native symbol table just prior
+ // to code generation.
+ bench.Start("GenSymsLate")
+ if thearch.GenSymsLate != nil {
+ thearch.GenSymsLate(ctxt, ctxt.loader)
+ }
+
+ bench.Start("Asmb2")
+ asmb2(ctxt)
+
+ bench.Start("Munmap")
+ ctxt.Out.Close() // Close handles Munmapping if necessary.
+
+ bench.Start("hostlink")
+ ctxt.hostlink()
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("%s", ctxt.loader.Stat())
+ ctxt.Logf("%d liveness data\n", liveness)
+ }
+ bench.Start("Flush")
+ ctxt.Bso.Flush()
+ bench.Start("archive")
+ ctxt.archive()
+ bench.Report(os.Stdout)
+
+ errorexit()
+}
+
+type Rpath struct {
+ set bool
+ val string
+}
+
+func (r *Rpath) Set(val string) error {
+ r.set = true
+ r.val = val
+ return nil
+}
+
+func (r *Rpath) String() string {
+ return r.val
+}
+
+func startProfile() {
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ log.Fatalf("%v", err)
+ }
+ if err := pprof.StartCPUProfile(f); err != nil {
+ log.Fatalf("%v", err)
+ }
+ AtExit(pprof.StopCPUProfile)
+ }
+ if *memprofile != "" {
+ if *memprofilerate != 0 {
+ runtime.MemProfileRate = int(*memprofilerate)
+ }
+ f, err := os.Create(*memprofile)
+ if err != nil {
+ log.Fatalf("%v", err)
+ }
+ AtExit(func() {
+ // Profile all outstanding allocations.
+ runtime.GC()
+ // compilebench parses the memory profile to extract memstats,
+ // which are only written in the legacy pprof format.
+ // See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap.
+ const writeLegacyFormat = 1
+ if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
+ log.Fatalf("%v", err)
+ }
+ })
+ }
+}
diff --git a/src/cmd/link/internal/ld/msync_darwin_libc.go b/src/cmd/link/internal/ld/msync_darwin_libc.go
new file mode 100644
index 0000000..eb2a526
--- /dev/null
+++ b/src/cmd/link/internal/ld/msync_darwin_libc.go
@@ -0,0 +1,12 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin && go1.20
+
+package ld
+
+import _ "unsafe" // for go:linkname
+
+//go:linkname msync syscall.msync
+func msync(b []byte, flags int) (err error)
diff --git a/src/cmd/link/internal/ld/msync_darwin_syscall.go b/src/cmd/link/internal/ld/msync_darwin_syscall.go
new file mode 100644
index 0000000..270d9f3
--- /dev/null
+++ b/src/cmd/link/internal/ld/msync_darwin_syscall.go
@@ -0,0 +1,24 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin && !go1.20
+
+package ld
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func msync(b []byte, flags int) (err error) {
+ var p unsafe.Pointer
+ if len(b) > 0 {
+ p = unsafe.Pointer(&b[0])
+ }
+ _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(p), uintptr(len(b)), uintptr(flags))
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
diff --git a/src/cmd/link/internal/ld/nooptcgolink_test.go b/src/cmd/link/internal/ld/nooptcgolink_test.go
new file mode 100644
index 0000000..646583f
--- /dev/null
+++ b/src/cmd/link/internal/ld/nooptcgolink_test.go
@@ -0,0 +1,29 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "internal/testenv"
+ "path/filepath"
+ "testing"
+)
+
+func TestNooptCgoBuild(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ t.Parallel()
+
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ dir := t.TempDir()
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out"))
+ cmd.Dir = filepath.Join(testenv.GOROOT(t), "src", "runtime", "testdata", "testprogcgo")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("go build output: %s", out)
+ t.Fatal(err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
new file mode 100644
index 0000000..54fafca
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -0,0 +1,325 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "encoding/binary"
+ "errors"
+ "log"
+ "os"
+)
+
+// If fallocate is not supported on this platform, return this error. The error
+// is ignored where needed, and OutBuf writes to heap memory.
+var errNoFallocate = errors.New("operation not supported")
+
+const outbufMode = 0775
+
+// OutBuf is a buffered file writer.
+//
+// It is similar to the Writer in cmd/internal/bio with a few small differences.
+//
+// First, it tracks the output architecture and uses it to provide
+// endian helpers.
+//
+// Second, it provides a very cheap offset counter that doesn't require
+// any system calls to read the value.
+//
+// Third, it also mmaps the output file (if available). The intended usage is:
+// - Mmap the output file
+// - Write the content
+// - possibly apply any edits in the output buffer
+// - possibly write more content to the file. These writes take place in a heap
+// backed buffer that will get synced to disk.
+// - Munmap the output file
+//
+// And finally, it provides a mechanism by which you can multithread the
+// writing of output files. This mechanism is accomplished by copying a OutBuf,
+// and using it in the thread/goroutine.
+//
+// Parallel OutBuf is intended to be used like:
+//
+// func write(out *OutBuf) {
+// var wg sync.WaitGroup
+// for i := 0; i < 10; i++ {
+// wg.Add(1)
+// view, err := out.View(start[i])
+// if err != nil {
+// // handle output
+// continue
+// }
+// go func(out *OutBuf, i int) {
+// // do output
+// wg.Done()
+// }(view, i)
+// }
+// wg.Wait()
+// }
+type OutBuf struct {
+ arch *sys.Arch
+ off int64
+
+ buf []byte // backing store of mmap'd output file
+ heap []byte // backing store for non-mmapped data
+
+ name string
+ f *os.File
+ encbuf [8]byte // temp buffer used by WriteN methods
+ isView bool // true if created from View()
+}
+
+func (out *OutBuf) Open(name string) error {
+ if out.f != nil {
+ return errors.New("cannot open more than one file")
+ }
+ f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, outbufMode)
+ if err != nil {
+ return err
+ }
+ out.off = 0
+ out.name = name
+ out.f = f
+ return nil
+}
+
+func NewOutBuf(arch *sys.Arch) *OutBuf {
+ return &OutBuf{
+ arch: arch,
+ }
+}
+
+var viewError = errors.New("output not mmapped")
+
+func (out *OutBuf) View(start uint64) (*OutBuf, error) {
+ return &OutBuf{
+ arch: out.arch,
+ name: out.name,
+ buf: out.buf,
+ heap: out.heap,
+ off: int64(start),
+ isView: true,
+ }, nil
+}
+
+var viewCloseError = errors.New("cannot Close OutBuf from View")
+
+func (out *OutBuf) Close() error {
+ if out.isView {
+ return viewCloseError
+ }
+ if out.isMmapped() {
+ out.copyHeap()
+ out.purgeSignatureCache()
+ out.munmap()
+ }
+ if out.f == nil {
+ return nil
+ }
+ if len(out.heap) != 0 {
+ if _, err := out.f.Write(out.heap); err != nil {
+ return err
+ }
+ }
+ if err := out.f.Close(); err != nil {
+ return err
+ }
+ out.f = nil
+ return nil
+}
+
+// ErrorClose closes the output file (if any).
+// It is supposed to be called only at exit on error, so it doesn't do
+// any clean up or buffer flushing, just closes the file.
+func (out *OutBuf) ErrorClose() {
+ if out.isView {
+ panic(viewCloseError)
+ }
+ if out.f == nil {
+ return
+ }
+ out.f.Close() // best effort, ignore error
+ out.f = nil
+}
+
+// isMmapped returns true if the OutBuf is mmaped.
+func (out *OutBuf) isMmapped() bool {
+ return len(out.buf) != 0
+}
+
+// Data returns the whole written OutBuf as a byte slice.
+func (out *OutBuf) Data() []byte {
+ if out.isMmapped() {
+ out.copyHeap()
+ return out.buf
+ }
+ return out.heap
+}
+
+// copyHeap copies the heap to the mmapped section of memory, returning true if
+// a copy takes place.
+func (out *OutBuf) copyHeap() bool {
+ if !out.isMmapped() { // only valuable for mmapped OutBufs.
+ return false
+ }
+ if out.isView {
+ panic("can't copyHeap a view")
+ }
+
+ bufLen := len(out.buf)
+ heapLen := len(out.heap)
+ total := uint64(bufLen + heapLen)
+ if heapLen != 0 {
+ if err := out.Mmap(total); err != nil { // Mmap will copy out.heap over to out.buf
+ Exitf("mapping output file failed: %v", err)
+ }
+ }
+ return true
+}
+
+// maxOutBufHeapLen limits the growth of the heap area.
+const maxOutBufHeapLen = 10 << 20
+
+// writeLoc determines the write location if a buffer is mmaped.
+// We maintain two write buffers, an mmapped section, and a heap section for
+// writing. When the mmapped section is full, we switch over the heap memory
+// for writing.
+func (out *OutBuf) writeLoc(lenToWrite int64) (int64, []byte) {
+ // See if we have enough space in the mmaped area.
+ bufLen := int64(len(out.buf))
+ if out.off+lenToWrite <= bufLen {
+ return out.off, out.buf
+ }
+
+ // Not enough space in the mmaped area, write to heap area instead.
+ heapPos := out.off - bufLen
+ heapLen := int64(len(out.heap))
+ lenNeeded := heapPos + lenToWrite
+ if lenNeeded > heapLen { // do we need to grow the heap storage?
+ // The heap variables aren't protected by a mutex. For now, just bomb if you
+ // try to use OutBuf in parallel. (Note this probably could be fixed.)
+ if out.isView {
+ panic("cannot write to heap in parallel")
+ }
+ // See if our heap would grow to be too large, and if so, copy it to the end
+ // of the mmapped area.
+ if heapLen > maxOutBufHeapLen && out.copyHeap() {
+ heapPos -= heapLen
+ lenNeeded = heapPos + lenToWrite
+ heapLen = 0
+ }
+ out.heap = append(out.heap, make([]byte, lenNeeded-heapLen)...)
+ }
+ return heapPos, out.heap
+}
+
+func (out *OutBuf) SeekSet(p int64) {
+ out.off = p
+}
+
+func (out *OutBuf) Offset() int64 {
+ return out.off
+}
+
+// Write writes the contents of v to the buffer.
+func (out *OutBuf) Write(v []byte) (int, error) {
+ n := len(v)
+ pos, buf := out.writeLoc(int64(n))
+ copy(buf[pos:], v)
+ out.off += int64(n)
+ return n, nil
+}
+
+func (out *OutBuf) Write8(v uint8) {
+ pos, buf := out.writeLoc(1)
+ buf[pos] = v
+ out.off++
+}
+
+// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface.
+func (out *OutBuf) WriteByte(v byte) error {
+ out.Write8(v)
+ return nil
+}
+
+func (out *OutBuf) Write16(v uint16) {
+ out.arch.ByteOrder.PutUint16(out.encbuf[:], v)
+ out.Write(out.encbuf[:2])
+}
+
+func (out *OutBuf) Write32(v uint32) {
+ out.arch.ByteOrder.PutUint32(out.encbuf[:], v)
+ out.Write(out.encbuf[:4])
+}
+
+func (out *OutBuf) Write32b(v uint32) {
+ binary.BigEndian.PutUint32(out.encbuf[:], v)
+ out.Write(out.encbuf[:4])
+}
+
+func (out *OutBuf) Write64(v uint64) {
+ out.arch.ByteOrder.PutUint64(out.encbuf[:], v)
+ out.Write(out.encbuf[:8])
+}
+
+func (out *OutBuf) Write64b(v uint64) {
+ binary.BigEndian.PutUint64(out.encbuf[:], v)
+ out.Write(out.encbuf[:8])
+}
+
+func (out *OutBuf) WriteString(s string) {
+ pos, buf := out.writeLoc(int64(len(s)))
+ n := copy(buf[pos:], s)
+ if n != len(s) {
+ log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s))
+ }
+ out.off += int64(n)
+}
+
+// WriteStringN writes the first n bytes of s.
+// If n is larger than len(s) then it is padded with zero bytes.
+func (out *OutBuf) WriteStringN(s string, n int) {
+ out.WriteStringPad(s, n, zeros[:])
+}
+
+// WriteStringPad writes the first n bytes of s.
+// If n is larger than len(s) then it is padded with the bytes in pad (repeated as needed).
+func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) {
+ if len(s) >= n {
+ out.WriteString(s[:n])
+ } else {
+ out.WriteString(s)
+ n -= len(s)
+ for n > len(pad) {
+ out.Write(pad)
+ n -= len(pad)
+
+ }
+ out.Write(pad[:n])
+ }
+}
+
+// WriteSym writes the content of a Symbol, and returns the output buffer
+// that we just wrote, so we can apply further edit to the symbol content.
+// For generator symbols, it also sets the symbol's Data to the output
+// buffer.
+func (out *OutBuf) WriteSym(ldr *loader.Loader, s loader.Sym) []byte {
+ if !ldr.IsGeneratedSym(s) {
+ P := ldr.Data(s)
+ n := int64(len(P))
+ pos, buf := out.writeLoc(n)
+ copy(buf[pos:], P)
+ out.off += n
+ ldr.FreeData(s)
+ return buf[pos : pos+n]
+ } else {
+ n := ldr.SymSize(s)
+ pos, buf := out.writeLoc(n)
+ out.off += n
+ ldr.MakeSymbolUpdater(s).SetData(buf[pos : pos+n])
+ return buf[pos : pos+n]
+ }
+}
diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go
new file mode 100644
index 0000000..17f7e2a
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_darwin.go
@@ -0,0 +1,48 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// Implemented in the syscall package.
+//
+//go:linkname fcntl syscall.fcntl
+func fcntl(fd int, cmd int, arg int) (int, error)
+
+func (out *OutBuf) fallocate(size uint64) error {
+ stat, err := out.f.Stat()
+ if err != nil {
+ return err
+ }
+ // F_PEOFPOSMODE allocates from the end of the file, so we want the size difference.
+ // Apparently, it uses the end of the allocation, instead of the logical end of the
+ // file.
+ cursize := uint64(stat.Sys().(*syscall.Stat_t).Blocks * 512) // allocated size
+ if size <= cursize {
+ return nil
+ }
+
+ store := &syscall.Fstore_t{
+ Flags: syscall.F_ALLOCATEALL,
+ Posmode: syscall.F_PEOFPOSMODE,
+ Offset: 0,
+ Length: int64(size - cursize),
+ }
+
+ _, err = fcntl(int(out.f.Fd()), syscall.F_PREALLOCATE, int(uintptr(unsafe.Pointer(store))))
+ return err
+}
+
+func (out *OutBuf) purgeSignatureCache() {
+ // Apparently, the Darwin kernel may cache the code signature at mmap.
+ // When we mmap the output buffer, it doesn't have a code signature
+ // (as we haven't generated one). Invalidate the kernel cache now that
+ // we have generated the signature. See issue #42684.
+ msync(out.buf, syscall.MS_INVALIDATE)
+ // Best effort. Ignore error.
+}
diff --git a/src/cmd/link/internal/ld/outbuf_linux.go b/src/cmd/link/internal/ld/outbuf_linux.go
new file mode 100644
index 0000000..bd9a0c6
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_linux.go
@@ -0,0 +1,11 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import "syscall"
+
+func (out *OutBuf) fallocate(size uint64) error {
+ return syscall.Fallocate(int(out.f.Fd()), 0, 0, int64(size))
+}
diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go
new file mode 100644
index 0000000..7bb728a
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_mmap.go
@@ -0,0 +1,59 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || (solaris && go1.20)
+
+package ld
+
+import (
+ "syscall"
+)
+
+// Mmap maps the output file with the given size. It unmaps the old mapping
+// if it is already mapped. It also flushes any in-heap data to the new
+// mapping.
+func (out *OutBuf) Mmap(filesize uint64) (err error) {
+ oldlen := len(out.buf)
+ if oldlen != 0 {
+ out.munmap()
+ }
+
+ for {
+ if err = out.fallocate(filesize); err != syscall.EINTR {
+ break
+ }
+ }
+ if err != nil {
+ // Some file systems do not support fallocate. We ignore that error as linking
+ // can still take place, but you might SIGBUS when you write to the mmapped
+ // area.
+ if err != syscall.ENOTSUP && err != syscall.EPERM && err != errNoFallocate {
+ return err
+ }
+ }
+ err = out.f.Truncate(int64(filesize))
+ if err != nil {
+ Exitf("resize output file failed: %v", err)
+ }
+ out.buf, err = syscall.Mmap(int(out.f.Fd()), 0, int(filesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FILE)
+ if err != nil {
+ return err
+ }
+
+ // copy heap to new mapping
+ if uint64(oldlen+len(out.heap)) > filesize {
+ panic("mmap size too small")
+ }
+ copy(out.buf[oldlen:], out.heap)
+ out.heap = out.heap[:0]
+ return nil
+}
+
+func (out *OutBuf) munmap() {
+ if out.buf == nil {
+ return
+ }
+ syscall.Munmap(out.buf)
+ out.buf = nil
+}
diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go
new file mode 100644
index 0000000..dd5afc6
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_nofallocate.go
@@ -0,0 +1,11 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !darwin && !linux
+
+package ld
+
+func (out *OutBuf) fallocate(size uint64) error {
+ return errNoFallocate
+}
diff --git a/src/cmd/link/internal/ld/outbuf_nommap.go b/src/cmd/link/internal/ld/outbuf_nommap.go
new file mode 100644
index 0000000..b1d3d27
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_nommap.go
@@ -0,0 +1,22 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !(solaris && go1.20) && !windows
+
+package ld
+
+// Mmap allocates an in-heap output buffer with the given size. It copies
+// any old data (if any) to the new buffer.
+func (out *OutBuf) Mmap(filesize uint64) error {
+ // We need space to put all the symbols before we apply relocations.
+ oldheap := out.heap
+ if filesize < uint64(len(oldheap)) {
+ panic("mmap size too small")
+ }
+ out.heap = make([]byte, filesize)
+ copy(out.heap, oldheap)
+ return nil
+}
+
+func (out *OutBuf) munmap() { panic("unreachable") }
diff --git a/src/cmd/link/internal/ld/outbuf_notdarwin.go b/src/cmd/link/internal/ld/outbuf_notdarwin.go
new file mode 100644
index 0000000..f9caa41
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_notdarwin.go
@@ -0,0 +1,10 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !darwin
+// +build !darwin
+
+package ld
+
+func (out *OutBuf) purgeSignatureCache() {}
diff --git a/src/cmd/link/internal/ld/outbuf_test.go b/src/cmd/link/internal/ld/outbuf_test.go
new file mode 100644
index 0000000..a7b105f
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_test.go
@@ -0,0 +1,93 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+// TestMMap ensures that we can actually mmap on every supported platform.
+func TestMMap(t *testing.T) {
+ switch runtime.GOOS {
+ default:
+ t.Skip("unsupported OS")
+ case "aix", "darwin", "ios", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "windows":
+ }
+ dir := t.TempDir()
+ filename := filepath.Join(dir, "foo.out")
+ ob := NewOutBuf(nil)
+ if err := ob.Open(filename); err != nil {
+ t.Fatalf("error opening file: %v", err)
+ }
+ defer ob.Close()
+ if err := ob.Mmap(1 << 20); err != nil {
+ t.Errorf("error mmapping file %v", err)
+ }
+ if !ob.isMmapped() {
+ t.Errorf("should be mmapped")
+ }
+}
+
+// TestWriteLoc ensures that the math surrounding writeLoc is correct.
+func TestWriteLoc(t *testing.T) {
+ tests := []struct {
+ bufLen int
+ off int64
+ heapLen int
+ lenToWrite int64
+ expectedHeapLen int
+ writePos int64
+ addressInHeap bool
+ }{
+ {100, 0, 0, 100, 0, 0, false},
+ {100, 100, 0, 100, 100, 0, true},
+ {10, 10, 0, 100, 100, 0, true},
+ {10, 20, 10, 100, 110, 10, true},
+ {0, 0, 0, 100, 100, 0, true},
+ }
+
+ for i, test := range tests {
+ ob := &OutBuf{
+ buf: make([]byte, test.bufLen),
+ off: test.off,
+ heap: make([]byte, test.heapLen),
+ }
+ pos, buf := ob.writeLoc(test.lenToWrite)
+ if pos != test.writePos {
+ t.Errorf("[%d] position = %d, expected %d", i, pos, test.writePos)
+ }
+ message := "mmapped area"
+ expected := ob.buf
+ if test.addressInHeap {
+ message = "heap"
+ expected = ob.heap
+ }
+ if &buf[0] != &expected[0] {
+ t.Errorf("[%d] expected position to be %q", i, message)
+ }
+ if len(ob.heap) != test.expectedHeapLen {
+ t.Errorf("[%d] expected len(ob.heap) == %d, got %d", i, test.expectedHeapLen, len(ob.heap))
+ }
+ }
+}
+
+func TestIsMmapped(t *testing.T) {
+ tests := []struct {
+ length int
+ expected bool
+ }{
+ {0, false},
+ {1, true},
+ }
+ for i, test := range tests {
+ ob := &OutBuf{buf: make([]byte, test.length)}
+ if v := ob.isMmapped(); v != test.expected {
+
+ t.Errorf("[%d] isMmapped == %t, expected %t", i, v, test.expected)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go
new file mode 100644
index 0000000..95937e7
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_windows.go
@@ -0,0 +1,79 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "internal/unsafeheader"
+ "syscall"
+ "unsafe"
+)
+
+// Mmap maps the output file with the given size. It unmaps the old mapping
+// if it is already mapped. It also flushes any in-heap data to the new
+// mapping.
+func (out *OutBuf) Mmap(filesize uint64) error {
+ oldlen := len(out.buf)
+ if oldlen != 0 {
+ out.munmap()
+ }
+
+ err := out.f.Truncate(int64(filesize))
+ if err != nil {
+ Exitf("resize output file failed: %v", err)
+ }
+
+ low, high := uint32(filesize), uint32(filesize>>32)
+ fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READWRITE, high, low, nil)
+ if err != nil {
+ return err
+ }
+ defer syscall.CloseHandle(fmap)
+
+ ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ|syscall.FILE_MAP_WRITE, 0, 0, uintptr(filesize))
+ if err != nil {
+ return err
+ }
+ bufHdr := (*unsafeheader.Slice)(unsafe.Pointer(&out.buf))
+ bufHdr.Data = unsafe.Pointer(ptr)
+ bufHdr.Len = int(filesize)
+ bufHdr.Cap = int(filesize)
+
+ // copy heap to new mapping
+ if uint64(oldlen+len(out.heap)) > filesize {
+ panic("mmap size too small")
+ }
+ copy(out.buf[oldlen:], out.heap)
+ out.heap = out.heap[:0]
+ return nil
+}
+
+func (out *OutBuf) munmap() {
+ if out.buf == nil {
+ return
+ }
+ // Apparently unmapping without flush may cause ACCESS_DENIED error
+ // (see issue 38440).
+ err := syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0)
+ if err != nil {
+ Exitf("FlushViewOfFile failed: %v", err)
+ }
+ // Issue 44817: apparently the call below may be needed (according
+ // to the Windows docs) in addition to the FlushViewOfFile call
+ // above, " ... to flush all the dirty pages plus the metadata for
+ // the file and ensure that they are physically written to disk".
+ // Windows DOC links:
+ //
+ // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-flushviewoffile
+ // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
+ err = syscall.FlushFileBuffers(syscall.Handle(out.f.Fd()))
+ if err != nil {
+ Exitf("FlushFileBuffers failed: %v", err)
+ }
+ err = syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])))
+ out.buf = nil
+ if err != nil {
+ Exitf("UnmapViewOfFile failed: %v", err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
new file mode 100644
index 0000000..34ab86c
--- /dev/null
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -0,0 +1,935 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "internal/buildcfg"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+const funcSize = 11 * 4 // funcSize is the size of the _func object in runtime/runtime2.go
+
+// pclntab holds the state needed for pclntab generation.
+type pclntab struct {
+ // The first and last functions found.
+ firstFunc, lastFunc loader.Sym
+
+ // Running total size of pclntab.
+ size int64
+
+ // runtime.pclntab's symbols
+ carrier loader.Sym
+ pclntab loader.Sym
+ pcheader loader.Sym
+ funcnametab loader.Sym
+ findfunctab loader.Sym
+ cutab loader.Sym
+ filetab loader.Sym
+ pctab loader.Sym
+
+ // The number of functions + number of TEXT sections - 1. This is such an
+ // unexpected value because platforms that have more than one TEXT section
+ // get a dummy function inserted between because the external linker can place
+ // functions in those areas. We mark those areas as not covered by the Go
+ // runtime.
+ //
+ // On most platforms this is the number of reachable functions.
+ nfunc int32
+
+ // The number of filenames in runtime.filetab.
+ nfiles uint32
+}
+
+// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
+// It is the caller's responsibility to save the symbol in state.
+func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym {
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
+ state.size += size
+ s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f)
+ ctxt.loader.SetAttrReachable(s, true)
+ ctxt.loader.SetCarrierSym(s, state.carrier)
+ ctxt.loader.SetAttrNotInSymbolTable(s, true)
+ return s
+}
+
+// makePclntab makes a pclntab object, and assembles all the compilation units
+// we'll need to write pclntab. Returns the pclntab structure, a slice of the
+// CompilationUnits we need, and a slice of the function symbols we need to
+// generate pclntab.
+func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) {
+ ldr := ctxt.loader
+ state := new(pclntab)
+
+ // Gather some basic stats and info.
+ seenCUs := make(map[*sym.CompilationUnit]struct{})
+ compUnits := []*sym.CompilationUnit{}
+ funcs := []loader.Sym{}
+
+ for _, s := range ctxt.Textp {
+ if !emitPcln(ctxt, s, container) {
+ continue
+ }
+ funcs = append(funcs, s)
+ state.nfunc++
+ if state.firstFunc == 0 {
+ state.firstFunc = s
+ }
+ state.lastFunc = s
+
+ // We need to keep track of all compilation units we see. Some symbols
+ // (eg, go.buildid, _cgoexp_, etc) won't have a compilation unit.
+ cu := ldr.SymUnit(s)
+ if _, ok := seenCUs[cu]; cu != nil && !ok {
+ seenCUs[cu] = struct{}{}
+ cu.PclnIndex = len(compUnits)
+ compUnits = append(compUnits, cu)
+ }
+ }
+ return state, compUnits, funcs
+}
+
+func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
+ // We want to generate func table entries only for the "lowest
+ // level" symbols, not containers of subsymbols.
+ return !container.Has(s)
+}
+
+func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 {
+ ldr := ctxt.loader
+ target := ctxt.Target
+ deferreturn := uint32(0)
+ lastWasmAddr := uint32(0)
+
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if target.IsWasm() && r.Type() == objabi.R_ADDR {
+ // wasm/ssa.go generates an ARESUMEPOINT just
+ // before the deferreturn call. The "PC" of
+ // the deferreturn call is stored in the
+ // R_ADDR relocation on the ARESUMEPOINT.
+ lastWasmAddr = uint32(r.Add())
+ }
+ if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) {
+ if target.IsWasm() {
+ deferreturn = lastWasmAddr - 1
+ } else {
+ // Note: the relocation target is in the call instruction, but
+ // is not necessarily the whole instruction (for instance, on
+ // x86 the relocation applies to bytes [1:5] of the 5 byte call
+ // instruction).
+ deferreturn = uint32(r.Off())
+ switch target.Arch.Family {
+ case sys.AMD64, sys.I386:
+ deferreturn--
+ case sys.ARM, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64:
+ // no change
+ case sys.S390X:
+ deferreturn -= 2
+ default:
+ panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family))
+ }
+ }
+ break // only need one
+ }
+ }
+ return deferreturn
+}
+
+// genInlTreeSym generates the InlTree sym for a function with the
+// specified FuncInfo.
+func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym {
+ ldr := ctxt.loader
+ its := ldr.CreateExtSym("", 0)
+ inlTreeSym := ldr.MakeSymbolUpdater(its)
+ // Note: the generated symbol is given a type of sym.SGOFUNC, as a
+ // signal to the symtab() phase that it needs to be grouped in with
+ // other similar symbols (gcdata, etc); the dodata() phase will
+ // eventually switch the type back to SRODATA.
+ inlTreeSym.SetType(sym.SGOFUNC)
+ ldr.SetAttrReachable(its, true)
+ ldr.SetSymAlign(its, 4) // it has 32-bit fields
+ ninl := fi.NumInlTree()
+ for i := 0; i < int(ninl); i++ {
+ call := fi.InlTree(i)
+ nameOff, ok := nameOffsets[call.Func]
+ if !ok {
+ panic("couldn't find function name offset")
+ }
+
+ inlFunc := ldr.FuncInfo(call.Func)
+ var funcID objabi.FuncID
+ startLine := int32(0)
+ if inlFunc.Valid() {
+ funcID = inlFunc.FuncID()
+ startLine = inlFunc.StartLine()
+ } else if !ctxt.linkShared {
+ // Inlined functions are always Go functions, and thus
+ // must have FuncInfo.
+ //
+ // Unfortunately, with -linkshared, the inlined
+ // function may be external symbols (from another
+ // shared library), and we don't load FuncInfo from the
+ // shared library. We will report potentially incorrect
+ // FuncID in this case. See https://go.dev/issue/55954.
+ panic(fmt.Sprintf("inlined function %s missing func info", ldr.SymName(call.Func)))
+ }
+
+ // Construct runtime.inlinedCall value.
+ const size = 16
+ inlTreeSym.SetUint8(arch, int64(i*size+0), uint8(funcID))
+ // Bytes 1-3 are unused.
+ inlTreeSym.SetUint32(arch, int64(i*size+4), uint32(nameOff))
+ inlTreeSym.SetUint32(arch, int64(i*size+8), uint32(call.ParentPC))
+ inlTreeSym.SetUint32(arch, int64(i*size+12), uint32(startLine))
+ }
+ return its
+}
+
+// makeInlSyms returns a map of loader.Sym that are created inlSyms.
+func makeInlSyms(ctxt *Link, funcs []loader.Sym, nameOffsets map[loader.Sym]uint32) map[loader.Sym]loader.Sym {
+ ldr := ctxt.loader
+ // Create the inline symbols we need.
+ inlSyms := make(map[loader.Sym]loader.Sym)
+ for _, s := range funcs {
+ if fi := ldr.FuncInfo(s); fi.Valid() {
+ fi.Preload()
+ if fi.NumInlTree() > 0 {
+ inlSyms[s] = genInlTreeSym(ctxt, ldr.SymUnit(s), fi, ctxt.Arch, nameOffsets)
+ }
+ }
+ }
+ return inlSyms
+}
+
+// generatePCHeader creates the runtime.pcheader symbol, setting it up as a
+// generator to fill in its data later.
+func (state *pclntab) generatePCHeader(ctxt *Link) {
+ ldr := ctxt.loader
+ textStartOff := int64(8 + 2*ctxt.Arch.PtrSize)
+ size := int64(8 + 8*ctxt.Arch.PtrSize)
+ writeHeader := func(ctxt *Link, s loader.Sym) {
+ header := ctxt.loader.MakeSymbolUpdater(s)
+
+ writeSymOffset := func(off int64, ws loader.Sym) int64 {
+ diff := ldr.SymValue(ws) - ldr.SymValue(s)
+ if diff <= 0 {
+ name := ldr.SymName(ws)
+ panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before %s(%x)", ldr.SymValue(s), name, ldr.SymValue(ws)))
+ }
+ return header.SetUintptr(ctxt.Arch, off, uintptr(diff))
+ }
+
+ // Write header.
+ // Keep in sync with runtime/symtab.go:pcHeader and package debug/gosym.
+ header.SetUint32(ctxt.Arch, 0, 0xfffffff1)
+ header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
+ header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
+ off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc))
+ off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles))
+ if off != textStartOff {
+ panic(fmt.Sprintf("pcHeader textStartOff: %d != %d", off, textStartOff))
+ }
+ off += int64(ctxt.Arch.PtrSize) // skip runtimeText relocation
+ off = writeSymOffset(off, state.funcnametab)
+ off = writeSymOffset(off, state.cutab)
+ off = writeSymOffset(off, state.filetab)
+ off = writeSymOffset(off, state.pctab)
+ off = writeSymOffset(off, state.pclntab)
+ if off != size {
+ panic(fmt.Sprintf("pcHeader size: %d != %d", off, size))
+ }
+ }
+
+ state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
+ // Create the runtimeText relocation.
+ sb := ldr.MakeSymbolUpdater(state.pcheader)
+ sb.SetAddr(ctxt.Arch, textStartOff, ldr.Lookup("runtime.text", 0))
+}
+
+// walkFuncs iterates over the funcs, calling a function for each unique
+// function and inlined function.
+func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) {
+ ldr := ctxt.loader
+ seen := make(map[loader.Sym]struct{})
+ for _, s := range funcs {
+ if _, ok := seen[s]; !ok {
+ f(s)
+ seen[s] = struct{}{}
+ }
+
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+ for i, ni := 0, fi.NumInlTree(); i < int(ni); i++ {
+ call := fi.InlTree(i).Func
+ if _, ok := seen[call]; !ok {
+ f(call)
+ seen[call] = struct{}{}
+ }
+ }
+ }
+}
+
+// generateFuncnametab creates the function name table. Returns a map of
+// func symbol to the name offset in runtime.funcnamtab.
+func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 {
+ nameOffsets := make(map[loader.Sym]uint32, state.nfunc)
+
+ // The name used by the runtime is the concatenation of the 3 returned strings.
+ // For regular functions, only one returned string is nonempty.
+ // For generic functions, we use three parts so that we can print everything
+ // within the outermost "[]" as "...".
+ nameParts := func(name string) (string, string, string) {
+ i := strings.IndexByte(name, '[')
+ if i < 0 {
+ return name, "", ""
+ }
+ // TODO: use LastIndexByte once the bootstrap compiler is >= Go 1.5.
+ j := len(name) - 1
+ for j > i && name[j] != ']' {
+ j--
+ }
+ if j <= i {
+ return name, "", ""
+ }
+ return name[:i], "[...]", name[j+1:]
+ }
+
+ // Write the null terminated strings.
+ writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
+ symtab := ctxt.loader.MakeSymbolUpdater(s)
+ for s, off := range nameOffsets {
+ a, b, c := nameParts(ctxt.loader.SymName(s))
+ o := int64(off)
+ o = symtab.AddStringAt(o, a)
+ o = symtab.AddStringAt(o, b)
+ _ = symtab.AddCStringAt(o, c)
+ }
+ }
+
+ // Loop through the CUs, and calculate the size needed.
+ var size int64
+ walkFuncs(ctxt, funcs, func(s loader.Sym) {
+ nameOffsets[s] = uint32(size)
+ a, b, c := nameParts(ctxt.loader.SymName(s))
+ size += int64(len(a) + len(b) + len(c) + 1) // NULL terminate
+ })
+
+ state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
+ return nameOffsets
+}
+
+// walkFilenames walks funcs, calling a function for each filename used in each
+// function's line table.
+func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) {
+ ldr := ctxt.loader
+
+ // Loop through all functions, finding the filenames we need.
+ for _, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ cu := ldr.SymUnit(s)
+ for i, nf := 0, int(fi.NumFile()); i < nf; i++ {
+ f(cu, fi.File(i))
+ }
+ for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ {
+ call := fi.InlTree(i)
+ f(cu, call.File)
+ }
+ }
+}
+
+// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice
+// of the index at which each CU begins in runtime.cutab.
+//
+// Function objects keep track of the files they reference to print the stack.
+// This function creates a per-CU list of filenames if CU[M] references
+// files[1-N], the following is generated:
+//
+// runtime.cutab:
+// CU[M]
+// offsetToFilename[0]
+// offsetToFilename[1]
+// ..
+//
+// runtime.filetab
+// filename[0]
+// filename[1]
+//
+// Looking up a filename then becomes:
+// 0. Given a func, and filename index [K]
+// 1. Get Func.CUIndex: M := func.cuOffset
+// 2. Find filename offset: fileOffset := runtime.cutab[M+K]
+// 3. Get the filename: getcstring(runtime.filetab[fileOffset])
+func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 {
+ // On a per-CU basis, keep track of all the filenames we need.
+ //
+ // Note, that we store the filenames in a separate section in the object
+ // files, and deduplicate based on the actual value. It would be better to
+ // store the filenames as symbols, using content addressable symbols (and
+ // then not loading extra filenames), and just use the hash value of the
+ // symbol name to do this cataloging.
+ //
+ // TODO: Store filenames as symbols. (Note this would be easiest if you
+ // also move strings to ALWAYS using the larger content addressable hash
+ // function, and use that hash value for uniqueness testing.)
+ cuEntries := make([]goobj.CUFileIndex, len(compUnits))
+ fileOffsets := make(map[string]uint32)
+
+ // Walk the filenames.
+ // We store the total filename string length we need to load, and the max
+ // file index we've seen per CU so we can calculate how large the
+ // CU->global table needs to be.
+ var fileSize int64
+ walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) {
+ // Note we use the raw filename for lookup, but use the expanded filename
+ // when we save the size.
+ filename := cu.FileTable[i]
+ if _, ok := fileOffsets[filename]; !ok {
+ fileOffsets[filename] = uint32(fileSize)
+ fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate
+ }
+
+ // Find the maximum file index we've seen.
+ if cuEntries[cu.PclnIndex] < i+1 {
+ cuEntries[cu.PclnIndex] = i + 1 // Store max + 1
+ }
+ })
+
+ // Calculate the size of the runtime.cutab variable.
+ var totalEntries uint32
+ cuOffsets := make([]uint32, len(cuEntries))
+ for i, entries := range cuEntries {
+ // Note, cutab is a slice of uint32, so an offset to a cu's entry is just the
+ // running total of all cu indices we've needed to store so far, not the
+ // number of bytes we've stored so far.
+ cuOffsets[i] = totalEntries
+ totalEntries += uint32(entries)
+ }
+
+ // Write cutab.
+ writeCutab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ var off int64
+ for i, max := range cuEntries {
+ // Write the per CU LUT.
+ cu := compUnits[i]
+ for j := goobj.CUFileIndex(0); j < max; j++ {
+ fileOffset, ok := fileOffsets[cu.FileTable[j]]
+ if !ok {
+ // We're looping through all possible file indices. It's possible a file's
+ // been deadcode eliminated, and although it's a valid file in the CU, it's
+ // not needed in this binary. When that happens, use an invalid offset.
+ fileOffset = ^uint32(0)
+ }
+ off = sb.SetUint32(ctxt.Arch, off, fileOffset)
+ }
+ }
+ }
+ state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab)
+
+ // Write filetab.
+ writeFiletab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ // Write the strings.
+ for filename, loc := range fileOffsets {
+ sb.AddStringAt(int64(loc), expandFile(filename))
+ }
+ }
+ state.nfiles = uint32(len(fileOffsets))
+ state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab)
+
+ return cuOffsets
+}
+
+// generatePctab creates the runtime.pctab variable, holding all the
+// deduplicated pcdata.
+func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
+ ldr := ctxt.loader
+
+ // Pctab offsets of 0 are considered invalid in the runtime. We respect
+ // that by just padding a single byte at the beginning of runtime.pctab,
+ // that way no real offsets can be zero.
+ size := int64(1)
+
+ // Walk the functions, finding offset to store each pcdata.
+ seen := make(map[loader.Sym]struct{})
+ saveOffset := func(pcSym loader.Sym) {
+ if _, ok := seen[pcSym]; !ok {
+ datSize := ldr.SymSize(pcSym)
+ if datSize != 0 {
+ ldr.SetSymValue(pcSym, size)
+ } else {
+ // Invalid PC data, record as zero.
+ ldr.SetSymValue(pcSym, 0)
+ }
+ size += datSize
+ seen[pcSym] = struct{}{}
+ }
+ }
+ var pcsp, pcline, pcfile, pcinline loader.Sym
+ var pcdata []loader.Sym
+ for _, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+ pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata)
+
+ pcSyms := []loader.Sym{pcsp, pcfile, pcline}
+ for _, pcSym := range pcSyms {
+ saveOffset(pcSym)
+ }
+ for _, pcSym := range pcdata {
+ saveOffset(pcSym)
+ }
+ if fi.NumInlTree() > 0 {
+ saveOffset(pcinline)
+ }
+ }
+
+ // TODO: There is no reason we need a generator for this variable, and it
+ // could be moved to a carrier symbol. However, carrier symbols containing
+ // carrier symbols don't work yet (as of Aug 2020). Once this is fixed,
+ // runtime.pctab could just be a carrier sym.
+ writePctab := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+ for sym := range seen {
+ sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym))
+ }
+ }
+
+ state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
+}
+
+// numPCData returns the number of PCData syms for the FuncInfo.
+// NB: Preload must be called on valid FuncInfos before calling this function.
+func numPCData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo) uint32 {
+ if !fi.Valid() {
+ return 0
+ }
+ numPCData := uint32(ldr.NumPcdata(s))
+ if fi.NumInlTree() > 0 {
+ if numPCData < objabi.PCDATA_InlTreeIndex+1 {
+ numPCData = objabi.PCDATA_InlTreeIndex + 1
+ }
+ }
+ return numPCData
+}
+
+// generateFunctab creates the runtime.functab
+//
+// runtime.functab contains two things:
+//
+// - pc->func look up table.
+// - array of func objects, interleaved with pcdata and funcdata
+func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
+ // Calculate the size of the table.
+ size, startLocations := state.calculateFunctabSize(ctxt, funcs)
+ writePcln := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+ // Write the data.
+ writePCToFunc(ctxt, sb, funcs, startLocations)
+ writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets)
+ }
+ state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln)
+}
+
+// funcData returns the funcdata and offsets for the FuncInfo.
+// The funcdata are written into runtime.functab after each func
+// object. This is a helper function to make querying the FuncInfo object
+// cleaner.
+//
+// NB: Preload must be called on the FuncInfo before calling.
+// NB: fdSyms is used as scratch space.
+func funcData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym) []loader.Sym {
+ fdSyms = fdSyms[:0]
+ if fi.Valid() {
+ fdSyms = ldr.Funcdata(s, fdSyms)
+ if fi.NumInlTree() > 0 {
+ if len(fdSyms) < objabi.FUNCDATA_InlTree+1 {
+ fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...)
+ }
+ fdSyms[objabi.FUNCDATA_InlTree] = inlSym
+ }
+ }
+ return fdSyms
+}
+
+// calculateFunctabSize calculates the size of the pclntab, and the offsets in
+// the output buffer for individual func entries.
+func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64, []uint32) {
+ ldr := ctxt.loader
+ startLocations := make([]uint32, len(funcs))
+
+ // Allocate space for the pc->func table. This structure consists of a pc offset
+ // and an offset to the func structure. After that, we have a single pc
+ // value that marks the end of the last function in the binary.
+ size := int64(int(state.nfunc)*2*4 + 4)
+
+ // Now find the space for the func objects. We do this in a running manner,
+ // so that we can find individual starting locations.
+ for i, s := range funcs {
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
+ startLocations[i] = uint32(size)
+ fi := ldr.FuncInfo(s)
+ size += funcSize
+ if fi.Valid() {
+ fi.Preload()
+ numFuncData := ldr.NumFuncdata(s)
+ if fi.NumInlTree() > 0 {
+ if numFuncData < objabi.FUNCDATA_InlTree+1 {
+ numFuncData = objabi.FUNCDATA_InlTree + 1
+ }
+ }
+ size += int64(numPCData(ldr, s, fi) * 4)
+ size += int64(numFuncData * 4)
+ }
+ }
+
+ return size, startLocations
+}
+
+// writePCToFunc writes the PC->func lookup table.
+func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32) {
+ ldr := ctxt.loader
+ textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
+ pcOff := func(s loader.Sym) uint32 {
+ off := ldr.SymValue(s) - textStart
+ if off < 0 {
+ panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
+ }
+ return uint32(off)
+ }
+ for i, s := range funcs {
+ sb.SetUint32(ctxt.Arch, int64(i*2*4), pcOff(s))
+ sb.SetUint32(ctxt.Arch, int64((i*2+1)*4), startLocations[i])
+ }
+
+ // Final entry of table is just end pc offset.
+ lastFunc := funcs[len(funcs)-1]
+ sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, pcOff(lastFunc)+uint32(ldr.SymSize(lastFunc)))
+}
+
+// writeFuncs writes the func structures and pcdata to runtime.functab.
+func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
+ ldr := ctxt.loader
+ deferReturnSym := ldr.Lookup("runtime.deferreturn", abiInternalVer)
+ gofunc := ldr.Lookup("go:func.*", 0)
+ gofuncBase := ldr.SymValue(gofunc)
+ textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
+ funcdata := []loader.Sym{}
+ var pcsp, pcfile, pcline, pcinline loader.Sym
+ var pcdata []loader.Sym
+
+ // Write the individual func objects.
+ for i, s := range funcs {
+ startLine := int32(0)
+ fi := ldr.FuncInfo(s)
+ if fi.Valid() {
+ fi.Preload()
+ pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata)
+ startLine = fi.StartLine()
+ }
+
+ off := int64(startLocations[i])
+ // entryOff uint32 (offset of func entry PC from textStart)
+ entryOff := ldr.SymValue(s) - textStart
+ if entryOff < 0 {
+ panic(fmt.Sprintf("expected func %s(%x) to be placed before or at textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
+ }
+ off = sb.SetUint32(ctxt.Arch, off, uint32(entryOff))
+
+ // nameOff int32
+ nameOff, ok := nameOffsets[s]
+ if !ok {
+ panic("couldn't find function name offset")
+ }
+ off = sb.SetUint32(ctxt.Arch, off, uint32(nameOff))
+
+ // args int32
+ // TODO: Move into funcinfo.
+ args := uint32(0)
+ if fi.Valid() {
+ args = uint32(fi.Args())
+ }
+ off = sb.SetUint32(ctxt.Arch, off, args)
+
+ // deferreturn
+ deferreturn := computeDeferReturn(ctxt, deferReturnSym, s)
+ off = sb.SetUint32(ctxt.Arch, off, deferreturn)
+
+ // pcdata
+ if fi.Valid() {
+ off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcsp)))
+ off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcfile)))
+ off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcline)))
+ } else {
+ off += 12
+ }
+ off = sb.SetUint32(ctxt.Arch, off, uint32(numPCData(ldr, s, fi)))
+
+ // Store the offset to compilation unit's file table.
+ cuIdx := ^uint32(0)
+ if cu := ldr.SymUnit(s); cu != nil {
+ cuIdx = cuOffsets[cu.PclnIndex]
+ }
+ off = sb.SetUint32(ctxt.Arch, off, cuIdx)
+
+ // startLine int32
+ off = sb.SetUint32(ctxt.Arch, off, uint32(startLine))
+
+ // funcID uint8
+ var funcID objabi.FuncID
+ if fi.Valid() {
+ funcID = fi.FuncID()
+ }
+ off = sb.SetUint8(ctxt.Arch, off, uint8(funcID))
+
+ // flag uint8
+ var flag objabi.FuncFlag
+ if fi.Valid() {
+ flag = fi.FuncFlag()
+ }
+ off = sb.SetUint8(ctxt.Arch, off, uint8(flag))
+
+ off += 1 // pad
+
+ // nfuncdata must be the final entry.
+ funcdata = funcData(ldr, s, fi, 0, funcdata)
+ off = sb.SetUint8(ctxt.Arch, off, uint8(len(funcdata)))
+
+ // Output the pcdata.
+ if fi.Valid() {
+ for j, pcSym := range pcdata {
+ sb.SetUint32(ctxt.Arch, off+int64(j*4), uint32(ldr.SymValue(pcSym)))
+ }
+ if fi.NumInlTree() > 0 {
+ sb.SetUint32(ctxt.Arch, off+objabi.PCDATA_InlTreeIndex*4, uint32(ldr.SymValue(pcinline)))
+ }
+ }
+
+ // Write funcdata refs as offsets from go:func.* and go:funcrel.*.
+ funcdata = funcData(ldr, s, fi, inlSyms[s], funcdata)
+ // Missing funcdata will be ^0. See runtime/symtab.go:funcdata.
+ off = int64(startLocations[i] + funcSize + numPCData(ldr, s, fi)*4)
+ for j := range funcdata {
+ dataoff := off + int64(4*j)
+ fdsym := funcdata[j]
+ if fdsym == 0 {
+ sb.SetUint32(ctxt.Arch, dataoff, ^uint32(0)) // ^0 is a sentinel for "no value"
+ continue
+ }
+
+ if outer := ldr.OuterSym(fdsym); outer != gofunc {
+ panic(fmt.Sprintf("bad carrier sym for symbol %s (funcdata %s#%d), want go:func.* got %s", ldr.SymName(fdsym), ldr.SymName(s), j, ldr.SymName(outer)))
+ }
+ sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)-gofuncBase))
+ }
+ }
+}
+
+// pclntab initializes the pclntab symbol with
+// runtime function and file name information.
+
+// pclntab generates the pcln table for the link output.
+func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
+ // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
+ // layout and data has changed since that time.
+ //
+ // As of August 2020, here's the layout of pclntab:
+ //
+ // .gopclntab/__gopclntab [elf/macho section]
+ // runtime.pclntab
+ // Carrier symbol for the entire pclntab section.
+ //
+ // runtime.pcheader (see: runtime/symtab.go:pcHeader)
+ // 8-byte magic
+ // nfunc [thearch.ptrsize bytes]
+ // offset to runtime.funcnametab from the beginning of runtime.pcheader
+ // offset to runtime.pclntab_old from beginning of runtime.pcheader
+ //
+ // runtime.funcnametab
+ // []list of null terminated function names
+ //
+ // runtime.cutab
+ // for i=0..#CUs
+ // for j=0..#max used file index in CU[i]
+ // uint32 offset into runtime.filetab for the filename[j]
+ //
+ // runtime.filetab
+ // []null terminated filename strings
+ //
+ // runtime.pctab
+ // []byte of deduplicated pc data.
+ //
+ // runtime.functab
+ // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
+ // end PC [thearch.ptrsize bytes]
+ // func structures, pcdata offsets, func data.
+
+ state, compUnits, funcs := makePclntab(ctxt, container)
+
+ ldr := ctxt.loader
+ state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
+ ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
+ ldr.SetAttrReachable(state.carrier, true)
+ setCarrierSym(sym.SPCLNTAB, state.carrier)
+
+ state.generatePCHeader(ctxt)
+ nameOffsets := state.generateFuncnametab(ctxt, funcs)
+ cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs)
+ state.generatePctab(ctxt, funcs)
+ inlSyms := makeInlSyms(ctxt, funcs, nameOffsets)
+ state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets)
+
+ return state
+}
+
+func gorootFinal() string {
+ root := buildcfg.GOROOT
+ if final := os.Getenv("GOROOT_FINAL"); final != "" {
+ root = final
+ }
+ return root
+}
+
+func expandGoroot(s string) string {
+ const n = len("$GOROOT")
+ if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
+ if final := gorootFinal(); final != "" {
+ return filepath.ToSlash(filepath.Join(final, s[n:]))
+ }
+ }
+ return s
+}
+
+const (
+ BUCKETSIZE = 256 * MINFUNC
+ SUBBUCKETS = 16
+ SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
+ NOIDX = 0x7fffffff
+)
+
+// findfunctab generates a lookup table to quickly find the containing
+// function for a pc. See src/runtime/symtab.go:findfunc for details.
+func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) {
+ ldr := ctxt.loader
+
+ // find min and max address
+ min := ldr.SymValue(ctxt.Textp[0])
+ lastp := ctxt.Textp[len(ctxt.Textp)-1]
+ max := ldr.SymValue(lastp) + ldr.SymSize(lastp)
+
+ // for each subbucket, compute the minimum of all symbol indexes
+ // that map to that subbucket.
+ n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
+
+ nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
+
+ size := 4*int64(nbuckets) + int64(n)
+
+ writeFindFuncTab := func(_ *Link, s loader.Sym) {
+ t := ldr.MakeSymbolUpdater(s)
+
+ indexes := make([]int32, n)
+ for i := int32(0); i < n; i++ {
+ indexes[i] = NOIDX
+ }
+ idx := int32(0)
+ for i, s := range ctxt.Textp {
+ if !emitPcln(ctxt, s, container) {
+ continue
+ }
+ p := ldr.SymValue(s)
+ var e loader.Sym
+ i++
+ if i < len(ctxt.Textp) {
+ e = ctxt.Textp[i]
+ }
+ for e != 0 && !emitPcln(ctxt, e, container) && i < len(ctxt.Textp) {
+ e = ctxt.Textp[i]
+ i++
+ }
+ q := max
+ if e != 0 {
+ q = ldr.SymValue(e)
+ }
+
+ //print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
+ for ; p < q; p += SUBBUCKETSIZE {
+ i = int((p - min) / SUBBUCKETSIZE)
+ if indexes[i] > idx {
+ indexes[i] = idx
+ }
+ }
+
+ i = int((q - 1 - min) / SUBBUCKETSIZE)
+ if indexes[i] > idx {
+ indexes[i] = idx
+ }
+ idx++
+ }
+
+ // fill in table
+ for i := int32(0); i < nbuckets; i++ {
+ base := indexes[i*SUBBUCKETS]
+ if base == NOIDX {
+ Errorf(nil, "hole in findfunctab")
+ }
+ t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base))
+ for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
+ idx = indexes[i*SUBBUCKETS+j]
+ if idx == NOIDX {
+ Errorf(nil, "hole in findfunctab")
+ }
+ if idx-base >= 256 {
+ Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
+ }
+
+ t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
+ }
+ }
+ }
+
+ state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab)
+ ldr.SetAttrReachable(state.findfunctab, true)
+ ldr.SetAttrLocal(state.findfunctab, true)
+}
+
+// findContainerSyms returns a bitmap, indexed by symbol number, where there's
+// a 1 for every container symbol.
+func (ctxt *Link) findContainerSyms() loader.Bitmap {
+ ldr := ctxt.loader
+ container := loader.MakeBitmap(ldr.NSym())
+ // Find container symbols and mark them as such.
+ for _, s := range ctxt.Textp {
+ outer := ldr.OuterSym(s)
+ if outer != 0 {
+ container.Set(outer)
+ }
+ }
+ return container
+}
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
new file mode 100644
index 0000000..0e29131
--- /dev/null
+++ b/src/cmd/link/internal/ld/pe.go
@@ -0,0 +1,1709 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// PE (Portable Executable) file writing
+// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/pe"
+ "encoding/binary"
+ "fmt"
+ "internal/buildcfg"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+type IMAGE_IMPORT_DESCRIPTOR struct {
+ OriginalFirstThunk uint32
+ TimeDateStamp uint32
+ ForwarderChain uint32
+ Name uint32
+ FirstThunk uint32
+}
+
+type IMAGE_EXPORT_DIRECTORY struct {
+ Characteristics uint32
+ TimeDateStamp uint32
+ MajorVersion uint16
+ MinorVersion uint16
+ Name uint32
+ Base uint32
+ NumberOfFunctions uint32
+ NumberOfNames uint32
+ AddressOfFunctions uint32
+ AddressOfNames uint32
+ AddressOfNameOrdinals uint32
+}
+
+var (
+ // PEBASE is the base address for the executable.
+ // It is small for 32-bit and large for 64-bit.
+ PEBASE int64
+
+ // SectionAlignment must be greater than or equal to FileAlignment.
+ // The default is the page size for the architecture.
+ PESECTALIGN int64 = 0x1000
+
+ // FileAlignment should be a power of 2 between 512 and 64 K, inclusive.
+ // The default is 512. If the SectionAlignment is less than
+ // the architecture's page size, then FileAlignment must match SectionAlignment.
+ PEFILEALIGN int64 = 2 << 8
+)
+
+const (
+ IMAGE_SCN_CNT_CODE = 0x00000020
+ IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
+ IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
+ IMAGE_SCN_LNK_OTHER = 0x00000100
+ IMAGE_SCN_LNK_INFO = 0x00000200
+ IMAGE_SCN_LNK_REMOVE = 0x00000800
+ IMAGE_SCN_LNK_COMDAT = 0x00001000
+ IMAGE_SCN_GPREL = 0x00008000
+ IMAGE_SCN_MEM_PURGEABLE = 0x00020000
+ IMAGE_SCN_MEM_16BIT = 0x00020000
+ IMAGE_SCN_MEM_LOCKED = 0x00040000
+ IMAGE_SCN_MEM_PRELOAD = 0x00080000
+ IMAGE_SCN_ALIGN_1BYTES = 0x00100000
+ IMAGE_SCN_ALIGN_2BYTES = 0x00200000
+ IMAGE_SCN_ALIGN_4BYTES = 0x00300000
+ IMAGE_SCN_ALIGN_8BYTES = 0x00400000
+ IMAGE_SCN_ALIGN_16BYTES = 0x00500000
+ IMAGE_SCN_ALIGN_32BYTES = 0x00600000
+ IMAGE_SCN_ALIGN_64BYTES = 0x00700000
+ IMAGE_SCN_ALIGN_128BYTES = 0x00800000
+ IMAGE_SCN_ALIGN_256BYTES = 0x00900000
+ IMAGE_SCN_ALIGN_512BYTES = 0x00A00000
+ IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000
+ IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000
+ IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000
+ IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000
+ IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000
+ IMAGE_SCN_MEM_DISCARDABLE = 0x02000000
+ IMAGE_SCN_MEM_NOT_CACHED = 0x04000000
+ IMAGE_SCN_MEM_NOT_PAGED = 0x08000000
+ IMAGE_SCN_MEM_SHARED = 0x10000000
+ IMAGE_SCN_MEM_EXECUTE = 0x20000000
+ IMAGE_SCN_MEM_READ = 0x40000000
+ IMAGE_SCN_MEM_WRITE = 0x80000000
+)
+
+// See https://docs.microsoft.com/en-us/windows/win32/debug/pe-format.
+// TODO(crawshaw): add these constants to debug/pe.
+const (
+ // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2
+ IMAGE_SYM_TYPE_NULL = 0
+ IMAGE_SYM_TYPE_STRUCT = 8
+ IMAGE_SYM_DTYPE_FUNCTION = 0x20
+ IMAGE_SYM_DTYPE_ARRAY = 0x30
+ IMAGE_SYM_CLASS_EXTERNAL = 2
+ IMAGE_SYM_CLASS_STATIC = 3
+
+ IMAGE_REL_I386_DIR32 = 0x0006
+ IMAGE_REL_I386_SECREL = 0x000B
+ IMAGE_REL_I386_REL32 = 0x0014
+
+ IMAGE_REL_AMD64_ADDR64 = 0x0001
+ IMAGE_REL_AMD64_ADDR32 = 0x0002
+ IMAGE_REL_AMD64_REL32 = 0x0004
+ IMAGE_REL_AMD64_SECREL = 0x000B
+
+ IMAGE_REL_ARM_ABSOLUTE = 0x0000
+ IMAGE_REL_ARM_ADDR32 = 0x0001
+ IMAGE_REL_ARM_ADDR32NB = 0x0002
+ IMAGE_REL_ARM_BRANCH24 = 0x0003
+ IMAGE_REL_ARM_BRANCH11 = 0x0004
+ IMAGE_REL_ARM_SECREL = 0x000F
+
+ IMAGE_REL_ARM64_ABSOLUTE = 0x0000
+ IMAGE_REL_ARM64_ADDR32 = 0x0001
+ IMAGE_REL_ARM64_ADDR32NB = 0x0002
+ IMAGE_REL_ARM64_BRANCH26 = 0x0003
+ IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004
+ IMAGE_REL_ARM64_REL21 = 0x0005
+ IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006
+ IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007
+ IMAGE_REL_ARM64_SECREL = 0x0008
+ IMAGE_REL_ARM64_SECREL_LOW12A = 0x0009
+ IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A
+ IMAGE_REL_ARM64_SECREL_LOW12L = 0x000B
+ IMAGE_REL_ARM64_TOKEN = 0x000C
+ IMAGE_REL_ARM64_SECTION = 0x000D
+ IMAGE_REL_ARM64_ADDR64 = 0x000E
+ IMAGE_REL_ARM64_BRANCH19 = 0x000F
+ IMAGE_REL_ARM64_BRANCH14 = 0x0010
+ IMAGE_REL_ARM64_REL32 = 0x0011
+
+ IMAGE_REL_BASED_HIGHLOW = 3
+ IMAGE_REL_BASED_DIR64 = 10
+)
+
+const (
+ PeMinimumTargetMajorVersion = 6
+ PeMinimumTargetMinorVersion = 1
+)
+
+// DOS stub that prints out
+// "This program cannot be run in DOS mode."
+var dosstub = []uint8{
+ 0x4d,
+ 0x5a,
+ 0x90,
+ 0x00,
+ 0x03,
+ 0x00,
+ 0x04,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0xff,
+ 0xff,
+ 0x00,
+ 0x00,
+ 0x8b,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x40,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x80,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x0e,
+ 0x1f,
+ 0xba,
+ 0x0e,
+ 0x00,
+ 0xb4,
+ 0x09,
+ 0xcd,
+ 0x21,
+ 0xb8,
+ 0x01,
+ 0x4c,
+ 0xcd,
+ 0x21,
+ 0x54,
+ 0x68,
+ 0x69,
+ 0x73,
+ 0x20,
+ 0x70,
+ 0x72,
+ 0x6f,
+ 0x67,
+ 0x72,
+ 0x61,
+ 0x6d,
+ 0x20,
+ 0x63,
+ 0x61,
+ 0x6e,
+ 0x6e,
+ 0x6f,
+ 0x74,
+ 0x20,
+ 0x62,
+ 0x65,
+ 0x20,
+ 0x72,
+ 0x75,
+ 0x6e,
+ 0x20,
+ 0x69,
+ 0x6e,
+ 0x20,
+ 0x44,
+ 0x4f,
+ 0x53,
+ 0x20,
+ 0x6d,
+ 0x6f,
+ 0x64,
+ 0x65,
+ 0x2e,
+ 0x0d,
+ 0x0d,
+ 0x0a,
+ 0x24,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+}
+
+type Imp struct {
+ s loader.Sym
+ off uint64
+ next *Imp
+ argsize int
+}
+
+type Dll struct {
+ name string
+ nameoff uint64
+ thunkoff uint64
+ ms *Imp
+ next *Dll
+}
+
+var (
+ rsrcsyms []loader.Sym
+ PESECTHEADR int32
+ PEFILEHEADR int32
+ pe64 int
+ dr *Dll
+
+ dexport = make([]loader.Sym, 0, 1024)
+)
+
+// peStringTable is a COFF string table.
+type peStringTable struct {
+ strings []string
+ stringsLen int
+}
+
+// size returns size of string table t.
+func (t *peStringTable) size() int {
+ // string table starts with 4-byte length at the beginning
+ return t.stringsLen + 4
+}
+
+// add adds string str to string table t.
+func (t *peStringTable) add(str string) int {
+ off := t.size()
+ t.strings = append(t.strings, str)
+ t.stringsLen += len(str) + 1 // each string will have 0 appended to it
+ return off
+}
+
+// write writes string table t into the output file.
+func (t *peStringTable) write(out *OutBuf) {
+ out.Write32(uint32(t.size()))
+ for _, s := range t.strings {
+ out.WriteString(s)
+ out.Write8(0)
+ }
+}
+
+// peSection represents section from COFF section table.
+type peSection struct {
+ name string
+ shortName string
+ index int // one-based index into the Section Table
+ virtualSize uint32
+ virtualAddress uint32
+ sizeOfRawData uint32
+ pointerToRawData uint32
+ pointerToRelocations uint32
+ numberOfRelocations uint16
+ characteristics uint32
+}
+
+// checkOffset verifies COFF section sect offset in the file.
+func (sect *peSection) checkOffset(off int64) {
+ if off != int64(sect.pointerToRawData) {
+ Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off))
+ errorexit()
+ }
+}
+
+// checkSegment verifies COFF section sect matches address
+// and file offset provided in segment seg.
+func (sect *peSection) checkSegment(seg *sym.Segment) {
+ if seg.Vaddr-uint64(PEBASE) != uint64(sect.virtualAddress) {
+ Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-uint64(PEBASE))))
+ errorexit()
+ }
+ if seg.Fileoff != uint64(sect.pointerToRawData) {
+ Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff)))
+ errorexit()
+ }
+}
+
+// pad adds zeros to the section sect. It writes as many bytes
+// as necessary to make section sect.SizeOfRawData bytes long.
+// It assumes that n bytes are already written to the file.
+func (sect *peSection) pad(out *OutBuf, n uint32) {
+ out.WriteStringN("", int(sect.sizeOfRawData-n))
+}
+
+// write writes COFF section sect into the output file.
+func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error {
+ h := pe.SectionHeader32{
+ VirtualSize: sect.virtualSize,
+ SizeOfRawData: sect.sizeOfRawData,
+ PointerToRawData: sect.pointerToRawData,
+ PointerToRelocations: sect.pointerToRelocations,
+ NumberOfRelocations: sect.numberOfRelocations,
+ Characteristics: sect.characteristics,
+ }
+ if linkmode != LinkExternal {
+ h.VirtualAddress = sect.virtualAddress
+ }
+ copy(h.Name[:], sect.shortName)
+ return binary.Write(out, binary.LittleEndian, h)
+}
+
+// emitRelocations emits the relocation entries for the sect.
+// The actual relocations are emitted by relocfn.
+// This updates the corresponding PE section table entry
+// with the relocation offset and count.
+func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) {
+ sect.pointerToRelocations = uint32(out.Offset())
+ // first entry: extended relocs
+ out.Write32(0) // placeholder for number of relocation + 1
+ out.Write32(0)
+ out.Write16(0)
+
+ n := relocfn() + 1
+
+ cpos := out.Offset()
+ out.SeekSet(int64(sect.pointerToRelocations))
+ out.Write32(uint32(n))
+ out.SeekSet(cpos)
+ if n > 0x10000 {
+ n = 0x10000
+ sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL
+ } else {
+ sect.pointerToRelocations += 10 // skip the extend reloc entry
+ }
+ sect.numberOfRelocations = uint16(n - 1)
+}
+
+// peFile is used to build COFF file.
+type peFile struct {
+ sections []*peSection
+ stringTable peStringTable
+ textSect *peSection
+ rdataSect *peSection
+ dataSect *peSection
+ bssSect *peSection
+ ctorsSect *peSection
+ nextSectOffset uint32
+ nextFileOffset uint32
+ symtabOffset int64 // offset to the start of symbol table
+ symbolCount int // number of symbol table records written
+ dataDirectory [16]pe.DataDirectory
+}
+
+// addSection adds section to the COFF file f.
+func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection {
+ sect := &peSection{
+ name: name,
+ shortName: name,
+ index: len(f.sections) + 1,
+ virtualAddress: f.nextSectOffset,
+ pointerToRawData: f.nextFileOffset,
+ }
+ f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN))
+ if filesize > 0 {
+ sect.virtualSize = uint32(sectsize)
+ sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN))
+ f.nextFileOffset += sect.sizeOfRawData
+ } else {
+ sect.sizeOfRawData = uint32(sectsize)
+ }
+ f.sections = append(f.sections, sect)
+ return sect
+}
+
+// addDWARFSection adds DWARF section to the COFF file f.
+// This function is similar to addSection, but DWARF section names are
+// longer than 8 characters, so they need to be stored in the string table.
+func (f *peFile) addDWARFSection(name string, size int) *peSection {
+ if size == 0 {
+ Exitf("DWARF section %q is empty", name)
+ }
+ // DWARF section names are longer than 8 characters.
+ // PE format requires such names to be stored in string table,
+ // and section names replaced with slash (/) followed by
+ // correspondent string table index.
+ // see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx
+ // for details
+ off := f.stringTable.add(name)
+ h := f.addSection(name, size, size)
+ h.shortName = fmt.Sprintf("/%d", off)
+ h.characteristics = IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA
+ return h
+}
+
+// addDWARF adds DWARF information to the COFF file f.
+func (f *peFile) addDWARF() {
+ if *FlagS { // disable symbol table
+ return
+ }
+ if *FlagW { // disable dwarf
+ return
+ }
+ for _, sect := range Segdwarf.Sections {
+ h := f.addDWARFSection(sect.Name, int(sect.Length))
+ fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff
+ if uint64(h.pointerToRawData) != fileoff {
+ Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff)
+ }
+ }
+}
+
+// addInitArray adds .ctors COFF section to the file f.
+func (f *peFile) addInitArray(ctxt *Link) *peSection {
+ // The size below was determined by the specification for array relocations,
+ // and by observing what GCC writes here. If the initarray section grows to
+ // contain more than one constructor entry, the size will need to be 8 * constructor_count.
+ // However, the entire Go runtime is initialized from just one function, so it is unlikely
+ // that this will need to grow in the future.
+ var size int
+ var alignment uint32
+ switch buildcfg.GOARCH {
+ default:
+ Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", buildcfg.GOARCH)
+ case "386", "arm":
+ size = 4
+ alignment = IMAGE_SCN_ALIGN_4BYTES
+ case "amd64", "arm64":
+ size = 8
+ alignment = IMAGE_SCN_ALIGN_8BYTES
+ }
+ sect := f.addSection(".ctors", size, size)
+ sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | alignment
+ sect.sizeOfRawData = uint32(size)
+ ctxt.Out.SeekSet(int64(sect.pointerToRawData))
+ sect.checkOffset(ctxt.Out.Offset())
+
+ init_entry := ctxt.loader.Lookup(*flagEntrySymbol, 0)
+ addr := uint64(ctxt.loader.SymValue(init_entry)) - ctxt.loader.SymSect(init_entry).Vaddr
+ switch buildcfg.GOARCH {
+ case "386", "arm":
+ ctxt.Out.Write32(uint32(addr))
+ case "amd64", "arm64":
+ ctxt.Out.Write64(addr)
+ }
+ return sect
+}
+
+// emitRelocations emits relocation entries for go.o in external linking.
+func (f *peFile) emitRelocations(ctxt *Link) {
+ for ctxt.Out.Offset()&7 != 0 {
+ ctxt.Out.Write8(0)
+ }
+
+ ldr := ctxt.loader
+
+ // relocsect relocates symbols from first in section sect, and returns
+ // the total number of relocations emitted.
+ relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) int {
+ // If main section has no bits, nothing to relocate.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return 0
+ }
+ sect.Reloff = uint64(ctxt.Out.Offset())
+ for i, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if uint64(ldr.SymValue(s)) >= sect.Vaddr {
+ syms = syms[i:]
+ break
+ }
+ }
+ eaddr := int64(sect.Vaddr + sect.Length)
+ for _, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymValue(s) >= eaddr {
+ break
+ }
+ // Compute external relocations on the go, and pass to PEreloc1
+ // to stream out.
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rr, ok := extreloc(ctxt, ldr, s, r)
+ if !ok {
+ continue
+ }
+ if rr.Xsym == 0 {
+ ctxt.Errorf(s, "missing xsym in relocation")
+ continue
+ }
+ if ldr.SymDynid(rr.Xsym) < 0 {
+ ctxt.Errorf(s, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()))
+ }
+ if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) {
+ ctxt.Errorf(s, "unsupported obj reloc %v/%d to %s", r.Type(), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ }
+ }
+ sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
+ const relocLen = 4 + 4 + 2
+ return int(sect.Rellen / relocLen)
+ }
+
+ sects := []struct {
+ peSect *peSection
+ seg *sym.Segment
+ syms []loader.Sym
+ }{
+ {f.textSect, &Segtext, ctxt.Textp},
+ {f.rdataSect, &Segrodata, ctxt.datap},
+ {f.dataSect, &Segdata, ctxt.datap},
+ }
+ for _, s := range sects {
+ s.peSect.emitRelocations(ctxt.Out, func() int {
+ var n int
+ for _, sect := range s.seg.Sections {
+ n += relocsect(sect, s.syms, s.seg.Vaddr)
+ }
+ return n
+ })
+ }
+
+dwarfLoop:
+ for i := 0; i < len(Segdwarf.Sections); i++ {
+ sect := Segdwarf.Sections[i]
+ si := dwarfp[i]
+ if si.secSym() != loader.Sym(sect.Sym) ||
+ ldr.SymSect(si.secSym()) != sect {
+ panic("inconsistency between dwarfp and Segdwarf")
+ }
+ for _, pesect := range f.sections {
+ if sect.Name == pesect.name {
+ pesect.emitRelocations(ctxt.Out, func() int {
+ return relocsect(sect, si.syms, sect.Vaddr)
+ })
+ continue dwarfLoop
+ }
+ }
+ Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
+ }
+
+ if f.ctorsSect == nil {
+ return
+ }
+
+ f.ctorsSect.emitRelocations(ctxt.Out, func() int {
+ dottext := ldr.Lookup(".text", 0)
+ ctxt.Out.Write32(0)
+ ctxt.Out.Write32(uint32(ldr.SymDynid(dottext)))
+ switch buildcfg.GOARCH {
+ default:
+ ctxt.Errorf(dottext, "unknown architecture for PE: %q\n", buildcfg.GOARCH)
+ case "386":
+ ctxt.Out.Write16(IMAGE_REL_I386_DIR32)
+ case "amd64":
+ ctxt.Out.Write16(IMAGE_REL_AMD64_ADDR64)
+ case "arm":
+ ctxt.Out.Write16(IMAGE_REL_ARM_ADDR32)
+ case "arm64":
+ ctxt.Out.Write16(IMAGE_REL_ARM64_ADDR64)
+ }
+ return 1
+ })
+}
+
+// writeSymbol appends symbol s to file f symbol table.
+// It also sets s.Dynid to written symbol number.
+func (f *peFile) writeSymbol(out *OutBuf, ldr *loader.Loader, s loader.Sym, name string, value int64, sectidx int, typ uint16, class uint8) {
+ if len(name) > 8 {
+ out.Write32(0)
+ out.Write32(uint32(f.stringTable.add(name)))
+ } else {
+ out.WriteStringN(name, 8)
+ }
+ out.Write32(uint32(value))
+ out.Write16(uint16(sectidx))
+ out.Write16(typ)
+ out.Write8(class)
+ out.Write8(0) // no aux entries
+
+ ldr.SetSymDynid(s, int32(f.symbolCount))
+
+ f.symbolCount++
+}
+
+// mapToPESection searches peFile f for s symbol's location.
+// It returns PE section index, and offset within that section.
+func (f *peFile) mapToPESection(ldr *loader.Loader, s loader.Sym, linkmode LinkMode) (pesectidx int, offset int64, err error) {
+ sect := ldr.SymSect(s)
+ if sect == nil {
+ return 0, 0, fmt.Errorf("could not map %s symbol with no section", ldr.SymName(s))
+ }
+ if sect.Seg == &Segtext {
+ return f.textSect.index, int64(uint64(ldr.SymValue(s)) - Segtext.Vaddr), nil
+ }
+ if sect.Seg == &Segrodata {
+ return f.rdataSect.index, int64(uint64(ldr.SymValue(s)) - Segrodata.Vaddr), nil
+ }
+ if sect.Seg != &Segdata {
+ return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", ldr.SymName(s))
+ }
+ v := uint64(ldr.SymValue(s)) - Segdata.Vaddr
+ if linkmode != LinkExternal {
+ return f.dataSect.index, int64(v), nil
+ }
+ if ldr.SymType(s) == sym.SDATA {
+ return f.dataSect.index, int64(v), nil
+ }
+ // Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section
+ // it still belongs to the .data section, not the .bss section.
+ if v < Segdata.Filelen {
+ return f.dataSect.index, int64(v), nil
+ }
+ return f.bssSect.index, int64(v - Segdata.Filelen), nil
+}
+
+var isLabel = make(map[loader.Sym]bool)
+
+func AddPELabelSym(ldr *loader.Loader, s loader.Sym) {
+ isLabel[s] = true
+}
+
+// writeSymbols writes all COFF symbol table records.
+func (f *peFile) writeSymbols(ctxt *Link) {
+ ldr := ctxt.loader
+ addsym := func(s loader.Sym) {
+ t := ldr.SymType(s)
+ if ldr.SymSect(s) == nil && t != sym.SDYNIMPORT && t != sym.SHOSTOBJ && t != sym.SUNDEFEXT {
+ return
+ }
+
+ name := ldr.SymName(s)
+
+ // Only windows/386 requires underscore prefix on external symbols.
+ if ctxt.Is386() && ctxt.IsExternal() &&
+ (t == sym.SHOSTOBJ || t == sym.SUNDEFEXT || ldr.AttrCgoExport(s) ||
+ // TODO(cuonglm): remove this hack
+ //
+ // Previously, windows/386 requires underscore prefix on external symbols,
+ // but that's only applied for SHOSTOBJ/SUNDEFEXT or cgo export symbols.
+ // "go.buildid" is STEXT, "type.*" is STYPE, thus they are not prefixed
+ // with underscore.
+ //
+ // In external linking mode, the external linker can't resolve them as
+ // external symbols. But we are lucky that they have "." in their name,
+ // so the external linker see them as Forwarder RVA exports. See:
+ //
+ // - https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table
+ // - https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=ld/pe-dll.c;h=e7b82ba6ffadf74dc1b9ee71dc13d48336941e51;hb=HEAD#l972)
+ //
+ // CL 317917 changes "." to ":" in symbols name, so theses symbols can not be
+ // found by external linker anymore. So a hacky way is adding the
+ // underscore prefix for these 2 symbols. I don't have enough knowledge to
+ // verify whether adding the underscore for all STEXT/STYPE symbols are
+ // fine, even if it could be, that would be done in future CL.
+ name == "go:buildid" || name == "type:*") {
+ name = "_" + name
+ }
+
+ name = mangleABIName(ctxt, ldr, s, name)
+
+ var peSymType uint16
+ if ctxt.IsExternal() {
+ peSymType = IMAGE_SYM_TYPE_NULL
+ } else {
+ // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308
+ // peSymType = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT
+ peSymType = 0x0308 // "array of structs"
+ }
+ sect, value, err := f.mapToPESection(ldr, s, ctxt.LinkMode)
+ if err != nil {
+ if t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT {
+ peSymType = IMAGE_SYM_DTYPE_FUNCTION
+ } else {
+ ctxt.Errorf(s, "addpesym: %v", err)
+ }
+ }
+ class := IMAGE_SYM_CLASS_EXTERNAL
+ if ldr.IsFileLocal(s) || ldr.AttrVisibilityHidden(s) || ldr.AttrLocal(s) {
+ class = IMAGE_SYM_CLASS_STATIC
+ }
+ f.writeSymbol(ctxt.Out, ldr, s, name, value, sect, peSymType, uint8(class))
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ // Include section symbols as external, because
+ // .ctors and .debug_* section relocations refer to it.
+ for _, pesect := range f.sections {
+ s := ldr.LookupOrCreateSym(pesect.name, 0)
+ f.writeSymbol(ctxt.Out, ldr, s, pesect.name, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC)
+ }
+ }
+
+ // Add special runtime.text and runtime.etext symbols.
+ s := ldr.Lookup("runtime.text", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ addsym(s)
+ }
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ addsym(s)
+ }
+
+ // Add text symbols.
+ for _, s := range ctxt.Textp {
+ addsym(s)
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym) bool {
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ name := ldr.SymName(s) // TODO: try not to read the name
+ if name == "" || name[0] == '.' {
+ return false
+ }
+ return true
+ }
+
+ // Add data symbols and external references.
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ t := ldr.SymType(s)
+ if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata
+ if t == sym.STLSBSS {
+ continue
+ }
+ if !shouldBeInSymbolTable(s) {
+ continue
+ }
+ addsym(s)
+ }
+
+ switch t {
+ case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT:
+ addsym(s)
+ default:
+ if len(isLabel) > 0 && isLabel[s] {
+ addsym(s)
+ }
+ }
+ }
+}
+
+// writeSymbolTableAndStringTable writes out symbol and string tables for peFile f.
+func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) {
+ f.symtabOffset = ctxt.Out.Offset()
+
+ // write COFF symbol table
+ if !*FlagS || ctxt.LinkMode == LinkExternal {
+ f.writeSymbols(ctxt)
+ }
+
+ // update COFF file header and section table
+ size := f.stringTable.size() + 18*f.symbolCount
+ var h *peSection
+ if ctxt.LinkMode != LinkExternal {
+ // We do not really need .symtab for go.o, and if we have one, ld
+ // will also include it in the exe, and that will confuse windows.
+ h = f.addSection(".symtab", size, size)
+ h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
+ h.checkOffset(f.symtabOffset)
+ }
+
+ // write COFF string table
+ f.stringTable.write(ctxt.Out)
+ if ctxt.LinkMode != LinkExternal {
+ h.pad(ctxt.Out, uint32(size))
+ }
+}
+
+// writeFileHeader writes COFF file header for peFile f.
+func (f *peFile) writeFileHeader(ctxt *Link) {
+ var fh pe.FileHeader
+
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
+ case sys.AMD64:
+ fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64
+ case sys.I386:
+ fh.Machine = pe.IMAGE_FILE_MACHINE_I386
+ case sys.ARM:
+ fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT
+ case sys.ARM64:
+ fh.Machine = pe.IMAGE_FILE_MACHINE_ARM64
+ }
+
+ fh.NumberOfSections = uint16(len(f.sections))
+
+ // Being able to produce identical output for identical input is
+ // much more beneficial than having build timestamp in the header.
+ fh.TimeDateStamp = 0
+
+ if ctxt.LinkMode == LinkExternal {
+ fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED
+ } else {
+ fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED
+ switch ctxt.Arch.Family {
+ case sys.AMD64, sys.I386:
+ if ctxt.BuildMode != BuildModePIE {
+ fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED
+ }
+ }
+ }
+ if pe64 != 0 {
+ var oh64 pe.OptionalHeader64
+ fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64))
+ fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE
+ } else {
+ var oh pe.OptionalHeader32
+ fh.SizeOfOptionalHeader = uint16(binary.Size(&oh))
+ fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE
+ }
+
+ fh.PointerToSymbolTable = uint32(f.symtabOffset)
+ fh.NumberOfSymbols = uint32(f.symbolCount)
+
+ binary.Write(ctxt.Out, binary.LittleEndian, &fh)
+}
+
+// writeOptionalHeader writes COFF optional header for peFile f.
+func (f *peFile) writeOptionalHeader(ctxt *Link) {
+ var oh pe.OptionalHeader32
+ var oh64 pe.OptionalHeader64
+
+ if pe64 != 0 {
+ oh64.Magic = 0x20b // PE32+
+ } else {
+ oh.Magic = 0x10b // PE32
+ oh.BaseOfData = f.dataSect.virtualAddress
+ }
+
+ // Fill out both oh64 and oh. We only use one. Oh well.
+ oh64.MajorLinkerVersion = 3
+ oh.MajorLinkerVersion = 3
+ oh64.MinorLinkerVersion = 0
+ oh.MinorLinkerVersion = 0
+ oh64.SizeOfCode = f.textSect.sizeOfRawData
+ oh.SizeOfCode = f.textSect.sizeOfRawData
+ oh64.SizeOfInitializedData = f.dataSect.sizeOfRawData
+ oh.SizeOfInitializedData = f.dataSect.sizeOfRawData
+ oh64.SizeOfUninitializedData = 0
+ oh.SizeOfUninitializedData = 0
+ if ctxt.LinkMode != LinkExternal {
+ oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
+ oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
+ }
+ oh64.BaseOfCode = f.textSect.virtualAddress
+ oh.BaseOfCode = f.textSect.virtualAddress
+ oh64.ImageBase = uint64(PEBASE)
+ oh.ImageBase = uint32(PEBASE)
+ oh64.SectionAlignment = uint32(PESECTALIGN)
+ oh.SectionAlignment = uint32(PESECTALIGN)
+ oh64.FileAlignment = uint32(PEFILEALIGN)
+ oh.FileAlignment = uint32(PEFILEALIGN)
+ oh64.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion
+ oh.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion
+ oh64.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion
+ oh.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion
+ oh64.MajorImageVersion = 1
+ oh.MajorImageVersion = 1
+ oh64.MinorImageVersion = 0
+ oh.MinorImageVersion = 0
+ oh64.MajorSubsystemVersion = PeMinimumTargetMajorVersion
+ oh.MajorSubsystemVersion = PeMinimumTargetMajorVersion
+ oh64.MinorSubsystemVersion = PeMinimumTargetMinorVersion
+ oh.MinorSubsystemVersion = PeMinimumTargetMinorVersion
+ oh64.SizeOfImage = f.nextSectOffset
+ oh.SizeOfImage = f.nextSectOffset
+ oh64.SizeOfHeaders = uint32(PEFILEHEADR)
+ oh.SizeOfHeaders = uint32(PEFILEHEADR)
+ if windowsgui {
+ oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
+ oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
+ } else {
+ oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
+ oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
+ }
+
+ // Mark as having awareness of terminal services, to avoid ancient compatibility hacks.
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
+ oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
+
+ // Enable DEP
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
+ oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
+
+ // The DLL can be relocated at load time.
+ if needPEBaseReloc(ctxt) {
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+ oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+ }
+
+ // Image can handle a high entropy 64-bit virtual address space.
+ if ctxt.BuildMode == BuildModePIE {
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
+ }
+
+ // Disable stack growth as we don't want Windows to
+ // fiddle with the thread stack limits, which we set
+ // ourselves to circumvent the stack checks in the
+ // Windows exception dispatcher.
+ // Commit size must be strictly less than reserve
+ // size otherwise reserve will be rounded up to a
+ // larger size, as verified with VMMap.
+
+ // On 64-bit, we always reserve 2MB stacks. "Pure" Go code is
+ // okay with much smaller stacks, but the syscall package
+ // makes it easy to call into arbitrary C code without cgo,
+ // and system calls even in "pure" Go code are actually C
+ // calls that may need more stack than we think.
+ //
+ // The default stack reserve size directly affects only the main
+ // thread.
+ //
+ // For other threads, the runtime explicitly asks the kernel
+ // to use the default stack size so that all stacks are
+ // consistent.
+ //
+ // At thread start, in minit, the runtime queries the OS for
+ // the actual stack bounds so that the stack size doesn't need
+ // to be hard-coded into the runtime.
+ oh64.SizeOfStackReserve = 0x00200000
+ if !iscgo {
+ oh64.SizeOfStackCommit = 0x00001000
+ } else {
+ // TODO(brainman): Maybe remove optional header writing altogether for cgo.
+ // For cgo it is the external linker that is building final executable.
+ // And it probably does not use any information stored in optional header.
+ oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages
+ }
+
+ oh.SizeOfStackReserve = 0x00100000
+ if !iscgo {
+ oh.SizeOfStackCommit = 0x00001000
+ } else {
+ oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages
+ }
+
+ oh64.SizeOfHeapReserve = 0x00100000
+ oh.SizeOfHeapReserve = 0x00100000
+ oh64.SizeOfHeapCommit = 0x00001000
+ oh.SizeOfHeapCommit = 0x00001000
+ oh64.NumberOfRvaAndSizes = 16
+ oh.NumberOfRvaAndSizes = 16
+
+ if pe64 != 0 {
+ oh64.DataDirectory = f.dataDirectory
+ } else {
+ oh.DataDirectory = f.dataDirectory
+ }
+
+ if pe64 != 0 {
+ binary.Write(ctxt.Out, binary.LittleEndian, &oh64)
+ } else {
+ binary.Write(ctxt.Out, binary.LittleEndian, &oh)
+ }
+}
+
+var pefile peFile
+
+func Peinit(ctxt *Link) {
+ var l int
+
+ if ctxt.Arch.PtrSize == 8 {
+ // 64-bit architectures
+ pe64 = 1
+ PEBASE = 1 << 32
+ if ctxt.Arch.Family == sys.AMD64 {
+ // TODO(rsc): For cgo we currently use 32-bit relocations
+ // that fail when PEBASE is too large.
+ // We need to fix this, but for now, use a smaller PEBASE.
+ PEBASE = 1 << 22
+ }
+ var oh64 pe.OptionalHeader64
+ l = binary.Size(&oh64)
+ } else {
+ // 32-bit architectures
+ PEBASE = 1 << 22
+ var oh pe.OptionalHeader32
+ l = binary.Size(&oh)
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ // .rdata section will contain "masks" and "shifts" symbols, and they
+ // need to be aligned to 16-bytes. So make all sections aligned
+ // to 32-byte and mark them all IMAGE_SCN_ALIGN_32BYTES so external
+ // linker will honour that requirement.
+ PESECTALIGN = 32
+ PEFILEALIGN = 0
+ // We are creating an object file. The absolute address is irrelevant.
+ PEBASE = 0
+ }
+
+ var sh [16]pe.SectionHeader32
+ var fh pe.FileHeader
+ PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN))
+ if ctxt.LinkMode != LinkExternal {
+ PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN))
+ } else {
+ PESECTHEADR = 0
+ }
+ pefile.nextSectOffset = uint32(PESECTHEADR)
+ pefile.nextFileOffset = uint32(PEFILEHEADR)
+
+ if ctxt.LinkMode == LinkInternal {
+ // some mingw libs depend on this symbol, for example, FindPESectionByName
+ for _, name := range [2]string{"__image_base__", "_image_base__"} {
+ sb := ctxt.loader.CreateSymForUpdate(name, 0)
+ sb.SetType(sym.SDATA)
+ sb.SetValue(PEBASE)
+ ctxt.loader.SetAttrSpecial(sb.Sym(), true)
+ ctxt.loader.SetAttrLocal(sb.Sym(), true)
+ }
+ }
+
+ HEADR = PEFILEHEADR
+ if *FlagTextAddr == -1 {
+ *FlagTextAddr = PEBASE + int64(PESECTHEADR)
+ }
+ if *FlagRound == -1 {
+ *FlagRound = int(PESECTALIGN)
+ }
+}
+
+func pewrite(ctxt *Link) {
+ ctxt.Out.SeekSet(0)
+ if ctxt.LinkMode != LinkExternal {
+ ctxt.Out.Write(dosstub)
+ ctxt.Out.WriteStringN("PE", 4)
+ }
+
+ pefile.writeFileHeader(ctxt)
+
+ pefile.writeOptionalHeader(ctxt)
+
+ for _, sect := range pefile.sections {
+ sect.write(ctxt.Out, ctxt.LinkMode)
+ }
+}
+
+func strput(out *OutBuf, s string) {
+ out.WriteString(s)
+ out.Write8(0)
+ // string must be padded to even size
+ if (len(s)+1)%2 != 0 {
+ out.Write8(0)
+ }
+}
+
+func initdynimport(ctxt *Link) *Dll {
+ ldr := ctxt.loader
+ var d *Dll
+
+ dr = nil
+ var m *Imp
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT {
+ continue
+ }
+ dynlib := ldr.SymDynimplib(s)
+ for d = dr; d != nil; d = d.next {
+ if d.name == dynlib {
+ m = new(Imp)
+ break
+ }
+ }
+
+ if d == nil {
+ d = new(Dll)
+ d.name = dynlib
+ d.next = dr
+ dr = d
+ m = new(Imp)
+ }
+
+ // Because external link requires properly stdcall decorated name,
+ // all external symbols in runtime use %n to denote that the number
+ // of uinptrs this function consumes. Store the argsize and discard
+ // the %n suffix if any.
+ m.argsize = -1
+ extName := ldr.SymExtname(s)
+ if i := strings.IndexByte(extName, '%'); i >= 0 {
+ var err error
+ m.argsize, err = strconv.Atoi(extName[i+1:])
+ if err != nil {
+ ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err)
+ }
+ m.argsize *= ctxt.Arch.PtrSize
+ ldr.SetSymExtname(s, extName[:i])
+ }
+
+ m.s = s
+ m.next = d.ms
+ d.ms = m
+ }
+
+ if ctxt.IsExternal() {
+ // Add real symbol name
+ for d := dr; d != nil; d = d.next {
+ for m = d.ms; m != nil; m = m.next {
+ sb := ldr.MakeSymbolUpdater(m.s)
+ sb.SetType(sym.SDATA)
+ sb.Grow(int64(ctxt.Arch.PtrSize))
+ dynName := sb.Extname()
+ // only windows/386 requires stdcall decoration
+ if ctxt.Is386() && m.argsize >= 0 {
+ dynName += fmt.Sprintf("@%d", m.argsize)
+ }
+ dynSym := ldr.CreateSymForUpdate(dynName, 0)
+ dynSym.SetType(sym.SHOSTOBJ)
+ r, _ := sb.AddRel(objabi.R_ADDR)
+ r.SetSym(dynSym.Sym())
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ }
+ }
+ } else {
+ dynamic := ldr.CreateSymForUpdate(".windynamic", 0)
+ dynamic.SetType(sym.SWINDOWS)
+ for d := dr; d != nil; d = d.next {
+ for m = d.ms; m != nil; m = m.next {
+ sb := ldr.MakeSymbolUpdater(m.s)
+ sb.SetType(sym.SWINDOWS)
+ sb.SetValue(dynamic.Size())
+ dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize))
+ dynamic.AddInteriorSym(m.s)
+ }
+
+ dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize))
+ }
+ }
+
+ return dr
+}
+
+// peimporteddlls returns the gcc command line argument to link all imported
+// DLLs.
+func peimporteddlls() []string {
+ var dlls []string
+
+ for d := dr; d != nil; d = d.next {
+ dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll"))
+ }
+
+ return dlls
+}
+
+func addimports(ctxt *Link, datsect *peSection) {
+ ldr := ctxt.loader
+ startoff := ctxt.Out.Offset()
+ dynamic := ldr.LookupOrCreateSym(".windynamic", 0)
+
+ // skip import descriptor table (will write it later)
+ n := uint64(0)
+
+ for d := dr; d != nil; d = d.next {
+ n++
+ }
+ ctxt.Out.SeekSet(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1))
+
+ // write dll names
+ for d := dr; d != nil; d = d.next {
+ d.nameoff = uint64(ctxt.Out.Offset()) - uint64(startoff)
+ strput(ctxt.Out, d.name)
+ }
+
+ // write function names
+ for d := dr; d != nil; d = d.next {
+ for m := d.ms; m != nil; m = m.next {
+ m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff)
+ ctxt.Out.Write16(0) // hint
+ strput(ctxt.Out, ldr.SymExtname(m.s))
+ }
+ }
+
+ // write OriginalFirstThunks
+ oftbase := uint64(ctxt.Out.Offset()) - uint64(startoff)
+
+ n = uint64(ctxt.Out.Offset())
+ for d := dr; d != nil; d = d.next {
+ d.thunkoff = uint64(ctxt.Out.Offset()) - n
+ for m := d.ms; m != nil; m = m.next {
+ if pe64 != 0 {
+ ctxt.Out.Write64(m.off)
+ } else {
+ ctxt.Out.Write32(uint32(m.off))
+ }
+ }
+
+ if pe64 != 0 {
+ ctxt.Out.Write64(0)
+ } else {
+ ctxt.Out.Write32(0)
+ }
+ }
+
+ // add pe section and pad it at the end
+ n = uint64(ctxt.Out.Offset()) - uint64(startoff)
+
+ isect := pefile.addSection(".idata", int(n), int(n))
+ isect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
+ isect.checkOffset(startoff)
+ isect.pad(ctxt.Out, uint32(n))
+ endoff := ctxt.Out.Offset()
+
+ // write FirstThunks (allocated in .data section)
+ ftbase := uint64(ldr.SymValue(dynamic)) - uint64(datsect.virtualAddress) - uint64(PEBASE)
+
+ ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase))
+ for d := dr; d != nil; d = d.next {
+ for m := d.ms; m != nil; m = m.next {
+ if pe64 != 0 {
+ ctxt.Out.Write64(m.off)
+ } else {
+ ctxt.Out.Write32(uint32(m.off))
+ }
+ }
+
+ if pe64 != 0 {
+ ctxt.Out.Write64(0)
+ } else {
+ ctxt.Out.Write32(0)
+ }
+ }
+
+ // finally write import descriptor table
+ out := ctxt.Out
+ out.SeekSet(startoff)
+
+ for d := dr; d != nil; d = d.next {
+ out.Write32(uint32(uint64(isect.virtualAddress) + oftbase + d.thunkoff))
+ out.Write32(0)
+ out.Write32(0)
+ out.Write32(uint32(uint64(isect.virtualAddress) + d.nameoff))
+ out.Write32(uint32(uint64(datsect.virtualAddress) + ftbase + d.thunkoff))
+ }
+
+ out.Write32(0) //end
+ out.Write32(0)
+ out.Write32(0)
+ out.Write32(0)
+ out.Write32(0)
+
+ // update data directory
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(ldr.SymValue(dynamic) - PEBASE)
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(ldr.SymSize(dynamic))
+
+ out.SeekSet(endoff)
+}
+
+func initdynexport(ctxt *Link) {
+ ldr := ctxt.loader
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) {
+ continue
+ }
+ if len(dexport)+1 > cap(dexport) {
+ ctxt.Errorf(s, "pe dynexport table is full")
+ errorexit()
+ }
+
+ dexport = append(dexport, s)
+ }
+
+ sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) })
+}
+
+func addexports(ctxt *Link) {
+ ldr := ctxt.loader
+ var e IMAGE_EXPORT_DIRECTORY
+
+ nexport := len(dexport)
+ size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1
+ for _, s := range dexport {
+ size += len(ldr.SymExtname(s)) + 1
+ }
+
+ if nexport == 0 {
+ return
+ }
+
+ sect := pefile.addSection(".edata", size, size)
+ sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+ sect.checkOffset(ctxt.Out.Offset())
+ va := int(sect.virtualAddress)
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va)
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize
+
+ vaName := va + binary.Size(&e) + nexport*4
+ vaAddr := va + binary.Size(&e)
+ vaNa := va + binary.Size(&e) + nexport*8
+
+ e.Characteristics = 0
+ e.MajorVersion = 0
+ e.MinorVersion = 0
+ e.NumberOfFunctions = uint32(nexport)
+ e.NumberOfNames = uint32(nexport)
+ e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names.
+ e.Base = 1
+ e.AddressOfFunctions = uint32(vaAddr)
+ e.AddressOfNames = uint32(vaName)
+ e.AddressOfNameOrdinals = uint32(vaNa)
+
+ out := ctxt.Out
+
+ // put IMAGE_EXPORT_DIRECTORY
+ binary.Write(out, binary.LittleEndian, &e)
+
+ // put EXPORT Address Table
+ for _, s := range dexport {
+ out.Write32(uint32(ldr.SymValue(s) - PEBASE))
+ }
+
+ // put EXPORT Name Pointer Table
+ v := int(e.Name + uint32(len(*flagOutfile)) + 1)
+
+ for _, s := range dexport {
+ out.Write32(uint32(v))
+ v += len(ldr.SymExtname(s)) + 1
+ }
+
+ // put EXPORT Ordinal Table
+ for i := 0; i < nexport; i++ {
+ out.Write16(uint16(i))
+ }
+
+ // put Names
+ out.WriteStringN(*flagOutfile, len(*flagOutfile)+1)
+
+ for _, s := range dexport {
+ name := ldr.SymExtname(s)
+ out.WriteStringN(name, len(name)+1)
+ }
+ sect.pad(out, uint32(size))
+}
+
+// peBaseRelocEntry represents a single relocation entry.
+type peBaseRelocEntry struct {
+ typeOff uint16
+}
+
+// peBaseRelocBlock represents a Base Relocation Block. A block
+// is a collection of relocation entries in a page, where each
+// entry describes a single relocation.
+// The block page RVA (Relative Virtual Address) is the index
+// into peBaseRelocTable.blocks.
+type peBaseRelocBlock struct {
+ entries []peBaseRelocEntry
+}
+
+// pePages is a type used to store the list of pages for which there
+// are base relocation blocks. This is defined as a type so that
+// it can be sorted.
+type pePages []uint32
+
+func (p pePages) Len() int { return len(p) }
+func (p pePages) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p pePages) Less(i, j int) bool { return p[i] < p[j] }
+
+// A PE base relocation table is a list of blocks, where each block
+// contains relocation information for a single page. The blocks
+// must be emitted in order of page virtual address.
+// See https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-reloc-section-image-only
+type peBaseRelocTable struct {
+ blocks map[uint32]peBaseRelocBlock
+
+ // pePages is a list of keys into blocks map.
+ // It is stored separately for ease of sorting.
+ pages pePages
+}
+
+func (rt *peBaseRelocTable) init(ctxt *Link) {
+ rt.blocks = make(map[uint32]peBaseRelocBlock)
+}
+
+func (rt *peBaseRelocTable) addentry(ldr *loader.Loader, s loader.Sym, r *loader.Reloc) {
+ // pageSize is the size in bytes of a page
+ // described by a base relocation block.
+ const pageSize = 0x1000
+ const pageMask = pageSize - 1
+
+ addr := ldr.SymValue(s) + int64(r.Off()) - int64(PEBASE)
+ page := uint32(addr &^ pageMask)
+ off := uint32(addr & pageMask)
+
+ b, ok := rt.blocks[page]
+ if !ok {
+ rt.pages = append(rt.pages, page)
+ }
+
+ e := peBaseRelocEntry{
+ typeOff: uint16(off & 0xFFF),
+ }
+
+ // Set entry type
+ switch r.Siz() {
+ default:
+ Exitf("unsupported relocation size %d\n", r.Siz)
+ case 4:
+ e.typeOff |= uint16(IMAGE_REL_BASED_HIGHLOW << 12)
+ case 8:
+ e.typeOff |= uint16(IMAGE_REL_BASED_DIR64 << 12)
+ }
+
+ b.entries = append(b.entries, e)
+ rt.blocks[page] = b
+}
+
+func (rt *peBaseRelocTable) write(ctxt *Link) {
+ out := ctxt.Out
+
+ // sort the pages array
+ sort.Sort(rt.pages)
+
+ for _, p := range rt.pages {
+ b := rt.blocks[p]
+ const sizeOfPEbaseRelocBlock = 8 // 2 * sizeof(uint32)
+ blockSize := uint32(sizeOfPEbaseRelocBlock + len(b.entries)*2)
+ out.Write32(p)
+ out.Write32(blockSize)
+
+ for _, e := range b.entries {
+ out.Write16(e.typeOff)
+ }
+ }
+}
+
+func addPEBaseRelocSym(ldr *loader.Loader, s loader.Sym, rt *peBaseRelocTable) {
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Type() >= objabi.ElfRelocOffset {
+ continue
+ }
+ if r.Siz() == 0 { // informational relocation
+ continue
+ }
+ if r.Type() == objabi.R_DWARFFILEREF {
+ continue
+ }
+ rs := r.Sym()
+ if rs == 0 {
+ continue
+ }
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+
+ switch r.Type() {
+ default:
+ case objabi.R_ADDR:
+ rt.addentry(ldr, s, &r)
+ }
+ }
+}
+
+func needPEBaseReloc(ctxt *Link) bool {
+ // Non-PIE x86 binaries don't need the base relocation table.
+ // Everyone else does.
+ if (ctxt.Arch.Family == sys.I386 || ctxt.Arch.Family == sys.AMD64) && ctxt.BuildMode != BuildModePIE {
+ return false
+ }
+ return true
+}
+
+func addPEBaseReloc(ctxt *Link) {
+ if !needPEBaseReloc(ctxt) {
+ return
+ }
+
+ var rt peBaseRelocTable
+ rt.init(ctxt)
+
+ // Get relocation information
+ ldr := ctxt.loader
+ for _, s := range ctxt.Textp {
+ addPEBaseRelocSym(ldr, s, &rt)
+ }
+ for _, s := range ctxt.datap {
+ addPEBaseRelocSym(ldr, s, &rt)
+ }
+
+ // Write relocation information
+ startoff := ctxt.Out.Offset()
+ rt.write(ctxt)
+ size := ctxt.Out.Offset() - startoff
+
+ // Add a PE section and pad it at the end
+ rsect := pefile.addSection(".reloc", int(size), int(size))
+ rsect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
+ rsect.checkOffset(startoff)
+ rsect.pad(ctxt.Out, uint32(size))
+
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize
+}
+
+func (ctxt *Link) dope() {
+ initdynimport(ctxt)
+ initdynexport(ctxt)
+}
+
+func setpersrc(ctxt *Link, syms []loader.Sym) {
+ if len(rsrcsyms) != 0 {
+ Errorf(nil, "too many .rsrc sections")
+ }
+ rsrcsyms = syms
+}
+
+func addpersrc(ctxt *Link) {
+ if len(rsrcsyms) == 0 {
+ return
+ }
+
+ var size int64
+ for _, rsrcsym := range rsrcsyms {
+ size += ctxt.loader.SymSize(rsrcsym)
+ }
+ h := pefile.addSection(".rsrc", int(size), int(size))
+ h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA
+ h.checkOffset(ctxt.Out.Offset())
+
+ for _, rsrcsym := range rsrcsyms {
+ // A split resource happens when the actual resource data and its relocations are
+ // split across multiple sections, denoted by a $01 or $02 at the end of the .rsrc
+ // section name.
+ splitResources := strings.Contains(ctxt.loader.SymName(rsrcsym), ".rsrc$")
+ relocs := ctxt.loader.Relocs(rsrcsym)
+ data := ctxt.loader.Data(rsrcsym)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ p := data[r.Off():]
+ val := uint32(int64(h.virtualAddress) + r.Add())
+ if splitResources {
+ // If we're a split resource section, and that section has relocation
+ // symbols, then the data that it points to doesn't actually begin at
+ // the virtual address listed in this current section, but rather
+ // begins at the section immediately after this one. So, in order to
+ // calculate the proper virtual address of the data it's pointing to,
+ // we have to add the length of this section to the virtual address.
+ // This works because .rsrc sections are divided into two (but not more)
+ // of these sections.
+ val += uint32(len(data))
+ }
+ binary.LittleEndian.PutUint32(p, val)
+ }
+ ctxt.Out.Write(data)
+ }
+ h.pad(ctxt.Out, uint32(size))
+
+ // update data directory
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize
+}
+
+func asmbPe(ctxt *Link) {
+ t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length))
+ t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
+ if ctxt.LinkMode == LinkExternal {
+ // some data symbols (e.g. masks) end up in the .text section, and they normally
+ // expect larger alignment requirement than the default text section alignment.
+ t.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+ }
+ t.checkSegment(&Segtext)
+ pefile.textSect = t
+
+ ro := pefile.addSection(".rdata", int(Segrodata.Length), int(Segrodata.Length))
+ ro.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+ if ctxt.LinkMode == LinkExternal {
+ // some data symbols (e.g. masks) end up in the .rdata section, and they normally
+ // expect larger alignment requirement than the default text section alignment.
+ ro.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+ }
+ ro.checkSegment(&Segrodata)
+ pefile.rdataSect = ro
+
+ var d *peSection
+ if ctxt.LinkMode != LinkExternal {
+ d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen))
+ d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
+ d.checkSegment(&Segdata)
+ pefile.dataSect = d
+ } else {
+ d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen))
+ d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
+ d.checkSegment(&Segdata)
+ pefile.dataSect = d
+
+ b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0)
+ b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
+ b.pointerToRawData = 0
+ pefile.bssSect = b
+ }
+
+ pefile.addDWARF()
+
+ if ctxt.LinkMode == LinkExternal {
+ pefile.ctorsSect = pefile.addInitArray(ctxt)
+ }
+
+ ctxt.Out.SeekSet(int64(pefile.nextFileOffset))
+ if ctxt.LinkMode != LinkExternal {
+ addimports(ctxt, d)
+ addexports(ctxt)
+ addPEBaseReloc(ctxt)
+ }
+ pefile.writeSymbolTableAndStringTable(ctxt)
+ addpersrc(ctxt)
+ if ctxt.LinkMode == LinkExternal {
+ pefile.emitRelocations(ctxt)
+ }
+
+ pewrite(ctxt)
+}
diff --git a/src/cmd/link/internal/ld/stackcheck.go b/src/cmd/link/internal/ld/stackcheck.go
new file mode 100644
index 0000000..c82dafe
--- /dev/null
+++ b/src/cmd/link/internal/ld/stackcheck.go
@@ -0,0 +1,421 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "fmt"
+ "internal/buildcfg"
+ "sort"
+ "strings"
+)
+
+type stackCheck struct {
+ ctxt *Link
+ ldr *loader.Loader
+ morestack loader.Sym
+ callSize int // The number of bytes added by a CALL
+
+ // height records the maximum number of bytes a function and
+ // its callees can add to the stack without a split check.
+ height map[loader.Sym]int16
+
+ // graph records the out-edges from each symbol. This is only
+ // populated on a second pass if the first pass reveals an
+ // over-limit function.
+ graph map[loader.Sym][]stackCheckEdge
+}
+
+type stackCheckEdge struct {
+ growth int // Stack growth in bytes at call to target
+ target loader.Sym // 0 for stack growth without a call
+}
+
+// stackCheckCycle is a sentinel stored in the height map to detect if
+// we've found a cycle. This is effectively an "infinite" stack
+// height, so we use the closest value to infinity that we can.
+const stackCheckCycle int16 = 1<<15 - 1
+
+// stackCheckIndirect is a sentinel Sym value used to represent the
+// target of an indirect/closure call.
+const stackCheckIndirect loader.Sym = -1
+
+// doStackCheck walks the call tree to check that there is always
+// enough stack space for call frames, especially for a chain of
+// nosplit functions.
+//
+// It walks all functions to accumulate the number of bytes they can
+// grow the stack by without a split check and checks this against the
+// limit.
+func (ctxt *Link) doStackCheck() {
+ sc := newStackCheck(ctxt, false)
+
+ // limit is number of bytes a splittable function ensures are
+ // available on the stack. If any call chain exceeds this
+ // depth, the stack check test fails.
+ //
+ // The call to morestack in every splittable function ensures
+ // that there are at least StackLimit bytes available below SP
+ // when morestack returns.
+ limit := objabi.StackLimit(*flagRace) - sc.callSize
+ if buildcfg.GOARCH == "arm64" {
+ // Need an extra 8 bytes below SP to save FP.
+ limit -= 8
+ }
+
+ // Compute stack heights without any back-tracking information.
+ // This will almost certainly succeed and we can simply
+ // return. If it fails, we do a second pass with back-tracking
+ // to produce a good error message.
+ //
+ // This accumulates stack heights bottom-up so it only has to
+ // visit every function once.
+ var failed []loader.Sym
+ for _, s := range ctxt.Textp {
+ if sc.check(s) > limit {
+ failed = append(failed, s)
+ }
+ }
+
+ if len(failed) > 0 {
+ // Something was over-limit, so now we do the more
+ // expensive work to report a good error. First, for
+ // the over-limit functions, redo the stack check but
+ // record the graph this time.
+ sc = newStackCheck(ctxt, true)
+ for _, s := range failed {
+ sc.check(s)
+ }
+
+ // Find the roots of the graph (functions that are not
+ // called by any other function).
+ roots := sc.findRoots()
+
+ // Find and report all paths that go over the limit.
+ // This accumulates stack depths top-down. This is
+ // much less efficient because we may have to visit
+ // the same function multiple times at different
+ // depths, but lets us find all paths.
+ for _, root := range roots {
+ ctxt.Errorf(root, "nosplit stack over %d byte limit", limit)
+ chain := []stackCheckChain{{stackCheckEdge{0, root}, false}}
+ sc.report(root, limit, &chain)
+ }
+ }
+}
+
+func newStackCheck(ctxt *Link, graph bool) *stackCheck {
+ sc := &stackCheck{
+ ctxt: ctxt,
+ ldr: ctxt.loader,
+ morestack: ctxt.loader.Lookup("runtime.morestack", 0),
+ height: make(map[loader.Sym]int16, len(ctxt.Textp)),
+ }
+ // Compute stack effect of a CALL operation. 0 on LR machines.
+ // 1 register pushed on non-LR machines.
+ if !ctxt.Arch.HasLR {
+ sc.callSize = ctxt.Arch.RegSize
+ }
+
+ if graph {
+ // We're going to record the call graph.
+ sc.graph = make(map[loader.Sym][]stackCheckEdge)
+ }
+
+ return sc
+}
+
+func (sc *stackCheck) symName(sym loader.Sym) string {
+ switch sym {
+ case stackCheckIndirect:
+ return "indirect"
+ case 0:
+ return "leaf"
+ }
+ return fmt.Sprintf("%s<%d>", sc.ldr.SymName(sym), sc.ldr.SymVersion(sym))
+}
+
+// check returns the stack height of sym. It populates sc.height and
+// sc.graph for sym and every function in its call tree.
+func (sc *stackCheck) check(sym loader.Sym) int {
+ if h, ok := sc.height[sym]; ok {
+ // We've already visited this symbol or we're in a cycle.
+ return int(h)
+ }
+ // Store the sentinel so we can detect cycles.
+ sc.height[sym] = stackCheckCycle
+ // Compute and record the height and optionally edges.
+ h, edges := sc.computeHeight(sym, *flagDebugNosplit || sc.graph != nil)
+ if h > int(stackCheckCycle) { // Prevent integer overflow
+ h = int(stackCheckCycle)
+ }
+ sc.height[sym] = int16(h)
+ if sc.graph != nil {
+ sc.graph[sym] = edges
+ }
+
+ if *flagDebugNosplit {
+ for _, edge := range edges {
+ fmt.Printf("nosplit: %s +%d", sc.symName(sym), edge.growth)
+ if edge.target == 0 {
+ // Local stack growth or leaf function.
+ fmt.Printf("\n")
+ } else {
+ fmt.Printf(" -> %s\n", sc.symName(edge.target))
+ }
+ }
+ }
+
+ return h
+}
+
+// computeHeight returns the stack height of sym. If graph is true, it
+// also returns the out-edges of sym.
+//
+// Caching is applied to this in check. Call check instead of calling
+// this directly.
+func (sc *stackCheck) computeHeight(sym loader.Sym, graph bool) (int, []stackCheckEdge) {
+ ldr := sc.ldr
+
+ // Check special cases.
+ if sym == sc.morestack {
+ // morestack looks like it calls functions, but they
+ // either happen only when already on the system stack
+ // (where there is ~infinite space), or after
+ // switching to the system stack. Hence, its stack
+ // height on the user stack is 0.
+ return 0, nil
+ }
+ if sym == stackCheckIndirect {
+ // Assume that indirect/closure calls are always to
+ // splittable functions, so they just need enough room
+ // to call morestack.
+ return sc.callSize, []stackCheckEdge{{sc.callSize, sc.morestack}}
+ }
+
+ // Ignore calls to external functions. Assume that these calls
+ // are only ever happening on the system stack, where there's
+ // plenty of room.
+ if ldr.AttrExternal(sym) {
+ return 0, nil
+ }
+ if info := ldr.FuncInfo(sym); !info.Valid() { // also external
+ return 0, nil
+ }
+
+ // Track the maximum height of this function and, if we're
+ // recording the graph, its out-edges.
+ var edges []stackCheckEdge
+ maxHeight := 0
+ ctxt := sc.ctxt
+ // addEdge adds a stack growth out of this function to
+ // function "target" or, if target == 0, a local stack growth
+ // within the function.
+ addEdge := func(growth int, target loader.Sym) {
+ if graph {
+ edges = append(edges, stackCheckEdge{growth, target})
+ }
+ height := growth
+ if target != 0 { // Don't walk into the leaf "edge"
+ height += sc.check(target)
+ }
+ if height > maxHeight {
+ maxHeight = height
+ }
+ }
+
+ if !ldr.IsNoSplit(sym) {
+ // Splittable functions start with a call to
+ // morestack, after which their height is 0. Account
+ // for the height of the call to morestack.
+ addEdge(sc.callSize, sc.morestack)
+ return maxHeight, edges
+ }
+
+ // This function is nosplit, so it adjusts SP without a split
+ // check.
+ //
+ // Walk through SP adjustments in function, consuming relocs
+ // and following calls.
+ maxLocalHeight := 0
+ relocs, ri := ldr.Relocs(sym), 0
+ pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
+ for pcsp.Init(ldr.Data(ldr.Pcsp(sym))); !pcsp.Done; pcsp.Next() {
+ // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
+ height := int(pcsp.Value)
+ if height > maxLocalHeight {
+ maxLocalHeight = height
+ }
+
+ // Process calls in this span.
+ for ; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if uint32(r.Off()) >= pcsp.NextPC {
+ break
+ }
+ t := r.Type()
+ if t.IsDirectCall() || t == objabi.R_CALLIND {
+ growth := height + sc.callSize
+ var target loader.Sym
+ if t == objabi.R_CALLIND {
+ target = stackCheckIndirect
+ } else {
+ target = r.Sym()
+ }
+ addEdge(growth, target)
+ }
+ }
+ }
+ if maxLocalHeight > maxHeight {
+ // This is either a leaf function, or the function
+ // grew its stack to larger than the maximum call
+ // height between calls. Either way, record that local
+ // stack growth.
+ addEdge(maxLocalHeight, 0)
+ }
+
+ return maxHeight, edges
+}
+
+func (sc *stackCheck) findRoots() []loader.Sym {
+ // Collect all nodes.
+ nodes := make(map[loader.Sym]struct{})
+ for k := range sc.graph {
+ nodes[k] = struct{}{}
+ }
+
+ // Start a DFS from each node and delete all reachable
+ // children. If we encounter an unrooted cycle, this will
+ // delete everything in that cycle, so we detect this case and
+ // track the lowest-numbered node encountered in the cycle and
+ // put that node back as a root.
+ var walk func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym)
+ walk = func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym) {
+ if _, ok := nodes[sym]; !ok {
+ // We already deleted this node.
+ return false, 0
+ }
+ delete(nodes, sym)
+
+ if origin == sym {
+ // We found an unrooted cycle. We already
+ // deleted all children of this node. Walk
+ // back up, tracking the lowest numbered
+ // symbol in this cycle.
+ return true, sym
+ }
+
+ // Delete children of this node.
+ for _, out := range sc.graph[sym] {
+ if c, l := walk(origin, out.target); c {
+ cycle = true
+ if lowest == 0 {
+ // On first cycle detection,
+ // add sym to the set of
+ // lowest-numbered candidates.
+ lowest = sym
+ }
+ if l < lowest {
+ lowest = l
+ }
+ }
+ }
+ return
+ }
+ for k := range nodes {
+ // Delete all children of k.
+ for _, out := range sc.graph[k] {
+ if cycle, lowest := walk(k, out.target); cycle {
+ // This is an unrooted cycle so we
+ // just deleted everything. Put back
+ // the lowest-numbered symbol.
+ nodes[lowest] = struct{}{}
+ }
+ }
+ }
+
+ // Sort roots by height. This makes the result deterministic
+ // and also improves the error reporting.
+ var roots []loader.Sym
+ for k := range nodes {
+ roots = append(roots, k)
+ }
+ sort.Slice(roots, func(i, j int) bool {
+ h1, h2 := sc.height[roots[i]], sc.height[roots[j]]
+ if h1 != h2 {
+ return h1 > h2
+ }
+ // Secondary sort by Sym.
+ return roots[i] < roots[j]
+ })
+ return roots
+}
+
+type stackCheckChain struct {
+ stackCheckEdge
+ printed bool
+}
+
+func (sc *stackCheck) report(sym loader.Sym, depth int, chain *[]stackCheckChain) {
+ // Walk the out-edges of sym. We temporarily pull the edges
+ // out of the graph to detect cycles and prevent infinite
+ // recursion.
+ edges, ok := sc.graph[sym]
+ isCycle := !(ok || sym == 0)
+ delete(sc.graph, sym)
+ for _, out := range edges {
+ *chain = append(*chain, stackCheckChain{out, false})
+ sc.report(out.target, depth-out.growth, chain)
+ *chain = (*chain)[:len(*chain)-1]
+ }
+ sc.graph[sym] = edges
+
+ // If we've reached the end of a chain and it went over the
+ // stack limit or was a cycle that would eventually go over,
+ // print the whole chain.
+ //
+ // We should either be in morestack (which has no out-edges)
+ // or the sentinel 0 Sym "called" from a leaf function (which
+ // has no out-edges), or we came back around a cycle (possibly
+ // to ourselves) and edges was temporarily nil'd.
+ if len(edges) == 0 && (depth < 0 || isCycle) {
+ var indent string
+ for i := range *chain {
+ ent := &(*chain)[i]
+ if ent.printed {
+ // Already printed on an earlier part
+ // of this call tree.
+ continue
+ }
+ ent.printed = true
+
+ if i == 0 {
+ // chain[0] is just the root function,
+ // not a stack growth.
+ fmt.Printf("%s\n", sc.symName(ent.target))
+ continue
+ }
+
+ indent = strings.Repeat(" ", i)
+ fmt.Print(indent)
+ // Grows the stack X bytes and (maybe) calls Y.
+ fmt.Printf("grows %d bytes", ent.growth)
+ if ent.target == 0 {
+ // Not a call, just a leaf. Print nothing.
+ } else {
+ fmt.Printf(", calls %s", sc.symName(ent.target))
+ }
+ fmt.Printf("\n")
+ }
+ // Print how far over this chain went.
+ if isCycle {
+ fmt.Printf("%sinfinite cycle\n", indent)
+ } else {
+ fmt.Printf("%s%d bytes over limit\n", indent, -depth)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/stackcheck_test.go b/src/cmd/link/internal/ld/stackcheck_test.go
new file mode 100644
index 0000000..dd7e205
--- /dev/null
+++ b/src/cmd/link/internal/ld/stackcheck_test.go
@@ -0,0 +1,87 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "fmt"
+ "internal/testenv"
+ "os"
+ "regexp"
+ "strconv"
+ "testing"
+)
+
+// See also $GOROOT/test/nosplit.go for multi-platform edge case tests.
+
+func TestStackCheckOutput(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", os.DevNull, "./testdata/stackcheck")
+ // The rules for computing frame sizes on all of the
+ // architectures are complicated, so just do this on amd64.
+ cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux")
+ outB, err := cmd.CombinedOutput()
+
+ if err == nil {
+ t.Fatalf("expected link to fail")
+ }
+ out := string(outB)
+
+ t.Logf("linker output:\n%s", out)
+
+ // Get expected limit.
+ limitRe := regexp.MustCompile(`nosplit stack over (\d+) byte limit`)
+ m := limitRe.FindStringSubmatch(out)
+ if m == nil {
+ t.Fatalf("no overflow errors in output")
+ }
+ limit, _ := strconv.Atoi(m[1])
+
+ wantMap := map[string]string{
+ "main.startSelf": fmt.Sprintf(
+ `main.startSelf<0>
+ grows 1008 bytes
+ %d bytes over limit
+`, 1008-limit),
+ "main.startChain": fmt.Sprintf(
+ `main.startChain<0>
+ grows 32 bytes, calls main.chain0<0>
+ grows 48 bytes, calls main.chainEnd<0>
+ grows 1008 bytes
+ %d bytes over limit
+ grows 32 bytes, calls main.chain2<0>
+ grows 80 bytes, calls main.chainEnd<0>
+ grows 1008 bytes
+ %d bytes over limit
+`, 32+48+1008-limit, 32+80+1008-limit),
+ "main.startRec": `main.startRec<0>
+ grows 8 bytes, calls main.startRec0<0>
+ grows 8 bytes, calls main.startRec<0>
+ infinite cycle
+`,
+ }
+
+ // Parse stanzas
+ stanza := regexp.MustCompile(`^(.*): nosplit stack over \d+ byte limit\n(.*\n(?: .*\n)*)`)
+ // Strip comments from cmd/go
+ out = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(out, "")
+ for len(out) > 0 {
+ m := stanza.FindStringSubmatch(out)
+ if m == nil {
+ t.Fatalf("unexpected output:\n%s", out)
+ }
+ out = out[len(m[0]):]
+ fn := m[1]
+ got := m[2]
+
+ want, ok := wantMap[fn]
+ if !ok {
+ t.Errorf("unexpected function: %s", fn)
+ } else if want != got {
+ t.Errorf("want:\n%sgot:\n%s", want, got)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
new file mode 100644
index 0000000..d51a59e
--- /dev/null
+++ b/src/cmd/link/internal/ld/sym.go
@@ -0,0 +1,117 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "internal/buildcfg"
+ "log"
+ "runtime"
+)
+
+func linknew(arch *sys.Arch) *Link {
+ ler := loader.ErrorReporter{AfterErrorAction: afterErrorAction}
+ ctxt := &Link{
+ Target: Target{Arch: arch},
+ version: sym.SymVerStatic,
+ outSem: make(chan int, 2*runtime.GOMAXPROCS(0)),
+ Out: NewOutBuf(arch),
+ LibraryByPkg: make(map[string]*sym.Library),
+ numelfsym: 1,
+ ErrorReporter: ErrorReporter{ErrorReporter: ler},
+ generatorSyms: make(map[loader.Sym]generatorFunc),
+ }
+
+ if buildcfg.GOARCH != arch.Name {
+ log.Fatalf("invalid buildcfg.GOARCH %s (want %s)", buildcfg.GOARCH, arch.Name)
+ }
+
+ AtExit(func() {
+ if nerrors > 0 {
+ ctxt.Out.ErrorClose()
+ mayberemoveoutfile()
+ }
+ })
+
+ return ctxt
+}
+
+// computeTLSOffset records the thread-local storage offset.
+// Not used for Android where the TLS offset is determined at runtime.
+func (ctxt *Link) computeTLSOffset() {
+ switch ctxt.HeadType {
+ default:
+ log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType)
+
+ case objabi.Hplan9, objabi.Hwindows, objabi.Hjs, objabi.Haix:
+ break
+
+ case objabi.Hlinux,
+ objabi.Hfreebsd,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd,
+ objabi.Hdragonfly,
+ objabi.Hsolaris:
+ /*
+ * ELF uses TLS offset negative from FS.
+ * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS).
+ * Known to low-level assembly in package runtime and runtime/cgo.
+ */
+ ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize
+
+ case objabi.Hdarwin:
+ /*
+ * OS X system constants - offset from 0(GS) to our TLS.
+ */
+ switch ctxt.Arch.Family {
+ default:
+ log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name)
+
+ /*
+ * For x86, Apple has reserved a slot in the TLS for Go. See issue 23617.
+ * That slot is at offset 0x30 on amd64.
+ * The slot will hold the G pointer.
+ * These constants should match those in runtime/sys_darwin_amd64.s
+ * and runtime/cgo/gcc_darwin_amd64.c.
+ */
+ case sys.AMD64:
+ ctxt.Tlsoffset = 0x30
+
+ case sys.ARM64:
+ ctxt.Tlsoffset = 0 // dummy value, not needed
+ }
+ }
+
+}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
new file mode 100644
index 0000000..21a1466
--- /dev/null
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -0,0 +1,890 @@
+// Inferno utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "internal/buildcfg"
+ "path/filepath"
+ "strings"
+)
+
+// Symbol table.
+
+func putelfstr(s string) int {
+ if len(Elfstrdat) == 0 && s != "" {
+ // first entry must be empty string
+ putelfstr("")
+ }
+
+ off := len(Elfstrdat)
+ Elfstrdat = append(Elfstrdat, s...)
+ Elfstrdat = append(Elfstrdat, 0)
+ return off
+}
+
+func putelfsyment(out *OutBuf, off int, addr int64, size int64, info uint8, shndx elf.SectionIndex, other int) {
+ if elf64 {
+ out.Write32(uint32(off))
+ out.Write8(info)
+ out.Write8(uint8(other))
+ out.Write16(uint16(shndx))
+ out.Write64(uint64(addr))
+ out.Write64(uint64(size))
+ symSize += ELF64SYMSIZE
+ } else {
+ out.Write32(uint32(off))
+ out.Write32(uint32(addr))
+ out.Write32(uint32(size))
+ out.Write8(info)
+ out.Write8(uint8(other))
+ out.Write16(uint16(shndx))
+ symSize += ELF32SYMSIZE
+ }
+}
+
+func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
+ ldr := ctxt.loader
+ addr := ldr.SymValue(x)
+ size := ldr.SymSize(x)
+
+ xo := x
+ if ldr.OuterSym(x) != 0 {
+ xo = ldr.OuterSym(x)
+ }
+ xot := ldr.SymType(xo)
+ xosect := ldr.SymSect(xo)
+
+ var elfshnum elf.SectionIndex
+ if xot == sym.SDYNIMPORT || xot == sym.SHOSTOBJ || xot == sym.SUNDEFEXT {
+ elfshnum = elf.SHN_UNDEF
+ size = 0
+ } else {
+ if xosect == nil {
+ ldr.Errorf(x, "missing section in putelfsym")
+ return
+ }
+ if xosect.Elfsect == nil {
+ ldr.Errorf(x, "missing ELF section in putelfsym")
+ return
+ }
+ elfshnum = xosect.Elfsect.(*ElfShdr).shnum
+ }
+
+ sname := ldr.SymExtname(x)
+ sname = mangleABIName(ctxt, ldr, x, sname)
+
+ // One pass for each binding: elf.STB_LOCAL, elf.STB_GLOBAL,
+ // maybe one day elf.STB_WEAK.
+ bind := elf.STB_GLOBAL
+ if ldr.IsFileLocal(x) && !isStaticTmp(sname) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
+ // Static tmp is package local, but a package can be shared among multiple DSOs.
+ // They need to have a single view of the static tmp that are writable.
+ bind = elf.STB_LOCAL
+ }
+
+ // In external linking mode, we have to invoke gcc with -rdynamic
+ // to get the exported symbols put into the dynamic symbol table.
+ // To avoid filling the dynamic table with lots of unnecessary symbols,
+ // mark all Go symbols local (not global) in the final executable.
+ // But when we're dynamically linking, we need all those global symbols.
+ if !ctxt.DynlinkingGo() && ctxt.IsExternal() && !ldr.AttrCgoExportStatic(x) && elfshnum != elf.SHN_UNDEF {
+ bind = elf.STB_LOCAL
+ }
+
+ if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF {
+ addr -= int64(xosect.Vaddr)
+ }
+ other := int(elf.STV_DEFAULT)
+ if ldr.AttrVisibilityHidden(x) {
+ // TODO(mwhudson): We only set AttrVisibilityHidden in ldelf, i.e. when
+ // internally linking. But STV_HIDDEN visibility only matters in object
+ // files and shared libraries, and as we are a long way from implementing
+ // internal linking for shared libraries and only create object files when
+ // externally linking, I don't think this makes a lot of sense.
+ other = int(elf.STV_HIDDEN)
+ }
+ if ctxt.IsPPC64() && typ == elf.STT_FUNC && ldr.AttrShared(x) && ldr.SymName(x) != "runtime.duffzero" && ldr.SymName(x) != "runtime.duffcopy" {
+ // On ppc64 the top three bits of the st_other field indicate how
+ // many instructions separate the global and local entry points. In
+ // our case it is two instructions, indicated by the value 3.
+ // The conditions here match those in preprocess in
+ // cmd/internal/obj/ppc64/obj9.go, which is where the
+ // instructions are inserted.
+ other |= 3 << 5
+ }
+
+ // When dynamically linking, we create Symbols by reading the names from
+ // the symbol tables of the shared libraries and so the names need to
+ // match exactly. Tools like DTrace will have to wait for now.
+ if !ctxt.DynlinkingGo() {
+ // Rewrite · to . for ASCII-only tools like DTrace (sigh)
+ sname = strings.Replace(sname, "·", ".", -1)
+ }
+
+ if ctxt.DynlinkingGo() && bind == elf.STB_GLOBAL && curbind == elf.STB_LOCAL && ldr.SymType(x) == sym.STEXT {
+ // When dynamically linking, we want references to functions defined
+ // in this module to always be to the function object, not to the
+ // PLT. We force this by writing an additional local symbol for every
+ // global function symbol and making all relocations against the
+ // global symbol refer to this local symbol instead (see
+ // (*sym.Symbol).ElfsymForReloc). This is approximately equivalent to the
+ // ELF linker -Bsymbolic-functions option, but that is buggy on
+ // several platforms.
+ putelfsyment(ctxt.Out, putelfstr("local."+sname), addr, size, elf.ST_INFO(elf.STB_LOCAL, typ), elfshnum, other)
+ ldr.SetSymLocalElfSym(x, int32(ctxt.numelfsym))
+ ctxt.numelfsym++
+ return
+ } else if bind != curbind {
+ return
+ }
+
+ putelfsyment(ctxt.Out, putelfstr(sname), addr, size, elf.ST_INFO(bind, typ), elfshnum, other)
+ ldr.SetSymElfSym(x, int32(ctxt.numelfsym))
+ ctxt.numelfsym++
+}
+
+func putelfsectionsym(ctxt *Link, out *OutBuf, s loader.Sym, shndx elf.SectionIndex) {
+ putelfsyment(out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_SECTION), shndx, 0)
+ ctxt.loader.SetSymElfSym(s, int32(ctxt.numelfsym))
+ ctxt.numelfsym++
+}
+
+func genelfsym(ctxt *Link, elfbind elf.SymBind) {
+ ldr := ctxt.loader
+
+ // runtime.text marker symbol(s).
+ s := ldr.Lookup("runtime.text", 0)
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
+ for k, sect := range Segtext.Sections[1:] {
+ n := k + 1
+ if sect.Name != ".text" || (ctxt.IsAIX() && ctxt.IsExternal()) {
+ // On AIX, runtime.text.X are symbols already in the symtab.
+ break
+ }
+ s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+ if s == 0 {
+ break
+ }
+ if ldr.SymType(s) != sym.STEXT {
+ panic("unexpected type for runtime.text symbol")
+ }
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
+ }
+
+ // Text symbols.
+ for _, s := range ctxt.Textp {
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
+ }
+
+ // runtime.etext marker symbol.
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym) bool {
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ // FIXME: avoid having to do name inspections here.
+ // NB: the restrictions below on file local symbols are a bit
+ // arbitrary -- if it turns out we need nameless static
+ // symbols they could be relaxed/removed.
+ sn := ldr.SymName(s)
+ if (sn == "" || sn[0] == '.') && ldr.IsFileLocal(s) {
+ panic(fmt.Sprintf("unexpected file local symbol %d %s<%d>\n",
+ s, sn, ldr.SymVersion(s)))
+ }
+ if (sn == "" || sn[0] == '.') && !ldr.IsFileLocal(s) {
+ return false
+ }
+ return true
+ }
+
+ // Data symbols.
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ st := ldr.SymType(s)
+ if st >= sym.SELFRXSECT && st < sym.SXREF {
+ typ := elf.STT_OBJECT
+ if st == sym.STLSBSS {
+ if ctxt.IsInternal() {
+ continue
+ }
+ typ = elf.STT_TLS
+ }
+ if !shouldBeInSymbolTable(s) {
+ continue
+ }
+ putelfsym(ctxt, s, typ, elfbind)
+ continue
+ }
+ if st == sym.SHOSTOBJ || st == sym.SDYNIMPORT || st == sym.SUNDEFEXT {
+ putelfsym(ctxt, s, ldr.SymElfType(s), elfbind)
+ }
+ }
+}
+
+func asmElfSym(ctxt *Link) {
+
+ // the first symbol entry is reserved
+ putelfsyment(ctxt.Out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE), 0, 0)
+
+ dwarfaddelfsectionsyms(ctxt)
+
+ // Some linkers will add a FILE sym if one is not present.
+ // Avoid having the working directory inserted into the symbol table.
+ // It is added with a name to avoid problems with external linking
+ // encountered on some versions of Solaris. See issue #14957.
+ putelfsyment(ctxt.Out, putelfstr("go.go"), 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_FILE), elf.SHN_ABS, 0)
+ ctxt.numelfsym++
+
+ bindings := []elf.SymBind{elf.STB_LOCAL, elf.STB_GLOBAL}
+ for _, elfbind := range bindings {
+ if elfbind == elf.STB_GLOBAL {
+ elfglobalsymndx = ctxt.numelfsym
+ }
+ genelfsym(ctxt, elfbind)
+ }
+}
+
+func putplan9sym(ctxt *Link, ldr *loader.Loader, s loader.Sym, char SymbolType) {
+ t := int(char)
+ if ldr.IsFileLocal(s) {
+ t += 'a' - 'A'
+ }
+ l := 4
+ addr := ldr.SymValue(s)
+ if ctxt.IsAMD64() && !flag8 {
+ ctxt.Out.Write32b(uint32(addr >> 32))
+ l = 8
+ }
+
+ ctxt.Out.Write32b(uint32(addr))
+ ctxt.Out.Write8(uint8(t + 0x80)) /* 0x80 is variable length */
+
+ name := ldr.SymName(s)
+ name = mangleABIName(ctxt, ldr, s, name)
+ ctxt.Out.WriteString(name)
+ ctxt.Out.Write8(0)
+
+ symSize += int32(l) + 1 + int32(len(name)) + 1
+}
+
+func asmbPlan9Sym(ctxt *Link) {
+ ldr := ctxt.loader
+
+ // Add special runtime.text and runtime.etext symbols.
+ s := ldr.Lookup("runtime.text", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ putplan9sym(ctxt, ldr, s, TextSym)
+ }
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ putplan9sym(ctxt, ldr, s, TextSym)
+ }
+
+ // Add text symbols.
+ for _, s := range ctxt.Textp {
+ putplan9sym(ctxt, ldr, s, TextSym)
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym) bool {
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ name := ldr.SymName(s) // TODO: try not to read the name
+ if name == "" || name[0] == '.' {
+ return false
+ }
+ return true
+ }
+
+ // Add data symbols and external references.
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ t := ldr.SymType(s)
+ if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata
+ if t == sym.STLSBSS {
+ continue
+ }
+ if !shouldBeInSymbolTable(s) {
+ continue
+ }
+ char := DataSym
+ if t == sym.SBSS || t == sym.SNOPTRBSS {
+ char = BSSSym
+ }
+ putplan9sym(ctxt, ldr, s, char)
+ }
+ }
+}
+
+type byPkg []*sym.Library
+
+func (libs byPkg) Len() int {
+ return len(libs)
+}
+
+func (libs byPkg) Less(a, b int) bool {
+ return libs[a].Pkg < libs[b].Pkg
+}
+
+func (libs byPkg) Swap(a, b int) {
+ libs[a], libs[b] = libs[b], libs[a]
+}
+
+// Create a table with information on the text sections.
+// Return the symbol of the table, and number of sections.
+func textsectionmap(ctxt *Link) (loader.Sym, uint32) {
+ ldr := ctxt.loader
+ t := ldr.CreateSymForUpdate("runtime.textsectionmap", 0)
+ t.SetType(sym.SRODATA)
+ nsections := int64(0)
+
+ for _, sect := range Segtext.Sections {
+ if sect.Name == ".text" {
+ nsections++
+ } else {
+ break
+ }
+ }
+ t.Grow(3 * nsections * int64(ctxt.Arch.PtrSize))
+
+ off := int64(0)
+ n := 0
+
+ // The vaddr for each text section is the difference between the section's
+ // Vaddr and the Vaddr for the first text section as determined at compile
+ // time.
+
+ // The symbol for the first text section is named runtime.text as before.
+ // Additional text sections are named runtime.text.n where n is the
+ // order of creation starting with 1. These symbols provide the section's
+ // address after relocation by the linker.
+
+ textbase := Segtext.Sections[0].Vaddr
+ for _, sect := range Segtext.Sections {
+ if sect.Name != ".text" {
+ break
+ }
+ // The fields written should match runtime/symtab.go:textsect.
+ // They are designed to minimize runtime calculations.
+ vaddr := sect.Vaddr - textbase
+ off = t.SetUint(ctxt.Arch, off, vaddr) // field vaddr
+ end := vaddr + sect.Length
+ off = t.SetUint(ctxt.Arch, off, end) // field end
+ name := "runtime.text"
+ if n != 0 {
+ name = fmt.Sprintf("runtime.text.%d", n)
+ }
+ s := ldr.Lookup(name, 0)
+ if s == 0 {
+ ctxt.Errorf(s, "Unable to find symbol %s\n", name)
+ }
+ off = t.SetAddr(ctxt.Arch, off, s) // field baseaddr
+ n++
+ }
+ return t.Sym(), uint32(n)
+}
+
+func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
+ ldr := ctxt.loader
+
+ if !ctxt.IsAIX() {
+ switch ctxt.BuildMode {
+ case BuildModeCArchive, BuildModeCShared:
+ s := ldr.Lookup(*flagEntrySymbol, sym.SymVerABI0)
+ if s != 0 {
+ addinitarrdata(ctxt, ldr, s)
+ }
+ }
+ }
+
+ // Define these so that they'll get put into the symbol table.
+ // data.c:/^address will provide the actual values.
+ ctxt.xdefine("runtime.rodata", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.erodata", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.types", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.etypes", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, 0)
+ ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, 0)
+ ctxt.xdefine("runtime.data", sym.SDATA, 0)
+ ctxt.xdefine("runtime.edata", sym.SDATA, 0)
+ ctxt.xdefine("runtime.bss", sym.SBSS, 0)
+ ctxt.xdefine("runtime.ebss", sym.SBSS, 0)
+ ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, 0)
+ ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, 0)
+ ctxt.xdefine("runtime.covctrs", sym.SNOPTRBSS, 0)
+ ctxt.xdefine("runtime.ecovctrs", sym.SNOPTRBSS, 0)
+ ctxt.xdefine("runtime.end", sym.SBSS, 0)
+ ctxt.xdefine("runtime.epclntab", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.esymtab", sym.SRODATA, 0)
+
+ // garbage collection symbols
+ s := ldr.CreateSymForUpdate("runtime.gcdata", 0)
+ s.SetType(sym.SRODATA)
+ s.SetSize(0)
+ ctxt.xdefine("runtime.egcdata", sym.SRODATA, 0)
+
+ s = ldr.CreateSymForUpdate("runtime.gcbss", 0)
+ s.SetType(sym.SRODATA)
+ s.SetSize(0)
+ ctxt.xdefine("runtime.egcbss", sym.SRODATA, 0)
+
+ // pseudo-symbols to mark locations of type, string, and go string data.
+ var symtype, symtyperel loader.Sym
+ if !ctxt.DynlinkingGo() {
+ if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
+ s = ldr.CreateSymForUpdate("type:*", 0)
+ s.SetType(sym.STYPE)
+ s.SetSize(0)
+ s.SetAlign(int32(ctxt.Arch.PtrSize))
+ symtype = s.Sym()
+
+ s = ldr.CreateSymForUpdate("typerel.*", 0)
+ s.SetType(sym.STYPERELRO)
+ s.SetSize(0)
+ s.SetAlign(int32(ctxt.Arch.PtrSize))
+ symtyperel = s.Sym()
+ } else {
+ s = ldr.CreateSymForUpdate("type:*", 0)
+ s.SetType(sym.STYPE)
+ s.SetSize(0)
+ s.SetAlign(int32(ctxt.Arch.PtrSize))
+ symtype = s.Sym()
+ symtyperel = s.Sym()
+ }
+ setCarrierSym(sym.STYPE, symtype)
+ setCarrierSym(sym.STYPERELRO, symtyperel)
+ }
+
+ groupSym := func(name string, t sym.SymKind) loader.Sym {
+ s := ldr.CreateSymForUpdate(name, 0)
+ s.SetType(t)
+ s.SetSize(0)
+ s.SetAlign(int32(ctxt.Arch.PtrSize))
+ s.SetLocal(true)
+ setCarrierSym(t, s.Sym())
+ return s.Sym()
+ }
+ var (
+ symgostring = groupSym("go:string.*", sym.SGOSTRING)
+ symgofunc = groupSym("go:func.*", sym.SGOFUNC)
+ symgcbits = groupSym("runtime.gcbits.*", sym.SGCBITS)
+ )
+
+ symgofuncrel := symgofunc
+ if ctxt.UseRelro() {
+ symgofuncrel = groupSym("go:funcrel.*", sym.SGOFUNCRELRO)
+ }
+
+ symt := ldr.CreateSymForUpdate("runtime.symtab", 0)
+ symt.SetType(sym.SSYMTAB)
+ symt.SetSize(0)
+ symt.SetLocal(true)
+
+ // assign specific types so that they sort together.
+ // within a type they sort by size, so the .* symbols
+ // just defined above will be first.
+ // hide the specific symbols.
+ // Some of these symbol section conditions are duplicated
+ // in cmd/internal/obj.contentHashSection.
+ nsym := loader.Sym(ldr.NSym())
+ symGroupType := make([]sym.SymKind, nsym)
+ for s := loader.Sym(1); s < nsym; s++ {
+ if (!ctxt.IsExternal() && ldr.IsFileLocal(s) && !ldr.IsFromAssembly(s) && ldr.SymPkg(s) != "") || (ctxt.LinkMode == LinkInternal && ldr.SymType(s) == sym.SCOVERAGE_COUNTER) {
+ ldr.SetAttrNotInSymbolTable(s, true)
+ }
+ if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || (ldr.SymType(s) != sym.SRODATA && ldr.SymType(s) != sym.SGOFUNC) {
+ continue
+ }
+
+ name := ldr.SymName(s)
+ switch {
+ case strings.HasPrefix(name, "go:string."):
+ symGroupType[s] = sym.SGOSTRING
+ ldr.SetAttrNotInSymbolTable(s, true)
+ ldr.SetCarrierSym(s, symgostring)
+
+ case strings.HasPrefix(name, "runtime.gcbits."),
+ strings.HasPrefix(name, "type:.gcprog."):
+ symGroupType[s] = sym.SGCBITS
+ ldr.SetAttrNotInSymbolTable(s, true)
+ ldr.SetCarrierSym(s, symgcbits)
+
+ case strings.HasSuffix(name, "·f"):
+ if !ctxt.DynlinkingGo() {
+ ldr.SetAttrNotInSymbolTable(s, true)
+ }
+ if ctxt.UseRelro() {
+ symGroupType[s] = sym.SGOFUNCRELRO
+ if !ctxt.DynlinkingGo() {
+ ldr.SetCarrierSym(s, symgofuncrel)
+ }
+ } else {
+ symGroupType[s] = sym.SGOFUNC
+ ldr.SetCarrierSym(s, symgofunc)
+ }
+
+ case strings.HasPrefix(name, "gcargs."),
+ strings.HasPrefix(name, "gclocals."),
+ strings.HasPrefix(name, "gclocals·"),
+ ldr.SymType(s) == sym.SGOFUNC && s != symgofunc, // inltree, see pcln.go
+ strings.HasSuffix(name, ".opendefer"),
+ strings.HasSuffix(name, ".arginfo0"),
+ strings.HasSuffix(name, ".arginfo1"),
+ strings.HasSuffix(name, ".argliveinfo"),
+ strings.HasSuffix(name, ".wrapinfo"),
+ strings.HasSuffix(name, ".args_stackmap"),
+ strings.HasSuffix(name, ".stkobj"):
+ ldr.SetAttrNotInSymbolTable(s, true)
+ symGroupType[s] = sym.SGOFUNC
+ ldr.SetCarrierSym(s, symgofunc)
+ if ctxt.Debugvlog != 0 {
+ align := ldr.SymAlign(s)
+ liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1)
+ }
+
+ // Note: Check for "type:" prefix after checking for .arginfo1 suffix.
+ // That way symbols like "type:.eq.[2]interface {}.arginfo1" that belong
+ // in go:func.* end up there.
+ case strings.HasPrefix(name, "type:"):
+ if !ctxt.DynlinkingGo() {
+ ldr.SetAttrNotInSymbolTable(s, true)
+ }
+ if ctxt.UseRelro() {
+ symGroupType[s] = sym.STYPERELRO
+ if symtyperel != 0 {
+ ldr.SetCarrierSym(s, symtyperel)
+ }
+ } else {
+ symGroupType[s] = sym.STYPE
+ if symtyperel != 0 {
+ ldr.SetCarrierSym(s, symtype)
+ }
+ }
+ }
+ }
+
+ if ctxt.BuildMode == BuildModeShared {
+ abihashgostr := ldr.CreateSymForUpdate("go:link.abihash."+filepath.Base(*flagOutfile), 0)
+ abihashgostr.SetType(sym.SRODATA)
+ hashsym := ldr.LookupOrCreateSym("go:link.abihashbytes", 0)
+ abihashgostr.AddAddr(ctxt.Arch, hashsym)
+ abihashgostr.AddUint(ctxt.Arch, uint64(ldr.SymSize(hashsym)))
+ }
+ if ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
+ for _, l := range ctxt.Library {
+ s := ldr.CreateSymForUpdate("go:link.pkghashbytes."+l.Pkg, 0)
+ s.SetType(sym.SRODATA)
+ s.SetSize(int64(len(l.Fingerprint)))
+ s.SetData(l.Fingerprint[:])
+ str := ldr.CreateSymForUpdate("go:link.pkghash."+l.Pkg, 0)
+ str.SetType(sym.SRODATA)
+ str.AddAddr(ctxt.Arch, s.Sym())
+ str.AddUint(ctxt.Arch, uint64(len(l.Fingerprint)))
+ }
+ }
+
+ textsectionmapSym, nsections := textsectionmap(ctxt)
+
+ // Information about the layout of the executable image for the
+ // runtime to use. Any changes here must be matched by changes to
+ // the definition of moduledata in runtime/symtab.go.
+ // This code uses several global variables that are set by pcln.go:pclntab.
+ moduledata := ldr.MakeSymbolUpdater(ctxt.Moduledata)
+ // The pcHeader
+ moduledata.AddAddr(ctxt.Arch, pcln.pcheader)
+ // The function name slice
+ moduledata.AddAddr(ctxt.Arch, pcln.funcnametab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab)))
+ // The cutab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.cutab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab)))
+ // The filetab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.filetab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
+ // The pctab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.pctab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab)))
+ // The pclntab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
+ // The ftab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
+ moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
+ moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
+ // findfunctab
+ moduledata.AddAddr(ctxt.Arch, pcln.findfunctab)
+ // minpc, maxpc
+ moduledata.AddAddr(ctxt.Arch, pcln.firstFunc)
+ moduledata.AddAddrPlus(ctxt.Arch, pcln.lastFunc, ldr.SymSize(pcln.lastFunc))
+ // pointers to specific parts of the module
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.text", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etext", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.noptrdata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.enoptrdata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.data", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.edata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.bss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.ebss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.noptrbss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.enoptrbss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.covctrs", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.ecovctrs", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.end", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcdata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcbss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.types", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etypes", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.rodata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("go:func.*", 0))
+
+ if ctxt.IsAIX() && ctxt.IsExternal() {
+ // Add R_XCOFFREF relocation to prevent ld's garbage collection of
+ // the following symbols. They might not be referenced in the program.
+ addRef := func(name string) {
+ s := ldr.Lookup(name, 0)
+ if s == 0 {
+ return
+ }
+ r, _ := moduledata.AddRel(objabi.R_XCOFFREF)
+ r.SetSym(s)
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ }
+ addRef("runtime.rodata")
+ addRef("runtime.erodata")
+ addRef("runtime.epclntab")
+ // As we use relative addressing for text symbols in functab, it is
+ // important that the offsets we computed stay unchanged by the external
+ // linker, i.e. all symbols in Textp should not be removed.
+ // Most of them are actually referenced (our deadcode pass ensures that),
+ // except go:buildid which is generated late and not used by the program.
+ addRef("go:buildid")
+ }
+
+ // text section information
+ moduledata.AddAddr(ctxt.Arch, textsectionmapSym)
+ moduledata.AddUint(ctxt.Arch, uint64(nsections))
+ moduledata.AddUint(ctxt.Arch, uint64(nsections))
+
+ // The typelinks slice
+ typelinkSym := ldr.Lookup("runtime.typelink", 0)
+ ntypelinks := uint64(ldr.SymSize(typelinkSym)) / 4
+ moduledata.AddAddr(ctxt.Arch, typelinkSym)
+ moduledata.AddUint(ctxt.Arch, ntypelinks)
+ moduledata.AddUint(ctxt.Arch, ntypelinks)
+ // The itablinks slice
+ itablinkSym := ldr.Lookup("runtime.itablink", 0)
+ nitablinks := uint64(ldr.SymSize(itablinkSym)) / uint64(ctxt.Arch.PtrSize)
+ moduledata.AddAddr(ctxt.Arch, itablinkSym)
+ moduledata.AddUint(ctxt.Arch, nitablinks)
+ moduledata.AddUint(ctxt.Arch, nitablinks)
+ // The ptab slice
+ if ptab := ldr.Lookup("go:plugin.tabs", 0); ptab != 0 && ldr.AttrReachable(ptab) {
+ ldr.SetAttrLocal(ptab, true)
+ if ldr.SymType(ptab) != sym.SRODATA {
+ panic(fmt.Sprintf("go:plugin.tabs is %v, not SRODATA", ldr.SymType(ptab)))
+ }
+ nentries := uint64(len(ldr.Data(ptab)) / 8) // sizeof(nameOff) + sizeof(typeOff)
+ moduledata.AddAddr(ctxt.Arch, ptab)
+ moduledata.AddUint(ctxt.Arch, nentries)
+ moduledata.AddUint(ctxt.Arch, nentries)
+ } else {
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0)
+ }
+ if ctxt.BuildMode == BuildModePlugin {
+ addgostring(ctxt, ldr, moduledata, "go:link.thispluginpath", objabi.PathToPrefix(*flagPluginPath))
+
+ pkghashes := ldr.CreateSymForUpdate("go:link.pkghashes", 0)
+ pkghashes.SetLocal(true)
+ pkghashes.SetType(sym.SRODATA)
+
+ for i, l := range ctxt.Library {
+ // pkghashes[i].name
+ addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go:link.pkgname.%d", i), l.Pkg)
+ // pkghashes[i].linktimehash
+ addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go:link.pkglinkhash.%d", i), string(l.Fingerprint[:]))
+ // pkghashes[i].runtimehash
+ hash := ldr.Lookup("go:link.pkghash."+l.Pkg, 0)
+ pkghashes.AddAddr(ctxt.Arch, hash)
+ }
+ moduledata.AddAddr(ctxt.Arch, pkghashes.Sym())
+ moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library)))
+ moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library)))
+ } else {
+ moduledata.AddUint(ctxt.Arch, 0) // pluginpath
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0) // pkghashes slice
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0)
+ }
+ if len(ctxt.Shlibs) > 0 {
+ thismodulename := filepath.Base(*flagOutfile)
+ switch ctxt.BuildMode {
+ case BuildModeExe, BuildModePIE:
+ // When linking an executable, outfile is just "a.out". Make
+ // it something slightly more comprehensible.
+ thismodulename = "the executable"
+ }
+ addgostring(ctxt, ldr, moduledata, "go:link.thismodulename", thismodulename)
+
+ modulehashes := ldr.CreateSymForUpdate("go:link.abihashes", 0)
+ modulehashes.SetLocal(true)
+ modulehashes.SetType(sym.SRODATA)
+
+ for i, shlib := range ctxt.Shlibs {
+ // modulehashes[i].modulename
+ modulename := filepath.Base(shlib.Path)
+ addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go:link.libname.%d", i), modulename)
+
+ // modulehashes[i].linktimehash
+ addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go:link.linkhash.%d", i), string(shlib.Hash))
+
+ // modulehashes[i].runtimehash
+ abihash := ldr.LookupOrCreateSym("go:link.abihash."+modulename, 0)
+ ldr.SetAttrReachable(abihash, true)
+ modulehashes.AddAddr(ctxt.Arch, abihash)
+ }
+
+ moduledata.AddAddr(ctxt.Arch, modulehashes.Sym())
+ moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs)))
+ moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs)))
+ } else {
+ moduledata.AddUint(ctxt.Arch, 0) // modulename
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0) // moduleshashes slice
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0)
+ }
+
+ hasmain := ctxt.BuildMode == BuildModeExe || ctxt.BuildMode == BuildModePIE
+ if hasmain {
+ moduledata.AddUint8(1)
+ } else {
+ moduledata.AddUint8(0)
+ }
+
+ // The rest of moduledata is zero initialized.
+ // When linking an object that does not contain the runtime we are
+ // creating the moduledata from scratch and it does not have a
+ // compiler-provided size, so read it from the type data.
+ moduledatatype := ldr.Lookup("type:runtime.moduledata", 0)
+ moduledata.SetSize(decodetypeSize(ctxt.Arch, ldr.Data(moduledatatype)))
+ moduledata.Grow(moduledata.Size())
+
+ lastmoduledatap := ldr.CreateSymForUpdate("runtime.lastmoduledatap", 0)
+ if lastmoduledatap.Type() != sym.SDYNIMPORT {
+ lastmoduledatap.SetType(sym.SNOPTRDATA)
+ lastmoduledatap.SetSize(0) // overwrite existing value
+ lastmoduledatap.SetData(nil)
+ lastmoduledatap.AddAddr(ctxt.Arch, moduledata.Sym())
+ }
+ return symGroupType
+}
+
+// CarrierSymByType tracks carrier symbols and their sizes.
+var CarrierSymByType [sym.SXREF]struct {
+ Sym loader.Sym
+ Size int64
+}
+
+func setCarrierSym(typ sym.SymKind, s loader.Sym) {
+ if CarrierSymByType[typ].Sym != 0 {
+ panic(fmt.Sprintf("carrier symbol for type %v already set", typ))
+ }
+ CarrierSymByType[typ].Sym = s
+}
+
+func setCarrierSize(typ sym.SymKind, sz int64) {
+ if CarrierSymByType[typ].Size != 0 {
+ panic(fmt.Sprintf("carrier symbol size for type %v already set", typ))
+ }
+ CarrierSymByType[typ].Size = sz
+}
+
+func isStaticTmp(name string) bool {
+ return strings.Contains(name, "."+obj.StaticNamePref)
+}
+
+// Mangle function name with ABI information.
+func mangleABIName(ctxt *Link, ldr *loader.Loader, x loader.Sym, name string) string {
+ // For functions with ABI wrappers, we have to make sure that we
+ // don't wind up with two symbol table entries with the same
+ // name (since this will generated an error from the external
+ // linker). If we have wrappers, keep the ABIInternal name
+ // unmangled since we want cross-load-module calls to target
+ // ABIInternal, and rename other symbols.
+ //
+ // TODO: avoid the ldr.Lookup calls below by instead using an aux
+ // sym or marker relocation to associate the wrapper with the
+ // wrapped function.
+ if !buildcfg.Experiment.RegabiWrappers {
+ return name
+ }
+
+ if ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) != sym.SymVerABIInternal && ldr.SymVersion(x) < sym.SymVerStatic {
+ if s2 := ldr.Lookup(name, sym.SymVerABIInternal); s2 != 0 && ldr.SymType(s2) == sym.STEXT {
+ name = fmt.Sprintf("%s.abi%d", name, ldr.SymVersion(x))
+ }
+ }
+
+ // When loading a shared library, if a symbol has only one ABI,
+ // and the name is not mangled, we don't know what ABI it is.
+ // So we always mangle ABIInternal function name in shared linkage,
+ // except symbols that are exported to C. Type symbols are always
+ // ABIInternal so they are not mangled.
+ if ctxt.IsShared() {
+ if ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) == sym.SymVerABIInternal && !ldr.AttrCgoExport(x) && !strings.HasPrefix(name, "type:") {
+ name = fmt.Sprintf("%s.abiinternal", name)
+ }
+ }
+
+ return name
+}
diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go
new file mode 100644
index 0000000..d0ce99f
--- /dev/null
+++ b/src/cmd/link/internal/ld/target.go
@@ -0,0 +1,206 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "encoding/binary"
+)
+
+// Target holds the configuration we're building for.
+type Target struct {
+ Arch *sys.Arch
+
+ HeadType objabi.HeadType
+
+ LinkMode LinkMode
+ BuildMode BuildMode
+
+ linkShared bool
+ canUsePlugins bool
+ IsELF bool
+}
+
+//
+// Target type functions
+//
+
+func (t *Target) IsExe() bool {
+ return t.BuildMode == BuildModeExe
+}
+
+func (t *Target) IsShared() bool {
+ return t.BuildMode == BuildModeShared
+}
+
+func (t *Target) IsPlugin() bool {
+ return t.BuildMode == BuildModePlugin
+}
+
+func (t *Target) IsInternal() bool {
+ return t.LinkMode == LinkInternal
+}
+
+func (t *Target) IsExternal() bool {
+ return t.LinkMode == LinkExternal
+}
+
+func (t *Target) IsPIE() bool {
+ return t.BuildMode == BuildModePIE
+}
+
+func (t *Target) IsSharedGoLink() bool {
+ return t.linkShared
+}
+
+func (t *Target) CanUsePlugins() bool {
+ return t.canUsePlugins
+}
+
+func (t *Target) IsElf() bool {
+ t.mustSetHeadType()
+ return t.IsELF
+}
+
+func (t *Target) IsDynlinkingGo() bool {
+ return t.IsShared() || t.IsSharedGoLink() || t.IsPlugin() || t.CanUsePlugins()
+}
+
+// UseRelro reports whether to make use of "read only relocations" aka
+// relro.
+func (t *Target) UseRelro() bool {
+ switch t.BuildMode {
+ case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin:
+ return t.IsELF || t.HeadType == objabi.Haix || t.HeadType == objabi.Hdarwin
+ default:
+ if t.HeadType == objabi.Hdarwin && t.IsARM64() {
+ // On darwin/ARM64, everything is PIE.
+ return true
+ }
+ return t.linkShared || (t.HeadType == objabi.Haix && t.LinkMode == LinkExternal)
+ }
+}
+
+//
+// Processor functions
+//
+
+func (t *Target) Is386() bool {
+ return t.Arch.Family == sys.I386
+}
+
+func (t *Target) IsARM() bool {
+ return t.Arch.Family == sys.ARM
+}
+
+func (t *Target) IsARM64() bool {
+ return t.Arch.Family == sys.ARM64
+}
+
+func (t *Target) IsAMD64() bool {
+ return t.Arch.Family == sys.AMD64
+}
+
+func (t *Target) IsMIPS() bool {
+ return t.Arch.Family == sys.MIPS
+}
+
+func (t *Target) IsMIPS64() bool {
+ return t.Arch.Family == sys.MIPS64
+}
+
+func (t *Target) IsLOONG64() bool {
+ return t.Arch.Family == sys.Loong64
+}
+
+func (t *Target) IsPPC64() bool {
+ return t.Arch.Family == sys.PPC64
+}
+
+func (t *Target) IsRISCV64() bool {
+ return t.Arch.Family == sys.RISCV64
+}
+
+func (t *Target) IsS390X() bool {
+ return t.Arch.Family == sys.S390X
+}
+
+func (t *Target) IsWasm() bool {
+ return t.Arch.Family == sys.Wasm
+}
+
+//
+// OS Functions
+//
+
+func (t *Target) IsLinux() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hlinux
+}
+
+func (t *Target) IsDarwin() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hdarwin
+}
+
+func (t *Target) IsWindows() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hwindows
+}
+
+func (t *Target) IsPlan9() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hplan9
+}
+
+func (t *Target) IsAIX() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Haix
+}
+
+func (t *Target) IsSolaris() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hsolaris
+}
+
+func (t *Target) IsNetbsd() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hnetbsd
+}
+
+func (t *Target) IsOpenbsd() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hopenbsd
+}
+
+func (t *Target) IsFreebsd() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hfreebsd
+}
+
+func (t *Target) mustSetHeadType() {
+ if t.HeadType == objabi.Hunknown {
+ panic("HeadType is not set")
+ }
+}
+
+//
+// MISC
+//
+
+func (t *Target) IsBigEndian() bool {
+ return t.Arch.ByteOrder == binary.BigEndian
+}
+
+func (t *Target) UsesLibc() bool {
+ t.mustSetHeadType()
+ switch t.HeadType {
+ case objabi.Haix, objabi.Hdarwin, objabi.Hopenbsd, objabi.Hsolaris, objabi.Hwindows:
+ // platforms where we use libc for syscalls.
+ return true
+ }
+ return false
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
new file mode 100644
index 0000000..32a24cf
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
@@ -0,0 +1,30 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that a method of a reachable type is not necessarily
+// live even if it matches an interface method, as long as
+// the type is never converted to an interface.
+
+package main
+
+type I interface{ M() }
+
+type T int
+
+func (T) M() { println("XXX") }
+
+var p *T
+var e interface{}
+
+func main() {
+ p = new(T) // used T, but never converted to interface in any reachable code
+ e.(I).M() // used I and I.M
+}
+
+func Unused() { // convert T to interface, but this function is not reachable
+ var i I = T(0)
+ i.M()
+}
+
+var Unused2 interface{} = T(1) // convert T to interface, in an unreachable global initializer
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go
new file mode 100644
index 0000000..48ba55d
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go
@@ -0,0 +1,22 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that a method *is* live if it matches an interface
+// method and the type is "indirectly" converted to an
+// interface through reflection.
+
+package main
+
+import "reflect"
+
+type I interface{ M() }
+
+type T int
+
+func (T) M() { println("XXX") }
+
+func main() {
+ e := reflect.ValueOf([]T{1}).Index(0).Interface()
+ e.(I).M()
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go
new file mode 100644
index 0000000..37c8937
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go
@@ -0,0 +1,29 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Like ifacemethod2.go, this tests that a method *is* live
+// if the type is "indirectly" converted to an interface
+// using reflection with a method descriptor as intermediate.
+
+package main
+
+import "reflect"
+
+type S int
+
+func (s S) M() { println("S.M") }
+
+type I interface{ M() }
+
+type T float64
+
+func (t T) F(s S) {}
+
+func main() {
+ var t T
+ ft := reflect.TypeOf(t).Method(0).Type
+ at := ft.In(1)
+ v := reflect.New(at).Elem()
+ v.Interface().(I).M()
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go
new file mode 100644
index 0000000..4af47ad
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go
@@ -0,0 +1,25 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that a live type's method is not live even if
+// it matches an interface method, as long as the interface
+// method is not used.
+
+package main
+
+type T int
+
+//go:noinline
+func (T) M() {}
+
+type I interface{ M() }
+
+var p *T
+var pp *I
+
+func main() {
+ p = new(T) // use type T
+ pp = new(I) // use type I
+ *pp = *p // convert T to I, build itab
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go b/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go
new file mode 100644
index 0000000..af95e46
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go
@@ -0,0 +1,24 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This example uses reflect.Value.Call, but not
+// reflect.{Value,Type}.Method. This should not
+// need to bring all methods live.
+
+package main
+
+import "reflect"
+
+func f() { println("call") }
+
+type T int
+
+func (T) M() {}
+
+func main() {
+ v := reflect.ValueOf(f)
+ v.Call(nil)
+ i := interface{}(T(1))
+ println(i)
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go b/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go
new file mode 100644
index 0000000..8246093
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go
@@ -0,0 +1,16 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that a live variable doesn't bring its type
+// descriptor live.
+
+package main
+
+type T [10]string
+
+var t T
+
+func main() {
+ println(t[8])
+}
diff --git a/src/cmd/link/internal/ld/testdata/httptest/main/main.go b/src/cmd/link/internal/ld/testdata/httptest/main/main.go
new file mode 100644
index 0000000..1bce301
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/httptest/main/main.go
@@ -0,0 +1,22 @@
+// A small test program that uses the net/http package. There is
+// nothing special about net/http here, this is just a convenient way
+// to pull in a lot of code.
+
+package main
+
+import (
+ "net/http"
+ "net/http/httptest"
+)
+
+type statusHandler int
+
+func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(int(*h))
+}
+
+func main() {
+ status := statusHandler(http.StatusNotFound)
+ s := httptest.NewServer(&status)
+ defer s.Close()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue10978/main.go b/src/cmd/link/internal/ld/testdata/issue10978/main.go
new file mode 100644
index 0000000..5e8c097
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue10978/main.go
@@ -0,0 +1,27 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func undefined()
+
+func defined1() int {
+ // To check multiple errors for a single symbol,
+ // reference undefined more than once.
+ undefined()
+ undefined()
+ return 0
+}
+
+func defined2() {
+ undefined()
+ undefined()
+}
+
+func init() {
+ _ = defined1()
+ defined2()
+}
+
+// The "main" function remains undeclared.
diff --git a/src/cmd/link/internal/ld/testdata/issue10978/main.s b/src/cmd/link/internal/ld/testdata/issue10978/main.s
new file mode 100644
index 0000000..1d00e76
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue10978/main.s
@@ -0,0 +1 @@
+// This file is needed to make "go build" work for package with external functions.
diff --git a/src/cmd/link/internal/ld/testdata/issue25459/a/a.go b/src/cmd/link/internal/ld/testdata/issue25459/a/a.go
new file mode 100644
index 0000000..6032d76
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue25459/a/a.go
@@ -0,0 +1,27 @@
+package a
+
+const Always = true
+
+var Count int
+
+type FuncReturningInt func() int
+
+var PointerToConstIf FuncReturningInt
+
+func ConstIf() int {
+ if Always {
+ return 1
+ }
+ var imdead [4]int
+ imdead[Count] = 1
+ return imdead[0]
+}
+
+func CallConstIf() int {
+ Count += 3
+ return ConstIf()
+}
+
+func Another() {
+ defer func() { PointerToConstIf = ConstIf; Count += 1 }()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue25459/main/main.go b/src/cmd/link/internal/ld/testdata/issue25459/main/main.go
new file mode 100644
index 0000000..7b5796d
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue25459/main/main.go
@@ -0,0 +1,10 @@
+package main
+
+import "cmd/link/internal/ld/testdata/issue25459/a"
+
+var Glob int
+
+func main() {
+ a.Another()
+ Glob += a.ConstIf() + a.CallConstIf()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go b/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go
new file mode 100644
index 0000000..ca57749
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go
@@ -0,0 +1,16 @@
+package b
+
+var q int
+
+func Top(x int) int {
+ q += 1
+ if q != x {
+ return 3
+ }
+ return 4
+}
+
+func OOO(x int) int {
+ defer func() { q += x & 7 }()
+ return Top(x + 1)
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue26237/main/main.go b/src/cmd/link/internal/ld/testdata/issue26237/main/main.go
new file mode 100644
index 0000000..fdb1223
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue26237/main/main.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+ "fmt"
+
+ b "cmd/link/internal/ld/testdata/issue26237/b.dir"
+)
+
+var skyx int
+
+func main() {
+ skyx += b.OOO(skyx)
+ if b.Top(1) == 99 {
+ fmt.Printf("Beware the Jabberwock, my son!\n")
+ }
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m b/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m
new file mode 100644
index 0000000..9462788
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m
@@ -0,0 +1,16 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+#import <AppKit/NSAppearance.h>
+
+BOOL function(void) {
+#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED > 101300)
+ NSAppearance *darkAppearance;
+ if (@available(macOS 10.14, *)) {
+ darkAppearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
+ }
+#endif
+ return NO;
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go b/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go
new file mode 100644
index 0000000..514b9b9
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go
@@ -0,0 +1,19 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package lib
+
+/*
+#cgo darwin CFLAGS: -D__MAC_OS_X_VERSION_MAX_ALLOWED=101450
+#cgo darwin LDFLAGS: -framework Foundation -framework AppKit
+#include "stdlib.h"
+int function(void);
+*/
+import "C"
+import "fmt"
+
+func DoC() {
+ C.function()
+ fmt.Println("called c function")
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue32233/main/main.go b/src/cmd/link/internal/ld/testdata/issue32233/main/main.go
new file mode 100644
index 0000000..ed0838a
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue32233/main/main.go
@@ -0,0 +1,11 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "cmd/link/internal/ld/testdata/issue32233/lib"
+
+func main() {
+ lib.DoC()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue38192/main.go b/src/cmd/link/internal/ld/testdata/issue38192/main.go
new file mode 100644
index 0000000..3b7df60
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue38192/main.go
@@ -0,0 +1,11 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func singleInstruction()
+
+func main() {
+ singleInstruction()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue38192/oneline.s b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s
new file mode 100644
index 0000000..f5290d7
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s
@@ -0,0 +1,8 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT ·singleInstruction(SB),NOSPLIT,$0
+ RET
diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.go b/src/cmd/link/internal/ld/testdata/issue39256/x.go
new file mode 100644
index 0000000..97bc1cc
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue39256/x.go
@@ -0,0 +1,22 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ _ "unsafe"
+)
+
+//go:cgo_import_dynamic libc_getpid getpid "libc.so"
+//go:cgo_import_dynamic libc_kill kill "libc.so"
+//go:cgo_import_dynamic libc_close close "libc.so"
+//go:cgo_import_dynamic libc_open open "libc.so"
+
+//go:cgo_import_dynamic _ _ "libc.so"
+
+func trampoline()
+
+func main() {
+ trampoline()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.s b/src/cmd/link/internal/ld/testdata/issue39256/x.s
new file mode 100644
index 0000000..41a54b2
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue39256/x.s
@@ -0,0 +1,10 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+TEXT ·trampoline(SB),0,$0
+ CALL libc_getpid(SB)
+ CALL libc_kill(SB)
+ CALL libc_open(SB)
+ CALL libc_close(SB)
+ RET
diff --git a/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go
new file mode 100644
index 0000000..76e0ea1
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go
@@ -0,0 +1,15 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+var G int
+
+func main() {
+ if G != 101 {
+ println("not 101")
+ } else {
+ println("well now that's interesting")
+ }
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue42484/main.go b/src/cmd/link/internal/ld/testdata/issue42484/main.go
new file mode 100644
index 0000000..60fc110
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue42484/main.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ a := 0
+ a++
+ b := 0
+ f1(a, b)
+}
+
+func f1(a, b int) {
+ fmt.Printf("%d %d\n", a, b)
+}
diff --git a/src/cmd/link/internal/ld/testdata/stackcheck/main.go b/src/cmd/link/internal/ld/testdata/stackcheck/main.go
new file mode 100644
index 0000000..b708cc5
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/stackcheck/main.go
@@ -0,0 +1,20 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main() { asmMain() }
+
+func asmMain()
+
+func startSelf()
+
+func startChain()
+func chain0()
+func chain1()
+func chain2()
+func chainEnd()
+
+func startRec()
+func startRec0()
diff --git a/src/cmd/link/internal/ld/testdata/stackcheck/main.s b/src/cmd/link/internal/ld/testdata/stackcheck/main.s
new file mode 100644
index 0000000..10f6a3f
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/stackcheck/main.s
@@ -0,0 +1,40 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#define NOSPLIT 7
+
+TEXT ·asmMain(SB),0,$0-0
+ CALL ·startSelf(SB)
+ CALL ·startChain(SB)
+ CALL ·startRec(SB)
+ RET
+
+// Test reporting of basic over-the-limit
+TEXT ·startSelf(SB),NOSPLIT,$1000-0
+ RET
+
+// Test reporting of multiple over-the-limit chains
+TEXT ·startChain(SB),NOSPLIT,$16-0
+ CALL ·chain0(SB)
+ CALL ·chain1(SB)
+ CALL ·chain2(SB)
+ RET
+TEXT ·chain0(SB),NOSPLIT,$32-0
+ CALL ·chainEnd(SB)
+ RET
+TEXT ·chain1(SB),NOSPLIT,$48-0 // Doesn't go over
+ RET
+TEXT ·chain2(SB),NOSPLIT,$64-0
+ CALL ·chainEnd(SB)
+ RET
+TEXT ·chainEnd(SB),NOSPLIT,$1000-0 // Should be reported twice
+ RET
+
+// Test reporting of rootless recursion
+TEXT ·startRec(SB),NOSPLIT,$0-0
+ CALL ·startRec0(SB)
+ RET
+TEXT ·startRec0(SB),NOSPLIT,$0-0
+ CALL ·startRec(SB)
+ RET
diff --git a/src/cmd/link/internal/ld/typelink.go b/src/cmd/link/internal/ld/typelink.go
new file mode 100644
index 0000000..5eca6e0
--- /dev/null
+++ b/src/cmd/link/internal/ld/typelink.go
@@ -0,0 +1,72 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "sort"
+)
+
+type byTypeStr []typelinkSortKey
+
+type typelinkSortKey struct {
+ TypeStr string
+ Type loader.Sym
+}
+
+func (s byTypeStr) Less(i, j int) bool { return s[i].TypeStr < s[j].TypeStr }
+func (s byTypeStr) Len() int { return len(s) }
+func (s byTypeStr) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// typelink generates the typelink table which is used by reflect.typelinks().
+// Types that should be added to the typelinks table are marked with the
+// MakeTypelink attribute by the compiler.
+func (ctxt *Link) typelink() {
+ ldr := ctxt.loader
+ typelinks := byTypeStr{}
+ var itabs []loader.Sym
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.IsTypelink(s) {
+ typelinks = append(typelinks, typelinkSortKey{decodetypeStr(ldr, ctxt.Arch, s), s})
+ } else if ldr.IsItab(s) {
+ itabs = append(itabs, s)
+ }
+ }
+ sort.Sort(typelinks)
+
+ tl := ldr.CreateSymForUpdate("runtime.typelink", 0)
+ tl.SetType(sym.STYPELINK)
+ ldr.SetAttrLocal(tl.Sym(), true)
+ tl.SetSize(int64(4 * len(typelinks)))
+ tl.Grow(tl.Size())
+ relocs := tl.AddRelocs(len(typelinks))
+ for i, s := range typelinks {
+ r := relocs.At(i)
+ r.SetSym(s.Type)
+ r.SetOff(int32(i * 4))
+ r.SetSiz(4)
+ r.SetType(objabi.R_ADDROFF)
+ }
+
+ ptrsize := ctxt.Arch.PtrSize
+ il := ldr.CreateSymForUpdate("runtime.itablink", 0)
+ il.SetType(sym.SITABLINK)
+ ldr.SetAttrLocal(il.Sym(), true)
+ il.SetSize(int64(ptrsize * len(itabs)))
+ il.Grow(il.Size())
+ relocs = il.AddRelocs(len(itabs))
+ for i, s := range itabs {
+ r := relocs.At(i)
+ r.SetSym(s)
+ r.SetOff(int32(i * ptrsize))
+ r.SetSiz(uint8(ptrsize))
+ r.SetType(objabi.R_ADDR)
+ }
+}
diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go
new file mode 100644
index 0000000..779f498
--- /dev/null
+++ b/src/cmd/link/internal/ld/util.go
@@ -0,0 +1,114 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/link/internal/loader"
+ "encoding/binary"
+ "fmt"
+ "os"
+)
+
+var atExitFuncs []func()
+
+func AtExit(f func()) {
+ atExitFuncs = append(atExitFuncs, f)
+}
+
+// runAtExitFuncs runs the queued set of AtExit functions.
+func runAtExitFuncs() {
+ for i := len(atExitFuncs) - 1; i >= 0; i-- {
+ atExitFuncs[i]()
+ }
+ atExitFuncs = nil
+}
+
+// Exit exits with code after executing all atExitFuncs.
+func Exit(code int) {
+ runAtExitFuncs()
+ os.Exit(code)
+}
+
+// Exitf logs an error message then calls Exit(2).
+func Exitf(format string, a ...interface{}) {
+ fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...)
+ nerrors++
+ Exit(2)
+}
+
+// afterErrorAction updates 'nerrors' on error and invokes exit or
+// panics in the proper circumstances.
+func afterErrorAction() {
+ nerrors++
+ if *flagH {
+ panic("error")
+ }
+ if nerrors > 20 {
+ Exitf("too many errors")
+ }
+}
+
+// Errorf logs an error message.
+//
+// If more than 20 errors have been printed, exit with an error.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+//
+// TODO: remove. Use ctxt.Errorf instead.
+// All remaining calls use nil as first arg.
+func Errorf(dummy *int, format string, args ...interface{}) {
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ afterErrorAction()
+}
+
+// Errorf method logs an error message.
+//
+// If more than 20 errors have been printed, exit with an error.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+func (ctxt *Link) Errorf(s loader.Sym, format string, args ...interface{}) {
+ if ctxt.loader != nil {
+ ctxt.loader.Errorf(s, format, args...)
+ return
+ }
+ // Note: this is not expected to happen very often.
+ format = fmt.Sprintf("sym %d: %s", s, format)
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ afterErrorAction()
+}
+
+func artrim(x []byte) string {
+ i := 0
+ j := len(x)
+ for i < len(x) && x[i] == ' ' {
+ i++
+ }
+ for j > i && x[j-1] == ' ' {
+ j--
+ }
+ return string(x[i:j])
+}
+
+func stringtouint32(x []uint32, s string) {
+ for i := 0; len(s) > 0; i++ {
+ var buf [4]byte
+ s = s[copy(buf[:], s):]
+ x[i] = binary.LittleEndian.Uint32(buf[:])
+ }
+}
+
+// contains reports whether v is in s.
+func contains(s []string, v string) bool {
+ for _, x := range s {
+ if x == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go
new file mode 100644
index 0000000..1265c45
--- /dev/null
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -0,0 +1,1815 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math/bits"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+)
+
+// This file handles all algorithms related to XCOFF files generation.
+// Most of them are adaptations of the ones in cmd/link/internal/pe.go
+// as PE and XCOFF are based on COFF files.
+// XCOFF files generated are 64 bits.
+
+const (
+ // Total amount of space to reserve at the start of the file
+ // for File Header, Auxiliary Header, and Section Headers.
+ // May waste some.
+ XCOFFHDRRESERVE = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23
+
+ // base on dump -o, then rounded from 32B to 64B to
+ // match worst case elf text section alignment on ppc64.
+ XCOFFSECTALIGN int64 = 64
+
+ // XCOFF binaries should normally have all its sections position-independent.
+ // However, this is not yet possible for .text because of some R_ADDR relocations
+ // inside RODATA symbols.
+ // .data and .bss are position-independent so their address start inside a unreachable
+ // segment during execution to force segfault if something is wrong.
+ XCOFFTEXTBASE = 0x100000000 // Start of text address
+ XCOFFDATABASE = 0x200000000 // Start of data address
+)
+
+// File Header
+type XcoffFileHdr64 struct {
+ Fmagic uint16 // Target machine
+ Fnscns uint16 // Number of sections
+ Ftimedat int32 // Time and date of file creation
+ Fsymptr uint64 // Byte offset to symbol table start
+ Fopthdr uint16 // Number of bytes in optional header
+ Fflags uint16 // Flags
+ Fnsyms int32 // Number of entries in symbol table
+}
+
+const (
+ U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF
+)
+
+// Flags that describe the type of the object file.
+const (
+ F_RELFLG = 0x0001
+ F_EXEC = 0x0002
+ F_LNNO = 0x0004
+ F_FDPR_PROF = 0x0010
+ F_FDPR_OPTI = 0x0020
+ F_DSA = 0x0040
+ F_VARPG = 0x0100
+ F_DYNLOAD = 0x1000
+ F_SHROBJ = 0x2000
+ F_LOADONLY = 0x4000
+)
+
+// Auxiliary Header
+type XcoffAoutHdr64 struct {
+ Omagic int16 // Flags - Ignored If Vstamp Is 1
+ Ovstamp int16 // Version
+ Odebugger uint32 // Reserved For Debugger
+ Otextstart uint64 // Virtual Address Of Text
+ Odatastart uint64 // Virtual Address Of Data
+ Otoc uint64 // Toc Address
+ Osnentry int16 // Section Number For Entry Point
+ Osntext int16 // Section Number For Text
+ Osndata int16 // Section Number For Data
+ Osntoc int16 // Section Number For Toc
+ Osnloader int16 // Section Number For Loader
+ Osnbss int16 // Section Number For Bss
+ Oalgntext int16 // Max Text Alignment
+ Oalgndata int16 // Max Data Alignment
+ Omodtype [2]byte // Module Type Field
+ Ocpuflag uint8 // Bit Flags - Cputypes Of Objects
+ Ocputype uint8 // Reserved for CPU type
+ Otextpsize uint8 // Requested text page size
+ Odatapsize uint8 // Requested data page size
+ Ostackpsize uint8 // Requested stack page size
+ Oflags uint8 // Flags And TLS Alignment
+ Otsize uint64 // Text Size In Bytes
+ Odsize uint64 // Data Size In Bytes
+ Obsize uint64 // Bss Size In Bytes
+ Oentry uint64 // Entry Point Address
+ Omaxstack uint64 // Max Stack Size Allowed
+ Omaxdata uint64 // Max Data Size Allowed
+ Osntdata int16 // Section Number For Tdata Section
+ Osntbss int16 // Section Number For Tbss Section
+ Ox64flags uint16 // Additional Flags For 64-Bit Objects
+ Oresv3a int16 // Reserved
+ Oresv3 [2]int32 // Reserved
+}
+
+// Section Header
+type XcoffScnHdr64 struct {
+ Sname [8]byte // Section Name
+ Spaddr uint64 // Physical Address
+ Svaddr uint64 // Virtual Address
+ Ssize uint64 // Section Size
+ Sscnptr uint64 // File Offset To Raw Data
+ Srelptr uint64 // File Offset To Relocation
+ Slnnoptr uint64 // File Offset To Line Numbers
+ Snreloc uint32 // Number Of Relocation Entries
+ Snlnno uint32 // Number Of Line Number Entries
+ Sflags uint32 // flags
+}
+
+// Flags defining the section type.
+const (
+ STYP_DWARF = 0x0010
+ STYP_TEXT = 0x0020
+ STYP_DATA = 0x0040
+ STYP_BSS = 0x0080
+ STYP_EXCEPT = 0x0100
+ STYP_INFO = 0x0200
+ STYP_TDATA = 0x0400
+ STYP_TBSS = 0x0800
+ STYP_LOADER = 0x1000
+ STYP_DEBUG = 0x2000
+ STYP_TYPCHK = 0x4000
+ STYP_OVRFLO = 0x8000
+)
+const (
+ SSUBTYP_DWINFO = 0x10000 // DWARF info section
+ SSUBTYP_DWLINE = 0x20000 // DWARF line-number section
+ SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section
+ SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section
+ SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section
+ SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section
+ SSUBTYP_DWSTR = 0x70000 // DWARF strings section
+ SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section
+ SSUBTYP_DWLOC = 0x90000 // DWARF location lists section
+ SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section
+ SSUBTYP_DWMAC = 0xB0000 // DWARF macros section
+)
+
+// Headers size
+const (
+ FILHSZ_32 = 20
+ FILHSZ_64 = 24
+ AOUTHSZ_EXEC32 = 72
+ AOUTHSZ_EXEC64 = 120
+ SCNHSZ_32 = 40
+ SCNHSZ_64 = 72
+ LDHDRSZ_32 = 32
+ LDHDRSZ_64 = 56
+ LDSYMSZ_64 = 24
+ RELSZ_64 = 14
+)
+
+// Type representing all XCOFF symbols.
+type xcoffSym interface {
+}
+
+// Symbol Table Entry
+type XcoffSymEnt64 struct {
+ Nvalue uint64 // Symbol value
+ Noffset uint32 // Offset of the name in string table or .debug section
+ Nscnum int16 // Section number of symbol
+ Ntype uint16 // Basic and derived type specification
+ Nsclass uint8 // Storage class of symbol
+ Nnumaux int8 // Number of auxiliary entries
+}
+
+const SYMESZ = 18
+
+const (
+ // Nscnum
+ N_DEBUG = -2
+ N_ABS = -1
+ N_UNDEF = 0
+
+ //Ntype
+ SYM_V_INTERNAL = 0x1000
+ SYM_V_HIDDEN = 0x2000
+ SYM_V_PROTECTED = 0x3000
+ SYM_V_EXPORTED = 0x4000
+ SYM_TYPE_FUNC = 0x0020 // is function
+)
+
+// Storage Class.
+const (
+ C_NULL = 0 // Symbol table entry marked for deletion
+ C_EXT = 2 // External symbol
+ C_STAT = 3 // Static symbol
+ C_BLOCK = 100 // Beginning or end of inner block
+ C_FCN = 101 // Beginning or end of function
+ C_FILE = 103 // Source file name and compiler information
+ C_HIDEXT = 107 // Unnamed external symbol
+ C_BINCL = 108 // Beginning of include file
+ C_EINCL = 109 // End of include file
+ C_WEAKEXT = 111 // Weak external symbol
+ C_DWARF = 112 // DWARF symbol
+ C_GSYM = 128 // Global variable
+ C_LSYM = 129 // Automatic variable allocated on stack
+ C_PSYM = 130 // Argument to subroutine allocated on stack
+ C_RSYM = 131 // Register variable
+ C_RPSYM = 132 // Argument to function or procedure stored in register
+ C_STSYM = 133 // Statically allocated symbol
+ C_BCOMM = 135 // Beginning of common block
+ C_ECOML = 136 // Local member of common block
+ C_ECOMM = 137 // End of common block
+ C_DECL = 140 // Declaration of object
+ C_ENTRY = 141 // Alternate entry
+ C_FUN = 142 // Function or procedure
+ C_BSTAT = 143 // Beginning of static block
+ C_ESTAT = 144 // End of static block
+ C_GTLS = 145 // Global thread-local variable
+ C_STTLS = 146 // Static thread-local variable
+)
+
+// File Auxiliary Entry
+type XcoffAuxFile64 struct {
+ Xzeroes uint32 // The name is always in the string table
+ Xoffset uint32 // Offset in the string table
+ X_pad1 [6]byte
+ Xftype uint8 // Source file string type
+ X_pad2 [2]byte
+ Xauxtype uint8 // Type of auxiliary entry
+}
+
+// Function Auxiliary Entry
+type XcoffAuxFcn64 struct {
+ Xlnnoptr uint64 // File pointer to line number
+ Xfsize uint32 // Size of function in bytes
+ Xendndx uint32 // Symbol table index of next entry
+ Xpad uint8 // Unused
+ Xauxtype uint8 // Type of auxiliary entry
+}
+
+// csect Auxiliary Entry.
+type XcoffAuxCSect64 struct {
+ Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index
+ Xparmhash uint32 // Offset of parameter type-check string
+ Xsnhash uint16 // .typchk section number
+ Xsmtyp uint8 // Symbol alignment and type
+ Xsmclas uint8 // Storage-mapping class
+ Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index
+ Xpad uint8 // Unused
+ Xauxtype uint8 // Type of auxiliary entry
+}
+
+// DWARF Auxiliary Entry
+type XcoffAuxDWARF64 struct {
+ Xscnlen uint64 // Length of this symbol section
+ X_pad [9]byte
+ Xauxtype uint8 // Type of auxiliary entry
+}
+
+// Auxiliary type
+const (
+ _AUX_EXCEPT = 255
+ _AUX_FCN = 254
+ _AUX_SYM = 253
+ _AUX_FILE = 252
+ _AUX_CSECT = 251
+ _AUX_SECT = 250
+)
+
+// Xftype field
+const (
+ XFT_FN = 0 // Source File Name
+ XFT_CT = 1 // Compile Time Stamp
+ XFT_CV = 2 // Compiler Version Number
+ XFT_CD = 128 // Compiler Defined Information/
+
+)
+
+// Symbol type field.
+const (
+ XTY_ER = 0 // External reference
+ XTY_SD = 1 // Section definition
+ XTY_LD = 2 // Label definition
+ XTY_CM = 3 // Common csect definition
+ XTY_WK = 0x8 // Weak symbol
+ XTY_EXP = 0x10 // Exported symbol
+ XTY_ENT = 0x20 // Entry point symbol
+ XTY_IMP = 0x40 // Imported symbol
+)
+
+// Storage-mapping class.
+const (
+ XMC_PR = 0 // Program code
+ XMC_RO = 1 // Read-only constant
+ XMC_DB = 2 // Debug dictionary table
+ XMC_TC = 3 // TOC entry
+ XMC_UA = 4 // Unclassified
+ XMC_RW = 5 // Read/Write data
+ XMC_GL = 6 // Global linkage
+ XMC_XO = 7 // Extended operation
+ XMC_SV = 8 // 32-bit supervisor call descriptor
+ XMC_BS = 9 // BSS class
+ XMC_DS = 10 // Function descriptor
+ XMC_UC = 11 // Unnamed FORTRAN common
+ XMC_TC0 = 15 // TOC anchor
+ XMC_TD = 16 // Scalar data entry in the TOC
+ XMC_SV64 = 17 // 64-bit supervisor call descriptor
+ XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit
+ XMC_TL = 20 // Read/Write thread-local data
+ XMC_UL = 21 // Read/Write thread-local data (.tbss)
+ XMC_TE = 22 // TOC entry
+)
+
+// Loader Header
+type XcoffLdHdr64 struct {
+ Lversion int32 // Loader section version number
+ Lnsyms int32 // Number of symbol table entries
+ Lnreloc int32 // Number of relocation table entries
+ Listlen uint32 // Length of import file ID string table
+ Lnimpid int32 // Number of import file IDs
+ Lstlen uint32 // Length of string table
+ Limpoff uint64 // Offset to start of import file IDs
+ Lstoff uint64 // Offset to start of string table
+ Lsymoff uint64 // Offset to start of symbol table
+ Lrldoff uint64 // Offset to start of relocation entries
+}
+
+// Loader Symbol
+type XcoffLdSym64 struct {
+ Lvalue uint64 // Address field
+ Loffset uint32 // Byte offset into string table of symbol name
+ Lscnum int16 // Section number containing symbol
+ Lsmtype int8 // Symbol type, export, import flags
+ Lsmclas int8 // Symbol storage class
+ Lifile int32 // Import file ID; ordinal of import file IDs
+ Lparm uint32 // Parameter type-check field
+}
+
+type xcoffLoaderSymbol struct {
+ sym loader.Sym
+ smtype int8
+ smclas int8
+}
+
+type XcoffLdImportFile64 struct {
+ Limpidpath string
+ Limpidbase string
+ Limpidmem string
+}
+
+type XcoffLdRel64 struct {
+ Lvaddr uint64 // Address Field
+ Lrtype uint16 // Relocation Size and Type
+ Lrsecnm int16 // Section Number being relocated
+ Lsymndx int32 // Loader-Section symbol table index
+}
+
+// xcoffLoaderReloc holds information about a relocation made by the loader.
+type xcoffLoaderReloc struct {
+ sym loader.Sym
+ roff int32
+ rtype uint16
+ symndx int32
+}
+
+const (
+ XCOFF_R_POS = 0x00 // A(sym) Positive Relocation
+ XCOFF_R_NEG = 0x01 // -A(sym) Negative Relocation
+ XCOFF_R_REL = 0x02 // A(sym-*) Relative to self
+ XCOFF_R_TOC = 0x03 // A(sym-TOC) Relative to TOC
+ XCOFF_R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load.
+
+ XCOFF_R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst
+ XCOFF_R_GL = 0x05 // A(external TOC of sym) Global Linkage
+ XCOFF_R_TCL = 0x06 // A(local TOC of sym) Local object TOC address
+ XCOFF_R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction
+ XCOFF_R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction
+ XCOFF_R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect
+ XCOFF_R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction
+ XCOFF_R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction
+ XCOFF_R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable
+ XCOFF_R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr
+
+ XCOFF_R_TLS = 0x20 // General-dynamic reference to TLS symbol
+ XCOFF_R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol
+ XCOFF_R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol
+ XCOFF_R_TLS_LE = 0x23 // Local-exec reference to TLS symbol
+ XCOFF_R_TLSM = 0x24 // Module reference to TLS symbol
+ XCOFF_R_TLSML = 0x25 // Module reference to local (own) module
+
+ XCOFF_R_TOCU = 0x30 // Relative to TOC - high order bits
+ XCOFF_R_TOCL = 0x31 // Relative to TOC - low order bits
+)
+
+type XcoffLdStr64 struct {
+ size uint16
+ name string
+}
+
+// xcoffFile is used to build XCOFF file.
+type xcoffFile struct {
+ xfhdr XcoffFileHdr64
+ xahdr XcoffAoutHdr64
+ sections []*XcoffScnHdr64
+ sectText *XcoffScnHdr64
+ sectData *XcoffScnHdr64
+ sectBss *XcoffScnHdr64
+ stringTable xcoffStringTable
+ sectNameToScnum map[string]int16
+ loaderSize uint64
+ symtabOffset int64 // offset to the start of symbol table
+ symbolCount uint32 // number of symbol table records written
+ symtabSym []xcoffSym // XCOFF symbols for the symbol table
+ dynLibraries map[string]int // Dynamic libraries in .loader section. The integer represents its import file number (- 1)
+ loaderSymbols []*xcoffLoaderSymbol // symbols inside .loader symbol table
+ loaderReloc []*xcoffLoaderReloc // Reloc that must be made inside loader
+ sync.Mutex // currently protect loaderReloc
+}
+
+// Var used by XCOFF Generation algorithms
+var (
+ xfile xcoffFile
+)
+
+// xcoffStringTable is a XCOFF string table.
+type xcoffStringTable struct {
+ strings []string
+ stringsLen int
+}
+
+// size returns size of string table t.
+func (t *xcoffStringTable) size() int {
+ // string table starts with 4-byte length at the beginning
+ return t.stringsLen + 4
+}
+
+// add adds string str to string table t.
+func (t *xcoffStringTable) add(str string) int {
+ off := t.size()
+ t.strings = append(t.strings, str)
+ t.stringsLen += len(str) + 1 // each string will have 0 appended to it
+ return off
+}
+
+// write writes string table t into the output file.
+func (t *xcoffStringTable) write(out *OutBuf) {
+ out.Write32(uint32(t.size()))
+ for _, s := range t.strings {
+ out.WriteString(s)
+ out.Write8(0)
+ }
+}
+
+// write writes XCOFF section sect into the output file.
+func (sect *XcoffScnHdr64) write(ctxt *Link) {
+ binary.Write(ctxt.Out, binary.BigEndian, sect)
+ ctxt.Out.Write32(0) // Add 4 empty bytes at the end to match alignment
+}
+
+// addSection adds section to the XCOFF file f.
+func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 {
+ sect := &XcoffScnHdr64{
+ Spaddr: addr,
+ Svaddr: addr,
+ Ssize: size,
+ Sscnptr: fileoff,
+ Sflags: flags,
+ }
+ copy(sect.Sname[:], name) // copy string to [8]byte
+ f.sections = append(f.sections, sect)
+ f.sectNameToScnum[name] = int16(len(f.sections))
+ return sect
+}
+
+// addDwarfSection adds a dwarf section to the XCOFF file f.
+// This function is similar to addSection, but Dwarf section names
+// must be modified to conventional names and they are various subtypes.
+func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 {
+ newName, subtype := xcoffGetDwarfSubtype(s.Name)
+ return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype)
+}
+
+// xcoffGetDwarfSubtype returns the XCOFF name of the DWARF section str
+// and its subtype constant.
+func xcoffGetDwarfSubtype(str string) (string, uint32) {
+ switch str {
+ default:
+ Exitf("unknown DWARF section name for XCOFF: %s", str)
+ case ".debug_abbrev":
+ return ".dwabrev", SSUBTYP_DWABREV
+ case ".debug_info":
+ return ".dwinfo", SSUBTYP_DWINFO
+ case ".debug_frame":
+ return ".dwframe", SSUBTYP_DWFRAME
+ case ".debug_line":
+ return ".dwline", SSUBTYP_DWLINE
+ case ".debug_loc":
+ return ".dwloc", SSUBTYP_DWLOC
+ case ".debug_pubnames":
+ return ".dwpbnms", SSUBTYP_DWPBNMS
+ case ".debug_pubtypes":
+ return ".dwpbtyp", SSUBTYP_DWPBTYP
+ case ".debug_ranges":
+ return ".dwrnges", SSUBTYP_DWRNGES
+ }
+ // never used
+ return "", 0
+}
+
+// getXCOFFscnum returns the XCOFF section number of a Go section.
+func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 {
+ switch sect.Seg {
+ case &Segtext:
+ return f.sectNameToScnum[".text"]
+ case &Segdata:
+ if sect.Name == ".noptrbss" || sect.Name == ".bss" {
+ return f.sectNameToScnum[".bss"]
+ }
+ if sect.Name == ".tbss" {
+ return f.sectNameToScnum[".tbss"]
+ }
+ return f.sectNameToScnum[".data"]
+ case &Segdwarf:
+ name, _ := xcoffGetDwarfSubtype(sect.Name)
+ return f.sectNameToScnum[name]
+ case &Segrelrodata:
+ return f.sectNameToScnum[".data"]
+ }
+ Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name)
+ return -1
+}
+
+// Xcoffinit initialised some internal value and setups
+// already known header information.
+func Xcoffinit(ctxt *Link) {
+ xfile.dynLibraries = make(map[string]int)
+
+ HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN))
+ if *FlagTextAddr != -1 {
+ Errorf(nil, "-T not available on AIX")
+ }
+ *FlagTextAddr = XCOFFTEXTBASE + int64(HEADR)
+ if *FlagRound != -1 {
+ Errorf(nil, "-R not available on AIX")
+ }
+ *FlagRound = int(XCOFFSECTALIGN)
+
+}
+
+// SYMBOL TABLE
+
+// type records C_FILE information needed for genasmsym in XCOFF.
+type xcoffSymSrcFile struct {
+ name string
+ file *XcoffSymEnt64 // Symbol of this C_FILE
+ csectAux *XcoffAuxCSect64 // Symbol for the current .csect
+ csectSymNb uint64 // Symbol number for the current .csect
+ csectVAStart int64
+ csectVAEnd int64
+}
+
+var (
+ currDwscnoff = make(map[string]uint64) // Needed to create C_DWARF symbols
+ currSymSrcFile xcoffSymSrcFile
+ outerSymSize = make(map[string]int64)
+)
+
+// xcoffUpdateOuterSize stores the size of outer symbols in order to have it
+// in the symbol table.
+func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
+ if size == 0 {
+ return
+ }
+ // TODO: use CarrierSymByType
+
+ ldr := ctxt.loader
+ switch stype {
+ default:
+ Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String())
+ case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING:
+ // Nothing to do
+ case sym.STYPERELRO:
+ if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
+ // runtime.types size must be removed, as it's a real symbol.
+ tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
+ outerSymSize["typerel.*"] = size - tsize
+ return
+ }
+ fallthrough
+ case sym.STYPE:
+ if !ctxt.DynlinkingGo() {
+ // runtime.types size must be removed, as it's a real symbol.
+ tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
+ outerSymSize["type:*"] = size - tsize
+ }
+ case sym.SGOSTRING:
+ outerSymSize["go:string.*"] = size
+ case sym.SGOFUNC:
+ if !ctxt.DynlinkingGo() {
+ outerSymSize["go:func.*"] = size
+ }
+ case sym.SGOFUNCRELRO:
+ outerSymSize["go:funcrel.*"] = size
+ case sym.SGCBITS:
+ outerSymSize["runtime.gcbits.*"] = size
+ case sym.SPCLNTAB:
+ outerSymSize["runtime.pclntab"] = size
+ }
+}
+
+// addSymbol writes a symbol or an auxiliary symbol entry on ctxt.out.
+func (f *xcoffFile) addSymbol(sym xcoffSym) {
+ f.symtabSym = append(f.symtabSym, sym)
+ f.symbolCount++
+}
+
+// xcoffAlign returns the log base 2 of the symbol's alignment.
+func xcoffAlign(ldr *loader.Loader, x loader.Sym, t SymbolType) uint8 {
+ align := ldr.SymAlign(x)
+ if align == 0 {
+ if t == TextSym {
+ align = int32(Funcalign)
+ } else {
+ align = symalign(ldr, x)
+ }
+ }
+ return logBase2(int(align))
+}
+
+// logBase2 returns the log in base 2 of a.
+func logBase2(a int) uint8 {
+ return uint8(bits.Len(uint(a)) - 1)
+}
+
+// Write symbols needed when a new file appeared:
+// - a C_FILE with one auxiliary entry for its name
+// - C_DWARF symbols to provide debug information
+// - a C_HIDEXT which will be a csect containing all of its functions
+// It needs several parameters to create .csect symbols such as its entry point and its section number.
+//
+// Currently, a new file is in fact a new package. It seems to be OK, but it might change
+// in the future.
+func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint64, extnum int16) {
+ ldr := ctxt.loader
+ /* C_FILE */
+ s := &XcoffSymEnt64{
+ Noffset: uint32(f.stringTable.add(".file")),
+ Nsclass: C_FILE,
+ Nscnum: N_DEBUG,
+ Ntype: 0, // Go isn't inside predefined language.
+ Nnumaux: 1,
+ }
+ f.addSymbol(s)
+ currSymSrcFile.file = s
+
+ // Auxiliary entry for file name.
+ auxf := &XcoffAuxFile64{
+ Xoffset: uint32(f.stringTable.add(name)),
+ Xftype: XFT_FN,
+ Xauxtype: _AUX_FILE,
+ }
+ f.addSymbol(auxf)
+
+ /* Dwarf */
+ for _, sect := range Segdwarf.Sections {
+ var dwsize uint64
+ if ctxt.LinkMode == LinkInternal {
+ // Find the size of this corresponding package DWARF compilation unit.
+ // This size is set during DWARF generation (see dwarf.go).
+ dwsize = getDwsectCUSize(sect.Name, name)
+ // .debug_abbrev is common to all packages and not found with the previous function
+ if sect.Name == ".debug_abbrev" {
+ dwsize = uint64(ldr.SymSize(loader.Sym(sect.Sym)))
+
+ }
+ } else {
+ // There is only one .FILE with external linking.
+ dwsize = sect.Length
+ }
+
+ // get XCOFF name
+ name, _ := xcoffGetDwarfSubtype(sect.Name)
+ s := &XcoffSymEnt64{
+ Nvalue: currDwscnoff[sect.Name],
+ Noffset: uint32(f.stringTable.add(name)),
+ Nsclass: C_DWARF,
+ Nscnum: f.getXCOFFscnum(sect),
+ Nnumaux: 1,
+ }
+
+ if currSymSrcFile.csectAux == nil {
+ // Dwarf relocations need the symbol number of .dw* symbols.
+ // It doesn't need to know it for each package, one is enough.
+ // currSymSrcFile.csectAux == nil means first package.
+ ldr.SetSymDynid(loader.Sym(sect.Sym), int32(f.symbolCount))
+
+ if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal {
+ // CIE size must be added to the first package.
+ dwsize += 48
+ }
+ }
+
+ f.addSymbol(s)
+
+ // update the DWARF section offset in this file
+ if sect.Name != ".debug_abbrev" {
+ currDwscnoff[sect.Name] += dwsize
+ }
+
+ // Auxiliary dwarf section
+ auxd := &XcoffAuxDWARF64{
+ Xscnlen: dwsize,
+ Xauxtype: _AUX_SECT,
+ }
+
+ f.addSymbol(auxd)
+ }
+
+ /* .csect */
+ // Check if extnum is in text.
+ // This is temporary and only here to check if this algorithm is correct.
+ if extnum != 1 {
+ Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text")
+ }
+
+ currSymSrcFile.csectSymNb = uint64(f.symbolCount)
+
+ // No offset because no name
+ s = &XcoffSymEnt64{
+ Nvalue: firstEntry,
+ Nscnum: extnum,
+ Nsclass: C_HIDEXT,
+ Ntype: 0, // check visibility ?
+ Nnumaux: 1,
+ }
+ f.addSymbol(s)
+
+ aux := &XcoffAuxCSect64{
+ Xsmclas: XMC_PR,
+ Xsmtyp: XTY_SD | logBase2(Funcalign)<<3,
+ Xauxtype: _AUX_CSECT,
+ }
+ f.addSymbol(aux)
+
+ currSymSrcFile.csectAux = aux
+ currSymSrcFile.csectVAStart = int64(firstEntry)
+ currSymSrcFile.csectVAEnd = int64(firstEntry)
+}
+
+// Update values for the previous package.
+// - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1
+// - Xsclen of the csect symbol.
+func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) {
+ // first file
+ if currSymSrcFile.file == nil {
+ return
+ }
+
+ // Update C_FILE
+ cfile := currSymSrcFile.file
+ if last {
+ cfile.Nvalue = 0xFFFFFFFFFFFFFFFF
+ } else {
+ cfile.Nvalue = uint64(f.symbolCount)
+ }
+
+ // update csect scnlen in this auxiliary entry
+ aux := currSymSrcFile.csectAux
+ csectSize := currSymSrcFile.csectVAEnd - currSymSrcFile.csectVAStart
+ aux.Xscnlenlo = uint32(csectSize & 0xFFFFFFFF)
+ aux.Xscnlenhi = uint32(csectSize >> 32)
+}
+
+// Write symbol representing a .text function.
+// The symbol table is split with C_FILE corresponding to each package
+// and not to each source file as it should be.
+func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x loader.Sym) []xcoffSym {
+ // New XCOFF symbols which will be written.
+ syms := []xcoffSym{}
+
+ // Check if a new file is detected.
+ ldr := ctxt.loader
+ name := ldr.SymName(x)
+ if strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") {
+ // Trampoline don't have a FILE so there are considered
+ // in the current file.
+ // Same goes for runtime.text.X symbols.
+ } else if ldr.SymPkg(x) == "" { // Undefined global symbol
+ // If this happens, the algorithm must be redone.
+ if currSymSrcFile.name != "" {
+ Exitf("undefined global symbol found inside another file")
+ }
+ } else {
+ // Current file has changed. New C_FILE, C_DWARF, etc must be generated.
+ if currSymSrcFile.name != ldr.SymPkg(x) {
+ if ctxt.LinkMode == LinkInternal {
+ // update previous file values
+ xfile.updatePreviousFile(ctxt, false)
+ currSymSrcFile.name = ldr.SymPkg(x)
+ f.writeSymbolNewFile(ctxt, ldr.SymPkg(x), uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x)))
+ } else {
+ // With external linking, ld will crash if there is several
+ // .FILE and DWARF debugging enable, somewhere during
+ // the relocation phase.
+ // Therefore, all packages are merged under a fake .FILE
+ // "go_functions".
+ // TODO(aix); remove once ld has been fixed or the triggering
+ // relocation has been found and fixed.
+ if currSymSrcFile.name == "" {
+ currSymSrcFile.name = ldr.SymPkg(x)
+ f.writeSymbolNewFile(ctxt, "go_functions", uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x)))
+ }
+ }
+
+ }
+ }
+
+ name = ldr.SymExtname(x)
+ name = mangleABIName(ctxt, ldr, x, name)
+
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nscnum: f.getXCOFFscnum(ldr.SymSect(x)),
+ Ntype: SYM_TYPE_FUNC,
+ Nnumaux: 2,
+ }
+
+ if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
+ s.Nsclass = C_HIDEXT
+ }
+
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ // Keep track of the section size by tracking the VA range. Individual
+ // alignment differences may introduce a few extra bytes of padding
+ // which are not fully accounted for by ldr.SymSize(x).
+ sv := ldr.SymValue(x) + ldr.SymSize(x)
+ if currSymSrcFile.csectVAEnd < sv {
+ currSymSrcFile.csectVAEnd = sv
+ }
+
+ // create auxiliary entries
+ a2 := &XcoffAuxFcn64{
+ Xfsize: uint32(ldr.SymSize(x)),
+ Xlnnoptr: 0, // TODO
+ Xendndx: xfile.symbolCount + 3, // this symbol + 2 aux entries
+ Xauxtype: _AUX_FCN,
+ }
+ syms = append(syms, a2)
+
+ a4 := &XcoffAuxCSect64{
+ Xscnlenlo: uint32(currSymSrcFile.csectSymNb & 0xFFFFFFFF),
+ Xscnlenhi: uint32(currSymSrcFile.csectSymNb >> 32),
+ Xsmclas: XMC_PR, // Program Code
+ Xsmtyp: XTY_LD, // label definition (based on C)
+ Xauxtype: _AUX_CSECT,
+ }
+ a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3)
+
+ syms = append(syms, a4)
+ return syms
+}
+
+// put function used by genasmsym to write symbol table.
+func putaixsym(ctxt *Link, x loader.Sym, t SymbolType) {
+ // All XCOFF symbols generated by this GO symbols
+ // Can be a symbol entry or a auxiliary entry
+ syms := []xcoffSym{}
+
+ ldr := ctxt.loader
+ name := ldr.SymName(x)
+ if t == UndefinedSym {
+ name = ldr.SymExtname(x)
+ }
+
+ switch t {
+ default:
+ return
+
+ case TextSym:
+ if ldr.SymPkg(x) != "" || strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") {
+ // Function within a file
+ syms = xfile.writeSymbolFunc(ctxt, x)
+ } else {
+ // Only runtime.text and runtime.etext come through this way
+ if name != "runtime.text" && name != "runtime.etext" && name != "go:buildid" {
+ Exitf("putaixsym: unknown text symbol %s", name)
+ }
+ s := &XcoffSymEnt64{
+ Nsclass: C_HIDEXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)),
+ Ntype: SYM_TYPE_FUNC,
+ Nnumaux: 1,
+ }
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ size := uint64(ldr.SymSize(x))
+ a4 := &XcoffAuxCSect64{
+ Xauxtype: _AUX_CSECT,
+ Xscnlenlo: uint32(size & 0xFFFFFFFF),
+ Xscnlenhi: uint32(size >> 32),
+ Xsmclas: XMC_PR,
+ Xsmtyp: XTY_SD,
+ }
+ a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3)
+ syms = append(syms, a4)
+ }
+
+ case DataSym, BSSSym:
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)),
+ Nnumaux: 1,
+ }
+
+ if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
+ // There is more symbols in the case of a global data
+ // which are related to the assembly generated
+ // to access such symbols.
+ // But as Golang as its own way to check if a symbol is
+ // global or local (the capital letter), we don't need to
+ // implement them yet.
+ s.Nsclass = C_HIDEXT
+ }
+
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ // Create auxiliary entry
+
+ // Normally, size should be the size of csect containing all
+ // the data and bss symbols of one file/package.
+ // However, it's easier to just have a csect for each symbol.
+ // It might change
+ size := uint64(ldr.SymSize(x))
+ a4 := &XcoffAuxCSect64{
+ Xauxtype: _AUX_CSECT,
+ Xscnlenlo: uint32(size & 0xFFFFFFFF),
+ Xscnlenhi: uint32(size >> 32),
+ }
+
+ if ty := ldr.SymType(x); ty >= sym.STYPE && ty <= sym.SPCLNTAB {
+ if ctxt.IsExternal() && strings.HasPrefix(ldr.SymSect(x).Name, ".data.rel.ro") {
+ // During external linking, read-only datas with relocation
+ // must be in .data.
+ a4.Xsmclas = XMC_RW
+ } else {
+ // Read only data
+ a4.Xsmclas = XMC_RO
+ }
+ } else if /*ty == sym.SDATA &&*/ strings.HasPrefix(ldr.SymName(x), "TOC.") && ctxt.IsExternal() {
+ a4.Xsmclas = XMC_TC
+ } else if ldr.SymName(x) == "TOC" {
+ a4.Xsmclas = XMC_TC0
+ } else {
+ a4.Xsmclas = XMC_RW
+ }
+ if t == DataSym {
+ a4.Xsmtyp |= XTY_SD
+ } else {
+ a4.Xsmtyp |= XTY_CM
+ }
+
+ a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, t) << 3)
+
+ syms = append(syms, a4)
+
+ case UndefinedSym:
+ if ty := ldr.SymType(x); ty != sym.SDYNIMPORT && ty != sym.SHOSTOBJ && ty != sym.SUNDEFEXT {
+ return
+ }
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nnumaux: 1,
+ }
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ a4 := &XcoffAuxCSect64{
+ Xauxtype: _AUX_CSECT,
+ Xsmclas: XMC_DS,
+ Xsmtyp: XTY_ER | XTY_IMP,
+ }
+
+ if ldr.SymName(x) == "__n_pthreads" {
+ // Currently, all imported symbols made by cgo_import_dynamic are
+ // syscall functions, except __n_pthreads which is a variable.
+ // TODO(aix): Find a way to detect variables imported by cgo.
+ a4.Xsmclas = XMC_RW
+ }
+
+ syms = append(syms, a4)
+
+ case TLSSym:
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nnumaux: 1,
+ }
+
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ size := uint64(ldr.SymSize(x))
+ a4 := &XcoffAuxCSect64{
+ Xauxtype: _AUX_CSECT,
+ Xsmclas: XMC_UL,
+ Xsmtyp: XTY_CM,
+ Xscnlenlo: uint32(size & 0xFFFFFFFF),
+ Xscnlenhi: uint32(size >> 32),
+ }
+
+ syms = append(syms, a4)
+ }
+
+ for _, s := range syms {
+ xfile.addSymbol(s)
+ }
+}
+
+// Generate XCOFF Symbol table.
+// It will be written in out file in Asmbxcoff, because it must be
+// at the very end, especially after relocation sections which needs symbols' index.
+func (f *xcoffFile) asmaixsym(ctxt *Link) {
+ ldr := ctxt.loader
+ // Get correct size for symbols wrapping others symbols like go.string.*
+ // sym.Size can be used directly as the symbols have already been written.
+ for name, size := range outerSymSize {
+ sym := ldr.Lookup(name, 0)
+ if sym == 0 {
+ Errorf(nil, "unknown outer symbol with name %s", name)
+ } else {
+ s := ldr.MakeSymbolUpdater(sym)
+ s.SetSize(size)
+ }
+ }
+
+ // These symbols won't show up in the first loop below because we
+ // skip sym.STEXT symbols. Normal sym.STEXT symbols are emitted by walking textp.
+ s := ldr.Lookup("runtime.text", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ // We've already included this symbol in ctxt.Textp on AIX with external linker.
+ // See data.go:/textaddress
+ if !ctxt.IsExternal() {
+ putaixsym(ctxt, s, TextSym)
+ }
+ }
+
+ n := 1
+ // Generate base addresses for all text sections if there are multiple
+ for _, sect := range Segtext.Sections[1:] {
+ if sect.Name != ".text" || ctxt.IsExternal() {
+ // On AIX, runtime.text.X are symbols already in the symtab.
+ break
+ }
+ s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+ if s == 0 {
+ break
+ }
+ if ldr.SymType(s) == sym.STEXT {
+ putaixsym(ctxt, s, TextSym)
+ }
+ n++
+ }
+
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ // We've already included this symbol in ctxt.Textp
+ // on AIX with external linker.
+ // See data.go:/textaddress
+ if !ctxt.IsExternal() {
+ putaixsym(ctxt, s, TextSym)
+ }
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym, name string) bool {
+ if name == ".go.buildinfo" {
+ // On AIX, .go.buildinfo must be in the symbol table as
+ // it has relocations.
+ return true
+ }
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ if (name == "" || name[0] == '.') && !ldr.IsFileLocal(s) && name != ".TOC." {
+ return false
+ }
+ return true
+ }
+
+ for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
+ if !shouldBeInSymbolTable(s, ldr.SymName(s)) {
+ continue
+ }
+ st := ldr.SymType(s)
+ switch {
+ case st == sym.STLSBSS:
+ if ctxt.IsExternal() {
+ putaixsym(ctxt, s, TLSSym)
+ }
+
+ case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_8BIT_COUNTER, st == sym.SCOVERAGE_COUNTER:
+ if ldr.AttrReachable(s) {
+ data := ldr.Data(s)
+ if len(data) > 0 {
+ ldr.Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(data), ldr.SymType(s), ldr.AttrSpecial(s))
+ }
+ putaixsym(ctxt, s, BSSSym)
+ }
+
+ case st >= sym.SELFRXSECT && st < sym.SXREF: // data sections handled in dodata
+ if ldr.AttrReachable(s) {
+ putaixsym(ctxt, s, DataSym)
+ }
+
+ case st == sym.SUNDEFEXT:
+ putaixsym(ctxt, s, UndefinedSym)
+
+ case st == sym.SDYNIMPORT:
+ if ldr.AttrReachable(s) {
+ putaixsym(ctxt, s, UndefinedSym)
+ }
+ }
+ }
+
+ for _, s := range ctxt.Textp {
+ putaixsym(ctxt, s, TextSym)
+ }
+
+ if ctxt.Debugvlog != 0 || *flagN {
+ ctxt.Logf("symsize = %d\n", uint32(symSize))
+ }
+ xfile.updatePreviousFile(ctxt, true)
+}
+
+func (f *xcoffFile) genDynSym(ctxt *Link) {
+ ldr := ctxt.loader
+ var dynsyms []loader.Sym
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT {
+ continue
+ }
+ dynsyms = append(dynsyms, s)
+ }
+
+ for _, s := range dynsyms {
+ f.adddynimpsym(ctxt, s)
+
+ if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok {
+ f.dynLibraries[ldr.SymDynimplib(s)] = len(f.dynLibraries)
+ }
+ }
+}
+
+// (*xcoffFile)adddynimpsym adds the dynamic symbol "s" to a XCOFF file.
+// A new symbol named s.Extname() is created to be the actual dynamic symbol
+// in the .loader section and in the symbol table as an External Reference.
+// The symbol "s" is transformed to SXCOFFTOC to end up in .data section.
+// However, there is no writing protection on those symbols and
+// it might need to be added.
+// TODO(aix): Handles dynamic symbols without library.
+func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) {
+ // Check that library name is given.
+ // Pattern is already checked when compiling.
+ ldr := ctxt.loader
+ if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" {
+ ctxt.Errorf(s, "imported symbol must have a given library")
+ }
+
+ sb := ldr.MakeSymbolUpdater(s)
+ sb.SetReachable(true)
+ sb.SetType(sym.SXCOFFTOC)
+
+ // Create new dynamic symbol
+ extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0)
+ extsym.SetType(sym.SDYNIMPORT)
+ extsym.SetDynimplib(ldr.SymDynimplib(s))
+ extsym.SetExtname(ldr.SymExtname(s))
+ extsym.SetDynimpvers(ldr.SymDynimpvers(s))
+
+ // Add loader symbol
+ lds := &xcoffLoaderSymbol{
+ sym: extsym.Sym(),
+ smtype: XTY_IMP,
+ smclas: XMC_DS,
+ }
+ if ldr.SymName(s) == "__n_pthreads" {
+ // Currently, all imported symbols made by cgo_import_dynamic are
+ // syscall functions, except __n_pthreads which is a variable.
+ // TODO(aix): Find a way to detect variables imported by cgo.
+ lds.smclas = XMC_RW
+ }
+ f.loaderSymbols = append(f.loaderSymbols, lds)
+
+ // Relocation to retrieve the external address
+ sb.AddBytes(make([]byte, 8))
+ r, _ := sb.AddRel(objabi.R_ADDR)
+ r.SetSym(extsym.Sym())
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ // TODO: maybe this could be
+ // sb.SetSize(0)
+ // sb.SetData(nil)
+ // sb.AddAddr(ctxt.Arch, extsym.Sym())
+ // If the size is not 0 to begin with, I don't think the added 8 bytes
+ // of zeros are necessary.
+}
+
+// Xcoffadddynrel adds a dynamic relocation in a XCOFF file.
+// This relocation will be made by the loader.
+func Xcoffadddynrel(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ if target.IsExternal() {
+ return true
+ }
+ if ldr.SymType(s) <= sym.SPCLNTAB {
+ ldr.Errorf(s, "cannot have a relocation to %s in a text section symbol", ldr.SymName(r.Sym()))
+ return false
+ }
+
+ xldr := &xcoffLoaderReloc{
+ sym: s,
+ roff: r.Off(),
+ }
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ ldr.Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", ldr.SymName(targ), r.Type().String())
+ return false
+ case objabi.R_ADDR:
+ if ldr.SymType(s) == sym.SXCOFFTOC && targType == sym.SDYNIMPORT {
+ // Imported symbol relocation
+ for i, dynsym := range xfile.loaderSymbols {
+ if ldr.SymName(dynsym.sym) == ldr.SymName(targ) {
+ xldr.symndx = int32(i + 3) // +3 because of 3 section symbols
+ break
+ }
+ }
+ } else if t := ldr.SymType(s); t == sym.SDATA || t == sym.SNOPTRDATA || t == sym.SBUILDINFO || t == sym.SXCOFFTOC {
+ switch ldr.SymSect(targ).Seg {
+ default:
+ ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ))
+ case &Segtext:
+ case &Segrodata:
+ xldr.symndx = 0 // .text
+ case &Segdata:
+ if targType == sym.SBSS || targType == sym.SNOPTRBSS {
+ xldr.symndx = 2 // .bss
+ } else {
+ xldr.symndx = 1 // .data
+ }
+ }
+
+ } else {
+ ldr.Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", ldr.SymName(targ), ldr.SymType(s), ldr.SymType(targ))
+ return false
+ }
+
+ xldr.rtype = 0x3F<<8 + XCOFF_R_POS
+ }
+
+ xfile.Lock()
+ xfile.loaderReloc = append(xfile.loaderReloc, xldr)
+ xfile.Unlock()
+ return true
+}
+
+func (ctxt *Link) doxcoff() {
+ ldr := ctxt.loader
+
+ // TOC
+ toc := ldr.CreateSymForUpdate("TOC", 0)
+ toc.SetType(sym.SXCOFFTOC)
+ toc.SetVisibilityHidden(true)
+
+ // Add entry point to .loader symbols.
+ ep := ldr.Lookup(*flagEntrySymbol, 0)
+ if ep == 0 || !ldr.AttrReachable(ep) {
+ Exitf("wrong entry point")
+ }
+
+ xfile.loaderSymbols = append(xfile.loaderSymbols, &xcoffLoaderSymbol{
+ sym: ep,
+ smtype: XTY_ENT | XTY_SD,
+ smclas: XMC_DS,
+ })
+
+ xfile.genDynSym(ctxt)
+
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if strings.HasPrefix(ldr.SymName(s), "TOC.") {
+ sb := ldr.MakeSymbolUpdater(s)
+ sb.SetType(sym.SXCOFFTOC)
+ }
+ }
+
+ if ctxt.IsExternal() {
+ // Change rt0_go name to match name in runtime/cgo:main().
+ rt0 := ldr.Lookup("runtime.rt0_go", 0)
+ ldr.SetSymExtname(rt0, "runtime_rt0_go")
+
+ nsym := loader.Sym(ldr.NSym())
+ for s := loader.Sym(1); s < nsym; s++ {
+ if !ldr.AttrCgoExport(s) {
+ continue
+ }
+ if ldr.IsFileLocal(s) {
+ panic("cgo_export on static symbol")
+ }
+
+ if ldr.SymType(s) == sym.STEXT {
+ // On AIX, a exported function must have two symbols:
+ // - a .text symbol which must start with a ".".
+ // - a .data symbol which is a function descriptor.
+ name := ldr.SymExtname(s)
+ ldr.SetSymExtname(s, "."+name)
+
+ desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0))
+ desc.SetReachable(true)
+ desc.SetType(sym.SNOPTRDATA)
+ desc.AddAddr(ctxt.Arch, s)
+ desc.AddAddr(ctxt.Arch, toc.Sym())
+ desc.AddUint64(ctxt.Arch, 0)
+ }
+ }
+ }
+}
+
+// Loader section
+// Currently, this section is created from scratch when assembling the XCOFF file
+// according to information retrieved in xfile object.
+
+// Create loader section and returns its size.
+func Loaderblk(ctxt *Link, off uint64) {
+ xfile.writeLdrScn(ctxt, off)
+}
+
+func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) {
+ var symtab []*XcoffLdSym64
+ var strtab []*XcoffLdStr64
+ var importtab []*XcoffLdImportFile64
+ var reloctab []*XcoffLdRel64
+ var dynimpreloc []*XcoffLdRel64
+
+ // As the string table is updated in any loader subsection,
+ // its length must be computed at the same time.
+ stlen := uint32(0)
+
+ // Loader Header
+ hdr := &XcoffLdHdr64{
+ Lversion: 2,
+ Lsymoff: LDHDRSZ_64,
+ }
+
+ ldr := ctxt.loader
+ /* Symbol table */
+ for _, s := range f.loaderSymbols {
+ lds := &XcoffLdSym64{
+ Loffset: uint32(stlen + 2),
+ Lsmtype: s.smtype,
+ Lsmclas: s.smclas,
+ }
+ sym := s.sym
+ switch s.smtype {
+ default:
+ ldr.Errorf(sym, "unexpected loader symbol type: 0x%x", s.smtype)
+ case XTY_ENT | XTY_SD:
+ lds.Lvalue = uint64(ldr.SymValue(sym))
+ lds.Lscnum = f.getXCOFFscnum(ldr.SymSect(sym))
+ case XTY_IMP:
+ lds.Lifile = int32(f.dynLibraries[ldr.SymDynimplib(sym)] + 1)
+ }
+ ldstr := &XcoffLdStr64{
+ size: uint16(len(ldr.SymName(sym)) + 1), // + null terminator
+ name: ldr.SymName(sym),
+ }
+ stlen += uint32(2 + ldstr.size) // 2 = sizeof ldstr.size
+ symtab = append(symtab, lds)
+ strtab = append(strtab, ldstr)
+
+ }
+
+ hdr.Lnsyms = int32(len(symtab))
+ hdr.Lrldoff = hdr.Lsymoff + uint64(24*hdr.Lnsyms) // 24 = sizeof one symbol
+ off := hdr.Lrldoff // current offset is the same of reloc offset
+
+ /* Reloc */
+ // Ensure deterministic order
+ sort.Slice(f.loaderReloc, func(i, j int) bool {
+ r1, r2 := f.loaderReloc[i], f.loaderReloc[j]
+ if r1.sym != r2.sym {
+ return r1.sym < r2.sym
+ }
+ if r1.roff != r2.roff {
+ return r1.roff < r2.roff
+ }
+ if r1.rtype != r2.rtype {
+ return r1.rtype < r2.rtype
+ }
+ return r1.symndx < r2.symndx
+ })
+
+ ep := ldr.Lookup(*flagEntrySymbol, 0)
+ xldr := &XcoffLdRel64{
+ Lvaddr: uint64(ldr.SymValue(ep)),
+ Lrtype: 0x3F00,
+ Lrsecnm: f.getXCOFFscnum(ldr.SymSect(ep)),
+ Lsymndx: 0,
+ }
+ off += 16
+ reloctab = append(reloctab, xldr)
+
+ off += uint64(16 * len(f.loaderReloc))
+ for _, r := range f.loaderReloc {
+ symp := r.sym
+ if symp == 0 {
+ panic("unexpected 0 sym value")
+ }
+ xldr = &XcoffLdRel64{
+ Lvaddr: uint64(ldr.SymValue(symp) + int64(r.roff)),
+ Lrtype: r.rtype,
+ Lsymndx: r.symndx,
+ }
+
+ if ldr.SymSect(symp) != nil {
+ xldr.Lrsecnm = f.getXCOFFscnum(ldr.SymSect(symp))
+ }
+
+ reloctab = append(reloctab, xldr)
+ }
+
+ off += uint64(16 * len(dynimpreloc))
+ reloctab = append(reloctab, dynimpreloc...)
+
+ hdr.Lnreloc = int32(len(reloctab))
+ hdr.Limpoff = off
+
+ /* Import */
+ // Default import: /usr/lib:/lib
+ ldimpf := &XcoffLdImportFile64{
+ Limpidpath: "/usr/lib:/lib",
+ }
+ off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter
+ importtab = append(importtab, ldimpf)
+
+ // The map created by adddynimpsym associates the name to a number
+ // This number represents the librairie index (- 1) in this import files section
+ // Therefore, they must be sorted before being put inside the section
+ libsOrdered := make([]string, len(f.dynLibraries))
+ for key, val := range f.dynLibraries {
+ if libsOrdered[val] != "" {
+ continue
+ }
+ libsOrdered[val] = key
+ }
+
+ for _, lib := range libsOrdered {
+ // lib string is defined as base.a/mem.o or path/base.a/mem.o
+ n := strings.Split(lib, "/")
+ path := ""
+ base := n[len(n)-2]
+ mem := n[len(n)-1]
+ if len(n) > 2 {
+ path = lib[:len(lib)-len(base)-len(mem)-2]
+
+ }
+ ldimpf = &XcoffLdImportFile64{
+ Limpidpath: path,
+ Limpidbase: base,
+ Limpidmem: mem,
+ }
+ off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter
+ importtab = append(importtab, ldimpf)
+ }
+
+ hdr.Lnimpid = int32(len(importtab))
+ hdr.Listlen = uint32(off - hdr.Limpoff)
+ hdr.Lstoff = off
+ hdr.Lstlen = stlen
+
+ /* Writing */
+ ctxt.Out.SeekSet(int64(globalOff))
+ binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, hdr)
+
+ for _, s := range symtab {
+ binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
+
+ }
+ for _, r := range reloctab {
+ binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, r)
+ }
+ for _, f := range importtab {
+ ctxt.Out.WriteString(f.Limpidpath)
+ ctxt.Out.Write8(0)
+ ctxt.Out.WriteString(f.Limpidbase)
+ ctxt.Out.Write8(0)
+ ctxt.Out.WriteString(f.Limpidmem)
+ ctxt.Out.Write8(0)
+ }
+ for _, s := range strtab {
+ ctxt.Out.Write16(s.size)
+ ctxt.Out.WriteString(s.name)
+ ctxt.Out.Write8(0) // null terminator
+ }
+
+ f.loaderSize = off + uint64(stlen)
+}
+
+// XCOFF assembling and writing file
+
+func (f *xcoffFile) writeFileHeader(ctxt *Link) {
+ // File header
+ f.xfhdr.Fmagic = U64_TOCMAGIC
+ f.xfhdr.Fnscns = uint16(len(f.sections))
+ f.xfhdr.Ftimedat = 0
+
+ if !*FlagS {
+ f.xfhdr.Fsymptr = uint64(f.symtabOffset)
+ f.xfhdr.Fnsyms = int32(f.symbolCount)
+ }
+
+ if ctxt.BuildMode == BuildModeExe && ctxt.LinkMode == LinkInternal {
+ ldr := ctxt.loader
+ f.xfhdr.Fopthdr = AOUTHSZ_EXEC64
+ f.xfhdr.Fflags = F_EXEC
+
+ // auxiliary header
+ f.xahdr.Ovstamp = 1 // based on dump -o
+ f.xahdr.Omagic = 0x10b
+ copy(f.xahdr.Omodtype[:], "1L")
+ entry := ldr.Lookup(*flagEntrySymbol, 0)
+ f.xahdr.Oentry = uint64(ldr.SymValue(entry))
+ f.xahdr.Osnentry = f.getXCOFFscnum(ldr.SymSect(entry))
+ toc := ldr.Lookup("TOC", 0)
+ f.xahdr.Otoc = uint64(ldr.SymValue(toc))
+ f.xahdr.Osntoc = f.getXCOFFscnum(ldr.SymSect(toc))
+
+ f.xahdr.Oalgntext = int16(logBase2(int(XCOFFSECTALIGN)))
+ f.xahdr.Oalgndata = 0x5
+
+ binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
+ binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr)
+ } else {
+ f.xfhdr.Fopthdr = 0
+ binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
+ }
+
+}
+
+func xcoffwrite(ctxt *Link) {
+ ctxt.Out.SeekSet(0)
+
+ xfile.writeFileHeader(ctxt)
+
+ for _, sect := range xfile.sections {
+ sect.write(ctxt)
+ }
+}
+
+// Generate XCOFF assembly file.
+func asmbXcoff(ctxt *Link) {
+ ctxt.Out.SeekSet(0)
+ fileoff := int64(Segdwarf.Fileoff + Segdwarf.Filelen)
+ fileoff = int64(Rnd(int64(fileoff), int64(*FlagRound)))
+
+ xfile.sectNameToScnum = make(map[string]int16)
+
+ // Add sections
+ s := xfile.addSection(".text", Segtext.Vaddr, Segtext.Length, Segtext.Fileoff, STYP_TEXT)
+ xfile.xahdr.Otextstart = s.Svaddr
+ xfile.xahdr.Osntext = xfile.sectNameToScnum[".text"]
+ xfile.xahdr.Otsize = s.Ssize
+ xfile.sectText = s
+
+ segdataVaddr := Segdata.Vaddr
+ segdataFilelen := Segdata.Filelen
+ segdataFileoff := Segdata.Fileoff
+ segbssFilelen := Segdata.Length - Segdata.Filelen
+ if len(Segrelrodata.Sections) > 0 {
+ // Merge relro segment to data segment as
+ // relro data are inside data segment on AIX.
+ segdataVaddr = Segrelrodata.Vaddr
+ segdataFileoff = Segrelrodata.Fileoff
+ segdataFilelen = Segdata.Vaddr + Segdata.Filelen - Segrelrodata.Vaddr
+ }
+
+ s = xfile.addSection(".data", segdataVaddr, segdataFilelen, segdataFileoff, STYP_DATA)
+ xfile.xahdr.Odatastart = s.Svaddr
+ xfile.xahdr.Osndata = xfile.sectNameToScnum[".data"]
+ xfile.xahdr.Odsize = s.Ssize
+ xfile.sectData = s
+
+ s = xfile.addSection(".bss", segdataVaddr+segdataFilelen, segbssFilelen, 0, STYP_BSS)
+ xfile.xahdr.Osnbss = xfile.sectNameToScnum[".bss"]
+ xfile.xahdr.Obsize = s.Ssize
+ xfile.sectBss = s
+
+ if ctxt.LinkMode == LinkExternal {
+ var tbss *sym.Section
+ for _, s := range Segdata.Sections {
+ if s.Name == ".tbss" {
+ tbss = s
+ break
+ }
+ }
+ s = xfile.addSection(".tbss", tbss.Vaddr, tbss.Length, 0, STYP_TBSS)
+ }
+
+ // add dwarf sections
+ for _, sect := range Segdwarf.Sections {
+ xfile.addDwarfSection(sect)
+ }
+
+ // add and write remaining sections
+ if ctxt.LinkMode == LinkInternal {
+ // Loader section
+ if ctxt.BuildMode == BuildModeExe {
+ Loaderblk(ctxt, uint64(fileoff))
+ s = xfile.addSection(".loader", 0, xfile.loaderSize, uint64(fileoff), STYP_LOADER)
+ xfile.xahdr.Osnloader = xfile.sectNameToScnum[".loader"]
+
+ // Update fileoff for symbol table
+ fileoff += int64(xfile.loaderSize)
+ }
+ }
+
+ // Create Symbol table
+ xfile.asmaixsym(ctxt)
+
+ if ctxt.LinkMode == LinkExternal {
+ xfile.emitRelocations(ctxt, fileoff)
+ }
+
+ // Write Symbol table
+ xfile.symtabOffset = ctxt.Out.Offset()
+ for _, s := range xfile.symtabSym {
+ binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
+ }
+ // write string table
+ xfile.stringTable.write(ctxt.Out)
+
+ // write headers
+ xcoffwrite(ctxt)
+}
+
+// emitRelocations emits relocation entries for go.o in external linking.
+func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) {
+ ctxt.Out.SeekSet(fileoff)
+ for ctxt.Out.Offset()&7 != 0 {
+ ctxt.Out.Write8(0)
+ }
+
+ ldr := ctxt.loader
+ // relocsect relocates symbols from first in section sect, and returns
+ // the total number of relocations emitted.
+ relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) uint32 {
+ // ctxt.Logf("%s 0x%x\n", sect.Name, sect.Vaddr)
+ // If main section has no bits, nothing to relocate.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return 0
+ }
+ sect.Reloff = uint64(ctxt.Out.Offset())
+ for i, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if uint64(ldr.SymValue(s)) >= sect.Vaddr {
+ syms = syms[i:]
+ break
+ }
+ }
+ eaddr := int64(sect.Vaddr + sect.Length)
+ for _, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymValue(s) >= int64(eaddr) {
+ break
+ }
+
+ // Compute external relocations on the go, and pass to Xcoffreloc1 to stream out.
+ // Relocation must be ordered by address, so create a list of sorted indices.
+ relocs := ldr.Relocs(s)
+ sorted := make([]int, relocs.Count())
+ for i := 0; i < relocs.Count(); i++ {
+ sorted[i] = i
+ }
+ sort.Slice(sorted, func(i, j int) bool {
+ return relocs.At(sorted[i]).Off() < relocs.At(sorted[j]).Off()
+ })
+
+ for _, ri := range sorted {
+ r := relocs.At(ri)
+ rr, ok := extreloc(ctxt, ldr, s, r)
+ if !ok {
+ continue
+ }
+ if rr.Xsym == 0 {
+ ldr.Errorf(s, "missing xsym in relocation")
+ continue
+ }
+ if ldr.SymDynid(rr.Xsym) < 0 {
+ ldr.Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymDynid(rr.Xsym))
+ }
+ if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) {
+ ldr.Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type(), r.Type(), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ }
+ }
+ sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
+ return uint32(sect.Rellen) / RELSZ_64
+ }
+ sects := []struct {
+ xcoffSect *XcoffScnHdr64
+ segs []*sym.Segment
+ }{
+ {f.sectText, []*sym.Segment{&Segtext}},
+ {f.sectData, []*sym.Segment{&Segrelrodata, &Segdata}},
+ }
+ for _, s := range sects {
+ s.xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
+ n := uint32(0)
+ for _, seg := range s.segs {
+ for _, sect := range seg.Sections {
+ if sect.Name == ".text" {
+ n += relocsect(sect, ctxt.Textp, 0)
+ } else {
+ n += relocsect(sect, ctxt.datap, 0)
+ }
+ }
+ }
+ s.xcoffSect.Snreloc += n
+ }
+
+dwarfLoop:
+ for i := 0; i < len(Segdwarf.Sections); i++ {
+ sect := Segdwarf.Sections[i]
+ si := dwarfp[i]
+ if si.secSym() != loader.Sym(sect.Sym) ||
+ ldr.SymSect(si.secSym()) != sect {
+ panic("inconsistency between dwarfp and Segdwarf")
+ }
+ for _, xcoffSect := range f.sections {
+ _, subtyp := xcoffGetDwarfSubtype(sect.Name)
+ if xcoffSect.Sflags&0xF0000 == subtyp {
+ xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
+ xcoffSect.Snreloc = relocsect(sect, si.syms, sect.Vaddr)
+ continue dwarfLoop
+ }
+ }
+ Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
+ }
+}
+
+// xcoffCreateExportFile creates a file with exported symbols for
+// -Wl,-bE option.
+// ld won't export symbols unless they are listed in an export file.
+func xcoffCreateExportFile(ctxt *Link) (fname string) {
+ fname = filepath.Join(*flagTmpdir, "export_file.exp")
+ var buf bytes.Buffer
+
+ ldr := ctxt.loader
+ for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
+ if !ldr.AttrCgoExport(s) {
+ continue
+ }
+ extname := ldr.SymExtname(s)
+ if !strings.HasPrefix(extname, "._cgoexp_") {
+ continue
+ }
+ if ldr.IsFileLocal(s) {
+ continue // Only export non-static symbols
+ }
+
+ // Retrieve the name of the initial symbol
+ // exported by cgo.
+ // The corresponding Go symbol is:
+ // _cgoexp_hashcode_symname.
+ name := strings.SplitN(extname, "_", 4)[3]
+
+ buf.Write([]byte(name + "\n"))
+ }
+
+ err := os.WriteFile(fname, buf.Bytes(), 0666)
+ if err != nil {
+ Errorf(nil, "WriteFile %s failed: %v", fname, err)
+ }
+
+ return fname
+}