summaryrefslogtreecommitdiffstats
path: root/src/cmd/link
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link')
-rw-r--r--src/cmd/link/doc.go129
-rw-r--r--src/cmd/link/dwarf_test.go212
-rw-r--r--src/cmd/link/elf_test.go424
-rw-r--r--src/cmd/link/internal/amd64/asm.go676
-rw-r--r--src/cmd/link/internal/amd64/l.go43
-rw-r--r--src/cmd/link/internal/amd64/obj.go122
-rw-r--r--src/cmd/link/internal/arm/asm.go657
-rw-r--r--src/cmd/link/internal/arm/l.go75
-rw-r--r--src/cmd/link/internal/arm/obj.go109
-rw-r--r--src/cmd/link/internal/arm64/asm.go1048
-rw-r--r--src/cmd/link/internal/arm64/l.go74
-rw-r--r--src/cmd/link/internal/arm64/obj.go112
-rw-r--r--src/cmd/link/internal/benchmark/bench.go195
-rw-r--r--src/cmd/link/internal/benchmark/bench_test.go54
-rw-r--r--src/cmd/link/internal/ld/ar.go191
-rw-r--r--src/cmd/link/internal/ld/asmb.go214
-rw-r--r--src/cmd/link/internal/ld/config.go282
-rw-r--r--src/cmd/link/internal/ld/data.go2685
-rw-r--r--src/cmd/link/internal/ld/data_test.go92
-rw-r--r--src/cmd/link/internal/ld/deadcode.go444
-rw-r--r--src/cmd/link/internal/ld/deadcode_test.go57
-rw-r--r--src/cmd/link/internal/ld/decodesym.go293
-rw-r--r--src/cmd/link/internal/ld/dwarf.go2315
-rw-r--r--src/cmd/link/internal/ld/dwarf_test.go1621
-rw-r--r--src/cmd/link/internal/ld/elf.go2140
-rw-r--r--src/cmd/link/internal/ld/elf_test.go135
-rw-r--r--src/cmd/link/internal/ld/errors.go66
-rw-r--r--src/cmd/link/internal/ld/execarchive.go37
-rw-r--r--src/cmd/link/internal/ld/execarchive_noexec.go13
-rw-r--r--src/cmd/link/internal/ld/fallocate_test.go69
-rw-r--r--src/cmd/link/internal/ld/go.go494
-rw-r--r--src/cmd/link/internal/ld/go_test.go121
-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.go55
-rw-r--r--src/cmd/link/internal/ld/ld.go271
-rw-r--r--src/cmd/link/internal/ld/ld_test.go244
-rw-r--r--src/cmd/link/internal/ld/lib.go2573
-rw-r--r--src/cmd/link/internal/ld/link.go176
-rw-r--r--src/cmd/link/internal/ld/macho.go1528
-rw-r--r--src/cmd/link/internal/ld/macho_combine_dwarf.go430
-rw-r--r--src/cmd/link/internal/ld/main.go417
-rw-r--r--src/cmd/link/internal/ld/nooptcgolink_test.go37
-rw-r--r--src/cmd/link/internal/ld/outbuf.go311
-rw-r--r--src/cmd/link/internal/ld/outbuf_darwin.go47
-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.go9
-rw-r--r--src/cmd/link/internal/ld/outbuf_test.go99
-rw-r--r--src/cmd/link/internal/ld/outbuf_windows.go67
-rw-r--r--src/cmd/link/internal/ld/pcln.go996
-rw-r--r--src/cmd/link/internal/ld/pe.go1628
-rw-r--r--src/cmd/link/internal/ld/sym.go116
-rw-r--r--src/cmd/link/internal/ld/symtab.go832
-rw-r--r--src/cmd/link/internal/ld/target.go187
-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.go23
-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/typelink.go72
-rw-r--r--src/cmd/link/internal/ld/util.go114
-rw-r--r--src/cmd/link/internal/ld/xcoff.go1809
-rw-r--r--src/cmd/link/internal/loadelf/ldelf.go1100
-rw-r--r--src/cmd/link/internal/loader/loader.go2683
-rw-r--r--src/cmd/link/internal/loader/loader_test.go454
-rw-r--r--src/cmd/link/internal/loader/symbolbuilder.go440
-rw-r--r--src/cmd/link/internal/loadmacho/ldmacho.go806
-rw-r--r--src/cmd/link/internal/loadpe/ldpe.go536
-rw-r--r--src/cmd/link/internal/loadxcoff/ldxcoff.go228
-rw-r--r--src/cmd/link/internal/mips/asm.go156
-rw-r--r--src/cmd/link/internal/mips/l.go74
-rw-r--r--src/cmd/link/internal/mips/obj.go88
-rw-r--r--src/cmd/link/internal/mips64/asm.go157
-rw-r--r--src/cmd/link/internal/mips64/l.go74
-rw-r--r--src/cmd/link/internal/mips64/obj.go98
-rw-r--r--src/cmd/link/internal/ppc64/asm.go1075
-rw-r--r--src/cmd/link/internal/ppc64/l.go74
-rw-r--r--src/cmd/link/internal/ppc64/obj.go107
-rw-r--r--src/cmd/link/internal/riscv64/asm.go244
-rw-r--r--src/cmd/link/internal/riscv64/l.go14
-rw-r--r--src/cmd/link/internal/riscv64/obj.go60
-rw-r--r--src/cmd/link/internal/s390x/asm.go455
-rw-r--r--src/cmd/link/internal/s390x/l.go74
-rw-r--r--src/cmd/link/internal/s390x/obj.go87
-rw-r--r--src/cmd/link/internal/sym/compilation_unit.go35
-rw-r--r--src/cmd/link/internal/sym/library.go27
-rw-r--r--src/cmd/link/internal/sym/reloc.go74
-rw-r--r--src/cmd/link/internal/sym/segment.go66
-rw-r--r--src/cmd/link/internal/sym/symbol.go35
-rw-r--r--src/cmd/link/internal/sym/symkind.go178
-rw-r--r--src/cmd/link/internal/sym/symkind_string.go80
-rw-r--r--src/cmd/link/internal/wasm/asm.go605
-rw-r--r--src/cmd/link/internal/wasm/obj.go35
-rw-r--r--src/cmd/link/internal/x86/asm.go492
-rw-r--r--src/cmd/link/internal/x86/l.go43
-rw-r--r--src/cmd/link/internal/x86/obj.go116
-rw-r--r--src/cmd/link/link_test.go1001
-rw-r--r--src/cmd/link/linkbig_test.go112
-rw-r--r--src/cmd/link/main.go69
-rw-r--r--src/cmd/link/testdata/testBuildFortvOS/lib.go8
-rw-r--r--src/cmd/link/testdata/testBuildFortvOS/main.m5
-rw-r--r--src/cmd/link/testdata/testHashedSyms/p.go33
-rw-r--r--src/cmd/link/testdata/testIndexMismatch/a.go8
-rw-r--r--src/cmd/link/testdata/testIndexMismatch/b.go8
-rw-r--r--src/cmd/link/testdata/testIndexMismatch/main.go9
-rw-r--r--src/cmd/link/testdata/testPErsrc-complex/main.go43
-rw-r--r--src/cmd/link/testdata/testPErsrc-complex/rsrc.sysobin0 -> 352 bytes
-rw-r--r--src/cmd/link/testdata/testPErsrc/main.go19
-rw-r--r--src/cmd/link/testdata/testPErsrc/rsrc.sysobin0 -> 228 bytes
-rw-r--r--src/cmd/link/testdata/testRO/x.go22
129 files changed, 40046 insertions, 0 deletions
diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go
new file mode 100644
index 0000000..604675c
--- /dev/null
+++ b/src/cmd/link/doc.go
@@ -0,0 +1,129 @@
+// 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.
+
+/*
+Link, typically invoked as ``go tool link'', reads the Go archive or object
+for a package main, along with its dependencies, and combines them
+into an executable binary.
+
+Command Line
+
+Usage:
+
+ go tool link [flags] main.a
+
+Flags:
+
+ -B note
+ Add an ELF_NT_GNU_BUILD_ID note when using ELF.
+ The value should start with 0x and be an even number of hex digits.
+ -D address
+ Set data segment address.
+ -E entry
+ Set entry symbol name.
+ -H type
+ Set executable format type.
+ The default format is inferred from GOOS and GOARCH.
+ On Windows, -H windowsgui writes a "GUI binary" instead of a "console binary."
+ -I interpreter
+ Set the ELF dynamic linker to use.
+ -L dir1 -L dir2
+ Search for imported packages in dir1, dir2, etc,
+ after consulting $GOROOT/pkg/$GOOS_$GOARCH.
+ -R quantum
+ Set address rounding quantum.
+ -T address
+ Set text segment address.
+ -V
+ Print linker version and exit.
+ -X importpath.name=value
+ Set the value of the string variable in importpath named name to value.
+ This is only effective if the variable is declared in the source code either uninitialized
+ or initialized to a constant string expression. -X will not work if the initializer makes
+ a function call or refers to other variables.
+ Note that before Go 1.5 this option took two separate arguments.
+ -a
+ Disassemble output.
+ -buildid id
+ Record id as Go toolchain build id.
+ -buildmode mode
+ Set build mode (default exe).
+ -c
+ Dump call graphs.
+ -compressdwarf
+ Compress DWARF if possible (default true).
+ -cpuprofile file
+ Write CPU profile to file.
+ -d
+ Disable generation of dynamic executables.
+ The emitted code is the same in either case; the option
+ controls only whether a dynamic header is included.
+ The dynamic header is on by default, even without any
+ references to dynamic libraries, because many common
+ system tools now assume the presence of the header.
+ -debugtramp int
+ Debug trampolines.
+ -dumpdep
+ Dump symbol dependency graph.
+ -extar ar
+ Set the external archive program (default "ar").
+ Used only for -buildmode=c-archive.
+ -extld linker
+ Set the external linker (default "clang" or "gcc").
+ -extldflags flags
+ Set space-separated flags to pass to the external linker.
+ -f
+ Ignore version mismatch in the linked archives.
+ -g
+ Disable Go package data checks.
+ -importcfg file
+ Read import configuration from file.
+ In the file, set packagefile, packageshlib to specify import resolution.
+ -installsuffix suffix
+ Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix
+ instead of $GOROOT/pkg/$GOOS_$GOARCH.
+ -k symbol
+ Set field tracking symbol. Use this flag when GOEXPERIMENT=fieldtrack is set.
+ -libgcc file
+ Set name of compiler support library.
+ This is only used in internal link mode.
+ If not set, default value comes from running the compiler,
+ which may be set by the -extld option.
+ Set to "none" to use no support library.
+ -linkmode mode
+ Set link mode (internal, external, auto).
+ This sets the linking mode as described in cmd/cgo/doc.go.
+ -linkshared
+ Link against installed Go shared libraries (experimental).
+ -memprofile file
+ Write memory profile to file.
+ -memprofilerate rate
+ Set runtime.MemProfileRate to rate.
+ -msan
+ Link with C/C++ memory sanitizer support.
+ -n
+ Dump symbol table.
+ -o file
+ Write output to file (default a.out, or a.out.exe on Windows).
+ -pluginpath path
+ The path name used to prefix exported plugin symbols.
+ -r dir1:dir2:...
+ Set the ELF dynamic linker search path.
+ -race
+ Link with race detection libraries.
+ -s
+ Omit the symbol table and debug information.
+ -shared
+ Generated shared object (implies -linkmode external; experimental).
+ -tmpdir dir
+ Write temporary files to dir.
+ Temporary files are only used in external linking mode.
+ -u
+ Reject unsafe packages.
+ -v
+ Print trace of linker operations.
+ -w
+ Omit the DWARF symbol table.
+*/
+package main
diff --git a/src/cmd/link/dwarf_test.go b/src/cmd/link/dwarf_test.go
new file mode 100644
index 0000000..db710be
--- /dev/null
+++ b/src/cmd/link/dwarf_test.go
@@ -0,0 +1,212 @@
+// 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 main
+
+import (
+ "bytes"
+ cmddwarf "cmd/internal/dwarf"
+ "cmd/internal/objfile"
+ "debug/dwarf"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string) {
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ out, err := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "cmd/link").CombinedOutput()
+ if err != nil {
+ t.Fatalf("go list: %v\n%s", err, out)
+ }
+ if string(out) != "false\n" {
+ if strings.HasPrefix(testenv.Builder(), "darwin-") {
+ t.Skipf("cmd/link is spuriously stale on Darwin builders - see #33598")
+ }
+ t.Fatalf("cmd/link is stale - run go install cmd/link")
+ }
+
+ for _, prog := range []string{"testprog", "testprogcgo"} {
+ prog := prog
+ expectDWARF := expectDWARF
+ if runtime.GOOS == "aix" && prog == "testprogcgo" {
+ extld := os.Getenv("CC")
+ if extld == "" {
+ extld = "gcc"
+ }
+ expectDWARF, err = cmddwarf.IsDWARFEnabledOnAIXLd(extld)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ }
+
+ t.Run(prog, func(t *testing.T) {
+ t.Parallel()
+
+ tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ exe := filepath.Join(tmpDir, prog+".exe")
+ dir := "../../runtime/testdata/" + prog
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
+ if buildmode != "" {
+ cmd.Args = append(cmd.Args, "-buildmode", buildmode)
+ }
+ cmd.Args = append(cmd.Args, dir)
+ if env != nil {
+ cmd.Env = append(os.Environ(), env...)
+ cmd.Env = append(cmd.Env, "CGO_CFLAGS=") // ensure CGO_CFLAGS does not contain any flags. Issue #35459
+ }
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
+ }
+
+ if buildmode == "c-archive" {
+ // Extract the archive and use the go.o object within.
+ cmd := exec.Command("ar", "-x", exe)
+ cmd.Dir = tmpDir
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("ar -x %s: %v\n%s", exe, err, out)
+ }
+ exe = filepath.Join(tmpDir, "go.o")
+ }
+
+ if runtime.GOOS == "darwin" {
+ if _, err = exec.LookPath("symbols"); err == nil {
+ // Ensure Apple's tooling can parse our object for symbols.
+ out, err = exec.Command("symbols", exe).CombinedOutput()
+ if err != nil {
+ t.Fatalf("symbols %v: %v: %s", filepath.Base(exe), err, out)
+ } else {
+ if bytes.HasPrefix(out, []byte("Unable to find file")) {
+ // This failure will cause the App Store to reject our binaries.
+ t.Fatalf("symbols %v: failed to parse file", filepath.Base(exe))
+ } else if bytes.Contains(out, []byte(", Empty]")) {
+ t.Fatalf("symbols %v: parsed as empty", filepath.Base(exe))
+ }
+ }
+ }
+ }
+
+ f, err := objfile.Open(exe)
+ if err != nil {
+ t.Fatal(err)
+ }
+ 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")
+ }
+
+ d, err := f.DWARF()
+ if err != nil {
+ if expectDWARF {
+ t.Fatal(err)
+ }
+ return
+ } else {
+ if !expectDWARF {
+ t.Fatal("unexpected DWARF section")
+ }
+ }
+
+ // TODO: We'd like to use filepath.Join here.
+ // Also related: golang.org/issue/19784.
+ wantFile := path.Join(prog, "main.go")
+ wantLine := 24
+ r := d.Reader()
+ entry, err := r.SeekPC(addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ lr, err := d.LineReader(entry)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var line dwarf.LineEntry
+ if err := lr.SeekPC(addr, &line); err == dwarf.ErrUnknownPC {
+ t.Fatalf("did not find file:line for %#x (main.main)", addr)
+ } else if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
+ t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
+ }
+ })
+ }
+}
+
+func TestDWARF(t *testing.T) {
+ testDWARF(t, "", true)
+ if !testing.Short() {
+ if runtime.GOOS == "windows" {
+ t.Skip("skipping Windows/c-archive; see Issue 35512 for more.")
+ }
+ t.Run("c-archive", func(t *testing.T) {
+ testDWARF(t, "c-archive", true)
+ })
+ }
+}
+
+func TestDWARFiOS(t *testing.T) {
+ // Normally we run TestDWARF on native platform. But on iOS we don't have
+ // go build, so we do this test with a cross build.
+ // Only run this on darwin/amd64, where we can cross build for iOS.
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
+ t.Skip("skipping on non-darwin/amd64 platform")
+ }
+ if err := exec.Command("xcrun", "--help").Run(); err != nil {
+ t.Skipf("error running xcrun, required for iOS cross build: %v", err)
+ }
+ // Check to see if the ios tools are installed. It's possible to have the command line tools
+ // installed without the iOS sdk.
+ if output, err := exec.Command("xcodebuild", "-showsdks").CombinedOutput(); err != nil {
+ t.Skipf("error running xcodebuild, required for iOS cross build: %v", err)
+ } else if !strings.Contains(string(output), "iOS SDK") {
+ t.Skipf("iOS SDK not detected.")
+ }
+ cc := "CC=" + runtime.GOROOT() + "/misc/ios/clangwrap.sh"
+ // iOS doesn't allow unmapped segments, so iOS executables don't have DWARF.
+ t.Run("exe", func(t *testing.T) {
+ testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=ios", "GOARCH=arm64")
+ })
+ // However, c-archive iOS objects have embedded DWARF.
+ t.Run("c-archive", func(t *testing.T) {
+ testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=ios", "GOARCH=arm64")
+ })
+}
diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go
new file mode 100644
index 0000000..20754d0
--- /dev/null
+++ b/src/cmd/link/elf_test.go
@@ -0,0 +1,424 @@
+// 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.
+
+// +build dragonfly freebsd linux netbsd openbsd
+
+package main
+
+import (
+ "cmd/internal/sys"
+ "debug/elf"
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
+ "testing"
+ "text/template"
+)
+
+func getCCAndCCFLAGS(t *testing.T, env []string) (string, []string) {
+ goTool := testenv.GoToolPath(t)
+ cmd := exec.Command(goTool, "env", "CC")
+ cmd.Env = env
+ ccb, err := cmd.Output()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cc := strings.TrimSpace(string(ccb))
+
+ cmd = exec.Command(goTool, "env", "GOGCCFLAGS")
+ cmd.Env = env
+ cflagsb, err := cmd.Output()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cflags := strings.Fields(string(cflagsb))
+
+ return cc, cflags
+}
+
+var asmSource = `
+ .section .text1,"ax"
+s1:
+ .byte 0
+ .section .text2,"ax"
+s2:
+ .byte 0
+`
+
+var goSource = `
+package main
+func main() {}
+`
+
+// The linker used to crash if an ELF input file had multiple text sections
+// with the same name.
+func TestSectionsWithSameName(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ objcopy, err := exec.LookPath("objcopy")
+ if err != nil {
+ t.Skipf("can't find objcopy: %v", err)
+ }
+
+ dir, err := ioutil.TempDir("", "go-link-TestSectionsWithSameName")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ gopath := filepath.Join(dir, "GOPATH")
+ env := append(os.Environ(), "GOPATH="+gopath)
+
+ if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ asmFile := filepath.Join(dir, "x.s")
+ if err := ioutil.WriteFile(asmFile, []byte(asmSource), 0444); err != nil {
+ t.Fatal(err)
+ }
+
+ goTool := testenv.GoToolPath(t)
+ cc, cflags := getCCAndCCFLAGS(t, env)
+
+ asmObj := filepath.Join(dir, "x.o")
+ t.Logf("%s %v -c -o %s %s", cc, cflags, asmObj, asmFile)
+ if out, err := exec.Command(cc, append(cflags, "-c", "-o", asmObj, asmFile)...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ asm2Obj := filepath.Join(dir, "x2.syso")
+ t.Logf("%s --rename-section .text2=.text1 %s %s", objcopy, asmObj, asm2Obj)
+ if out, err := exec.Command(objcopy, "--rename-section", ".text2=.text1", asmObj, asm2Obj).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ for _, s := range []string{asmFile, asmObj} {
+ if err := os.Remove(s); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ goFile := filepath.Join(dir, "main.go")
+ if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command(goTool, "build")
+ cmd.Dir = dir
+ cmd.Env = env
+ t.Logf("%s build", goTool)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+}
+
+var cSources35779 = []string{`
+static int blah() { return 42; }
+int Cfunc1() { return blah(); }
+`, `
+static int blah() { return 42; }
+int Cfunc2() { return blah(); }
+`,
+}
+
+// TestMinusRSymsWithSameName tests a corner case in the new
+// loader. Prior to the fix this failed with the error 'loadelf:
+// $WORK/b001/_pkg_.a(ldr.syso): duplicate symbol reference: blah in
+// both main(.text) and main(.text)'. See issue #35779.
+func TestMinusRSymsWithSameName(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ dir, err := ioutil.TempDir("", "go-link-TestMinusRSymsWithSameName")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ gopath := filepath.Join(dir, "GOPATH")
+ env := append(os.Environ(), "GOPATH="+gopath)
+
+ if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ goTool := testenv.GoToolPath(t)
+ cc, cflags := getCCAndCCFLAGS(t, env)
+
+ objs := []string{}
+ csrcs := []string{}
+ for i, content := range cSources35779 {
+ csrcFile := filepath.Join(dir, fmt.Sprintf("x%d.c", i))
+ csrcs = append(csrcs, csrcFile)
+ if err := ioutil.WriteFile(csrcFile, []byte(content), 0444); err != nil {
+ t.Fatal(err)
+ }
+
+ obj := filepath.Join(dir, fmt.Sprintf("x%d.o", i))
+ objs = append(objs, obj)
+ t.Logf("%s %v -c -o %s %s", cc, cflags, obj, csrcFile)
+ if out, err := exec.Command(cc, append(cflags, "-c", "-o", obj, csrcFile)...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ }
+
+ sysoObj := filepath.Join(dir, "ldr.syso")
+ t.Logf("%s %v -nostdlib -r -o %s %v", cc, cflags, sysoObj, objs)
+ if out, err := exec.Command(cc, append(cflags, "-nostdlib", "-r", "-o", sysoObj, objs[0], objs[1])...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ cruft := [][]string{objs, csrcs}
+ for _, sl := range cruft {
+ for _, s := range sl {
+ if err := os.Remove(s); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+
+ goFile := filepath.Join(dir, "main.go")
+ if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("%s build", goTool)
+ cmd := exec.Command(goTool, "build")
+ cmd.Dir = dir
+ cmd.Env = env
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+}
+
+const pieSourceTemplate = `
+package main
+
+import "fmt"
+
+// Force the creation of a lot of type descriptors that will go into
+// the .data.rel.ro section.
+{{range $index, $element := .}}var V{{$index}} interface{} = [{{$index}}]int{}
+{{end}}
+
+func main() {
+{{range $index, $element := .}} fmt.Println(V{{$index}})
+{{end}}
+}
+`
+
+func TestPIESize(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // We don't want to test -linkmode=external if cgo is not supported.
+ // On some systems -buildmode=pie implies -linkmode=external, so just
+ // always skip the test if cgo is not supported.
+ testenv.MustHaveCGO(t)
+
+ if !sys.BuildModeSupported(runtime.Compiler, "pie", runtime.GOOS, runtime.GOARCH) {
+ t.Skip("-buildmode=pie not supported")
+ }
+
+ t.Parallel()
+
+ tmpl := template.Must(template.New("pie").Parse(pieSourceTemplate))
+
+ writeGo := func(t *testing.T, dir string) {
+ f, err := os.Create(filepath.Join(dir, "pie.go"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Passing a 100-element slice here will cause
+ // pieSourceTemplate to create 100 variables with
+ // different types.
+ if err := tmpl.Execute(f, make([]byte, 100)); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := f.Close(); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ for _, external := range []bool{false, true} {
+ external := external
+
+ name := "TestPieSize-"
+ if external {
+ name += "external"
+ } else {
+ name += "internal"
+ }
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dir, err := ioutil.TempDir("", "go-link-"+name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ writeGo(t, dir)
+
+ binexe := filepath.Join(dir, "exe")
+ binpie := filepath.Join(dir, "pie")
+ if external {
+ binexe += "external"
+ binpie += "external"
+ }
+
+ build := func(bin, mode string) error {
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", bin, "-buildmode="+mode)
+ if external {
+ cmd.Args = append(cmd.Args, "-ldflags=-linkmode=external")
+ }
+ cmd.Args = append(cmd.Args, "pie.go")
+ cmd.Dir = dir
+ t.Logf("%v", cmd.Args)
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Error(err)
+ }
+ return err
+ }
+
+ var errexe, errpie error
+ var wg sync.WaitGroup
+ wg.Add(2)
+ go func() {
+ defer wg.Done()
+ errexe = build(binexe, "exe")
+ }()
+ go func() {
+ defer wg.Done()
+ errpie = build(binpie, "pie")
+ }()
+ wg.Wait()
+ if errexe != nil || errpie != nil {
+ t.Fatal("link failed")
+ }
+
+ var sizeexe, sizepie uint64
+ if fi, err := os.Stat(binexe); err != nil {
+ t.Fatal(err)
+ } else {
+ sizeexe = uint64(fi.Size())
+ }
+ if fi, err := os.Stat(binpie); err != nil {
+ t.Fatal(err)
+ } else {
+ sizepie = uint64(fi.Size())
+ }
+
+ elfexe, err := elf.Open(binexe)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer elfexe.Close()
+
+ elfpie, err := elf.Open(binpie)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer elfpie.Close()
+
+ // The difference in size between exe and PIE
+ // should be approximately the difference in
+ // size of the .text section plus the size of
+ // the PIE dynamic data sections plus the
+ // difference in size of the .got and .plt
+ // sections if they exist.
+ // We ignore unallocated sections.
+ // There may be gaps between non-writeable and
+ // writable PT_LOAD segments. We also skip those
+ // gaps (see issue #36023).
+
+ textsize := func(ef *elf.File, name string) uint64 {
+ for _, s := range ef.Sections {
+ if s.Name == ".text" {
+ return s.Size
+ }
+ }
+ t.Fatalf("%s: no .text section", name)
+ return 0
+ }
+ textexe := textsize(elfexe, binexe)
+ textpie := textsize(elfpie, binpie)
+
+ dynsize := func(ef *elf.File) uint64 {
+ var ret uint64
+ for _, s := range ef.Sections {
+ if s.Flags&elf.SHF_ALLOC == 0 {
+ continue
+ }
+ switch s.Type {
+ case elf.SHT_DYNSYM, elf.SHT_STRTAB, elf.SHT_REL, elf.SHT_RELA, elf.SHT_HASH, elf.SHT_GNU_HASH, elf.SHT_GNU_VERDEF, elf.SHT_GNU_VERNEED, elf.SHT_GNU_VERSYM:
+ ret += s.Size
+ }
+ if s.Flags&elf.SHF_WRITE != 0 && (strings.Contains(s.Name, ".got") || strings.Contains(s.Name, ".plt")) {
+ ret += s.Size
+ }
+ }
+ return ret
+ }
+
+ dynexe := dynsize(elfexe)
+ dynpie := dynsize(elfpie)
+
+ extrasize := func(ef *elf.File) uint64 {
+ var ret uint64
+ // skip unallocated sections
+ for _, s := range ef.Sections {
+ if s.Flags&elf.SHF_ALLOC == 0 {
+ ret += s.Size
+ }
+ }
+ // also skip gaps between PT_LOAD segments
+ var prev *elf.Prog
+ for _, seg := range ef.Progs {
+ if seg.Type != elf.PT_LOAD {
+ continue
+ }
+ if prev != nil {
+ ret += seg.Off - prev.Off - prev.Filesz
+ }
+ prev = seg
+ }
+ return ret
+ }
+
+ extraexe := extrasize(elfexe)
+ extrapie := extrasize(elfpie)
+
+ diffReal := (sizepie - extrapie) - (sizeexe - extraexe)
+ diffExpected := (textpie + dynpie) - (textexe + dynexe)
+
+ t.Logf("real size difference %#x, expected %#x", diffReal, diffExpected)
+
+ if diffReal > (diffExpected + diffExpected/10) {
+ t.Errorf("PIE unexpectedly large: got difference of %d (%d - %d), expected difference %d", diffReal, sizepie, sizeexe, diffExpected)
+ }
+ })
+ }
+}
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
new file mode 100644
index 0000000..2d09a61
--- /dev/null
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -0,0 +1,676 @@
+// Inferno utils/6l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/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 amd64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "log"
+)
+
+func PADDR(x uint32) uint32 {
+ return x &^ 0x80000000
+}
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op ...uint8) {
+ for _, op1 := range op {
+ initfunc.AddUint8(op1)
+ }
+ }
+
+ // 0000000000000000 <local.dso_init>:
+ // 0: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 7 <local.dso_init+0x7>
+ // 3: R_X86_64_PC32 runtime.firstmoduledata-0x4
+ o(0x48, 0x8d, 0x3d)
+ initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 0)
+ // 7: e8 00 00 00 00 callq c <local.dso_init+0xc>
+ // 8: R_X86_64_PLT32 runtime.addmoduledata-0x4
+ o(0xe8)
+ initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALL, 4)
+ // c: c3 retq
+ o(0xc3)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch rt := r.Type(); rt {
+ default:
+ if rt >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_X86_64_PC32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+ // sense and should be removed when someone has thought about it properly.
+ if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_X86_64_PC64 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+8)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PLT32):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCREL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCRELX),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_REX_GOTPCRELX):
+ su := ldr.MakeSymbolUpdater(s)
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ sData := ldr.Data(s)
+ if r.Off() >= 2 && sData[r.Off()-2] == 0x8b {
+ su.MakeWritable()
+ // turn MOVQ of GOT entry into LEAQ of symbol itself
+ writeableData := su.Data()
+ writeableData[r.Off()-2] = 0x8d
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+ }
+ }
+
+ // fall back to using GOT and hope for the best (CMOV*)
+ // TODO: just needs relocation, no need to put in .dynsym
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT))
+
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ break
+ }
+ return true
+
+ // Handle relocations found in Mach-O object files.
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0:
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ if rt == objabi.MachoRelocOffset+ld.MACHO_X86_64_RELOC_UNSIGNED*2 {
+ break
+ } else {
+ // MACHO_X86_64_RELOC_SIGNED or MACHO_X86_64_RELOC_BRANCH
+ // Can this happen? The object is expected to be PIC.
+ ldr.Errorf(s, "unsupported relocation for PIE: %v", rt)
+ }
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1:
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ return true
+ }
+ fallthrough
+
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 1,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 1,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_1*2 + 1,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_2*2 + 1,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_4*2 + 1:
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected pc-relative reloc for dynamic symbol %s", ldr.SymName(targ))
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1:
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ // turn MOVQ of GOT entry into LEAQ of symbol itself
+ sdata := ldr.Data(s)
+ if r.Off() < 2 || sdata[r.Off()-2] != 0x8b {
+ ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+
+ su := ldr.MakeSymbolUpdater(s)
+ su.MakeWritable()
+ sdata = su.Data()
+ sdata[r.Off()-2] = 0x8d
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+ }
+ fallthrough
+
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT*2 + 1:
+ if targType != sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ }
+ ld.AddGotSym(target, ldr, syms, targ, 0)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ // Reread the reloc to incorporate any changes in type above.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_CALL,
+ objabi.R_PCREL:
+ if targType != sym.SDYNIMPORT {
+ // nothing to do, the relocation will be laid out in reloc
+ return true
+ }
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ // Internal linking, for both ELF and Mach-O.
+ // Build a PLT entry and change the relocation target to that entry.
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ return true
+
+ case objabi.R_ADDR:
+ if ldr.SymType(s) == sym.STEXT && target.IsElf() {
+ su := ldr.MakeSymbolUpdater(s)
+ if target.IsSolaris() {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ return true
+ }
+ // The code is asking for the address of an external
+ // function. We provide it with the address of the
+ // correspondent GOT symbol.
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT))
+
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ // Process dynamic relocations for the data sections.
+ if target.IsPIE() && target.IsInternal() {
+ // When internally linking, generate dynamic relocations
+ // for all typical R_ADDR relocations. The exception
+ // are those R_ADDR that are created as part of generating
+ // the dynamic relocations and must be resolved statically.
+ //
+ // There are three phases relevant to understanding this:
+ //
+ // dodata() // we are here
+ // address() // symbol address assignment
+ // reloc() // resolution of static R_ADDR relocs
+ //
+ // At this point symbol addresses have not been
+ // assigned yet (as the final size of the .rela section
+ // will affect the addresses), and so we cannot write
+ // the Elf64_Rela.r_offset now. Instead we delay it
+ // until after the 'address' phase of the linker is
+ // complete. We do this via Addaddrplus, which creates
+ // a new R_ADDR relocation which will be resolved in
+ // the 'reloc' phase.
+ //
+ // These synthetic static R_ADDR relocs must be skipped
+ // now, or else we will be caught in an infinite loop
+ // of generating synthetic relocs for our synthetic
+ // relocs.
+ //
+ // Furthermore, the rela sections contain dynamic
+ // relocations with R_ADDR relocations on
+ // Elf64_Rela.r_offset. This field should contain the
+ // symbol offset as determined by reloc(), not the
+ // final dynamically linked address as a dynamic
+ // relocation would provide.
+ switch ldr.SymName(s) {
+ case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic":
+ return false
+ }
+ } else {
+ // Either internally linking a static executable,
+ // in which case we can resolve these relocations
+ // statically in the 'reloc' phase, or externally
+ // linking, in which case the relocation will be
+ // prepared in the 'reloc' phase and passed to the
+ // external linker in the 'asmb' phase.
+ if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA {
+ break
+ }
+ }
+
+ if target.IsElf() {
+ // Generate R_X86_64_RELATIVE relocations for best
+ // efficiency in the dynamic linker.
+ //
+ // As noted above, symbol addresses have not been
+ // assigned yet, so we can't generate the final reloc
+ // entry yet. We ultimately want:
+ //
+ // r_offset = s + r.Off
+ // r_info = R_X86_64_RELATIVE
+ // r_addend = targ + r.Add
+ //
+ // The dynamic linker will set *offset = base address +
+ // addend.
+ //
+ // AddAddrPlus is used for r_offset and r_addend to
+ // generate new R_ADDR relocations that will update
+ // these fields in the 'reloc' phase.
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ if r.Siz() == 8 {
+ rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_X86_64_RELATIVE)))
+ } else {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ rela.AddAddrPlus(target.Arch, targ, int64(r.Add()))
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+
+ if target.IsDarwin() {
+ // 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.
+ ld.MachoAddRebase(s, int64(r.Off()))
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+ }
+
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_X86_64_32) | uint64(elfsym)<<32)
+ } else if siz == 8 {
+ out.Write64(uint64(elf.R_X86_64_64) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ case objabi.R_TLS_LE:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_X86_64_TPOFF32) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ case objabi.R_TLS_IE:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_X86_64_GOTTPOFF) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ case objabi.R_CALL:
+ if siz == 4 {
+ if ldr.SymType(r.Xsym) == sym.SDYNIMPORT {
+ out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32)
+ } else {
+ out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_PCREL:
+ if siz == 4 {
+ if ldr.SymType(r.Xsym) == sym.SDYNIMPORT && ldr.SymElfType(r.Xsym) == elf.STT_FUNC {
+ out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32)
+ } else {
+ out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_GOTPCREL:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_X86_64_GOTPCREL) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ }
+
+ out.Write64(uint64(r.Xadd))
+ return true
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ var v uint32
+
+ rs := r.Xsym
+ rt := r.Type
+
+ if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_PCREL || rt == objabi.R_GOTPCREL || rt == objabi.R_CALL {
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ v = uint32(ldr.SymDynid(rs))
+ v |= 1 << 27 // external relocation
+ } else {
+ v = uint32(ldr.SymSect(rs).Extnum)
+ if v == 0 {
+ ldr.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymSect(rs).Name, ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+ }
+
+ switch rt {
+ default:
+ return false
+
+ case objabi.R_ADDR:
+ v |= ld.MACHO_X86_64_RELOC_UNSIGNED << 28
+
+ case objabi.R_CALL:
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_X86_64_RELOC_BRANCH << 28
+
+ // NOTE: Only works with 'external' relocation. Forced above.
+ case objabi.R_PCREL:
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_X86_64_RELOC_SIGNED << 28
+ case objabi.R_GOTPCREL:
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_X86_64_RELOC_GOT_LOAD << 28
+ }
+
+ switch r.Size {
+ default:
+ return false
+
+ case 1:
+ v |= 0 << 25
+
+ case 2:
+ v |= 1 << 25
+
+ case 4:
+ v |= 2 << 25
+
+ case 8:
+ v |= 3 << 25
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(v)
+ return true
+}
+
+func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ var v uint32
+
+ rs := r.Xsym
+ rt := r.Type
+
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(ldr.SymDynid(rs)))
+
+ switch rt {
+ default:
+ return false
+
+ case objabi.R_DWARFSECREF:
+ v = ld.IMAGE_REL_AMD64_SECREL
+
+ case objabi.R_ADDR:
+ if r.Size == 8 {
+ v = ld.IMAGE_REL_AMD64_ADDR64
+ } else {
+ v = ld.IMAGE_REL_AMD64_ADDR32
+ }
+
+ case objabi.R_CALL,
+ objabi.R_PCREL:
+ v = ld.IMAGE_REL_AMD64_REL32
+ }
+
+ out.Write16(uint16(v))
+
+ return true
+}
+
+func archreloc(*ld.Target, *loader.Loader, *ld.ArchSyms, loader.Reloc, loader.Sym, int64) (int64, int, bool) {
+ return -1, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 {
+ log.Fatalf("unexpected relocation variant")
+ return -1
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // pushq got+8(IP)
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x35)
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 8)
+
+ // jmpq got+16(IP)
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x25)
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 16)
+
+ // nopl 0(AX)
+ plt.AddUint32(ctxt.Arch, 0x00401f0f)
+
+ // assume got->size == 0 too
+ got.AddAddrPlus(ctxt.Arch, dynamic, 0)
+
+ got.AddUint64(ctxt.Arch, 0)
+ got.AddUint64(ctxt.Arch, 0)
+ }
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ got := ldr.MakeSymbolUpdater(syms.GOTPLT)
+ rela := ldr.MakeSymbolUpdater(syms.RelaPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // jmpq *got+size(IP)
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x25)
+ plt.AddPCRelPlus(target.Arch, got.Sym(), got.Size())
+
+ // add to got: pointer to current pos in plt
+ got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size())
+
+ // pushq $x
+ plt.AddUint8(0x68)
+
+ plt.AddUint32(target.Arch, uint32((got.Size()-24-8)/8))
+
+ // jmpq .plt
+ plt.AddUint8(0xe9)
+
+ plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4)))
+
+ // rela
+ rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8)
+
+ sDynid := ldr.SymDynid(s)
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_X86_64_JMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
+
+ ldr.SetPlt(s, int32(plt.Size()-16))
+ } else if target.IsDarwin() {
+ ld.AddGotSym(target, ldr, syms, s, 0)
+
+ sDynid := ldr.SymDynid(s)
+ lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
+ lep.AddUint32(target.Arch, uint32(sDynid))
+
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ ldr.SetPlt(s, int32(plt.Size()))
+
+ // jmpq *got+size(IP)
+ plt.AddUint8(0xff)
+ plt.AddUint8(0x25)
+ plt.AddPCRelPlus(target.Arch, syms.GOT, int64(ldr.SymGot(s)))
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
+
+func tlsIEtoLE(P []byte, off, size int) {
+ // Transform the PC-relative instruction into a constant load.
+ // That is,
+ //
+ // MOVQ X(IP), REG -> MOVQ $Y, REG
+ //
+ // To determine the instruction and register, we study the op codes.
+ // Consult an AMD64 instruction encoding guide to decipher this.
+ if off < 3 {
+ log.Fatal("R_X86_64_GOTTPOFF reloc not preceded by MOVQ or ADDQ instruction")
+ }
+ op := P[off-3 : off]
+ reg := op[2] >> 3
+
+ if op[1] == 0x8b || reg == 4 {
+ // MOVQ
+ if op[0] == 0x4c {
+ op[0] = 0x49
+ } else if size == 4 && op[0] == 0x44 {
+ op[0] = 0x41
+ }
+ if op[1] == 0x8b {
+ op[1] = 0xc7
+ } else {
+ op[1] = 0x81 // special case for SP
+ }
+ op[2] = 0xc0 | reg
+ } else {
+ // An alternate op is ADDQ. This is handled by GNU gold,
+ // but right now is not generated by the Go compiler:
+ // ADDQ X(IP), REG -> ADDQ $Y, REG
+ // Consider adding support for it here.
+ log.Fatalf("expected TLS IE op to be MOVQ, got %v", op)
+ }
+}
diff --git a/src/cmd/link/internal/amd64/l.go b/src/cmd/link/internal/amd64/l.go
new file mode 100644
index 0000000..c9ea90a
--- /dev/null
+++ b/src/cmd/link/internal/amd64/l.go
@@ -0,0 +1,43 @@
+// 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.
+
+package amd64
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 32
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 7
+ dwarfRegLR = 16
+)
diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go
new file mode 100644
index 0000000..d09c90e
--- /dev/null
+++ b/src/cmd/link/internal/amd64/obj.go
@@ -0,0 +1,122 @@
+// 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 amd64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchAMD64
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ // 0xCC is INT $3 - breakpoint instruction
+ CodePad: []byte{0xCC},
+
+ Plan9Magic: uint32(4*26*26 + 7),
+ Plan9_64Bit: true,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+ MachorelocSize: 8,
+ PEreloc1: pereloc1,
+ TLSIEtoLE: tlsIEtoLE,
+
+ Linuxdynld: "/lib64/ld-linux-x86-64.so.2",
+ Freebsddynld: "/libexec/ld-elf.so.1",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "/libexec/ld.elf_so",
+ Dragonflydynld: "/usr/libexec/ld-elf.so.2",
+ Solarisdynld: "/lib/amd64/ld.so.1",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32 + 8
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x200000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x200000
+ }
+
+ case objabi.Hdarwin: /* apple MACH */
+ ld.HEADR = ld.INITIAL_MACHO_HEADR
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x1000000 + int64(ld.HEADR)
+ }
+
+ case objabi.Hlinux, /* elf64 executable */
+ objabi.Hfreebsd, /* freebsd */
+ objabi.Hnetbsd, /* netbsd */
+ objabi.Hopenbsd, /* openbsd */
+ objabi.Hdragonfly, /* dragonfly */
+ objabi.Hsolaris: /* solaris */
+ ld.Elfinit(ctxt)
+
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = (1 << 22) + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hwindows: /* PE executable */
+ // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+ return
+ }
+}
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
new file mode 100644
index 0000000..03caeae
--- /dev/null
+++ b/src/cmd/link/internal/arm/asm.go
@@ -0,0 +1,657 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 arm
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "log"
+)
+
+// This assembler:
+//
+// .align 2
+// local.dso_init:
+// ldr r0, .Lmoduledata
+// .Lloadfrom:
+// ldr r0, [r0]
+// b runtime.addmoduledata@plt
+// .align 2
+// .Lmoduledata:
+// .word local.moduledata(GOT_PREL) + (. - (.Lloadfrom + 4))
+// assembles to:
+//
+// 00000000 <local.dso_init>:
+// 0: e59f0004 ldr r0, [pc, #4] ; c <local.dso_init+0xc>
+// 4: e5900000 ldr r0, [r0]
+// 8: eafffffe b 0 <runtime.addmoduledata>
+// 8: R_ARM_JUMP24 runtime.addmoduledata
+// c: 00000004 .word 0x00000004
+// c: R_ARM_GOT_PREL local.moduledata
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op uint32) {
+ initfunc.AddUint32(ctxt.Arch, op)
+ }
+ o(0xe59f0004)
+ o(0xe08f0000)
+
+ o(0xeafffffe)
+ rel, _ := initfunc.AddRel(objabi.R_CALLARM)
+ rel.SetOff(8)
+ rel.SetSiz(4)
+ rel.SetSym(addmoduledata)
+ rel.SetAdd(0xeafffffe) // vomit
+
+ o(0x00000000)
+
+ rel2, _ := initfunc.AddRel(objabi.R_PCREL)
+ rel2.SetOff(12)
+ rel2.SetSiz(4)
+ rel2.SetSym(ctxt.Moduledata)
+ rel2.SetAdd(4)
+}
+
+// Preserve highest 8 bits of a, and do addition to lower 24-bit
+// of a and b; used to adjust ARM branch instruction's target
+func braddoff(a int32, b int32) int32 {
+ return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b))
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PLT32):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM)
+
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4)))
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_THM_PC22): // R_ARM_THM_CALL
+ ld.Exitf("R_ARM_THM_CALL, are you using -marm?")
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT32): // R_ARM_GOT_BREL
+ if targType != sym.SDYNIMPORT {
+ addgotsyminternal(target, ldr, syms, targ)
+ } else {
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT))
+ }
+
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
+ su.SetRelocSym(rIdx, 0)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil
+ if targType != sym.SDYNIMPORT {
+ addgotsyminternal(target, ldr, syms, targ)
+ } else {
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTOFF): // R_ARM_GOTOFF32
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_GOTOFF)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTPC): // R_ARM_BASE_PREL
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4)))
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_REL32): // R_ARM_REL32
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_ABS32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4)))
+ }
+
+ return true
+ }
+
+ // Handle references to ELF symbols from our own object files.
+ if targType != sym.SDYNIMPORT {
+ return true
+ }
+
+ // Reread the reloc to incorporate any changes in type above.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_CALLARM:
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) // TODO: don't use r.Add for instruction bytes (issue 19811)
+ return true
+
+ case objabi.R_ADDR:
+ if ldr.SymType(s) != sym.SDATA {
+ break
+ }
+ if target.IsElf() {
+ ld.Adddynsym(ldr, target, syms, targ)
+ rel := ldr.MakeSymbolUpdater(syms.Rel)
+ rel.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(targ)), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
+ su.SetRelocSym(rIdx, 0)
+ return true
+ }
+ }
+
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write32(uint32(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_ARM_ABS32) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_PCREL:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_ARM_REL32) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_CALLARM:
+ if siz == 4 {
+ relocs := ldr.Relocs(s)
+ r := relocs.At(ri)
+ if r.Add()&0xff000000 == 0xeb000000 { // BL // TODO: using r.Add here is bad (issue 19811)
+ out.Write32(uint32(elf.R_ARM_CALL) | uint32(elfsym)<<8)
+ } else {
+ out.Write32(uint32(elf.R_ARM_JUMP24) | uint32(elfsym)<<8)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_TLS_LE:
+ out.Write32(uint32(elf.R_ARM_TLS_LE32) | uint32(elfsym)<<8)
+ case objabi.R_TLS_IE:
+ out.Write32(uint32(elf.R_ARM_TLS_IE32) | uint32(elfsym)<<8)
+ case objabi.R_GOTPCREL:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_ARM_GOT_PREL) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ }
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // str lr, [sp, #-4]!
+ plt.AddUint32(ctxt.Arch, 0xe52de004)
+
+ // ldr lr, [pc, #4]
+ plt.AddUint32(ctxt.Arch, 0xe59fe004)
+
+ // add lr, pc, lr
+ plt.AddUint32(ctxt.Arch, 0xe08fe00e)
+
+ // ldr pc, [lr, #8]!
+ plt.AddUint32(ctxt.Arch, 0xe5bef008)
+
+ // .word &GLOBAL_OFFSET_TABLE[0] - .
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 4)
+
+ // the first .plt entry requires 3 .plt.got entries
+ got.AddUint32(ctxt.Arch, 0)
+
+ got.AddUint32(ctxt.Arch, 0)
+ got.AddUint32(ctxt.Arch, 0)
+ }
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ rs := r.Xsym
+ rt := r.Type
+
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(ldr.SymDynid(rs)))
+
+ var v uint32
+ switch rt {
+ default:
+ // unsupported relocation type
+ return false
+
+ case objabi.R_DWARFSECREF:
+ v = ld.IMAGE_REL_ARM_SECREL
+
+ case objabi.R_ADDR:
+ v = ld.IMAGE_REL_ARM_ADDR32
+ }
+
+ out.Write16(uint16(v))
+
+ return true
+}
+
+// sign extend a 24-bit integer
+func signext24(x int64) int32 {
+ return (int32(x) << 8) >> 8
+}
+
+// encode an immediate in ARM's imm12 format. copied from ../../../internal/obj/arm/asm5.go
+func immrot(v uint32) uint32 {
+ for i := 0; i < 16; i++ {
+ if v&^0xff == 0 {
+ return uint32(i<<8) | v | 1<<25
+ }
+ v = v<<2 | v>>30
+ }
+ return 0
+}
+
+// Convert the direct jump relocation r to refer to a trampoline if the target is too far
+func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
+ relocs := ldr.Relocs(s)
+ r := relocs.At(ri)
+ switch r.Type() {
+ case objabi.R_CALLARM:
+ var t int64
+ // ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
+ // laid out. Conservatively use a trampoline. This should be rare, as we lay out packages
+ // in dependency order.
+ if ldr.SymValue(rs) != 0 {
+ // r.Add is the instruction
+ // low 24-bit encodes the target address
+ t = (ldr.SymValue(rs) + int64(signext24(r.Add()&0xffffff)*4) - (ldr.SymValue(s) + int64(r.Off()))) / 4
+ }
+ if t > 0x7fffff || t < -0x800000 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && ldr.SymPkg(s) != ldr.SymPkg(rs)) {
+ // direct call too far, need to insert trampoline.
+ // look up existing trampolines first. if we found one within the range
+ // of direct call, we can reuse it. otherwise create a new one.
+ offset := (signext24(r.Add()&0xffffff) + 2) * 4
+ var tramp loader.Sym
+ for i := 0; ; i++ {
+ oName := ldr.SymName(rs)
+ name := oName + fmt.Sprintf("%+d-tramp%d", offset, i)
+ tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
+ ldr.SetAttrReachable(tramp, true)
+ if ldr.SymType(tramp) == sym.SDYNIMPORT {
+ // don't reuse trampoline defined in other module
+ continue
+ }
+ if oName == "runtime.deferreturn" {
+ ldr.SetIsDeferReturnTramp(tramp, true)
+ }
+ if ldr.SymValue(tramp) == 0 {
+ // either the trampoline does not exist -- we need to create one,
+ // or found one the address which is not assigned -- this will be
+ // laid down immediately after the current function. use this one.
+ break
+ }
+
+ t = (ldr.SymValue(tramp) - 8 - (ldr.SymValue(s) + int64(r.Off()))) / 4
+ if t >= -0x800000 && t < 0x7fffff {
+ // found an existing trampoline that is not too far
+ // we can just use it
+ break
+ }
+ }
+ if ldr.SymType(tramp) == 0 {
+ // trampoline does not exist, create one
+ trampb := ldr.MakeSymbolUpdater(tramp)
+ ctxt.AddTramp(trampb)
+ if ctxt.DynlinkingGo() {
+ if immrot(uint32(offset)) == 0 {
+ ctxt.Errorf(s, "odd offset in dynlink direct call: %v+%d", ldr.SymName(rs), offset)
+ }
+ gentrampdyn(ctxt.Arch, trampb, rs, int64(offset))
+ } else if ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE {
+ gentramppic(ctxt.Arch, trampb, rs, int64(offset))
+ } else {
+ gentramp(ctxt.Arch, ctxt.LinkMode, ldr, trampb, rs, int64(offset))
+ }
+ }
+ // modify reloc to point to tramp, which will be resolved later
+ sb := ldr.MakeSymbolUpdater(s)
+ relocs := sb.Relocs()
+ r := relocs.At(ri)
+ r.SetSym(tramp)
+ r.SetAdd(r.Add()&0xff000000 | 0xfffffe) // clear the offset embedded in the instruction
+ }
+ default:
+ ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+ }
+}
+
+// generate a trampoline to target+offset
+func gentramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(12) // 3 instructions
+ P := make([]byte, tramp.Size())
+ t := ldr.SymValue(target) + offset
+ o1 := uint32(0xe5900000 | 11<<12 | 15<<16) // MOVW (R15), R11 // R15 is actual pc + 8
+ o2 := uint32(0xe12fff10 | 11) // JMP (R11)
+ o3 := uint32(t) // WORD $target
+ arch.ByteOrder.PutUint32(P, o1)
+ arch.ByteOrder.PutUint32(P[4:], o2)
+ arch.ByteOrder.PutUint32(P[8:], o3)
+ tramp.SetData(P)
+
+ if linkmode == ld.LinkExternal || ldr.SymValue(target) == 0 {
+ r, _ := tramp.AddRel(objabi.R_ADDR)
+ r.SetOff(8)
+ r.SetSiz(4)
+ r.SetSym(target)
+ r.SetAdd(offset)
+ }
+}
+
+// generate a trampoline to target+offset in position independent code
+func gentramppic(arch *sys.Arch, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(16) // 4 instructions
+ P := make([]byte, tramp.Size())
+ o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 4) // MOVW 4(R15), R11 // R15 is actual pc + 8
+ o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11
+ o3 := uint32(0xe12fff10 | 11) // JMP (R11)
+ o4 := uint32(0) // WORD $(target-pc) // filled in with relocation
+ arch.ByteOrder.PutUint32(P, o1)
+ arch.ByteOrder.PutUint32(P[4:], o2)
+ arch.ByteOrder.PutUint32(P[8:], o3)
+ arch.ByteOrder.PutUint32(P[12:], o4)
+ tramp.SetData(P)
+
+ r, _ := tramp.AddRel(objabi.R_PCREL)
+ r.SetOff(12)
+ r.SetSiz(4)
+ r.SetSym(target)
+ r.SetAdd(offset + 4)
+}
+
+// generate a trampoline to target+offset in dynlink mode (using GOT)
+func gentrampdyn(arch *sys.Arch, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(20) // 5 instructions
+ o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 8) // MOVW 8(R15), R11 // R15 is actual pc + 8
+ o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11
+ o3 := uint32(0xe5900000 | 11<<12 | 11<<16) // MOVW (R11), R11
+ o4 := uint32(0xe12fff10 | 11) // JMP (R11)
+ o5 := uint32(0) // WORD $target@GOT // filled in with relocation
+ o6 := uint32(0)
+ if offset != 0 {
+ // insert an instruction to add offset
+ tramp.SetSize(24) // 6 instructions
+ o6 = o5
+ o5 = o4
+ o4 = 0xe2800000 | 11<<12 | 11<<16 | immrot(uint32(offset)) // ADD $offset, R11, R11
+ o1 = uint32(0xe5900000 | 11<<12 | 15<<16 | 12) // MOVW 12(R15), R11
+ }
+ P := make([]byte, tramp.Size())
+ arch.ByteOrder.PutUint32(P, o1)
+ arch.ByteOrder.PutUint32(P[4:], o2)
+ arch.ByteOrder.PutUint32(P[8:], o3)
+ arch.ByteOrder.PutUint32(P[12:], o4)
+ arch.ByteOrder.PutUint32(P[16:], o5)
+ if offset != 0 {
+ arch.ByteOrder.PutUint32(P[20:], o6)
+ }
+ tramp.SetData(P)
+
+ r, _ := tramp.AddRel(objabi.R_GOTPCREL)
+ r.SetOff(16)
+ r.SetSiz(4)
+ r.SetSym(target)
+ r.SetAdd(8)
+ if offset != 0 {
+ // increase reloc offset by 4 as we inserted an ADD instruction
+ r.SetOff(20)
+ r.SetAdd(12)
+ }
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ rs := r.Sym()
+ rs = ldr.ResolveABIAlias(rs)
+ if target.IsExternal() {
+ switch r.Type() {
+ case objabi.R_CALLARM:
+ // set up addend for eventual relocation via outer symbol.
+ _, off := ld.FoldSubSymbolOffset(ldr, rs)
+ xadd := int64(signext24(r.Add()&0xffffff))*4 + off
+ if xadd/4 > 0x7fffff || xadd/4 < -0x800000 {
+ ldr.Errorf(s, "direct call too far %d", xadd/4)
+ }
+ return int64(braddoff(int32(0xff000000&uint32(r.Add())), int32(0xffffff&uint32(xadd/4)))), 1, true
+ }
+ return -1, 0, false
+ }
+
+ const isOk = true
+ const noExtReloc = 0
+ switch r.Type() {
+ // The following three arch specific relocations are only for generation of
+ // Linux/ARM ELF's PLT entry (3 assembler instruction)
+ case objabi.R_PLT0: // add ip, pc, #0xXX00000
+ if ldr.SymValue(syms.GOTPLT) < ldr.SymValue(syms.PLT) {
+ ldr.Errorf(s, ".got.plt should be placed after .plt section.")
+ }
+ return 0xe28fc600 + (0xff & (int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add())) >> 20)), noExtReloc, isOk
+ case objabi.R_PLT1: // add ip, ip, #0xYY000
+ return 0xe28cca00 + (0xff & (int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add()+4)) >> 12)), noExtReloc, isOk
+ case objabi.R_PLT2: // ldr pc, [ip, #0xZZZ]!
+ return 0xe5bcf000 + (0xfff & int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add()+8))), noExtReloc, isOk
+ case objabi.R_CALLARM: // bl XXXXXX or b YYYYYY
+ // r.Add is the instruction
+ // low 24-bit encodes the target address
+ t := (ldr.SymValue(rs) + int64(signext24(r.Add()&0xffffff)*4) - (ldr.SymValue(s) + int64(r.Off()))) / 4
+ if t > 0x7fffff || t < -0x800000 {
+ ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t)
+ }
+ return int64(braddoff(int32(0xff000000&uint32(r.Add())), int32(0xffffff&t))), noExtReloc, isOk
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 {
+ log.Fatalf("unexpected relocation variant")
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ rs := ldr.ResolveABIAlias(r.Sym())
+ var rr loader.ExtReloc
+ switch r.Type() {
+ case objabi.R_CALLARM:
+ // set up addend for eventual relocation via outer symbol.
+ rs, off := ld.FoldSubSymbolOffset(ldr, rs)
+ rr.Xadd = int64(signext24(r.Add()&0xffffff))*4 + 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, true
+ }
+ return rr, false
+}
+
+func addpltreloc(ldr *loader.Loader, plt *loader.SymbolBuilder, got *loader.SymbolBuilder, s loader.Sym, typ objabi.RelocType) {
+ r, _ := plt.AddRel(typ)
+ r.SetSym(got.Sym())
+ r.SetOff(int32(plt.Size()))
+ r.SetSiz(4)
+ r.SetAdd(int64(ldr.SymGot(s)) - 8)
+
+ plt.SetReachable(true)
+ plt.SetSize(plt.Size() + 4)
+ plt.Grow(plt.Size())
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ got := ldr.MakeSymbolUpdater(syms.GOTPLT)
+ rel := ldr.MakeSymbolUpdater(syms.RelPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // .got entry
+ ldr.SetGot(s, int32(got.Size()))
+
+ // In theory, all GOT should point to the first PLT entry,
+ // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's
+ // dynamic linker won't, so we'd better do it ourselves.
+ got.AddAddrPlus(target.Arch, plt.Sym(), 0)
+
+ // .plt entry, this depends on the .got entry
+ ldr.SetPlt(s, int32(plt.Size()))
+
+ addpltreloc(ldr, plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000
+ addpltreloc(ldr, plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000
+ addpltreloc(ldr, plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]!
+
+ // rel
+ rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
+
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), uint32(elf.R_ARM_JUMP_SLOT)))
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
+
+func addgotsyminternal(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymGot(s) >= 0 {
+ return
+ }
+
+ got := ldr.MakeSymbolUpdater(syms.GOT)
+ ldr.SetGot(s, int32(got.Size()))
+ got.AddAddrPlus(target.Arch, s, 0)
+
+ if target.IsElf() {
+ } else {
+ ldr.Errorf(s, "addgotsyminternal: unsupported binary format")
+ }
+}
diff --git a/src/cmd/link/internal/arm/l.go b/src/cmd/link/internal/arm/l.go
new file mode 100644
index 0000000..d16dc27
--- /dev/null
+++ b/src/cmd/link/internal/arm/l.go
@@ -0,0 +1,75 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 arm
+
+// Writing object files.
+
+// Inferno utils/5l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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.
+
+const (
+ maxAlign = 8 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 4 // single-instruction alignment
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 13
+ dwarfRegLR = 14
+)
diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go
new file mode 100644
index 0000000..fed8dce
--- /dev/null
+++ b/src/cmd/link/internal/arm/obj.go
@@ -0,0 +1,109 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 arm
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchARM
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+
+ Plan9Magic: 0x647,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Trampoline: trampoline,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 8,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+ PEreloc1: pereloc1,
+
+ Linuxdynld: "/lib/ld-linux.so.3", // 2 for OABI, 3 for EABI
+ Freebsddynld: "/usr/libexec/ld-elf.so.1",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "/libexec/ld.elf_so",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4128
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hlinux, /* arm elf */
+ objabi.Hfreebsd,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd:
+ *ld.FlagD = false
+ // with dynamic linking
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+
+ case objabi.Hwindows: /* PE executable */
+ // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+ return
+ }
+}
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
new file mode 100644
index 0000000..14a20a1
--- /dev/null
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -0,0 +1,1048 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 arm64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "log"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op uint32) {
+ initfunc.AddUint32(ctxt.Arch, op)
+ }
+ // 0000000000000000 <local.dso_init>:
+ // 0: 90000000 adrp x0, 0 <runtime.firstmoduledata>
+ // 0: R_AARCH64_ADR_PREL_PG_HI21 local.moduledata
+ // 4: 91000000 add x0, x0, #0x0
+ // 4: R_AARCH64_ADD_ABS_LO12_NC local.moduledata
+ o(0x90000000)
+ o(0x91000000)
+ rel, _ := initfunc.AddRel(objabi.R_ADDRARM64)
+ rel.SetOff(0)
+ rel.SetSiz(8)
+ rel.SetSym(ctxt.Moduledata)
+
+ // 8: 14000000 b 0 <runtime.addmoduledata>
+ // 8: R_AARCH64_CALL26 runtime.addmoduledata
+ o(0x14000000)
+ rel2, _ := initfunc.AddRel(objabi.R_CALLARM64)
+ rel2.SetOff(8)
+ rel2.SetSiz(4)
+ rel2.SetSym(addmoduledata)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ const pcrel = 1
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_AARCH64_PREL32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+ // sense and should be removed when someone has thought about it properly.
+ if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_AARCH64_PREL64 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+8)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26):
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+ if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) {
+ ldr.Errorf(s, "unknown symbol %s in callarm64", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM64)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_GOT_PAGE),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LD64_GOT_LO12_NC):
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ // TODO: turn LDR of GOT entry into ADR of symbol itself
+ }
+
+ // fall back to using GOT
+ // TODO: just needs relocation, no need to put in .dynsym
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_AARCH64_GLOB_DAT))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_GOT)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_PREL_PG_HI21),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADD_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_PCREL)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ABS64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_AARCH64_ABS64 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ break
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST8_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST8)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST16_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST16)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST32_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST32)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST64_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST64)
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST128_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST128)
+ return true
+
+ // Handle relocations found in Mach-O object files.
+ case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_UNSIGNED*2:
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ break
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM64)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_PAGE21*2 + pcrel,
+ objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_PAGEOFF12*2:
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_PCREL)
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21*2 + pcrel,
+ objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12*2:
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ // turn MOVD sym@GOT (adrp+ldr) into MOVD $sym (adrp+add)
+ data := ldr.Data(s)
+ off := r.Off()
+ if int(off+3) >= len(data) {
+ ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+ o := target.Arch.ByteOrder.Uint32(data[off:])
+ su := ldr.MakeSymbolUpdater(s)
+ switch {
+ case (o>>24)&0x9f == 0x90: // adrp
+ // keep instruction unchanged, change relocation type below
+ case o>>24 == 0xf9: // ldr
+ // rewrite to add
+ o = (0x91 << 24) | (o & (1<<22 - 1))
+ su.MakeWritable()
+ su.SetUint32(target.Arch, int64(off), o)
+ default:
+ ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+ su.SetRelocType(rIdx, objabi.R_ARM64_PCREL)
+ return true
+ }
+ ld.AddGotSym(target, ldr, syms, targ, 0)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_GOT)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ // Reread the reloc to incorporate any changes in type above.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_CALL,
+ objabi.R_PCREL,
+ objabi.R_CALLARM64:
+ if targType != sym.SDYNIMPORT {
+ // nothing to do, the relocation will be laid out in reloc
+ return true
+ }
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ // Internal linking.
+ if r.Add() != 0 {
+ ldr.Errorf(s, "PLT call with non-zero addend (%v)", r.Add())
+ }
+ // Build a PLT entry and change the relocation target to that entry.
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ return true
+
+ case objabi.R_ADDR:
+ if ldr.SymType(s) == sym.STEXT && target.IsElf() {
+ // The code is asking for the address of an external
+ // function. We provide it with the address of the
+ // correspondent GOT symbol.
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_AARCH64_GLOB_DAT))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ // Process dynamic relocations for the data sections.
+ if target.IsPIE() && target.IsInternal() {
+ // When internally linking, generate dynamic relocations
+ // for all typical R_ADDR relocations. The exception
+ // are those R_ADDR that are created as part of generating
+ // the dynamic relocations and must be resolved statically.
+ //
+ // There are three phases relevant to understanding this:
+ //
+ // dodata() // we are here
+ // address() // symbol address assignment
+ // reloc() // resolution of static R_ADDR relocs
+ //
+ // At this point symbol addresses have not been
+ // assigned yet (as the final size of the .rela section
+ // will affect the addresses), and so we cannot write
+ // the Elf64_Rela.r_offset now. Instead we delay it
+ // until after the 'address' phase of the linker is
+ // complete. We do this via Addaddrplus, which creates
+ // a new R_ADDR relocation which will be resolved in
+ // the 'reloc' phase.
+ //
+ // These synthetic static R_ADDR relocs must be skipped
+ // now, or else we will be caught in an infinite loop
+ // of generating synthetic relocs for our synthetic
+ // relocs.
+ //
+ // Furthermore, the rela sections contain dynamic
+ // relocations with R_ADDR relocations on
+ // Elf64_Rela.r_offset. This field should contain the
+ // symbol offset as determined by reloc(), not the
+ // final dynamically linked address as a dynamic
+ // relocation would provide.
+ switch ldr.SymName(s) {
+ case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic":
+ return false
+ }
+ } else {
+ // Either internally linking a static executable,
+ // in which case we can resolve these relocations
+ // statically in the 'reloc' phase, or externally
+ // linking, in which case the relocation will be
+ // prepared in the 'reloc' phase and passed to the
+ // external linker in the 'asmb' phase.
+ if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA {
+ break
+ }
+ }
+
+ if target.IsElf() {
+ // Generate R_AARCH64_RELATIVE relocations for best
+ // efficiency in the dynamic linker.
+ //
+ // As noted above, symbol addresses have not been
+ // assigned yet, so we can't generate the final reloc
+ // entry yet. We ultimately want:
+ //
+ // r_offset = s + r.Off
+ // r_info = R_AARCH64_RELATIVE
+ // r_addend = targ + r.Add
+ //
+ // The dynamic linker will set *offset = base address +
+ // addend.
+ //
+ // AddAddrPlus is used for r_offset and r_addend to
+ // generate new R_ADDR relocations that will update
+ // these fields in the 'reloc' phase.
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ if r.Siz() == 8 {
+ rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_AARCH64_RELATIVE)))
+ } else {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ rela.AddAddrPlus(target.Arch, targ, int64(r.Add()))
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+
+ if target.IsDarwin() {
+ // 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.
+ ld.MachoAddRebase(s, int64(r.Off()))
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+ }
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch siz {
+ case 4:
+ out.Write64(uint64(elf.R_AARCH64_ABS32) | uint64(elfsym)<<32)
+ case 8:
+ out.Write64(uint64(elf.R_AARCH64_ABS64) | uint64(elfsym)<<32)
+ default:
+ return false
+ }
+ case objabi.R_ADDRARM64:
+ // two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_ADD_ABS_LO12_NC
+ out.Write64(uint64(elf.R_AARCH64_ADR_PREL_PG_HI21) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_AARCH64_ADD_ABS_LO12_NC) | uint64(elfsym)<<32)
+ case objabi.R_ARM64_TLS_LE:
+ out.Write64(uint64(elf.R_AARCH64_TLSLE_MOVW_TPREL_G0) | uint64(elfsym)<<32)
+ case objabi.R_ARM64_TLS_IE:
+ out.Write64(uint64(elf.R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) | uint64(elfsym)<<32)
+ case objabi.R_ARM64_GOTPCREL:
+ out.Write64(uint64(elf.R_AARCH64_ADR_GOT_PAGE) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_AARCH64_LD64_GOT_LO12_NC) | uint64(elfsym)<<32)
+ case objabi.R_CALLARM64:
+ if siz != 4 {
+ return false
+ }
+ out.Write64(uint64(elf.R_AARCH64_CALL26) | uint64(elfsym)<<32)
+
+ }
+ out.Write64(uint64(r.Xadd))
+
+ return true
+}
+
+// sign-extends from 24-bit.
+func signext24(x int64) int64 { return x << 40 >> 40 }
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ var v uint32
+
+ rs := r.Xsym
+ rt := r.Type
+ siz := r.Size
+ xadd := r.Xadd
+
+ if xadd != signext24(xadd) {
+ // If the relocation target would overflow the addend, then target
+ // a linker-manufactured label symbol with a smaller addend instead.
+ label := ldr.Lookup(machoLabelName(ldr, rs, xadd), ldr.SymVersion(rs))
+ if label != 0 {
+ xadd = ldr.SymValue(rs) + xadd - ldr.SymValue(label)
+ rs = label
+ }
+ if xadd != signext24(xadd) {
+ ldr.Errorf(s, "internal error: relocation addend overflow: %s+0x%x", ldr.SymName(rs), xadd)
+ }
+ }
+
+ if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_GOTPCREL {
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ v = uint32(ldr.SymDynid(rs))
+ v |= 1 << 27 // external relocation
+ } else {
+ v = uint32(ldr.SymSect(rs).Extnum)
+ if v == 0 {
+ ldr.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymSect(rs).Name, ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+ }
+
+ switch rt {
+ default:
+ return false
+ case objabi.R_ADDR:
+ v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28
+ case objabi.R_CALLARM64:
+ if xadd != 0 {
+ ldr.Errorf(s, "ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", ldr.SymName(rs), xadd)
+ }
+
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_BRANCH26 << 28
+ case objabi.R_ADDRARM64:
+ siz = 4
+ // Two relocation entries: MACHO_ARM64_RELOC_PAGEOFF12 MACHO_ARM64_RELOC_PAGE21
+ // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff + 4))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
+ }
+ out.Write32(uint32(sectoff + 4))
+ out.Write32(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25))
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
+ }
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
+ case objabi.R_ARM64_GOTPCREL:
+ siz = 4
+ // Two relocation entries: MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 MACHO_ARM64_RELOC_GOT_LOAD_PAGE21
+ // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff + 4))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
+ }
+ out.Write32(uint32(sectoff + 4))
+ out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25))
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
+ }
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
+ }
+
+ switch siz {
+ default:
+ return false
+ case 1:
+ v |= 0 << 25
+ case 2:
+ v |= 1 << 25
+ case 4:
+ v |= 2 << 25
+ case 8:
+ v |= 3 << 25
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(v)
+ return true
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (int64, int, bool) {
+ const noExtReloc = 0
+ const isOk = true
+
+ rs := ldr.ResolveABIAlias(r.Sym())
+
+ if target.IsExternal() {
+ nExtReloc := 0
+ switch rt := r.Type(); rt {
+ default:
+ case objabi.R_ARM64_GOTPCREL,
+ objabi.R_ADDRARM64:
+
+ // set up addend for eventual relocation via outer symbol.
+ rs, off := ld.FoldSubSymbolOffset(ldr, rs)
+ xadd := r.Add() + off
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil {
+ ldr.Errorf(s, "missing section for %s", ldr.SymName(rs))
+ }
+
+ nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
+ if target.IsDarwin() && xadd != 0 {
+ nExtReloc = 4 // need another two relocations for non-zero addend
+ }
+
+ // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation
+ // will make the linking fail because it thinks the code is not PIC even though
+ // the BR26 relocation should be fully resolved at link time.
+ // That is the reason why the next if block is disabled. When the bug in ld64
+ // is fixed, we can enable this block and also enable duff's device in cmd/7g.
+ if false && target.IsDarwin() {
+ var o0, o1 uint32
+
+ if target.IsBigEndian() {
+ o0 = uint32(val >> 32)
+ o1 = uint32(val)
+ } else {
+ o0 = uint32(val)
+ o1 = uint32(val >> 32)
+ }
+ // Mach-O wants the addend to be encoded in the instruction
+ // Note that although Mach-O supports ARM64_RELOC_ADDEND, it
+ // can only encode 24-bit of signed addend, but the instructions
+ // supports 33-bit of signed addend, so we always encode the
+ // addend in place.
+ o0 |= (uint32((xadd>>12)&3) << 29) | (uint32((xadd>>12>>2)&0x7ffff) << 5)
+ o1 |= uint32(xadd&0xfff) << 10
+
+ // when laid out, the instruction order must always be o1, o2.
+ if target.IsBigEndian() {
+ val = int64(o0)<<32 | int64(o1)
+ } else {
+ val = int64(o1)<<32 | int64(o0)
+ }
+ }
+
+ return val, nExtReloc, isOk
+ case objabi.R_CALLARM64,
+ objabi.R_ARM64_TLS_LE,
+ objabi.R_ARM64_TLS_IE:
+ nExtReloc = 1
+ if rt == objabi.R_ARM64_TLS_IE {
+ nExtReloc = 2 // need two ELF relocations. see elfreloc1
+ }
+ return val, nExtReloc, isOk
+ }
+ }
+
+ switch r.Type() {
+ case objabi.R_ADDRARM64:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t >= 1<<32 || t < -1<<32 {
+ ldr.Errorf(s, "program too large, address relocation distance = %d", t)
+ }
+
+ var o0, o1 uint32
+
+ if target.IsBigEndian() {
+ o0 = uint32(val >> 32)
+ o1 = uint32(val)
+ } else {
+ o0 = uint32(val)
+ o1 = uint32(val >> 32)
+ }
+
+ o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5)
+ o1 |= uint32(t&0xfff) << 10
+
+ // when laid out, the instruction order must always be o1, o2.
+ if target.IsBigEndian() {
+ return int64(o0)<<32 | int64(o1), noExtReloc, true
+ }
+ return int64(o1)<<32 | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_TLS_LE:
+ if target.IsDarwin() {
+ ldr.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
+ }
+ // The TCB is two pointers. This is not documented anywhere, but is
+ // de facto part of the ABI.
+ v := ldr.SymValue(rs) + int64(2*target.Arch.PtrSize)
+ if v < 0 || v >= 32678 {
+ ldr.Errorf(s, "TLS offset out of range %d", v)
+ }
+ return val | (v << 5), noExtReloc, true
+
+ case objabi.R_ARM64_TLS_IE:
+ if target.IsPIE() && target.IsElf() {
+ // We are linking the final executable, so we
+ // can optimize any TLS IE relocation to LE.
+
+ if !target.IsLinux() {
+ ldr.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
+ }
+
+ // The TCB is two pointers. This is not documented anywhere, but is
+ // de facto part of the ABI.
+ v := ldr.SymAddr(rs) + int64(2*target.Arch.PtrSize) + r.Add()
+ if v < 0 || v >= 32678 {
+ ldr.Errorf(s, "TLS offset out of range %d", v)
+ }
+
+ var o0, o1 uint32
+ if target.IsBigEndian() {
+ o0 = uint32(val >> 32)
+ o1 = uint32(val)
+ } else {
+ o0 = uint32(val)
+ o1 = uint32(val >> 32)
+ }
+
+ // R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21
+ // turn ADRP to MOVZ
+ o0 = 0xd2a00000 | uint32(o0&0x1f) | (uint32((v>>16)&0xffff) << 5)
+ // R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC
+ // turn LD64 to MOVK
+ if v&3 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC", v)
+ }
+ o1 = 0xf2800000 | uint32(o1&0x1f) | (uint32(v&0xffff) << 5)
+
+ // when laid out, the instruction order must always be o0, o1.
+ if target.IsBigEndian() {
+ return int64(o0)<<32 | int64(o1), noExtReloc, isOk
+ }
+ return int64(o1)<<32 | int64(o0), noExtReloc, isOk
+ } else {
+ log.Fatalf("cannot handle R_ARM64_TLS_IE (sym %s) when linking internally", ldr.SymName(s))
+ }
+
+ case objabi.R_CALLARM64:
+ var t int64
+ if ldr.SymType(rs) == sym.SDYNIMPORT {
+ t = (ldr.SymAddr(syms.PLT) + r.Add()) - (ldr.SymValue(s) + int64(r.Off()))
+ } else {
+ t = (ldr.SymAddr(rs) + r.Add()) - (ldr.SymValue(s) + int64(r.Off()))
+ }
+ if t >= 1<<27 || t < -1<<27 {
+ ldr.Errorf(s, "program too large, call relocation distance = %d", t)
+ }
+ return val | ((t >> 2) & 0x03ffffff), noExtReloc, true
+
+ case objabi.R_ARM64_GOT:
+ if (val>>24)&0x9f == 0x90 {
+ // R_AARCH64_ADR_GOT_PAGE
+ // patch instruction: adrp
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t >= 1<<32 || t < -1<<32 {
+ ldr.Errorf(s, "program too large, address relocation distance = %d", t)
+ }
+ var o0 uint32
+ o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5)
+ return val | int64(o0), noExtReloc, isOk
+ } else if val>>24 == 0xf9 {
+ // R_AARCH64_LD64_GOT_LO12_NC
+ // patch instruction: ldr
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&7 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LD64_GOT_LO12_NC", t)
+ }
+ var o1 uint32
+ o1 |= uint32(t&0xfff) << (10 - 3)
+ return val | int64(uint64(o1)), noExtReloc, isOk
+ } else {
+ ldr.Errorf(s, "unsupported instruction for %x R_GOTARM64", val)
+ }
+
+ case objabi.R_ARM64_PCREL:
+ if (val>>24)&0x9f == 0x90 {
+ // R_AARCH64_ADR_PREL_PG_HI21
+ // patch instruction: adrp
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t >= 1<<32 || t < -1<<32 {
+ ldr.Errorf(s, "program too large, address relocation distance = %d", t)
+ }
+ o0 := (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5)
+ return val | int64(o0), noExtReloc, isOk
+ } else if (val>>24)&0x9f == 0x91 {
+ // ELF R_AARCH64_ADD_ABS_LO12_NC or Mach-O ARM64_RELOC_PAGEOFF12
+ // patch instruction: add
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ o1 := uint32(t&0xfff) << 10
+ return val | int64(o1), noExtReloc, isOk
+ } else if (val>>24)&0x3b == 0x39 {
+ // Mach-O ARM64_RELOC_PAGEOFF12
+ // patch ldr/str(b/h/w/d/q) (integer or vector) instructions, which have different scaling factors.
+ // Mach-O uses same relocation type for them.
+ shift := uint32(val) >> 30
+ if shift == 0 && (val>>20)&0x048 == 0x048 { // 128-bit vector load
+ shift = 4
+ }
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&(1<<shift-1) != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: ARM64_RELOC_PAGEOFF12", t)
+ }
+ o1 := (uint32(t&0xfff) >> shift) << 10
+ return val | int64(o1), noExtReloc, isOk
+ } else {
+ ldr.Errorf(s, "unsupported instruction for %x R_ARM64_PCREL", val)
+ }
+
+ case objabi.R_ARM64_LDST8:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ o0 := uint32(t&0xfff) << 10
+ return val | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_LDST16:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&1 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST16_ABS_LO12_NC", t)
+ }
+ o0 := (uint32(t&0xfff) >> 1) << 10
+ return val | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_LDST32:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&3 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST32_ABS_LO12_NC", t)
+ }
+ o0 := (uint32(t&0xfff) >> 2) << 10
+ return val | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_LDST64:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&7 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST64_ABS_LO12_NC", t)
+ }
+ o0 := (uint32(t&0xfff) >> 3) << 10
+ return val | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_LDST128:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&15 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST128_ABS_LO12_NC", t)
+ }
+ o0 := (uint32(t&0xfff) >> 4) << 10
+ return val | int64(o0), noExtReloc, true
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 {
+ log.Fatalf("unexpected relocation variant")
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch rt := r.Type(); rt {
+ case objabi.R_ARM64_GOTPCREL,
+ objabi.R_ADDRARM64:
+ rr := ld.ExtrelocViaOuterSym(ldr, r, s)
+
+ // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation
+ // will make the linking fail because it thinks the code is not PIC even though
+ // the BR26 relocation should be fully resolved at link time.
+ // That is the reason why the next if block is disabled. When the bug in ld64
+ // is fixed, we can enable this block and also enable duff's device in cmd/7g.
+ if false && target.IsDarwin() {
+ // Mach-O wants the addend to be encoded in the instruction
+ // Note that although Mach-O supports ARM64_RELOC_ADDEND, it
+ // can only encode 24-bit of signed addend, but the instructions
+ // supports 33-bit of signed addend, so we always encode the
+ // addend in place.
+ rr.Xadd = 0
+ }
+ return rr, true
+ case objabi.R_CALLARM64,
+ objabi.R_ARM64_TLS_LE,
+ objabi.R_ARM64_TLS_IE:
+ return ld.ExtrelocSimple(ldr, r), true
+ }
+ return loader.ExtReloc{}, false
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // stp x16, x30, [sp, #-16]!
+ // identifying information
+ plt.AddUint32(ctxt.Arch, 0xa9bf7bf0)
+
+ // the following two instructions (adrp + ldr) load *got[2] into x17
+ // adrp x16, &got[0]
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x90000010)
+
+ // <imm> is the offset value of &got[2] to &got[0], the same below
+ // ldr x17, [x16, <imm>]
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0xf9400211)
+
+ // add x16, x16, <imm>
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_PCREL, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x91000210)
+
+ // br x17
+ plt.AddUint32(ctxt.Arch, 0xd61f0220)
+
+ // 3 nop for place holder
+ plt.AddUint32(ctxt.Arch, 0xd503201f)
+ plt.AddUint32(ctxt.Arch, 0xd503201f)
+ plt.AddUint32(ctxt.Arch, 0xd503201f)
+
+ // check gotplt.size == 0
+ if gotplt.Size() != 0 {
+ ctxt.Errorf(gotplt.Sym(), "got.plt is not empty at the very beginning")
+ }
+ gotplt.AddAddrPlus(ctxt.Arch, dynamic, 0)
+
+ gotplt.AddUint64(ctxt.Arch, 0)
+ gotplt.AddUint64(ctxt.Arch, 0)
+ }
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ gotplt := ldr.MakeSymbolUpdater(syms.GOTPLT)
+ rela := ldr.MakeSymbolUpdater(syms.RelaPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // adrp x16, &got.plt[0]
+ plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size())
+ plt.SetUint32(target.Arch, plt.Size()-4, 0x90000010)
+ relocs := plt.Relocs()
+ plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_GOT)
+
+ // <offset> is the offset value of &got.plt[n] to &got.plt[0]
+ // ldr x17, [x16, <offset>]
+ plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size())
+ plt.SetUint32(target.Arch, plt.Size()-4, 0xf9400211)
+ relocs = plt.Relocs()
+ plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_GOT)
+
+ // add x16, x16, <offset>
+ plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size())
+ plt.SetUint32(target.Arch, plt.Size()-4, 0x91000210)
+ relocs = plt.Relocs()
+ plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_PCREL)
+
+ // br x17
+ plt.AddUint32(target.Arch, 0xd61f0220)
+
+ // add to got.plt: pointer to plt[0]
+ gotplt.AddAddrPlus(target.Arch, plt.Sym(), 0)
+
+ // rela
+ rela.AddAddrPlus(target.Arch, gotplt.Sym(), gotplt.Size()-8)
+ sDynid := ldr.SymDynid(s)
+
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_AARCH64_JUMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
+
+ ldr.SetPlt(s, int32(plt.Size()-16))
+ } else if target.IsDarwin() {
+ ld.AddGotSym(target, ldr, syms, s, 0)
+
+ sDynid := ldr.SymDynid(s)
+ lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
+ lep.AddUint32(target.Arch, uint32(sDynid))
+
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ ldr.SetPlt(s, int32(plt.Size()))
+
+ // adrp x16, GOT
+ plt.AddUint32(target.Arch, 0x90000010)
+ r, _ := plt.AddRel(objabi.R_ARM64_GOT)
+ r.SetOff(int32(plt.Size() - 4))
+ r.SetSiz(4)
+ r.SetSym(syms.GOT)
+ r.SetAdd(int64(ldr.SymGot(s)))
+
+ // ldr x17, [x16, <offset>]
+ plt.AddUint32(target.Arch, 0xf9400211)
+ r, _ = plt.AddRel(objabi.R_ARM64_GOT)
+ r.SetOff(int32(plt.Size() - 4))
+ r.SetSiz(4)
+ r.SetSym(syms.GOT)
+ r.SetAdd(int64(ldr.SymGot(s)))
+
+ // br x17
+ plt.AddUint32(target.Arch, 0xd61f0220)
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
+
+const machoRelocLimit = 1 << 23
+
+func gensymlate(ctxt *ld.Link, ldr *loader.Loader) {
+ // When external linking on darwin, Mach-O relocation has only signed 24-bit
+ // addend. For large symbols, we generate "label" symbols in the middle, so
+ // that relocations can target them with smaller addends.
+ if !ctxt.IsDarwin() || !ctxt.IsExternal() {
+ return
+ }
+
+ big := false
+ for _, seg := range ld.Segments {
+ if seg.Length >= machoRelocLimit {
+ big = true
+ break
+ }
+ }
+ if !big {
+ return // skip work if nothing big
+ }
+
+ // addLabelSyms adds "label" symbols at s+machoRelocLimit, s+2*machoRelocLimit, etc.
+ addLabelSyms := func(s loader.Sym, sz int64) {
+ v := ldr.SymValue(s)
+ for off := int64(machoRelocLimit); off < sz; off += machoRelocLimit {
+ p := ldr.LookupOrCreateSym(machoLabelName(ldr, s, off), ldr.SymVersion(s))
+ ldr.SetAttrReachable(p, true)
+ ldr.SetSymValue(p, v+off)
+ ldr.SetSymSect(p, ldr.SymSect(s))
+ ld.AddMachoSym(ldr, p)
+ //fmt.Printf("gensymlate %s %x\n", ldr.SymName(p), ldr.SymValue(p))
+ }
+ }
+
+ for s, n := loader.Sym(1), loader.Sym(ldr.NSym()); s < n; s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymType(s) == sym.STEXT {
+ continue // we don't target the middle of a function
+ }
+ sz := ldr.SymSize(s)
+ if sz <= machoRelocLimit {
+ continue
+ }
+ addLabelSyms(s, sz)
+ }
+
+ // Also for carrier symbols (for which SymSize is 0)
+ for _, ss := range ld.CarrierSymByType {
+ if ss.Sym != 0 && ss.Size > machoRelocLimit {
+ addLabelSyms(ss.Sym, ss.Size)
+ }
+ }
+}
+
+// machoLabelName returns the name of the "label" symbol used for a
+// relocation targeting s+off. The label symbols is used on darwin
+// when external linking, so that the addend fits in a Mach-O relocation.
+func machoLabelName(ldr *loader.Loader, s loader.Sym, off int64) string {
+ return fmt.Sprintf("%s.%d", ldr.SymExtname(s), off/machoRelocLimit)
+}
diff --git a/src/cmd/link/internal/arm64/l.go b/src/cmd/link/internal/arm64/l.go
new file mode 100644
index 0000000..4aa2708
--- /dev/null
+++ b/src/cmd/link/internal/arm64/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 arm64
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// 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-2008 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-2008 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.
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 31
+ dwarfRegLR = 30
+)
diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go
new file mode 100644
index 0000000..bd13295
--- /dev/null
+++ b/src/cmd/link/internal/arm64/obj.go
@@ -0,0 +1,112 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 arm64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchARM64
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ GenSymsLate: gensymlate,
+ Machoreloc1: machoreloc1,
+ MachorelocSize: 8,
+
+ Androiddynld: "/system/bin/linker64",
+ Linuxdynld: "/lib/ld-linux-aarch64.so.1",
+
+ Freebsddynld: "/usr/libexec/ld-elf.so.1",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "/libexec/ld.elf_so",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hlinux, /* arm64 elf */
+ objabi.Hfreebsd,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd:
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+
+ case objabi.Hdarwin: /* apple MACH */
+ ld.HEADR = ld.INITIAL_MACHO_HEADR
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 1<<32 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 16384 // 16K page alignment
+ }
+ }
+}
diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go
new file mode 100644
index 0000000..6c163c8
--- /dev/null
+++ b/src/cmd/link/internal/benchmark/bench.go
@@ -0,0 +1,195 @@
+// 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 benchmark provides a Metrics object that enables memory and CPU
+// profiling for the linker. The Metrics objects can be used to mark stages
+// of the code, and name the measurements during that stage. There is also
+// optional GCs that can be performed at the end of each stage, so you
+// can get an accurate measurement of how each stage changes live memory.
+package benchmark
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "time"
+ "unicode"
+)
+
+type Flags int
+
+const (
+ GC = 1 << iota
+ NoGC Flags = 0
+)
+
+type Metrics struct {
+ gc Flags
+ marks []*mark
+ curMark *mark
+ filebase string
+ pprofFile *os.File
+}
+
+type mark struct {
+ name string
+ startM, endM, gcM runtime.MemStats
+ startT, endT time.Time
+}
+
+// New creates a new Metrics object.
+//
+// Typical usage should look like:
+//
+// func main() {
+// filename := "" // Set to enable per-phase pprof file output.
+// bench := benchmark.New(benchmark.GC, filename)
+// defer bench.Report(os.Stdout)
+// // etc
+// bench.Start("foo")
+// foo()
+// bench.Start("bar")
+// bar()
+// }
+//
+// Note that a nil Metrics object won't cause any errors, so one could write
+// code like:
+//
+// func main() {
+// enableBenchmarking := flag.Bool("enable", true, "enables benchmarking")
+// flag.Parse()
+// var bench *benchmark.Metrics
+// if *enableBenchmarking {
+// bench = benchmark.New(benchmark.GC)
+// }
+// bench.Start("foo")
+// // etc.
+// }
+func New(gc Flags, filebase string) *Metrics {
+ if gc == GC {
+ runtime.GC()
+ }
+ return &Metrics{gc: gc, filebase: filebase}
+}
+
+// Report reports the metrics.
+// Closes the currently Start(ed) range, and writes the report to the given io.Writer.
+func (m *Metrics) Report(w io.Writer) {
+ if m == nil {
+ return
+ }
+
+ m.closeMark()
+
+ gcString := ""
+ if m.gc == GC {
+ gcString = "_GC"
+ }
+
+ var totTime time.Duration
+ for _, curMark := range m.marks {
+ dur := curMark.endT.Sub(curMark.startT)
+ totTime += dur
+ fmt.Fprintf(w, "%s 1 %d ns/op", makeBenchString(curMark.name+gcString), dur.Nanoseconds())
+ fmt.Fprintf(w, "\t%d B/op", curMark.endM.TotalAlloc-curMark.startM.TotalAlloc)
+ fmt.Fprintf(w, "\t%d allocs/op", curMark.endM.Mallocs-curMark.startM.Mallocs)
+ if m.gc == GC {
+ fmt.Fprintf(w, "\t%d live-B", curMark.gcM.HeapAlloc)
+ } else {
+ fmt.Fprintf(w, "\t%d heap-B", curMark.endM.HeapAlloc)
+ }
+ fmt.Fprintf(w, "\n")
+ }
+ fmt.Fprintf(w, "%s 1 %d ns/op\n", makeBenchString("total time"+gcString), totTime.Nanoseconds())
+}
+
+// Starts marks the beginning of a new measurement phase.
+// Once a metric is started, it continues until either a Report is issued, or another Start is called.
+func (m *Metrics) Start(name string) {
+ if m == nil {
+ return
+ }
+ m.closeMark()
+ m.curMark = &mark{name: name}
+ // Unlikely we need to a GC here, as one was likely just done in closeMark.
+ if m.shouldPProf() {
+ f, err := os.Create(makePProfFilename(m.filebase, name, "cpuprof"))
+ if err != nil {
+ panic(err)
+ }
+ m.pprofFile = f
+ if err = pprof.StartCPUProfile(m.pprofFile); err != nil {
+ panic(err)
+ }
+ }
+ runtime.ReadMemStats(&m.curMark.startM)
+ m.curMark.startT = time.Now()
+}
+
+func (m *Metrics) closeMark() {
+ if m == nil || m.curMark == nil {
+ return
+ }
+ m.curMark.endT = time.Now()
+ if m.shouldPProf() {
+ pprof.StopCPUProfile()
+ m.pprofFile.Close()
+ m.pprofFile = nil
+ }
+ runtime.ReadMemStats(&m.curMark.endM)
+ if m.gc == GC {
+ runtime.GC()
+ runtime.ReadMemStats(&m.curMark.gcM)
+ if m.shouldPProf() {
+ // Collect a profile of the live heap. Do a
+ // second GC to force sweep completion so we
+ // get a complete snapshot of the live heap at
+ // the end of this phase.
+ runtime.GC()
+ f, err := os.Create(makePProfFilename(m.filebase, m.curMark.name, "memprof"))
+ if err != nil {
+ panic(err)
+ }
+ err = pprof.WriteHeapProfile(f)
+ if err != nil {
+ panic(err)
+ }
+ err = f.Close()
+ if err != nil {
+ panic(err)
+ }
+ }
+ }
+ m.marks = append(m.marks, m.curMark)
+ m.curMark = nil
+}
+
+// shouldPProf returns true if we should be doing pprof runs.
+func (m *Metrics) shouldPProf() bool {
+ return m != nil && len(m.filebase) > 0
+}
+
+// makeBenchString makes a benchmark string consumable by Go's benchmarking tools.
+func makeBenchString(name string) string {
+ needCap := true
+ ret := []rune("Benchmark")
+ for _, r := range name {
+ if unicode.IsSpace(r) {
+ needCap = true
+ continue
+ }
+ if needCap {
+ r = unicode.ToUpper(r)
+ needCap = false
+ }
+ ret = append(ret, r)
+ }
+ return string(ret)
+}
+
+func makePProfFilename(filebase, name, typ string) string {
+ return fmt.Sprintf("%s_%s.%s", filebase, makeBenchString(name), typ)
+}
diff --git a/src/cmd/link/internal/benchmark/bench_test.go b/src/cmd/link/internal/benchmark/bench_test.go
new file mode 100644
index 0000000..419dc55
--- /dev/null
+++ b/src/cmd/link/internal/benchmark/bench_test.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 benchmark
+
+import (
+ "testing"
+)
+
+func TestMakeBenchString(t *testing.T) {
+ tests := []struct {
+ have, want string
+ }{
+ {"foo", "BenchmarkFoo"},
+ {" foo ", "BenchmarkFoo"},
+ {"foo bar", "BenchmarkFooBar"},
+ }
+ for i, test := range tests {
+ if v := makeBenchString(test.have); test.want != v {
+ t.Errorf("test[%d] makeBenchString(%q) == %q, want %q", i, test.have, v, test.want)
+ }
+ }
+}
+
+func TestPProfFlag(t *testing.T) {
+ tests := []struct {
+ name string
+ want bool
+ }{
+ {"", false},
+ {"foo", true},
+ }
+ for i, test := range tests {
+ b := New(GC, test.name)
+ if v := b.shouldPProf(); test.want != v {
+ t.Errorf("test[%d] shouldPProf() == %v, want %v", i, v, test.want)
+ }
+ }
+}
+
+func TestPProfNames(t *testing.T) {
+ want := "foo_BenchmarkTest.cpuprof"
+ if v := makePProfFilename("foo", "test", "cpuprof"); v != want {
+ t.Errorf("makePProfFilename() == %q, want %q", v, want)
+ }
+}
+
+// Ensure that public APIs work with a nil Metrics object.
+func TestNilBenchmarkObject(t *testing.T) {
+ var b *Metrics
+ b.Start("TEST")
+ b.Report(nil)
+}
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
new file mode 100644
index 0000000..e4fd591
--- /dev/null
+++ b/src/cmd/link/internal/ld/ar.go
@@ -0,0 +1,191 @@
+// 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/internal/objabi"
+ "cmd/link/internal/sym"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "os"
+)
+
+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
+}
+
+// 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) {
+ 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 := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
+ for _, symIdx := range undefs {
+ name := ctxt.loader.SymName(symIdx)
+ if off := armap[name]; off != 0 && !loaded[off] {
+ load = append(load, off)
+ loaded[off] = true
+ }
+ }
+
+ 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)
+
+ libgcc := sym.Library{Pkg: "libgcc"}
+ h := ldobj(ctxt, f, &libgcc, l, pname, name)
+ f.MustSeek(h.off, 0)
+ h.ld(ctxt, f, h.pkg, h.length, h.pn)
+ }
+
+ 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 objabi.GOOS == "darwin" || objabi.GOOS == "ios" || (objabi.GOOS == "windows" && objabi.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..fda0439
--- /dev/null
+++ b/src/cmd/link/internal/ld/asmb.go
@@ -0,0 +1,214 @@
+// 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
+ sect := Segtext.Sections[0]
+ offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff
+ f := func(ctxt *Link, out *OutBuf, start, length int64) {
+ pad := thearch.CodePad
+ if pad == nil {
+ pad = zeros[:]
+ }
+ CodeblkPad(ctxt, out, start, length, pad)
+ }
+
+ if !thearch.WriteTextBlocks {
+ writeParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length)
+ for _, sect := range Segtext.Sections[1:] {
+ offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff
+ writeParallel(&wg, datblk, ctxt, offset, sect.Vaddr, sect.Length)
+ }
+ } else {
+ // TODO why can't we handle all sections this way?
+ for _, sect := range Segtext.Sections {
+ offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff
+ // Handle additional 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
+ ctxt.Out.Mmap(uint64(filesz))
+}
+
+// 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..d1e0623
--- /dev/null
+++ b/src/cmd/link/internal/ld/config.go
@@ -0,0 +1,282 @@
+// 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/internal/sys"
+ "fmt"
+ "log"
+)
+
+// 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, objabi.GOOS, objabi.GOARCH)
+ }
+ switch s {
+ default:
+ return fmt.Errorf("invalid buildmode: %q", s)
+ case "exe":
+ switch objabi.GOOS + "/" + objabi.GOARCH {
+ case "darwin/arm64", "windows/arm": // On these platforms, everything is PIE
+ *mode = BuildModePIE
+ default:
+ *mode = BuildModeExe
+ }
+ case "pie":
+ switch objabi.GOOS {
+ case "aix", "android", "linux", "windows", "darwin", "ios":
+ case "freebsd":
+ switch objabi.GOARCH {
+ case "amd64":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModePIE
+ case "c-archive":
+ switch objabi.GOOS {
+ case "aix", "darwin", "ios", "linux":
+ case "freebsd":
+ switch objabi.GOARCH {
+ case "amd64":
+ default:
+ return badmode()
+ }
+ case "windows":
+ switch objabi.GOARCH {
+ case "amd64", "386", "arm":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModeCArchive
+ case "c-shared":
+ switch objabi.GOARCH {
+ case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
+ default:
+ return badmode()
+ }
+ *mode = BuildModeCShared
+ case "shared":
+ switch objabi.GOOS {
+ case "linux":
+ switch objabi.GOARCH {
+ case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModeShared
+ case "plugin":
+ switch objabi.GOOS {
+ case "linux":
+ switch objabi.GOARCH {
+ case "386", "amd64", "arm", "arm64", "s390x", "ppc64le":
+ default:
+ return badmode()
+ }
+ case "darwin":
+ switch objabi.GOARCH {
+ case "amd64", "arm64":
+ default:
+ return badmode()
+ }
+ case "freebsd":
+ switch objabi.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 {
+ log.Printf("external linking is forced by: %s\n", reason)
+ }
+ }()
+ }
+
+ if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) {
+ return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH)
+ }
+
+ if *flagMsan {
+ return true, "msan"
+ }
+
+ // Internally linking cgo is incomplete on some architectures.
+ // https://golang.org/issue/14449
+ // https://golang.org/issue/21961
+ if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
+ return true, objabi.GOARCH + " does not support internal cgo"
+ }
+ if iscgo && objabi.GOOS == "android" {
+ return true, objabi.GOOS + " does not support internal cgo"
+ }
+
+ // When the race flag is set, the LLVM tsan relocatable file is linked
+ // into the final binary, which means external linking is required because
+ // internal linking does not support it.
+ if *flagRace && ctxt.Arch.InFamily(sys.PPC64) {
+ return true, "race on " + objabi.GOARCH
+ }
+
+ // 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 objabi.GOOS + "/" + objabi.GOARCH {
+ case "linux/amd64", "linux/arm64", "android/arm64":
+ case "windows/386", "windows/amd64", "windows/arm":
+ 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"
+ }
+
+ 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 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 objabi.Getgoextlinkenabled() {
+ case "0":
+ ctxt.LinkMode = LinkInternal
+ via = "via GO_EXTLINK_ENABLED "
+ case "1":
+ ctxt.LinkMode = LinkExternal
+ via = "via GO_EXTLINK_ENABLED "
+ default:
+ if extNeeded || (iscgo && externalobj) {
+ 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 objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
+ Exitf("external linking not supported for %s/ppc64", objabi.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..7028b85
--- /dev/null
+++ b/src/cmd/link/internal/ld/data.go
@@ -0,0 +1,2685 @@
+// 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/sym"
+ "compress/zlib"
+ "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/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 maxSizeTrampolinesPPC64(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++
+ }
+ }
+ // Trampolines in ppc64 are 4 instructions.
+ return n * 16
+}
+
+// detect too-far jumps in function s, and add trampolines if necessary
+// ARM, PPC64 & PPC64LE support trampoline insertion for internal and external linking
+// On PPC64 & 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)
+ if !r.Type().IsDirectCallOrJump() {
+ 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
+ }
+ rs = ldr.ResolveABIAlias(rs)
+ if ldr.SymValue(rs) == 0 && (ldr.SymType(rs) != sym.SDYNIMPORT && ldr.SymType(rs) != sym.SUNDEFEXT) {
+ if ldr.SymPkg(rs) == ldr.SymPkg(s) {
+ continue // symbols in the same package are laid out together
+ }
+ if isRuntimeDepPkg(ldr.SymPkg(s)) && isRuntimeDepPkg(ldr.SymPkg(rs)) {
+ continue // runtime packages are laid out together
+ }
+ }
+
+ thearch.Trampoline(ctxt, ldr, ri, rs, s)
+ }
+
+}
+
+// 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()
+ rs = ldr.ResolveABIAlias(rs)
+ rt := r.Type()
+ 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 && !ldr.AttrVisibilityHidden(rs)) || 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 {
+ 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 && rt != objabi.R_WEAKADDROFF && 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 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_WEAKADDROFF, objabi.R_METHODOFF:
+ if !ldr.AttrReachable(rs) {
+ if rt == objabi.R_METHODOFF {
+ // Set it to a sentinel value. The runtime knows this is not pointing to
+ // anything valid.
+ o = -1
+ break
+ }
+ continue
+ }
+ fallthrough
+ case objabi.R_ADDROFF:
+ // 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)
+ }
+ }
+
+ 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 := ldr.ResolveABIAlias(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 := ldr.ResolveABIAlias(r.Sym())
+ 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 := ldr.ResolveABIAlias(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 := ldr.ResolveABIAlias(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_WEAKADDROFF, 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 := ldr.ResolveABIAlias(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 := ldr.ResolveABIAlias(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,
+ }
+}
+
+func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) {
+ 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
+ }
+ rt := r.Type()
+ if !ctxt.loader.AttrReachable(targ) {
+ if rt == objabi.R_WEAKADDROFF {
+ continue
+ }
+ ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s",
+ ctxt.loader.SymName(targ))
+ }
+
+ tplt := ctxt.loader.SymPlt(targ)
+ tgot := ctxt.loader.SymGot(targ)
+ if tplt == -2 && tgot != -2 { // 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:
+ ctxt.Errorf(s, "unsupported arch %v", ctxt.Arch.Family)
+ return
+ 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))
+ }
+ }
+}
+
+// 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 {
+ windynrelocsym(ctxt, rel, s)
+ }
+
+ 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
+ }
+ 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
+ }
+
+ rSym := r.Sym()
+ 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 containter 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 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)
+
+ bld.SetSize(0)
+ bld.SetData(make([]byte, 0, arch.PtrSize*2))
+ bld.SetReadOnly(false)
+ bld.ResetRelocs()
+ bld.AddAddrPlus(arch, sbld.Sym(), 0)
+ bld.AddUint(arch, 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
+ }
+ // FIXME: figure out a way to avoid checking by name here.
+ sname := ldr.SymName(s)
+ if strings.HasPrefix(sname, "go.string.") || strings.HasPrefix(sname, "type..namedata.") {
+ // String data is just bytes.
+ // If we align it, we waste a lot of space to padding.
+ 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
+ }
+ }
+ 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.
+ // Otherwize, 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)
+ 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 (and .toc on ppc64)
+ if len(state.data[sym.SELFGOT]) > 0 {
+ sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06)
+ if ctxt.IsPPC64() {
+ for _, s := range state.data[sym.SELFGOT] {
+ // Resolve .TOC. symbol for this object file (ppc64)
+
+ toc := ldr.Lookup(".TOC.", int(ldr.SymVersion(s)))
+ if toc != 0 {
+ ldr.SetSymSect(toc, sect)
+ ldr.AddInteriorSym(s, toc)
+ ldr.SetSymValue(toc, 0x8000)
+ }
+ }
+ }
+ }
+
+ /* 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)
+
+ // Coverage instrumentation counters for libfuzzer.
+ if len(state.data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 {
+ state.allocateNamedSectionAndAssignSyms(&Segdata, "__libfuzzer_extra_counters", sym.SLIBFUZZER_EXTRA_COUNTER, sym.Sxxx, 06)
+ }
+
+ 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
+ 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
+
+ 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 has the proper order based on value.
+ // Sort the symbols as such.
+ for k, s := range syms {
+ sl[k].val = ldr.SymValue(s)
+ }
+ sort.Slice(sl, func(i, j int) bool { return sl[i].val < sl[j].val })
+ }
+
+ // 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
+ }
+
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate(".go.buildinfo", 0)
+ // On AIX, .go.buildinfo must be in the symbol table as
+ // it has relocations.
+ s.SetNotInSymbolTable(!ctxt.IsAIX())
+ 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
+ }
+ s.SetData(data)
+ s.SetSize(int64(len(data)))
+ r, _ := s.AddRel(objabi.R_ADDR)
+ r.SetOff(16)
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ r.SetSym(ldr.LookupOrCreateSym("runtime.buildVersion", 0))
+ r, _ = s.AddRel(objabi.R_ADDR)
+ r.SetOff(16 + int32(ctxt.Arch.PtrSize))
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ r.SetSym(ldr.LookupOrCreateSym("runtime.modinfo", 0))
+}
+
+// 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
+ }
+
+ va := uint64(Rnd(*FlagTextAddr, int64(Funcalign)))
+ n := 1
+ sect.Vaddr = va
+ ntramps := 0
+ for _, s := range ctxt.Textp {
+ sect, n, va = assignAddress(ctxt, sect, n, s, va, false)
+
+ 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)
+ }
+ }
+
+ 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))
+ }
+
+ // 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
+ }
+}
+
+// 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 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))
+ }
+
+ // On ppc64x a text section should not be larger than 2^26 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 GNU linker to modify the long calls
+ // appropriately. The limit allows for the space needed for tables inserted by the linker.
+
+ // If this function doesn't fit in the current text section, then create a new one.
+
+ // Only break at outermost syms.
+
+ // 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 = 0x1c00000
+ if *FlagDebugTextSize != 0 {
+ textSizelimit = uint64(*FlagDebugTextSize)
+ }
+
+ if ctxt.Arch.InFamily(sys.PPC64) && ldr.OuterSym(s) == 0 && ctxt.IsExternal() {
+ // Sanity check: make sure the limit is larger than any
+ // individual text symbol.
+ if funcsize > textSizelimit {
+ panic(fmt.Sprintf("error: ppc64 text size limit %d less than text symbol %s size of %d", textSizelimit, ldr.SymName(s), funcsize))
+ }
+
+ if va-sect.Vaddr+funcsize+maxSizeTrampolinesPPC64(ldr, s, isTramp) > textSizelimit {
+ // 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
+ 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)
+ 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))
+ }
+
+ va += funcsize
+
+ return sect, n, va
+}
+
+// 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
+ 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
+ if s.Name == ".data" {
+ data = s
+ }
+ if s.Name == ".noptrdata" {
+ noptr = s
+ }
+ if s.Name == ".bss" {
+ bss = s
+ }
+ if s.Name == ".noptrbss" {
+ noptrbss = 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.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.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))
+ }
+
+ 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
+ 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..7c46307
--- /dev/null
+++ b/src/cmd/link/internal/ld/data_test.go
@@ -0,0 +1,92 @@
+// 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"
+ "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 := objabi.GOARCH
+ defer func() {
+ objabi.GOARCH = origArch
+ }()
+
+ for i, test := range tests {
+ iself := len(test.rel) != 0
+ objabi.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..bfa8640
--- /dev/null
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -0,0 +1,444 @@
+// 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"
+ "unicode"
+)
+
+var _ = fmt.Print
+
+type deadcodePass struct {
+ ctxt *Link
+ ldr *loader.Loader
+ wq heap // work queue, using min-heap for beter locality
+
+ ifaceMethod map[methodsig]bool // methods declared in reached interfaces
+ 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)
+ if objabi.Fieldtrack_enabled != 0 {
+ 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)
+ if !d.ctxt.linkShared && d.ctxt.BuildMode != BuildModePlugin {
+ // runtime.buildVersion and runtime.modinfo are referenced in .go.buildinfo section
+ // (see function buildinfo in data.go). They should normally be reachable from the
+ // runtime. Just make it explicit, in case.
+ names = append(names, "runtime.buildVersion", "runtime.modinfo")
+ }
+ 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)
+ }
+ }
+ }
+ }
+
+ dynexpMap := d.ctxt.cgo_export_dynamic
+ if d.ctxt.LinkMode == LinkExternal {
+ dynexpMap = d.ctxt.cgo_export_static
+ }
+ for exp := range dynexpMap {
+ names = append(names, exp)
+ }
+
+ for _, name := range names {
+ // Mark symbol as a data/ABI0 symbol.
+ d.mark(d.ldr.Lookup(name, 0), 0)
+ // Also mark any Go functions (internal ABI).
+ d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal), 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 dynaamic 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)
+ t := r.Type()
+ switch t {
+ case objabi.R_WEAKADDROFF:
+ continue
+ 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
+ }
+ 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 objabi.Fieldtrack_enabled != 0 && 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", sym.SymVerABIInternal)
+ methByNameSym := ldr.Lookup("reflect.Value.MethodByName", sym.SymVerABIInternal)
+
+ 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.ifaceMethod[m.m] {
+ 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 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
+}
+
+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..b756091
--- /dev/null
+++ b/src/cmd/link/internal/ld/deadcode_test.go
@@ -0,0 +1,57 @@
+// 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"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+)
+
+func TestDeadcode(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestDeadcode")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ 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 := exec.Command(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)) {
+ t.Errorf("%s should be reachable. Output:\n%s", test.pos, out)
+ }
+ if test.neg != "" && bytes.Contains(out, []byte(test.neg)) {
+ 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..fc179fc
--- /dev/null
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -0,0 +1,293 @@
+// 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"
+ "log"
+)
+
+// Decoding the type.* symbols. This has to be in sync with
+// ../../runtime/type.go, or more specifically, with what
+// cmd/compile/internal/gc/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/gc/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 := int(uint16(data[1])<<8 | uint16(data[2]))
+ return string(data[3 : 3+namelen])
+}
+
+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 decodetypeStructFieldOffsAnon(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))
+}
+
+// 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..2ab9a55
--- /dev/null
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -0,0 +1,2315 @@
+// 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"
+ "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
+}
+
+func newdwctxt(linkctxt *Link, forTypeGen bool) dwctxt {
+ d := dwctxt{
+ linkctxt: linkctxt,
+ ldr: linkctxt.loader,
+ arch: linkctxt.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")
+ return d
+}
+
+// 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
+}
+
+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{}) *dwarf.DWAttr {
+ a := new(dwarf.DWAttr)
+ a.Link = die.Attr
+ die.Attr = a
+ a.Atr = attr
+ a.Cls = uint8(cls)
+ a.Value = value
+ a.Data = data
+ return a
+}
+
+// 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, version int) *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 have a non-empty
+ // name and be version zero.
+ if name == "" || version != 0 {
+ panic("nameless or version non-zero 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, version)
+ 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) int64 {
+ var result int64
+ switch size {
+ default:
+ d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size)
+ case d.arch.PtrSize, 4:
+ }
+ result = sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size)
+ return result
+}
+
+func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) *dwarf.DWAttr {
+ if ref == 0 {
+ return nil
+ }
+ 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)
+}
+
+// GDB doesn't like FORM_addr for AT_location, so emit a
+// location expression that evals to a const.
+func (d *dwctxt) newabslocexprattr(die *dwarf.DWDie, addr int64, symIdx loader.Sym) {
+ newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, dwSym(symIdx))
+}
+
+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, gotype loader.Sym, 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
+ }
+ 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, 0)
+
+ 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)
+ 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, 0)
+ 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, 0)
+ 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, 0)
+ 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, 0)
+ 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, 0)
+ 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, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, 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", 0)
+
+ // 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, 0)
+ 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, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, 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:], 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
+ }
+
+ if decodetypeFuncDotdotdot(d.arch, data) {
+ d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
+ }
+ 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:], 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s)))
+ }
+
+ case objabi.KindInterface:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, 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, 0)
+ 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, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, 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, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, 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, 0)
+ 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, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, 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, 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
+ offsetAnon := decodetypeStructFieldOffsAnon(d.ldr, d.arch, gotype, i)
+ newmemberoffsetattr(fld, int32(offsetAnon>>1))
+ if offsetAnon&1 != 0 { // is embedded field
+ 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, 0)
+
+ default:
+ d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0)
+ 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, 0)
+ 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), 0)
+ 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, 0)
+ 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", 0)
+ 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", 0)
+ 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", 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, dwhks)
+ newmemberoffsetattr(fld, BucketSize)
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, dwhvs)
+ newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0)
+ 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", 0)
+ 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))
+ }
+}
+
+func (d *dwctxt) dwarfDefineGlobal(ctxt *Link, symIdx loader.Sym, str string, v int64, gotype loader.Sym) {
+ // Find a suitable CU DIE to include the global.
+ // One would think it's as simple as just looking at the unit, but that might
+ // not have any reachable code. So, we go to the runtime's CU if our unit
+ // isn't otherwise reachable.
+ unit := d.ldr.SymUnit(symIdx)
+ if unit == nil {
+ unit = ctxt.runtimeCU
+ }
+ ver := d.ldr.SymVersion(symIdx)
+ dv := d.newdie(unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(ver))
+ d.newabslocexprattr(dv, v, symIdx)
+ if d.ldr.SymVersion(symIdx) < sym.SymVerStatic {
+ newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0)
+ }
+ dt := d.defgotype(gotype)
+ d.newrefattr(dv, dwarf.DW_AT_type, dt)
+}
+
+// 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 {
+ 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 := haslinkregister(d.linkctxt)
+
+ // 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 := fi.Pcsp()
+
+ // Emit a FDE, Section 6.4.1.
+ // First build the section contents into a byte buffer.
+ deltaBuf = deltaBuf[:0]
+ if haslr && d.ldr.AttrTopFrame(fn) {
+ // 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 && !d.ldr.AttrTopFrame(fn) {
+ // 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 {
+ 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))
+ }
+ 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(1) // magic 1 byte?
+ 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, 0)
+
+ // 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 := newdwctxt(ctxt, true)
+
+ 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>", 0)
+
+ // 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, 0)
+ 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 " + objabi.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
+ }
+
+ // Create DIEs for global variables and the types they use.
+ // FIXME: ideally this should be done in the compiler, since
+ // for globals there isn't any abiguity about which package
+ // a global belongs to.
+ 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
+ if d.ldr.SymGoType(idx) == 0 {
+ 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
+ }
+
+ // Create DIE for global.
+ sv := d.ldr.SymValue(idx)
+ gt := d.ldr.SymGoType(idx)
+ d.dwarfDefineGlobal(ctxt, idx, sn, sv, gt)
+ }
+
+ 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 {
+ compressedSegName := ".zdebug_" + ldr.SymSect(s).Name[len(".debug_"):]
+ sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04)
+ sect.Align = 1
+ sect.Length = uint64(len(z.compressed))
+ newSym := ldr.CreateSymForUpdate(compressedSegName, 0)
+ 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 saveDwsectCUSize(sname string, pkgname string, size uint64) {
+ dwsectCUSizeMu.Lock()
+ defer dwsectCUSizeMu.Unlock()
+ dwsectCUSize[sname+"."+pkgname] = size
+}
+
+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..a66506d
--- /dev/null
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -0,0 +1,1621 @@
+// 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 (
+ intdwarf "cmd/internal/dwarf"
+ objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function
+ "debug/dwarf"
+ "debug/pe"
+ "errors"
+ "fmt"
+ "internal/testenv"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+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, err := ioutil.TempDir("", "TestRuntimeTypesPresent")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ 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 := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst, src)
+ 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}
+}
+
+// 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 := exec.Command(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, err := ioutil.TempDir("", "TestEmbeddedStructMarker")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ 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, err := ioutil.TempDir("", "TestSizes")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+ 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, err := ioutil.TempDir("", "TestFieldOverlap")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ 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, err := ioutil.TempDir("", testpoint)
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := 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 := ex.FileRef(t, d, mainIdx, fileIdx)
+ 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")
+}
+
+// Helper class for supporting queries on DIEs within a DWARF .debug_info
+// section. Invoke the populate() method below passing in a dwarf.Reader,
+// which will read in all DIEs and keep track of parent/child
+// relationships. Queries can then be made to ask for DIEs by name or
+// by offset. This will hopefully reduce boilerplate for future test
+// writing.
+
+type examiner struct {
+ dies []*dwarf.Entry
+ idxByOffset map[dwarf.Offset]int
+ kids map[int][]int
+ parent map[int]int
+ byname map[string][]int
+}
+
+// Populate the examiner using the DIEs read from rdr.
+func (ex *examiner) populate(rdr *dwarf.Reader) error {
+ ex.idxByOffset = make(map[dwarf.Offset]int)
+ ex.kids = make(map[int][]int)
+ ex.parent = make(map[int]int)
+ ex.byname = make(map[string][]int)
+ var nesting []int
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ return err
+ }
+ if entry.Tag == 0 {
+ // terminator
+ if len(nesting) == 0 {
+ return errors.New("nesting stack underflow")
+ }
+ nesting = nesting[:len(nesting)-1]
+ continue
+ }
+ idx := len(ex.dies)
+ ex.dies = append(ex.dies, entry)
+ if _, found := ex.idxByOffset[entry.Offset]; found {
+ return errors.New("DIE clash on offset")
+ }
+ ex.idxByOffset[entry.Offset] = idx
+ if name, ok := entry.Val(dwarf.AttrName).(string); ok {
+ ex.byname[name] = append(ex.byname[name], idx)
+ }
+ if len(nesting) > 0 {
+ parent := nesting[len(nesting)-1]
+ ex.kids[parent] = append(ex.kids[parent], idx)
+ ex.parent[idx] = parent
+ }
+ if entry.Children {
+ nesting = append(nesting, idx)
+ }
+ }
+ if len(nesting) > 0 {
+ return errors.New("unterminated child sequence")
+ }
+ return nil
+}
+
+func indent(ilevel int) {
+ for i := 0; i < ilevel; i++ {
+ fmt.Printf(" ")
+ }
+}
+
+// For debugging new tests
+func (ex *examiner) dumpEntry(idx int, dumpKids bool, ilevel int) error {
+ if idx >= len(ex.dies) {
+ msg := fmt.Sprintf("bad DIE %d: index out of range\n", idx)
+ return errors.New(msg)
+ }
+ entry := ex.dies[idx]
+ indent(ilevel)
+ fmt.Printf("0x%x: %v\n", idx, entry.Tag)
+ for _, f := range entry.Field {
+ indent(ilevel)
+ fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val)
+ }
+ if dumpKids {
+ ksl := ex.kids[idx]
+ for _, k := range ksl {
+ ex.dumpEntry(k, true, ilevel+2)
+ }
+ }
+ return nil
+}
+
+// Given a DIE offset, return the previously read dwarf.Entry, or nil
+func (ex *examiner) entryFromOffset(off dwarf.Offset) *dwarf.Entry {
+ if idx, found := ex.idxByOffset[off]; found && idx != -1 {
+ return ex.entryFromIdx(idx)
+ }
+ return nil
+}
+
+// Return the ID that examiner uses to refer to the DIE at offset off
+func (ex *examiner) idxFromOffset(off dwarf.Offset) int {
+ if idx, found := ex.idxByOffset[off]; found {
+ return idx
+ }
+ return -1
+}
+
+// Return the dwarf.Entry pointer for the DIE with id 'idx'
+func (ex *examiner) entryFromIdx(idx int) *dwarf.Entry {
+ if idx >= len(ex.dies) || idx < 0 {
+ return nil
+ }
+ return ex.dies[idx]
+}
+
+// Returns a list of child entries for a die with ID 'idx'
+func (ex *examiner) Children(idx int) []*dwarf.Entry {
+ sl := ex.kids[idx]
+ ret := make([]*dwarf.Entry, len(sl))
+ for i, k := range sl {
+ ret[i] = ex.entryFromIdx(k)
+ }
+ return ret
+}
+
+// Returns parent DIE for DIE 'idx', or nil if the DIE is top level
+func (ex *examiner) Parent(idx int) *dwarf.Entry {
+ p, found := ex.parent[idx]
+ if !found {
+ return nil
+ }
+ return ex.entryFromIdx(p)
+}
+
+// ParentCU returns the enclosing compilation unit DIE for the DIE
+// with a given index, or nil if for some reason we can't establish a
+// parent.
+func (ex *examiner) ParentCU(idx int) *dwarf.Entry {
+ for {
+ parentDie := ex.Parent(idx)
+ if parentDie == nil {
+ return nil
+ }
+ if parentDie.Tag == dwarf.TagCompileUnit {
+ return parentDie
+ }
+ idx = ex.idxFromOffset(parentDie.Offset)
+ }
+}
+
+// FileRef takes a given DIE by index and a numeric file reference
+// (presumably from a decl_file or call_file attribute), looks up the
+// reference in the .debug_line file table, and returns the proper
+// string for it. We need to know which DIE is making the reference
+// so as find the right compilation unit.
+func (ex *examiner) FileRef(t *testing.T, dw *dwarf.Data, dieIdx int, fileRef int64) string {
+
+ // Find the parent compilation unit DIE for the specified DIE.
+ cuDie := ex.ParentCU(dieIdx)
+ if cuDie == nil {
+ t.Fatalf("no parent CU DIE for DIE with idx %d?", dieIdx)
+ return ""
+ }
+ // Construct a line reader and then use it to get the file string.
+ lr, lrerr := dw.LineReader(cuDie)
+ if lrerr != nil {
+ t.Fatal("d.LineReader: ", lrerr)
+ return ""
+ }
+ files := lr.Files()
+ if fileRef < 0 || int(fileRef) > len(files)-1 {
+ t.Fatalf("examiner.FileRef: malformed file reference %d", fileRef)
+ return ""
+ }
+ return files[fileRef].Name
+}
+
+// Return a list of all DIEs with name 'name'. When searching for DIEs
+// by name, keep in mind that the returned results will include child
+// DIEs such as params/variables. For example, asking for all DIEs named
+// "p" for even a small program will give you 400-500 entries.
+func (ex *examiner) Named(name string) []*dwarf.Entry {
+ sl := ex.byname[name]
+ ret := make([]*dwarf.Entry, len(sl))
+ for i, k := range sl {
+ ret[i] = ex.entryFromIdx(k)
+ }
+ return ret
+}
+
+func TestInlinedRoutineRecords(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" {
+ t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168")
+ }
+
+ 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, err := ioutil.TempDir("", "TestInlinedRoutineRecords")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ // 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)
+
+ 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 := 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 := ex.FileRef(t, d, mainIdx, cf)
+ 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, err := ioutil.TempDir("", "TestAbstractOriginSanity")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ // Build with inlining, to exercise DWARF inlining support.
+ f := gobuildTestdata(t, dir, filepath.Join(pkgDir, "main"), flags)
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ rdr := d.Reader()
+ ex := 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 runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" {
+ t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168")
+ }
+
+ 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.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" {
+ t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168")
+ }
+ if runtime.GOARCH != "amd64" && runtime.GOARCH != "x86" {
+ t.Skip("skipping on not-amd64 not-x86; 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 runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" {
+ t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168")
+ }
+ 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, err := ioutil.TempDir("", "TestRuntimeType")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ f := gobuild(t, dir, prog, flags)
+ out, err := exec.Command(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 := 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, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ 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, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ 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, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ 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, err := ioutil.TempDir("", "TestMachoIssue32233")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ wd, err2 := os.Getwd()
+ if err2 != 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, err := ioutil.TempDir("", "TestEmbeddedStructMarker")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ prog := `
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello World")
+}`
+ f := gobuild(t, dir, prog, NoOpt)
+ exe, err := pe.Open(f.path)
+ if err != nil {
+ t.Fatalf("error opening pe file: %v", err)
+ }
+ 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, err := ioutil.TempDir("", "TestIssue38192")
+ 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", "issue38192")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+
+ // 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, err := ioutil.TempDir("", "TestIssue38192")
+ 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", "issue39757")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+
+ 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 := 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)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
new file mode 100644
index 0000000..f5823a8
--- /dev/null
+++ b/src/cmd/link/internal/ld/elf.go
@@ -0,0 +1,2140 @@
+// 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/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "crypto/sha1"
+ "debug/elf"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "path/filepath"
+ "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.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.MIPS64, sys.RISCV64:
+ if ctxt.Arch.Family == sys.MIPS64 {
+ ehdr.Flags = 0x20000004 /* MIPS 3 CPIC */
+ }
+ 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 elfwritedynentsym(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
+ Elfwritedynentsymplus(ctxt, s, tag, t, 0)
+}
+
+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)
+}
+
+func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int) int {
+ n := 3*4 + uint64(sz) + resoff%4
+
+ 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)
+}
+
+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)
+ 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 {
+ 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 strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") {
+ sh.Flags = 0
+ }
+
+ if linkmode != LinkExternal {
+ sh.Addr = sect.Vaddr
+ }
+ 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("__libfuzzer_extra_counters")
+ shstrtab.Addstring(".go.buildinfo")
+
+ // 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 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")
+
+ // 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(sha1.Size)
+
+ sort.Sort(byPkg(ctxt.Library))
+ h := sha1.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))
+ }
+}
+
+// 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.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
+ if *flagRace && ctxt.IsNetbsd() {
+ sh := elfshname(".note.netbsd.pax")
+ resoff -= int64(elfnetbsdpax(sh, uint64(startva), uint64(resoff)))
+ pnote = newElfPhdr()
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
+ phsh(pnote, 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 == "" && objabi.GO_LDSO != "" {
+ interpreter = objabi.GO_LDSO
+ }
+
+ if interpreter == "" {
+ switch ctxt.HeadType {
+ case objabi.Hlinux:
+ if objabi.GOOS == "android" {
+ interpreter = thearch.Androiddynld
+ if interpreter == "" {
+ Exitf("ELF interpreter not set")
+ }
+ } else {
+ interpreter = thearch.Linuxdynld
+ }
+
+ 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)
+ }
+
+ pnote = nil
+ if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd {
+ 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)))
+ }
+
+ pnote = newElfPhdr()
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
+ phsh(pnote, sh)
+ }
+
+ if len(buildinfo) > 0 {
+ sh := elfshname(".note.gnu.build-id")
+ resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff)))
+
+ if pnote == nil {
+ pnote = newElfPhdr()
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
+ }
+
+ phsh(pnote, sh)
+ }
+
+ if *flagBuildid != "" {
+ sh := elfshname(".note.go.buildid")
+ resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff)))
+
+ pnote := newElfPhdr()
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
+ phsh(pnote, 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
+ }
+
+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)
+
+ // 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.LinkMode != LinkExternal {
+ if ctxt.HeadType == objabi.Hnetbsd {
+ a += int64(elfwritenetbsdsig(ctxt.Out))
+ }
+ if ctxt.HeadType == objabi.Hopenbsd {
+ a += int64(elfwriteopenbsdsig(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)
+ }
+}
+
+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 target.Arch.Family == sys.AMD64 && !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..776fc1b
--- /dev/null
+++ b/src/cmd/link/internal/ld/elf_test.go
@@ -0,0 +1,135 @@
+// +build cgo
+
+// 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 (
+ "debug/elf"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestDynSymShInfo(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ dir, err := ioutil.TempDir("", "go-build-issue33358")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ const prog = `
+package main
+
+import "net"
+
+func main() {
+ net.Dial("", "")
+}
+`
+ src := filepath.Join(dir, "issue33358.go")
+ if err := ioutil.WriteFile(src, []byte(prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ binFile := filepath.Join(dir, "issue33358")
+ cmd := exec.Command(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)
+ }
+
+ 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", "freebsd-amd64", "openbsd-amd64":
+ default:
+ t.Skip("no need for test on " + pair)
+ }
+
+ t.Parallel()
+
+ dir, err := ioutil.TempDir("", "no-dup-needed")
+ if err != nil {
+ t.Fatalf("Failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ 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 := exec.Command(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..d6e8ff2
--- /dev/null
+++ b/src/cmd/link/internal/ld/errors.go
@@ -0,0 +1,66 @@
+// 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
+ unresOnce sync.Once
+ 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.unresOnce.Do(func() { reporter.unresSyms = make(map[unresolvedSymKey]bool) })
+
+ k := unresolvedSymKey{from: s, to: rs}
+ reporter.unresMutex.Lock()
+ defer reporter.unresMutex.Unlock()
+ 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..4687c62
--- /dev/null
+++ b/src/cmd/link/internal/ld/execarchive.go
@@ -0,0 +1,37 @@
+// 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.
+
+// +build !wasm,!windows
+
+package ld
+
+import (
+ exec "internal/execabs"
+ "os"
+ "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..a70dea9
--- /dev/null
+++ b/src/cmd/link/internal/ld/execarchive_noexec.go
@@ -0,0 +1,13 @@
+// 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.
+
+// +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..244b70f
--- /dev/null
+++ b/src/cmd/link/internal/ld/fallocate_test.go
@@ -0,0 +1,69 @@
+// 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.
+
+// +build darwin linux
+
+package ld
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "syscall"
+ "testing"
+)
+
+func TestFallocate(t *testing.T) {
+ dir, err := ioutil.TempDir("", "TestFallocate")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ 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..fbc7a78
--- /dev/null
+++ b/src/cmd/link/internal/ld/go.go
@@ -0,0 +1,494 @@
+// 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 (
+ "bytes"
+ "cmd/internal/bio"
+ "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).
+
+// replace all "". with pkg.
+func expandpkg(t0 string, pkg string) string {
+ return strings.Replace(t0, `"".`, pkg+".", -1)
+}
+
+// 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
+ }
+
+ // Find cgo_export symbols. They are roots in the deadcode pass.
+ for _, f := range directives {
+ switch f[0] {
+ case "cgo_export_static", "cgo_export_dynamic":
+ if len(f) < 2 || len(f) > 3 {
+ continue
+ }
+ local := f[1]
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
+ if local == "main" {
+ continue
+ }
+ }
+ local = expandpkg(local, pkg)
+ if f[0] == "cgo_export_static" {
+ ctxt.cgo_export_static[local] = true
+ } else {
+ ctxt.cgo_export_dynamic[local] = true
+ }
+ }
+ }
+
+ // 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, lookup func(string, int) loader.Sym, 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
+ }
+
+ local = expandpkg(local, pkg)
+ q := ""
+ if i := strings.Index(remote, "#"); i >= 0 {
+ remote, q = remote[:i], remote[i+1:]
+ }
+ s := lookup(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 := lookup(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) > 3 {
+ break
+ }
+ local := f[1]
+ remote := local
+ if len(f) > 2 {
+ remote = f[2]
+ }
+ local = expandpkg(local, pkg)
+
+ // The compiler arranges for an ABI0 wrapper
+ // to be available for all cgo-exported
+ // functions. Link.loadlib will resolve any
+ // ABI aliases we find here (since we may not
+ // yet know it's an alias).
+ s := lookup(local, 0)
+
+ if l.SymType(s) == sym.SHOSTOBJ {
+ hostObjSyms[s] = struct{}{}
+ }
+
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
+ if s == 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
+ }
+
+ if f[0] == "cgo_export_static" {
+ l.SetAttrCgoExportStatic(s, true)
+ } else {
+ 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 bytes.Buffer
+ 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
+ }
+
+ for _, exp := range ctxt.dynexp {
+ Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, exp)
+ }
+ 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..0197196
--- /dev/null
+++ b/src/cmd/link/internal/ld/go_test.go
@@ -0,0 +1,121 @@
+// 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"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "testing"
+)
+
+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, err := ioutil.TempDir("", "dedup-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ // 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 := ioutil.WriteFile(srcFile, []byte(src), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ exe := filepath.Join(dir, "deduped.exe")
+ out, err := exec.Command(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 = exec.Command(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..92a47fa
--- /dev/null
+++ b/src/cmd/link/internal/ld/issue33808_test.go
@@ -0,0 +1,55 @@
+// 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"
+ "io/ioutil"
+ "os"
+ "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, err := ioutil.TempDir("", "TestIssue33808")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ 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..7ff9c41
--- /dev/null
+++ b/src/cmd/link/internal/ld/ld.go
@@ -0,0 +1,271 @@
+// 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/goobj"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+func (ctxt *Link) readImportCfg(file string) {
+ ctxt.PackageFile = make(map[string]string)
+ ctxt.PackageShlib = make(map[string]string)
+ data, err := ioutil.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
+ }
+ }
+}
+
+func pkgname(ctxt *Link, lib string) string {
+ name := path.Clean(lib)
+
+ // When using importcfg, we have the final package name.
+ if ctxt.PackageFile != nil {
+ return name
+ }
+
+ // runtime.a -> runtime, runtime.6 -> runtime
+ pkg := name
+ if len(pkg) >= 2 && pkg[len(pkg)-2] == '.' {
+ pkg = pkg[:len(pkg)-2]
+ }
+ return pkg
+}
+
+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 {
+ if filepath.IsAbs(name) {
+ pname = name
+ } else {
+ pkg := pkgname(ctxt, lib)
+ // Add .a if needed; the new -importcfg modes
+ // do not put .a into the package name anymore.
+ // This only matters when people try to mix
+ // compiles using -importcfg with links not using -importcfg,
+ // such as when running quick things like
+ // 'go tool compile x.go && go tool link x.o'
+ // by hand against a standard library built using -importcfg.
+ if !strings.HasSuffix(name, ".a") && !strings.HasSuffix(name, ".o") {
+ name += ".a"
+ }
+ // try dot, -L "libdir", and then goroot.
+ 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)
+ 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 := ioutil.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..cdfaadb
--- /dev/null
+++ b/src/cmd/link/internal/ld/ld_test.go
@@ -0,0 +1,244 @@
+// 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 (
+ "debug/pe"
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "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()
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ out, err := exec.Command(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, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ srcfile := filepath.Join(dir, "test.go")
+ arfile := filepath.Join(dir, "test.a")
+ if err := ioutil.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 := exec.Command(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 TestPPC64LargeTextSectionSplitting(t *testing.T) {
+ // The behavior we're checking for is of interest only on ppc64.
+ if !strings.HasPrefix(runtime.GOARCH, "ppc64") {
+ t.Skip("test useful only for ppc64")
+ }
+
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ // NB: the use of -ldflags=-debugppc64textsize=1048576 tells the linker to
+ // split text sections at a size threshold of 1M instead of the
+ // architected limit of 67M. 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, eerr := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "-ldflags=-linkmode=external -debugppc64textsize=1048576", "cmd/go").CombinedOutput()
+ if eerr != nil {
+ t.Fatalf("build failure: %s\n%s\n", eerr, string(out))
+ }
+
+ // Result should be runnable.
+ _, err = exec.Command(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")
+ }
+
+ 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, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ srcfile := filepath.Join(dir, "test.go")
+ objfile := filepath.Join(dir, "test.dll")
+ if err := ioutil.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 := exec.Command(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")
+ }
+}
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
new file mode 100644
index 0000000..18db567
--- /dev/null
+++ b/src/cmd/link/internal/ld/lib.go
@@ -0,0 +1,2573 @@
+// 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"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loadelf"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/loadmacho"
+ "cmd/link/internal/loadpe"
+ "cmd/link/internal/loadxcoff"
+ "cmd/link/internal/sym"
+ "crypto/sha1"
+ "debug/elf"
+ "debug/macho"
+ "encoding/base64"
+ "encoding/binary"
+ "fmt"
+ exec "internal/execabs"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+)
+
+// 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
+}
+
+// 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)
+
+ if ctxt.IsPPC64() {
+ ctxt.mkArchSym("TOC", 0, &ctxt.TOC)
+
+ // NB: note the +2 below for DotTOC2 compared to the +1 for
+ // DocTOC. This is because loadlibfull() creates an additional
+ // syms version during conversion of loader.Sym symbols to
+ // *sym.Symbol symbols. Symbols that are assigned this final
+ // version are not going to have TOC references, so it should
+ // be ok for them to inherit an invalid .TOC. symbol.
+ // TODO: revisit the +2, now that loadlibfull is gone.
+ ctxt.DotTOC = make([]loader.Sym, ctxt.MaxVersion()+2)
+ for i := 0; i <= ctxt.MaxVersion(); i++ {
+ if i >= 2 && 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
+ Androiddynld string
+ Linuxdynld string
+ Freebsddynld string
+ Netbsddynld string
+ Openbsddynld string
+ Dragonflydynld string
+ Solarisdynld string
+
+ // Empty spaces between codeblocks will be padded with this value.
+ // For example an architecture might want to pad with a trap instruction to
+ // catch wayward programs. Architectures that do not define a padding value
+ // are padded with zeros.
+ CodePad []byte
+
+ // Set to true to write all text blocks in with CodeBlkWrite
+ WriteTextBlocks bool
+
+ // 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) (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
+)
+
+// 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
+
+ // 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 (
+ // Set if we see an object compiled by the host compiler that is not
+ // from a package that is known to support internal linking mode.
+ externalobj = false
+ theline string
+)
+
+func Lflag(ctxt *Link, arg string) {
+ ctxt.Libdir = append(ctxt.Libdir, arg)
+}
+
+/*
+ * Unix doesn't like it when we write to a running (or, sometimes,
+ * recently run) binary, so remove the output file before writing it.
+ * On Windows 7, remove() can force a subsequent create() to fail.
+ * S_ISREG() does not exist on Plan 9.
+ */
+func mayberemoveoutfile() {
+ if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() {
+ return
+ }
+ os.Remove(*flagOutfile)
+}
+
+func libinit(ctxt *Link) {
+ Funcalign = thearch.Funcalign
+
+ // add goroot to the end of the libdir list.
+ suffix := ""
+
+ suffixsep := ""
+ if *flagInstallSuffix != "" {
+ suffixsep = "_"
+ suffix = *flagInstallSuffix
+ } else if *flagRace {
+ suffixsep = "_"
+ suffix = "race"
+ } else if *flagMsan {
+ suffixsep = "_"
+ suffix = "msan"
+ }
+
+ Lflag(ctxt, filepath.Join(objabi.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", objabi.GOOS, objabi.GOARCH, suffixsep, suffix)))
+
+ mayberemoveoutfile()
+
+ 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", objabi.GOARCH, objabi.GOOS)
+ case BuildModeExe, BuildModePIE:
+ *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", objabi.GOARCH, objabi.GOOS)
+ case BuildModeShared, BuildModePlugin:
+ // No *flagEntrySymbol for -buildmode=shared and plugin
+ default:
+ Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode)
+ }
+ }
+}
+
+func 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)
+ }
+ }
+
+ ctxt.Logf("warning: unable to find %s.a\n", name)
+ return nil
+}
+
+// extld returns the current external linker.
+func (ctxt *Link) extld() string {
+ if *flagExtld == "" {
+ *flagExtld = "gcc"
+ }
+ return *flagExtld
+}
+
+// findLibPathCmd uses cmd command to find gcc library libname.
+// It returns library full path if found, or "none" if not found.
+func (ctxt *Link) findLibPathCmd(cmd, libname string) string {
+ extld := ctxt.extld()
+ args := hostlinkArchArgs(ctxt.Arch)
+ args = append(args, cmd)
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("%s %v\n", extld, args)
+ }
+ out, err := exec.Command(extld, 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.cgo_export_static = make(map[string]bool)
+ ctxt.cgo_export_dynamic = make(map[string]bool)
+
+ // 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")
+ }
+ 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 && !(objabi.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")
+ }
+ 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 := ctxt.loader.UndefinedRelocTargets(1)
+ if len(undefs) > 0 {
+ any = true
+ }
+ if any {
+ if *flagLibGCC == "" {
+ *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc")
+ }
+ if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" {
+ // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a".
+ // In this case we fail to load libgcc.a and can encounter link
+ // errors - see if we can find libcompiler_rt.a instead.
+ *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
+ }
+ if *flagLibGCC != "none" {
+ hostArchive(ctxt, *flagLibGCC)
+ }
+ if ctxt.HeadType == objabi.Hwindows {
+ if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
+ hostArchive(ctxt, p)
+ }
+ if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
+ hostArchive(ctxt, p)
+ }
+ // 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)
+ }
+ // 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
+ */
+ }
+ }
+ }
+
+ // We've loaded all the code now.
+ ctxt.Loaded = true
+
+ importcycles()
+
+ strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
+}
+
+// setupdynexp constructs ctxt.dynexp, a list of loader.Sym.
+func setupdynexp(ctxt *Link) {
+ dynexpMap := ctxt.cgo_export_dynamic
+ if ctxt.LinkMode == LinkExternal {
+ dynexpMap = ctxt.cgo_export_static
+ }
+ d := make([]loader.Sym, 0, len(dynexpMap))
+ for exp := range dynexpMap {
+ s := ctxt.loader.LookupOrCreateSym(exp, 0)
+ d = append(d, s)
+ // sanity check
+ if !ctxt.loader.AttrReachable(s) {
+ panic("dynexp entry not reachable")
+ }
+ }
+ sort.Slice(d, func(i, j int) bool {
+ return ctxt.loader.SymName(d[i]) < ctxt.loader.SymName(d[j])
+ })
+
+ // Resolve ABI aliases in the list of cgo-exported functions.
+ // This is necessary because we load the ABI0 symbol for all
+ // cgo exports.
+ for i, s := range d {
+ if ctxt.loader.SymType(s) != sym.SABIALIAS {
+ continue
+ }
+ t := ctxt.loader.ResolveABIAlias(s)
+ ctxt.loader.CopyAttributes(s, t)
+ ctxt.loader.SetSymExtname(t, ctxt.loader.SymExtname(s))
+ d[i] = t
+ }
+ ctxt.dynexp = d
+
+ ctxt.cgo_export_static = nil
+ ctxt.cgo_export_dynamic = nil
+}
+
+// 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, ctxt.loader.LookupOrCreateSym, 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.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
+ *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 && objabi.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 objabi.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(objabi.GOARM))
+ }
+ } 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 := sha1.Sum([]byte(name))
+ prefix := "type."
+ if name[5] == '.' {
+ prefix = "type.."
+ }
+ return prefix + base64.StdEncoding.EncodeToString(hash[:6])
+}
+
+/*
+ * look for the next file in an archive.
+ * adapted from libmach.
+ */
+func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 {
+ if off&1 != 0 {
+ off++
+ }
+ bp.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
+ }
+
+ // Skip other special (non-object-file) sections that
+ // build tools may have added. Such sections must have
+ // short names so that the suffix is not truncated.
+ if len(arhdr.name) < 16 {
+ if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" {
+ continue
+ }
+ }
+
+ pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name)
+ l = atolwhex(arhdr.size)
+ ldobj(ctxt, f, lib, l, pname, lib.File)
+ }
+}
+
+type Hostobj struct {
+ ld func(*Link, *bio.Reader, string, int64, string)
+ pkg string
+ pn string
+ file string
+ off int64
+ length int64
+}
+
+var hostobj []Hostobj
+
+// These packages can use internal linking mode.
+// Others trigger external mode.
+var internalpkg = []string{
+ "crypto/x509",
+ "net",
+ "os/user",
+ "runtime/cgo",
+ "runtime/race",
+ "runtime/msan",
+}
+
+func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj {
+ isinternal := false
+ for _, intpkg := range internalpkg {
+ if pkg == intpkg {
+ isinternal = true
+ break
+ }
+ }
+
+ // DragonFly declares errno with __thread, which results in a symbol
+ // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not
+ // currently know how to handle TLS relocations, hence we have to
+ // force external linking for any libraries that link in code that
+ // uses errno. This can be removed if the Go linker ever supports
+ // these relocation types.
+ if headType == objabi.Hdragonfly {
+ if pkg == "net" || pkg == "os/user" {
+ isinternal = false
+ }
+ }
+
+ if !isinternal {
+ externalobj = true
+ }
+
+ hostobj = append(hostobj, Hostobj{})
+ h := &hostobj[len(hostobj)-1]
+ h.ld = ld
+ h.pkg = pkg
+ h.pn = pn
+ h.file = file
+ h.off = f.Offset()
+ h.length = length
+ return h
+}
+
+func hostobjs(ctxt *Link) {
+ 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)
+ h.ld(ctxt, f, h.pkg, h.length, h.pn)
+ f.Close()
+ }
+}
+
+func hostlinksetup(ctxt *Link) {
+ if ctxt.LinkMode != LinkExternal {
+ return
+ }
+
+ // For external link, record that we need to tell the external linker -s,
+ // and turn off -s internally: the external linker needs the symbol
+ // information for its final link.
+ debug_s = *FlagS
+ *FlagS = false
+
+ // create temporary directory and arrange cleanup
+ if *flagTmpdir == "" {
+ dir, err := ioutil.TempDir("", "go-link-")
+ if err != nil {
+ log.Fatal(err)
+ }
+ *flagTmpdir = dir
+ ownTmpDir = true
+ AtExit(func() {
+ ctxt.Out.Close()
+ 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 := ioutil.WriteFile(path, []byte(src), 0666)
+ if err != nil {
+ Errorf(nil, "WriteFile %s failed: %v", name, err)
+ }
+ return path
+}
+
+// archive builds a .a archive from the hostobj object files.
+func (ctxt *Link) archive() {
+ if ctxt.BuildMode != BuildModeCArchive {
+ return
+ }
+
+ 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 combineDwarf {
+ // Leave room for DWARF combining.
+ // -headerpad is incompatible with -fembed-bitcode.
+ argv = append(argv, "-Wl,-headerpad,1144")
+ }
+ if ctxt.DynlinkingGo() && objabi.GOOS != "ios" {
+ // -flat_namespace is deprecated on iOS.
+ // It is useful for supporting plugins. We don't support plugins on iOS.
+ argv = append(argv, "-Wl,-flat_namespace")
+ }
+ 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 ASLR on Windows.
+ addASLRargs := func(argv []string) []string {
+ // Enable ASLR.
+ argv = append(argv, "-Wl,--dynamicbase")
+ // enable high-entropy ASLR on 64-bit.
+ if ctxt.Arch.PtrSize >= 8 {
+ argv = append(argv, "-Wl,--high-entropy-va")
+ }
+ return argv
+ }
+
+ switch ctxt.BuildMode {
+ case BuildModeExe:
+ if ctxt.HeadType == objabi.Hdarwin {
+ if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() {
+ argv = append(argv, "-Wl,-no_pie")
+ argv = append(argv, "-Wl,-pagezero_size,4000000")
+ }
+ }
+ case BuildModePIE:
+ switch ctxt.HeadType {
+ case objabi.Hdarwin, objabi.Haix:
+ case objabi.Hwindows:
+ argv = addASLRargs(argv)
+ 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 {
+ // ELF.
+ argv = append(argv, "-Wl,-Bsymbolic")
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ if ctxt.HeadType == objabi.Hwindows {
+ if *flagAslr {
+ argv = addASLRargs(argv)
+ }
+ } else {
+ // Pass -z nodelete to mark the shared library as
+ // non-closeable: a dlclose will do nothing.
+ argv = append(argv, "-Wl,-z,nodelete")
+ }
+ }
+ case BuildModeShared:
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ case BuildModePlugin:
+ if ctxt.HeadType == objabi.Hdarwin {
+ argv = append(argv, "-dynamiclib")
+ } else {
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ }
+ }
+
+ 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,-znow")
+
+ // Do not let the host linker generate COPY relocations. These
+ // can move symbols out of sections that rely on stable offsets
+ // from the beginning of the section (like sym.STYPE).
+ argv = append(argv, "-Wl,-znocopyreloc")
+
+ if objabi.GOOS == "android" {
+ // Use lld to avoid errors from default linker (issue #38838)
+ altLinker = "lld"
+ }
+
+ if ctxt.Arch.InFamily(sys.ARM, sys.ARM64) && objabi.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.
+ cmd := exec.Command(*flagExtld, "-fuse-ld=gold", "-Wl,--version")
+ if out, err := cmd.CombinedOutput(); err == nil {
+ if !bytes.Contains(out, []byte("GNU gold")) {
+ log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out)
+ }
+ }
+ }
+ }
+ if ctxt.Arch.Family == sys.ARM64 && objabi.GOOS == "freebsd" {
+ // Switch to ld.bfd on freebsd/arm64.
+ altLinker = "bfd"
+
+ // Provide a useful error if ld.bfd is missing.
+ cmd := exec.Command(*flagExtld, "-fuse-ld=bfd", "-Wl,--version")
+ 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 objabi.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" {
+ outopt += "."
+ }
+ argv = append(argv, "-o")
+ argv = append(argv, outopt)
+
+ if rpath.val != "" {
+ argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val))
+ }
+
+ // Force global symbols to be exported for dlopen, etc.
+ if ctxt.IsELF {
+ argv = append(argv, "-rdynamic")
+ }
+ if ctxt.HeadType == objabi.Haix {
+ fileName := xcoffCreateExportFile(ctxt)
+ argv = append(argv, "-Wl,-bE:"+fileName)
+ }
+
+ if strings.Contains(argv[0], "clang") {
+ argv = append(argv, "-Qunused-arguments")
+ }
+
+ const compressDWARF = "-Wl,--compress-debug-sections=zlib-gnu"
+ 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()
+ // Get starting files.
+ getPathFile := func(file string) string {
+ args := []string{"-maix64", "--print-file-name=" + file}
+ out, err := exec.Command(extld, args...).CombinedOutput()
+ if err != nil {
+ log.Fatalf("running %s failed: %v\n%s", extld, err, out)
+ }
+ return strings.Trim(string(out), "\n")
+ }
+ argv = append(argv, getPathFile("crtcxa.o"))
+ argv = append(argv, getPathFile("crtdbase.o"))
+ }
+
+ if ctxt.linkShared {
+ seenDirs := make(map[string]bool)
+ seenLibs := make(map[string]bool)
+ addshlib := func(path string) {
+ dir, base := filepath.Split(path)
+ if !seenDirs[dir] {
+ argv = append(argv, "-L"+dir)
+ if !rpath.set {
+ argv = append(argv, "-Wl,-rpath="+dir)
+ }
+ seenDirs[dir] = true
+ }
+ base = strings.TrimSuffix(base, ".so")
+ base = strings.TrimPrefix(base, "lib")
+ if !seenLibs[base] {
+ argv = append(argv, "-l"+base)
+ seenLibs[base] = true
+ }
+ }
+ for _, shlib := range ctxt.Shlibs {
+ addshlib(shlib.Path)
+ for _, dep := range shlib.Deps {
+ if dep == "" {
+ continue
+ }
+ libpath := findshlib(ctxt, dep)
+ if libpath != "" {
+ addshlib(libpath)
+ }
+ }
+ }
+ }
+
+ // clang, unlike GCC, passes -rdynamic to the linker
+ // even when linking with -static, causing a linker
+ // error when using GNU ld. So take out -rdynamic if
+ // we added it. We do it in this order, rather than
+ // only adding -rdynamic later, so that -*extldflags
+ // can override -rdynamic without using -static.
+ checkStatic := func(arg string) {
+ if ctxt.IsELF && arg == "-static" {
+ for i := range argv {
+ if argv[i] == "-rdynamic" {
+ argv[i] = "-static"
+ }
+ }
+ }
+ }
+
+ for _, p := range ldflag {
+ argv = append(argv, p)
+ checkStatic(p)
+ }
+
+ // When building a program with the default -buildmode=exe the
+ // gc compiler generates code requires DT_TEXTREL in a
+ // position independent executable (PIE). On systems where the
+ // toolchain creates PIEs by default, and where DT_TEXTREL
+ // does not work, the resulting programs will not run. See
+ // issue #17847. To avoid this problem pass -no-pie to the
+ // toolchain if it is supported.
+ if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared && !(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 strings.Fields(*flagExtldflags) {
+ argv = append(argv, p)
+ checkStatic(p)
+ }
+ if ctxt.HeadType == objabi.Hwindows {
+ // Determine which linker we're using. Add in the extldflags in
+ // case used has specified "-fuse-ld=...".
+ cmd := exec.Command(*flagExtld, *flagExtldflags, "-Wl,--version")
+ 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)
+ }
+ // 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).
+ ctxt.Logf("%s", out)
+ }
+
+ if combineDwarf {
+ dsym := filepath.Join(*flagTmpdir, "go.dwarf")
+ if out, err := exec.Command("xcrun", "dsymutil", "-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("xcrun", "strip", "-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 := ioutil.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil {
+ Errorf(nil, "WriteFile trivial.c failed: %v", err)
+ }
+ })
+
+ flagsWithNextArgSkip := []string{
+ "-F",
+ "-l",
+ "-L",
+ "-framework",
+ "-Wl,-framework",
+ "-Wl,-rpath",
+ "-Wl,-undefined",
+ }
+ flagsWithNextArgKeep := []string{
+ "-arch",
+ "-isysroot",
+ "--sysroot",
+ "-target",
+ }
+ prefixesToKeep := []string{
+ "-f",
+ "-m",
+ "-p",
+ "-Wl,",
+ "-arch",
+ "-isysroot",
+ "--sysroot",
+ "-target",
+ }
+
+ flags := hostlinkArchArgs(arch)
+ keep := false
+ skip := false
+ extldflags := strings.Fields(*flagExtldflags)
+ for _, f := range append(extldflags, ldflag...) {
+ if keep {
+ flags = append(flags, f)
+ keep = false
+ } else if skip {
+ skip = false
+ } else if f == "" || f[0] != '-' {
+ } else if contains(flagsWithNextArgSkip, f) {
+ skip = true
+ } else if contains(flagsWithNextArgKeep, f) {
+ flags = append(flags, f)
+ keep = true
+ } else {
+ for _, p := range prefixesToKeep {
+ if strings.HasPrefix(f, p) {
+ flags = append(flags, f)
+ break
+ }
+ }
+ }
+ }
+
+ 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 objabi.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 objabi.GOOS == "darwin" {
+ return []string{"-arch", "arm64"}
+ }
+ case sys.MIPS64:
+ return []string{"-mabi=64"}
+ case sys.MIPS:
+ return []string{"-mabi=32"}
+ case sys.PPC64:
+ if objabi.GOOS == "aix" {
+ return []string{"-maix64"}
+ } else {
+ return []string{"-m64"}
+ }
+
+ }
+ return nil
+}
+
+// ldobj loads an input object. If it is a host object (an object
+// compiled by a non-Go compiler) it returns the Hostobj pointer. If
+// it is a Go object, it returns nil.
+func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj {
+ pkg := objabi.PathToPrefix(lib.Pkg)
+
+ eof := f.Offset() + length
+ start := f.Offset()
+ c1 := bgetc(f)
+ c2 := bgetc(f)
+ c3 := bgetc(f)
+ c4 := bgetc(f)
+ f.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)
+ }
+
+ if /* x86 */ c1 == 0x4c && c2 == 0x01 || /* x86_64 */ c1 == 0x64 && c2 == 0x86 || /* armv7 */ c1 == 0xc4 && c2 == 0x01 {
+ 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)
+ }
+
+ /* check the header */
+ line, err := f.ReadString('\n')
+ if err != nil {
+ Errorf(nil, "truncated object file: %s: %v", pn, err)
+ return nil
+ }
+
+ if !strings.HasPrefix(line, "go object ") {
+ if strings.HasSuffix(pn, ".go") {
+ Exitf("%s: uncompiled .go source file", pn)
+ return nil
+ }
+
+ if line == ctxt.Arch.Name {
+ // old header format: just $GOOS
+ Errorf(nil, "%s: stale object file", pn)
+ return nil
+ }
+
+ Errorf(nil, "%s: not an object file", pn)
+ return nil
+ }
+
+ // First, check that the basic GOOS, GOARCH, and Version match.
+ t := fmt.Sprintf("%s %s %s ", objabi.GOOS, objabi.GOARCH, objabi.Version)
+
+ line = strings.TrimRight(line, "\n")
+ if !strings.HasPrefix(line[10:]+" ", t) && !*flagF {
+ Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], t)
+ return nil
+ }
+
+ // Second, check that longer lines match each other exactly,
+ // so that the Go compiler and write additional information
+ // that must be the same from run to run.
+ if len(line) >= len(t)+10 {
+ if theline == "" {
+ theline = line[10:]
+ } else if theline != line[10:] {
+ Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], theline)
+ return nil
+ }
+ }
+
+ // Skip over exports and other info -- ends with \n!\n.
+ //
+ // Note: It's possible for "\n!\n" to appear within the binary
+ // package export data format. To avoid truncating the package
+ // definition prematurely (issue 21703), we keep track of
+ // how many "$$" delimiters we've seen.
+
+ import0 := f.Offset()
+
+ c1 = '\n' // the last line ended in \n
+ c2 = bgetc(f)
+ c3 = bgetc(f)
+ markers := 0
+ for {
+ if c1 == '\n' {
+ if markers%2 == 0 && c2 == '!' && c3 == '\n' {
+ break
+ }
+ if c2 == '$' && c3 == '$' {
+ markers++
+ }
+ }
+
+ c1 = c2
+ c2 = c3
+ c3 = bgetc(f)
+ if c3 == -1 {
+ Errorf(nil, "truncated object file: %s", pn)
+ return nil
+ }
+ }
+
+ import1 := f.Offset()
+
+ f.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
+}
+
+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
+ if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type.") {
+ ver = sym.SymVerABIInternal
+ }
+
+ l := ctxt.loader
+ s := l.LookupOrCreateSym(elfsym.Name, 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))
+ }
+ }
+
+ // For function symbols, we don't know what ABI is
+ // available, so alias it under both ABIs.
+ //
+ // TODO(austin): This is almost certainly wrong once
+ // the ABIs are actually different. We might have to
+ // mangle Go function names in the .so to include the
+ // ABI.
+ if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
+ alias := ctxt.loader.LookupOrCreateSym(elfsym.Name, sym.SymVerABIInternal)
+ if l.SymType(alias) != 0 {
+ continue
+ }
+ su := l.MakeSymbolUpdater(alias)
+ su.SetType(sym.SABIALIAS)
+ r, _ := su.AddRel(0) // type doesn't matter
+ r.SetSym(s)
+ }
+ }
+ ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
+}
+
+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
+}
+
+type chain struct {
+ sym loader.Sym
+ up *chain
+ limit int // limit on entry to sym
+}
+
+func haslinkregister(ctxt *Link) bool {
+ return ctxt.FixedFrameSize() != 0
+}
+
+func callsize(ctxt *Link) int {
+ if haslinkregister(ctxt) {
+ return 0
+ }
+ return ctxt.Arch.RegSize
+}
+
+type stkChk struct {
+ ldr *loader.Loader
+ ctxt *Link
+ morestack loader.Sym
+ done loader.Bitmap
+}
+
+// Walk the call tree and check that there is always enough stack space
+// for the call frames, especially for a chain of nosplit functions.
+func (ctxt *Link) dostkcheck() {
+ ldr := ctxt.loader
+ sc := stkChk{
+ ldr: ldr,
+ ctxt: ctxt,
+ morestack: ldr.Lookup("runtime.morestack", 0),
+ done: loader.MakeBitmap(ldr.NSym()),
+ }
+
+ // Every splitting function ensures that there are at least StackLimit
+ // bytes available below SP when the splitting prologue finishes.
+ // If the splitting function calls F, then F begins execution with
+ // at least StackLimit - callsize() bytes available.
+ // Check that every function behaves correctly with this amount
+ // of stack, following direct calls in order to piece together chains
+ // of non-splitting functions.
+ var ch chain
+ ch.limit = objabi.StackLimit - callsize(ctxt)
+ if objabi.GOARCH == "arm64" {
+ // need extra 8 bytes below SP to save FP
+ ch.limit -= 8
+ }
+
+ // Check every function, but do the nosplit functions in a first pass,
+ // to make the printed failure chains as short as possible.
+ for _, s := range ctxt.Textp {
+ if ldr.IsNoSplit(s) {
+ ch.sym = s
+ sc.check(&ch, 0)
+ }
+ }
+
+ for _, s := range ctxt.Textp {
+ if !ldr.IsNoSplit(s) {
+ ch.sym = s
+ sc.check(&ch, 0)
+ }
+ }
+}
+
+func (sc *stkChk) check(up *chain, depth int) int {
+ limit := up.limit
+ s := up.sym
+ ldr := sc.ldr
+ ctxt := sc.ctxt
+
+ // Don't duplicate work: only need to consider each
+ // function at top of safe zone once.
+ top := limit == objabi.StackLimit-callsize(ctxt)
+ if top {
+ if sc.done.Has(s) {
+ return 0
+ }
+ sc.done.Set(s)
+ }
+
+ if depth > 500 {
+ sc.ctxt.Errorf(s, "nosplit stack check too deep")
+ sc.broke(up, 0)
+ return -1
+ }
+
+ if ldr.AttrExternal(s) {
+ // external function.
+ // should never be called directly.
+ // onlyctxt.Diagnose the direct caller.
+ // TODO(mwhudson): actually think about this.
+ // TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack.
+ // See the trampolines in src/runtime/sys_darwin_$ARCH.go.
+ //if depth == 1 && ldr.SymType(s) != sym.SXREF && !ctxt.DynlinkingGo() &&
+ // ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin {
+ // Errorf(s, "call to external function")
+ //}
+ return -1
+ }
+ info := ldr.FuncInfo(s)
+ if !info.Valid() { // external function. see above.
+ return -1
+ }
+
+ if limit < 0 {
+ sc.broke(up, limit)
+ return -1
+ }
+
+ // morestack looks like it calls functions,
+ // but it switches the stack pointer first.
+ if s == sc.morestack {
+ return 0
+ }
+
+ var ch chain
+ ch.up = up
+
+ if !ldr.IsNoSplit(s) {
+ // Ensure we have enough stack to call morestack.
+ ch.limit = limit - callsize(ctxt)
+ ch.sym = sc.morestack
+ if sc.check(&ch, depth+1) < 0 {
+ return -1
+ }
+ if !top {
+ return 0
+ }
+ // Raise limit to allow frame.
+ locals := info.Locals()
+ limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize())
+ }
+
+ // Walk through sp adjustments in function, consuming relocs.
+ relocs := ldr.Relocs(s)
+ var ch1 chain
+ pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
+ ri := 0
+ for pcsp.Init(ldr.Data(info.Pcsp())); !pcsp.Done; pcsp.Next() {
+ // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
+
+ // Check stack size in effect for this span.
+ if int32(limit)-pcsp.Value < 0 {
+ sc.broke(up, int(int32(limit)-pcsp.Value))
+ return -1
+ }
+
+ // Process calls in this span.
+ for ; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if uint32(r.Off()) >= pcsp.NextPC {
+ break
+ }
+ t := r.Type()
+ switch {
+ case t.IsDirectCall():
+ ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
+ ch.sym = r.Sym()
+ if sc.check(&ch, depth+1) < 0 {
+ return -1
+ }
+
+ // Indirect call. Assume it is a call to a splitting function,
+ // so we have to make sure it can call morestack.
+ // Arrange the data structures to report both calls, so that
+ // if there is an error, stkprint shows all the steps involved.
+ case t == objabi.R_CALLIND:
+ ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
+ ch.sym = 0
+ ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue
+ ch1.up = &ch
+ ch1.sym = sc.morestack
+ if sc.check(&ch1, depth+2) < 0 {
+ return -1
+ }
+ }
+ }
+ }
+
+ return 0
+}
+
+func (sc *stkChk) broke(ch *chain, limit int) {
+ sc.ctxt.Errorf(ch.sym, "nosplit stack overflow")
+ sc.print(ch, limit)
+}
+
+func (sc *stkChk) print(ch *chain, limit int) {
+ ldr := sc.ldr
+ ctxt := sc.ctxt
+ var name string
+ if ch.sym != 0 {
+ name = ldr.SymName(ch.sym)
+ if ldr.IsNoSplit(ch.sym) {
+ name += " (nosplit)"
+ }
+ } else {
+ name = "function pointer"
+ }
+
+ if ch.up == nil {
+ // top of chain. ch.sym != 0.
+ if ldr.IsNoSplit(ch.sym) {
+ fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name)
+ } else {
+ fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name)
+ }
+ } else {
+ sc.print(ch.up, ch.limit+callsize(ctxt))
+ if !haslinkregister(ctxt) {
+ fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name)
+ }
+ }
+
+ if ch.limit != limit {
+ fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit)
+ }
+}
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n")
+ objabi.Flagprint(os.Stderr)
+ 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)
+ 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 || ldr.SymType(rs) == sym.SABIALIAS) {
+ 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")
+ }
+}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
new file mode 100644
index 0000000..f26d051
--- /dev/null
+++ b/src/cmd/link/internal/ld/link.go
@@ -0,0 +1,176 @@
+// 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/internal/sys"
+ "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
+
+ cgo_export_static map[string]bool
+ cgo_export_dynamic map[string]bool
+
+ 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
+}
+
+// The smallest possible offset from the hardware stack pointer to a local
+// variable on the stack. Architectures that use a link register save its value
+// on the stack in the function prologue and so always have a pointer between
+// the hardware stack pointer and the local variable area.
+func (ctxt *Link) FixedFrameSize() int64 {
+ switch ctxt.Arch.Family {
+ case sys.AMD64, sys.I386:
+ return 0
+ case sys.PPC64:
+ // PIC code on ppc64le requires 32 bytes of stack, and it's easier to
+ // just use that much stack always on ppc64x.
+ return int64(4 * ctxt.Arch.PtrSize)
+ default:
+ return int64(ctxt.Arch.PtrSize)
+ }
+}
+
+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.
+// Linker created symbols that are large, and shouldn't really live in the
+// heap can define a generator function, and their bytes can be generated
+// directly in the output mmap.
+//
+// Generator symbols shouldn't grow the symbol size, and might be called in
+// parallel in the future.
+//
+// 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..a9f4d87
--- /dev/null
+++ b/src/cmd/link/internal/ld/macho.go
@@ -0,0 +1,1528 @@
+// 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"
+ "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
+ 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
+)
+
+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 {
+ switch ctxt.Arch.Family {
+ default:
+ machoPlatform = PLATFORM_MACOS
+ if ctxt.LinkMode == LinkInternal {
+ // For lldb, must say LC_VERSION_MIN_MACOSX or else
+ // it won't know that this Mach-O binary is from OS X
+ // (could be iOS or WatchOS instead).
+ // Go on iOS uses linkmode=external, and linkmode=external
+ // adds this itself. So we only need this code for linkmode=internal
+ // and we can assume OS X.
+ //
+ // See golang.org/issues/12941.
+ //
+ // The version must be at least 10.9; see golang.org/issues/30488.
+ ml := newMachoLoad(ctxt.Arch, LC_VERSION_MIN_MACOSX, 2)
+ ml.data[0] = 10<<16 | 9<<8 | 0<<0 // OS X version 10.9.0
+ ml.data[1] = 10<<16 | 9<<8 | 0<<0 // SDK 10.9.0
+ }
+ case sys.ARM, sys.ARM64:
+ machoPlatform = PLATFORM_IOS
+ }
+ }
+
+ // 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)
+ }
+
+ // Do not export C symbols dynamically in plugins, as runtime C symbols like crosscall2
+ // are in pclntab and end up pointing at the host binary, breaking unwinding.
+ // See Issue #18190.
+ if ctxt.BuildMode == BuildModePlugin {
+ for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} {
+ s := ctxt.loader.Lookup(name, 0)
+ 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)
+ }
+ 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.RawSymName(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 objabi.GOARCH {
+ case "amd64":
+ ldr.SetSymExtname(s, n+"$INODE64")
+ }
+ case "readdir_r", "getfsstat":
+ switch objabi.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.RawSymName(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.
+ symstr.Addstring(strings.Replace(ldr.SymExtname(s), "·", ".", -1))
+
+ 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:] {
+ 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..77ee8a4
--- /dev/null
+++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go
@@ -0,0 +1,430 @@
+// 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:
+ 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 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..5a096f1
--- /dev/null
+++ b/src/cmd/link/internal/ld/main.go
@@ -0,0 +1,417 @@
+// 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/sys"
+ "cmd/link/internal/benchmark"
+ "flag"
+ "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:...")
+}
+
+// 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")
+ 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 = flag.String("extld", "", "use `linker` when linking in external mode")
+ flagExtldflags = flag.String("extldflags", "", "pass `flags` to external linker")
+ flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive")
+
+ 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("debugppc64textsize", 0, "debug PPC64 text section max")
+ 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)
+ }
+ }
+
+ final := gorootFinal()
+ addstrdata1(ctxt, "runtime/internal/sys.DefaultGoroot="+final)
+ addstrdata1(ctxt, "cmd/internal/objabi.defaultGOROOT="+final)
+
+ // TODO(matloob): define these above and then check flag values here
+ if ctxt.Arch.Family == sys.AMD64 && objabi.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(objabi.GOOS)
+ }
+
+ if !*flagAslr && ctxt.BuildMode != BuildModeCShared {
+ Errorf(nil, "-aslr=false is only allowed for -buildmode=c-shared")
+ usage()
+ }
+
+ checkStrictDups = *FlagStrictDups
+
+ 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 objabi.Fieldtrack_enabled != 0 {
+ bench.Start("fieldtrack")
+ fieldtrack(ctxt.Arch, ctxt.loader)
+ }
+
+ bench.Start("dwarfGenerateDebugInfo")
+ dwarfGenerateDebugInfo(ctxt)
+
+ bench.Start("callgraph")
+ ctxt.callgraph()
+
+ bench.Start("dostkcheck")
+ ctxt.dostkcheck()
+
+ 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")
+ setupdynexp(ctxt)
+ 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 {
+ panic(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/nooptcgolink_test.go b/src/cmd/link/internal/ld/nooptcgolink_test.go
new file mode 100644
index 0000000..4d2ff1a
--- /dev/null
+++ b/src/cmd/link/internal/ld/nooptcgolink_test.go
@@ -0,0 +1,37 @@
+// 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"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "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, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out"))
+ cmd.Dir = filepath.Join(runtime.GOROOT(), "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..530836e
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -0,0 +1,311 @@
+// 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 simlar 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
+}
+
+// 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
+ panic(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..9444b65
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_darwin.go
@@ -0,0 +1,47 @@
+// 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"
+)
+
+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
+ // 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),
+ }
+
+ _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store)))
+ if errno != 0 {
+ return errno
+ }
+
+ return nil
+}
+
+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.
+ syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(len(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..807fe24
--- /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.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
+
+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..6bf96bc
--- /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.
+
+// +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..6b40253
--- /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.
+
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!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..8c5666f
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_notdarwin.go
@@ -0,0 +1,9 @@
+// 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.
+
+// +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..e6643da
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_test.go
@@ -0,0 +1,99 @@
+// 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 (
+ "io/ioutil"
+ "os"
+ "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, err := ioutil.TempDir("", "TestMMap")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ 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..915c72b
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_windows.go
@@ -0,0 +1,67 @@
+// 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 (
+ "reflect"
+ "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 := (*reflect.SliceHeader)(unsafe.Pointer(&out.buf))
+ bufHdr.Data = 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)
+ }
+ 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..72bf33e
--- /dev/null
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -0,0 +1,996 @@
+// 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"
+ "os"
+ "path/filepath"
+)
+
+// pclntab holds the state needed for pclntab generation.
+type pclntab struct {
+ // The size of the func object in the runtime.
+ funcSize uint32
+
+ // 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 responsibilty to save they 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 := &pclntab{
+ // This is the size of the _func object in runtime/runtime2.go.
+ funcSize: uint32(ctxt.Arch.PtrSize + 9*4),
+ }
+
+ // Gather some basic stats and info.
+ seenCUs := make(map[*sym.CompilationUnit]struct{})
+ prevSect := ldr.SymSect(ctxt.Textp[0])
+ 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
+ ss := ldr.SymSect(s)
+ if ss != prevSect {
+ // With multiple text sections, the external linker may
+ // insert functions between the sections, which are not
+ // known by Go. This leaves holes in the PC range covered
+ // by the func table. We need to generate an entry to mark
+ // the hole.
+ state.nfunc++
+ prevSect = ss
+ }
+
+ // 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 does not have a live variable set at the deferreturn
+ // call itself. Instead it has one identified by the
+ // resumption point immediately preceding the deferreturn.
+ // The wasm code has a R_ADDR relocation which is used to
+ // set the resumption point to PC_B.
+ 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.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64:
+ // no change
+ case sys.RISCV64:
+ // TODO(jsing): The JALR instruction is marked with
+ // R_CALLRISCV, whereas the actual reloc is currently
+ // one instruction earlier starting with the AUIPC.
+ deferreturn -= 4
+ 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)
+ ninl := fi.NumInlTree()
+ for i := 0; i < int(ninl); i++ {
+ call := fi.InlTree(i)
+ val := call.File
+ nameoff, ok := nameOffsets[call.Func]
+ if !ok {
+ panic("couldn't find function name offset")
+ }
+
+ inlTreeSym.SetUint16(arch, int64(i*20+0), uint16(call.Parent))
+ inlFunc := ldr.FuncInfo(call.Func)
+
+ var funcID objabi.FuncID
+ if inlFunc.Valid() {
+ funcID = inlFunc.FuncID()
+ }
+ inlTreeSym.SetUint8(arch, int64(i*20+2), uint8(funcID))
+
+ // byte 3 is unused
+ inlTreeSym.SetUint32(arch, int64(i*20+4), uint32(val))
+ inlTreeSym.SetUint32(arch, int64(i*20+8), uint32(call.Line))
+ inlTreeSym.SetUint32(arch, int64(i*20+12), uint32(nameoff))
+ inlTreeSym.SetUint32(arch, int64(i*20+16), uint32(call.ParentPC))
+ }
+ 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) {
+ writeHeader := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ 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.
+ header.SetUint32(ctxt.Arch, 0, 0xfffffffa)
+ 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))
+ 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)
+ }
+
+ size := int64(8 + 7*ctxt.Arch.PtrSize)
+ state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
+}
+
+// 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)
+
+ // Write the null terminated strings.
+ writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
+ symtab := ctxt.loader.MakeSymbolUpdater(s)
+ for s, off := range nameOffsets {
+ symtab.AddStringAt(int64(off), ctxt.loader.SymName(s))
+ }
+ }
+
+ // Loop through the CUs, and calculate the size needed.
+ var size int64
+ walkFuncs(ctxt, funcs, func(s loader.Sym) {
+ nameOffsets[s] = uint32(size)
+ size += int64(ctxt.loader.SymNameLen(s)) + 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.
+ //
+ // TOOD: 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{}{}
+ }
+ }
+ for _, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ pcSyms := []loader.Sym{fi.Pcsp(), fi.Pcfile(), fi.Pcline()}
+ for _, pcSym := range pcSyms {
+ saveOffset(pcSym)
+ }
+ for _, pcSym := range fi.Pcdata() {
+ saveOffset(pcSym)
+ }
+ if fi.NumInlTree() > 0 {
+ saveOffset(fi.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(fi loader.FuncInfo) uint32 {
+ if !fi.Valid() {
+ return 0
+ }
+ numPCData := uint32(len(fi.Pcdata()))
+ if fi.NumInlTree() > 0 {
+ if numPCData < objabi.PCDATA_InlTreeIndex+1 {
+ numPCData = objabi.PCDATA_InlTreeIndex + 1
+ }
+ }
+ return numPCData
+}
+
+// Helper types for iterating pclntab.
+type pclnSetAddr func(*loader.SymbolBuilder, *sys.Arch, int64, loader.Sym, int64) int64
+type pclnSetUint func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64
+
+// generateFunctab creates the runtime.functab
+//
+// runtime.functab contains two things:
+//
+// - pc->func look up table.
+// - array of func objects, interleaved with pcdata and funcdata
+//
+// Because of timing in the linker, generating this table takes two passes.
+// The first pass is executed early in the link, and it creates any needed
+// relocations to layout the data. The pieces that need relocations are:
+// 1) the PC->func table.
+// 2) The entry points in the func objects.
+// 3) The funcdata.
+// (1) and (2) are handled in walkPCToFunc. (3) is handled in walkFuncdata.
+//
+// After relocations, once we know where to write things in the output buffer,
+// we execute the second pass, which is actually writing the data.
+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)
+
+ // If we are internally linking a static executable, the function addresses
+ // are known, so we can just use them instead of emitting relocations. For
+ // other cases we still need to emit relocations.
+ //
+ // This boolean just helps us figure out which callback to use.
+ useSymValue := ctxt.IsExe() && ctxt.IsInternal()
+
+ writePcln := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+
+ // Create our callbacks.
+ var setAddr pclnSetAddr
+ if useSymValue {
+ // We need to write the offset.
+ setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
+ if v := ldr.SymValue(tgt); v != 0 {
+ s.SetUint(arch, off, uint64(v+add))
+ }
+ return 0
+ }
+ } else {
+ // We already wrote relocations.
+ setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 { return 0 }
+ }
+
+ // Write the data.
+ writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, (*loader.SymbolBuilder).SetUint)
+ writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets)
+ state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, (*loader.SymbolBuilder).SetUint)
+ }
+
+ state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln)
+
+ // Create the relocations we need.
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(state.pclntab)
+
+ var setAddr pclnSetAddr
+ if useSymValue {
+ // If we should use the symbol value, and we don't have one, write a relocation.
+ setAddr = func(sb *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
+ if v := ldr.SymValue(tgt); v == 0 {
+ sb.SetAddrPlus(arch, off, tgt, add)
+ }
+ return 0
+ }
+ } else {
+ // If we're externally linking, write a relocation.
+ setAddr = (*loader.SymbolBuilder).SetAddrPlus
+ }
+ setUintNOP := func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64 { return 0 }
+ writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, setUintNOP)
+ if !useSymValue {
+ // Generate relocations for funcdata when externally linking.
+ state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, setUintNOP)
+ }
+}
+
+// funcData returns the funcdata and offsets for the FuncInfo.
+// The funcdata and offsets are written into runtime.functab after each func
+// object. This is a helper function to make querying the FuncInfo object
+// cleaner.
+//
+// Note, the majority of fdOffsets are 0, meaning there is no offset between
+// the compiler's generated symbol, and what the runtime needs. They are
+// plumbed through for no loss of generality.
+//
+// NB: Preload must be called on the FuncInfo before calling.
+// NB: fdSyms and fdOffs are used as scratch space.
+func funcData(fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym, fdOffs []int64) ([]loader.Sym, []int64) {
+ fdSyms, fdOffs = fdSyms[:0], fdOffs[:0]
+ if fi.Valid() {
+ numOffsets := int(fi.NumFuncdataoff())
+ for i := 0; i < numOffsets; i++ {
+ fdOffs = append(fdOffs, fi.Funcdataoff(i))
+ }
+ fdSyms = fi.Funcdata(fdSyms)
+ if fi.NumInlTree() > 0 {
+ if len(fdSyms) < objabi.FUNCDATA_InlTree+1 {
+ fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...)
+ fdOffs = append(fdOffs, make([]int64, objabi.FUNCDATA_InlTree+1-len(fdOffs))...)
+ }
+ fdSyms[objabi.FUNCDATA_InlTree] = inlSym
+ }
+ }
+ return fdSyms, fdOffs
+}
+
+// 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
+ // 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*ctxt.Arch.PtrSize + ctxt.Arch.PtrSize)
+
+ // Now find the space for the func objects. We do this in a running manner,
+ // so that we can find individual starting locations, and because funcdata
+ // requires alignment.
+ for i, s := range funcs {
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
+ startLocations[i] = uint32(size)
+ fi := ldr.FuncInfo(s)
+ size += int64(state.funcSize)
+ if fi.Valid() {
+ fi.Preload()
+ numFuncData := int(fi.NumFuncdataoff())
+ if fi.NumInlTree() > 0 {
+ if numFuncData < objabi.FUNCDATA_InlTree+1 {
+ numFuncData = objabi.FUNCDATA_InlTree + 1
+ }
+ }
+ size += int64(numPCData(fi) * 4)
+ if numFuncData > 0 { // Func data is aligned.
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
+ }
+ size += int64(numFuncData * ctxt.Arch.PtrSize)
+ }
+ }
+
+ return size, startLocations
+}
+
+// writePcToFunc writes the PC->func lookup table.
+// This function walks the pc->func lookup table, executing callbacks
+// to generate relocations and writing the values for the table.
+func writePcToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) {
+ ldr := ctxt.loader
+ var prevFunc loader.Sym
+ prevSect := ldr.SymSect(funcs[0])
+ funcIndex := 0
+ for i, s := range funcs {
+ if thisSect := ldr.SymSect(s); thisSect != prevSect {
+ // With multiple text sections, there may be a hole here in the
+ // address space. We use an invalid funcoff value to mark the hole.
+ // See also runtime/symtab.go:findfunc
+ prevFuncSize := int64(ldr.SymSize(prevFunc))
+ setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
+ setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), ^uint64(0))
+ funcIndex++
+ prevSect = thisSect
+ }
+ prevFunc = s
+ // TODO: We don't actually need these relocations, provided we go to a
+ // module->func look-up-table like we do for filenames. We could have a
+ // single relocation for the module, and have them all laid out as
+ // offsets from the beginning of that module.
+ setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), s, 0)
+ setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), uint64(startLocations[i]))
+ funcIndex++
+
+ // Write the entry location.
+ setAddr(sb, ctxt.Arch, int64(startLocations[i]), s, 0)
+ }
+
+ // Final entry of table is just end pc.
+ setAddr(sb, ctxt.Arch, int64(funcIndex)*2*int64(ctxt.Arch.PtrSize), prevFunc, ldr.SymSize(prevFunc))
+}
+
+// writeFuncData writes the funcdata tables.
+//
+// This function executes a callback for each funcdata needed in
+// runtime.functab. It should be called once for internally linked static
+// binaries, or twice (once to generate the needed relocations) for other
+// build modes.
+//
+// Note the output of this function is interwoven with writeFuncs, but this is
+// a separate function, because it's needed in different passes in
+// generateFunctab.
+func (state *pclntab) writeFuncData(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) {
+ ldr := ctxt.loader
+ funcdata, funcdataoff := []loader.Sym{}, []int64{}
+ for i, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ // funcdata, must be pointer-aligned and we're only int32-aligned.
+ // Missing funcdata will be 0 (nil pointer).
+ funcdata, funcdataoff := funcData(fi, inlSyms[s], funcdata, funcdataoff)
+ if len(funcdata) > 0 {
+ off := int64(startLocations[i] + state.funcSize + numPCData(fi)*4)
+ off = Rnd(off, int64(ctxt.Arch.PtrSize))
+ for j := range funcdata {
+ dataoff := off + int64(ctxt.Arch.PtrSize*j)
+ if funcdata[j] == 0 {
+ setUint(sb, ctxt.Arch, dataoff, uint64(funcdataoff[j]))
+ continue
+ }
+ // TODO: Does this need deduping?
+ setAddr(sb, ctxt.Arch, dataoff, funcdata[j], funcdataoff[j])
+ }
+ }
+ }
+}
+
+// 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", sym.SymVerABIInternal)
+ funcdata, funcdataoff := []loader.Sym{}, []int64{}
+
+ // Write the individual func objects.
+ for i, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if fi.Valid() {
+ fi.Preload()
+ }
+
+ // Note we skip the space for the entry value -- that's handled inn
+ // walkPCToFunc. We don't write it here, because it might require a
+ // relocation.
+ off := startLocations[i] + uint32(ctxt.Arch.PtrSize) // entry
+
+ // name int32
+ nameoff, ok := nameOffsets[s]
+ if !ok {
+ panic("couldn't find function name offset")
+ }
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
+
+ // args int32
+ // TODO: Move into funcinfo.
+ args := uint32(0)
+ if fi.Valid() {
+ args = uint32(fi.Args())
+ }
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), args))
+
+ // deferreturn
+ deferreturn := computeDeferReturn(ctxt, deferReturnSym, s)
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), deferreturn))
+
+ // pcdata
+ if fi.Valid() {
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcsp()))))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcfile()))))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcline()))))
+ } else {
+ off += 12
+ }
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(numPCData(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 = uint32(sb.SetUint32(ctxt.Arch, int64(off), cuIdx))
+
+ // funcID uint8
+ var funcID objabi.FuncID
+ if fi.Valid() {
+ funcID = fi.FuncID()
+ }
+ off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
+
+ off += 2 // pad
+
+ // nfuncdata must be the final entry.
+ funcdata, funcdataoff = funcData(fi, 0, funcdata, funcdataoff)
+ off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
+
+ // Output the pcdata.
+ if fi.Valid() {
+ for j, pcSym := range fi.Pcdata() {
+ sb.SetUint32(ctxt.Arch, int64(off+uint32(j*4)), uint32(ldr.SymValue(pcSym)))
+ }
+ if fi.NumInlTree() > 0 {
+ sb.SetUint32(ctxt.Arch, int64(off+objabi.PCDATA_InlTreeIndex*4), uint32(ldr.SymValue(fi.Pcinline())))
+ }
+ }
+ }
+}
+
+// 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 := objabi.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] == '\\') {
+ return filepath.ToSlash(filepath.Join(gorootFinal(), 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..5edaf54
--- /dev/null
+++ b/src/cmd/link/internal/ld/pe.go
@@ -0,0 +1,1628 @@
+// 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://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/pe"
+ "encoding/binary"
+ "fmt"
+ "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
+}
+
+const (
+ PEBASE = 0x00400000
+)
+
+var (
+ // 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_MEM_EXECUTE = 0x20000000
+ IMAGE_SCN_MEM_READ = 0x40000000
+ IMAGE_SCN_MEM_WRITE = 0x80000000
+ IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
+ IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
+ IMAGE_SCN_ALIGN_32BYTES = 0x600000
+)
+
+// 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_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-PEBASE != uint64(sect.virtualAddress) {
+ Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-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,
+ virtualSize: uint32(sectsize),
+ virtualAddress: f.nextSectOffset,
+ pointerToRawData: f.nextFileOffset,
+ }
+ f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN))
+ if filesize > 0 {
+ sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN))
+ f.nextFileOffset += sect.sizeOfRawData
+ }
+ 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_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
+ 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
+ switch objabi.GOARCH {
+ default:
+ Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", objabi.GOARCH)
+ case "386":
+ size = 4
+ case "amd64":
+ size = 8
+ case "arm":
+ size = 4
+ }
+ sect := f.addSection(".ctors", size, size)
+ sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+ 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 objabi.GOARCH {
+ case "386", "arm":
+ ctxt.Out.Write32(uint32(addr))
+ case "amd64":
+ 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
+ }
+ nrelocs := 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 := int32(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 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 %d/%d to %s", r.Type(), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ nrelocs++
+ }
+ }
+ sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
+ return nrelocs
+ }
+
+ 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)
+ }
+
+ f.ctorsSect.emitRelocations(ctxt.Out, func() int {
+ dottext := ldr.Lookup(".text", 0)
+ ctxt.Out.Write32(0)
+ ctxt.Out.Write32(uint32(ldr.SymDynid(dottext)))
+ switch objabi.GOARCH {
+ default:
+ ctxt.Errorf(dottext, "unknown architecture for PE: %q\n", objabi.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)
+ }
+ 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
+}
+
+// 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)) {
+ name = "_" + 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.RawSymName(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)
+ }
+ }
+}
+
+// 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
+ }
+
+ 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 = PEBASE
+ oh.ImageBase = 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.
+ switch ctxt.Arch.Family {
+ case sys.AMD64, sys.I386:
+ if ctxt.BuildMode == BuildModePIE {
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+ oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+ }
+ case sys.ARM:
+ 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, ctrlhandler thread, and profileloop thread. For
+ // these, it must be greater than the stack size assumed by
+ // externalthreadhandler.
+ //
+ // 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
+
+ switch ctxt.Arch.Family {
+ // 64-bit architectures
+ case sys.AMD64:
+ pe64 = 1
+ var oh64 pe.OptionalHeader64
+ l = binary.Size(&oh64)
+
+ // 32-bit architectures
+ default:
+ 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
+ }
+
+ 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) - 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()
+ rs = ldr.ResolveABIAlias(rs)
+ if rs == 0 {
+ continue
+ }
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+
+ switch r.Type() {
+ default:
+ case objabi.R_ADDR:
+ rt.addentry(ldr, s, &r)
+ }
+ }
+}
+
+func addPEBaseReloc(ctxt *Link) {
+ // Arm does not work without base relocation table.
+ // 386 and amd64 will only require the table for BuildModePIE.
+ switch ctxt.Arch.Family {
+ default:
+ return
+ case sys.I386, sys.AMD64:
+ if ctxt.BuildMode != BuildModePIE {
+ return
+ }
+ case sys.ARM:
+ }
+
+ 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) {
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
+ case sys.AMD64, sys.I386, sys.ARM:
+ }
+
+ t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length))
+ t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | 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/sym.go b/src/cmd/link/internal/ld/sym.go
new file mode 100644
index 0000000..7548972
--- /dev/null
+++ b/src/cmd/link/internal/ld/sym.go
@@ -0,0 +1,116 @@
+// 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"
+ "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 objabi.GOARCH != arch.Name {
+ log.Fatalf("invalid objabi.GOARCH %s (want %s)", objabi.GOARCH, arch.Name)
+ }
+
+ AtExit(func() {
+ if nerrors > 0 {
+ ctxt.Out.Close()
+ 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..f54cf9e
--- /dev/null
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -0,0 +1,832 @@
+// 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"
+ "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)
+
+ // 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)
+ 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.RawSymName(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
+ }
+ off = t.SetUint(ctxt.Arch, off, sect.Vaddr-textbase)
+ off = t.SetUint(ctxt.Arch, off, sect.Length)
+ if n == 0 {
+ s := ldr.Lookup("runtime.text", 0)
+ if s == 0 {
+ ctxt.Errorf(s, "Unable to find symbol runtime.text\n")
+ }
+ off = t.SetAddr(ctxt.Arch, off, s)
+
+ } else {
+ s := ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+ if s == 0 {
+ ctxt.Errorf(s, "Unable to find symbol runtime.text.%d\n", n)
+ }
+ off = t.SetAddr(ctxt.Arch, off, s)
+ }
+ 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.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)
+ symtype = s.Sym()
+
+ s = ldr.CreateSymForUpdate("typerel.*", 0)
+ s.SetType(sym.STYPERELRO)
+ s.SetSize(0)
+ symtyperel = s.Sym()
+ } else {
+ s = ldr.CreateSymForUpdate("type.*", 0)
+ s.SetType(sym.STYPE)
+ s.SetSize(0)
+ 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.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)
+ )
+
+ var symgofuncrel loader.Sym
+ if !ctxt.DynlinkingGo() {
+ if ctxt.UseRelro() {
+ symgofuncrel = groupSym("go.funcrel.*", sym.SGOFUNCRELRO)
+ } else {
+ symgofuncrel = symgofunc
+ }
+ }
+
+ 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.
+ 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) != "" {
+ 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, "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)
+ }
+ }
+
+ case strings.HasPrefix(name, "go.importpath.") && ctxt.UseRelro():
+ // Keep go.importpath symbols in the same section as types and
+ // names, as they can be referred to by a section offset.
+ symGroupType[s] = sym.STYPERELRO
+
+ case strings.HasPrefix(name, "go.string."):
+ symGroupType[s] = sym.SGOSTRING
+ ldr.SetAttrNotInSymbolTable(s, true)
+ ldr.SetCarrierSym(s, symgostring)
+
+ case strings.HasPrefix(name, "runtime.gcbits."):
+ 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 symgofuncrel != 0 {
+ 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,
+ strings.HasSuffix(name, ".opendefer"):
+ symGroupType[s] = sym.SGOFUNC
+ ldr.SetAttrNotInSymbolTable(s, true)
+ ldr.SetCarrierSym(s, symgofunc)
+ align := int32(4)
+ if a := ldr.SymAlign(s); a < align {
+ ldr.SetSymAlign(s, align)
+ } else {
+ align = a
+ }
+ liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1)
+ }
+ }
+
+ 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.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))
+
+ if ctxt.IsAIX() && ctxt.IsExternal() {
+ // Add R_XCOFFREF relocation to prevent ld's garbage collection of
+ // runtime.rodata, runtime.erodata and runtime.epclntab.
+ addRef := func(name string) {
+ r, _ := moduledata.AddRel(objabi.R_XCOFFREF)
+ r.SetSym(ldr.Lookup(name, 0))
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ }
+ addRef("runtime.rodata")
+ addRef("runtime.erodata")
+ addRef("runtime.epclntab")
+ }
+
+ // 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)
+}
diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go
new file mode 100644
index 0000000..f68de8f
--- /dev/null
+++ b/src/cmd/link/internal/ld/target.go
@@ -0,0 +1,187 @@
+// 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) 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) mustSetHeadType() {
+ if t.HeadType == objabi.Hunknown {
+ panic("HeadType is not set")
+ }
+}
+
+//
+// MISC
+//
+
+func (t *Target) IsBigEndian() bool {
+ return t.Arch.ByteOrder == binary.BigEndian
+}
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..9a8dfbc
--- /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..52ee2e3
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go
@@ -0,0 +1,23 @@
+// 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
+
+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
+}
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/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..9228ed1
--- /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.Errof 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..ba818ea
--- /dev/null
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -0,0 +1,1809 @@
+// 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"
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "encoding/binary"
+ "fmt"
+ "io/ioutil"
+ "math/bits"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+)
+
+// 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
+ XCOFFSECTALIGN int64 = 32 // base on dump -o
+
+ // 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
+ csectSize 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.csectSize = 0
+}
+
+// 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
+ aux.Xscnlenlo = uint32(currSymSrcFile.csectSize & 0xFFFFFFFF)
+ aux.Xscnlenhi = uint32(currSymSrcFile.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)))
+ }
+ }
+
+ }
+ }
+
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(ldr.SymExtname(x))),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nscnum: f.getXCOFFscnum(ldr.SymSect(x)),
+ Ntype: SYM_TYPE_FUNC,
+ Nnumaux: 2,
+ }
+
+ if ldr.SymVersion(x) != 0 || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
+ s.Nsclass = C_HIDEXT
+ }
+
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ // Update current csect size
+ currSymSrcFile.csectSize += ldr.SymSize(x)
+
+ // 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.SymVersion(x) != 0 || 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_EXTRA_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 := ldr.ResolveABIAlias(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() {
+ if *FlagD {
+ // All XCOFF files have dynamic symbols because of the syscalls.
+ Exitf("-d is not available on AIX")
+ }
+ 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.SymVersion(s) != 0 { // sanity check
+ panic("cgo_export on non-version 0 symbol")
+ }
+
+ if ldr.SymType(s) == sym.STEXT || ldr.SymType(s) == sym.SABIALIAS {
+ // 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.
+ //
+ // CgoExport attribute should only be set on a version 0
+ // symbol, which can be TEXT or ABIALIAS.
+ // (before, setupdynexp copies the attribute from the
+ // alias to the aliased. Now we are before setupdynexp.)
+ 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(Funcalign)))
+ 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.SymVersion(s) != 0 {
+ continue // Only export version 0 symbols. See the comment in doxcoff.
+ }
+
+ // 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 := ioutil.WriteFile(fname, buf.Bytes(), 0666)
+ if err != nil {
+ Errorf(nil, "WriteFile %s failed: %v", fname, err)
+ }
+
+ return fname
+}
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go
new file mode 100644
index 0000000..c698874
--- /dev/null
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -0,0 +1,1100 @@
+// 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 loadelf implements an ELF file reader.
+package loadelf
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "log"
+ "strings"
+)
+
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+http://code.swtch.com/plan9port/src/tip/src/libmach/
+
+ Copyright © 2004 Russ Cox.
+ Portions Copyright © 2008-2010 Google Inc.
+ Portions Copyright © 2010 The Go Authors.
+
+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.
+*/
+
+const (
+ SHT_ARM_ATTRIBUTES = 0x70000003
+)
+
+type ElfSect struct {
+ name string
+ nameoff uint32
+ type_ elf.SectionType
+ flags elf.SectionFlag
+ addr uint64
+ off uint64
+ size uint64
+ link uint32
+ info uint32
+ align uint64
+ entsize uint64
+ base []byte
+ readOnlyMem bool // Is this section in readonly memory?
+ sym loader.Sym
+}
+
+type ElfObj struct {
+ f *bio.Reader
+ base int64 // offset in f where ELF begins
+ length int64 // length of ELF
+ is64 int
+ name string
+ e binary.ByteOrder
+ sect []ElfSect
+ nsect uint
+ nsymtab int
+ symtab *ElfSect
+ symstr *ElfSect
+ type_ uint32
+ machine uint32
+ version uint32
+ entry uint64
+ phoff uint64
+ shoff uint64
+ flags uint32
+ ehsize uint32
+ phentsize uint32
+ phnum uint32
+ shentsize uint32
+ shnum uint32
+ shstrndx uint32
+}
+
+type ElfSym struct {
+ name string
+ value uint64
+ size uint64
+ bind elf.SymBind
+ type_ elf.SymType
+ other uint8
+ shndx elf.SectionIndex
+ sym loader.Sym
+}
+
+const (
+ TagFile = 1
+ TagCPUName = 4
+ TagCPURawName = 5
+ TagCompatibility = 32
+ TagNoDefaults = 64
+ TagAlsoCompatibleWith = 65
+ TagABIVFPArgs = 28
+)
+
+type elfAttribute struct {
+ tag uint64
+ sval string
+ ival uint64
+}
+
+type elfAttributeList struct {
+ data []byte
+ err error
+}
+
+func (a *elfAttributeList) string() string {
+ if a.err != nil {
+ return ""
+ }
+ nul := bytes.IndexByte(a.data, 0)
+ if nul < 0 {
+ a.err = io.EOF
+ return ""
+ }
+ s := string(a.data[:nul])
+ a.data = a.data[nul+1:]
+ return s
+}
+
+func (a *elfAttributeList) uleb128() uint64 {
+ if a.err != nil {
+ return 0
+ }
+ v, size := binary.Uvarint(a.data)
+ a.data = a.data[size:]
+ return v
+}
+
+// Read an elfAttribute from the list following the rules used on ARM systems.
+func (a *elfAttributeList) armAttr() elfAttribute {
+ attr := elfAttribute{tag: a.uleb128()}
+ switch {
+ case attr.tag == TagCompatibility:
+ attr.ival = a.uleb128()
+ attr.sval = a.string()
+
+ case attr.tag == TagNoDefaults: // Tag_nodefaults has no argument
+
+ case attr.tag == TagAlsoCompatibleWith:
+ // Not really, but we don't actually care about this tag.
+ attr.sval = a.string()
+
+ // Tag with string argument
+ case attr.tag == TagCPUName || attr.tag == TagCPURawName || (attr.tag >= 32 && attr.tag&1 != 0):
+ attr.sval = a.string()
+
+ default: // Tag with integer argument
+ attr.ival = a.uleb128()
+ }
+ return attr
+}
+
+func (a *elfAttributeList) done() bool {
+ if a.err != nil || len(a.data) == 0 {
+ return true
+ }
+ return false
+}
+
+// Look for the attribute that indicates the object uses the hard-float ABI (a
+// file-level attribute with tag Tag_VFP_arch and value 1). Unfortunately the
+// format used means that we have to parse all of the file-level attributes to
+// find the one we are looking for. This format is slightly documented in "ELF
+// for the ARM Architecture" but mostly this is derived from reading the source
+// to gold and readelf.
+func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags uint32, err error) {
+ found = false
+ if data[0] != 'A' {
+ return false, 0, fmt.Errorf(".ARM.attributes has unexpected format %c\n", data[0])
+ }
+ data = data[1:]
+ for len(data) != 0 {
+ sectionlength := e.Uint32(data)
+ sectiondata := data[4:sectionlength]
+ data = data[sectionlength:]
+
+ nulIndex := bytes.IndexByte(sectiondata, 0)
+ if nulIndex < 0 {
+ return false, 0, fmt.Errorf("corrupt .ARM.attributes (section name not NUL-terminated)\n")
+ }
+ name := string(sectiondata[:nulIndex])
+ sectiondata = sectiondata[nulIndex+1:]
+
+ if name != "aeabi" {
+ continue
+ }
+ for len(sectiondata) != 0 {
+ subsectiontag, sz := binary.Uvarint(sectiondata)
+ subsectionsize := e.Uint32(sectiondata[sz:])
+ subsectiondata := sectiondata[sz+4 : subsectionsize]
+ sectiondata = sectiondata[subsectionsize:]
+
+ if subsectiontag != TagFile {
+ continue
+ }
+ attrList := elfAttributeList{data: subsectiondata}
+ for !attrList.done() {
+ attr := attrList.armAttr()
+ if attr.tag == TagABIVFPArgs && attr.ival == 1 {
+ found = true
+ ehdrFlags = 0x5000402 // has entry point, Version5 EABI, hard-float ABI
+ }
+ }
+ if attrList.err != nil {
+ return false, 0, fmt.Errorf("could not parse .ARM.attributes\n")
+ }
+ }
+ }
+ return found, ehdrFlags, nil
+}
+
+// Load loads the ELF file pn from f.
+// Symbols are installed into the loader, and a slice of the text symbols is returned.
+//
+// On ARM systems, Load will attempt to determine what ELF header flags to
+// emit by scanning the attributes in the ELF file being loaded. The
+// parameter initEhdrFlags contains the current header flags for the output
+// object, and the returned ehdrFlags contains what this Load function computes.
+// TODO: find a better place for this logic.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []loader.Sym, ehdrFlags uint32, err error) {
+ newSym := func(name string, version int) loader.Sym {
+ return l.CreateStaticSym(name)
+ }
+ lookup := func(name string, version int) loader.Sym {
+ return l.LookupOrCreateSym(name, version)
+ }
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) {
+ return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ base := f.Offset()
+
+ var hdrbuf [64]byte
+ if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ var e binary.ByteOrder
+ switch elf.Data(hdrbuf[elf.EI_DATA]) {
+ case elf.ELFDATA2LSB:
+ e = binary.LittleEndian
+
+ case elf.ELFDATA2MSB:
+ e = binary.BigEndian
+
+ default:
+ return errorf("malformed elf file, unknown header")
+ }
+
+ hdr := new(elf.Header32)
+ binary.Read(bytes.NewReader(hdrbuf[:]), e, hdr)
+
+ if string(hdr.Ident[:elf.EI_CLASS]) != elf.ELFMAG {
+ return errorf("malformed elf file, bad header")
+ }
+
+ // read header
+ elfobj := new(ElfObj)
+
+ elfobj.e = e
+ elfobj.f = f
+ elfobj.base = base
+ elfobj.length = length
+ elfobj.name = pn
+
+ is64 := 0
+ class := elf.Class(hdrbuf[elf.EI_CLASS])
+ if class == elf.ELFCLASS64 {
+ is64 = 1
+ hdr := new(elf.Header64)
+ binary.Read(bytes.NewReader(hdrbuf[:]), e, hdr)
+ elfobj.type_ = uint32(hdr.Type)
+ elfobj.machine = uint32(hdr.Machine)
+ elfobj.version = hdr.Version
+ elfobj.entry = hdr.Entry
+ elfobj.phoff = hdr.Phoff
+ elfobj.shoff = hdr.Shoff
+ elfobj.flags = hdr.Flags
+ elfobj.ehsize = uint32(hdr.Ehsize)
+ elfobj.phentsize = uint32(hdr.Phentsize)
+ elfobj.phnum = uint32(hdr.Phnum)
+ elfobj.shentsize = uint32(hdr.Shentsize)
+ elfobj.shnum = uint32(hdr.Shnum)
+ elfobj.shstrndx = uint32(hdr.Shstrndx)
+ } else {
+ elfobj.type_ = uint32(hdr.Type)
+ elfobj.machine = uint32(hdr.Machine)
+ elfobj.version = hdr.Version
+ elfobj.entry = uint64(hdr.Entry)
+ elfobj.phoff = uint64(hdr.Phoff)
+ elfobj.shoff = uint64(hdr.Shoff)
+ elfobj.flags = hdr.Flags
+ elfobj.ehsize = uint32(hdr.Ehsize)
+ elfobj.phentsize = uint32(hdr.Phentsize)
+ elfobj.phnum = uint32(hdr.Phnum)
+ elfobj.shentsize = uint32(hdr.Shentsize)
+ elfobj.shnum = uint32(hdr.Shnum)
+ elfobj.shstrndx = uint32(hdr.Shstrndx)
+ }
+
+ elfobj.is64 = is64
+
+ if v := uint32(hdrbuf[elf.EI_VERSION]); v != elfobj.version {
+ return errorf("malformed elf version: got %d, want %d", v, elfobj.version)
+ }
+
+ if elf.Type(elfobj.type_) != elf.ET_REL {
+ return errorf("elf but not elf relocatable object")
+ }
+
+ mach := elf.Machine(elfobj.machine)
+ switch arch.Family {
+ default:
+ return errorf("elf %s unimplemented", arch.Name)
+
+ case sys.MIPS:
+ if mach != elf.EM_MIPS || class != elf.ELFCLASS32 {
+ return errorf("elf object but not mips")
+ }
+
+ case sys.MIPS64:
+ if mach != elf.EM_MIPS || class != elf.ELFCLASS64 {
+ return errorf("elf object but not mips64")
+ }
+
+ case sys.ARM:
+ if e != binary.LittleEndian || mach != elf.EM_ARM || class != elf.ELFCLASS32 {
+ return errorf("elf object but not arm")
+ }
+
+ case sys.AMD64:
+ if e != binary.LittleEndian || mach != elf.EM_X86_64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not amd64")
+ }
+
+ case sys.ARM64:
+ if e != binary.LittleEndian || mach != elf.EM_AARCH64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not arm64")
+ }
+
+ case sys.I386:
+ if e != binary.LittleEndian || mach != elf.EM_386 || class != elf.ELFCLASS32 {
+ return errorf("elf object but not 386")
+ }
+
+ case sys.PPC64:
+ if mach != elf.EM_PPC64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not ppc64")
+ }
+
+ case sys.RISCV64:
+ if mach != elf.EM_RISCV || class != elf.ELFCLASS64 {
+ return errorf("elf object but not riscv64")
+ }
+
+ case sys.S390X:
+ if mach != elf.EM_S390 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not s390x")
+ }
+ }
+
+ // load section list into memory.
+ elfobj.sect = make([]ElfSect, elfobj.shnum)
+
+ elfobj.nsect = uint(elfobj.shnum)
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ f.MustSeek(int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0)
+ sect := &elfobj.sect[i]
+ if is64 != 0 {
+ var b elf.Section64
+ if err := binary.Read(f, e, &b); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ sect.nameoff = b.Name
+ sect.type_ = elf.SectionType(b.Type)
+ sect.flags = elf.SectionFlag(b.Flags)
+ sect.addr = b.Addr
+ sect.off = b.Off
+ sect.size = b.Size
+ sect.link = b.Link
+ sect.info = b.Info
+ sect.align = b.Addralign
+ sect.entsize = b.Entsize
+ } else {
+ var b elf.Section32
+
+ if err := binary.Read(f, e, &b); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ sect.nameoff = b.Name
+ sect.type_ = elf.SectionType(b.Type)
+ sect.flags = elf.SectionFlag(b.Flags)
+ sect.addr = uint64(b.Addr)
+ sect.off = uint64(b.Off)
+ sect.size = uint64(b.Size)
+ sect.link = b.Link
+ sect.info = b.Info
+ sect.align = uint64(b.Addralign)
+ sect.entsize = uint64(b.Entsize)
+ }
+ }
+
+ // read section string table and translate names
+ if elfobj.shstrndx >= uint32(elfobj.nsect) {
+ return errorf("malformed elf file: shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect)
+ }
+
+ sect := &elfobj.sect[elfobj.shstrndx]
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ if elfobj.sect[i].nameoff != 0 {
+ elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:])
+ }
+ }
+
+ // load string table for symbols into memory.
+ elfobj.symtab = section(elfobj, ".symtab")
+
+ if elfobj.symtab == nil {
+ // our work is done here - no symbols means nothing can refer to this file
+ return
+ }
+
+ if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) {
+ return errorf("elf object has symbol table with invalid string table link")
+ }
+
+ elfobj.symstr = &elfobj.sect[elfobj.symtab.link]
+ if is64 != 0 {
+ elfobj.nsymtab = int(elfobj.symtab.size / elf.Sym64Size)
+ } else {
+ elfobj.nsymtab = int(elfobj.symtab.size / elf.Sym32Size)
+ }
+
+ if err := elfmap(elfobj, elfobj.symtab); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ if err := elfmap(elfobj, elfobj.symstr); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ // load text and data segments into memory.
+ // they are not as small as the section lists, but we'll need
+ // the memory anyway for the symbol images, so we might
+ // as well use one large chunk.
+
+ // create symbols for elfmapped sections
+ sectsymNames := make(map[string]bool)
+ counter := 0
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ sect = &elfobj.sect[i]
+ if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" {
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ // We assume the soft-float ABI unless we see a tag indicating otherwise.
+ if initEhdrFlags == 0x5000002 {
+ ehdrFlags = 0x5000202
+ } else {
+ ehdrFlags = initEhdrFlags
+ }
+ found, newEhdrFlags, err := parseArmAttributes(e, sect.base[:sect.size])
+ if err != nil {
+ // TODO(dfc) should this return an error?
+ log.Printf("%s: %v", pn, err)
+ }
+ if found {
+ ehdrFlags = newEhdrFlags
+ }
+ }
+ if (sect.type_ != elf.SHT_PROGBITS && sect.type_ != elf.SHT_NOBITS) || sect.flags&elf.SHF_ALLOC == 0 {
+ continue
+ }
+ if sect.type_ != elf.SHT_NOBITS {
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ }
+
+ name := fmt.Sprintf("%s(%s)", pkg, sect.name)
+ for sectsymNames[name] {
+ counter++
+ name = fmt.Sprintf("%s(%s%d)", pkg, sect.name, counter)
+ }
+ sectsymNames[name] = true
+
+ sb := l.MakeSymbolUpdater(lookup(name, localSymVersion))
+
+ switch sect.flags & (elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_EXECINSTR) {
+ default:
+ return errorf("%s: unexpected flags for ELF section %s", pn, sect.name)
+
+ case elf.SHF_ALLOC:
+ sb.SetType(sym.SRODATA)
+
+ case elf.SHF_ALLOC + elf.SHF_WRITE:
+ if sect.type_ == elf.SHT_NOBITS {
+ sb.SetType(sym.SNOPTRBSS)
+ } else {
+ sb.SetType(sym.SNOPTRDATA)
+ }
+
+ case elf.SHF_ALLOC + elf.SHF_EXECINSTR:
+ sb.SetType(sym.STEXT)
+ }
+
+ if sect.name == ".got" || sect.name == ".toc" {
+ sb.SetType(sym.SELFGOT)
+ }
+ if sect.type_ == elf.SHT_PROGBITS {
+ sb.SetData(sect.base[:sect.size])
+ }
+
+ sb.SetSize(int64(sect.size))
+ sb.SetAlign(int32(sect.align))
+ sb.SetReadOnly(sect.readOnlyMem)
+
+ sect.sym = sb.Sym()
+ }
+
+ // enter sub-symbols into symbol table.
+ // symbol 0 is the null symbol.
+ symbols := make([]loader.Sym, elfobj.nsymtab)
+
+ for i := 1; i < elfobj.nsymtab; i++ {
+ var elfsym ElfSym
+ if err := readelfsym(newSym, lookup, l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ symbols[i] = elfsym.sym
+ if elfsym.type_ != elf.STT_FUNC && elfsym.type_ != elf.STT_OBJECT && elfsym.type_ != elf.STT_NOTYPE && elfsym.type_ != elf.STT_COMMON {
+ continue
+ }
+ if elfsym.shndx == elf.SHN_COMMON || elfsym.type_ == elf.STT_COMMON {
+ sb := l.MakeSymbolUpdater(elfsym.sym)
+ if uint64(sb.Size()) < elfsym.size {
+ sb.SetSize(int64(elfsym.size))
+ }
+ if sb.Type() == 0 || sb.Type() == sym.SXREF {
+ sb.SetType(sym.SNOPTRBSS)
+ }
+ continue
+ }
+
+ if uint(elfsym.shndx) >= elfobj.nsect || elfsym.shndx == 0 {
+ continue
+ }
+
+ // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols
+ if elfsym.sym == 0 {
+ continue
+ }
+ sect = &elfobj.sect[elfsym.shndx]
+ if sect.sym == 0 {
+ if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this
+ continue
+ }
+
+ if elfsym.name == "" && elfsym.type_ == 0 && sect.name == ".debug_str" {
+ // This reportedly happens with clang 3.7 on ARM.
+ // See issue 13139.
+ continue
+ }
+
+ if strings.HasPrefix(elfsym.name, "$d") && elfsym.type_ == 0 && sect.name == ".debug_frame" {
+ // "$d" is a marker, not a real symbol.
+ // This happens with gcc on ARM64.
+ // See https://sourceware.org/bugzilla/show_bug.cgi?id=21809
+ continue
+ }
+
+ if strings.HasPrefix(elfsym.name, ".LASF") { // gcc on s390x does this
+ continue
+ }
+ return errorf("%v: sym#%d: ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.shndx, elfsym.type_)
+ }
+
+ s := elfsym.sym
+ if l.OuterSym(s) != 0 {
+ if l.AttrDuplicateOK(s) {
+ continue
+ }
+ return errorf("duplicate symbol reference: %s in both %s and %s",
+ l.SymName(s), l.SymName(l.OuterSym(s)), l.SymName(sect.sym))
+ }
+
+ sectsb := l.MakeSymbolUpdater(sect.sym)
+ sb := l.MakeSymbolUpdater(s)
+
+ sb.SetType(sectsb.Type())
+ sectsb.AddInteriorSym(s)
+ if !l.AttrCgoExportDynamic(s) {
+ sb.SetDynimplib("") // satisfy dynimport
+ }
+ sb.SetValue(int64(elfsym.value))
+ sb.SetSize(int64(elfsym.size))
+ if sectsb.Type() == sym.STEXT {
+ if l.AttrExternal(s) && !l.AttrDuplicateOK(s) {
+ return errorf("%s: duplicate symbol definition", sb.Name())
+ }
+ l.SetAttrExternal(s, true)
+ }
+
+ if elf.Machine(elfobj.machine) == elf.EM_PPC64 {
+ flag := int(elfsym.other) >> 5
+ if 2 <= flag && flag <= 6 {
+ l.SetSymLocalentry(s, 1<<uint(flag-2))
+ } else if flag == 7 {
+ return errorf("%s: invalid sym.other 0x%x", sb.Name(), elfsym.other)
+ }
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for i := uint(0); i < elfobj.nsect; i++ {
+ s := elfobj.sect[i].sym
+ if s == 0 {
+ continue
+ }
+ sb := l.MakeSymbolUpdater(s)
+ if l.SubSym(s) != 0 {
+ sb.SortSub()
+ }
+ if sb.Type() == sym.STEXT {
+ if l.AttrOnList(s) {
+ return errorf("symbol %s listed multiple times",
+ l.SymName(s))
+ }
+ l.SetAttrOnList(s, true)
+ textp = append(textp, s)
+ for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) {
+ if l.AttrOnList(ss) {
+ return errorf("symbol %s listed multiple times",
+ l.SymName(ss))
+ }
+ l.SetAttrOnList(ss, true)
+ textp = append(textp, ss)
+ }
+ }
+ }
+
+ // load relocations
+ for i := uint(0); i < elfobj.nsect; i++ {
+ rsect := &elfobj.sect[i]
+ if rsect.type_ != elf.SHT_RELA && rsect.type_ != elf.SHT_REL {
+ continue
+ }
+ if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil {
+ continue
+ }
+ sect = &elfobj.sect[rsect.info]
+ if err := elfmap(elfobj, rsect); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ rela := 0
+ if rsect.type_ == elf.SHT_RELA {
+ rela = 1
+ }
+ n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela))
+ p := rsect.base
+ sb := l.MakeSymbolUpdater(sect.sym)
+ for j := 0; j < n; j++ {
+ var add uint64
+ var symIdx int
+ var relocType uint64
+ var rOff int32
+ var rAdd int64
+ var rSym loader.Sym
+
+ if is64 != 0 {
+ // 64-bit rel/rela
+ rOff = int32(e.Uint64(p))
+
+ p = p[8:]
+ switch arch.Family {
+ case sys.MIPS64:
+ // https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf
+ // The doc shows it's different with general Linux ELF
+ symIdx = int(e.Uint32(p))
+ relocType = uint64(p[7])
+ default:
+ info := e.Uint64(p)
+ relocType = info & 0xffffffff
+ symIdx = int(info >> 32)
+ }
+ p = p[8:]
+ if rela != 0 {
+ add = e.Uint64(p)
+ p = p[8:]
+ }
+ } else {
+ // 32-bit rel/rela
+ rOff = int32(e.Uint32(p))
+
+ p = p[4:]
+ info := e.Uint32(p)
+ relocType = uint64(info & 0xff)
+ symIdx = int(info >> 8)
+ p = p[4:]
+ if rela != 0 {
+ add = uint64(e.Uint32(p))
+ p = p[4:]
+ }
+ }
+
+ if relocType == 0 { // skip R_*_NONE relocation
+ j--
+ n--
+ continue
+ }
+
+ if symIdx == 0 { // absolute relocation, don't bother reading the null symbol
+ rSym = 0
+ } else {
+ var elfsym ElfSym
+ if err := readelfsym(newSym, lookup, l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ elfsym.sym = symbols[symIdx]
+ if elfsym.sym == 0 {
+ return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", l.SymName(sect.sym), j, int(symIdx), elfsym.name, elfsym.shndx, elfsym.type_)
+ }
+
+ rSym = elfsym.sym
+ }
+
+ rType := objabi.ElfRelocOffset + objabi.RelocType(relocType)
+ rSize, err := relSize(arch, pn, uint32(relocType))
+ if err != nil {
+ return nil, 0, err
+ }
+ if rela != 0 {
+ rAdd = int64(add)
+ } else {
+ // load addend from image
+ if rSize == 4 {
+ rAdd = int64(e.Uint32(sect.base[rOff:]))
+ } else if rSize == 8 {
+ rAdd = int64(e.Uint64(sect.base[rOff:]))
+ } else {
+ return errorf("invalid rela size %d", rSize)
+ }
+ }
+
+ if rSize == 2 {
+ rAdd = int64(int16(rAdd))
+ }
+ if rSize == 4 {
+ rAdd = int64(int32(rAdd))
+ }
+
+ r, _ := sb.AddRel(rType)
+ r.SetOff(rOff)
+ r.SetSiz(rSize)
+ r.SetSym(rSym)
+ r.SetAdd(rAdd)
+ }
+
+ sb.SortRelocs() // just in case
+ }
+
+ return textp, ehdrFlags, nil
+}
+
+func section(elfobj *ElfObj, name string) *ElfSect {
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ if elfobj.sect[i].name != "" && name != "" && elfobj.sect[i].name == name {
+ return &elfobj.sect[i]
+ }
+ }
+ return nil
+}
+
+func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
+ if sect.base != nil {
+ return nil
+ }
+
+ if sect.off+sect.size > uint64(elfobj.length) {
+ err = fmt.Errorf("elf section past end of file")
+ return err
+ }
+
+ elfobj.f.MustSeek(int64(uint64(elfobj.base)+sect.off), 0)
+ sect.base, sect.readOnlyMem, err = elfobj.f.Slice(uint64(sect.size))
+ if err != nil {
+ return fmt.Errorf("short read: %v", err)
+ }
+
+ return nil
+}
+
+func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
+ if i >= elfobj.nsymtab || i < 0 {
+ err = fmt.Errorf("invalid elf symbol index")
+ return err
+ }
+
+ if i == 0 {
+ return fmt.Errorf("readym: read null symbol!")
+ }
+
+ if elfobj.is64 != 0 {
+ b := new(elf.Sym64)
+ binary.Read(bytes.NewReader(elfobj.symtab.base[i*elf.Sym64Size:(i+1)*elf.Sym64Size]), elfobj.e, b)
+ elfsym.name = cstring(elfobj.symstr.base[b.Name:])
+ elfsym.value = b.Value
+ elfsym.size = b.Size
+ elfsym.shndx = elf.SectionIndex(b.Shndx)
+ elfsym.bind = elf.ST_BIND(b.Info)
+ elfsym.type_ = elf.ST_TYPE(b.Info)
+ elfsym.other = b.Other
+ } else {
+ b := new(elf.Sym32)
+ binary.Read(bytes.NewReader(elfobj.symtab.base[i*elf.Sym32Size:(i+1)*elf.Sym32Size]), elfobj.e, b)
+ elfsym.name = cstring(elfobj.symstr.base[b.Name:])
+ elfsym.value = uint64(b.Value)
+ elfsym.size = uint64(b.Size)
+ elfsym.shndx = elf.SectionIndex(b.Shndx)
+ elfsym.bind = elf.ST_BIND(b.Info)
+ elfsym.type_ = elf.ST_TYPE(b.Info)
+ elfsym.other = b.Other
+ }
+
+ var s loader.Sym
+
+ if elfsym.name == "_GLOBAL_OFFSET_TABLE_" {
+ elfsym.name = ".got"
+ }
+ if elfsym.name == ".TOC." {
+ // Magic symbol on ppc64. Will be set to this object
+ // file's .got+0x8000.
+ elfsym.bind = elf.STB_LOCAL
+ }
+
+ switch elfsym.type_ {
+ case elf.STT_SECTION:
+ s = elfobj.sect[elfsym.shndx].sym
+
+ case elf.STT_OBJECT, elf.STT_FUNC, elf.STT_NOTYPE, elf.STT_COMMON:
+ switch elfsym.bind {
+ case elf.STB_GLOBAL:
+ if needSym != 0 {
+ s = lookup(elfsym.name, 0)
+
+ // for global scoped hidden symbols we should insert it into
+ // symbol hash table, but mark them as hidden.
+ // __i686.get_pc_thunk.bx is allowed to be duplicated, to
+ // workaround that we set dupok.
+ // TODO(minux): correctly handle __i686.get_pc_thunk.bx without
+ // set dupok generally. See https://golang.org/cl/5823055
+ // comment #5 for details.
+ if s != 0 && elfsym.other == 2 {
+ if !l.IsExternal(s) {
+ l.MakeSymbolUpdater(s)
+ }
+ l.SetAttrDuplicateOK(s, true)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+ }
+
+ case elf.STB_LOCAL:
+ if (arch.Family == sys.ARM || arch.Family == sys.ARM64) && (strings.HasPrefix(elfsym.name, "$a") || strings.HasPrefix(elfsym.name, "$d") || strings.HasPrefix(elfsym.name, "$x")) {
+ // binutils for arm and arm64 generate these mapping
+ // symbols, ignore these
+ break
+ }
+
+ if elfsym.name == ".TOC." {
+ // We need to be able to look this up,
+ // so put it in the hash table.
+ if needSym != 0 {
+ s = lookup(elfsym.name, localSymVersion)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+ break
+ }
+
+ if needSym != 0 {
+ // local names and hidden global names are unique
+ // and should only be referenced by their index, not name, so we
+ // don't bother to add them into the hash table
+ // FIXME: pass empty string here for name? This would
+ // reduce mem use, but also (possibly) make it harder
+ // to debug problems.
+ s = newSym(elfsym.name, localSymVersion)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+
+ case elf.STB_WEAK:
+ if needSym != 0 {
+ s = lookup(elfsym.name, 0)
+ if elfsym.other == 2 {
+ l.SetAttrVisibilityHidden(s, true)
+ }
+
+ // Allow weak symbols to be duplicated when already defined.
+ if l.OuterSym(s) != 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ }
+
+ default:
+ err = fmt.Errorf("%s: invalid symbol binding %d", elfsym.name, elfsym.bind)
+ return err
+ }
+ }
+
+ // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+ // sense and should be removed when someone has thought about it properly.
+ if s != 0 && l.SymType(s) == 0 && !l.AttrVisibilityHidden(s) && elfsym.type_ != elf.STT_SECTION {
+ sb := l.MakeSymbolUpdater(s)
+ sb.SetType(sym.SXREF)
+ }
+ elfsym.sym = s
+
+ return nil
+}
+
+func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) {
+ // TODO(mdempsky): Replace this with a struct-valued switch statement
+ // once golang.org/issue/15164 is fixed or found to not impair cmd/link
+ // performance.
+
+ const (
+ AMD64 = uint32(sys.AMD64)
+ ARM = uint32(sys.ARM)
+ ARM64 = uint32(sys.ARM64)
+ I386 = uint32(sys.I386)
+ MIPS = uint32(sys.MIPS)
+ MIPS64 = uint32(sys.MIPS64)
+ PPC64 = uint32(sys.PPC64)
+ RISCV64 = uint32(sys.RISCV64)
+ S390X = uint32(sys.S390X)
+ )
+
+ switch uint32(arch.Family) | elftype<<16 {
+ default:
+ return 0, fmt.Errorf("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype)
+
+ case MIPS | uint32(elf.R_MIPS_HI16)<<16,
+ MIPS | uint32(elf.R_MIPS_LO16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_HI16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_LO16)<<16,
+ MIPS | uint32(elf.R_MIPS_GPREL16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_PAGE)<<16,
+ MIPS | uint32(elf.R_MIPS_JALR)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_OFST)<<16,
+ MIPS64 | uint32(elf.R_MIPS_HI16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_LO16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_HI16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_LO16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GPREL16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_PAGE)<<16,
+ MIPS64 | uint32(elf.R_MIPS_JALR)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_OFST)<<16:
+ return 4, nil
+
+ case S390X | uint32(elf.R_390_8)<<16:
+ return 1, nil
+
+ case PPC64 | uint32(elf.R_PPC64_TOC16)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_LO)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_HI)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_HA)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_DS)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_LO_DS)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_LO)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_HI)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_HA)<<16,
+ S390X | uint32(elf.R_390_16)<<16,
+ S390X | uint32(elf.R_390_GOT16)<<16,
+ S390X | uint32(elf.R_390_PC16)<<16,
+ S390X | uint32(elf.R_390_PC16DBL)<<16,
+ S390X | uint32(elf.R_390_PLT16DBL)<<16:
+ return 2, nil
+
+ case ARM | uint32(elf.R_ARM_ABS32)<<16,
+ ARM | uint32(elf.R_ARM_GOT32)<<16,
+ ARM | uint32(elf.R_ARM_PLT32)<<16,
+ ARM | uint32(elf.R_ARM_GOTOFF)<<16,
+ ARM | uint32(elf.R_ARM_GOTPC)<<16,
+ ARM | uint32(elf.R_ARM_THM_PC22)<<16,
+ ARM | uint32(elf.R_ARM_REL32)<<16,
+ ARM | uint32(elf.R_ARM_CALL)<<16,
+ ARM | uint32(elf.R_ARM_V4BX)<<16,
+ ARM | uint32(elf.R_ARM_GOT_PREL)<<16,
+ ARM | uint32(elf.R_ARM_PC24)<<16,
+ ARM | uint32(elf.R_ARM_JUMP24)<<16,
+ ARM64 | uint32(elf.R_AARCH64_CALL26)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADR_GOT_PAGE)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LD64_GOT_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADR_PREL_PG_HI21)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADD_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST8_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST16_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST32_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST64_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST128_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_PREL32)<<16,
+ ARM64 | uint32(elf.R_AARCH64_JUMP26)<<16,
+ AMD64 | uint32(elf.R_X86_64_PC32)<<16,
+ AMD64 | uint32(elf.R_X86_64_PLT32)<<16,
+ AMD64 | uint32(elf.R_X86_64_GOTPCREL)<<16,
+ AMD64 | uint32(elf.R_X86_64_GOTPCRELX)<<16,
+ AMD64 | uint32(elf.R_X86_64_REX_GOTPCRELX)<<16,
+ I386 | uint32(elf.R_386_32)<<16,
+ I386 | uint32(elf.R_386_PC32)<<16,
+ I386 | uint32(elf.R_386_GOT32)<<16,
+ I386 | uint32(elf.R_386_PLT32)<<16,
+ I386 | uint32(elf.R_386_GOTOFF)<<16,
+ I386 | uint32(elf.R_386_GOTPC)<<16,
+ I386 | uint32(elf.R_386_GOT32X)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL24)<<16,
+ PPC64 | uint32(elf.R_PPC_REL32)<<16,
+ S390X | uint32(elf.R_390_32)<<16,
+ S390X | uint32(elf.R_390_PC32)<<16,
+ S390X | uint32(elf.R_390_GOT32)<<16,
+ S390X | uint32(elf.R_390_PLT32)<<16,
+ S390X | uint32(elf.R_390_PC32DBL)<<16,
+ S390X | uint32(elf.R_390_PLT32DBL)<<16,
+ S390X | uint32(elf.R_390_GOTPCDBL)<<16,
+ S390X | uint32(elf.R_390_GOTENT)<<16:
+ return 4, nil
+
+ case AMD64 | uint32(elf.R_X86_64_64)<<16,
+ AMD64 | uint32(elf.R_X86_64_PC64)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ABS64)<<16,
+ ARM64 | uint32(elf.R_AARCH64_PREL64)<<16,
+ PPC64 | uint32(elf.R_PPC64_ADDR64)<<16,
+ S390X | uint32(elf.R_390_GLOB_DAT)<<16,
+ S390X | uint32(elf.R_390_RELATIVE)<<16,
+ S390X | uint32(elf.R_390_GOTOFF)<<16,
+ S390X | uint32(elf.R_390_GOTPC)<<16,
+ S390X | uint32(elf.R_390_64)<<16,
+ S390X | uint32(elf.R_390_PC64)<<16,
+ S390X | uint32(elf.R_390_GOT64)<<16,
+ S390X | uint32(elf.R_390_PLT64)<<16:
+ return 8, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_RVC_BRANCH)<<16,
+ RISCV64 | uint32(elf.R_RISCV_RVC_JUMP)<<16:
+ return 2, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_BRANCH)<<16,
+ RISCV64 | uint32(elf.R_RISCV_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_LO12_I)<<16,
+ RISCV64 | uint32(elf.R_RISCV_LO12_S)<<16,
+ RISCV64 | uint32(elf.R_RISCV_GOT_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_LO12_I)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_LO12_S)<<16,
+ RISCV64 | uint32(elf.R_RISCV_RELAX)<<16:
+ return 4, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_64)<<16,
+ RISCV64 | uint32(elf.R_RISCV_CALL)<<16,
+ RISCV64 | uint32(elf.R_RISCV_CALL_PLT)<<16:
+ return 8, nil
+ }
+}
+
+func cstring(x []byte) string {
+ i := bytes.IndexByte(x, '\x00')
+ if i >= 0 {
+ x = x[:i]
+ }
+ return string(x)
+}
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
new file mode 100644
index 0000000..85b9489
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader.go
@@ -0,0 +1,2683 @@
+// 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 loader
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "log"
+ "math/bits"
+ "os"
+ "sort"
+ "strings"
+)
+
+var _ = fmt.Print
+
+// Sym encapsulates a global symbol index, used to identify a specific
+// Go symbol. The 0-valued Sym is corresponds to an invalid symbol.
+type Sym int
+
+// Relocs encapsulates the set of relocations on a given symbol; an
+// instance of this type is returned by the Loader Relocs() method.
+type Relocs struct {
+ rs []goobj.Reloc
+
+ li uint32 // local index of symbol whose relocs we're examining
+ r *oReader // object reader for containing package
+ l *Loader // loader
+}
+
+// ExtReloc contains the payload for an external relocation.
+type ExtReloc struct {
+ Xsym Sym
+ Xadd int64
+ Type objabi.RelocType
+ Size uint8
+}
+
+// Reloc holds a "handle" to access a relocation record from an
+// object file.
+type Reloc struct {
+ *goobj.Reloc
+ r *oReader
+ l *Loader
+
+ // External reloc types may not fit into a uint8 which the Go object file uses.
+ // Store it here, instead of in the byte of goobj.Reloc.
+ // For Go symbols this will always be zero.
+ // goobj.Reloc.Type() + typ is always the right type, for both Go and external
+ // symbols.
+ typ objabi.RelocType
+}
+
+func (rel Reloc) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc.Type()) + rel.typ }
+func (rel Reloc) Sym() Sym { return rel.l.resolve(rel.r, rel.Reloc.Sym()) }
+func (rel Reloc) SetSym(s Sym) { rel.Reloc.SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) }
+func (rel Reloc) IsMarker() bool { return rel.Siz() == 0 }
+
+func (rel Reloc) SetType(t objabi.RelocType) {
+ if t != objabi.RelocType(uint8(t)) {
+ panic("SetType: type doesn't fit into Reloc")
+ }
+ rel.Reloc.SetType(uint8(t))
+ if rel.typ != 0 {
+ // should use SymbolBuilder.SetRelocType
+ panic("wrong method to set reloc type")
+ }
+}
+
+// Aux holds a "handle" to access an aux symbol record from an
+// object file.
+type Aux struct {
+ *goobj.Aux
+ r *oReader
+ l *Loader
+}
+
+func (a Aux) Sym() Sym { return a.l.resolve(a.r, a.Aux.Sym()) }
+
+// oReader is a wrapper type of obj.Reader, along with some
+// extra information.
+type oReader struct {
+ *goobj.Reader
+ unit *sym.CompilationUnit
+ version int // version of static symbol
+ flags uint32 // read from object file
+ pkgprefix string
+ syms []Sym // Sym's global index, indexed by local index
+ pkg []uint32 // indices of referenced package by PkgIdx (index into loader.objs array)
+ ndef int // cache goobj.Reader.NSym()
+ nhashed64def int // cache goobj.Reader.NHashed64Def()
+ nhasheddef int // cache goobj.Reader.NHashedDef()
+ objidx uint32 // index of this reader in the objs slice
+}
+
+// Total number of defined symbols (package symbols, hashed symbols, and
+// non-package symbols).
+func (r *oReader) NAlldef() int { return r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef() }
+
+type objIdx struct {
+ r *oReader
+ i Sym // start index
+}
+
+// objSym represents a symbol in an object file. It is a tuple of
+// the object and the symbol's local index.
+// For external symbols, objidx is the index of l.extReader (extObj),
+// s is its index into the payload array.
+// {0, 0} represents the nil symbol.
+type objSym struct {
+ objidx uint32 // index of the object (in l.objs array)
+ s uint32 // local index
+}
+
+type nameVer struct {
+ name string
+ v int
+}
+
+type Bitmap []uint32
+
+// set the i-th bit.
+func (bm Bitmap) Set(i Sym) {
+ n, r := uint(i)/32, uint(i)%32
+ bm[n] |= 1 << r
+}
+
+// unset the i-th bit.
+func (bm Bitmap) Unset(i Sym) {
+ n, r := uint(i)/32, uint(i)%32
+ bm[n] &^= (1 << r)
+}
+
+// whether the i-th bit is set.
+func (bm Bitmap) Has(i Sym) bool {
+ n, r := uint(i)/32, uint(i)%32
+ return bm[n]&(1<<r) != 0
+}
+
+// return current length of bitmap in bits.
+func (bm Bitmap) Len() int {
+ return len(bm) * 32
+}
+
+// return the number of bits set.
+func (bm Bitmap) Count() int {
+ s := 0
+ for _, x := range bm {
+ s += bits.OnesCount32(x)
+ }
+ return s
+}
+
+func MakeBitmap(n int) Bitmap {
+ return make(Bitmap, (n+31)/32)
+}
+
+// growBitmap insures that the specified bitmap has enough capacity,
+// reallocating (doubling the size) if needed.
+func growBitmap(reqLen int, b Bitmap) Bitmap {
+ curLen := b.Len()
+ if reqLen > curLen {
+ b = append(b, MakeBitmap(reqLen+1-curLen)...)
+ }
+ return b
+}
+
+type symAndSize struct {
+ sym Sym
+ size uint32
+}
+
+// A Loader loads new object files and resolves indexed symbol references.
+//
+// Notes on the layout of global symbol index space:
+//
+// - Go object files are read before host object files; each Go object
+// read adds its defined package symbols to the global index space.
+// Nonpackage symbols are not yet added.
+//
+// - In loader.LoadNonpkgSyms, add non-package defined symbols and
+// references in all object files to the global index space.
+//
+// - Host object file loading happens; the host object loader does a
+// name/version lookup for each symbol it finds; this can wind up
+// extending the external symbol index space range. The host object
+// loader stores symbol payloads in loader.payloads using SymbolBuilder.
+//
+// - Each symbol gets a unique global index. For duplicated and
+// overwriting/overwritten symbols, the second (or later) appearance
+// of the symbol gets the same global index as the first appearance.
+type Loader struct {
+ start map[*oReader]Sym // map from object file to its start index
+ objs []objIdx // sorted by start index (i.e. objIdx.i)
+ extStart Sym // from this index on, the symbols are externally defined
+ builtinSyms []Sym // global index of builtin symbols
+
+ objSyms []objSym // global index mapping to local index
+
+ symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal
+ extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name
+
+ extReader *oReader // a dummy oReader, for external symbols
+ payloadBatch []extSymPayload
+ payloads []*extSymPayload // contents of linker-materialized external syms
+ values []int64 // symbol values, indexed by global sym index
+
+ sects []*sym.Section // sections
+ symSects []uint16 // symbol's section, index to sects array
+
+ align []uint8 // symbol 2^N alignment, indexed by global index
+
+ deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call
+
+ objByPkg map[string]uint32 // map package path to the index of its Go object reader
+
+ anonVersion int // most recently assigned ext static sym pseudo-version
+
+ // Bitmaps and other side structures used to store data used to store
+ // symbol flags/attributes; these are to be accessed via the
+ // corresponding loader "AttrXXX" and "SetAttrXXX" methods. Please
+ // visit the comments on these methods for more details on the
+ // semantics / interpretation of the specific flags or attribute.
+ attrReachable Bitmap // reachable symbols, indexed by global index
+ attrOnList Bitmap // "on list" symbols, indexed by global index
+ attrLocal Bitmap // "local" symbols, indexed by global index
+ attrNotInSymbolTable Bitmap // "not in symtab" symbols, indexed by global idx
+ attrUsedInIface Bitmap // "used in interface" symbols, indexed by global idx
+ attrVisibilityHidden Bitmap // hidden symbols, indexed by ext sym index
+ attrDuplicateOK Bitmap // dupOK symbols, indexed by ext sym index
+ attrShared Bitmap // shared symbols, indexed by ext sym index
+ attrExternal Bitmap // external symbols, indexed by ext sym index
+
+ attrReadOnly map[Sym]bool // readonly data for this sym
+ attrTopFrame map[Sym]struct{} // top frame symbols
+ attrSpecial map[Sym]struct{} // "special" frame symbols
+ attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols
+ attrCgoExportStatic map[Sym]struct{} // "cgo_export_static" symbols
+ generatedSyms map[Sym]struct{} // symbols that generate their content
+
+ // Outer and Sub relations for symbols.
+ // TODO: figure out whether it's more efficient to just have these
+ // as fields on extSymPayload (note that this won't be a viable
+ // strategy if somewhere in the linker we set sub/outer for a
+ // non-external sym).
+ outer map[Sym]Sym
+ sub map[Sym]Sym
+
+ dynimplib map[Sym]string // stores Dynimplib symbol attribute
+ dynimpvers map[Sym]string // stores Dynimpvers symbol attribute
+ localentry map[Sym]uint8 // stores Localentry symbol attribute
+ extname map[Sym]string // stores Extname symbol attribute
+ elfType map[Sym]elf.SymType // stores elf type symbol property
+ elfSym map[Sym]int32 // stores elf sym symbol property
+ localElfSym map[Sym]int32 // stores "local" elf sym symbol property
+ symPkg map[Sym]string // stores package for symbol, or library for shlib-derived syms
+ plt map[Sym]int32 // stores dynimport for pe objects
+ got map[Sym]int32 // stores got for pe objects
+ dynid map[Sym]int32 // stores Dynid for symbol
+
+ relocVariant map[relocId]sym.RelocVariant // stores variant relocs
+
+ // Used to implement field tracking; created during deadcode if
+ // field tracking is enabled. Reachparent[K] contains the index of
+ // the symbol that triggered the marking of symbol K as live.
+ Reachparent []Sym
+
+ flags uint32
+
+ hasUnknownPkgPath bool // if any Go object has unknown package path
+
+ strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
+
+ elfsetstring elfsetstringFunc
+
+ errorReporter *ErrorReporter
+
+ npkgsyms int // number of package symbols, for accounting
+ nhashedsyms int // number of hashed symbols, for accounting
+}
+
+const (
+ pkgDef = iota
+ hashed64Def
+ hashedDef
+ nonPkgDef
+ nonPkgRef
+)
+
+// objidx
+const (
+ nilObj = iota
+ extObj
+ goObjStart
+)
+
+type elfsetstringFunc func(str string, off int)
+
+// extSymPayload holds the payload (data + relocations) for linker-synthesized
+// external symbols (note that symbol value is stored in a separate slice).
+type extSymPayload struct {
+ name string // TODO: would this be better as offset into str table?
+ size int64
+ ver int
+ kind sym.SymKind
+ objidx uint32 // index of original object if sym made by cloneToExternal
+ relocs []goobj.Reloc
+ reltypes []objabi.RelocType // relocation types
+ data []byte
+ auxs []goobj.Aux
+}
+
+const (
+ // Loader.flags
+ FlagStrictDups = 1 << iota
+)
+
+func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorReporter) *Loader {
+ nbuiltin := goobj.NBuiltin()
+ extReader := &oReader{objidx: extObj}
+ ldr := &Loader{
+ start: make(map[*oReader]Sym),
+ objs: []objIdx{{}, {extReader, 0}}, // reserve index 0 for nil symbol, 1 for external symbols
+ objSyms: make([]objSym, 1, 1), // This will get overwritten later.
+ extReader: extReader,
+ symsByName: [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols
+ objByPkg: make(map[string]uint32),
+ outer: make(map[Sym]Sym),
+ sub: make(map[Sym]Sym),
+ dynimplib: make(map[Sym]string),
+ dynimpvers: make(map[Sym]string),
+ localentry: make(map[Sym]uint8),
+ extname: make(map[Sym]string),
+ attrReadOnly: make(map[Sym]bool),
+ elfType: make(map[Sym]elf.SymType),
+ elfSym: make(map[Sym]int32),
+ localElfSym: make(map[Sym]int32),
+ symPkg: make(map[Sym]string),
+ plt: make(map[Sym]int32),
+ got: make(map[Sym]int32),
+ dynid: make(map[Sym]int32),
+ attrTopFrame: make(map[Sym]struct{}),
+ attrSpecial: make(map[Sym]struct{}),
+ attrCgoExportDynamic: make(map[Sym]struct{}),
+ attrCgoExportStatic: make(map[Sym]struct{}),
+ generatedSyms: make(map[Sym]struct{}),
+ deferReturnTramp: make(map[Sym]bool),
+ extStaticSyms: make(map[nameVer]Sym),
+ builtinSyms: make([]Sym, nbuiltin),
+ flags: flags,
+ elfsetstring: elfsetstring,
+ errorReporter: reporter,
+ sects: []*sym.Section{nil}, // reserve index 0 for nil section
+ }
+ reporter.ldr = ldr
+ return ldr
+}
+
+// Add object file r, return the start index.
+func (l *Loader) addObj(pkg string, r *oReader) Sym {
+ if _, ok := l.start[r]; ok {
+ panic("already added")
+ }
+ pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
+ if _, ok := l.objByPkg[pkg]; !ok {
+ l.objByPkg[pkg] = r.objidx
+ }
+ i := Sym(len(l.objSyms))
+ l.start[r] = i
+ l.objs = append(l.objs, objIdx{r, i})
+ if r.NeedNameExpansion() && !r.FromAssembly() {
+ l.hasUnknownPkgPath = true
+ }
+ return i
+}
+
+// Add a symbol from an object file, return the global index.
+// If the symbol already exist, it returns the index of that symbol.
+func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind int, osym *goobj.Sym) Sym {
+ l := st.l
+ if l.extStart != 0 {
+ panic("addSym called after external symbol is created")
+ }
+ i := Sym(len(l.objSyms))
+ addToGlobal := func() {
+ l.objSyms = append(l.objSyms, objSym{r.objidx, li})
+ }
+ if name == "" && kind != hashed64Def && kind != hashedDef {
+ addToGlobal()
+ return i // unnamed aux symbol
+ }
+ if ver == r.version {
+ // Static symbol. Add its global index but don't
+ // add to name lookup table, as it cannot be
+ // referenced by name.
+ addToGlobal()
+ return i
+ }
+ switch kind {
+ case pkgDef:
+ // Defined package symbols cannot be dup to each other.
+ // We load all the package symbols first, so we don't need
+ // to check dup here.
+ // We still add it to the lookup table, as it may still be
+ // referenced by name (e.g. through linkname).
+ l.symsByName[ver][name] = i
+ addToGlobal()
+ return i
+ case hashed64Def, hashedDef:
+ // Hashed (content-addressable) symbol. Check the hash
+ // but don't add to name lookup table, as they are not
+ // referenced by name. Also no need to do overwriting
+ // check, as same hash indicates same content.
+ var checkHash func() (symAndSize, bool)
+ var addToHashMap func(symAndSize)
+ var h64 uint64 // only used for hashed64Def
+ var h *goobj.HashType // only used for hashedDef
+ if kind == hashed64Def {
+ checkHash = func() (symAndSize, bool) {
+ h64 = r.Hash64(li - uint32(r.ndef))
+ s, existed := st.hashed64Syms[h64]
+ return s, existed
+ }
+ addToHashMap = func(ss symAndSize) { st.hashed64Syms[h64] = ss }
+ } else {
+ checkHash = func() (symAndSize, bool) {
+ h = r.Hash(li - uint32(r.ndef+r.nhashed64def))
+ s, existed := st.hashedSyms[*h]
+ return s, existed
+ }
+ addToHashMap = func(ss symAndSize) { st.hashedSyms[*h] = ss }
+ }
+ siz := osym.Siz()
+ if s, existed := checkHash(); existed {
+ // The content hash is built from symbol data and relocations. In the
+ // object file, the symbol data may not always contain trailing zeros,
+ // e.g. for [5]int{1,2,3} and [100]int{1,2,3}, the data is same
+ // (although the size is different).
+ // Also, for short symbols, the content hash is the identity function of
+ // the 8 bytes, and trailing zeros doesn't change the hash value, e.g.
+ // hash("A") == hash("A\0\0\0").
+ // So when two symbols have the same hash, we need to use the one with
+ // larger size.
+ if siz > s.size {
+ // New symbol has larger size, use the new one. Rewrite the index mapping.
+ l.objSyms[s.sym] = objSym{r.objidx, li}
+ addToHashMap(symAndSize{s.sym, siz})
+ }
+ return s.sym
+ }
+ addToHashMap(symAndSize{i, siz})
+ addToGlobal()
+ return i
+ }
+
+ // Non-package (named) symbol. Check if it already exists.
+ oldi, existed := l.symsByName[ver][name]
+ if !existed {
+ l.symsByName[ver][name] = i
+ addToGlobal()
+ return i
+ }
+ // symbol already exists
+ if osym.Dupok() {
+ if l.flags&FlagStrictDups != 0 {
+ l.checkdup(name, r, li, oldi)
+ }
+ // Fix for issue #47185 -- given two dupok symbols with
+ // different sizes, favor symbol with larger size. See
+ // also issue #46653.
+ szdup := l.SymSize(oldi)
+ sz := int64(r.Sym(li).Siz())
+ if szdup < sz {
+ // new symbol overwrites old symbol.
+ l.objSyms[oldi] = objSym{r.objidx, li}
+ }
+ return oldi
+ }
+ oldr, oldli := l.toLocal(oldi)
+ oldsym := oldr.Sym(oldli)
+ if oldsym.Dupok() {
+ return oldi
+ }
+ overwrite := r.DataSize(li) != 0
+ if overwrite {
+ // new symbol overwrites old symbol.
+ oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) {
+ log.Fatalf("duplicated definition of symbol " + name)
+ }
+ l.objSyms[oldi] = objSym{r.objidx, li}
+ } else {
+ // old symbol overwrites new symbol.
+ typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ if !typ.IsData() { // only allow overwriting data symbol
+ log.Fatalf("duplicated definition of symbol " + name)
+ }
+ }
+ return oldi
+}
+
+// newExtSym creates a new external sym with the specified
+// name/version.
+func (l *Loader) newExtSym(name string, ver int) Sym {
+ i := Sym(len(l.objSyms))
+ if l.extStart == 0 {
+ l.extStart = i
+ }
+ l.growValues(int(i) + 1)
+ l.growAttrBitmaps(int(i) + 1)
+ pi := l.newPayload(name, ver)
+ l.objSyms = append(l.objSyms, objSym{l.extReader.objidx, uint32(pi)})
+ l.extReader.syms = append(l.extReader.syms, i)
+ return i
+}
+
+// LookupOrCreateSym looks up the symbol with the specified name/version,
+// returning its Sym index if found. If the lookup fails, a new external
+// Sym will be created, entered into the lookup tables, and returned.
+func (l *Loader) LookupOrCreateSym(name string, ver int) Sym {
+ i := l.Lookup(name, ver)
+ if i != 0 {
+ return i
+ }
+ i = l.newExtSym(name, ver)
+ static := ver >= sym.SymVerStatic || ver < 0
+ if static {
+ l.extStaticSyms[nameVer{name, ver}] = i
+ } else {
+ l.symsByName[ver][name] = i
+ }
+ return i
+}
+
+func (l *Loader) IsExternal(i Sym) bool {
+ r, _ := l.toLocal(i)
+ return l.isExtReader(r)
+}
+
+func (l *Loader) isExtReader(r *oReader) bool {
+ return r == l.extReader
+}
+
+// For external symbol, return its index in the payloads array.
+// XXX result is actually not a global index. We (ab)use the Sym type
+// so we don't need conversion for accessing bitmaps.
+func (l *Loader) extIndex(i Sym) Sym {
+ _, li := l.toLocal(i)
+ return Sym(li)
+}
+
+// Get a new payload for external symbol, return its index in
+// the payloads array.
+func (l *Loader) newPayload(name string, ver int) int {
+ pi := len(l.payloads)
+ pp := l.allocPayload()
+ pp.name = name
+ pp.ver = ver
+ l.payloads = append(l.payloads, pp)
+ l.growExtAttrBitmaps()
+ return pi
+}
+
+// getPayload returns a pointer to the extSymPayload struct for an
+// external symbol if the symbol has a payload. Will panic if the
+// symbol in question is bogus (zero or not an external sym).
+func (l *Loader) getPayload(i Sym) *extSymPayload {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("bogus symbol index %d in getPayload", i))
+ }
+ pi := l.extIndex(i)
+ return l.payloads[pi]
+}
+
+// allocPayload allocates a new payload.
+func (l *Loader) allocPayload() *extSymPayload {
+ batch := l.payloadBatch
+ if len(batch) == 0 {
+ batch = make([]extSymPayload, 1000)
+ }
+ p := &batch[0]
+ l.payloadBatch = batch[1:]
+ return p
+}
+
+func (ms *extSymPayload) Grow(siz int64) {
+ if int64(int(siz)) != siz {
+ log.Fatalf("symgrow size %d too long", siz)
+ }
+ if int64(len(ms.data)) >= siz {
+ return
+ }
+ if cap(ms.data) < int(siz) {
+ cl := len(ms.data)
+ ms.data = append(ms.data, make([]byte, int(siz)+1-cl)...)
+ ms.data = ms.data[0:cl]
+ }
+ ms.data = ms.data[:siz]
+}
+
+// Convert a local index to a global index.
+func (l *Loader) toGlobal(r *oReader, i uint32) Sym {
+ return r.syms[i]
+}
+
+// Convert a global index to a local index.
+func (l *Loader) toLocal(i Sym) (*oReader, uint32) {
+ return l.objs[l.objSyms[i].objidx].r, l.objSyms[i].s
+}
+
+// Resolve a local symbol reference. Return global index.
+func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym {
+ var rr *oReader
+ switch p := s.PkgIdx; p {
+ case goobj.PkgIdxInvalid:
+ // {0, X} with non-zero X is never a valid sym reference from a Go object.
+ // We steal this space for symbol references from external objects.
+ // In this case, X is just the global index.
+ if l.isExtReader(r) {
+ return Sym(s.SymIdx)
+ }
+ if s.SymIdx != 0 {
+ panic("bad sym ref")
+ }
+ return 0
+ case goobj.PkgIdxHashed64:
+ i := int(s.SymIdx) + r.ndef
+ return r.syms[i]
+ case goobj.PkgIdxHashed:
+ i := int(s.SymIdx) + r.ndef + r.nhashed64def
+ return r.syms[i]
+ case goobj.PkgIdxNone:
+ i := int(s.SymIdx) + r.ndef + r.nhashed64def + r.nhasheddef
+ return r.syms[i]
+ case goobj.PkgIdxBuiltin:
+ if bi := l.builtinSyms[s.SymIdx]; bi != 0 {
+ return bi
+ }
+ l.reportMissingBuiltin(int(s.SymIdx), r.unit.Lib.Pkg)
+ return 0
+ case goobj.PkgIdxSelf:
+ rr = r
+ default:
+ rr = l.objs[r.pkg[p]].r
+ }
+ return l.toGlobal(rr, s.SymIdx)
+}
+
+// reportMissingBuiltin issues an error in the case where we have a
+// relocation against a runtime builtin whose definition is not found
+// when the runtime package is built. The canonical example is
+// "runtime.racefuncenter" -- currently if you do something like
+//
+// go build -gcflags=-race myprogram.go
+//
+// the compiler will insert calls to the builtin runtime.racefuncenter,
+// but the version of the runtime used for linkage won't actually contain
+// definitions of that symbol. See issue #42396 for details.
+//
+// As currently implemented, this is a fatal error. This has drawbacks
+// in that if there are multiple missing builtins, the error will only
+// cite the first one. On the plus side, terminating the link here has
+// advantages in that we won't run the risk of panics or crashes later
+// on in the linker due to R_CALL relocations with 0-valued target
+// symbols.
+func (l *Loader) reportMissingBuiltin(bsym int, reflib string) {
+ bname, _ := goobj.BuiltinName(bsym)
+ log.Fatalf("reference to undefined builtin %q from package %q",
+ bname, reflib)
+}
+
+// Look up a symbol by name, return global index, or 0 if not found.
+// This is more like Syms.ROLookup than Lookup -- it doesn't create
+// new symbol.
+func (l *Loader) Lookup(name string, ver int) Sym {
+ if ver >= sym.SymVerStatic || ver < 0 {
+ return l.extStaticSyms[nameVer{name, ver}]
+ }
+ return l.symsByName[ver][name]
+}
+
+// Check that duplicate symbols have same contents.
+func (l *Loader) checkdup(name string, r *oReader, li uint32, dup Sym) {
+ p := r.Data(li)
+ rdup, ldup := l.toLocal(dup)
+ pdup := rdup.Data(ldup)
+ if bytes.Equal(p, pdup) {
+ return
+ }
+ reason := "same length but different contents"
+ if len(p) != len(pdup) {
+ reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
+ }
+ fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)
+
+ // For the moment, allow DWARF subprogram DIEs for
+ // auto-generated wrapper functions. What seems to happen
+ // here is that we get different line numbers on formal
+ // params; I am guessing that the pos is being inherited
+ // from the spot where the wrapper is needed.
+ allowed := strings.HasPrefix(name, "go.info.go.interface") ||
+ strings.HasPrefix(name, "go.info.go.builtin") ||
+ strings.HasPrefix(name, "go.debuglines")
+ if !allowed {
+ l.strictDupMsgs++
+ }
+}
+
+func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
+
+// Number of total symbols.
+func (l *Loader) NSym() int {
+ return len(l.objSyms)
+}
+
+// Number of defined Go symbols.
+func (l *Loader) NDef() int {
+ return int(l.extStart)
+}
+
+// Number of reachable symbols.
+func (l *Loader) NReachableSym() int {
+ return l.attrReachable.Count()
+}
+
+// SymNameLen returns the length of the symbol name, trying hard not to load
+// the name.
+func (l *Loader) SymNameLen(i Sym) int {
+ // Not much we can do about external symbols.
+ if l.IsExternal(i) {
+ return len(l.SymName(i))
+ }
+ r, li := l.toLocal(i)
+ le := r.Sym(li).NameLen(r.Reader)
+ if !r.NeedNameExpansion() {
+ return le
+ }
+ // Just load the symbol name. We don't know how expanded it'll be.
+ return len(l.SymName(i))
+}
+
+// Returns the raw (unpatched) name of the i-th symbol.
+func (l *Loader) RawSymName(i Sym) string {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.name
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).Name(r.Reader)
+}
+
+// Returns the (patched) name of the i-th symbol.
+func (l *Loader) SymName(i Sym) string {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.name
+ }
+ r, li := l.toLocal(i)
+ name := r.Sym(li).Name(r.Reader)
+ if !r.NeedNameExpansion() {
+ return name
+ }
+ return strings.Replace(name, "\"\".", r.pkgprefix, -1)
+}
+
+// Returns the version of the i-th symbol.
+func (l *Loader) SymVersion(i Sym) int {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.ver
+ }
+ r, li := l.toLocal(i)
+ return int(abiToVer(r.Sym(li).ABI(), r.version))
+}
+
+func (l *Loader) IsFileLocal(i Sym) bool {
+ return l.SymVersion(i) >= sym.SymVerStatic
+}
+
+// IsFromAssembly returns true if this symbol is derived from an
+// object file generated by the Go assembler.
+func (l *Loader) IsFromAssembly(i Sym) bool {
+ if l.IsExternal(i) {
+ return false
+ }
+ r, _ := l.toLocal(i)
+ return r.FromAssembly()
+}
+
+// Returns the type of the i-th symbol.
+func (l *Loader) SymType(i Sym) sym.SymKind {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ return pp.kind
+ }
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return sym.AbiSymKindToSymKind[objabi.SymKind(r.Sym(li).Type())]
+}
+
+// Returns the attributes of the i-th symbol.
+func (l *Loader) SymAttr(i Sym) uint8 {
+ if l.IsExternal(i) {
+ // TODO: do something? External symbols have different representation of attributes.
+ // For now, ReflectMethod, NoSplit, GoType, and Typelink are used and they cannot be
+ // set by external symbol.
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).Flag()
+}
+
+// Returns the size of the i-th symbol.
+func (l *Loader) SymSize(i Sym) int64 {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.size
+ }
+ r, li := l.toLocal(i)
+ return int64(r.Sym(li).Siz())
+}
+
+// AttrReachable returns true for symbols that are transitively
+// referenced from the entry points. Unreachable symbols are not
+// written to the output.
+func (l *Loader) AttrReachable(i Sym) bool {
+ return l.attrReachable.Has(i)
+}
+
+// SetAttrReachable sets the reachability property for a symbol (see
+// AttrReachable).
+func (l *Loader) SetAttrReachable(i Sym, v bool) {
+ if v {
+ l.attrReachable.Set(i)
+ } else {
+ l.attrReachable.Unset(i)
+ }
+}
+
+// AttrOnList returns true for symbols that are on some list (such as
+// the list of all text symbols, or one of the lists of data symbols)
+// and is consulted to avoid bugs where a symbol is put on a list
+// twice.
+func (l *Loader) AttrOnList(i Sym) bool {
+ return l.attrOnList.Has(i)
+}
+
+// SetAttrOnList sets the "on list" property for a symbol (see
+// AttrOnList).
+func (l *Loader) SetAttrOnList(i Sym, v bool) {
+ if v {
+ l.attrOnList.Set(i)
+ } else {
+ l.attrOnList.Unset(i)
+ }
+}
+
+// AttrLocal returns true for symbols that are only visible within the
+// module (executable or shared library) being linked. This attribute
+// is applied to thunks and certain other linker-generated symbols.
+func (l *Loader) AttrLocal(i Sym) bool {
+ return l.attrLocal.Has(i)
+}
+
+// SetAttrLocal the "local" property for a symbol (see AttrLocal above).
+func (l *Loader) SetAttrLocal(i Sym, v bool) {
+ if v {
+ l.attrLocal.Set(i)
+ } else {
+ l.attrLocal.Unset(i)
+ }
+}
+
+// AttrUsedInIface returns true for a type symbol that is used in
+// an interface.
+func (l *Loader) AttrUsedInIface(i Sym) bool {
+ return l.attrUsedInIface.Has(i)
+}
+
+func (l *Loader) SetAttrUsedInIface(i Sym, v bool) {
+ if v {
+ l.attrUsedInIface.Set(i)
+ } else {
+ l.attrUsedInIface.Unset(i)
+ }
+}
+
+// SymAddr checks that a symbol is reachable, and returns its value.
+func (l *Loader) SymAddr(i Sym) int64 {
+ if !l.AttrReachable(i) {
+ panic("unreachable symbol in symaddr")
+ }
+ return l.values[i]
+}
+
+// AttrNotInSymbolTable returns true for symbols that should not be
+// added to the symbol table of the final generated load module.
+func (l *Loader) AttrNotInSymbolTable(i Sym) bool {
+ return l.attrNotInSymbolTable.Has(i)
+}
+
+// SetAttrNotInSymbolTable the "not in symtab" property for a symbol
+// (see AttrNotInSymbolTable above).
+func (l *Loader) SetAttrNotInSymbolTable(i Sym, v bool) {
+ if v {
+ l.attrNotInSymbolTable.Set(i)
+ } else {
+ l.attrNotInSymbolTable.Unset(i)
+ }
+}
+
+// AttrVisibilityHidden symbols returns true for ELF symbols with
+// visibility set to STV_HIDDEN. They become local symbols in
+// the final executable. Only relevant when internally linking
+// on an ELF platform.
+func (l *Loader) AttrVisibilityHidden(i Sym) bool {
+ if !l.IsExternal(i) {
+ return false
+ }
+ return l.attrVisibilityHidden.Has(l.extIndex(i))
+}
+
+// SetAttrVisibilityHidden sets the "hidden visibility" property for a
+// symbol (see AttrVisibilityHidden).
+func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("tried to set visibility attr on non-external symbol")
+ }
+ if v {
+ l.attrVisibilityHidden.Set(l.extIndex(i))
+ } else {
+ l.attrVisibilityHidden.Unset(l.extIndex(i))
+ }
+}
+
+// AttrDuplicateOK returns true for a symbol that can be present in
+// multiple object files.
+func (l *Loader) AttrDuplicateOK(i Sym) bool {
+ if !l.IsExternal(i) {
+ // TODO: if this path winds up being taken frequently, it
+ // might make more sense to copy the flag value out of the object
+ // into a larger bitmap during preload.
+ r, li := l.toLocal(i)
+ return r.Sym(li).Dupok()
+ }
+ return l.attrDuplicateOK.Has(l.extIndex(i))
+}
+
+// SetAttrDuplicateOK sets the "duplicate OK" property for an external
+// symbol (see AttrDuplicateOK).
+func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("tried to set dupok attr on non-external symbol")
+ }
+ if v {
+ l.attrDuplicateOK.Set(l.extIndex(i))
+ } else {
+ l.attrDuplicateOK.Unset(l.extIndex(i))
+ }
+}
+
+// AttrShared returns true for symbols compiled with the -shared option.
+func (l *Loader) AttrShared(i Sym) bool {
+ if !l.IsExternal(i) {
+ // TODO: if this path winds up being taken frequently, it
+ // might make more sense to copy the flag value out of the
+ // object into a larger bitmap during preload.
+ r, _ := l.toLocal(i)
+ return r.Shared()
+ }
+ return l.attrShared.Has(l.extIndex(i))
+}
+
+// SetAttrShared sets the "shared" property for an external
+// symbol (see AttrShared).
+func (l *Loader) SetAttrShared(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("tried to set shared attr on non-external symbol %d %s", i, l.SymName(i)))
+ }
+ if v {
+ l.attrShared.Set(l.extIndex(i))
+ } else {
+ l.attrShared.Unset(l.extIndex(i))
+ }
+}
+
+// AttrExternal returns true for function symbols loaded from host
+// object files.
+func (l *Loader) AttrExternal(i Sym) bool {
+ if !l.IsExternal(i) {
+ return false
+ }
+ return l.attrExternal.Has(l.extIndex(i))
+}
+
+// SetAttrExternal sets the "external" property for an host object
+// symbol (see AttrExternal).
+func (l *Loader) SetAttrExternal(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("tried to set external attr on non-external symbol %q", l.RawSymName(i)))
+ }
+ if v {
+ l.attrExternal.Set(l.extIndex(i))
+ } else {
+ l.attrExternal.Unset(l.extIndex(i))
+ }
+}
+
+// AttrTopFrame returns true for a function symbol that is an entry
+// point, meaning that unwinders should stop when they hit this
+// function.
+func (l *Loader) AttrTopFrame(i Sym) bool {
+ _, ok := l.attrTopFrame[i]
+ return ok
+}
+
+// SetAttrTopFrame sets the "top frame" property for a symbol (see
+// AttrTopFrame).
+func (l *Loader) SetAttrTopFrame(i Sym, v bool) {
+ if v {
+ l.attrTopFrame[i] = struct{}{}
+ } else {
+ delete(l.attrTopFrame, i)
+ }
+}
+
+// AttrSpecial returns true for a symbols that do not have their
+// address (i.e. Value) computed by the usual mechanism of
+// data.go:dodata() & data.go:address().
+func (l *Loader) AttrSpecial(i Sym) bool {
+ _, ok := l.attrSpecial[i]
+ return ok
+}
+
+// SetAttrSpecial sets the "special" property for a symbol (see
+// AttrSpecial).
+func (l *Loader) SetAttrSpecial(i Sym, v bool) {
+ if v {
+ l.attrSpecial[i] = struct{}{}
+ } else {
+ delete(l.attrSpecial, i)
+ }
+}
+
+// AttrCgoExportDynamic returns true for a symbol that has been
+// specially marked via the "cgo_export_dynamic" compiler directive
+// written by cgo (in response to //export directives in the source).
+func (l *Loader) AttrCgoExportDynamic(i Sym) bool {
+ _, ok := l.attrCgoExportDynamic[i]
+ return ok
+}
+
+// SetAttrCgoExportDynamic sets the "cgo_export_dynamic" for a symbol
+// (see AttrCgoExportDynamic).
+func (l *Loader) SetAttrCgoExportDynamic(i Sym, v bool) {
+ if v {
+ l.attrCgoExportDynamic[i] = struct{}{}
+ } else {
+ delete(l.attrCgoExportDynamic, i)
+ }
+}
+
+// AttrCgoExportStatic returns true for a symbol that has been
+// specially marked via the "cgo_export_static" directive
+// written by cgo.
+func (l *Loader) AttrCgoExportStatic(i Sym) bool {
+ _, ok := l.attrCgoExportStatic[i]
+ return ok
+}
+
+// SetAttrCgoExportStatic sets the "cgo_export_static" for a symbol
+// (see AttrCgoExportStatic).
+func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
+ if v {
+ l.attrCgoExportStatic[i] = struct{}{}
+ } else {
+ delete(l.attrCgoExportStatic, i)
+ }
+}
+
+// IsGeneratedSym returns true if a symbol's been previously marked as a
+// generator symbol through the SetIsGeneratedSym. The functions for generator
+// symbols are kept in the Link context.
+func (l *Loader) IsGeneratedSym(i Sym) bool {
+ _, ok := l.generatedSyms[i]
+ return ok
+}
+
+// SetIsGeneratedSym marks symbols as generated symbols. Data shouldn't be
+// stored in generated symbols, and a function is registered and called for
+// each of these symbols.
+func (l *Loader) SetIsGeneratedSym(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("only external symbols can be generated")
+ }
+ if v {
+ l.generatedSyms[i] = struct{}{}
+ } else {
+ delete(l.generatedSyms, i)
+ }
+}
+
+func (l *Loader) AttrCgoExport(i Sym) bool {
+ return l.AttrCgoExportDynamic(i) || l.AttrCgoExportStatic(i)
+}
+
+// AttrReadOnly returns true for a symbol whose underlying data
+// is stored via a read-only mmap.
+func (l *Loader) AttrReadOnly(i Sym) bool {
+ if v, ok := l.attrReadOnly[i]; ok {
+ return v
+ }
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ return l.objs[pp.objidx].r.ReadOnly()
+ }
+ return false
+ }
+ r, _ := l.toLocal(i)
+ return r.ReadOnly()
+}
+
+// SetAttrReadOnly sets the "data is read only" property for a symbol
+// (see AttrReadOnly).
+func (l *Loader) SetAttrReadOnly(i Sym, v bool) {
+ l.attrReadOnly[i] = v
+}
+
+// AttrSubSymbol returns true for symbols that are listed as a
+// sub-symbol of some other outer symbol. The sub/outer mechanism is
+// used when loading host objects (sections from the host object
+// become regular linker symbols and symbols go on the Sub list of
+// their section) and for constructing the global offset table when
+// internally linking a dynamic executable.
+//
+// Note that in later stages of the linker, we set Outer(S) to some
+// container symbol C, but don't set Sub(C). Thus we have two
+// distinct scenarios:
+//
+// - Outer symbol covers the address ranges of its sub-symbols.
+// Outer.Sub is set in this case.
+// - Outer symbol doesn't conver the address ranges. It is zero-sized
+// and doesn't have sub-symbols. In the case, the inner symbol is
+// not actually a "SubSymbol". (Tricky!)
+//
+// This method returns TRUE only for sub-symbols in the first scenario.
+//
+// FIXME: would be better to do away with this and have a better way
+// to represent container symbols.
+
+func (l *Loader) AttrSubSymbol(i Sym) bool {
+ // we don't explicitly store this attribute any more -- return
+ // a value based on the sub-symbol setting.
+ o := l.OuterSym(i)
+ if o == 0 {
+ return false
+ }
+ return l.SubSym(o) != 0
+}
+
+// Note that we don't have a 'SetAttrSubSymbol' method in the loader;
+// clients should instead use the AddInteriorSym method to establish
+// containment relationships for host object symbols.
+
+// Returns whether the i-th symbol has ReflectMethod attribute set.
+func (l *Loader) IsReflectMethod(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagReflectMethod != 0
+}
+
+// Returns whether the i-th symbol is nosplit.
+func (l *Loader) IsNoSplit(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagNoSplit != 0
+}
+
+// Returns whether this is a Go type symbol.
+func (l *Loader) IsGoType(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagGoType != 0
+}
+
+// Returns whether this symbol should be included in typelink.
+func (l *Loader) IsTypelink(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagTypelink != 0
+}
+
+// Returns whether this symbol is an itab symbol.
+func (l *Loader) IsItab(i Sym) bool {
+ if l.IsExternal(i) {
+ return false
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).IsItab()
+}
+
+// Return whether this is a trampoline of a deferreturn call.
+func (l *Loader) IsDeferReturnTramp(i Sym) bool {
+ return l.deferReturnTramp[i]
+}
+
+// Set that i is a trampoline of a deferreturn call.
+func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) {
+ l.deferReturnTramp[i] = v
+}
+
+// growValues grows the slice used to store symbol values.
+func (l *Loader) growValues(reqLen int) {
+ curLen := len(l.values)
+ if reqLen > curLen {
+ l.values = append(l.values, make([]int64, reqLen+1-curLen)...)
+ }
+}
+
+// SymValue returns the value of the i-th symbol. i is global index.
+func (l *Loader) SymValue(i Sym) int64 {
+ return l.values[i]
+}
+
+// SetSymValue sets the value of the i-th symbol. i is global index.
+func (l *Loader) SetSymValue(i Sym, val int64) {
+ l.values[i] = val
+}
+
+// AddToSymValue adds to the value of the i-th symbol. i is the global index.
+func (l *Loader) AddToSymValue(i Sym, val int64) {
+ l.values[i] += val
+}
+
+// Returns the symbol content of the i-th symbol. i is global index.
+func (l *Loader) Data(i Sym) []byte {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ return pp.data
+ }
+ return nil
+ }
+ r, li := l.toLocal(i)
+ return r.Data(li)
+}
+
+// FreeData clears the symbol data of an external symbol, allowing the memory
+// to be freed earlier. No-op for non-external symbols.
+// i is global index.
+func (l *Loader) FreeData(i Sym) {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ pp.data = nil
+ }
+ }
+}
+
+// SymAlign returns the alignment for a symbol.
+func (l *Loader) SymAlign(i Sym) int32 {
+ if int(i) >= len(l.align) {
+ // align is extended lazily -- it the sym in question is
+ // outside the range of the existing slice, then we assume its
+ // alignment has not yet been set.
+ return 0
+ }
+ // TODO: would it make sense to return an arch-specific
+ // alignment depending on section type? E.g. STEXT => 32,
+ // SDATA => 1, etc?
+ abits := l.align[i]
+ if abits == 0 {
+ return 0
+ }
+ return int32(1 << (abits - 1))
+}
+
+// SetSymAlign sets the alignment for a symbol.
+func (l *Loader) SetSymAlign(i Sym, align int32) {
+ // Reject nonsense alignments.
+ if align < 0 || align&(align-1) != 0 {
+ panic("bad alignment value")
+ }
+ if int(i) >= len(l.align) {
+ l.align = append(l.align, make([]uint8, l.NSym()-len(l.align))...)
+ }
+ if align == 0 {
+ l.align[i] = 0
+ }
+ l.align[i] = uint8(bits.Len32(uint32(align)))
+}
+
+// SymValue returns the section of the i-th symbol. i is global index.
+func (l *Loader) SymSect(i Sym) *sym.Section {
+ if int(i) >= len(l.symSects) {
+ // symSects is extended lazily -- it the sym in question is
+ // outside the range of the existing slice, then we assume its
+ // section has not yet been set.
+ return nil
+ }
+ return l.sects[l.symSects[i]]
+}
+
+// SetSymSect sets the section of the i-th symbol. i is global index.
+func (l *Loader) SetSymSect(i Sym, sect *sym.Section) {
+ if int(i) >= len(l.symSects) {
+ l.symSects = append(l.symSects, make([]uint16, l.NSym()-len(l.symSects))...)
+ }
+ l.symSects[i] = sect.Index
+}
+
+// growSects grows the slice used to store symbol sections.
+func (l *Loader) growSects(reqLen int) {
+ curLen := len(l.symSects)
+ if reqLen > curLen {
+ l.symSects = append(l.symSects, make([]uint16, reqLen+1-curLen)...)
+ }
+}
+
+// NewSection creates a new (output) section.
+func (l *Loader) NewSection() *sym.Section {
+ sect := new(sym.Section)
+ idx := len(l.sects)
+ if idx != int(uint16(idx)) {
+ panic("too many sections created")
+ }
+ sect.Index = uint16(idx)
+ l.sects = append(l.sects, sect)
+ return sect
+}
+
+// SymDynImplib returns the "dynimplib" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimplib(i Sym) string {
+ return l.dynimplib[i]
+}
+
+// SetSymDynimplib sets the "dynimplib" attribute for a symbol.
+func (l *Loader) SetSymDynimplib(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetDynimplib")
+ }
+ if value == "" {
+ delete(l.dynimplib, i)
+ } else {
+ l.dynimplib[i] = value
+ }
+}
+
+// SymDynimpvers returns the "dynimpvers" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimpvers(i Sym) string {
+ return l.dynimpvers[i]
+}
+
+// SetSymDynimpvers sets the "dynimpvers" attribute for a symbol.
+func (l *Loader) SetSymDynimpvers(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetDynimpvers")
+ }
+ if value == "" {
+ delete(l.dynimpvers, i)
+ } else {
+ l.dynimpvers[i] = value
+ }
+}
+
+// SymExtname returns the "extname" value for the specified
+// symbol.
+func (l *Loader) SymExtname(i Sym) string {
+ if s, ok := l.extname[i]; ok {
+ return s
+ }
+ return l.SymName(i)
+}
+
+// SetSymExtname sets the "extname" attribute for a symbol.
+func (l *Loader) SetSymExtname(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetExtname")
+ }
+ if value == "" {
+ delete(l.extname, i)
+ } else {
+ l.extname[i] = value
+ }
+}
+
+// SymElfType returns the previously recorded ELF type for a symbol
+// (used only for symbols read from shared libraries by ldshlibsyms).
+// It is not set for symbols defined by the packages being linked or
+// by symbols read by ldelf (and so is left as elf.STT_NOTYPE).
+func (l *Loader) SymElfType(i Sym) elf.SymType {
+ if et, ok := l.elfType[i]; ok {
+ return et
+ }
+ return elf.STT_NOTYPE
+}
+
+// SetSymElfType sets the elf type attribute for a symbol.
+func (l *Loader) SetSymElfType(i Sym, et elf.SymType) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymElfType")
+ }
+ if et == elf.STT_NOTYPE {
+ delete(l.elfType, i)
+ } else {
+ l.elfType[i] = et
+ }
+}
+
+// SymElfSym returns the ELF symbol index for a given loader
+// symbol, assigned during ELF symtab generation.
+func (l *Loader) SymElfSym(i Sym) int32 {
+ return l.elfSym[i]
+}
+
+// SetSymElfSym sets the elf symbol index for a symbol.
+func (l *Loader) SetSymElfSym(i Sym, es int32) {
+ if i == 0 {
+ panic("bad sym index")
+ }
+ if es == 0 {
+ delete(l.elfSym, i)
+ } else {
+ l.elfSym[i] = es
+ }
+}
+
+// SymLocalElfSym returns the "local" ELF symbol index for a given loader
+// symbol, assigned during ELF symtab generation.
+func (l *Loader) SymLocalElfSym(i Sym) int32 {
+ return l.localElfSym[i]
+}
+
+// SetSymLocalElfSym sets the "local" elf symbol index for a symbol.
+func (l *Loader) SetSymLocalElfSym(i Sym, es int32) {
+ if i == 0 {
+ panic("bad sym index")
+ }
+ if es == 0 {
+ delete(l.localElfSym, i)
+ } else {
+ l.localElfSym[i] = es
+ }
+}
+
+// SymPlt returns the plt value for pe symbols.
+func (l *Loader) SymPlt(s Sym) int32 {
+ if v, ok := l.plt[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetPlt sets the plt value for pe symbols.
+func (l *Loader) SetPlt(i Sym, v int32) {
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol for SetPlt")
+ }
+ if v == -1 {
+ delete(l.plt, i)
+ } else {
+ l.plt[i] = v
+ }
+}
+
+// SymGot returns the got value for pe symbols.
+func (l *Loader) SymGot(s Sym) int32 {
+ if v, ok := l.got[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetGot sets the got value for pe symbols.
+func (l *Loader) SetGot(i Sym, v int32) {
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol for SetGot")
+ }
+ if v == -1 {
+ delete(l.got, i)
+ } else {
+ l.got[i] = v
+ }
+}
+
+// SymDynid returns the "dynid" property for the specified symbol.
+func (l *Loader) SymDynid(i Sym) int32 {
+ if s, ok := l.dynid[i]; ok {
+ return s
+ }
+ return -1
+}
+
+// SetSymDynid sets the "dynid" property for a symbol.
+func (l *Loader) SetSymDynid(i Sym, val int32) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymDynid")
+ }
+ if val == -1 {
+ delete(l.dynid, i)
+ } else {
+ l.dynid[i] = val
+ }
+}
+
+// DynIdSyms returns the set of symbols for which dynID is set to an
+// interesting (non-default) value. This is expected to be a fairly
+// small set.
+func (l *Loader) DynidSyms() []Sym {
+ sl := make([]Sym, 0, len(l.dynid))
+ for s := range l.dynid {
+ sl = append(sl, s)
+ }
+ sort.Slice(sl, func(i, j int) bool { return sl[i] < sl[j] })
+ return sl
+}
+
+// SymGoType returns the 'Gotype' property for a given symbol (set by
+// the Go compiler for variable symbols). This version relies on
+// reading aux symbols for the target sym -- it could be that a faster
+// approach would be to check for gotype during preload and copy the
+// results in to a map (might want to try this at some point and see
+// if it helps speed things up).
+func (l *Loader) SymGoType(i Sym) Sym {
+ var r *oReader
+ var auxs []goobj.Aux
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ r = l.objs[pp.objidx].r
+ auxs = pp.auxs
+ } else {
+ var li uint32
+ r, li = l.toLocal(i)
+ auxs = r.Auxs(li)
+ }
+ for j := range auxs {
+ a := &auxs[j]
+ switch a.Type() {
+ case goobj.AuxGotype:
+ return l.resolve(r, a.Sym())
+ }
+ }
+ return 0
+}
+
+// SymUnit returns the compilation unit for a given symbol (which will
+// typically be nil for external or linker-manufactured symbols).
+func (l *Loader) SymUnit(i Sym) *sym.CompilationUnit {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ r := l.objs[pp.objidx].r
+ return r.unit
+ }
+ return nil
+ }
+ r, _ := l.toLocal(i)
+ return r.unit
+}
+
+// SymPkg returns the package where the symbol came from (for
+// regular compiler-generated Go symbols), but in the case of
+// building with "-linkshared" (when a symbol is read from a
+// shared library), will hold the library name.
+// NOTE: this correspondes to sym.Symbol.File field.
+func (l *Loader) SymPkg(i Sym) string {
+ if f, ok := l.symPkg[i]; ok {
+ return f
+ }
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ r := l.objs[pp.objidx].r
+ return r.unit.Lib.Pkg
+ }
+ return ""
+ }
+ r, _ := l.toLocal(i)
+ return r.unit.Lib.Pkg
+}
+
+// SetSymPkg sets the package/library for a symbol. This is
+// needed mainly for external symbols, specifically those imported
+// from shared libraries.
+func (l *Loader) SetSymPkg(i Sym, pkg string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymPkg")
+ }
+ l.symPkg[i] = pkg
+}
+
+// SymLocalentry returns the "local entry" value for the specified
+// symbol.
+func (l *Loader) SymLocalentry(i Sym) uint8 {
+ return l.localentry[i]
+}
+
+// SetSymLocalentry sets the "local entry" attribute for a symbol.
+func (l *Loader) SetSymLocalentry(i Sym, value uint8) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymLocalentry")
+ }
+ if value == 0 {
+ delete(l.localentry, i)
+ } else {
+ l.localentry[i] = value
+ }
+}
+
+// Returns the number of aux symbols given a global index.
+func (l *Loader) NAux(i Sym) int {
+ if l.IsExternal(i) {
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return r.NAux(li)
+}
+
+// Returns the "handle" to the j-th aux symbol of the i-th symbol.
+func (l *Loader) Aux(i Sym, j int) Aux {
+ if l.IsExternal(i) {
+ return Aux{}
+ }
+ r, li := l.toLocal(i)
+ if j >= r.NAux(li) {
+ return Aux{}
+ }
+ return Aux{r.Aux(li, j), r, l}
+}
+
+// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF
+// symbols associated with a given function symbol. Prior to the
+// introduction of the loader, this was done purely using name
+// lookups, e.f. for function with name XYZ we would then look up
+// go.info.XYZ, etc.
+func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) {
+ if l.SymType(fnSymIdx) != sym.STEXT {
+ log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
+ }
+ if l.IsExternal(fnSymIdx) {
+ // Current expectation is that any external function will
+ // not have auxsyms.
+ return
+ }
+ r, li := l.toLocal(fnSymIdx)
+ auxs := r.Auxs(li)
+ for i := range auxs {
+ a := &auxs[i]
+ switch a.Type() {
+ case goobj.AuxDwarfInfo:
+ auxDwarfInfo = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfInfo) != sym.SDWARFFCN {
+ panic("aux dwarf info sym with wrong type")
+ }
+ case goobj.AuxDwarfLoc:
+ auxDwarfLoc = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfLoc) != sym.SDWARFLOC {
+ panic("aux dwarf loc sym with wrong type")
+ }
+ case goobj.AuxDwarfRanges:
+ auxDwarfRanges = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE {
+ panic("aux dwarf ranges sym with wrong type")
+ }
+ case goobj.AuxDwarfLines:
+ auxDwarfLines = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfLines) != sym.SDWARFLINES {
+ panic("aux dwarf lines sym with wrong type")
+ }
+ }
+ }
+ return
+}
+
+// AddInteriorSym sets up 'interior' as an interior symbol of
+// container/payload symbol 'container'. An interior symbol does not
+// itself have data, but gives a name to a subrange of the data in its
+// container symbol. The container itself may or may not have a name.
+// This method is intended primarily for use in the host object
+// loaders, to capture the semantics of symbols and sections in an
+// object file. When reading a host object file, we'll typically
+// encounter a static section symbol (ex: ".text") containing content
+// for a collection of functions, then a series of ELF (or macho, etc)
+// symbol table entries each of which points into a sub-section
+// (offset and length) of its corresponding container symbol. Within
+// the go linker we create a loader.Sym for the container (which is
+// expected to have the actual content/payload) and then a set of
+// interior loader.Sym's that point into a portion of the container.
+func (l *Loader) AddInteriorSym(container Sym, interior Sym) {
+ // Container symbols are expected to have content/data.
+ // NB: this restriction may turn out to be too strict (it's possible
+ // to imagine a zero-sized container with an interior symbol pointing
+ // into it); it's ok to relax or remove it if we counter an
+ // oddball host object that triggers this.
+ if l.SymSize(container) == 0 && len(l.Data(container)) == 0 {
+ panic("unexpected empty container symbol")
+ }
+ // The interior symbols for a container are not expected to have
+ // content/data or relocations.
+ if len(l.Data(interior)) != 0 {
+ panic("unexpected non-empty interior symbol")
+ }
+ // Interior symbol is expected to be in the symbol table.
+ if l.AttrNotInSymbolTable(interior) {
+ panic("interior symbol must be in symtab")
+ }
+ // Only a single level of containment is allowed.
+ if l.OuterSym(container) != 0 {
+ panic("outer has outer itself")
+ }
+ // Interior sym should not already have a sibling.
+ if l.SubSym(interior) != 0 {
+ panic("sub set for subsym")
+ }
+ // Interior sym should not already point at a container.
+ if l.OuterSym(interior) != 0 {
+ panic("outer already set for subsym")
+ }
+ l.sub[interior] = l.sub[container]
+ l.sub[container] = interior
+ l.outer[interior] = container
+}
+
+// OuterSym gets the outer symbol for host object loaded symbols.
+func (l *Loader) OuterSym(i Sym) Sym {
+ // FIXME: add check for isExternal?
+ return l.outer[i]
+}
+
+// SubSym gets the subsymbol for host object loaded symbols.
+func (l *Loader) SubSym(i Sym) Sym {
+ // NB: note -- no check for l.isExternal(), since I am pretty sure
+ // that later phases in the linker set subsym for "type." syms
+ return l.sub[i]
+}
+
+// SetCarrierSym declares that 'c' is the carrier or container symbol
+// for 's'. Carrier symbols are used in the linker to as a container
+// for a collection of sub-symbols where the content of the
+// sub-symbols is effectively concatenated to form the content of the
+// carrier. The carrier is given a name in the output symbol table
+// while the sub-symbol names are not. For example, the Go compiler
+// emits named string symbols (type SGOSTRING) when compiling a
+// package; after being deduplicated, these symbols are collected into
+// a single unit by assigning them a new carrier symbol named
+// "go.string.*" (which appears in the final symbol table for the
+// output load module).
+func (l *Loader) SetCarrierSym(s Sym, c Sym) {
+ if c == 0 {
+ panic("invalid carrier in SetCarrierSym")
+ }
+ if s == 0 {
+ panic("invalid sub-symbol in SetCarrierSym")
+ }
+ // Carrier symbols are not expected to have content/data. It is
+ // ok for them to have non-zero size (to allow for use of generator
+ // symbols).
+ if len(l.Data(c)) != 0 {
+ panic("unexpected non-empty carrier symbol")
+ }
+ l.outer[s] = c
+ // relocsym's foldSubSymbolOffset requires that we only
+ // have a single level of containment-- enforce here.
+ if l.outer[c] != 0 {
+ panic("invalid nested carrier sym")
+ }
+}
+
+// Initialize Reachable bitmap and its siblings for running deadcode pass.
+func (l *Loader) InitReachable() {
+ l.growAttrBitmaps(l.NSym() + 1)
+}
+
+type symWithVal struct {
+ s Sym
+ v int64
+}
+type bySymValue []symWithVal
+
+func (s bySymValue) Len() int { return len(s) }
+func (s bySymValue) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s bySymValue) Less(i, j int) bool { return s[i].v < s[j].v }
+
+// SortSub walks through the sub-symbols for 's' and sorts them
+// in place by increasing value. Return value is the new
+// sub symbol for the specified outer symbol.
+func (l *Loader) SortSub(s Sym) Sym {
+
+ if s == 0 || l.sub[s] == 0 {
+ return s
+ }
+
+ // Sort symbols using a slice first. Use a stable sort on the off
+ // chance that there's more than once symbol with the same value,
+ // so as to preserve reproducible builds.
+ sl := []symWithVal{}
+ for ss := l.sub[s]; ss != 0; ss = l.sub[ss] {
+ sl = append(sl, symWithVal{s: ss, v: l.SymValue(ss)})
+ }
+ sort.Stable(bySymValue(sl))
+
+ // Then apply any changes needed to the sub map.
+ ns := Sym(0)
+ for i := len(sl) - 1; i >= 0; i-- {
+ s := sl[i].s
+ l.sub[s] = ns
+ ns = s
+ }
+
+ // Update sub for outer symbol, then return
+ l.sub[s] = sl[0].s
+ return sl[0].s
+}
+
+// SortSyms sorts a list of symbols by their value.
+func (l *Loader) SortSyms(ss []Sym) {
+ sort.SliceStable(ss, func(i, j int) bool { return l.SymValue(ss[i]) < l.SymValue(ss[j]) })
+}
+
+// Insure that reachable bitmap and its siblings have enough size.
+func (l *Loader) growAttrBitmaps(reqLen int) {
+ if reqLen > l.attrReachable.Len() {
+ // These are indexed by global symbol
+ l.attrReachable = growBitmap(reqLen, l.attrReachable)
+ l.attrOnList = growBitmap(reqLen, l.attrOnList)
+ l.attrLocal = growBitmap(reqLen, l.attrLocal)
+ l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable)
+ l.attrUsedInIface = growBitmap(reqLen, l.attrUsedInIface)
+ }
+ l.growExtAttrBitmaps()
+}
+
+func (l *Loader) growExtAttrBitmaps() {
+ // These are indexed by external symbol index (e.g. l.extIndex(i))
+ extReqLen := len(l.payloads)
+ if extReqLen > l.attrVisibilityHidden.Len() {
+ l.attrVisibilityHidden = growBitmap(extReqLen, l.attrVisibilityHidden)
+ l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK)
+ l.attrShared = growBitmap(extReqLen, l.attrShared)
+ l.attrExternal = growBitmap(extReqLen, l.attrExternal)
+ }
+}
+
+func (relocs *Relocs) Count() int { return len(relocs.rs) }
+
+// At returns the j-th reloc for a global symbol.
+func (relocs *Relocs) At(j int) Reloc {
+ if relocs.l.isExtReader(relocs.r) {
+ pp := relocs.l.payloads[relocs.li]
+ return Reloc{&relocs.rs[j], relocs.r, relocs.l, pp.reltypes[j]}
+ }
+ return Reloc{&relocs.rs[j], relocs.r, relocs.l, 0}
+}
+
+// Relocs returns a Relocs object for the given global sym.
+func (l *Loader) Relocs(i Sym) Relocs {
+ r, li := l.toLocal(i)
+ if r == nil {
+ panic(fmt.Sprintf("trying to get oreader for invalid sym %d\n\n", i))
+ }
+ return l.relocs(r, li)
+}
+
+// Relocs returns a Relocs object given a local sym index and reader.
+func (l *Loader) relocs(r *oReader, li uint32) Relocs {
+ var rs []goobj.Reloc
+ if l.isExtReader(r) {
+ pp := l.payloads[li]
+ rs = pp.relocs
+ } else {
+ rs = r.Relocs(li)
+ }
+ return Relocs{
+ rs: rs,
+ li: li,
+ r: r,
+ l: l,
+ }
+}
+
+// FuncInfo provides hooks to access goobj.FuncInfo in the objects.
+type FuncInfo struct {
+ l *Loader
+ r *oReader
+ data []byte
+ auxs []goobj.Aux
+ lengths goobj.FuncInfoLengths
+}
+
+func (fi *FuncInfo) Valid() bool { return fi.r != nil }
+
+func (fi *FuncInfo) Args() int {
+ return int((*goobj.FuncInfo)(nil).ReadArgs(fi.data))
+}
+
+func (fi *FuncInfo) Locals() int {
+ return int((*goobj.FuncInfo)(nil).ReadLocals(fi.data))
+}
+
+func (fi *FuncInfo) FuncID() objabi.FuncID {
+ return objabi.FuncID((*goobj.FuncInfo)(nil).ReadFuncID(fi.data))
+}
+
+func (fi *FuncInfo) Pcsp() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcfile() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcline() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcinline() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+// Preload has to be called prior to invoking the various methods
+// below related to pcdata, funcdataoff, files, and inltree nodes.
+func (fi *FuncInfo) Preload() {
+ fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data)
+}
+
+func (fi *FuncInfo) Pcdata() []Sym {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ syms := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data)
+ ret := make([]Sym, len(syms))
+ for i := range ret {
+ ret[i] = fi.l.resolve(fi.r, syms[i])
+ }
+ return ret
+}
+
+func (fi *FuncInfo) NumFuncdataoff() uint32 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return fi.lengths.NumFuncdataoff
+}
+
+func (fi *FuncInfo) Funcdataoff(k int) int64 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return (*goobj.FuncInfo)(nil).ReadFuncdataoff(fi.data, fi.lengths.FuncdataoffOff, uint32(k))
+}
+
+func (fi *FuncInfo) Funcdata(syms []Sym) []Sym {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ if int(fi.lengths.NumFuncdataoff) > cap(syms) {
+ syms = make([]Sym, 0, fi.lengths.NumFuncdataoff)
+ } else {
+ syms = syms[:0]
+ }
+ for j := range fi.auxs {
+ a := &fi.auxs[j]
+ if a.Type() == goobj.AuxFuncdata {
+ syms = append(syms, fi.l.resolve(fi.r, a.Sym()))
+ }
+ }
+ return syms
+}
+
+func (fi *FuncInfo) NumFile() uint32 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return fi.lengths.NumFile
+}
+
+func (fi *FuncInfo) File(k int) goobj.CUFileIndex {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return (*goobj.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
+}
+
+type InlTreeNode struct {
+ Parent int32
+ File goobj.CUFileIndex
+ Line int32
+ Func Sym
+ ParentPC int32
+}
+
+func (fi *FuncInfo) NumInlTree() uint32 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return fi.lengths.NumInlTree
+}
+
+func (fi *FuncInfo) InlTree(k int) InlTreeNode {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ node := (*goobj.FuncInfo)(nil).ReadInlTree(fi.data, fi.lengths.InlTreeOff, uint32(k))
+ return InlTreeNode{
+ Parent: node.Parent,
+ File: node.File,
+ Line: node.Line,
+ Func: fi.l.resolve(fi.r, node.Func),
+ ParentPC: node.ParentPC,
+ }
+}
+
+func (l *Loader) FuncInfo(i Sym) FuncInfo {
+ var r *oReader
+ var auxs []goobj.Aux
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx == 0 {
+ return FuncInfo{}
+ }
+ r = l.objs[pp.objidx].r
+ auxs = pp.auxs
+ } else {
+ var li uint32
+ r, li = l.toLocal(i)
+ auxs = r.Auxs(li)
+ }
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxFuncInfo {
+ b := r.Data(a.Sym().SymIdx)
+ return FuncInfo{l, r, b, auxs, goobj.FuncInfoLengths{}}
+ }
+ }
+ return FuncInfo{}
+}
+
+// Preload a package: adds autolib.
+// Does not add defined package or non-packaged symbols to the symbol table.
+// These are done in LoadSyms.
+// Does not read symbol data.
+// Returns the fingerprint of the object.
+func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj.FingerprintType {
+ roObject, readonly, err := f.Slice(uint64(length)) // TODO: no need to map blocks that are for tools only (e.g. RefName)
+ if err != nil {
+ log.Fatal("cannot read object file:", err)
+ }
+ r := goobj.NewReaderFromBytes(roObject, readonly)
+ if r == nil {
+ if len(roObject) >= 8 && bytes.Equal(roObject[:8], []byte("\x00go114ld")) {
+ log.Fatalf("found object file %s in old format", f.File().Name())
+ }
+ panic("cannot read object file")
+ }
+ pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+ ndef := r.NSym()
+ nhashed64def := r.NHashed64def()
+ nhasheddef := r.NHasheddef()
+ or := &oReader{
+ Reader: r,
+ unit: unit,
+ version: localSymVersion,
+ flags: r.Flags(),
+ pkgprefix: pkgprefix,
+ syms: make([]Sym, ndef+nhashed64def+nhasheddef+r.NNonpkgdef()+r.NNonpkgref()),
+ ndef: ndef,
+ nhasheddef: nhasheddef,
+ nhashed64def: nhashed64def,
+ objidx: uint32(len(l.objs)),
+ }
+
+ // Autolib
+ lib.Autolib = append(lib.Autolib, r.Autolib()...)
+
+ // DWARF file table
+ nfile := r.NFile()
+ unit.FileTable = make([]string, nfile)
+ for i := range unit.FileTable {
+ unit.FileTable[i] = r.File(i)
+ }
+
+ l.addObj(lib.Pkg, or)
+
+ // The caller expects us consuming all the data
+ f.MustSeek(length, os.SEEK_CUR)
+
+ return r.Fingerprint()
+}
+
+// Holds the loader along with temporary states for loading symbols.
+type loadState struct {
+ l *Loader
+ hashed64Syms map[uint64]symAndSize // short hashed (content-addressable) symbols, keyed by content hash
+ hashedSyms map[goobj.HashType]symAndSize // hashed (content-addressable) symbols, keyed by content hash
+}
+
+// Preload symbols of given kind from an object.
+func (st *loadState) preloadSyms(r *oReader, kind int) {
+ l := st.l
+ var start, end uint32
+ switch kind {
+ case pkgDef:
+ start = 0
+ end = uint32(r.ndef)
+ case hashed64Def:
+ start = uint32(r.ndef)
+ end = uint32(r.ndef + r.nhashed64def)
+ case hashedDef:
+ start = uint32(r.ndef + r.nhashed64def)
+ end = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
+ if l.hasUnknownPkgPath {
+ // The content hash depends on symbol name expansion. If any package is
+ // built without fully expanded names, the content hash is unreliable.
+ // Treat them as named symbols.
+ // This is rare.
+ // (We don't need to do this for hashed64Def case, as there the hash
+ // function is simply the identity function, which doesn't depend on
+ // name expansion.)
+ kind = nonPkgDef
+ }
+ case nonPkgDef:
+ start = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
+ end = uint32(r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef())
+ default:
+ panic("preloadSyms: bad kind")
+ }
+ l.growAttrBitmaps(len(l.objSyms) + int(end-start))
+ needNameExpansion := r.NeedNameExpansion()
+ loadingRuntimePkg := r.unit.Lib.Pkg == "runtime"
+ for i := start; i < end; i++ {
+ osym := r.Sym(i)
+ var name string
+ var v int
+ if kind != hashed64Def && kind != hashedDef { // we don't need the name, etc. for hashed symbols
+ name = osym.Name(r.Reader)
+ if needNameExpansion {
+ name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
+ }
+ v = abiToVer(osym.ABI(), r.version)
+ }
+ gi := st.addSym(name, v, r, i, kind, osym)
+ r.syms[i] = gi
+ if osym.TopFrame() {
+ l.SetAttrTopFrame(gi, true)
+ }
+ if osym.Local() {
+ l.SetAttrLocal(gi, true)
+ }
+ if osym.UsedInIface() {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ if strings.HasPrefix(name, "runtime.") ||
+ (loadingRuntimePkg && strings.HasPrefix(name, "type.")) {
+ if bi := goobj.BuiltinIdx(name, v); bi != -1 {
+ // This is a definition of a builtin symbol. Record where it is.
+ l.builtinSyms[bi] = gi
+ }
+ }
+ if a := int32(osym.Align()); a != 0 && a > l.SymAlign(gi) {
+ l.SetSymAlign(gi, a)
+ }
+ }
+}
+
+// Add syms, hashed (content-addressable) symbols, non-package symbols, and
+// references to external symbols (which are always named).
+func (l *Loader) LoadSyms(arch *sys.Arch) {
+ // Allocate space for symbols, making a guess as to how much space we need.
+ // This function was determined empirically by looking at the cmd/compile on
+ // Darwin, and picking factors for hashed and hashed64 syms.
+ var symSize, hashedSize, hashed64Size int
+ for _, o := range l.objs[goObjStart:] {
+ symSize += o.r.ndef + o.r.nhasheddef/2 + o.r.nhashed64def/2 + o.r.NNonpkgdef()
+ hashedSize += o.r.nhasheddef / 2
+ hashed64Size += o.r.nhashed64def / 2
+ }
+ // Index 0 is invalid for symbols.
+ l.objSyms = make([]objSym, 1, symSize)
+
+ l.npkgsyms = l.NSym()
+ st := loadState{
+ l: l,
+ hashed64Syms: make(map[uint64]symAndSize, hashed64Size),
+ hashedSyms: make(map[goobj.HashType]symAndSize, hashedSize),
+ }
+
+ for _, o := range l.objs[goObjStart:] {
+ st.preloadSyms(o.r, pkgDef)
+ }
+ for _, o := range l.objs[goObjStart:] {
+ st.preloadSyms(o.r, hashed64Def)
+ st.preloadSyms(o.r, hashedDef)
+ st.preloadSyms(o.r, nonPkgDef)
+ }
+ l.nhashedsyms = len(st.hashed64Syms) + len(st.hashedSyms)
+ for _, o := range l.objs[goObjStart:] {
+ loadObjRefs(l, o.r, arch)
+ }
+ l.values = make([]int64, l.NSym(), l.NSym()+1000) // +1000 make some room for external symbols
+}
+
+func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
+ // load non-package refs
+ ndef := uint32(r.NAlldef())
+ needNameExpansion := r.NeedNameExpansion()
+ for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ {
+ osym := r.Sym(ndef + i)
+ name := osym.Name(r.Reader)
+ if needNameExpansion {
+ name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
+ }
+ v := abiToVer(osym.ABI(), r.version)
+ r.syms[ndef+i] = l.LookupOrCreateSym(name, v)
+ gi := r.syms[ndef+i]
+ if osym.Local() {
+ l.SetAttrLocal(gi, true)
+ }
+ if osym.UsedInIface() {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ }
+
+ // referenced packages
+ npkg := r.NPkg()
+ r.pkg = make([]uint32, npkg)
+ for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package
+ pkg := r.Pkg(i)
+ objidx, ok := l.objByPkg[pkg]
+ if !ok {
+ log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
+ }
+ r.pkg[i] = objidx
+ }
+
+ // load flags of package refs
+ for i, n := 0, r.NRefFlags(); i < n; i++ {
+ rf := r.RefFlags(i)
+ gi := l.resolve(r, rf.Sym())
+ if rf.Flag2()&goobj.SymFlagUsedInIface != 0 {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ }
+}
+
+func abiToVer(abi uint16, localSymVersion int) int {
+ var v int
+ if abi == goobj.SymABIstatic {
+ // Static
+ v = localSymVersion
+ } else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
+ // Note that data symbols are "ABI0", which maps to version 0.
+ v = abiver
+ } else {
+ log.Fatalf("invalid symbol ABI: %d", abi)
+ }
+ return v
+}
+
+// ResolveABIAlias given a symbol returns the ABI alias target of that
+// symbol. If the sym in question is not an alias, the sym itself is
+// returned.
+func (l *Loader) ResolveABIAlias(s Sym) Sym {
+ if s == 0 {
+ return 0
+ }
+ if l.SymType(s) != sym.SABIALIAS {
+ return s
+ }
+ relocs := l.Relocs(s)
+ target := relocs.At(0).Sym()
+ if l.SymType(target) == sym.SABIALIAS {
+ panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", l.SymName(s), l.SymName(target)))
+ }
+ return target
+}
+
+// TopLevelSym tests a symbol (by name and kind) to determine whether
+// the symbol first class sym (participating in the link) or is an
+// anonymous aux or sub-symbol containing some sub-part or payload of
+// another symbol.
+func (l *Loader) TopLevelSym(s Sym) bool {
+ return topLevelSym(l.RawSymName(s), l.SymType(s))
+}
+
+// topLevelSym tests a symbol name and kind to determine whether
+// the symbol first class sym (participating in the link) or is an
+// anonymous aux or sub-symbol containing some sub-part or payload of
+// another symbol.
+func topLevelSym(sname string, skind sym.SymKind) bool {
+ if sname != "" {
+ return true
+ }
+ switch skind {
+ case sym.SDWARFFCN, sym.SDWARFABSFCN, sym.SDWARFTYPE, sym.SDWARFCONST, sym.SDWARFCUINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES, sym.SGOFUNC:
+ return true
+ default:
+ return false
+ }
+}
+
+// cloneToExternal takes the existing object file symbol (symIdx)
+// and creates a new external symbol payload that is a clone with
+// respect to name, version, type, relocations, etc. The idea here
+// is that if the linker decides it wants to update the contents of
+// a symbol originally discovered as part of an object file, it's
+// easier to do this if we make the updates to an external symbol
+// payload.
+func (l *Loader) cloneToExternal(symIdx Sym) {
+ if l.IsExternal(symIdx) {
+ panic("sym is already external, no need for clone")
+ }
+
+ // Read the particulars from object.
+ r, li := l.toLocal(symIdx)
+ osym := r.Sym(li)
+ sname := osym.Name(r.Reader)
+ if r.NeedNameExpansion() {
+ sname = strings.Replace(sname, "\"\".", r.pkgprefix, -1)
+ }
+ sver := abiToVer(osym.ABI(), r.version)
+ skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+
+ // Create new symbol, update version and kind.
+ pi := l.newPayload(sname, sver)
+ pp := l.payloads[pi]
+ pp.kind = skind
+ pp.ver = sver
+ pp.size = int64(osym.Siz())
+ pp.objidx = r.objidx
+
+ // If this is a def, then copy the guts. We expect this case
+ // to be very rare (one case it may come up is with -X).
+ if li < uint32(r.NAlldef()) {
+
+ // Copy relocations
+ relocs := l.Relocs(symIdx)
+ pp.relocs = make([]goobj.Reloc, relocs.Count())
+ pp.reltypes = make([]objabi.RelocType, relocs.Count())
+ for i := range pp.relocs {
+ // Copy the relocs slice.
+ // Convert local reference to global reference.
+ rel := relocs.At(i)
+ pp.relocs[i].Set(rel.Off(), rel.Siz(), 0, rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
+ pp.reltypes[i] = rel.Type()
+ }
+
+ // Copy data
+ pp.data = r.Data(li)
+ }
+
+ // If we're overriding a data symbol, collect the associated
+ // Gotype, so as to propagate it to the new symbol.
+ auxs := r.Auxs(li)
+ pp.auxs = auxs
+
+ // Install new payload to global index space.
+ // (This needs to happen at the end, as the accessors above
+ // need to access the old symbol content.)
+ l.objSyms[symIdx] = objSym{l.extReader.objidx, uint32(pi)}
+ l.extReader.syms = append(l.extReader.syms, symIdx)
+}
+
+// Copy the payload of symbol src to dst. Both src and dst must be external
+// symbols.
+// The intended use case is that when building/linking against a shared library,
+// where we do symbol name mangling, the Go object file may have reference to
+// the original symbol name whereas the shared library provides a symbol with
+// the mangled name. When we do mangling, we copy payload of mangled to original.
+func (l *Loader) CopySym(src, dst Sym) {
+ if !l.IsExternal(dst) {
+ panic("dst is not external") //l.newExtSym(l.SymName(dst), l.SymVersion(dst))
+ }
+ if !l.IsExternal(src) {
+ panic("src is not external") //l.cloneToExternal(src)
+ }
+ l.payloads[l.extIndex(dst)] = l.payloads[l.extIndex(src)]
+ l.SetSymPkg(dst, l.SymPkg(src))
+ // TODO: other attributes?
+}
+
+// CopyAttributes copies over all of the attributes of symbol 'src' to
+// symbol 'dst'.
+func (l *Loader) CopyAttributes(src Sym, dst Sym) {
+ l.SetAttrReachable(dst, l.AttrReachable(src))
+ l.SetAttrOnList(dst, l.AttrOnList(src))
+ l.SetAttrLocal(dst, l.AttrLocal(src))
+ l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src))
+ if l.IsExternal(dst) {
+ l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src))
+ l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src))
+ l.SetAttrShared(dst, l.AttrShared(src))
+ l.SetAttrExternal(dst, l.AttrExternal(src))
+ } else {
+ // Some attributes are modifiable only for external symbols.
+ // In such cases, don't try to transfer over the attribute
+ // from the source even if there is a clash. This comes up
+ // when copying attributes from a dupOK ABI wrapper symbol to
+ // the real target symbol (which may not be marked dupOK).
+ }
+ l.SetAttrTopFrame(dst, l.AttrTopFrame(src))
+ l.SetAttrSpecial(dst, l.AttrSpecial(src))
+ l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src))
+ l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src))
+ l.SetAttrReadOnly(dst, l.AttrReadOnly(src))
+}
+
+// CreateExtSym creates a new external symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateExtSym(name string, ver int) Sym {
+ return l.newExtSym(name, ver)
+}
+
+// CreateStaticSym creates a new static symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateStaticSym(name string) Sym {
+ // Assign a new unique negative version -- this is to mark the
+ // symbol so that it is not included in the name lookup table.
+ l.anonVersion--
+ return l.newExtSym(name, l.anonVersion)
+}
+
+func (l *Loader) FreeSym(i Sym) {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ *pp = extSymPayload{}
+ }
+}
+
+// relocId is essentially a <S,R> tuple identifying the Rth
+// relocation of symbol S.
+type relocId struct {
+ sym Sym
+ ridx int
+}
+
+// SetRelocVariant sets the 'variant' property of a relocation on
+// some specific symbol.
+func (l *Loader) SetRelocVariant(s Sym, ri int, v sym.RelocVariant) {
+ // sanity check
+ if relocs := l.Relocs(s); ri >= relocs.Count() {
+ panic("invalid relocation ID")
+ }
+ if l.relocVariant == nil {
+ l.relocVariant = make(map[relocId]sym.RelocVariant)
+ }
+ if v != 0 {
+ l.relocVariant[relocId{s, ri}] = v
+ } else {
+ delete(l.relocVariant, relocId{s, ri})
+ }
+}
+
+// RelocVariant returns the 'variant' property of a relocation on
+// some specific symbol.
+func (l *Loader) RelocVariant(s Sym, ri int) sym.RelocVariant {
+ return l.relocVariant[relocId{s, ri}]
+}
+
+// UndefinedRelocTargets iterates through the global symbol index
+// space, looking for symbols with relocations targeting undefined
+// references. The linker's loadlib method uses this to determine if
+// there are unresolved references to functions in system libraries
+// (for example, libgcc.a), presumably due to CGO code. Return
+// value is a list of loader.Sym's corresponding to the undefined
+// cross-refs. The "limit" param controls the maximum number of
+// results returned; if "limit" is -1, then all undefs are returned.
+func (l *Loader) UndefinedRelocTargets(limit int) []Sym {
+ result := []Sym{}
+ for si := Sym(1); si < Sym(len(l.objSyms)); si++ {
+ relocs := l.Relocs(si)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rs := r.Sym()
+ if rs != 0 && l.SymType(rs) == sym.SXREF && l.RawSymName(rs) != ".got" {
+ result = append(result, rs)
+ if limit != -1 && len(result) >= limit {
+ break
+ }
+ }
+ }
+ }
+ return result
+}
+
+// AssignTextSymbolOrder populates the Textp slices within each
+// library and compilation unit, insuring that packages are laid down
+// in dependency order (internal first, then everything else). Return value
+// is a slice of all text syms.
+func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym {
+
+ // Library Textp lists should be empty at this point.
+ for _, lib := range libs {
+ if len(lib.Textp) != 0 {
+ panic("expected empty Textp slice for library")
+ }
+ if len(lib.DupTextSyms) != 0 {
+ panic("expected empty DupTextSyms slice for library")
+ }
+ }
+
+ // Used to record which dupok symbol we've assigned to a unit.
+ // Can't use the onlist attribute here because it will need to
+ // clear for the later assignment of the sym.Symbol to a unit.
+ // NB: we can convert to using onList once we no longer have to
+ // call the regular addToTextp.
+ assignedToUnit := MakeBitmap(l.NSym() + 1)
+
+ // Start off textp with reachable external syms.
+ textp := []Sym{}
+ for _, sym := range extsyms {
+ if !l.attrReachable.Has(sym) {
+ continue
+ }
+ textp = append(textp, sym)
+ }
+
+ // Walk through all text symbols from Go object files and append
+ // them to their corresponding library's textp list.
+ for _, o := range l.objs[goObjStart:] {
+ r := o.r
+ lib := r.unit.Lib
+ for i, n := uint32(0), uint32(r.NAlldef()); i < n; i++ {
+ gi := l.toGlobal(r, i)
+ if !l.attrReachable.Has(gi) {
+ continue
+ }
+ osym := r.Sym(i)
+ st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+ if st != sym.STEXT {
+ continue
+ }
+ dupok := osym.Dupok()
+ if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+ // A dupok text symbol is resolved to another package.
+ // We still need to record its presence in the current
+ // package, as the trampoline pass expects packages
+ // are laid out in dependency order.
+ lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
+ continue // symbol in different object
+ }
+ if dupok {
+ lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
+ continue
+ }
+
+ lib.Textp = append(lib.Textp, sym.LoaderSym(gi))
+ }
+ }
+
+ // Now assemble global textp, and assign text symbols to units.
+ for _, doInternal := range [2]bool{true, false} {
+ for idx, lib := range libs {
+ if intlibs[idx] != doInternal {
+ continue
+ }
+ lists := [2][]sym.LoaderSym{lib.Textp, lib.DupTextSyms}
+ for i, list := range lists {
+ for _, s := range list {
+ sym := Sym(s)
+ if l.attrReachable.Has(sym) && !assignedToUnit.Has(sym) {
+ textp = append(textp, sym)
+ unit := l.SymUnit(sym)
+ if unit != nil {
+ unit.Textp = append(unit.Textp, s)
+ assignedToUnit.Set(sym)
+ }
+ // Dupok symbols may be defined in multiple packages; the
+ // associated package for a dupok sym is chosen sort of
+ // arbitrarily (the first containing package that the linker
+ // loads). Canonicalizes its Pkg to the package with which
+ // it will be laid down in text.
+ if i == 1 /* DupTextSyms2 */ && l.SymPkg(sym) != lib.Pkg {
+ l.SetSymPkg(sym, lib.Pkg)
+ }
+ }
+ }
+ }
+ lib.Textp = nil
+ lib.DupTextSyms = nil
+ }
+ }
+
+ return textp
+}
+
+// ErrorReporter is a helper class for reporting errors.
+type ErrorReporter struct {
+ ldr *Loader
+ AfterErrorAction func()
+}
+
+// Errorf method logs an error message.
+//
+// After each error, the error actions function will be invoked; this
+// will either terminate the link immediately (if -h option given)
+// or it will keep a count and exit if more than 20 errors have been printed.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+//
+func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) {
+ if s != 0 && reporter.ldr.SymName(s) != "" {
+ format = reporter.ldr.SymName(s) + ": " + format
+ } else {
+ format = fmt.Sprintf("sym %d: %s", s, format)
+ }
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ reporter.AfterErrorAction()
+}
+
+// GetErrorReporter returns the loader's associated error reporter.
+func (l *Loader) GetErrorReporter() *ErrorReporter {
+ return l.errorReporter
+}
+
+// Errorf method logs an error message. See ErrorReporter.Errorf for details.
+func (l *Loader) Errorf(s Sym, format string, args ...interface{}) {
+ l.errorReporter.Errorf(s, format, args...)
+}
+
+// Symbol statistics.
+func (l *Loader) Stat() string {
+ s := fmt.Sprintf("%d symbols, %d reachable\n", l.NSym(), l.NReachableSym())
+ s += fmt.Sprintf("\t%d package symbols, %d hashed symbols, %d non-package symbols, %d external symbols\n",
+ l.npkgsyms, l.nhashedsyms, int(l.extStart)-l.npkgsyms-l.nhashedsyms, l.NSym()-int(l.extStart))
+ return s
+}
+
+// For debugging.
+func (l *Loader) Dump() {
+ fmt.Println("objs")
+ for _, obj := range l.objs[goObjStart:] {
+ if obj.r != nil {
+ fmt.Println(obj.i, obj.r.unit.Lib)
+ }
+ }
+ fmt.Println("extStart:", l.extStart)
+ fmt.Println("Nsyms:", len(l.objSyms))
+ fmt.Println("syms")
+ for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
+ pi := ""
+ if l.IsExternal(i) {
+ pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
+ }
+ sect := ""
+ if l.SymSect(i) != nil {
+ sect = l.SymSect(i).Name
+ }
+ fmt.Printf("%v %v %v %v %x %v\n", i, l.SymName(i), l.SymType(i), pi, l.SymValue(i), sect)
+ }
+ fmt.Println("symsByName")
+ for name, i := range l.symsByName[0] {
+ fmt.Println(i, name, 0)
+ }
+ for name, i := range l.symsByName[1] {
+ fmt.Println(i, name, 1)
+ }
+ fmt.Println("payloads:")
+ for i := range l.payloads {
+ pp := l.payloads[i]
+ fmt.Println(i, pp.name, pp.ver, pp.kind)
+ }
+}
diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go
new file mode 100644
index 0000000..1371c2a
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader_test.go
@@ -0,0 +1,454 @@
+// 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 loader
+
+import (
+ "bytes"
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "fmt"
+ "testing"
+)
+
+// dummyAddSym adds the named symbol to the loader as if it had been
+// read from a Go object file. Note that it allocates a global
+// index without creating an associated object reader, so one can't
+// do anything interesting with this symbol (such as look at its
+// data or relocations).
+func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym {
+ idx := uint32(len(ldr.objSyms))
+ st := loadState{l: ldr}
+ return st.addSym(name, 0, or, idx, nonPkgDef, &goobj.Sym{})
+}
+
+func mkLoader() *Loader {
+ edummy := func(str string, off int) {}
+ er := ErrorReporter{}
+ ldr := NewLoader(0, edummy, &er)
+ er.ldr = ldr
+ return ldr
+}
+
+func TestAddMaterializedSymbol(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Create some syms from a dummy object file symbol to get things going.
+ ts1 := addDummyObjSym(t, ldr, or, "type.uint8")
+ ts2 := addDummyObjSym(t, ldr, or, "mumble")
+ ts3 := addDummyObjSym(t, ldr, or, "type.string")
+
+ // Create some external symbols.
+ es1 := ldr.LookupOrCreateSym("extnew1", 0)
+ if es1 == 0 {
+ t.Fatalf("LookupOrCreateSym failed for extnew1")
+ }
+ es1x := ldr.LookupOrCreateSym("extnew1", 0)
+ if es1x != es1 {
+ t.Fatalf("LookupOrCreateSym lookup: expected %d got %d for second lookup", es1, es1x)
+ }
+ es2 := ldr.LookupOrCreateSym("go.info.type.uint8", 0)
+ if es2 == 0 {
+ t.Fatalf("LookupOrCreateSym failed for go.info.type.uint8")
+ }
+ // Create a nameless symbol
+ es3 := ldr.CreateStaticSym("")
+ if es3 == 0 {
+ t.Fatalf("CreateStaticSym failed for nameless sym")
+ }
+
+ // Grab symbol builder pointers
+ sb1 := ldr.MakeSymbolUpdater(es1)
+ sb2 := ldr.MakeSymbolUpdater(es2)
+ sb3 := ldr.MakeSymbolUpdater(es3)
+
+ // Suppose we create some more symbols, which triggers a grow.
+ // Make sure the symbol builder's payload pointer is valid,
+ // even across a grow.
+ for i := 0; i < 9999; i++ {
+ ldr.CreateStaticSym("dummy")
+ }
+
+ // Check get/set symbol type
+ es3typ := sb3.Type()
+ if es3typ != sym.Sxxx {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ)
+ }
+ sb3.SetType(sym.SRODATA)
+ es3typ = sb3.Type()
+ if es3typ != sym.SRODATA {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+ }
+ es3typ = ldr.SymType(es3)
+ if es3typ != sym.SRODATA {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+ }
+
+ // New symbols should not initially be reachable.
+ if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) {
+ t.Errorf("newly materialized symbols should not be reachable")
+ }
+
+ // ... however it should be possible to set/unset their reachability.
+ ldr.SetAttrReachable(es3, true)
+ if !ldr.AttrReachable(es3) {
+ t.Errorf("expected reachable symbol after update")
+ }
+ ldr.SetAttrReachable(es3, false)
+ if ldr.AttrReachable(es3) {
+ t.Errorf("expected unreachable symbol after update")
+ }
+
+ // Test expansion of attr bitmaps
+ for idx := 0; idx < 36; idx++ {
+ es := ldr.LookupOrCreateSym(fmt.Sprintf("zext%d", idx), 0)
+ if ldr.AttrOnList(es) {
+ t.Errorf("expected OnList after creation")
+ }
+ ldr.SetAttrOnList(es, true)
+ if !ldr.AttrOnList(es) {
+ t.Errorf("expected !OnList after update")
+ }
+ if ldr.AttrDuplicateOK(es) {
+ t.Errorf("expected DupOK after creation")
+ }
+ ldr.SetAttrDuplicateOK(es, true)
+ if !ldr.AttrDuplicateOK(es) {
+ t.Errorf("expected !DupOK after update")
+ }
+ }
+
+ sb1 = ldr.MakeSymbolUpdater(es1)
+ sb2 = ldr.MakeSymbolUpdater(es2)
+
+ // Get/set a few other attributes
+ if ldr.AttrVisibilityHidden(es3) {
+ t.Errorf("expected initially not hidden")
+ }
+ ldr.SetAttrVisibilityHidden(es3, true)
+ if !ldr.AttrVisibilityHidden(es3) {
+ t.Errorf("expected hidden after update")
+ }
+
+ // Test get/set symbol value.
+ toTest := []Sym{ts2, es3}
+ for i, s := range toTest {
+ if v := ldr.SymValue(s); v != 0 {
+ t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v)
+ }
+ nv := int64(i + 101)
+ ldr.SetSymValue(s, nv)
+ if v := ldr.SymValue(s); v != nv {
+ t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v)
+ }
+ }
+
+ // Check/set alignment
+ es3al := ldr.SymAlign(es3)
+ if es3al != 0 {
+ t.Errorf("SymAlign(es3): expected 0, got %d", es3al)
+ }
+ ldr.SetSymAlign(es3, 128)
+ es3al = ldr.SymAlign(es3)
+ if es3al != 128 {
+ t.Errorf("SymAlign(es3): expected 128, got %d", es3al)
+ }
+
+ // Add some relocations to the new symbols.
+ r1, _ := sb1.AddRel(objabi.R_ADDR)
+ r1.SetOff(0)
+ r1.SetSiz(1)
+ r1.SetSym(ts1)
+ r2, _ := sb1.AddRel(objabi.R_CALL)
+ r2.SetOff(3)
+ r2.SetSiz(8)
+ r2.SetSym(ts2)
+ r3, _ := sb2.AddRel(objabi.R_USETYPE)
+ r3.SetOff(7)
+ r3.SetSiz(1)
+ r3.SetSym(ts3)
+
+ // Add some data to the symbols.
+ d1 := []byte{1, 2, 3}
+ d2 := []byte{4, 5, 6, 7}
+ sb1.AddBytes(d1)
+ sb2.AddBytes(d2)
+
+ // Now invoke the usual loader interfaces to make sure
+ // we're getting the right things back for these symbols.
+ // First relocations...
+ expRel := [][]Reloc{{r1, r2}, {r3}}
+ for k, sb := range []*SymbolBuilder{sb1, sb2} {
+ rsl := sb.Relocs()
+ exp := expRel[k]
+ if !sameRelocSlice(&rsl, exp) {
+ t.Errorf("expected relocs %v, got %v", exp, rsl)
+ }
+ }
+
+ // ... then data.
+ dat := sb2.Data()
+ if bytes.Compare(dat, d2) != 0 {
+ t.Errorf("expected es2 data %v, got %v", d2, dat)
+ }
+
+ // Nameless symbol should still be nameless.
+ es3name := ldr.RawSymName(es3)
+ if "" != es3name {
+ t.Errorf("expected es3 name of '', got '%s'", es3name)
+ }
+
+ // Read value of materialized symbol.
+ es1val := sb1.Value()
+ if 0 != es1val {
+ t.Errorf("expected es1 value of 0, got %v", es1val)
+ }
+
+ // Test other misc methods
+ irm := ldr.IsReflectMethod(es1)
+ if 0 != es1val {
+ t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm)
+ }
+}
+
+func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool {
+ if s1.Count() != len(s2) {
+ return false
+ }
+ for i := 0; i < s1.Count(); i++ {
+ r1 := s1.At(i)
+ r2 := &s2[i]
+ if r1.Sym() != r2.Sym() ||
+ r1.Type() != r2.Type() ||
+ r1.Off() != r2.Off() ||
+ r1.Add() != r2.Add() ||
+ r1.Siz() != r2.Siz() {
+ return false
+ }
+ }
+ return true
+}
+
+type addFunc func(l *Loader, s Sym, s2 Sym) Sym
+
+func mkReloc(l *Loader, typ objabi.RelocType, off int32, siz uint8, add int64, sym Sym) Reloc {
+ r := Reloc{&goobj.Reloc{}, l.extReader, l, typ}
+ r.SetOff(off)
+ r.SetSiz(siz)
+ r.SetAdd(add)
+ r.SetSym(sym)
+ return r
+}
+
+func TestAddDataMethods(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Populate loader with some symbols.
+ addDummyObjSym(t, ldr, or, "type.uint8")
+ ldr.LookupOrCreateSym("hello", 0)
+
+ arch := sys.ArchAMD64
+ var testpoints = []struct {
+ which string
+ addDataFunc addFunc
+ expData []byte
+ expKind sym.SymKind
+ expRel []Reloc
+ }{
+ {
+ which: "AddUint8",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUint8('a')
+ return s
+ },
+ expData: []byte{'a'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "AddUintXX",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUintXX(arch, 25185, 2)
+ return s
+ },
+ expData: []byte{'a', 'b'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "SetUint8",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUint8('a')
+ sb.AddUint8('b')
+ sb.SetUint8(arch, 1, 'c')
+ return s
+ },
+ expData: []byte{'a', 'c'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "AddString",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.Addstring("hello")
+ return s
+ },
+ expData: []byte{'h', 'e', 'l', 'l', 'o', 0},
+ expKind: sym.SNOPTRDATA,
+ },
+ {
+ which: "AddAddrPlus",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddAddrPlus(arch, s2, 3)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 8, 3, 6)},
+ },
+ {
+ which: "AddAddrPlus4",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddAddrPlus4(arch, s2, 3)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 4, 3, 7)},
+ },
+ {
+ which: "AddCURelativeAddrPlus",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddCURelativeAddrPlus(arch, s2, 7)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDRCUOFF, 0, 8, 7, 8)},
+ },
+ }
+
+ var pmi Sym
+ for k, tp := range testpoints {
+ name := fmt.Sprintf("new%d", k+1)
+ mi := ldr.LookupOrCreateSym(name, 0)
+ if mi == 0 {
+ t.Fatalf("LookupOrCreateSym failed for '" + name + "'")
+ }
+ mi = tp.addDataFunc(ldr, mi, pmi)
+ if ldr.SymType(mi) != tp.expKind {
+ t.Errorf("testing Loader.%s: expected kind %s got %s",
+ tp.which, tp.expKind, ldr.SymType(mi))
+ }
+ if bytes.Compare(ldr.Data(mi), tp.expData) != 0 {
+ t.Errorf("testing Loader.%s: expected data %v got %v",
+ tp.which, tp.expData, ldr.Data(mi))
+ }
+ relocs := ldr.Relocs(mi)
+ if !sameRelocSlice(&relocs, tp.expRel) {
+ t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v",
+ tp.which, relocs, tp.expRel)
+ }
+ pmi = mi
+ }
+}
+
+func TestOuterSub(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Populate loader with some symbols.
+ addDummyObjSym(t, ldr, or, "type.uint8")
+ es1 := ldr.LookupOrCreateSym("outer", 0)
+ ldr.MakeSymbolUpdater(es1).SetSize(101)
+ es2 := ldr.LookupOrCreateSym("sub1", 0)
+ es3 := ldr.LookupOrCreateSym("sub2", 0)
+ es4 := ldr.LookupOrCreateSym("sub3", 0)
+ es5 := ldr.LookupOrCreateSym("sub4", 0)
+ es6 := ldr.LookupOrCreateSym("sub5", 0)
+
+ // Should not have an outer sym initially
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("es1 outer sym set ")
+ }
+ if ldr.SubSym(es2) != 0 {
+ t.Errorf("es2 outer sym set ")
+ }
+
+ // Establish first outer/sub relationship
+ ldr.AddInteriorSym(es1, es2)
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+ }
+ if ldr.OuterSym(es2) != es1 {
+ t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+ }
+ if ldr.SubSym(es1) != es2 {
+ t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2)
+ }
+ if ldr.SubSym(es2) != 0 {
+ t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0)
+ }
+
+ // Establish second outer/sub relationship
+ ldr.AddInteriorSym(es1, es3)
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+ }
+ if ldr.OuterSym(es2) != es1 {
+ t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+ }
+ if ldr.OuterSym(es3) != es1 {
+ t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1)
+ }
+ if ldr.SubSym(es1) != es3 {
+ t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3)
+ }
+ if ldr.SubSym(es3) != es2 {
+ t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2)
+ }
+
+ // Some more
+ ldr.AddInteriorSym(es1, es4)
+ ldr.AddInteriorSym(es1, es5)
+ ldr.AddInteriorSym(es1, es6)
+
+ // Set values.
+ ldr.SetSymValue(es2, 7)
+ ldr.SetSymValue(es3, 1)
+ ldr.SetSymValue(es4, 13)
+ ldr.SetSymValue(es5, 101)
+ ldr.SetSymValue(es6, 3)
+
+ // Sort
+ news := ldr.SortSub(es1)
+ if news != es3 {
+ t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3)
+ }
+ pv := int64(-1)
+ count := 0
+ for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) {
+ v := ldr.SymValue(ss)
+ if v <= pv {
+ t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d",
+ ss, v, pv)
+ }
+ pv = v
+ count++
+ }
+ if count != 5 {
+ t.Errorf("expected %d in sub list got %d", 5, count)
+ }
+}
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
new file mode 100644
index 0000000..5d37da8
--- /dev/null
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -0,0 +1,440 @@
+// 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 loader
+
+import (
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "sort"
+)
+
+// SymbolBuilder is a helper designed to help with the construction
+// of new symbol contents.
+type SymbolBuilder struct {
+ *extSymPayload // points to payload being updated
+ symIdx Sym // index of symbol being updated/constructed
+ l *Loader // loader
+}
+
+// MakeSymbolBuilder creates a symbol builder for use in constructing
+// an entirely new symbol.
+func (l *Loader) MakeSymbolBuilder(name string) *SymbolBuilder {
+ // for now assume that any new sym is intended to be static
+ symIdx := l.CreateStaticSym(name)
+ sb := &SymbolBuilder{l: l, symIdx: symIdx}
+ sb.extSymPayload = l.getPayload(symIdx)
+ return sb
+}
+
+// MakeSymbolUpdater creates a symbol builder helper for an existing
+// symbol 'symIdx'. If 'symIdx' is not an external symbol, then create
+// a clone of it (copy name, properties, etc) fix things up so that
+// the lookup tables and caches point to the new version, not the old
+// version.
+func (l *Loader) MakeSymbolUpdater(symIdx Sym) *SymbolBuilder {
+ if symIdx == 0 {
+ panic("can't update the null symbol")
+ }
+ if !l.IsExternal(symIdx) {
+ // Create a clone with the same name/version/kind etc.
+ l.cloneToExternal(symIdx)
+ }
+
+ // Construct updater and return.
+ sb := &SymbolBuilder{l: l, symIdx: symIdx}
+ sb.extSymPayload = l.getPayload(symIdx)
+ return sb
+}
+
+// CreateSymForUpdate creates a symbol with given name and version,
+// returns a CreateSymForUpdate for update. If the symbol already
+// exists, it will update in-place.
+func (l *Loader) CreateSymForUpdate(name string, version int) *SymbolBuilder {
+ s := l.LookupOrCreateSym(name, version)
+ l.SetAttrReachable(s, true)
+ return l.MakeSymbolUpdater(s)
+}
+
+// Getters for properties of the symbol we're working on.
+
+func (sb *SymbolBuilder) Sym() Sym { return sb.symIdx }
+func (sb *SymbolBuilder) Name() string { return sb.name }
+func (sb *SymbolBuilder) Version() int { return sb.ver }
+func (sb *SymbolBuilder) Type() sym.SymKind { return sb.kind }
+func (sb *SymbolBuilder) Size() int64 { return sb.size }
+func (sb *SymbolBuilder) Data() []byte { return sb.data }
+func (sb *SymbolBuilder) Value() int64 { return sb.l.SymValue(sb.symIdx) }
+func (sb *SymbolBuilder) Align() int32 { return sb.l.SymAlign(sb.symIdx) }
+func (sb *SymbolBuilder) Localentry() uint8 { return sb.l.SymLocalentry(sb.symIdx) }
+func (sb *SymbolBuilder) OnList() bool { return sb.l.AttrOnList(sb.symIdx) }
+func (sb *SymbolBuilder) External() bool { return sb.l.AttrExternal(sb.symIdx) }
+func (sb *SymbolBuilder) Extname() string { return sb.l.SymExtname(sb.symIdx) }
+func (sb *SymbolBuilder) CgoExportDynamic() bool { return sb.l.AttrCgoExportDynamic(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimplib() string { return sb.l.SymDynimplib(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimpvers() string { return sb.l.SymDynimpvers(sb.symIdx) }
+func (sb *SymbolBuilder) SubSym() Sym { return sb.l.SubSym(sb.symIdx) }
+func (sb *SymbolBuilder) GoType() Sym { return sb.l.SymGoType(sb.symIdx) }
+func (sb *SymbolBuilder) VisibilityHidden() bool { return sb.l.AttrVisibilityHidden(sb.symIdx) }
+func (sb *SymbolBuilder) Sect() *sym.Section { return sb.l.SymSect(sb.symIdx) }
+
+// Setters for symbol properties.
+
+func (sb *SymbolBuilder) SetType(kind sym.SymKind) { sb.kind = kind }
+func (sb *SymbolBuilder) SetSize(size int64) { sb.size = size }
+func (sb *SymbolBuilder) SetData(data []byte) { sb.data = data }
+func (sb *SymbolBuilder) SetOnList(v bool) { sb.l.SetAttrOnList(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetExternal(v bool) { sb.l.SetAttrExternal(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetValue(v int64) { sb.l.SetSymValue(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetAlign(align int32) { sb.l.SetSymAlign(sb.symIdx, align) }
+func (sb *SymbolBuilder) SetLocalentry(value uint8) { sb.l.SetSymLocalentry(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetExtname(value string) { sb.l.SetSymExtname(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimplib(value string) { sb.l.SetSymDynimplib(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimpvers(value string) { sb.l.SetSymDynimpvers(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetPlt(value int32) { sb.l.SetPlt(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetGot(value int32) { sb.l.SetGot(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetSpecial(value bool) { sb.l.SetAttrSpecial(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetLocal(value bool) { sb.l.SetAttrLocal(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetVisibilityHidden(value bool) {
+ sb.l.SetAttrVisibilityHidden(sb.symIdx, value)
+}
+func (sb *SymbolBuilder) SetNotInSymbolTable(value bool) {
+ sb.l.SetAttrNotInSymbolTable(sb.symIdx, value)
+}
+func (sb *SymbolBuilder) SetSect(sect *sym.Section) { sb.l.SetSymSect(sb.symIdx, sect) }
+
+func (sb *SymbolBuilder) AddBytes(data []byte) {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ sb.data = append(sb.data, data...)
+ sb.size = int64(len(sb.data))
+}
+
+func (sb *SymbolBuilder) Relocs() Relocs {
+ return sb.l.Relocs(sb.symIdx)
+}
+
+// ResetRelocs removes all relocations on this symbol.
+func (sb *SymbolBuilder) ResetRelocs() {
+ sb.relocs = sb.relocs[:0]
+ sb.reltypes = sb.reltypes[:0]
+}
+
+// SetRelocType sets the type of the 'i'-th relocation on this sym to 't'
+func (sb *SymbolBuilder) SetRelocType(i int, t objabi.RelocType) {
+ sb.relocs[i].SetType(0)
+ sb.reltypes[i] = t
+}
+
+// SetRelocSym sets the target sym of the 'i'-th relocation on this sym to 's'
+func (sb *SymbolBuilder) SetRelocSym(i int, tgt Sym) {
+ sb.relocs[i].SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(tgt)})
+}
+
+// SetRelocAdd sets the addend of the 'i'-th relocation on this sym to 'a'
+func (sb *SymbolBuilder) SetRelocAdd(i int, a int64) {
+ sb.relocs[i].SetAdd(a)
+}
+
+// Add n relocations, return a handle to the relocations.
+func (sb *SymbolBuilder) AddRelocs(n int) Relocs {
+ sb.relocs = append(sb.relocs, make([]goobj.Reloc, n)...)
+ sb.reltypes = append(sb.reltypes, make([]objabi.RelocType, n)...)
+ return sb.l.Relocs(sb.symIdx)
+}
+
+// Add a relocation with given type, return its handle and index
+// (to set other fields).
+func (sb *SymbolBuilder) AddRel(typ objabi.RelocType) (Reloc, int) {
+ j := len(sb.relocs)
+ sb.relocs = append(sb.relocs, goobj.Reloc{})
+ sb.reltypes = append(sb.reltypes, typ)
+ relocs := sb.Relocs()
+ return relocs.At(j), j
+}
+
+// Sort relocations by offset.
+func (sb *SymbolBuilder) SortRelocs() {
+ sort.Sort((*relocsByOff)(sb.extSymPayload))
+}
+
+// Implement sort.Interface
+type relocsByOff extSymPayload
+
+func (p *relocsByOff) Len() int { return len(p.relocs) }
+func (p *relocsByOff) Less(i, j int) bool { return p.relocs[i].Off() < p.relocs[j].Off() }
+func (p *relocsByOff) Swap(i, j int) {
+ p.relocs[i], p.relocs[j] = p.relocs[j], p.relocs[i]
+ p.reltypes[i], p.reltypes[j] = p.reltypes[j], p.reltypes[i]
+}
+
+func (sb *SymbolBuilder) Reachable() bool {
+ return sb.l.AttrReachable(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReachable(v bool) {
+ sb.l.SetAttrReachable(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) setReachable() {
+ sb.SetReachable(true)
+}
+
+func (sb *SymbolBuilder) ReadOnly() bool {
+ return sb.l.AttrReadOnly(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReadOnly(v bool) {
+ sb.l.SetAttrReadOnly(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) DuplicateOK() bool {
+ return sb.l.AttrDuplicateOK(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetDuplicateOK(v bool) {
+ sb.l.SetAttrDuplicateOK(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) Outer() Sym {
+ return sb.l.OuterSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) Sub() Sym {
+ return sb.l.SubSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SortSub() {
+ sb.l.SortSub(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) AddInteriorSym(sub Sym) {
+ sb.l.AddInteriorSym(sb.symIdx, sub)
+}
+
+func (sb *SymbolBuilder) AddUint8(v uint8) int64 {
+ off := sb.size
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ sb.size++
+ sb.data = append(sb.data, v)
+ return off
+}
+
+func (sb *SymbolBuilder) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 {
+ off := sb.size
+ sb.setUintXX(arch, off, v, int64(wid))
+ return off
+}
+
+func (sb *SymbolBuilder) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ if sb.size < off+wid {
+ sb.size = off + wid
+ sb.Grow(sb.size)
+ }
+
+ switch wid {
+ case 1:
+ sb.data[off] = uint8(v)
+ case 2:
+ arch.ByteOrder.PutUint16(sb.data[off:], uint16(v))
+ case 4:
+ arch.ByteOrder.PutUint32(sb.data[off:], uint32(v))
+ case 8:
+ arch.ByteOrder.PutUint64(sb.data[off:], v)
+ }
+
+ return off + wid
+}
+
+func (sb *SymbolBuilder) AddUint16(arch *sys.Arch, v uint16) int64 {
+ return sb.AddUintXX(arch, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) AddUint32(arch *sys.Arch, v uint32) int64 {
+ return sb.AddUintXX(arch, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) AddUint64(arch *sys.Arch, v uint64) int64 {
+ return sb.AddUintXX(arch, v, 8)
+}
+
+func (sb *SymbolBuilder) AddUint(arch *sys.Arch, v uint64) int64 {
+ return sb.AddUintXX(arch, v, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) SetUint8(arch *sys.Arch, r int64, v uint8) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 1)
+}
+
+func (sb *SymbolBuilder) SetUint16(arch *sys.Arch, r int64, v uint16) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) SetUint32(arch *sys.Arch, r int64, v uint32) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) SetUint(arch *sys.Arch, r int64, v uint64) int64 {
+ return sb.setUintXX(arch, r, v, int64(arch.PtrSize))
+}
+
+func (sb *SymbolBuilder) SetUintptr(arch *sys.Arch, r int64, v uintptr) int64 {
+ return sb.setUintXX(arch, r, uint64(v), int64(arch.PtrSize))
+}
+
+func (sb *SymbolBuilder) SetAddrPlus(arch *sys.Arch, off int64, tgt Sym, add int64) int64 {
+ if sb.Type() == 0 {
+ sb.SetType(sym.SDATA)
+ }
+ if off+int64(arch.PtrSize) > sb.size {
+ sb.size = off + int64(arch.PtrSize)
+ sb.Grow(sb.size)
+ }
+ r, _ := sb.AddRel(objabi.R_ADDR)
+ r.SetSym(tgt)
+ r.SetOff(int32(off))
+ r.SetSiz(uint8(arch.PtrSize))
+ r.SetAdd(add)
+ return off + int64(r.Siz())
+}
+
+func (sb *SymbolBuilder) SetAddr(arch *sys.Arch, off int64, tgt Sym) int64 {
+ return sb.SetAddrPlus(arch, off, tgt, 0)
+}
+
+func (sb *SymbolBuilder) AddStringAt(off int64, str string) int64 {
+ strLen := int64(len(str))
+ if off+strLen+1 > int64(len(sb.data)) {
+ panic("attempt to write past end of buffer")
+ }
+ copy(sb.data[off:off+strLen], str)
+ sb.data[off+strLen] = 0
+ return off + strLen + 1
+}
+
+func (sb *SymbolBuilder) Addstring(str string) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SNOPTRDATA
+ }
+ r := sb.size
+ if sb.name == ".shstrtab" {
+ // FIXME: find a better mechanism for this
+ sb.l.elfsetstring(str, int(r))
+ }
+ sb.data = append(sb.data, str...)
+ sb.data = append(sb.data, 0)
+ sb.size = int64(len(sb.data))
+ return r
+}
+
+func (sb *SymbolBuilder) SetBytesAt(off int64, b []byte) int64 {
+ datLen := int64(len(b))
+ if off+datLen > int64(len(sb.data)) {
+ panic("attempt to write past end of buffer")
+ }
+ copy(sb.data[off:off+datLen], b)
+ return off + datLen
+}
+
+func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ i := sb.size
+
+ sb.size += int64(rsize)
+ sb.Grow(sb.size)
+
+ r, _ := sb.AddRel(typ)
+ r.SetSym(tgt)
+ r.SetOff(int32(i))
+ r.SetSiz(uint8(rsize))
+ r.SetAdd(add)
+
+ return i + int64(rsize)
+}
+
+// Add a symbol reference (relocation) with given type, addend, and size
+// (the most generic form).
+func (sb *SymbolBuilder) AddSymRef(arch *sys.Arch, tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+ return sb.addSymRef(tgt, add, typ, rsize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDR, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus4(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDR, 4)
+}
+
+func (sb *SymbolBuilder) AddAddr(arch *sys.Arch, tgt Sym) int64 {
+ return sb.AddAddrPlus(arch, tgt, 0)
+}
+
+func (sb *SymbolBuilder) AddPCRelPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_PCREL, 4)
+}
+
+func (sb *SymbolBuilder) AddCURelativeAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDRCUOFF, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddSize(arch *sys.Arch, tgt Sym) int64 {
+ return sb.addSymRef(tgt, 0, objabi.R_SIZE, arch.PtrSize)
+}
+
+// GenAddAddrPlusFunc returns a function to be called when capturing
+// a function symbol's address. In later stages of the link (when
+// address assignment is done) when doing internal linking and
+// targeting an executable, we can just emit the address of a function
+// directly instead of generating a relocation. Clients can call
+// this function (setting 'internalExec' based on build mode and target)
+// and then invoke the returned function in roughly the same way that
+// loader.*SymbolBuilder.AddAddrPlus would be used.
+func GenAddAddrPlusFunc(internalExec bool) func(s *SymbolBuilder, arch *sys.Arch, tgt Sym, add int64) int64 {
+ if internalExec {
+ return func(s *SymbolBuilder, arch *sys.Arch, tgt Sym, add int64) int64 {
+ if v := s.l.SymValue(tgt); v != 0 {
+ return s.AddUint(arch, uint64(v+add))
+ }
+ return s.AddAddrPlus(arch, tgt, add)
+ }
+ } else {
+ return (*SymbolBuilder).AddAddrPlus
+ }
+}
+
+func (sb *SymbolBuilder) MakeWritable() {
+ if sb.ReadOnly() {
+ sb.data = append([]byte(nil), sb.data...)
+ sb.l.SetAttrReadOnly(sb.symIdx, false)
+ }
+}
+
+func (sb *SymbolBuilder) AddUleb(v uint64) {
+ if v < 128 { // common case: 1 byte
+ sb.AddUint8(uint8(v))
+ return
+ }
+ for {
+ c := uint8(v & 0x7f)
+ v >>= 7
+ if v != 0 {
+ c |= 0x80
+ }
+ sb.AddUint8(c)
+ if c&0x80 == 0 {
+ break
+ }
+ }
+}
diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go
new file mode 100644
index 0000000..6d1d9bb
--- /dev/null
+++ b/src/cmd/link/internal/loadmacho/ldmacho.go
@@ -0,0 +1,806 @@
+// 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 loadmacho implements a Mach-O file reader.
+package loadmacho
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "encoding/binary"
+ "fmt"
+)
+
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+http://code.swtch.com/plan9port/src/tip/src/libmach/
+
+ Copyright © 2004 Russ Cox.
+ Portions Copyright © 2008-2010 Google Inc.
+ Portions Copyright © 2010 The Go Authors.
+
+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.
+*/
+
+// TODO(crawshaw): de-duplicate these symbols with cmd/link/internal/ld
+const (
+ MACHO_X86_64_RELOC_UNSIGNED = 0
+ MACHO_X86_64_RELOC_SIGNED = 1
+ MACHO_ARM64_RELOC_ADDEND = 10
+)
+
+type ldMachoObj struct {
+ f *bio.Reader
+ base int64 // off in f where Mach-O begins
+ length int64 // length of Mach-O
+ is64 bool
+ name string
+ e binary.ByteOrder
+ cputype uint
+ subcputype uint
+ filetype uint32
+ flags uint32
+ cmd []ldMachoCmd
+ ncmd uint
+}
+
+type ldMachoCmd struct {
+ type_ int
+ off uint32
+ size uint32
+ seg ldMachoSeg
+ sym ldMachoSymtab
+ dsym ldMachoDysymtab
+}
+
+type ldMachoSeg struct {
+ name string
+ vmaddr uint64
+ vmsize uint64
+ fileoff uint32
+ filesz uint32
+ maxprot uint32
+ initprot uint32
+ nsect uint32
+ flags uint32
+ sect []ldMachoSect
+}
+
+type ldMachoSect struct {
+ name string
+ segname string
+ addr uint64
+ size uint64
+ off uint32
+ align uint32
+ reloff uint32
+ nreloc uint32
+ flags uint32
+ res1 uint32
+ res2 uint32
+ sym loader.Sym
+ rel []ldMachoRel
+}
+
+type ldMachoRel struct {
+ addr uint32
+ symnum uint32
+ pcrel uint8
+ length uint8
+ extrn uint8
+ type_ uint8
+ scattered uint8
+ value uint32
+}
+
+type ldMachoSymtab struct {
+ symoff uint32
+ nsym uint32
+ stroff uint32
+ strsize uint32
+ str []byte
+ sym []ldMachoSym
+}
+
+type ldMachoSym struct {
+ name string
+ type_ uint8
+ sectnum uint8
+ desc uint16
+ kind int8
+ value uint64
+ sym loader.Sym
+}
+
+type ldMachoDysymtab struct {
+ ilocalsym uint32
+ nlocalsym uint32
+ iextdefsym uint32
+ nextdefsym uint32
+ iundefsym uint32
+ nundefsym uint32
+ tocoff uint32
+ ntoc uint32
+ modtaboff uint32
+ nmodtab uint32
+ extrefsymoff uint32
+ nextrefsyms uint32
+ indirectsymoff uint32
+ nindirectsyms uint32
+ extreloff uint32
+ nextrel uint32
+ locreloff uint32
+ nlocrel uint32
+ indir []uint32
+}
+
+// ldMachoSym.type_
+const (
+ N_EXT = 0x01
+ N_TYPE = 0x1e
+ N_STAB = 0xe0
+)
+
+// ldMachoSym.desc
+const (
+ N_WEAK_REF = 0x40
+ N_WEAK_DEF = 0x80
+)
+
+const (
+ LdMachoCpuVax = 1
+ LdMachoCpu68000 = 6
+ LdMachoCpu386 = 7
+ LdMachoCpuAmd64 = 1<<24 | 7
+ LdMachoCpuMips = 8
+ LdMachoCpu98000 = 10
+ LdMachoCpuHppa = 11
+ LdMachoCpuArm = 12
+ LdMachoCpuArm64 = 1<<24 | 12
+ LdMachoCpu88000 = 13
+ LdMachoCpuSparc = 14
+ LdMachoCpu860 = 15
+ LdMachoCpuAlpha = 16
+ LdMachoCpuPower = 18
+ LdMachoCmdSegment = 1
+ LdMachoCmdSymtab = 2
+ LdMachoCmdSymseg = 3
+ LdMachoCmdThread = 4
+ LdMachoCmdDysymtab = 11
+ LdMachoCmdSegment64 = 25
+ LdMachoFileObject = 1
+ LdMachoFileExecutable = 2
+ LdMachoFileFvmlib = 3
+ LdMachoFileCore = 4
+ LdMachoFilePreload = 5
+)
+
+func unpackcmd(p []byte, m *ldMachoObj, c *ldMachoCmd, type_ uint, sz uint) int {
+ e4 := m.e.Uint32
+ e8 := m.e.Uint64
+
+ c.type_ = int(type_)
+ c.size = uint32(sz)
+ switch type_ {
+ default:
+ return -1
+
+ case LdMachoCmdSegment:
+ if sz < 56 {
+ return -1
+ }
+ c.seg.name = cstring(p[8:24])
+ c.seg.vmaddr = uint64(e4(p[24:]))
+ c.seg.vmsize = uint64(e4(p[28:]))
+ c.seg.fileoff = e4(p[32:])
+ c.seg.filesz = e4(p[36:])
+ c.seg.maxprot = e4(p[40:])
+ c.seg.initprot = e4(p[44:])
+ c.seg.nsect = e4(p[48:])
+ c.seg.flags = e4(p[52:])
+ c.seg.sect = make([]ldMachoSect, c.seg.nsect)
+ if uint32(sz) < 56+c.seg.nsect*68 {
+ return -1
+ }
+ p = p[56:]
+ var s *ldMachoSect
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ s = &c.seg.sect[i]
+ s.name = cstring(p[0:16])
+ s.segname = cstring(p[16:32])
+ s.addr = uint64(e4(p[32:]))
+ s.size = uint64(e4(p[36:]))
+ s.off = e4(p[40:])
+ s.align = e4(p[44:])
+ s.reloff = e4(p[48:])
+ s.nreloc = e4(p[52:])
+ s.flags = e4(p[56:])
+ s.res1 = e4(p[60:])
+ s.res2 = e4(p[64:])
+ p = p[68:]
+ }
+
+ case LdMachoCmdSegment64:
+ if sz < 72 {
+ return -1
+ }
+ c.seg.name = cstring(p[8:24])
+ c.seg.vmaddr = e8(p[24:])
+ c.seg.vmsize = e8(p[32:])
+ c.seg.fileoff = uint32(e8(p[40:]))
+ c.seg.filesz = uint32(e8(p[48:]))
+ c.seg.maxprot = e4(p[56:])
+ c.seg.initprot = e4(p[60:])
+ c.seg.nsect = e4(p[64:])
+ c.seg.flags = e4(p[68:])
+ c.seg.sect = make([]ldMachoSect, c.seg.nsect)
+ if uint32(sz) < 72+c.seg.nsect*80 {
+ return -1
+ }
+ p = p[72:]
+ var s *ldMachoSect
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ s = &c.seg.sect[i]
+ s.name = cstring(p[0:16])
+ s.segname = cstring(p[16:32])
+ s.addr = e8(p[32:])
+ s.size = e8(p[40:])
+ s.off = e4(p[48:])
+ s.align = e4(p[52:])
+ s.reloff = e4(p[56:])
+ s.nreloc = e4(p[60:])
+ s.flags = e4(p[64:])
+ s.res1 = e4(p[68:])
+ s.res2 = e4(p[72:])
+
+ // p+76 is reserved
+ p = p[80:]
+ }
+
+ case LdMachoCmdSymtab:
+ if sz < 24 {
+ return -1
+ }
+ c.sym.symoff = e4(p[8:])
+ c.sym.nsym = e4(p[12:])
+ c.sym.stroff = e4(p[16:])
+ c.sym.strsize = e4(p[20:])
+
+ case LdMachoCmdDysymtab:
+ if sz < 80 {
+ return -1
+ }
+ c.dsym.ilocalsym = e4(p[8:])
+ c.dsym.nlocalsym = e4(p[12:])
+ c.dsym.iextdefsym = e4(p[16:])
+ c.dsym.nextdefsym = e4(p[20:])
+ c.dsym.iundefsym = e4(p[24:])
+ c.dsym.nundefsym = e4(p[28:])
+ c.dsym.tocoff = e4(p[32:])
+ c.dsym.ntoc = e4(p[36:])
+ c.dsym.modtaboff = e4(p[40:])
+ c.dsym.nmodtab = e4(p[44:])
+ c.dsym.extrefsymoff = e4(p[48:])
+ c.dsym.nextrefsyms = e4(p[52:])
+ c.dsym.indirectsymoff = e4(p[56:])
+ c.dsym.nindirectsyms = e4(p[60:])
+ c.dsym.extreloff = e4(p[64:])
+ c.dsym.nextrel = e4(p[68:])
+ c.dsym.locreloff = e4(p[72:])
+ c.dsym.nlocrel = e4(p[76:])
+ }
+
+ return 0
+}
+
+func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int {
+ if sect.rel != nil || sect.nreloc == 0 {
+ return 0
+ }
+ rel := make([]ldMachoRel, sect.nreloc)
+ m.f.MustSeek(m.base+int64(sect.reloff), 0)
+ buf, _, err := m.f.Slice(uint64(sect.nreloc * 8))
+ if err != nil {
+ return -1
+ }
+ for i := uint32(0); i < sect.nreloc; i++ {
+ r := &rel[i]
+ p := buf[i*8:]
+ r.addr = m.e.Uint32(p)
+
+ // TODO(rsc): Wrong interpretation for big-endian bitfields?
+ if r.addr&0x80000000 != 0 {
+ // scatterbrained relocation
+ r.scattered = 1
+
+ v := r.addr >> 24
+ r.addr &= 0xFFFFFF
+ r.type_ = uint8(v & 0xF)
+ v >>= 4
+ r.length = 1 << (v & 3)
+ v >>= 2
+ r.pcrel = uint8(v & 1)
+ r.value = m.e.Uint32(p[4:])
+ } else {
+ v := m.e.Uint32(p[4:])
+ r.symnum = v & 0xFFFFFF
+ v >>= 24
+ r.pcrel = uint8(v & 1)
+ v >>= 1
+ r.length = 1 << (v & 3)
+ v >>= 2
+ r.extrn = uint8(v & 1)
+ v >>= 1
+ r.type_ = uint8(v)
+ }
+ }
+
+ sect.rel = rel
+ return 0
+}
+
+func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int {
+ n := int(d.nindirectsyms)
+ m.f.MustSeek(m.base+int64(d.indirectsymoff), 0)
+ p, _, err := m.f.Slice(uint64(n * 4))
+ if err != nil {
+ return -1
+ }
+
+ d.indir = make([]uint32, n)
+ for i := 0; i < n; i++ {
+ d.indir[i] = m.e.Uint32(p[4*i:])
+ }
+ return 0
+}
+
+func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
+ if symtab.sym != nil {
+ return 0
+ }
+
+ m.f.MustSeek(m.base+int64(symtab.stroff), 0)
+ strbuf, _, err := m.f.Slice(uint64(symtab.strsize))
+ if err != nil {
+ return -1
+ }
+
+ symsize := 12
+ if m.is64 {
+ symsize = 16
+ }
+ n := int(symtab.nsym * uint32(symsize))
+ m.f.MustSeek(m.base+int64(symtab.symoff), 0)
+ symbuf, _, err := m.f.Slice(uint64(n))
+ if err != nil {
+ return -1
+ }
+ sym := make([]ldMachoSym, symtab.nsym)
+ p := symbuf
+ for i := uint32(0); i < symtab.nsym; i++ {
+ s := &sym[i]
+ v := m.e.Uint32(p)
+ if v >= symtab.strsize {
+ return -1
+ }
+ s.name = cstring(strbuf[v:])
+ s.type_ = p[4]
+ s.sectnum = p[5]
+ s.desc = m.e.Uint16(p[6:])
+ if m.is64 {
+ s.value = m.e.Uint64(p[8:])
+ } else {
+ s.value = uint64(m.e.Uint32(p[8:]))
+ }
+ p = p[symsize:]
+ }
+
+ symtab.str = strbuf
+ symtab.sym = sym
+ return 0
+}
+
+// Load the Mach-O file pn from f.
+// Symbols are written into syms, and a slice of the text symbols is returned.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
+ return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ base := f.Offset()
+
+ hdr, _, err := f.Slice(7 * 4)
+ if err != nil {
+ return errorf("reading hdr: %v", err)
+ }
+
+ var e binary.ByteOrder
+ if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
+ e = binary.BigEndian
+ } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
+ e = binary.LittleEndian
+ } else {
+ return errorf("bad magic - not mach-o file")
+ }
+
+ is64 := e.Uint32(hdr[:]) == 0xFEEDFACF
+ ncmd := e.Uint32(hdr[4*4:])
+ cmdsz := e.Uint32(hdr[5*4:])
+ if ncmd > 0x10000 || cmdsz >= 0x01000000 {
+ return errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz)
+ }
+
+ if is64 {
+ f.MustSeek(4, 1) // skip reserved word in header
+ }
+
+ m := &ldMachoObj{
+ f: f,
+ e: e,
+ cputype: uint(e.Uint32(hdr[1*4:])),
+ subcputype: uint(e.Uint32(hdr[2*4:])),
+ filetype: e.Uint32(hdr[3*4:]),
+ ncmd: uint(ncmd),
+ flags: e.Uint32(hdr[6*4:]),
+ is64: is64,
+ base: base,
+ length: length,
+ name: pn,
+ }
+
+ switch arch.Family {
+ default:
+ return errorf("mach-o %s unimplemented", arch.Name)
+ case sys.AMD64:
+ if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 {
+ return errorf("mach-o object but not amd64")
+ }
+ case sys.ARM64:
+ if e != binary.LittleEndian || m.cputype != LdMachoCpuArm64 {
+ return errorf("mach-o object but not arm64")
+ }
+ }
+
+ m.cmd = make([]ldMachoCmd, ncmd)
+ cmdp, _, err := f.Slice(uint64(cmdsz))
+ if err != nil {
+ return errorf("reading cmds: %v", err)
+ }
+
+ // read and parse load commands
+ var c *ldMachoCmd
+
+ var symtab *ldMachoSymtab
+ var dsymtab *ldMachoDysymtab
+
+ off := uint32(len(hdr))
+ for i := uint32(0); i < ncmd; i++ {
+ ty := e.Uint32(cmdp)
+ sz := e.Uint32(cmdp[4:])
+ m.cmd[i].off = off
+ unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz))
+ cmdp = cmdp[sz:]
+ off += sz
+ if ty == LdMachoCmdSymtab {
+ if symtab != nil {
+ return errorf("multiple symbol tables")
+ }
+
+ symtab = &m.cmd[i].sym
+ macholoadsym(m, symtab)
+ }
+
+ if ty == LdMachoCmdDysymtab {
+ dsymtab = &m.cmd[i].dsym
+ macholoaddsym(m, dsymtab)
+ }
+
+ if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) {
+ if c != nil {
+ return errorf("multiple load commands")
+ }
+
+ c = &m.cmd[i]
+ }
+ }
+
+ // load text and data segments into memory.
+ // they are not as small as the load commands, but we'll need
+ // the memory anyway for the symbol images, so we might
+ // as well use one large chunk.
+ if c == nil {
+ return errorf("no load command")
+ }
+
+ if symtab == nil {
+ // our work is done here - no symbols means nothing can refer to this file
+ return
+ }
+
+ if int64(c.seg.fileoff+c.seg.filesz) >= length {
+ return errorf("load segment out of range")
+ }
+
+ f.MustSeek(m.base+int64(c.seg.fileoff), 0)
+ dat, readOnly, err := f.Slice(uint64(c.seg.filesz))
+ if err != nil {
+ return errorf("cannot load object data: %v", err)
+ }
+
+ for i := uint32(0); i < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ if sect.segname != "__TEXT" && sect.segname != "__DATA" {
+ continue
+ }
+ if sect.name == "__eh_frame" {
+ continue
+ }
+ name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
+ s := l.LookupOrCreateSym(name, localSymVersion)
+ bld := l.MakeSymbolUpdater(s)
+ if bld.Type() != 0 {
+ return errorf("duplicate %s/%s", sect.segname, sect.name)
+ }
+
+ if sect.flags&0xff == 1 { // S_ZEROFILL
+ bld.SetData(make([]byte, sect.size))
+ } else {
+ bld.SetReadOnly(readOnly)
+ bld.SetData(dat[sect.addr-c.seg.vmaddr:][:sect.size])
+ }
+ bld.SetSize(int64(len(bld.Data())))
+
+ if sect.segname == "__TEXT" {
+ if sect.name == "__text" {
+ bld.SetType(sym.STEXT)
+ } else {
+ bld.SetType(sym.SRODATA)
+ }
+ } else {
+ if sect.name == "__bss" {
+ bld.SetType(sym.SNOPTRBSS)
+ bld.SetData(nil)
+ } else {
+ bld.SetType(sym.SNOPTRDATA)
+ }
+ }
+
+ sect.sym = s
+ }
+
+ // enter sub-symbols into symbol table.
+ // have to guess sizes from next symbol.
+ for i := uint32(0); i < symtab.nsym; i++ {
+ machsym := &symtab.sym[i]
+ if machsym.type_&N_STAB != 0 {
+ continue
+ }
+
+ // TODO: check sym->type against outer->type.
+ name := machsym.name
+
+ if name[0] == '_' && name[1] != '\x00' {
+ name = name[1:]
+ }
+ v := 0
+ if machsym.type_&N_EXT == 0 {
+ v = localSymVersion
+ }
+ s := l.LookupOrCreateSym(name, v)
+ if machsym.type_&N_EXT == 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ machsym.sym = s
+ if machsym.sectnum == 0 { // undefined
+ continue
+ }
+ if uint32(machsym.sectnum) > c.seg.nsect {
+ return errorf("reference to invalid section %d", machsym.sectnum)
+ }
+
+ sect := &c.seg.sect[machsym.sectnum-1]
+ bld := l.MakeSymbolUpdater(s)
+ outer := sect.sym
+ if outer == 0 {
+ continue // ignore reference to invalid section
+ }
+
+ if osym := l.OuterSym(s); osym != 0 {
+ if l.AttrDuplicateOK(s) {
+ continue
+ }
+ return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym))
+ }
+
+ bld.SetType(l.SymType(outer))
+ if l.SymSize(outer) != 0 { // skip empty section (0-sized symbol)
+ l.AddInteriorSym(outer, s)
+ }
+
+ bld.SetValue(int64(machsym.value - sect.addr))
+ if !l.AttrCgoExportDynamic(s) {
+ bld.SetDynimplib("") // satisfy dynimport
+ }
+ if l.SymType(outer) == sym.STEXT {
+ if bld.External() && !bld.DuplicateOK() {
+ return errorf("%v: duplicate symbol definition", s)
+ }
+ bld.SetExternal(true)
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ s := sect.sym
+ if s == 0 {
+ continue
+ }
+ bld := l.MakeSymbolUpdater(s)
+ if bld.SubSym() != 0 {
+
+ bld.SortSub()
+
+ // assign sizes, now that we know symbols in sorted order.
+ for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+ s1Bld := l.MakeSymbolUpdater(s1)
+ if sub := l.SubSym(s1); sub != 0 {
+ s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1))
+ } else {
+ dlen := int64(len(l.Data(s)))
+ s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1))
+ }
+ }
+ }
+
+ if bld.Type() == sym.STEXT {
+ if bld.OnList() {
+ return errorf("symbol %s listed multiple times", bld.Name())
+ }
+ bld.SetOnList(true)
+ textp = append(textp, s)
+ for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+ if l.AttrOnList(s1) {
+ return errorf("symbol %s listed multiple times", l.RawSymName(s1))
+ }
+ l.SetAttrOnList(s1, true)
+ textp = append(textp, s1)
+ }
+ }
+ }
+
+ // load relocations
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ s := sect.sym
+ if s == 0 {
+ continue
+ }
+ macholoadrel(m, sect)
+ if sect.rel == nil {
+ continue
+ }
+
+ sb := l.MakeSymbolUpdater(sect.sym)
+ var rAdd int64
+ for j := uint32(0); j < sect.nreloc; j++ {
+ var (
+ rOff int32
+ rSize uint8
+ rType objabi.RelocType
+ rSym loader.Sym
+ )
+ rel := &sect.rel[j]
+ if rel.scattered != 0 {
+ // mach-o only uses scattered relocation on 32-bit platforms,
+ // which are no longer supported.
+ return errorf("%v: unexpected scattered relocation", s)
+ }
+
+ if arch.Family == sys.ARM64 && rel.type_ == MACHO_ARM64_RELOC_ADDEND {
+ // Two relocations. This addend will be applied to the next one.
+ rAdd = int64(rel.symnum) << 40 >> 40 // convert unsigned 24-bit to signed 24-bit
+ continue
+ }
+
+ rSize = rel.length
+ rType = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel)
+ rOff = int32(rel.addr)
+
+ // Handle X86_64_RELOC_SIGNED referencing a section (rel.extrn == 0).
+ p := l.Data(s)
+ if arch.Family == sys.AMD64 {
+ if rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED {
+ // Calculate the addend as the offset into the section.
+ //
+ // The rip-relative offset stored in the object file is encoded
+ // as follows:
+ //
+ // movsd 0x00000360(%rip),%xmm0
+ //
+ // To get the absolute address of the value this rip-relative address is pointing
+ // to, we must add the address of the next instruction to it. This is done by
+ // taking the address of the relocation and adding 4 to it (since the rip-relative
+ // offset can at most be 32 bits long). To calculate the offset into the section the
+ // relocation is referencing, we subtract the vaddr of the start of the referenced
+ // section found in the original object file.
+ //
+ // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h]
+ secaddr := c.seg.sect[rel.symnum-1].addr
+ rAdd = int64(uint64(int64(int32(e.Uint32(p[rOff:])))+int64(rOff)+4) - secaddr)
+ } else {
+ rAdd = int64(int32(e.Uint32(p[rOff:])))
+ }
+ }
+
+ // An unsigned internal relocation has a value offset
+ // by the section address.
+ if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_UNSIGNED {
+ secaddr := c.seg.sect[rel.symnum-1].addr
+ rAdd -= int64(secaddr)
+ }
+
+ if rel.extrn == 0 {
+ if rel.symnum < 1 || rel.symnum > c.seg.nsect {
+ return errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect)
+ }
+
+ rSym = c.seg.sect[rel.symnum-1].sym
+ if rSym == 0 {
+ return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
+ }
+ } else {
+ if rel.symnum >= symtab.nsym {
+ return errorf("invalid relocation: symbol reference out of range")
+ }
+
+ rSym = symtab.sym[rel.symnum].sym
+ }
+
+ r, _ := sb.AddRel(rType)
+ r.SetOff(rOff)
+ r.SetSiz(rSize)
+ r.SetSym(rSym)
+ r.SetAdd(rAdd)
+
+ rAdd = 0 // clear rAdd for next iteration
+ }
+
+ sb.SortRelocs()
+ }
+
+ return textp, nil
+}
+
+func cstring(x []byte) string {
+ i := bytes.IndexByte(x, '\x00')
+ if i >= 0 {
+ x = x[:i]
+ }
+ return string(x)
+}
diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go
new file mode 100644
index 0000000..a5c025d
--- /dev/null
+++ b/src/cmd/link/internal/loadpe/ldpe.go
@@ -0,0 +1,536 @@
+// Copyright 2010 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 loadpe implements a PE/COFF file reader.
+package loadpe
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/pe"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+)
+
+const (
+ // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 (same with IMAGE_SYM_DTYPE_POINTER and IMAGE_SYM_DTYPE_FUNCTION)
+ IMAGE_SYM_UNDEFINED = 0
+ IMAGE_SYM_ABSOLUTE = -1
+ IMAGE_SYM_DEBUG = -2
+ IMAGE_SYM_TYPE_NULL = 0
+ IMAGE_SYM_TYPE_VOID = 1
+ IMAGE_SYM_TYPE_CHAR = 2
+ IMAGE_SYM_TYPE_SHORT = 3
+ IMAGE_SYM_TYPE_INT = 4
+ IMAGE_SYM_TYPE_LONG = 5
+ IMAGE_SYM_TYPE_FLOAT = 6
+ IMAGE_SYM_TYPE_DOUBLE = 7
+ IMAGE_SYM_TYPE_STRUCT = 8
+ IMAGE_SYM_TYPE_UNION = 9
+ IMAGE_SYM_TYPE_ENUM = 10
+ IMAGE_SYM_TYPE_MOE = 11
+ IMAGE_SYM_TYPE_BYTE = 12
+ IMAGE_SYM_TYPE_WORD = 13
+ IMAGE_SYM_TYPE_UINT = 14
+ IMAGE_SYM_TYPE_DWORD = 15
+ IMAGE_SYM_TYPE_PCODE = 32768
+ IMAGE_SYM_DTYPE_NULL = 0
+ IMAGE_SYM_DTYPE_POINTER = 0x10
+ IMAGE_SYM_DTYPE_FUNCTION = 0x20
+ IMAGE_SYM_DTYPE_ARRAY = 0x30
+ IMAGE_SYM_CLASS_END_OF_FUNCTION = -1
+ IMAGE_SYM_CLASS_NULL = 0
+ IMAGE_SYM_CLASS_AUTOMATIC = 1
+ IMAGE_SYM_CLASS_EXTERNAL = 2
+ IMAGE_SYM_CLASS_STATIC = 3
+ IMAGE_SYM_CLASS_REGISTER = 4
+ IMAGE_SYM_CLASS_EXTERNAL_DEF = 5
+ IMAGE_SYM_CLASS_LABEL = 6
+ IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7
+ IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8
+ IMAGE_SYM_CLASS_ARGUMENT = 9
+ IMAGE_SYM_CLASS_STRUCT_TAG = 10
+ IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11
+ IMAGE_SYM_CLASS_UNION_TAG = 12
+ IMAGE_SYM_CLASS_TYPE_DEFINITION = 13
+ IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14
+ IMAGE_SYM_CLASS_ENUM_TAG = 15
+ IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16
+ IMAGE_SYM_CLASS_REGISTER_PARAM = 17
+ IMAGE_SYM_CLASS_BIT_FIELD = 18
+ IMAGE_SYM_CLASS_FAR_EXTERNAL = 68 /* Not in PECOFF v8 spec */
+ IMAGE_SYM_CLASS_BLOCK = 100
+ IMAGE_SYM_CLASS_FUNCTION = 101
+ IMAGE_SYM_CLASS_END_OF_STRUCT = 102
+ IMAGE_SYM_CLASS_FILE = 103
+ IMAGE_SYM_CLASS_SECTION = 104
+ IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105
+ IMAGE_SYM_CLASS_CLR_TOKEN = 107
+ IMAGE_REL_I386_ABSOLUTE = 0x0000
+ IMAGE_REL_I386_DIR16 = 0x0001
+ IMAGE_REL_I386_REL16 = 0x0002
+ IMAGE_REL_I386_DIR32 = 0x0006
+ IMAGE_REL_I386_DIR32NB = 0x0007
+ IMAGE_REL_I386_SEG12 = 0x0009
+ IMAGE_REL_I386_SECTION = 0x000A
+ IMAGE_REL_I386_SECREL = 0x000B
+ IMAGE_REL_I386_TOKEN = 0x000C
+ IMAGE_REL_I386_SECREL7 = 0x000D
+ IMAGE_REL_I386_REL32 = 0x0014
+ IMAGE_REL_AMD64_ABSOLUTE = 0x0000
+ IMAGE_REL_AMD64_ADDR64 = 0x0001
+ IMAGE_REL_AMD64_ADDR32 = 0x0002
+ IMAGE_REL_AMD64_ADDR32NB = 0x0003
+ IMAGE_REL_AMD64_REL32 = 0x0004
+ IMAGE_REL_AMD64_REL32_1 = 0x0005
+ IMAGE_REL_AMD64_REL32_2 = 0x0006
+ IMAGE_REL_AMD64_REL32_3 = 0x0007
+ IMAGE_REL_AMD64_REL32_4 = 0x0008
+ IMAGE_REL_AMD64_REL32_5 = 0x0009
+ IMAGE_REL_AMD64_SECTION = 0x000A
+ IMAGE_REL_AMD64_SECREL = 0x000B
+ IMAGE_REL_AMD64_SECREL7 = 0x000C
+ IMAGE_REL_AMD64_TOKEN = 0x000D
+ IMAGE_REL_AMD64_SREL32 = 0x000E
+ IMAGE_REL_AMD64_PAIR = 0x000F
+ IMAGE_REL_AMD64_SSPAN32 = 0x0010
+ 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_SECTION = 0x000E
+ IMAGE_REL_ARM_SECREL = 0x000F
+ IMAGE_REL_ARM_MOV32 = 0x0010
+ IMAGE_REL_THUMB_MOV32 = 0x0011
+ IMAGE_REL_THUMB_BRANCH20 = 0x0012
+ IMAGE_REL_THUMB_BRANCH24 = 0x0014
+ IMAGE_REL_THUMB_BLX23 = 0x0015
+ IMAGE_REL_ARM_PAIR = 0x0016
+)
+
+// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld, ideally in debug/pe.
+const (
+ IMAGE_SCN_CNT_CODE = 0x00000020
+ IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
+ IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
+ IMAGE_SCN_MEM_DISCARDABLE = 0x02000000
+ IMAGE_SCN_MEM_EXECUTE = 0x20000000
+ IMAGE_SCN_MEM_READ = 0x40000000
+ IMAGE_SCN_MEM_WRITE = 0x80000000
+)
+
+// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf
+
+// peBiobuf makes bio.Reader look like io.ReaderAt.
+type peBiobuf bio.Reader
+
+func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) {
+ ret := ((*bio.Reader)(f)).MustSeek(off, 0)
+ if ret < 0 {
+ return 0, errors.New("fail to seek")
+ }
+ n, err := f.Read(p)
+ if err != nil {
+ return 0, err
+ }
+ return n, nil
+}
+
+// makeUpdater creates a loader.SymbolBuilder if one hasn't been created previously.
+// We use this to lazily make SymbolBuilders as we don't always need a builder, and creating them for all symbols might be an error.
+func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loader.SymbolBuilder {
+ if bld != nil {
+ return bld
+ }
+ bld = l.MakeSymbolUpdater(s)
+ return bld
+}
+
+// Load loads the PE file pn from input.
+// Symbols are written into syms, and a slice of the text symbols is returned.
+// If an .rsrc section or set of .rsrc$xx sections is found, its symbols are
+// returned as rsrc.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) {
+ lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) {
+ s := l.LookupOrCreateSym(name, version)
+ sb := l.MakeSymbolUpdater(s)
+ return sb, s
+ }
+ sectsyms := make(map[*pe.Section]loader.Sym)
+ sectdata := make(map[*pe.Section][]byte)
+
+ // Some input files are archives containing multiple of
+ // object files, and pe.NewFile seeks to the start of
+ // input file and get confused. Create section reader
+ // to stop pe.NewFile looking before current position.
+ sr := io.NewSectionReader((*peBiobuf)(input), input.Offset(), 1<<63-1)
+
+ // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
+ f, err := pe.NewFile(sr)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer f.Close()
+
+ // TODO return error if found .cormeta
+
+ // create symbols for mapped sections
+ for _, sect := range f.Sections {
+ if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+ continue
+ }
+
+ if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+ // This has been seen for .idata sections, which we
+ // want to ignore. See issues 5106 and 5273.
+ continue
+ }
+
+ name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
+ bld, s := lookup(name, localSymVersion)
+
+ switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
+ case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
+ bld.SetType(sym.SRODATA)
+
+ case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss
+ bld.SetType(sym.SNOPTRBSS)
+
+ case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data
+ bld.SetType(sym.SNOPTRDATA)
+
+ case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text
+ bld.SetType(sym.STEXT)
+
+ default:
+ return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
+ }
+
+ if bld.Type() != sym.SNOPTRBSS {
+ data, err := sect.Data()
+ if err != nil {
+ return nil, nil, err
+ }
+ sectdata[sect] = data
+ bld.SetData(data)
+ }
+ bld.SetSize(int64(sect.Size))
+ sectsyms[sect] = s
+ if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") {
+ rsrc = append(rsrc, s)
+ }
+ }
+
+ // load relocations
+ for _, rsect := range f.Sections {
+ if _, found := sectsyms[rsect]; !found {
+ continue
+ }
+ if rsect.NumberOfRelocations == 0 {
+ continue
+ }
+ if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+ continue
+ }
+ if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+ // This has been seen for .idata sections, which we
+ // want to ignore. See issues 5106 and 5273.
+ continue
+ }
+
+ splitResources := strings.HasPrefix(rsect.Name, ".rsrc$")
+ sb := l.MakeSymbolUpdater(sectsyms[rsect])
+ for j, r := range rsect.Relocs {
+ if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
+ return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
+ }
+ pesym := &f.COFFSymbols[r.SymbolTableIndex]
+ _, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
+ if err != nil {
+ return nil, nil, err
+ }
+ if gosym == 0 {
+ name, err := pesym.FullName(f.StringTable)
+ if err != nil {
+ name = string(pesym.Name[:])
+ }
+ return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
+ }
+
+ rSym := gosym
+ rSize := uint8(4)
+ rOff := int32(r.VirtualAddress)
+ var rAdd int64
+ var rType objabi.RelocType
+ switch arch.Family {
+ default:
+ return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
+ case sys.I386, sys.AMD64:
+ switch r.Type {
+ default:
+ return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
+
+ case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
+ IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
+ IMAGE_REL_AMD64_ADDR32NB:
+ rType = objabi.R_PCREL
+
+ rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+
+ case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
+ rType = objabi.R_ADDR
+
+ // load addend from image
+ rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+
+ case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
+ rSize = 8
+
+ rType = objabi.R_ADDR
+
+ // load addend from image
+ rAdd = int64(binary.LittleEndian.Uint64(sectdata[rsect][rOff:]))
+ }
+
+ case sys.ARM:
+ switch r.Type {
+ default:
+ return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
+
+ case IMAGE_REL_ARM_SECREL:
+ rType = objabi.R_PCREL
+
+ rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+
+ case IMAGE_REL_ARM_ADDR32, IMAGE_REL_ARM_ADDR32NB:
+ rType = objabi.R_ADDR
+
+ rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+
+ case IMAGE_REL_ARM_BRANCH24:
+ rType = objabi.R_CALLARM
+
+ rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+ }
+ }
+
+ // ld -r could generate multiple section symbols for the
+ // same section but with different values, we have to take
+ // that into account, or in the case of split resources,
+ // the section and its symbols are split into two sections.
+ if issect(pesym) || splitResources {
+ rAdd += int64(pesym.Value)
+ }
+
+ rel, _ := sb.AddRel(rType)
+ rel.SetOff(rOff)
+ rel.SetSiz(rSize)
+ rel.SetSym(rSym)
+ rel.SetAdd(rAdd)
+ }
+
+ sb.SortRelocs()
+ }
+
+ // enter sub-symbols into symbol table.
+ for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 {
+ pesym := &f.COFFSymbols[i]
+
+ numaux = int(pesym.NumberOfAuxSymbols)
+
+ name, err := pesym.FullName(f.StringTable)
+ if err != nil {
+ return nil, nil, err
+ }
+ if name == "" {
+ continue
+ }
+ if issect(pesym) {
+ continue
+ }
+ if int(pesym.SectionNumber) > len(f.Sections) {
+ continue
+ }
+ if pesym.SectionNumber == IMAGE_SYM_DEBUG {
+ continue
+ }
+ if pesym.SectionNumber == IMAGE_SYM_ABSOLUTE && bytes.Equal(pesym.Name[:], []byte("@feat.00")) {
+ // Microsoft's linker looks at whether all input objects have an empty
+ // section called @feat.00. If all of them do, then it enables SEH;
+ // otherwise it doesn't enable that feature. So, since around the Windows
+ // XP SP2 era, most tools that make PE objects just tack on that section,
+ // so that it won't gimp Microsoft's linker logic. Go doesn't support SEH,
+ // so in theory, none of this really matters to us. But actually, if the
+ // linker tries to ingest an object with @feat.00 -- which are produced by
+ // LLVM's resource compiler, for example -- it chokes because of the
+ // IMAGE_SYM_ABSOLUTE section that it doesn't know how to deal with. Since
+ // @feat.00 is just a marking anyway, skip IMAGE_SYM_ABSOLUTE sections that
+ // are called @feat.00.
+ continue
+ }
+ var sect *pe.Section
+ if pesym.SectionNumber > 0 {
+ sect = f.Sections[pesym.SectionNumber-1]
+ if _, found := sectsyms[sect]; !found {
+ continue
+ }
+ }
+
+ bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if pesym.SectionNumber == 0 { // extern
+ if l.SymType(s) == sym.SDYNIMPORT {
+ bld = makeUpdater(l, bld, s)
+ bld.SetPlt(-2) // flag for dynimport in PE object files.
+ }
+ if l.SymType(s) == sym.SXREF && pesym.Value > 0 { // global data
+ bld = makeUpdater(l, bld, s)
+ bld.SetType(sym.SNOPTRDATA)
+ bld.SetSize(int64(pesym.Value))
+ }
+
+ continue
+ } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
+ sect = f.Sections[pesym.SectionNumber-1]
+ if _, found := sectsyms[sect]; !found {
+ return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
+ }
+ } else {
+ return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
+ }
+
+ if sect == nil {
+ return nil, nil, nil
+ }
+
+ if l.OuterSym(s) != 0 {
+ if l.AttrDuplicateOK(s) {
+ continue
+ }
+ outerName := l.SymName(l.OuterSym(s))
+ sectName := l.SymName(sectsyms[sect])
+ return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
+ }
+
+ bld = makeUpdater(l, bld, s)
+ sectsym := sectsyms[sect]
+ bld.SetType(l.SymType(sectsym))
+ l.AddInteriorSym(sectsym, s)
+ bld.SetValue(int64(pesym.Value))
+ bld.SetSize(4)
+ if l.SymType(sectsym) == sym.STEXT {
+ if bld.External() && !bld.DuplicateOK() {
+ return nil, nil, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
+ }
+ bld.SetExternal(true)
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for _, sect := range f.Sections {
+ s := sectsyms[sect]
+ if s == 0 {
+ continue
+ }
+ l.SortSub(s)
+ if l.SymType(s) == sym.STEXT {
+ for ; s != 0; s = l.SubSym(s) {
+ if l.AttrOnList(s) {
+ return nil, nil, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
+ }
+ l.SetAttrOnList(s, true)
+ textp = append(textp, s)
+ }
+ }
+ }
+
+ return textp, rsrc, nil
+}
+
+func issect(s *pe.COFFSymbol) bool {
+ return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
+}
+
+func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader.Sym, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]loader.Sym, localSymVersion int) (*loader.SymbolBuilder, loader.Sym, error) {
+ symname, err := pesym.FullName(f.StringTable)
+ if err != nil {
+ return nil, 0, err
+ }
+ var name string
+ if issect(pesym) {
+ name = l.SymName(sectsyms[f.Sections[pesym.SectionNumber-1]])
+ } else {
+ name = symname
+ switch arch.Family {
+ case sys.AMD64:
+ if name == "__imp___acrt_iob_func" {
+ // Do not rename __imp___acrt_iob_func into __acrt_iob_func,
+ // because __imp___acrt_iob_func symbol is real
+ // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details).
+ } else {
+ name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
+ }
+ case sys.I386:
+ if name == "__imp____acrt_iob_func" {
+ // Do not rename __imp____acrt_iob_func into ___acrt_iob_func,
+ // because __imp____acrt_iob_func symbol is real
+ // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details).
+ } else {
+ name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
+ }
+ if name[0] == '_' {
+ name = name[1:] // _Name => Name
+ }
+ }
+ }
+
+ // remove last @XXX
+ if i := strings.LastIndex(name, "@"); i >= 0 {
+ name = name[:i]
+ }
+
+ var s loader.Sym
+ var bld *loader.SymbolBuilder
+ switch pesym.Type {
+ default:
+ return nil, 0, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type)
+
+ case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
+ switch pesym.StorageClass {
+ case IMAGE_SYM_CLASS_EXTERNAL: //global
+ s = lookup(name, 0)
+
+ case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
+ s = lookup(name, localSymVersion)
+ bld = makeUpdater(l, bld, s)
+ bld.SetDuplicateOK(true)
+
+ default:
+ return nil, 0, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass)
+ }
+ }
+
+ if s != 0 && l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) {
+ bld = makeUpdater(l, bld, s)
+ bld.SetType(sym.SXREF)
+ }
+ if strings.HasPrefix(symname, "__imp_") {
+ bld = makeUpdater(l, bld, s)
+ bld.SetGot(-2) // flag for __imp_
+ }
+
+ return bld, s, nil
+}
diff --git a/src/cmd/link/internal/loadxcoff/ldxcoff.go b/src/cmd/link/internal/loadxcoff/ldxcoff.go
new file mode 100644
index 0000000..a574421
--- /dev/null
+++ b/src/cmd/link/internal/loadxcoff/ldxcoff.go
@@ -0,0 +1,228 @@
+// 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 loadxcoff implements a XCOFF file reader.
+package loadxcoff
+
+import (
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "errors"
+ "fmt"
+ "internal/xcoff"
+)
+
+// ldSection is an XCOFF section with its symbols.
+type ldSection struct {
+ xcoff.Section
+ sym loader.Sym
+}
+
+// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf
+
+// xcoffBiobuf makes bio.Reader look like io.ReaderAt.
+type xcoffBiobuf bio.Reader
+
+func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) {
+ ret := ((*bio.Reader)(f)).MustSeek(off, 0)
+ if ret < 0 {
+ return 0, errors.New("fail to seek")
+ }
+ n, err := f.Read(p)
+ if err != nil {
+ return 0, err
+ }
+ return n, nil
+}
+
+// loads the Xcoff file pn from f.
+// Symbols are written into loader, and a slice of the text symbols is returned.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
+ return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ var ldSections []*ldSection
+
+ f, err := xcoff.NewFile((*xcoffBiobuf)(input))
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ for _, sect := range f.Sections {
+ //only text, data and bss section
+ if sect.Type < xcoff.STYP_TEXT || sect.Type > xcoff.STYP_BSS {
+ continue
+ }
+ lds := new(ldSection)
+ lds.Section = *sect
+ name := fmt.Sprintf("%s(%s)", pkg, lds.Name)
+ symbol := l.LookupOrCreateSym(name, localSymVersion)
+ s := l.MakeSymbolUpdater(symbol)
+
+ switch lds.Type {
+ default:
+ return errorf("unrecognized section type 0x%x", lds.Type)
+ case xcoff.STYP_TEXT:
+ s.SetType(sym.STEXT)
+ case xcoff.STYP_DATA:
+ s.SetType(sym.SNOPTRDATA)
+ case xcoff.STYP_BSS:
+ s.SetType(sym.SNOPTRBSS)
+ }
+
+ s.SetSize(int64(lds.Size))
+ if s.Type() != sym.SNOPTRBSS {
+ data, err := lds.Section.Data()
+ if err != nil {
+ return nil, err
+ }
+ s.SetData(data)
+ }
+
+ lds.sym = symbol
+ ldSections = append(ldSections, lds)
+ }
+
+ // sx = symbol from file
+ // s = symbol for loader
+ for _, sx := range f.Symbols {
+ // get symbol type
+ stype, errmsg := getSymbolType(f, sx)
+ if errmsg != "" {
+ return errorf("error reading symbol %s: %s", sx.Name, errmsg)
+ }
+ if stype == sym.Sxxx {
+ continue
+ }
+
+ s := l.LookupOrCreateSym(sx.Name, 0)
+
+ // Text symbol
+ if l.SymType(s) == sym.STEXT {
+ if l.AttrOnList(s) {
+ return errorf("symbol %s listed multiple times", l.SymName(s))
+ }
+ l.SetAttrOnList(s, true)
+ textp = append(textp, s)
+ }
+ }
+
+ // Read relocations
+ for _, sect := range ldSections {
+ // TODO(aix): Dwarf section relocation if needed
+ if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA {
+ continue
+ }
+ sb := l.MakeSymbolUpdater(sect.sym)
+ for _, rx := range sect.Relocs {
+ rSym := l.LookupOrCreateSym(rx.Symbol.Name, 0)
+ if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
+ return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
+ }
+ rOff := int32(rx.VirtualAddress)
+ var rSize uint8
+ var rType objabi.RelocType
+ var rAdd int64
+ switch rx.Type {
+ default:
+ return errorf("section %s: unknown relocation of type 0x%x", sect.Name, rx.Type)
+ case xcoff.R_POS:
+ // Reloc the address of r.Sym
+ // Length should be 64
+ if rx.Length != 64 {
+ return errorf("section %s: relocation R_POS has length different from 64: %d", sect.Name, rx.Length)
+ }
+ rSize = 8
+ rType = objabi.R_CONST
+ rAdd = int64(rx.Symbol.Value)
+
+ case xcoff.R_RBR:
+ rSize = 4
+ rType = objabi.R_CALLPOWER
+ rAdd = 0
+ }
+ r, _ := sb.AddRel(rType)
+ r.SetOff(rOff)
+ r.SetSiz(rSize)
+ r.SetSym(rSym)
+ r.SetAdd(rAdd)
+ }
+ }
+ return textp, nil
+
+}
+
+// Convert symbol xcoff type to sym.SymKind
+// Returns nil if this shouldn't be added into loader (like .file or .dw symbols )
+func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) {
+ // .file symbol
+ if s.SectionNumber == -2 {
+ if s.StorageClass == xcoff.C_FILE {
+ return sym.Sxxx, ""
+ }
+ return sym.Sxxx, "unrecognised StorageClass for sectionNumber = -2"
+ }
+
+ // extern symbols
+ // TODO(aix)
+ if s.SectionNumber == 0 {
+ return sym.Sxxx, ""
+ }
+
+ sectType := f.Sections[s.SectionNumber-1].SectionHeader.Type
+ switch sectType {
+ default:
+ return sym.Sxxx, fmt.Sprintf("getSymbolType for Section type 0x%x not implemented", sectType)
+ case xcoff.STYP_DWARF, xcoff.STYP_DEBUG:
+ return sym.Sxxx, ""
+ case xcoff.STYP_DATA, xcoff.STYP_BSS, xcoff.STYP_TEXT:
+ }
+
+ switch s.StorageClass {
+ default:
+ return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x not implemented", s.StorageClass)
+ case xcoff.C_HIDEXT, xcoff.C_EXT, xcoff.C_WEAKEXT:
+ switch s.AuxCSect.StorageMappingClass {
+ default:
+ return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x and Storage Map 0x%x not implemented", s.StorageClass, s.AuxCSect.StorageMappingClass)
+
+ // Program Code
+ case xcoff.XMC_PR:
+ if sectType == xcoff.STYP_TEXT {
+ return sym.STEXT, ""
+ }
+ return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_PR", sectType, s.StorageClass)
+
+ // Read/Write Data
+ case xcoff.XMC_RW:
+ if sectType == xcoff.STYP_DATA {
+ return sym.SDATA, ""
+ }
+ if sectType == xcoff.STYP_BSS {
+ return sym.SBSS, ""
+ }
+ return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_RW", sectType, s.StorageClass)
+
+ // Function descriptor
+ case xcoff.XMC_DS:
+ if sectType == xcoff.STYP_DATA {
+ return sym.SDATA, ""
+ }
+ return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass)
+
+ // TOC anchor and TOC entry
+ case xcoff.XMC_TC0, xcoff.XMC_TE:
+ if sectType == xcoff.STYP_DATA {
+ return sym.SXCOFFTOC, ""
+ }
+ return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass)
+
+ }
+ }
+}
diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go
new file mode 100644
index 0000000..17b1b20
--- /dev/null
+++ b/src/cmd/link/internal/mips/asm.go
@@ -0,0 +1,156 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 © 2016 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 mips
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ return
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write32(uint32(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ if r.Size != 4 {
+ return false
+ }
+ out.Write32(uint32(elf.R_MIPS_32) | uint32(elfsym)<<8)
+ case objabi.R_ADDRMIPS:
+ out.Write32(uint32(elf.R_MIPS_LO16) | uint32(elfsym)<<8)
+ case objabi.R_ADDRMIPSU:
+ out.Write32(uint32(elf.R_MIPS_HI16) | uint32(elfsym)<<8)
+ case objabi.R_ADDRMIPSTLS:
+ out.Write32(uint32(elf.R_MIPS_TLS_TPREL_LO16) | uint32(elfsym)<<8)
+ case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ out.Write32(uint32(elf.R_MIPS_26) | uint32(elfsym)<<8)
+ }
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ return
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func applyrel(arch *sys.Arch, ldr *loader.Loader, rt objabi.RelocType, off int32, s loader.Sym, val int64, t int64) int64 {
+ o := uint32(val)
+ switch rt {
+ case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSTLS:
+ return int64(o&0xffff0000 | uint32(t)&0xffff)
+ case objabi.R_ADDRMIPSU:
+ return int64(o&0xffff0000 | uint32((t+(1<<15))>>16)&0xffff)
+ case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ return int64(o&0xfc000000 | uint32(t>>2)&^0xfc000000)
+ default:
+ return val
+ }
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ rs := r.Sym()
+ rs = ldr.ResolveABIAlias(rs)
+ if target.IsExternal() {
+ switch r.Type() {
+ default:
+ return val, 0, false
+
+ case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
+ // set up addend for eventual relocation via outer symbol.
+ _, off := ld.FoldSubSymbolOffset(ldr, rs)
+ xadd := r.Add() + off
+ return applyrel(target.Arch, ldr, r.Type(), r.Off(), s, val, xadd), 1, true
+
+ case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ return applyrel(target.Arch, ldr, r.Type(), r.Off(), s, val, r.Add()), 1, true
+ }
+ }
+
+ const isOk = true
+ const noExtReloc = 0
+ switch rt := r.Type(); rt {
+ case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
+ t := ldr.SymValue(rs) + r.Add()
+ return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk
+ case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ t := ldr.SymValue(rs) + r.Add()
+
+ if t&3 != 0 {
+ ldr.Errorf(s, "direct call is not aligned: %s %x", ldr.SymName(rs), t)
+ }
+
+ // check if target address is in the same 256 MB region as the next instruction
+ if (ldr.SymValue(s)+int64(r.Off())+4)&0xf0000000 != (t & 0xf0000000) {
+ ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t)
+ }
+
+ return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk
+ case objabi.R_ADDRMIPSTLS:
+ // thread pointer is at 0x7000 offset from the start of TLS data area
+ t := ldr.SymValue(rs) + r.Add() - 0x7000
+ if t < -32768 || t >= 32678 {
+ ldr.Errorf(s, "TLS offset out of range %d", t)
+ }
+ return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 {
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+
+ case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ return ld.ExtrelocSimple(ldr, r), true
+ }
+ return loader.ExtReloc{}, false
+}
diff --git a/src/cmd/link/internal/mips/l.go b/src/cmd/link/internal/mips/l.go
new file mode 100644
index 0000000..affc48c
--- /dev/null
+++ b/src/cmd/link/internal/mips/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 © 2016 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 mips
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// 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-2008 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-2008 Lucent Technologies Inc. and others
+// Portions Copyright © 2016 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.
+
+const (
+ MaxAlign = 32 // max data alignment
+ MinAlign = 1 // min data alignment
+ FuncAlign = 4
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ DWARFREGSP = 29
+ DWARFREGLR = 31
+)
diff --git a/src/cmd/link/internal/mips/obj.go b/src/cmd/link/internal/mips/obj.go
new file mode 100644
index 0000000..f20597c
--- /dev/null
+++ b/src/cmd/link/internal/mips/obj.go
@@ -0,0 +1,88 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 © 2016 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 mips
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchMIPS
+ if objabi.GOARCH == "mipsle" {
+ arch = sys.ArchMIPSLE
+ }
+
+ theArch := ld.Arch{
+ Funcalign: FuncAlign,
+ Maxalign: MaxAlign,
+ Minalign: MinAlign,
+ Dwarfregsp: DWARFREGSP,
+ Dwarfreglr: DWARFREGLR,
+
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 8,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+
+ Linuxdynld: "/lib/ld.so.1",
+
+ Freebsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+ case objabi.Hlinux: /* mips elf */
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ }
+}
diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go
new file mode 100644
index 0000000..4789b41
--- /dev/null
+++ b/src/cmd/link/internal/mips64/asm.go
@@ -0,0 +1,157 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 mips64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+
+ // mips64 ELF relocation (endian neutral)
+ // offset uint64
+ // sym uint32
+ // ssym uint8
+ // type3 uint8
+ // type2 uint8
+ // type uint8
+ // addend int64
+
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ out.Write32(uint32(elfsym))
+ out.Write8(0)
+ out.Write8(0)
+ out.Write8(0)
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch r.Size {
+ case 4:
+ out.Write8(uint8(elf.R_MIPS_32))
+ case 8:
+ out.Write8(uint8(elf.R_MIPS_64))
+ default:
+ return false
+ }
+ case objabi.R_ADDRMIPS:
+ out.Write8(uint8(elf.R_MIPS_LO16))
+ case objabi.R_ADDRMIPSU:
+ out.Write8(uint8(elf.R_MIPS_HI16))
+ case objabi.R_ADDRMIPSTLS:
+ out.Write8(uint8(elf.R_MIPS_TLS_TPREL_LO16))
+ case objabi.R_CALLMIPS,
+ objabi.R_JMPMIPS:
+ out.Write8(uint8(elf.R_MIPS_26))
+ }
+ out.Write64(uint64(r.Xadd))
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ return
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ if target.IsExternal() {
+ switch r.Type() {
+ default:
+ return val, 0, false
+
+ case objabi.R_ADDRMIPS,
+ objabi.R_ADDRMIPSU,
+ objabi.R_ADDRMIPSTLS,
+ objabi.R_CALLMIPS,
+ objabi.R_JMPMIPS:
+ return val, 1, true
+ }
+ }
+
+ const isOk = true
+ const noExtReloc = 0
+ rs := r.Sym()
+ rs = ldr.ResolveABIAlias(rs)
+ switch r.Type() {
+ case objabi.R_ADDRMIPS,
+ objabi.R_ADDRMIPSU:
+ t := ldr.SymValue(rs) + r.Add()
+ if r.Type() == objabi.R_ADDRMIPS {
+ return int64(val&0xffff0000 | t&0xffff), noExtReloc, isOk
+ }
+ return int64(val&0xffff0000 | ((t+1<<15)>>16)&0xffff), noExtReloc, isOk
+ case objabi.R_ADDRMIPSTLS:
+ // thread pointer is at 0x7000 offset from the start of TLS data area
+ t := ldr.SymValue(rs) + r.Add() - 0x7000
+ if t < -32768 || t >= 32678 {
+ ldr.Errorf(s, "TLS offset out of range %d", t)
+ }
+ return int64(val&0xffff0000 | t&0xffff), noExtReloc, isOk
+ case objabi.R_CALLMIPS,
+ objabi.R_JMPMIPS:
+ // Low 26 bits = (S + A) >> 2
+ t := ldr.SymValue(rs) + r.Add()
+ return int64(val&0xfc000000 | (t>>2)&^0xfc000000), noExtReloc, isOk
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 {
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_ADDRMIPS,
+ objabi.R_ADDRMIPSU:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+
+ case objabi.R_ADDRMIPSTLS,
+ objabi.R_CALLMIPS,
+ objabi.R_JMPMIPS:
+ return ld.ExtrelocSimple(ldr, r), true
+ }
+ return loader.ExtReloc{}, false
+}
diff --git a/src/cmd/link/internal/mips64/l.go b/src/cmd/link/internal/mips64/l.go
new file mode 100644
index 0000000..837af0e
--- /dev/null
+++ b/src/cmd/link/internal/mips64/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 mips64
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// 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-2008 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-2008 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.
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 8
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 29
+ dwarfRegLR = 31
+)
diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go
new file mode 100644
index 0000000..01d89a2
--- /dev/null
+++ b/src/cmd/link/internal/mips64/obj.go
@@ -0,0 +1,98 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 mips64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchMIPS64
+ if objabi.GOARCH == "mips64le" {
+ arch = sys.ArchMIPS64LE
+ }
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+
+ Linuxdynld: "/lib64/ld64.so.1",
+ Freebsddynld: "XXX",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 16*1024 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 16 * 1024
+ }
+
+ case objabi.Hlinux, /* mips64 elf */
+ objabi.Hopenbsd:
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
new file mode 100644
index 0000000..539afac
--- /dev/null
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -0,0 +1,1075 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 ppc64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "fmt"
+ "log"
+ "strings"
+)
+
+func genplt(ctxt *ld.Link, ldr *loader.Loader) {
+ // The ppc64 ABI PLT has similar concepts to other
+ // architectures, but is laid out quite differently. When we
+ // see an R_PPC64_REL24 relocation to a dynamic symbol
+ // (indicating that the call needs to go through the PLT), we
+ // generate up to three stubs and reserve a PLT slot.
+ //
+ // 1) The call site will be bl x; nop (where the relocation
+ // applies to the bl). We rewrite this to bl x_stub; ld
+ // r2,24(r1). The ld is necessary because x_stub will save
+ // r2 (the TOC pointer) at 24(r1) (the "TOC save slot").
+ //
+ // 2) We reserve space for a pointer in the .plt section (once
+ // per referenced dynamic function). .plt is a data
+ // section filled solely by the dynamic linker (more like
+ // .plt.got on other architectures). Initially, the
+ // dynamic linker will fill each slot with a pointer to the
+ // corresponding x@plt entry point.
+ //
+ // 3) We generate the "call stub" x_stub (once per dynamic
+ // function/object file pair). This saves the TOC in the
+ // TOC save slot, reads the function pointer from x's .plt
+ // slot and calls it like any other global entry point
+ // (including setting r12 to the function address).
+ //
+ // 4) We generate the "symbol resolver stub" x@plt (once per
+ // dynamic function). This is solely a branch to the glink
+ // resolver stub.
+ //
+ // 5) We generate the glink resolver stub (only once). This
+ // computes which symbol resolver stub we came through and
+ // invokes the dynamic resolver via a pointer provided by
+ // the dynamic linker. This will patch up the .plt slot to
+ // point directly at the function so future calls go
+ // straight from the call stub to the real function, and
+ // then call the function.
+
+ // NOTE: It's possible we could make ppc64 closer to other
+ // architectures: ppc64's .plt is like .plt.got on other
+ // platforms and ppc64's .glink is like .plt on other
+ // platforms.
+
+ // Find all R_PPC64_REL24 relocations that reference dynamic
+ // imports. Reserve PLT entries for these symbols and
+ // generate call stubs. The call stubs need to live in .text,
+ // which is why we need to do this pass this early.
+ //
+ // This assumes "case 1" from the ABI, where the caller needs
+ // us to save and restore the TOC pointer.
+ var stubs []loader.Sym
+ for _, s := range ctxt.Textp {
+ relocs := ldr.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ if r.Type() != objabi.ElfRelocOffset+objabi.RelocType(elf.R_PPC64_REL24) || ldr.SymType(r.Sym()) != sym.SDYNIMPORT {
+ continue
+ }
+
+ // Reserve PLT entry and generate symbol
+ // resolver
+ addpltsym(ctxt, ldr, r.Sym())
+
+ // Generate call stub. Important to note that we're looking
+ // up the stub using the same version as the parent symbol (s),
+ // needed so that symtoc() will select the right .TOC. symbol
+ // when processing the stub. In older versions of the linker
+ // this was done by setting stub.Outer to the parent, but
+ // if the stub has the right version initially this is not needed.
+ n := fmt.Sprintf("%s.%s", ldr.SymName(s), ldr.SymName(r.Sym()))
+ stub := ldr.CreateSymForUpdate(n, ldr.SymVersion(s))
+ if stub.Size() == 0 {
+ stubs = append(stubs, stub.Sym())
+ gencallstub(ctxt, ldr, 1, stub, r.Sym())
+ }
+
+ // Update the relocation to use the call stub
+ r.SetSym(stub.Sym())
+
+ // make sure the data is writeable
+ if ldr.AttrReadOnly(s) {
+ panic("can't write to read-only sym data")
+ }
+
+ // Restore TOC after bl. The compiler put a
+ // nop here for us to overwrite.
+ sp := ldr.Data(s)
+ const o1 = 0xe8410018 // ld r2,24(r1)
+ ctxt.Arch.ByteOrder.PutUint32(sp[r.Off()+4:], o1)
+ }
+ }
+ // Put call stubs at the beginning (instead of the end).
+ // So when resolving the relocations to calls to the stubs,
+ // the addresses are known and trampolines can be inserted
+ // when necessary.
+ ctxt.Textp = append(stubs, ctxt.Textp...)
+}
+
+func genaddmoduledata(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op uint32) {
+ initfunc.AddUint32(ctxt.Arch, op)
+ }
+
+ // addis r2, r12, .TOC.-func@ha
+ toc := ctxt.DotTOC[0]
+ rel1, _ := initfunc.AddRel(objabi.R_ADDRPOWER_PCREL)
+ rel1.SetOff(0)
+ rel1.SetSiz(8)
+ rel1.SetSym(toc)
+ o(0x3c4c0000)
+ // addi r2, r2, .TOC.-func@l
+ o(0x38420000)
+ // mflr r31
+ o(0x7c0802a6)
+ // stdu r31, -32(r1)
+ o(0xf801ffe1)
+ // addis r3, r2, local.moduledata@got@ha
+ var tgt loader.Sym
+ if s := ldr.Lookup("local.moduledata", 0); s != 0 {
+ tgt = s
+ } else if s := ldr.Lookup("local.pluginmoduledata", 0); s != 0 {
+ tgt = s
+ } else {
+ tgt = ldr.LookupOrCreateSym("runtime.firstmoduledata", 0)
+ }
+ rel2, _ := initfunc.AddRel(objabi.R_ADDRPOWER_GOT)
+ rel2.SetOff(int32(initfunc.Size()))
+ rel2.SetSiz(8)
+ rel2.SetSym(tgt)
+ o(0x3c620000)
+ // ld r3, local.moduledata@got@l(r3)
+ o(0xe8630000)
+ // bl runtime.addmoduledata
+ rel3, _ := initfunc.AddRel(objabi.R_CALLPOWER)
+ rel3.SetOff(int32(initfunc.Size()))
+ rel3.SetSiz(4)
+ rel3.SetSym(addmoduledata)
+ o(0x48000001)
+ // nop
+ o(0x60000000)
+ // ld r31, 0(r1)
+ o(0xe8010000)
+ // mtlr r31
+ o(0x7c0803a6)
+ // addi r1,r1,32
+ o(0x38210020)
+ // blr
+ o(0x4e800020)
+}
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ if ctxt.DynlinkingGo() {
+ genaddmoduledata(ctxt, ldr)
+ }
+
+ if ctxt.LinkMode == ld.LinkInternal {
+ genplt(ctxt, ldr)
+ }
+}
+
+// Construct a call stub in stub that calls symbol targ via its PLT
+// entry.
+func gencallstub(ctxt *ld.Link, ldr *loader.Loader, abicase int, stub *loader.SymbolBuilder, targ loader.Sym) {
+ if abicase != 1 {
+ // If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC
+ // relocations, we'll need to implement cases 2 and 3.
+ log.Fatalf("gencallstub only implements case 1 calls")
+ }
+
+ plt := ctxt.PLT
+
+ stub.SetType(sym.STEXT)
+
+ // Save TOC pointer in TOC save slot
+ stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1)
+
+ // Load the function pointer from the PLT.
+ rel, ri1 := stub.AddRel(objabi.R_POWER_TOC)
+ rel.SetOff(int32(stub.Size()))
+ rel.SetSiz(2)
+ rel.SetAdd(int64(ldr.SymPlt(targ)))
+ rel.SetSym(plt)
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ rel.SetOff(rel.Off() + int32(rel.Siz()))
+ }
+ ldr.SetRelocVariant(stub.Sym(), int(ri1), sym.RV_POWER_HA)
+ stub.AddUint32(ctxt.Arch, 0x3d820000) // addis r12,r2,targ@plt@toc@ha
+
+ rel2, ri2 := stub.AddRel(objabi.R_POWER_TOC)
+ rel2.SetOff(int32(stub.Size()))
+ rel2.SetSiz(2)
+ rel2.SetAdd(int64(ldr.SymPlt(targ)))
+ rel2.SetSym(plt)
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ rel2.SetOff(rel2.Off() + int32(rel2.Siz()))
+ }
+ ldr.SetRelocVariant(stub.Sym(), int(ri2), sym.RV_POWER_LO)
+ stub.AddUint32(ctxt.Arch, 0xe98c0000) // ld r12,targ@plt@toc@l(r12)
+
+ // Jump to the loaded pointer
+ stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
+ stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ if target.IsElf() {
+ return addelfdynrel(target, ldr, syms, s, r, rIdx)
+ } else if target.IsAIX() {
+ return ld.Xcoffadddynrel(target, ldr, syms, s, r, rIdx)
+ }
+ return false
+}
+
+func addelfdynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLPOWER)
+
+ // This is a local call, so the caller isn't setting
+ // up r12 and r2 is the same for the caller and
+ // callee. Hence, we need to go to the local entry
+ // point. (If we don't do this, the callee will try
+ // to use r12 to compute r2.)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymLocalentry(targ))*4)
+
+ if targType == sym.SDYNIMPORT {
+ // Should have been handled in elfsetupplt
+ ldr.Errorf(s, "unexpected R_PPC64_REL24 for dyn import")
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC_REL32):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_PPC_REL32 for dyn import")
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_ADDR64):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if targType == sym.SDYNIMPORT {
+ // These happen in .toc sections
+ ld.Adddynsym(ldr, target, syms, targ)
+
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_PPC64_ADDR64)))
+ rela.AddUint64(target.Arch, uint64(r.Add()))
+ su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO|sym.RV_CHECK_OVERFLOW)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HA):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HI):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HI|sym.RV_CHECK_OVERFLOW)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_DS):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS|sym.RV_CHECK_OVERFLOW)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO_DS):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_LO):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO)
+ su.SetRelocAdd(rIdx, r.Add()+2) // Compensate for relocation size of 2
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HI):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HI|sym.RV_CHECK_OVERFLOW)
+ su.SetRelocAdd(rIdx, r.Add()+2)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HA):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW)
+ su.SetRelocAdd(rIdx, r.Add()+2)
+ return true
+ }
+
+ // Handle references to ELF symbols from our own object files.
+ if targType != sym.SDYNIMPORT {
+ return true
+ }
+
+ // TODO(austin): Translate our relocations to ELF
+
+ return false
+}
+
+func xcoffreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ rs := r.Xsym
+
+ emitReloc := func(v uint16, off uint64) {
+ out.Write64(uint64(sectoff) + off)
+ out.Write32(uint32(ldr.SymDynid(rs)))
+ out.Write16(v)
+ }
+
+ var v uint16
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ v = ld.XCOFF_R_POS
+ if r.Size == 4 {
+ v |= 0x1F << 8
+ } else {
+ v |= 0x3F << 8
+ }
+ emitReloc(v, 0)
+ case objabi.R_ADDRPOWER_TOCREL:
+ case objabi.R_ADDRPOWER_TOCREL_DS:
+ emitReloc(ld.XCOFF_R_TOCU|(0x0F<<8), 2)
+ emitReloc(ld.XCOFF_R_TOCL|(0x0F<<8), 6)
+ case objabi.R_POWER_TLS_LE:
+ emitReloc(ld.XCOFF_R_TLS_LE|0x0F<<8, 2)
+ case objabi.R_CALLPOWER:
+ if r.Size != 4 {
+ return false
+ }
+ emitReloc(ld.XCOFF_R_RBR|0x19<<8, 0)
+ case objabi.R_XCOFFREF:
+ emitReloc(ld.XCOFF_R_REF|0x3F<<8, 0)
+ }
+ return true
+
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ // Beware that bit0~bit15 start from the third byte of a instruction in Big-Endian machines.
+ rt := r.Type
+ if rt == objabi.R_ADDR || rt == objabi.R_POWER_TLS || rt == objabi.R_CALLPOWER {
+ } else {
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ sectoff += 2
+ }
+ }
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ switch rt {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch r.Size {
+ case 4:
+ out.Write64(uint64(elf.R_PPC64_ADDR32) | uint64(elfsym)<<32)
+ case 8:
+ out.Write64(uint64(elf.R_PPC64_ADDR64) | uint64(elfsym)<<32)
+ default:
+ return false
+ }
+ case objabi.R_POWER_TLS:
+ out.Write64(uint64(elf.R_PPC64_TLS) | uint64(elfsym)<<32)
+ case objabi.R_POWER_TLS_LE:
+ out.Write64(uint64(elf.R_PPC64_TPREL16) | uint64(elfsym)<<32)
+ case objabi.R_POWER_TLS_IE:
+ out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_LO_DS) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER:
+ out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_ADDR16_LO) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_DS:
+ out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_ADDR16_LO_DS) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_GOT:
+ out.Write64(uint64(elf.R_PPC64_GOT16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_GOT16_LO_DS) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_PCREL:
+ out.Write64(uint64(elf.R_PPC64_REL16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_REL16_LO) | uint64(elfsym)<<32)
+ r.Xadd += 4
+ case objabi.R_ADDRPOWER_TOCREL:
+ out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_TOC16_LO) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_TOCREL_DS:
+ out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_TOC16_LO_DS) | uint64(elfsym)<<32)
+ case objabi.R_CALLPOWER:
+ if r.Size != 4 {
+ return false
+ }
+ out.Write64(uint64(elf.R_PPC64_REL24) | uint64(elfsym)<<32)
+
+ }
+ out.Write64(uint64(r.Xadd))
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // The dynamic linker stores the address of the
+ // dynamic resolver and the DSO identifier in the two
+ // doublewords at the beginning of the .plt section
+ // before the PLT array. Reserve space for these.
+ plt.SetSize(16)
+ }
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+// Return the value of .TOC. for symbol s
+func symtoc(ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) int64 {
+ v := ldr.SymVersion(s)
+ if out := ldr.OuterSym(s); out != 0 {
+ v = ldr.SymVersion(out)
+ }
+
+ toc := syms.DotTOC[v]
+ if toc == 0 {
+ ldr.Errorf(s, "TOC-relative relocation in object without .TOC.")
+ return 0
+ }
+
+ return ldr.SymValue(toc)
+}
+
+// archreloctoc relocates a TOC relative symbol.
+// If the symbol pointed by this TOC relative symbol is in .data or .bss, the
+// default load instruction can be changed to an addi instruction and the
+// symbol address can be used directly.
+// This code is for AIX only.
+func archreloctoc(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) int64 {
+ rs := ldr.ResolveABIAlias(r.Sym())
+ if target.IsLinux() {
+ ldr.Errorf(s, "archrelocaddr called for %s relocation\n", ldr.SymName(rs))
+ }
+ var o1, o2 uint32
+
+ o1 = uint32(val >> 32)
+ o2 = uint32(val)
+
+ if !strings.HasPrefix(ldr.SymName(rs), "TOC.") {
+ ldr.Errorf(s, "archreloctoc called for a symbol without TOC anchor")
+ }
+ var t int64
+ useAddi := false
+ relocs := ldr.Relocs(rs)
+ tarSym := ldr.ResolveABIAlias(relocs.At(0).Sym())
+
+ if target.IsInternal() && tarSym != 0 && ldr.AttrReachable(tarSym) && ldr.SymSect(tarSym).Seg == &ld.Segdata {
+ t = ldr.SymValue(tarSym) + r.Add() - ldr.SymValue(syms.TOC)
+ // change ld to addi in the second instruction
+ o2 = (o2 & 0x03FF0000) | 0xE<<26
+ useAddi = true
+ } else {
+ t = ldr.SymValue(rs) + r.Add() - ldr.SymValue(syms.TOC)
+ }
+
+ if t != int64(int32(t)) {
+ ldr.Errorf(s, "TOC relocation for %s is too big to relocate %s: 0x%x", ldr.SymName(s), rs, t)
+ }
+
+ if t&0x8000 != 0 {
+ t += 0x10000
+ }
+
+ o1 |= uint32((t >> 16) & 0xFFFF)
+
+ switch r.Type() {
+ case objabi.R_ADDRPOWER_TOCREL_DS:
+ if useAddi {
+ o2 |= uint32(t) & 0xFFFF
+ } else {
+ if t&3 != 0 {
+ ldr.Errorf(s, "bad DS reloc for %s: %d", ldr.SymName(s), ldr.SymValue(rs))
+ }
+ o2 |= uint32(t) & 0xFFFC
+ }
+ default:
+ return -1
+ }
+
+ return int64(o1)<<32 | int64(o2)
+}
+
+// archrelocaddr relocates a symbol address.
+// This code is for AIX only.
+func archrelocaddr(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) int64 {
+ rs := ldr.ResolveABIAlias(r.Sym())
+ if target.IsAIX() {
+ ldr.Errorf(s, "archrelocaddr called for %s relocation\n", ldr.SymName(rs))
+ }
+ var o1, o2 uint32
+ if target.IsBigEndian() {
+ o1 = uint32(val >> 32)
+ o2 = uint32(val)
+ } else {
+ o1 = uint32(val)
+ o2 = uint32(val >> 32)
+ }
+
+ // We are spreading a 31-bit address across two instructions, putting the
+ // high (adjusted) part in the low 16 bits of the first instruction and the
+ // low part in the low 16 bits of the second instruction, or, in the DS case,
+ // bits 15-2 (inclusive) of the address into bits 15-2 of the second
+ // instruction (it is an error in this case if the low 2 bits of the address
+ // are non-zero).
+
+ t := ldr.SymAddr(rs) + r.Add()
+ if t < 0 || t >= 1<<31 {
+ ldr.Errorf(s, "relocation for %s is too big (>=2G): 0x%x", ldr.SymName(s), ldr.SymValue(rs))
+ }
+ if t&0x8000 != 0 {
+ t += 0x10000
+ }
+
+ switch r.Type() {
+ case objabi.R_ADDRPOWER:
+ o1 |= (uint32(t) >> 16) & 0xffff
+ o2 |= uint32(t) & 0xffff
+ case objabi.R_ADDRPOWER_DS:
+ o1 |= (uint32(t) >> 16) & 0xffff
+ if t&3 != 0 {
+ ldr.Errorf(s, "bad DS reloc for %s: %d", ldr.SymName(s), ldr.SymValue(rs))
+ }
+ o2 |= uint32(t) & 0xfffc
+ default:
+ return -1
+ }
+
+ if target.IsBigEndian() {
+ return int64(o1)<<32 | int64(o2)
+ }
+ return int64(o2)<<32 | int64(o1)
+}
+
+// Determine if the code was compiled so that the TOC register R2 is initialized and maintained
+func r2Valid(ctxt *ld.Link) bool {
+ switch ctxt.BuildMode {
+ case ld.BuildModeCArchive, ld.BuildModeCShared, ld.BuildModePIE, ld.BuildModeShared, ld.BuildModePlugin:
+ return true
+ }
+ // -linkshared option
+ return ctxt.IsSharedGoLink()
+}
+
+// resolve direct jump relocation r in s, and add trampoline if necessary
+func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
+
+ // Trampolines are created if the branch offset is too large and the linker cannot insert a call stub to handle it.
+ // For internal linking, trampolines are always created for long calls.
+ // For external linking, the linker can insert a call stub to handle a long call, but depends on having the TOC address in
+ // r2. For those build modes with external linking where the TOC address is not maintained in r2, trampolines must be created.
+ if ctxt.IsExternal() && r2Valid(ctxt) {
+ // No trampolines needed since r2 contains the TOC
+ return
+ }
+
+ relocs := ldr.Relocs(s)
+ r := relocs.At(ri)
+ var t int64
+ // ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
+ // laid out. Conservatively use a trampoline. This should be rare, as we lay out packages
+ // in dependency order.
+ if ldr.SymValue(rs) != 0 {
+ t = ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
+ }
+ switch r.Type() {
+ case objabi.R_CALLPOWER:
+
+ // If branch offset is too far then create a trampoline.
+
+ if (ctxt.IsExternal() && ldr.SymSect(s) != ldr.SymSect(rs)) || (ctxt.IsInternal() && int64(int32(t<<6)>>6) != t) || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && ldr.SymPkg(s) != ldr.SymPkg(rs)) {
+ var tramp loader.Sym
+ for i := 0; ; i++ {
+
+ // Using r.Add as part of the name is significant in functions like duffzero where the call
+ // target is at some offset within the function. Calls to duff+8 and duff+256 must appear as
+ // distinct trampolines.
+
+ oName := ldr.SymName(rs)
+ name := oName
+ if r.Add() == 0 {
+ name += fmt.Sprintf("-tramp%d", i)
+ } else {
+ name += fmt.Sprintf("%+x-tramp%d", r.Add(), i)
+ }
+
+ // Look up the trampoline in case it already exists
+
+ tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
+ if oName == "runtime.deferreturn" {
+ ldr.SetIsDeferReturnTramp(tramp, true)
+ }
+ if ldr.SymValue(tramp) == 0 {
+ break
+ }
+
+ t = ldr.SymValue(tramp) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
+
+ // With internal linking, the trampoline can be used if it is not too far.
+ // With external linking, the trampoline must be in this section for it to be reused.
+ if (ctxt.IsInternal() && int64(int32(t<<6)>>6) == t) || (ctxt.IsExternal() && ldr.SymSect(s) == ldr.SymSect(tramp)) {
+ break
+ }
+ }
+ if ldr.SymType(tramp) == 0 {
+ if r2Valid(ctxt) {
+ // Should have returned for above cases
+ ctxt.Errorf(s, "unexpected trampoline for shared or dynamic linking")
+ } else {
+ trampb := ldr.MakeSymbolUpdater(tramp)
+ ctxt.AddTramp(trampb)
+ gentramp(ctxt, ldr, trampb, rs, r.Add())
+ }
+ }
+ sb := ldr.MakeSymbolUpdater(s)
+ relocs := sb.Relocs()
+ r := relocs.At(ri)
+ r.SetSym(tramp)
+ r.SetAdd(0) // This was folded into the trampoline target address
+ }
+ default:
+ ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+ }
+}
+
+func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(16) // 4 instructions
+ P := make([]byte, tramp.Size())
+ t := ldr.SymValue(target) + offset
+ var o1, o2 uint32
+
+ if ctxt.IsAIX() {
+ // On AIX, the address is retrieved with a TOC symbol.
+ // For internal linking, the "Linux" way might still be used.
+ // However, all text symbols are accessed with a TOC symbol as
+ // text relocations aren't supposed to be possible.
+ // So, keep using the external linking way to be more AIX friendly.
+ o1 = uint32(0x3fe20000) // lis r2, toctargetaddr hi
+ o2 = uint32(0xebff0000) // ld r31, toctargetaddr lo
+
+ toctramp := ldr.CreateSymForUpdate("TOC."+ldr.SymName(tramp.Sym()), 0)
+ toctramp.SetType(sym.SXCOFFTOC)
+ toctramp.AddAddrPlus(ctxt.Arch, target, offset)
+
+ r, _ := tramp.AddRel(objabi.R_ADDRPOWER_TOCREL_DS)
+ r.SetOff(0)
+ r.SetSiz(8) // generates 2 relocations: HA + LO
+ r.SetSym(toctramp.Sym())
+ } else {
+ // Used for default build mode for an executable
+ // Address of the call target is generated using
+ // relocation and doesn't depend on r2 (TOC).
+ o1 = uint32(0x3fe00000) // lis r31,targetaddr hi
+ o2 = uint32(0x3bff0000) // addi r31,targetaddr lo
+
+ // With external linking, the target address must be
+ // relocated using LO and HA
+ if ctxt.IsExternal() || ldr.SymValue(target) == 0 {
+ r, _ := tramp.AddRel(objabi.R_ADDRPOWER)
+ r.SetOff(0)
+ r.SetSiz(8) // generates 2 relocations: HA + LO
+ r.SetSym(target)
+ r.SetAdd(offset)
+ } else {
+ // adjustment needed if lo has sign bit set
+ // when using addi to compute address
+ val := uint32((t & 0xffff0000) >> 16)
+ if t&0x8000 != 0 {
+ val += 1
+ }
+ o1 |= val // hi part of addr
+ o2 |= uint32(t & 0xffff) // lo part of addr
+ }
+ }
+
+ o3 := uint32(0x7fe903a6) // mtctr r31
+ o4 := uint32(0x4e800420) // bctr
+ ctxt.Arch.ByteOrder.PutUint32(P, o1)
+ ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
+ ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
+ ctxt.Arch.ByteOrder.PutUint32(P[12:], o4)
+ tramp.SetData(P)
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (relocatedOffset int64, nExtReloc int, ok bool) {
+ rs := ldr.ResolveABIAlias(r.Sym())
+ if target.IsExternal() {
+ // On AIX, relocations (except TLS ones) must be also done to the
+ // value with the current addresses.
+ switch rt := r.Type(); rt {
+ default:
+ if !target.IsAIX() {
+ return val, nExtReloc, false
+ }
+ case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE:
+ // check Outer is nil, Type is TLSBSS?
+ nExtReloc = 1
+ if rt == objabi.R_POWER_TLS_IE {
+ nExtReloc = 2 // need two ELF relocations, see elfreloc1
+ }
+ return val, nExtReloc, true
+ case objabi.R_ADDRPOWER,
+ objabi.R_ADDRPOWER_DS,
+ objabi.R_ADDRPOWER_TOCREL,
+ objabi.R_ADDRPOWER_TOCREL_DS,
+ objabi.R_ADDRPOWER_GOT,
+ objabi.R_ADDRPOWER_PCREL:
+ nExtReloc = 2 // need two ELF relocations, see elfreloc1
+ if !target.IsAIX() {
+ return val, nExtReloc, true
+ }
+ case objabi.R_CALLPOWER:
+ nExtReloc = 1
+ if !target.IsAIX() {
+ return val, nExtReloc, true
+ }
+ }
+ }
+
+ switch r.Type() {
+ case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS:
+ return archreloctoc(ldr, target, syms, r, s, val), nExtReloc, true
+ case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS:
+ return archrelocaddr(ldr, target, syms, r, s, val), nExtReloc, true
+ case objabi.R_CALLPOWER:
+ // Bits 6 through 29 = (S + A - P) >> 2
+
+ t := ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
+
+ if t&3 != 0 {
+ ldr.Errorf(s, "relocation for %s+%d is not aligned: %d", ldr.SymName(rs), r.Off(), t)
+ }
+ // If branch offset is too far then create a trampoline.
+
+ if int64(int32(t<<6)>>6) != t {
+ ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t)
+ }
+ return val | int64(uint32(t)&^0xfc000003), nExtReloc, true
+ case objabi.R_POWER_TOC: // S + A - .TOC.
+ return ldr.SymValue(rs) + r.Add() - symtoc(ldr, syms, s), nExtReloc, true
+
+ case objabi.R_POWER_TLS_LE:
+ // The thread pointer points 0x7000 bytes after the start of the
+ // thread local storage area as documented in section "3.7.2 TLS
+ // Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI
+ // Specification".
+ v := ldr.SymValue(rs) - 0x7000
+ if target.IsAIX() {
+ // On AIX, the thread pointer points 0x7800 bytes after
+ // the TLS.
+ v -= 0x800
+ }
+ if int64(int16(v)) != v {
+ ldr.Errorf(s, "TLS offset out of range %d", v)
+ }
+ return (val &^ 0xffff) | (v & 0xffff), nExtReloc, true
+ }
+
+ return val, nExtReloc, false
+}
+
+func archrelocvariant(target *ld.Target, ldr *loader.Loader, r loader.Reloc, rv sym.RelocVariant, s loader.Sym, t int64) (relocatedOffset int64) {
+ rs := ldr.ResolveABIAlias(r.Sym())
+ switch rv & sym.RV_TYPE_MASK {
+ default:
+ ldr.Errorf(s, "unexpected relocation variant %d", rv)
+ fallthrough
+
+ case sym.RV_NONE:
+ return t
+
+ case sym.RV_POWER_LO:
+ if rv&sym.RV_CHECK_OVERFLOW != 0 {
+ // Whether to check for signed or unsigned
+ // overflow depends on the instruction
+ var o1 uint32
+ if target.IsBigEndian() {
+ o1 = binary.BigEndian.Uint32(ldr.Data(s)[r.Off()-2:])
+ } else {
+ o1 = binary.LittleEndian.Uint32(ldr.Data(s)[r.Off():])
+ }
+ switch o1 >> 26 {
+ case 24, // ori
+ 26, // xori
+ 28: // andi
+ if t>>16 != 0 {
+ goto overflow
+ }
+
+ default:
+ if int64(int16(t)) != t {
+ goto overflow
+ }
+ }
+ }
+
+ return int64(int16(t))
+
+ case sym.RV_POWER_HA:
+ t += 0x8000
+ fallthrough
+
+ // Fallthrough
+ case sym.RV_POWER_HI:
+ t >>= 16
+
+ if rv&sym.RV_CHECK_OVERFLOW != 0 {
+ // Whether to check for signed or unsigned
+ // overflow depends on the instruction
+ var o1 uint32
+ if target.IsBigEndian() {
+ o1 = binary.BigEndian.Uint32(ldr.Data(s)[r.Off()-2:])
+ } else {
+ o1 = binary.LittleEndian.Uint32(ldr.Data(s)[r.Off():])
+ }
+ switch o1 >> 26 {
+ case 25, // oris
+ 27, // xoris
+ 29: // andis
+ if t>>16 != 0 {
+ goto overflow
+ }
+
+ default:
+ if int64(int16(t)) != t {
+ goto overflow
+ }
+ }
+ }
+
+ return int64(int16(t))
+
+ case sym.RV_POWER_DS:
+ var o1 uint32
+ if target.IsBigEndian() {
+ o1 = uint32(binary.BigEndian.Uint16(ldr.Data(s)[r.Off():]))
+ } else {
+ o1 = uint32(binary.LittleEndian.Uint16(ldr.Data(s)[r.Off():]))
+ }
+ if t&3 != 0 {
+ ldr.Errorf(s, "relocation for %s+%d is not aligned: %d", ldr.SymName(rs), r.Off(), t)
+ }
+ if (rv&sym.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t {
+ goto overflow
+ }
+ return int64(o1)&0x3 | int64(int16(t))
+ }
+
+overflow:
+ ldr.Errorf(s, "relocation for %s+%d is too big: %d", ldr.SymName(rs), r.Off(), t)
+ return t
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE, objabi.R_CALLPOWER:
+ return ld.ExtrelocSimple(ldr, r), true
+ case objabi.R_ADDRPOWER,
+ objabi.R_ADDRPOWER_DS,
+ objabi.R_ADDRPOWER_TOCREL,
+ objabi.R_ADDRPOWER_TOCREL_DS,
+ objabi.R_ADDRPOWER_GOT,
+ objabi.R_ADDRPOWER_PCREL:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+ }
+ return loader.ExtReloc{}, false
+}
+
+func addpltsym(ctxt *ld.Link, ldr *loader.Loader, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, &ctxt.Target, &ctxt.ArchSyms, s)
+
+ if ctxt.IsELF {
+ plt := ldr.MakeSymbolUpdater(ctxt.PLT)
+ rela := ldr.MakeSymbolUpdater(ctxt.RelaPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // Create the glink resolver if necessary
+ glink := ensureglinkresolver(ctxt, ldr)
+
+ // Write symbol resolver stub (just a branch to the
+ // glink resolver stub)
+ rel, _ := glink.AddRel(objabi.R_CALLPOWER)
+ rel.SetOff(int32(glink.Size()))
+ rel.SetSiz(4)
+ rel.SetSym(glink.Sym())
+ glink.AddUint32(ctxt.Arch, 0x48000000) // b .glink
+
+ // In the ppc64 ABI, the dynamic linker is responsible
+ // for writing the entire PLT. We just need to
+ // reserve 8 bytes for each PLT entry and generate a
+ // JMP_SLOT dynamic relocation for it.
+ //
+ // TODO(austin): ABI v1 is different
+ ldr.SetPlt(s, int32(plt.Size()))
+
+ plt.Grow(plt.Size() + 8)
+ plt.SetSize(plt.Size() + 8)
+
+ rela.AddAddrPlus(ctxt.Arch, plt.Sym(), int64(ldr.SymPlt(s)))
+ rela.AddUint64(ctxt.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_PPC64_JMP_SLOT)))
+ rela.AddUint64(ctxt.Arch, 0)
+ } else {
+ ctxt.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
+
+// Generate the glink resolver stub if necessary and return the .glink section
+func ensureglinkresolver(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuilder {
+ glink := ldr.CreateSymForUpdate(".glink", 0)
+ if glink.Size() != 0 {
+ return glink
+ }
+
+ // This is essentially the resolver from the ppc64 ELF ABI.
+ // At entry, r12 holds the address of the symbol resolver stub
+ // for the target routine and the argument registers hold the
+ // arguments for the target routine.
+ //
+ // This stub is PIC, so first get the PC of label 1 into r11.
+ // Other things will be relative to this.
+ glink.AddUint32(ctxt.Arch, 0x7c0802a6) // mflr r0
+ glink.AddUint32(ctxt.Arch, 0x429f0005) // bcl 20,31,1f
+ glink.AddUint32(ctxt.Arch, 0x7d6802a6) // 1: mflr r11
+ glink.AddUint32(ctxt.Arch, 0x7c0803a6) // mtlf r0
+
+ // Compute the .plt array index from the entry point address.
+ // Because this is PIC, everything is relative to label 1b (in
+ // r11):
+ // r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4
+ glink.AddUint32(ctxt.Arch, 0x3800ffd0) // li r0,-(res_0-1b)=-48
+ glink.AddUint32(ctxt.Arch, 0x7c006214) // add r0,r0,r12
+ glink.AddUint32(ctxt.Arch, 0x7c0b0050) // sub r0,r0,r11
+ glink.AddUint32(ctxt.Arch, 0x7800f082) // srdi r0,r0,2
+
+ // r11 = address of the first byte of the PLT
+ glink.AddSymRef(ctxt.Arch, ctxt.PLT, 0, objabi.R_ADDRPOWER, 8)
+
+ glink.AddUint32(ctxt.Arch, 0x3d600000) // addis r11,0,.plt@ha
+ glink.AddUint32(ctxt.Arch, 0x396b0000) // addi r11,r11,.plt@l
+
+ // Load r12 = dynamic resolver address and r11 = DSO
+ // identifier from the first two doublewords of the PLT.
+ glink.AddUint32(ctxt.Arch, 0xe98b0000) // ld r12,0(r11)
+ glink.AddUint32(ctxt.Arch, 0xe96b0008) // ld r11,8(r11)
+
+ // Jump to the dynamic resolver
+ glink.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
+ glink.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+
+ // The symbol resolvers must immediately follow.
+ // res_0:
+
+ // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes
+ // before the first symbol resolver stub.
+ du := ldr.MakeSymbolUpdater(ctxt.Dynamic)
+ ld.Elfwritedynentsymplus(ctxt, du, elf.DT_PPC64_GLINK, glink.Sym(), glink.Size()-32)
+
+ return glink
+}
diff --git a/src/cmd/link/internal/ppc64/l.go b/src/cmd/link/internal/ppc64/l.go
new file mode 100644
index 0000000..e8d3b68
--- /dev/null
+++ b/src/cmd/link/internal/ppc64/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 ppc64
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// 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-2008 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-2008 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.
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 1
+ dwarfRegLR = 65
+)
diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go
new file mode 100644
index 0000000..ef4393f
--- /dev/null
+++ b/src/cmd/link/internal/ppc64/obj.go
@@ -0,0 +1,107 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 ppc64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchPPC64
+ if objabi.GOARCH == "ppc64le" {
+ arch = sys.ArchPPC64LE
+ }
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ WriteTextBlocks: true,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Trampoline: trampoline,
+ Machoreloc1: machoreloc1,
+ Xcoffreloc1: xcoffreloc1,
+
+ // TODO(austin): ABI v1 uses /usr/lib/ld.so.1,
+ Linuxdynld: "/lib64/ld64.so.1",
+
+ Freebsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4128
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hlinux: /* ppc64 elf */
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+
+ case objabi.Haix:
+ ld.Xcoffinit(ctxt)
+ }
+}
diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go
new file mode 100644
index 0000000..c18e054
--- /dev/null
+++ b/src/cmd/link/internal/riscv64/asm.go
@@ -0,0 +1,244 @@
+// 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 riscv64
+
+import (
+ "cmd/internal/obj/riscv"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "log"
+ "sort"
+)
+
+// fakeLabelName matches the RISCV_FAKE_LABEL_NAME from binutils.
+const fakeLabelName = ".L0 "
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+}
+
+func genSymsLate(ctxt *ld.Link, ldr *loader.Loader) {
+ if ctxt.LinkMode != ld.LinkExternal {
+ return
+ }
+
+ // Generate a local text symbol for each relocation target, as the
+ // R_RISCV_PCREL_LO12_* relocations generated by elfreloc1 need it.
+ if ctxt.Textp == nil {
+ log.Fatal("genSymsLate called before Textp has been assigned")
+ }
+ var hi20Syms []loader.Sym
+ for _, s := range ctxt.Textp {
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Type() != objabi.R_RISCV_PCREL_ITYPE && r.Type() != objabi.R_RISCV_PCREL_STYPE &&
+ r.Type() != objabi.R_RISCV_TLS_IE_ITYPE && r.Type() != objabi.R_RISCV_TLS_IE_STYPE {
+ continue
+ }
+ if r.Off() == 0 && ldr.SymType(s) == sym.STEXT {
+ // Use the symbol for the function instead of creating
+ // an overlapping symbol.
+ continue
+ }
+
+ // TODO(jsing): Consider generating ELF symbols without needing
+ // loader symbols, in order to reduce memory consumption. This
+ // would require changes to genelfsym so that it called
+ // putelfsym and putelfsyment as appropriate.
+ sb := ldr.MakeSymbolBuilder(fakeLabelName)
+ sb.SetType(sym.STEXT)
+ sb.SetValue(ldr.SymValue(s) + int64(r.Off()))
+ sb.SetLocal(true)
+ sb.SetReachable(true)
+ sb.SetVisibilityHidden(true)
+ sb.SetSect(ldr.SymSect(s))
+ if outer := ldr.OuterSym(s); outer != 0 {
+ ldr.AddInteriorSym(outer, sb.Sym())
+ }
+ hi20Syms = append(hi20Syms, sb.Sym())
+ }
+ }
+ ctxt.Textp = append(ctxt.Textp, hi20Syms...)
+ ldr.SortSyms(ctxt.Textp)
+}
+
+func findHI20Symbol(ctxt *ld.Link, ldr *loader.Loader, val int64) loader.Sym {
+ idx := sort.Search(len(ctxt.Textp), func(i int) bool { return ldr.SymValue(ctxt.Textp[i]) >= val })
+ if idx >= len(ctxt.Textp) {
+ return 0
+ }
+ if s := ctxt.Textp[idx]; ldr.SymValue(s) == val && ldr.SymType(s) == sym.STEXT {
+ return s
+ }
+ return 0
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ switch r.Type {
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ out.Write64(uint64(sectoff))
+ switch r.Size {
+ case 4:
+ out.Write64(uint64(elf.R_RISCV_32) | uint64(elfsym)<<32)
+ case 8:
+ out.Write64(uint64(elf.R_RISCV_64) | uint64(elfsym)<<32)
+ default:
+ ld.Errorf(nil, "unknown size %d for %v relocation", r.Size, r.Type)
+ return false
+ }
+ out.Write64(uint64(r.Xadd))
+
+ case objabi.R_CALLRISCV:
+ // Call relocations are currently handled via R_RISCV_PCREL_ITYPE.
+ // TODO(jsing): Consider generating elf.R_RISCV_CALL instead of a
+ // HI20/LO12_I pair.
+
+ case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
+ // Find the text symbol for the AUIPC instruction targeted
+ // by this relocation.
+ relocs := ldr.Relocs(s)
+ offset := int64(relocs.At(ri).Off())
+ hi20Sym := findHI20Symbol(ctxt, ldr, ldr.SymValue(s)+offset)
+ if hi20Sym == 0 {
+ ld.Errorf(nil, "failed to find text symbol for HI20 relocation at %d (%x)", sectoff, ldr.SymValue(s)+offset)
+ return false
+ }
+ hi20ElfSym := ld.ElfSymForReloc(ctxt, hi20Sym)
+
+ // Emit two relocations - a R_RISCV_PCREL_HI20 relocation and a
+ // corresponding R_RISCV_PCREL_LO12_I or R_RISCV_PCREL_LO12_S relocation.
+ // Note that the LO12 relocation must point to a target that has a valid
+ // HI20 PC-relative relocation text symbol, which in turn points to the
+ // given symbol. For further details see the ELF specification for RISC-V:
+ //
+ // https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#pc-relative-symbol-addresses
+ //
+ var hiRel, loRel elf.R_RISCV
+ switch r.Type {
+ case objabi.R_RISCV_PCREL_ITYPE:
+ hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_I
+ case objabi.R_RISCV_PCREL_STYPE:
+ hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_S
+ case objabi.R_RISCV_TLS_IE_ITYPE:
+ hiRel, loRel = elf.R_RISCV_TLS_GOT_HI20, elf.R_RISCV_PCREL_LO12_I
+ case objabi.R_RISCV_TLS_IE_STYPE:
+ hiRel, loRel = elf.R_RISCV_TLS_GOT_HI20, elf.R_RISCV_PCREL_LO12_S
+ }
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(hiRel) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(loRel) | uint64(hi20ElfSym)<<32)
+ out.Write64(uint64(0))
+
+ default:
+ return false
+ }
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ log.Fatalf("elfsetupplt")
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ log.Fatalf("machoreloc1 not implemented")
+ return false
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ if target.IsExternal() {
+ switch r.Type() {
+ case objabi.R_CALLRISCV:
+ return val, 0, true
+
+ case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
+ return val, 2, true
+ }
+
+ return val, 0, false
+ }
+
+ rs := ldr.ResolveABIAlias(r.Sym())
+
+ switch r.Type() {
+ case objabi.R_CALLRISCV:
+ // Nothing to do.
+ return val, 0, true
+
+ case objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
+ // TLS relocations are not currently handled for internal linking.
+ // For now, TLS is only used when cgo is in use and cgo currently
+ // requires external linking. However, we need to accept these
+ // relocations so that code containing TLS variables will link,
+ // even when they're not being used. For now, replace these
+ // instructions with EBREAK to detect accidental use.
+ const ebreakIns = 0x00100073
+ return ebreakIns<<32 | ebreakIns, 0, true
+
+ case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
+ pc := ldr.SymValue(s) + int64(r.Off())
+ off := ldr.SymValue(rs) + r.Add() - pc
+
+ // Generate AUIPC and second instruction immediates.
+ low, high, err := riscv.Split32BitImmediate(off)
+ if err != nil {
+ ldr.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32-bits: %d", off)
+ }
+
+ auipcImm, err := riscv.EncodeUImmediate(high)
+ if err != nil {
+ ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", ldr.SymName(rs), err)
+ }
+
+ var secondImm, secondImmMask int64
+ switch r.Type() {
+ case objabi.R_RISCV_PCREL_ITYPE:
+ secondImmMask = riscv.ITypeImmMask
+ secondImm, err = riscv.EncodeIImmediate(low)
+ if err != nil {
+ ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
+ }
+ case objabi.R_RISCV_PCREL_STYPE:
+ secondImmMask = riscv.STypeImmMask
+ secondImm, err = riscv.EncodeSImmediate(low)
+ if err != nil {
+ ldr.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
+ }
+ default:
+ panic(fmt.Sprintf("Unknown relocation type: %v", r.Type()))
+ }
+
+ auipc := int64(uint32(val))
+ second := int64(uint32(val >> 32))
+
+ auipc = (auipc &^ riscv.UTypeImmMask) | int64(uint32(auipcImm))
+ second = (second &^ secondImmMask) | int64(uint32(secondImm))
+
+ return second<<32 | auipc, 0, true
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 {
+ log.Fatalf("archrelocvariant")
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+ }
+ return loader.ExtReloc{}, false
+}
diff --git a/src/cmd/link/internal/riscv64/l.go b/src/cmd/link/internal/riscv64/l.go
new file mode 100644
index 0000000..a302657
--- /dev/null
+++ b/src/cmd/link/internal/riscv64/l.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.
+
+package riscv64
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1
+ funcAlign = 8
+
+ dwarfRegLR = 1
+ dwarfRegSP = 2
+)
diff --git a/src/cmd/link/internal/riscv64/obj.go b/src/cmd/link/internal/riscv64/obj.go
new file mode 100644
index 0000000..917324d
--- /dev/null
+++ b/src/cmd/link/internal/riscv64/obj.go
@@ -0,0 +1,60 @@
+// 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 riscv64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchRISCV64
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ GenSymsLate: genSymsLate,
+ Machoreloc1: machoreloc1,
+
+ Linuxdynld: "/lib/ld.so.1",
+
+ Freebsddynld: "XXX",
+ Netbsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ case objabi.Hlinux:
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+ }
+}
diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go
new file mode 100644
index 0000000..78d2cc8
--- /dev/null
+++ b/src/cmd/link/internal/s390x/asm.go
@@ -0,0 +1,455 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 s390x
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+)
+
+// gentext generates assembly to append the local moduledata to the global
+// moduledata linked list at initialization time. This is only done if the runtime
+// is in a different module.
+//
+// <go.link.addmoduledata>:
+// larl %r2, <local.moduledata>
+// jg <runtime.addmoduledata@plt>
+// undef
+//
+// The job of appending the moduledata is delegated to runtime.addmoduledata.
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ // larl %r2, <local.moduledata>
+ initfunc.AddUint8(0xc0)
+ initfunc.AddUint8(0x20)
+ initfunc.AddSymRef(ctxt.Arch, ctxt.Moduledata, 6, objabi.R_PCREL, 4)
+ r1 := initfunc.Relocs()
+ ldr.SetRelocVariant(initfunc.Sym(), r1.Count()-1, sym.RV_390_DBL)
+
+ // jg <runtime.addmoduledata[@plt]>
+ initfunc.AddUint8(0xc0)
+ initfunc.AddUint8(0xf4)
+ initfunc.AddSymRef(ctxt.Arch, addmoduledata, 6, objabi.R_CALL, 4)
+ r2 := initfunc.Relocs()
+ ldr.SetRelocVariant(initfunc.Sym(), r2.Count()-1, sym.RV_390_DBL)
+
+ // undef (for debugging)
+ initfunc.AddUint32(ctxt.Arch, 0)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d", r.Type())
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_12),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT12):
+ ldr.Errorf(s, "s390x 12-bit relocations have not been implemented (relocation type %d)", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_8),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_16),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_390_nn relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_390_PCnn relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+ // sense and should be removed when someone has thought about it properly.
+ if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT16),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT64):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT16DBL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32DBL):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ r.SetSym(syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT64):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ r.SetSym(syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_COPY):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GLOB_DAT):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_JMP_SLOT):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_RELATIVE):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTOFF):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_390_GOTOFF relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_GOTOFF)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPC):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ r.SetSym(syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16DBL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32DBL):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_390_PCnnDBL relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPCDBL):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL)
+ r.SetSym(syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT):
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_390_GLOB_DAT))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL)
+ r.SetSym(syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))+int64(r.Siz()))
+ return true
+ }
+ // Handle references to ELF symbols from our own object files.
+ if targType != sym.SDYNIMPORT {
+ return true
+ }
+
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_TLS_LE:
+ switch siz {
+ default:
+ return false
+ case 4:
+ // WARNING - silently ignored by linker in ELF64
+ out.Write64(uint64(elf.R_390_TLS_LE32) | uint64(elfsym)<<32)
+ case 8:
+ // WARNING - silently ignored by linker in ELF32
+ out.Write64(uint64(elf.R_390_TLS_LE64) | uint64(elfsym)<<32)
+ }
+ case objabi.R_TLS_IE:
+ switch siz {
+ default:
+ return false
+ case 4:
+ out.Write64(uint64(elf.R_390_TLS_IEENT) | uint64(elfsym)<<32)
+ }
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch siz {
+ default:
+ return false
+ case 4:
+ out.Write64(uint64(elf.R_390_32) | uint64(elfsym)<<32)
+ case 8:
+ out.Write64(uint64(elf.R_390_64) | uint64(elfsym)<<32)
+ }
+ case objabi.R_GOTPCREL:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_390_GOTENT) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ case objabi.R_PCREL, objabi.R_PCRELDBL, objabi.R_CALL:
+ elfrel := elf.R_390_NONE
+ rVariant := ldr.RelocVariant(s, ri)
+ isdbl := rVariant&sym.RV_TYPE_MASK == sym.RV_390_DBL
+ // TODO(mundaym): all DBL style relocations should be
+ // signalled using the variant - see issue 14218.
+ switch r.Type {
+ case objabi.R_PCRELDBL, objabi.R_CALL:
+ isdbl = true
+ }
+ if ldr.SymType(r.Xsym) == sym.SDYNIMPORT && (ldr.SymElfType(r.Xsym) == elf.STT_FUNC || r.Type == objabi.R_CALL) {
+ if isdbl {
+ switch siz {
+ case 2:
+ elfrel = elf.R_390_PLT16DBL
+ case 4:
+ elfrel = elf.R_390_PLT32DBL
+ }
+ } else {
+ switch siz {
+ case 4:
+ elfrel = elf.R_390_PLT32
+ case 8:
+ elfrel = elf.R_390_PLT64
+ }
+ }
+ } else {
+ if isdbl {
+ switch siz {
+ case 2:
+ elfrel = elf.R_390_PC16DBL
+ case 4:
+ elfrel = elf.R_390_PC32DBL
+ }
+ } else {
+ switch siz {
+ case 2:
+ elfrel = elf.R_390_PC16
+ case 4:
+ elfrel = elf.R_390_PC32
+ case 8:
+ elfrel = elf.R_390_PC64
+ }
+ }
+ }
+ if elfrel == elf.R_390_NONE {
+ return false // unsupported size/dbl combination
+ }
+ out.Write64(uint64(elfrel) | uint64(elfsym)<<32)
+ }
+
+ out.Write64(uint64(r.Xadd))
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // stg %r1,56(%r15)
+ plt.AddUint8(0xe3)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0xf0)
+ plt.AddUint8(0x38)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x24)
+ // larl %r1,_GLOBAL_OFFSET_TABLE_
+ plt.AddUint8(0xc0)
+ plt.AddUint8(0x10)
+ plt.AddSymRef(ctxt.Arch, got.Sym(), 6, objabi.R_PCRELDBL, 4)
+ // mvc 48(8,%r15),8(%r1)
+ plt.AddUint8(0xd2)
+ plt.AddUint8(0x07)
+ plt.AddUint8(0xf0)
+ plt.AddUint8(0x30)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x08)
+ // lg %r1,16(%r1)
+ plt.AddUint8(0xe3)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x04)
+ // br %r1
+ plt.AddUint8(0x07)
+ plt.AddUint8(0xf1)
+ // nopr %r0
+ plt.AddUint8(0x07)
+ plt.AddUint8(0x00)
+ // nopr %r0
+ plt.AddUint8(0x07)
+ plt.AddUint8(0x00)
+ // nopr %r0
+ plt.AddUint8(0x07)
+ plt.AddUint8(0x00)
+
+ // assume got->size == 0 too
+ got.AddAddrPlus(ctxt.Arch, dynamic, 0)
+
+ got.AddUint64(ctxt.Arch, 0)
+ got.AddUint64(ctxt.Arch, 0)
+ }
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ return val, 0, false
+}
+
+func archrelocvariant(target *ld.Target, ldr *loader.Loader, r loader.Reloc, rv sym.RelocVariant, s loader.Sym, t int64) int64 {
+ switch rv & sym.RV_TYPE_MASK {
+ default:
+ ldr.Errorf(s, "unexpected relocation variant %d", rv)
+ return t
+
+ case sym.RV_NONE:
+ return t
+
+ case sym.RV_390_DBL:
+ if t&1 != 0 {
+ ldr.Errorf(s, "%s+%v is not 2-byte aligned", ldr.SymName(r.Sym()), ldr.SymValue(r.Sym()))
+ }
+ return t >> 1
+ }
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ got := ldr.MakeSymbolUpdater(syms.GOT)
+ rela := ldr.MakeSymbolUpdater(syms.RelaPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+ // larl %r1,_GLOBAL_OFFSET_TABLE_+index
+
+ plt.AddUint8(0xc0)
+ plt.AddUint8(0x10)
+ plt.AddPCRelPlus(target.Arch, got.Sym(), got.Size()+6)
+ pltrelocs := plt.Relocs()
+ ldr.SetRelocVariant(plt.Sym(), pltrelocs.Count()-1, sym.RV_390_DBL)
+
+ // add to got: pointer to current pos in plt
+ got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()+8) // weird but correct
+ // lg %r1,0(%r1)
+ plt.AddUint8(0xe3)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x04)
+ // br %r1
+ plt.AddUint8(0x07)
+ plt.AddUint8(0xf1)
+ // basr %r1,%r0
+ plt.AddUint8(0x0d)
+ plt.AddUint8(0x10)
+ // lgf %r1,12(%r1)
+ plt.AddUint8(0xe3)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x0c)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x14)
+ // jg .plt
+ plt.AddUint8(0xc0)
+ plt.AddUint8(0xf4)
+
+ plt.AddUint32(target.Arch, uint32(-((plt.Size() - 2) >> 1))) // roll-your-own relocation
+ //.plt index
+ plt.AddUint32(target.Arch, uint32(rela.Size())) // rela size before current entry
+
+ // rela
+ rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8)
+
+ sDynid := ldr.SymDynid(s)
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_390_JMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
+
+ ldr.SetPlt(s, int32(plt.Size()-32))
+
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
diff --git a/src/cmd/link/internal/s390x/l.go b/src/cmd/link/internal/s390x/l.go
new file mode 100644
index 0000000..f040587
--- /dev/null
+++ b/src/cmd/link/internal/s390x/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 s390x
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// 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-2008 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-2008 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.
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 2 // min data alignment
+ funcAlign = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 15
+ dwarfRegLR = 14
+)
diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go
new file mode 100644
index 0000000..8acc1d4
--- /dev/null
+++ b/src/cmd/link/internal/s390x/obj.go
@@ -0,0 +1,87 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/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 s390x
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchS390X
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+
+ Linuxdynld: "/lib64/ld64.so.1",
+
+ // not relevant for s390x
+ Freebsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hlinux: // s390x ELF
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ }
+}
diff --git a/src/cmd/link/internal/sym/compilation_unit.go b/src/cmd/link/internal/sym/compilation_unit.go
new file mode 100644
index 0000000..5d7206d
--- /dev/null
+++ b/src/cmd/link/internal/sym/compilation_unit.go
@@ -0,0 +1,35 @@
+// 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 sym
+
+import "cmd/internal/dwarf"
+
+// LoaderSym holds a loader.Sym value. We can't refer to this
+// type from the sym package since loader imports sym.
+type LoaderSym int
+
+// A CompilationUnit represents a set of source files that are compiled
+// together. Since all Go sources in a Go package are compiled together,
+// there's one CompilationUnit per package that represents all Go sources in
+// that package, plus one for each assembly file.
+//
+// Equivalently, there's one CompilationUnit per object file in each Library
+// loaded by the linker.
+//
+// These are used for both DWARF and pclntab generation.
+type CompilationUnit struct {
+ Pkg string // The package name, eg ("fmt", or "runtime")
+ Lib *Library // Our library
+ PclnIndex int // Index of this CU in pclntab
+ PCs []dwarf.Range // PC ranges, relative to Textp[0]
+ DWInfo *dwarf.DWDie // CU root DIE
+ FileTable []string // The file table used in this compilation unit.
+
+ Consts LoaderSym // Package constants DIEs
+ FuncDIEs []LoaderSym // Function DIE subtrees
+ AbsFnDIEs []LoaderSym // Abstract function DIE subtrees
+ RangeSyms []LoaderSym // Symbols for debug_range
+ Textp []LoaderSym // Text symbols in this CU
+}
diff --git a/src/cmd/link/internal/sym/library.go b/src/cmd/link/internal/sym/library.go
new file mode 100644
index 0000000..876b5ff
--- /dev/null
+++ b/src/cmd/link/internal/sym/library.go
@@ -0,0 +1,27 @@
+// 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 sym
+
+import "cmd/internal/goobj"
+
+type Library struct {
+ Objref string
+ Srcref string
+ File string
+ Pkg string
+ Shlib string
+ Fingerprint goobj.FingerprintType
+ Autolib []goobj.ImportedPkg
+ Imports []*Library
+ Main bool
+ Units []*CompilationUnit
+
+ Textp []LoaderSym // text syms defined in this library
+ DupTextSyms []LoaderSym // dupok text syms defined in this library
+}
+
+func (l Library) String() string {
+ return l.Pkg
+}
diff --git a/src/cmd/link/internal/sym/reloc.go b/src/cmd/link/internal/sym/reloc.go
new file mode 100644
index 0000000..a543233
--- /dev/null
+++ b/src/cmd/link/internal/sym/reloc.go
@@ -0,0 +1,74 @@
+// 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 sym
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "debug/elf"
+)
+
+// RelocVariant is a linker-internal variation on a relocation.
+type RelocVariant uint8
+
+const (
+ RV_NONE RelocVariant = iota
+ RV_POWER_LO
+ RV_POWER_HI
+ RV_POWER_HA
+ RV_POWER_DS
+
+ // RV_390_DBL is a s390x-specific relocation variant that indicates that
+ // the value to be placed into the relocatable field should first be
+ // divided by 2.
+ RV_390_DBL
+
+ RV_CHECK_OVERFLOW RelocVariant = 1 << 7
+ RV_TYPE_MASK RelocVariant = RV_CHECK_OVERFLOW - 1
+)
+
+func RelocName(arch *sys.Arch, r objabi.RelocType) string {
+ // We didn't have some relocation types at Go1.4.
+ // Uncomment code when we include those in bootstrap code.
+
+ switch {
+ case r >= objabi.MachoRelocOffset: // Mach-O
+ // nr := (r - objabi.MachoRelocOffset)>>1
+ // switch ctxt.Arch.Family {
+ // case sys.AMD64:
+ // return macho.RelocTypeX86_64(nr).String()
+ // case sys.ARM:
+ // return macho.RelocTypeARM(nr).String()
+ // case sys.ARM64:
+ // return macho.RelocTypeARM64(nr).String()
+ // case sys.I386:
+ // return macho.RelocTypeGeneric(nr).String()
+ // default:
+ // panic("unreachable")
+ // }
+ case r >= objabi.ElfRelocOffset: // ELF
+ nr := r - objabi.ElfRelocOffset
+ switch arch.Family {
+ case sys.AMD64:
+ return elf.R_X86_64(nr).String()
+ case sys.ARM:
+ return elf.R_ARM(nr).String()
+ case sys.ARM64:
+ return elf.R_AARCH64(nr).String()
+ case sys.I386:
+ return elf.R_386(nr).String()
+ case sys.MIPS, sys.MIPS64:
+ return elf.R_MIPS(nr).String()
+ case sys.PPC64:
+ return elf.R_PPC64(nr).String()
+ case sys.S390X:
+ return elf.R_390(nr).String()
+ default:
+ panic("unreachable")
+ }
+ }
+
+ return r.String()
+}
diff --git a/src/cmd/link/internal/sym/segment.go b/src/cmd/link/internal/sym/segment.go
new file mode 100644
index 0000000..97853b9
--- /dev/null
+++ b/src/cmd/link/internal/sym/segment.go
@@ -0,0 +1,66 @@
+// 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 sym
+
+// Terrible but standard terminology.
+// A segment describes a block of file to load into memory.
+// A section further describes the pieces of that block for
+// use in debuggers and such.
+
+type Segment struct {
+ Rwx uint8 // permission as usual unix bits (5 = r-x etc)
+ Vaddr uint64 // virtual address
+ Length uint64 // length in memory
+ Fileoff uint64 // file offset
+ Filelen uint64 // length on disk
+ Sections []*Section
+}
+
+type Section struct {
+ Rwx uint8
+ Extnum int16
+ Align int32
+ Name string
+ Vaddr uint64
+ Length uint64
+ Seg *Segment
+ Elfsect interface{} // an *ld.ElfShdr
+ Reloff uint64
+ Rellen uint64
+ // Relcount is the number of *host* relocations applied to this section
+ // (when external linking).
+ // Incremented atomically on multiple goroutines.
+ // Note: this may differ from number of Go relocations, as one Go relocation
+ // may turn into multiple host relocations.
+ Relcount uint32
+ Sym LoaderSym // symbol for the section, if any
+ Index uint16 // each section has a unique index, used internally
+}
diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go
new file mode 100644
index 0000000..70cf36a
--- /dev/null
+++ b/src/cmd/link/internal/sym/symbol.go
@@ -0,0 +1,35 @@
+// 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 sym
+
+import (
+ "cmd/internal/obj"
+)
+
+const (
+ SymVerABI0 = 0
+ SymVerABIInternal = 1
+ SymVerStatic = 10 // Minimum version used by static (file-local) syms
+)
+
+func ABIToVersion(abi obj.ABI) int {
+ switch abi {
+ case obj.ABI0:
+ return SymVerABI0
+ case obj.ABIInternal:
+ return SymVerABIInternal
+ }
+ return -1
+}
+
+func VersionToABI(v int) (obj.ABI, bool) {
+ switch v {
+ case SymVerABI0:
+ return obj.ABI0, true
+ case SymVerABIInternal:
+ return obj.ABIInternal, true
+ }
+ return ^obj.ABI(0), false
+}
diff --git a/src/cmd/link/internal/sym/symkind.go b/src/cmd/link/internal/sym/symkind.go
new file mode 100644
index 0000000..c176d5e
--- /dev/null
+++ b/src/cmd/link/internal/sym/symkind.go
@@ -0,0 +1,178 @@
+// 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 sym
+
+// A SymKind describes the kind of memory represented by a symbol.
+type SymKind uint8
+
+// Defined SymKind values.
+//
+// TODO(rsc): Give idiomatic Go names.
+//go:generate stringer -type=SymKind
+const (
+ Sxxx SymKind = iota
+ STEXT
+ SELFRXSECT
+ SMACHOPLT
+
+ // Read-only sections.
+ STYPE
+ SSTRING
+ SGOSTRING
+ SGOFUNC
+ SGCBITS
+ SRODATA
+ SFUNCTAB
+
+ SELFROSECT
+
+ // Read-only sections with relocations.
+ //
+ // Types STYPE-SFUNCTAB above are written to the .rodata section by default.
+ // When linking a shared object, some conceptually "read only" types need to
+ // be written to by relocations and putting them in a section called
+ // ".rodata" interacts poorly with the system linkers. The GNU linkers
+ // support this situation by arranging for sections of the name
+ // ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after
+ // relocations have applied, so when the Go linker is creating a shared
+ // object it checks all objects of the above types and bumps any object that
+ // has a relocation to it to the corresponding type below, which are then
+ // written to sections with appropriate magic names.
+ STYPERELRO
+ SSTRINGRELRO
+ SGOSTRINGRELRO
+ SGOFUNCRELRO
+ SGCBITSRELRO
+ SRODATARELRO
+ SFUNCTABRELRO
+
+ // Part of .data.rel.ro if it exists, otherwise part of .rodata.
+ STYPELINK
+ SITABLINK
+ SSYMTAB
+ SPCLNTAB
+
+ // Writable sections.
+ SFirstWritable
+ SBUILDINFO
+ SELFSECT
+ SMACHO
+ SMACHOGOT
+ SWINDOWS
+ SELFGOT
+ SNOPTRDATA
+ SINITARR
+ SDATA
+ SXCOFFTOC
+ SBSS
+ SNOPTRBSS
+ SLIBFUZZER_EXTRA_COUNTER
+ STLSBSS
+ SXREF
+ SMACHOSYMSTR
+ SMACHOSYMTAB
+ SMACHOINDIRECTPLT
+ SMACHOINDIRECTGOT
+ SFILEPATH
+ SDYNIMPORT
+ SHOSTOBJ
+ SUNDEFEXT // Undefined symbol for resolution by external linker
+
+ // Sections for debugging information
+ SDWARFSECT
+ // DWARF symbol types
+ SDWARFCUINFO
+ SDWARFCONST
+ SDWARFFCN
+ SDWARFABSFCN
+ SDWARFTYPE
+ SDWARFVAR
+ SDWARFRANGE
+ SDWARFLOC
+ SDWARFLINES
+
+ // ABI aliases (these never appear in the output)
+ SABIALIAS
+)
+
+// AbiSymKindToSymKind maps values read from object files (which are
+// of type cmd/internal/objabi.SymKind) to values of type SymKind.
+var AbiSymKindToSymKind = [...]SymKind{
+ Sxxx,
+ STEXT,
+ SRODATA,
+ SNOPTRDATA,
+ SDATA,
+ SBSS,
+ SNOPTRBSS,
+ STLSBSS,
+ SDWARFCUINFO,
+ SDWARFCONST,
+ SDWARFFCN,
+ SDWARFABSFCN,
+ SDWARFTYPE,
+ SDWARFVAR,
+ SDWARFRANGE,
+ SDWARFLOC,
+ SDWARFLINES,
+ SABIALIAS,
+ SLIBFUZZER_EXTRA_COUNTER,
+}
+
+// ReadOnly are the symbol kinds that form read-only sections. In some
+// cases, if they will require relocations, they are transformed into
+// rel-ro sections using relROMap.
+var ReadOnly = []SymKind{
+ STYPE,
+ SSTRING,
+ SGOSTRING,
+ SGOFUNC,
+ SGCBITS,
+ SRODATA,
+ SFUNCTAB,
+}
+
+// RelROMap describes the transformation of read-only symbols to rel-ro
+// symbols.
+var RelROMap = map[SymKind]SymKind{
+ STYPE: STYPERELRO,
+ SSTRING: SSTRINGRELRO,
+ SGOSTRING: SGOSTRINGRELRO,
+ SGOFUNC: SGOFUNCRELRO,
+ SGCBITS: SGCBITSRELRO,
+ SRODATA: SRODATARELRO,
+ SFUNCTAB: SFUNCTABRELRO,
+}
+
+// IsData returns true if the type is a data type.
+func (t SymKind) IsData() bool {
+ return t == SDATA || t == SNOPTRDATA || t == SBSS || t == SNOPTRBSS
+}
diff --git a/src/cmd/link/internal/sym/symkind_string.go b/src/cmd/link/internal/sym/symkind_string.go
new file mode 100644
index 0000000..34cb314
--- /dev/null
+++ b/src/cmd/link/internal/sym/symkind_string.go
@@ -0,0 +1,80 @@
+// Code generated by "stringer -type=SymKind"; DO NOT EDIT.
+
+package sym
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[Sxxx-0]
+ _ = x[STEXT-1]
+ _ = x[SELFRXSECT-2]
+ _ = x[SMACHOPLT-3]
+ _ = x[STYPE-4]
+ _ = x[SSTRING-5]
+ _ = x[SGOSTRING-6]
+ _ = x[SGOFUNC-7]
+ _ = x[SGCBITS-8]
+ _ = x[SRODATA-9]
+ _ = x[SFUNCTAB-10]
+ _ = x[SELFROSECT-11]
+ _ = x[STYPERELRO-12]
+ _ = x[SSTRINGRELRO-13]
+ _ = x[SGOSTRINGRELRO-14]
+ _ = x[SGOFUNCRELRO-15]
+ _ = x[SGCBITSRELRO-16]
+ _ = x[SRODATARELRO-17]
+ _ = x[SFUNCTABRELRO-18]
+ _ = x[STYPELINK-19]
+ _ = x[SITABLINK-20]
+ _ = x[SSYMTAB-21]
+ _ = x[SPCLNTAB-22]
+ _ = x[SFirstWritable-23]
+ _ = x[SBUILDINFO-24]
+ _ = x[SELFSECT-25]
+ _ = x[SMACHO-26]
+ _ = x[SMACHOGOT-27]
+ _ = x[SWINDOWS-28]
+ _ = x[SELFGOT-29]
+ _ = x[SNOPTRDATA-30]
+ _ = x[SINITARR-31]
+ _ = x[SDATA-32]
+ _ = x[SXCOFFTOC-33]
+ _ = x[SBSS-34]
+ _ = x[SNOPTRBSS-35]
+ _ = x[SLIBFUZZER_EXTRA_COUNTER-36]
+ _ = x[STLSBSS-37]
+ _ = x[SXREF-38]
+ _ = x[SMACHOSYMSTR-39]
+ _ = x[SMACHOSYMTAB-40]
+ _ = x[SMACHOINDIRECTPLT-41]
+ _ = x[SMACHOINDIRECTGOT-42]
+ _ = x[SFILEPATH-43]
+ _ = x[SDYNIMPORT-44]
+ _ = x[SHOSTOBJ-45]
+ _ = x[SUNDEFEXT-46]
+ _ = x[SDWARFSECT-47]
+ _ = x[SDWARFCUINFO-48]
+ _ = x[SDWARFCONST-49]
+ _ = x[SDWARFFCN-50]
+ _ = x[SDWARFABSFCN-51]
+ _ = x[SDWARFTYPE-52]
+ _ = x[SDWARFVAR-53]
+ _ = x[SDWARFRANGE-54]
+ _ = x[SDWARFLOC-55]
+ _ = x[SDWARFLINES-56]
+ _ = x[SABIALIAS-57]
+}
+
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSMACHOPLTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_EXTRA_COUNTERSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSABIALIAS"
+
+var _SymKind_index = [...]uint16{0, 4, 9, 19, 28, 33, 40, 49, 56, 63, 70, 78, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 337, 344, 349, 361, 373, 390, 407, 416, 426, 434, 443, 453, 465, 476, 485, 497, 507, 516, 527, 536, 547, 556}
+
+func (i SymKind) String() string {
+ if i >= SymKind(len(_SymKind_index)-1) {
+ return "SymKind(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]]
+}
diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go
new file mode 100644
index 0000000..31851fb
--- /dev/null
+++ b/src/cmd/link/internal/wasm/asm.go
@@ -0,0 +1,605 @@
+// 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 wasm
+
+import (
+ "bytes"
+ "cmd/internal/objabi"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "io"
+ "regexp"
+)
+
+const (
+ I32 = 0x7F
+ I64 = 0x7E
+ F32 = 0x7D
+ F64 = 0x7C
+)
+
+const (
+ sectionCustom = 0
+ sectionType = 1
+ sectionImport = 2
+ sectionFunction = 3
+ sectionTable = 4
+ sectionMemory = 5
+ sectionGlobal = 6
+ sectionExport = 7
+ sectionStart = 8
+ sectionElement = 9
+ sectionCode = 10
+ sectionData = 11
+)
+
+// funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly
+const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+}
+
+type wasmFunc struct {
+ Name string
+ Type uint32
+ Code []byte
+}
+
+type wasmFuncType struct {
+ Params []byte
+ Results []byte
+}
+
+var wasmFuncTypes = map[string]*wasmFuncType{
+ "_rt0_wasm_js": {Params: []byte{}}, //
+ "wasm_export_run": {Params: []byte{I32, I32}}, // argc, argv
+ "wasm_export_resume": {Params: []byte{}}, //
+ "wasm_export_getsp": {Results: []byte{I32}}, // sp
+ "wasm_pc_f_loop": {Params: []byte{}}, //
+ "runtime.wasmMove": {Params: []byte{I32, I32, I32}}, // dst, src, len
+ "runtime.wasmZero": {Params: []byte{I32, I32}}, // ptr, len
+ "runtime.wasmDiv": {Params: []byte{I64, I64}, Results: []byte{I64}}, // x, y -> x/y
+ "runtime.wasmTruncS": {Params: []byte{F64}, Results: []byte{I64}}, // x -> int(x)
+ "runtime.wasmTruncU": {Params: []byte{F64}, Results: []byte{I64}}, // x -> uint(x)
+ "runtime.gcWriteBarrier": {Params: []byte{I64, I64}}, // ptr, val
+ "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1
+ "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1
+ "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0
+ "memchr": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // s, c, len -> index
+}
+
+func assignAddress(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) {
+ // WebAssembly functions do not live in the same address space as the linear memory.
+ // Instead, WebAssembly automatically assigns indices. Imported functions (section "import")
+ // have indices 0 to n. They are followed by native functions (sections "function" and "code")
+ // with indices n+1 and following.
+ //
+ // The following rules describe how wasm handles function indices and addresses:
+ // PC_F = funcValueOffset + WebAssembly function index (not including the imports)
+ // s.Value = PC = PC_F<<16 + PC_B
+ //
+ // The funcValueOffset is necessary to avoid conflicts with expectations
+ // that the Go runtime has about function addresses.
+ // The field "s.Value" corresponds to the concept of PC at runtime.
+ // However, there is no PC register, only PC_F and PC_B. PC_F denotes the function,
+ // PC_B the resume point inside of that function. The entry of the function has PC_B = 0.
+ ldr.SetSymSect(s, sect)
+ ldr.SetSymValue(s, int64(funcValueOffset+va/ld.MINFUNC)<<16) // va starts at zero
+ va += uint64(ld.MINFUNC)
+ return sect, n, va
+}
+
+type wasmDataSect struct {
+ sect *sym.Section
+ data []byte
+}
+
+var dataSects []wasmDataSect
+
+func asmb(ctxt *ld.Link, ldr *loader.Loader) {
+ sections := []*sym.Section{
+ ldr.SymSect(ldr.Lookup("runtime.rodata", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.typelink", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.itablink", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.symtab", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.pclntab", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.noptrdata", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.data", 0)),
+ }
+
+ dataSects = make([]wasmDataSect, len(sections))
+ for i, sect := range sections {
+ data := ld.DatblkBytes(ctxt, int64(sect.Vaddr), int64(sect.Length))
+ dataSects[i] = wasmDataSect{sect, data}
+ }
+}
+
+// asmb writes the final WebAssembly module binary.
+// Spec: https://webassembly.github.io/spec/core/binary/modules.html
+func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
+ types := []*wasmFuncType{
+ // For normal Go functions, the single parameter is PC_B,
+ // the return value is
+ // 0 if the function returned normally or
+ // 1 if the stack needs to be unwound.
+ {Params: []byte{I32}, Results: []byte{I32}},
+ }
+
+ // collect host imports (functions that get imported from the WebAssembly host, usually JavaScript)
+ hostImports := []*wasmFunc{
+ {
+ Name: "debug",
+ Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
+ },
+ }
+ hostImportMap := make(map[loader.Sym]int64)
+ for _, fn := range ctxt.Textp {
+ relocs := ldr.Relocs(fn)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Type() == objabi.R_WASMIMPORT {
+ hostImportMap[r.Sym()] = int64(len(hostImports))
+ hostImports = append(hostImports, &wasmFunc{
+ Name: ldr.SymName(r.Sym()),
+ Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
+ })
+ }
+ }
+ }
+
+ // collect functions with WebAssembly body
+ var buildid []byte
+ fns := make([]*wasmFunc, len(ctxt.Textp))
+ for i, fn := range ctxt.Textp {
+ wfn := new(bytes.Buffer)
+ if ldr.SymName(fn) == "go.buildid" {
+ writeUleb128(wfn, 0) // number of sets of locals
+ writeI32Const(wfn, 0)
+ wfn.WriteByte(0x0b) // end
+ buildid = ldr.Data(fn)
+ } else {
+ // Relocations have variable length, handle them here.
+ relocs := ldr.Relocs(fn)
+ P := ldr.Data(fn)
+ off := int32(0)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Siz() == 0 {
+ continue // skip marker relocations
+ }
+ wfn.Write(P[off:r.Off()])
+ off = r.Off()
+ rs := ldr.ResolveABIAlias(r.Sym())
+ switch r.Type() {
+ case objabi.R_ADDR:
+ writeSleb128(wfn, ldr.SymValue(rs)+r.Add())
+ case objabi.R_CALL:
+ writeSleb128(wfn, int64(len(hostImports))+ldr.SymValue(rs)>>16-funcValueOffset)
+ case objabi.R_WASMIMPORT:
+ writeSleb128(wfn, hostImportMap[rs])
+ default:
+ ldr.Errorf(fn, "bad reloc type %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+ continue
+ }
+ }
+ wfn.Write(P[off:])
+ }
+
+ typ := uint32(0)
+ if sig, ok := wasmFuncTypes[ldr.SymName(fn)]; ok {
+ typ = lookupType(sig, &types)
+ }
+
+ name := nameRegexp.ReplaceAllString(ldr.SymName(fn), "_")
+ fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()}
+ }
+
+ ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic
+ ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version
+
+ // Add any buildid early in the binary:
+ if len(buildid) != 0 {
+ writeBuildID(ctxt, buildid)
+ }
+
+ writeTypeSec(ctxt, types)
+ writeImportSec(ctxt, hostImports)
+ writeFunctionSec(ctxt, fns)
+ writeTableSec(ctxt, fns)
+ writeMemorySec(ctxt, ldr)
+ writeGlobalSec(ctxt)
+ writeExportSec(ctxt, ldr, len(hostImports))
+ writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns)))
+ writeCodeSec(ctxt, fns)
+ writeDataSec(ctxt)
+ writeProducerSec(ctxt)
+ if !*ld.FlagS {
+ writeNameSec(ctxt, len(hostImports), fns)
+ }
+}
+
+func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 {
+ for i, t := range *types {
+ if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) {
+ return uint32(i)
+ }
+ }
+ *types = append(*types, sig)
+ return uint32(len(*types) - 1)
+}
+
+func writeSecHeader(ctxt *ld.Link, id uint8) int64 {
+ ctxt.Out.WriteByte(id)
+ sizeOffset := ctxt.Out.Offset()
+ ctxt.Out.Write(make([]byte, 5)) // placeholder for length
+ return sizeOffset
+}
+
+func writeSecSize(ctxt *ld.Link, sizeOffset int64) {
+ endOffset := ctxt.Out.Offset()
+ ctxt.Out.SeekSet(sizeOffset)
+ writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5)
+ ctxt.Out.SeekSet(endOffset)
+}
+
+func writeBuildID(ctxt *ld.Link, buildid []byte) {
+ sizeOffset := writeSecHeader(ctxt, sectionCustom)
+ writeName(ctxt.Out, "go.buildid")
+ ctxt.Out.Write(buildid)
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeTypeSec writes the section that declares all function types
+// so they can be referenced by index.
+func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) {
+ sizeOffset := writeSecHeader(ctxt, sectionType)
+
+ writeUleb128(ctxt.Out, uint64(len(types)))
+
+ for _, t := range types {
+ ctxt.Out.WriteByte(0x60) // functype
+ writeUleb128(ctxt.Out, uint64(len(t.Params)))
+ for _, v := range t.Params {
+ ctxt.Out.WriteByte(byte(v))
+ }
+ writeUleb128(ctxt.Out, uint64(len(t.Results)))
+ for _, v := range t.Results {
+ ctxt.Out.WriteByte(byte(v))
+ }
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeImportSec writes the section that lists the functions that get
+// imported from the WebAssembly host, usually JavaScript.
+func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionImport)
+
+ writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports
+ for _, fn := range hostImports {
+ writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js
+ writeName(ctxt.Out, fn.Name)
+ ctxt.Out.WriteByte(0x00) // func import
+ writeUleb128(ctxt.Out, uint64(fn.Type))
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeFunctionSec writes the section that declares the types of functions.
+// The bodies of these functions will later be provided in the "code" section.
+func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionFunction)
+
+ writeUleb128(ctxt.Out, uint64(len(fns)))
+ for _, fn := range fns {
+ writeUleb128(ctxt.Out, uint64(fn.Type))
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeTableSec writes the section that declares tables. Currently there is only a single table
+// that is used by the CallIndirect operation to dynamically call any function.
+// The contents of the table get initialized by the "element" section.
+func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionTable)
+
+ numElements := uint64(funcValueOffset + len(fns))
+ writeUleb128(ctxt.Out, 1) // number of tables
+ ctxt.Out.WriteByte(0x70) // type: anyfunc
+ ctxt.Out.WriteByte(0x00) // no max
+ writeUleb128(ctxt.Out, numElements) // min
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used.
+// Linear memory always starts at address zero. More memory can be requested with the GrowMemory instruction.
+func writeMemorySec(ctxt *ld.Link, ldr *loader.Loader) {
+ sizeOffset := writeSecHeader(ctxt, sectionMemory)
+
+ dataSection := ldr.SymSect(ldr.Lookup("runtime.data", 0))
+ dataEnd := dataSection.Vaddr + dataSection.Length
+ var initialSize = dataEnd + 16<<20 // 16MB, enough for runtime init without growing
+
+ const wasmPageSize = 64 << 10 // 64KB
+
+ writeUleb128(ctxt.Out, 1) // number of memories
+ ctxt.Out.WriteByte(0x00) // no maximum memory size
+ writeUleb128(ctxt.Out, initialSize/wasmPageSize) // minimum (initial) memory size
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeGlobalSec writes the section that declares global variables.
+func writeGlobalSec(ctxt *ld.Link) {
+ sizeOffset := writeSecHeader(ctxt, sectionGlobal)
+
+ globalRegs := []byte{
+ I32, // 0: SP
+ I64, // 1: CTXT
+ I64, // 2: g
+ I64, // 3: RET0
+ I64, // 4: RET1
+ I64, // 5: RET2
+ I64, // 6: RET3
+ I32, // 7: PAUSE
+ }
+
+ writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals
+
+ for _, typ := range globalRegs {
+ ctxt.Out.WriteByte(typ)
+ ctxt.Out.WriteByte(0x01) // var
+ switch typ {
+ case I32:
+ writeI32Const(ctxt.Out, 0)
+ case I64:
+ writeI64Const(ctxt.Out, 0)
+ }
+ ctxt.Out.WriteByte(0x0b) // end
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeExportSec writes the section that declares exports.
+// Exports can be accessed by the WebAssembly host, usually JavaScript.
+// The wasm_export_* functions and the linear memory get exported.
+func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) {
+ sizeOffset := writeSecHeader(ctxt, sectionExport)
+
+ writeUleb128(ctxt.Out, 4) // number of exports
+
+ for _, name := range []string{"run", "resume", "getsp"} {
+ s := ldr.Lookup("wasm_export_"+name, 0)
+ idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
+ writeName(ctxt.Out, name) // inst.exports.run/resume/getsp in wasm_exec.js
+ ctxt.Out.WriteByte(0x00) // func export
+ writeUleb128(ctxt.Out, uint64(idx)) // funcidx
+ }
+
+ writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
+ ctxt.Out.WriteByte(0x02) // mem export
+ writeUleb128(ctxt.Out, 0) // memidx
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeElementSec writes the section that initializes the tables declared by the "table" section.
+// The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value)
+// maps linearly to the function index (numImports + PC_F).
+func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
+ sizeOffset := writeSecHeader(ctxt, sectionElement)
+
+ writeUleb128(ctxt.Out, 1) // number of element segments
+
+ writeUleb128(ctxt.Out, 0) // tableidx
+ writeI32Const(ctxt.Out, funcValueOffset)
+ ctxt.Out.WriteByte(0x0b) // end
+
+ writeUleb128(ctxt.Out, numFns) // number of entries
+ for i := uint64(0); i < numFns; i++ {
+ writeUleb128(ctxt.Out, numImports+i)
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeElementSec writes the section that provides the function bodies for the functions
+// declared by the "func" section.
+func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionCode)
+
+ writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries
+ for _, fn := range fns {
+ writeUleb128(ctxt.Out, uint64(len(fn.Code)))
+ ctxt.Out.Write(fn.Code)
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeDataSec writes the section that provides data that will be used to initialize the linear memory.
+func writeDataSec(ctxt *ld.Link) {
+ sizeOffset := writeSecHeader(ctxt, sectionData)
+
+ type dataSegment struct {
+ offset int32
+ data []byte
+ }
+
+ // Omit blocks of zeroes and instead emit data segments with offsets skipping the zeroes.
+ // This reduces the size of the WebAssembly binary. We use 8 bytes as an estimate for the
+ // overhead of adding a new segment (same as wasm-opt's memory-packing optimization uses).
+ const segmentOverhead = 8
+
+ // Generate at most this many segments. A higher number of segments gets rejected by some WebAssembly runtimes.
+ const maxNumSegments = 100000
+
+ var segments []*dataSegment
+ for secIndex, ds := range dataSects {
+ data := ds.data
+ offset := int32(ds.sect.Vaddr)
+
+ // skip leading zeroes
+ for len(data) > 0 && data[0] == 0 {
+ data = data[1:]
+ offset++
+ }
+
+ for len(data) > 0 {
+ dataLen := int32(len(data))
+ var segmentEnd, zeroEnd int32
+ if len(segments)+(len(dataSects)-secIndex) == maxNumSegments {
+ segmentEnd = dataLen
+ zeroEnd = dataLen
+ } else {
+ for {
+ // look for beginning of zeroes
+ for segmentEnd < dataLen && data[segmentEnd] != 0 {
+ segmentEnd++
+ }
+ // look for end of zeroes
+ zeroEnd = segmentEnd
+ for zeroEnd < dataLen && data[zeroEnd] == 0 {
+ zeroEnd++
+ }
+ // emit segment if omitting zeroes reduces the output size
+ if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen {
+ break
+ }
+ segmentEnd = zeroEnd
+ }
+ }
+
+ segments = append(segments, &dataSegment{
+ offset: offset,
+ data: data[:segmentEnd],
+ })
+ data = data[zeroEnd:]
+ offset += zeroEnd
+ }
+ }
+
+ writeUleb128(ctxt.Out, uint64(len(segments))) // number of data entries
+ for _, seg := range segments {
+ writeUleb128(ctxt.Out, 0) // memidx
+ writeI32Const(ctxt.Out, seg.offset)
+ ctxt.Out.WriteByte(0x0b) // end
+ writeUleb128(ctxt.Out, uint64(len(seg.data)))
+ ctxt.Out.Write(seg.data)
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeProducerSec writes an optional section that reports the source language and compiler version.
+func writeProducerSec(ctxt *ld.Link) {
+ sizeOffset := writeSecHeader(ctxt, sectionCustom)
+ writeName(ctxt.Out, "producers")
+
+ writeUleb128(ctxt.Out, 2) // number of fields
+
+ writeName(ctxt.Out, "language") // field name
+ writeUleb128(ctxt.Out, 1) // number of values
+ writeName(ctxt.Out, "Go") // value: name
+ writeName(ctxt.Out, objabi.Version) // value: version
+
+ writeName(ctxt.Out, "processed-by") // field name
+ writeUleb128(ctxt.Out, 1) // number of values
+ writeName(ctxt.Out, "Go cmd/compile") // value: name
+ writeName(ctxt.Out, objabi.Version) // value: version
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+var nameRegexp = regexp.MustCompile(`[^\w\.]`)
+
+// writeNameSec writes an optional section that assigns names to the functions declared by the "func" section.
+// The names are only used by WebAssembly stack traces, debuggers and decompilers.
+// TODO(neelance): add symbol table of DATA symbols
+func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionCustom)
+ writeName(ctxt.Out, "name")
+
+ sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names
+ writeUleb128(ctxt.Out, uint64(len(fns)))
+ for i, fn := range fns {
+ writeUleb128(ctxt.Out, uint64(firstFnIndex+i))
+ writeName(ctxt.Out, fn.Name)
+ }
+ writeSecSize(ctxt, sizeOffset2)
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+type nameWriter interface {
+ io.ByteWriter
+ io.Writer
+}
+
+func writeI32Const(w io.ByteWriter, v int32) {
+ w.WriteByte(0x41) // i32.const
+ writeSleb128(w, int64(v))
+}
+
+func writeI64Const(w io.ByteWriter, v int64) {
+ w.WriteByte(0x42) // i64.const
+ writeSleb128(w, v)
+}
+
+func writeName(w nameWriter, name string) {
+ writeUleb128(w, uint64(len(name)))
+ w.Write([]byte(name))
+}
+
+func writeUleb128(w io.ByteWriter, v uint64) {
+ if v < 128 {
+ w.WriteByte(uint8(v))
+ return
+ }
+ more := true
+ for more {
+ c := uint8(v & 0x7f)
+ v >>= 7
+ more = v != 0
+ if more {
+ c |= 0x80
+ }
+ w.WriteByte(c)
+ }
+}
+
+func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) {
+ for i := 0; i < length; i++ {
+ c := uint8(v & 0x7f)
+ v >>= 7
+ if i < length-1 {
+ c |= 0x80
+ }
+ w.WriteByte(c)
+ }
+ if v != 0 {
+ panic("writeUleb128FixedLength: length too small")
+ }
+}
+
+func writeSleb128(w io.ByteWriter, v int64) {
+ more := true
+ for more {
+ c := uint8(v & 0x7f)
+ s := uint8(v & 0x40)
+ v >>= 7
+ more = !((v == 0 && s == 0) || (v == -1 && s != 0))
+ if more {
+ c |= 0x80
+ }
+ w.WriteByte(c)
+ }
+}
diff --git a/src/cmd/link/internal/wasm/obj.go b/src/cmd/link/internal/wasm/obj.go
new file mode 100644
index 0000000..f8090a3
--- /dev/null
+++ b/src/cmd/link/internal/wasm/obj.go
@@ -0,0 +1,35 @@
+// 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 wasm
+
+import (
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ theArch := ld.Arch{
+ Funcalign: 16,
+ Maxalign: 32,
+ Minalign: 1,
+
+ Archinit: archinit,
+ AssignAddress: assignAddress,
+ Asmb: asmb,
+ Asmb2: asmb2,
+ Gentext: gentext,
+ }
+
+ return sys.ArchWasm, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0
+ }
+}
diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go
new file mode 100644
index 0000000..af0ce11
--- /dev/null
+++ b/src/cmd/link/internal/x86/asm.go
@@ -0,0 +1,492 @@
+// 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 x86
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "log"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ if ctxt.DynlinkingGo() {
+ // We need get_pc_thunk.
+ } else {
+ switch ctxt.BuildMode {
+ case ld.BuildModeCArchive:
+ if !ctxt.IsELF {
+ return
+ }
+ case ld.BuildModePIE, ld.BuildModeCShared, ld.BuildModePlugin:
+ // We need get_pc_thunk.
+ default:
+ return
+ }
+ }
+
+ // Generate little thunks that load the PC of the next instruction into a register.
+ thunks := make([]loader.Sym, 0, 7+len(ctxt.Textp))
+ for _, r := range [...]struct {
+ name string
+ num uint8
+ }{
+ {"ax", 0},
+ {"cx", 1},
+ {"dx", 2},
+ {"bx", 3},
+ // sp
+ {"bp", 5},
+ {"si", 6},
+ {"di", 7},
+ } {
+ thunkfunc := ldr.CreateSymForUpdate("__x86.get_pc_thunk."+r.name, 0)
+ thunkfunc.SetType(sym.STEXT)
+ ldr.SetAttrLocal(thunkfunc.Sym(), true)
+ o := func(op ...uint8) {
+ for _, op1 := range op {
+ thunkfunc.AddUint8(op1)
+ }
+ }
+ // 8b 04 24 mov (%esp),%eax
+ // Destination register is in bits 3-5 of the middle byte, so add that in.
+ o(0x8b, 0x04+r.num<<3, 0x24)
+ // c3 ret
+ o(0xc3)
+
+ thunks = append(thunks, thunkfunc.Sym())
+ }
+ ctxt.Textp = append(thunks, ctxt.Textp...) // keep Textp in dependency order
+
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op ...uint8) {
+ for _, op1 := range op {
+ initfunc.AddUint8(op1)
+ }
+ }
+
+ // go.link.addmoduledata:
+ // 53 push %ebx
+ // e8 00 00 00 00 call __x86.get_pc_thunk.cx + R_CALL __x86.get_pc_thunk.cx
+ // 8d 81 00 00 00 00 lea 0x0(%ecx), %eax + R_PCREL ctxt.Moduledata
+ // 8d 99 00 00 00 00 lea 0x0(%ecx), %ebx + R_GOTPC _GLOBAL_OFFSET_TABLE_
+ // e8 00 00 00 00 call runtime.addmoduledata@plt + R_CALL runtime.addmoduledata
+ // 5b pop %ebx
+ // c3 ret
+
+ o(0x53)
+
+ o(0xe8)
+ initfunc.AddSymRef(ctxt.Arch, ldr.Lookup("__x86.get_pc_thunk.cx", 0), 0, objabi.R_CALL, 4)
+
+ o(0x8d, 0x81)
+ initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 6)
+
+ o(0x8d, 0x99)
+ gotsym := ldr.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0)
+ initfunc.AddSymRef(ctxt.Arch, gotsym, 12, objabi.R_PCREL, 4)
+ o(0xe8)
+ initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALL, 4)
+
+ o(0x5b)
+
+ o(0xc3)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PC32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+ // sense and should be removed when someone has thought about it properly.
+ if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PLT32):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32X):
+ su := ldr.MakeSymbolUpdater(s)
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ sData := ldr.Data(s)
+
+ if r.Off() >= 2 && sData[r.Off()-2] == 0x8b {
+ su.MakeWritable()
+
+ // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT.
+ writeableData := su.Data()
+ writeableData[r.Off()-2] = 0x8d
+ su.SetRelocType(rIdx, objabi.R_GOTOFF)
+ return true
+ }
+
+ if r.Off() >= 2 && sData[r.Off()-2] == 0xff && sData[r.Off()-1] == 0xb3 {
+ su.MakeWritable()
+ // turn PUSHL of GOT entry into PUSHL of symbol itself.
+ // use unnecessary SS prefix to keep instruction same length.
+ writeableData := su.Data()
+ writeableData[r.Off()-2] = 0x36
+ writeableData[r.Off()-1] = 0x68
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ return true
+ }
+
+ ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT))
+ su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
+ su.SetRelocSym(rIdx, 0)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTOFF):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_GOTOFF)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0:
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1:
+ su := ldr.MakeSymbolUpdater(s)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+ }
+
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_FAKE_GOTPCREL:
+ su := ldr.MakeSymbolUpdater(s)
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ // turn MOVL of GOT entry into LEAL of symbol itself
+ sData := ldr.Data(s)
+ if r.Off() < 2 || sData[r.Off()-2] != 0x8b {
+ ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+
+ su.MakeWritable()
+ writeableData := su.Data()
+ writeableData[r.Off()-2] = 0x8d
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+ }
+
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT))
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+ }
+
+ // Handle references to ELF symbols from our own object files.
+ if targType != sym.SDYNIMPORT {
+ return true
+ }
+
+ // Reread the reloc to incorporate any changes in type above.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_CALL,
+ objabi.R_PCREL:
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ return true
+
+ case objabi.R_ADDR:
+ if ldr.SymType(s) != sym.SDATA {
+ break
+ }
+ if target.IsElf() {
+ ld.Adddynsym(ldr, target, syms, targ)
+ rel := ldr.MakeSymbolUpdater(syms.Rel)
+ rel.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(targ)), uint32(elf.R_386_32)))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
+ su.SetRelocSym(rIdx, 0)
+ return true
+ }
+ }
+
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write32(uint32(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_32) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_GOTPCREL:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_GOTPC))
+ if ldr.SymName(r.Xsym) != "_GLOBAL_OFFSET_TABLE_" {
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(elf.R_386_GOT32) | uint32(elfsym)<<8)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_CALL:
+ if siz == 4 {
+ if ldr.SymType(r.Xsym) == sym.SDYNIMPORT {
+ out.Write32(uint32(elf.R_386_PLT32) | uint32(elfsym)<<8)
+ } else {
+ out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_PCREL:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_TLS_LE:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_TLS_LE) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_TLS_IE:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_GOTPC))
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(elf.R_386_TLS_GOTIE) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ }
+
+ return true
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ var v uint32
+
+ rs := r.Xsym
+ rt := r.Type
+
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(ldr.SymDynid(rs)))
+
+ switch rt {
+ default:
+ return false
+
+ case objabi.R_DWARFSECREF:
+ v = ld.IMAGE_REL_I386_SECREL
+
+ case objabi.R_ADDR:
+ v = ld.IMAGE_REL_I386_DIR32
+
+ case objabi.R_CALL,
+ objabi.R_PCREL:
+ v = ld.IMAGE_REL_I386_REL32
+ }
+
+ out.Write16(uint16(v))
+
+ return true
+}
+
+func archreloc(*ld.Target, *loader.Loader, *ld.ArchSyms, loader.Reloc, loader.Sym, int64) (int64, int, bool) {
+ return -1, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 {
+ log.Fatalf("unexpected relocation variant")
+ return -1
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // pushl got+4
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x35)
+ plt.AddAddrPlus(ctxt.Arch, got.Sym(), 4)
+
+ // jmp *got+8
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x25)
+ plt.AddAddrPlus(ctxt.Arch, got.Sym(), 8)
+
+ // zero pad
+ plt.AddUint32(ctxt.Arch, 0)
+
+ // assume got->size == 0 too
+ got.AddAddrPlus(ctxt.Arch, dynamic, 0)
+
+ got.AddUint32(ctxt.Arch, 0)
+ got.AddUint32(ctxt.Arch, 0)
+ }
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ got := ldr.MakeSymbolUpdater(syms.GOTPLT)
+ rel := ldr.MakeSymbolUpdater(syms.RelPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // jmpq *got+size
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x25)
+ plt.AddAddrPlus(target.Arch, got.Sym(), got.Size())
+
+ // add to got: pointer to current pos in plt
+ got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size())
+
+ // pushl $x
+ plt.AddUint8(0x68)
+
+ plt.AddUint32(target.Arch, uint32(rel.Size()))
+
+ // jmp .plt
+ plt.AddUint8(0xe9)
+
+ plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4)))
+
+ // rel
+ rel.AddAddrPlus(target.Arch, got.Sym(), got.Size()-4)
+
+ sDynid := ldr.SymDynid(s)
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(sDynid), uint32(elf.R_386_JMP_SLOT)))
+
+ ldr.SetPlt(s, int32(plt.Size()-16))
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
diff --git a/src/cmd/link/internal/x86/l.go b/src/cmd/link/internal/x86/l.go
new file mode 100644
index 0000000..5875d45
--- /dev/null
+++ b/src/cmd/link/internal/x86/l.go
@@ -0,0 +1,43 @@
+// Inferno utils/8l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/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 x86
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 4
+ dwarfRegLR = 8
+)
diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go
new file mode 100644
index 0000000..a19437d
--- /dev/null
+++ b/src/cmd/link/internal/x86/obj.go
@@ -0,0 +1,116 @@
+// Inferno utils/8l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/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 x86
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.Arch386
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ // 0xCC is INT $3 - breakpoint instruction
+ CodePad: []byte{0xCC},
+
+ Plan9Magic: uint32(4*11*11 + 7),
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 8,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+ PEreloc1: pereloc1,
+
+ Linuxdynld: "/lib/ld-linux.so.2",
+ Freebsddynld: "/usr/libexec/ld-elf.so.1",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "/usr/libexec/ld.elf_so",
+ Solarisdynld: "/lib/ld.so.1",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hdarwin: /* apple MACH */
+ ld.HEADR = ld.INITIAL_MACHO_HEADR
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hlinux, /* elf32 executable */
+ objabi.Hfreebsd,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd:
+ ld.Elfinit(ctxt)
+
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x08048000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hwindows: /* PE executable */
+ // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+ return
+ }
+}
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
new file mode 100644
index 0000000..08ddd00
--- /dev/null
+++ b/src/cmd/link/link_test.go
@@ -0,0 +1,1001 @@
+// 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 main
+
+import (
+ "bufio"
+ "bytes"
+ "cmd/internal/sys"
+ "debug/macho"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+var AuthorPaidByTheColumnInch struct {
+ fog int `text:"London. Michaelmas term lately over, and the Lord Chancellor sitting in Lincoln’s Inn Hall. Implacable November weather. As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill. Smoke lowering down from chimney-pots, making a soft black drizzle, with flakes of soot in it as big as full-grown snowflakes—gone into mourning, one might imagine, for the death of the sun. Dogs, undistinguishable in mire. Horses, scarcely better; splashed to their very blinkers. Foot passengers, jostling one another’s umbrellas in a general infection of ill temper, and losing their foot-hold at street-corners, where tens of thousands of other foot passengers have been slipping and sliding since the day broke (if this day ever broke), adding new deposits to the crust upon crust of mud, sticking at those points tenaciously to the pavement, and accumulating at compound interest. Fog everywhere. Fog up the river, where it flows among green aits and meadows; fog down the river, where it rolls defiled among the tiers of shipping and the waterside pollutions of a great (and dirty) city. Fog on the Essex marshes, fog on the Kentish heights. Fog creeping into the cabooses of collier-brigs; fog lying out on the yards and hovering in the rigging of great ships; fog drooping on the gunwales of barges and small boats. Fog in the eyes and throats of ancient Greenwich pensioners, wheezing by the firesides of their wards; fog in the stem and bowl of the afternoon pipe of the wrathful skipper, down in his close cabin; fog cruelly pinching the toes and fingers of his shivering little ‘prentice boy on deck. Chance people on the bridges peeping over the parapets into a nether sky of fog, with fog all round them, as if they were up in a balloon and hanging in the misty clouds. Gas looming through the fog in divers places in the streets, much as the sun may, from the spongey fields, be seen to loom by husbandman and ploughboy. Most of the shops lighted two hours before their time—as the gas seems to know, for it has a haggard and unwilling look. The raw afternoon is rawest, and the dense fog is densest, and the muddy streets are muddiest near that leaden-headed old obstruction, appropriate ornament for the threshold of a leaden-headed old corporation, Temple Bar. And hard by Temple Bar, in Lincoln’s Inn Hall, at the very heart of the fog, sits the Lord High Chancellor in his High Court of Chancery."`
+
+ wind int `text:"It was grand to see how the wind awoke, and bent the trees, and drove the rain before it like a cloud of smoke; and to hear the solemn thunder, and to see the lightning; and while thinking with awe of the tremendous powers by which our little lives are encompassed, to consider how beneficent they are, and how upon the smallest flower and leaf there was already a freshness poured from all this seeming rage, which seemed to make creation new again."`
+
+ jarndyce int `text:"Jarndyce and Jarndyce drones on. This scarecrow of a suit has, over the course of time, become so complicated, that no man alive knows what it means. The parties to it understand it least; but it has been observed that no two Chancery lawyers can talk about it for five minutes, without coming to a total disagreement as to all the premises. Innumerable children have been born into the cause; innumerable young people have married into it; innumerable old people have died out of it. Scores of persons have deliriously found themselves made parties in Jarndyce and Jarndyce, without knowing how or why; whole families have inherited legendary hatreds with the suit. The little plaintiff or defendant, who was promised a new rocking-horse when Jarndyce and Jarndyce should be settled, has grown up, possessed himself of a real horse, and trotted away into the other world. Fair wards of court have faded into mothers and grandmothers; a long procession of Chancellors has come in and gone out; the legion of bills in the suit have been transformed into mere bills of mortality; there are not three Jarndyces left upon the earth perhaps, since old Tom Jarndyce in despair blew his brains out at a coffee-house in Chancery Lane; but Jarndyce and Jarndyce still drags its dreary length before the Court, perennially hopeless."`
+
+ principle int `text:"The one great principle of the English law is, to make business for itself. There is no other principle distinctly, certainly, and consistently maintained through all its narrow turnings. Viewed by this light it becomes a coherent scheme, and not the monstrous maze the laity are apt to think it. Let them but once clearly perceive that its grand principle is to make business for itself at their expense, and surely they will cease to grumble."`
+}
+
+func TestLargeSymName(t *testing.T) {
+ // The compiler generates a symbol name using the string form of the
+ // type. This tests that the linker can read symbol names larger than
+ // the bufio buffer. Issue #15104.
+ _ = AuthorPaidByTheColumnInch
+}
+
+func TestIssue21703(t *testing.T) {
+ t.Parallel()
+
+ testenv.MustHaveGoBuild(t)
+
+ const source = `
+package main
+const X = "\n!\n"
+func main() {}
+`
+
+ tmpdir, err := ioutil.TempDir("", "issue21703")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v\n", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ err = ioutil.WriteFile(filepath.Join(tmpdir, "main.go"), []byte(source), 0666)
+ if err != nil {
+ t.Fatalf("failed to write main.go: %v\n", err)
+ }
+
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "main.go")
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
+ }
+
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "main.o")
+ cmd.Dir = tmpdir
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
+ }
+}
+
+// TestIssue28429 ensures that the linker does not attempt to link
+// sections not named *.o. Such sections may be used by a build system
+// to, for example, save facts produced by a modular static analysis
+// such as golang.org/x/tools/go/analysis.
+func TestIssue28429(t *testing.T) {
+ t.Parallel()
+
+ testenv.MustHaveGoBuild(t)
+
+ tmpdir, err := ioutil.TempDir("", "issue28429-")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ write := func(name, content string) {
+ err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ runGo := func(args ...string) {
+ cmd := exec.Command(testenv.GoToolPath(t), args...)
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("'go %s' failed: %v, output: %s",
+ strings.Join(args, " "), err, out)
+ }
+ }
+
+ // Compile a main package.
+ write("main.go", "package main; func main() {}")
+ runGo("tool", "compile", "-p", "main", "main.go")
+ runGo("tool", "pack", "c", "main.a", "main.o")
+
+ // Add an extra section with a short, non-.o name.
+ // This simulates an alternative build system.
+ write(".facts", "this is not an object file")
+ runGo("tool", "pack", "r", "main.a", ".facts")
+
+ // Verify that the linker does not attempt
+ // to compile the extra section.
+ runGo("tool", "link", "main.a")
+}
+
+func TestUnresolved(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "unresolved-")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ write := func(name, content string) {
+ err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Test various undefined references. Because of issue #29852,
+ // this used to give confusing error messages because the
+ // linker would find an undefined reference to "zero" created
+ // by the runtime package.
+
+ write("go.mod", "module testunresolved\n")
+ write("main.go", `package main
+
+func main() {
+ x()
+}
+
+func x()
+`)
+ write("main.s", `
+TEXT ·x(SB),0,$0
+ MOVD zero<>(SB), AX
+ MOVD zero(SB), AX
+ MOVD ·zero(SB), AX
+ RET
+`)
+ cmd := exec.Command(testenv.GoToolPath(t), "build")
+ cmd.Dir = tmpdir
+ cmd.Env = append(os.Environ(),
+ "GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Fatalf("expected build to fail, but it succeeded")
+ }
+ out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
+ got := string(out)
+ want := `main.x: relocation target zero not defined
+main.x: relocation target zero not defined
+main.x: relocation target main.zero not defined
+`
+ if want != got {
+ t.Fatalf("want:\n%sgot:\n%s", want, got)
+ }
+}
+
+func TestIssue33979(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustInternalLink(t)
+
+ // Skip test on platforms that do not support cgo internal linking.
+ switch runtime.GOARCH {
+ case "mips", "mipsle", "mips64", "mips64le":
+ t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
+ if runtime.GOOS == "aix" {
+ t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "unresolved-")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ write := func(name, content string) {
+ err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ run := func(name string, args ...string) string {
+ cmd := exec.Command(name, args...)
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)
+ }
+ return string(out)
+ }
+ runGo := func(args ...string) string {
+ return run(testenv.GoToolPath(t), args...)
+ }
+
+ // Test object with undefined reference that was not generated
+ // by Go, resulting in an SXREF symbol being loaded during linking.
+ // Because of issue #33979, the SXREF symbol would be found during
+ // error reporting, resulting in confusing error messages.
+
+ write("main.go", `package main
+func main() {
+ x()
+}
+func x()
+`)
+ // The following assembly must work on all architectures.
+ write("x.s", `
+TEXT ·x(SB),0,$0
+ CALL foo(SB)
+ RET
+`)
+ write("x.c", `
+void undefined();
+
+void foo() {
+ undefined();
+}
+`)
+
+ cc := strings.TrimSpace(runGo("env", "CC"))
+ cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))
+
+ // Compile, assemble and pack the Go and C code.
+ runGo("tool", "asm", "-gensymabis", "-o", "symabis", "x.s")
+ runGo("tool", "compile", "-symabis", "symabis", "-p", "main", "-o", "x1.o", "main.go")
+ runGo("tool", "asm", "-o", "x2.o", "x.s")
+ run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)
+ runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")
+
+ // Now attempt to link using the internal linker.
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "link", "-linkmode=internal", "x.a")
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Fatalf("expected link to fail, but it succeeded")
+ }
+ re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)
+ if !re.Match(out) {
+ t.Fatalf("got:\n%q\nwant:\n%s", out, re)
+ }
+}
+
+func TestBuildForTvOS(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveGoBuild(t)
+
+ // Only run this on darwin/amd64, where we can cross build for tvOS.
+ if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
+ t.Skip("skipping on non-darwin/amd64 platform")
+ }
+ if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+ t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
+ }
+ if err := exec.Command("xcrun", "--help").Run(); err != nil {
+ t.Skipf("error running xcrun, required for iOS cross build: %v", err)
+ }
+
+ t.Parallel()
+
+ sdkPath, err := exec.Command("xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
+ if err != nil {
+ t.Skip("failed to locate appletvos SDK, skipping")
+ }
+ CC := []string{
+ "clang",
+ "-arch",
+ "arm64",
+ "-isysroot", strings.TrimSpace(string(sdkPath)),
+ "-mtvos-version-min=12.0",
+ "-fembed-bitcode",
+ "-framework", "CoreFoundation",
+ }
+ lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
+ tmpDir, err := ioutil.TempDir("", "go-link-TestBuildFortvOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ ar := filepath.Join(tmpDir, "lib.a")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
+ cmd.Env = append(os.Environ(),
+ "CGO_ENABLED=1",
+ "GOOS=ios",
+ "GOARCH=arm64",
+ "CC="+strings.Join(CC, " "),
+ "CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
+ )
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+ }
+
+ link := exec.Command(CC[0], CC[1:]...)
+ link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out")) // Avoid writing to package directory.
+ link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
+ if out, err := link.CombinedOutput(); err != nil {
+ t.Fatalf("%v: %v:\n%s", link.Args, err, out)
+ }
+}
+
+var testXFlagSrc = `
+package main
+var X = "hello"
+var Z = [99999]int{99998:12345} // make it large enough to be mmaped
+func main() { println(X) }
+`
+
+func TestXFlag(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestXFlag")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "main.go")
+ err = ioutil.WriteFile(src, []byte(testXFlagSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
+ }
+}
+
+var testMacOSVersionSrc = `
+package main
+func main() { }
+`
+
+func TestMacOSVersion(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestMacOSVersion")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "main.go")
+ err = ioutil.WriteFile(src, []byte(testMacOSVersionSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exe := filepath.Join(tmpdir, "main")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
+ cmd.Env = append(os.Environ(),
+ "CGO_ENABLED=0",
+ "GOOS=darwin",
+ "GOARCH=amd64",
+ )
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+ }
+ exef, err := os.Open(exe)
+ if err != nil {
+ t.Fatal(err)
+ }
+ exem, err := macho.NewFile(exef)
+ if err != nil {
+ t.Fatal(err)
+ }
+ found := false
+ const LC_VERSION_MIN_MACOSX = 0x24
+ checkMin := func(ver uint32) {
+ major, minor := (ver>>16)&0xff, (ver>>8)&0xff
+ if major != 10 || minor < 9 {
+ t.Errorf("LC_VERSION_MIN_MACOSX version %d.%d < 10.9", major, minor)
+ }
+ }
+ for _, cmd := range exem.Loads {
+ raw := cmd.Raw()
+ type_ := exem.ByteOrder.Uint32(raw)
+ if type_ != LC_VERSION_MIN_MACOSX {
+ continue
+ }
+ osVer := exem.ByteOrder.Uint32(raw[8:])
+ checkMin(osVer)
+ sdkVer := exem.ByteOrder.Uint32(raw[12:])
+ checkMin(sdkVer)
+ found = true
+ break
+ }
+ if !found {
+ t.Errorf("no LC_VERSION_MIN_MACOSX load command found")
+ }
+}
+
+const Issue34788src = `
+
+package blah
+
+func Blah(i int) int {
+ a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
+ return a[i&7]
+}
+`
+
+func TestIssue34788Android386TLSSequence(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This is a cross-compilation test, so it doesn't make
+ // sense to run it on every GOOS/GOARCH combination. Limit
+ // the test to amd64 + darwin/linux.
+ if runtime.GOARCH != "amd64" ||
+ (runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
+ t.Skip("skipping on non-{linux,darwin}/amd64 platform")
+ }
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue34788Android386TLSSequence")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "blah.go")
+ err = ioutil.WriteFile(src, []byte(Issue34788src), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ obj := filepath.Join(tmpdir, "blah.o")
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", obj, src)
+ cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
+ }
+
+ // Run objdump on the resulting object.
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "objdump", obj)
+ out, oerr := cmd.CombinedOutput()
+ if oerr != nil {
+ t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
+ }
+
+ // Sift through the output; we should not be seeing any R_TLS_LE relocs.
+ scanner := bufio.NewScanner(bytes.NewReader(out))
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.Contains(line, "R_TLS_LE") {
+ t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
+ }
+ }
+}
+
+const testStrictDupGoSrc = `
+package main
+func f()
+func main() { f() }
+`
+
+const testStrictDupAsmSrc1 = `
+#include "textflag.h"
+TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
+ RET
+`
+
+const testStrictDupAsmSrc2 = `
+#include "textflag.h"
+TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
+ JMP 0(PC)
+`
+
+func TestStrictDup(t *testing.T) {
+ // Check that -strictdups flag works.
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestStrictDup")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "x.go")
+ err = ioutil.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ src = filepath.Join(tmpdir, "a.s")
+ err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc1), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ src = filepath.Join(tmpdir, "b.s")
+ err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc2), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ src = filepath.Join(tmpdir, "go.mod")
+ err = ioutil.WriteFile(src, []byte("module teststrictdup\n"), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("linking with -strictdups=1 failed: %v", err)
+ }
+ if !bytes.Contains(out, []byte("mismatched payload")) {
+ t.Errorf("unexpected output:\n%s", out)
+ }
+
+ cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
+ cmd.Dir = tmpdir
+ out, err = cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("linking with -strictdups=2 did not fail")
+ }
+ if !bytes.Contains(out, []byte("mismatched payload")) {
+ t.Errorf("unexpected output:\n%s", out)
+ }
+}
+
+const testFuncAlignSrc = `
+package main
+import (
+ "fmt"
+ "reflect"
+)
+func alignPc()
+
+func main() {
+ addr := reflect.ValueOf(alignPc).Pointer()
+ if (addr % 512) != 0 {
+ fmt.Printf("expected 512 bytes alignment, got %v\n", addr)
+ } else {
+ fmt.Printf("PASS")
+ }
+}
+`
+
+const testFuncAlignAsmSrc = `
+#include "textflag.h"
+
+TEXT ·alignPc(SB),NOSPLIT, $0-0
+ MOVD $2, R0
+ PCALIGN $512
+ MOVD $3, R1
+ RET
+`
+
+// TestFuncAlign verifies that the address of a function can be aligned
+// with a specific value on arm64.
+func TestFuncAlign(t *testing.T) {
+ if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
+ t.Skip("skipping on non-linux/arm64 platform")
+ }
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestFuncAlign")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "go.mod")
+ err = ioutil.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ src = filepath.Join(tmpdir, "falign.go")
+ err = ioutil.WriteFile(src, []byte(testFuncAlignSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ src = filepath.Join(tmpdir, "falign.s")
+ err = ioutil.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Build and run with old object file format.
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "falign")
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("build failed: %v", err)
+ }
+ cmd = exec.Command(tmpdir + "/falign")
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("failed to run with err %v, output: %s", err, out)
+ }
+ if string(out) != "PASS" {
+ t.Errorf("unexpected output: %s\n", out)
+ }
+}
+
+const testTrampSrc = `
+package main
+import "fmt"
+func main() {
+ fmt.Println("hello")
+
+ defer func(){
+ if e := recover(); e == nil {
+ panic("did not panic")
+ }
+ }()
+ f1()
+}
+
+// Test deferreturn trampolines. See issue #39049.
+func f1() { defer f2() }
+func f2() { panic("XXX") }
+`
+
+func TestTrampoline(t *testing.T) {
+ // Test that trampoline insertion works as expected.
+ // For stress test, we set -debugtramp=2 flag, which sets a very low
+ // threshold for trampoline generation, and essentially all cross-package
+ // calls will use trampolines.
+ switch runtime.GOARCH {
+ case "arm", "ppc64", "ppc64le":
+ default:
+ t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
+ }
+
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestTrampoline")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "hello.go")
+ err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ exe := filepath.Join(tmpdir, "hello.exe")
+
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failed: %v\n%s", err, out)
+ }
+ cmd = exec.Command(exe)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("executable failed to run: %v\n%s", err, out)
+ }
+ if string(out) != "hello\n" {
+ t.Errorf("unexpected output:\n%s", out)
+ }
+}
+
+func TestIndexMismatch(t *testing.T) {
+ // Test that index mismatch will cause a link-time error (not run-time error).
+ // This shouldn't happen with "go build". We invoke the compiler and the linker
+ // manually, and try to "trick" the linker with an inconsistent object file.
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIndexMismatch")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
+ bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
+ mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
+ aObj := filepath.Join(tmpdir, "a.o")
+ mObj := filepath.Join(tmpdir, "main.o")
+ exe := filepath.Join(tmpdir, "main.exe")
+
+ // Build a program with main package importing package a.
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", aObj, aSrc)
+ t.Log(cmd)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("compiling a.go failed: %v\n%s", err, out)
+ }
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", tmpdir, "-o", mObj, mSrc)
+ t.Log(cmd)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("compiling main.go failed: %v\n%s", err, out)
+ }
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj)
+ t.Log(cmd)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("linking failed: %v\n%s", err, out)
+ }
+
+ // Now, overwrite a.o with the object of b.go. This should
+ // result in an index mismatch.
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", aObj, bSrc)
+ t.Log(cmd)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("compiling a.go failed: %v\n%s", err, out)
+ }
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj)
+ t.Log(cmd)
+ out, err = cmd.CombinedOutput()
+ if err == nil {
+ t.Fatalf("linking didn't fail")
+ }
+ if !bytes.Contains(out, []byte("fingerprint mismatch")) {
+ t.Errorf("did not see expected error message. out:\n%s", out)
+ }
+}
+
+func TestPErsrc(t *testing.T) {
+ // Test that PE rsrc section is handled correctly (issue 39658).
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOARCH != "amd64" || runtime.GOOS != "windows" {
+ t.Skipf("this is a windows/amd64-only test")
+ }
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestPErsrc")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ pkgdir := filepath.Join("testdata", "testPErsrc")
+ exe := filepath.Join(tmpdir, "a.exe")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
+ cmd.Dir = pkgdir
+ // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("building failed: %v, output:\n%s", err, out)
+ }
+
+ // Check that the binary contains the rsrc data
+ b, err := ioutil.ReadFile(exe)
+ if err != nil {
+ t.Fatalf("reading output failed: %v", err)
+ }
+ if !bytes.Contains(b, []byte("Hello Gophers!")) {
+ t.Fatalf("binary does not contain expected content")
+ }
+
+ pkgdir = filepath.Join("testdata", "testPErsrc-complex")
+ exe = filepath.Join(tmpdir, "a.exe")
+ cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
+ cmd.Dir = pkgdir
+ // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("building failed: %v, output:\n%s", err, out)
+ }
+
+ // Check that the binary contains the rsrc data
+ b, err = ioutil.ReadFile(exe)
+ if err != nil {
+ t.Fatalf("reading output failed: %v", err)
+ }
+ if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
+ t.Fatalf("binary does not contain expected content")
+ }
+}
+
+func TestContentAddressableSymbols(t *testing.T) {
+ // Test that the linker handles content-addressable symbols correctly.
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestContentAddressableSymbols")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join("testdata", "testHashedSyms", "p.go")
+ cmd := exec.Command(testenv.GoToolPath(t), "run", src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("command %s failed: %v\n%s", cmd, err, out)
+ }
+}
+
+func TestReadOnly(t *testing.T) {
+ // Test that read-only data is indeed read-only.
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ src := filepath.Join("testdata", "testRO", "x.go")
+ cmd := exec.Command(testenv.GoToolPath(t), "run", src)
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("running test program did not fail. output:\n%s", out)
+ }
+}
+
+const testIssue38554Src = `
+package main
+
+type T [10<<20]byte
+
+//go:noinline
+func f() T {
+ return T{} // compiler will make a large stmp symbol, but not used.
+}
+
+func main() {
+ x := f()
+ println(x[1])
+}
+`
+
+func TestIssue38554(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue38554")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "x.go")
+ err = ioutil.WriteFile(src, []byte(testIssue38554Src), 0666)
+ if err != nil {
+ t.Fatalf("failed to write source file: %v", err)
+ }
+ exe := filepath.Join(tmpdir, "x.exe")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failed: %v\n%s", err, out)
+ }
+
+ fi, err := os.Stat(exe)
+ if err != nil {
+ t.Fatalf("failed to stat output file: %v", err)
+ }
+
+ // The test program is not much different from a helloworld, which is
+ // typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
+ // it will be over 10 MB.
+ const want = 5 << 20
+ if got := fi.Size(); got > want {
+ t.Errorf("binary too big: got %d, want < %d", got, want)
+ }
+}
+
+const testIssue42396src = `
+package main
+
+//go:noinline
+//go:nosplit
+func callee(x int) {
+}
+
+func main() {
+ callee(9)
+}
+`
+
+func TestIssue42396(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if !sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
+ t.Skip("no race detector support")
+ }
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue42396")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "main.go")
+ err = ioutil.WriteFile(src, []byte(testIssue42396src), 0666)
+ if err != nil {
+ t.Fatalf("failed to write source file: %v", err)
+ }
+ exe := filepath.Join(tmpdir, "main.exe")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Fatalf("build unexpectedly succeeded")
+ }
+
+ // Check to make sure that we see a reasonable error message
+ // and not a panic.
+ if strings.Contains(string(out), "panic:") {
+ t.Fatalf("build should not fail with panic:\n%s", out)
+ }
+ const want = "reference to undefined builtin"
+ if !strings.Contains(string(out), want) {
+ t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
+ }
+}
+
+const testLargeRelocSrc = `
+package main
+
+var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
+
+func main() {
+ check(x[1<<23-1], 0)
+ check(x[1<<23], 23)
+ check(x[1<<23+1], 0)
+ check(x[1<<24-1], 0)
+ check(x[1<<24], 24)
+ check(x[1<<24+1], 0)
+}
+
+func check(x, y byte) {
+ if x != y {
+ panic("FAIL")
+ }
+}
+`
+
+func TestLargeReloc(t *testing.T) {
+ // Test that large relocation addend is handled correctly.
+ // In particular, on darwin/arm64 when external linking,
+ // Mach-O relocation has only 24-bit addend. See issue #42738.
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue42396")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "x.go")
+ err = ioutil.WriteFile(src, []byte(testLargeRelocSrc), 0666)
+ if err != nil {
+ t.Fatalf("failed to write source file: %v", err)
+ }
+ cmd := exec.Command(testenv.GoToolPath(t), "run", src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("build failed: %v. output:\n%s", err, out)
+ }
+
+ if testenv.HasCGO() { // currently all targets that support cgo can external link
+ cmd = exec.Command(testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failed: %v. output:\n%s", err, out)
+ }
+ }
+}
diff --git a/src/cmd/link/linkbig_test.go b/src/cmd/link/linkbig_test.go
new file mode 100644
index 0000000..78d2bc1
--- /dev/null
+++ b/src/cmd/link/linkbig_test.go
@@ -0,0 +1,112 @@
+// 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.
+
+// This program generates a test to verify that a program can be
+// successfully linked even when there are very large text
+// sections present.
+
+package main
+
+import (
+ "bytes"
+ "cmd/internal/objabi"
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "testing"
+)
+
+func TestLargeText(t *testing.T) {
+ if testing.Short() || (objabi.GOARCH != "ppc64le" && objabi.GOARCH != "ppc64" && objabi.GOARCH != "arm") {
+ t.Skipf("Skipping large text section test in short mode or on %s", objabi.GOARCH)
+ }
+ testenv.MustHaveGoBuild(t)
+
+ var w bytes.Buffer
+ const FN = 4
+ tmpdir, err := ioutil.TempDir("", "bigtext")
+ if err != nil {
+ t.Fatalf("can't create temp directory: %v\n", err)
+ }
+
+ defer os.RemoveAll(tmpdir)
+
+ // Generate the scenario where the total amount of text exceeds the
+ // limit for the jmp/call instruction, on RISC architectures like ppc64le,
+ // which is 2^26. When that happens the call requires special trampolines or
+ // long branches inserted by the linker where supported.
+ // Multiple .s files are generated instead of one.
+ instOnArch := map[string]string{
+ "ppc64": "\tMOVD\tR0,R3\n",
+ "ppc64le": "\tMOVD\tR0,R3\n",
+ "arm": "\tMOVW\tR0,R1\n",
+ }
+ inst := instOnArch[objabi.GOARCH]
+ for j := 0; j < FN; j++ {
+ testname := fmt.Sprintf("bigfn%d", j)
+ fmt.Fprintf(&w, "TEXT ·%s(SB),$0\n", testname)
+ for i := 0; i < 2200000; i++ {
+ fmt.Fprintf(&w, inst)
+ }
+ fmt.Fprintf(&w, "\tRET\n")
+ err := ioutil.WriteFile(tmpdir+"/"+testname+".s", w.Bytes(), 0666)
+ if err != nil {
+ t.Fatalf("can't write output: %v\n", err)
+ }
+ w.Reset()
+ }
+ fmt.Fprintf(&w, "package main\n")
+ fmt.Fprintf(&w, "\nimport (\n")
+ fmt.Fprintf(&w, "\t\"os\"\n")
+ fmt.Fprintf(&w, "\t\"fmt\"\n")
+ fmt.Fprintf(&w, ")\n\n")
+
+ for i := 0; i < FN; i++ {
+ fmt.Fprintf(&w, "func bigfn%d()\n", i)
+ }
+ fmt.Fprintf(&w, "\nfunc main() {\n")
+
+ // There are lots of dummy code generated in the .s files just to generate a lot
+ // of text. Link them in but guard their call so their code is not executed but
+ // the main part of the program can be run.
+ fmt.Fprintf(&w, "\tif os.Getenv(\"LINKTESTARG\") != \"\" {\n")
+ for i := 0; i < FN; i++ {
+ fmt.Fprintf(&w, "\t\tbigfn%d()\n", i)
+ }
+ fmt.Fprintf(&w, "\t}\n")
+ fmt.Fprintf(&w, "\tfmt.Printf(\"PASS\\n\")\n")
+ fmt.Fprintf(&w, "}")
+ err = ioutil.WriteFile(tmpdir+"/bigfn.go", w.Bytes(), 0666)
+ if err != nil {
+ t.Fatalf("can't write output: %v\n", err)
+ }
+
+ // Build and run with internal linking.
+ os.Chdir(tmpdir)
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "bigtext")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Build failed for big text program with internal linking: %v, output: %s", err, out)
+ }
+ cmd = exec.Command(tmpdir + "/bigtext")
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Program built with internal linking failed to run with err %v, output: %s", err, out)
+ }
+
+ // Build and run with external linking
+ os.Chdir(tmpdir)
+ cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", "bigtext", "-ldflags", "'-linkmode=external'")
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Build failed for big text program with external linking: %v, output: %s", err, out)
+ }
+ cmd = exec.Command(tmpdir + "/bigtext")
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Program built with external linking failed to run with err %v, output: %s", err, out)
+ }
+}
diff --git a/src/cmd/link/main.go b/src/cmd/link/main.go
new file mode 100644
index 0000000..6b4ca97
--- /dev/null
+++ b/src/cmd/link/main.go
@@ -0,0 +1,69 @@
+// 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 main
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/amd64"
+ "cmd/link/internal/arm"
+ "cmd/link/internal/arm64"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/mips"
+ "cmd/link/internal/mips64"
+ "cmd/link/internal/ppc64"
+ "cmd/link/internal/riscv64"
+ "cmd/link/internal/s390x"
+ "cmd/link/internal/wasm"
+ "cmd/link/internal/x86"
+ "fmt"
+ "os"
+)
+
+// The bulk of the linker implementation lives in cmd/link/internal/ld.
+// Architecture-specific code lives in cmd/link/internal/GOARCH.
+//
+// Program initialization:
+//
+// Before any argument parsing is done, the Init function of relevant
+// architecture package is called. The only job done in Init is
+// configuration of the architecture-specific variables.
+//
+// Then control flow passes to ld.Main, which parses flags, makes
+// some configuration decisions, and then gives the architecture
+// packages a second chance to modify the linker's configuration
+// via the ld.Arch.Archinit function.
+
+func main() {
+ var arch *sys.Arch
+ var theArch ld.Arch
+
+ switch objabi.GOARCH {
+ default:
+ fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", objabi.GOARCH)
+ os.Exit(2)
+ case "386":
+ arch, theArch = x86.Init()
+ case "amd64":
+ arch, theArch = amd64.Init()
+ case "arm":
+ arch, theArch = arm.Init()
+ case "arm64":
+ arch, theArch = arm64.Init()
+ case "mips", "mipsle":
+ arch, theArch = mips.Init()
+ case "mips64", "mips64le":
+ arch, theArch = mips64.Init()
+ case "ppc64", "ppc64le":
+ arch, theArch = ppc64.Init()
+ case "riscv64":
+ arch, theArch = riscv64.Init()
+ case "s390x":
+ arch, theArch = s390x.Init()
+ case "wasm":
+ arch, theArch = wasm.Init()
+ }
+ ld.Main(arch, theArch)
+}
diff --git a/src/cmd/link/testdata/testBuildFortvOS/lib.go b/src/cmd/link/testdata/testBuildFortvOS/lib.go
new file mode 100644
index 0000000..bc6c699
--- /dev/null
+++ b/src/cmd/link/testdata/testBuildFortvOS/lib.go
@@ -0,0 +1,8 @@
+package main
+
+import "C"
+
+//export GoFunc
+func GoFunc() {}
+
+func main() {}
diff --git a/src/cmd/link/testdata/testBuildFortvOS/main.m b/src/cmd/link/testdata/testBuildFortvOS/main.m
new file mode 100644
index 0000000..1c8175f
--- /dev/null
+++ b/src/cmd/link/testdata/testBuildFortvOS/main.m
@@ -0,0 +1,5 @@
+extern void GoFunc();
+
+int main(int argc, char **argv) {
+ GoFunc();
+}
diff --git a/src/cmd/link/testdata/testHashedSyms/p.go b/src/cmd/link/testdata/testHashedSyms/p.go
new file mode 100644
index 0000000..87dddcf
--- /dev/null
+++ b/src/cmd/link/testdata/testHashedSyms/p.go
@@ -0,0 +1,33 @@
+// 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 test case contains two static temps (the array literals)
+// with same contents but different sizes. The linker should not
+// report a hash collision. The linker can (and actually does)
+// dedup the two symbols, by keeping the larger symbol. The dedup
+// is not a requirement for correctness and not checked in this test.
+// We do check the emitted symbol contents are correct, though.
+
+package main
+
+func main() {
+ F([10]int{1, 2, 3, 4, 5, 6}, [20]int{1, 2, 3, 4, 5, 6})
+}
+
+//go:noinline
+func F(x, y interface{}) {
+ x1 := x.([10]int)
+ y1 := y.([20]int)
+ for i := range y1 {
+ if i < 6 {
+ if x1[i] != i+1 || y1[i] != i+1 {
+ panic("FAIL")
+ }
+ } else {
+ if (i < len(x1) && x1[i] != 0) || y1[i] != 0 {
+ panic("FAIL")
+ }
+ }
+ }
+}
diff --git a/src/cmd/link/testdata/testIndexMismatch/a.go b/src/cmd/link/testdata/testIndexMismatch/a.go
new file mode 100644
index 0000000..1f3b2c5
--- /dev/null
+++ b/src/cmd/link/testdata/testIndexMismatch/a.go
@@ -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.
+
+package a
+
+//go:noinline
+func A() { println("A") }
diff --git a/src/cmd/link/testdata/testIndexMismatch/b.go b/src/cmd/link/testdata/testIndexMismatch/b.go
new file mode 100644
index 0000000..9b55dbf
--- /dev/null
+++ b/src/cmd/link/testdata/testIndexMismatch/b.go
@@ -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.
+
+package a
+
+//go:noinline
+func B() { println("B") }
diff --git a/src/cmd/link/testdata/testIndexMismatch/main.go b/src/cmd/link/testdata/testIndexMismatch/main.go
new file mode 100644
index 0000000..bc15236
--- /dev/null
+++ b/src/cmd/link/testdata/testIndexMismatch/main.go
@@ -0,0 +1,9 @@
+// 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 "a"
+
+func main() { a.A() }
diff --git a/src/cmd/link/testdata/testPErsrc-complex/main.go b/src/cmd/link/testdata/testPErsrc-complex/main.go
new file mode 100644
index 0000000..affd6ea
--- /dev/null
+++ b/src/cmd/link/testdata/testPErsrc-complex/main.go
@@ -0,0 +1,43 @@
+// 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 PE rsrc section is handled correctly, when the object files
+// have been created by llvm-rc or msvc's rc.exe, which means there's the
+// @feat.00 symbol as well as split .rsrc$00 and .rsrc$01 section to deal with.
+//
+// rsrc.syso is created with:
+// windres -i a.rc -o rsrc.syso -O coff
+// where this windres calls into llvm-rc and llvm-cvtres. The source file,
+// a.rc, simply contains a reference to its own bytes:
+//
+// resname RCDATA a.rc
+//
+// Object dumping the resultant rsrc.syso, we can see the split sections and
+// the @feat.00 SEH symbol:
+//
+// rsrc.syso: file format coff-x86-64
+//
+// architecture: x86_64
+// start address: 0x0000000000000000
+//
+// Export Table:
+// Sections:
+// Idx Name Size VMA Type
+// 0 .rsrc$01 00000068 0000000000000000 DATA
+// 1 .rsrc$02 00000018 0000000000000000 DATA
+//
+// SYMBOL TABLE:
+// [ 0](sec -1)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000011 @feat.00
+// [ 1](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rsrc$01
+// AUX scnlen 0x68 nreloc 1 nlnno 0 checksum 0x0 assoc 0 comdat 0
+// [ 3](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rsrc$02
+// AUX scnlen 0x18 nreloc 0 nlnno 0 checksum 0x0 assoc 0 comdat 0
+// [ 5](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 $R000000
+// RELOCATION RECORDS FOR [.rsrc$01]:
+// OFFSET TYPE VALUE
+// 0000000000000048 IMAGE_REL_AMD64_ADDR32NB $R000000
+
+package main
+
+func main() {}
diff --git a/src/cmd/link/testdata/testPErsrc-complex/rsrc.syso b/src/cmd/link/testdata/testPErsrc-complex/rsrc.syso
new file mode 100644
index 0000000..eff630b
--- /dev/null
+++ b/src/cmd/link/testdata/testPErsrc-complex/rsrc.syso
Binary files differ
diff --git a/src/cmd/link/testdata/testPErsrc/main.go b/src/cmd/link/testdata/testPErsrc/main.go
new file mode 100644
index 0000000..5eb66fb
--- /dev/null
+++ b/src/cmd/link/testdata/testPErsrc/main.go
@@ -0,0 +1,19 @@
+// 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 PE rsrc section is handled correctly (issue 39658).
+//
+// rsrc.syso is created with:
+// windres -i a.rc -o rsrc.syso -O coff
+// on windows-amd64-2016 builder, where a.rc is a text file with
+// the following content:
+//
+// resname RCDATA {
+// "Hello Gophers!\0",
+// "This is a test.\0",
+// }
+
+package main
+
+func main() {}
diff --git a/src/cmd/link/testdata/testPErsrc/rsrc.syso b/src/cmd/link/testdata/testPErsrc/rsrc.syso
new file mode 100644
index 0000000..0d9699d
--- /dev/null
+++ b/src/cmd/link/testdata/testPErsrc/rsrc.syso
Binary files differ
diff --git a/src/cmd/link/testdata/testRO/x.go b/src/cmd/link/testdata/testRO/x.go
new file mode 100644
index 0000000..d77db6d
--- /dev/null
+++ b/src/cmd/link/testdata/testRO/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.
+
+// Test that read-only data is indeed read-only. This
+// program attempts to modify read-only data, and it
+// should fail.
+
+package main
+
+import "unsafe"
+
+var s = "hello"
+
+func main() {
+ println(s)
+ *(*struct {
+ p *byte
+ l int
+ })(unsafe.Pointer(&s)).p = 'H'
+ println(s)
+}