diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/link | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/link')
145 files changed, 44154 insertions, 0 deletions
diff --git a/src/cmd/link/cgo_test.go b/src/cmd/link/cgo_test.go new file mode 100644 index 0000000..4393c3f --- /dev/null +++ b/src/cmd/link/cgo_test.go @@ -0,0 +1,141 @@ +// Copyright 2021 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" + "fmt" + "internal/testenv" + "os" + "os/exec" + "path/filepath" + "testing" +) + +// Issues 43830, 46295 +func TestCGOLTO(t *testing.T) { + testenv.MustHaveCGO(t) + testenv.MustHaveGoBuild(t) + + t.Parallel() + + for _, cc := range []string{"gcc", "clang"} { + for test := 0; test < 2; test++ { + t.Run(fmt.Sprintf("%s-%d", cc, test), func(t *testing.T) { + testCGOLTO(t, cc, test) + }) + } + } +} + +const test1_main = ` +package main + +/* +extern int myadd(int, int); +int c_add(int a, int b) { + return myadd(a, b); +} +*/ +import "C" + +func main() { + println(C.c_add(1, 2)) +} +` + +const test1_add = ` +package main + +import "C" + +/* test */ + +//export myadd +func myadd(a C.int, b C.int) C.int { + return a + b +} +` + +const test2_main = ` +package main + +import "fmt" + +/* +#include <stdio.h> + +void hello(void) { + printf("hello\n"); +} +*/ +import "C" + +func main() { + hello := C.hello + fmt.Printf("%v\n", hello) +} +` + +func testCGOLTO(t *testing.T, cc string, test int) { + t.Parallel() + + if _, err := exec.LookPath(cc); err != nil { + t.Skipf("no %s compiler", cc) + } + + dir := t.TempDir() + + writeTempFile := func(name, contents string) { + if err := os.WriteFile(filepath.Join(dir, name), []byte(contents), 0644); err != nil { + t.Fatal(err) + } + } + + writeTempFile("go.mod", "module cgolto\n") + + switch test { + case 0: + writeTempFile("main.go", test1_main) + writeTempFile("add.go", test1_add) + case 1: + writeTempFile("main.go", test2_main) + default: + t.Fatalf("bad case %d", test) + } + + cmd := testenv.Command(t, testenv.GoToolPath(t), "build") + cmd.Dir = dir + cmd.Env = append(os.Environ(), + "CC="+cc, + "CGO_CFLAGS=-flto", + ) + + t.Log("go build") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + + if err != nil { + t.Logf("go build failed: %v", err) + + // Error messages we've seen indicating that LTO is not supported. + // These errors come from GCC or clang, not Go. + var noLTO = []string{ + `unrecognized command line option "-flto"`, + "unable to pass LLVM bit-code files to linker", + "file not recognized: File format not recognized", + "LTO support has not been enabled", + "linker command failed with exit code", + "gcc: can't load library", + } + for _, msg := range noLTO { + if bytes.Contains(out, []byte(msg)) { + t.Skipf("C compiler %v does not support LTO", cc) + } + } + + t.Error("failed") + } +} diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go new file mode 100644 index 0000000..a570132 --- /dev/null +++ b/src/cmd/link/doc.go @@ -0,0 +1,131 @@ +// 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. + -asan + Link with C/C++ address sanitizer support. + -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..53dc686 --- /dev/null +++ b/src/cmd/link/dwarf_test.go @@ -0,0 +1,230 @@ +// 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" + "cmd/internal/quoted" + "debug/dwarf" + "internal/testenv" + "os" + "os/exec" + "path" + "path/filepath" + "runtime" + "strings" + "testing" +) + +// TestMain allows this test binary to run as a -toolexec wrapper for the 'go' +// command. If LINK_TEST_TOOLEXEC is set, TestMain runs the binary as if it were +// cmd/link, and otherwise runs the requested tool as a subprocess. +// +// This allows the test to verify the behavior of the current contents of the +// cmd/link package even if the installed cmd/link binary is stale. +func TestMain(m *testing.M) { + if os.Getenv("LINK_TEST_TOOLEXEC") == "" { + // Not running as a -toolexec wrapper. Just run the tests. + os.Exit(m.Run()) + } + + if strings.TrimSuffix(filepath.Base(os.Args[1]), ".exe") == "link" { + // Running as a -toolexec linker, and the tool is cmd/link. + // Substitute this test binary for the linker. + os.Args = os.Args[1:] + main() + os.Exit(0) + } + + cmd := exec.Command(os.Args[1], os.Args[2:]...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + os.Exit(1) + } + os.Exit(0) +} + +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() + + for _, prog := range []string{"testprog", "testprogcgo"} { + prog := prog + expectDWARF := expectDWARF + if runtime.GOOS == "aix" && prog == "testprogcgo" { + extld := os.Getenv("CC") + if extld == "" { + extld = "gcc" + } + extldArgs, err := quoted.Split(extld) + if err != nil { + t.Fatal(err) + } + expectDWARF, err = cmddwarf.IsDWARFEnabledOnAIXLd(extldArgs) + if err != nil { + t.Fatal(err) + } + } + + t.Run(prog, func(t *testing.T) { + t.Parallel() + + tmpDir := t.TempDir() + + exe := filepath.Join(tmpDir, prog+".exe") + dir := "../../runtime/testdata/" + prog + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-toolexec", os.Args[0], "-o", exe) + if buildmode != "" { + cmd.Args = append(cmd.Args, "-buildmode", buildmode) + } + cmd.Args = append(cmd.Args, dir) + cmd.Env = append(os.Environ(), env...) + cmd.Env = append(cmd.Env, "CGO_CFLAGS=") // ensure CGO_CFLAGS does not contain any flags. Issue #35459 + cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1") + 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 := testenv.Command(t, "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") + } + + darwinSymbolTestIsTooFlaky := true // Turn this off, it is too flaky -- See #32218 + if runtime.GOOS == "darwin" && !darwinSymbolTestIsTooFlaky { + if _, err = exec.LookPath("symbols"); err == nil { + // Ensure Apple's tooling can parse our object for symbols. + out, err = testenv.Command(t, "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 := testenv.Command(t, "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 := testenv.Command(t, "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..27285ff --- /dev/null +++ b/src/cmd/link/elf_test.go @@ -0,0 +1,497 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build dragonfly || freebsd || linux || netbsd || openbsd + +package main + +import ( + "debug/elf" + "fmt" + "internal/platform" + "internal/testenv" + "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 := testenv.Command(t, goTool, "env", "CC") + cmd.Env = env + ccb, err := cmd.Output() + if err != nil { + t.Fatal(err) + } + cc := strings.TrimSpace(string(ccb)) + + cmd = testenv.Command(t, 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 := t.TempDir() + + gopath := filepath.Join(dir, "GOPATH") + env := append(os.Environ(), "GOPATH="+gopath) + + if err := os.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 := os.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 := testenv.Command(t, 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 := testenv.Command(t, 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 := os.WriteFile(goFile, []byte(goSource), 0444); err != nil { + t.Fatal(err) + } + + cmd := testenv.Command(t, 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 := t.TempDir() + + gopath := filepath.Join(dir, "GOPATH") + env := append(os.Environ(), "GOPATH="+gopath) + + if err := os.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 := os.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 := testenv.Command(t, 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 := testenv.Command(t, 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 := os.WriteFile(goFile, []byte(goSource), 0444); err != nil { + t.Fatal(err) + } + + t.Logf("%s build", goTool) + cmd := testenv.Command(t, goTool, "build") + cmd.Dir = dir + cmd.Env = env + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } +} + +func TestMergeNoteSections(t *testing.T) { + testenv.MustHaveGoBuild(t) + expected := 1 + + switch runtime.GOOS { + case "linux", "dragonfly": + case "openbsd", "netbsd", "freebsd": + // These OSes require independent segment + expected = 2 + default: + t.Skip("We should only test on elf output.") + } + t.Parallel() + + goFile := filepath.Join(t.TempDir(), "notes.go") + if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil { + t.Fatal(err) + } + outFile := filepath.Join(t.TempDir(), "notes.exe") + goTool := testenv.GoToolPath(t) + // sha1sum of "gopher" + id := "0xf4e8cd51ce8bae2996dc3b74639cdeaa1f7fee5f" + cmd := testenv.Command(t, goTool, "build", "-o", outFile, "-ldflags", + "-B "+id, goFile) + cmd.Dir = t.TempDir() + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + ef, err := elf.Open(outFile) + if err != nil { + t.Fatalf("open elf file failed:%v", err) + } + defer ef.Close() + sec := ef.Section(".note.gnu.build-id") + if sec == nil { + t.Fatalf("can't find gnu build id") + } + + sec = ef.Section(".note.go.buildid") + if sec == nil { + t.Fatalf("can't find go build id") + } + cnt := 0 + for _, ph := range ef.Progs { + if ph.Type == elf.PT_NOTE { + cnt += 1 + } + } + if cnt != expected { + t.Fatalf("want %d PT_NOTE segment, got %d", expected, cnt) + } +} + +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 !platform.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 := t.TempDir() + + 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 := testenv.Command(t, 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) + + if sizepie < sizeexe || sizepie-extrapie < sizeexe-extraexe { + return + } + 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) + } + }) + } +} + +func TestIssue51939(t *testing.T) { + testenv.MustHaveGoBuild(t) + t.Parallel() + td := t.TempDir() + goFile := filepath.Join(td, "issue51939.go") + if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil { + t.Fatal(err) + } + outFile := filepath.Join(td, "issue51939.exe") + goTool := testenv.GoToolPath(t) + cmd := testenv.Command(t, goTool, "build", "-o", outFile, goFile) + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + ef, err := elf.Open(outFile) + if err != nil { + t.Fatal(err) + } + + for _, s := range ef.Sections { + if s.Flags&elf.SHF_ALLOC == 0 && s.Addr != 0 { + t.Errorf("section %s should not allocated with addr %x", s.Name, s.Addr) + } + } +} diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go new file mode 100644 index 0000000..f4832ef --- /dev/null +++ b/src/cmd/link/internal/amd64/asm.go @@ -0,0 +1,674 @@ +// 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)) + } + 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()+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, []byte) 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..c5e2117 --- /dev/null +++ b/src/cmd/link/internal/amd64/obj.go @@ -0,0 +1,123 @@ +// 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", + LinuxdynldMusl: "/lib/ld-musl-x86_64.so.1", + 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..4574f2d --- /dev/null +++ b/src/cmd/link/internal/arm/asm.go @@ -0,0 +1,694 @@ +// 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 + } + + case objabi.R_GOTPCREL: + if target.IsExternal() { + // External linker will do this relocation. + return true + } + if targType != sym.SDYNIMPORT { + ldr.Errorf(s, "R_GOTPCREL target is not SDYNIMPORT symbol: %v", ldr.SymName(targ)) + } + 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()+int64(ldr.SymGot(targ))) + 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.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24): + // Host object relocations that will be turned into a PLT call. + // The PLT may be too far. Insert a trampoline for them. + fallthrough + 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 { + // Workaround for issue #58425: it appears that the + // external linker doesn't always take into account the + // relocation addend when doing reachability checks. This + // means that if you have a call from function XYZ at + // offset 8 to runtime.duffzero with addend 800 (for + // example), where the distance between the start of XYZ + // and the start of runtime.duffzero is just over the + // limit (by 100 bytes, say), you can get "relocation + // doesn't fit" errors from the external linker. To deal + // with this, ignore the addend when performing the + // distance calculation (this assumes that we're only + // handling backward jumps; ideally we might want to check + // both with and without the addend). + if ctxt.IsExternal() { + t = (ldr.SymValue(rs) - (ldr.SymValue(s) + int64(r.Off()))) / 4 + } else { + // 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() || ldr.SymType(rs) == sym.SDYNIMPORT { + 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 | 12<<12 | 15<<16) // MOVW (R15), R12 // R15 is actual pc + 8 + o2 := uint32(0xe12fff10 | 12) // JMP (R12) + 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 | 12<<12 | 15<<16 | 4) // MOVW 4(R15), R12 // R15 is actual pc + 8 + o2 := uint32(0xe0800000 | 12<<12 | 15<<16 | 12) // ADD R15, R12, R12 + o3 := uint32(0xe12fff10 | 12) // JMP (R12) + 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 | 12<<12 | 15<<16 | 8) // MOVW 8(R15), R12 // R15 is actual pc + 8 + o2 := uint32(0xe0800000 | 12<<12 | 15<<16 | 12) // ADD R15, R12, R12 + o3 := uint32(0xe5900000 | 12<<12 | 12<<16) // MOVW (R12), R12 + o4 := uint32(0xe12fff10 | 12) // JMP (R12) + 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 | 12<<12 | 12<<16 | immrot(uint32(offset)) // ADD $offset, R12, R12 + o1 = uint32(0xe5900000 | 12<<12 | 15<<16 | 12) // MOVW 12(R15), R12 + } + 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() + 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, []byte) 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 := 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..6da0c77 --- /dev/null +++ b/src/cmd/link/internal/arm/obj.go @@ -0,0 +1,111 @@ +// 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, + TrampLimit: 0x1c00000, // 24-bit signed offset * 4, leave room for PLT etc. + + 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 + LinuxdynldMusl: "/lib/ld-musl-arm.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 = 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..7109d84 --- /dev/null +++ b/src/cmd/link/internal/arm64/asm.go @@ -0,0 +1,1389 @@ +// 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)) + } + 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()+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.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 + } + + case objabi.R_ARM64_GOTPCREL: + if target.IsExternal() { + // External linker will do this relocation. + return true + } + if targType != sym.SDYNIMPORT { + ldr.Errorf(s, "R_ARM64_GOTPCREL target is not SDYNIMPORT symbol: %v", ldr.SymName(targ)) + } + if r.Add() != 0 { + ldr.Errorf(s, "R_ARM64_GOTPCREL with non-zero addend (%v)", r.Add()) + } + if target.IsElf() { + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_AARCH64_GLOB_DAT)) + } else { + ld.AddGotSym(target, ldr, syms, targ, 0) + } + // turn into two relocations, one for each instruction. + su := ldr.MakeSymbolUpdater(s) + r.SetType(objabi.R_ARM64_GOT) + r.SetSiz(4) + r.SetSym(syms.GOT) + r.SetAdd(int64(ldr.SymGot(targ))) + r2, _ := su.AddRel(objabi.R_ARM64_GOT) + r2.SetSiz(4) + r2.SetOff(r.Off() + 4) + r2.SetSym(syms.GOT) + r2.SetAdd(int64(ldr.SymGot(targ))) + 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_PCREL_LDST8, + objabi.R_ARM64_PCREL_LDST16, + objabi.R_ARM64_PCREL_LDST32, + objabi.R_ARM64_PCREL_LDST64: + // two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_LDST{64/32/16/8}_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)) + var ldstType elf.R_AARCH64 + switch r.Type { + case objabi.R_ARM64_PCREL_LDST8: + ldstType = elf.R_AARCH64_LDST8_ABS_LO12_NC + case objabi.R_ARM64_PCREL_LDST16: + ldstType = elf.R_AARCH64_LDST16_ABS_LO12_NC + case objabi.R_ARM64_PCREL_LDST32: + ldstType = elf.R_AARCH64_LDST32_ABS_LO12_NC + case objabi.R_ARM64_PCREL_LDST64: + ldstType = elf.R_AARCH64_LDST64_ABS_LO12_NC + } + out.Write64(uint64(ldstType) | 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 21, 24-bit. +func signext21(x int64) int64 { return x << (64 - 21) >> (64 - 21) } +func signext24(x int64) int64 { return x << (64 - 24) >> (64 - 24) } + +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(offsetLabelName(ldr, rs, xadd/machoRelocLimit*machoRelocLimit), 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 rt == objabi.R_CALLARM64 && xadd != 0 { + label := ldr.Lookup(offsetLabelName(ldr, rs, xadd), ldr.SymVersion(rs)) + if label != 0 { + xadd = ldr.SymValue(rs) + xadd - ldr.SymValue(label) // should always be 0 (checked below) + rs = label + } + } + + if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || + rt == objabi.R_ARM64_PCREL_LDST8 || rt == objabi.R_ARM64_PCREL_LDST16 || + rt == objabi.R_ARM64_PCREL_LDST32 || rt == objabi.R_ARM64_PCREL_LDST64 || + 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 { + // Addend should be handled above via label symbols. + ldr.Errorf(s, "unexpected 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, + objabi.R_ARM64_PCREL_LDST8, + objabi.R_ARM64_PCREL_LDST16, + objabi.R_ARM64_PCREL_LDST32, + objabi.R_ARM64_PCREL_LDST64: + 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 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 (rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_PCREL_LDST8 || rt == objabi.R_ARM64_PCREL_LDST16 || + rt == objabi.R_ARM64_PCREL_LDST32 || rt == objabi.R_ARM64_PCREL_LDST64) && r.Xadd != signext21(r.Xadd) { + // If the relocation target would overflow the addend, then target + // a linker-manufactured label symbol with a smaller addend instead. + label := ldr.Lookup(offsetLabelName(ldr, rs, r.Xadd/peRelocLimit*peRelocLimit), ldr.SymVersion(rs)) + if label == 0 { + ldr.Errorf(s, "invalid relocation: %v %s+0x%x", rt, ldr.SymName(rs), r.Xadd) + return false + } + rs = label + } + if rt == objabi.R_CALLARM64 && r.Xadd != 0 { + label := ldr.Lookup(offsetLabelName(ldr, rs, r.Xadd), ldr.SymVersion(rs)) + if label == 0 { + ldr.Errorf(s, "invalid relocation: %v %s+0x%x", rt, ldr.SymName(rs), r.Xadd) + return false + } + rs = label + } + symdynid := ldr.SymDynid(rs) + if symdynid < 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 + } + + switch rt { + default: + return false + + case objabi.R_DWARFSECREF: + out.Write32(uint32(sectoff)) + out.Write32(uint32(symdynid)) + out.Write16(ld.IMAGE_REL_ARM64_SECREL) + + case objabi.R_ADDR: + out.Write32(uint32(sectoff)) + out.Write32(uint32(symdynid)) + if r.Size == 8 { + out.Write16(ld.IMAGE_REL_ARM64_ADDR64) + } else { + out.Write16(ld.IMAGE_REL_ARM64_ADDR32) + } + + case objabi.R_ADDRARM64: + // Note: r.Xadd has been taken care of below, in archreloc. + out.Write32(uint32(sectoff)) + out.Write32(uint32(symdynid)) + out.Write16(ld.IMAGE_REL_ARM64_PAGEBASE_REL21) + + out.Write32(uint32(sectoff + 4)) + out.Write32(uint32(symdynid)) + out.Write16(ld.IMAGE_REL_ARM64_PAGEOFFSET_12A) + + case objabi.R_ARM64_PCREL_LDST8, + objabi.R_ARM64_PCREL_LDST16, + objabi.R_ARM64_PCREL_LDST32, + objabi.R_ARM64_PCREL_LDST64: + // Note: r.Xadd has been taken care of below, in archreloc. + out.Write32(uint32(sectoff)) + out.Write32(uint32(symdynid)) + out.Write16(ld.IMAGE_REL_ARM64_PAGEBASE_REL21) + + out.Write32(uint32(sectoff + 4)) + out.Write32(uint32(symdynid)) + out.Write16(ld.IMAGE_REL_ARM64_PAGEOFFSET_12L) + + case objabi.R_CALLARM64: + // Note: r.Xadd has been taken care of above, by using a label pointing into the middle of the function. + out.Write32(uint32(sectoff)) + out.Write32(uint32(symdynid)) + out.Write16(ld.IMAGE_REL_ARM64_BRANCH26) + } + + 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 := r.Sym() + + if target.IsExternal() { + nExtReloc := 0 + switch rt := r.Type(); rt { + default: + case objabi.R_ARM64_GOTPCREL, + objabi.R_ARM64_PCREL_LDST8, + objabi.R_ARM64_PCREL_LDST16, + objabi.R_ARM64_PCREL_LDST32, + objabi.R_ARM64_PCREL_LDST64, + 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 + } + + if target.IsWindows() { + var o0, o1 uint32 + if target.IsBigEndian() { + o0 = uint32(val >> 32) + o1 = uint32(val) + } else { + o0 = uint32(val) + o1 = uint32(val >> 32) + } + + // The first instruction (ADRP) has a 21-bit immediate field, + // and the second (ADD or LD/ST) has a 12-bit immediate field. + // The first instruction is only for high bits, but to get the carry bits right we have + // to put the full addend, including the bottom 12 bits again. + // That limits the distance of any addend to only 21 bits. + // But we assume that ADRP's top bit will be interpreted as a sign bit, + // so we only use 20 bits. + // pereloc takes care of introducing new symbol labels + // every megabyte for longer relocations. + xadd := uint32(xadd) + o0 |= (xadd&3)<<29 | (xadd&0xffffc)<<3 + switch rt { + case objabi.R_ARM64_PCREL_LDST8, objabi.R_ADDRARM64: + o1 |= (xadd & 0xfff) << 10 + case objabi.R_ARM64_PCREL_LDST16: + if xadd&0x1 != 0 { + ldr.Errorf(s, "offset for 16-bit load/store has unaligned value %d", xadd&0xfff) + } + o1 |= ((xadd & 0xfff) >> 1) << 10 + case objabi.R_ARM64_PCREL_LDST32: + if xadd&0x3 != 0 { + ldr.Errorf(s, "offset for 32-bit load/store has unaligned value %d", xadd&0xfff) + } + o1 |= ((xadd & 0xfff) >> 2) << 10 + case objabi.R_ARM64_PCREL_LDST64: + if xadd&0x7 != 0 { + ldr.Errorf(s, "offset for 64-bit load/store has unaligned value %d", xadd&0xfff) + } + o1 |= ((xadd & 0xfff) >> 3) << 10 + } + + if target.IsBigEndian() { + val = int64(o0)<<32 | int64(o1) + } else { + val = int64(o1)<<32 | int64(o0) + } + } + + return val, nExtReloc, isOk + + case objabi.R_CALLARM64: + nExtReloc = 1 + return val, nExtReloc, isOk + + case objabi.R_ARM64_TLS_LE: + nExtReloc = 1 + return val, nExtReloc, isOk + + case objabi.R_ARM64_TLS_IE: + nExtReloc = 2 // need two ELF relocations. see elfreloc1 + return val, nExtReloc, isOk + + case objabi.R_ADDR: + if target.IsWindows() && r.Add() != 0 { + if r.Siz() == 8 { + val = r.Add() + } else if target.IsBigEndian() { + val = int64(uint32(val)) | int64(r.Add())<<32 + } else { + val = val>>32<<32 | int64(uint32(r.Add())) + } + return val, 1, true + } + } + } + + switch rt := r.Type(); rt { + case objabi.R_ADDRARM64, + objabi.R_ARM64_PCREL_LDST8, + objabi.R_ARM64_PCREL_LDST16, + objabi.R_ARM64_PCREL_LDST32, + objabi.R_ARM64_PCREL_LDST64: + 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) + switch rt { + case objabi.R_ARM64_PCREL_LDST8, objabi.R_ADDRARM64: + o1 |= uint32(t&0xfff) << 10 + case objabi.R_ARM64_PCREL_LDST16: + if t&0x1 != 0 { + ldr.Errorf(s, "offset for 16-bit load/store has unaligned value %d", t&0xfff) + } + o1 |= (uint32(t&0xfff) >> 1) << 10 + case objabi.R_ARM64_PCREL_LDST32: + if t&0x3 != 0 { + ldr.Errorf(s, "offset for 32-bit load/store has unaligned value %d", t&0xfff) + } + o1 |= (uint32(t&0xfff) >> 2) << 10 + case objabi.R_ARM64_PCREL_LDST64: + if t&0x7 != 0 { + ldr.Errorf(s, "offset for 64-bit load/store has unaligned value %d", t&0xfff) + } + o1 |= (uint32(t&0xfff) >> 3) << 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, []byte) 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_ARM64_PCREL_LDST8, + objabi.R_ARM64_PCREL_LDST16, + objabi.R_ARM64_PCREL_LDST32, + objabi.R_ARM64_PCREL_LDST64, + objabi.R_ADDRARM64: + rr := ld.ExtrelocViaOuterSym(ldr, r, s) + 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 + peRelocLimit = 1 << 20 +) + +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. + // On Windows, we only get 21 bits, again (presumably) signed. + // Also, on Windows (always) and Darwin (for very large binaries), the external + // linker does't support CALL relocations with addend, so we generate "label" + // symbols for functions of which we can target the middle (Duff's devices). + if !ctxt.IsDarwin() && !ctxt.IsWindows() || !ctxt.IsExternal() { + return + } + + limit := int64(machoRelocLimit) + if ctxt.IsWindows() { + limit = peRelocLimit + } + + // addLabelSyms adds "label" symbols at s+limit, s+2*limit, etc. + addLabelSyms := func(s loader.Sym, limit, sz int64) { + v := ldr.SymValue(s) + for off := limit; off < sz; off += limit { + p := ldr.LookupOrCreateSym(offsetLabelName(ldr, s, off), ldr.SymVersion(s)) + ldr.SetAttrReachable(p, true) + ldr.SetSymValue(p, v+off) + ldr.SetSymSect(p, ldr.SymSect(s)) + if ctxt.IsDarwin() { + ld.AddMachoSym(ldr, p) + } else if ctxt.IsWindows() { + ld.AddPELabelSym(ldr, p) + } else { + panic("missing case in gensymlate") + } + // fmt.Printf("gensymlate %s %x\n", ldr.SymName(p), ldr.SymValue(p)) + } + } + + // Generate symbol names for every offset we need in duffcopy/duffzero (only 64 each). + if s := ldr.Lookup("runtime.duffcopy", sym.SymVerABIInternal); s != 0 && ldr.AttrReachable(s) { + addLabelSyms(s, 8, 8*64) + } + if s := ldr.Lookup("runtime.duffzero", sym.SymVerABIInternal); s != 0 && ldr.AttrReachable(s) { + addLabelSyms(s, 4, 4*64) + } + + if ctxt.IsDarwin() { + big := false + for _, seg := range ld.Segments { + if seg.Length >= machoRelocLimit { + big = true + break + } + } + if !big { + return // skip work if nothing big + } + } + + for s, n := loader.Sym(1), loader.Sym(ldr.NSym()); s < n; s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t == sym.STEXT { + // Except for Duff's devices (handled above), we don't + // target the middle of a function. + continue + } + if t >= sym.SDWARFSECT { + continue // no need to add label for DWARF symbols + } + sz := ldr.SymSize(s) + if sz <= limit { + continue + } + addLabelSyms(s, limit, sz) + } + + // Also for carrier symbols (for which SymSize is 0) + for _, ss := range ld.CarrierSymByType { + if ss.Sym != 0 && ss.Size > limit { + addLabelSyms(ss.Sym, limit, ss.Size) + } + } +} + +// offsetLabelName returns the name of the "label" symbol used for a +// relocation targeting s+off. The label symbols is used on Darwin/Windows +// when external linking, so that the addend fits in a Mach-O/PE relocation. +func offsetLabelName(ldr *loader.Loader, s loader.Sym, off int64) string { + if off>>20<<20 == off { + return fmt.Sprintf("%s+%dMB", ldr.SymExtname(s), off>>20) + } + return fmt.Sprintf("%s+%d", ldr.SymExtname(s), off) +} + +// 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) + const pcrel = 1 + switch r.Type() { + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26), + objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel: + // Host object relocations that will be turned into a PLT call. + // The PLT may be too far. Insert a trampoline for them. + fallthrough + case objabi.R_CALLARM64: + 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())) + } + if t >= 1<<27 || t < -1<<27 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && (ldr.SymPkg(s) == "" || 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. + var tramp loader.Sym + for i := 0; ; i++ { + oName := ldr.SymName(rs) + name := oName + fmt.Sprintf("%+x-tramp%d", r.Add(), 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) - (ldr.SymValue(s) + int64(r.Off())) + if t >= -1<<27 && t < 1<<27 { + // 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 ldr.SymType(rs) == sym.SDYNIMPORT { + if r.Add() != 0 { + ctxt.Errorf(s, "nonzero addend for DYNIMPORT call: %v+%d", ldr.SymName(rs), r.Add()) + } + gentrampgot(ctxt, ldr, trampb, rs) + } else { + gentramp(ctxt, ldr, trampb, rs, r.Add()) + } + } + // 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(0) // 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(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { + tramp.SetSize(12) // 3 instructions + P := make([]byte, tramp.Size()) + o1 := uint32(0x90000010) // adrp x16, target + o2 := uint32(0x91000210) // add x16, pc-relative-offset + o3 := uint32(0xd61f0200) // br x16 + ctxt.Arch.ByteOrder.PutUint32(P, o1) + ctxt.Arch.ByteOrder.PutUint32(P[4:], o2) + ctxt.Arch.ByteOrder.PutUint32(P[8:], o3) + tramp.SetData(P) + + r, _ := tramp.AddRel(objabi.R_ADDRARM64) + r.SetSiz(8) + r.SetSym(target) + r.SetAdd(offset) +} + +// generate a trampoline to target+offset for a DYNIMPORT symbol via GOT. +func gentrampgot(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym) { + tramp.SetSize(12) // 3 instructions + P := make([]byte, tramp.Size()) + o1 := uint32(0x90000010) // adrp x16, target@GOT + o2 := uint32(0xf9400210) // ldr x16, [x16, offset] + o3 := uint32(0xd61f0200) // br x16 + ctxt.Arch.ByteOrder.PutUint32(P, o1) + ctxt.Arch.ByteOrder.PutUint32(P[4:], o2) + ctxt.Arch.ByteOrder.PutUint32(P[8:], o3) + tramp.SetData(P) + + r, _ := tramp.AddRel(objabi.R_ARM64_GOTPCREL) + r.SetSiz(8) + r.SetSym(target) +} 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..a47be0b --- /dev/null +++ b/src/cmd/link/internal/arm64/obj.go @@ -0,0 +1,120 @@ +// 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, + TrampLimit: 0x7c00000, // 26-bit signed offset * 4, leave room for PLT etc. + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Extreloc: extreloc, + Elfreloc1: elfreloc1, + ElfrelocSize: 24, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + GenSymsLate: gensymlate, + Machoreloc1: machoreloc1, + MachorelocSize: 8, + PEreloc1: pereloc1, + Trampoline: trampoline, + + Androiddynld: "/system/bin/linker64", + Linuxdynld: "/lib/ld-linux-aarch64.so.1", + LinuxdynldMusl: "/lib/ld-musl-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 + } + + case objabi.Hwindows: /* PE executable */ + // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit + return + } +} diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go new file mode 100644 index 0000000..7c6f278 --- /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/dwtest/dwtest.go b/src/cmd/link/internal/dwtest/dwtest.go new file mode 100644 index 0000000..c68edf4 --- /dev/null +++ b/src/cmd/link/internal/dwtest/dwtest.go @@ -0,0 +1,197 @@ +// Copyright 2021 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 dwtest + +import ( + "debug/dwarf" + "errors" + "fmt" + "os" +) + +// Helper type 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 (e *Examiner) DIEs() []*dwarf.Entry { + return e.dies +} + +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) { + if idx >= len(ex.dies) { + fmt.Fprintf(os.Stderr, "DumpEntry: bad DIE %d: index out of range\n", idx) + return + } + 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) + } + } +} + +// 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 to find the right compilation unit. +func (ex *Examiner) FileRef(dw *dwarf.Data, dieIdx int, fileRef int64) (string, error) { + + // Find the parent compilation unit DIE for the specified DIE. + cuDie := ex.ParentCU(dieIdx) + if cuDie == nil { + return "", fmt.Errorf("no parent CU DIE for DIE with idx %d?", dieIdx) + } + // Construct a line reader and then use it to get the file string. + lr, lrerr := dw.LineReader(cuDie) + if lrerr != nil { + return "", fmt.Errorf("d.LineReader: %v", lrerr) + } + files := lr.Files() + if fileRef < 0 || int(fileRef) > len(files)-1 { + return "", fmt.Errorf("Examiner.FileRef: malformed file reference %d", fileRef) + } + return files[fileRef].Name, nil +} + +// 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 +} diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go new file mode 100644 index 0000000..73c1cd3 --- /dev/null +++ b/src/cmd/link/internal/ld/ar.go @@ -0,0 +1,247 @@ +// Inferno utils/include/ar.h +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/include/ar.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/bio" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "encoding/binary" + "fmt" + "internal/buildcfg" + "io" + "os" + "path/filepath" + "strings" +) + +const ( + SARMAG = 8 + SAR_HDR = 16 + 44 +) + +const ( + ARMAG = "!<arch>\n" +) + +type ArHdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string +} + +// pruneUndefsForWindows trims the list "undefs" of currently +// outstanding unresolved symbols to remove references to DLL import +// symbols (e.g. "__imp_XXX"). In older versions of the linker, we +// would just immediately forward references from the import sym +// (__imp_XXX) to the DLL sym (XXX), but with newer compilers this +// strategy falls down in certain cases. We instead now do this +// forwarding later on as a post-processing step, and meaning that +// during the middle part of host object loading we can see a lot of +// unresolved (SXREF) import symbols. We do not, however, want to +// trigger the inclusion of an object from a host archive if the +// reference is going to be eventually forwarded to the corresponding +// SDYNIMPORT symbol, so here we strip out such refs from the undefs +// list. +func pruneUndefsForWindows(ldr *loader.Loader, undefs, froms []loader.Sym) ([]loader.Sym, []loader.Sym) { + var newundefs []loader.Sym + var newfroms []loader.Sym + for _, s := range undefs { + sname := ldr.SymName(s) + if strings.HasPrefix(sname, "__imp_") { + dname := sname[len("__imp_"):] + ds := ldr.Lookup(dname, 0) + if ds != 0 && ldr.SymType(ds) == sym.SDYNIMPORT { + // Don't try to pull things out of a host archive to + // satisfy this symbol. + continue + } + } + newundefs = append(newundefs, s) + newfroms = append(newfroms, s) + } + return newundefs, newfroms +} + +// hostArchive reads an archive file holding host objects and links in +// required objects. The general format is the same as a Go archive +// file, but it has an armap listing symbols and the objects that +// define them. This is used for the compiler support library +// libgcc.a. +func hostArchive(ctxt *Link, name string) { + if ctxt.Debugvlog > 1 { + ctxt.Logf("hostArchive(%s)\n", name) + } + f, err := bio.Open(name) + if err != nil { + if os.IsNotExist(err) { + // It's OK if we don't have a libgcc file at all. + if ctxt.Debugvlog != 0 { + ctxt.Logf("skipping libgcc file: %v\n", err) + } + return + } + Exitf("cannot open file %s: %v", name, err) + } + defer f.Close() + + var magbuf [len(ARMAG)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { + Exitf("file %s too short", name) + } + + if string(magbuf[:]) != ARMAG { + Exitf("%s is not an archive file", name) + } + + var arhdr ArHdr + l := nextar(f, f.Offset(), &arhdr) + if l <= 0 { + Exitf("%s missing armap", name) + } + + var armap archiveMap + if arhdr.name == "/" || arhdr.name == "/SYM64/" { + armap = readArmap(name, f, arhdr) + } else { + Exitf("%s missing armap", name) + } + + loaded := make(map[uint64]bool) + any := true + for any { + var load []uint64 + returnAllUndefs := -1 + undefs, froms := ctxt.loader.UndefinedRelocTargets(returnAllUndefs) + if buildcfg.GOOS == "windows" { + undefs, froms = pruneUndefsForWindows(ctxt.loader, undefs, froms) + } + for k, symIdx := range undefs { + sname := ctxt.loader.SymName(symIdx) + if off := armap[sname]; off != 0 && !loaded[off] { + load = append(load, off) + loaded[off] = true + if ctxt.Debugvlog > 1 { + ctxt.Logf("hostArchive(%s): selecting object at offset %x to resolve %s [%d] reference from %s [%d]\n", name, off, sname, symIdx, ctxt.loader.SymName(froms[k]), froms[k]) + } + } + } + + for _, off := range load { + l := nextar(f, int64(off), &arhdr) + if l <= 0 { + Exitf("%s missing archive entry at offset %d", name, off) + } + pname := fmt.Sprintf("%s(%s)", name, arhdr.name) + l = atolwhex(arhdr.size) + + pkname := filepath.Base(name) + if i := strings.LastIndex(pkname, ".a"); i >= 0 { + pkname = pkname[:i] + } + libar := sym.Library{Pkg: pkname} + h := ldobj(ctxt, f, &libar, l, pname, name) + if h.ld == nil { + Errorf(nil, "%s unrecognized object file at offset %d", name, off) + continue + } + f.MustSeek(h.off, 0) + h.ld(ctxt, f, h.pkg, h.length, h.pn) + if *flagCaptureHostObjs != "" { + captureHostObj(h) + } + } + + any = len(load) > 0 + } +} + +// archiveMap is an archive symbol map: a mapping from symbol name to +// offset within the archive file. +type archiveMap map[string]uint64 + +// readArmap reads the archive symbol map. +func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap { + is64 := arhdr.name == "/SYM64/" + wordSize := 4 + if is64 { + wordSize = 8 + } + + contents := make([]byte, atolwhex(arhdr.size)) + if _, err := io.ReadFull(f, contents); err != nil { + Exitf("short read from %s", filename) + } + + var c uint64 + if is64 { + c = binary.BigEndian.Uint64(contents) + } else { + c = uint64(binary.BigEndian.Uint32(contents)) + } + contents = contents[wordSize:] + + ret := make(archiveMap) + + names := contents[c*uint64(wordSize):] + for i := uint64(0); i < c; i++ { + n := 0 + for names[n] != 0 { + n++ + } + name := string(names[:n]) + names = names[n+1:] + + // For Mach-O and PE/386 files we strip a leading + // underscore from the symbol name. + if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" || (buildcfg.GOOS == "windows" && buildcfg.GOARCH == "386") { + if name[0] == '_' && len(name) > 1 { + name = name[1:] + } + } + + var off uint64 + if is64 { + off = binary.BigEndian.Uint64(contents) + } else { + off = uint64(binary.BigEndian.Uint32(contents)) + } + contents = contents[wordSize:] + + ret[name] = off + } + + return ret +} diff --git a/src/cmd/link/internal/ld/asmb.go b/src/cmd/link/internal/ld/asmb.go new file mode 100644 index 0000000..cd8927b --- /dev/null +++ b/src/cmd/link/internal/ld/asmb.go @@ -0,0 +1,208 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "runtime" + "sync" +) + +// Assembling the binary is broken into two steps: +// - writing out the code/data/dwarf Segments, applying relocations on the fly +// - writing out the architecture specific pieces. +// +// This function handles the first part. +func asmb(ctxt *Link) { + // TODO(jfaller): delete me. + if thearch.Asmb != nil { + thearch.Asmb(ctxt, ctxt.loader) + return + } + + if ctxt.IsELF { + Asmbelfsetup() + } + + var wg sync.WaitGroup + f := func(ctxt *Link, out *OutBuf, start, length int64) { + pad := thearch.CodePad + if pad == nil { + pad = zeros[:] + } + CodeblkPad(ctxt, out, start, length, pad) + } + + for _, sect := range Segtext.Sections { + offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff + // Handle text sections with Codeblk + if sect.Name == ".text" { + writeParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length) + } else { + writeParallel(&wg, datblk, ctxt, offset, sect.Vaddr, sect.Length) + } + } + + if Segrodata.Filelen > 0 { + writeParallel(&wg, datblk, ctxt, Segrodata.Fileoff, Segrodata.Vaddr, Segrodata.Filelen) + } + + if Segrelrodata.Filelen > 0 { + writeParallel(&wg, datblk, ctxt, Segrelrodata.Fileoff, Segrelrodata.Vaddr, Segrelrodata.Filelen) + } + + writeParallel(&wg, datblk, ctxt, Segdata.Fileoff, Segdata.Vaddr, Segdata.Filelen) + + writeParallel(&wg, dwarfblk, ctxt, Segdwarf.Fileoff, Segdwarf.Vaddr, Segdwarf.Filelen) + + wg.Wait() +} + +// Assembling the binary is broken into two steps: +// - writing out the code/data/dwarf Segments +// - writing out the architecture specific pieces. +// +// This function handles the second part. +func asmb2(ctxt *Link) { + if thearch.Asmb2 != nil { + thearch.Asmb2(ctxt, ctxt.loader) + return + } + + symSize = 0 + spSize = 0 + lcSize = 0 + + switch ctxt.HeadType { + default: + panic("unknown platform") + + // Macho + case objabi.Hdarwin: + asmbMacho(ctxt) + + // Plan9 + case objabi.Hplan9: + asmbPlan9(ctxt) + + // PE + case objabi.Hwindows: + asmbPe(ctxt) + + // Xcoff + case objabi.Haix: + asmbXcoff(ctxt) + + // Elf + case objabi.Hdragonfly, + objabi.Hfreebsd, + objabi.Hlinux, + objabi.Hnetbsd, + objabi.Hopenbsd, + objabi.Hsolaris: + asmbElf(ctxt) + } + + if *FlagC { + fmt.Printf("textsize=%d\n", Segtext.Filelen) + fmt.Printf("datsize=%d\n", Segdata.Filelen) + fmt.Printf("bsssize=%d\n", Segdata.Length-Segdata.Filelen) + fmt.Printf("symsize=%d\n", symSize) + fmt.Printf("lcsize=%d\n", lcSize) + fmt.Printf("total=%d\n", Segtext.Filelen+Segdata.Length+uint64(symSize)+uint64(lcSize)) + } +} + +// writePlan9Header writes out the plan9 header at the present position in the OutBuf. +func writePlan9Header(buf *OutBuf, magic uint32, entry int64, is64Bit bool) { + if is64Bit { + magic |= 0x00008000 + } + buf.Write32b(magic) + buf.Write32b(uint32(Segtext.Filelen)) + buf.Write32b(uint32(Segdata.Filelen)) + buf.Write32b(uint32(Segdata.Length - Segdata.Filelen)) + buf.Write32b(uint32(symSize)) + if is64Bit { + buf.Write32b(uint32(entry &^ 0x80000000)) + } else { + buf.Write32b(uint32(entry)) + } + buf.Write32b(uint32(spSize)) + buf.Write32b(uint32(lcSize)) + // amd64 includes the entry at the beginning of the symbol table. + if is64Bit { + buf.Write64b(uint64(entry)) + } +} + +// asmbPlan9 assembles a plan 9 binary. +func asmbPlan9(ctxt *Link) { + if !*FlagS { + *FlagS = true + symo := int64(Segdata.Fileoff + Segdata.Filelen) + ctxt.Out.SeekSet(symo) + asmbPlan9Sym(ctxt) + } + ctxt.Out.SeekSet(0) + writePlan9Header(ctxt.Out, thearch.Plan9Magic, Entryvalue(ctxt), thearch.Plan9_64Bit) +} + +// sizeExtRelocs precomputes the size needed for the reloc records, +// sets the size and offset for relocation records in each section, +// and mmap the output buffer with the proper size. +func sizeExtRelocs(ctxt *Link, relsize uint32) { + if relsize == 0 { + panic("sizeExtRelocs: relocation size not set") + } + var sz int64 + for _, seg := range Segments { + for _, sect := range seg.Sections { + sect.Reloff = uint64(ctxt.Out.Offset() + sz) + sect.Rellen = uint64(relsize * sect.Relcount) + sz += int64(sect.Rellen) + } + } + filesz := ctxt.Out.Offset() + sz + err := ctxt.Out.Mmap(uint64(filesz)) + if err != nil { + Exitf("mapping output file failed: %v", err) + } +} + +// relocSectFn wraps the function writing relocations of a section +// for parallel execution. Returns the wrapped function and a wait +// group for which the caller should wait. +func relocSectFn(ctxt *Link, relocSect func(*Link, *OutBuf, *sym.Section, []loader.Sym)) (func(*Link, *sym.Section, []loader.Sym), *sync.WaitGroup) { + var fn func(ctxt *Link, sect *sym.Section, syms []loader.Sym) + var wg sync.WaitGroup + var sem chan int + if ctxt.Out.isMmapped() { + // Write sections in parallel. + sem = make(chan int, 2*runtime.GOMAXPROCS(0)) + fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) { + wg.Add(1) + sem <- 1 + out, err := ctxt.Out.View(sect.Reloff) + if err != nil { + panic(err) + } + go func() { + relocSect(ctxt, out, sect, syms) + wg.Done() + <-sem + }() + } + } else { + // We cannot Mmap. Write sequentially. + fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) { + relocSect(ctxt, ctxt.Out, sect, syms) + } + } + return fn, &wg +} diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go new file mode 100644 index 0000000..ba74b6f --- /dev/null +++ b/src/cmd/link/internal/ld/config.go @@ -0,0 +1,307 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/sys" + "fmt" + "internal/buildcfg" + "internal/platform" +) + +// A BuildMode indicates the sort of object we are building. +// +// Possible build modes are the same as those for the -buildmode flag +// in cmd/go, and are documented in 'go help buildmode'. +type BuildMode uint8 + +const ( + BuildModeUnset BuildMode = iota + BuildModeExe + BuildModePIE + BuildModeCArchive + BuildModeCShared + BuildModeShared + BuildModePlugin +) + +func (mode *BuildMode) Set(s string) error { + badmode := func() error { + return fmt.Errorf("buildmode %s not supported on %s/%s", s, buildcfg.GOOS, buildcfg.GOARCH) + } + switch s { + default: + return fmt.Errorf("invalid buildmode: %q", s) + case "exe": + switch buildcfg.GOOS + "/" + buildcfg.GOARCH { + case "darwin/arm64", "windows/arm", "windows/arm64": // On these platforms, everything is PIE + *mode = BuildModePIE + default: + *mode = BuildModeExe + } + case "pie": + switch buildcfg.GOOS { + case "aix", "android", "linux", "windows", "darwin", "ios": + case "freebsd": + switch buildcfg.GOARCH { + case "amd64": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModePIE + case "c-archive": + switch buildcfg.GOOS { + case "aix", "darwin", "ios", "linux": + case "freebsd": + switch buildcfg.GOARCH { + case "amd64": + default: + return badmode() + } + case "windows": + switch buildcfg.GOARCH { + case "amd64", "386", "arm", "arm64": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModeCArchive + case "c-shared": + switch buildcfg.GOARCH { + case "386", "amd64", "arm", "arm64", "ppc64le", "riscv64", "s390x": + default: + return badmode() + } + *mode = BuildModeCShared + case "shared": + switch buildcfg.GOOS { + case "linux": + switch buildcfg.GOARCH { + case "386", "amd64", "arm", "arm64", "ppc64le", "s390x": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModeShared + case "plugin": + switch buildcfg.GOOS { + case "linux": + switch buildcfg.GOARCH { + case "386", "amd64", "arm", "arm64", "s390x", "ppc64le": + default: + return badmode() + } + case "darwin": + switch buildcfg.GOARCH { + case "amd64", "arm64": + default: + return badmode() + } + case "freebsd": + switch buildcfg.GOARCH { + case "amd64": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModePlugin + } + return nil +} + +func (mode *BuildMode) String() string { + switch *mode { + case BuildModeUnset: + return "" // avoid showing a default in usage message + case BuildModeExe: + return "exe" + case BuildModePIE: + return "pie" + case BuildModeCArchive: + return "c-archive" + case BuildModeCShared: + return "c-shared" + case BuildModeShared: + return "shared" + case BuildModePlugin: + return "plugin" + } + return fmt.Sprintf("BuildMode(%d)", uint8(*mode)) +} + +// LinkMode indicates whether an external linker is used for the final link. +type LinkMode uint8 + +const ( + LinkAuto LinkMode = iota + LinkInternal + LinkExternal +) + +func (mode *LinkMode) Set(s string) error { + switch s { + default: + return fmt.Errorf("invalid linkmode: %q", s) + case "auto": + *mode = LinkAuto + case "internal": + *mode = LinkInternal + case "external": + *mode = LinkExternal + } + return nil +} + +func (mode *LinkMode) String() string { + switch *mode { + case LinkAuto: + return "auto" + case LinkInternal: + return "internal" + case LinkExternal: + return "external" + } + return fmt.Sprintf("LinkMode(%d)", uint8(*mode)) +} + +// mustLinkExternal reports whether the program being linked requires +// the external linker be used to complete the link. +func mustLinkExternal(ctxt *Link) (res bool, reason string) { + if ctxt.Debugvlog > 1 { + defer func() { + if res { + ctxt.Logf("external linking is forced by: %s\n", reason) + } + }() + } + + if platform.MustLinkExternal(buildcfg.GOOS, buildcfg.GOARCH) { + return true, fmt.Sprintf("%s/%s requires external linking", buildcfg.GOOS, buildcfg.GOARCH) + } + + if *flagMsan { + return true, "msan" + } + + if *flagAsan { + return true, "asan" + } + + // Internally linking cgo is incomplete on some architectures. + // https://golang.org/issue/14449 + if iscgo && ctxt.Arch.InFamily(sys.Loong64, sys.MIPS64, sys.MIPS, sys.RISCV64) { + return true, buildcfg.GOARCH + " does not support internal cgo" + } + if iscgo && (buildcfg.GOOS == "android" || buildcfg.GOOS == "dragonfly") { + // It seems that on Dragonfly thread local storage is + // set up by the dynamic linker, so internal cgo linking + // doesn't work. Test case is "go test runtime/cgo". + return true, buildcfg.GOOS + " does not support internal cgo" + } + if iscgo && buildcfg.GOOS == "windows" && buildcfg.GOARCH == "arm64" { + // windows/arm64 internal linking is not implemented. + return true, buildcfg.GOOS + "/" + buildcfg.GOARCH + " does not support internal cgo" + } + if iscgo && ctxt.Arch == sys.ArchPPC64 { + // Big Endian PPC64 cgo internal linking is not implemented for aix or linux. + return true, buildcfg.GOOS + " does not support internal cgo" + } + + // Some build modes require work the internal linker cannot do (yet). + switch ctxt.BuildMode { + case BuildModeCArchive: + return true, "buildmode=c-archive" + case BuildModeCShared: + return true, "buildmode=c-shared" + case BuildModePIE: + switch buildcfg.GOOS + "/" + buildcfg.GOARCH { + case "android/arm64": + case "linux/amd64", "linux/arm64", "linux/ppc64le": + case "windows/386", "windows/amd64", "windows/arm", "windows/arm64": + case "darwin/amd64", "darwin/arm64": + default: + // Internal linking does not support TLS_IE. + return true, "buildmode=pie" + } + case BuildModePlugin: + return true, "buildmode=plugin" + case BuildModeShared: + return true, "buildmode=shared" + } + if ctxt.linkShared { + return true, "dynamically linking with a shared library" + } + + if unknownObjFormat { + return true, "some input objects have an unrecognized file format" + } + + if len(dynimportfail) > 0 { + // This error means that we were unable to generate + // the _cgo_import.go file for some packages. + // This typically means that there are some dependencies + // that the cgo tool could not figure out. + // See issue #52863. + return true, fmt.Sprintf("some packages could not be built to support internal linking (%v)", dynimportfail) + } + + return false, "" +} + +// determineLinkMode sets ctxt.LinkMode. +// +// It is called after flags are processed and inputs are processed, +// so the ctxt.LinkMode variable has an initial value from the -linkmode +// flag and the iscgo, externalobj, and unknownObjFormat variables are set. +func determineLinkMode(ctxt *Link) { + extNeeded, extReason := mustLinkExternal(ctxt) + via := "" + + if ctxt.LinkMode == LinkAuto { + // The environment variable GO_EXTLINK_ENABLED controls the + // default value of -linkmode. If it is not set when the + // linker is called we take the value it was set to when + // cmd/link was compiled. (See make.bash.) + switch buildcfg.Getgoextlinkenabled() { + case "0": + ctxt.LinkMode = LinkInternal + via = "via GO_EXTLINK_ENABLED " + case "1": + ctxt.LinkMode = LinkExternal + via = "via GO_EXTLINK_ENABLED " + default: + preferExternal := len(preferlinkext) != 0 + if preferExternal && ctxt.Debugvlog > 0 { + ctxt.Logf("external linking prefer list is %v\n", preferlinkext) + } + if extNeeded || (iscgo && (externalobj || preferExternal)) { + ctxt.LinkMode = LinkExternal + } else { + ctxt.LinkMode = LinkInternal + } + } + } + + switch ctxt.LinkMode { + case LinkInternal: + if extNeeded { + Exitf("internal linking requested %sbut external linking required: %s", via, extReason) + } + case LinkExternal: + switch { + case buildcfg.GOARCH == "ppc64" && buildcfg.GOOS != "aix": + Exitf("external linking not supported for %s/ppc64", buildcfg.GOOS) + } + } +} diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go new file mode 100644 index 0000000..afba7d3 --- /dev/null +++ b/src/cmd/link/internal/ld/data.go @@ -0,0 +1,2956 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bytes" + "cmd/internal/gcprog" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/loadpe" + "cmd/link/internal/sym" + "compress/zlib" + "debug/elf" + "encoding/binary" + "fmt" + "log" + "os" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" +) + +// isRuntimeDepPkg reports whether pkg is the runtime package or its dependency. +func isRuntimeDepPkg(pkg string) bool { + switch pkg { + case "runtime", + "sync/atomic", // runtime may call to sync/atomic, due to go:linkname + "internal/abi", // used by reflectcall (and maybe more) + "internal/bytealg", // for IndexByte + "internal/cpu": // for cpu features + return true + } + return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test") +} + +// Estimate the max size needed to hold any new trampolines created for this function. This +// is used to determine when the section can be split if it becomes too large, to ensure that +// the trampolines are in the same section as the function that uses them. +func maxSizeTrampolines(ctxt *Link, ldr *loader.Loader, s loader.Sym, isTramp bool) uint64 { + // If thearch.Trampoline is nil, then trampoline support is not available on this arch. + // A trampoline does not need any dependent trampolines. + if thearch.Trampoline == nil || isTramp { + return 0 + } + + n := uint64(0) + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Type().IsDirectCallOrJump() { + n++ + } + } + + if ctxt.IsARM() { + return n * 20 // Trampolines in ARM range from 3 to 5 instructions. + } + if ctxt.IsPPC64() { + return n * 16 // Trampolines in PPC64 are 4 instructions. + } + if ctxt.IsARM64() { + return n * 12 // Trampolines in ARM64 are 3 instructions. + } + panic("unreachable") +} + +// Detect too-far jumps in function s, and add trampolines if necessary. +// ARM, PPC64, PPC64LE and RISCV64 support trampoline insertion for internal +// and external linking. On PPC64 and PPC64LE the text sections might be split +// but will still insert trampolines where necessary. +func trampoline(ctxt *Link, s loader.Sym) { + if thearch.Trampoline == nil { + return // no need or no support of trampolines on this arch + } + + ldr := ctxt.loader + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rt := r.Type() + if !rt.IsDirectCallOrJump() && !isPLTCall(rt) { + continue + } + rs := r.Sym() + if !ldr.AttrReachable(rs) || ldr.SymType(rs) == sym.Sxxx { + continue // something is wrong. skip it here and we'll emit a better error later + } + + // RISC-V is only able to reach +/-1MiB via a JAL instruction, + // which we can readily exceed in the same package. As such, we + // need to generate trampolines when the address is unknown. + if ldr.SymValue(rs) == 0 && !ctxt.Target.IsRISCV64() && ldr.SymType(rs) != sym.SDYNIMPORT && ldr.SymType(rs) != sym.SUNDEFEXT { + if ldr.SymPkg(s) != "" && ldr.SymPkg(rs) == ldr.SymPkg(s) { + // Symbols in the same package are laid out together. + // Except that if SymPkg(s) == "", it is a host object symbol + // which may call an external symbol via PLT. + continue + } + if isRuntimeDepPkg(ldr.SymPkg(s)) && isRuntimeDepPkg(ldr.SymPkg(rs)) { + continue // runtime packages are laid out together + } + } + thearch.Trampoline(ctxt, ldr, ri, rs, s) + } +} + +// whether rt is a (host object) relocation that will be turned into +// a call to PLT. +func isPLTCall(rt objabi.RelocType) bool { + const pcrel = 1 + switch rt { + // ARM64 + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26), + objabi.MachoRelocOffset + MACHO_ARM64_RELOC_BRANCH26*2 + pcrel: + return true + + // ARM + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24): + return true + } + // TODO: other architectures. + return false +} + +// FoldSubSymbolOffset computes the offset of symbol s to its top-level outer +// symbol. Returns the top-level symbol and the offset. +// This is used in generating external relocations. +func FoldSubSymbolOffset(ldr *loader.Loader, s loader.Sym) (loader.Sym, int64) { + outer := ldr.OuterSym(s) + off := int64(0) + if outer != 0 { + off += ldr.SymValue(s) - ldr.SymValue(outer) + s = outer + } + return s, off +} + +// relocsym resolve relocations in "s", updating the symbol's content +// in "P". +// The main loop walks through the list of relocations attached to "s" +// and resolves them where applicable. Relocations are often +// architecture-specific, requiring calls into the 'archreloc' and/or +// 'archrelocvariant' functions for the architecture. When external +// linking is in effect, it may not be possible to completely resolve +// the address/offset for a symbol, in which case the goal is to lay +// the groundwork for turning a given relocation into an external reloc +// (to be applied by the external linker). For more on how relocations +// work in general, see +// +// "Linkers and Loaders", by John R. Levine (Morgan Kaufmann, 1999), ch. 7 +// +// This is a performance-critical function for the linker; be careful +// to avoid introducing unnecessary allocations in the main loop. +func (st *relocSymState) relocsym(s loader.Sym, P []byte) { + ldr := st.ldr + relocs := ldr.Relocs(s) + if relocs.Count() == 0 { + return + } + target := st.target + syms := st.syms + nExtReloc := 0 // number of external relocations + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + off := r.Off() + siz := int32(r.Siz()) + rs := r.Sym() + rt := r.Type() + weak := r.Weak() + if off < 0 || off+siz > int32(len(P)) { + rname := "" + if rs != 0 { + rname = ldr.SymName(rs) + } + st.err.Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(P)) + continue + } + if siz == 0 { // informational relocation - no work to do + continue + } + + var rst sym.SymKind + if rs != 0 { + rst = ldr.SymType(rs) + } + + if rs != 0 && (rst == sym.Sxxx || rst == sym.SXREF) { + // When putting the runtime but not main into a shared library + // these symbols are undefined and that's OK. + if target.IsShared() || target.IsPlugin() { + if ldr.SymName(rs) == "main.main" || (!target.IsPlugin() && ldr.SymName(rs) == "main..inittask") { + sb := ldr.MakeSymbolUpdater(rs) + sb.SetType(sym.SDYNIMPORT) + } else if strings.HasPrefix(ldr.SymName(rs), "go:info.") { + // Skip go.info symbols. They are only needed to communicate + // DWARF info between the compiler and linker. + continue + } + } else if target.IsPPC64() && ldr.SymName(rs) == ".TOC." { + // TOC symbol doesn't have a type but we do assign a value + // (see the address pass) and we can resolve it. + // TODO: give it a type. + } else { + st.err.errorUnresolved(ldr, s, rs) + continue + } + } + + if rt >= objabi.ElfRelocOffset { + continue + } + + // We need to be able to reference dynimport symbols when linking against + // shared libraries, and AIX, Darwin, OpenBSD and Solaris always need it. + if !target.IsAIX() && !target.IsDarwin() && !target.IsSolaris() && !target.IsOpenbsd() && rs != 0 && rst == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !ldr.AttrSubSymbol(rs) { + if !(target.IsPPC64() && target.IsExternal() && ldr.SymName(rs) == ".TOC.") { + st.err.Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", ldr.SymName(rs), rst, rst, rt, sym.RelocName(target.Arch, rt)) + } + } + if rs != 0 && rst != sym.STLSBSS && !weak && rt != objabi.R_METHODOFF && !ldr.AttrReachable(rs) { + st.err.Errorf(s, "unreachable sym in relocation: %s", ldr.SymName(rs)) + } + + var rv sym.RelocVariant + if target.IsPPC64() || target.IsS390X() { + rv = ldr.RelocVariant(s, ri) + } + + // TODO(mundaym): remove this special case - see issue 14218. + if target.IsS390X() { + switch rt { + case objabi.R_PCRELDBL: + rt = objabi.R_PCREL + rv = sym.RV_390_DBL + case objabi.R_CALL: + rv = sym.RV_390_DBL + } + } + + var o int64 + switch rt { + default: + switch siz { + default: + st.err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs)) + case 1: + o = int64(P[off]) + case 2: + o = int64(target.Arch.ByteOrder.Uint16(P[off:])) + case 4: + o = int64(target.Arch.ByteOrder.Uint32(P[off:])) + case 8: + o = int64(target.Arch.ByteOrder.Uint64(P[off:])) + } + out, n, ok := thearch.Archreloc(target, ldr, syms, r, s, o) + if target.IsExternal() { + nExtReloc += n + } + if ok { + o = out + } else { + st.err.Errorf(s, "unknown reloc to %v: %d (%s)", ldr.SymName(rs), rt, sym.RelocName(target.Arch, rt)) + } + case objabi.R_TLS_LE: + if target.IsExternal() && target.IsElf() { + nExtReloc++ + o = 0 + if !target.IsAMD64() { + o = r.Add() + } + break + } + + if target.IsElf() && target.IsARM() { + // On ELF ARM, the thread pointer is 8 bytes before + // the start of the thread-local data block, so add 8 + // to the actual TLS offset (r->sym->value). + // This 8 seems to be a fundamental constant of + // ELF on ARM (or maybe Glibc on ARM); it is not + // related to the fact that our own TLS storage happens + // to take up 8 bytes. + o = 8 + ldr.SymValue(rs) + } else if target.IsElf() || target.IsPlan9() || target.IsDarwin() { + o = int64(syms.Tlsoffset) + r.Add() + } else if target.IsWindows() { + o = r.Add() + } else { + log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType) + } + case objabi.R_TLS_IE: + if target.IsExternal() && target.IsElf() { + nExtReloc++ + o = 0 + if !target.IsAMD64() { + o = r.Add() + } + if target.Is386() { + nExtReloc++ // need two ELF relocations on 386, see ../x86/asm.go:elfreloc1 + } + break + } + if target.IsPIE() && target.IsElf() { + // We are linking the final executable, so we + // can optimize any TLS IE relocation to LE. + if thearch.TLSIEtoLE == nil { + log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) + } + thearch.TLSIEtoLE(P, int(off), int(siz)) + o = int64(syms.Tlsoffset) + } else { + log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s)) + } + case objabi.R_ADDR: + if weak && !ldr.AttrReachable(rs) { + // Redirect it to runtime.unreachableMethod, which will throw if called. + rs = syms.unreachableMethod + } + if target.IsExternal() { + nExtReloc++ + + // set up addend for eventual relocation via outer symbol. + rs := rs + rs, off := FoldSubSymbolOffset(ldr, rs) + xadd := r.Add() + off + rst := ldr.SymType(rs) + if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil { + st.err.Errorf(s, "missing section for relocation target %s", ldr.SymName(rs)) + } + + o = xadd + if target.IsElf() { + if target.IsAMD64() { + o = 0 + } + } else if target.IsDarwin() { + if ldr.SymType(rs) != sym.SHOSTOBJ { + o += ldr.SymValue(rs) + } + } else if target.IsWindows() { + // nothing to do + } else if target.IsAIX() { + o = ldr.SymValue(rs) + xadd + } else { + st.err.Errorf(s, "unhandled pcrel relocation to %s on %v", ldr.SymName(rs), target.HeadType) + } + + break + } + + // On AIX, a second relocation must be done by the loader, + // as section addresses can change once loaded. + // The "default" symbol address is still needed by the loader so + // the current relocation can't be skipped. + if target.IsAIX() && rst != sym.SDYNIMPORT { + // It's not possible to make a loader relocation in a + // symbol which is not inside .data section. + // FIXME: It should be forbidden to have R_ADDR from a + // symbol which isn't in .data. However, as .text has the + // same address once loaded, this is possible. + if ldr.SymSect(s).Seg == &Segdata { + Xcoffadddynrel(target, ldr, syms, s, r, ri) + } + } + + o = ldr.SymValue(rs) + r.Add() + + // On amd64, 4-byte offsets will be sign-extended, so it is impossible to + // access more than 2GB of static data; fail at link time is better than + // fail at runtime. See https://golang.org/issue/7980. + // Instead of special casing only amd64, we treat this as an error on all + // 64-bit architectures so as to be future-proof. + if int32(o) < 0 && target.Arch.PtrSize > 4 && siz == 4 { + st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", ldr.SymName(rs), uint64(o), ldr.SymValue(rs), r.Add()) + errorexit() + } + case objabi.R_DWARFSECREF: + if ldr.SymSect(rs) == nil { + st.err.Errorf(s, "missing DWARF section for relocation target %s", ldr.SymName(rs)) + } + + if target.IsExternal() { + // On most platforms, the external linker needs to adjust DWARF references + // as it combines DWARF sections. However, on Darwin, dsymutil does the + // DWARF linking, and it understands how to follow section offsets. + // Leaving in the relocation records confuses it (see + // https://golang.org/issue/22068) so drop them for Darwin. + if !target.IsDarwin() { + nExtReloc++ + } + + xadd := r.Add() + ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + + o = xadd + if target.IsElf() && target.IsAMD64() { + o = 0 + } + break + } + o = ldr.SymValue(rs) + r.Add() - int64(ldr.SymSect(rs).Vaddr) + case objabi.R_METHODOFF: + if !ldr.AttrReachable(rs) { + // Set it to a sentinel value. The runtime knows this is not pointing to + // anything valid. + o = -1 + break + } + fallthrough + case objabi.R_ADDROFF: + if weak && !ldr.AttrReachable(rs) { + continue + } + if ldr.SymSect(rs) == nil { + st.err.Errorf(s, "unreachable sym in relocation: %s", ldr.SymName(rs)) + continue + } + + // The method offset tables using this relocation expect the offset to be relative + // to the start of the first text section, even if there are multiple. + if ldr.SymSect(rs).Name == ".text" { + o = ldr.SymValue(rs) - int64(Segtext.Sections[0].Vaddr) + r.Add() + } else { + o = ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + r.Add() + } + + case objabi.R_ADDRCUOFF: + // debug_range and debug_loc elements use this relocation type to get an + // offset from the start of the compile unit. + o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(loader.Sym(ldr.SymUnit(rs).Textp[0])) + + // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call. + case objabi.R_GOTPCREL: + if target.IsDynlinkingGo() && target.IsDarwin() && rs != 0 { + nExtReloc++ + o = r.Add() + break + } + if target.Is386() && target.IsExternal() && target.IsELF { + nExtReloc++ // need two ELF relocations on 386, see ../x86/asm.go:elfreloc1 + } + fallthrough + case objabi.R_CALL, objabi.R_PCREL: + if target.IsExternal() && rs != 0 && rst == sym.SUNDEFEXT { + // pass through to the external linker. + nExtReloc++ + o = 0 + break + } + if target.IsExternal() && rs != 0 && (ldr.SymSect(rs) != ldr.SymSect(s) || rt == objabi.R_GOTPCREL) { + nExtReloc++ + + // set up addend for eventual relocation via outer symbol. + rs := rs + rs, off := FoldSubSymbolOffset(ldr, rs) + xadd := r.Add() + off - int64(siz) // relative to address after the relocated chunk + rst := ldr.SymType(rs) + if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil { + st.err.Errorf(s, "missing section for relocation target %s", ldr.SymName(rs)) + } + + o = xadd + if target.IsElf() { + if target.IsAMD64() { + o = 0 + } + } else if target.IsDarwin() { + if rt == objabi.R_CALL { + if target.IsExternal() && rst == sym.SDYNIMPORT { + if target.IsAMD64() { + // AMD64 dynamic relocations are relative to the end of the relocation. + o += int64(siz) + } + } else { + if rst != sym.SHOSTOBJ { + o += int64(uint64(ldr.SymValue(rs)) - ldr.SymSect(rs).Vaddr) + } + o -= int64(off) // relative to section offset, not symbol + } + } else { + o += int64(siz) + } + } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL + // PE/COFF's PC32 relocation uses the address after the relocated + // bytes as the base. Compensate by skewing the addend. + o += int64(siz) + } else { + st.err.Errorf(s, "unhandled pcrel relocation to %s on %v", ldr.SymName(rs), target.HeadType) + } + + break + } + + o = 0 + if rs != 0 { + o = ldr.SymValue(rs) + } + + o += r.Add() - (ldr.SymValue(s) + int64(off) + int64(siz)) + case objabi.R_SIZE: + o = ldr.SymSize(rs) + r.Add() + + case objabi.R_XCOFFREF: + if !target.IsAIX() { + st.err.Errorf(s, "find XCOFF R_REF on non-XCOFF files") + } + if !target.IsExternal() { + st.err.Errorf(s, "find XCOFF R_REF with internal linking") + } + nExtReloc++ + continue + + case objabi.R_DWARFFILEREF: + // We don't renumber files in dwarf.go:writelines anymore. + continue + + case objabi.R_CONST: + o = r.Add() + + case objabi.R_GOTOFF: + o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(syms.GOT) + } + + if target.IsPPC64() || target.IsS390X() { + if rv != sym.RV_NONE { + o = thearch.Archrelocvariant(target, ldr, r, rv, s, o, P) + } + } + + switch siz { + default: + st.err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs)) + case 1: + P[off] = byte(int8(o)) + case 2: + if o != int64(int16(o)) { + st.err.Errorf(s, "relocation address for %s is too big: %#x", ldr.SymName(rs), o) + } + target.Arch.ByteOrder.PutUint16(P[off:], uint16(o)) + case 4: + if rt == objabi.R_PCREL || rt == objabi.R_CALL { + if o != int64(int32(o)) { + st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o) + } + } else { + if o != int64(int32(o)) && o != int64(uint32(o)) { + st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o)) + } + } + target.Arch.ByteOrder.PutUint32(P[off:], uint32(o)) + case 8: + target.Arch.ByteOrder.PutUint64(P[off:], uint64(o)) + } + } + if target.IsExternal() { + // We'll stream out the external relocations in asmb2 (e.g. elfrelocsect) + // and we only need the count here. + atomic.AddUint32(&ldr.SymSect(s).Relcount, uint32(nExtReloc)) + } +} + +// Convert a Go relocation to an external relocation. +func extreloc(ctxt *Link, ldr *loader.Loader, s loader.Sym, r loader.Reloc) (loader.ExtReloc, bool) { + var rr loader.ExtReloc + target := &ctxt.Target + siz := int32(r.Siz()) + if siz == 0 { // informational relocation - no work to do + return rr, false + } + + rt := r.Type() + if rt >= objabi.ElfRelocOffset { + return rr, false + } + rr.Type = rt + rr.Size = uint8(siz) + + // TODO(mundaym): remove this special case - see issue 14218. + if target.IsS390X() { + switch rt { + case objabi.R_PCRELDBL: + rt = objabi.R_PCREL + } + } + + switch rt { + default: + return thearch.Extreloc(target, ldr, r, s) + + case objabi.R_TLS_LE, objabi.R_TLS_IE: + if target.IsElf() { + rs := r.Sym() + rr.Xsym = rs + if rr.Xsym == 0 { + rr.Xsym = ctxt.Tlsg + } + rr.Xadd = r.Add() + break + } + return rr, false + + case objabi.R_ADDR: + // set up addend for eventual relocation via outer symbol. + rs := r.Sym() + if r.Weak() && !ldr.AttrReachable(rs) { + rs = ctxt.ArchSyms.unreachableMethod + } + rs, off := FoldSubSymbolOffset(ldr, rs) + rr.Xadd = r.Add() + off + rr.Xsym = rs + + case objabi.R_DWARFSECREF: + // On most platforms, the external linker needs to adjust DWARF references + // as it combines DWARF sections. However, on Darwin, dsymutil does the + // DWARF linking, and it understands how to follow section offsets. + // Leaving in the relocation records confuses it (see + // https://golang.org/issue/22068) so drop them for Darwin. + if target.IsDarwin() { + return rr, false + } + rs := r.Sym() + rr.Xsym = loader.Sym(ldr.SymSect(rs).Sym) + rr.Xadd = r.Add() + ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + + // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call. + case objabi.R_GOTPCREL, objabi.R_CALL, objabi.R_PCREL: + rs := r.Sym() + if rt == objabi.R_GOTPCREL && target.IsDynlinkingGo() && target.IsDarwin() && rs != 0 { + rr.Xadd = r.Add() + rr.Xadd -= int64(siz) // relative to address after the relocated chunk + rr.Xsym = rs + break + } + if rs != 0 && ldr.SymType(rs) == sym.SUNDEFEXT { + // pass through to the external linker. + rr.Xadd = 0 + if target.IsElf() { + rr.Xadd -= int64(siz) + } + rr.Xsym = rs + break + } + if rs != 0 && (ldr.SymSect(rs) != ldr.SymSect(s) || rt == objabi.R_GOTPCREL) { + // set up addend for eventual relocation via outer symbol. + rs := rs + rs, off := FoldSubSymbolOffset(ldr, rs) + rr.Xadd = r.Add() + off + rr.Xadd -= int64(siz) // relative to address after the relocated chunk + rr.Xsym = rs + break + } + return rr, false + + case objabi.R_XCOFFREF: + return ExtrelocSimple(ldr, r), true + + // These reloc types don't need external relocations. + case objabi.R_ADDROFF, objabi.R_METHODOFF, objabi.R_ADDRCUOFF, + objabi.R_SIZE, objabi.R_CONST, objabi.R_GOTOFF: + return rr, false + } + return rr, true +} + +// ExtrelocSimple creates a simple external relocation from r, with the same +// symbol and addend. +func ExtrelocSimple(ldr *loader.Loader, r loader.Reloc) loader.ExtReloc { + var rr loader.ExtReloc + rs := r.Sym() + rr.Xsym = rs + rr.Xadd = r.Add() + rr.Type = r.Type() + rr.Size = r.Siz() + return rr +} + +// ExtrelocViaOuterSym creates an external relocation from r targeting the +// outer symbol and folding the subsymbol's offset into the addend. +func ExtrelocViaOuterSym(ldr *loader.Loader, r loader.Reloc, s loader.Sym) loader.ExtReloc { + // set up addend for eventual relocation via outer symbol. + var rr loader.ExtReloc + rs := r.Sym() + rs, off := FoldSubSymbolOffset(ldr, rs) + rr.Xadd = r.Add() + off + rst := ldr.SymType(rs) + if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil { + ldr.Errorf(s, "missing section for %s", ldr.SymName(rs)) + } + rr.Xsym = rs + rr.Type = r.Type() + rr.Size = r.Siz() + return rr +} + +// relocSymState hold state information needed when making a series of +// successive calls to relocsym(). The items here are invariant +// (meaning that they are set up once initially and then don't change +// during the execution of relocsym), with the exception of a slice +// used to facilitate batch allocation of external relocations. Calls +// to relocsym happen in parallel; the assumption is that each +// parallel thread will have its own state object. +type relocSymState struct { + target *Target + ldr *loader.Loader + err *ErrorReporter + syms *ArchSyms +} + +// makeRelocSymState creates a relocSymState container object to +// pass to relocsym(). If relocsym() calls happen in parallel, +// each parallel thread should have its own state object. +func (ctxt *Link) makeRelocSymState() *relocSymState { + return &relocSymState{ + target: &ctxt.Target, + ldr: ctxt.loader, + err: &ctxt.ErrorReporter, + syms: &ctxt.ArchSyms, + } +} + +// windynrelocsym examines a text symbol 's' and looks for relocations +// from it that correspond to references to symbols defined in DLLs, +// then fixes up those relocations as needed. A reference to a symbol +// XYZ from some DLL will fall into one of two categories: an indirect +// ref via "__imp_XYZ", or a direct ref to "XYZ". Here's an example of +// an indirect ref (this is an excerpt from objdump -ldr): +// +// 1c1: 48 89 c6 movq %rax, %rsi +// 1c4: ff 15 00 00 00 00 callq *(%rip) +// 00000000000001c6: IMAGE_REL_AMD64_REL32 __imp__errno +// +// In the assembly above, the code loads up the value of __imp_errno +// and then does an indirect call to that value. +// +// Here is what a direct reference might look like: +// +// 137: e9 20 06 00 00 jmp 0x75c <pow+0x75c> +// 13c: e8 00 00 00 00 callq 0x141 <pow+0x141> +// 000000000000013d: IMAGE_REL_AMD64_REL32 _errno +// +// The assembly below dispenses with the import symbol and just makes +// a direct call to _errno. +// +// The code below handles indirect refs by redirecting the target of +// the relocation from "__imp_XYZ" to "XYZ" (since the latter symbol +// is what the Windows loader is expected to resolve). For direct refs +// the call is redirected to a stub, where the stub first loads the +// symbol and then direct an indirect call to that value. +// +// Note that for a given symbol (as above) it is perfectly legal to +// have both direct and indirect references. +func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) error { + var su *loader.SymbolBuilder + relocs := ctxt.loader.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.IsMarker() { + continue // skip marker relocations + } + targ := r.Sym() + if targ == 0 { + continue + } + if !ctxt.loader.AttrReachable(targ) { + if r.Weak() { + continue + } + return fmt.Errorf("dynamic relocation to unreachable symbol %s", + ctxt.loader.SymName(targ)) + } + tgot := ctxt.loader.SymGot(targ) + if tgot == loadpe.RedirectToDynImportGotToken { + + // Consistency check: name should be __imp_X + sname := ctxt.loader.SymName(targ) + if !strings.HasPrefix(sname, "__imp_") { + return fmt.Errorf("internal error in windynrelocsym: redirect GOT token applied to non-import symbol %s", sname) + } + + // Locate underlying symbol (which originally had type + // SDYNIMPORT but has since been retyped to SWINDOWS). + ds, err := loadpe.LookupBaseFromImport(targ, ctxt.loader, ctxt.Arch) + if err != nil { + return err + } + dstyp := ctxt.loader.SymType(ds) + if dstyp != sym.SWINDOWS { + return fmt.Errorf("internal error in windynrelocsym: underlying sym for %q has wrong type %s", sname, dstyp.String()) + } + + // Redirect relocation to the dynimport. + r.SetSym(ds) + continue + } + + tplt := ctxt.loader.SymPlt(targ) + if tplt == loadpe.CreateImportStubPltToken { + + // Consistency check: don't want to see both PLT and GOT tokens. + if tgot != -1 { + return fmt.Errorf("internal error in windynrelocsym: invalid GOT setting %d for reloc to %s", tgot, ctxt.loader.SymName(targ)) + } + + // make dynimport JMP table for PE object files. + tplt := int32(rel.Size()) + ctxt.loader.SetPlt(targ, tplt) + + if su == nil { + su = ctxt.loader.MakeSymbolUpdater(s) + } + r.SetSym(rel.Sym()) + r.SetAdd(int64(tplt)) + + // jmp *addr + switch ctxt.Arch.Family { + default: + return fmt.Errorf("internal error in windynrelocsym: unsupported arch %v", ctxt.Arch.Family) + case sys.I386: + rel.AddUint8(0xff) + rel.AddUint8(0x25) + rel.AddAddrPlus(ctxt.Arch, targ, 0) + rel.AddUint8(0x90) + rel.AddUint8(0x90) + case sys.AMD64: + rel.AddUint8(0xff) + rel.AddUint8(0x24) + rel.AddUint8(0x25) + rel.AddAddrPlus4(ctxt.Arch, targ, 0) + rel.AddUint8(0x90) + } + } else if tplt >= 0 { + if su == nil { + su = ctxt.loader.MakeSymbolUpdater(s) + } + r.SetSym(rel.Sym()) + r.SetAdd(int64(tplt)) + } + } + return nil +} + +// windynrelocsyms generates jump table to C library functions that will be +// added later. windynrelocsyms writes the table into .rel symbol. +func (ctxt *Link) windynrelocsyms() { + if !(ctxt.IsWindows() && iscgo && ctxt.IsInternal()) { + return + } + + rel := ctxt.loader.CreateSymForUpdate(".rel", 0) + rel.SetType(sym.STEXT) + + for _, s := range ctxt.Textp { + if err := windynrelocsym(ctxt, rel, s); err != nil { + ctxt.Errorf(s, "%v", err) + } + } + + ctxt.Textp = append(ctxt.Textp, rel.Sym()) +} + +func dynrelocsym(ctxt *Link, s loader.Sym) { + target := &ctxt.Target + ldr := ctxt.loader + syms := &ctxt.ArchSyms + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.IsMarker() { + continue // skip marker relocations + } + rSym := r.Sym() + if r.Weak() && !ldr.AttrReachable(rSym) { + continue + } + if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal { + // It's expected that some relocations will be done + // later by relocsym (R_TLS_LE, R_ADDROFF), so + // don't worry if Adddynrel returns false. + thearch.Adddynrel(target, ldr, syms, s, r, ri) + continue + } + + if rSym != 0 && ldr.SymType(rSym) == sym.SDYNIMPORT || r.Type() >= objabi.ElfRelocOffset { + if rSym != 0 && !ldr.AttrReachable(rSym) { + ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s", ldr.SymName(rSym)) + } + if !thearch.Adddynrel(target, ldr, syms, s, r, ri) { + ctxt.Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", ldr.SymName(rSym), r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymType(rSym), ldr.SymType(rSym)) + } + } + } +} + +func (state *dodataState) dynreloc(ctxt *Link) { + if ctxt.HeadType == objabi.Hwindows { + return + } + // -d suppresses dynamic loader format, so we may as well not + // compute these sections or mark their symbols as reachable. + if *FlagD { + return + } + + for _, s := range ctxt.Textp { + dynrelocsym(ctxt, s) + } + for _, syms := range state.data { + for _, s := range syms { + dynrelocsym(ctxt, s) + } + } + if ctxt.IsELF { + elfdynhash(ctxt) + } +} + +func CodeblkPad(ctxt *Link, out *OutBuf, addr int64, size int64, pad []byte) { + writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, ctxt.Textp, addr, size, pad) +} + +const blockSize = 1 << 20 // 1MB chunks written at a time. + +// writeBlocks writes a specified chunk of symbols to the output buffer. It +// breaks the write up into ≥blockSize chunks to write them out, and schedules +// as many goroutines as necessary to accomplish this task. This call then +// blocks, waiting on the writes to complete. Note that we use the sem parameter +// to limit the number of concurrent writes taking place. +func writeBlocks(ctxt *Link, out *OutBuf, sem chan int, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { + for i, s := range syms { + if ldr.SymValue(s) >= addr && !ldr.AttrSubSymbol(s) { + syms = syms[i:] + break + } + } + + var wg sync.WaitGroup + max, lastAddr, written := int64(blockSize), addr+size, int64(0) + for addr < lastAddr { + // Find the last symbol we'd write. + idx := -1 + for i, s := range syms { + if ldr.AttrSubSymbol(s) { + continue + } + + // If the next symbol's size would put us out of bounds on the total length, + // stop looking. + end := ldr.SymValue(s) + ldr.SymSize(s) + if end > lastAddr { + break + } + + // We're gonna write this symbol. + idx = i + + // If we cross over the max size, we've got enough symbols. + if end > addr+max { + break + } + } + + // If we didn't find any symbols to write, we're done here. + if idx < 0 { + break + } + + // Compute the length to write, including padding. + // We need to write to the end address (lastAddr), or the next symbol's + // start address, whichever comes first. If there is no more symbols, + // just write to lastAddr. This ensures we don't leave holes between the + // blocks or at the end. + length := int64(0) + if idx+1 < len(syms) { + // Find the next top-level symbol. + // Skip over sub symbols so we won't split a container symbol + // into two blocks. + next := syms[idx+1] + for ldr.AttrSubSymbol(next) { + idx++ + next = syms[idx+1] + } + length = ldr.SymValue(next) - addr + } + if length == 0 || length > lastAddr-addr { + length = lastAddr - addr + } + + // Start the block output operator. + if o, err := out.View(uint64(out.Offset() + written)); err == nil { + sem <- 1 + wg.Add(1) + go func(o *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { + writeBlock(ctxt, o, ldr, syms, addr, size, pad) + wg.Done() + <-sem + }(o, ldr, syms, addr, length, pad) + } else { // output not mmaped, don't parallelize. + writeBlock(ctxt, out, ldr, syms, addr, length, pad) + } + + // Prepare for the next loop. + if idx != -1 { + syms = syms[idx+1:] + } + written += length + addr += length + } + wg.Wait() +} + +func writeBlock(ctxt *Link, out *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { + + st := ctxt.makeRelocSymState() + + // This doesn't distinguish the memory size from the file + // size, and it lays out the file based on Symbol.Value, which + // is the virtual address. DWARF compression changes file sizes, + // so dwarfcompress will fix this up later if necessary. + eaddr := addr + size + for _, s := range syms { + if ldr.AttrSubSymbol(s) { + continue + } + val := ldr.SymValue(s) + if val >= eaddr { + break + } + if val < addr { + ldr.Errorf(s, "phase error: addr=%#x but sym=%#x type=%v sect=%v", addr, val, ldr.SymType(s), ldr.SymSect(s).Name) + errorexit() + } + if addr < val { + out.WriteStringPad("", int(val-addr), pad) + addr = val + } + P := out.WriteSym(ldr, s) + st.relocsym(s, P) + if f, ok := ctxt.generatorSyms[s]; ok { + f(ctxt, s) + } + addr += int64(len(P)) + siz := ldr.SymSize(s) + if addr < val+siz { + out.WriteStringPad("", int(val+siz-addr), pad) + addr = val + siz + } + if addr != val+siz { + ldr.Errorf(s, "phase error: addr=%#x value+size=%#x", addr, val+siz) + errorexit() + } + if val+siz >= eaddr { + break + } + } + + if addr < eaddr { + out.WriteStringPad("", int(eaddr-addr), pad) + } +} + +type writeFn func(*Link, *OutBuf, int64, int64) + +// writeParallel handles scheduling parallel execution of data write functions. +func writeParallel(wg *sync.WaitGroup, fn writeFn, ctxt *Link, seek, vaddr, length uint64) { + if out, err := ctxt.Out.View(seek); err != nil { + ctxt.Out.SeekSet(int64(seek)) + fn(ctxt, ctxt.Out, int64(vaddr), int64(length)) + } else { + wg.Add(1) + go func() { + defer wg.Done() + fn(ctxt, out, int64(vaddr), int64(length)) + }() + } +} + +func datblk(ctxt *Link, out *OutBuf, addr, size int64) { + writeDatblkToOutBuf(ctxt, out, addr, size) +} + +// Used only on Wasm for now. +func DatblkBytes(ctxt *Link, addr int64, size int64) []byte { + buf := make([]byte, size) + out := &OutBuf{heap: buf} + writeDatblkToOutBuf(ctxt, out, addr, size) + return buf +} + +func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) { + writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, ctxt.datap, addr, size, zeros[:]) +} + +func dwarfblk(ctxt *Link, out *OutBuf, addr int64, size int64) { + // Concatenate the section symbol lists into a single list to pass + // to writeBlocks. + // + // NB: ideally we would do a separate writeBlocks call for each + // section, but this would run the risk of undoing any file offset + // adjustments made during layout. + n := 0 + for i := range dwarfp { + n += len(dwarfp[i].syms) + } + syms := make([]loader.Sym, 0, n) + for i := range dwarfp { + syms = append(syms, dwarfp[i].syms...) + } + writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, syms, addr, size, zeros[:]) +} + +var covCounterDataStartOff, covCounterDataLen uint64 + +var zeros [512]byte + +var ( + strdata = make(map[string]string) + strnames []string +) + +func addstrdata1(ctxt *Link, arg string) { + eq := strings.Index(arg, "=") + dot := strings.LastIndex(arg[:eq+1], ".") + if eq < 0 || dot < 0 { + Exitf("-X flag requires argument of the form importpath.name=value") + } + pkg := arg[:dot] + if ctxt.BuildMode == BuildModePlugin && pkg == "main" { + pkg = *flagPluginPath + } + pkg = objabi.PathToPrefix(pkg) + name := pkg + arg[dot:eq] + value := arg[eq+1:] + if _, ok := strdata[name]; !ok { + strnames = append(strnames, name) + } + strdata[name] = value +} + +// addstrdata sets the initial value of the string variable name to value. +func addstrdata(arch *sys.Arch, l *loader.Loader, name, value string) { + s := l.Lookup(name, 0) + if s == 0 { + return + } + if goType := l.SymGoType(s); goType == 0 { + return + } else if typeName := l.SymName(goType); typeName != "type:string" { + Errorf(nil, "%s: cannot set with -X: not a var of type string (%s)", name, typeName) + return + } + if !l.AttrReachable(s) { + return // don't bother setting unreachable variable + } + bld := l.MakeSymbolUpdater(s) + if bld.Type() == sym.SBSS { + bld.SetType(sym.SDATA) + } + + p := fmt.Sprintf("%s.str", name) + sbld := l.CreateSymForUpdate(p, 0) + sbld.Addstring(value) + sbld.SetType(sym.SRODATA) + + // Don't reset the variable's size. String variable usually has size of + // 2*PtrSize, but in ASAN build it can be larger due to red zone. + // (See issue 56175.) + bld.SetData(make([]byte, arch.PtrSize*2)) + bld.SetReadOnly(false) + bld.ResetRelocs() + bld.SetAddrPlus(arch, 0, sbld.Sym(), 0) + bld.SetUint(arch, int64(arch.PtrSize), uint64(len(value))) +} + +func (ctxt *Link) dostrdata() { + for _, name := range strnames { + addstrdata(ctxt.Arch, ctxt.loader, name, strdata[name]) + } +} + +// addgostring adds str, as a Go string value, to s. symname is the name of the +// symbol used to define the string data and must be unique per linked object. +func addgostring(ctxt *Link, ldr *loader.Loader, s *loader.SymbolBuilder, symname, str string) { + sdata := ldr.CreateSymForUpdate(symname, 0) + if sdata.Type() != sym.Sxxx { + ctxt.Errorf(s.Sym(), "duplicate symname in addgostring: %s", symname) + } + sdata.SetLocal(true) + sdata.SetType(sym.SRODATA) + sdata.SetSize(int64(len(str))) + sdata.SetData([]byte(str)) + s.AddAddr(ctxt.Arch, sdata.Sym()) + s.AddUint(ctxt.Arch, uint64(len(str))) +} + +func addinitarrdata(ctxt *Link, ldr *loader.Loader, s loader.Sym) { + p := ldr.SymName(s) + ".ptr" + sp := ldr.CreateSymForUpdate(p, 0) + sp.SetType(sym.SINITARR) + sp.SetSize(0) + sp.SetDuplicateOK(true) + sp.AddAddr(ctxt.Arch, s) +} + +// symalign returns the required alignment for the given symbol s. +func symalign(ldr *loader.Loader, s loader.Sym) int32 { + min := int32(thearch.Minalign) + align := ldr.SymAlign(s) + if align >= min { + return align + } else if align != 0 { + return min + } + align = int32(thearch.Maxalign) + ssz := ldr.SymSize(s) + for int64(align) > ssz && align > min { + align >>= 1 + } + ldr.SetSymAlign(s, align) + return align +} + +func aligndatsize(state *dodataState, datsize int64, s loader.Sym) int64 { + return Rnd(datsize, int64(symalign(state.ctxt.loader, s))) +} + +const debugGCProg = false + +type GCProg struct { + ctxt *Link + sym *loader.SymbolBuilder + w gcprog.Writer +} + +func (p *GCProg) Init(ctxt *Link, name string) { + p.ctxt = ctxt + p.sym = ctxt.loader.CreateSymForUpdate(name, 0) + p.w.Init(p.writeByte()) + if debugGCProg { + fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name) + p.w.Debug(os.Stderr) + } +} + +func (p *GCProg) writeByte() func(x byte) { + return func(x byte) { + p.sym.AddUint8(x) + } +} + +func (p *GCProg) End(size int64) { + p.w.ZeroUntil(size / int64(p.ctxt.Arch.PtrSize)) + p.w.End() + if debugGCProg { + fmt.Fprintf(os.Stderr, "ld: end GCProg\n") + } +} + +func (p *GCProg) AddSym(s loader.Sym) { + ldr := p.ctxt.loader + typ := ldr.SymGoType(s) + + // Things without pointers should be in sym.SNOPTRDATA or sym.SNOPTRBSS; + // everything we see should have pointers and should therefore have a type. + if typ == 0 { + switch ldr.SymName(s) { + case "runtime.data", "runtime.edata", "runtime.bss", "runtime.ebss": + // Ignore special symbols that are sometimes laid out + // as real symbols. See comment about dyld on darwin in + // the address function. + return + } + p.ctxt.Errorf(p.sym.Sym(), "missing Go type information for global symbol %s: size %d", ldr.SymName(s), ldr.SymSize(s)) + return + } + + ptrsize := int64(p.ctxt.Arch.PtrSize) + typData := ldr.Data(typ) + nptr := decodetypePtrdata(p.ctxt.Arch, typData) / ptrsize + + if debugGCProg { + fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", ldr.SymName(s), ldr.SymValue(s), ldr.SymValue(s)/ptrsize, nptr) + } + + sval := ldr.SymValue(s) + if decodetypeUsegcprog(p.ctxt.Arch, typData) == 0 { + // Copy pointers from mask into program. + mask := decodetypeGcmask(p.ctxt, typ) + for i := int64(0); i < nptr; i++ { + if (mask[i/8]>>uint(i%8))&1 != 0 { + p.w.Ptr(sval/ptrsize + i) + } + } + return + } + + // Copy program. + prog := decodetypeGcprog(p.ctxt, typ) + p.w.ZeroUntil(sval / ptrsize) + p.w.Append(prog[4:], nptr) +} + +// cutoff is the maximum data section size permitted by the linker +// (see issue #9862). +const cutoff = 2e9 // 2 GB (or so; looks better in errors than 2^31) + +func (state *dodataState) checkdatsize(symn sym.SymKind) { + if state.datsize > cutoff { + Errorf(nil, "too much data in section %v (over %v bytes)", symn, cutoff) + } +} + +// fixZeroSizedSymbols gives a few special symbols with zero size some space. +func fixZeroSizedSymbols(ctxt *Link) { + // The values in moduledata are filled out by relocations + // pointing to the addresses of these special symbols. + // Typically these symbols have no size and are not laid + // out with their matching section. + // + // However on darwin, dyld will find the special symbol + // in the first loaded module, even though it is local. + // + // (An hypothesis, formed without looking in the dyld sources: + // these special symbols have no size, so their address + // matches a real symbol. The dynamic linker assumes we + // want the normal symbol with the same address and finds + // it in the other module.) + // + // To work around this we lay out the symbls whose + // addresses are vital for multi-module programs to work + // as normal symbols, and give them a little size. + // + // On AIX, as all DATA sections are merged together, ld might not put + // these symbols at the beginning of their respective section if there + // aren't real symbols, their alignment might not match the + // first symbol alignment. Therefore, there are explicitly put at the + // beginning of their section with the same alignment. + if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + return + } + + ldr := ctxt.loader + bss := ldr.CreateSymForUpdate("runtime.bss", 0) + bss.SetSize(8) + ldr.SetAttrSpecial(bss.Sym(), false) + + ebss := ldr.CreateSymForUpdate("runtime.ebss", 0) + ldr.SetAttrSpecial(ebss.Sym(), false) + + data := ldr.CreateSymForUpdate("runtime.data", 0) + data.SetSize(8) + ldr.SetAttrSpecial(data.Sym(), false) + + edata := ldr.CreateSymForUpdate("runtime.edata", 0) + ldr.SetAttrSpecial(edata.Sym(), false) + + if ctxt.HeadType == objabi.Haix { + // XCOFFTOC symbols are part of .data section. + edata.SetType(sym.SXCOFFTOC) + } + + types := ldr.CreateSymForUpdate("runtime.types", 0) + types.SetType(sym.STYPE) + types.SetSize(8) + ldr.SetAttrSpecial(types.Sym(), false) + + etypes := ldr.CreateSymForUpdate("runtime.etypes", 0) + etypes.SetType(sym.SFUNCTAB) + ldr.SetAttrSpecial(etypes.Sym(), false) + + if ctxt.HeadType == objabi.Haix { + rodata := ldr.CreateSymForUpdate("runtime.rodata", 0) + rodata.SetType(sym.SSTRING) + rodata.SetSize(8) + ldr.SetAttrSpecial(rodata.Sym(), false) + + erodata := ldr.CreateSymForUpdate("runtime.erodata", 0) + ldr.SetAttrSpecial(erodata.Sym(), false) + } +} + +// makeRelroForSharedLib creates a section of readonly data if necessary. +func (state *dodataState) makeRelroForSharedLib(target *Link) { + if !target.UseRelro() { + return + } + + // "read only" data with relocations needs to go in its own section + // when building a shared library. We do this by boosting objects of + // type SXXX with relocations to type SXXXRELRO. + ldr := target.loader + for _, symnro := range sym.ReadOnly { + symnrelro := sym.RelROMap[symnro] + + ro := []loader.Sym{} + relro := state.data[symnrelro] + + for _, s := range state.data[symnro] { + relocs := ldr.Relocs(s) + isRelro := relocs.Count() > 0 + switch state.symType(s) { + case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO: + // Symbols are not sorted yet, so it is possible + // that an Outer symbol has been changed to a + // relro Type before it reaches here. + isRelro = true + case sym.SFUNCTAB: + if ldr.SymName(s) == "runtime.etypes" { + // runtime.etypes must be at the end of + // the relro data. + isRelro = true + } + case sym.SGOFUNC: + // The only SGOFUNC symbols that contain relocations are .stkobj, + // and their relocations are of type objabi.R_ADDROFF, + // which always get resolved during linking. + isRelro = false + } + if isRelro { + state.setSymType(s, symnrelro) + if outer := ldr.OuterSym(s); outer != 0 { + state.setSymType(outer, symnrelro) + } + relro = append(relro, s) + } else { + ro = append(ro, s) + } + } + + // Check that we haven't made two symbols with the same .Outer into + // different types (because references two symbols with non-nil Outer + // become references to the outer symbol + offset it's vital that the + // symbol and the outer end up in the same section). + for _, s := range relro { + if outer := ldr.OuterSym(s); outer != 0 { + st := state.symType(s) + ost := state.symType(outer) + if st != ost { + state.ctxt.Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)", + ldr.SymName(outer), st, ost) + } + } + } + + state.data[symnro] = ro + state.data[symnrelro] = relro + } +} + +// dodataState holds bits of state information needed by dodata() and the +// various helpers it calls. The lifetime of these items should not extend +// past the end of dodata(). +type dodataState struct { + // Link context + ctxt *Link + // Data symbols bucketed by type. + data [sym.SXREF][]loader.Sym + // Max alignment for each flavor of data symbol. + dataMaxAlign [sym.SXREF]int32 + // Overridden sym type + symGroupType []sym.SymKind + // Current data size so far. + datsize int64 +} + +// A note on symType/setSymType below: +// +// In the legacy linker, the types of symbols (notably data symbols) are +// changed during the symtab() phase so as to insure that similar symbols +// are bucketed together, then their types are changed back again during +// dodata. Symbol to section assignment also plays tricks along these lines +// in the case where a relro segment is needed. +// +// The value returned from setType() below reflects the effects of +// any overrides made by symtab and/or dodata. + +// symType returns the (possibly overridden) type of 's'. +func (state *dodataState) symType(s loader.Sym) sym.SymKind { + if int(s) < len(state.symGroupType) { + if override := state.symGroupType[s]; override != 0 { + return override + } + } + return state.ctxt.loader.SymType(s) +} + +// setSymType sets a new override type for 's'. +func (state *dodataState) setSymType(s loader.Sym, kind sym.SymKind) { + if s == 0 { + panic("bad") + } + if int(s) < len(state.symGroupType) { + state.symGroupType[s] = kind + } else { + su := state.ctxt.loader.MakeSymbolUpdater(s) + su.SetType(kind) + } +} + +func (ctxt *Link) dodata(symGroupType []sym.SymKind) { + + // Give zeros sized symbols space if necessary. + fixZeroSizedSymbols(ctxt) + + // Collect data symbols by type into data. + state := dodataState{ctxt: ctxt, symGroupType: symGroupType} + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || ldr.AttrSubSymbol(s) || + !ldr.TopLevelSym(s) { + continue + } + + st := state.symType(s) + + if st <= sym.STEXT || st >= sym.SXREF { + continue + } + state.data[st] = append(state.data[st], s) + + // Similarly with checking the onlist attr. + if ldr.AttrOnList(s) { + log.Fatalf("symbol %s listed multiple times", ldr.SymName(s)) + } + ldr.SetAttrOnList(s, true) + } + + // Now that we have the data symbols, but before we start + // to assign addresses, record all the necessary + // dynamic relocations. These will grow the relocation + // symbol, which is itself data. + // + // On darwin, we need the symbol table numbers for dynreloc. + if ctxt.HeadType == objabi.Hdarwin { + machosymorder(ctxt) + } + state.dynreloc(ctxt) + + // Move any RO data with relocations to a separate section. + state.makeRelroForSharedLib(ctxt) + + // Set alignment for the symbol with the largest known index, + // so as to trigger allocation of the loader's internal + // alignment array. This will avoid data races in the parallel + // section below. + lastSym := loader.Sym(ldr.NSym() - 1) + ldr.SetSymAlign(lastSym, ldr.SymAlign(lastSym)) + + // Sort symbols. + var wg sync.WaitGroup + for symn := range state.data { + symn := sym.SymKind(symn) + wg.Add(1) + go func() { + state.data[symn], state.dataMaxAlign[symn] = state.dodataSect(ctxt, symn, state.data[symn]) + wg.Done() + }() + } + wg.Wait() + + if ctxt.IsELF { + // Make .rela and .rela.plt contiguous, the ELF ABI requires this + // and Solaris actually cares. + syms := state.data[sym.SELFROSECT] + reli, plti := -1, -1 + for i, s := range syms { + switch ldr.SymName(s) { + case ".rel.plt", ".rela.plt": + plti = i + case ".rel", ".rela": + reli = i + } + } + if reli >= 0 && plti >= 0 && plti != reli+1 { + var first, second int + if plti > reli { + first, second = reli, plti + } else { + first, second = plti, reli + } + rel, plt := syms[reli], syms[plti] + copy(syms[first+2:], syms[first+1:second]) + syms[first+0] = rel + syms[first+1] = plt + + // Make sure alignment doesn't introduce a gap. + // Setting the alignment explicitly prevents + // symalign from basing it on the size and + // getting it wrong. + ldr.SetSymAlign(rel, int32(ctxt.Arch.RegSize)) + ldr.SetSymAlign(plt, int32(ctxt.Arch.RegSize)) + } + state.data[sym.SELFROSECT] = syms + } + + if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal { + // These symbols must have the same alignment as their section. + // Otherwise, ld might change the layout of Go sections. + ldr.SetSymAlign(ldr.Lookup("runtime.data", 0), state.dataMaxAlign[sym.SDATA]) + ldr.SetSymAlign(ldr.Lookup("runtime.bss", 0), state.dataMaxAlign[sym.SBSS]) + } + + // Create *sym.Section objects and assign symbols to sections for + // data/rodata (and related) symbols. + state.allocateDataSections(ctxt) + + // Create *sym.Section objects and assign symbols to sections for + // DWARF symbols. + state.allocateDwarfSections(ctxt) + + /* number the sections */ + n := int16(1) + + for _, sect := range Segtext.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segrodata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segrelrodata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segdata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segdwarf.Sections { + sect.Extnum = n + n++ + } +} + +// allocateDataSectionForSym creates a new sym.Section into which a a +// single symbol will be placed. Here "seg" is the segment into which +// the section will go, "s" is the symbol to be placed into the new +// section, and "rwx" contains permissions for the section. +func (state *dodataState) allocateDataSectionForSym(seg *sym.Segment, s loader.Sym, rwx int) *sym.Section { + ldr := state.ctxt.loader + sname := ldr.SymName(s) + if strings.HasPrefix(sname, "go:") { + sname = ".go." + sname[len("go:"):] + } + sect := addsection(ldr, state.ctxt.Arch, seg, sname, rwx) + sect.Align = symalign(ldr, s) + state.datsize = Rnd(state.datsize, int64(sect.Align)) + sect.Vaddr = uint64(state.datsize) + return sect +} + +// allocateNamedDataSection creates a new sym.Section for a category +// of data symbols. Here "seg" is the segment into which the section +// will go, "sName" is the name to give to the section, "types" is a +// range of symbol types to be put into the section, and "rwx" +// contains permissions for the section. +func (state *dodataState) allocateNamedDataSection(seg *sym.Segment, sName string, types []sym.SymKind, rwx int) *sym.Section { + sect := addsection(state.ctxt.loader, state.ctxt.Arch, seg, sName, rwx) + if len(types) == 0 { + sect.Align = 1 + } else if len(types) == 1 { + sect.Align = state.dataMaxAlign[types[0]] + } else { + for _, symn := range types { + align := state.dataMaxAlign[symn] + if sect.Align < align { + sect.Align = align + } + } + } + state.datsize = Rnd(state.datsize, int64(sect.Align)) + sect.Vaddr = uint64(state.datsize) + return sect +} + +// assignDsymsToSection assigns a collection of data symbols to a +// newly created section. "sect" is the section into which to place +// the symbols, "syms" holds the list of symbols to assign, +// "forceType" (if non-zero) contains a new sym type to apply to each +// sym during the assignment, and "aligner" is a hook to call to +// handle alignment during the assignment process. +func (state *dodataState) assignDsymsToSection(sect *sym.Section, syms []loader.Sym, forceType sym.SymKind, aligner func(state *dodataState, datsize int64, s loader.Sym) int64) { + ldr := state.ctxt.loader + for _, s := range syms { + state.datsize = aligner(state, state.datsize, s) + ldr.SetSymSect(s, sect) + if forceType != sym.Sxxx { + state.setSymType(s, forceType) + } + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) + } + sect.Length = uint64(state.datsize) - sect.Vaddr +} + +func (state *dodataState) assignToSection(sect *sym.Section, symn sym.SymKind, forceType sym.SymKind) { + state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) + state.checkdatsize(symn) +} + +// allocateSingleSymSections walks through the bucketed data symbols +// with type 'symn', creates a new section for each sym, and assigns +// the sym to a newly created section. Section name is set from the +// symbol name. "Seg" is the segment into which to place the new +// section, "forceType" is the new sym.SymKind to assign to the symbol +// within the section, and "rwx" holds section permissions. +func (state *dodataState) allocateSingleSymSections(seg *sym.Segment, symn sym.SymKind, forceType sym.SymKind, rwx int) { + ldr := state.ctxt.loader + for _, s := range state.data[symn] { + sect := state.allocateDataSectionForSym(seg, s, rwx) + ldr.SetSymSect(s, sect) + state.setSymType(s, forceType) + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) + sect.Length = uint64(state.datsize) - sect.Vaddr + } + state.checkdatsize(symn) +} + +// allocateNamedSectionAndAssignSyms creates a new section with the +// specified name, then walks through the bucketed data symbols with +// type 'symn' and assigns each of them to this new section. "Seg" is +// the segment into which to place the new section, "secName" is the +// name to give to the new section, "forceType" (if non-zero) contains +// a new sym type to apply to each sym during the assignment, and +// "rwx" holds section permissions. +func (state *dodataState) allocateNamedSectionAndAssignSyms(seg *sym.Segment, secName string, symn sym.SymKind, forceType sym.SymKind, rwx int) *sym.Section { + + sect := state.allocateNamedDataSection(seg, secName, []sym.SymKind{symn}, rwx) + state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) + return sect +} + +// allocateDataSections allocates sym.Section objects for data/rodata +// (and related) symbols, and then assigns symbols to those sections. +func (state *dodataState) allocateDataSections(ctxt *Link) { + // Allocate sections. + // Data is processed before segtext, because we need + // to see all symbols in the .data and .bss sections in order + // to generate garbage collection information. + + // Writable data sections that do not need any specialized handling. + writable := []sym.SymKind{ + sym.SBUILDINFO, + sym.SELFSECT, + sym.SMACHO, + sym.SMACHOGOT, + sym.SWINDOWS, + } + for _, symn := range writable { + state.allocateSingleSymSections(&Segdata, symn, sym.SDATA, 06) + } + ldr := ctxt.loader + + // .got + if len(state.data[sym.SELFGOT]) > 0 { + state.allocateNamedSectionAndAssignSyms(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06) + } + + /* pointer-free data */ + sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrdata", sym.SNOPTRDATA, sym.SDATA, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrdata", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrdata", 0), sect) + + hasinitarr := ctxt.linkShared + + /* shared library initializer */ + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin: + hasinitarr = true + } + + if ctxt.HeadType == objabi.Haix { + if len(state.data[sym.SINITARR]) > 0 { + Errorf(nil, "XCOFF format doesn't allow .init_array section") + } + } + + if hasinitarr && len(state.data[sym.SINITARR]) > 0 { + state.allocateNamedSectionAndAssignSyms(&Segdata, ".init_array", sym.SINITARR, sym.Sxxx, 06) + } + + /* data */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".data", sym.SDATA, sym.SDATA, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.data", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.edata", 0), sect) + dataGcEnd := state.datsize - int64(sect.Vaddr) + + // On AIX, TOC entries must be the last of .data + // These aren't part of gc as they won't change during the runtime. + state.assignToSection(sect, sym.SXCOFFTOC, sym.SDATA) + state.checkdatsize(sym.SDATA) + sect.Length = uint64(state.datsize) - sect.Vaddr + + /* bss */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".bss", sym.SBSS, sym.Sxxx, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.bss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ebss", 0), sect) + bssGcEnd := state.datsize - int64(sect.Vaddr) + + // Emit gcdata for bss symbols now that symbol values have been assigned. + gcsToEmit := []struct { + symName string + symKind sym.SymKind + gcEnd int64 + }{ + {"runtime.gcdata", sym.SDATA, dataGcEnd}, + {"runtime.gcbss", sym.SBSS, bssGcEnd}, + } + for _, g := range gcsToEmit { + var gc GCProg + gc.Init(ctxt, g.symName) + for _, s := range state.data[g.symKind] { + gc.AddSym(s) + } + gc.End(g.gcEnd) + } + + /* pointer-free bss */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrbss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrbss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.end", 0), sect) + + // Code coverage counters are assigned to the .noptrbss section. + // We assign them in a separate pass so that they stay aggregated + // together in a single blob (coverage runtime depends on this). + covCounterDataStartOff = sect.Length + state.assignToSection(sect, sym.SCOVERAGE_COUNTER, sym.SNOPTRBSS) + covCounterDataLen = sect.Length - covCounterDataStartOff + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.covctrs", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ecovctrs", 0), sect) + + // Coverage instrumentation counters for libfuzzer. + if len(state.data[sym.SLIBFUZZER_8BIT_COUNTER]) > 0 { + sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".go.fuzzcntrs", sym.SLIBFUZZER_8BIT_COUNTER, sym.Sxxx, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.__start___sancov_cntrs", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.__stop___sancov_cntrs", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._counters", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._ecounters", 0), sect) + } + + if len(state.data[sym.STLSBSS]) > 0 { + var sect *sym.Section + // FIXME: not clear why it is sometimes necessary to suppress .tbss section creation. + if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) { + sect = addsection(ldr, ctxt.Arch, &Segdata, ".tbss", 06) + sect.Align = int32(ctxt.Arch.PtrSize) + // FIXME: why does this need to be set to zero? + sect.Vaddr = 0 + } + state.datsize = 0 + + for _, s := range state.data[sym.STLSBSS] { + state.datsize = aligndatsize(state, state.datsize, s) + if sect != nil { + ldr.SetSymSect(s, sect) + } + ldr.SetSymValue(s, state.datsize) + state.datsize += ldr.SymSize(s) + } + state.checkdatsize(sym.STLSBSS) + + if sect != nil { + sect.Length = uint64(state.datsize) + } + } + + /* + * We finished data, begin read-only data. + * Not all systems support a separate read-only non-executable data section. + * ELF and Windows PE systems do. + * OS X and Plan 9 do not. + * And if we're using external linking mode, the point is moot, + * since it's not our decision; that code expects the sections in + * segtext. + */ + var segro *sym.Segment + if ctxt.IsELF && ctxt.LinkMode == LinkInternal { + segro = &Segrodata + } else if ctxt.HeadType == objabi.Hwindows { + segro = &Segrodata + } else { + segro = &Segtext + } + + state.datsize = 0 + + /* read-only executable ELF, Mach-O sections */ + if len(state.data[sym.STEXT]) != 0 { + culprit := ldr.SymName(state.data[sym.STEXT][0]) + Errorf(nil, "dodata found an sym.STEXT symbol: %s", culprit) + } + state.allocateSingleSymSections(&Segtext, sym.SELFRXSECT, sym.SRODATA, 05) + state.allocateSingleSymSections(&Segtext, sym.SMACHOPLT, sym.SRODATA, 05) + + /* read-only data */ + sect = state.allocateNamedDataSection(segro, ".rodata", sym.ReadOnly, 04) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.rodata", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.erodata", 0), sect) + if !ctxt.UseRelro() { + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect) + } + for _, symn := range sym.ReadOnly { + symnStartValue := state.datsize + if len(state.data[symn]) != 0 { + symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0]) + } + state.assignToSection(sect, symn, sym.SRODATA) + setCarrierSize(symn, state.datsize-symnStartValue) + if ctxt.HeadType == objabi.Haix { + // Read-only symbols might be wrapped inside their outer + // symbol. + // XCOFF symbol table needs to know the size of + // these outer symbols. + xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) + } + } + + /* read-only ELF, Mach-O sections */ + state.allocateSingleSymSections(segro, sym.SELFROSECT, sym.SRODATA, 04) + + // There is some data that are conceptually read-only but are written to by + // relocations. On GNU systems, we can arrange for the dynamic linker to + // mprotect sections after relocations are applied by giving them write + // permissions in the object file and calling them ".data.rel.ro.FOO". We + // divide the .rodata section between actual .rodata and .data.rel.ro.rodata, + // but for the other sections that this applies to, we just write a read-only + // .FOO section or a read-write .data.rel.ro.FOO section depending on the + // situation. + // TODO(mwhudson): It would make sense to do this more widely, but it makes + // the system linker segfault on darwin. + const relroPerm = 06 + const fallbackPerm = 04 + relroSecPerm := fallbackPerm + genrelrosecname := func(suffix string) string { + if suffix == "" { + return ".rodata" + } + return suffix + } + seg := segro + + if ctxt.UseRelro() { + segrelro := &Segrelrodata + if ctxt.LinkMode == LinkExternal && !ctxt.IsAIX() && !ctxt.IsDarwin() { + // Using a separate segment with an external + // linker results in some programs moving + // their data sections unexpectedly, which + // corrupts the moduledata. So we use the + // rodata segment and let the external linker + // sort out a rel.ro segment. + segrelro = segro + } else { + // Reset datsize for new segment. + state.datsize = 0 + } + + if !ctxt.IsDarwin() { // We don't need the special names on darwin. + genrelrosecname = func(suffix string) string { + return ".data.rel.ro" + suffix + } + } + + relroReadOnly := []sym.SymKind{} + for _, symnro := range sym.ReadOnly { + symn := sym.RelROMap[symnro] + relroReadOnly = append(relroReadOnly, symn) + } + seg = segrelro + relroSecPerm = relroPerm + + /* data only written by relocations */ + sect = state.allocateNamedDataSection(segrelro, genrelrosecname(""), relroReadOnly, relroSecPerm) + + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect) + + for i, symnro := range sym.ReadOnly { + if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix { + // Skip forward so that no type + // reference uses a zero offset. + // This is unlikely but possible in small + // programs with no other read-only data. + state.datsize++ + } + + symn := sym.RelROMap[symnro] + symnStartValue := state.datsize + if len(state.data[symn]) != 0 { + symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0]) + } + + for _, s := range state.data[symn] { + outer := ldr.OuterSym(s) + if s != 0 && ldr.SymSect(outer) != nil && ldr.SymSect(outer) != sect { + ctxt.Errorf(s, "s.Outer (%s) in different section from s, %s != %s", ldr.SymName(outer), ldr.SymSect(outer).Name, sect.Name) + } + } + state.assignToSection(sect, symn, sym.SRODATA) + setCarrierSize(symn, state.datsize-symnStartValue) + if ctxt.HeadType == objabi.Haix { + // Read-only symbols might be wrapped inside their outer + // symbol. + // XCOFF symbol table needs to know the size of + // these outer symbols. + xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) + } + } + + sect.Length = uint64(state.datsize) - sect.Vaddr + } + + /* typelink */ + sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm) + + typelink := ldr.CreateSymForUpdate("runtime.typelink", 0) + ldr.SetSymSect(typelink.Sym(), sect) + typelink.SetType(sym.SRODATA) + state.datsize += typelink.Size() + state.checkdatsize(sym.STYPELINK) + sect.Length = uint64(state.datsize) - sect.Vaddr + + /* itablink */ + sect = state.allocateNamedDataSection(seg, genrelrosecname(".itablink"), []sym.SymKind{sym.SITABLINK}, relroSecPerm) + + itablink := ldr.CreateSymForUpdate("runtime.itablink", 0) + ldr.SetSymSect(itablink.Sym(), sect) + itablink.SetType(sym.SRODATA) + state.datsize += itablink.Size() + state.checkdatsize(sym.SITABLINK) + sect.Length = uint64(state.datsize) - sect.Vaddr + + /* gosymtab */ + sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gosymtab"), sym.SSYMTAB, sym.SRODATA, relroSecPerm) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.symtab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.esymtab", 0), sect) + + /* gopclntab */ + sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect) + setCarrierSize(sym.SPCLNTAB, int64(sect.Length)) + if ctxt.HeadType == objabi.Haix { + xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB) + } + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if state.datsize != int64(uint32(state.datsize)) { + Errorf(nil, "read-only data segment too large: %d", state.datsize) + } + + siz := 0 + for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { + siz += len(state.data[symn]) + } + ctxt.datap = make([]loader.Sym, 0, siz) + for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { + ctxt.datap = append(ctxt.datap, state.data[symn]...) + } +} + +// allocateDwarfSections allocates sym.Section objects for DWARF +// symbols, and assigns symbols to sections. +func (state *dodataState) allocateDwarfSections(ctxt *Link) { + + alignOne := func(state *dodataState, datsize int64, s loader.Sym) int64 { return datsize } + + ldr := ctxt.loader + for i := 0; i < len(dwarfp); i++ { + // First the section symbol. + s := dwarfp[i].secSym() + sect := state.allocateNamedDataSection(&Segdwarf, ldr.SymName(s), []sym.SymKind{}, 04) + ldr.SetSymSect(s, sect) + sect.Sym = sym.LoaderSym(s) + curType := ldr.SymType(s) + state.setSymType(s, sym.SRODATA) + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) + + // Then any sub-symbols for the section symbol. + subSyms := dwarfp[i].subSyms() + state.assignDsymsToSection(sect, subSyms, sym.SRODATA, alignOne) + + for j := 0; j < len(subSyms); j++ { + s := subSyms[j] + if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC { + // Update the size of .debug_loc for this symbol's + // package. + addDwsectCUSize(".debug_loc", ldr.SymPkg(s), uint64(ldr.SymSize(s))) + } + } + sect.Length = uint64(state.datsize) - sect.Vaddr + state.checkdatsize(curType) + } +} + +type symNameSize struct { + name string + sz int64 + val int64 + sym loader.Sym +} + +func (state *dodataState) dodataSect(ctxt *Link, symn sym.SymKind, syms []loader.Sym) (result []loader.Sym, maxAlign int32) { + var head, tail loader.Sym + ldr := ctxt.loader + sl := make([]symNameSize, len(syms)) + for k, s := range syms { + ss := ldr.SymSize(s) + sl[k] = symNameSize{name: ldr.SymName(s), sz: ss, sym: s} + ds := int64(len(ldr.Data(s))) + switch { + case ss < ds: + ctxt.Errorf(s, "initialize bounds (%d < %d)", ss, ds) + case ss < 0: + ctxt.Errorf(s, "negative size (%d bytes)", ss) + case ss > cutoff: + ctxt.Errorf(s, "symbol too large (%d bytes)", ss) + } + + // If the usually-special section-marker symbols are being laid + // out as regular symbols, put them either at the beginning or + // end of their section. + if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + switch ldr.SymName(s) { + case "runtime.text", "runtime.bss", "runtime.data", "runtime.types", "runtime.rodata": + head = s + continue + case "runtime.etext", "runtime.ebss", "runtime.edata", "runtime.etypes", "runtime.erodata": + tail = s + continue + } + } + } + + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type sym.SELFGOT, so in that case + // we skip size comparison and fall through to the name + // comparison (conveniently, .got sorts before .toc). + checkSize := symn != sym.SELFGOT + + // Perform the sort. + if symn != sym.SPCLNTAB { + sort.Slice(sl, func(i, j int) bool { + si, sj := sl[i].sym, sl[j].sym + switch { + case si == head, sj == tail: + return true + case sj == head, si == tail: + return false + } + if checkSize { + isz := sl[i].sz + jsz := sl[j].sz + if isz != jsz { + return isz < jsz + } + } + iname := sl[i].name + jname := sl[j].name + if iname != jname { + return iname < jname + } + return si < sj + }) + } else { + // PCLNTAB was built internally, and already has the proper order. + } + + // Set alignment, construct result + syms = syms[:0] + for k := range sl { + s := sl[k].sym + if s != head && s != tail { + align := symalign(ldr, s) + if maxAlign < align { + maxAlign = align + } + } + syms = append(syms, s) + } + + return syms, maxAlign +} + +// Add buildid to beginning of text segment, on non-ELF systems. +// Non-ELF binary formats are not always flexible enough to +// give us a place to put the Go build ID. On those systems, we put it +// at the very beginning of the text segment. +// This “header” is read by cmd/go. +func (ctxt *Link) textbuildid() { + if ctxt.IsELF || ctxt.BuildMode == BuildModePlugin || *flagBuildid == "" { + return + } + + ldr := ctxt.loader + s := ldr.CreateSymForUpdate("go:buildid", 0) + // The \xff is invalid UTF-8, meant to make it less likely + // to find one of these accidentally. + data := "\xff Go build ID: " + strconv.Quote(*flagBuildid) + "\n \xff" + s.SetType(sym.STEXT) + s.SetData([]byte(data)) + s.SetSize(int64(len(data))) + + ctxt.Textp = append(ctxt.Textp, 0) + copy(ctxt.Textp[1:], ctxt.Textp) + ctxt.Textp[0] = s.Sym() +} + +func (ctxt *Link) buildinfo() { + if ctxt.linkShared || ctxt.BuildMode == BuildModePlugin { + // -linkshared and -buildmode=plugin get confused + // about the relocations in go.buildinfo + // pointing at the other data sections. + // The version information is only available in executables. + return + } + + // Write the buildinfo symbol, which go version looks for. + // The code reading this data is in package debug/buildinfo. + ldr := ctxt.loader + s := ldr.CreateSymForUpdate("go:buildinfo", 0) + s.SetType(sym.SBUILDINFO) + s.SetAlign(16) + // The \xff is invalid UTF-8, meant to make it less likely + // to find one of these accidentally. + const prefix = "\xff Go buildinf:" // 14 bytes, plus 2 data bytes filled in below + data := make([]byte, 32) + copy(data, prefix) + data[len(prefix)] = byte(ctxt.Arch.PtrSize) + data[len(prefix)+1] = 0 + if ctxt.Arch.ByteOrder == binary.BigEndian { + data[len(prefix)+1] = 1 + } + data[len(prefix)+1] |= 2 // signals new pointer-free format + data = appendString(data, strdata["runtime.buildVersion"]) + data = appendString(data, strdata["runtime.modinfo"]) + // MacOS linker gets very upset if the size os not a multiple of alignment. + for len(data)%16 != 0 { + data = append(data, 0) + } + s.SetData(data) + s.SetSize(int64(len(data))) + + // Add reference to go:buildinfo from the rodata section, + // so that external linking with -Wl,--gc-sections does not + // delete the build info. + sr := ldr.CreateSymForUpdate("go:buildinfo.ref", 0) + sr.SetType(sym.SRODATA) + sr.SetAlign(int32(ctxt.Arch.PtrSize)) + sr.AddAddr(ctxt.Arch, s.Sym()) +} + +// appendString appends s to data, prefixed by its varint-encoded length. +func appendString(data []byte, s string) []byte { + var v [binary.MaxVarintLen64]byte + n := binary.PutUvarint(v[:], uint64(len(s))) + data = append(data, v[:n]...) + data = append(data, s...) + return data +} + +// assign addresses to text +func (ctxt *Link) textaddress() { + addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05) + + // Assign PCs in text segment. + // Could parallelize, by assigning to text + // and then letting threads copy down, but probably not worth it. + sect := Segtext.Sections[0] + + sect.Align = int32(Funcalign) + + ldr := ctxt.loader + + text := ctxt.xdefine("runtime.text", sym.STEXT, 0) + etext := ctxt.xdefine("runtime.etext", sym.STEXT, 0) + ldr.SetSymSect(text, sect) + if ctxt.IsAIX() && ctxt.IsExternal() { + // Setting runtime.text has a real symbol prevents ld to + // change its base address resulting in wrong offsets for + // reflect methods. + u := ldr.MakeSymbolUpdater(text) + u.SetAlign(sect.Align) + u.SetSize(8) + } + + if (ctxt.DynlinkingGo() && ctxt.IsDarwin()) || (ctxt.IsAIX() && ctxt.IsExternal()) { + ldr.SetSymSect(etext, sect) + ctxt.Textp = append(ctxt.Textp, etext, 0) + copy(ctxt.Textp[1:], ctxt.Textp) + ctxt.Textp[0] = text + } + + start := uint64(Rnd(*FlagTextAddr, int64(Funcalign))) + va := start + n := 1 + sect.Vaddr = va + + limit := thearch.TrampLimit + if limit == 0 { + limit = 1 << 63 // unlimited + } + if *FlagDebugTextSize != 0 { + limit = uint64(*FlagDebugTextSize) + } + if *FlagDebugTramp > 1 { + limit = 1 // debug mode, force generating trampolines for everything + } + + if ctxt.IsAIX() && ctxt.IsExternal() { + // On AIX, normally we won't generate direct calls to external symbols, + // except in one test, cmd/go/testdata/script/link_syso_issue33139.txt. + // That test doesn't make much sense, and I'm not sure it ever works. + // Just generate trampoline for now (which will turn a direct call to + // an indirect call, which at least builds). + limit = 1 + } + + // First pass: assign addresses assuming the program is small and + // don't generate trampolines. + big := false + for _, s := range ctxt.Textp { + sect, n, va = assignAddress(ctxt, sect, n, s, va, false, big) + if va-start >= limit { + big = true + break + } + } + + // Second pass: only if it is too big, insert trampolines for too-far + // jumps and targets with unknown addresses. + if big { + // reset addresses + for _, s := range ctxt.Textp { + if ldr.OuterSym(s) != 0 || s == text { + continue + } + oldv := ldr.SymValue(s) + for sub := s; sub != 0; sub = ldr.SubSym(sub) { + ldr.SetSymValue(sub, ldr.SymValue(sub)-oldv) + } + } + va = start + + ntramps := 0 + for _, s := range ctxt.Textp { + sect, n, va = assignAddress(ctxt, sect, n, s, va, false, big) + + trampoline(ctxt, s) // resolve jumps, may add trampolines if jump too far + + // lay down trampolines after each function + for ; ntramps < len(ctxt.tramps); ntramps++ { + tramp := ctxt.tramps[ntramps] + if ctxt.IsAIX() && strings.HasPrefix(ldr.SymName(tramp), "runtime.text.") { + // Already set in assignAddress + continue + } + sect, n, va = assignAddress(ctxt, sect, n, tramp, va, true, big) + } + } + + // merge tramps into Textp, keeping Textp in address order + if ntramps != 0 { + newtextp := make([]loader.Sym, 0, len(ctxt.Textp)+ntramps) + i := 0 + for _, s := range ctxt.Textp { + for ; i < ntramps && ldr.SymValue(ctxt.tramps[i]) < ldr.SymValue(s); i++ { + newtextp = append(newtextp, ctxt.tramps[i]) + } + newtextp = append(newtextp, s) + } + newtextp = append(newtextp, ctxt.tramps[i:ntramps]...) + + ctxt.Textp = newtextp + } + } + + sect.Length = va - sect.Vaddr + ldr.SetSymSect(etext, sect) + if ldr.SymValue(etext) == 0 { + // Set the address of the start/end symbols, if not already + // (i.e. not darwin+dynlink or AIX+external, see above). + ldr.SetSymValue(etext, int64(va)) + ldr.SetSymValue(text, int64(Segtext.Sections[0].Vaddr)) + } +} + +// assigns address for a text symbol, returns (possibly new) section, its number, and the address. +func assignAddress(ctxt *Link, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp, big bool) (*sym.Section, int, uint64) { + ldr := ctxt.loader + if thearch.AssignAddress != nil { + return thearch.AssignAddress(ldr, sect, n, s, va, isTramp) + } + + ldr.SetSymSect(s, sect) + if ldr.AttrSubSymbol(s) { + return sect, n, va + } + + align := ldr.SymAlign(s) + if align == 0 { + align = int32(Funcalign) + } + va = uint64(Rnd(int64(va), int64(align))) + if sect.Align < align { + sect.Align = align + } + + funcsize := uint64(MINFUNC) // spacing required for findfunctab + if ldr.SymSize(s) > MINFUNC { + funcsize = uint64(ldr.SymSize(s)) + } + + // If we need to split text sections, and this function doesn't fit in the current + // section, then create a new one. + // + // Only break at outermost syms. + if big && splitTextSections(ctxt) && ldr.OuterSym(s) == 0 { + // For debugging purposes, allow text size limit to be cranked down, + // so as to stress test the code that handles multiple text sections. + var textSizelimit uint64 = thearch.TrampLimit + if *FlagDebugTextSize != 0 { + textSizelimit = uint64(*FlagDebugTextSize) + } + + // Sanity check: make sure the limit is larger than any + // individual text symbol. + if funcsize > textSizelimit { + panic(fmt.Sprintf("error: text size limit %d less than text symbol %s size of %d", textSizelimit, ldr.SymName(s), funcsize)) + } + + if va-sect.Vaddr+funcsize+maxSizeTrampolines(ctxt, ldr, s, isTramp) > textSizelimit { + sectAlign := int32(thearch.Funcalign) + if ctxt.IsPPC64() { + // Align the next text section to the worst case function alignment likely + // to be encountered when processing function symbols. The start address + // is rounded against the final alignment of the text section later on in + // (*Link).address. This may happen due to usage of PCALIGN directives + // larger than Funcalign, or usage of ISA 3.1 prefixed instructions + // (see ISA 3.1 Book I 1.9). + const ppc64maxFuncalign = 64 + sectAlign = ppc64maxFuncalign + va = uint64(Rnd(int64(va), ppc64maxFuncalign)) + } + + // Set the length for the previous text section + sect.Length = va - sect.Vaddr + + // Create new section, set the starting Vaddr + sect = addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05) + + sect.Vaddr = va + sect.Align = sectAlign + ldr.SetSymSect(s, sect) + + // Create a symbol for the start of the secondary text sections + ntext := ldr.CreateSymForUpdate(fmt.Sprintf("runtime.text.%d", n), 0) + ntext.SetSect(sect) + if ctxt.IsAIX() { + // runtime.text.X must be a real symbol on AIX. + // Assign its address directly in order to be the + // first symbol of this new section. + ntext.SetType(sym.STEXT) + ntext.SetSize(int64(MINFUNC)) + ntext.SetOnList(true) + ntext.SetAlign(sectAlign) + ctxt.tramps = append(ctxt.tramps, ntext.Sym()) + + ntext.SetValue(int64(va)) + va += uint64(ntext.Size()) + + if align := ldr.SymAlign(s); align != 0 { + va = uint64(Rnd(int64(va), int64(align))) + } else { + va = uint64(Rnd(int64(va), int64(Funcalign))) + } + } + n++ + } + } + + ldr.SetSymValue(s, 0) + for sub := s; sub != 0; sub = ldr.SubSym(sub) { + ldr.SetSymValue(sub, ldr.SymValue(sub)+int64(va)) + if ctxt.Debugvlog > 2 { + fmt.Println("assign text address:", ldr.SymName(sub), ldr.SymValue(sub)) + } + } + + va += funcsize + + return sect, n, va +} + +// Return whether we may need to split text sections. +// +// On PPC64x, when external linking, a text section should not be +// larger than 2^25 bytes due to the size of call target offset field +// in the 'bl' instruction. Splitting into smaller text sections +// smaller than this limit allows the system linker to modify the long +// calls appropriately. The limit allows for the space needed for +// tables inserted by the linker. +// +// The same applies to Darwin/ARM64, with 2^27 byte threshold. +// +// Similarly for ARM, we split sections (at 2^25 bytes) to avoid +// inconsistencies between the Go linker's reachability calculations +// (e.g. will direct call from X to Y need a trampoline) and similar +// machinery in the external linker; see #58425 for more on the +// history here. +func splitTextSections(ctxt *Link) bool { + return (ctxt.IsARM() || ctxt.IsPPC64() || (ctxt.IsARM64() && ctxt.IsDarwin())) && ctxt.IsExternal() +} + +// On Wasm, we reserve 4096 bytes for zero page, then 8192 bytes for wasm_exec.js +// to store command line args and environment variables. +// Data sections starts from at least address 12288. +// Keep in sync with wasm_exec.js. +const wasmMinDataAddr = 4096 + 8192 + +// address assigns virtual addresses to all segments and sections and +// returns all segments in file order. +func (ctxt *Link) address() []*sym.Segment { + var order []*sym.Segment // Layout order + + va := uint64(*FlagTextAddr) + order = append(order, &Segtext) + Segtext.Rwx = 05 + Segtext.Vaddr = va + for i, s := range Segtext.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + + if ctxt.IsWasm() && i == 0 && va < wasmMinDataAddr { + va = wasmMinDataAddr + } + } + + Segtext.Length = va - uint64(*FlagTextAddr) + + if len(Segrodata.Sections) > 0 { + // align to page boundary so as not to mix + // rodata and executable text. + // + // Note: gold or GNU ld will reduce the size of the executable + // file by arranging for the relro segment to end at a page + // boundary, and overlap the end of the text segment with the + // start of the relro segment in the file. The PT_LOAD segments + // will be such that the last page of the text segment will be + // mapped twice, once r-x and once starting out rw- and, after + // relocation processing, changed to r--. + // + // Ideally the last page of the text segment would not be + // writable even for this short period. + va = uint64(Rnd(int64(va), int64(*FlagRound))) + + order = append(order, &Segrodata) + Segrodata.Rwx = 04 + Segrodata.Vaddr = va + for _, s := range Segrodata.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrodata.Length = va - Segrodata.Vaddr + } + if len(Segrelrodata.Sections) > 0 { + // align to page boundary so as not to mix + // rodata, rel-ro data, and executable text. + va = uint64(Rnd(int64(va), int64(*FlagRound))) + if ctxt.HeadType == objabi.Haix { + // Relro data are inside data segment on AIX. + va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE) + } + + order = append(order, &Segrelrodata) + Segrelrodata.Rwx = 06 + Segrelrodata.Vaddr = va + for _, s := range Segrelrodata.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrelrodata.Length = va - Segrelrodata.Vaddr + } + + va = uint64(Rnd(int64(va), int64(*FlagRound))) + if ctxt.HeadType == objabi.Haix && len(Segrelrodata.Sections) == 0 { + // Data sections are moved to an unreachable segment + // to ensure that they are position-independent. + // Already done if relro sections exist. + va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE) + } + order = append(order, &Segdata) + Segdata.Rwx = 06 + Segdata.Vaddr = va + var data *sym.Section + var noptr *sym.Section + var bss *sym.Section + var noptrbss *sym.Section + var fuzzCounters *sym.Section + for i, s := range Segdata.Sections { + if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && s.Name == ".tbss" { + continue + } + vlen := int64(s.Length) + if i+1 < len(Segdata.Sections) && !((ctxt.IsELF || ctxt.HeadType == objabi.Haix) && Segdata.Sections[i+1].Name == ".tbss") { + vlen = int64(Segdata.Sections[i+1].Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + Segdata.Length = va - Segdata.Vaddr + switch s.Name { + case ".data": + data = s + case ".noptrdata": + noptr = s + case ".bss": + bss = s + case ".noptrbss": + noptrbss = s + case ".go.fuzzcntrs": + fuzzCounters = s + } + } + + // Assign Segdata's Filelen omitting the BSS. We do this here + // simply because right now we know where the BSS starts. + Segdata.Filelen = bss.Vaddr - Segdata.Vaddr + + va = uint64(Rnd(int64(va), int64(*FlagRound))) + order = append(order, &Segdwarf) + Segdwarf.Rwx = 06 + Segdwarf.Vaddr = va + for i, s := range Segdwarf.Sections { + vlen := int64(s.Length) + if i+1 < len(Segdwarf.Sections) { + vlen = int64(Segdwarf.Sections[i+1].Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + if ctxt.HeadType == objabi.Hwindows { + va = uint64(Rnd(int64(va), PEFILEALIGN)) + } + Segdwarf.Length = va - Segdwarf.Vaddr + } + + ldr := ctxt.loader + var ( + rodata = ldr.SymSect(ldr.LookupOrCreateSym("runtime.rodata", 0)) + symtab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.symtab", 0)) + pclntab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0)) + types = ldr.SymSect(ldr.LookupOrCreateSym("runtime.types", 0)) + ) + + for _, s := range ctxt.datap { + if sect := ldr.SymSect(s); sect != nil { + ldr.AddToSymValue(s, int64(sect.Vaddr)) + } + v := ldr.SymValue(s) + for sub := ldr.SubSym(s); sub != 0; sub = ldr.SubSym(sub) { + ldr.AddToSymValue(sub, v) + } + } + + for _, si := range dwarfp { + for _, s := range si.syms { + if sect := ldr.SymSect(s); sect != nil { + ldr.AddToSymValue(s, int64(sect.Vaddr)) + } + sub := ldr.SubSym(s) + if sub != 0 { + panic(fmt.Sprintf("unexpected sub-sym for %s %s", ldr.SymName(s), ldr.SymType(s).String())) + } + v := ldr.SymValue(s) + for ; sub != 0; sub = ldr.SubSym(sub) { + ldr.AddToSymValue(s, v) + } + } + } + + if ctxt.BuildMode == BuildModeShared { + s := ldr.LookupOrCreateSym("go:link.abihashbytes", 0) + sect := ldr.SymSect(ldr.LookupOrCreateSym(".note.go.abihash", 0)) + ldr.SetSymSect(s, sect) + ldr.SetSymValue(s, int64(sect.Vaddr+16)) + } + + // If there are multiple text sections, create runtime.text.n for + // their section Vaddr, using n for index + n := 1 + for _, sect := range Segtext.Sections[1:] { + if sect.Name != ".text" { + break + } + symname := fmt.Sprintf("runtime.text.%d", n) + if ctxt.HeadType != objabi.Haix || ctxt.LinkMode != LinkExternal { + // Addresses are already set on AIX with external linker + // because these symbols are part of their sections. + ctxt.xdefine(symname, sym.STEXT, int64(sect.Vaddr)) + } + n++ + } + + ctxt.xdefine("runtime.rodata", sym.SRODATA, int64(rodata.Vaddr)) + ctxt.xdefine("runtime.erodata", sym.SRODATA, int64(rodata.Vaddr+rodata.Length)) + ctxt.xdefine("runtime.types", sym.SRODATA, int64(types.Vaddr)) + ctxt.xdefine("runtime.etypes", sym.SRODATA, int64(types.Vaddr+types.Length)) + + s := ldr.Lookup("runtime.gcdata", 0) + ldr.SetAttrLocal(s, true) + ctxt.xdefine("runtime.egcdata", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s)) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcdata", 0), ldr.SymSect(s)) + + s = ldr.LookupOrCreateSym("runtime.gcbss", 0) + ldr.SetAttrLocal(s, true) + ctxt.xdefine("runtime.egcbss", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s)) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcbss", 0), ldr.SymSect(s)) + + ctxt.xdefine("runtime.symtab", sym.SRODATA, int64(symtab.Vaddr)) + ctxt.xdefine("runtime.esymtab", sym.SRODATA, int64(symtab.Vaddr+symtab.Length)) + ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr)) + ctxt.defineInternal("runtime.pcheader", sym.SRODATA) + ctxt.defineInternal("runtime.funcnametab", sym.SRODATA) + ctxt.defineInternal("runtime.cutab", sym.SRODATA) + ctxt.defineInternal("runtime.filetab", sym.SRODATA) + ctxt.defineInternal("runtime.pctab", sym.SRODATA) + ctxt.defineInternal("runtime.functab", sym.SRODATA) + ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length)) + ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr)) + ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length)) + ctxt.xdefine("runtime.bss", sym.SBSS, int64(bss.Vaddr)) + ctxt.xdefine("runtime.ebss", sym.SBSS, int64(bss.Vaddr+bss.Length)) + ctxt.xdefine("runtime.data", sym.SDATA, int64(data.Vaddr)) + ctxt.xdefine("runtime.edata", sym.SDATA, int64(data.Vaddr+data.Length)) + ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr)) + ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) + ctxt.xdefine("runtime.covctrs", sym.SCOVERAGE_COUNTER, int64(noptrbss.Vaddr+covCounterDataStartOff)) + ctxt.xdefine("runtime.ecovctrs", sym.SCOVERAGE_COUNTER, int64(noptrbss.Vaddr+covCounterDataStartOff+covCounterDataLen)) + ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length)) + + if fuzzCounters != nil { + ctxt.xdefine("runtime.__start___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr)) + ctxt.xdefine("runtime.__stop___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length)) + ctxt.xdefine("internal/fuzz._counters", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr)) + ctxt.xdefine("internal/fuzz._ecounters", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length)) + } + + if ctxt.IsSolaris() { + // On Solaris, in the runtime it sets the external names of the + // end symbols. Unset them and define separate symbols, so we + // keep both. + etext := ldr.Lookup("runtime.etext", 0) + edata := ldr.Lookup("runtime.edata", 0) + end := ldr.Lookup("runtime.end", 0) + ldr.SetSymExtname(etext, "runtime.etext") + ldr.SetSymExtname(edata, "runtime.edata") + ldr.SetSymExtname(end, "runtime.end") + ctxt.xdefine("_etext", ldr.SymType(etext), ldr.SymValue(etext)) + ctxt.xdefine("_edata", ldr.SymType(edata), ldr.SymValue(edata)) + ctxt.xdefine("_end", ldr.SymType(end), ldr.SymValue(end)) + ldr.SetSymSect(ldr.Lookup("_etext", 0), ldr.SymSect(etext)) + ldr.SetSymSect(ldr.Lookup("_edata", 0), ldr.SymSect(edata)) + ldr.SetSymSect(ldr.Lookup("_end", 0), ldr.SymSect(end)) + } + + if ctxt.IsPPC64() && ctxt.IsElf() { + // Resolve .TOC. symbols for all objects. Only one TOC region is supported. If a + // GOT section is present, compute it as suggested by the ELFv2 ABI. Otherwise, + // choose a similar offset from the start of the data segment. + tocAddr := int64(Segdata.Vaddr) + 0x8000 + if gotAddr := ldr.SymValue(ctxt.GOT); gotAddr != 0 { + tocAddr = gotAddr + 0x8000 + } + for i := range ctxt.DotTOC { + if i >= sym.SymVerABICount && i < sym.SymVerStatic { // these versions are not used currently + continue + } + if toc := ldr.Lookup(".TOC.", i); toc != 0 { + ldr.SetSymValue(toc, tocAddr) + } + } + } + + return order +} + +// layout assigns file offsets and lengths to the segments in order. +// Returns the file size containing all the segments. +func (ctxt *Link) layout(order []*sym.Segment) uint64 { + var prev *sym.Segment + for _, seg := range order { + if prev == nil { + seg.Fileoff = uint64(HEADR) + } else { + switch ctxt.HeadType { + default: + // Assuming the previous segment was + // aligned, the following rounding + // should ensure that this segment's + // VA ≡ Fileoff mod FlagRound. + seg.Fileoff = uint64(Rnd(int64(prev.Fileoff+prev.Filelen), int64(*FlagRound))) + if seg.Vaddr%uint64(*FlagRound) != seg.Fileoff%uint64(*FlagRound) { + Exitf("bad segment rounding (Vaddr=%#x Fileoff=%#x FlagRound=%#x)", seg.Vaddr, seg.Fileoff, *FlagRound) + } + case objabi.Hwindows: + seg.Fileoff = prev.Fileoff + uint64(Rnd(int64(prev.Filelen), PEFILEALIGN)) + case objabi.Hplan9: + seg.Fileoff = prev.Fileoff + prev.Filelen + } + } + if seg != &Segdata { + // Link.address already set Segdata.Filelen to + // account for BSS. + seg.Filelen = seg.Length + } + prev = seg + } + return prev.Fileoff + prev.Filelen +} + +// add a trampoline with symbol s (to be laid down after the current function) +func (ctxt *Link) AddTramp(s *loader.SymbolBuilder) { + s.SetType(sym.STEXT) + s.SetReachable(true) + s.SetOnList(true) + ctxt.tramps = append(ctxt.tramps, s.Sym()) + if *FlagDebugTramp > 0 && ctxt.Debugvlog > 0 { + ctxt.Logf("trampoline %s inserted\n", s.Name()) + } +} + +// compressSyms compresses syms and returns the contents of the +// compressed section. If the section would get larger, it returns nil. +func compressSyms(ctxt *Link, syms []loader.Sym) []byte { + ldr := ctxt.loader + var total int64 + for _, sym := range syms { + total += ldr.SymSize(sym) + } + + var buf bytes.Buffer + if ctxt.IsELF { + switch ctxt.Arch.PtrSize { + case 8: + binary.Write(&buf, ctxt.Arch.ByteOrder, elf.Chdr64{ + Type: uint32(elf.COMPRESS_ZLIB), + Size: uint64(total), + Addralign: uint64(ctxt.Arch.Alignment), + }) + case 4: + binary.Write(&buf, ctxt.Arch.ByteOrder, elf.Chdr32{ + Type: uint32(elf.COMPRESS_ZLIB), + Size: uint32(total), + Addralign: uint32(ctxt.Arch.Alignment), + }) + default: + log.Fatalf("can't compress header size:%d", ctxt.Arch.PtrSize) + } + } else { + buf.Write([]byte("ZLIB")) + var sizeBytes [8]byte + binary.BigEndian.PutUint64(sizeBytes[:], uint64(total)) + buf.Write(sizeBytes[:]) + } + + var relocbuf []byte // temporary buffer for applying relocations + + // Using zlib.BestSpeed achieves very nearly the same + // compression levels of zlib.DefaultCompression, but takes + // substantially less time. This is important because DWARF + // compression can be a significant fraction of link time. + z, err := zlib.NewWriterLevel(&buf, zlib.BestSpeed) + if err != nil { + log.Fatalf("NewWriterLevel failed: %s", err) + } + st := ctxt.makeRelocSymState() + for _, s := range syms { + // Symbol data may be read-only. Apply relocations in a + // temporary buffer, and immediately write it out. + P := ldr.Data(s) + relocs := ldr.Relocs(s) + if relocs.Count() != 0 { + relocbuf = append(relocbuf[:0], P...) + P = relocbuf + st.relocsym(s, P) + } + if _, err := z.Write(P); err != nil { + log.Fatalf("compression failed: %s", err) + } + for i := ldr.SymSize(s) - int64(len(P)); i > 0; { + b := zeros[:] + if i < int64(len(b)) { + b = b[:i] + } + n, err := z.Write(b) + if err != nil { + log.Fatalf("compression failed: %s", err) + } + i -= int64(n) + } + } + if err := z.Close(); err != nil { + log.Fatalf("compression failed: %s", err) + } + if int64(buf.Len()) >= total { + // Compression didn't save any space. + return nil + } + return buf.Bytes() +} diff --git a/src/cmd/link/internal/ld/data_test.go b/src/cmd/link/internal/ld/data_test.go new file mode 100644 index 0000000..f91493b --- /dev/null +++ b/src/cmd/link/internal/ld/data_test.go @@ -0,0 +1,93 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "internal/buildcfg" + "testing" +) + +func setUpContext(arch *sys.Arch, iself bool, ht objabi.HeadType, bm, lm string) *Link { + ctxt := linknew(arch) + edummy := func(str string, off int) {} + ctxt.HeadType = ht + er := loader.ErrorReporter{} + ctxt.loader = loader.NewLoader(0, edummy, &er) + ctxt.BuildMode.Set(bm) + ctxt.LinkMode.Set(lm) + ctxt.IsELF = iself + ctxt.mustSetHeadType() + ctxt.setArchSyms() + return ctxt +} + +// Make sure the addgotsym properly increases the symbols. +func TestAddGotSym(t *testing.T) { + tests := []struct { + arch *sys.Arch + ht objabi.HeadType + bm, lm string + rel string + relsize int + gotsize int + }{ + { + arch: sys.Arch386, + ht: objabi.Hlinux, + bm: "pie", + lm: "internal", + rel: ".rel", + relsize: 2 * sys.Arch386.PtrSize, + gotsize: sys.Arch386.PtrSize, + }, + { + arch: sys.ArchAMD64, + ht: objabi.Hlinux, + bm: "pie", + lm: "internal", + rel: ".rela", + relsize: 3 * sys.ArchAMD64.PtrSize, + gotsize: sys.ArchAMD64.PtrSize, + }, + { + arch: sys.ArchAMD64, + ht: objabi.Hdarwin, + bm: "pie", + lm: "external", + gotsize: sys.ArchAMD64.PtrSize, + }, + } + + // Save the architecture as we're going to set it on each test run. + origArch := buildcfg.GOARCH + defer func() { + buildcfg.GOARCH = origArch + }() + + for i, test := range tests { + iself := len(test.rel) != 0 + buildcfg.GOARCH = test.arch.Name + ctxt := setUpContext(test.arch, iself, test.ht, test.bm, test.lm) + foo := ctxt.loader.CreateSymForUpdate("foo", 0) + ctxt.loader.CreateExtSym("bar", 0) + AddGotSym(&ctxt.Target, ctxt.loader, &ctxt.ArchSyms, foo.Sym(), 0) + + if iself { + rel := ctxt.loader.Lookup(test.rel, 0) + if rel == 0 { + t.Fatalf("[%d] could not find symbol: %q", i, test.rel) + } + if s := ctxt.loader.SymSize(rel); s != int64(test.relsize) { + t.Fatalf("[%d] expected ldr.Size(%q) == %v, got %v", i, test.rel, test.relsize, s) + } + } + if s := ctxt.loader.SymSize(ctxt.loader.Lookup(".got", 0)); s != int64(test.gotsize) { + t.Fatalf(`[%d] expected ldr.Size(".got") == %v, got %v`, i, test.gotsize, s) + } + } +} diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go new file mode 100644 index 0000000..0738a51 --- /dev/null +++ b/src/cmd/link/internal/ld/deadcode.go @@ -0,0 +1,469 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "internal/buildcfg" + "unicode" +) + +var _ = fmt.Print + +type deadcodePass struct { + ctxt *Link + ldr *loader.Loader + wq heap // work queue, using min-heap for better locality + + ifaceMethod map[methodsig]bool // methods called from reached interface call sites + genericIfaceMethod map[string]bool // names of methods called from reached generic interface call sites + markableMethods []methodref // methods of reached types + reflectSeen bool // whether we have seen a reflect method call + dynlink bool + + methodsigstmp []methodsig // scratch buffer for decoding method signatures +} + +func (d *deadcodePass) init() { + d.ldr.InitReachable() + d.ifaceMethod = make(map[methodsig]bool) + d.genericIfaceMethod = make(map[string]bool) + if buildcfg.Experiment.FieldTrack { + d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym()) + } + d.dynlink = d.ctxt.DynlinkingGo() + + if d.ctxt.BuildMode == BuildModeShared { + // Mark all symbols defined in this library as reachable when + // building a shared library. + n := d.ldr.NDef() + for i := 1; i < n; i++ { + s := loader.Sym(i) + d.mark(s, 0) + } + return + } + + var names []string + + // In a normal binary, start at main.main and the init + // functions and mark what is reachable from there. + if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + names = append(names, "main.main", "main..inittask") + } else { + // The external linker refers main symbol directly. + if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 { + *flagEntrySymbol = "_main" + } else { + *flagEntrySymbol = "main" + } + } + names = append(names, *flagEntrySymbol) + } + // runtime.unreachableMethod is a function that will throw if called. + // We redirect unreachable methods to it. + names = append(names, "runtime.unreachableMethod") + if d.ctxt.BuildMode == BuildModePlugin { + names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go:plugin.tabs") + + // We don't keep the go.plugin.exports symbol, + // but we do keep the symbols it refers to. + exportsIdx := d.ldr.Lookup("go:plugin.exports", 0) + if exportsIdx != 0 { + relocs := d.ldr.Relocs(exportsIdx) + for i := 0; i < relocs.Count(); i++ { + d.mark(relocs.At(i).Sym(), 0) + } + } + } + + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("deadcode start names: %v\n", names) + } + + for _, name := range names { + // Mark symbol as a data/ABI0 symbol. + d.mark(d.ldr.Lookup(name, 0), 0) + if abiInternalVer != 0 { + // Also mark any Go functions (internal ABI). + d.mark(d.ldr.Lookup(name, abiInternalVer), 0) + } + } + + // All dynamic exports are roots. + for _, s := range d.ctxt.dynexp { + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("deadcode start dynexp: %s<%d>\n", d.ldr.SymName(s), d.ldr.SymVersion(s)) + } + d.mark(s, 0) + } +} + +func (d *deadcodePass) flood() { + var methods []methodref + for !d.wq.empty() { + symIdx := d.wq.pop() + + d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx) + + isgotype := d.ldr.IsGoType(symIdx) + relocs := d.ldr.Relocs(symIdx) + var usedInIface bool + + if isgotype { + if d.dynlink { + // When dynamic linking, a type may be passed across DSO + // boundary and get converted to interface at the other side. + d.ldr.SetAttrUsedInIface(symIdx, true) + } + usedInIface = d.ldr.AttrUsedInIface(symIdx) + } + + methods = methods[:0] + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + // When build with "-linkshared", we can't tell if the interface + // method in itab will be used or not. Ignore the weak attribute. + if r.Weak() && !(d.ctxt.linkShared && d.ldr.IsItab(symIdx)) { + continue + } + t := r.Type() + switch t { + case objabi.R_METHODOFF: + if i+2 >= relocs.Count() { + panic("expect three consecutive R_METHODOFF relocs") + } + if usedInIface { + methods = append(methods, methodref{src: symIdx, r: i}) + // The method descriptor is itself a type descriptor, and + // it can be used to reach other types, e.g. by using + // reflect.Type.Method(i).Type.In(j). We need to traverse + // its child types with UsedInIface set. (See also the + // comment below.) + rs := r.Sym() + if !d.ldr.AttrUsedInIface(rs) { + d.ldr.SetAttrUsedInIface(rs, true) + if d.ldr.AttrReachable(rs) { + d.ldr.SetAttrReachable(rs, false) + d.mark(rs, symIdx) + } + } + } + i += 2 + continue + case objabi.R_USETYPE: + // type symbol used for DWARF. we need to load the symbol but it may not + // be otherwise reachable in the program. + // do nothing for now as we still load all type symbols. + continue + case objabi.R_USEIFACE: + // R_USEIFACE is a marker relocation that tells the linker the type is + // converted to an interface, i.e. should have UsedInIface set. See the + // comment below for why we need to unset the Reachable bit and re-mark it. + rs := r.Sym() + if !d.ldr.AttrUsedInIface(rs) { + d.ldr.SetAttrUsedInIface(rs, true) + if d.ldr.AttrReachable(rs) { + d.ldr.SetAttrReachable(rs, false) + d.mark(rs, symIdx) + } + } + continue + case objabi.R_USEIFACEMETHOD: + // R_USEIFACEMETHOD is a marker relocation that marks an interface + // method as used. + rs := r.Sym() + if d.ctxt.linkShared && (d.ldr.SymType(rs) == sym.SDYNIMPORT || d.ldr.SymType(rs) == sym.Sxxx) { + // Don't decode symbol from shared library (we'll mark all exported methods anyway). + // We check for both SDYNIMPORT and Sxxx because name-mangled symbols haven't + // been resolved at this point. + continue + } + m := d.decodeIfaceMethod(d.ldr, d.ctxt.Arch, rs, r.Add()) + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("reached iface method: %v\n", m) + } + d.ifaceMethod[m] = true + continue + case objabi.R_USEGENERICIFACEMETHOD: + name := d.decodeGenericIfaceMethod(d.ldr, r.Sym()) + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("reached generic iface method: %s\n", name) + } + d.genericIfaceMethod[name] = true + continue // don't mark referenced symbol - it is not needed in the final binary. + } + rs := r.Sym() + if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) { + // If a type is converted to an interface, it is possible to obtain an + // interface with a "child" type of it using reflection (e.g. obtain an + // interface of T from []chan T). We need to traverse its "child" types + // with UsedInIface attribute set. + // When visiting the child type (chan T in the example above), it will + // have UsedInIface set, so it in turn will mark and (re)visit its children + // (e.g. T above). + // We unset the reachable bit here, so if the child type is already visited, + // it will be visited again. + // Note that a type symbol can be visited at most twice, one without + // UsedInIface and one with. So termination is still guaranteed. + d.ldr.SetAttrUsedInIface(rs, true) + d.ldr.SetAttrReachable(rs, false) + } + d.mark(rs, symIdx) + } + naux := d.ldr.NAux(symIdx) + for i := 0; i < naux; i++ { + a := d.ldr.Aux(symIdx, i) + if a.Type() == goobj.AuxGotype { + // A symbol being reachable doesn't imply we need its + // type descriptor. Don't mark it. + continue + } + d.mark(a.Sym(), symIdx) + } + // Some host object symbols have an outer object, which acts like a + // "carrier" symbol, or it holds all the symbols for a particular + // section. We need to mark all "referenced" symbols from that carrier, + // so we make sure we're pulling in all outer symbols, and their sub + // symbols. This is not ideal, and these carrier/section symbols could + // be removed. + if d.ldr.IsExternal(symIdx) { + d.mark(d.ldr.OuterSym(symIdx), symIdx) + d.mark(d.ldr.SubSym(symIdx), symIdx) + } + + if len(methods) != 0 { + if !isgotype { + panic("method found on non-type symbol") + } + // Decode runtime type information for type methods + // to help work out which methods can be called + // dynamically via interfaces. + methodsigs := d.decodetypeMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs) + if len(methods) != len(methodsigs) { + panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs))) + } + for i, m := range methodsigs { + methods[i].m = m + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("markable method: %v of sym %v %s\n", m, symIdx, d.ldr.SymName(symIdx)) + } + } + d.markableMethods = append(d.markableMethods, methods...) + } + } +} + +func (d *deadcodePass) mark(symIdx, parent loader.Sym) { + if symIdx != 0 && !d.ldr.AttrReachable(symIdx) { + d.wq.push(symIdx) + d.ldr.SetAttrReachable(symIdx, true) + if buildcfg.Experiment.FieldTrack && d.ldr.Reachparent[symIdx] == 0 { + d.ldr.Reachparent[symIdx] = parent + } + if *flagDumpDep { + to := d.ldr.SymName(symIdx) + if to != "" { + if d.ldr.AttrUsedInIface(symIdx) { + to += " <UsedInIface>" + } + from := "_" + if parent != 0 { + from = d.ldr.SymName(parent) + if d.ldr.AttrUsedInIface(parent) { + from += " <UsedInIface>" + } + } + fmt.Printf("%s -> %s\n", from, to) + } + } + } +} + +func (d *deadcodePass) markMethod(m methodref) { + relocs := d.ldr.Relocs(m.src) + d.mark(relocs.At(m.r).Sym(), m.src) + d.mark(relocs.At(m.r+1).Sym(), m.src) + d.mark(relocs.At(m.r+2).Sym(), m.src) +} + +// deadcode marks all reachable symbols. +// +// The basis of the dead code elimination is a flood fill of symbols, +// following their relocations, beginning at *flagEntrySymbol. +// +// This flood fill is wrapped in logic for pruning unused methods. +// All methods are mentioned by relocations on their receiver's *rtype. +// These relocations are specially defined as R_METHODOFF by the compiler +// so we can detect and manipulated them here. +// +// There are three ways a method of a reachable type can be invoked: +// +// 1. direct call +// 2. through a reachable interface type +// 3. reflect.Value.Method (or MethodByName), or reflect.Type.Method +// (or MethodByName) +// +// The first case is handled by the flood fill, a directly called method +// is marked as reachable. +// +// The second case is handled by decomposing all reachable interface +// types into method signatures. Each encountered method is compared +// against the interface method signatures, if it matches it is marked +// as reachable. This is extremely conservative, but easy and correct. +// +// The third case is handled by looking to see if any of: +// - reflect.Value.Method or MethodByName is reachable +// - reflect.Type.Method or MethodByName is called (through the +// REFLECTMETHOD attribute marked by the compiler). +// +// If any of these happen, all bets are off and all exported methods +// of reachable types are marked reachable. +// +// Any unreached text symbols are removed from ctxt.Textp. +func deadcode(ctxt *Link) { + ldr := ctxt.loader + d := deadcodePass{ctxt: ctxt, ldr: ldr} + d.init() + d.flood() + + methSym := ldr.Lookup("reflect.Value.Method", abiInternalVer) + methByNameSym := ldr.Lookup("reflect.Value.MethodByName", abiInternalVer) + + if ctxt.DynlinkingGo() { + // Exported methods may satisfy interfaces we don't know + // about yet when dynamically linking. + d.reflectSeen = true + } + + for { + // Methods might be called via reflection. Give up on + // static analysis, mark all exported methods of + // all reachable types as reachable. + d.reflectSeen = d.reflectSeen || (methSym != 0 && ldr.AttrReachable(methSym)) || (methByNameSym != 0 && ldr.AttrReachable(methByNameSym)) + + // Mark all methods that could satisfy a discovered + // interface as reachable. We recheck old marked interfaces + // as new types (with new methods) may have been discovered + // in the last pass. + rem := d.markableMethods[:0] + for _, m := range d.markableMethods { + if (d.reflectSeen && (m.isExported() || d.dynlink)) || d.ifaceMethod[m.m] || d.genericIfaceMethod[m.m.name] { + d.markMethod(m) + } else { + rem = append(rem, m) + } + } + d.markableMethods = rem + + if d.wq.empty() { + // No new work was discovered. Done. + break + } + d.flood() + } +} + +// methodsig is a typed method signature (name + type). +type methodsig struct { + name string + typ loader.Sym // type descriptor symbol of the function +} + +// methodref holds the relocations from a receiver type symbol to its +// method. There are three relocations, one for each of the fields in +// the reflect.method struct: mtyp, ifn, and tfn. +type methodref struct { + m methodsig + src loader.Sym // receiver type symbol + r int // the index of R_METHODOFF relocations +} + +func (m methodref) isExported() bool { + for _, r := range m.m.name { + return unicode.IsUpper(r) + } + panic("methodref has no signature") +} + +// decodeMethodSig decodes an array of method signature information. +// Each element of the array is size bytes. The first 4 bytes is a +// nameOff for the method name, and the next 4 bytes is a typeOff for +// the function type. +// +// Conveniently this is the layout of both runtime.method and runtime.imethod. +func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig { + if cap(d.methodsigstmp) < count { + d.methodsigstmp = append(d.methodsigstmp[:0], make([]methodsig, count)...) + } + var methods = d.methodsigstmp[:count] + for i := 0; i < count; i++ { + methods[i].name = decodetypeName(ldr, symIdx, relocs, off) + methods[i].typ = decodeRelocSym(ldr, symIdx, relocs, int32(off+4)) + off += size + } + return methods +} + +// Decode the method of interface type symbol symIdx at offset off. +func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig { + p := ldr.Data(symIdx) + if p == nil { + panic(fmt.Sprintf("missing symbol %q", ldr.SymName(symIdx))) + } + if decodetypeKind(arch, p)&kindMask != kindInterface { + panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx))) + } + relocs := ldr.Relocs(symIdx) + var m methodsig + m.name = decodetypeName(ldr, symIdx, &relocs, int(off)) + m.typ = decodeRelocSym(ldr, symIdx, &relocs, int32(off+4)) + return m +} + +// Decode the method name stored in symbol symIdx. The symbol should contain just the bytes of a method name. +func (d *deadcodePass) decodeGenericIfaceMethod(ldr *loader.Loader, symIdx loader.Sym) string { + return string(ldr.Data(symIdx)) +} + +func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { + p := ldr.Data(symIdx) + if !decodetypeHasUncommon(arch, p) { + panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx))) + } + off := commonsize(arch) // reflect.rtype + switch decodetypeKind(arch, p) & kindMask { + case kindStruct: // reflect.structType + off += 4 * arch.PtrSize + case kindPtr: // reflect.ptrType + off += arch.PtrSize + case kindFunc: // reflect.funcType + off += arch.PtrSize // 4 bytes, pointer aligned + case kindSlice: // reflect.sliceType + off += arch.PtrSize + case kindArray: // reflect.arrayType + off += 3 * arch.PtrSize + case kindChan: // reflect.chanType + off += 2 * arch.PtrSize + case kindMap: // reflect.mapType + off += 4*arch.PtrSize + 8 + case kindInterface: // reflect.interfaceType + off += 3 * arch.PtrSize + default: + // just Sizeof(rtype) + } + + mcount := int(decodeInuxi(arch, p[off+4:], 2)) + moff := int(decodeInuxi(arch, p[off+4+2+2:], 4)) + off += moff // offset to array of reflect.method values + const sizeofMethod = 4 * 4 // sizeof reflect.method in program + return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount) +} diff --git a/src/cmd/link/internal/ld/deadcode_test.go b/src/cmd/link/internal/ld/deadcode_test.go new file mode 100644 index 0000000..573bff3 --- /dev/null +++ b/src/cmd/link/internal/ld/deadcode_test.go @@ -0,0 +1,50 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "internal/testenv" + "path/filepath" + "testing" +) + +func TestDeadcode(t *testing.T) { + testenv.MustHaveGoBuild(t) + t.Parallel() + + tmpdir := t.TempDir() + + tests := []struct { + src string + pos, neg string // positive and negative patterns + }{ + {"reflectcall", "", "main.T.M"}, + {"typedesc", "", "type:main.T"}, + {"ifacemethod", "", "main.T.M"}, + {"ifacemethod2", "main.T.M", ""}, + {"ifacemethod3", "main.S.M", ""}, + {"ifacemethod4", "", "main.T.M"}, + } + for _, test := range tests { + test := test + t.Run(test.src, func(t *testing.T) { + t.Parallel() + src := filepath.Join("testdata", "deadcode", test.src+".go") + exe := filepath.Join(tmpdir, test.src+".exe") + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-dumpdep", "-o", exe, src) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%v: %v:\n%s", cmd.Args, err, out) + } + if test.pos != "" && !bytes.Contains(out, []byte(test.pos+"\n")) { + t.Errorf("%s should be reachable. Output:\n%s", test.pos, out) + } + if test.neg != "" && bytes.Contains(out, []byte(test.neg+"\n")) { + t.Errorf("%s should not be reachable. Output:\n%s", test.neg, out) + } + }) + } +} diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go new file mode 100644 index 0000000..b0f4b87 --- /dev/null +++ b/src/cmd/link/internal/ld/decodesym.go @@ -0,0 +1,310 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "encoding/binary" + "log" +) + +// Decoding the type.* symbols. This has to be in sync with +// ../../runtime/type.go, or more specifically, with what +// cmd/compile/internal/reflectdata/reflect.go stuffs in these. + +// tflag is documented in reflect/type.go. +// +// tflag values must be kept in sync with copies in: +// +// cmd/compile/internal/reflectdata/reflect.go +// cmd/link/internal/ld/decodesym.go +// reflect/type.go +// runtime/type.go +const ( + tflagUncommon = 1 << 0 + tflagExtraStar = 1 << 1 +) + +func decodeInuxi(arch *sys.Arch, p []byte, sz int) uint64 { + switch sz { + case 2: + return uint64(arch.ByteOrder.Uint16(p)) + case 4: + return uint64(arch.ByteOrder.Uint32(p)) + case 8: + return arch.ByteOrder.Uint64(p) + default: + Exitf("dwarf: decode inuxi %d", sz) + panic("unreachable") + } +} + +func commonsize(arch *sys.Arch) int { return 4*arch.PtrSize + 8 + 8 } // runtime._type +func structfieldSize(arch *sys.Arch) int { return 3 * arch.PtrSize } // runtime.structfield +func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype + +// Type.commonType.kind +func decodetypeKind(arch *sys.Arch, p []byte) uint8 { + return p[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f +} + +// Type.commonType.kind +func decodetypeUsegcprog(arch *sys.Arch, p []byte) uint8 { + return p[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f +} + +// Type.commonType.size +func decodetypeSize(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p, arch.PtrSize)) // 0x8 / 0x10 +} + +// Type.commonType.ptrdata +func decodetypePtrdata(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10 +} + +// Type.commonType.tflag +func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool { + return p[2*arch.PtrSize+4]&tflagUncommon != 0 +} + +// Type.FuncType.dotdotdot +func decodetypeFuncDotdotdot(arch *sys.Arch, p []byte) bool { + return uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2))&(1<<15) != 0 +} + +// Type.FuncType.inCount +func decodetypeFuncInCount(arch *sys.Arch, p []byte) int { + return int(decodeInuxi(arch, p[commonsize(arch):], 2)) +} + +func decodetypeFuncOutCount(arch *sys.Arch, p []byte) int { + return int(uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2)) & (1<<15 - 1)) +} + +// InterfaceType.methods.length +func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +// Matches runtime/typekind.go and reflect.Kind. +const ( + kindArray = 17 + kindChan = 18 + kindFunc = 19 + kindInterface = 20 + kindMap = 21 + kindPtr = 22 + kindSlice = 23 + kindStruct = 25 + kindMask = (1 << 5) - 1 +) + +func decodeReloc(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc { + for j := 0; j < relocs.Count(); j++ { + rel := relocs.At(j) + if rel.Off() == off { + return rel + } + } + return loader.Reloc{} +} + +func decodeRelocSym(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym { + return decodeReloc(ldr, symIdx, relocs, off).Sym() +} + +// decodetypeName decodes the name from a reflect.name. +func decodetypeName(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string { + r := decodeRelocSym(ldr, symIdx, relocs, int32(off)) + if r == 0 { + return "" + } + + data := ldr.Data(r) + nameLen, nameLenLen := binary.Uvarint(data[1:]) + return string(data[1+nameLenLen : 1+nameLenLen+int(nameLen)]) +} + +func decodetypeNameEmbedded(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) bool { + r := decodeRelocSym(ldr, symIdx, relocs, int32(off)) + if r == 0 { + return false + } + data := ldr.Data(r) + return data[0]&(1<<3) != 0 +} + +func decodetypeFuncInType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { + uadd := commonsize(arch) + 4 + if arch.PtrSize == 8 { + uadd += 4 + } + if decodetypeHasUncommon(arch, ldr.Data(symIdx)) { + uadd += uncommonSize() + } + return decodeRelocSym(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize)) +} + +func decodetypeFuncOutType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { + return decodetypeFuncInType(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx))) +} + +func decodetypeArrayElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeArrayLen(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int64 { + data := ldr.Data(symIdx) + return int64(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +func decodetypeChanElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeMapKey(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeMapValue(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38 +} + +func decodetypePtrElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeStructFieldCount(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int { + data := ldr.Data(symIdx) + return int(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +func decodetypeStructFieldArrayOff(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int { + data := ldr.Data(symIdx) + off := commonsize(arch) + 4*arch.PtrSize + if decodetypeHasUncommon(arch, data) { + off += uncommonSize() + } + off += i * structfieldSize(arch) + return off +} + +func decodetypeStructFieldName(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) string { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodetypeName(ldr, symIdx, &relocs, off) +} + +func decodetypeStructFieldType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(off+arch.PtrSize)) +} + +func decodetypeStructFieldOffset(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + data := ldr.Data(symIdx) + return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize)) +} + +func decodetypeStructFieldEmbedded(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) bool { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodetypeNameEmbedded(ldr, symIdx, &relocs, off) +} + +// decodetypeStr returns the contents of an rtype's str field (a nameOff). +func decodetypeStr(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) string { + relocs := ldr.Relocs(symIdx) + str := decodetypeName(ldr, symIdx, &relocs, 4*arch.PtrSize+8) + data := ldr.Data(symIdx) + if data[2*arch.PtrSize+4]&tflagExtraStar != 0 { + return str[1:] + } + return str +} + +func decodetypeGcmask(ctxt *Link, s loader.Sym) []byte { + if ctxt.loader.SymType(s) == sym.SDYNIMPORT { + symData := ctxt.loader.Data(s) + addr := decodetypeGcprogShlib(ctxt, symData) + ptrdata := decodetypePtrdata(ctxt.Arch, symData) + sect := findShlibSection(ctxt, ctxt.loader.SymPkg(s), addr) + if sect != nil { + bits := ptrdata / int64(ctxt.Arch.PtrSize) + r := make([]byte, (bits+7)/8) + // ldshlibsyms avoids closing the ELF file so sect.ReadAt works. + // If we remove this read (and the ones in decodetypeGcprog), we + // can close the file. + _, err := sect.ReadAt(r, int64(addr-sect.Addr)) + if err != nil { + log.Fatal(err) + } + return r + } + Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s)) + return nil + } + relocs := ctxt.loader.Relocs(s) + mask := decodeRelocSym(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize)) + return ctxt.loader.Data(mask) +} + +// Type.commonType.gc +func decodetypeGcprog(ctxt *Link, s loader.Sym) []byte { + if ctxt.loader.SymType(s) == sym.SDYNIMPORT { + symData := ctxt.loader.Data(s) + addr := decodetypeGcprogShlib(ctxt, symData) + sect := findShlibSection(ctxt, ctxt.loader.SymPkg(s), addr) + if sect != nil { + // A gcprog is a 4-byte uint32 indicating length, followed by + // the actual program. + progsize := make([]byte, 4) + _, err := sect.ReadAt(progsize, int64(addr-sect.Addr)) + if err != nil { + log.Fatal(err) + } + progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize)) + _, err = sect.ReadAt(progbytes, int64(addr-sect.Addr+4)) + if err != nil { + log.Fatal(err) + } + return append(progsize, progbytes...) + } + Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s)) + return nil + } + relocs := ctxt.loader.Relocs(s) + rs := decodeRelocSym(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize)) + return ctxt.loader.Data(rs) +} + +// Find the elf.Section of a given shared library that contains a given address. +func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section { + for _, shlib := range ctxt.Shlibs { + if shlib.Path == path { + for _, sect := range shlib.File.Sections[1:] { // skip the NULL section + if sect.Addr <= addr && addr < sect.Addr+sect.Size { + return sect + } + } + } + } + return nil +} + +func decodetypeGcprogShlib(ctxt *Link, data []byte) uint64 { + return decodeInuxi(ctxt.Arch, data[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize) +} diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go new file mode 100644 index 0000000..feea864 --- /dev/null +++ b/src/cmd/link/internal/ld/dwarf.go @@ -0,0 +1,2328 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type + +package ld + +import ( + "cmd/internal/dwarf" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "internal/buildcfg" + "log" + "path" + "runtime" + "sort" + "strings" + "sync" +) + +// dwctxt is a wrapper intended to satisfy the method set of +// dwarf.Context, so that functions like dwarf.PutAttrs will work with +// DIEs that use loader.Sym as opposed to *sym.Symbol. It is also +// being used as a place to store tables/maps that are useful as part +// of type conversion (this is just a convenience; it would be easy to +// split these things out into another type if need be). +type dwctxt struct { + linkctxt *Link + ldr *loader.Loader + arch *sys.Arch + + // This maps type name string (e.g. "uintptr") to loader symbol for + // the DWARF DIE for that type (e.g. "go:info.type.uintptr") + tmap map[string]loader.Sym + + // This maps loader symbol for the DWARF DIE symbol generated for + // a type (e.g. "go:info.uintptr") to the type symbol itself + // ("type:uintptr"). + // FIXME: try converting this map (and the next one) to a single + // array indexed by loader.Sym -- this may perform better. + rtmap map[loader.Sym]loader.Sym + + // This maps Go type symbol (e.g. "type:XXX") to loader symbol for + // the typedef DIE for that type (e.g. "go:info.XXX..def") + tdmap map[loader.Sym]loader.Sym + + // Cache these type symbols, so as to avoid repeatedly looking them up + typeRuntimeEface loader.Sym + typeRuntimeIface loader.Sym + uintptrInfoSym loader.Sym + + // Used at various points in that parallel portion of DWARF gen to + // protect against conflicting updates to globals (such as "gdbscript") + dwmu *sync.Mutex +} + +// dwSym wraps a loader.Sym; this type is meant to obey the interface +// rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and +// DwAttr objects contain references to symbols via this type. +type dwSym loader.Sym + +func (s dwSym) Length(dwarfContext interface{}) int64 { + l := dwarfContext.(dwctxt).ldr + return int64(len(l.Data(loader.Sym(s)))) +} + +func (c dwctxt) PtrSize() int { + return c.arch.PtrSize +} + +func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.AddUintXX(c.arch, uint64(i), size) +} + +func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.AddBytes(b) +} + +func (c dwctxt) AddString(s dwarf.Sym, v string) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.Addstring(v) +} + +func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + if value != 0 { + value -= dsu.Value() + } + tgtds := loader.Sym(data.(dwSym)) + dsu.AddAddrPlus(c.arch, tgtds, value) +} + +func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + if value != 0 { + value -= dsu.Value() + } + tgtds := loader.Sym(data.(dwSym)) + dsu.AddCURelativeAddrPlus(c.arch, tgtds, value) +} + +func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + tds := loader.Sym(t.(dwSym)) + switch size { + default: + c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size) + case c.arch.PtrSize, 4: + } + dsu.AddSymRef(c.arch, tds, ofs, objabi.R_ADDROFF, size) +} + +func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { + size := 4 + if isDwarf64(c.linkctxt) { + size = 8 + } + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + tds := loader.Sym(t.(dwSym)) + switch size { + default: + c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size) + case c.arch.PtrSize, 4: + } + dsu.AddSymRef(c.arch, tds, ofs, objabi.R_DWARFSECREF, size) +} + +func (c dwctxt) Logf(format string, args ...interface{}) { + c.linkctxt.Logf(format, args...) +} + +// At the moment these interfaces are only used in the compiler. + +func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) { + panic("should be used only in the compiler") +} + +func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 { + panic("should be used only in the compiler") +} + +func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) { + panic("should be used only in the compiler") +} + +func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { + panic("should be used only in the compiler") +} + +func isDwarf64(ctxt *Link) bool { + return ctxt.HeadType == objabi.Haix +} + +// https://sourceware.org/gdb/onlinedocs/gdb/dotdebug_005fgdb_005fscripts-section.html +// Each entry inside .debug_gdb_scripts section begins with a non-null prefix +// byte that specifies the kind of entry. The following entries are supported: +const ( + GdbScriptPythonFileId = 1 + GdbScriptSchemeFileId = 3 + GdbScriptPythonTextId = 4 + GdbScriptSchemeTextId = 6 +) + +var gdbscript string + +// dwarfSecInfo holds information about a DWARF output section, +// specifically a section symbol and a list of symbols contained in +// that section. On the syms list, the first symbol will always be the +// section symbol, then any remaining symbols (if any) will be +// sub-symbols in that section. Note that for some sections (eg: +// .debug_abbrev), the section symbol is all there is (all content is +// contained in it). For other sections (eg: .debug_info), the section +// symbol is empty and all the content is in the sub-symbols. Finally +// there are some sections (eg: .debug_ranges) where it is a mix (both +// the section symbol and the sub-symbols have content) +type dwarfSecInfo struct { + syms []loader.Sym +} + +// secSym returns the section symbol for the section. +func (dsi *dwarfSecInfo) secSym() loader.Sym { + if len(dsi.syms) == 0 { + return 0 + } + return dsi.syms[0] +} + +// subSyms returns a list of sub-symbols for the section. +func (dsi *dwarfSecInfo) subSyms() []loader.Sym { + if len(dsi.syms) == 0 { + return []loader.Sym{} + } + return dsi.syms[1:] +} + +// dwarfp stores the collected DWARF symbols created during +// dwarf generation. +var dwarfp []dwarfSecInfo + +func (d *dwctxt) writeabbrev() dwarfSecInfo { + abrvs := d.ldr.CreateSymForUpdate(".debug_abbrev", 0) + abrvs.SetType(sym.SDWARFSECT) + abrvs.AddBytes(dwarf.GetAbbrev()) + return dwarfSecInfo{syms: []loader.Sym{abrvs.Sym()}} +} + +var dwtypes dwarf.DWDie + +// newattr attaches a new attribute to the specified DIE. +// +// FIXME: at the moment attributes are stored in a linked list in a +// fairly space-inefficient way -- it might be better to instead look +// up all attrs in a single large table, then store indices into the +// table in the DIE. This would allow us to common up storage for +// attributes that are shared by many DIEs (ex: byte size of N). +func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) { + a := new(dwarf.DWAttr) + a.Link = die.Attr + die.Attr = a + a.Atr = attr + a.Cls = uint8(cls) + a.Value = value + a.Data = data +} + +// Each DIE (except the root ones) has at least 1 attribute: its +// name. getattr moves the desired one to the front so +// frequently searched ones are found faster. +func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr { + if die.Attr.Atr == attr { + return die.Attr + } + + a := die.Attr + b := a.Link + for b != nil { + if b.Atr == attr { + a.Link = b.Link + b.Link = die.Attr + die.Attr = b + return b + } + + a = b + b = b.Link + } + + return nil +} + +// Every DIE manufactured by the linker has at least an AT_name +// attribute (but it will only be written out if it is listed in the abbrev). +// The compiler does create nameless DWARF DIEs (ex: concrete subprogram +// instance). +// FIXME: it would be more efficient to bulk-allocate DIEs. +func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string) *dwarf.DWDie { + die := new(dwarf.DWDie) + die.Abbrev = abbrev + die.Link = parent.Child + parent.Child = die + + newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name) + + // Sanity check: all DIEs created in the linker should be named. + if name == "" { + panic("nameless DWARF DIE") + } + + var st sym.SymKind + switch abbrev { + case dwarf.DW_ABRV_FUNCTYPEPARAM, dwarf.DW_ABRV_DOTDOTDOT, dwarf.DW_ABRV_STRUCTFIELD, dwarf.DW_ABRV_ARRAYRANGE: + // There are no relocations against these dies, and their names + // are not unique, so don't create a symbol. + return die + case dwarf.DW_ABRV_COMPUNIT, dwarf.DW_ABRV_COMPUNIT_TEXTLESS: + // Avoid collisions with "real" symbol names. + name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits)) + st = sym.SDWARFCUINFO + case dwarf.DW_ABRV_VARIABLE: + st = sym.SDWARFVAR + default: + // Everything else is assigned a type of SDWARFTYPE. that + // this also includes loose ends such as STRUCT_FIELD. + st = sym.SDWARFTYPE + } + ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, 0) + dsu := d.ldr.MakeSymbolUpdater(ds) + dsu.SetType(st) + d.ldr.SetAttrNotInSymbolTable(ds, true) + d.ldr.SetAttrReachable(ds, true) + die.Sym = dwSym(ds) + if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL { + d.tmap[name] = ds + } + + return die +} + +func walktypedef(die *dwarf.DWDie) *dwarf.DWDie { + if die == nil { + return nil + } + // Resolve typedef if present. + if die.Abbrev == dwarf.DW_ABRV_TYPEDECL { + for attr := die.Attr; attr != nil; attr = attr.Link { + if attr.Atr == dwarf.DW_AT_type && attr.Cls == dwarf.DW_CLS_REFERENCE && attr.Data != nil { + return attr.Data.(*dwarf.DWDie) + } + } + } + + return die +} + +func (d *dwctxt) walksymtypedef(symIdx loader.Sym) loader.Sym { + + // We're being given the loader symbol for the type DIE, e.g. + // "go:info.type.uintptr". Map that first to the type symbol (e.g. + // "type:uintptr") and then to the typedef DIE for the type. + // FIXME: this seems clunky, maybe there is a better way to do this. + + if ts, ok := d.rtmap[symIdx]; ok { + if def, ok := d.tdmap[ts]; ok { + return def + } + d.linkctxt.Errorf(ts, "internal error: no entry for sym %d in tdmap\n", ts) + return 0 + } + d.linkctxt.Errorf(symIdx, "internal error: no entry for sym %d in rtmap\n", symIdx) + return 0 +} + +// Find child by AT_name using hashtable if available or linear scan +// if not. +func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie { + var prev *dwarf.DWDie + for ; die != prev; prev, die = die, walktypedef(die) { + for a := die.Child; a != nil; a = a.Link { + if name == getattr(a, dwarf.DW_AT_name).Data { + return a + } + } + continue + } + return nil +} + +// find looks up the loader symbol for the DWARF DIE generated for the +// type with the specified name. +func (d *dwctxt) find(name string) loader.Sym { + return d.tmap[name] +} + +func (d *dwctxt) mustFind(name string) loader.Sym { + r := d.find(name) + if r == 0 { + Exitf("dwarf find: cannot find %s", name) + } + return r +} + +func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) { + switch size { + default: + d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size) + case d.arch.PtrSize, 4: + } + sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size) +} + +func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) { + if ref == 0 { + return + } + newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref)) +} + +func (d *dwctxt) dtolsym(s dwarf.Sym) loader.Sym { + if s == nil { + return 0 + } + dws := loader.Sym(s.(dwSym)) + return dws +} + +func (d *dwctxt) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym { + s := d.dtolsym(die.Sym) + if s == 0 { + s = syms[len(syms)-1] + } else { + syms = append(syms, s) + } + sDwsym := dwSym(s) + dwarf.Uleb128put(d, sDwsym, int64(die.Abbrev)) + dwarf.PutAttrs(d, sDwsym, die.Abbrev, die.Attr) + if dwarf.HasChildren(die) { + for die := die.Child; die != nil; die = die.Link { + syms = d.putdie(syms, die) + } + dsu := d.ldr.MakeSymbolUpdater(syms[len(syms)-1]) + dsu.AddUint8(0) + } + return syms +} + +func reverselist(list **dwarf.DWDie) { + curr := *list + var prev *dwarf.DWDie + for curr != nil { + next := curr.Link + curr.Link = prev + prev = curr + curr = next + } + + *list = prev +} + +func reversetree(list **dwarf.DWDie) { + reverselist(list) + for die := *list; die != nil; die = die.Link { + if dwarf.HasChildren(die) { + reversetree(&die.Child) + } + } +} + +func newmemberoffsetattr(die *dwarf.DWDie, offs int32) { + newattr(die, dwarf.DW_AT_data_member_location, dwarf.DW_CLS_CONSTANT, int64(offs), nil) +} + +func (d *dwctxt) lookupOrDiag(n string) loader.Sym { + symIdx := d.ldr.Lookup(n, 0) + if symIdx == 0 { + Exitf("dwarf: missing type: %s", n) + } + if len(d.ldr.Data(symIdx)) == 0 { + Exitf("dwarf: missing type (no data): %s", n) + } + + return symIdx +} + +func (d *dwctxt) dotypedef(parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie { + // Only emit typedefs for real names. + if strings.HasPrefix(name, "map[") { + return nil + } + if strings.HasPrefix(name, "struct {") { + return nil + } + // cmd/compile uses "noalg.struct {...}" as type name when hash and eq algorithm generation of + // this struct type is suppressed. + if strings.HasPrefix(name, "noalg.struct {") { + return nil + } + if strings.HasPrefix(name, "chan ") { + return nil + } + if name[0] == '[' || name[0] == '*' { + return nil + } + if def == nil { + Errorf(nil, "dwarf: bad def in dotypedef") + } + + // Create a new loader symbol for the typedef. We no longer + // do lookups of typedef symbols by name, so this is going + // to be an anonymous symbol (we want this for perf reasons). + tds := d.ldr.CreateExtSym("", 0) + tdsu := d.ldr.MakeSymbolUpdater(tds) + tdsu.SetType(sym.SDWARFTYPE) + def.Sym = dwSym(tds) + d.ldr.SetAttrNotInSymbolTable(tds, true) + d.ldr.SetAttrReachable(tds, true) + + // The typedef entry must be created after the def, + // so that future lookups will find the typedef instead + // of the real definition. This hooks the typedef into any + // circular definition loops, so that gdb can understand them. + die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name) + + d.newrefattr(die, dwarf.DW_AT_type, tds) + + return die +} + +// Define gotype, for composite ones recurse into constituents. +func (d *dwctxt) defgotype(gotype loader.Sym) loader.Sym { + if gotype == 0 { + return d.mustFind("<unspecified>") + } + + // If we already have a tdmap entry for the gotype, return it. + if ds, ok := d.tdmap[gotype]; ok { + return ds + } + + sn := d.ldr.SymName(gotype) + if !strings.HasPrefix(sn, "type:") { + d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type:\"") + return d.mustFind("<unspecified>") + } + name := sn[5:] // could also decode from Type.string + + sdie := d.find(name) + if sdie != 0 { + return sdie + } + + gtdwSym := d.newtype(gotype) + d.tdmap[gotype] = loader.Sym(gtdwSym.Sym.(dwSym)) + return loader.Sym(gtdwSym.Sym.(dwSym)) +} + +func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { + sn := d.ldr.SymName(gotype) + name := sn[5:] // could also decode from Type.string + tdata := d.ldr.Data(gotype) + if len(tdata) == 0 { + d.linkctxt.Errorf(gotype, "missing type") + } + kind := decodetypeKind(d.arch, tdata) + bytesize := decodetypeSize(d.arch, tdata) + + var die, typedefdie *dwarf.DWDie + switch kind { + case objabi.KindBool: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindInt, + objabi.KindInt8, + objabi.KindInt16, + objabi.KindInt32, + objabi.KindInt64: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindUint, + objabi.KindUint8, + objabi.KindUint16, + objabi.KindUint32, + objabi.KindUint64, + objabi.KindUintptr: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindFloat32, + objabi.KindFloat64: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindComplex64, + objabi.KindComplex128: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindArray: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + s := decodetypeArrayElem(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) + fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range") + + // use actual length not upper bound; correct for 0-length arrays. + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(d.ldr, d.arch, gotype), 0) + + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + + case objabi.KindChan: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name) + s := decodetypeChanElem(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) + // Save elem type for synthesizechantypes. We could synthesize here + // but that would change the order of DIEs we output. + d.newrefattr(die, dwarf.DW_AT_type, s) + + case objabi.KindFunc: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + typedefdie = d.dotypedef(&dwtypes, name, die) + data := d.ldr.Data(gotype) + // FIXME: add caching or reuse reloc slice. + relocs := d.ldr.Relocs(gotype) + nfields := decodetypeFuncInCount(d.arch, data) + for i := 0; i < nfields; i++ { + s := decodetypeFuncInType(d.ldr, d.arch, gotype, &relocs, i) + sn := d.ldr.SymName(s) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:]) + d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) + } + + if decodetypeFuncDotdotdot(d.arch, data) { + d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...") + } + nfields = decodetypeFuncOutCount(d.arch, data) + for i := 0; i < nfields; i++ { + s := decodetypeFuncOutType(d.ldr, d.arch, gotype, &relocs, i) + sn := d.ldr.SymName(s) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:]) + d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s))) + } + + case objabi.KindInterface: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) + data := d.ldr.Data(gotype) + nfields := int(decodetypeIfaceMethodCount(d.arch, data)) + var s loader.Sym + if nfields == 0 { + s = d.typeRuntimeEface + } else { + s = d.typeRuntimeIface + } + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) + + case objabi.KindMap: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name) + s := decodetypeMapKey(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s)) + s = decodetypeMapValue(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) + // Save gotype for use in synthesizemaptypes. We could synthesize here, + // but that would change the order of the DIEs. + d.newrefattr(die, dwarf.DW_AT_type, gotype) + + case objabi.KindPtr: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) + s := decodetypePtrElem(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) + + case objabi.KindSlice: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + s := decodetypeArrayElem(d.ldr, d.arch, gotype) + elem := d.defgotype(s) + d.newrefattr(die, dwarf.DW_AT_go_elem, elem) + + case objabi.KindString: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindStruct: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + nfields := decodetypeStructFieldCount(d.ldr, d.arch, gotype) + for i := 0; i < nfields; i++ { + f := decodetypeStructFieldName(d.ldr, d.arch, gotype, i) + s := decodetypeStructFieldType(d.ldr, d.arch, gotype, i) + if f == "" { + sn := d.ldr.SymName(s) + f = sn[5:] // skip "type:" + } + fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f) + d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) + offset := decodetypeStructFieldOffset(d.ldr, d.arch, gotype, i) + newmemberoffsetattr(fld, int32(offset)) + if decodetypeStructFieldEmbedded(d.ldr, d.arch, gotype, i) { + newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) + } + } + + case objabi.KindUnsafePointer: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name) + + default: + d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name) + d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("<unspecified>")) + } + + newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0) + + if d.ldr.AttrReachable(gotype) { + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gotype)) + } + + // Sanity check. + if _, ok := d.rtmap[gotype]; ok { + log.Fatalf("internal error: rtmap entry already installed\n") + } + + ds := loader.Sym(die.Sym.(dwSym)) + if typedefdie != nil { + ds = loader.Sym(typedefdie.Sym.(dwSym)) + } + d.rtmap[ds] = gotype + + if _, ok := prototypedies[sn]; ok { + prototypedies[sn] = die + } + + if typedefdie != nil { + return typedefdie + } + return die +} + +func (d *dwctxt) nameFromDIESym(dwtypeDIESym loader.Sym) string { + sn := d.ldr.SymName(dwtypeDIESym) + return sn[len(dwarf.InfoPrefix):] +} + +func (d *dwctxt) defptrto(dwtype loader.Sym) loader.Sym { + + // FIXME: it would be nice if the compiler attached an aux symbol + // ref from the element type to the pointer type -- it would be + // more efficient to do it this way as opposed to via name lookups. + + ptrname := "*" + d.nameFromDIESym(dwtype) + if die := d.find(ptrname); die != 0 { + return die + } + + pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname) + d.newrefattr(pdie, dwarf.DW_AT_type, dwtype) + + // The DWARF info synthesizes pointer types that don't exist at the + // language level, like *hash<...> and *bucket<...>, and the data + // pointers of slices. Link to the ones we can find. + gts := d.ldr.Lookup("type:"+ptrname, 0) + if gts != 0 && d.ldr.AttrReachable(gts) { + newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts)) + } + + if gts != 0 { + ds := loader.Sym(pdie.Sym.(dwSym)) + d.rtmap[ds] = gts + d.tdmap[gts] = ds + } + + return d.dtolsym(pdie.Sym) +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. If except is one of +// the top-level children, it will not be copied. +func (d *dwctxt) copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) { + for src = src.Child; src != nil; src = src.Link { + if src == except { + continue + } + c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string)) + for a := src.Attr; a != nil; a = a.Link { + newattr(c, a.Atr, int(a.Cls), a.Value, a.Data) + } + d.copychildrenexcept(ctxt, c, src, nil) + } + + reverselist(&dst.Child) +} + +func (d *dwctxt) copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) { + d.copychildrenexcept(ctxt, dst, src, nil) +} + +// Search children (assumed to have TAG_member) for the one named +// field and set its AT_type to dwtype +func (d *dwctxt) substitutetype(structdie *dwarf.DWDie, field string, dwtype loader.Sym) { + child := findchild(structdie, field) + if child == nil { + Exitf("dwarf substitutetype: %s does not have member %s", + getattr(structdie, dwarf.DW_AT_name).Data, field) + return + } + + a := getattr(child, dwarf.DW_AT_type) + if a != nil { + a.Data = dwSym(dwtype) + } else { + d.newrefattr(child, dwarf.DW_AT_type, dwtype) + } +} + +func (d *dwctxt) findprotodie(ctxt *Link, name string) *dwarf.DWDie { + die, ok := prototypedies[name] + if ok && die == nil { + d.defgotype(d.lookupOrDiag(name)) + die = prototypedies[name] + } + if die == nil { + log.Fatalf("internal error: DIE generation failed for %s\n", name) + } + return die +} + +func (d *dwctxt) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(d.findprotodie(ctxt, "type:runtime.stringStructDWARF")) + if prototype == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE { + continue + } + d.copychildren(ctxt, die, prototype) + } +} + +func (d *dwctxt) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(d.findprotodie(ctxt, "type:runtime.slice")) + if prototype == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_SLICETYPE { + continue + } + d.copychildren(ctxt, die, prototype) + elem := loader.Sym(getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym)) + d.substitutetype(die, "array", d.defptrto(elem)) + } +} + +func mkinternaltypename(base string, arg1 string, arg2 string) string { + if arg2 == "" { + return fmt.Sprintf("%s<%s>", base, arg1) + } + return fmt.Sprintf("%s<%s,%s>", base, arg1, arg2) +} + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +const ( + MaxKeySize = 128 + MaxValSize = 128 + BucketSize = 8 +) + +func (d *dwctxt) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) loader.Sym { + name := mkinternaltypename(typename, keyname, valname) + symname := dwarf.InfoPrefix + name + s := d.ldr.Lookup(symname, 0) + if s != 0 && d.ldr.SymType(s) == sym.SDWARFTYPE { + return s + } + die := d.newdie(&dwtypes, abbrev, name) + f(die) + return d.dtolsym(die.Sym) +} + +func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { + hash := walktypedef(d.findprotodie(ctxt, "type:runtime.hmap")) + bucket := walktypedef(d.findprotodie(ctxt, "type:runtime.bmap")) + + if hash == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_MAPTYPE { + continue + } + gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym)) + keytype := decodetypeMapKey(d.ldr, d.arch, gotype) + valtype := decodetypeMapValue(d.ldr, d.arch, gotype) + keydata := d.ldr.Data(keytype) + valdata := d.ldr.Data(valtype) + keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata) + keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype)) + + // compute size info like hashmap.c does. + indirectKey, indirectVal := false, false + if keysize > MaxKeySize { + keysize = int64(d.arch.PtrSize) + indirectKey = true + } + if valsize > MaxValSize { + valsize = int64(d.arch.PtrSize) + indirectVal = true + } + + // Construct type to represent an array of BucketSize keys + keyname := d.nameFromDIESym(keytype) + dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) { + newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0) + t := keytype + if indirectKey { + t = d.defptrto(keytype) + } + d.newrefattr(dwhk, dwarf.DW_AT_type, t) + fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size") + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + }) + + // Construct type to represent an array of BucketSize values + valname := d.nameFromDIESym(valtype) + dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) { + newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0) + t := valtype + if indirectVal { + t = d.defptrto(valtype) + } + d.newrefattr(dwhv, dwarf.DW_AT_type, t) + fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size") + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + }) + + // Construct bucket<K,V> + dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) { + // Copy over all fields except the field "data" from the generic + // bucket. "data" will be replaced with keys/values below. + d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) + + fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys") + d.newrefattr(fld, dwarf.DW_AT_type, dwhks) + newmemberoffsetattr(fld, BucketSize) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values") + d.newrefattr(fld, dwarf.DW_AT_type, dwhvs) + newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow") + d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym))) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) + if d.arch.RegSize > d.arch.PtrSize { + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad") + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize)) + } + + newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(d.arch.RegSize), 0) + }) + + // Construct hash<K,V> + dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) { + d.copychildren(ctxt, dwh, hash) + d.substitutetype(dwh, "buckets", d.defptrto(dwhbs)) + d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs)) + newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil) + }) + + // make map type a pointer to hash<K,V> + d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) + } +} + +func (d *dwctxt) synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { + sudog := walktypedef(d.findprotodie(ctxt, "type:runtime.sudog")) + waitq := walktypedef(d.findprotodie(ctxt, "type:runtime.waitq")) + hchan := walktypedef(d.findprotodie(ctxt, "type:runtime.hchan")) + if sudog == nil || waitq == nil || hchan == nil { + return + } + + sudogsize := int(getattr(sudog, dwarf.DW_AT_byte_size).Value) + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_CHANTYPE { + continue + } + elemgotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym)) + tname := d.ldr.SymName(elemgotype) + elemname := tname[5:] + elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname))) + + // sudog<T> + dwss := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) { + d.copychildren(ctxt, dws, sudog) + d.substitutetype(dws, "elem", d.defptrto(elemtype)) + newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil) + }) + + // waitq<T> + dwws := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { + + d.copychildren(ctxt, dww, waitq) + d.substitutetype(dww, "first", d.defptrto(dwss)) + d.substitutetype(dww, "last", d.defptrto(dwss)) + newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil) + }) + + // hchan<T> + dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) { + d.copychildren(ctxt, dwh, hchan) + d.substitutetype(dwh, "recvq", dwws) + d.substitutetype(dwh, "sendq", dwws) + newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil) + }) + + d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) + } +} + +// createUnitLength creates the initial length field with value v and update +// offset of unit_length if needed. +func (d *dwctxt) createUnitLength(su *loader.SymbolBuilder, v uint64) { + if isDwarf64(d.linkctxt) { + su.AddUint32(d.arch, 0xFFFFFFFF) + } + d.addDwarfAddrField(su, v) +} + +// addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits. +func (d *dwctxt) addDwarfAddrField(sb *loader.SymbolBuilder, v uint64) { + if isDwarf64(d.linkctxt) { + sb.AddUint(d.arch, v) + } else { + sb.AddUint32(d.arch, uint32(v)) + } +} + +// addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits. +func (d *dwctxt) addDwarfAddrRef(sb *loader.SymbolBuilder, t loader.Sym) { + if isDwarf64(d.linkctxt) { + d.adddwarfref(sb, t, 8) + } else { + d.adddwarfref(sb, t, 4) + } +} + +// calcCompUnitRanges calculates the PC ranges of the compilation units. +func (d *dwctxt) calcCompUnitRanges() { + var prevUnit *sym.CompilationUnit + for _, s := range d.linkctxt.Textp { + sym := loader.Sym(s) + + fi := d.ldr.FuncInfo(sym) + if !fi.Valid() { + continue + } + + // Skip linker-created functions (ex: runtime.addmoduledata), since they + // don't have DWARF to begin with. + unit := d.ldr.SymUnit(sym) + if unit == nil { + continue + } + + // Update PC ranges. + // + // We don't simply compare the end of the previous + // symbol with the start of the next because there's + // often a little padding between them. Instead, we + // only create boundaries between symbols from + // different units. + sval := d.ldr.SymValue(sym) + u0val := d.ldr.SymValue(loader.Sym(unit.Textp[0])) + if prevUnit != unit { + unit.PCs = append(unit.PCs, dwarf.Range{Start: sval - u0val}) + prevUnit = unit + } + unit.PCs[len(unit.PCs)-1].End = sval - u0val + int64(len(d.ldr.Data(sym))) + } +} + +func movetomodule(ctxt *Link, parent *dwarf.DWDie) { + die := ctxt.runtimeCU.DWInfo.Child + if die == nil { + ctxt.runtimeCU.DWInfo.Child = parent.Child + return + } + for die.Link != nil { + die = die.Link + } + die.Link = parent.Child +} + +/* + * Generate a sequence of opcodes that is as short as possible. + * See section 6.2.5 + */ +const ( + LINE_BASE = -4 + LINE_RANGE = 10 + PC_RANGE = (255 - OPCODE_BASE) / LINE_RANGE + OPCODE_BASE = 11 +) + +/* + * Walk prog table, emit line program and build DIE tree. + */ + +func getCompilationDir() string { + // OSX requires this be set to something, but it's not easy to choose + // a value. Linking takes place in a temporary directory, so there's + // no point including it here. Paths in the file table are usually + // absolute, in which case debuggers will ignore this value. -trimpath + // produces relative paths, but we don't know where they start, so + // all we can do here is try not to make things worse. + return "." +} + +func (d *dwctxt) importInfoSymbol(dsym loader.Sym) { + d.ldr.SetAttrReachable(dsym, true) + d.ldr.SetAttrNotInSymbolTable(dsym, true) + dst := d.ldr.SymType(dsym) + if dst != sym.SDWARFCONST && dst != sym.SDWARFABSFCN { + log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String()) + } + relocs := d.ldr.Relocs(dsym) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + if r.Type() != objabi.R_DWARFSECREF { + continue + } + rsym := r.Sym() + // If there is an entry for the symbol in our rtmap, then it + // means we've processed the type already, and can skip this one. + if _, ok := d.rtmap[rsym]; ok { + // type already generated + continue + } + // FIXME: is there a way we could avoid materializing the + // symbol name here? + sn := d.ldr.SymName(rsym) + tn := sn[len(dwarf.InfoPrefix):] + ts := d.ldr.Lookup("type:"+tn, 0) + d.defgotype(ts) + } +} + +func expandFile(fname string) string { + if strings.HasPrefix(fname, src.FileSymPrefix) { + fname = fname[len(src.FileSymPrefix):] + } + return expandGoroot(fname) +} + +// writeDirFileTables emits the portion of the DWARF line table +// prologue containing the include directories and file names, +// described in section 6.2.4 of the DWARF 4 standard. It walks the +// filepaths for the unit to discover any common directories, which +// are emitted to the directory table first, then the file table is +// emitted after that. +func (d *dwctxt) writeDirFileTables(unit *sym.CompilationUnit, lsu *loader.SymbolBuilder) { + type fileDir struct { + base string + dir int + } + dirNums := make(map[string]int) + dirs := []string{""} + files := []fileDir{} + + // Preprocess files to collect directories. This assumes that the + // file table is already de-duped. + for i, name := range unit.FileTable { + name := expandFile(name) + if len(name) == 0 { + // Can't have empty filenames, and having a unique + // filename is quite useful for debugging. + name = fmt.Sprintf("<missing>_%d", i) + } + // Note the use of "path" here and not "filepath". The compiler + // hard-codes to use "/" in DWARF paths (even for Windows), so we + // want to maintain that here. + file := path.Base(name) + dir := path.Dir(name) + dirIdx, ok := dirNums[dir] + if !ok && dir != "." { + dirIdx = len(dirNums) + 1 + dirNums[dir] = dirIdx + dirs = append(dirs, dir) + } + files = append(files, fileDir{base: file, dir: dirIdx}) + + // We can't use something that may be dead-code + // eliminated from a binary here. proc.go contains + // main and the scheduler, so it's not going anywhere. + if i := strings.Index(name, "runtime/proc.go"); i >= 0 && unit.Lib.Pkg == "runtime" { + d.dwmu.Lock() + if gdbscript == "" { + k := strings.Index(name, "runtime/proc.go") + gdbscript = name[:k] + "runtime/runtime-gdb.py" + } + d.dwmu.Unlock() + } + } + + // Emit directory section. This is a series of nul terminated + // strings, followed by a single zero byte. + lsDwsym := dwSym(lsu.Sym()) + for k := 1; k < len(dirs); k++ { + d.AddString(lsDwsym, dirs[k]) + } + lsu.AddUint8(0) // terminator + + // Emit file section. + for k := 0; k < len(files); k++ { + d.AddString(lsDwsym, files[k].base) + dwarf.Uleb128put(d, lsDwsym, int64(files[k].dir)) + lsu.AddUint8(0) // mtime + lsu.AddUint8(0) // length + } + lsu.AddUint8(0) // terminator +} + +// writelines collects up and chains together the symbols needed to +// form the DWARF line table for the specified compilation unit, +// returning a list of symbols. The returned list will include an +// initial symbol containing the line table header and prologue (with +// file table), then a series of compiler-emitted line table symbols +// (one per live function), and finally an epilog symbol containing an +// end-of-sequence operator. The prologue and epilog symbols are passed +// in (having been created earlier); here we add content to them. +func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []loader.Sym { + is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles. + + unitstart := int64(-1) + headerstart := int64(-1) + headerend := int64(-1) + + syms := make([]loader.Sym, 0, len(unit.Textp)+2) + syms = append(syms, lineProlog) + lsu := d.ldr.MakeSymbolUpdater(lineProlog) + lsDwsym := dwSym(lineProlog) + newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, lsDwsym) + + // Write .debug_line Line Number Program Header (sec 6.2.4) + // Fields marked with (*) must be changed for 64-bit dwarf + unitLengthOffset := lsu.Size() + d.createUnitLength(lsu, 0) // unit_length (*), filled in at end + unitstart = lsu.Size() + lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05 + headerLengthOffset := lsu.Size() + d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end + headerstart = lsu.Size() + + // cpos == unitstart + 4 + 2 + 4 + lsu.AddUint8(1) // minimum_instruction_length + lsu.AddUint8(is_stmt) // default_is_stmt + lsu.AddUint8(LINE_BASE & 0xFF) // line_base + lsu.AddUint8(LINE_RANGE) // line_range + lsu.AddUint8(OPCODE_BASE) // opcode_base + lsu.AddUint8(0) // standard_opcode_lengths[1] + lsu.AddUint8(1) // standard_opcode_lengths[2] + lsu.AddUint8(1) // standard_opcode_lengths[3] + lsu.AddUint8(1) // standard_opcode_lengths[4] + lsu.AddUint8(1) // standard_opcode_lengths[5] + lsu.AddUint8(0) // standard_opcode_lengths[6] + lsu.AddUint8(0) // standard_opcode_lengths[7] + lsu.AddUint8(0) // standard_opcode_lengths[8] + lsu.AddUint8(1) // standard_opcode_lengths[9] + lsu.AddUint8(0) // standard_opcode_lengths[10] + + // Call helper to emit dir and file sections. + d.writeDirFileTables(unit, lsu) + + // capture length at end of file names. + headerend = lsu.Size() + unitlen := lsu.Size() - unitstart + + // Output the state machine for each function remaining. + for _, s := range unit.Textp { + fnSym := loader.Sym(s) + _, _, _, lines := d.ldr.GetFuncDwarfAuxSyms(fnSym) + + // Chain the line symbol onto the list. + if lines != 0 { + syms = append(syms, lines) + unitlen += int64(len(d.ldr.Data(lines))) + } + } + + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(unitlen)) + } + + if isDwarf64(d.linkctxt) { + lsu.SetUint(d.arch, unitLengthOffset+4, uint64(unitlen)) // +4 because of 0xFFFFFFFF + lsu.SetUint(d.arch, headerLengthOffset, uint64(headerend-headerstart)) + } else { + lsu.SetUint32(d.arch, unitLengthOffset, uint32(unitlen)) + lsu.SetUint32(d.arch, headerLengthOffset, uint32(headerend-headerstart)) + } + + return syms +} + +// writepcranges generates the DW_AT_ranges table for compilation unit +// "unit", and returns a collection of ranges symbols (one for the +// compilation unit DIE itself and the remainder from functions in the unit). +func (d *dwctxt) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, rangeProlog loader.Sym) []loader.Sym { + + syms := make([]loader.Sym, 0, len(unit.RangeSyms)+1) + syms = append(syms, rangeProlog) + rsu := d.ldr.MakeSymbolUpdater(rangeProlog) + rDwSym := dwSym(rangeProlog) + + // Create PC ranges for the compilation unit DIE. + newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym) + newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base)) + dwarf.PutBasedRanges(d, rDwSym, pcs) + + // Collect up the ranges for functions in the unit. + rsize := uint64(rsu.Size()) + for _, ls := range unit.RangeSyms { + s := loader.Sym(ls) + syms = append(syms, s) + rsize += uint64(d.ldr.SymSize(s)) + } + + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, rsize) + } + + return syms +} + +/* + * Emit .debug_frame + */ +const ( + dataAlignmentFactor = -4 +) + +// appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice. +func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte { + b = append(b, dwarf.DW_CFA_def_cfa_offset_sf) + b = dwarf.AppendSleb128(b, cfa/dataAlignmentFactor) + + switch { + case deltapc < 0x40: + b = append(b, uint8(dwarf.DW_CFA_advance_loc+deltapc)) + case deltapc < 0x100: + b = append(b, dwarf.DW_CFA_advance_loc1) + b = append(b, uint8(deltapc)) + case deltapc < 0x10000: + b = append(b, dwarf.DW_CFA_advance_loc2, 0, 0) + arch.ByteOrder.PutUint16(b[len(b)-2:], uint16(deltapc)) + default: + b = append(b, dwarf.DW_CFA_advance_loc4, 0, 0, 0, 0) + arch.ByteOrder.PutUint32(b[len(b)-4:], uint32(deltapc)) + } + return b +} + +func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo { + fsd := dwSym(fs) + fsu := d.ldr.MakeSymbolUpdater(fs) + fsu.SetType(sym.SDWARFSECT) + isdw64 := isDwarf64(d.linkctxt) + haslr := d.linkctxt.Arch.HasLR + + // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64 + lengthFieldSize := int64(4) + if isdw64 { + lengthFieldSize += 8 + } + + // Emit the CIE, Section 6.4.1 + cieReserve := uint32(16) + if haslr { + cieReserve = 32 + } + if isdw64 { + cieReserve += 4 // 4 bytes added for cid + } + d.createUnitLength(fsu, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize + d.addDwarfAddrField(fsu, ^uint64(0)) // cid + fsu.AddUint8(3) // dwarf version (appendix F) + fsu.AddUint8(0) // augmentation "" + dwarf.Uleb128put(d, fsd, 1) // code_alignment_factor + dwarf.Sleb128put(d, fsd, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // return_address_register + + fsu.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address.. + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... + if haslr { + dwarf.Uleb128put(d, fsd, int64(0)) // ...plus a 0 offset. + + fsu.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) + + fsu.AddUint8(dwarf.DW_CFA_val_offset) // The previous value... + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...of the platform's SP register... + dwarf.Uleb128put(d, fsd, int64(0)) // ...is CFA+0. + } else { + dwarf.Uleb128put(d, fsd, int64(d.arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). + + fsu.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value... + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // ...of the return address... + dwarf.Uleb128put(d, fsd, int64(-d.arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. + } + + pad := int64(cieReserve) + lengthFieldSize - int64(len(d.ldr.Data(fs))) + + if pad < 0 { + Exitf("dwarf: cieReserve too small by %d bytes.", -pad) + } + + internalExec := d.linkctxt.BuildMode == BuildModeExe && d.linkctxt.IsInternal() + addAddrPlus := loader.GenAddAddrPlusFunc(internalExec) + + fsu.AddBytes(zeros[:pad]) + + var deltaBuf []byte + pcsp := obj.NewPCIter(uint32(d.arch.MinLC)) + for _, s := range d.linkctxt.Textp { + fn := loader.Sym(s) + fi := d.ldr.FuncInfo(fn) + if !fi.Valid() { + continue + } + fpcsp := d.ldr.Pcsp(s) + + // Emit a FDE, Section 6.4.1. + // First build the section contents into a byte buffer. + deltaBuf = deltaBuf[:0] + if haslr && fi.TopFrame() { + // Mark the link register as having an undefined value. + // This stops call stack unwinders progressing any further. + // TODO: similar mark on non-LR architectures. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + } + + for pcsp.Init(d.linkctxt.loader.Data(fpcsp)); !pcsp.Done; pcsp.Next() { + nextpc := pcsp.NextPC + + // pciterinit goes up to the end of the function, + // but DWARF expects us to stop just before the end. + if int64(nextpc) == int64(len(d.ldr.Data(fn))) { + nextpc-- + if nextpc < pcsp.PC { + continue + } + } + + spdelta := int64(pcsp.Value) + if !haslr { + // Return address has been pushed onto stack. + spdelta += int64(d.arch.PtrSize) + } + + if haslr && !fi.TopFrame() { + // TODO(bryanpkc): This is imprecise. In general, the instruction + // that stores the return address to the stack frame is not the + // same one that allocates the frame. + if pcsp.Value > 0 { + // The return address is preserved at (CFA-frame_size) + // after a stack frame has been allocated. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + deltaBuf = dwarf.AppendSleb128(deltaBuf, -spdelta/dataAlignmentFactor) + } else { + // The return address is restored into the link register + // when a stack frame has been de-allocated. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + } + } + + deltaBuf = appendPCDeltaCFA(d.arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta) + } + pad := int(Rnd(int64(len(deltaBuf)), int64(d.arch.PtrSize))) - len(deltaBuf) + deltaBuf = append(deltaBuf, zeros[:pad]...) + + // Emit the FDE header, Section 6.4.1. + // 4 bytes: length, must be multiple of thearch.ptrsize + // 4/8 bytes: Pointer to the CIE above, at offset 0 + // ptrsize: initial location + // ptrsize: address range + + fdeLength := uint64(4 + 2*d.arch.PtrSize + len(deltaBuf)) + if isdw64 { + fdeLength += 4 // 4 bytes added for CIE pointer + } + d.createUnitLength(fsu, fdeLength) + + if d.linkctxt.LinkMode == LinkExternal { + d.addDwarfAddrRef(fsu, fs) + } else { + d.addDwarfAddrField(fsu, 0) // CIE offset + } + addAddrPlus(fsu, d.arch, s, 0) + fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range + fsu.AddBytes(deltaBuf) + + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_frame", d.ldr.SymPkg(fn), fdeLength+uint64(lengthFieldSize)) + } + } + + return dwarfSecInfo{syms: []loader.Sym{fs}} +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ + +const ( + COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 +) + +// appendSyms appends the syms from 'src' into 'syms' and returns the +// result. This can go away once we do away with sym.LoaderSym +// entirely. +func appendSyms(syms []loader.Sym, src []sym.LoaderSym) []loader.Sym { + for _, s := range src { + syms = append(syms, loader.Sym(s)) + } + return syms +} + +func (d *dwctxt) writeUnitInfo(u *sym.CompilationUnit, abbrevsym loader.Sym, infoEpilog loader.Sym) []loader.Sym { + syms := []loader.Sym{} + if len(u.Textp) == 0 && u.DWInfo.Child == nil && len(u.VarDIEs) == 0 { + return syms + } + + compunit := u.DWInfo + s := d.dtolsym(compunit.Sym) + su := d.ldr.MakeSymbolUpdater(s) + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + d.createUnitLength(su, 0) // unit_length (*), will be filled in later. + su.AddUint16(d.arch, 4) // dwarf version (appendix F) + + // debug_abbrev_offset (*) + d.addDwarfAddrRef(su, abbrevsym) + + su.AddUint8(uint8(d.arch.PtrSize)) // address_size + + ds := dwSym(s) + dwarf.Uleb128put(d, ds, int64(compunit.Abbrev)) + dwarf.PutAttrs(d, ds, compunit.Abbrev, compunit.Attr) + + // This is an under-estimate; more will be needed for type DIEs. + cu := make([]loader.Sym, 0, len(u.AbsFnDIEs)+len(u.FuncDIEs)) + cu = append(cu, s) + cu = appendSyms(cu, u.AbsFnDIEs) + cu = appendSyms(cu, u.FuncDIEs) + if u.Consts != 0 { + cu = append(cu, loader.Sym(u.Consts)) + } + cu = appendSyms(cu, u.VarDIEs) + var cusize int64 + for _, child := range cu { + cusize += int64(len(d.ldr.Data(child))) + } + + for die := compunit.Child; die != nil; die = die.Link { + l := len(cu) + lastSymSz := int64(len(d.ldr.Data(cu[l-1]))) + cu = d.putdie(cu, die) + if lastSymSz != int64(len(d.ldr.Data(cu[l-1]))) { + // putdie will sometimes append directly to the last symbol of the list + cusize = cusize - lastSymSz + int64(len(d.ldr.Data(cu[l-1]))) + } + for _, child := range cu[l:] { + cusize += int64(len(d.ldr.Data(child))) + } + } + + culu := d.ldr.MakeSymbolUpdater(infoEpilog) + culu.AddUint8(0) // closes compilation unit DIE + cu = append(cu, infoEpilog) + cusize++ + + // Save size for AIX symbol table. + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_info", d.getPkgFromCUSym(s), uint64(cusize)) + } + if isDwarf64(d.linkctxt) { + cusize -= 12 // exclude the length field. + su.SetUint(d.arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF + } else { + cusize -= 4 // exclude the length field. + su.SetUint32(d.arch, 0, uint32(cusize)) + } + return append(syms, cu...) +} + +func (d *dwctxt) writegdbscript() dwarfSecInfo { + // TODO (aix): make it available + if d.linkctxt.HeadType == objabi.Haix { + return dwarfSecInfo{} + } + if d.linkctxt.LinkMode == LinkExternal && d.linkctxt.HeadType == objabi.Hwindows && d.linkctxt.BuildMode == BuildModeCArchive { + // gcc on Windows places .debug_gdb_scripts in the wrong location, which + // causes the program not to run. See https://golang.org/issue/20183 + // Non c-archives can avoid this issue via a linker script + // (see fix near writeGDBLinkerScript). + // c-archive users would need to specify the linker script manually. + // For UX it's better not to deal with this. + return dwarfSecInfo{} + } + if gdbscript == "" { + return dwarfSecInfo{} + } + + gs := d.ldr.CreateSymForUpdate(".debug_gdb_scripts", 0) + gs.SetType(sym.SDWARFSECT) + + gs.AddUint8(GdbScriptPythonFileId) + gs.Addstring(gdbscript) + return dwarfSecInfo{syms: []loader.Sym{gs.Sym()}} +} + +// FIXME: might be worth looking replacing this map with a function +// that switches based on symbol instead. + +var prototypedies map[string]*dwarf.DWDie + +func dwarfEnabled(ctxt *Link) bool { + if *FlagW { // disable dwarf + return false + } + if *FlagS && ctxt.HeadType != objabi.Hdarwin { + return false + } + if ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hjs { + return false + } + + if ctxt.LinkMode == LinkExternal { + switch { + case ctxt.IsELF: + case ctxt.HeadType == objabi.Hdarwin: + case ctxt.HeadType == objabi.Hwindows: + case ctxt.HeadType == objabi.Haix: + res, err := dwarf.IsDWARFEnabledOnAIXLd(ctxt.extld()) + if err != nil { + Exitf("%v", err) + } + return res + default: + return false + } + } + + return true +} + +// mkBuiltinType populates the dwctxt2 sym lookup maps for the +// newly created builtin type DIE 'typeDie'. +func (d *dwctxt) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie { + // create type DIE + die := d.newdie(&dwtypes, abrv, tname) + + // Look up type symbol. + gotype := d.lookupOrDiag("type:" + tname) + + // Map from die sym to type sym + ds := loader.Sym(die.Sym.(dwSym)) + d.rtmap[ds] = gotype + + // Map from type to def sym + d.tdmap[gotype] = ds + + return die +} + +// dwarfVisitFunction takes a function (text) symbol and processes the +// subprogram DIE for the function and picks up any other DIEs +// (absfns, types) that it references. +func (d *dwctxt) dwarfVisitFunction(fnSym loader.Sym, unit *sym.CompilationUnit) { + // The DWARF subprogram DIE symbol is listed as an aux sym + // of the text (fcn) symbol, so ask the loader to retrieve it, + // as well as the associated range symbol. + infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym) + if infosym == 0 { + return + } + d.ldr.SetAttrNotInSymbolTable(infosym, true) + d.ldr.SetAttrReachable(infosym, true) + unit.FuncDIEs = append(unit.FuncDIEs, sym.LoaderSym(infosym)) + if rangesym != 0 { + d.ldr.SetAttrNotInSymbolTable(rangesym, true) + d.ldr.SetAttrReachable(rangesym, true) + unit.RangeSyms = append(unit.RangeSyms, sym.LoaderSym(rangesym)) + } + + // Walk the relocations of the subprogram DIE symbol to discover + // references to abstract function DIEs, Go type DIES, and + // (via R_USETYPE relocs) types that were originally assigned to + // locals/params but were optimized away. + drelocs := d.ldr.Relocs(infosym) + for ri := 0; ri < drelocs.Count(); ri++ { + r := drelocs.At(ri) + // Look for "use type" relocs. + if r.Type() == objabi.R_USETYPE { + d.defgotype(r.Sym()) + continue + } + if r.Type() != objabi.R_DWARFSECREF { + continue + } + + rsym := r.Sym() + rst := d.ldr.SymType(rsym) + + // Look for abstract function references. + if rst == sym.SDWARFABSFCN { + if !d.ldr.AttrOnList(rsym) { + // abstract function + d.ldr.SetAttrOnList(rsym, true) + unit.AbsFnDIEs = append(unit.AbsFnDIEs, sym.LoaderSym(rsym)) + d.importInfoSymbol(rsym) + } + continue + } + + // Look for type references. + if rst != sym.SDWARFTYPE && rst != sym.Sxxx { + continue + } + if _, ok := d.rtmap[rsym]; ok { + // type already generated + continue + } + + rsn := d.ldr.SymName(rsym) + tn := rsn[len(dwarf.InfoPrefix):] + ts := d.ldr.Lookup("type:"+tn, 0) + d.defgotype(ts) + } +} + +// dwarfGenerateDebugInfo generated debug info entries for all types, +// variables and functions in the program. +// Along with dwarfGenerateDebugSyms they are the two main entry points into +// dwarf generation: dwarfGenerateDebugInfo does all the work that should be +// done before symbol names are mangled while dwarfGenerateDebugSyms does +// all the work that can only be done after addresses have been assigned to +// text symbols. +func dwarfGenerateDebugInfo(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return + } + + d := &dwctxt{ + linkctxt: ctxt, + ldr: ctxt.loader, + arch: ctxt.Arch, + tmap: make(map[string]loader.Sym), + tdmap: make(map[loader.Sym]loader.Sym), + rtmap: make(map[loader.Sym]loader.Sym), + } + d.typeRuntimeEface = d.lookupOrDiag("type:runtime.eface") + d.typeRuntimeIface = d.lookupOrDiag("type:runtime.iface") + + if ctxt.HeadType == objabi.Haix { + // Initial map used to store package size for each DWARF section. + dwsectCUSize = make(map[string]uint64) + } + + // For ctxt.Diagnostic messages. + newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") + + // Unspecified type. There are no references to this in the symbol table. + d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>") + + // Some types that must exist to define other ones (uintptr in particular + // is needed for array size) + d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") + die := d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BASETYPE, "uintptr") + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(d.arch.PtrSize), 0) + newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0) + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, dwSym(d.lookupOrDiag("type:uintptr"))) + + d.uintptrInfoSym = d.mustFind("uintptr") + + // Prototypes needed for type synthesis. + prototypedies = map[string]*dwarf.DWDie{ + "type:runtime.stringStructDWARF": nil, + "type:runtime.slice": nil, + "type:runtime.hmap": nil, + "type:runtime.bmap": nil, + "type:runtime.sudog": nil, + "type:runtime.waitq": nil, + "type:runtime.hchan": nil, + } + + // Needed by the prettyprinter code for interface inspection. + for _, typ := range []string{ + "type:runtime._type", + "type:runtime.arraytype", + "type:runtime.chantype", + "type:runtime.functype", + "type:runtime.maptype", + "type:runtime.ptrtype", + "type:runtime.slicetype", + "type:runtime.structtype", + "type:runtime.interfacetype", + "type:runtime.itab", + "type:runtime.imethod"} { + d.defgotype(d.lookupOrDiag(typ)) + } + + // fake root DIE for compile unit DIEs + var dwroot dwarf.DWDie + flagVariants := make(map[string]bool) + + for _, lib := range ctxt.Library { + + consts := d.ldr.Lookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) + for _, unit := range lib.Units { + // We drop the constants into the first CU. + if consts != 0 { + unit.Consts = sym.LoaderSym(consts) + d.importInfoSymbol(consts) + consts = 0 + } + ctxt.compUnits = append(ctxt.compUnits, unit) + + // We need at least one runtime unit. + if unit.Lib.Pkg == "runtime" { + ctxt.runtimeCU = unit + } + + cuabrv := dwarf.DW_ABRV_COMPUNIT + if len(unit.Textp) == 0 { + cuabrv = dwarf.DW_ABRV_COMPUNIT_TEXTLESS + } + unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg) + newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0) + // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. + compDir := getCompilationDir() + // TODO: Make this be the actual compilation directory, not + // the linker directory. If we move CU construction into the + // compiler, this should happen naturally. + newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir) + + var peData []byte + if producerExtra := d.ldr.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0); producerExtra != 0 { + peData = d.ldr.Data(producerExtra) + } + producer := "Go cmd/compile " + buildcfg.Version + if len(peData) > 0 { + // We put a semicolon before the flags to clearly + // separate them from the version, which can be long + // and have lots of weird things in it in development + // versions. We promise not to put a semicolon in the + // version, so it should be safe for readers to scan + // forward to the semicolon. + producer += "; " + string(peData) + flagVariants[string(peData)] = true + } else { + flagVariants[""] = true + } + + newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer) + + var pkgname string + if pnSymIdx := d.ldr.Lookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); pnSymIdx != 0 { + pnsData := d.ldr.Data(pnSymIdx) + pkgname = string(pnsData) + } + newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname) + + // Scan all functions in this compilation unit, create + // DIEs for all referenced types, find all referenced + // abstract functions, visit range symbols. Note that + // Textp has been dead-code-eliminated already. + for _, s := range unit.Textp { + d.dwarfVisitFunction(loader.Sym(s), unit) + } + } + } + + // Fix for 31034: if the objects feeding into this link were compiled + // with different sets of flags, then don't issue an error if + // the -strictdups checks fail. + if checkStrictDups > 1 && len(flagVariants) > 1 { + checkStrictDups = 1 + } + + // Make a pass through all data symbols, looking for those + // corresponding to reachable, Go-generated, user-visible + // global variables. For each global of this sort, locate + // the corresponding compiler-generated DIE symbol and tack + // it onto the list associated with the unit. + // Also looks for dictionary symbols and generates DIE symbols for each + // type they reference. + for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ { + if !d.ldr.AttrReachable(idx) || + d.ldr.AttrNotInSymbolTable(idx) || + d.ldr.SymVersion(idx) >= sym.SymVerStatic { + continue + } + t := d.ldr.SymType(idx) + switch t { + case sym.SRODATA, sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS: + // ok + default: + continue + } + // Skip things with no type, unless it's a dictionary + gt := d.ldr.SymGoType(idx) + if gt == 0 { + if t == sym.SRODATA { + if d.ldr.IsDict(idx) { + // This is a dictionary, make sure that all types referenced by this dictionary are reachable + relocs := d.ldr.Relocs(idx) + for i := 0; i < relocs.Count(); i++ { + reloc := relocs.At(i) + if reloc.Type() == objabi.R_USEIFACE { + d.defgotype(reloc.Sym()) + } + } + } + } + continue + } + // Skip file local symbols (this includes static tmps, stack + // object symbols, and local symbols in assembler src files). + if d.ldr.IsFileLocal(idx) { + continue + } + sn := d.ldr.SymName(idx) + if sn == "" { + // skip aux symbols + continue + } + + // Find compiler-generated DWARF info sym for global in question, + // and tack it onto the appropriate unit. Note that there are + // circumstances under which we can't find the compiler-generated + // symbol-- this typically happens as a result of compiler options + // (e.g. compile package X with "-dwarf=0"). + + // FIXME: use an aux sym or a relocation here instead of a + // name lookup. + varDIE := d.ldr.Lookup(dwarf.InfoPrefix+sn, 0) + if varDIE != 0 { + unit := d.ldr.SymUnit(idx) + d.defgotype(gt) + unit.VarDIEs = append(unit.VarDIEs, sym.LoaderSym(varDIE)) + } + } + + d.synthesizestringtypes(ctxt, dwtypes.Child) + d.synthesizeslicetypes(ctxt, dwtypes.Child) + d.synthesizemaptypes(ctxt, dwtypes.Child) + d.synthesizechantypes(ctxt, dwtypes.Child) +} + +// dwarfGenerateDebugSyms constructs debug_line, debug_frame, and +// debug_loc. It also writes out the debug_info section using symbols +// generated in dwarfGenerateDebugInfo2. +func dwarfGenerateDebugSyms(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return + } + d := &dwctxt{ + linkctxt: ctxt, + ldr: ctxt.loader, + arch: ctxt.Arch, + dwmu: new(sync.Mutex), + } + d.dwarfGenerateDebugSyms() +} + +// dwUnitSyms stores input and output symbols for DWARF generation +// for a given compilation unit. +type dwUnitSyms struct { + // Inputs for a given unit. + lineProlog loader.Sym + rangeProlog loader.Sym + infoEpilog loader.Sym + + // Outputs for a given unit. + linesyms []loader.Sym + infosyms []loader.Sym + locsyms []loader.Sym + rangessyms []loader.Sym +} + +// dwUnitPortion assembles the DWARF content for a given compilation +// unit: debug_info, debug_lines, debug_ranges, debug_loc (debug_frame +// is handled elsewere). Order is important; the calls to writelines +// and writepcranges below make updates to the compilation unit DIE, +// hence they have to happen before the call to writeUnitInfo. +func (d *dwctxt) dwUnitPortion(u *sym.CompilationUnit, abbrevsym loader.Sym, us *dwUnitSyms) { + if u.DWInfo.Abbrev != dwarf.DW_ABRV_COMPUNIT_TEXTLESS { + us.linesyms = d.writelines(u, us.lineProlog) + base := loader.Sym(u.Textp[0]) + us.rangessyms = d.writepcranges(u, base, u.PCs, us.rangeProlog) + us.locsyms = d.collectUnitLocs(u) + } + us.infosyms = d.writeUnitInfo(u, abbrevsym, us.infoEpilog) +} + +func (d *dwctxt) dwarfGenerateDebugSyms() { + abbrevSec := d.writeabbrev() + dwarfp = append(dwarfp, abbrevSec) + d.calcCompUnitRanges() + sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits)) + + // newdie adds DIEs to the *beginning* of the parent's DIE list. + // Now that we're done creating DIEs, reverse the trees so DIEs + // appear in the order they were created. + for _, u := range d.linkctxt.compUnits { + reversetree(&u.DWInfo.Child) + } + reversetree(&dwtypes.Child) + movetomodule(d.linkctxt, &dwtypes) + + mkSecSym := func(name string) loader.Sym { + s := d.ldr.CreateSymForUpdate(name, 0) + s.SetType(sym.SDWARFSECT) + s.SetReachable(true) + return s.Sym() + } + mkAnonSym := func(kind sym.SymKind) loader.Sym { + s := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0)) + s.SetType(kind) + s.SetReachable(true) + return s.Sym() + } + + // Create the section symbols. + frameSym := mkSecSym(".debug_frame") + locSym := mkSecSym(".debug_loc") + lineSym := mkSecSym(".debug_line") + rangesSym := mkSecSym(".debug_ranges") + infoSym := mkSecSym(".debug_info") + + // Create the section objects + lineSec := dwarfSecInfo{syms: []loader.Sym{lineSym}} + locSec := dwarfSecInfo{syms: []loader.Sym{locSym}} + rangesSec := dwarfSecInfo{syms: []loader.Sym{rangesSym}} + frameSec := dwarfSecInfo{syms: []loader.Sym{frameSym}} + infoSec := dwarfSecInfo{syms: []loader.Sym{infoSym}} + + // Create any new symbols that will be needed during the + // parallel portion below. + ncu := len(d.linkctxt.compUnits) + unitSyms := make([]dwUnitSyms, ncu) + for i := 0; i < ncu; i++ { + us := &unitSyms[i] + us.lineProlog = mkAnonSym(sym.SDWARFLINES) + us.rangeProlog = mkAnonSym(sym.SDWARFRANGE) + us.infoEpilog = mkAnonSym(sym.SDWARFFCN) + } + + var wg sync.WaitGroup + sema := make(chan struct{}, runtime.GOMAXPROCS(0)) + + // Kick off generation of .debug_frame, since it doesn't have + // any entanglements and can be started right away. + wg.Add(1) + go func() { + sema <- struct{}{} + defer func() { + <-sema + wg.Done() + }() + frameSec = d.writeframes(frameSym) + }() + + // Create a goroutine per comp unit to handle the generation that + // unit's portion of .debug_line, .debug_loc, .debug_ranges, and + // .debug_info. + wg.Add(len(d.linkctxt.compUnits)) + for i := 0; i < ncu; i++ { + go func(u *sym.CompilationUnit, us *dwUnitSyms) { + sema <- struct{}{} + defer func() { + <-sema + wg.Done() + }() + d.dwUnitPortion(u, abbrevSec.secSym(), us) + }(d.linkctxt.compUnits[i], &unitSyms[i]) + } + wg.Wait() + + markReachable := func(syms []loader.Sym) []loader.Sym { + for _, s := range syms { + d.ldr.SetAttrNotInSymbolTable(s, true) + d.ldr.SetAttrReachable(s, true) + } + return syms + } + + // Stitch together the results. + for i := 0; i < ncu; i++ { + r := &unitSyms[i] + lineSec.syms = append(lineSec.syms, markReachable(r.linesyms)...) + infoSec.syms = append(infoSec.syms, markReachable(r.infosyms)...) + locSec.syms = append(locSec.syms, markReachable(r.locsyms)...) + rangesSec.syms = append(rangesSec.syms, markReachable(r.rangessyms)...) + } + dwarfp = append(dwarfp, lineSec) + dwarfp = append(dwarfp, frameSec) + gdbScriptSec := d.writegdbscript() + if gdbScriptSec.secSym() != 0 { + dwarfp = append(dwarfp, gdbScriptSec) + } + dwarfp = append(dwarfp, infoSec) + if len(locSec.syms) > 1 { + dwarfp = append(dwarfp, locSec) + } + dwarfp = append(dwarfp, rangesSec) + + // Check to make sure we haven't listed any symbols more than once + // in the info section. This used to be done by setting and + // checking the OnList attribute in "putdie", but that strategy + // was not friendly for concurrency. + seen := loader.MakeBitmap(d.ldr.NSym()) + for _, s := range infoSec.syms { + if seen.Has(s) { + log.Fatalf("symbol %s listed multiple times", d.ldr.SymName(s)) + } + seen.Set(s) + } +} + +func (d *dwctxt) collectUnitLocs(u *sym.CompilationUnit) []loader.Sym { + syms := []loader.Sym{} + for _, fn := range u.FuncDIEs { + relocs := d.ldr.Relocs(loader.Sym(fn)) + for i := 0; i < relocs.Count(); i++ { + reloc := relocs.At(i) + if reloc.Type() != objabi.R_DWARFSECREF { + continue + } + rsym := reloc.Sym() + if d.ldr.SymType(rsym) == sym.SDWARFLOC { + syms = append(syms, rsym) + // One location list entry per function, but many relocations to it. Don't duplicate. + break + } + } + } + return syms +} + +/* + * Elf. + */ +func dwarfaddshstrings(ctxt *Link, shstrtab *loader.SymbolBuilder) { + if *FlagW { // disable dwarf + return + } + + secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts", "ranges"} + for _, sec := range secs { + shstrtab.Addstring(".debug_" + sec) + if ctxt.IsExternal() { + shstrtab.Addstring(elfRelType + ".debug_" + sec) + } else { + shstrtab.Addstring(".zdebug_" + sec) + } + } +} + +func dwarfaddelfsectionsyms(ctxt *Link) { + if *FlagW { // disable dwarf + return + } + if ctxt.LinkMode != LinkExternal { + return + } + + ldr := ctxt.loader + for _, si := range dwarfp { + s := si.secSym() + sect := ldr.SymSect(si.secSym()) + putelfsectionsym(ctxt, ctxt.Out, s, sect.Elfsect.(*ElfShdr).shnum) + } +} + +// dwarfcompress compresses the DWARF sections. Relocations are applied +// on the fly. After this, dwarfp will contain a different (new) set of +// symbols, and sections may have been replaced. +func dwarfcompress(ctxt *Link) { + // compressedSect is a helper type for parallelizing compression. + type compressedSect struct { + index int + compressed []byte + syms []loader.Sym + } + + supported := ctxt.IsELF || ctxt.IsWindows() || ctxt.IsDarwin() + if !ctxt.compressDWARF || !supported || ctxt.IsExternal() { + return + } + + var compressedCount int + resChannel := make(chan compressedSect) + for i := range dwarfp { + go func(resIndex int, syms []loader.Sym) { + resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms} + }(compressedCount, dwarfp[i].syms) + compressedCount++ + } + res := make([]compressedSect, compressedCount) + for ; compressedCount > 0; compressedCount-- { + r := <-resChannel + res[r.index] = r + } + + ldr := ctxt.loader + var newDwarfp []dwarfSecInfo + Segdwarf.Sections = Segdwarf.Sections[:0] + for _, z := range res { + s := z.syms[0] + if z.compressed == nil { + // Compression didn't help. + ds := dwarfSecInfo{syms: z.syms} + newDwarfp = append(newDwarfp, ds) + Segdwarf.Sections = append(Segdwarf.Sections, ldr.SymSect(s)) + } else { + var compressedSegName string + if ctxt.IsELF { + compressedSegName = ldr.SymSect(s).Name + } else { + compressedSegName = ".zdebug_" + ldr.SymSect(s).Name[len(".debug_"):] + } + sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04) + sect.Align = int32(ctxt.Arch.Alignment) + sect.Length = uint64(len(z.compressed)) + sect.Compressed = true + newSym := ldr.MakeSymbolBuilder(compressedSegName) + ldr.SetAttrReachable(s, true) + newSym.SetData(z.compressed) + newSym.SetSize(int64(len(z.compressed))) + ldr.SetSymSect(newSym.Sym(), sect) + ds := dwarfSecInfo{syms: []loader.Sym{newSym.Sym()}} + newDwarfp = append(newDwarfp, ds) + + // compressed symbols are no longer needed. + for _, s := range z.syms { + ldr.SetAttrReachable(s, false) + ldr.FreeSym(s) + } + } + } + dwarfp = newDwarfp + + // Re-compute the locations of the compressed DWARF symbols + // and sections, since the layout of these within the file is + // based on Section.Vaddr and Symbol.Value. + pos := Segdwarf.Vaddr + var prevSect *sym.Section + for _, si := range dwarfp { + for _, s := range si.syms { + ldr.SetSymValue(s, int64(pos)) + sect := ldr.SymSect(s) + if sect != prevSect { + sect.Vaddr = uint64(pos) + prevSect = sect + } + if ldr.SubSym(s) != 0 { + log.Fatalf("%s: unexpected sub-symbols", ldr.SymName(s)) + } + pos += uint64(ldr.SymSize(s)) + if ctxt.IsWindows() { + pos = uint64(Rnd(int64(pos), PEFILEALIGN)) + } + } + } + Segdwarf.Length = pos - Segdwarf.Vaddr +} + +type compilationUnitByStartPC []*sym.CompilationUnit + +func (v compilationUnitByStartPC) Len() int { return len(v) } +func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] } + +func (v compilationUnitByStartPC) Less(i, j int) bool { + switch { + case len(v[i].Textp) == 0 && len(v[j].Textp) == 0: + return v[i].Lib.Pkg < v[j].Lib.Pkg + case len(v[i].Textp) != 0 && len(v[j].Textp) == 0: + return true + case len(v[i].Textp) == 0 && len(v[j].Textp) != 0: + return false + default: + return v[i].PCs[0].Start < v[j].PCs[0].Start + } +} + +// getPkgFromCUSym returns the package name for the compilation unit +// represented by s. +// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get +// the package name. +func (d *dwctxt) getPkgFromCUSym(s loader.Sym) string { + return strings.TrimPrefix(d.ldr.SymName(s), dwarf.InfoPrefix+".pkg.") +} + +// On AIX, the symbol table needs to know where are the compilation units parts +// for a specific package in each .dw section. +// dwsectCUSize map will save the size of a compilation unit for +// the corresponding .dw section. +// This size can later be retrieved with the index "sectionName.pkgName". +var dwsectCUSizeMu sync.Mutex +var dwsectCUSize map[string]uint64 + +// getDwsectCUSize retrieves the corresponding package size inside the current section. +func getDwsectCUSize(sname string, pkgname string) uint64 { + return dwsectCUSize[sname+"."+pkgname] +} + +func addDwsectCUSize(sname string, pkgname string, size uint64) { + dwsectCUSizeMu.Lock() + defer dwsectCUSizeMu.Unlock() + dwsectCUSize[sname+"."+pkgname] += size +} diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go new file mode 100644 index 0000000..a3db4a9 --- /dev/null +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -0,0 +1,1971 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "debug/dwarf" + "debug/pe" + "fmt" + "internal/testenv" + "io" + "os" + "path/filepath" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "testing" + + intdwarf "cmd/internal/dwarf" + objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function + "cmd/link/internal/dwtest" +) + +const ( + DefaultOpt = "-gcflags=" + NoOpt = "-gcflags=-l -N" + OptInl4 = "-gcflags=-l=4" + OptAllInl4 = "-gcflags=all=-l=4" +) + +func TestRuntimeTypesPresent(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + dir := t.TempDir() + + f := gobuild(t, dir, `package main; func main() { }`, NoOpt) + defer f.Close() + + dwarf, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + want := map[string]bool{ + "runtime._type": true, + "runtime.arraytype": true, + "runtime.chantype": true, + "runtime.functype": true, + "runtime.maptype": true, + "runtime.ptrtype": true, + "runtime.slicetype": true, + "runtime.structtype": true, + "runtime.interfacetype": true, + "runtime.itab": true, + "runtime.imethod": true, + } + + found := findTypes(t, dwarf, want) + if len(found) != len(want) { + t.Errorf("found %v, want %v", found, want) + } +} + +func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) { + found = make(map[string]bool) + rdr := dw.Reader() + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + switch entry.Tag { + case dwarf.TagTypedef: + if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] { + found[name] = true + } + } + } + return +} + +type builtFile struct { + *objfilepkg.File + path string +} + +func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile { + src := filepath.Join(dir, "test.go") + dst := filepath.Join(dir, "out.exe") + + if err := os.WriteFile(src, []byte(testfile), 0666); err != nil { + t.Fatal(err) + } + + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", gcflags, "-o", dst, src) + b, err := cmd.CombinedOutput() + if len(b) != 0 { + t.Logf("## build output:\n%s", b) + } + if err != nil { + t.Fatalf("build error: %v", err) + } + + f, err := objfilepkg.Open(dst) + if err != nil { + t.Fatal(err) + } + return &builtFile{f, dst} +} + +// Similar to gobuild() above, but uses a main package instead of a test.go file. + +func gobuildTestdata(t *testing.T, tdir string, pkgDir string, gcflags string) *builtFile { + dst := filepath.Join(tdir, "out.exe") + + // Run a build with an updated GOPATH + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", gcflags, "-o", dst) + cmd.Dir = pkgDir + if b, err := cmd.CombinedOutput(); err != nil { + t.Logf("build: %s\n", b) + t.Fatalf("build error: %v", err) + } + + f, err := objfilepkg.Open(dst) + if err != nil { + t.Fatal(err) + } + return &builtFile{f, dst} +} + +func TestEmbeddedStructMarker(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + const prog = ` +package main + +import "fmt" + +type Foo struct { v int } +type Bar struct { + Foo + name string +} +type Baz struct { + *Foo + name string +} + +func main() { + bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"} + baz := Baz{ Foo: &bar.Foo, name: "123" } + fmt.Println(bar, baz) +}` + + want := map[string]map[string]bool{ + "main.Foo": {"v": false}, + "main.Bar": {"Foo": true, "name": false}, + "main.Baz": {"Foo": true, "name": false}, + } + + dir := t.TempDir() + + f := gobuild(t, dir, prog, NoOpt) + + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + switch entry.Tag { + case dwarf.TagStructType: + name := entry.Val(dwarf.AttrName).(string) + wantMembers := want[name] + if wantMembers == nil { + continue + } + gotMembers, err := findMembers(rdr) + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + if !reflect.DeepEqual(gotMembers, wantMembers) { + t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers) + } + delete(want, name) + } + } + if len(want) != 0 { + t.Errorf("failed to check all expected types: missing types = %+v", want) + } +} + +func findMembers(rdr *dwarf.Reader) (map[string]bool, error) { + memberEmbedded := map[string]bool{} + // TODO(hyangah): define in debug/dwarf package + const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field) + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + return nil, err + } + switch entry.Tag { + case dwarf.TagMember: + name := entry.Val(dwarf.AttrName).(string) + embedded := entry.Val(goEmbeddedStruct).(bool) + memberEmbedded[name] = embedded + case 0: + return memberEmbedded, nil + } + } + return memberEmbedded, nil +} + +func TestSizes(t *testing.T) { + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + // External linking may bring in C symbols with unknown size. Skip. + testenv.MustInternalLink(t) + + t.Parallel() + + // DWARF sizes should never be -1. + // See issue #21097 + const prog = ` +package main +var x func() +var y [4]func() +func main() { + x = nil + y[0] = nil +} +` + dir := t.TempDir() + + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + rdr := d.Reader() + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + switch entry.Tag { + case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef: + default: + continue + } + typ, err := d.Type(entry.Offset) + if err != nil { + t.Fatalf("can't read type: %v", err) + } + if typ.Size() < 0 { + t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ) + } + } +} + +func TestFieldOverlap(t *testing.T) { + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + t.Parallel() + + // This test grew out of issue 21094, where specific sudog<T> DWARF types + // had elem fields set to values instead of pointers. + const prog = ` +package main + +var c chan string + +func main() { + c <- "foo" +} +` + dir := t.TempDir() + + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if entry.Tag != dwarf.TagStructType { + continue + } + typ, err := d.Type(entry.Offset) + if err != nil { + t.Fatalf("can't read type: %v", err) + } + s := typ.(*dwarf.StructType) + for i := 0; i < len(s.Field); i++ { + end := s.Field[i].ByteOffset + s.Field[i].Type.Size() + var limit int64 + if i == len(s.Field)-1 { + limit = s.Size() + } else { + limit = s.Field[i+1].ByteOffset + } + if end > limit { + name := entry.Val(dwarf.AttrName).(string) + t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name) + } + } + } +} + +func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) { + t.Parallel() + + prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive) + + dir := t.TempDir() + + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Locate the main.main DIE + mains := ex.Named("main.main") + if len(mains) == 0 { + t.Fatalf("unable to locate DIE for main.main") + } + if len(mains) != 1 { + t.Fatalf("more than one main.main DIE") + } + maindie := mains[0] + + // Vet the main.main DIE + if maindie.Tag != dwarf.TagSubprogram { + t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag) + } + + // Walk main's children and select variable "i". + mainIdx := ex.IdxFromOffset(maindie.Offset) + childDies := ex.Children(mainIdx) + var iEntry *dwarf.Entry + for _, child := range childDies { + if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" { + iEntry = child + break + } + } + if iEntry == nil { + t.Fatalf("didn't find DW_TAG_variable for i in main.main") + } + + // Verify line/file attributes. + line := iEntry.Val(dwarf.AttrDeclLine) + if line == nil || line.(int64) != int64(expectLine) { + t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine) + } + + fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64) + if !fileIdxOK { + t.Errorf("missing or invalid DW_AT_decl_file for main") + } + file, err := ex.FileRef(d, mainIdx, fileIdx) + if err != nil { + t.Fatalf("FileRef: %v", err) + } + base := filepath.Base(file) + if base != expectFile { + t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile) + } +} + +func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", "test.go", 5, "") +} + +func TestVarDeclCoordsWithLineDirective(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective", + "foobar.go", 202, "//line /foobar.go:200") +} + +func TestInlinedRoutineRecords(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + const prog = ` +package main + +var G int + +func noinline(x int) int { + defer func() { G += x }() + return x +} + +func cand(x, y int) int { + return noinline(x+y) ^ (y - x) +} + +func main() { + x := cand(G*G,G|7%G) + G = x +} +` + dir := t.TempDir() + + // Note: this is a build with "-l=4", as opposed to "-l -N". The + // test is intended to verify DWARF that is only generated when + // the inliner is active. We're only going to look at the DWARF for + // main.main, however, hence we build with "-gcflags=-l=4" as opposed + // to "-gcflags=all=-l=4". + f := gobuild(t, dir, prog, OptInl4) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // The inlined subroutines we expect to visit + expectedInl := []string{"main.cand"} + + rdr := d.Reader() + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Locate the main.main DIE + mains := ex.Named("main.main") + if len(mains) == 0 { + t.Fatalf("unable to locate DIE for main.main") + } + if len(mains) != 1 { + t.Fatalf("more than one main.main DIE") + } + maindie := mains[0] + + // Vet the main.main DIE + if maindie.Tag != dwarf.TagSubprogram { + t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag) + } + + // Walk main's children and pick out the inlined subroutines + mainIdx := ex.IdxFromOffset(maindie.Offset) + childDies := ex.Children(mainIdx) + exCount := 0 + for _, child := range childDies { + if child.Tag == dwarf.TagInlinedSubroutine { + // Found an inlined subroutine, locate abstract origin. + ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !originOK { + t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset) + } + originDIE := ex.EntryFromOffset(ooff) + if originDIE == nil { + t.Fatalf("can't locate origin DIE at off %v", ooff) + } + + // Walk the children of the abstract subroutine. We expect + // to see child variables there, even if (perhaps due to + // optimization) there are no references to them from the + // inlined subroutine DIE. + absFcnIdx := ex.IdxFromOffset(ooff) + absFcnChildDies := ex.Children(absFcnIdx) + if len(absFcnChildDies) != 2 { + t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies)) + } + formalCount := 0 + for _, absChild := range absFcnChildDies { + if absChild.Tag == dwarf.TagFormalParameter { + formalCount += 1 + continue + } + t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag) + } + if formalCount != 2 { + t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount) + } + + if exCount >= len(expectedInl) { + t.Fatalf("too many inlined subroutines found in main.main") + } + + // Name should check out. + expected := expectedInl[exCount] + if name, ok := originDIE.Val(dwarf.AttrName).(string); ok { + if name != expected { + t.Fatalf("expected inlined routine %s got %s", name, expected) + } + } + exCount++ + + // Verify that the call_file attribute for the inlined + // instance is ok. In this case it should match the file + // for the main routine. To do this we need to locate the + // compilation unit DIE that encloses what we're looking + // at; this can be done with the examiner. + cf, cfOK := child.Val(dwarf.AttrCallFile).(int64) + if !cfOK { + t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset) + } + file, err := ex.FileRef(d, mainIdx, cf) + if err != nil { + t.Errorf("FileRef: %v", err) + continue + } + base := filepath.Base(file) + if base != "test.go" { + t.Errorf("bad call_file attribute, found '%s', want '%s'", + file, "test.go") + } + + omap := make(map[dwarf.Offset]bool) + + // Walk the child variables of the inlined routine. Each + // of them should have a distinct abstract origin-- if two + // vars point to the same origin things are definitely broken. + inlIdx := ex.IdxFromOffset(child.Offset) + inlChildDies := ex.Children(inlIdx) + for _, k := range inlChildDies { + ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !originOK { + t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset) + } + if _, found := omap[ooff]; found { + t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset) + } + omap[ooff] = true + } + } + } + if exCount != len(expectedInl) { + t.Fatalf("not enough inlined subroutines found in main.main") + } +} + +func abstractOriginSanity(t *testing.T, pkgDir string, flags string) { + t.Parallel() + + dir := t.TempDir() + + // Build with inlining, to exercise DWARF inlining support. + f := gobuildTestdata(t, dir, filepath.Join(pkgDir, "main"), flags) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + rdr := d.Reader() + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Make a pass through all DIEs looking for abstract origin + // references. + abscount := 0 + for i, die := range ex.DIEs() { + // Does it have an abstract origin? + ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !originOK { + continue + } + + // All abstract origin references should be resolvable. + abscount += 1 + originDIE := ex.EntryFromOffset(ooff) + if originDIE == nil { + ex.DumpEntry(i, false, 0) + t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset) + } + + // Suppose that DIE X has parameter/variable children {K1, + // K2, ... KN}. If X has an abstract origin of A, then for + // each KJ, the abstract origin of KJ should be a child of A. + // Note that this same rule doesn't hold for non-variable DIEs. + pidx := ex.IdxFromOffset(die.Offset) + if pidx < 0 { + t.Fatalf("can't locate DIE id") + } + kids := ex.Children(pidx) + for _, kid := range kids { + if kid.Tag != dwarf.TagVariable && + kid.Tag != dwarf.TagFormalParameter { + continue + } + kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !originOK { + continue + } + childOriginDIE := ex.EntryFromOffset(kooff) + if childOriginDIE == nil { + ex.DumpEntry(i, false, 0) + t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset) + } + coidx := ex.IdxFromOffset(childOriginDIE.Offset) + childOriginParent := ex.Parent(coidx) + if childOriginParent != originDIE { + ex.DumpEntry(i, false, 0) + t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset) + } + } + } + if abscount == 0 { + t.Fatalf("no abstract origin refs found, something is wrong") + } +} + +func TestAbstractOriginSanity(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + if wd, err := os.Getwd(); err == nil { + gopathdir := filepath.Join(wd, "testdata", "httptest") + abstractOriginSanity(t, gopathdir, OptAllInl4) + } else { + t.Fatalf("os.Getwd() failed %v", err) + } +} + +func TestAbstractOriginSanityIssue25459(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + if runtime.GOARCH != "amd64" && runtime.GOARCH != "386" { + t.Skip("skipping on not-amd64 not-386; location lists not supported") + } + + if wd, err := os.Getwd(); err == nil { + gopathdir := filepath.Join(wd, "testdata", "issue25459") + abstractOriginSanity(t, gopathdir, DefaultOpt) + } else { + t.Fatalf("os.Getwd() failed %v", err) + } +} + +func TestAbstractOriginSanityIssue26237(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + if wd, err := os.Getwd(); err == nil { + gopathdir := filepath.Join(wd, "testdata", "issue26237") + abstractOriginSanity(t, gopathdir, DefaultOpt) + } else { + t.Fatalf("os.Getwd() failed %v", err) + } +} + +func TestRuntimeTypeAttrInternal(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustInternalLink(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + if runtime.GOOS == "windows" { + t.Skip("skipping on windows; test is incompatible with relocatable binaries") + } + + testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal") +} + +// External linking requires a host linker (https://golang.org/src/cmd/cgo/doc.go l.732) +func TestRuntimeTypeAttrExternal(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + // Explicitly test external linking, for dsymutil compatibility on Darwin. + if runtime.GOARCH == "ppc64" { + t.Skip("-linkmode=external not supported on ppc64") + } + + if runtime.GOOS == "windows" { + t.Skip("skipping on windows; test is incompatible with relocatable binaries") + } + + testRuntimeTypeAttr(t, "-ldflags=-linkmode=external") +} + +func testRuntimeTypeAttr(t *testing.T, flags string) { + t.Parallel() + + const prog = ` +package main + +import "unsafe" + +type X struct{ _ int } + +func main() { + var x interface{} = &X{} + p := *(*uintptr)(unsafe.Pointer(&x)) + print(p) +} +` + dir := t.TempDir() + + f := gobuild(t, dir, prog, flags) + defer f.Close() + + out, err := testenv.Command(t, f.path).CombinedOutput() + if err != nil { + t.Fatalf("could not run test program: %v", err) + } + addr, err := strconv.ParseUint(string(out), 10, 64) + if err != nil { + t.Fatalf("could not parse type address from program output %q: %v", out, err) + } + + symbols, err := f.Symbols() + if err != nil { + t.Fatalf("error reading symbols: %v", err) + } + var types *objfilepkg.Sym + for _, sym := range symbols { + if sym.Name == "runtime.types" { + types = &sym + break + } + } + if types == nil { + t.Fatal("couldn't find runtime.types in symbols") + } + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + dies := ex.Named("*main.X") + if len(dies) != 1 { + t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies)) + } + rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type) + if rtAttr == nil { + t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0]) + } + + if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { + return // everything is PIE on ARM64, addresses are relocated + } + if rtAttr.(uint64)+types.Addr != addr { + t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr) + } +} + +func TestIssue27614(t *testing.T) { + // Type references in debug_info should always use the DW_TAG_typedef_type + // for the type, when that's generated. + + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + dir := t.TempDir() + + const prog = `package main + +import "fmt" + +type astruct struct { + X int +} + +type bstruct struct { + X float32 +} + +var globalptr *astruct +var globalvar astruct +var bvar0, bvar1, bvar2 bstruct + +func main() { + fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2) +} +` + + f := gobuild(t, dir, prog, NoOpt) + + defer f.Close() + + data, err := f.DWARF() + if err != nil { + t.Fatal(err) + } + + rdr := data.Reader() + + var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry + var globalptrDIE, globalvarDIE *dwarf.Entry + var bvarDIE [3]*dwarf.Entry + + for { + e, err := rdr.Next() + if err != nil { + t.Fatal(err) + } + if e == nil { + break + } + + name, _ := e.Val(dwarf.AttrName).(string) + + switch e.Tag { + case dwarf.TagTypedef: + switch name { + case "main.astruct": + astructTypeDIE = e + case "main.bstruct": + bstructTypeDIE = e + } + case dwarf.TagPointerType: + if name == "*main.astruct" { + ptrastructTypeDIE = e + } + case dwarf.TagVariable: + switch name { + case "main.globalptr": + globalptrDIE = e + case "main.globalvar": + globalvarDIE = e + default: + const bvarprefix = "main.bvar" + if strings.HasPrefix(name, bvarprefix) { + i, _ := strconv.Atoi(name[len(bvarprefix):]) + bvarDIE[i] = e + } + } + } + } + + typedieof := func(e *dwarf.Entry) dwarf.Offset { + return e.Val(dwarf.AttrType).(dwarf.Offset) + } + + if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset { + t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset) + } + + if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset { + t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset) + } + + if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset { + t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset) + } + + for i := range bvarDIE { + if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset { + t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset) + } + } +} + +func TestStaticTmp(t *testing.T) { + // Checks that statictmp variables do not appear in debug_info or the + // symbol table. + // Also checks that statictmp variables do not collide with user defined + // variables (issue #25113) + + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + dir := t.TempDir() + + const prog = `package main + +var stmp_0 string +var a []int + +func init() { + a = []int{ 7 } +} + +func main() { + println(a[0]) +} +` + + f := gobuild(t, dir, prog, NoOpt) + + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + for { + e, err := rdr.Next() + if err != nil { + t.Fatal(err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagVariable { + continue + } + name, ok := e.Val(dwarf.AttrName).(string) + if !ok { + continue + } + if strings.Contains(name, "stmp") { + t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset) + } + } + + // When external linking, we put all symbols in the symbol table (so the + // external linker can find them). Skip the symbol table check. + // TODO: maybe there is some way to tell the external linker not to put + // those symbols in the executable's symbol table? Prefix the symbol name + // with "." or "L" to pretend it is a label? + if !testenv.CanInternalLink() { + return + } + + syms, err := f.Symbols() + if err != nil { + t.Fatalf("error reading symbols: %v", err) + } + for _, sym := range syms { + if strings.Contains(sym.Name, "stmp") { + t.Errorf("statictmp variable found in symbol table: %s", sym.Name) + } + } +} + +func TestPackageNameAttr(t *testing.T) { + const dwarfAttrGoPackageName = dwarf.Attr(0x2905) + const dwarfGoLanguage = 22 + + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + dir := t.TempDir() + + const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n" + + f := gobuild(t, dir, prog, NoOpt) + + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + runtimeUnitSeen := false + for { + e, err := rdr.Next() + if err != nil { + t.Fatal(err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagCompileUnit { + continue + } + if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage { + continue + } + + pn, ok := e.Val(dwarfAttrGoPackageName).(string) + if !ok { + name, _ := e.Val(dwarf.AttrName).(string) + t.Errorf("found compile unit without package name: %s", name) + + } + if pn == "" { + name, _ := e.Val(dwarf.AttrName).(string) + t.Errorf("found compile unit with empty package name: %s", name) + } else { + if pn == "runtime" { + runtimeUnitSeen = true + } + } + } + + // Something is wrong if there's no runtime compilation unit. + if !runtimeUnitSeen { + t.Errorf("no package name for runtime unit") + } +} + +func TestMachoIssue32233(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + if runtime.GOOS != "darwin" { + t.Skip("skipping; test only interesting on darwin") + } + + tmpdir := t.TempDir() + + wd, err := os.Getwd() + if err != nil { + t.Fatalf("where am I? %v", err) + } + pdir := filepath.Join(wd, "testdata", "issue32233", "main") + f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt) + f.Close() +} + +func TestWindowsIssue36495(t *testing.T) { + testenv.MustHaveGoBuild(t) + if runtime.GOOS != "windows" { + t.Skip("skipping: test only on windows") + } + + dir := t.TempDir() + + prog := ` +package main + +import "fmt" + +func main() { + fmt.Println("Hello World") +}` + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + exe, err := pe.Open(f.path) + if err != nil { + t.Fatalf("error opening pe file: %v", err) + } + defer exe.Close() + dw, err := exe.DWARF() + if err != nil { + t.Fatalf("error parsing DWARF: %v", err) + } + rdr := dw.Reader() + for { + e, err := rdr.Next() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagCompileUnit { + continue + } + lnrdr, err := dw.LineReader(e) + if err != nil { + t.Fatalf("error creating DWARF line reader: %v", err) + } + if lnrdr != nil { + var lne dwarf.LineEntry + for { + err := lnrdr.Next(&lne) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("error reading next DWARF line: %v", err) + } + if strings.Contains(lne.File.Name, `\`) { + t.Errorf("filename should not contain backslash: %v", lne.File.Name) + } + } + } + rdr.SkipChildren() + } +} + +func TestIssue38192(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + // Build a test program that contains a translation unit whose + // text (from am assembly source) contains only a single instruction. + tmpdir := t.TempDir() + wd, err := os.Getwd() + if err != nil { + t.Fatalf("where am I? %v", err) + } + pdir := filepath.Join(wd, "testdata", "issue38192") + f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt) + defer f.Close() + + // Open the resulting binary and examine the DWARF it contains. + // Look for the function of interest ("main.singleInstruction") + // and verify that the line table has an entry not just for the + // single instruction but also a dummy instruction following it, + // so as to test that whoever is emitting the DWARF doesn't + // emit an end-sequence op immediately after the last instruction + // in the translation unit. + // + // NB: another way to write this test would have been to run the + // resulting executable under GDB, set a breakpoint in + // "main.singleInstruction", then verify that GDB displays the + // correct line/file information. Given the headache and flakiness + // associated with GDB-based tests these days, a direct read of + // the line table seems more desirable. + rows := []dwarf.LineEntry{} + dw, err := f.DWARF() + if err != nil { + t.Fatalf("error parsing DWARF: %v", err) + } + rdr := dw.Reader() + for { + e, err := rdr.Next() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagCompileUnit { + continue + } + // NB: there can be multiple compile units named "main". + name := e.Val(dwarf.AttrName).(string) + if name != "main" { + continue + } + lnrdr, err := dw.LineReader(e) + if err != nil { + t.Fatalf("error creating DWARF line reader: %v", err) + } + if lnrdr != nil { + var lne dwarf.LineEntry + for { + err := lnrdr.Next(&lne) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("error reading next DWARF line: %v", err) + } + if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") { + continue + } + rows = append(rows, lne) + } + } + rdr.SkipChildren() + } + f.Close() + + // Make sure that: + // - main.singleInstruction appears in the line table + // - more than one PC value appears the line table for + // that compilation unit. + // - at least one row has the correct line number (8) + pcs := make(map[uint64]bool) + line8seen := false + for _, r := range rows { + pcs[r.Address] = true + if r.Line == 8 { + line8seen = true + } + } + failed := false + if len(pcs) < 2 { + failed = true + t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs)) + } + if !line8seen { + failed = true + t.Errorf("line table does not contain correct line for main.singleInstruction") + } + if !failed { + return + } + for i, r := range rows { + t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line) + } +} + +func TestIssue39757(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + // In this bug the DWARF line table contents for the last couple of + // instructions in a function were incorrect (bad file/line). This + // test verifies that all of the line table rows for a function + // of interest have the same file (no "autogenerated"). + // + // Note: the function in this test was written with an eye towards + // ensuring that there are no inlined routines from other packages + // (which could introduce other source files into the DWARF); it's + // possible that at some point things could evolve in the + // compiler/runtime in ways that aren't happening now, so this + // might be something to check for if it does start failing. + + tmpdir := t.TempDir() + + wd, err := os.Getwd() + if err != nil { + t.Fatalf("where am I? %v", err) + } + pdir := filepath.Join(wd, "testdata", "issue39757") + f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt) + defer f.Close() + + syms, err := f.Symbols() + if err != nil { + t.Fatal(err) + } + + var addr uint64 + for _, sym := range syms { + if sym.Name == "main.main" { + addr = sym.Addr + break + } + } + if addr == 0 { + t.Fatal("cannot find main.main in symbols") + } + + // Open the resulting binary and examine the DWARF it contains. + // Look for the function of interest ("main.main") + // and verify that all line table entries show the same source + // file. + dw, err := f.DWARF() + if err != nil { + t.Fatalf("error parsing DWARF: %v", err) + } + rdr := dw.Reader() + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Locate the main.main DIE + mains := ex.Named("main.main") + if len(mains) == 0 { + t.Fatalf("unable to locate DIE for main.main") + } + if len(mains) != 1 { + t.Fatalf("more than one main.main DIE") + } + maindie := mains[0] + + // Collect the start/end PC for main.main + lowpc := maindie.Val(dwarf.AttrLowpc).(uint64) + highpc := maindie.Val(dwarf.AttrHighpc).(uint64) + + // Now read the line table for the 'main' compilation unit. + mainIdx := ex.IdxFromOffset(maindie.Offset) + cuentry := ex.Parent(mainIdx) + if cuentry == nil { + t.Fatalf("main.main DIE appears orphaned") + } + lnrdr, lerr := dw.LineReader(cuentry) + if lerr != nil { + t.Fatalf("error creating DWARF line reader: %v", err) + } + if lnrdr == nil { + t.Fatalf("no line table for main.main compilation unit") + } + rows := []dwarf.LineEntry{} + mainrows := 0 + var lne dwarf.LineEntry + for { + err := lnrdr.Next(&lne) + if err == io.EOF { + break + } + rows = append(rows, lne) + if err != nil { + t.Fatalf("error reading next DWARF line: %v", err) + } + if lne.Address < lowpc || lne.Address > highpc { + continue + } + if !strings.HasSuffix(lne.File.Name, "issue39757main.go") { + t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name) + } + mainrows++ + } + f.Close() + + // Make sure we saw a few rows. + if mainrows < 3 { + t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows) + for i, r := range rows { + t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line) + } + } +} + +func TestIssue42484(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + tmpdir, err := os.MkdirTemp("", "TestIssue42484") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(tmpdir) + wd, err := os.Getwd() + if err != nil { + t.Fatalf("where am I? %v", err) + } + pdir := filepath.Join(wd, "testdata", "issue42484") + f := gobuildTestdata(t, tmpdir, pdir, NoOpt) + + var lastAddr uint64 + var lastFile string + var lastLine int + + dw, err := f.DWARF() + if err != nil { + t.Fatalf("error parsing DWARF: %v", err) + } + rdr := dw.Reader() + for { + e, err := rdr.Next() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagCompileUnit { + continue + } + lnrdr, err := dw.LineReader(e) + if err != nil { + t.Fatalf("error creating DWARF line reader: %v", err) + } + if lnrdr != nil { + var lne dwarf.LineEntry + for { + err := lnrdr.Next(&lne) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("error reading next DWARF line: %v", err) + } + if lne.EndSequence { + continue + } + if lne.Address == lastAddr && (lne.File.Name != lastFile || lne.Line != lastLine) { + t.Errorf("address %#x is assigned to both %s:%d and %s:%d", lastAddr, lastFile, lastLine, lne.File.Name, lne.Line) + } + lastAddr = lne.Address + lastFile = lne.File.Name + lastLine = lne.Line + } + } + rdr.SkipChildren() + } + f.Close() +} + +// processParams examines the formal parameter children of subprogram +// DIE "die" using the explorer "ex" and returns a string that +// captures the name, order, and classification of the subprogram's +// input and output parameters. For example, for the go function +// +// func foo(i1 int, f1 float64) (string, bool) { +// +// this function would return a string something like +// +// i1:0:1 f1:1:1 ~r0:2:2 ~r1:3:2 +// +// where each chunk above is of the form NAME:ORDER:INOUTCLASSIFICATION +func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string { + // Values in the returned map are of the form <order>:<varparam> + // where order is the order within the child DIE list of the + // param, and <varparam> is an integer: + // + // -1: varparm attr not found + // 1: varparm found with value false + // 2: varparm found with value true + // + foundParams := make(map[string]string) + + // Walk the subprogram DIE's children looking for params. + pIdx := ex.IdxFromOffset(die.Offset) + childDies := ex.Children(pIdx) + idx := 0 + for _, child := range childDies { + if child.Tag == dwarf.TagFormalParameter { + // NB: a setting of DW_AT_variable_parameter indicates + // that the param in question is an output parameter; we + // want to see this attribute set to TRUE for all Go + // return params. It would be OK to have it missing for + // input parameters, but for the moment we verify that the + // attr is present but set to false. + st := -1 + if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok { + if vp { + st = 2 + } else { + st = 1 + } + } + if name, ok := child.Val(dwarf.AttrName).(string); ok { + foundParams[name] = fmt.Sprintf("%d:%d", idx, st) + idx++ + } + } + } + + found := make([]string, 0, len(foundParams)) + for k, v := range foundParams { + found = append(found, fmt.Sprintf("%s:%s", k, v)) + } + sort.Strings(found) + + return fmt.Sprintf("%+v", found) +} + +func TestOutputParamAbbrevAndAttr(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + t.Parallel() + + // This test verifies that the compiler is selecting the correct + // DWARF abbreviation for output parameters, and that the + // variable parameter attribute is correct for in-params and + // out-params. + + const prog = ` +package main + +//go:noinline +func ABC(c1, c2, c3 int, d1, d2, d3, d4 string, f1, f2, f3 float32, g1 [1024]int) (r1 int, r2 int, r3 [1024]int, r4 byte, r5 string, r6 float32) { + g1[0] = 6 + r1, r2, r3, r4, r5, r6 = c3, c2+c1, g1, 'a', d1+d2+d3+d4, f1+f2+f3 + return +} + +func main() { + a := [1024]int{} + v1, v2, v3, v4, v5, v6 := ABC(1, 2, 3, "a", "b", "c", "d", 1.0, 2.0, 1.0, a) + println(v1, v2, v3[0], v4, v5, v6) +} +` + dir := t.TempDir() + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Locate the main.ABC DIE + abcs := ex.Named("main.ABC") + if len(abcs) == 0 { + t.Fatalf("unable to locate DIE for main.ABC") + } + if len(abcs) != 1 { + t.Fatalf("more than one main.ABC DIE") + } + abcdie := abcs[0] + + // Vet the DIE + if abcdie.Tag != dwarf.TagSubprogram { + t.Fatalf("unexpected tag %v on main.ABC DIE", abcdie.Tag) + } + + // Call a helper to collect param info. + found := processParams(abcdie, &ex) + + // Make sure we see all of the expected params in the proper + // order, that they have the varparam attr, and the varparam is + // set for the returns. + expected := "[c1:0:1 c2:1:1 c3:2:1 d1:3:1 d2:4:1 d3:5:1 d4:6:1 f1:7:1 f2:8:1 f3:9:1 g1:10:1 r1:11:2 r2:12:2 r3:13:2 r4:14:2 r5:15:2 r6:16:2]" + if found != expected { + t.Errorf("param check failed, wanted:\n%s\ngot:\n%s\n", + expected, found) + } +} + +func TestDictIndex(t *testing.T) { + // Check that variables with a parametric type have a dictionary index + // attribute and that types that are only referenced through dictionaries + // have DIEs. + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + t.Parallel() + + const prog = ` +package main + +import "fmt" + +type CustomInt int + +func testfn[T any](arg T) { + var mapvar = make(map[int]T) + mapvar[0] = arg + fmt.Println(arg, mapvar) +} + +func main() { + testfn(CustomInt(3)) +} +` + + dir := t.TempDir() + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + found := false + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + name, _ := entry.Val(dwarf.AttrName).(string) + if strings.HasPrefix(name, "main.testfn") { + found = true + break + } + } + + if !found { + t.Fatalf("could not find main.testfn") + } + + offs := []dwarf.Offset{} + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if entry.Tag == 0 { + break + } + name, _ := entry.Val(dwarf.AttrName).(string) + switch name { + case "arg", "mapvar": + offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset)) + } + } + if len(offs) != 2 { + t.Errorf("wrong number of variables found in main.testfn %d", len(offs)) + } + for _, off := range offs { + rdr.Seek(off) + entry, err := rdr.Next() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok { + t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index)) + } + } + + rdr.Seek(0) + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} { + dies := ex.Named(typeName) + if len(dies) != 1 { + t.Errorf("wanted 1 DIE named %s, found %v", typeName, len(dies)) + } + if dies[0].Val(intdwarf.DW_AT_go_runtime_type).(uint64) == 0 { + t.Errorf("type %s does not have DW_AT_go_runtime_type", typeName) + } + } +} + +func TestOptimizedOutParamHandling(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + t.Parallel() + + // This test is intended to verify that the compiler emits DWARF + // DIE entries for all input and output parameters, and that: + // + // - attributes are set correctly for output params, + // - things appear in the proper order + // - things work properly for both register-resident + // params and params passed on the stack + // - things work for both referenced and unreferenced params + // - things work for named return values un-named return vals + // + // The scenarios below don't cover all possible permutations and + // combinations, but they hit a bunch of the high points. + + const prog = ` +package main + +// First testcase. All input params in registers, all params used. + +//go:noinline +func tc1(p1, p2 int, p3 string) (int, string) { + return p1 + p2, p3 + "foo" +} + +// Second testcase. Some params in registers, some on stack. + +//go:noinline +func tc2(p1 int, p2 [128]int, p3 string) (int, string, [128]int) { + return p1 + p2[p1], p3 + "foo", [128]int{p1} +} + +// Third testcase. Named return params. + +//go:noinline +func tc3(p1 int, p2 [128]int, p3 string) (r1 int, r2 bool, r3 string, r4 [128]int) { + if p1 == 101 { + r1 = p1 + p2[p1] + r2 = p3 == "foo" + r4 = [128]int{p1} + return + } else { + return p1 - p2[p1+3], false, "bar", [128]int{p1 + 2} + } +} + +// Fourth testcase. Some thing are used, some are unused. + +//go:noinline +func tc4(p1, p1un int, p2, p2un [128]int, p3, p3un string) (r1 int, r1un int, r2 bool, r3 string, r4, r4un [128]int) { + if p1 == 101 { + r1 = p1 + p2[p2[0]] + r2 = p3 == "foo" + r4 = [128]int{p1} + return + } else { + return p1, -1, true, "plex", [128]int{p1 + 2}, [128]int{-1} + } +} + +func main() { + { + r1, r2 := tc1(3, 4, "five") + println(r1, r2) + } + { + x := [128]int{9} + r1, r2, r3 := tc2(3, x, "five") + println(r1, r2, r3[0]) + } + { + x := [128]int{9} + r1, r2, r3, r4 := tc3(3, x, "five") + println(r1, r2, r3, r4[0]) + } + { + x := [128]int{3} + y := [128]int{7} + r1, r1u, r2, r3, r4, r4u := tc4(0, 1, x, y, "a", "b") + println(r1, r1u, r2, r3, r4[0], r4u[1]) + } + +} +` + dir := t.TempDir() + f := gobuild(t, dir, prog, DefaultOpt) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + testcases := []struct { + tag string + expected string + }{ + { + tag: "tc1", + expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2]", + }, + { + tag: "tc2", + expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2 ~r2:5:2]", + }, + { + tag: "tc3", + expected: "[p1:0:1 p2:1:1 p3:2:1 r1:3:2 r2:4:2 r3:5:2 r4:6:2]", + }, + { + tag: "tc4", + expected: "[p1:0:1 p1un:1:1 p2:2:1 p2un:3:1 p3:4:1 p3un:5:1 r1:6:2 r1un:7:2 r2:8:2 r3:9:2 r4:10:2 r4un:11:2]", + }, + } + + for _, tc := range testcases { + // Locate the proper DIE + which := fmt.Sprintf("main.%s", tc.tag) + tcs := ex.Named(which) + if len(tcs) == 0 { + t.Fatalf("unable to locate DIE for " + which) + } + if len(tcs) != 1 { + t.Fatalf("more than one " + which + " DIE") + } + die := tcs[0] + + // Vet the DIE + if die.Tag != dwarf.TagSubprogram { + t.Fatalf("unexpected tag %v on "+which+" DIE", die.Tag) + } + + // Examine params for this subprogram. + foundParams := processParams(die, &ex) + if foundParams != tc.expected { + t.Errorf("check failed for testcase %s -- wanted:\n%s\ngot:%s\n", + tc.tag, tc.expected, foundParams) + } + } +} +func TestIssue54320(t *testing.T) { + // Check that when trampolines are used, the DWARF LPT is correctly + // emitted in the final binary + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + const prog = ` +package main + +import "fmt" + +func main() { + fmt.Printf("Hello world\n"); +} +` + + dir := t.TempDir() + f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2") + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + found := false + var entry *dwarf.Entry + for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if entry.Tag != dwarf.TagCompileUnit { + continue + } + name, _ := entry.Val(dwarf.AttrName).(string) + if name == "main" { + found = true + break + } + rdr.SkipChildren() + } + + if !found { + t.Fatalf("could not find main compile unit") + } + lr, err := d.LineReader(entry) + if err != nil { + t.Fatalf("error obtaining linereader: %v", err) + } + + var le dwarf.LineEntry + found = false + for { + if err := lr.Next(&le); err != nil { + if err == io.EOF { + break + } + t.Fatalf("error reading linentry: %v", err) + } + // check LE contains an entry to test.go + if le.File == nil { + continue + } + file := filepath.Base(le.File.Name) + if file == "test.go" { + found = true + break + } + } + if !found { + t.Errorf("no LPT entries for test.go") + } +} + +func TestZeroSizedVariable(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + t.Parallel() + + // This test verifies that the compiler emits DIEs for zero sized variables + // (for example variables of type 'struct {}'). + // See go.dev/issues/54615. + + const prog = ` +package main + +import ( + "fmt" +) + +func main() { + zeroSizedVariable := struct{}{} + fmt.Println(zeroSizedVariable) +} +` + + for _, opt := range []string{NoOpt, DefaultOpt} { + dir := t.TempDir() + f := gobuild(t, dir, prog, opt) + defer f.Close() + defer os.RemoveAll(dir) + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + ex := dwtest.Examiner{} + if err := ex.Populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Locate the main.zeroSizedVariable DIE + abcs := ex.Named("zeroSizedVariable") + if len(abcs) == 0 { + t.Fatalf("unable to locate DIE for zeroSizedVariable") + } + if len(abcs) != 1 { + t.Fatalf("more than one zeroSizedVariable DIE") + } + } +} diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go new file mode 100644 index 0000000..a1ae7ea --- /dev/null +++ b/src/cmd/link/internal/ld/elf.go @@ -0,0 +1,2395 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/notsha256" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "encoding/binary" + "encoding/hex" + "fmt" + "internal/buildcfg" + "os" + "path/filepath" + "runtime" + "sort" + "strings" +) + +/* + * Derived from: + * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ + * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ + * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ + * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ + * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ + * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * + * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. + * Copyright (c) 2001 David E. O'Brien + * Portions Copyright 2009 The Go Authors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * ELF definitions that are independent of architecture or word size. + */ + +/* + * Note header. The ".note" section contains an array of notes. Each + * begins with this header, aligned to a word boundary. Immediately + * following the note header is n_namesz bytes of name, padded to the + * next word boundary. Then comes n_descsz bytes of descriptor, again + * padded to a word boundary. The values of n_namesz and n_descsz do + * not include the padding. + */ +type elfNote struct { + nNamesz uint32 + nDescsz uint32 + nType uint32 +} + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Relocation types. + */ +const ( + ARM_MAGIC_TRAMP_NUMBER = 0x5c000003 +) + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * ELF header. + */ +type ElfEhdr elf.Header64 + +/* + * Section header. + */ +type ElfShdr struct { + elf.Section64 + shnum elf.SectionIndex +} + +/* + * Program header. + */ +type ElfPhdr elf.ProgHeader + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * Go linker interface + */ +const ( + ELF64HDRSIZE = 64 + ELF64PHDRSIZE = 56 + ELF64SHDRSIZE = 64 + ELF64RELSIZE = 16 + ELF64RELASIZE = 24 + ELF64SYMSIZE = 24 + ELF32HDRSIZE = 52 + ELF32PHDRSIZE = 32 + ELF32SHDRSIZE = 40 + ELF32SYMSIZE = 16 + ELF32RELSIZE = 8 +) + +/* + * The interface uses the 64-bit structures always, + * to avoid code duplication. The writers know how to + * marshal a 32-bit representation from the 64-bit structure. + */ + +var Elfstrdat []byte + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, SHeaders, and interp. + * May waste some. + * On FreeBSD, cannot be larger than a page. + */ +const ( + ELFRESERVE = 4096 +) + +/* + * We use the 64-bit data structures on both 32- and 64-bit machines + * in order to write the code just once. The 64-bit data structure is + * written in the 32-bit format on the 32-bit machines. + */ +const ( + NSECT = 400 +) + +var ( + Nelfsym = 1 + + elf64 bool + // Either ".rel" or ".rela" depending on which type of relocation the + // target platform uses. + elfRelType string + + ehdr ElfEhdr + phdr [NSECT]*ElfPhdr + shdr [NSECT]*ElfShdr + + interp string +) + +type Elfstring struct { + s string + off int +} + +var elfstr [100]Elfstring + +var nelfstr int + +var buildinfo []byte + +/* +Initialize the global variable that describes the ELF header. It will be updated as +we write section and prog headers. +*/ +func Elfinit(ctxt *Link) { + ctxt.IsELF = true + + if ctxt.Arch.InFamily(sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) { + elfRelType = ".rela" + } else { + elfRelType = ".rel" + } + + switch ctxt.Arch.Family { + // 64-bit architectures + case sys.PPC64, sys.S390X: + if ctxt.Arch.ByteOrder == binary.BigEndian { + ehdr.Flags = 1 /* Version 1 ABI */ + } else { + ehdr.Flags = 2 /* Version 2 ABI */ + } + fallthrough + case sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.RISCV64: + if ctxt.Arch.Family == sys.MIPS64 { + ehdr.Flags = 0x20000004 /* MIPS 3 CPIC */ + } + if ctxt.Arch.Family == sys.Loong64 { + ehdr.Flags = 0x3 /* LoongArch lp64d */ + } + if ctxt.Arch.Family == sys.RISCV64 { + ehdr.Flags = 0x4 /* RISCV Float ABI Double */ + } + elf64 = true + + ehdr.Phoff = ELF64HDRSIZE /* Must be ELF64HDRSIZE: first PHdr must follow ELF header */ + ehdr.Shoff = ELF64HDRSIZE /* Will move as we add PHeaders */ + ehdr.Ehsize = ELF64HDRSIZE /* Must be ELF64HDRSIZE */ + ehdr.Phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */ + ehdr.Shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */ + + // 32-bit architectures + case sys.ARM, sys.MIPS: + if ctxt.Arch.Family == sys.ARM { + // we use EABI on linux/arm, freebsd/arm, netbsd/arm. + if ctxt.HeadType == objabi.Hlinux || ctxt.HeadType == objabi.Hfreebsd || ctxt.HeadType == objabi.Hnetbsd { + // We set a value here that makes no indication of which + // float ABI the object uses, because this is information + // used by the dynamic linker to compare executables and + // shared libraries -- so it only matters for cgo calls, and + // the information properly comes from the object files + // produced by the host C compiler. parseArmAttributes in + // ldelf.go reads that information and updates this field as + // appropriate. + ehdr.Flags = 0x5000002 // has entry point, Version5 EABI + } + } else if ctxt.Arch.Family == sys.MIPS { + ehdr.Flags = 0x50001004 /* MIPS 32 CPIC O32*/ + } + fallthrough + default: + ehdr.Phoff = ELF32HDRSIZE + /* Must be ELF32HDRSIZE: first PHdr must follow ELF header */ + ehdr.Shoff = ELF32HDRSIZE /* Will move as we add PHeaders */ + ehdr.Ehsize = ELF32HDRSIZE /* Must be ELF32HDRSIZE */ + ehdr.Phentsize = ELF32PHDRSIZE /* Must be ELF32PHDRSIZE */ + ehdr.Shentsize = ELF32SHDRSIZE /* Must be ELF32SHDRSIZE */ + } +} + +// Make sure PT_LOAD is aligned properly and +// that there is no gap, +// correct ELF loaders will do this implicitly, +// but buggy ELF loaders like the one in some +// versions of QEMU and UPX won't. +func fixElfPhdr(e *ElfPhdr) { + frag := int(e.Vaddr & (e.Align - 1)) + + e.Off -= uint64(frag) + e.Vaddr -= uint64(frag) + e.Paddr -= uint64(frag) + e.Filesz += uint64(frag) + e.Memsz += uint64(frag) +} + +func elf64phdr(out *OutBuf, e *ElfPhdr) { + if e.Type == elf.PT_LOAD { + fixElfPhdr(e) + } + + out.Write32(uint32(e.Type)) + out.Write32(uint32(e.Flags)) + out.Write64(e.Off) + out.Write64(e.Vaddr) + out.Write64(e.Paddr) + out.Write64(e.Filesz) + out.Write64(e.Memsz) + out.Write64(e.Align) +} + +func elf32phdr(out *OutBuf, e *ElfPhdr) { + if e.Type == elf.PT_LOAD { + fixElfPhdr(e) + } + + out.Write32(uint32(e.Type)) + out.Write32(uint32(e.Off)) + out.Write32(uint32(e.Vaddr)) + out.Write32(uint32(e.Paddr)) + out.Write32(uint32(e.Filesz)) + out.Write32(uint32(e.Memsz)) + out.Write32(uint32(e.Flags)) + out.Write32(uint32(e.Align)) +} + +func elf64shdr(out *OutBuf, e *ElfShdr) { + out.Write32(e.Name) + out.Write32(uint32(e.Type)) + out.Write64(uint64(e.Flags)) + out.Write64(e.Addr) + out.Write64(e.Off) + out.Write64(e.Size) + out.Write32(e.Link) + out.Write32(e.Info) + out.Write64(e.Addralign) + out.Write64(e.Entsize) +} + +func elf32shdr(out *OutBuf, e *ElfShdr) { + out.Write32(e.Name) + out.Write32(uint32(e.Type)) + out.Write32(uint32(e.Flags)) + out.Write32(uint32(e.Addr)) + out.Write32(uint32(e.Off)) + out.Write32(uint32(e.Size)) + out.Write32(e.Link) + out.Write32(e.Info) + out.Write32(uint32(e.Addralign)) + out.Write32(uint32(e.Entsize)) +} + +func elfwriteshdrs(out *OutBuf) uint32 { + if elf64 { + for i := 0; i < int(ehdr.Shnum); i++ { + elf64shdr(out, shdr[i]) + } + return uint32(ehdr.Shnum) * ELF64SHDRSIZE + } + + for i := 0; i < int(ehdr.Shnum); i++ { + elf32shdr(out, shdr[i]) + } + return uint32(ehdr.Shnum) * ELF32SHDRSIZE +} + +func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) { + if nelfstr >= len(elfstr) { + ctxt.Errorf(s, "too many elf strings") + errorexit() + } + + elfstr[nelfstr].s = str + elfstr[nelfstr].off = off + nelfstr++ +} + +func elfwritephdrs(out *OutBuf) uint32 { + if elf64 { + for i := 0; i < int(ehdr.Phnum); i++ { + elf64phdr(out, phdr[i]) + } + return uint32(ehdr.Phnum) * ELF64PHDRSIZE + } + + for i := 0; i < int(ehdr.Phnum); i++ { + elf32phdr(out, phdr[i]) + } + return uint32(ehdr.Phnum) * ELF32PHDRSIZE +} + +func newElfPhdr() *ElfPhdr { + e := new(ElfPhdr) + if ehdr.Phnum >= NSECT { + Errorf(nil, "too many phdrs") + } else { + phdr[ehdr.Phnum] = e + ehdr.Phnum++ + } + if elf64 { + ehdr.Shoff += ELF64PHDRSIZE + } else { + ehdr.Shoff += ELF32PHDRSIZE + } + return e +} + +func newElfShdr(name int64) *ElfShdr { + e := new(ElfShdr) + e.Name = uint32(name) + e.shnum = elf.SectionIndex(ehdr.Shnum) + if ehdr.Shnum >= NSECT { + Errorf(nil, "too many shdrs") + } else { + shdr[ehdr.Shnum] = e + ehdr.Shnum++ + } + + return e +} + +func getElfEhdr() *ElfEhdr { + return &ehdr +} + +func elf64writehdr(out *OutBuf) uint32 { + out.Write(ehdr.Ident[:]) + out.Write16(uint16(ehdr.Type)) + out.Write16(uint16(ehdr.Machine)) + out.Write32(uint32(ehdr.Version)) + out.Write64(ehdr.Entry) + out.Write64(ehdr.Phoff) + out.Write64(ehdr.Shoff) + out.Write32(ehdr.Flags) + out.Write16(ehdr.Ehsize) + out.Write16(ehdr.Phentsize) + out.Write16(ehdr.Phnum) + out.Write16(ehdr.Shentsize) + out.Write16(ehdr.Shnum) + out.Write16(ehdr.Shstrndx) + return ELF64HDRSIZE +} + +func elf32writehdr(out *OutBuf) uint32 { + out.Write(ehdr.Ident[:]) + out.Write16(uint16(ehdr.Type)) + out.Write16(uint16(ehdr.Machine)) + out.Write32(uint32(ehdr.Version)) + out.Write32(uint32(ehdr.Entry)) + out.Write32(uint32(ehdr.Phoff)) + out.Write32(uint32(ehdr.Shoff)) + out.Write32(ehdr.Flags) + out.Write16(ehdr.Ehsize) + out.Write16(ehdr.Phentsize) + out.Write16(ehdr.Phnum) + out.Write16(ehdr.Shentsize) + out.Write16(ehdr.Shnum) + out.Write16(ehdr.Shstrndx) + return ELF32HDRSIZE +} + +func elfwritehdr(out *OutBuf) uint32 { + if elf64 { + return elf64writehdr(out) + } + return elf32writehdr(out) +} + +/* Taken directly from the definition document for ELF64. */ +func elfhash(name string) uint32 { + var h uint32 + for i := 0; i < len(name); i++ { + h = (h << 4) + uint32(name[i]) + if g := h & 0xf0000000; g != 0 { + h ^= g >> 24 + } + h &= 0x0fffffff + } + return h +} + +func elfWriteDynEntSym(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) { + Elfwritedynentsymplus(ctxt, s, tag, t, 0) +} + +func Elfwritedynent(arch *sys.Arch, s *loader.SymbolBuilder, tag elf.DynTag, val uint64) { + if elf64 { + s.AddUint64(arch, uint64(tag)) + s.AddUint64(arch, val) + } else { + s.AddUint32(arch, uint32(tag)) + s.AddUint32(arch, uint32(val)) + } +} + +func Elfwritedynentsymplus(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym, add int64) { + if elf64 { + s.AddUint64(ctxt.Arch, uint64(tag)) + } else { + s.AddUint32(ctxt.Arch, uint32(tag)) + } + s.AddAddrPlus(ctxt.Arch, t, add) +} + +func elfwritedynentsymsize(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) { + if elf64 { + s.AddUint64(ctxt.Arch, uint64(tag)) + } else { + s.AddUint32(ctxt.Arch, uint32(tag)) + } + s.AddSize(ctxt.Arch, t) +} + +func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int { + interp = p + n := len(interp) + 1 + sh.Addr = startva + resoff - uint64(n) + sh.Off = resoff - uint64(n) + sh.Size = uint64(n) + + return n +} + +func elfwriteinterp(out *OutBuf) int { + sh := elfshname(".interp") + out.SeekSet(int64(sh.Off)) + out.WriteString(interp) + out.Write8(0) + return int(sh.Size) +} + +// member of .gnu.attributes of MIPS for fpAbi +const ( + // No floating point is present in the module (default) + MIPS_FPABI_NONE = 0 + // FP code in the module uses the FP32 ABI for a 32-bit ABI + MIPS_FPABI_ANY = 1 + // FP code in the module only uses single precision ABI + MIPS_FPABI_SINGLE = 2 + // FP code in the module uses soft-float ABI + MIPS_FPABI_SOFT = 3 + // FP code in the module assumes an FPU with FR=1 and has 12 + // callee-saved doubles. Historic, no longer supported. + MIPS_FPABI_HIST = 4 + // FP code in the module uses the FPXX ABI + MIPS_FPABI_FPXX = 5 + // FP code in the module uses the FP64 ABI + MIPS_FPABI_FP64 = 6 + // FP code in the module uses the FP64A ABI + MIPS_FPABI_FP64A = 7 +) + +func elfMipsAbiFlags(sh *ElfShdr, startva uint64, resoff uint64) int { + n := 24 + sh.Addr = startva + resoff - uint64(n) + sh.Off = resoff - uint64(n) + sh.Size = uint64(n) + sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS) + sh.Flags = uint64(elf.SHF_ALLOC) + + return n +} + +// Layout is given by this C definition: +// +// typedef struct +// { +// /* Version of flags structure. */ +// uint16_t version; +// /* The level of the ISA: 1-5, 32, 64. */ +// uint8_t isa_level; +// /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ +// uint8_t isa_rev; +// /* The size of general purpose registers. */ +// uint8_t gpr_size; +// /* The size of co-processor 1 registers. */ +// uint8_t cpr1_size; +// /* The size of co-processor 2 registers. */ +// uint8_t cpr2_size; +// /* The floating-point ABI. */ +// uint8_t fp_abi; +// /* Processor-specific extension. */ +// uint32_t isa_ext; +// /* Mask of ASEs used. */ +// uint32_t ases; +// /* Mask of general flags. */ +// uint32_t flags1; +// uint32_t flags2; +// } Elf_Internal_ABIFlags_v0; +func elfWriteMipsAbiFlags(ctxt *Link) int { + sh := elfshname(".MIPS.abiflags") + ctxt.Out.SeekSet(int64(sh.Off)) + ctxt.Out.Write16(0) // version + ctxt.Out.Write8(32) // isaLevel + ctxt.Out.Write8(1) // isaRev + ctxt.Out.Write8(1) // gprSize + ctxt.Out.Write8(1) // cpr1Size + ctxt.Out.Write8(0) // cpr2Size + if buildcfg.GOMIPS == "softfloat" { + ctxt.Out.Write8(MIPS_FPABI_SOFT) // fpAbi + } else { + // Go cannot make sure non odd-number-fpr is used (ie, in load a double from memory). + // So, we mark the object is MIPS I style paired float/double register scheme, + // aka MIPS_FPABI_ANY. If we mark the object as FPXX, the kernel may use FR=1 mode, + // then we meet some problem. + // Note: MIPS_FPABI_ANY is bad naming: in fact it is MIPS I style FPR usage. + // It is not for 'ANY'. + // TODO: switch to FPXX after be sure that no odd-number-fpr is used. + ctxt.Out.Write8(MIPS_FPABI_ANY) // fpAbi + } + ctxt.Out.Write32(0) // isaExt + ctxt.Out.Write32(0) // ases + ctxt.Out.Write32(0) // flags1 + ctxt.Out.Write32(0) // flags2 + return int(sh.Size) +} + +func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sizes ...int) int { + n := resoff % 4 + // if section contains multiple notes (as is the case with FreeBSD signature), + // multiple note sizes can be specified + for _, sz := range sizes { + n += 3*4 + uint64(sz) + } + + sh.Type = uint32(elf.SHT_NOTE) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = 4 + sh.Addr = startva + resoff - n + sh.Off = resoff - n + sh.Size = n - resoff%4 + + return int(n) +} + +func elfwritenotehdr(out *OutBuf, str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr { + sh := elfshname(str) + + // Write Elf_Note header. + out.SeekSet(int64(sh.Off)) + + out.Write32(namesz) + out.Write32(descsz) + out.Write32(tag) + + return sh +} + +// NetBSD Signature (as per sys/exec_elf.h) +const ( + ELF_NOTE_NETBSD_NAMESZ = 7 + ELF_NOTE_NETBSD_DESCSZ = 4 + ELF_NOTE_NETBSD_TAG = 1 + ELF_NOTE_NETBSD_VERSION = 700000000 /* NetBSD 7.0 */ +) + +var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00") + +func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritenetbsdsig(out *OutBuf) int { + // Write Elf_Note header. + sh := elfwritenotehdr(out, ".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by NetBSD string and version. + out.Write(ELF_NOTE_NETBSD_NAME) + out.Write8(0) + out.Write32(ELF_NOTE_NETBSD_VERSION) + + return int(sh.Size) +} + +// The race detector can't handle ASLR (address space layout randomization). +// ASLR is on by default for NetBSD, so we turn the ASLR off explicitly +// using a magic elf Note when building race binaries. + +func elfnetbsdpax(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(Rnd(4, 4) + Rnd(4, 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritenetbsdpax(out *OutBuf) int { + sh := elfwritenotehdr(out, ".note.netbsd.pax", 4 /* length of PaX\x00 */, 4 /* length of flags */, 0x03 /* PaX type */) + if sh == nil { + return 0 + } + out.Write([]byte("PaX\x00")) + out.Write32(0x20) // 0x20 = Force disable ASLR + return int(sh.Size) +} + +// OpenBSD Signature +const ( + ELF_NOTE_OPENBSD_NAMESZ = 8 + ELF_NOTE_OPENBSD_DESCSZ = 4 + ELF_NOTE_OPENBSD_TAG = 1 + ELF_NOTE_OPENBSD_VERSION = 0 +) + +var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00") + +func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ + return elfnote(sh, startva, resoff, n) +} + +func elfwriteopenbsdsig(out *OutBuf) int { + // Write Elf_Note header. + sh := elfwritenotehdr(out, ".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by OpenBSD string and version. + out.Write(ELF_NOTE_OPENBSD_NAME) + + out.Write32(ELF_NOTE_OPENBSD_VERSION) + + return int(sh.Size) +} + +// FreeBSD Signature (as per sys/elf_common.h) +const ( + ELF_NOTE_FREEBSD_NAMESZ = 8 + ELF_NOTE_FREEBSD_DESCSZ = 4 + ELF_NOTE_FREEBSD_ABI_TAG = 1 + ELF_NOTE_FREEBSD_NOINIT_TAG = 2 + ELF_NOTE_FREEBSD_FEATURE_CTL_TAG = 4 + ELF_NOTE_FREEBSD_VERSION = 1203000 // 12.3-RELEASE + ELF_NOTE_FREEBSD_FCTL_ASLR_DISABLE = 0x1 +) + +const ELF_NOTE_FREEBSD_NAME = "FreeBSD\x00" + +func elffreebsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := ELF_NOTE_FREEBSD_NAMESZ + ELF_NOTE_FREEBSD_DESCSZ + // FreeBSD signature section contains 3 equally sized notes + return elfnote(sh, startva, resoff, n, n, n) +} + +// elfwritefreebsdsig writes FreeBSD .note section. +// +// See https://www.netbsd.org/docs/kernel/elf-notes.html for the description of +// a Note element format and +// https://github.com/freebsd/freebsd-src/blob/main/sys/sys/elf_common.h#L790 +// for the FreeBSD-specific values. +func elfwritefreebsdsig(out *OutBuf) int { + sh := elfshname(".note.tag") + if sh == nil { + return 0 + } + out.SeekSet(int64(sh.Off)) + + // NT_FREEBSD_ABI_TAG + out.Write32(ELF_NOTE_FREEBSD_NAMESZ) + out.Write32(ELF_NOTE_FREEBSD_DESCSZ) + out.Write32(ELF_NOTE_FREEBSD_ABI_TAG) + out.WriteString(ELF_NOTE_FREEBSD_NAME) + out.Write32(ELF_NOTE_FREEBSD_VERSION) + + // NT_FREEBSD_NOINIT_TAG + out.Write32(ELF_NOTE_FREEBSD_NAMESZ) + out.Write32(ELF_NOTE_FREEBSD_DESCSZ) + out.Write32(ELF_NOTE_FREEBSD_NOINIT_TAG) + out.WriteString(ELF_NOTE_FREEBSD_NAME) + out.Write32(0) + + // NT_FREEBSD_FEATURE_CTL + out.Write32(ELF_NOTE_FREEBSD_NAMESZ) + out.Write32(ELF_NOTE_FREEBSD_DESCSZ) + out.Write32(ELF_NOTE_FREEBSD_FEATURE_CTL_TAG) + out.WriteString(ELF_NOTE_FREEBSD_NAME) + if *flagRace { + // The race detector can't handle ASLR, turn the ASLR off when compiling with -race. + out.Write32(ELF_NOTE_FREEBSD_FCTL_ASLR_DISABLE) + } else { + out.Write32(0) + } + + return int(sh.Size) +} + +func addbuildinfo(val string) { + if !strings.HasPrefix(val, "0x") { + Exitf("-B argument must start with 0x: %s", val) + } + + ov := val + val = val[2:] + + const maxLen = 32 + if hex.DecodedLen(len(val)) > maxLen { + Exitf("-B option too long (max %d digits): %s", maxLen, ov) + } + + b, err := hex.DecodeString(val) + if err != nil { + if err == hex.ErrLength { + Exitf("-B argument must have even number of digits: %s", ov) + } + if inv, ok := err.(hex.InvalidByteError); ok { + Exitf("-B argument contains invalid hex digit %c: %s", byte(inv), ov) + } + Exitf("-B argument contains invalid hex: %s", ov) + } + + buildinfo = b +} + +// Build info note +const ( + ELF_NOTE_BUILDINFO_NAMESZ = 4 + ELF_NOTE_BUILDINFO_TAG = 3 +) + +var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00") + +func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int { + n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(*flagBuildid)), 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritebuildinfo(out *OutBuf) int { + sh := elfwritenotehdr(out, ".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG) + if sh == nil { + return 0 + } + + out.Write(ELF_NOTE_BUILDINFO_NAME) + out.Write(buildinfo) + var zero = make([]byte, 4) + out.Write(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))]) + + return int(sh.Size) +} + +func elfwritegobuildid(out *OutBuf) int { + sh := elfwritenotehdr(out, ".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(*flagBuildid)), ELF_NOTE_GOBUILDID_TAG) + if sh == nil { + return 0 + } + + out.Write(ELF_NOTE_GO_NAME) + out.Write([]byte(*flagBuildid)) + var zero = make([]byte, 4) + out.Write(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))]) + + return int(sh.Size) +} + +// Go specific notes +const ( + ELF_NOTE_GOPKGLIST_TAG = 1 + ELF_NOTE_GOABIHASH_TAG = 2 + ELF_NOTE_GODEPS_TAG = 3 + ELF_NOTE_GOBUILDID_TAG = 4 +) + +var ELF_NOTE_GO_NAME = []byte("Go\x00\x00") + +var elfverneed int + +type Elfaux struct { + next *Elfaux + num int + vers string +} + +type Elflib struct { + next *Elflib + aux *Elfaux + file string +} + +func addelflib(list **Elflib, file string, vers string) *Elfaux { + var lib *Elflib + + for lib = *list; lib != nil; lib = lib.next { + if lib.file == file { + goto havelib + } + } + lib = new(Elflib) + lib.next = *list + lib.file = file + *list = lib + +havelib: + for aux := lib.aux; aux != nil; aux = aux.next { + if aux.vers == vers { + return aux + } + } + aux := new(Elfaux) + aux.next = lib.aux + aux.vers = vers + lib.aux = aux + + return aux +} + +func elfdynhash(ctxt *Link) { + if !ctxt.IsELF { + return + } + + nsym := Nelfsym + ldr := ctxt.loader + s := ldr.CreateSymForUpdate(".hash", 0) + s.SetType(sym.SELFROSECT) + + i := nsym + nbucket := 1 + for i > 0 { + nbucket++ + i >>= 1 + } + + var needlib *Elflib + need := make([]*Elfaux, nsym) + chain := make([]uint32, nsym) + buckets := make([]uint32, nbucket) + + for _, sy := range ldr.DynidSyms() { + + dynid := ldr.SymDynid(sy) + if ldr.SymDynimpvers(sy) != "" { + need[dynid] = addelflib(&needlib, ldr.SymDynimplib(sy), ldr.SymDynimpvers(sy)) + } + + name := ldr.SymExtname(sy) + hc := elfhash(name) + + b := hc % uint32(nbucket) + chain[dynid] = buckets[b] + buckets[b] = uint32(dynid) + } + + // s390x (ELF64) hash table entries are 8 bytes + if ctxt.Arch.Family == sys.S390X { + s.AddUint64(ctxt.Arch, uint64(nbucket)) + s.AddUint64(ctxt.Arch, uint64(nsym)) + for i := 0; i < nbucket; i++ { + s.AddUint64(ctxt.Arch, uint64(buckets[i])) + } + for i := 0; i < nsym; i++ { + s.AddUint64(ctxt.Arch, uint64(chain[i])) + } + } else { + s.AddUint32(ctxt.Arch, uint32(nbucket)) + s.AddUint32(ctxt.Arch, uint32(nsym)) + for i := 0; i < nbucket; i++ { + s.AddUint32(ctxt.Arch, buckets[i]) + } + for i := 0; i < nsym; i++ { + s.AddUint32(ctxt.Arch, chain[i]) + } + } + + dynstr := ldr.CreateSymForUpdate(".dynstr", 0) + + // version symbols + gnuVersionR := ldr.CreateSymForUpdate(".gnu.version_r", 0) + s = gnuVersionR + i = 2 + nfile := 0 + for l := needlib; l != nil; l = l.next { + nfile++ + + // header + s.AddUint16(ctxt.Arch, 1) // table version + j := 0 + for x := l.aux; x != nil; x = x.next { + j++ + } + s.AddUint16(ctxt.Arch, uint16(j)) // aux count + s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(l.file))) // file string offset + s.AddUint32(ctxt.Arch, 16) // offset from header to first aux + if l.next != nil { + s.AddUint32(ctxt.Arch, 16+uint32(j)*16) // offset from this header to next + } else { + s.AddUint32(ctxt.Arch, 0) + } + + for x := l.aux; x != nil; x = x.next { + x.num = i + i++ + + // aux struct + s.AddUint32(ctxt.Arch, elfhash(x.vers)) // hash + s.AddUint16(ctxt.Arch, 0) // flags + s.AddUint16(ctxt.Arch, uint16(x.num)) // other - index we refer to this by + s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(x.vers))) // version string offset + if x.next != nil { + s.AddUint32(ctxt.Arch, 16) // offset from this aux to next + } else { + s.AddUint32(ctxt.Arch, 0) + } + } + } + + // version references + gnuVersion := ldr.CreateSymForUpdate(".gnu.version", 0) + s = gnuVersion + + for i := 0; i < nsym; i++ { + if i == 0 { + s.AddUint16(ctxt.Arch, 0) // first entry - no symbol + } else if need[i] == nil { + s.AddUint16(ctxt.Arch, 1) // global + } else { + s.AddUint16(ctxt.Arch, uint16(need[i].num)) + } + } + + s = ldr.CreateSymForUpdate(".dynamic", 0) + if ctxt.BuildMode == BuildModePIE { + // https://github.com/bminor/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/elf/elf.h#L986 + const DTFLAGS_1_PIE = 0x08000000 + Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(DTFLAGS_1_PIE)) + } + elfverneed = nfile + if elfverneed != 0 { + elfWriteDynEntSym(ctxt, s, elf.DT_VERNEED, gnuVersionR.Sym()) + Elfwritedynent(ctxt.Arch, s, elf.DT_VERNEEDNUM, uint64(nfile)) + elfWriteDynEntSym(ctxt, s, elf.DT_VERSYM, gnuVersion.Sym()) + } + + sy := ldr.CreateSymForUpdate(elfRelType+".plt", 0) + if sy.Size() > 0 { + if elfRelType == ".rela" { + Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_RELA)) + } else { + Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_REL)) + } + elfwritedynentsymsize(ctxt, s, elf.DT_PLTRELSZ, sy.Sym()) + elfWriteDynEntSym(ctxt, s, elf.DT_JMPREL, sy.Sym()) + } + + Elfwritedynent(ctxt.Arch, s, elf.DT_NULL, 0) +} + +func elfphload(seg *sym.Segment) *ElfPhdr { + ph := newElfPhdr() + ph.Type = elf.PT_LOAD + if seg.Rwx&4 != 0 { + ph.Flags |= elf.PF_R + } + if seg.Rwx&2 != 0 { + ph.Flags |= elf.PF_W + } + if seg.Rwx&1 != 0 { + ph.Flags |= elf.PF_X + } + ph.Vaddr = seg.Vaddr + ph.Paddr = seg.Vaddr + ph.Memsz = seg.Length + ph.Off = seg.Fileoff + ph.Filesz = seg.Filelen + ph.Align = uint64(*FlagRound) + + return ph +} + +func elfphrelro(seg *sym.Segment) { + ph := newElfPhdr() + ph.Type = elf.PT_GNU_RELRO + ph.Vaddr = seg.Vaddr + ph.Paddr = seg.Vaddr + ph.Memsz = seg.Length + ph.Off = seg.Fileoff + ph.Filesz = seg.Filelen + ph.Align = uint64(*FlagRound) +} + +func elfshname(name string) *ElfShdr { + for i := 0; i < nelfstr; i++ { + if name != elfstr[i].s { + continue + } + off := elfstr[i].off + for i = 0; i < int(ehdr.Shnum); i++ { + sh := shdr[i] + if sh.Name == uint32(off) { + return sh + } + } + return newElfShdr(int64(off)) + } + Exitf("cannot find elf name %s", name) + return nil +} + +// Create an ElfShdr for the section with name. +// Create a duplicate if one already exists with that name. +func elfshnamedup(name string) *ElfShdr { + for i := 0; i < nelfstr; i++ { + if name == elfstr[i].s { + off := elfstr[i].off + return newElfShdr(int64(off)) + } + } + + Errorf(nil, "cannot find elf name %s", name) + errorexit() + return nil +} + +func elfshalloc(sect *sym.Section) *ElfShdr { + sh := elfshname(sect.Name) + sect.Elfsect = sh + return sh +} + +func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr { + var sh *ElfShdr + + if sect.Name == ".text" { + if sect.Elfsect == nil { + sect.Elfsect = elfshnamedup(sect.Name) + } + sh = sect.Elfsect.(*ElfShdr) + } else { + sh = elfshalloc(sect) + } + + // If this section has already been set up as a note, we assume type_ and + // flags are already correct, but the other fields still need filling in. + if sh.Type == uint32(elf.SHT_NOTE) { + if linkmode != LinkExternal { + // TODO(mwhudson): the approach here will work OK when + // linking internally for notes that we want to be included + // in a loadable segment (e.g. the abihash note) but not for + // notes that we do not want to be mapped (e.g. the package + // list note). The real fix is probably to define new values + // for Symbol.Type corresponding to mapped and unmapped notes + // and handle them in dodata(). + Errorf(nil, "sh.Type == SHT_NOTE in elfshbits when linking internally") + } + sh.Addralign = uint64(sect.Align) + sh.Size = sect.Length + sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + return sh + } + if sh.Type > 0 { + return sh + } + + if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { + switch sect.Name { + case ".init_array": + sh.Type = uint32(elf.SHT_INIT_ARRAY) + default: + sh.Type = uint32(elf.SHT_PROGBITS) + } + } else { + sh.Type = uint32(elf.SHT_NOBITS) + } + sh.Flags = uint64(elf.SHF_ALLOC) + if sect.Rwx&1 != 0 { + sh.Flags |= uint64(elf.SHF_EXECINSTR) + } + if sect.Rwx&2 != 0 { + sh.Flags |= uint64(elf.SHF_WRITE) + } + if sect.Name == ".tbss" { + sh.Flags |= uint64(elf.SHF_TLS) + sh.Type = uint32(elf.SHT_NOBITS) + } + if linkmode != LinkExternal { + sh.Addr = sect.Vaddr + } + + if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") { + sh.Flags = 0 + sh.Addr = 0 + if sect.Compressed { + sh.Flags |= uint64(elf.SHF_COMPRESSED) + } + } + + sh.Addralign = uint64(sect.Align) + sh.Size = sect.Length + if sect.Name != ".tbss" { + sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + } + + return sh +} + +func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr { + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab or notes. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return nil + } + if sect.Name == ".shstrtab" || sect.Name == ".tbss" { + return nil + } + if sect.Elfsect.(*ElfShdr).Type == uint32(elf.SHT_NOTE) { + return nil + } + + typ := elf.SHT_REL + if elfRelType == ".rela" { + typ = elf.SHT_RELA + } + + sh := elfshname(elfRelType + sect.Name) + // There could be multiple text sections but each needs + // its own .rela.text. + + if sect.Name == ".text" { + if sh.Info != 0 && sh.Info != uint32(sect.Elfsect.(*ElfShdr).shnum) { + sh = elfshnamedup(elfRelType + sect.Name) + } + } + + sh.Type = uint32(typ) + sh.Entsize = uint64(arch.RegSize) * 2 + if typ == elf.SHT_RELA { + sh.Entsize += uint64(arch.RegSize) + } + sh.Link = uint32(elfshname(".symtab").shnum) + sh.Info = uint32(sect.Elfsect.(*ElfShdr).shnum) + sh.Off = sect.Reloff + sh.Size = sect.Rellen + sh.Addralign = uint64(arch.RegSize) + return sh +} + +func elfrelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) { + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + if sect.Name == ".shstrtab" { + return + } + + ldr := ctxt.loader + for i, s := range syms { + if !ldr.AttrReachable(s) { + panic("should never happen") + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + + eaddr := sect.Vaddr + sect.Length + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= int64(eaddr) { + break + } + + // Compute external relocations on the go, and pass to Elfreloc1 + // to stream out. + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ldr.Errorf(s, "missing xsym in relocation") + continue + } + esr := ElfSymForReloc(ctxt, rr.Xsym) + if esr == 0 { + ldr.Errorf(s, "reloc %d (%s) to non-elf symbol %s (outer=%s) %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymType(r.Sym()).String()) + } + if !ldr.AttrReachable(rr.Xsym) { + ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym)) + } + if !thearch.Elfreloc1(ctxt, out, ldr, s, rr, ri, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) { + ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym())) + } + } + } + + // sanity check + if uint64(out.Offset()) != sect.Reloff+sect.Rellen { + panic(fmt.Sprintf("elfrelocsect: size mismatch %d != %d + %d", out.Offset(), sect.Reloff, sect.Rellen)) + } +} + +func elfEmitReloc(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + sizeExtRelocs(ctxt, thearch.ElfrelocSize) + relocSect, wg := relocSectFn(ctxt, elfrelocsect) + + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + relocSect(ctxt, sect, ctxt.Textp) + } else { + relocSect(ctxt, sect, ctxt.datap) + } + } + + for _, sect := range Segrodata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for _, sect := range Segrelrodata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for _, sect := range Segdata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ctxt.loader.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + relocSect(ctxt, sect, si.syms) + } + wg.Wait() +} + +func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) { + ldr := ctxt.loader + s := ldr.CreateSymForUpdate(sectionName, 0) + s.SetType(sym.SELFROSECT) + // namesz + s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME))) + // descsz + s.AddUint32(ctxt.Arch, uint32(len(desc))) + // tag + s.AddUint32(ctxt.Arch, tag) + // name + padding + s.AddBytes(ELF_NOTE_GO_NAME) + for len(s.Data())%4 != 0 { + s.AddUint8(0) + } + // desc + padding + s.AddBytes(desc) + for len(s.Data())%4 != 0 { + s.AddUint8(0) + } + s.SetSize(int64(len(s.Data()))) + s.SetAlign(4) +} + +func (ctxt *Link) doelf() { + ldr := ctxt.loader + + /* predefine strings we need for section headers */ + shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0) + + shstrtab.SetType(sym.SELFROSECT) + + shstrtab.Addstring("") + shstrtab.Addstring(".text") + shstrtab.Addstring(".noptrdata") + shstrtab.Addstring(".data") + shstrtab.Addstring(".bss") + shstrtab.Addstring(".noptrbss") + shstrtab.Addstring(".go.fuzzcntrs") + shstrtab.Addstring(".go.buildinfo") + if ctxt.IsMIPS() { + shstrtab.Addstring(".MIPS.abiflags") + shstrtab.Addstring(".gnu.attributes") + } + + // generate .tbss section for dynamic internal linker or external + // linking, so that various binutils could correctly calculate + // PT_TLS size. See https://golang.org/issue/5200. + if !*FlagD || ctxt.IsExternal() { + shstrtab.Addstring(".tbss") + } + if ctxt.IsNetbsd() { + shstrtab.Addstring(".note.netbsd.ident") + if *flagRace { + shstrtab.Addstring(".note.netbsd.pax") + } + } + if ctxt.IsOpenbsd() { + shstrtab.Addstring(".note.openbsd.ident") + } + if ctxt.IsFreebsd() { + shstrtab.Addstring(".note.tag") + } + if len(buildinfo) > 0 { + shstrtab.Addstring(".note.gnu.build-id") + } + if *flagBuildid != "" { + shstrtab.Addstring(".note.go.buildid") + } + shstrtab.Addstring(".elfdata") + shstrtab.Addstring(".rodata") + // See the comment about data.rel.ro.FOO section names in data.go. + relro_prefix := "" + if ctxt.UseRelro() { + shstrtab.Addstring(".data.rel.ro") + relro_prefix = ".data.rel.ro" + } + shstrtab.Addstring(relro_prefix + ".typelink") + shstrtab.Addstring(relro_prefix + ".itablink") + shstrtab.Addstring(relro_prefix + ".gosymtab") + shstrtab.Addstring(relro_prefix + ".gopclntab") + + if ctxt.IsExternal() { + *FlagD = true + + shstrtab.Addstring(elfRelType + ".text") + shstrtab.Addstring(elfRelType + ".rodata") + shstrtab.Addstring(elfRelType + relro_prefix + ".typelink") + shstrtab.Addstring(elfRelType + relro_prefix + ".itablink") + shstrtab.Addstring(elfRelType + relro_prefix + ".gosymtab") + shstrtab.Addstring(elfRelType + relro_prefix + ".gopclntab") + shstrtab.Addstring(elfRelType + ".noptrdata") + shstrtab.Addstring(elfRelType + ".data") + if ctxt.UseRelro() { + shstrtab.Addstring(elfRelType + ".data.rel.ro") + } + shstrtab.Addstring(elfRelType + ".go.buildinfo") + if ctxt.IsMIPS() { + shstrtab.Addstring(elfRelType + ".MIPS.abiflags") + shstrtab.Addstring(elfRelType + ".gnu.attributes") + } + + // add a .note.GNU-stack section to mark the stack as non-executable + shstrtab.Addstring(".note.GNU-stack") + + if ctxt.IsShared() { + shstrtab.Addstring(".note.go.abihash") + shstrtab.Addstring(".note.go.pkg-list") + shstrtab.Addstring(".note.go.deps") + } + } + + hasinitarr := ctxt.linkShared + + /* shared library initializer */ + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin: + hasinitarr = true + } + + if hasinitarr { + shstrtab.Addstring(".init_array") + shstrtab.Addstring(elfRelType + ".init_array") + } + + if !*FlagS { + shstrtab.Addstring(".symtab") + shstrtab.Addstring(".strtab") + dwarfaddshstrings(ctxt, shstrtab) + } + + shstrtab.Addstring(".shstrtab") + + if !*FlagD { /* -d suppresses dynamic loader format */ + shstrtab.Addstring(".interp") + shstrtab.Addstring(".hash") + shstrtab.Addstring(".got") + if ctxt.IsPPC64() { + shstrtab.Addstring(".glink") + } + shstrtab.Addstring(".got.plt") + shstrtab.Addstring(".dynamic") + shstrtab.Addstring(".dynsym") + shstrtab.Addstring(".dynstr") + shstrtab.Addstring(elfRelType) + shstrtab.Addstring(elfRelType + ".plt") + + shstrtab.Addstring(".plt") + shstrtab.Addstring(".gnu.version") + shstrtab.Addstring(".gnu.version_r") + + /* dynamic symbol table - first entry all zeros */ + dynsym := ldr.CreateSymForUpdate(".dynsym", 0) + + dynsym.SetType(sym.SELFROSECT) + if elf64 { + dynsym.SetSize(dynsym.Size() + ELF64SYMSIZE) + } else { + dynsym.SetSize(dynsym.Size() + ELF32SYMSIZE) + } + + /* dynamic string table */ + dynstr := ldr.CreateSymForUpdate(".dynstr", 0) + + dynstr.SetType(sym.SELFROSECT) + if dynstr.Size() == 0 { + dynstr.Addstring("") + } + + /* relocation table */ + s := ldr.CreateSymForUpdate(elfRelType, 0) + s.SetType(sym.SELFROSECT) + + /* global offset table */ + got := ldr.CreateSymForUpdate(".got", 0) + got.SetType(sym.SELFGOT) // writable + + /* ppc64 glink resolver */ + if ctxt.IsPPC64() { + s := ldr.CreateSymForUpdate(".glink", 0) + s.SetType(sym.SELFRXSECT) + } + + /* hash */ + hash := ldr.CreateSymForUpdate(".hash", 0) + hash.SetType(sym.SELFROSECT) + + gotplt := ldr.CreateSymForUpdate(".got.plt", 0) + gotplt.SetType(sym.SELFSECT) // writable + + plt := ldr.CreateSymForUpdate(".plt", 0) + if ctxt.IsPPC64() { + // In the ppc64 ABI, .plt is a data section + // written by the dynamic linker. + plt.SetType(sym.SELFSECT) + } else { + plt.SetType(sym.SELFRXSECT) + } + + s = ldr.CreateSymForUpdate(elfRelType+".plt", 0) + s.SetType(sym.SELFROSECT) + + s = ldr.CreateSymForUpdate(".gnu.version", 0) + s.SetType(sym.SELFROSECT) + + s = ldr.CreateSymForUpdate(".gnu.version_r", 0) + s.SetType(sym.SELFROSECT) + + /* define dynamic elf table */ + dynamic := ldr.CreateSymForUpdate(".dynamic", 0) + dynamic.SetType(sym.SELFSECT) // writable + + if ctxt.IsS390X() { + // S390X uses .got instead of .got.plt + gotplt = got + } + thearch.Elfsetupplt(ctxt, plt, gotplt, dynamic.Sym()) + + /* + * .dynamic table + */ + elfWriteDynEntSym(ctxt, dynamic, elf.DT_HASH, hash.Sym()) + + elfWriteDynEntSym(ctxt, dynamic, elf.DT_SYMTAB, dynsym.Sym()) + if elf64 { + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF64SYMSIZE) + } else { + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF32SYMSIZE) + } + elfWriteDynEntSym(ctxt, dynamic, elf.DT_STRTAB, dynstr.Sym()) + elfwritedynentsymsize(ctxt, dynamic, elf.DT_STRSZ, dynstr.Sym()) + if elfRelType == ".rela" { + rela := ldr.LookupOrCreateSym(".rela", 0) + elfWriteDynEntSym(ctxt, dynamic, elf.DT_RELA, rela) + elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELASZ, rela) + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELAENT, ELF64RELASIZE) + } else { + rel := ldr.LookupOrCreateSym(".rel", 0) + elfWriteDynEntSym(ctxt, dynamic, elf.DT_REL, rel) + elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELSZ, rel) + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELENT, ELF32RELSIZE) + } + + if rpath.val != "" { + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RUNPATH, uint64(dynstr.Addstring(rpath.val))) + } + + if ctxt.IsPPC64() { + elfWriteDynEntSym(ctxt, dynamic, elf.DT_PLTGOT, plt.Sym()) + } else { + elfWriteDynEntSym(ctxt, dynamic, elf.DT_PLTGOT, gotplt.Sym()) + } + + if ctxt.IsPPC64() { + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_PPC64_OPT, 0) + } + + // Solaris dynamic linker can't handle an empty .rela.plt if + // DT_JMPREL is emitted so we have to defer generation of elf.DT_PLTREL, + // DT_PLTRELSZ, and elf.DT_JMPREL dynamic entries until after we know the + // size of .rel(a).plt section. + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_DEBUG, 0) + } + + if ctxt.IsShared() { + // The go.link.abihashbytes symbol will be pointed at the appropriate + // part of the .note.go.abihash section in data.go:func address(). + s := ldr.LookupOrCreateSym("go:link.abihashbytes", 0) + sb := ldr.MakeSymbolUpdater(s) + ldr.SetAttrLocal(s, true) + sb.SetType(sym.SRODATA) + ldr.SetAttrSpecial(s, true) + sb.SetReachable(true) + sb.SetSize(notsha256.Size) + + sort.Sort(byPkg(ctxt.Library)) + h := notsha256.New() + for _, l := range ctxt.Library { + h.Write(l.Fingerprint[:]) + } + addgonote(ctxt, ".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{})) + addgonote(ctxt, ".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote) + var deplist []string + for _, shlib := range ctxt.Shlibs { + deplist = append(deplist, filepath.Base(shlib.Path)) + } + addgonote(ctxt, ".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n"))) + } + + if ctxt.LinkMode == LinkExternal && *flagBuildid != "" { + addgonote(ctxt, ".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(*flagBuildid)) + } + + //type mipsGnuAttributes struct { + // version uint8 // 'A' + // length uint32 // 15 including itself + // gnu [4]byte // "gnu\0" + // tag uint8 // 1:file, 2: section, 3: symbol, 1 here + // taglen uint32 // tag length, including tag, 7 here + // tagfp uint8 // 4 + // fpAbi uint8 // see .MIPS.abiflags + //} + if ctxt.IsMIPS() { + gnuattributes := ldr.CreateSymForUpdate(".gnu.attributes", 0) + gnuattributes.SetType(sym.SELFROSECT) + gnuattributes.SetReachable(true) + gnuattributes.AddUint8('A') // version 'A' + gnuattributes.AddUint32(ctxt.Arch, 15) // length 15 including itself + gnuattributes.AddBytes([]byte("gnu\x00")) // "gnu\0" + gnuattributes.AddUint8(1) // 1:file, 2: section, 3: symbol, 1 here + gnuattributes.AddUint32(ctxt.Arch, 7) // tag length, including tag, 7 here + gnuattributes.AddUint8(4) // 4 for FP, 8 for MSA + if buildcfg.GOMIPS == "softfloat" { + gnuattributes.AddUint8(MIPS_FPABI_SOFT) + } else { + // Note: MIPS_FPABI_ANY is bad naming: in fact it is MIPS I style FPR usage. + // It is not for 'ANY'. + // TODO: switch to FPXX after be sure that no odd-number-fpr is used. + gnuattributes.AddUint8(MIPS_FPABI_ANY) + } + } +} + +// Do not write DT_NULL. elfdynhash will finish it. +func shsym(sh *ElfShdr, ldr *loader.Loader, s loader.Sym) { + if s == 0 { + panic("bad symbol in shsym2") + } + addr := ldr.SymValue(s) + if sh.Flags&uint64(elf.SHF_ALLOC) != 0 { + sh.Addr = uint64(addr) + } + sh.Off = uint64(datoff(ldr, s, addr)) + sh.Size = uint64(ldr.SymSize(s)) +} + +func phsh(ph *ElfPhdr, sh *ElfShdr) { + ph.Vaddr = sh.Addr + ph.Paddr = ph.Vaddr + ph.Off = sh.Off + ph.Filesz = sh.Size + ph.Memsz = sh.Size + ph.Align = sh.Addralign +} + +func Asmbelfsetup() { + /* This null SHdr must appear before all others */ + elfshname("") + + for _, sect := range Segtext.Sections { + // There could be multiple .text sections. Instead check the Elfsect + // field to determine if already has an ElfShdr and if not, create one. + if sect.Name == ".text" { + if sect.Elfsect == nil { + sect.Elfsect = elfshnamedup(sect.Name) + } + } else { + elfshalloc(sect) + } + } + for _, sect := range Segrodata.Sections { + elfshalloc(sect) + } + for _, sect := range Segrelrodata.Sections { + elfshalloc(sect) + } + for _, sect := range Segdata.Sections { + elfshalloc(sect) + } + for _, sect := range Segdwarf.Sections { + elfshalloc(sect) + } +} + +func asmbElf(ctxt *Link) { + var symo int64 + if !*FlagS { + symo = int64(Segdwarf.Fileoff + Segdwarf.Filelen) + symo = Rnd(symo, int64(ctxt.Arch.PtrSize)) + ctxt.Out.SeekSet(symo) + asmElfSym(ctxt) + ctxt.Out.Write(Elfstrdat) + if ctxt.IsExternal() { + elfEmitReloc(ctxt) + } + } + ctxt.Out.SeekSet(0) + + ldr := ctxt.loader + eh := getElfEhdr() + switch ctxt.Arch.Family { + default: + Exitf("unknown architecture in asmbelf: %v", ctxt.Arch.Family) + case sys.MIPS, sys.MIPS64: + eh.Machine = uint16(elf.EM_MIPS) + case sys.Loong64: + eh.Machine = uint16(elf.EM_LOONGARCH) + case sys.ARM: + eh.Machine = uint16(elf.EM_ARM) + case sys.AMD64: + eh.Machine = uint16(elf.EM_X86_64) + case sys.ARM64: + eh.Machine = uint16(elf.EM_AARCH64) + case sys.I386: + eh.Machine = uint16(elf.EM_386) + case sys.PPC64: + eh.Machine = uint16(elf.EM_PPC64) + case sys.RISCV64: + eh.Machine = uint16(elf.EM_RISCV) + case sys.S390X: + eh.Machine = uint16(elf.EM_S390) + } + + elfreserve := int64(ELFRESERVE) + + numtext := int64(0) + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + numtext++ + } + } + + // If there are multiple text sections, extra space is needed + // in the elfreserve for the additional .text and .rela.text + // section headers. It can handle 4 extra now. Headers are + // 64 bytes. + + if numtext > 4 { + elfreserve += elfreserve + numtext*64*2 + } + + startva := *FlagTextAddr - int64(HEADR) + resoff := elfreserve + + var pph *ElfPhdr + var pnote *ElfPhdr + getpnote := func() *ElfPhdr { + if pnote == nil { + pnote = newElfPhdr() + pnote.Type = elf.PT_NOTE + pnote.Flags = elf.PF_R + } + return pnote + } + if *flagRace && ctxt.IsNetbsd() { + sh := elfshname(".note.netbsd.pax") + resoff -= int64(elfnetbsdpax(sh, uint64(startva), uint64(resoff))) + phsh(getpnote(), sh) + } + if ctxt.LinkMode == LinkExternal { + /* skip program headers */ + eh.Phoff = 0 + + eh.Phentsize = 0 + + if ctxt.BuildMode == BuildModeShared { + sh := elfshname(".note.go.pkg-list") + sh.Type = uint32(elf.SHT_NOTE) + sh = elfshname(".note.go.abihash") + sh.Type = uint32(elf.SHT_NOTE) + sh.Flags = uint64(elf.SHF_ALLOC) + sh = elfshname(".note.go.deps") + sh.Type = uint32(elf.SHT_NOTE) + } + + if *flagBuildid != "" { + sh := elfshname(".note.go.buildid") + sh.Type = uint32(elf.SHT_NOTE) + sh.Flags = uint64(elf.SHF_ALLOC) + } + + goto elfobj + } + + /* program header info */ + pph = newElfPhdr() + + pph.Type = elf.PT_PHDR + pph.Flags = elf.PF_R + pph.Off = uint64(eh.Ehsize) + pph.Vaddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off + pph.Paddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off + pph.Align = uint64(*FlagRound) + + /* + * PHDR must be in a loaded segment. Adjust the text + * segment boundaries downwards to include it. + */ + { + o := int64(Segtext.Vaddr - pph.Vaddr) + Segtext.Vaddr -= uint64(o) + Segtext.Length += uint64(o) + o = int64(Segtext.Fileoff - pph.Off) + Segtext.Fileoff -= uint64(o) + Segtext.Filelen += uint64(o) + } + + if !*FlagD { /* -d suppresses dynamic loader format */ + /* interpreter */ + sh := elfshname(".interp") + + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = 1 + + if interpreter == "" && buildcfg.GOOS == runtime.GOOS && buildcfg.GOARCH == runtime.GOARCH && buildcfg.GO_LDSO != "" { + interpreter = buildcfg.GO_LDSO + } + + if interpreter == "" { + switch ctxt.HeadType { + case objabi.Hlinux: + if buildcfg.GOOS == "android" { + interpreter = thearch.Androiddynld + if interpreter == "" { + Exitf("ELF interpreter not set") + } + } else { + interpreter = thearch.Linuxdynld + // If interpreter does not exist, try musl instead. + // This lets the same cmd/link binary work on + // both glibc-based and musl-based systems. + if _, err := os.Stat(interpreter); err != nil { + if musl := thearch.LinuxdynldMusl; musl != "" { + if _, err := os.Stat(musl); err == nil { + interpreter = musl + } + } + } + } + + case objabi.Hfreebsd: + interpreter = thearch.Freebsddynld + + case objabi.Hnetbsd: + interpreter = thearch.Netbsddynld + + case objabi.Hopenbsd: + interpreter = thearch.Openbsddynld + + case objabi.Hdragonfly: + interpreter = thearch.Dragonflydynld + + case objabi.Hsolaris: + interpreter = thearch.Solarisdynld + } + } + + resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter)) + + ph := newElfPhdr() + ph.Type = elf.PT_INTERP + ph.Flags = elf.PF_R + phsh(ph, sh) + } + + if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd || ctxt.HeadType == objabi.Hfreebsd { + var sh *ElfShdr + switch ctxt.HeadType { + case objabi.Hnetbsd: + sh = elfshname(".note.netbsd.ident") + resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff))) + + case objabi.Hopenbsd: + sh = elfshname(".note.openbsd.ident") + resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff))) + + case objabi.Hfreebsd: + sh = elfshname(".note.tag") + resoff -= int64(elffreebsdsig(sh, uint64(startva), uint64(resoff))) + } + // NetBSD, OpenBSD and FreeBSD require ident in an independent segment. + pnotei := newElfPhdr() + pnotei.Type = elf.PT_NOTE + pnotei.Flags = elf.PF_R + phsh(pnotei, sh) + } + + if len(buildinfo) > 0 { + sh := elfshname(".note.gnu.build-id") + resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff))) + phsh(getpnote(), sh) + } + + if *flagBuildid != "" { + sh := elfshname(".note.go.buildid") + resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff))) + phsh(getpnote(), sh) + } + + // Additions to the reserved area must be above this line. + + elfphload(&Segtext) + if len(Segrodata.Sections) > 0 { + elfphload(&Segrodata) + } + if len(Segrelrodata.Sections) > 0 { + elfphload(&Segrelrodata) + elfphrelro(&Segrelrodata) + } + elfphload(&Segdata) + + /* Dynamic linking sections */ + if !*FlagD { + sh := elfshname(".dynsym") + sh.Type = uint32(elf.SHT_DYNSYM) + sh.Flags = uint64(elf.SHF_ALLOC) + if elf64 { + sh.Entsize = ELF64SYMSIZE + } else { + sh.Entsize = ELF32SYMSIZE + } + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".dynstr").shnum) + + // sh.info is the index of first non-local symbol (number of local symbols) + s := ldr.Lookup(".dynsym", 0) + i := uint32(0) + for sub := s; sub != 0; sub = ldr.SubSym(sub) { + i++ + if !ldr.AttrLocal(sub) { + break + } + } + sh.Info = i + shsym(sh, ldr, s) + + sh = elfshname(".dynstr") + sh.Type = uint32(elf.SHT_STRTAB) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = 1 + shsym(sh, ldr, ldr.Lookup(".dynstr", 0)) + + if elfverneed != 0 { + sh := elfshname(".gnu.version") + sh.Type = uint32(elf.SHT_GNU_VERSYM) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = 2 + sh.Link = uint32(elfshname(".dynsym").shnum) + sh.Entsize = 2 + shsym(sh, ldr, ldr.Lookup(".gnu.version", 0)) + + sh = elfshname(".gnu.version_r") + sh.Type = uint32(elf.SHT_GNU_VERNEED) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Info = uint32(elfverneed) + sh.Link = uint32(elfshname(".dynstr").shnum) + shsym(sh, ldr, ldr.Lookup(".gnu.version_r", 0)) + } + + if elfRelType == ".rela" { + sh := elfshname(".rela.plt") + sh.Type = uint32(elf.SHT_RELA) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = ELF64RELASIZE + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".dynsym").shnum) + sh.Info = uint32(elfshname(".plt").shnum) + shsym(sh, ldr, ldr.Lookup(".rela.plt", 0)) + + sh = elfshname(".rela") + sh.Type = uint32(elf.SHT_RELA) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = ELF64RELASIZE + sh.Addralign = 8 + sh.Link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ldr, ldr.Lookup(".rela", 0)) + } else { + sh := elfshname(".rel.plt") + sh.Type = uint32(elf.SHT_REL) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = ELF32RELSIZE + sh.Addralign = 4 + sh.Link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ldr, ldr.Lookup(".rel.plt", 0)) + + sh = elfshname(".rel") + sh.Type = uint32(elf.SHT_REL) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = ELF32RELSIZE + sh.Addralign = 4 + sh.Link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ldr, ldr.Lookup(".rel", 0)) + } + + if elf.Machine(eh.Machine) == elf.EM_PPC64 { + sh := elfshname(".glink") + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR) + sh.Addralign = 4 + shsym(sh, ldr, ldr.Lookup(".glink", 0)) + } + + sh = elfshname(".plt") + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR) + if elf.Machine(eh.Machine) == elf.EM_X86_64 { + sh.Entsize = 16 + } else if elf.Machine(eh.Machine) == elf.EM_S390 { + sh.Entsize = 32 + } else if elf.Machine(eh.Machine) == elf.EM_PPC64 { + // On ppc64, this is just a table of addresses + // filled by the dynamic linker + sh.Type = uint32(elf.SHT_NOBITS) + + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE) + sh.Entsize = 8 + } else { + sh.Entsize = 4 + } + sh.Addralign = sh.Entsize + shsym(sh, ldr, ldr.Lookup(".plt", 0)) + + // On ppc64, .got comes from the input files, so don't + // create it here, and .got.plt is not used. + if elf.Machine(eh.Machine) != elf.EM_PPC64 { + sh := elfshname(".got") + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE) + sh.Entsize = uint64(ctxt.Arch.RegSize) + sh.Addralign = uint64(ctxt.Arch.RegSize) + shsym(sh, ldr, ldr.Lookup(".got", 0)) + + sh = elfshname(".got.plt") + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE) + sh.Entsize = uint64(ctxt.Arch.RegSize) + sh.Addralign = uint64(ctxt.Arch.RegSize) + shsym(sh, ldr, ldr.Lookup(".got.plt", 0)) + } + + sh = elfshname(".hash") + sh.Type = uint32(elf.SHT_HASH) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = 4 + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ldr, ldr.Lookup(".hash", 0)) + + /* sh and elf.PT_DYNAMIC for .dynamic section */ + sh = elfshname(".dynamic") + + sh.Type = uint32(elf.SHT_DYNAMIC) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE) + sh.Entsize = 2 * uint64(ctxt.Arch.RegSize) + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".dynstr").shnum) + shsym(sh, ldr, ldr.Lookup(".dynamic", 0)) + ph := newElfPhdr() + ph.Type = elf.PT_DYNAMIC + ph.Flags = elf.PF_R + elf.PF_W + phsh(ph, sh) + + /* + * Thread-local storage segment (really just size). + */ + tlssize := uint64(0) + for _, sect := range Segdata.Sections { + if sect.Name == ".tbss" { + tlssize = sect.Length + } + } + if tlssize != 0 { + ph := newElfPhdr() + ph.Type = elf.PT_TLS + ph.Flags = elf.PF_R + ph.Memsz = tlssize + ph.Align = uint64(ctxt.Arch.RegSize) + } + } + + if ctxt.HeadType == objabi.Hlinux { + ph := newElfPhdr() + ph.Type = elf.PT_GNU_STACK + ph.Flags = elf.PF_W + elf.PF_R + ph.Align = uint64(ctxt.Arch.RegSize) + + ph = newElfPhdr() + ph.Type = elf.PT_PAX_FLAGS + ph.Flags = 0x2a00 // mprotect, randexec, emutramp disabled + ph.Align = uint64(ctxt.Arch.RegSize) + } else if ctxt.HeadType == objabi.Hsolaris { + ph := newElfPhdr() + ph.Type = elf.PT_SUNWSTACK + ph.Flags = elf.PF_W + elf.PF_R + } else if ctxt.HeadType == objabi.Hfreebsd { + ph := newElfPhdr() + ph.Type = elf.PT_GNU_STACK + ph.Flags = elf.PF_W + elf.PF_R + ph.Align = uint64(ctxt.Arch.RegSize) + } + +elfobj: + sh := elfshname(".shstrtab") + sh.Type = uint32(elf.SHT_STRTAB) + sh.Addralign = 1 + shsym(sh, ldr, ldr.Lookup(".shstrtab", 0)) + eh.Shstrndx = uint16(sh.shnum) + + if ctxt.IsMIPS() { + sh = elfshname(".MIPS.abiflags") + sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = 8 + resoff -= int64(elfMipsAbiFlags(sh, uint64(startva), uint64(resoff))) + + ph := newElfPhdr() + ph.Type = elf.PT_MIPS_ABIFLAGS + ph.Flags = elf.PF_R + phsh(ph, sh) + + sh = elfshname(".gnu.attributes") + sh.Type = uint32(elf.SHT_GNU_ATTRIBUTES) + sh.Addralign = 1 + ldr := ctxt.loader + shsym(sh, ldr, ldr.Lookup(".gnu.attributes", 0)) + } + + // put these sections early in the list + if !*FlagS { + elfshname(".symtab") + elfshname(".strtab") + } + + for _, sect := range Segtext.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segrodata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segrelrodata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segdata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segdwarf.Sections { + elfshbits(ctxt.LinkMode, sect) + } + + if ctxt.LinkMode == LinkExternal { + for _, sect := range Segtext.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segrodata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segrelrodata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segdata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, si := range dwarfp { + sect := ldr.SymSect(si.secSym()) + elfshreloc(ctxt.Arch, sect) + } + // add a .note.GNU-stack section to mark the stack as non-executable + sh := elfshname(".note.GNU-stack") + + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Addralign = 1 + sh.Flags = 0 + } + + if !*FlagS { + sh := elfshname(".symtab") + sh.Type = uint32(elf.SHT_SYMTAB) + sh.Off = uint64(symo) + sh.Size = uint64(symSize) + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Entsize = 8 + 2*uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".strtab").shnum) + sh.Info = uint32(elfglobalsymndx) + + sh = elfshname(".strtab") + sh.Type = uint32(elf.SHT_STRTAB) + sh.Off = uint64(symo) + uint64(symSize) + sh.Size = uint64(len(Elfstrdat)) + sh.Addralign = 1 + } + + /* Main header */ + copy(eh.Ident[:], elf.ELFMAG) + + var osabi elf.OSABI + switch ctxt.HeadType { + case objabi.Hfreebsd: + osabi = elf.ELFOSABI_FREEBSD + case objabi.Hnetbsd: + osabi = elf.ELFOSABI_NETBSD + case objabi.Hopenbsd: + osabi = elf.ELFOSABI_OPENBSD + case objabi.Hdragonfly: + osabi = elf.ELFOSABI_NONE + } + eh.Ident[elf.EI_OSABI] = byte(osabi) + + if elf64 { + eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS64) + } else { + eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS32) + } + if ctxt.Arch.ByteOrder == binary.BigEndian { + eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2MSB) + } else { + eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2LSB) + } + eh.Ident[elf.EI_VERSION] = byte(elf.EV_CURRENT) + + if ctxt.LinkMode == LinkExternal { + eh.Type = uint16(elf.ET_REL) + } else if ctxt.BuildMode == BuildModePIE { + eh.Type = uint16(elf.ET_DYN) + } else { + eh.Type = uint16(elf.ET_EXEC) + } + + if ctxt.LinkMode != LinkExternal { + eh.Entry = uint64(Entryvalue(ctxt)) + } + + eh.Version = uint32(elf.EV_CURRENT) + + if pph != nil { + pph.Filesz = uint64(eh.Phnum) * uint64(eh.Phentsize) + pph.Memsz = pph.Filesz + } + + ctxt.Out.SeekSet(0) + a := int64(0) + a += int64(elfwritehdr(ctxt.Out)) + a += int64(elfwritephdrs(ctxt.Out)) + a += int64(elfwriteshdrs(ctxt.Out)) + if !*FlagD { + a += int64(elfwriteinterp(ctxt.Out)) + } + if ctxt.IsMIPS() { + a += int64(elfWriteMipsAbiFlags(ctxt)) + } + + if ctxt.LinkMode != LinkExternal { + if ctxt.HeadType == objabi.Hnetbsd { + a += int64(elfwritenetbsdsig(ctxt.Out)) + } + if ctxt.HeadType == objabi.Hopenbsd { + a += int64(elfwriteopenbsdsig(ctxt.Out)) + } + if ctxt.HeadType == objabi.Hfreebsd { + a += int64(elfwritefreebsdsig(ctxt.Out)) + } + if len(buildinfo) > 0 { + a += int64(elfwritebuildinfo(ctxt.Out)) + } + if *flagBuildid != "" { + a += int64(elfwritegobuildid(ctxt.Out)) + } + } + if *flagRace && ctxt.IsNetbsd() { + a += int64(elfwritenetbsdpax(ctxt.Out)) + } + + if a > elfreserve { + Errorf(nil, "ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext) + } + + // Verify the amount of space allocated for the elf header is sufficient. The file offsets are + // already computed in layout, so we could spill into another section. + if a > int64(HEADR) { + Errorf(nil, "HEADR too small: %d > %d with %d text sections", a, HEADR, numtext) + } +} + +func elfadddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) { + ldr.SetSymDynid(s, int32(Nelfsym)) + Nelfsym++ + d := ldr.MakeSymbolUpdater(syms.DynSym) + name := ldr.SymExtname(s) + dstru := ldr.MakeSymbolUpdater(syms.DynStr) + st := ldr.SymType(s) + cgoeStatic := ldr.AttrCgoExportStatic(s) + cgoeDynamic := ldr.AttrCgoExportDynamic(s) + cgoexp := (cgoeStatic || cgoeDynamic) + + d.AddUint32(target.Arch, uint32(dstru.Addstring(name))) + + if elf64 { + + /* type */ + var t uint8 + + if cgoexp && st == sym.STEXT { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC) + } else { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT) + } + d.AddUint8(t) + + /* reserved */ + d.AddUint8(0) + + /* section where symbol is defined */ + if st == sym.SDYNIMPORT { + d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF)) + } else { + d.AddUint16(target.Arch, 1) + } + + /* value */ + if st == sym.SDYNIMPORT { + d.AddUint64(target.Arch, 0) + } else { + d.AddAddrPlus(target.Arch, s, 0) + } + + /* size of object */ + d.AddUint64(target.Arch, uint64(len(ldr.Data(s)))) + + dil := ldr.SymDynimplib(s) + + if !cgoeDynamic && dil != "" && !seenlib[dil] { + du := ldr.MakeSymbolUpdater(syms.Dynamic) + Elfwritedynent(target.Arch, du, elf.DT_NEEDED, uint64(dstru.Addstring(dil))) + seenlib[dil] = true + } + } else { + + /* value */ + if st == sym.SDYNIMPORT { + d.AddUint32(target.Arch, 0) + } else { + d.AddAddrPlus(target.Arch, s, 0) + } + + /* size of object */ + d.AddUint32(target.Arch, uint32(len(ldr.Data(s)))) + + /* type */ + var t uint8 + + // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386. + if target.Arch.Family == sys.I386 && cgoexp && st == sym.STEXT { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC) + } else if target.Arch.Family == sys.ARM && cgoeDynamic && st == sym.STEXT { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC) + } else { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT) + } + d.AddUint8(t) + d.AddUint8(0) + + /* shndx */ + if st == sym.SDYNIMPORT { + d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF)) + } else { + d.AddUint16(target.Arch, 1) + } + } +} diff --git a/src/cmd/link/internal/ld/elf_test.go b/src/cmd/link/internal/ld/elf_test.go new file mode 100644 index 0000000..8af0ca1 --- /dev/null +++ b/src/cmd/link/internal/ld/elf_test.go @@ -0,0 +1,127 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo +// +build cgo + +package ld + +import ( + "debug/elf" + "internal/testenv" + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestDynSymShInfo(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + dir := t.TempDir() + + const prog = ` +package main + +import "net" + +func main() { + net.Dial("", "") +} +` + src := filepath.Join(dir, "issue33358.go") + if err := os.WriteFile(src, []byte(prog), 0666); err != nil { + t.Fatal(err) + } + + binFile := filepath.Join(dir, "issue33358") + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, src) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("%v: %v:\n%s", cmd.Args, err, out) + } + + fi, err := os.Open(binFile) + if err != nil { + t.Fatalf("failed to open built file: %v", err) + } + defer fi.Close() + + elfFile, err := elf.NewFile(fi) + if err != nil { + t.Skip("The system may not support ELF, skipped.") + } + + section := elfFile.Section(".dynsym") + if section == nil { + t.Fatal("no dynsym") + } + + symbols, err := elfFile.DynamicSymbols() + if err != nil { + t.Fatalf("failed to get dynamic symbols: %v", err) + } + + var numLocalSymbols uint32 + for i, s := range symbols { + if elf.ST_BIND(s.Info) != elf.STB_LOCAL { + numLocalSymbols = uint32(i + 1) + break + } + } + + if section.Info != numLocalSymbols { + t.Fatalf("Unexpected sh info, want greater than 0, got: %d", section.Info) + } +} + +func TestNoDuplicateNeededEntries(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + // run this test on just a small set of platforms (no need to test it + // across the board given the nature of the test). + pair := runtime.GOOS + "-" + runtime.GOARCH + switch pair { + case "linux-amd64", "linux-arm64", "freebsd-amd64", "openbsd-amd64": + default: + t.Skip("no need for test on " + pair) + } + + t.Parallel() + + dir := t.TempDir() + + wd, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get working directory: %v", err) + } + + path := filepath.Join(dir, "x") + argv := []string{"build", "-o", path, filepath.Join(wd, "testdata", "issue39256")} + out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput() + if err != nil { + t.Fatalf("Build failure: %s\n%s\n", err, string(out)) + } + + f, err := elf.Open(path) + if err != nil { + t.Fatalf("Failed to open ELF file: %v", err) + } + libs, err := f.ImportedLibraries() + if err != nil { + t.Fatalf("Failed to read imported libraries: %v", err) + } + + var count int + for _, lib := range libs { + if lib == "libc.so" || strings.HasPrefix(lib, "libc.so.") { + count++ + } + } + + if got, want := count, 1; got != want { + t.Errorf("Got %d entries for `libc.so`, want %d", got, want) + } +} diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go new file mode 100644 index 0000000..b553d68 --- /dev/null +++ b/src/cmd/link/internal/ld/errors.go @@ -0,0 +1,67 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/obj" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "sync" +) + +type unresolvedSymKey struct { + from loader.Sym // Symbol that referenced unresolved "to" + to loader.Sym // Unresolved symbol referenced by "from" +} + +type symNameFn func(s loader.Sym) string + +// ErrorReporter is used to make error reporting thread safe. +type ErrorReporter struct { + loader.ErrorReporter + unresSyms map[unresolvedSymKey]bool + unresMutex sync.Mutex + SymName symNameFn +} + +// errorUnresolved prints unresolved symbol error for rs that is referenced from s. +func (reporter *ErrorReporter) errorUnresolved(ldr *loader.Loader, s, rs loader.Sym) { + reporter.unresMutex.Lock() + defer reporter.unresMutex.Unlock() + + if reporter.unresSyms == nil { + reporter.unresSyms = make(map[unresolvedSymKey]bool) + } + k := unresolvedSymKey{from: s, to: rs} + if !reporter.unresSyms[k] { + reporter.unresSyms[k] = true + name := ldr.SymName(rs) + + // Try to find symbol under another ABI. + var reqABI, haveABI obj.ABI + haveABI = ^obj.ABI(0) + reqABI, ok := sym.VersionToABI(ldr.SymVersion(rs)) + if ok { + for abi := obj.ABI(0); abi < obj.ABICount; abi++ { + v := sym.ABIToVersion(abi) + if v == -1 { + continue + } + if rs1 := ldr.Lookup(name, v); rs1 != 0 && ldr.SymType(rs1) != sym.Sxxx && ldr.SymType(rs1) != sym.SXREF { + haveABI = abi + } + } + } + + // Give a special error message for main symbol (see #24809). + if name == "main.main" { + reporter.Errorf(s, "function main is undeclared in the main package") + } else if haveABI != ^obj.ABI(0) { + reporter.Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", name, reqABI, haveABI) + } else { + reporter.Errorf(s, "relocation target %s not defined", name) + } + } +} diff --git a/src/cmd/link/internal/ld/execarchive.go b/src/cmd/link/internal/ld/execarchive.go new file mode 100644 index 0000000..a9376e9 --- /dev/null +++ b/src/cmd/link/internal/ld/execarchive.go @@ -0,0 +1,38 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !wasm && !windows +// +build !wasm,!windows + +package ld + +import ( + "os" + "os/exec" + "path/filepath" + "syscall" +) + +const syscallExecSupported = true + +// execArchive invokes the archiver tool with syscall.Exec(), with +// the expectation that this is the last thing that takes place +// in the linking operation. +func (ctxt *Link) execArchive(argv []string) { + var err error + argv0 := argv[0] + if filepath.Base(argv0) == argv0 { + argv0, err = exec.LookPath(argv0) + if err != nil { + Exitf("cannot find %s: %v", argv[0], err) + } + } + if ctxt.Debugvlog != 0 { + ctxt.Logf("invoking archiver with syscall.Exec()\n") + } + err = syscall.Exec(argv0, argv, os.Environ()) + if err != nil { + Exitf("running %s failed: %v", argv[0], err) + } +} diff --git a/src/cmd/link/internal/ld/execarchive_noexec.go b/src/cmd/link/internal/ld/execarchive_noexec.go new file mode 100644 index 0000000..5e1f266 --- /dev/null +++ b/src/cmd/link/internal/ld/execarchive_noexec.go @@ -0,0 +1,14 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build wasm || windows +// +build wasm windows + +package ld + +const syscallExecSupported = false + +func (ctxt *Link) execArchive(argv []string) { + panic("should never arrive here") +} diff --git a/src/cmd/link/internal/ld/fallocate_test.go b/src/cmd/link/internal/ld/fallocate_test.go new file mode 100644 index 0000000..1ed0eb2 --- /dev/null +++ b/src/cmd/link/internal/ld/fallocate_test.go @@ -0,0 +1,65 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || linux +// +build darwin linux + +package ld + +import ( + "os" + "path/filepath" + "syscall" + "testing" +) + +func TestFallocate(t *testing.T) { + dir := t.TempDir() + filename := filepath.Join(dir, "a.out") + out := NewOutBuf(nil) + err := out.Open(filename) + if err != nil { + t.Fatalf("Open file failed: %v", err) + } + defer out.Close() + + // Try fallocate first. + for { + err = out.fallocate(1 << 10) + if err == syscall.EOPNOTSUPP { // The underlying file system may not support fallocate + t.Skip("fallocate is not supported") + } + if err == syscall.EINTR { + continue // try again + } + if err != nil { + t.Fatalf("fallocate failed: %v", err) + } + break + } + + // Mmap 1 MiB initially, and grow to 2 and 3 MiB. + // Check if the file size and disk usage is expected. + for _, sz := range []int64{1 << 20, 2 << 20, 3 << 20} { + err = out.Mmap(uint64(sz)) + if err != nil { + t.Fatalf("Mmap failed: %v", err) + } + stat, err := os.Stat(filename) + if err != nil { + t.Fatalf("Stat failed: %v", err) + } + if got := stat.Size(); got != sz { + t.Errorf("unexpected file size: got %d, want %d", got, sz) + } + // The number of blocks must be enough for the requested size. + // We used to require an exact match, but it appears that + // some file systems allocate a few extra blocks in some cases. + // See issue #41127. + if got, want := stat.Sys().(*syscall.Stat_t).Blocks, (sz+511)/512; got < want { + t.Errorf("unexpected disk usage: got %d blocks, want at least %d", got, want) + } + out.munmap() + } +} diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go new file mode 100644 index 0000000..9dfb0f7 --- /dev/null +++ b/src/cmd/link/internal/ld/go.go @@ -0,0 +1,499 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go-specific code shared across loaders (5l, 6l, 8l). + +package ld + +import ( + "cmd/internal/bio" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "encoding/json" + "fmt" + "io" + "os" + "sort" + "strconv" + "strings" +) + +// go-specific code shared across loaders (5l, 6l, 8l). + +// TODO: +// generate debugging section in binary. +// once the dust settles, try to move some code to +// libmach, so that other linkers and ar can share. + +func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) { + if *flagG { + return + } + + if int64(int(length)) != length { + fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) + return + } + + bdata := make([]byte, length) + if _, err := io.ReadFull(f, bdata); err != nil { + fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) + return + } + data := string(bdata) + + // process header lines + for data != "" { + var line string + if i := strings.Index(data, "\n"); i >= 0 { + line, data = data[:i], data[i+1:] + } else { + line, data = data, "" + } + if line == "main" { + lib.Main = true + } + if line == "" { + break + } + } + + // look for cgo section + p0 := strings.Index(data, "\n$$ // cgo") + var p1 int + if p0 >= 0 { + p0 += p1 + i := strings.IndexByte(data[p0+1:], '\n') + if i < 0 { + fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) + return + } + p0 += 1 + i + + p1 = strings.Index(data[p0:], "\n$$") + if p1 < 0 { + p1 = strings.Index(data[p0:], "\n!\n") + } + if p1 < 0 { + fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) + return + } + p1 += p0 + loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1]) + } +} + +func loadcgo(ctxt *Link, file string, pkg string, p string) { + var directives [][]string + if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil { + fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err) + nerrors++ + return + } + + // Record the directives. We'll process them later after Symbols are created. + ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives}) +} + +// Set symbol attributes or flags based on cgo directives. +// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'. +func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) { + l := ctxt.loader + for _, f := range directives { + switch f[0] { + case "cgo_import_dynamic": + if len(f) < 2 || len(f) > 4 { + break + } + + local := f[1] + remote := local + if len(f) > 2 { + remote = f[2] + } + lib := "" + if len(f) > 3 { + lib = f[3] + } + + if *FlagD { + fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file) + nerrors++ + return + } + + if local == "_" && remote == "_" { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + havedynamic = 1 + + if ctxt.HeadType == objabi.Hdarwin { + machoadddynlib(lib, ctxt.LinkMode) + } else { + dynlib = append(dynlib, lib) + } + continue + } + + q := "" + if i := strings.Index(remote, "#"); i >= 0 { + remote, q = remote[:i], remote[i+1:] + } + s := l.LookupOrCreateSym(local, 0) + st := l.SymType(s) + if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ { + l.SetSymDynimplib(s, lib) + l.SetSymExtname(s, remote) + l.SetSymDynimpvers(s, q) + if st != sym.SHOSTOBJ { + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SDYNIMPORT) + } else { + hostObjSyms[s] = struct{}{} + } + havedynamic = 1 + if lib != "" && ctxt.IsDarwin() { + machoadddynlib(lib, ctxt.LinkMode) + } + } + + continue + + case "cgo_import_static": + if len(f) != 2 { + break + } + local := f[1] + + s := l.LookupOrCreateSym(local, 0) + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SHOSTOBJ) + su.SetSize(0) + hostObjSyms[s] = struct{}{} + continue + + case "cgo_export_static", "cgo_export_dynamic": + if len(f) < 2 || len(f) > 4 { + break + } + local := f[1] + remote := local + if len(f) > 2 { + remote = f[2] + } + // The compiler adds a fourth argument giving + // the definition ABI of function symbols. + abi := obj.ABI0 + if len(f) > 3 { + var ok bool + abi, ok = obj.ParseABI(f[3]) + if !ok { + fmt.Fprintf(os.Stderr, "%s: bad ABI in cgo_export directive %s\n", os.Args[0], f) + nerrors++ + return + } + } + + s := l.LookupOrCreateSym(local, sym.ABIToVersion(abi)) + + if l.SymType(s) == sym.SHOSTOBJ { + hostObjSyms[s] = struct{}{} + } + + switch ctxt.BuildMode { + case BuildModeCShared, BuildModeCArchive, BuildModePlugin: + if s == l.Lookup("main", 0) { + continue + } + } + + // export overrides import, for openbsd/cgo. + // see issue 4878. + if l.SymDynimplib(s) != "" { + l.SetSymDynimplib(s, "") + l.SetSymDynimpvers(s, "") + l.SetSymExtname(s, "") + var su *loader.SymbolBuilder + su = l.MakeSymbolUpdater(s) + su.SetType(0) + } + + if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) { + l.SetSymExtname(s, remote) + } else if l.SymExtname(s) != remote { + fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote) + nerrors++ + return + } + + // Mark exported symbols and also add them to + // the lists used for roots in the deadcode pass. + if f[0] == "cgo_export_static" { + if ctxt.LinkMode == LinkExternal && !l.AttrCgoExportStatic(s) { + // Static cgo exports appear + // in the exported symbol table. + ctxt.dynexp = append(ctxt.dynexp, s) + } + if ctxt.LinkMode == LinkInternal { + // For internal linking, we're + // responsible for resolving + // relocations from host objects. + // Record the right Go symbol + // version to use. + l.AddCgoExport(s) + } + l.SetAttrCgoExportStatic(s, true) + } else { + if ctxt.LinkMode == LinkInternal && !l.AttrCgoExportDynamic(s) { + // Dynamic cgo exports appear + // in the exported symbol table. + ctxt.dynexp = append(ctxt.dynexp, s) + } + l.SetAttrCgoExportDynamic(s, true) + } + + continue + + case "cgo_dynamic_linker": + if len(f) != 2 { + break + } + + if *flagInterpreter == "" { + if interpreter != "" && interpreter != f[1] { + fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1]) + nerrors++ + return + } + + interpreter = f[1] + } + continue + + case "cgo_ldflag": + if len(f) != 2 { + break + } + ldflag = append(ldflag, f[1]) + continue + } + + fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f) + nerrors++ + } + return +} + +// openbsdTrimLibVersion indicates whether a shared library is +// versioned and if it is, returns the unversioned name. The +// OpenBSD library naming scheme is lib<name>.so.<major>.<minor> +func openbsdTrimLibVersion(lib string) (string, bool) { + parts := strings.Split(lib, ".") + if len(parts) != 4 { + return "", false + } + if parts[1] != "so" { + return "", false + } + if _, err := strconv.Atoi(parts[2]); err != nil { + return "", false + } + if _, err := strconv.Atoi(parts[3]); err != nil { + return "", false + } + return fmt.Sprintf("%s.%s", parts[0], parts[1]), true +} + +// dedupLibrariesOpenBSD dedups a list of shared libraries, treating versioned +// and unversioned libraries as equivalents. Versioned libraries are preferred +// and retained over unversioned libraries. This avoids the situation where +// the use of cgo results in a DT_NEEDED for a versioned library (for example, +// libc.so.96.1), while a dynamic import specifies an unversioned library (for +// example, libc.so) - this would otherwise result in two DT_NEEDED entries +// for the same library, resulting in a failure when ld.so attempts to load +// the Go binary. +func dedupLibrariesOpenBSD(ctxt *Link, libs []string) []string { + libraries := make(map[string]string) + for _, lib := range libs { + if name, ok := openbsdTrimLibVersion(lib); ok { + // Record unversioned name as seen. + seenlib[name] = true + libraries[name] = lib + } else if _, ok := libraries[lib]; !ok { + libraries[lib] = lib + } + } + + libs = nil + for _, lib := range libraries { + libs = append(libs, lib) + } + sort.Strings(libs) + + return libs +} + +func dedupLibraries(ctxt *Link, libs []string) []string { + if ctxt.Target.IsOpenbsd() { + return dedupLibrariesOpenBSD(ctxt, libs) + } + return libs +} + +var seenlib = make(map[string]bool) + +func adddynlib(ctxt *Link, lib string) { + if seenlib[lib] || ctxt.LinkMode == LinkExternal { + return + } + seenlib[lib] = true + + if ctxt.IsELF { + dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr) + if dsu.Size() == 0 { + dsu.Addstring("") + } + du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic) + Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib))) + } else { + Errorf(nil, "adddynlib: unsupported binary format") + } +} + +func Adddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) { + if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal { + return + } + + if target.IsELF { + elfadddynsym(ldr, target, syms, s) + } else if target.HeadType == objabi.Hdarwin { + ldr.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s)) + } else if target.HeadType == objabi.Hwindows { + // already taken care of + } else { + ldr.Errorf(s, "adddynsym: unsupported binary format") + } +} + +func fieldtrack(arch *sys.Arch, l *loader.Loader) { + var buf strings.Builder + for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ { + if name := l.SymName(i); strings.HasPrefix(name, "go:track.") { + if l.AttrReachable(i) { + l.SetAttrSpecial(i, true) + l.SetAttrNotInSymbolTable(i, true) + buf.WriteString(name[9:]) + for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] { + buf.WriteString("\t") + buf.WriteString(l.SymName(p)) + } + buf.WriteString("\n") + } + } + } + l.Reachparent = nil // we are done with it + if *flagFieldTrack == "" { + return + } + s := l.Lookup(*flagFieldTrack, 0) + if s == 0 || !l.AttrReachable(s) { + return + } + bld := l.MakeSymbolUpdater(s) + bld.SetType(sym.SDATA) + addstrdata(arch, l, *flagFieldTrack, buf.String()) +} + +func (ctxt *Link) addexport() { + // Track undefined external symbols during external link. + if ctxt.LinkMode == LinkExternal { + for _, s := range ctxt.Textp { + if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) { + continue + } + relocs := ctxt.loader.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + if rs := relocs.At(i).Sym(); rs != 0 { + if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) { + // sanity check + if len(ctxt.loader.Data(rs)) != 0 { + panic("expected no data on undef symbol") + } + su := ctxt.loader.MakeSymbolUpdater(rs) + su.SetType(sym.SUNDEFEXT) + } + } + } + } + } + + // TODO(aix) + if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix { + return + } + + // Add dynamic symbols. + for _, s := range ctxt.dynexp { + // Consistency check. + if !ctxt.loader.AttrReachable(s) { + panic("dynexp entry not reachable") + } + + Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, s) + } + + for _, lib := range dedupLibraries(ctxt, dynlib) { + adddynlib(ctxt, lib) + } +} + +type Pkg struct { + mark bool + checked bool + path string + impby []*Pkg +} + +var pkgall []*Pkg + +func (p *Pkg) cycle() *Pkg { + if p.checked { + return nil + } + + if p.mark { + nerrors++ + fmt.Printf("import cycle:\n") + fmt.Printf("\t%s\n", p.path) + return p + } + + p.mark = true + for _, q := range p.impby { + if bad := q.cycle(); bad != nil { + p.mark = false + p.checked = true + fmt.Printf("\timports %s\n", p.path) + if bad == p { + return nil + } + return bad + } + } + + p.checked = true + p.mark = false + return nil +} + +func importcycles() { + for _, p := range pkgall { + p.cycle() + } +} diff --git a/src/cmd/link/internal/ld/go_test.go b/src/cmd/link/internal/ld/go_test.go new file mode 100644 index 0000000..836731a --- /dev/null +++ b/src/cmd/link/internal/ld/go_test.go @@ -0,0 +1,116 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "internal/testenv" + "os" + "path/filepath" + "reflect" + "runtime" + "testing" + + "cmd/internal/objabi" +) + +func TestDedupLibraries(t *testing.T) { + ctxt := &Link{} + ctxt.Target.HeadType = objabi.Hlinux + + libs := []string{"libc.so", "libc.so.6"} + + got := dedupLibraries(ctxt, libs) + if !reflect.DeepEqual(got, libs) { + t.Errorf("dedupLibraries(%v) = %v, want %v", libs, got, libs) + } +} + +func TestDedupLibrariesOpenBSD(t *testing.T) { + ctxt := &Link{} + ctxt.Target.HeadType = objabi.Hopenbsd + + tests := []struct { + libs []string + want []string + }{ + { + libs: []string{"libc.so"}, + want: []string{"libc.so"}, + }, + { + libs: []string{"libc.so", "libc.so.96.1"}, + want: []string{"libc.so.96.1"}, + }, + { + libs: []string{"libc.so.96.1", "libc.so"}, + want: []string{"libc.so.96.1"}, + }, + { + libs: []string{"libc.a", "libc.so.96.1"}, + want: []string{"libc.a", "libc.so.96.1"}, + }, + { + libs: []string{"libpthread.so", "libc.so"}, + want: []string{"libc.so", "libpthread.so"}, + }, + { + libs: []string{"libpthread.so.26.1", "libpthread.so", "libc.so.96.1", "libc.so"}, + want: []string{"libc.so.96.1", "libpthread.so.26.1"}, + }, + { + libs: []string{"libpthread.so.26.1", "libpthread.so", "libc.so.96.1", "libc.so", "libfoo.so"}, + want: []string{"libc.so.96.1", "libfoo.so", "libpthread.so.26.1"}, + }, + } + + for _, test := range tests { + t.Run("dedup", func(t *testing.T) { + got := dedupLibraries(ctxt, test.libs) + if !reflect.DeepEqual(got, test.want) { + t.Errorf("dedupLibraries(%v) = %v, want %v", test.libs, got, test.want) + } + }) + } +} + +func TestDedupLibrariesOpenBSDLink(t *testing.T) { + // The behavior we're checking for is of interest only on OpenBSD. + if runtime.GOOS != "openbsd" { + t.Skip("test only useful on openbsd") + } + + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + t.Parallel() + + dir := t.TempDir() + + // cgo_import_dynamic both the unversioned libraries and pull in the + // net package to get a cgo package with a versioned library. + srcFile := filepath.Join(dir, "x.go") + src := `package main + +import ( + _ "net" +) + +//go:cgo_import_dynamic _ _ "libc.so" + +func main() {}` + if err := os.WriteFile(srcFile, []byte(src), 0644); err != nil { + t.Fatal(err) + } + + exe := filepath.Join(dir, "deduped.exe") + out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, srcFile).CombinedOutput() + if err != nil { + t.Fatalf("build failure: %s\n%s\n", err, string(out)) + } + + // Result should be runnable. + if _, err = testenv.Command(t, exe).CombinedOutput(); err != nil { + t.Fatal(err) + } +} diff --git a/src/cmd/link/internal/ld/heap.go b/src/cmd/link/internal/ld/heap.go new file mode 100644 index 0000000..ea2d772 --- /dev/null +++ b/src/cmd/link/internal/ld/heap.go @@ -0,0 +1,54 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import "cmd/link/internal/loader" + +// Min-heap implementation, for the deadcode pass. +// Specialized for loader.Sym elements. + +type heap []loader.Sym + +func (h *heap) push(s loader.Sym) { + *h = append(*h, s) + // sift up + n := len(*h) - 1 + for n > 0 { + p := (n - 1) / 2 // parent + if (*h)[p] <= (*h)[n] { + break + } + (*h)[n], (*h)[p] = (*h)[p], (*h)[n] + n = p + } +} + +func (h *heap) pop() loader.Sym { + r := (*h)[0] + n := len(*h) - 1 + (*h)[0] = (*h)[n] + *h = (*h)[:n] + + // sift down + i := 0 + for { + c := 2*i + 1 // left child + if c >= n { + break + } + if c1 := c + 1; c1 < n && (*h)[c1] < (*h)[c] { + c = c1 // right child + } + if (*h)[i] <= (*h)[c] { + break + } + (*h)[i], (*h)[c] = (*h)[c], (*h)[i] + i = c + } + + return r +} + +func (h *heap) empty() bool { return len(*h) == 0 } diff --git a/src/cmd/link/internal/ld/heap_test.go b/src/cmd/link/internal/ld/heap_test.go new file mode 100644 index 0000000..08c9030 --- /dev/null +++ b/src/cmd/link/internal/ld/heap_test.go @@ -0,0 +1,90 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/link/internal/loader" + "testing" +) + +func TestHeap(t *testing.T) { + tests := [][]loader.Sym{ + {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, + {100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, + {30, 50, 80, 20, 60, 70, 10, 100, 90, 40}, + } + for _, s := range tests { + h := heap{} + for _, i := range s { + h.push(i) + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + } + for j := 0; j < len(s); j++ { + x := h.pop() + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + // pop should return elements in ascending order. + if want := loader.Sym((j + 1) * 10); x != want { + t.Errorf("pop returns wrong element: want %d, got %d", want, x) + } + } + if !h.empty() { + t.Errorf("heap is not empty after all pops") + } + } + + // Also check that mixed pushes and pops work correctly. + for _, s := range tests { + h := heap{} + for i := 0; i < len(s)/2; i++ { + // two pushes, one pop + h.push(s[2*i]) + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + h.push(s[2*i+1]) + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + h.pop() + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + } + for !h.empty() { // pop remaining elements + h.pop() + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + } + } +} + +// recursively verify heap-ness, starting at element i. +func verify(h *heap, i int) bool { + n := len(*h) + c1 := 2*i + 1 // left child + c2 := 2*i + 2 // right child + if c1 < n { + if (*h)[c1] < (*h)[i] { + return false + } + if !verify(h, c1) { + return false + } + } + if c2 < n { + if (*h)[c2] < (*h)[i] { + return false + } + if !verify(h, c2) { + return false + } + } + return true +} diff --git a/src/cmd/link/internal/ld/issue33808_test.go b/src/cmd/link/internal/ld/issue33808_test.go new file mode 100644 index 0000000..43f4540 --- /dev/null +++ b/src/cmd/link/internal/ld/issue33808_test.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "internal/testenv" + "runtime" + "strings" + "testing" +) + +const prog = ` +package main + +import "log" + +func main() { + log.Fatalf("HERE") +} +` + +func TestIssue33808(t *testing.T) { + if runtime.GOOS != "darwin" { + return + } + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + t.Parallel() + + dir := t.TempDir() + + f := gobuild(t, dir, prog, "-ldflags=-linkmode=external") + f.Close() + + syms, err := f.Symbols() + if err != nil { + t.Fatalf("Error reading symbols: %v", err) + } + + name := "log.Fatalf" + for _, sym := range syms { + if strings.Contains(sym.Name, name) { + return + } + } + t.Fatalf("Didn't find %v", name) +} diff --git a/src/cmd/link/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go new file mode 100644 index 0000000..d416571 --- /dev/null +++ b/src/cmd/link/internal/ld/ld.go @@ -0,0 +1,256 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "log" + "os" + "path" + "path/filepath" + "strconv" + "strings" + + "cmd/internal/goobj" + "cmd/link/internal/loader" + "cmd/link/internal/sym" +) + +func (ctxt *Link) readImportCfg(file string) { + ctxt.PackageFile = make(map[string]string) + ctxt.PackageShlib = make(map[string]string) + data, err := os.ReadFile(file) + if err != nil { + log.Fatalf("-importcfg: %v", err) + } + + for lineNum, line := range strings.Split(string(data), "\n") { + lineNum++ // 1-based + line = strings.TrimSpace(line) + if line == "" { + continue + } + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + var verb, args string + if i := strings.Index(line, " "); i < 0 { + verb = line + } else { + verb, args = line[:i], strings.TrimSpace(line[i+1:]) + } + var before, after string + if i := strings.Index(args, "="); i >= 0 { + before, after = args[:i], args[i+1:] + } + switch verb { + default: + log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb) + case "packagefile": + if before == "" || after == "" { + log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum) + } + ctxt.PackageFile[before] = after + case "packageshlib": + if before == "" || after == "" { + log.Fatalf(`%s:%d: invalid packageshlib: syntax is "packageshlib path=filename"`, file, lineNum) + } + ctxt.PackageShlib[before] = after + case "modinfo": + s, err := strconv.Unquote(args) + if err != nil { + log.Fatalf("%s:%d: invalid modinfo: %v", file, lineNum, err) + } + addstrdata1(ctxt, "runtime.modinfo="+s) + } + } +} + +func pkgname(ctxt *Link, lib string) string { + return path.Clean(lib) +} + +func findlib(ctxt *Link, lib string) (string, bool) { + name := path.Clean(lib) + + var pname string + isshlib := false + + if ctxt.linkShared && ctxt.PackageShlib[name] != "" { + pname = ctxt.PackageShlib[name] + isshlib = true + } else if ctxt.PackageFile != nil { + pname = ctxt.PackageFile[name] + if pname == "" { + ctxt.Logf("cannot find package %s (using -importcfg)\n", name) + return "", false + } + } else { + pkg := pkgname(ctxt, lib) + + // search -L "libdir" directories + for _, dir := range ctxt.Libdir { + if ctxt.linkShared { + pname = filepath.Join(dir, pkg+".shlibname") + if _, err := os.Stat(pname); err == nil { + isshlib = true + break + } + } + pname = filepath.Join(dir, name+".a") + if _, err := os.Stat(pname); err == nil { + break + } + pname = filepath.Join(dir, name+".o") + if _, err := os.Stat(pname); err == nil { + break + } + } + pname = filepath.Clean(pname) + } + + return pname, isshlib +} + +func addlib(ctxt *Link, src, obj, lib string, fingerprint goobj.FingerprintType) *sym.Library { + pkg := pkgname(ctxt, lib) + + // already loaded? + if l := ctxt.LibraryByPkg[pkg]; l != nil && !l.Fingerprint.IsZero() { + // Normally, packages are loaded in dependency order, and if l != nil + // l is already loaded with the actual fingerprint. In shared build mode, + // however, packages may be added not in dependency order, and it is + // possible that l's fingerprint is not yet loaded -- exclude it in + // checking. + checkFingerprint(l, l.Fingerprint, src, fingerprint) + return l + } + + pname, isshlib := findlib(ctxt, lib) + + if ctxt.Debugvlog > 1 { + ctxt.Logf("addlib: %s %s pulls in %s isshlib %v\n", obj, src, pname, isshlib) + } + + if isshlib { + return addlibpath(ctxt, src, obj, "", pkg, pname, fingerprint) + } + return addlibpath(ctxt, src, obj, pname, pkg, "", fingerprint) +} + +/* + * add library to library list, return added library. + * srcref: src file referring to package + * objref: object file referring to package + * file: object file, e.g., /home/rsc/go/pkg/container/vector.a + * pkg: package import path, e.g. container/vector + * shlib: path to shared library, or .shlibname file holding path + * fingerprint: if not 0, expected fingerprint for import from srcref + * fingerprint is 0 if the library is not imported (e.g. main) + */ +func addlibpath(ctxt *Link, srcref, objref, file, pkg, shlib string, fingerprint goobj.FingerprintType) *sym.Library { + if l := ctxt.LibraryByPkg[pkg]; l != nil { + return l + } + + if ctxt.Debugvlog > 1 { + ctxt.Logf("addlibpath: srcref: %s objref: %s file: %s pkg: %s shlib: %s fingerprint: %x\n", srcref, objref, file, pkg, shlib, fingerprint) + } + + l := &sym.Library{} + ctxt.LibraryByPkg[pkg] = l + ctxt.Library = append(ctxt.Library, l) + l.Objref = objref + l.Srcref = srcref + l.File = file + l.Pkg = pkg + l.Fingerprint = fingerprint + if shlib != "" { + if strings.HasSuffix(shlib, ".shlibname") { + data, err := os.ReadFile(shlib) + if err != nil { + Errorf(nil, "cannot read %s: %v", shlib, err) + } + shlib = strings.TrimSpace(string(data)) + } + l.Shlib = shlib + } + return l +} + +func atolwhex(s string) int64 { + n, _ := strconv.ParseInt(s, 0, 64) + return n +} + +// PrepareAddmoduledata returns a symbol builder that target-specific +// code can use to build up the linker-generated go.link.addmoduledata +// function, along with the sym for runtime.addmoduledata itself. If +// this function is not needed (for example in cases where we're +// linking a module that contains the runtime) the returned builder +// will be nil. +func PrepareAddmoduledata(ctxt *Link) (*loader.SymbolBuilder, loader.Sym) { + if !ctxt.DynlinkingGo() { + return nil, 0 + } + amd := ctxt.loader.LookupOrCreateSym("runtime.addmoduledata", 0) + if ctxt.loader.SymType(amd) == sym.STEXT && ctxt.BuildMode != BuildModePlugin { + // we're linking a module containing the runtime -> no need for + // an init function + return nil, 0 + } + ctxt.loader.SetAttrReachable(amd, true) + + // Create a new init func text symbol. Caller will populate this + // sym with arch-specific content. + ifs := ctxt.loader.LookupOrCreateSym("go:link.addmoduledata", 0) + initfunc := ctxt.loader.MakeSymbolUpdater(ifs) + ctxt.loader.SetAttrReachable(ifs, true) + ctxt.loader.SetAttrLocal(ifs, true) + initfunc.SetType(sym.STEXT) + + // Add the init func and/or addmoduledata to Textp. + if ctxt.BuildMode == BuildModePlugin { + ctxt.Textp = append(ctxt.Textp, amd) + } + ctxt.Textp = append(ctxt.Textp, initfunc.Sym()) + + // Create an init array entry + amdi := ctxt.loader.LookupOrCreateSym("go:link.addmoduledatainit", 0) + initarray_entry := ctxt.loader.MakeSymbolUpdater(amdi) + ctxt.loader.SetAttrReachable(amdi, true) + ctxt.loader.SetAttrLocal(amdi, true) + initarray_entry.SetType(sym.SINITARR) + initarray_entry.AddAddr(ctxt.Arch, ifs) + + return initfunc, amd +} diff --git a/src/cmd/link/internal/ld/ld_test.go b/src/cmd/link/internal/ld/ld_test.go new file mode 100644 index 0000000..4d32dd6 --- /dev/null +++ b/src/cmd/link/internal/ld/ld_test.go @@ -0,0 +1,343 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "debug/pe" + "fmt" + "internal/testenv" + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestUndefinedRelocErrors(t *testing.T) { + testenv.MustHaveGoBuild(t) + + // When external linking, symbols may be defined externally, so we allow + // undefined symbols and let external linker resolve. Skip the test. + testenv.MustInternalLink(t) + + t.Parallel() + + out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "./testdata/issue10978").CombinedOutput() + if err == nil { + t.Fatal("expected build to fail") + } + + wantErrors := map[string]int{ + // Main function has dedicated error message. + "function main is undeclared in the main package": 1, + + // Single error reporting per each symbol. + // This way, duplicated messages are not reported for + // multiple relocations with a same name. + "main.defined1: relocation target main.undefined not defined": 1, + "main.defined2: relocation target main.undefined not defined": 1, + } + unexpectedErrors := map[string]int{} + + for _, l := range strings.Split(string(out), "\n") { + if strings.HasPrefix(l, "#") || l == "" { + continue + } + matched := "" + for want := range wantErrors { + if strings.Contains(l, want) { + matched = want + break + } + } + if matched != "" { + wantErrors[matched]-- + } else { + unexpectedErrors[l]++ + } + } + + for want, n := range wantErrors { + switch { + case n > 0: + t.Errorf("unmatched error: %s (x%d)", want, n) + case n < 0: + t.Errorf("extra errors: %s (x%d)", want, -n) + } + } + for unexpected, n := range unexpectedErrors { + t.Errorf("unexpected error: %s (x%d)", unexpected, n) + } +} + +const carchiveSrcText = ` +package main + +//export GoFunc +func GoFunc() { + println(42) +} + +func main() { +} +` + +func TestArchiveBuildInvokeWithExec(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + // run this test on just a small set of platforms (no need to test it + // across the board given the nature of the test). + pair := runtime.GOOS + "-" + runtime.GOARCH + switch pair { + case "darwin-amd64", "darwin-arm64", "linux-amd64", "freebsd-amd64": + default: + t.Skip("no need for test on " + pair) + } + switch runtime.GOOS { + case "openbsd", "windows": + t.Skip("c-archive unsupported") + } + dir := t.TempDir() + + srcfile := filepath.Join(dir, "test.go") + arfile := filepath.Join(dir, "test.a") + if err := os.WriteFile(srcfile, []byte(carchiveSrcText), 0666); err != nil { + t.Fatal(err) + } + + ldf := fmt.Sprintf("-ldflags=-v -tmpdir=%s", dir) + argv := []string{"build", "-buildmode=c-archive", "-o", arfile, ldf, srcfile} + out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput() + if err != nil { + t.Fatalf("build failure: %s\n%s\n", err, string(out)) + } + + found := false + const want = "invoking archiver with syscall.Exec" + for _, l := range strings.Split(string(out), "\n") { + if strings.HasPrefix(l, want) { + found = true + break + } + } + + if !found { + t.Errorf("expected '%s' in -v output, got:\n%s\n", want, string(out)) + } +} + +func TestLargeTextSectionSplitting(t *testing.T) { + switch runtime.GOARCH { + case "ppc64", "ppc64le", "arm": + case "arm64": + if runtime.GOOS == "darwin" { + break + } + fallthrough + default: + t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH) + } + + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + t.Parallel() + dir := t.TempDir() + + // NB: the use of -ldflags=-debugtextsize=1048576 tells the linker to + // split text sections at a size threshold of 1M instead of the + // architected limit of 67M or larger. The choice of building cmd/go + // is arbitrary; we just need something sufficiently large that uses + // external linking. + exe := filepath.Join(dir, "go.exe") + out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, "-ldflags=-linkmode=external -debugtextsize=1048576", "cmd/go").CombinedOutput() + if err != nil { + t.Fatalf("build failure: %s\n%s\n", err, string(out)) + } + + // Check that we did split text sections. + out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput() + if err != nil { + t.Fatalf("nm failure: %s\n%s\n", err, string(out)) + } + if !bytes.Contains(out, []byte("runtime.text.1")) { + t.Errorf("runtime.text.1 not found, text section not split?") + } + + // Result should be runnable. + _, err = testenv.Command(t, exe, "version").CombinedOutput() + if err != nil { + t.Fatal(err) + } +} + +func TestWindowsBuildmodeCSharedASLR(t *testing.T) { + platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) + switch platform { + case "windows/amd64", "windows/386": + default: + t.Skip("skipping windows amd64/386 only test") + } + + testenv.MustHaveCGO(t) + + t.Run("aslr", func(t *testing.T) { + testWindowsBuildmodeCSharedASLR(t, true) + }) + t.Run("no-aslr", func(t *testing.T) { + testWindowsBuildmodeCSharedASLR(t, false) + }) +} + +func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) { + t.Parallel() + testenv.MustHaveGoBuild(t) + + dir := t.TempDir() + + srcfile := filepath.Join(dir, "test.go") + objfile := filepath.Join(dir, "test.dll") + if err := os.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil { + t.Fatal(err) + } + argv := []string{"build", "-buildmode=c-shared"} + if !useASLR { + argv = append(argv, "-ldflags", "-aslr=false") + } + argv = append(argv, "-o", objfile, srcfile) + out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput() + if err != nil { + t.Fatalf("build failure: %s\n%s\n", err, string(out)) + } + + f, err := pe.Open(objfile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + var dc uint16 + switch oh := f.OptionalHeader.(type) { + case *pe.OptionalHeader32: + dc = oh.DllCharacteristics + case *pe.OptionalHeader64: + dc = oh.DllCharacteristics + hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0 + if useASLR && !hasHEVA { + t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set") + } else if !useASLR && hasHEVA { + t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set") + } + default: + t.Fatalf("unexpected optional header type of %T", f.OptionalHeader) + } + hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0 + if useASLR && !hasASLR { + t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set") + } else if !useASLR && hasASLR { + t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set") + } +} + +// TestMemProfileCheck tests that cmd/link sets +// runtime.disableMemoryProfiling if the runtime.MemProfile +// symbol is unreachable after deadcode (and not dynlinking). +// The runtime then uses that to set the default value of +// runtime.MemProfileRate, which this test checks. +func TestMemProfileCheck(t *testing.T) { + testenv.MustHaveGoBuild(t) + t.Parallel() + + tests := []struct { + name string + prog string + wantOut string + }{ + { + "no_memprofile", + ` +package main +import "runtime" +func main() { + println(runtime.MemProfileRate) +} +`, + "0", + }, + { + "with_memprofile", + ` +package main +import "runtime" +func main() { + runtime.MemProfile(nil, false) + println(runtime.MemProfileRate) +} +`, + "524288", + }, + { + "with_memprofile_indirect", + ` +package main +import "runtime" +var f = runtime.MemProfile +func main() { + if f == nil { + panic("no f") + } + println(runtime.MemProfileRate) +} +`, + "524288", + }, + { + "with_memprofile_runtime_pprof", + ` +package main +import "runtime" +import "runtime/pprof" +func main() { + _ = pprof.Profiles() + println(runtime.MemProfileRate) +} +`, + "524288", + }, + { + "with_memprofile_http_pprof", + ` +package main +import "runtime" +import _ "net/http/pprof" +func main() { + println(runtime.MemProfileRate) +} +`, + "524288", + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + tempDir := t.TempDir() + src := filepath.Join(tempDir, "x.go") + if err := os.WriteFile(src, []byte(tt.prog), 0644); err != nil { + t.Fatal(err) + } + cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + got := strings.TrimSpace(string(out)) + if got != tt.wantOut { + t.Errorf("got %q; want %q", got, tt.wantOut) + } + }) + } +} diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go new file mode 100644 index 0000000..6eae900 --- /dev/null +++ b/src/cmd/link/internal/ld/lib.go @@ -0,0 +1,2696 @@ +// Inferno utils/8l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bytes" + "debug/elf" + "debug/macho" + "encoding/base64" + "encoding/binary" + "fmt" + "internal/buildcfg" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "sync" + + "cmd/internal/bio" + "cmd/internal/goobj" + "cmd/internal/notsha256" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loadelf" + "cmd/link/internal/loader" + "cmd/link/internal/loadmacho" + "cmd/link/internal/loadpe" + "cmd/link/internal/loadxcoff" + "cmd/link/internal/sym" +) + +// Data layout and relocation. + +// Derived from Inferno utils/6l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// ArchSyms holds a number of architecture specific symbols used during +// relocation. Rather than allowing them universal access to all symbols, +// we keep a subset for relocation application. +type ArchSyms struct { + Rel loader.Sym + Rela loader.Sym + RelPLT loader.Sym + RelaPLT loader.Sym + + LinkEditGOT loader.Sym + LinkEditPLT loader.Sym + + TOC loader.Sym + DotTOC []loader.Sym // for each version + + GOT loader.Sym + PLT loader.Sym + GOTPLT loader.Sym + + Tlsg loader.Sym + Tlsoffset int + + Dynamic loader.Sym + DynSym loader.Sym + DynStr loader.Sym + + unreachableMethod loader.Sym +} + +// mkArchSym is a helper for setArchSyms, to set up a special symbol. +func (ctxt *Link) mkArchSym(name string, ver int, ls *loader.Sym) { + *ls = ctxt.loader.LookupOrCreateSym(name, ver) + ctxt.loader.SetAttrReachable(*ls, true) +} + +// mkArchVecSym is similar to setArchSyms, but operates on elements within +// a slice, where each element corresponds to some symbol version. +func (ctxt *Link) mkArchSymVec(name string, ver int, ls []loader.Sym) { + ls[ver] = ctxt.loader.LookupOrCreateSym(name, ver) + ctxt.loader.SetAttrReachable(ls[ver], true) +} + +// setArchSyms sets up the ArchSyms structure, and must be called before +// relocations are applied. +func (ctxt *Link) setArchSyms() { + ctxt.mkArchSym(".got", 0, &ctxt.GOT) + ctxt.mkArchSym(".plt", 0, &ctxt.PLT) + ctxt.mkArchSym(".got.plt", 0, &ctxt.GOTPLT) + ctxt.mkArchSym(".dynamic", 0, &ctxt.Dynamic) + ctxt.mkArchSym(".dynsym", 0, &ctxt.DynSym) + ctxt.mkArchSym(".dynstr", 0, &ctxt.DynStr) + ctxt.mkArchSym("runtime.unreachableMethod", abiInternalVer, &ctxt.unreachableMethod) + + if ctxt.IsPPC64() { + ctxt.mkArchSym("TOC", 0, &ctxt.TOC) + + ctxt.DotTOC = make([]loader.Sym, ctxt.MaxVersion()+1) + for i := 0; i <= ctxt.MaxVersion(); i++ { + if i >= sym.SymVerABICount && i < sym.SymVerStatic { // these versions are not used currently + continue + } + ctxt.mkArchSymVec(".TOC.", i, ctxt.DotTOC) + } + } + if ctxt.IsElf() { + ctxt.mkArchSym(".rel", 0, &ctxt.Rel) + ctxt.mkArchSym(".rela", 0, &ctxt.Rela) + ctxt.mkArchSym(".rel.plt", 0, &ctxt.RelPLT) + ctxt.mkArchSym(".rela.plt", 0, &ctxt.RelaPLT) + } + if ctxt.IsDarwin() { + ctxt.mkArchSym(".linkedit.got", 0, &ctxt.LinkEditGOT) + ctxt.mkArchSym(".linkedit.plt", 0, &ctxt.LinkEditPLT) + } +} + +type Arch struct { + Funcalign int + Maxalign int + Minalign int + Dwarfregsp int + Dwarfreglr int + + // Threshold of total text size, used for trampoline insertion. If the total + // text size is smaller than TrampLimit, we won't need to insert trampolines. + // It is pretty close to the offset range of a direct CALL machine instruction. + // We leave some room for extra stuff like PLT stubs. + TrampLimit uint64 + + Androiddynld string + Linuxdynld string + LinuxdynldMusl string + Freebsddynld string + Netbsddynld string + Openbsddynld string + Dragonflydynld string + Solarisdynld string + + // Empty spaces between codeblocks will be padded with this value. + // For example an architecture might want to pad with a trap instruction to + // catch wayward programs. Architectures that do not define a padding value + // are padded with zeros. + CodePad []byte + + // Plan 9 variables. + Plan9Magic uint32 + Plan9_64Bit bool + + Adddynrel func(*Target, *loader.Loader, *ArchSyms, loader.Sym, loader.Reloc, int) bool + Archinit func(*Link) + // Archreloc is an arch-specific hook that assists in relocation processing + // (invoked by 'relocsym'); it handles target-specific relocation tasks. + // Here "rel" is the current relocation being examined, "sym" is the symbol + // containing the chunk of data to which the relocation applies, and "off" + // is the contents of the to-be-relocated data item (from sym.P). Return + // value is the appropriately relocated value (to be written back to the + // same spot in sym.P), number of external _host_ relocations needed (i.e. + // ELF/Mach-O/etc. relocations, not Go relocations, this must match Elfreloc1, + // etc.), and a boolean indicating success/failure (a failing value indicates + // a fatal error). + Archreloc func(*Target, *loader.Loader, *ArchSyms, loader.Reloc, loader.Sym, + int64) (relocatedOffset int64, nExtReloc int, ok bool) + // Archrelocvariant is a second arch-specific hook used for + // relocation processing; it handles relocations where r.Type is + // insufficient to describe the relocation (r.Variant != + // sym.RV_NONE). Here "rel" is the relocation being applied, "sym" + // is the symbol containing the chunk of data to which the + // relocation applies, and "off" is the contents of the + // to-be-relocated data item (from sym.P). Return is an updated + // offset value. + Archrelocvariant func(target *Target, ldr *loader.Loader, rel loader.Reloc, + rv sym.RelocVariant, sym loader.Sym, offset int64, data []byte) (relocatedOffset int64) + + // Generate a trampoline for a call from s to rs if necessary. ri is + // index of the relocation. + Trampoline func(ctxt *Link, ldr *loader.Loader, ri int, rs, s loader.Sym) + + // Assembling the binary breaks into two phases, writing the code/data/ + // dwarf information (which is rather generic), and some more architecture + // specific work like setting up the elf headers/dynamic relocations, etc. + // The phases are called "Asmb" and "Asmb2". Asmb2 needs to be defined for + // every architecture, but only if architecture has an Asmb function will + // it be used for assembly. Otherwise a generic assembly Asmb function is + // used. + Asmb func(*Link, *loader.Loader) + Asmb2 func(*Link, *loader.Loader) + + // Extreloc is an arch-specific hook that converts a Go relocation to an + // external relocation. Return the external relocation and whether it is + // needed. + Extreloc func(*Target, *loader.Loader, loader.Reloc, loader.Sym) (loader.ExtReloc, bool) + + Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool + ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1. + Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) + Gentext func(*Link, *loader.Loader) // Generate text before addressing has been performed. + Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool + MachorelocSize uint32 // size of an Mach-O relocation record, must match Machoreloc1. + PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool + Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool + + // Generate additional symbols for the native symbol table just prior to + // code generation. + GenSymsLate func(*Link, *loader.Loader) + + // TLSIEtoLE converts a TLS Initial Executable relocation to + // a TLS Local Executable relocation. + // + // This is possible when a TLS IE relocation refers to a local + // symbol in an executable, which is typical when internally + // linking PIE binaries. + TLSIEtoLE func(P []byte, off, size int) + + // optional override for assignAddress + AssignAddress func(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) +} + +var ( + thearch Arch + lcSize int32 + rpath Rpath + spSize int32 + symSize int32 +) + +const ( + MINFUNC = 16 // minimum size for a function +) + +// Symbol version of ABIInternal symbols. It is sym.SymVerABIInternal if ABI wrappers +// are used, 0 otherwise. +var abiInternalVer = sym.SymVerABIInternal + +// DynlinkingGo reports whether we are producing Go code that can live +// in separate shared libraries linked together at runtime. +func (ctxt *Link) DynlinkingGo() bool { + if !ctxt.Loaded { + panic("DynlinkingGo called before all symbols loaded") + } + return ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin || ctxt.canUsePlugins +} + +// CanUsePlugins reports whether a plugins can be used +func (ctxt *Link) CanUsePlugins() bool { + if !ctxt.Loaded { + panic("CanUsePlugins called before all symbols loaded") + } + return ctxt.canUsePlugins +} + +// NeedCodeSign reports whether we need to code-sign the output binary. +func (ctxt *Link) NeedCodeSign() bool { + return ctxt.IsDarwin() && ctxt.IsARM64() +} + +var ( + dynlib []string + ldflag []string + havedynamic int + Funcalign int + iscgo bool + elfglobalsymndx int + interpreter string + + debug_s bool // backup old value of debug['s'] + HEADR int32 + + nerrors int + liveness int64 // size of liveness data (funcdata), printed if -v + + // See -strictdups command line flag. + checkStrictDups int // 0=off 1=warning 2=error + strictDupMsgCount int +) + +var ( + Segtext sym.Segment + Segrodata sym.Segment + Segrelrodata sym.Segment + Segdata sym.Segment + Segdwarf sym.Segment + + Segments = []*sym.Segment{&Segtext, &Segrodata, &Segrelrodata, &Segdata, &Segdwarf} +) + +const pkgdef = "__.PKGDEF" + +var ( + // externalobj is set to true if we see an object compiled by + // the host compiler that is not from a package that is known + // to support internal linking mode. + externalobj = false + + // dynimportfail is a list of packages for which generating + // the dynimport file, _cgo_import.go, failed. If there are + // any of these objects, we must link externally. Issue 52863. + dynimportfail []string + + // preferlinkext is a list of packages for which the Go command + // noticed use of peculiar C flags. If we see any of these, + // default to linking externally unless overridden by the + // user. See issues #58619, #58620, and #58848. + preferlinkext []string + + // unknownObjFormat is set to true if we see an object whose + // format we don't recognize. + unknownObjFormat = false + + theline string +) + +func Lflag(ctxt *Link, arg string) { + ctxt.Libdir = append(ctxt.Libdir, arg) +} + +/* + * Unix doesn't like it when we write to a running (or, sometimes, + * recently run) binary, so remove the output file before writing it. + * On Windows 7, remove() can force a subsequent create() to fail. + * S_ISREG() does not exist on Plan 9. + */ +func mayberemoveoutfile() { + if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() { + return + } + os.Remove(*flagOutfile) +} + +func libinit(ctxt *Link) { + Funcalign = thearch.Funcalign + + // add goroot to the end of the libdir list. + suffix := "" + + suffixsep := "" + if *flagInstallSuffix != "" { + suffixsep = "_" + suffix = *flagInstallSuffix + } else if *flagRace { + suffixsep = "_" + suffix = "race" + } else if *flagMsan { + suffixsep = "_" + suffix = "msan" + } else if *flagAsan { + suffixsep = "_" + suffix = "asan" + } + + if buildcfg.GOROOT != "" { + Lflag(ctxt, filepath.Join(buildcfg.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", buildcfg.GOOS, buildcfg.GOARCH, suffixsep, suffix))) + } + + mayberemoveoutfile() + + if err := ctxt.Out.Open(*flagOutfile); err != nil { + Exitf("cannot create %s: %v", *flagOutfile, err) + } + + if *flagEntrySymbol == "" { + switch ctxt.BuildMode { + case BuildModeCShared, BuildModeCArchive: + *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", buildcfg.GOARCH, buildcfg.GOOS) + case BuildModeExe, BuildModePIE: + *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", buildcfg.GOARCH, buildcfg.GOOS) + case BuildModeShared, BuildModePlugin: + // No *flagEntrySymbol for -buildmode=shared and plugin + default: + Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode) + } + } +} + +func exitIfErrors() { + if nerrors != 0 || checkStrictDups > 1 && strictDupMsgCount > 0 { + mayberemoveoutfile() + Exit(2) + } + +} + +func errorexit() { + exitIfErrors() + Exit(0) +} + +func loadinternal(ctxt *Link, name string) *sym.Library { + zerofp := goobj.FingerprintType{} + if ctxt.linkShared && ctxt.PackageShlib != nil { + if shlib := ctxt.PackageShlib[name]; shlib != "" { + return addlibpath(ctxt, "internal", "internal", "", name, shlib, zerofp) + } + } + if ctxt.PackageFile != nil { + if pname := ctxt.PackageFile[name]; pname != "" { + return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp) + } + ctxt.Logf("loadinternal: cannot find %s\n", name) + return nil + } + + for _, libdir := range ctxt.Libdir { + if ctxt.linkShared { + shlibname := filepath.Join(libdir, name+".shlibname") + if ctxt.Debugvlog != 0 { + ctxt.Logf("searching for %s.a in %s\n", name, shlibname) + } + if _, err := os.Stat(shlibname); err == nil { + return addlibpath(ctxt, "internal", "internal", "", name, shlibname, zerofp) + } + } + pname := filepath.Join(libdir, name+".a") + if ctxt.Debugvlog != 0 { + ctxt.Logf("searching for %s.a in %s\n", name, pname) + } + if _, err := os.Stat(pname); err == nil { + return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp) + } + } + + if name == "runtime" { + Exitf("error: unable to find runtime.a") + } + ctxt.Logf("warning: unable to find %s.a\n", name) + return nil +} + +// extld returns the current external linker. +func (ctxt *Link) extld() []string { + if len(flagExtld) == 0 { + // Return the default external linker for the platform. + // This only matters when link tool is called directly without explicit -extld, + // go tool already passes the correct linker in other cases. + switch buildcfg.GOOS { + case "darwin", "freebsd", "openbsd": + flagExtld = []string{"clang"} + default: + flagExtld = []string{"gcc"} + } + } + return flagExtld +} + +// findLibPathCmd uses cmd command to find gcc library libname. +// It returns library full path if found, or "none" if not found. +func (ctxt *Link) findLibPathCmd(cmd, libname string) string { + extld := ctxt.extld() + name, args := extld[0], extld[1:] + args = append(args, hostlinkArchArgs(ctxt.Arch)...) + args = append(args, cmd) + if ctxt.Debugvlog != 0 { + ctxt.Logf("%s %v\n", extld, args) + } + out, err := exec.Command(name, args...).Output() + if err != nil { + if ctxt.Debugvlog != 0 { + ctxt.Logf("not using a %s file because compiler failed\n%v\n%s\n", libname, err, out) + } + return "none" + } + return strings.TrimSpace(string(out)) +} + +// findLibPath searches for library libname. +// It returns library full path if found, or "none" if not found. +func (ctxt *Link) findLibPath(libname string) string { + return ctxt.findLibPathCmd("--print-file-name="+libname, libname) +} + +func (ctxt *Link) loadlib() { + var flags uint32 + switch *FlagStrictDups { + case 0: + // nothing to do + case 1, 2: + flags |= loader.FlagStrictDups + default: + log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) + } + elfsetstring1 := func(str string, off int) { elfsetstring(ctxt, 0, str, off) } + ctxt.loader = loader.NewLoader(flags, elfsetstring1, &ctxt.ErrorReporter.ErrorReporter) + ctxt.ErrorReporter.SymName = func(s loader.Sym) string { + return ctxt.loader.SymName(s) + } + + // ctxt.Library grows during the loop, so not a range loop. + i := 0 + for ; i < len(ctxt.Library); i++ { + lib := ctxt.Library[i] + if lib.Shlib == "" { + if ctxt.Debugvlog > 1 { + ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref) + } + loadobjfile(ctxt, lib) + } + } + + // load internal packages, if not already + if *flagRace { + loadinternal(ctxt, "runtime/race") + } + if *flagMsan { + loadinternal(ctxt, "runtime/msan") + } + if *flagAsan { + loadinternal(ctxt, "runtime/asan") + } + loadinternal(ctxt, "runtime") + for ; i < len(ctxt.Library); i++ { + lib := ctxt.Library[i] + if lib.Shlib == "" { + loadobjfile(ctxt, lib) + } + } + // At this point, the Go objects are "preloaded". Not all the symbols are + // added to the symbol table (only defined package symbols are). Looking + // up symbol by name may not get expected result. + + iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil + + // Plugins a require cgo support to function. Similarly, plugins may require additional + // internal linker support on some platforms which may not be implemented. + ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != nil && iscgo + + // We now have enough information to determine the link mode. + determineLinkMode(ctxt) + + if ctxt.LinkMode == LinkExternal && !iscgo && !(buildcfg.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && ctxt.Arch.Family == sys.AMD64) { + // This indicates a user requested -linkmode=external. + // The startup code uses an import of runtime/cgo to decide + // whether to initialize the TLS. So give it one. This could + // be handled differently but it's an unusual case. + if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" { + if ctxt.BuildMode == BuildModeShared || ctxt.linkShared { + Exitf("cannot implicitly include runtime/cgo in a shared library") + } + for ; i < len(ctxt.Library); i++ { + lib := ctxt.Library[i] + if lib.Shlib == "" { + loadobjfile(ctxt, lib) + } + } + } + } + + // Add non-package symbols and references of externally defined symbols. + ctxt.loader.LoadSyms(ctxt.Arch) + + // Load symbols from shared libraries, after all Go object symbols are loaded. + for _, lib := range ctxt.Library { + if lib.Shlib != "" { + if ctxt.Debugvlog > 1 { + ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref) + } + ldshlibsyms(ctxt, lib.Shlib) + } + } + + // Process cgo directives (has to be done before host object loading). + ctxt.loadcgodirectives() + + // Conditionally load host objects, or setup for external linking. + hostobjs(ctxt) + hostlinksetup(ctxt) + + if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 { + // If we have any undefined symbols in external + // objects, try to read them from the libgcc file. + any := false + undefs, froms := ctxt.loader.UndefinedRelocTargets(1) + if len(undefs) > 0 { + any = true + if ctxt.Debugvlog > 1 { + ctxt.Logf("loadlib: first unresolved is %s [%d] from %s [%d]\n", + ctxt.loader.SymName(undefs[0]), undefs[0], + ctxt.loader.SymName(froms[0]), froms[0]) + } + } + if any { + if *flagLibGCC == "" { + *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc") + } + if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" { + // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a". + // In this case we fail to load libgcc.a and can encounter link + // errors - see if we can find libcompiler_rt.a instead. + *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") + } + if ctxt.HeadType == objabi.Hwindows { + loadWindowsHostArchives(ctxt) + } + if *flagLibGCC != "none" { + hostArchive(ctxt, *flagLibGCC) + } + } + } + + // We've loaded all the code now. + ctxt.Loaded = true + + importcycles() + + strictDupMsgCount = ctxt.loader.NStrictDupMsgs() +} + +// loadWindowsHostArchives loads in host archives and objects when +// doing internal linking on windows. Older toolchains seem to require +// just a single pass through the various archives, but some modern +// toolchains when linking a C program with mingw pass library paths +// multiple times to the linker, e.g. "... -lmingwex -lmingw32 ... +// -lmingwex -lmingw32 ...". To accommodate this behavior, we make two +// passes over the host archives below. +func loadWindowsHostArchives(ctxt *Link) { + any := true + for i := 0; any && i < 2; i++ { + // Link crt2.o (if present) to resolve "atexit" when + // using LLVM-based compilers. + isunresolved := symbolsAreUnresolved(ctxt, []string{"atexit"}) + if isunresolved[0] { + if p := ctxt.findLibPath("crt2.o"); p != "none" { + hostObject(ctxt, "crt2", p) + } + } + if *flagRace { + if p := ctxt.findLibPath("libsynchronization.a"); p != "none" { + hostArchive(ctxt, p) + } + } + if p := ctxt.findLibPath("libmingwex.a"); p != "none" { + hostArchive(ctxt, p) + } + if p := ctxt.findLibPath("libmingw32.a"); p != "none" { + hostArchive(ctxt, p) + } + // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol + // (see https://golang.org/issue/23649 for details). + if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" { + hostArchive(ctxt, p) + } + any = false + undefs, froms := ctxt.loader.UndefinedRelocTargets(1) + if len(undefs) > 0 { + any = true + if ctxt.Debugvlog > 1 { + ctxt.Logf("loadWindowsHostArchives: remaining unresolved is %s [%d] from %s [%d]\n", + ctxt.loader.SymName(undefs[0]), undefs[0], + ctxt.loader.SymName(froms[0]), froms[0]) + } + } + } + // If needed, create the __CTOR_LIST__ and __DTOR_LIST__ + // symbols (referenced by some of the mingw support library + // routines). Creation of these symbols is normally done by the + // linker if not already present. + want := []string{"__CTOR_LIST__", "__DTOR_LIST__"} + isunresolved := symbolsAreUnresolved(ctxt, want) + for k, w := range want { + if isunresolved[k] { + sb := ctxt.loader.CreateSymForUpdate(w, 0) + sb.SetType(sym.SDATA) + sb.AddUint64(ctxt.Arch, 0) + sb.SetReachable(true) + ctxt.loader.SetAttrSpecial(sb.Sym(), true) + } + } + + // Fix up references to DLL import symbols now that we're done + // pulling in new objects. + if err := loadpe.PostProcessImports(); err != nil { + Errorf(nil, "%v", err) + } + + // TODO: maybe do something similar to peimporteddlls to collect + // all lib names and try link them all to final exe just like + // libmingwex.a and libmingw32.a: + /* + for: + #cgo windows LDFLAGS: -lmsvcrt -lm + import: + libmsvcrt.a libm.a + */ +} + +// loadcgodirectives reads the previously discovered cgo directives, creating +// symbols in preparation for host object loading or use later in the link. +func (ctxt *Link) loadcgodirectives() { + l := ctxt.loader + hostObjSyms := make(map[loader.Sym]struct{}) + for _, d := range ctxt.cgodata { + setCgoAttr(ctxt, d.file, d.pkg, d.directives, hostObjSyms) + } + ctxt.cgodata = nil + + if ctxt.LinkMode == LinkInternal { + // Drop all the cgo_import_static declarations. + // Turns out we won't be needing them. + for symIdx := range hostObjSyms { + if l.SymType(symIdx) == sym.SHOSTOBJ { + // If a symbol was marked both + // cgo_import_static and cgo_import_dynamic, + // then we want to make it cgo_import_dynamic + // now. + su := l.MakeSymbolUpdater(symIdx) + if l.SymExtname(symIdx) != "" && l.SymDynimplib(symIdx) != "" && !(l.AttrCgoExportStatic(symIdx) || l.AttrCgoExportDynamic(symIdx)) { + su.SetType(sym.SDYNIMPORT) + } else { + su.SetType(0) + } + } + } + } +} + +// Set up flags and special symbols depending on the platform build mode. +// This version works with loader.Loader. +func (ctxt *Link) linksetup() { + switch ctxt.BuildMode { + case BuildModeCShared, BuildModePlugin: + symIdx := ctxt.loader.LookupOrCreateSym("runtime.islibrary", 0) + sb := ctxt.loader.MakeSymbolUpdater(symIdx) + sb.SetType(sym.SNOPTRDATA) + sb.AddUint8(1) + case BuildModeCArchive: + symIdx := ctxt.loader.LookupOrCreateSym("runtime.isarchive", 0) + sb := ctxt.loader.MakeSymbolUpdater(symIdx) + sb.SetType(sym.SNOPTRDATA) + sb.AddUint8(1) + } + + // Recalculate pe parameters now that we have ctxt.LinkMode set. + if ctxt.HeadType == objabi.Hwindows { + Peinit(ctxt) + } + + if ctxt.LinkMode == LinkExternal { + // When external linking, we are creating an object file. The + // absolute address is irrelevant. + *FlagTextAddr = 0 + } + + // If there are no dynamic libraries needed, gcc disables dynamic linking. + // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) + // assumes that a dynamic binary always refers to at least one dynamic library. + // Rather than be a source of test cases for glibc, disable dynamic linking + // the same way that gcc would. + // + // Exception: on OS X, programs such as Shark only work with dynamic + // binaries, so leave it enabled on OS X (Mach-O) binaries. + // Also leave it enabled on Solaris which doesn't support + // statically linked binaries. + if ctxt.BuildMode == BuildModeExe { + if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris { + *FlagD = true + } + } + + if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && buildcfg.GOOS != "aix" { + toc := ctxt.loader.LookupOrCreateSym(".TOC.", 0) + sb := ctxt.loader.MakeSymbolUpdater(toc) + sb.SetType(sym.SDYNIMPORT) + } + + // The Android Q linker started to complain about underalignment of the our TLS + // section. We don't actually use the section on android, so don't + // generate it. + if buildcfg.GOOS != "android" { + tlsg := ctxt.loader.LookupOrCreateSym("runtime.tlsg", 0) + sb := ctxt.loader.MakeSymbolUpdater(tlsg) + + // runtime.tlsg is used for external linking on platforms that do not define + // a variable to hold g in assembly (currently only intel). + if sb.Type() == 0 { + sb.SetType(sym.STLSBSS) + sb.SetSize(int64(ctxt.Arch.PtrSize)) + } else if sb.Type() != sym.SDYNIMPORT { + Errorf(nil, "runtime declared tlsg variable %v", sb.Type()) + } + ctxt.loader.SetAttrReachable(tlsg, true) + ctxt.Tlsg = tlsg + } + + var moduledata loader.Sym + var mdsb *loader.SymbolBuilder + if ctxt.BuildMode == BuildModePlugin { + moduledata = ctxt.loader.LookupOrCreateSym("local.pluginmoduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) + ctxt.loader.SetAttrLocal(moduledata, true) + } else { + moduledata = ctxt.loader.LookupOrCreateSym("runtime.firstmoduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) + } + if mdsb.Type() != 0 && mdsb.Type() != sym.SDYNIMPORT { + // If the module (toolchain-speak for "executable or shared + // library") we are linking contains the runtime package, it + // will define the runtime.firstmoduledata symbol and we + // truncate it back to 0 bytes so we can define its entire + // contents in symtab.go:symtab(). + mdsb.SetSize(0) + + // In addition, on ARM, the runtime depends on the linker + // recording the value of GOARM. + if ctxt.Arch.Family == sys.ARM { + goarm := ctxt.loader.LookupOrCreateSym("runtime.goarm", 0) + sb := ctxt.loader.MakeSymbolUpdater(goarm) + sb.SetType(sym.SDATA) + sb.SetSize(0) + sb.AddUint8(uint8(buildcfg.GOARM)) + } + + // Set runtime.disableMemoryProfiling bool if + // runtime.MemProfile is not retained in the binary after + // deadcode (and we're not dynamically linking). + memProfile := ctxt.loader.Lookup("runtime.MemProfile", abiInternalVer) + if memProfile != 0 && !ctxt.loader.AttrReachable(memProfile) && !ctxt.DynlinkingGo() { + memProfSym := ctxt.loader.LookupOrCreateSym("runtime.disableMemoryProfiling", 0) + sb := ctxt.loader.MakeSymbolUpdater(memProfSym) + sb.SetType(sym.SDATA) + sb.SetSize(0) + sb.AddUint8(1) // true bool + } + } else { + // If OTOH the module does not contain the runtime package, + // create a local symbol for the moduledata. + moduledata = ctxt.loader.LookupOrCreateSym("local.moduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) + ctxt.loader.SetAttrLocal(moduledata, true) + } + // In all cases way we mark the moduledata as noptrdata to hide it from + // the GC. + mdsb.SetType(sym.SNOPTRDATA) + ctxt.loader.SetAttrReachable(moduledata, true) + ctxt.Moduledata = moduledata + + if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows { + if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() { + got := ctxt.loader.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0) + sb := ctxt.loader.MakeSymbolUpdater(got) + sb.SetType(sym.SDYNIMPORT) + ctxt.loader.SetAttrReachable(got, true) + } + } + + // DWARF-gen and other phases require that the unit Textp slices + // be populated, so that it can walk the functions in each unit. + // Call into the loader to do this (requires that we collect the + // set of internal libraries first). NB: might be simpler if we + // moved isRuntimeDepPkg to cmd/internal and then did the test in + // loader.AssignTextSymbolOrder. + ctxt.Library = postorder(ctxt.Library) + intlibs := []bool{} + for _, lib := range ctxt.Library { + intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg)) + } + ctxt.Textp = ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs, ctxt.Textp) +} + +// mangleTypeSym shortens the names of symbols that represent Go types +// if they are visible in the symbol table. +// +// As the names of these symbols are derived from the string of +// the type, they can run to many kilobytes long. So we shorten +// them using a SHA-1 when the name appears in the final binary. +// This also removes characters that upset external linkers. +// +// These are the symbols that begin with the prefix 'type.' and +// contain run-time type information used by the runtime and reflect +// packages. All Go binaries contain these symbols, but only +// those programs loaded dynamically in multiple parts need these +// symbols to have entries in the symbol table. +func (ctxt *Link) mangleTypeSym() { + if ctxt.BuildMode != BuildModeShared && !ctxt.linkShared && ctxt.BuildMode != BuildModePlugin && !ctxt.CanUsePlugins() { + return + } + + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) && !ctxt.linkShared { + // If -linkshared, the GCProg generation code may need to reach + // out to the shared library for the type descriptor's data, even + // the type descriptor itself is not actually needed at run time + // (therefore not reachable). We still need to mangle its name, + // so it is consistent with the one stored in the shared library. + continue + } + name := ldr.SymName(s) + newName := typeSymbolMangle(name) + if newName != name { + ldr.SetSymExtname(s, newName) + + // When linking against a shared library, the Go object file may + // have reference to the original symbol name whereas the shared + // library provides a symbol with the mangled name. We need to + // copy the payload of mangled to original. + // XXX maybe there is a better way to do this. + dup := ldr.Lookup(newName, ldr.SymVersion(s)) + if dup != 0 { + st := ldr.SymType(s) + dt := ldr.SymType(dup) + if st == sym.Sxxx && dt != sym.Sxxx { + ldr.CopySym(dup, s) + } + } + } + } +} + +// typeSymbolMangle mangles the given symbol name into something shorter. +// +// Keep the type:. prefix, which parts of the linker (like the +// DWARF generator) know means the symbol is not decodable. +// Leave type:runtime. symbols alone, because other parts of +// the linker manipulates them. +func typeSymbolMangle(name string) string { + if !strings.HasPrefix(name, "type:") { + return name + } + if strings.HasPrefix(name, "type:runtime.") { + return name + } + if len(name) <= 14 && !strings.Contains(name, "@") { // Issue 19529 + return name + } + hash := notsha256.Sum256([]byte(name)) + prefix := "type:" + if name[5] == '.' { + prefix = "type:." + } + return prefix + base64.StdEncoding.EncodeToString(hash[:6]) +} + +/* + * look for the next file in an archive. + * adapted from libmach. + */ +func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 { + if off&1 != 0 { + off++ + } + bp.MustSeek(off, 0) + var buf [SAR_HDR]byte + if n, err := io.ReadFull(bp, buf[:]); err != nil { + if n == 0 && err != io.EOF { + return -1 + } + return 0 + } + + a.name = artrim(buf[0:16]) + a.date = artrim(buf[16:28]) + a.uid = artrim(buf[28:34]) + a.gid = artrim(buf[34:40]) + a.mode = artrim(buf[40:48]) + a.size = artrim(buf[48:58]) + a.fmag = artrim(buf[58:60]) + + arsize := atolwhex(a.size) + if arsize&1 != 0 { + arsize++ + } + return arsize + SAR_HDR +} + +func loadobjfile(ctxt *Link, lib *sym.Library) { + pkg := objabi.PathToPrefix(lib.Pkg) + + if ctxt.Debugvlog > 1 { + ctxt.Logf("ldobj: %s (%s)\n", lib.File, pkg) + } + f, err := bio.Open(lib.File) + if err != nil { + Exitf("cannot open file %s: %v", lib.File, err) + } + defer f.Close() + defer func() { + if pkg == "main" && !lib.Main { + Exitf("%s: not package main", lib.File) + } + }() + + for i := 0; i < len(ARMAG); i++ { + if c, err := f.ReadByte(); err == nil && c == ARMAG[i] { + continue + } + + /* load it as a regular file */ + l := f.MustSeek(0, 2) + f.MustSeek(0, 0) + ldobj(ctxt, f, lib, l, lib.File, lib.File) + return + } + + /* + * load all the object files from the archive now. + * this gives us sequential file access and keeps us + * from needing to come back later to pick up more + * objects. it breaks the usual C archive model, but + * this is Go, not C. the common case in Go is that + * we need to load all the objects, and then we throw away + * the individual symbols that are unused. + * + * loading every object will also make it possible to + * load foreign objects not referenced by __.PKGDEF. + */ + var arhdr ArHdr + off := f.Offset() + for { + l := nextar(f, off, &arhdr) + if l == 0 { + break + } + if l < 0 { + Exitf("%s: malformed archive", lib.File) + } + off += l + + // __.PKGDEF isn't a real Go object file, and it's + // absent in -linkobj builds anyway. Skipping it + // ensures consistency between -linkobj and normal + // build modes. + if arhdr.name == pkgdef { + continue + } + + if arhdr.name == "dynimportfail" { + dynimportfail = append(dynimportfail, lib.Pkg) + } + if arhdr.name == "preferlinkext" { + // Ignore this directive if -linkmode has been + // set explicitly. + if ctxt.LinkMode == LinkAuto { + preferlinkext = append(preferlinkext, lib.Pkg) + } + } + + // Skip other special (non-object-file) sections that + // build tools may have added. Such sections must have + // short names so that the suffix is not truncated. + if len(arhdr.name) < 16 { + if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" { + continue + } + } + + pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name) + l = atolwhex(arhdr.size) + ldobj(ctxt, f, lib, l, pname, lib.File) + } +} + +type Hostobj struct { + ld func(*Link, *bio.Reader, string, int64, string) + pkg string + pn string + file string + off int64 + length int64 +} + +var hostobj []Hostobj + +// These packages can use internal linking mode. +// Others trigger external mode. +var internalpkg = []string{ + "crypto/internal/boring", + "crypto/internal/boring/syso", + "crypto/x509", + "net", + "os/user", + "runtime/cgo", + "runtime/race", + "runtime/race/internal/amd64v1", + "runtime/race/internal/amd64v3", + "runtime/msan", + "runtime/asan", +} + +func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj { + isinternal := false + for _, intpkg := range internalpkg { + if pkg == intpkg { + isinternal = true + break + } + } + + // DragonFly declares errno with __thread, which results in a symbol + // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not + // currently know how to handle TLS relocations, hence we have to + // force external linking for any libraries that link in code that + // uses errno. This can be removed if the Go linker ever supports + // these relocation types. + if headType == objabi.Hdragonfly { + if pkg == "net" || pkg == "os/user" { + isinternal = false + } + } + + if !isinternal { + externalobj = true + } + + hostobj = append(hostobj, Hostobj{}) + h := &hostobj[len(hostobj)-1] + h.ld = ld + h.pkg = pkg + h.pn = pn + h.file = file + h.off = f.Offset() + h.length = length + return h +} + +func hostobjs(ctxt *Link) { + if ctxt.LinkMode != LinkInternal { + return + } + var h *Hostobj + + for i := 0; i < len(hostobj); i++ { + h = &hostobj[i] + f, err := bio.Open(h.file) + if err != nil { + Exitf("cannot reopen %s: %v", h.pn, err) + } + f.MustSeek(h.off, 0) + if h.ld == nil { + Errorf(nil, "%s: unrecognized object file format", h.pn) + continue + } + h.ld(ctxt, f, h.pkg, h.length, h.pn) + if *flagCaptureHostObjs != "" { + captureHostObj(h) + } + f.Close() + } +} + +func hostlinksetup(ctxt *Link) { + if ctxt.LinkMode != LinkExternal { + return + } + + // For external link, record that we need to tell the external linker -s, + // and turn off -s internally: the external linker needs the symbol + // information for its final link. + debug_s = *FlagS + *FlagS = false + + // create temporary directory and arrange cleanup + if *flagTmpdir == "" { + dir, err := os.MkdirTemp("", "go-link-") + if err != nil { + log.Fatal(err) + } + *flagTmpdir = dir + ownTmpDir = true + AtExit(func() { + os.RemoveAll(*flagTmpdir) + }) + } + + // change our output to temporary object file + if err := ctxt.Out.Close(); err != nil { + Exitf("error closing output file") + } + mayberemoveoutfile() + + p := filepath.Join(*flagTmpdir, "go.o") + if err := ctxt.Out.Open(p); err != nil { + Exitf("cannot create %s: %v", p, err) + } +} + +// hostobjCopy creates a copy of the object files in hostobj in a +// temporary directory. +func hostobjCopy() (paths []string) { + var wg sync.WaitGroup + sema := make(chan struct{}, runtime.NumCPU()) // limit open file descriptors + for i, h := range hostobj { + h := h + dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i)) + paths = append(paths, dst) + + wg.Add(1) + go func() { + sema <- struct{}{} + defer func() { + <-sema + wg.Done() + }() + f, err := os.Open(h.file) + if err != nil { + Exitf("cannot reopen %s: %v", h.pn, err) + } + defer f.Close() + if _, err := f.Seek(h.off, 0); err != nil { + Exitf("cannot seek %s: %v", h.pn, err) + } + + w, err := os.Create(dst) + if err != nil { + Exitf("cannot create %s: %v", dst, err) + } + if _, err := io.CopyN(w, f, h.length); err != nil { + Exitf("cannot write %s: %v", dst, err) + } + if err := w.Close(); err != nil { + Exitf("cannot close %s: %v", dst, err) + } + }() + } + wg.Wait() + return paths +} + +// writeGDBLinkerScript creates gcc linker script file in temp +// directory. writeGDBLinkerScript returns created file path. +// The script is used to work around gcc bug +// (see https://golang.org/issue/20183 for details). +func writeGDBLinkerScript() string { + name := "fix_debug_gdb_scripts.ld" + path := filepath.Join(*flagTmpdir, name) + src := `SECTIONS +{ + .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : + { + *(.debug_gdb_scripts) + } +} +INSERT AFTER .debug_types; +` + err := os.WriteFile(path, []byte(src), 0666) + if err != nil { + Errorf(nil, "WriteFile %s failed: %v", name, err) + } + return path +} + +// archive builds a .a archive from the hostobj object files. +func (ctxt *Link) archive() { + if ctxt.BuildMode != BuildModeCArchive { + return + } + + exitIfErrors() + + if *flagExtar == "" { + *flagExtar = "ar" + } + + mayberemoveoutfile() + + // Force the buffer to flush here so that external + // tools will see a complete file. + if err := ctxt.Out.Close(); err != nil { + Exitf("error closing %v", *flagOutfile) + } + + argv := []string{*flagExtar, "-q", "-c", "-s"} + if ctxt.HeadType == objabi.Haix { + argv = append(argv, "-X64") + } + argv = append(argv, *flagOutfile) + argv = append(argv, filepath.Join(*flagTmpdir, "go.o")) + argv = append(argv, hostobjCopy()...) + + if ctxt.Debugvlog != 0 { + ctxt.Logf("archive: %s\n", strings.Join(argv, " ")) + } + + // If supported, use syscall.Exec() to invoke the archive command, + // which should be the final remaining step needed for the link. + // This will reduce peak RSS for the link (and speed up linking of + // large applications), since when the archive command runs we + // won't be holding onto all of the linker's live memory. + if syscallExecSupported && !ownTmpDir { + runAtExitFuncs() + ctxt.execArchive(argv) + panic("should not get here") + } + + // Otherwise invoke 'ar' in the usual way (fork + exec). + if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { + Exitf("running %s failed: %v\n%s", argv[0], err, out) + } +} + +func (ctxt *Link) hostlink() { + if ctxt.LinkMode != LinkExternal || nerrors > 0 { + return + } + if ctxt.BuildMode == BuildModeCArchive { + return + } + + var argv []string + argv = append(argv, ctxt.extld()...) + argv = append(argv, hostlinkArchArgs(ctxt.Arch)...) + + if *FlagS || debug_s { + if ctxt.HeadType == objabi.Hdarwin { + // Recent versions of macOS print + // ld: warning: option -s is obsolete and being ignored + // so do not pass any arguments. + } else { + argv = append(argv, "-s") + } + } + + // On darwin, whether to combine DWARF into executable. + // Only macOS supports unmapped segments such as our __DWARF segment. + combineDwarf := ctxt.IsDarwin() && !*FlagS && !*FlagW && !debug_s && machoPlatform == PLATFORM_MACOS + + switch ctxt.HeadType { + case objabi.Hdarwin: + if linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,-ld_classic") { + // Force old linker to work around bugs in Apple's new linker. + argv = append(argv, "-Wl,-ld_classic") + } + if combineDwarf { + // Leave room for DWARF combining. + // -headerpad is incompatible with -fembed-bitcode. + argv = append(argv, "-Wl,-headerpad,1144") + } + if ctxt.DynlinkingGo() && buildcfg.GOOS != "ios" { + // -flat_namespace is deprecated on iOS. + // It is useful for supporting plugins. We don't support plugins on iOS. + // -flat_namespace may cause the dynamic linker to hang at forkExec when + // resolving a lazy binding. See issue 38824. + // Force eager resolution to work around. + argv = append(argv, "-Wl,-flat_namespace", "-Wl,-bind_at_load") + } + if !combineDwarf { + argv = append(argv, "-Wl,-S") // suppress STAB (symbolic debugging) symbols + } + case objabi.Hopenbsd: + argv = append(argv, "-Wl,-nopie") + argv = append(argv, "-pthread") + case objabi.Hwindows: + if windowsgui { + argv = append(argv, "-mwindows") + } else { + argv = append(argv, "-mconsole") + } + // Mark as having awareness of terminal services, to avoid + // ancient compatibility hacks. + argv = append(argv, "-Wl,--tsaware") + + // Enable DEP + argv = append(argv, "-Wl,--nxcompat") + + argv = append(argv, fmt.Sprintf("-Wl,--major-os-version=%d", PeMinimumTargetMajorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--minor-os-version=%d", PeMinimumTargetMinorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--major-subsystem-version=%d", PeMinimumTargetMajorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--minor-subsystem-version=%d", PeMinimumTargetMinorVersion)) + case objabi.Haix: + argv = append(argv, "-pthread") + // prevent ld to reorder .text functions to keep the same + // first/last functions for moduledata. + argv = append(argv, "-Wl,-bnoobjreorder") + // mcmodel=large is needed for every gcc generated files, but + // ld still need -bbigtoc in order to allow larger TOC. + argv = append(argv, "-mcmodel=large") + argv = append(argv, "-Wl,-bbigtoc") + } + + // Enable/disable ASLR on Windows. + addASLRargs := func(argv []string, val bool) []string { + // Old/ancient versions of GCC support "--dynamicbase" and + // "--high-entropy-va" but don't enable it by default. In + // addition, they don't accept "--disable-dynamicbase" or + // "--no-dynamicbase", so the only way to disable ASLR is to + // not pass any flags at all. + // + // More modern versions of GCC (and also clang) enable ASLR + // by default. With these compilers, however you can turn it + // off if you want using "--disable-dynamicbase" or + // "--no-dynamicbase". + // + // The strategy below is to try using "--disable-dynamicbase"; + // if this succeeds, then assume we're working with more + // modern compilers and act accordingly. If it fails, assume + // an ancient compiler with ancient defaults. + var dbopt string + var heopt string + dbon := "--dynamicbase" + heon := "--high-entropy-va" + dboff := "--disable-dynamicbase" + heoff := "--disable-high-entropy-va" + if val { + dbopt = dbon + heopt = heon + } else { + // Test to see whether "--disable-dynamicbase" works. + newer := linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,"+dboff) + if newer { + // Newer compiler, which supports both on/off options. + dbopt = dboff + heopt = heoff + } else { + // older toolchain: we have to say nothing in order to + // get a no-ASLR binary. + dbopt = "" + heopt = "" + } + } + if dbopt != "" { + argv = append(argv, "-Wl,"+dbopt) + } + // enable high-entropy ASLR on 64-bit. + if ctxt.Arch.PtrSize >= 8 && heopt != "" { + argv = append(argv, "-Wl,"+heopt) + } + return argv + } + + switch ctxt.BuildMode { + case BuildModeExe: + if ctxt.HeadType == objabi.Hdarwin { + if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() { + argv = append(argv, "-Wl,-no_pie") + } + } + if *flagRace && ctxt.HeadType == objabi.Hwindows { + // Current windows/amd64 race detector tsan support + // library can't handle PIE mode (see #53539 for more details). + // For now, explicitly disable PIE (since some compilers + // default to it) if -race is in effect. + argv = addASLRargs(argv, false) + } + case BuildModePIE: + switch ctxt.HeadType { + case objabi.Hdarwin, objabi.Haix: + case objabi.Hwindows: + if *flagAslr && *flagRace { + // Current windows/amd64 race detector tsan support + // library can't handle PIE mode (see #53539 for more details). + // Disable alsr if -race in effect. + *flagAslr = false + } + argv = addASLRargs(argv, *flagAslr) + default: + // ELF. + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-pie") + } + case BuildModeCShared: + if ctxt.HeadType == objabi.Hdarwin { + argv = append(argv, "-dynamiclib") + } else { + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + if ctxt.HeadType == objabi.Hwindows { + argv = addASLRargs(argv, *flagAslr) + } else { + // Pass -z nodelete to mark the shared library as + // non-closeable: a dlclose will do nothing. + argv = append(argv, "-Wl,-z,nodelete") + // Only pass Bsymbolic on non-Windows. + argv = append(argv, "-Wl,-Bsymbolic") + } + } + case BuildModeShared: + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + case BuildModePlugin: + if ctxt.HeadType == objabi.Hdarwin { + argv = append(argv, "-dynamiclib") + } else { + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + } + } + + var altLinker string + if ctxt.IsELF && ctxt.DynlinkingGo() { + // We force all symbol resolution to be done at program startup + // because lazy PLT resolution can use large amounts of stack at + // times we cannot allow it to do so. + argv = append(argv, "-Wl,-z,now") + + // Do not let the host linker generate COPY relocations. These + // can move symbols out of sections that rely on stable offsets + // from the beginning of the section (like sym.STYPE). + argv = append(argv, "-Wl,-z,nocopyreloc") + + if buildcfg.GOOS == "android" { + // Use lld to avoid errors from default linker (issue #38838) + altLinker = "lld" + } + + if ctxt.Arch.InFamily(sys.ARM, sys.ARM64) && buildcfg.GOOS == "linux" { + // On ARM, the GNU linker will generate COPY relocations + // even with -znocopyreloc set. + // https://sourceware.org/bugzilla/show_bug.cgi?id=19962 + // + // On ARM64, the GNU linker will fail instead of + // generating COPY relocations. + // + // In both cases, switch to gold. + altLinker = "gold" + + // If gold is not installed, gcc will silently switch + // back to ld.bfd. So we parse the version information + // and provide a useful error if gold is missing. + name, args := flagExtld[0], flagExtld[1:] + args = append(args, "-fuse-ld=gold", "-Wl,--version") + cmd := exec.Command(name, args...) + if out, err := cmd.CombinedOutput(); err == nil { + if !bytes.Contains(out, []byte("GNU gold")) { + log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out) + } + } + } + } + if ctxt.Arch.Family == sys.ARM64 && buildcfg.GOOS == "freebsd" { + // Switch to ld.bfd on freebsd/arm64. + altLinker = "bfd" + + // Provide a useful error if ld.bfd is missing. + name, args := flagExtld[0], flagExtld[1:] + args = append(args, "-fuse-ld=bfd", "-Wl,--version") + cmd := exec.Command(name, args...) + if out, err := cmd.CombinedOutput(); err == nil { + if !bytes.Contains(out, []byte("GNU ld")) { + log.Fatalf("ARM64 external linker must be ld.bfd (issue #35197), please install devel/binutils") + } + } + } + if altLinker != "" { + argv = append(argv, "-fuse-ld="+altLinker) + } + + if ctxt.IsELF && len(buildinfo) > 0 { + argv = append(argv, fmt.Sprintf("-Wl,--build-id=0x%x", buildinfo)) + } + + // On Windows, given -o foo, GCC will append ".exe" to produce + // "foo.exe". We have decided that we want to honor the -o + // option. To make this work, we append a '.' so that GCC + // will decide that the file already has an extension. We + // only want to do this when producing a Windows output file + // on a Windows host. + outopt := *flagOutfile + if buildcfg.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" { + outopt += "." + } + argv = append(argv, "-o") + argv = append(argv, outopt) + + if rpath.val != "" { + argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val)) + } + + if *flagInterpreter != "" { + // Many linkers support both -I and the --dynamic-linker flags + // to set the ELF interpreter, but lld only supports + // --dynamic-linker so prefer that (ld on very old Solaris only + // supports -I but that seems less important). + argv = append(argv, fmt.Sprintf("-Wl,--dynamic-linker,%s", *flagInterpreter)) + } + + // Force global symbols to be exported for dlopen, etc. + if ctxt.IsELF { + argv = append(argv, "-rdynamic") + } + if ctxt.HeadType == objabi.Haix { + fileName := xcoffCreateExportFile(ctxt) + argv = append(argv, "-Wl,-bE:"+fileName) + } + + const unusedArguments = "-Qunused-arguments" + if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, unusedArguments) { + argv = append(argv, unusedArguments) + } + + if ctxt.IsWindows() { + // Suppress generation of the PE file header timestamp, + // so as to avoid spurious build ID differences between + // linked binaries that are otherwise identical other than + // the date/time they were linked. + const noTimeStamp = "-Wl,--no-insert-timestamp" + if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, noTimeStamp) { + argv = append(argv, noTimeStamp) + } + } + + const compressDWARF = "-Wl,--compress-debug-sections=zlib" + if ctxt.compressDWARF && linkerFlagSupported(ctxt.Arch, argv[0], altLinker, compressDWARF) { + argv = append(argv, compressDWARF) + } + + argv = append(argv, filepath.Join(*flagTmpdir, "go.o")) + argv = append(argv, hostobjCopy()...) + if ctxt.HeadType == objabi.Haix { + // We want to have C files after Go files to remove + // trampolines csects made by ld. + argv = append(argv, "-nostartfiles") + argv = append(argv, "/lib/crt0_64.o") + + extld := ctxt.extld() + name, args := extld[0], extld[1:] + // Get starting files. + getPathFile := func(file string) string { + args := append(args, "-maix64", "--print-file-name="+file) + out, err := exec.Command(name, args...).CombinedOutput() + if err != nil { + log.Fatalf("running %s failed: %v\n%s", extld, err, out) + } + return strings.Trim(string(out), "\n") + } + // Since GCC version 11, the 64-bit version of GCC starting files + // are now suffixed by "_64". Even under "-maix64" multilib directory + // "crtcxa.o" is 32-bit. + crtcxa := getPathFile("crtcxa_64.o") + if !filepath.IsAbs(crtcxa) { + crtcxa = getPathFile("crtcxa.o") + } + crtdbase := getPathFile("crtdbase_64.o") + if !filepath.IsAbs(crtdbase) { + crtdbase = getPathFile("crtdbase.o") + } + argv = append(argv, crtcxa) + argv = append(argv, crtdbase) + } + + if ctxt.linkShared { + seenDirs := make(map[string]bool) + seenLibs := make(map[string]bool) + addshlib := func(path string) { + dir, base := filepath.Split(path) + if !seenDirs[dir] { + argv = append(argv, "-L"+dir) + if !rpath.set { + argv = append(argv, "-Wl,-rpath="+dir) + } + seenDirs[dir] = true + } + base = strings.TrimSuffix(base, ".so") + base = strings.TrimPrefix(base, "lib") + if !seenLibs[base] { + argv = append(argv, "-l"+base) + seenLibs[base] = true + } + } + for _, shlib := range ctxt.Shlibs { + addshlib(shlib.Path) + for _, dep := range shlib.Deps { + if dep == "" { + continue + } + libpath := findshlib(ctxt, dep) + if libpath != "" { + addshlib(libpath) + } + } + } + } + + // clang, unlike GCC, passes -rdynamic to the linker + // even when linking with -static, causing a linker + // error when using GNU ld. So take out -rdynamic if + // we added it. We do it in this order, rather than + // only adding -rdynamic later, so that -extldflags + // can override -rdynamic without using -static. + // Similarly for -Wl,--dynamic-linker. + checkStatic := func(arg string) { + if ctxt.IsELF && arg == "-static" { + for i := range argv { + if argv[i] == "-rdynamic" || strings.HasPrefix(argv[i], "-Wl,--dynamic-linker,") { + argv[i] = "-static" + } + } + } + } + + for _, p := range ldflag { + argv = append(argv, p) + checkStatic(p) + } + + // When building a program with the default -buildmode=exe the + // gc compiler generates code requires DT_TEXTREL in a + // position independent executable (PIE). On systems where the + // toolchain creates PIEs by default, and where DT_TEXTREL + // does not work, the resulting programs will not run. See + // issue #17847. To avoid this problem pass -no-pie to the + // toolchain if it is supported. + if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared && !(ctxt.IsDarwin() && ctxt.IsARM64()) { + // GCC uses -no-pie, clang uses -nopie. + for _, nopie := range []string{"-no-pie", "-nopie"} { + if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, nopie) { + argv = append(argv, nopie) + break + } + } + } + + for _, p := range flagExtldflags { + argv = append(argv, p) + checkStatic(p) + } + if ctxt.HeadType == objabi.Hwindows { + // Determine which linker we're using. Add in the extldflags in + // case used has specified "-fuse-ld=...". + extld := ctxt.extld() + name, args := extld[0], extld[1:] + args = append(args, flagExtldflags...) + args = append(args, "-Wl,--version") + cmd := exec.Command(name, args...) + usingLLD := false + if out, err := cmd.CombinedOutput(); err == nil { + if bytes.Contains(out, []byte("LLD ")) { + usingLLD = true + } + } + + // use gcc linker script to work around gcc bug + // (see https://golang.org/issue/20183 for details). + if !usingLLD { + p := writeGDBLinkerScript() + argv = append(argv, "-Wl,-T,"+p) + } + if *flagRace { + if p := ctxt.findLibPath("libsynchronization.a"); p != "libsynchronization.a" { + argv = append(argv, "-lsynchronization") + } + } + // libmingw32 and libmingwex have some inter-dependencies, + // so must use linker groups. + argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group") + argv = append(argv, peimporteddlls()...) + } + + if ctxt.Debugvlog != 0 { + ctxt.Logf("host link:") + for _, v := range argv { + ctxt.Logf(" %q", v) + } + ctxt.Logf("\n") + } + + out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput() + if err != nil { + Exitf("running %s failed: %v\n%s", argv[0], err, out) + } + + // Filter out useless linker warnings caused by bugs outside Go. + // See also cmd/go/internal/work/exec.go's gccld method. + var save [][]byte + var skipLines int + for _, line := range bytes.SplitAfter(out, []byte("\n")) { + // golang.org/issue/26073 - Apple Xcode bug + if bytes.Contains(line, []byte("ld: warning: text-based stub file")) { + continue + } + + if skipLines > 0 { + skipLines-- + continue + } + + // Remove TOC overflow warning on AIX. + if bytes.Contains(line, []byte("ld: 0711-783")) { + skipLines = 2 + continue + } + + save = append(save, line) + } + out = bytes.Join(save, nil) + + if len(out) > 0 { + // always print external output even if the command is successful, so that we don't + // swallow linker warnings (see https://golang.org/issue/17935). + if ctxt.IsDarwin() && ctxt.IsAMD64() { + const noPieWarning = "ld: warning: -no_pie is deprecated when targeting new OS versions\n" + if i := bytes.Index(out, []byte(noPieWarning)); i >= 0 { + // swallow -no_pie deprecation warning, issue 54482 + out = append(out[:i], out[i+len(noPieWarning):]...) + } + } + if ctxt.IsDarwin() { + const bindAtLoadWarning = "ld: warning: -bind_at_load is deprecated on macOS\n" + if i := bytes.Index(out, []byte(bindAtLoadWarning)); i >= 0 { + // -bind_at_load is deprecated with ld-prime, but needed for + // correctness with older versions of ld64. Swallow the warning. + // TODO: maybe pass -bind_at_load conditionally based on C + // linker version. + out = append(out[:i], out[i+len(bindAtLoadWarning):]...) + } + } + ctxt.Logf("%s", out) + } + + if combineDwarf { + // Find "dsymutils" and "strip" tools using CC --print-prog-name. + var cc []string + cc = append(cc, ctxt.extld()...) + cc = append(cc, hostlinkArchArgs(ctxt.Arch)...) + cc = append(cc, "--print-prog-name", "dsymutil") + out, err := exec.Command(cc[0], cc[1:]...).CombinedOutput() + if err != nil { + Exitf("%s: finding dsymutil failed: %v\n%s", os.Args[0], err, out) + } + dsymutilCmd := strings.TrimSuffix(string(out), "\n") + + cc[len(cc)-1] = "strip" + out, err = exec.Command(cc[0], cc[1:]...).CombinedOutput() + if err != nil { + Exitf("%s: finding strip failed: %v\n%s", os.Args[0], err, out) + } + stripCmd := strings.TrimSuffix(string(out), "\n") + + dsym := filepath.Join(*flagTmpdir, "go.dwarf") + if out, err := exec.Command(dsymutilCmd, "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil { + Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out) + } + // Remove STAB (symbolic debugging) symbols after we are done with them (by dsymutil). + // They contain temporary file paths and make the build not reproducible. + if out, err := exec.Command(stripCmd, "-S", *flagOutfile).CombinedOutput(); err != nil { + Exitf("%s: running strip failed: %v\n%s", os.Args[0], err, out) + } + // Skip combining if `dsymutil` didn't generate a file. See #11994. + if _, err := os.Stat(dsym); os.IsNotExist(err) { + return + } + // For os.Rename to work reliably, must be in same directory as outfile. + combinedOutput := *flagOutfile + "~" + exef, err := os.Open(*flagOutfile) + if err != nil { + Exitf("%s: combining dwarf failed: %v", os.Args[0], err) + } + defer exef.Close() + exem, err := macho.NewFile(exef) + if err != nil { + Exitf("%s: parsing Mach-O header failed: %v", os.Args[0], err) + } + if err := machoCombineDwarf(ctxt, exef, exem, dsym, combinedOutput); err != nil { + Exitf("%s: combining dwarf failed: %v", os.Args[0], err) + } + os.Remove(*flagOutfile) + if err := os.Rename(combinedOutput, *flagOutfile); err != nil { + Exitf("%s: %v", os.Args[0], err) + } + } + if ctxt.NeedCodeSign() { + err := machoCodeSign(ctxt, *flagOutfile) + if err != nil { + Exitf("%s: code signing failed: %v", os.Args[0], err) + } + } +} + +var createTrivialCOnce sync.Once + +func linkerFlagSupported(arch *sys.Arch, linker, altLinker, flag string) bool { + createTrivialCOnce.Do(func() { + src := filepath.Join(*flagTmpdir, "trivial.c") + if err := os.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil { + Errorf(nil, "WriteFile trivial.c failed: %v", err) + } + }) + + flagsWithNextArgSkip := []string{ + "-F", + "-l", + "-L", + "-framework", + "-Wl,-framework", + "-Wl,-rpath", + "-Wl,-undefined", + } + flagsWithNextArgKeep := []string{ + "-arch", + "-isysroot", + "--sysroot", + "-target", + } + prefixesToKeep := []string{ + "-f", + "-m", + "-p", + "-Wl,", + "-arch", + "-isysroot", + "--sysroot", + "-target", + } + + flags := hostlinkArchArgs(arch) + keep := false + skip := false + for _, f := range append(flagExtldflags, ldflag...) { + if keep { + flags = append(flags, f) + keep = false + } else if skip { + skip = false + } else if f == "" || f[0] != '-' { + } else if contains(flagsWithNextArgSkip, f) { + skip = true + } else if contains(flagsWithNextArgKeep, f) { + flags = append(flags, f) + keep = true + } else { + for _, p := range prefixesToKeep { + if strings.HasPrefix(f, p) { + flags = append(flags, f) + break + } + } + } + } + + if altLinker != "" { + flags = append(flags, "-fuse-ld="+altLinker) + } + flags = append(flags, flag, "trivial.c") + + cmd := exec.Command(linker, flags...) + cmd.Dir = *flagTmpdir + cmd.Env = append([]string{"LC_ALL=C"}, os.Environ()...) + out, err := cmd.CombinedOutput() + // GCC says "unrecognized command line option ‘-no-pie’" + // clang says "unknown argument: '-no-pie'" + return err == nil && !bytes.Contains(out, []byte("unrecognized")) && !bytes.Contains(out, []byte("unknown")) +} + +// hostlinkArchArgs returns arguments to pass to the external linker +// based on the architecture. +func hostlinkArchArgs(arch *sys.Arch) []string { + switch arch.Family { + case sys.I386: + return []string{"-m32"} + case sys.AMD64: + if buildcfg.GOOS == "darwin" { + return []string{"-arch", "x86_64", "-m64"} + } + return []string{"-m64"} + case sys.S390X: + return []string{"-m64"} + case sys.ARM: + return []string{"-marm"} + case sys.ARM64: + if buildcfg.GOOS == "darwin" { + return []string{"-arch", "arm64"} + } + case sys.Loong64: + return []string{"-mabi=lp64d"} + case sys.MIPS64: + return []string{"-mabi=64"} + case sys.MIPS: + return []string{"-mabi=32"} + case sys.PPC64: + if buildcfg.GOOS == "aix" { + return []string{"-maix64"} + } else { + return []string{"-m64"} + } + + } + return nil +} + +var wantHdr = objabi.HeaderString() + +// ldobj loads an input object. If it is a host object (an object +// compiled by a non-Go compiler) it returns the Hostobj pointer. If +// it is a Go object, it returns nil. +func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj { + pkg := objabi.PathToPrefix(lib.Pkg) + + eof := f.Offset() + length + start := f.Offset() + c1 := bgetc(f) + c2 := bgetc(f) + c3 := bgetc(f) + c4 := bgetc(f) + f.MustSeek(start, 0) + + unit := &sym.CompilationUnit{Lib: lib} + lib.Units = append(lib.Units, unit) + + magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) + if magic == 0x7f454c46 { // \x7F E L F + ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn, ehdr.Flags) + if err != nil { + Errorf(nil, "%v", err) + return + } + ehdr.Flags = flags + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) + } + + if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { + ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) + } + + switch c1<<8 | c2 { + case 0x4c01, // 386 + 0x6486, // amd64 + 0xc401, // arm + 0x64aa: // arm64 + ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + if len(rsrc) != 0 { + setpersrc(ctxt, rsrc) + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) + } + + if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) { + ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) + } + + if c1 != 'g' || c2 != 'o' || c3 != ' ' || c4 != 'o' { + // An unrecognized object is just passed to the external linker. + // If we try to read symbols from this object, we will + // report an error at that time. + unknownObjFormat = true + return ldhostobj(nil, ctxt.HeadType, f, pkg, length, pn, file) + } + + /* check the header */ + line, err := f.ReadString('\n') + if err != nil { + Errorf(nil, "truncated object file: %s: %v", pn, err) + return nil + } + + if !strings.HasPrefix(line, "go object ") { + if strings.HasSuffix(pn, ".go") { + Exitf("%s: uncompiled .go source file", pn) + return nil + } + + if line == ctxt.Arch.Name { + // old header format: just $GOOS + Errorf(nil, "%s: stale object file", pn) + return nil + } + + Errorf(nil, "%s: not an object file: @%d %q", pn, start, line) + return nil + } + + // First, check that the basic GOOS, GOARCH, and Version match. + if line != wantHdr { + Errorf(nil, "%s: linked object header mismatch:\nhave %q\nwant %q\n", pn, line, wantHdr) + } + + // Skip over exports and other info -- ends with \n!\n. + // + // Note: It's possible for "\n!\n" to appear within the binary + // package export data format. To avoid truncating the package + // definition prematurely (issue 21703), we keep track of + // how many "$$" delimiters we've seen. + + import0 := f.Offset() + + c1 = '\n' // the last line ended in \n + c2 = bgetc(f) + c3 = bgetc(f) + markers := 0 + for { + if c1 == '\n' { + if markers%2 == 0 && c2 == '!' && c3 == '\n' { + break + } + if c2 == '$' && c3 == '$' { + markers++ + } + } + + c1 = c2 + c2 = c3 + c3 = bgetc(f) + if c3 == -1 { + Errorf(nil, "truncated object file: %s", pn) + return nil + } + } + + import1 := f.Offset() + + f.MustSeek(import0, 0) + ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n + f.MustSeek(import1, 0) + + fingerprint := ctxt.loader.Preload(ctxt.IncVersion(), f, lib, unit, eof-f.Offset()) + if !fingerprint.IsZero() { // Assembly objects don't have fingerprints. Ignore them. + // Check fingerprint, to ensure the importing and imported packages + // have consistent view of symbol indices. + // Normally the go command should ensure this. But in case something + // goes wrong, it could lead to obscure bugs like run-time crash. + // Check it here to be sure. + if lib.Fingerprint.IsZero() { // Not yet imported. Update its fingerprint. + lib.Fingerprint = fingerprint + } + checkFingerprint(lib, fingerprint, lib.Srcref, lib.Fingerprint) + } + + addImports(ctxt, lib, pn) + return nil +} + +// symbolsAreUnresolved scans through the loader's list of unresolved +// symbols and checks to see whether any of them match the names of the +// symbols in 'want'. Return value is a list of bools, with list[K] set +// to true if there is an unresolved reference to the symbol in want[K]. +func symbolsAreUnresolved(ctxt *Link, want []string) []bool { + returnAllUndefs := -1 + undefs, _ := ctxt.loader.UndefinedRelocTargets(returnAllUndefs) + seen := make(map[loader.Sym]struct{}) + rval := make([]bool, len(want)) + wantm := make(map[string]int) + for k, w := range want { + wantm[w] = k + } + count := 0 + for _, s := range undefs { + if _, ok := seen[s]; ok { + continue + } + seen[s] = struct{}{} + if k, ok := wantm[ctxt.loader.SymName(s)]; ok { + rval[k] = true + count++ + if count == len(want) { + return rval + } + } + } + return rval +} + +// hostObject reads a single host object file (compare to "hostArchive"). +// This is used as part of internal linking when we need to pull in +// files such as "crt?.o". +func hostObject(ctxt *Link, objname string, path string) { + if ctxt.Debugvlog > 1 { + ctxt.Logf("hostObject(%s)\n", path) + } + objlib := sym.Library{ + Pkg: objname, + } + f, err := bio.Open(path) + if err != nil { + Exitf("cannot open host object %q file %s: %v", objname, path, err) + } + defer f.Close() + h := ldobj(ctxt, f, &objlib, 0, path, path) + if h.ld == nil { + Exitf("unrecognized object file format in %s", path) + } + h.file = path + h.length = f.MustSeek(0, 2) + f.MustSeek(h.off, 0) + h.ld(ctxt, f, h.pkg, h.length, h.pn) + if *flagCaptureHostObjs != "" { + captureHostObj(h) + } +} + +func checkFingerprint(lib *sym.Library, libfp goobj.FingerprintType, src string, srcfp goobj.FingerprintType) { + if libfp != srcfp { + Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp) + } +} + +func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte { + data := make([]byte, sym.Size) + sect := f.Sections[sym.Section] + if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE { + Errorf(nil, "reading %s from non-data section", sym.Name) + } + n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr)) + if uint64(n) != sym.Size { + Errorf(nil, "reading contents of %s: %v", sym.Name, err) + } + return data +} + +func readwithpad(r io.Reader, sz int32) ([]byte, error) { + data := make([]byte, Rnd(int64(sz), 4)) + _, err := io.ReadFull(r, data) + if err != nil { + return nil, err + } + data = data[:sz] + return data, nil +} + +func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) { + for _, sect := range f.Sections { + if sect.Type != elf.SHT_NOTE { + continue + } + r := sect.Open() + for { + var namesize, descsize, noteType int32 + err := binary.Read(r, f.ByteOrder, &namesize) + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("read namesize failed: %v", err) + } + err = binary.Read(r, f.ByteOrder, &descsize) + if err != nil { + return nil, fmt.Errorf("read descsize failed: %v", err) + } + err = binary.Read(r, f.ByteOrder, ¬eType) + if err != nil { + return nil, fmt.Errorf("read type failed: %v", err) + } + noteName, err := readwithpad(r, namesize) + if err != nil { + return nil, fmt.Errorf("read name failed: %v", err) + } + desc, err := readwithpad(r, descsize) + if err != nil { + return nil, fmt.Errorf("read desc failed: %v", err) + } + if string(name) == string(noteName) && typ == noteType { + return desc, nil + } + } + } + return nil, nil +} + +func findshlib(ctxt *Link, shlib string) string { + if filepath.IsAbs(shlib) { + return shlib + } + for _, libdir := range ctxt.Libdir { + libpath := filepath.Join(libdir, shlib) + if _, err := os.Stat(libpath); err == nil { + return libpath + } + } + Errorf(nil, "cannot find shared library: %s", shlib) + return "" +} + +func ldshlibsyms(ctxt *Link, shlib string) { + var libpath string + if filepath.IsAbs(shlib) { + libpath = shlib + shlib = filepath.Base(shlib) + } else { + libpath = findshlib(ctxt, shlib) + if libpath == "" { + return + } + } + for _, processedlib := range ctxt.Shlibs { + if processedlib.Path == libpath { + return + } + } + if ctxt.Debugvlog > 1 { + ctxt.Logf("ldshlibsyms: found library with name %s at %s\n", shlib, libpath) + } + + f, err := elf.Open(libpath) + if err != nil { + Errorf(nil, "cannot open shared library: %s", libpath) + return + } + // Keep the file open as decodetypeGcprog needs to read from it. + // TODO: fix. Maybe mmap the file. + //defer f.Close() + + hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) + if err != nil { + Errorf(nil, "cannot read ABI hash from shared library %s: %v", libpath, err) + return + } + + depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG) + if err != nil { + Errorf(nil, "cannot read dep list from shared library %s: %v", libpath, err) + return + } + var deps []string + for _, dep := range strings.Split(string(depsbytes), "\n") { + if dep == "" { + continue + } + if !filepath.IsAbs(dep) { + // If the dep can be interpreted as a path relative to the shlib + // in which it was found, do that. Otherwise, we will leave it + // to be resolved by libdir lookup. + abs := filepath.Join(filepath.Dir(libpath), dep) + if _, err := os.Stat(abs); err == nil { + dep = abs + } + } + deps = append(deps, dep) + } + + syms, err := f.DynamicSymbols() + if err != nil { + Errorf(nil, "cannot read symbols from shared library: %s", libpath) + return + } + + for _, elfsym := range syms { + if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION { + continue + } + + // Symbols whose names start with "type:" are compiler generated, + // so make functions with that prefix internal. + ver := 0 + symname := elfsym.Name // (unmangled) symbol name + if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type:") { + ver = abiInternalVer + } else if buildcfg.Experiment.RegabiWrappers && elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC { + // Demangle the ABI name. Keep in sync with symtab.go:mangleABIName. + if strings.HasSuffix(elfsym.Name, ".abiinternal") { + ver = sym.SymVerABIInternal + symname = strings.TrimSuffix(elfsym.Name, ".abiinternal") + } else if strings.HasSuffix(elfsym.Name, ".abi0") { + ver = 0 + symname = strings.TrimSuffix(elfsym.Name, ".abi0") + } + } + + l := ctxt.loader + s := l.LookupOrCreateSym(symname, ver) + + // Because loadlib above loads all .a files before loading + // any shared libraries, any non-dynimport symbols we find + // that duplicate symbols already loaded should be ignored + // (the symbols from the .a files "win"). + if l.SymType(s) != 0 && l.SymType(s) != sym.SDYNIMPORT { + continue + } + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SDYNIMPORT) + l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info)) + su.SetSize(int64(elfsym.Size)) + if elfsym.Section != elf.SHN_UNDEF { + // Set .File for the library that actually defines the symbol. + l.SetSymPkg(s, libpath) + + // The decodetype_* functions in decodetype.go need access to + // the type data. + sname := l.SymName(s) + if strings.HasPrefix(sname, "type:") && !strings.HasPrefix(sname, "type:.") { + su.SetData(readelfsymboldata(ctxt, f, &elfsym)) + } + } + + if symname != elfsym.Name { + l.SetSymExtname(s, elfsym.Name) + } + } + ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f}) +} + +func addsection(ldr *loader.Loader, arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section { + sect := ldr.NewSection() + sect.Rwx = uint8(rwx) + sect.Name = name + sect.Seg = seg + sect.Align = int32(arch.PtrSize) // everything is at least pointer-aligned + seg.Sections = append(seg.Sections, sect) + return sect +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n") + objabi.Flagprint(os.Stderr) + Exit(2) +} + +type SymbolType int8 // TODO: after genasmsym is gone, maybe rename to plan9typeChar or something + +const ( + // see also https://9p.io/magic/man2html/1/nm + TextSym SymbolType = 'T' + DataSym SymbolType = 'D' + BSSSym SymbolType = 'B' + UndefinedSym SymbolType = 'U' + TLSSym SymbolType = 't' + FrameSym SymbolType = 'm' + ParamSym SymbolType = 'p' + AutoSym SymbolType = 'a' + + // Deleted auto (not a real sym, just placeholder for type) + DeletedAutoSym = 'x' +) + +// defineInternal defines a symbol used internally by the go runtime. +func (ctxt *Link) defineInternal(p string, t sym.SymKind) loader.Sym { + s := ctxt.loader.CreateSymForUpdate(p, 0) + s.SetType(t) + s.SetSpecial(true) + s.SetLocal(true) + return s.Sym() +} + +func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) loader.Sym { + s := ctxt.defineInternal(p, t) + ctxt.loader.SetSymValue(s, v) + return s +} + +func datoff(ldr *loader.Loader, s loader.Sym, addr int64) int64 { + if uint64(addr) >= Segdata.Vaddr { + return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff) + } + if uint64(addr) >= Segtext.Vaddr { + return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff) + } + ldr.Errorf(s, "invalid datoff %#x", addr) + return 0 +} + +func Entryvalue(ctxt *Link) int64 { + a := *flagEntrySymbol + if a[0] >= '0' && a[0] <= '9' { + return atolwhex(a) + } + ldr := ctxt.loader + s := ldr.Lookup(a, 0) + if s == 0 { + Errorf(nil, "missing entry symbol %q", a) + return 0 + } + st := ldr.SymType(s) + if st == 0 { + return *FlagTextAddr + } + if !ctxt.IsAIX() && st != sym.STEXT { + ldr.Errorf(s, "entry not text") + } + return ldr.SymValue(s) +} + +func (ctxt *Link) callgraph() { + if !*FlagC { + return + } + + ldr := ctxt.loader + for _, s := range ctxt.Textp { + relocs := ldr.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + rs := r.Sym() + if rs == 0 { + continue + } + if r.Type().IsDirectCall() && ldr.SymType(rs) == sym.STEXT { + ctxt.Logf("%s calls %s\n", ldr.SymName(s), ldr.SymName(rs)) + } + } + } +} + +func Rnd(v int64, r int64) int64 { + if r <= 0 { + return v + } + v += r - 1 + c := v % r + if c < 0 { + c += r + } + v -= c + return v +} + +func bgetc(r *bio.Reader) int { + c, err := r.ReadByte() + if err != nil { + if err != io.EOF { + log.Fatalf("reading input: %v", err) + } + return -1 + } + return int(c) +} + +type markKind uint8 // for postorder traversal +const ( + _ markKind = iota + visiting + visited +) + +func postorder(libs []*sym.Library) []*sym.Library { + order := make([]*sym.Library, 0, len(libs)) // hold the result + mark := make(map[*sym.Library]markKind, len(libs)) + for _, lib := range libs { + dfs(lib, mark, &order) + } + return order +} + +func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library) { + if mark[lib] == visited { + return + } + if mark[lib] == visiting { + panic("found import cycle while visiting " + lib.Pkg) + } + mark[lib] = visiting + for _, i := range lib.Imports { + dfs(i, mark, order) + } + mark[lib] = visited + *order = append(*order, lib) +} + +func ElfSymForReloc(ctxt *Link, s loader.Sym) int32 { + // If putelfsym created a local version of this symbol, use that in all + // relocations. + les := ctxt.loader.SymLocalElfSym(s) + if les != 0 { + return les + } else { + return ctxt.loader.SymElfSym(s) + } +} + +func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, elfRelocTyp uint32) { + if ldr.SymGot(s) >= 0 { + return + } + + Adddynsym(ldr, target, syms, s) + got := ldr.MakeSymbolUpdater(syms.GOT) + ldr.SetGot(s, int32(got.Size())) + got.AddUint(target.Arch, 0) + + if target.IsElf() { + if target.Arch.PtrSize == 8 { + rela := ldr.MakeSymbolUpdater(syms.Rela) + rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp)) + rela.AddUint64(target.Arch, 0) + } else { + rel := ldr.MakeSymbolUpdater(syms.Rel) + rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), elfRelocTyp)) + } + } else if target.IsDarwin() { + leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT) + leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s))) + if target.IsPIE() && target.IsInternal() { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation. + // Here we record what are needed and encode them later. + MachoAddBind(int64(ldr.SymGot(s)), s) + } + } else { + ldr.Errorf(s, "addgotsym: unsupported binary format") + } +} + +var hostobjcounter int + +// captureHostObj writes out the content of a host object (pulled from +// an archive or loaded from a *.o file directly) to a directory +// specified via the linker's "-capturehostobjs" debugging flag. This +// is intended to make it easier for a developer to inspect the actual +// object feeding into "CGO internal" link step. +func captureHostObj(h *Hostobj) { + // Form paths for info file and obj file. + ofile := fmt.Sprintf("captured-obj-%d.o", hostobjcounter) + ifile := fmt.Sprintf("captured-obj-%d.txt", hostobjcounter) + hostobjcounter++ + opath := filepath.Join(*flagCaptureHostObjs, ofile) + ipath := filepath.Join(*flagCaptureHostObjs, ifile) + + // Write the info file. + info := fmt.Sprintf("pkg: %s\npn: %s\nfile: %s\noff: %d\nlen: %d\n", + h.pkg, h.pn, h.file, h.off, h.length) + if err := os.WriteFile(ipath, []byte(info), 0666); err != nil { + log.Fatalf("error writing captured host obj info %s: %v", ipath, err) + } + + readObjData := func() []byte { + inf, err := os.Open(h.file) + if err != nil { + log.Fatalf("capturing host obj: open failed on %s: %v", h.pn, err) + } + res := make([]byte, h.length) + if n, err := inf.ReadAt(res, h.off); err != nil || n != int(h.length) { + log.Fatalf("capturing host obj: readat failed on %s: %v", h.pn, err) + } + return res + } + + // Write the object file. + if err := os.WriteFile(opath, readObjData(), 0666); err != nil { + log.Fatalf("error writing captured host object %s: %v", opath, err) + } + + fmt.Fprintf(os.Stderr, "link: info: captured host object %s to %s\n", + h.file, opath) +} diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go new file mode 100644 index 0000000..34221df --- /dev/null +++ b/src/cmd/link/internal/ld/link.go @@ -0,0 +1,161 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "fmt" +) + +type Shlib struct { + Path string + Hash []byte + Deps []string + File *elf.File +} + +// Link holds the context for writing object code from a compiler +// or for reading that input into the linker. +type Link struct { + Target + ErrorReporter + ArchSyms + + outSem chan int // limits the number of output writers + Out *OutBuf + + version int // current version number for static/file-local symbols + + Debugvlog int + Bso *bufio.Writer + + Loaded bool // set after all inputs have been loaded as symbols + + compressDWARF bool + + Libdir []string + Library []*sym.Library + LibraryByPkg map[string]*sym.Library + Shlibs []Shlib + Textp []loader.Sym + Moduledata loader.Sym + + PackageFile map[string]string + PackageShlib map[string]string + + tramps []loader.Sym // trampolines + + compUnits []*sym.CompilationUnit // DWARF compilation units + runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen. + + loader *loader.Loader + cgodata []cgodata // cgo directives to load, three strings are args for loadcgo + + datap []loader.Sym + dynexp []loader.Sym + + // Elf symtab variables. + numelfsym int // starts at 0, 1 is reserved + + // These are symbols that created and written by the linker. + // Rather than creating a symbol, and writing all its data into the heap, + // you can create a symbol, and just a generation function will be called + // after the symbol's been created in the output mmap. + generatorSyms map[loader.Sym]generatorFunc +} + +type cgodata struct { + file string + pkg string + directives [][]string +} + +func (ctxt *Link) Logf(format string, args ...interface{}) { + fmt.Fprintf(ctxt.Bso, format, args...) + ctxt.Bso.Flush() +} + +func addImports(ctxt *Link, l *sym.Library, pn string) { + pkg := objabi.PathToPrefix(l.Pkg) + for _, imp := range l.Autolib { + lib := addlib(ctxt, pkg, pn, imp.Pkg, imp.Fingerprint) + if lib != nil { + l.Imports = append(l.Imports, lib) + } + } + l.Autolib = nil +} + +// Allocate a new version (i.e. symbol namespace). +func (ctxt *Link) IncVersion() int { + ctxt.version++ + return ctxt.version - 1 +} + +// returns the maximum version number +func (ctxt *Link) MaxVersion() int { + return ctxt.version +} + +// generatorFunc is a convenience type. +// Some linker-created Symbols are large and shouldn't really live in the heap. +// Such Symbols can define a generator function. Their bytes can be generated +// directly in the output mmap. +// +// Relocations are applied prior to emitting generator Symbol contents. +// Generator Symbols that require relocations can be written in two passes. +// The first pass, at Symbol creation time, adds only relocations. +// The second pass, at content generation time, adds the rest. +// See generateFunctab for an example. +// +// Generator functions shouldn't grow the Symbol size. +// Generator functions must be safe for concurrent use. +// +// Generator Symbols have their Data set to the mmapped area when the +// generator is called. +type generatorFunc func(*Link, loader.Sym) + +// createGeneratorSymbol is a convenience method for creating a generator +// symbol. +func (ctxt *Link) createGeneratorSymbol(name string, version int, t sym.SymKind, size int64, gen generatorFunc) loader.Sym { + ldr := ctxt.loader + s := ldr.LookupOrCreateSym(name, version) + ldr.SetIsGeneratedSym(s, true) + sb := ldr.MakeSymbolUpdater(s) + sb.SetType(t) + sb.SetSize(size) + ctxt.generatorSyms[s] = gen + return s +} diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go new file mode 100644 index 0000000..d6c28e4 --- /dev/null +++ b/src/cmd/link/internal/ld/macho.go @@ -0,0 +1,1568 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "cmd/internal/codesign" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/macho" + "encoding/binary" + "fmt" + "internal/buildcfg" + "io" + "os" + "sort" + "strings" + "unsafe" +) + +type MachoHdr struct { + cpu uint32 + subcpu uint32 +} + +type MachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloc uint32 + nreloc uint32 + flag uint32 + res1 uint32 + res2 uint32 +} + +type MachoSeg struct { + name string + vsize uint64 + vaddr uint64 + fileoffset uint64 + filesize uint64 + prot1 uint32 + prot2 uint32 + nsect uint32 + msect uint32 + sect []MachoSect + flag uint32 +} + +// MachoPlatformLoad represents a LC_VERSION_MIN_* or +// LC_BUILD_VERSION load command. +type MachoPlatformLoad struct { + platform MachoPlatform // One of PLATFORM_* constants. + cmd MachoLoad +} + +type MachoLoad struct { + type_ uint32 + data []uint32 +} + +type MachoPlatform int + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, and SHeaders. + * May waste some. + */ +const ( + INITIAL_MACHO_HEADR = 4 * 1024 +) + +const ( + MACHO_CPU_AMD64 = 1<<24 | 7 + MACHO_CPU_386 = 7 + MACHO_SUBCPU_X86 = 3 + MACHO_CPU_ARM = 12 + MACHO_SUBCPU_ARM = 0 + MACHO_SUBCPU_ARMV7 = 9 + MACHO_CPU_ARM64 = 1<<24 | 12 + MACHO_SUBCPU_ARM64_ALL = 0 + MACHO_SUBCPU_ARM64_V8 = 1 + MACHO_SUBCPU_ARM64E = 2 + MACHO32SYMSIZE = 12 + MACHO64SYMSIZE = 16 + MACHO_X86_64_RELOC_UNSIGNED = 0 + MACHO_X86_64_RELOC_SIGNED = 1 + MACHO_X86_64_RELOC_BRANCH = 2 + MACHO_X86_64_RELOC_GOT_LOAD = 3 + MACHO_X86_64_RELOC_GOT = 4 + MACHO_X86_64_RELOC_SUBTRACTOR = 5 + MACHO_X86_64_RELOC_SIGNED_1 = 6 + MACHO_X86_64_RELOC_SIGNED_2 = 7 + MACHO_X86_64_RELOC_SIGNED_4 = 8 + MACHO_ARM_RELOC_VANILLA = 0 + MACHO_ARM_RELOC_PAIR = 1 + MACHO_ARM_RELOC_SECTDIFF = 2 + MACHO_ARM_RELOC_BR24 = 5 + MACHO_ARM64_RELOC_UNSIGNED = 0 + MACHO_ARM64_RELOC_BRANCH26 = 2 + MACHO_ARM64_RELOC_PAGE21 = 3 + MACHO_ARM64_RELOC_PAGEOFF12 = 4 + MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5 + MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6 + MACHO_ARM64_RELOC_ADDEND = 10 + MACHO_GENERIC_RELOC_VANILLA = 0 + MACHO_FAKE_GOTPCREL = 100 +) + +const ( + MH_MAGIC = 0xfeedface + MH_MAGIC_64 = 0xfeedfacf + + MH_OBJECT = 0x1 + MH_EXECUTE = 0x2 + + MH_NOUNDEFS = 0x1 + MH_DYLDLINK = 0x4 + MH_PIE = 0x200000 +) + +const ( + LC_SEGMENT = 0x1 + LC_SYMTAB = 0x2 + LC_SYMSEG = 0x3 + LC_THREAD = 0x4 + LC_UNIXTHREAD = 0x5 + LC_LOADFVMLIB = 0x6 + LC_IDFVMLIB = 0x7 + LC_IDENT = 0x8 + LC_FVMFILE = 0x9 + LC_PREPAGE = 0xa + LC_DYSYMTAB = 0xb + LC_LOAD_DYLIB = 0xc + LC_ID_DYLIB = 0xd + LC_LOAD_DYLINKER = 0xe + LC_ID_DYLINKER = 0xf + LC_PREBOUND_DYLIB = 0x10 + LC_ROUTINES = 0x11 + LC_SUB_FRAMEWORK = 0x12 + LC_SUB_UMBRELLA = 0x13 + LC_SUB_CLIENT = 0x14 + LC_SUB_LIBRARY = 0x15 + LC_TWOLEVEL_HINTS = 0x16 + LC_PREBIND_CKSUM = 0x17 + LC_LOAD_WEAK_DYLIB = 0x80000018 + LC_SEGMENT_64 = 0x19 + LC_ROUTINES_64 = 0x1a + LC_UUID = 0x1b + LC_RPATH = 0x8000001c + LC_CODE_SIGNATURE = 0x1d + LC_SEGMENT_SPLIT_INFO = 0x1e + LC_REEXPORT_DYLIB = 0x8000001f + LC_LAZY_LOAD_DYLIB = 0x20 + LC_ENCRYPTION_INFO = 0x21 + LC_DYLD_INFO = 0x22 + LC_DYLD_INFO_ONLY = 0x80000022 + LC_LOAD_UPWARD_DYLIB = 0x80000023 + LC_VERSION_MIN_MACOSX = 0x24 + LC_VERSION_MIN_IPHONEOS = 0x25 + LC_FUNCTION_STARTS = 0x26 + LC_DYLD_ENVIRONMENT = 0x27 + LC_MAIN = 0x80000028 + LC_DATA_IN_CODE = 0x29 + LC_SOURCE_VERSION = 0x2A + LC_DYLIB_CODE_SIGN_DRS = 0x2B + LC_ENCRYPTION_INFO_64 = 0x2C + LC_LINKER_OPTION = 0x2D + LC_LINKER_OPTIMIZATION_HINT = 0x2E + LC_VERSION_MIN_TVOS = 0x2F + LC_VERSION_MIN_WATCHOS = 0x30 + LC_VERSION_NOTE = 0x31 + LC_BUILD_VERSION = 0x32 + LC_DYLD_EXPORTS_TRIE = 0x80000033 + LC_DYLD_CHAINED_FIXUPS = 0x80000034 +) + +const ( + S_REGULAR = 0x0 + S_ZEROFILL = 0x1 + S_NON_LAZY_SYMBOL_POINTERS = 0x6 + S_SYMBOL_STUBS = 0x8 + S_MOD_INIT_FUNC_POINTERS = 0x9 + S_ATTR_PURE_INSTRUCTIONS = 0x80000000 + S_ATTR_DEBUG = 0x02000000 + S_ATTR_SOME_INSTRUCTIONS = 0x00000400 +) + +const ( + PLATFORM_MACOS MachoPlatform = 1 + PLATFORM_IOS MachoPlatform = 2 + PLATFORM_TVOS MachoPlatform = 3 + PLATFORM_WATCHOS MachoPlatform = 4 + PLATFORM_BRIDGEOS MachoPlatform = 5 +) + +// rebase table opcode +const ( + REBASE_TYPE_POINTER = 1 + REBASE_TYPE_TEXT_ABSOLUTE32 = 2 + REBASE_TYPE_TEXT_PCREL32 = 3 + + REBASE_OPCODE_MASK = 0xF0 + REBASE_IMMEDIATE_MASK = 0x0F + REBASE_OPCODE_DONE = 0x00 + REBASE_OPCODE_SET_TYPE_IMM = 0x10 + REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20 + REBASE_OPCODE_ADD_ADDR_ULEB = 0x30 + REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40 + REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50 + REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60 + REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70 + REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80 +) + +// bind table opcode +const ( + BIND_TYPE_POINTER = 1 + BIND_TYPE_TEXT_ABSOLUTE32 = 2 + BIND_TYPE_TEXT_PCREL32 = 3 + + BIND_SPECIAL_DYLIB_SELF = 0 + BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1 + BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2 + BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3 + + BIND_OPCODE_MASK = 0xF0 + BIND_IMMEDIATE_MASK = 0x0F + BIND_OPCODE_DONE = 0x00 + BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10 + BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20 + BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30 + BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40 + BIND_OPCODE_SET_TYPE_IMM = 0x50 + BIND_OPCODE_SET_ADDEND_SLEB = 0x60 + BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70 + BIND_OPCODE_ADD_ADDR_ULEB = 0x80 + BIND_OPCODE_DO_BIND = 0x90 + BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0 + BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0 + BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0 + BIND_OPCODE_THREADED = 0xD0 + BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00 + BIND_SUBOPCODE_THREADED_APPLY = 0x01 +) + +const machoHeaderSize64 = 8 * 4 // size of 64-bit Mach-O header + +// Mach-O file writing +// https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html + +var machohdr MachoHdr + +var load []MachoLoad + +var machoPlatform MachoPlatform + +var seg [16]MachoSeg + +var nseg int + +var ndebug int + +var nsect int + +const ( + SymKindLocal = 0 + iota + SymKindExtdef + SymKindUndef + NumSymKind +) + +var nkind [NumSymKind]int + +var sortsym []loader.Sym + +var nsortsym int + +// Amount of space left for adding load commands +// that refer to dynamic libraries. Because these have +// to go in the Mach-O header, we can't just pick a +// "big enough" header size. The initial header is +// one page, the non-dynamic library stuff takes +// up about 1300 bytes; we overestimate that as 2k. +var loadBudget = INITIAL_MACHO_HEADR - 2*1024 + +func getMachoHdr() *MachoHdr { + return &machohdr +} + +func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad { + if arch.PtrSize == 8 && (ndata&1 != 0) { + ndata++ + } + + load = append(load, MachoLoad{}) + l := &load[len(load)-1] + l.type_ = type_ + l.data = make([]uint32, ndata) + return l +} + +func newMachoSeg(name string, msect int) *MachoSeg { + if nseg >= len(seg) { + Exitf("too many segs") + } + + s := &seg[nseg] + nseg++ + s.name = name + s.msect = uint32(msect) + s.sect = make([]MachoSect, msect) + return s +} + +func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect { + if seg.nsect >= seg.msect { + Exitf("too many sects in segment %s", seg.name) + } + + s := &seg.sect[seg.nsect] + seg.nsect++ + s.name = name + s.segname = segname + nsect++ + return s +} + +// Generic linking code. + +var dylib []string + +var linkoff int64 + +func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int { + o1 := out.Offset() + + loadsize := 4 * 4 * ndebug + for i := range load { + loadsize += 4 * (len(load[i].data) + 2) + } + if arch.PtrSize == 8 { + loadsize += 18 * 4 * nseg + loadsize += 20 * 4 * nsect + } else { + loadsize += 14 * 4 * nseg + loadsize += 17 * 4 * nsect + } + + if arch.PtrSize == 8 { + out.Write32(MH_MAGIC_64) + } else { + out.Write32(MH_MAGIC) + } + out.Write32(machohdr.cpu) + out.Write32(machohdr.subcpu) + if linkmode == LinkExternal { + out.Write32(MH_OBJECT) /* file type - mach object */ + } else { + out.Write32(MH_EXECUTE) /* file type - mach executable */ + } + out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) + out.Write32(uint32(loadsize)) + flags := uint32(0) + if nkind[SymKindUndef] == 0 { + flags |= MH_NOUNDEFS + } + if ctxt.IsPIE() && linkmode == LinkInternal { + flags |= MH_PIE | MH_DYLDLINK + } + out.Write32(flags) /* flags */ + if arch.PtrSize == 8 { + out.Write32(0) /* reserved */ + } + + for i := 0; i < nseg; i++ { + s := &seg[i] + if arch.PtrSize == 8 { + out.Write32(LC_SEGMENT_64) + out.Write32(72 + 80*s.nsect) + out.WriteStringN(s.name, 16) + out.Write64(s.vaddr) + out.Write64(s.vsize) + out.Write64(s.fileoffset) + out.Write64(s.filesize) + out.Write32(s.prot1) + out.Write32(s.prot2) + out.Write32(s.nsect) + out.Write32(s.flag) + } else { + out.Write32(LC_SEGMENT) + out.Write32(56 + 68*s.nsect) + out.WriteStringN(s.name, 16) + out.Write32(uint32(s.vaddr)) + out.Write32(uint32(s.vsize)) + out.Write32(uint32(s.fileoffset)) + out.Write32(uint32(s.filesize)) + out.Write32(s.prot1) + out.Write32(s.prot2) + out.Write32(s.nsect) + out.Write32(s.flag) + } + + for j := uint32(0); j < s.nsect; j++ { + t := &s.sect[j] + if arch.PtrSize == 8 { + out.WriteStringN(t.name, 16) + out.WriteStringN(t.segname, 16) + out.Write64(t.addr) + out.Write64(t.size) + out.Write32(t.off) + out.Write32(t.align) + out.Write32(t.reloc) + out.Write32(t.nreloc) + out.Write32(t.flag) + out.Write32(t.res1) /* reserved */ + out.Write32(t.res2) /* reserved */ + out.Write32(0) /* reserved */ + } else { + out.WriteStringN(t.name, 16) + out.WriteStringN(t.segname, 16) + out.Write32(uint32(t.addr)) + out.Write32(uint32(t.size)) + out.Write32(t.off) + out.Write32(t.align) + out.Write32(t.reloc) + out.Write32(t.nreloc) + out.Write32(t.flag) + out.Write32(t.res1) /* reserved */ + out.Write32(t.res2) /* reserved */ + } + } + } + + for i := range load { + l := &load[i] + out.Write32(l.type_) + out.Write32(4 * (uint32(len(l.data)) + 2)) + for j := 0; j < len(l.data); j++ { + out.Write32(l.data[j]) + } + } + + return int(out.Offset() - o1) +} + +func (ctxt *Link) domacho() { + if *FlagD { + return + } + + // Copy platform load command. + for _, h := range hostobj { + load, err := hostobjMachoPlatform(&h) + if err != nil { + Exitf("%v", err) + } + if load != nil { + machoPlatform = load.platform + ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data))) + copy(ml.data, load.cmd.data) + break + } + } + if machoPlatform == 0 { + machoPlatform = PLATFORM_MACOS + if buildcfg.GOOS == "ios" { + machoPlatform = PLATFORM_IOS + } + if ctxt.LinkMode == LinkInternal && machoPlatform == PLATFORM_MACOS { + var version uint32 + switch ctxt.Arch.Family { + case sys.AMD64: + // This must be fairly recent for Apple signing (go.dev/issue/30488). + // Having too old a version here was also implicated in some problems + // calling into macOS libraries (go.dev/issue/56784). + // In general this can be the most recent supported macOS version. + version = 10<<16 | 13<<8 | 0<<0 // 10.13.0 + case sys.ARM64: + version = 11<<16 | 0<<8 | 0<<0 // 11.0.0 + } + ml := newMachoLoad(ctxt.Arch, LC_BUILD_VERSION, 4) + ml.data[0] = uint32(machoPlatform) + ml.data[1] = version // OS version + ml.data[2] = version // SDK version + ml.data[3] = 0 // ntools + } + } + + // empirically, string table must begin with " \x00". + s := ctxt.loader.LookupOrCreateSym(".machosymstr", 0) + sb := ctxt.loader.MakeSymbolUpdater(s) + + sb.SetType(sym.SMACHOSYMSTR) + sb.SetReachable(true) + sb.AddUint8(' ') + sb.AddUint8('\x00') + + s = ctxt.loader.LookupOrCreateSym(".machosymtab", 0) + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOSYMTAB) + sb.SetReachable(true) + + if ctxt.IsInternal() { + s = ctxt.loader.LookupOrCreateSym(".plt", 0) // will be __symbol_stub + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOPLT) + sb.SetReachable(true) + + s = ctxt.loader.LookupOrCreateSym(".got", 0) // will be __nl_symbol_ptr + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOGOT) + sb.SetReachable(true) + sb.SetAlign(4) + + s = ctxt.loader.LookupOrCreateSym(".linkedit.plt", 0) // indirect table for .plt + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOINDIRECTPLT) + sb.SetReachable(true) + + s = ctxt.loader.LookupOrCreateSym(".linkedit.got", 0) // indirect table for .got + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOINDIRECTGOT) + sb.SetReachable(true) + } + + // Add a dummy symbol that will become the __asm marker section. + if ctxt.IsExternal() { + s = ctxt.loader.LookupOrCreateSym(".llvmasm", 0) + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHO) + sb.SetReachable(true) + sb.AddUint8(0) + } + + // Un-export runtime symbols from plugins. Since the runtime + // is included in both the main binary and each plugin, these + // symbols appear in both images. If we leave them exported in + // the plugin, then the dynamic linker will resolve + // relocations to these functions in the plugin's functab to + // point to the main image, causing the runtime to think the + // plugin's functab is corrupted. By unexporting them, these + // become static references, which are resolved to the + // plugin's text. + // + // It would be better to omit the runtime from plugins. (Using + // relative PCs in the functab instead of relocations would + // also address this.) + // + // See issue #18190. + if ctxt.BuildMode == BuildModePlugin { + for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} { + // Most of these are data symbols or C + // symbols, so they have symbol version 0. + ver := 0 + // _cgo_panic is a Go function, so it uses ABIInternal. + if name == "_cgo_panic" { + ver = abiInternalVer + } + s := ctxt.loader.Lookup(name, ver) + if s != 0 { + ctxt.loader.SetAttrCgoExportDynamic(s, false) + } + } + } +} + +func machoadddynlib(lib string, linkmode LinkMode) { + if seenlib[lib] || linkmode == LinkExternal { + return + } + seenlib[lib] = true + + // Will need to store the library name rounded up + // and 24 bytes of header metadata. If not enough + // space, grab another page of initial space at the + // beginning of the output file. + loadBudget -= (len(lib)+7)/8*8 + 24 + + if loadBudget < 0 { + HEADR += 4096 + *FlagTextAddr += 4096 + loadBudget += 4096 + } + + dylib = append(dylib, lib) +} + +func machoshbits(ctxt *Link, mseg *MachoSeg, sect *sym.Section, segname string) { + buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) + + msect := newMachoSect(mseg, buf, segname) + + if sect.Rellen > 0 { + msect.reloc = uint32(sect.Reloff) + msect.nreloc = uint32(sect.Rellen / 8) + } + + for 1<<msect.align < sect.Align { + msect.align++ + } + msect.addr = sect.Vaddr + msect.size = sect.Length + + if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { + // data in file + if sect.Length > sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr { + Errorf(nil, "macho cannot represent section %s crossing data and bss", sect.Name) + } + msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr) + } else { + msect.off = 0 + msect.flag |= S_ZEROFILL + } + + if sect.Rwx&1 != 0 { + msect.flag |= S_ATTR_SOME_INSTRUCTIONS + } + + if sect.Name == ".text" { + msect.flag |= S_ATTR_PURE_INSTRUCTIONS + } + + if sect.Name == ".plt" { + msect.name = "__symbol_stub1" + msect.flag = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS | S_SYMBOL_STUBS + msect.res1 = 0 //nkind[SymKindLocal]; + msect.res2 = 6 + } + + if sect.Name == ".got" { + msect.name = "__nl_symbol_ptr" + msect.flag = S_NON_LAZY_SYMBOL_POINTERS + msect.res1 = uint32(ctxt.loader.SymSize(ctxt.ArchSyms.LinkEditPLT) / 4) /* offset into indirect symbol table */ + } + + if sect.Name == ".init_array" { + msect.name = "__mod_init_func" + msect.flag = S_MOD_INIT_FUNC_POINTERS + } + + // Some platforms such as watchOS and tvOS require binaries with + // bitcode enabled. The Go toolchain can't output bitcode, so use + // a marker section in the __LLVM segment, "__asm", to tell the Apple + // toolchain that the Go text came from assembler and thus has no + // bitcode. This is not true, but Kotlin/Native, Rust and Flutter + // are also using this trick. + if sect.Name == ".llvmasm" { + msect.name = "__asm" + msect.segname = "__LLVM" + } + + if segname == "__DWARF" { + msect.flag |= S_ATTR_DEBUG + } +} + +func asmbMacho(ctxt *Link) { + machlink := doMachoLink(ctxt) + if !*FlagS && ctxt.IsExternal() { + symo := int64(Segdwarf.Fileoff + uint64(Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))) + uint64(machlink)) + ctxt.Out.SeekSet(symo) + machoEmitReloc(ctxt) + } + ctxt.Out.SeekSet(0) + + ldr := ctxt.loader + + /* apple MACH */ + va := *FlagTextAddr - int64(HEADR) + + mh := getMachoHdr() + switch ctxt.Arch.Family { + default: + Exitf("unknown macho architecture: %v", ctxt.Arch.Family) + + case sys.AMD64: + mh.cpu = MACHO_CPU_AMD64 + mh.subcpu = MACHO_SUBCPU_X86 + + case sys.ARM64: + mh.cpu = MACHO_CPU_ARM64 + mh.subcpu = MACHO_SUBCPU_ARM64_ALL + } + + var ms *MachoSeg + if ctxt.LinkMode == LinkExternal { + /* segment for entire file */ + ms = newMachoSeg("", 40) + + ms.fileoffset = Segtext.Fileoff + ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff + ms.vsize = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr + } + + /* segment for zero page */ + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__PAGEZERO", 0) + ms.vsize = uint64(va) + } + + /* text */ + v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__TEXT", 20) + ms.vaddr = uint64(va) + ms.vsize = uint64(v) + ms.fileoffset = 0 + ms.filesize = uint64(v) + ms.prot1 = 7 + ms.prot2 = 5 + } + + for _, sect := range Segtext.Sections { + machoshbits(ctxt, ms, sect, "__TEXT") + } + + /* rodata */ + if ctxt.LinkMode != LinkExternal && Segrelrodata.Length > 0 { + ms = newMachoSeg("__DATA_CONST", 20) + ms.vaddr = Segrelrodata.Vaddr + ms.vsize = Segrelrodata.Length + ms.fileoffset = Segrelrodata.Fileoff + ms.filesize = Segrelrodata.Filelen + ms.prot1 = 3 + ms.prot2 = 3 + ms.flag = 0x10 // SG_READ_ONLY + } + + for _, sect := range Segrelrodata.Sections { + machoshbits(ctxt, ms, sect, "__DATA_CONST") + } + + /* data */ + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__DATA", 20) + ms.vaddr = Segdata.Vaddr + ms.vsize = Segdata.Length + ms.fileoffset = Segdata.Fileoff + ms.filesize = Segdata.Filelen + ms.prot1 = 3 + ms.prot2 = 3 + } + + for _, sect := range Segdata.Sections { + machoshbits(ctxt, ms, sect, "__DATA") + } + + /* dwarf */ + if !*FlagW { + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__DWARF", 20) + ms.vaddr = Segdwarf.Vaddr + ms.vsize = 0 + ms.fileoffset = Segdwarf.Fileoff + ms.filesize = Segdwarf.Filelen + } + for _, sect := range Segdwarf.Sections { + machoshbits(ctxt, ms, sect, "__DWARF") + } + } + + if ctxt.LinkMode != LinkExternal { + switch ctxt.Arch.Family { + default: + Exitf("unknown macho architecture: %v", ctxt.Arch.Family) + + case sys.AMD64: + ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 42+2) + ml.data[0] = 4 /* thread type */ + ml.data[1] = 42 /* word count */ + ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */ + ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32) + + case sys.ARM64: + ml := newMachoLoad(ctxt.Arch, LC_MAIN, 4) + ml.data[0] = uint32(uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) + ml.data[1] = uint32((uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) >> 32) + } + } + + var codesigOff int64 + if !*FlagD { + // must match doMachoLink below + s1 := ldr.SymSize(ldr.Lookup(".machorebase", 0)) + s2 := ldr.SymSize(ldr.Lookup(".machobind", 0)) + s3 := ldr.SymSize(ldr.Lookup(".machosymtab", 0)) + s4 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT) + s5 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT) + s6 := ldr.SymSize(ldr.Lookup(".machosymstr", 0)) + s7 := ldr.SymSize(ldr.Lookup(".machocodesig", 0)) + + if ctxt.LinkMode != LinkExternal { + ms := newMachoSeg("__LINKEDIT", 0) + ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound))) + ms.vsize = uint64(s1 + s2 + s3 + s4 + s5 + s6 + s7) + ms.fileoffset = uint64(linkoff) + ms.filesize = ms.vsize + ms.prot1 = 1 + ms.prot2 = 1 + + codesigOff = linkoff + s1 + s2 + s3 + s4 + s5 + s6 + } + + if ctxt.LinkMode != LinkExternal && ctxt.IsPIE() { + ml := newMachoLoad(ctxt.Arch, LC_DYLD_INFO_ONLY, 10) + ml.data[0] = uint32(linkoff) // rebase off + ml.data[1] = uint32(s1) // rebase size + ml.data[2] = uint32(linkoff + s1) // bind off + ml.data[3] = uint32(s2) // bind size + ml.data[4] = 0 // weak bind off + ml.data[5] = 0 // weak bind size + ml.data[6] = 0 // lazy bind off + ml.data[7] = 0 // lazy bind size + ml.data[8] = 0 // export + ml.data[9] = 0 // export size + } + + ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4) + ml.data[0] = uint32(linkoff + s1 + s2) /* symoff */ + ml.data[1] = uint32(nsortsym) /* nsyms */ + ml.data[2] = uint32(linkoff + s1 + s2 + s3 + s4 + s5) /* stroff */ + ml.data[3] = uint32(s6) /* strsize */ + + machodysymtab(ctxt, linkoff+s1+s2) + + if ctxt.LinkMode != LinkExternal { + ml := newMachoLoad(ctxt.Arch, LC_LOAD_DYLINKER, 6) + ml.data[0] = 12 /* offset to string */ + stringtouint32(ml.data[1:], "/usr/lib/dyld") + + for _, lib := range dylib { + ml = newMachoLoad(ctxt.Arch, LC_LOAD_DYLIB, 4+(uint32(len(lib))+1+7)/8*2) + ml.data[0] = 24 /* offset of string from beginning of load */ + ml.data[1] = 0 /* time stamp */ + ml.data[2] = 0 /* version */ + ml.data[3] = 0 /* compatibility version */ + stringtouint32(ml.data[4:], lib) + } + } + + if ctxt.IsInternal() && ctxt.NeedCodeSign() { + ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2) + ml.data[0] = uint32(codesigOff) + ml.data[1] = uint32(s7) + } + } + + a := machowrite(ctxt, ctxt.Arch, ctxt.Out, ctxt.LinkMode) + if int32(a) > HEADR { + Exitf("HEADR too small: %d > %d", a, HEADR) + } + + // Now we have written everything. Compute the code signature (which + // is a hash of the file content, so it must be done at last.) + if ctxt.IsInternal() && ctxt.NeedCodeSign() { + cs := ldr.Lookup(".machocodesig", 0) + data := ctxt.Out.Data() + if int64(len(data)) != codesigOff { + panic("wrong size") + } + codesign.Sign(ldr.Data(cs), bytes.NewReader(data), "a.out", codesigOff, int64(Segtext.Fileoff), int64(Segtext.Filelen), ctxt.IsExe() || ctxt.IsPIE()) + ctxt.Out.SeekSet(codesigOff) + ctxt.Out.Write(ldr.Data(cs)) + } +} + +func symkind(ldr *loader.Loader, s loader.Sym) int { + if ldr.SymType(s) == sym.SDYNIMPORT { + return SymKindUndef + } + if ldr.AttrCgoExport(s) { + return SymKindExtdef + } + return SymKindLocal +} + +func collectmachosyms(ctxt *Link) { + ldr := ctxt.loader + + addsym := func(s loader.Sym) { + sortsym = append(sortsym, s) + nkind[symkind(ldr, s)]++ + } + + // Add special runtime.text and runtime.etext symbols. + // We've already included this symbol in Textp on darwin if ctxt.DynlinkingGo(). + // See data.go:/textaddress + if !ctxt.DynlinkingGo() { + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + for n := range Segtext.Sections[1:] { + s := ldr.Lookup(fmt.Sprintf("runtime.text.%d", n+1), 0) + if s != 0 { + addsym(s) + } else { + break + } + } + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + } + + // Add text symbols. + for _, s := range ctxt.Textp { + addsym(s) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + name := ldr.SymName(s) // TODO: try not to read the name + if name == "" || name[0] == '.' { + return false + } + return true + } + + // Add data symbols and external references. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata + if t == sym.STLSBSS { + // TLSBSS is not used on darwin. See data.go:allocateDataSections + continue + } + if !shouldBeInSymbolTable(s) { + continue + } + addsym(s) + } + + switch t { + case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: + addsym(s) + } + + // Some 64-bit functions have a "$INODE64" or "$INODE64$UNIX2003" suffix. + if t == sym.SDYNIMPORT && ldr.SymDynimplib(s) == "/usr/lib/libSystem.B.dylib" { + // But only on macOS. + if machoPlatform == PLATFORM_MACOS { + switch n := ldr.SymExtname(s); n { + case "fdopendir": + switch buildcfg.GOARCH { + case "amd64": + ldr.SetSymExtname(s, n+"$INODE64") + } + case "readdir_r", "getfsstat": + switch buildcfg.GOARCH { + case "amd64": + ldr.SetSymExtname(s, n+"$INODE64") + } + } + } + } + } + + nsortsym = len(sortsym) +} + +func machosymorder(ctxt *Link) { + ldr := ctxt.loader + + // On Mac OS X Mountain Lion, we must sort exported symbols + // So we sort them here and pre-allocate dynid for them + // See https://golang.org/issue/4029 + for _, s := range ctxt.dynexp { + if !ldr.AttrReachable(s) { + panic("dynexp symbol is not reachable") + } + } + collectmachosyms(ctxt) + sort.Slice(sortsym[:nsortsym], func(i, j int) bool { + s1 := sortsym[i] + s2 := sortsym[j] + k1 := symkind(ldr, s1) + k2 := symkind(ldr, s2) + if k1 != k2 { + return k1 < k2 + } + return ldr.SymExtname(s1) < ldr.SymExtname(s2) // Note: unnamed symbols are not added in collectmachosyms + }) + for i, s := range sortsym { + ldr.SetSymDynid(s, int32(i)) + } +} + +// AddMachoSym adds s to Mach-O symbol table, used in GenSymLate. +// Currently only used on ARM64 when external linking. +func AddMachoSym(ldr *loader.Loader, s loader.Sym) { + ldr.SetSymDynid(s, int32(nsortsym)) + sortsym = append(sortsym, s) + nsortsym++ + nkind[symkind(ldr, s)]++ +} + +// machoShouldExport reports whether a symbol needs to be exported. +// +// When dynamically linking, all non-local variables and plugin-exported +// symbols need to be exported. +func machoShouldExport(ctxt *Link, ldr *loader.Loader, s loader.Sym) bool { + if !ctxt.DynlinkingGo() || ldr.AttrLocal(s) { + return false + } + if ctxt.BuildMode == BuildModePlugin && strings.HasPrefix(ldr.SymExtname(s), objabi.PathToPrefix(*flagPluginPath)) { + return true + } + name := ldr.SymName(s) + if strings.HasPrefix(name, "go:itab.") { + return true + } + if strings.HasPrefix(name, "type:") && !strings.HasPrefix(name, "type:.") { + // reduce runtime typemap pressure, but do not + // export alg functions (type:.*), as these + // appear in pclntable. + return true + } + if strings.HasPrefix(name, "go:link.pkghash") { + return true + } + return ldr.SymType(s) >= sym.SFirstWritable // only writable sections +} + +func machosymtab(ctxt *Link) { + ldr := ctxt.loader + symtab := ldr.CreateSymForUpdate(".machosymtab", 0) + symstr := ldr.CreateSymForUpdate(".machosymstr", 0) + + for _, s := range sortsym[:nsortsym] { + symtab.AddUint32(ctxt.Arch, uint32(symstr.Size())) + + export := machoShouldExport(ctxt, ldr, s) + + // Prefix symbol names with "_" to match the system toolchain. + // (We used to only prefix C symbols, which is all required for the build. + // But some tools don't recognize Go symbols as symbols, so we prefix them + // as well.) + symstr.AddUint8('_') + + // replace "·" as ".", because DTrace cannot handle it. + name := strings.Replace(ldr.SymExtname(s), "·", ".", -1) + + name = mangleABIName(ctxt, ldr, s, name) + symstr.Addstring(name) + + if t := ldr.SymType(s); t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT { + symtab.AddUint8(0x01) // type N_EXT, external symbol + symtab.AddUint8(0) // no section + symtab.AddUint16(ctxt.Arch, 0) // desc + symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value + } else { + if export || ldr.AttrCgoExportDynamic(s) { + symtab.AddUint8(0x0f) // N_SECT | N_EXT + } else if ldr.AttrCgoExportStatic(s) { + // Only export statically, not dynamically. (N_PEXT is like hidden visibility) + symtab.AddUint8(0x1f) // N_SECT | N_EXT | N_PEXT + } else { + symtab.AddUint8(0x0e) // N_SECT + } + o := s + if outer := ldr.OuterSym(o); outer != 0 { + o = outer + } + if ldr.SymSect(o) == nil { + ldr.Errorf(s, "missing section for symbol") + symtab.AddUint8(0) + } else { + symtab.AddUint8(uint8(ldr.SymSect(o).Extnum)) + } + symtab.AddUint16(ctxt.Arch, 0) // desc + symtab.AddUintXX(ctxt.Arch, uint64(ldr.SymAddr(s)), ctxt.Arch.PtrSize) + } + } +} + +func machodysymtab(ctxt *Link, base int64) { + ml := newMachoLoad(ctxt.Arch, LC_DYSYMTAB, 18) + + n := 0 + ml.data[0] = uint32(n) /* ilocalsym */ + ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */ + n += nkind[SymKindLocal] + + ml.data[2] = uint32(n) /* iextdefsym */ + ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */ + n += nkind[SymKindExtdef] + + ml.data[4] = uint32(n) /* iundefsym */ + ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */ + + ml.data[6] = 0 /* tocoffset */ + ml.data[7] = 0 /* ntoc */ + ml.data[8] = 0 /* modtaboff */ + ml.data[9] = 0 /* nmodtab */ + ml.data[10] = 0 /* extrefsymoff */ + ml.data[11] = 0 /* nextrefsyms */ + + ldr := ctxt.loader + + // must match domacholink below + s1 := ldr.SymSize(ldr.Lookup(".machosymtab", 0)) + s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT) + s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT) + ml.data[12] = uint32(base + s1) /* indirectsymoff */ + ml.data[13] = uint32((s2 + s3) / 4) /* nindirectsyms */ + + ml.data[14] = 0 /* extreloff */ + ml.data[15] = 0 /* nextrel */ + ml.data[16] = 0 /* locreloff */ + ml.data[17] = 0 /* nlocrel */ +} + +func doMachoLink(ctxt *Link) int64 { + machosymtab(ctxt) + machoDyldInfo(ctxt) + + ldr := ctxt.loader + + // write data that will be linkedit section + s1 := ldr.Lookup(".machorebase", 0) + s2 := ldr.Lookup(".machobind", 0) + s3 := ldr.Lookup(".machosymtab", 0) + s4 := ctxt.ArchSyms.LinkEditPLT + s5 := ctxt.ArchSyms.LinkEditGOT + s6 := ldr.Lookup(".machosymstr", 0) + + size := ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4) + ldr.SymSize(s5) + ldr.SymSize(s6) + + // Force the linkedit section to end on a 16-byte + // boundary. This allows pure (non-cgo) Go binaries + // to be code signed correctly. + // + // Apple's codesign_allocate (a helper utility for + // the codesign utility) can do this fine itself if + // it is run on a dynamic Mach-O binary. However, + // when it is run on a pure (non-cgo) Go binary, where + // the linkedit section is mostly empty, it fails to + // account for the extra padding that it itself adds + // when adding the LC_CODE_SIGNATURE load command + // (which must be aligned on a 16-byte boundary). + // + // By forcing the linkedit section to end on a 16-byte + // boundary, codesign_allocate will not need to apply + // any alignment padding itself, working around the + // issue. + if size%16 != 0 { + n := 16 - size%16 + s6b := ldr.MakeSymbolUpdater(s6) + s6b.Grow(s6b.Size() + n) + s6b.SetSize(s6b.Size() + n) + size += n + } + + if size > 0 { + linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segrelrodata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound)) + ctxt.Out.SeekSet(linkoff) + + ctxt.Out.Write(ldr.Data(s1)) + ctxt.Out.Write(ldr.Data(s2)) + ctxt.Out.Write(ldr.Data(s3)) + ctxt.Out.Write(ldr.Data(s4)) + ctxt.Out.Write(ldr.Data(s5)) + ctxt.Out.Write(ldr.Data(s6)) + + // Add code signature if necessary. This must be the last. + s7 := machoCodeSigSym(ctxt, linkoff+size) + size += ldr.SymSize(s7) + } + + return Rnd(size, int64(*FlagRound)) +} + +func machorelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + ldr := ctxt.loader + + for i, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + + eaddr := sect.Vaddr + sect.Length + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= int64(eaddr) { + break + } + + // Compute external relocations on the go, and pass to Machoreloc1 + // to stream out. + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ldr.Errorf(s, "missing xsym in relocation") + continue + } + if !ldr.AttrReachable(rr.Xsym) { + ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym)) + } + if !thearch.Machoreloc1(ctxt.Arch, out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) { + ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym())) + } + } + } + + // sanity check + if uint64(out.Offset()) != sect.Reloff+sect.Rellen { + panic("machorelocsect: size mismatch") + } +} + +func machoEmitReloc(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + sizeExtRelocs(ctxt, thearch.MachorelocSize) + relocSect, wg := relocSectFn(ctxt, machorelocsect) + + relocSect(ctxt, Segtext.Sections[0], ctxt.Textp) + for _, sect := range Segtext.Sections[1:] { + if sect.Name == ".text" { + relocSect(ctxt, sect, ctxt.Textp) + } else { + relocSect(ctxt, sect, ctxt.datap) + } + } + for _, sect := range Segrelrodata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for _, sect := range Segdata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ctxt.loader.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + relocSect(ctxt, sect, si.syms) + } + wg.Wait() +} + +// hostobjMachoPlatform returns the first platform load command found +// in the host object, if any. +func hostobjMachoPlatform(h *Hostobj) (*MachoPlatformLoad, error) { + f, err := os.Open(h.file) + if err != nil { + return nil, fmt.Errorf("%s: failed to open host object: %v\n", h.file, err) + } + defer f.Close() + sr := io.NewSectionReader(f, h.off, h.length) + m, err := macho.NewFile(sr) + if err != nil { + // Not a valid Mach-O file. + return nil, nil + } + return peekMachoPlatform(m) +} + +// peekMachoPlatform returns the first LC_VERSION_MIN_* or LC_BUILD_VERSION +// load command found in the Mach-O file, if any. +func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) { + for _, cmd := range m.Loads { + raw := cmd.Raw() + ml := MachoLoad{ + type_: m.ByteOrder.Uint32(raw), + } + // Skip the type and command length. + data := raw[8:] + var p MachoPlatform + switch ml.type_ { + case LC_VERSION_MIN_IPHONEOS: + p = PLATFORM_IOS + case LC_VERSION_MIN_MACOSX: + p = PLATFORM_MACOS + case LC_VERSION_MIN_WATCHOS: + p = PLATFORM_WATCHOS + case LC_VERSION_MIN_TVOS: + p = PLATFORM_TVOS + case LC_BUILD_VERSION: + p = MachoPlatform(m.ByteOrder.Uint32(data)) + default: + continue + } + ml.data = make([]uint32, len(data)/4) + r := bytes.NewReader(data) + if err := binary.Read(r, m.ByteOrder, &ml.data); err != nil { + return nil, err + } + return &MachoPlatformLoad{ + platform: p, + cmd: ml, + }, nil + } + return nil, nil +} + +// A rebase entry tells the dynamic linker the data at sym+off needs to be +// relocated when the in-memory image moves. (This is somewhat like, say, +// ELF R_X86_64_RELATIVE). +// For now, the only kind of entry we support is that the data is an absolute +// address. That seems all we need. +// In the binary it uses a compact stateful bytecode encoding. So we record +// entries as we go and build the table at the end. +type machoRebaseRecord struct { + sym loader.Sym + off int64 +} + +var machorebase []machoRebaseRecord + +func MachoAddRebase(s loader.Sym, off int64) { + machorebase = append(machorebase, machoRebaseRecord{s, off}) +} + +// A bind entry tells the dynamic linker the data at GOT+off should be bound +// to the address of the target symbol, which is a dynamic import. +// For now, the only kind of entry we support is that the data is an absolute +// address, and the source symbol is always the GOT. That seems all we need. +// In the binary it uses a compact stateful bytecode encoding. So we record +// entries as we go and build the table at the end. +type machoBindRecord struct { + off int64 + targ loader.Sym +} + +var machobind []machoBindRecord + +func MachoAddBind(off int64, targ loader.Sym) { + machobind = append(machobind, machoBindRecord{off, targ}) +} + +// Generate data for the dynamic linker, used in LC_DYLD_INFO_ONLY load command. +// See mach-o/loader.h, struct dyld_info_command, for the encoding. +// e.g. https://opensource.apple.com/source/xnu/xnu-6153.81.5/EXTERNAL_HEADERS/mach-o/loader.h +func machoDyldInfo(ctxt *Link) { + ldr := ctxt.loader + rebase := ldr.CreateSymForUpdate(".machorebase", 0) + bind := ldr.CreateSymForUpdate(".machobind", 0) + + if !(ctxt.IsPIE() && ctxt.IsInternal()) { + return + } + + segId := func(seg *sym.Segment) uint8 { + switch seg { + case &Segtext: + return 1 + case &Segrelrodata: + return 2 + case &Segdata: + if Segrelrodata.Length > 0 { + return 3 + } + return 2 + } + panic("unknown segment") + } + + dylibId := func(s loader.Sym) int { + slib := ldr.SymDynimplib(s) + for i, lib := range dylib { + if lib == slib { + return i + 1 + } + } + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP // don't know where it is from + } + + // Rebase table. + // TODO: use more compact encoding. The encoding is stateful, and + // we can use delta encoding. + rebase.AddUint8(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER) + for _, r := range machorebase { + seg := ldr.SymSect(r.sym).Seg + off := uint64(ldr.SymValue(r.sym)+r.off) - seg.Vaddr + rebase.AddUint8(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg)) + rebase.AddUleb(off) + + rebase.AddUint8(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1) + } + rebase.AddUint8(REBASE_OPCODE_DONE) + sz := Rnd(rebase.Size(), 8) + rebase.Grow(sz) + rebase.SetSize(sz) + + // Bind table. + // TODO: compact encoding, as above. + // TODO: lazy binding? + got := ctxt.GOT + seg := ldr.SymSect(got).Seg + gotAddr := ldr.SymValue(got) + bind.AddUint8(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER) + for _, r := range machobind { + off := uint64(gotAddr+r.off) - seg.Vaddr + bind.AddUint8(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg)) + bind.AddUleb(off) + + d := dylibId(r.targ) + if d > 0 && d < 128 { + bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | uint8(d)&0xf) + } else if d >= 128 { + bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) + bind.AddUleb(uint64(d)) + } else { // d <= 0 + bind.AddUint8(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | uint8(d)&0xf) + } + + bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM) + // target symbol name as a C string, with _ prefix + bind.AddUint8('_') + bind.Addstring(ldr.SymExtname(r.targ)) + + bind.AddUint8(BIND_OPCODE_DO_BIND) + } + bind.AddUint8(BIND_OPCODE_DONE) + sz = Rnd(bind.Size(), 16) // make it 16-byte aligned, see the comment in doMachoLink + bind.Grow(sz) + bind.SetSize(sz) + + // TODO: export table. + // The symbols names are encoded as a trie. I'm really too lazy to do that + // for now. + // Without it, the symbols are not dynamically exported, so they cannot be + // e.g. dlsym'd. But internal linking is not the default in that case, so + // it is fine. +} + +// machoCodeSigSym creates and returns a symbol for code signature. +// The symbol context is left as zeros, which will be generated at the end +// (as it depends on the rest of the file). +func machoCodeSigSym(ctxt *Link, codeSize int64) loader.Sym { + ldr := ctxt.loader + cs := ldr.CreateSymForUpdate(".machocodesig", 0) + if !ctxt.NeedCodeSign() || ctxt.IsExternal() { + return cs.Sym() + } + sz := codesign.Size(codeSize, "a.out") + cs.Grow(sz) + cs.SetSize(sz) + return cs.Sym() +} + +// machoCodeSign code-signs Mach-O file fname with an ad-hoc signature. +// This is used for updating an external linker generated binary. +func machoCodeSign(ctxt *Link, fname string) error { + f, err := os.OpenFile(fname, os.O_RDWR, 0) + if err != nil { + return err + } + defer f.Close() + + mf, err := macho.NewFile(f) + if err != nil { + return err + } + if mf.Magic != macho.Magic64 { + Exitf("not 64-bit Mach-O file: %s", fname) + } + + // Find existing LC_CODE_SIGNATURE and __LINKEDIT segment + var sigOff, sigSz, csCmdOff, linkeditOff int64 + var linkeditSeg, textSeg *macho.Segment + loadOff := int64(machoHeaderSize64) + get32 := mf.ByteOrder.Uint32 + for _, l := range mf.Loads { + data := l.Raw() + cmd, sz := get32(data), get32(data[4:]) + if cmd == LC_CODE_SIGNATURE { + sigOff = int64(get32(data[8:])) + sigSz = int64(get32(data[12:])) + csCmdOff = loadOff + } + if seg, ok := l.(*macho.Segment); ok { + switch seg.Name { + case "__LINKEDIT": + linkeditSeg = seg + linkeditOff = loadOff + case "__TEXT": + textSeg = seg + } + } + loadOff += int64(sz) + } + + if sigOff == 0 { + // The C linker doesn't generate a signed binary, for some reason. + // Skip. + return nil + } + + fi, err := f.Stat() + if err != nil { + return err + } + if sigOff+sigSz != fi.Size() { + // We don't expect anything after the signature (this will invalidate + // the signature anyway.) + return fmt.Errorf("unexpected content after code signature") + } + + sz := codesign.Size(sigOff, "a.out") + if sz != sigSz { + // Update the load command, + var tmp [8]byte + mf.ByteOrder.PutUint32(tmp[:4], uint32(sz)) + _, err = f.WriteAt(tmp[:4], csCmdOff+12) + if err != nil { + return err + } + + // Uodate the __LINKEDIT segment. + segSz := sigOff + sz - int64(linkeditSeg.Offset) + mf.ByteOrder.PutUint64(tmp[:8], uint64(segSz)) + _, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Memsz))) + if err != nil { + return err + } + _, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Filesz))) + if err != nil { + return err + } + } + + cs := make([]byte, sz) + codesign.Sign(cs, f, "a.out", sigOff, int64(textSeg.Offset), int64(textSeg.Filesz), ctxt.IsExe() || ctxt.IsPIE()) + _, err = f.WriteAt(cs, sigOff) + if err != nil { + return err + } + err = f.Truncate(sigOff + sz) + return err +} diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go new file mode 100644 index 0000000..2ab7da9 --- /dev/null +++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go @@ -0,0 +1,438 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "compress/zlib" + "debug/macho" + "encoding/binary" + "fmt" + "io" + "os" + "reflect" + "unsafe" +) + +type loadCmd struct { + Cmd macho.LoadCmd + Len uint32 +} + +type dyldInfoCmd struct { + Cmd macho.LoadCmd + Len uint32 + RebaseOff, RebaseLen uint32 + BindOff, BindLen uint32 + WeakBindOff, WeakBindLen uint32 + LazyBindOff, LazyBindLen uint32 + ExportOff, ExportLen uint32 +} + +type linkEditDataCmd struct { + Cmd macho.LoadCmd + Len uint32 + DataOff, DataLen uint32 +} + +type encryptionInfoCmd struct { + Cmd macho.LoadCmd + Len uint32 + CryptOff, CryptLen uint32 + CryptId uint32 +} + +type loadCmdReader struct { + offset, next int64 + f *os.File + order binary.ByteOrder +} + +func (r *loadCmdReader) Next() (loadCmd, error) { + var cmd loadCmd + + r.offset = r.next + if _, err := r.f.Seek(r.offset, 0); err != nil { + return cmd, err + } + if err := binary.Read(r.f, r.order, &cmd); err != nil { + return cmd, err + } + r.next = r.offset + int64(cmd.Len) + return cmd, nil +} + +func (r loadCmdReader) ReadAt(offset int64, data interface{}) error { + if _, err := r.f.Seek(r.offset+offset, 0); err != nil { + return err + } + return binary.Read(r.f, r.order, data) +} + +func (r loadCmdReader) WriteAt(offset int64, data interface{}) error { + if _, err := r.f.Seek(r.offset+offset, 0); err != nil { + return err + } + return binary.Write(r.f, r.order, data) +} + +// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable. +// +// With internal linking, DWARF is embedded into the executable, this lets us do the +// same for external linking. +// exef is the file of the executable with no DWARF. It must have enough room in the macho +// header to add the DWARF sections. (Use ld's -headerpad option) +// exem is the macho representation of exef. +// dsym is the path to the macho file containing DWARF from dsymutil. +// outexe is the path where the combined executable should be saved. +func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe string) error { + dwarff, err := os.Open(dsym) + if err != nil { + return err + } + defer dwarff.Close() + outf, err := os.OpenFile(outexe, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return err + } + defer outf.Close() + dwarfm, err := macho.NewFile(dwarff) + if err != nil { + return err + } + defer dwarfm.Close() + + // The string table needs to be the last thing in the file + // for code signing to work. So we'll need to move the + // linkedit section, but all the others can be copied directly. + linkseg := exem.Segment("__LINKEDIT") + if linkseg == nil { + return fmt.Errorf("missing __LINKEDIT segment") + } + + if _, err := exef.Seek(0, 0); err != nil { + return err + } + if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil { + return err + } + + realdwarf := dwarfm.Segment("__DWARF") + if realdwarf == nil { + return fmt.Errorf("missing __DWARF segment") + } + + // Try to compress the DWARF sections. This includes some Apple + // proprietary sections like __apple_types. + compressedSects, compressedBytes, err := machoCompressSections(ctxt, dwarfm) + if err != nil { + return err + } + + // Now copy the dwarf data into the output. + // Kernel requires all loaded segments to be page-aligned in the file, + // even though we mark this one as being 0 bytes of virtual address space. + dwarfstart := Rnd(int64(linkseg.Offset), int64(*FlagRound)) + if _, err := outf.Seek(dwarfstart, 0); err != nil { + return err + } + + if _, err := dwarff.Seek(int64(realdwarf.Offset), 0); err != nil { + return err + } + + // Write out the compressed sections, or the originals if we gave up + // on compressing them. + var dwarfsize uint64 + if compressedBytes != nil { + dwarfsize = uint64(len(compressedBytes)) + if _, err := outf.Write(compressedBytes); err != nil { + return err + } + } else { + if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil { + return err + } + dwarfsize = realdwarf.Filesz + } + + // And finally the linkedit section. + if _, err := exef.Seek(int64(linkseg.Offset), 0); err != nil { + return err + } + linkstart := Rnd(dwarfstart+int64(dwarfsize), int64(*FlagRound)) + if _, err := outf.Seek(linkstart, 0); err != nil { + return err + } + if _, err := io.Copy(outf, exef); err != nil { + return err + } + + // Now we need to update the headers. + textsect := exem.Section("__text") + if textsect == nil { + return fmt.Errorf("missing __text section") + } + + cmdOffset := unsafe.Sizeof(exem.FileHeader) + if is64bit := exem.Magic == macho.Magic64; is64bit { + // mach_header_64 has one extra uint32. + cmdOffset += unsafe.Sizeof(exem.Magic) + } + dwarfCmdOffset := uint32(cmdOffset) + exem.FileHeader.Cmdsz + availablePadding := textsect.Offset - dwarfCmdOffset + if availablePadding < realdwarf.Len { + return fmt.Errorf("no room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding) + } + // First, copy the dwarf load command into the header. It will be + // updated later with new offsets and lengths as necessary. + if _, err := outf.Seek(int64(dwarfCmdOffset), 0); err != nil { + return err + } + if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil { + return err + } + if _, err := outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil { + return err + } + if err := binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil { + return err + } + if err := binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil { + return err + } + + reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder} + for i := uint32(0); i < exem.Ncmd; i++ { + cmd, err := reader.Next() + if err != nil { + return err + } + linkoffset := uint64(linkstart) - linkseg.Offset + switch cmd.Cmd { + case macho.LoadCmdSegment64: + err = machoUpdateSegment(reader, linkseg, linkoffset) + case macho.LoadCmdSegment: + panic("unexpected 32-bit segment") + case LC_DYLD_INFO, LC_DYLD_INFO_ONLY: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff") + case macho.LoadCmdSymtab: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.SymtabCmd{}, "Symoff", "Stroff") + case macho.LoadCmdDysymtab: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff") + case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS, + LC_DYLD_EXPORTS_TRIE, LC_DYLD_CHAINED_FIXUPS: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &linkEditDataCmd{}, "DataOff") + case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &encryptionInfoCmd{}, "CryptOff") + case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, + LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, + LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH, LC_ID_DYLIB, + LC_SYMSEG, LC_LOADFVMLIB, LC_IDFVMLIB, LC_IDENT, LC_FVMFILE, LC_PREPAGE, LC_ID_DYLINKER, + LC_ROUTINES, LC_SUB_FRAMEWORK, LC_SUB_UMBRELLA, LC_SUB_CLIENT, LC_SUB_LIBRARY, LC_TWOLEVEL_HINTS, + LC_PREBIND_CKSUM, LC_ROUTINES_64, LC_LAZY_LOAD_DYLIB, LC_LOAD_UPWARD_DYLIB, LC_DYLD_ENVIRONMENT, + LC_LINKER_OPTION, LC_LINKER_OPTIMIZATION_HINT, LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS, + LC_VERSION_NOTE, LC_BUILD_VERSION: + // Nothing to update + default: + err = fmt.Errorf("unknown load command 0x%x (%s)", int(cmd.Cmd), cmd.Cmd) + } + if err != nil { + return err + } + } + // Do the final update of the DWARF segment's load command. + return machoUpdateDwarfHeader(&reader, compressedSects, dwarfsize, dwarfstart, realdwarf) +} + +// machoCompressSections tries to compress the DWARF segments in dwarfm, +// returning the updated sections and segment contents, nils if the sections +// weren't compressed, or an error if there was a problem reading dwarfm. +func machoCompressSections(ctxt *Link, dwarfm *macho.File) ([]*macho.Section, []byte, error) { + if !ctxt.compressDWARF { + return nil, nil, nil + } + + dwarfseg := dwarfm.Segment("__DWARF") + var sects []*macho.Section + var buf bytes.Buffer + + for _, sect := range dwarfm.Sections { + if sect.Seg != "__DWARF" { + continue + } + + // As of writing, there are no relocations in dsymutil's output + // so there's no point in worrying about them. Bail out if that + // changes. + if sect.Nreloc != 0 { + return nil, nil, nil + } + + data, err := sect.Data() + if err != nil { + return nil, nil, err + } + + compressed, contents, err := machoCompressSection(data) + if err != nil { + return nil, nil, err + } + + newSec := *sect + newSec.Offset = uint32(dwarfseg.Offset) + uint32(buf.Len()) + newSec.Addr = dwarfseg.Addr + uint64(buf.Len()) + if compressed { + newSec.Name = "__z" + sect.Name[2:] + newSec.Size = uint64(len(contents)) + } + sects = append(sects, &newSec) + buf.Write(contents) + } + return sects, buf.Bytes(), nil +} + +// machoCompressSection compresses secBytes if it results in less data. +func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, err error) { + var buf bytes.Buffer + buf.WriteString("ZLIB") + var sizeBytes [8]byte + binary.BigEndian.PutUint64(sizeBytes[:], uint64(len(sectBytes))) + buf.Write(sizeBytes[:]) + + z := zlib.NewWriter(&buf) + if _, err := z.Write(sectBytes); err != nil { + return false, nil, err + } + if err := z.Close(); err != nil { + return false, nil, err + } + if buf.Len() >= len(sectBytes) { + return false, sectBytes, nil + } + return true, buf.Bytes(), nil +} + +// machoUpdateSegment updates the load command for a moved segment. +// Only the linkedit segment should move, and it should have 0 sections. +func machoUpdateSegment(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64) error { + var seg macho.Segment64 + if err := r.ReadAt(0, &seg); err != nil { + return err + } + + // Only the linkedit segment moved, anything before that is fine. + if seg.Offset < linkseg.Offset { + return nil + } + seg.Offset += linkoffset + if err := r.WriteAt(0, &seg); err != nil { + return err + } + // There shouldn't be any sections, but just to make sure... + return machoUpdateSections(r, &seg, linkoffset, nil) +} + +func machoUpdateSections(r loadCmdReader, seg *macho.Segment64, deltaOffset uint64, compressedSects []*macho.Section) error { + nsect := seg.Nsect + if nsect == 0 { + return nil + } + sectOffset := int64(unsafe.Sizeof(*seg)) + + var sect macho.Section64 + sectSize := int64(unsafe.Sizeof(sect)) + for i := uint32(0); i < nsect; i++ { + if err := r.ReadAt(sectOffset, §); 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, §); err != nil { + return err + } + sectOffset += sectSize + } + return nil +} + +// machoUpdateDwarfHeader updates the DWARF segment load command. +func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, dwarfsize uint64, dwarfstart int64, realdwarf *macho.Segment) error { + cmd, err := r.Next() + if err != nil { + return err + } + if cmd.Cmd != macho.LoadCmdSegment64 { + panic("not a Segment64") + } + var seg macho.Segment64 + if err := r.ReadAt(0, &seg); err != nil { + return err + } + seg.Offset = uint64(dwarfstart) + + if compressedSects != nil { + var segSize uint64 + for _, newSect := range compressedSects { + segSize += newSect.Size + } + seg.Filesz = segSize + } else { + seg.Filesz = dwarfsize + } + + // We want the DWARF segment to be considered non-loadable, so + // force vmaddr and vmsize to zero. In addition, set the initial + // protection to zero so as to make the dynamic loader happy, + // since otherwise it may complain that the vm size and file + // size don't match for the segment. See issues 21647 and 32673 + // for more context. Also useful to refer to the Apple dynamic + // loader source, specifically ImageLoaderMachO::sniffLoadCommands + // in ImageLoaderMachO.cpp (various versions can be found online, see + // https://opensource.apple.com/source/dyld/dyld-519.2.2/src/ImageLoaderMachO.cpp.auto.html + // as one example). + seg.Addr = 0 + seg.Memsz = 0 + seg.Prot = 0 + + if err := r.WriteAt(0, &seg); err != nil { + return err + } + return machoUpdateSections(*r, &seg, uint64(dwarfstart)-realdwarf.Offset, compressedSects) +} + +func machoUpdateLoadCommand(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64, cmd interface{}, fields ...string) error { + if err := r.ReadAt(0, cmd); err != nil { + return err + } + value := reflect.Indirect(reflect.ValueOf(cmd)) + + for _, name := range fields { + field := value.FieldByName(name) + if fieldval := field.Uint(); fieldval >= linkseg.Offset { + field.SetUint(fieldval + linkoffset) + } + } + if err := r.WriteAt(0, cmd); err != nil { + return err + } + return nil +} diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go new file mode 100644 index 0000000..34411d7 --- /dev/null +++ b/src/cmd/link/internal/ld/main.go @@ -0,0 +1,444 @@ +// Inferno utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/quoted" + "cmd/internal/sys" + "cmd/link/internal/benchmark" + "flag" + "internal/buildcfg" + "log" + "os" + "runtime" + "runtime/pprof" + "strings" +) + +var ( + pkglistfornote []byte + windowsgui bool // writes a "GUI binary" instead of a "console binary" + ownTmpDir bool // set to true if tmp dir created by linker (e.g. no -tmpdir) +) + +func init() { + flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...") + flag.Var(&flagExtld, "extld", "use `linker` when linking in external mode") + flag.Var(&flagExtldflags, "extldflags", "pass `flags` to external linker") +} + +// Flags used by the linker. The exported flags are used by the architecture-specific packages. +var ( + flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id") + + flagOutfile = flag.String("o", "", "write output to `file`") + flagPluginPath = flag.String("pluginpath", "", "full path name for plugin") + + flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`") + flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph") + flagRace = flag.Bool("race", false, "enable race detector") + flagMsan = flag.Bool("msan", false, "enable MSan interface") + flagAsan = flag.Bool("asan", false, "enable ASan interface") + flagAslr = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows") + + flagFieldTrack = flag.String("k", "", "set field tracking `symbol`") + flagLibGCC = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable") + flagTmpdir = flag.String("tmpdir", "", "use `directory` for temporary files") + + flagExtld quoted.Flag + flagExtldflags quoted.Flag + flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive") + + flagCaptureHostObjs = flag.String("capturehostobjs", "", "capture host object files loaded during internal linking to specified dir") + + flagA = flag.Bool("a", false, "no-op (deprecated)") + FlagC = flag.Bool("c", false, "dump call graph") + FlagD = flag.Bool("d", false, "disable dynamic executable") + flagF = flag.Bool("f", false, "ignore version mismatch") + flagG = flag.Bool("g", false, "disable go package data checks") + flagH = flag.Bool("h", false, "halt on error") + flagN = flag.Bool("n", false, "dump symbol table") + FlagS = flag.Bool("s", false, "disable symbol table") + FlagW = flag.Bool("w", false, "disable DWARF generation") + flag8 bool // use 64-bit addresses in symbol table + flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") + FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines") + FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size") + flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph") + FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).") + FlagRound = flag.Int("R", -1, "set address rounding `quantum`") + FlagTextAddr = flag.Int64("T", -1, "set text segment `address`") + flagEntrySymbol = flag.String("E", "", "set `entry` symbol name") + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") + memprofile = flag.String("memprofile", "", "write memory profile to `file`") + memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`") + benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking") + benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof") +) + +// Main is the main entry point for the linker code. +func Main(arch *sys.Arch, theArch Arch) { + thearch = theArch + ctxt := linknew(arch) + ctxt.Bso = bufio.NewWriter(os.Stdout) + + // For testing behavior of go command when tools crash silently. + // Undocumented, not in standard flag parser to avoid + // exposing in usage message. + for _, arg := range os.Args { + if arg == "-crash_for_testing" { + os.Exit(2) + } + } + + if final := gorootFinal(); final == "$GOROOT" { + // cmd/go sets GOROOT_FINAL to the dummy value "$GOROOT" when -trimpath is set, + // but runtime.GOROOT() should return the empty string, not a bogus value. + // (See https://go.dev/issue/51461.) + } else { + addstrdata1(ctxt, "runtime.defaultGOROOT="+final) + } + + buildVersion := buildcfg.Version + if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { + buildVersion += " X:" + goexperiment + } + addstrdata1(ctxt, "runtime.buildVersion="+buildVersion) + + // TODO(matloob): define these above and then check flag values here + if ctxt.Arch.Family == sys.AMD64 && buildcfg.GOOS == "plan9" { + flag.BoolVar(&flag8, "8", false, "use 64-bit addresses in symbol table") + } + flagHeadType := flag.String("H", "", "set header `type`") + flag.BoolVar(&ctxt.linkShared, "linkshared", false, "link against installed Go shared libraries") + flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`") + flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`") + flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible") + objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) + objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) }) + objabi.AddVersionFlag() // -V + objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) }) + objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog) + objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg) + + objabi.Flagparse(usage) + + if ctxt.Debugvlog > 0 { + // dump symbol info on crash + defer func() { ctxt.loader.Dump() }() + } + + switch *flagHeadType { + case "": + case "windowsgui": + ctxt.HeadType = objabi.Hwindows + windowsgui = true + default: + if err := ctxt.HeadType.Set(*flagHeadType); err != nil { + Errorf(nil, "%v", err) + usage() + } + } + if ctxt.HeadType == objabi.Hunknown { + ctxt.HeadType.Set(buildcfg.GOOS) + } + + if !*flagAslr && ctxt.BuildMode != BuildModeCShared { + Errorf(nil, "-aslr=false is only allowed for -buildmode=c-shared") + usage() + } + + if *FlagD && ctxt.UsesLibc() { + Exitf("dynamic linking required on %s; -d flag cannot be used", buildcfg.GOOS) + } + + checkStrictDups = *FlagStrictDups + + if ctxt.IsDarwin() && ctxt.BuildMode == BuildModeCShared { + *FlagW = true // default to -w in c-shared mode on darwin, see #61229 + } + + if !buildcfg.Experiment.RegabiWrappers { + abiInternalVer = 0 + } + + startProfile() + if ctxt.BuildMode == BuildModeUnset { + ctxt.BuildMode.Set("exe") + } + + if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 { + usage() + } + + if *flagOutfile == "" { + *flagOutfile = "a.out" + if ctxt.HeadType == objabi.Hwindows { + *flagOutfile += ".exe" + } + } + + interpreter = *flagInterpreter + + if *flagBuildid == "" && ctxt.Target.IsOpenbsd() { + // TODO(jsing): Remove once direct syscalls are no longer in use. + // OpenBSD 6.7 onwards will not permit direct syscalls from a + // dynamically linked binary unless it identifies the binary + // contains a .note.go.buildid ELF note. See issue #36435. + *flagBuildid = "go-openbsd" + } + + // enable benchmarking + var bench *benchmark.Metrics + if len(*benchmarkFlag) != 0 { + if *benchmarkFlag == "mem" { + bench = benchmark.New(benchmark.GC, *benchmarkFileFlag) + } else if *benchmarkFlag == "cpu" { + bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag) + } else { + Errorf(nil, "unknown benchmark flag: %q", *benchmarkFlag) + usage() + } + } + + bench.Start("libinit") + libinit(ctxt) // creates outfile + bench.Start("computeTLSOffset") + ctxt.computeTLSOffset() + bench.Start("Archinit") + thearch.Archinit(ctxt) + + if ctxt.linkShared && !ctxt.IsELF { + Exitf("-linkshared can only be used on elf systems") + } + + if ctxt.Debugvlog != 0 { + ctxt.Logf("HEADER = -H%d -T0x%x -R0x%x\n", ctxt.HeadType, uint64(*FlagTextAddr), uint32(*FlagRound)) + } + + zerofp := goobj.FingerprintType{} + switch ctxt.BuildMode { + case BuildModeShared: + for i := 0; i < flag.NArg(); i++ { + arg := flag.Arg(i) + parts := strings.SplitN(arg, "=", 2) + var pkgpath, file string + if len(parts) == 1 { + pkgpath, file = "main", arg + } else { + pkgpath, file = parts[0], parts[1] + } + pkglistfornote = append(pkglistfornote, pkgpath...) + pkglistfornote = append(pkglistfornote, '\n') + addlibpath(ctxt, "command line", "command line", file, pkgpath, "", zerofp) + } + case BuildModePlugin: + addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "", zerofp) + default: + addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "", zerofp) + } + bench.Start("loadlib") + ctxt.loadlib() + + bench.Start("deadcode") + deadcode(ctxt) + + bench.Start("linksetup") + ctxt.linksetup() + + bench.Start("dostrdata") + ctxt.dostrdata() + if buildcfg.Experiment.FieldTrack { + bench.Start("fieldtrack") + fieldtrack(ctxt.Arch, ctxt.loader) + } + + bench.Start("dwarfGenerateDebugInfo") + dwarfGenerateDebugInfo(ctxt) + + bench.Start("callgraph") + ctxt.callgraph() + + bench.Start("doStackCheck") + ctxt.doStackCheck() + + bench.Start("mangleTypeSym") + ctxt.mangleTypeSym() + + if ctxt.IsELF { + bench.Start("doelf") + ctxt.doelf() + } + if ctxt.IsDarwin() { + bench.Start("domacho") + ctxt.domacho() + } + if ctxt.IsWindows() { + bench.Start("dope") + ctxt.dope() + bench.Start("windynrelocsyms") + ctxt.windynrelocsyms() + } + if ctxt.IsAIX() { + bench.Start("doxcoff") + ctxt.doxcoff() + } + + bench.Start("textbuildid") + ctxt.textbuildid() + bench.Start("addexport") + ctxt.setArchSyms() + ctxt.addexport() + bench.Start("Gentext") + thearch.Gentext(ctxt, ctxt.loader) // trampolines, call stubs, etc. + + bench.Start("textaddress") + ctxt.textaddress() + bench.Start("typelink") + ctxt.typelink() + bench.Start("buildinfo") + ctxt.buildinfo() + bench.Start("pclntab") + containers := ctxt.findContainerSyms() + pclnState := ctxt.pclntab(containers) + bench.Start("findfunctab") + ctxt.findfunctab(pclnState, containers) + bench.Start("dwarfGenerateDebugSyms") + dwarfGenerateDebugSyms(ctxt) + bench.Start("symtab") + symGroupType := ctxt.symtab(pclnState) + bench.Start("dodata") + ctxt.dodata(symGroupType) + bench.Start("address") + order := ctxt.address() + bench.Start("dwarfcompress") + dwarfcompress(ctxt) + bench.Start("layout") + filesize := ctxt.layout(order) + + // Write out the output file. + // It is split into two parts (Asmb and Asmb2). The first + // part writes most of the content (sections and segments), + // for which we have computed the size and offset, in a + // mmap'd region. The second part writes more content, for + // which we don't know the size. + if ctxt.Arch.Family != sys.Wasm { + // Don't mmap if we're building for Wasm. Wasm file + // layout is very different so filesize is meaningless. + if err := ctxt.Out.Mmap(filesize); err != nil { + Exitf("mapping output file failed: %v", err) + } + } + // asmb will redirect symbols to the output file mmap, and relocations + // will be applied directly there. + bench.Start("Asmb") + asmb(ctxt) + + exitIfErrors() + + // Generate additional symbols for the native symbol table just prior + // to code generation. + bench.Start("GenSymsLate") + if thearch.GenSymsLate != nil { + thearch.GenSymsLate(ctxt, ctxt.loader) + } + + bench.Start("Asmb2") + asmb2(ctxt) + + bench.Start("Munmap") + ctxt.Out.Close() // Close handles Munmapping if necessary. + + bench.Start("hostlink") + ctxt.hostlink() + if ctxt.Debugvlog != 0 { + ctxt.Logf("%s", ctxt.loader.Stat()) + ctxt.Logf("%d liveness data\n", liveness) + } + bench.Start("Flush") + ctxt.Bso.Flush() + bench.Start("archive") + ctxt.archive() + bench.Report(os.Stdout) + + errorexit() +} + +type Rpath struct { + set bool + val string +} + +func (r *Rpath) Set(val string) error { + r.set = true + r.val = val + return nil +} + +func (r *Rpath) String() string { + return r.val +} + +func startProfile() { + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatalf("%v", err) + } + if err := pprof.StartCPUProfile(f); err != nil { + log.Fatalf("%v", err) + } + AtExit(pprof.StopCPUProfile) + } + if *memprofile != "" { + if *memprofilerate != 0 { + runtime.MemProfileRate = int(*memprofilerate) + } + f, err := os.Create(*memprofile) + if err != nil { + log.Fatalf("%v", err) + } + AtExit(func() { + // Profile all outstanding allocations. + runtime.GC() + // compilebench parses the memory profile to extract memstats, + // which are only written in the legacy pprof format. + // See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap. + const writeLegacyFormat = 1 + if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil { + log.Fatalf("%v", err) + } + }) + } +} diff --git a/src/cmd/link/internal/ld/msync_darwin_libc.go b/src/cmd/link/internal/ld/msync_darwin_libc.go new file mode 100644 index 0000000..eb2a526 --- /dev/null +++ b/src/cmd/link/internal/ld/msync_darwin_libc.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && go1.20 + +package ld + +import _ "unsafe" // for go:linkname + +//go:linkname msync syscall.msync +func msync(b []byte, flags int) (err error) diff --git a/src/cmd/link/internal/ld/msync_darwin_syscall.go b/src/cmd/link/internal/ld/msync_darwin_syscall.go new file mode 100644 index 0000000..270d9f3 --- /dev/null +++ b/src/cmd/link/internal/ld/msync_darwin_syscall.go @@ -0,0 +1,24 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && !go1.20 + +package ld + +import ( + "syscall" + "unsafe" +) + +func msync(b []byte, flags int) (err error) { + var p unsafe.Pointer + if len(b) > 0 { + p = unsafe.Pointer(&b[0]) + } + _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(p), uintptr(len(b)), uintptr(flags)) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/cmd/link/internal/ld/nooptcgolink_test.go b/src/cmd/link/internal/ld/nooptcgolink_test.go new file mode 100644 index 0000000..646583f --- /dev/null +++ b/src/cmd/link/internal/ld/nooptcgolink_test.go @@ -0,0 +1,29 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "internal/testenv" + "path/filepath" + "testing" +) + +func TestNooptCgoBuild(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + t.Parallel() + + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + dir := t.TempDir() + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out")) + cmd.Dir = filepath.Join(testenv.GOROOT(t), "src", "runtime", "testdata", "testprogcgo") + out, err := cmd.CombinedOutput() + if err != nil { + t.Logf("go build output: %s", out) + t.Fatal(err) + } +} diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go new file mode 100644 index 0000000..54fafca --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf.go @@ -0,0 +1,325 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/sys" + "cmd/link/internal/loader" + "encoding/binary" + "errors" + "log" + "os" +) + +// If fallocate is not supported on this platform, return this error. The error +// is ignored where needed, and OutBuf writes to heap memory. +var errNoFallocate = errors.New("operation not supported") + +const outbufMode = 0775 + +// OutBuf is a buffered file writer. +// +// It is similar to the Writer in cmd/internal/bio with a few small differences. +// +// First, it tracks the output architecture and uses it to provide +// endian helpers. +// +// Second, it provides a very cheap offset counter that doesn't require +// any system calls to read the value. +// +// Third, it also mmaps the output file (if available). The intended usage is: +// - Mmap the output file +// - Write the content +// - possibly apply any edits in the output buffer +// - possibly write more content to the file. These writes take place in a heap +// backed buffer that will get synced to disk. +// - Munmap the output file +// +// And finally, it provides a mechanism by which you can multithread the +// writing of output files. This mechanism is accomplished by copying a OutBuf, +// and using it in the thread/goroutine. +// +// Parallel OutBuf is intended to be used like: +// +// func write(out *OutBuf) { +// var wg sync.WaitGroup +// for i := 0; i < 10; i++ { +// wg.Add(1) +// view, err := out.View(start[i]) +// if err != nil { +// // handle output +// continue +// } +// go func(out *OutBuf, i int) { +// // do output +// wg.Done() +// }(view, i) +// } +// wg.Wait() +// } +type OutBuf struct { + arch *sys.Arch + off int64 + + buf []byte // backing store of mmap'd output file + heap []byte // backing store for non-mmapped data + + name string + f *os.File + encbuf [8]byte // temp buffer used by WriteN methods + isView bool // true if created from View() +} + +func (out *OutBuf) Open(name string) error { + if out.f != nil { + return errors.New("cannot open more than one file") + } + f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, outbufMode) + if err != nil { + return err + } + out.off = 0 + out.name = name + out.f = f + return nil +} + +func NewOutBuf(arch *sys.Arch) *OutBuf { + return &OutBuf{ + arch: arch, + } +} + +var viewError = errors.New("output not mmapped") + +func (out *OutBuf) View(start uint64) (*OutBuf, error) { + return &OutBuf{ + arch: out.arch, + name: out.name, + buf: out.buf, + heap: out.heap, + off: int64(start), + isView: true, + }, nil +} + +var viewCloseError = errors.New("cannot Close OutBuf from View") + +func (out *OutBuf) Close() error { + if out.isView { + return viewCloseError + } + if out.isMmapped() { + out.copyHeap() + out.purgeSignatureCache() + out.munmap() + } + if out.f == nil { + return nil + } + if len(out.heap) != 0 { + if _, err := out.f.Write(out.heap); err != nil { + return err + } + } + if err := out.f.Close(); err != nil { + return err + } + out.f = nil + return nil +} + +// ErrorClose closes the output file (if any). +// It is supposed to be called only at exit on error, so it doesn't do +// any clean up or buffer flushing, just closes the file. +func (out *OutBuf) ErrorClose() { + if out.isView { + panic(viewCloseError) + } + if out.f == nil { + return + } + out.f.Close() // best effort, ignore error + out.f = nil +} + +// isMmapped returns true if the OutBuf is mmaped. +func (out *OutBuf) isMmapped() bool { + return len(out.buf) != 0 +} + +// Data returns the whole written OutBuf as a byte slice. +func (out *OutBuf) Data() []byte { + if out.isMmapped() { + out.copyHeap() + return out.buf + } + return out.heap +} + +// copyHeap copies the heap to the mmapped section of memory, returning true if +// a copy takes place. +func (out *OutBuf) copyHeap() bool { + if !out.isMmapped() { // only valuable for mmapped OutBufs. + return false + } + if out.isView { + panic("can't copyHeap a view") + } + + bufLen := len(out.buf) + heapLen := len(out.heap) + total := uint64(bufLen + heapLen) + if heapLen != 0 { + if err := out.Mmap(total); err != nil { // Mmap will copy out.heap over to out.buf + Exitf("mapping output file failed: %v", err) + } + } + return true +} + +// maxOutBufHeapLen limits the growth of the heap area. +const maxOutBufHeapLen = 10 << 20 + +// writeLoc determines the write location if a buffer is mmaped. +// We maintain two write buffers, an mmapped section, and a heap section for +// writing. When the mmapped section is full, we switch over the heap memory +// for writing. +func (out *OutBuf) writeLoc(lenToWrite int64) (int64, []byte) { + // See if we have enough space in the mmaped area. + bufLen := int64(len(out.buf)) + if out.off+lenToWrite <= bufLen { + return out.off, out.buf + } + + // Not enough space in the mmaped area, write to heap area instead. + heapPos := out.off - bufLen + heapLen := int64(len(out.heap)) + lenNeeded := heapPos + lenToWrite + if lenNeeded > heapLen { // do we need to grow the heap storage? + // The heap variables aren't protected by a mutex. For now, just bomb if you + // try to use OutBuf in parallel. (Note this probably could be fixed.) + if out.isView { + panic("cannot write to heap in parallel") + } + // See if our heap would grow to be too large, and if so, copy it to the end + // of the mmapped area. + if heapLen > maxOutBufHeapLen && out.copyHeap() { + heapPos -= heapLen + lenNeeded = heapPos + lenToWrite + heapLen = 0 + } + out.heap = append(out.heap, make([]byte, lenNeeded-heapLen)...) + } + return heapPos, out.heap +} + +func (out *OutBuf) SeekSet(p int64) { + out.off = p +} + +func (out *OutBuf) Offset() int64 { + return out.off +} + +// Write writes the contents of v to the buffer. +func (out *OutBuf) Write(v []byte) (int, error) { + n := len(v) + pos, buf := out.writeLoc(int64(n)) + copy(buf[pos:], v) + out.off += int64(n) + return n, nil +} + +func (out *OutBuf) Write8(v uint8) { + pos, buf := out.writeLoc(1) + buf[pos] = v + out.off++ +} + +// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface. +func (out *OutBuf) WriteByte(v byte) error { + out.Write8(v) + return nil +} + +func (out *OutBuf) Write16(v uint16) { + out.arch.ByteOrder.PutUint16(out.encbuf[:], v) + out.Write(out.encbuf[:2]) +} + +func (out *OutBuf) Write32(v uint32) { + out.arch.ByteOrder.PutUint32(out.encbuf[:], v) + out.Write(out.encbuf[:4]) +} + +func (out *OutBuf) Write32b(v uint32) { + binary.BigEndian.PutUint32(out.encbuf[:], v) + out.Write(out.encbuf[:4]) +} + +func (out *OutBuf) Write64(v uint64) { + out.arch.ByteOrder.PutUint64(out.encbuf[:], v) + out.Write(out.encbuf[:8]) +} + +func (out *OutBuf) Write64b(v uint64) { + binary.BigEndian.PutUint64(out.encbuf[:], v) + out.Write(out.encbuf[:8]) +} + +func (out *OutBuf) WriteString(s string) { + pos, buf := out.writeLoc(int64(len(s))) + n := copy(buf[pos:], s) + if n != len(s) { + log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s)) + } + out.off += int64(n) +} + +// WriteStringN writes the first n bytes of s. +// If n is larger than len(s) then it is padded with zero bytes. +func (out *OutBuf) WriteStringN(s string, n int) { + out.WriteStringPad(s, n, zeros[:]) +} + +// WriteStringPad writes the first n bytes of s. +// If n is larger than len(s) then it is padded with the bytes in pad (repeated as needed). +func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) { + if len(s) >= n { + out.WriteString(s[:n]) + } else { + out.WriteString(s) + n -= len(s) + for n > len(pad) { + out.Write(pad) + n -= len(pad) + + } + out.Write(pad[:n]) + } +} + +// WriteSym writes the content of a Symbol, and returns the output buffer +// that we just wrote, so we can apply further edit to the symbol content. +// For generator symbols, it also sets the symbol's Data to the output +// buffer. +func (out *OutBuf) WriteSym(ldr *loader.Loader, s loader.Sym) []byte { + if !ldr.IsGeneratedSym(s) { + P := ldr.Data(s) + n := int64(len(P)) + pos, buf := out.writeLoc(n) + copy(buf[pos:], P) + out.off += n + ldr.FreeData(s) + return buf[pos : pos+n] + } else { + n := ldr.SymSize(s) + pos, buf := out.writeLoc(n) + out.off += n + ldr.MakeSymbolUpdater(s).SetData(buf[pos : pos+n]) + return buf[pos : pos+n] + } +} diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go new file mode 100644 index 0000000..17f7e2a --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_darwin.go @@ -0,0 +1,48 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "syscall" + "unsafe" +) + +// Implemented in the syscall package. +// +//go:linkname fcntl syscall.fcntl +func fcntl(fd int, cmd int, arg int) (int, error) + +func (out *OutBuf) fallocate(size uint64) error { + stat, err := out.f.Stat() + if err != nil { + return err + } + // F_PEOFPOSMODE allocates from the end of the file, so we want the size difference. + // Apparently, it uses the end of the allocation, instead of the logical end of the + // file. + cursize := uint64(stat.Sys().(*syscall.Stat_t).Blocks * 512) // allocated size + if size <= cursize { + return nil + } + + store := &syscall.Fstore_t{ + Flags: syscall.F_ALLOCATEALL, + Posmode: syscall.F_PEOFPOSMODE, + Offset: 0, + Length: int64(size - cursize), + } + + _, err = fcntl(int(out.f.Fd()), syscall.F_PREALLOCATE, int(uintptr(unsafe.Pointer(store)))) + return err +} + +func (out *OutBuf) purgeSignatureCache() { + // Apparently, the Darwin kernel may cache the code signature at mmap. + // When we mmap the output buffer, it doesn't have a code signature + // (as we haven't generated one). Invalidate the kernel cache now that + // we have generated the signature. See issue #42684. + msync(out.buf, syscall.MS_INVALIDATE) + // Best effort. Ignore error. +} diff --git a/src/cmd/link/internal/ld/outbuf_linux.go b/src/cmd/link/internal/ld/outbuf_linux.go new file mode 100644 index 0000000..bd9a0c6 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_linux.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import "syscall" + +func (out *OutBuf) fallocate(size uint64) error { + return syscall.Fallocate(int(out.f.Fd()), 0, 0, int64(size)) +} diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go new file mode 100644 index 0000000..7bb728a --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_mmap.go @@ -0,0 +1,59 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || (solaris && go1.20) + +package ld + +import ( + "syscall" +) + +// Mmap maps the output file with the given size. It unmaps the old mapping +// if it is already mapped. It also flushes any in-heap data to the new +// mapping. +func (out *OutBuf) Mmap(filesize uint64) (err error) { + oldlen := len(out.buf) + if oldlen != 0 { + out.munmap() + } + + for { + if err = out.fallocate(filesize); err != syscall.EINTR { + break + } + } + if err != nil { + // Some file systems do not support fallocate. We ignore that error as linking + // can still take place, but you might SIGBUS when you write to the mmapped + // area. + if err != syscall.ENOTSUP && err != syscall.EPERM && err != errNoFallocate { + return err + } + } + err = out.f.Truncate(int64(filesize)) + if err != nil { + Exitf("resize output file failed: %v", err) + } + out.buf, err = syscall.Mmap(int(out.f.Fd()), 0, int(filesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FILE) + if err != nil { + return err + } + + // copy heap to new mapping + if uint64(oldlen+len(out.heap)) > filesize { + panic("mmap size too small") + } + copy(out.buf[oldlen:], out.heap) + out.heap = out.heap[:0] + return nil +} + +func (out *OutBuf) munmap() { + if out.buf == nil { + return + } + syscall.Munmap(out.buf) + out.buf = nil +} diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go new file mode 100644 index 0000000..dd5afc6 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_nofallocate.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !darwin && !linux + +package ld + +func (out *OutBuf) fallocate(size uint64) error { + return errNoFallocate +} diff --git a/src/cmd/link/internal/ld/outbuf_nommap.go b/src/cmd/link/internal/ld/outbuf_nommap.go new file mode 100644 index 0000000..b1d3d27 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_nommap.go @@ -0,0 +1,22 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !(solaris && go1.20) && !windows + +package ld + +// Mmap allocates an in-heap output buffer with the given size. It copies +// any old data (if any) to the new buffer. +func (out *OutBuf) Mmap(filesize uint64) error { + // We need space to put all the symbols before we apply relocations. + oldheap := out.heap + if filesize < uint64(len(oldheap)) { + panic("mmap size too small") + } + out.heap = make([]byte, filesize) + copy(out.heap, oldheap) + return nil +} + +func (out *OutBuf) munmap() { panic("unreachable") } diff --git a/src/cmd/link/internal/ld/outbuf_notdarwin.go b/src/cmd/link/internal/ld/outbuf_notdarwin.go new file mode 100644 index 0000000..f9caa41 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_notdarwin.go @@ -0,0 +1,10 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !darwin +// +build !darwin + +package ld + +func (out *OutBuf) purgeSignatureCache() {} diff --git a/src/cmd/link/internal/ld/outbuf_test.go b/src/cmd/link/internal/ld/outbuf_test.go new file mode 100644 index 0000000..a7b105f --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_test.go @@ -0,0 +1,93 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "path/filepath" + "runtime" + "testing" +) + +// TestMMap ensures that we can actually mmap on every supported platform. +func TestMMap(t *testing.T) { + switch runtime.GOOS { + default: + t.Skip("unsupported OS") + case "aix", "darwin", "ios", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "windows": + } + dir := t.TempDir() + filename := filepath.Join(dir, "foo.out") + ob := NewOutBuf(nil) + if err := ob.Open(filename); err != nil { + t.Fatalf("error opening file: %v", err) + } + defer ob.Close() + if err := ob.Mmap(1 << 20); err != nil { + t.Errorf("error mmapping file %v", err) + } + if !ob.isMmapped() { + t.Errorf("should be mmapped") + } +} + +// TestWriteLoc ensures that the math surrounding writeLoc is correct. +func TestWriteLoc(t *testing.T) { + tests := []struct { + bufLen int + off int64 + heapLen int + lenToWrite int64 + expectedHeapLen int + writePos int64 + addressInHeap bool + }{ + {100, 0, 0, 100, 0, 0, false}, + {100, 100, 0, 100, 100, 0, true}, + {10, 10, 0, 100, 100, 0, true}, + {10, 20, 10, 100, 110, 10, true}, + {0, 0, 0, 100, 100, 0, true}, + } + + for i, test := range tests { + ob := &OutBuf{ + buf: make([]byte, test.bufLen), + off: test.off, + heap: make([]byte, test.heapLen), + } + pos, buf := ob.writeLoc(test.lenToWrite) + if pos != test.writePos { + t.Errorf("[%d] position = %d, expected %d", i, pos, test.writePos) + } + message := "mmapped area" + expected := ob.buf + if test.addressInHeap { + message = "heap" + expected = ob.heap + } + if &buf[0] != &expected[0] { + t.Errorf("[%d] expected position to be %q", i, message) + } + if len(ob.heap) != test.expectedHeapLen { + t.Errorf("[%d] expected len(ob.heap) == %d, got %d", i, test.expectedHeapLen, len(ob.heap)) + } + } +} + +func TestIsMmapped(t *testing.T) { + tests := []struct { + length int + expected bool + }{ + {0, false}, + {1, true}, + } + for i, test := range tests { + ob := &OutBuf{buf: make([]byte, test.length)} + if v := ob.isMmapped(); v != test.expected { + + t.Errorf("[%d] isMmapped == %t, expected %t", i, v, test.expected) + } + } +} diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go new file mode 100644 index 0000000..95937e7 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_windows.go @@ -0,0 +1,79 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "internal/unsafeheader" + "syscall" + "unsafe" +) + +// Mmap maps the output file with the given size. It unmaps the old mapping +// if it is already mapped. It also flushes any in-heap data to the new +// mapping. +func (out *OutBuf) Mmap(filesize uint64) error { + oldlen := len(out.buf) + if oldlen != 0 { + out.munmap() + } + + err := out.f.Truncate(int64(filesize)) + if err != nil { + Exitf("resize output file failed: %v", err) + } + + low, high := uint32(filesize), uint32(filesize>>32) + fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READWRITE, high, low, nil) + if err != nil { + return err + } + defer syscall.CloseHandle(fmap) + + ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ|syscall.FILE_MAP_WRITE, 0, 0, uintptr(filesize)) + if err != nil { + return err + } + bufHdr := (*unsafeheader.Slice)(unsafe.Pointer(&out.buf)) + bufHdr.Data = unsafe.Pointer(ptr) + bufHdr.Len = int(filesize) + bufHdr.Cap = int(filesize) + + // copy heap to new mapping + if uint64(oldlen+len(out.heap)) > filesize { + panic("mmap size too small") + } + copy(out.buf[oldlen:], out.heap) + out.heap = out.heap[:0] + return nil +} + +func (out *OutBuf) munmap() { + if out.buf == nil { + return + } + // Apparently unmapping without flush may cause ACCESS_DENIED error + // (see issue 38440). + err := syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0) + if err != nil { + Exitf("FlushViewOfFile failed: %v", err) + } + // Issue 44817: apparently the call below may be needed (according + // to the Windows docs) in addition to the FlushViewOfFile call + // above, " ... to flush all the dirty pages plus the metadata for + // the file and ensure that they are physically written to disk". + // Windows DOC links: + // + // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-flushviewoffile + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers + err = syscall.FlushFileBuffers(syscall.Handle(out.f.Fd())) + if err != nil { + Exitf("FlushFileBuffers failed: %v", err) + } + err = syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&out.buf[0]))) + out.buf = nil + if err != nil { + Exitf("UnmapViewOfFile failed: %v", err) + } +} diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go new file mode 100644 index 0000000..34ab86c --- /dev/null +++ b/src/cmd/link/internal/ld/pcln.go @@ -0,0 +1,935 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "internal/buildcfg" + "os" + "path/filepath" + "strings" +) + +const funcSize = 11 * 4 // funcSize is the size of the _func object in runtime/runtime2.go + +// pclntab holds the state needed for pclntab generation. +type pclntab struct { + // The first and last functions found. + firstFunc, lastFunc loader.Sym + + // Running total size of pclntab. + size int64 + + // runtime.pclntab's symbols + carrier loader.Sym + pclntab loader.Sym + pcheader loader.Sym + funcnametab loader.Sym + findfunctab loader.Sym + cutab loader.Sym + filetab loader.Sym + pctab loader.Sym + + // The number of functions + number of TEXT sections - 1. This is such an + // unexpected value because platforms that have more than one TEXT section + // get a dummy function inserted between because the external linker can place + // functions in those areas. We mark those areas as not covered by the Go + // runtime. + // + // On most platforms this is the number of reachable functions. + nfunc int32 + + // The number of filenames in runtime.filetab. + nfiles uint32 +} + +// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym. +// It is the caller's responsibility to save the symbol in state. +func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym { + size = Rnd(size, int64(ctxt.Arch.PtrSize)) + state.size += size + s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f) + ctxt.loader.SetAttrReachable(s, true) + ctxt.loader.SetCarrierSym(s, state.carrier) + ctxt.loader.SetAttrNotInSymbolTable(s, true) + return s +} + +// makePclntab makes a pclntab object, and assembles all the compilation units +// we'll need to write pclntab. Returns the pclntab structure, a slice of the +// CompilationUnits we need, and a slice of the function symbols we need to +// generate pclntab. +func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) { + ldr := ctxt.loader + state := new(pclntab) + + // Gather some basic stats and info. + seenCUs := make(map[*sym.CompilationUnit]struct{}) + compUnits := []*sym.CompilationUnit{} + funcs := []loader.Sym{} + + for _, s := range ctxt.Textp { + if !emitPcln(ctxt, s, container) { + continue + } + funcs = append(funcs, s) + state.nfunc++ + if state.firstFunc == 0 { + state.firstFunc = s + } + state.lastFunc = s + + // We need to keep track of all compilation units we see. Some symbols + // (eg, go.buildid, _cgoexp_, etc) won't have a compilation unit. + cu := ldr.SymUnit(s) + if _, ok := seenCUs[cu]; cu != nil && !ok { + seenCUs[cu] = struct{}{} + cu.PclnIndex = len(compUnits) + compUnits = append(compUnits, cu) + } + } + return state, compUnits, funcs +} + +func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool { + // We want to generate func table entries only for the "lowest + // level" symbols, not containers of subsymbols. + return !container.Has(s) +} + +func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 { + ldr := ctxt.loader + target := ctxt.Target + deferreturn := uint32(0) + lastWasmAddr := uint32(0) + + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if target.IsWasm() && r.Type() == objabi.R_ADDR { + // wasm/ssa.go generates an ARESUMEPOINT just + // before the deferreturn call. The "PC" of + // the deferreturn call is stored in the + // R_ADDR relocation on the ARESUMEPOINT. + lastWasmAddr = uint32(r.Add()) + } + if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) { + if target.IsWasm() { + deferreturn = lastWasmAddr - 1 + } else { + // Note: the relocation target is in the call instruction, but + // is not necessarily the whole instruction (for instance, on + // x86 the relocation applies to bytes [1:5] of the 5 byte call + // instruction). + deferreturn = uint32(r.Off()) + switch target.Arch.Family { + case sys.AMD64, sys.I386: + deferreturn-- + case sys.ARM, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64: + // no change + case sys.S390X: + deferreturn -= 2 + default: + panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family)) + } + } + break // only need one + } + } + return deferreturn +} + +// genInlTreeSym generates the InlTree sym for a function with the +// specified FuncInfo. +func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym { + ldr := ctxt.loader + its := ldr.CreateExtSym("", 0) + inlTreeSym := ldr.MakeSymbolUpdater(its) + // Note: the generated symbol is given a type of sym.SGOFUNC, as a + // signal to the symtab() phase that it needs to be grouped in with + // other similar symbols (gcdata, etc); the dodata() phase will + // eventually switch the type back to SRODATA. + inlTreeSym.SetType(sym.SGOFUNC) + ldr.SetAttrReachable(its, true) + ldr.SetSymAlign(its, 4) // it has 32-bit fields + ninl := fi.NumInlTree() + for i := 0; i < int(ninl); i++ { + call := fi.InlTree(i) + nameOff, ok := nameOffsets[call.Func] + if !ok { + panic("couldn't find function name offset") + } + + inlFunc := ldr.FuncInfo(call.Func) + var funcID objabi.FuncID + startLine := int32(0) + if inlFunc.Valid() { + funcID = inlFunc.FuncID() + startLine = inlFunc.StartLine() + } else if !ctxt.linkShared { + // Inlined functions are always Go functions, and thus + // must have FuncInfo. + // + // Unfortunately, with -linkshared, the inlined + // function may be external symbols (from another + // shared library), and we don't load FuncInfo from the + // shared library. We will report potentially incorrect + // FuncID in this case. See https://go.dev/issue/55954. + panic(fmt.Sprintf("inlined function %s missing func info", ldr.SymName(call.Func))) + } + + // Construct runtime.inlinedCall value. + const size = 16 + inlTreeSym.SetUint8(arch, int64(i*size+0), uint8(funcID)) + // Bytes 1-3 are unused. + inlTreeSym.SetUint32(arch, int64(i*size+4), uint32(nameOff)) + inlTreeSym.SetUint32(arch, int64(i*size+8), uint32(call.ParentPC)) + inlTreeSym.SetUint32(arch, int64(i*size+12), uint32(startLine)) + } + return its +} + +// makeInlSyms returns a map of loader.Sym that are created inlSyms. +func makeInlSyms(ctxt *Link, funcs []loader.Sym, nameOffsets map[loader.Sym]uint32) map[loader.Sym]loader.Sym { + ldr := ctxt.loader + // Create the inline symbols we need. + inlSyms := make(map[loader.Sym]loader.Sym) + for _, s := range funcs { + if fi := ldr.FuncInfo(s); fi.Valid() { + fi.Preload() + if fi.NumInlTree() > 0 { + inlSyms[s] = genInlTreeSym(ctxt, ldr.SymUnit(s), fi, ctxt.Arch, nameOffsets) + } + } + } + return inlSyms +} + +// generatePCHeader creates the runtime.pcheader symbol, setting it up as a +// generator to fill in its data later. +func (state *pclntab) generatePCHeader(ctxt *Link) { + ldr := ctxt.loader + textStartOff := int64(8 + 2*ctxt.Arch.PtrSize) + size := int64(8 + 8*ctxt.Arch.PtrSize) + writeHeader := func(ctxt *Link, s loader.Sym) { + header := ctxt.loader.MakeSymbolUpdater(s) + + writeSymOffset := func(off int64, ws loader.Sym) int64 { + diff := ldr.SymValue(ws) - ldr.SymValue(s) + if diff <= 0 { + name := ldr.SymName(ws) + panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before %s(%x)", ldr.SymValue(s), name, ldr.SymValue(ws))) + } + return header.SetUintptr(ctxt.Arch, off, uintptr(diff)) + } + + // Write header. + // Keep in sync with runtime/symtab.go:pcHeader and package debug/gosym. + header.SetUint32(ctxt.Arch, 0, 0xfffffff1) + header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC)) + header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize)) + off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc)) + off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles)) + if off != textStartOff { + panic(fmt.Sprintf("pcHeader textStartOff: %d != %d", off, textStartOff)) + } + off += int64(ctxt.Arch.PtrSize) // skip runtimeText relocation + off = writeSymOffset(off, state.funcnametab) + off = writeSymOffset(off, state.cutab) + off = writeSymOffset(off, state.filetab) + off = writeSymOffset(off, state.pctab) + off = writeSymOffset(off, state.pclntab) + if off != size { + panic(fmt.Sprintf("pcHeader size: %d != %d", off, size)) + } + } + + state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader) + // Create the runtimeText relocation. + sb := ldr.MakeSymbolUpdater(state.pcheader) + sb.SetAddr(ctxt.Arch, textStartOff, ldr.Lookup("runtime.text", 0)) +} + +// walkFuncs iterates over the funcs, calling a function for each unique +// function and inlined function. +func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) { + ldr := ctxt.loader + seen := make(map[loader.Sym]struct{}) + for _, s := range funcs { + if _, ok := seen[s]; !ok { + f(s) + seen[s] = struct{}{} + } + + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + for i, ni := 0, fi.NumInlTree(); i < int(ni); i++ { + call := fi.InlTree(i).Func + if _, ok := seen[call]; !ok { + f(call) + seen[call] = struct{}{} + } + } + } +} + +// generateFuncnametab creates the function name table. Returns a map of +// func symbol to the name offset in runtime.funcnamtab. +func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 { + nameOffsets := make(map[loader.Sym]uint32, state.nfunc) + + // The name used by the runtime is the concatenation of the 3 returned strings. + // For regular functions, only one returned string is nonempty. + // For generic functions, we use three parts so that we can print everything + // within the outermost "[]" as "...". + nameParts := func(name string) (string, string, string) { + i := strings.IndexByte(name, '[') + if i < 0 { + return name, "", "" + } + // TODO: use LastIndexByte once the bootstrap compiler is >= Go 1.5. + j := len(name) - 1 + for j > i && name[j] != ']' { + j-- + } + if j <= i { + return name, "", "" + } + return name[:i], "[...]", name[j+1:] + } + + // Write the null terminated strings. + writeFuncNameTab := func(ctxt *Link, s loader.Sym) { + symtab := ctxt.loader.MakeSymbolUpdater(s) + for s, off := range nameOffsets { + a, b, c := nameParts(ctxt.loader.SymName(s)) + o := int64(off) + o = symtab.AddStringAt(o, a) + o = symtab.AddStringAt(o, b) + _ = symtab.AddCStringAt(o, c) + } + } + + // Loop through the CUs, and calculate the size needed. + var size int64 + walkFuncs(ctxt, funcs, func(s loader.Sym) { + nameOffsets[s] = uint32(size) + a, b, c := nameParts(ctxt.loader.SymName(s)) + size += int64(len(a) + len(b) + len(c) + 1) // NULL terminate + }) + + state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab) + return nameOffsets +} + +// walkFilenames walks funcs, calling a function for each filename used in each +// function's line table. +func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) { + ldr := ctxt.loader + + // Loop through all functions, finding the filenames we need. + for _, s := range funcs { + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + + cu := ldr.SymUnit(s) + for i, nf := 0, int(fi.NumFile()); i < nf; i++ { + f(cu, fi.File(i)) + } + for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ { + call := fi.InlTree(i) + f(cu, call.File) + } + } +} + +// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice +// of the index at which each CU begins in runtime.cutab. +// +// Function objects keep track of the files they reference to print the stack. +// This function creates a per-CU list of filenames if CU[M] references +// files[1-N], the following is generated: +// +// runtime.cutab: +// CU[M] +// offsetToFilename[0] +// offsetToFilename[1] +// .. +// +// runtime.filetab +// filename[0] +// filename[1] +// +// Looking up a filename then becomes: +// 0. Given a func, and filename index [K] +// 1. Get Func.CUIndex: M := func.cuOffset +// 2. Find filename offset: fileOffset := runtime.cutab[M+K] +// 3. Get the filename: getcstring(runtime.filetab[fileOffset]) +func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 { + // On a per-CU basis, keep track of all the filenames we need. + // + // Note, that we store the filenames in a separate section in the object + // files, and deduplicate based on the actual value. It would be better to + // store the filenames as symbols, using content addressable symbols (and + // then not loading extra filenames), and just use the hash value of the + // symbol name to do this cataloging. + // + // TODO: Store filenames as symbols. (Note this would be easiest if you + // also move strings to ALWAYS using the larger content addressable hash + // function, and use that hash value for uniqueness testing.) + cuEntries := make([]goobj.CUFileIndex, len(compUnits)) + fileOffsets := make(map[string]uint32) + + // Walk the filenames. + // We store the total filename string length we need to load, and the max + // file index we've seen per CU so we can calculate how large the + // CU->global table needs to be. + var fileSize int64 + walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) { + // Note we use the raw filename for lookup, but use the expanded filename + // when we save the size. + filename := cu.FileTable[i] + if _, ok := fileOffsets[filename]; !ok { + fileOffsets[filename] = uint32(fileSize) + fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate + } + + // Find the maximum file index we've seen. + if cuEntries[cu.PclnIndex] < i+1 { + cuEntries[cu.PclnIndex] = i + 1 // Store max + 1 + } + }) + + // Calculate the size of the runtime.cutab variable. + var totalEntries uint32 + cuOffsets := make([]uint32, len(cuEntries)) + for i, entries := range cuEntries { + // Note, cutab is a slice of uint32, so an offset to a cu's entry is just the + // running total of all cu indices we've needed to store so far, not the + // number of bytes we've stored so far. + cuOffsets[i] = totalEntries + totalEntries += uint32(entries) + } + + // Write cutab. + writeCutab := func(ctxt *Link, s loader.Sym) { + sb := ctxt.loader.MakeSymbolUpdater(s) + + var off int64 + for i, max := range cuEntries { + // Write the per CU LUT. + cu := compUnits[i] + for j := goobj.CUFileIndex(0); j < max; j++ { + fileOffset, ok := fileOffsets[cu.FileTable[j]] + if !ok { + // We're looping through all possible file indices. It's possible a file's + // been deadcode eliminated, and although it's a valid file in the CU, it's + // not needed in this binary. When that happens, use an invalid offset. + fileOffset = ^uint32(0) + } + off = sb.SetUint32(ctxt.Arch, off, fileOffset) + } + } + } + state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab) + + // Write filetab. + writeFiletab := func(ctxt *Link, s loader.Sym) { + sb := ctxt.loader.MakeSymbolUpdater(s) + + // Write the strings. + for filename, loc := range fileOffsets { + sb.AddStringAt(int64(loc), expandFile(filename)) + } + } + state.nfiles = uint32(len(fileOffsets)) + state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab) + + return cuOffsets +} + +// generatePctab creates the runtime.pctab variable, holding all the +// deduplicated pcdata. +func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) { + ldr := ctxt.loader + + // Pctab offsets of 0 are considered invalid in the runtime. We respect + // that by just padding a single byte at the beginning of runtime.pctab, + // that way no real offsets can be zero. + size := int64(1) + + // Walk the functions, finding offset to store each pcdata. + seen := make(map[loader.Sym]struct{}) + saveOffset := func(pcSym loader.Sym) { + if _, ok := seen[pcSym]; !ok { + datSize := ldr.SymSize(pcSym) + if datSize != 0 { + ldr.SetSymValue(pcSym, size) + } else { + // Invalid PC data, record as zero. + ldr.SetSymValue(pcSym, 0) + } + size += datSize + seen[pcSym] = struct{}{} + } + } + var pcsp, pcline, pcfile, pcinline loader.Sym + var pcdata []loader.Sym + for _, s := range funcs { + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata) + + pcSyms := []loader.Sym{pcsp, pcfile, pcline} + for _, pcSym := range pcSyms { + saveOffset(pcSym) + } + for _, pcSym := range pcdata { + saveOffset(pcSym) + } + if fi.NumInlTree() > 0 { + saveOffset(pcinline) + } + } + + // TODO: There is no reason we need a generator for this variable, and it + // could be moved to a carrier symbol. However, carrier symbols containing + // carrier symbols don't work yet (as of Aug 2020). Once this is fixed, + // runtime.pctab could just be a carrier sym. + writePctab := func(ctxt *Link, s loader.Sym) { + ldr := ctxt.loader + sb := ldr.MakeSymbolUpdater(s) + for sym := range seen { + sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym)) + } + } + + state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab) +} + +// numPCData returns the number of PCData syms for the FuncInfo. +// NB: Preload must be called on valid FuncInfos before calling this function. +func numPCData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo) uint32 { + if !fi.Valid() { + return 0 + } + numPCData := uint32(ldr.NumPcdata(s)) + if fi.NumInlTree() > 0 { + if numPCData < objabi.PCDATA_InlTreeIndex+1 { + numPCData = objabi.PCDATA_InlTreeIndex + 1 + } + } + return numPCData +} + +// generateFunctab creates the runtime.functab +// +// runtime.functab contains two things: +// +// - pc->func look up table. +// - array of func objects, interleaved with pcdata and funcdata +func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) { + // Calculate the size of the table. + size, startLocations := state.calculateFunctabSize(ctxt, funcs) + writePcln := func(ctxt *Link, s loader.Sym) { + ldr := ctxt.loader + sb := ldr.MakeSymbolUpdater(s) + // Write the data. + writePCToFunc(ctxt, sb, funcs, startLocations) + writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets) + } + state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln) +} + +// funcData returns the funcdata and offsets for the FuncInfo. +// The funcdata are written into runtime.functab after each func +// object. This is a helper function to make querying the FuncInfo object +// cleaner. +// +// NB: Preload must be called on the FuncInfo before calling. +// NB: fdSyms is used as scratch space. +func funcData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym) []loader.Sym { + fdSyms = fdSyms[:0] + if fi.Valid() { + fdSyms = ldr.Funcdata(s, fdSyms) + if fi.NumInlTree() > 0 { + if len(fdSyms) < objabi.FUNCDATA_InlTree+1 { + fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...) + } + fdSyms[objabi.FUNCDATA_InlTree] = inlSym + } + } + return fdSyms +} + +// calculateFunctabSize calculates the size of the pclntab, and the offsets in +// the output buffer for individual func entries. +func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64, []uint32) { + ldr := ctxt.loader + startLocations := make([]uint32, len(funcs)) + + // Allocate space for the pc->func table. This structure consists of a pc offset + // and an offset to the func structure. After that, we have a single pc + // value that marks the end of the last function in the binary. + size := int64(int(state.nfunc)*2*4 + 4) + + // Now find the space for the func objects. We do this in a running manner, + // so that we can find individual starting locations. + for i, s := range funcs { + size = Rnd(size, int64(ctxt.Arch.PtrSize)) + startLocations[i] = uint32(size) + fi := ldr.FuncInfo(s) + size += funcSize + if fi.Valid() { + fi.Preload() + numFuncData := ldr.NumFuncdata(s) + if fi.NumInlTree() > 0 { + if numFuncData < objabi.FUNCDATA_InlTree+1 { + numFuncData = objabi.FUNCDATA_InlTree + 1 + } + } + size += int64(numPCData(ldr, s, fi) * 4) + size += int64(numFuncData * 4) + } + } + + return size, startLocations +} + +// writePCToFunc writes the PC->func lookup table. +func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32) { + ldr := ctxt.loader + textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0)) + pcOff := func(s loader.Sym) uint32 { + off := ldr.SymValue(s) - textStart + if off < 0 { + panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart)) + } + return uint32(off) + } + for i, s := range funcs { + sb.SetUint32(ctxt.Arch, int64(i*2*4), pcOff(s)) + sb.SetUint32(ctxt.Arch, int64((i*2+1)*4), startLocations[i]) + } + + // Final entry of table is just end pc offset. + lastFunc := funcs[len(funcs)-1] + sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, pcOff(lastFunc)+uint32(ldr.SymSize(lastFunc))) +} + +// writeFuncs writes the func structures and pcdata to runtime.functab. +func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) { + ldr := ctxt.loader + deferReturnSym := ldr.Lookup("runtime.deferreturn", abiInternalVer) + gofunc := ldr.Lookup("go:func.*", 0) + gofuncBase := ldr.SymValue(gofunc) + textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0)) + funcdata := []loader.Sym{} + var pcsp, pcfile, pcline, pcinline loader.Sym + var pcdata []loader.Sym + + // Write the individual func objects. + for i, s := range funcs { + startLine := int32(0) + fi := ldr.FuncInfo(s) + if fi.Valid() { + fi.Preload() + pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata) + startLine = fi.StartLine() + } + + off := int64(startLocations[i]) + // entryOff uint32 (offset of func entry PC from textStart) + entryOff := ldr.SymValue(s) - textStart + if entryOff < 0 { + panic(fmt.Sprintf("expected func %s(%x) to be placed before or at textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart)) + } + off = sb.SetUint32(ctxt.Arch, off, uint32(entryOff)) + + // nameOff int32 + nameOff, ok := nameOffsets[s] + if !ok { + panic("couldn't find function name offset") + } + off = sb.SetUint32(ctxt.Arch, off, uint32(nameOff)) + + // args int32 + // TODO: Move into funcinfo. + args := uint32(0) + if fi.Valid() { + args = uint32(fi.Args()) + } + off = sb.SetUint32(ctxt.Arch, off, args) + + // deferreturn + deferreturn := computeDeferReturn(ctxt, deferReturnSym, s) + off = sb.SetUint32(ctxt.Arch, off, deferreturn) + + // pcdata + if fi.Valid() { + off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcsp))) + off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcfile))) + off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcline))) + } else { + off += 12 + } + off = sb.SetUint32(ctxt.Arch, off, uint32(numPCData(ldr, s, fi))) + + // Store the offset to compilation unit's file table. + cuIdx := ^uint32(0) + if cu := ldr.SymUnit(s); cu != nil { + cuIdx = cuOffsets[cu.PclnIndex] + } + off = sb.SetUint32(ctxt.Arch, off, cuIdx) + + // startLine int32 + off = sb.SetUint32(ctxt.Arch, off, uint32(startLine)) + + // funcID uint8 + var funcID objabi.FuncID + if fi.Valid() { + funcID = fi.FuncID() + } + off = sb.SetUint8(ctxt.Arch, off, uint8(funcID)) + + // flag uint8 + var flag objabi.FuncFlag + if fi.Valid() { + flag = fi.FuncFlag() + } + off = sb.SetUint8(ctxt.Arch, off, uint8(flag)) + + off += 1 // pad + + // nfuncdata must be the final entry. + funcdata = funcData(ldr, s, fi, 0, funcdata) + off = sb.SetUint8(ctxt.Arch, off, uint8(len(funcdata))) + + // Output the pcdata. + if fi.Valid() { + for j, pcSym := range pcdata { + sb.SetUint32(ctxt.Arch, off+int64(j*4), uint32(ldr.SymValue(pcSym))) + } + if fi.NumInlTree() > 0 { + sb.SetUint32(ctxt.Arch, off+objabi.PCDATA_InlTreeIndex*4, uint32(ldr.SymValue(pcinline))) + } + } + + // Write funcdata refs as offsets from go:func.* and go:funcrel.*. + funcdata = funcData(ldr, s, fi, inlSyms[s], funcdata) + // Missing funcdata will be ^0. See runtime/symtab.go:funcdata. + off = int64(startLocations[i] + funcSize + numPCData(ldr, s, fi)*4) + for j := range funcdata { + dataoff := off + int64(4*j) + fdsym := funcdata[j] + if fdsym == 0 { + sb.SetUint32(ctxt.Arch, dataoff, ^uint32(0)) // ^0 is a sentinel for "no value" + continue + } + + if outer := ldr.OuterSym(fdsym); outer != gofunc { + panic(fmt.Sprintf("bad carrier sym for symbol %s (funcdata %s#%d), want go:func.* got %s", ldr.SymName(fdsym), ldr.SymName(s), j, ldr.SymName(outer))) + } + sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)-gofuncBase)) + } + } +} + +// pclntab initializes the pclntab symbol with +// runtime function and file name information. + +// pclntab generates the pcln table for the link output. +func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { + // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the + // layout and data has changed since that time. + // + // As of August 2020, here's the layout of pclntab: + // + // .gopclntab/__gopclntab [elf/macho section] + // runtime.pclntab + // Carrier symbol for the entire pclntab section. + // + // runtime.pcheader (see: runtime/symtab.go:pcHeader) + // 8-byte magic + // nfunc [thearch.ptrsize bytes] + // offset to runtime.funcnametab from the beginning of runtime.pcheader + // offset to runtime.pclntab_old from beginning of runtime.pcheader + // + // runtime.funcnametab + // []list of null terminated function names + // + // runtime.cutab + // for i=0..#CUs + // for j=0..#max used file index in CU[i] + // uint32 offset into runtime.filetab for the filename[j] + // + // runtime.filetab + // []null terminated filename strings + // + // runtime.pctab + // []byte of deduplicated pc data. + // + // runtime.functab + // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] + // end PC [thearch.ptrsize bytes] + // func structures, pcdata offsets, func data. + + state, compUnits, funcs := makePclntab(ctxt, container) + + ldr := ctxt.loader + state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0) + ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB) + ldr.SetAttrReachable(state.carrier, true) + setCarrierSym(sym.SPCLNTAB, state.carrier) + + state.generatePCHeader(ctxt) + nameOffsets := state.generateFuncnametab(ctxt, funcs) + cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs) + state.generatePctab(ctxt, funcs) + inlSyms := makeInlSyms(ctxt, funcs, nameOffsets) + state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets) + + return state +} + +func gorootFinal() string { + root := buildcfg.GOROOT + if final := os.Getenv("GOROOT_FINAL"); final != "" { + root = final + } + return root +} + +func expandGoroot(s string) string { + const n = len("$GOROOT") + if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') { + if final := gorootFinal(); final != "" { + return filepath.ToSlash(filepath.Join(final, s[n:])) + } + } + return s +} + +const ( + BUCKETSIZE = 256 * MINFUNC + SUBBUCKETS = 16 + SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS + NOIDX = 0x7fffffff +) + +// findfunctab generates a lookup table to quickly find the containing +// function for a pc. See src/runtime/symtab.go:findfunc for details. +func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) { + ldr := ctxt.loader + + // find min and max address + min := ldr.SymValue(ctxt.Textp[0]) + lastp := ctxt.Textp[len(ctxt.Textp)-1] + max := ldr.SymValue(lastp) + ldr.SymSize(lastp) + + // for each subbucket, compute the minimum of all symbol indexes + // that map to that subbucket. + n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) + + nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) + + size := 4*int64(nbuckets) + int64(n) + + writeFindFuncTab := func(_ *Link, s loader.Sym) { + t := ldr.MakeSymbolUpdater(s) + + indexes := make([]int32, n) + for i := int32(0); i < n; i++ { + indexes[i] = NOIDX + } + idx := int32(0) + for i, s := range ctxt.Textp { + if !emitPcln(ctxt, s, container) { + continue + } + p := ldr.SymValue(s) + var e loader.Sym + i++ + if i < len(ctxt.Textp) { + e = ctxt.Textp[i] + } + for e != 0 && !emitPcln(ctxt, e, container) && i < len(ctxt.Textp) { + e = ctxt.Textp[i] + i++ + } + q := max + if e != 0 { + q = ldr.SymValue(e) + } + + //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); + for ; p < q; p += SUBBUCKETSIZE { + i = int((p - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + } + + i = int((q - 1 - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + idx++ + } + + // fill in table + for i := int32(0); i < nbuckets; i++ { + base := indexes[i*SUBBUCKETS] + if base == NOIDX { + Errorf(nil, "hole in findfunctab") + } + t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base)) + for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { + idx = indexes[i*SUBBUCKETS+j] + if idx == NOIDX { + Errorf(nil, "hole in findfunctab") + } + if idx-base >= 256 { + Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) + } + + t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) + } + } + } + + state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab) + ldr.SetAttrReachable(state.findfunctab, true) + ldr.SetAttrLocal(state.findfunctab, true) +} + +// findContainerSyms returns a bitmap, indexed by symbol number, where there's +// a 1 for every container symbol. +func (ctxt *Link) findContainerSyms() loader.Bitmap { + ldr := ctxt.loader + container := loader.MakeBitmap(ldr.NSym()) + // Find container symbols and mark them as such. + for _, s := range ctxt.Textp { + outer := ldr.OuterSym(s) + if outer != 0 { + container.Set(outer) + } + } + return container +} diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go new file mode 100644 index 0000000..0e29131 --- /dev/null +++ b/src/cmd/link/internal/ld/pe.go @@ -0,0 +1,1709 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// PE (Portable Executable) file writing +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/pe" + "encoding/binary" + "fmt" + "internal/buildcfg" + "sort" + "strconv" + "strings" +) + +type IMAGE_IMPORT_DESCRIPTOR struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 +} + +type IMAGE_EXPORT_DIRECTORY struct { + Characteristics uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + Name uint32 + Base uint32 + NumberOfFunctions uint32 + NumberOfNames uint32 + AddressOfFunctions uint32 + AddressOfNames uint32 + AddressOfNameOrdinals uint32 +} + +var ( + // PEBASE is the base address for the executable. + // It is small for 32-bit and large for 64-bit. + PEBASE int64 + + // SectionAlignment must be greater than or equal to FileAlignment. + // The default is the page size for the architecture. + PESECTALIGN int64 = 0x1000 + + // FileAlignment should be a power of 2 between 512 and 64 K, inclusive. + // The default is 512. If the SectionAlignment is less than + // the architecture's page size, then FileAlignment must match SectionAlignment. + PEFILEALIGN int64 = 2 << 8 +) + +const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_LNK_OTHER = 0x00000100 + IMAGE_SCN_LNK_INFO = 0x00000200 + IMAGE_SCN_LNK_REMOVE = 0x00000800 + IMAGE_SCN_LNK_COMDAT = 0x00001000 + IMAGE_SCN_GPREL = 0x00008000 + IMAGE_SCN_MEM_PURGEABLE = 0x00020000 + IMAGE_SCN_MEM_16BIT = 0x00020000 + IMAGE_SCN_MEM_LOCKED = 0x00040000 + IMAGE_SCN_MEM_PRELOAD = 0x00080000 + IMAGE_SCN_ALIGN_1BYTES = 0x00100000 + IMAGE_SCN_ALIGN_2BYTES = 0x00200000 + IMAGE_SCN_ALIGN_4BYTES = 0x00300000 + IMAGE_SCN_ALIGN_8BYTES = 0x00400000 + IMAGE_SCN_ALIGN_16BYTES = 0x00500000 + IMAGE_SCN_ALIGN_32BYTES = 0x00600000 + IMAGE_SCN_ALIGN_64BYTES = 0x00700000 + IMAGE_SCN_ALIGN_128BYTES = 0x00800000 + IMAGE_SCN_ALIGN_256BYTES = 0x00900000 + IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 + IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 + IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 + IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 + IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 + IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 + IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 + IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 + IMAGE_SCN_MEM_SHARED = 0x10000000 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 +) + +// See https://docs.microsoft.com/en-us/windows/win32/debug/pe-format. +// TODO(crawshaw): add these constants to debug/pe. +const ( + // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2 + IMAGE_SYM_TYPE_NULL = 0 + IMAGE_SYM_TYPE_STRUCT = 8 + IMAGE_SYM_DTYPE_FUNCTION = 0x20 + IMAGE_SYM_DTYPE_ARRAY = 0x30 + IMAGE_SYM_CLASS_EXTERNAL = 2 + IMAGE_SYM_CLASS_STATIC = 3 + + IMAGE_REL_I386_DIR32 = 0x0006 + IMAGE_REL_I386_SECREL = 0x000B + IMAGE_REL_I386_REL32 = 0x0014 + + IMAGE_REL_AMD64_ADDR64 = 0x0001 + IMAGE_REL_AMD64_ADDR32 = 0x0002 + IMAGE_REL_AMD64_REL32 = 0x0004 + IMAGE_REL_AMD64_SECREL = 0x000B + + IMAGE_REL_ARM_ABSOLUTE = 0x0000 + IMAGE_REL_ARM_ADDR32 = 0x0001 + IMAGE_REL_ARM_ADDR32NB = 0x0002 + IMAGE_REL_ARM_BRANCH24 = 0x0003 + IMAGE_REL_ARM_BRANCH11 = 0x0004 + IMAGE_REL_ARM_SECREL = 0x000F + + IMAGE_REL_ARM64_ABSOLUTE = 0x0000 + IMAGE_REL_ARM64_ADDR32 = 0x0001 + IMAGE_REL_ARM64_ADDR32NB = 0x0002 + IMAGE_REL_ARM64_BRANCH26 = 0x0003 + IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004 + IMAGE_REL_ARM64_REL21 = 0x0005 + IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006 + IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007 + IMAGE_REL_ARM64_SECREL = 0x0008 + IMAGE_REL_ARM64_SECREL_LOW12A = 0x0009 + IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A + IMAGE_REL_ARM64_SECREL_LOW12L = 0x000B + IMAGE_REL_ARM64_TOKEN = 0x000C + IMAGE_REL_ARM64_SECTION = 0x000D + IMAGE_REL_ARM64_ADDR64 = 0x000E + IMAGE_REL_ARM64_BRANCH19 = 0x000F + IMAGE_REL_ARM64_BRANCH14 = 0x0010 + IMAGE_REL_ARM64_REL32 = 0x0011 + + IMAGE_REL_BASED_HIGHLOW = 3 + IMAGE_REL_BASED_DIR64 = 10 +) + +const ( + PeMinimumTargetMajorVersion = 6 + PeMinimumTargetMinorVersion = 1 +) + +// DOS stub that prints out +// "This program cannot be run in DOS mode." +var dosstub = []uint8{ + 0x4d, + 0x5a, + 0x90, + 0x00, + 0x03, + 0x00, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xff, + 0xff, + 0x00, + 0x00, + 0x8b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x0e, + 0x1f, + 0xba, + 0x0e, + 0x00, + 0xb4, + 0x09, + 0xcd, + 0x21, + 0xb8, + 0x01, + 0x4c, + 0xcd, + 0x21, + 0x54, + 0x68, + 0x69, + 0x73, + 0x20, + 0x70, + 0x72, + 0x6f, + 0x67, + 0x72, + 0x61, + 0x6d, + 0x20, + 0x63, + 0x61, + 0x6e, + 0x6e, + 0x6f, + 0x74, + 0x20, + 0x62, + 0x65, + 0x20, + 0x72, + 0x75, + 0x6e, + 0x20, + 0x69, + 0x6e, + 0x20, + 0x44, + 0x4f, + 0x53, + 0x20, + 0x6d, + 0x6f, + 0x64, + 0x65, + 0x2e, + 0x0d, + 0x0d, + 0x0a, + 0x24, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +} + +type Imp struct { + s loader.Sym + off uint64 + next *Imp + argsize int +} + +type Dll struct { + name string + nameoff uint64 + thunkoff uint64 + ms *Imp + next *Dll +} + +var ( + rsrcsyms []loader.Sym + PESECTHEADR int32 + PEFILEHEADR int32 + pe64 int + dr *Dll + + dexport = make([]loader.Sym, 0, 1024) +) + +// peStringTable is a COFF string table. +type peStringTable struct { + strings []string + stringsLen int +} + +// size returns size of string table t. +func (t *peStringTable) size() int { + // string table starts with 4-byte length at the beginning + return t.stringsLen + 4 +} + +// add adds string str to string table t. +func (t *peStringTable) add(str string) int { + off := t.size() + t.strings = append(t.strings, str) + t.stringsLen += len(str) + 1 // each string will have 0 appended to it + return off +} + +// write writes string table t into the output file. +func (t *peStringTable) write(out *OutBuf) { + out.Write32(uint32(t.size())) + for _, s := range t.strings { + out.WriteString(s) + out.Write8(0) + } +} + +// peSection represents section from COFF section table. +type peSection struct { + name string + shortName string + index int // one-based index into the Section Table + virtualSize uint32 + virtualAddress uint32 + sizeOfRawData uint32 + pointerToRawData uint32 + pointerToRelocations uint32 + numberOfRelocations uint16 + characteristics uint32 +} + +// checkOffset verifies COFF section sect offset in the file. +func (sect *peSection) checkOffset(off int64) { + if off != int64(sect.pointerToRawData) { + Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off)) + errorexit() + } +} + +// checkSegment verifies COFF section sect matches address +// and file offset provided in segment seg. +func (sect *peSection) checkSegment(seg *sym.Segment) { + if seg.Vaddr-uint64(PEBASE) != uint64(sect.virtualAddress) { + Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-uint64(PEBASE)))) + errorexit() + } + if seg.Fileoff != uint64(sect.pointerToRawData) { + Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff))) + errorexit() + } +} + +// pad adds zeros to the section sect. It writes as many bytes +// as necessary to make section sect.SizeOfRawData bytes long. +// It assumes that n bytes are already written to the file. +func (sect *peSection) pad(out *OutBuf, n uint32) { + out.WriteStringN("", int(sect.sizeOfRawData-n)) +} + +// write writes COFF section sect into the output file. +func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error { + h := pe.SectionHeader32{ + VirtualSize: sect.virtualSize, + SizeOfRawData: sect.sizeOfRawData, + PointerToRawData: sect.pointerToRawData, + PointerToRelocations: sect.pointerToRelocations, + NumberOfRelocations: sect.numberOfRelocations, + Characteristics: sect.characteristics, + } + if linkmode != LinkExternal { + h.VirtualAddress = sect.virtualAddress + } + copy(h.Name[:], sect.shortName) + return binary.Write(out, binary.LittleEndian, h) +} + +// emitRelocations emits the relocation entries for the sect. +// The actual relocations are emitted by relocfn. +// This updates the corresponding PE section table entry +// with the relocation offset and count. +func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) { + sect.pointerToRelocations = uint32(out.Offset()) + // first entry: extended relocs + out.Write32(0) // placeholder for number of relocation + 1 + out.Write32(0) + out.Write16(0) + + n := relocfn() + 1 + + cpos := out.Offset() + out.SeekSet(int64(sect.pointerToRelocations)) + out.Write32(uint32(n)) + out.SeekSet(cpos) + if n > 0x10000 { + n = 0x10000 + sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL + } else { + sect.pointerToRelocations += 10 // skip the extend reloc entry + } + sect.numberOfRelocations = uint16(n - 1) +} + +// peFile is used to build COFF file. +type peFile struct { + sections []*peSection + stringTable peStringTable + textSect *peSection + rdataSect *peSection + dataSect *peSection + bssSect *peSection + ctorsSect *peSection + nextSectOffset uint32 + nextFileOffset uint32 + symtabOffset int64 // offset to the start of symbol table + symbolCount int // number of symbol table records written + dataDirectory [16]pe.DataDirectory +} + +// addSection adds section to the COFF file f. +func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection { + sect := &peSection{ + name: name, + shortName: name, + index: len(f.sections) + 1, + virtualAddress: f.nextSectOffset, + pointerToRawData: f.nextFileOffset, + } + f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN)) + if filesize > 0 { + sect.virtualSize = uint32(sectsize) + sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN)) + f.nextFileOffset += sect.sizeOfRawData + } else { + sect.sizeOfRawData = uint32(sectsize) + } + f.sections = append(f.sections, sect) + return sect +} + +// addDWARFSection adds DWARF section to the COFF file f. +// This function is similar to addSection, but DWARF section names are +// longer than 8 characters, so they need to be stored in the string table. +func (f *peFile) addDWARFSection(name string, size int) *peSection { + if size == 0 { + Exitf("DWARF section %q is empty", name) + } + // DWARF section names are longer than 8 characters. + // PE format requires such names to be stored in string table, + // and section names replaced with slash (/) followed by + // correspondent string table index. + // see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx + // for details + off := f.stringTable.add(name) + h := f.addSection(name, size, size) + h.shortName = fmt.Sprintf("/%d", off) + h.characteristics = IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA + return h +} + +// addDWARF adds DWARF information to the COFF file f. +func (f *peFile) addDWARF() { + if *FlagS { // disable symbol table + return + } + if *FlagW { // disable dwarf + return + } + for _, sect := range Segdwarf.Sections { + h := f.addDWARFSection(sect.Name, int(sect.Length)) + fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff + if uint64(h.pointerToRawData) != fileoff { + Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff) + } + } +} + +// addInitArray adds .ctors COFF section to the file f. +func (f *peFile) addInitArray(ctxt *Link) *peSection { + // The size below was determined by the specification for array relocations, + // and by observing what GCC writes here. If the initarray section grows to + // contain more than one constructor entry, the size will need to be 8 * constructor_count. + // However, the entire Go runtime is initialized from just one function, so it is unlikely + // that this will need to grow in the future. + var size int + var alignment uint32 + switch buildcfg.GOARCH { + default: + Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", buildcfg.GOARCH) + case "386", "arm": + size = 4 + alignment = IMAGE_SCN_ALIGN_4BYTES + case "amd64", "arm64": + size = 8 + alignment = IMAGE_SCN_ALIGN_8BYTES + } + sect := f.addSection(".ctors", size, size) + sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | alignment + sect.sizeOfRawData = uint32(size) + ctxt.Out.SeekSet(int64(sect.pointerToRawData)) + sect.checkOffset(ctxt.Out.Offset()) + + init_entry := ctxt.loader.Lookup(*flagEntrySymbol, 0) + addr := uint64(ctxt.loader.SymValue(init_entry)) - ctxt.loader.SymSect(init_entry).Vaddr + switch buildcfg.GOARCH { + case "386", "arm": + ctxt.Out.Write32(uint32(addr)) + case "amd64", "arm64": + ctxt.Out.Write64(addr) + } + return sect +} + +// emitRelocations emits relocation entries for go.o in external linking. +func (f *peFile) emitRelocations(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + ldr := ctxt.loader + + // relocsect relocates symbols from first in section sect, and returns + // the total number of relocations emitted. + relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) int { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return 0 + } + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + eaddr := int64(sect.Vaddr + sect.Length) + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= eaddr { + break + } + // Compute external relocations on the go, and pass to PEreloc1 + // to stream out. + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ctxt.Errorf(s, "missing xsym in relocation") + continue + } + if ldr.SymDynid(rr.Xsym) < 0 { + ctxt.Errorf(s, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym())) + } + if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) { + ctxt.Errorf(s, "unsupported obj reloc %v/%d to %s", r.Type(), r.Siz(), ldr.SymName(r.Sym())) + } + } + } + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff + const relocLen = 4 + 4 + 2 + return int(sect.Rellen / relocLen) + } + + sects := []struct { + peSect *peSection + seg *sym.Segment + syms []loader.Sym + }{ + {f.textSect, &Segtext, ctxt.Textp}, + {f.rdataSect, &Segrodata, ctxt.datap}, + {f.dataSect, &Segdata, ctxt.datap}, + } + for _, s := range sects { + s.peSect.emitRelocations(ctxt.Out, func() int { + var n int + for _, sect := range s.seg.Sections { + n += relocsect(sect, s.syms, s.seg.Vaddr) + } + return n + }) + } + +dwarfLoop: + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ldr.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + for _, pesect := range f.sections { + if sect.Name == pesect.name { + pesect.emitRelocations(ctxt.Out, func() int { + return relocsect(sect, si.syms, sect.Vaddr) + }) + continue dwarfLoop + } + } + Errorf(nil, "emitRelocations: could not find %q section", sect.Name) + } + + if f.ctorsSect == nil { + return + } + + f.ctorsSect.emitRelocations(ctxt.Out, func() int { + dottext := ldr.Lookup(".text", 0) + ctxt.Out.Write32(0) + ctxt.Out.Write32(uint32(ldr.SymDynid(dottext))) + switch buildcfg.GOARCH { + default: + ctxt.Errorf(dottext, "unknown architecture for PE: %q\n", buildcfg.GOARCH) + case "386": + ctxt.Out.Write16(IMAGE_REL_I386_DIR32) + case "amd64": + ctxt.Out.Write16(IMAGE_REL_AMD64_ADDR64) + case "arm": + ctxt.Out.Write16(IMAGE_REL_ARM_ADDR32) + case "arm64": + ctxt.Out.Write16(IMAGE_REL_ARM64_ADDR64) + } + return 1 + }) +} + +// writeSymbol appends symbol s to file f symbol table. +// It also sets s.Dynid to written symbol number. +func (f *peFile) writeSymbol(out *OutBuf, ldr *loader.Loader, s loader.Sym, name string, value int64, sectidx int, typ uint16, class uint8) { + if len(name) > 8 { + out.Write32(0) + out.Write32(uint32(f.stringTable.add(name))) + } else { + out.WriteStringN(name, 8) + } + out.Write32(uint32(value)) + out.Write16(uint16(sectidx)) + out.Write16(typ) + out.Write8(class) + out.Write8(0) // no aux entries + + ldr.SetSymDynid(s, int32(f.symbolCount)) + + f.symbolCount++ +} + +// mapToPESection searches peFile f for s symbol's location. +// It returns PE section index, and offset within that section. +func (f *peFile) mapToPESection(ldr *loader.Loader, s loader.Sym, linkmode LinkMode) (pesectidx int, offset int64, err error) { + sect := ldr.SymSect(s) + if sect == nil { + return 0, 0, fmt.Errorf("could not map %s symbol with no section", ldr.SymName(s)) + } + if sect.Seg == &Segtext { + return f.textSect.index, int64(uint64(ldr.SymValue(s)) - Segtext.Vaddr), nil + } + if sect.Seg == &Segrodata { + return f.rdataSect.index, int64(uint64(ldr.SymValue(s)) - Segrodata.Vaddr), nil + } + if sect.Seg != &Segdata { + return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", ldr.SymName(s)) + } + v := uint64(ldr.SymValue(s)) - Segdata.Vaddr + if linkmode != LinkExternal { + return f.dataSect.index, int64(v), nil + } + if ldr.SymType(s) == sym.SDATA { + return f.dataSect.index, int64(v), nil + } + // Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section + // it still belongs to the .data section, not the .bss section. + if v < Segdata.Filelen { + return f.dataSect.index, int64(v), nil + } + return f.bssSect.index, int64(v - Segdata.Filelen), nil +} + +var isLabel = make(map[loader.Sym]bool) + +func AddPELabelSym(ldr *loader.Loader, s loader.Sym) { + isLabel[s] = true +} + +// writeSymbols writes all COFF symbol table records. +func (f *peFile) writeSymbols(ctxt *Link) { + ldr := ctxt.loader + addsym := func(s loader.Sym) { + t := ldr.SymType(s) + if ldr.SymSect(s) == nil && t != sym.SDYNIMPORT && t != sym.SHOSTOBJ && t != sym.SUNDEFEXT { + return + } + + name := ldr.SymName(s) + + // Only windows/386 requires underscore prefix on external symbols. + if ctxt.Is386() && ctxt.IsExternal() && + (t == sym.SHOSTOBJ || t == sym.SUNDEFEXT || ldr.AttrCgoExport(s) || + // TODO(cuonglm): remove this hack + // + // Previously, windows/386 requires underscore prefix on external symbols, + // but that's only applied for SHOSTOBJ/SUNDEFEXT or cgo export symbols. + // "go.buildid" is STEXT, "type.*" is STYPE, thus they are not prefixed + // with underscore. + // + // In external linking mode, the external linker can't resolve them as + // external symbols. But we are lucky that they have "." in their name, + // so the external linker see them as Forwarder RVA exports. See: + // + // - https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table + // - https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=ld/pe-dll.c;h=e7b82ba6ffadf74dc1b9ee71dc13d48336941e51;hb=HEAD#l972) + // + // CL 317917 changes "." to ":" in symbols name, so theses symbols can not be + // found by external linker anymore. So a hacky way is adding the + // underscore prefix for these 2 symbols. I don't have enough knowledge to + // verify whether adding the underscore for all STEXT/STYPE symbols are + // fine, even if it could be, that would be done in future CL. + name == "go:buildid" || name == "type:*") { + name = "_" + name + } + + name = mangleABIName(ctxt, ldr, s, name) + + var peSymType uint16 + if ctxt.IsExternal() { + peSymType = IMAGE_SYM_TYPE_NULL + } else { + // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308 + // peSymType = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT + peSymType = 0x0308 // "array of structs" + } + sect, value, err := f.mapToPESection(ldr, s, ctxt.LinkMode) + if err != nil { + if t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT { + peSymType = IMAGE_SYM_DTYPE_FUNCTION + } else { + ctxt.Errorf(s, "addpesym: %v", err) + } + } + class := IMAGE_SYM_CLASS_EXTERNAL + if ldr.IsFileLocal(s) || ldr.AttrVisibilityHidden(s) || ldr.AttrLocal(s) { + class = IMAGE_SYM_CLASS_STATIC + } + f.writeSymbol(ctxt.Out, ldr, s, name, value, sect, peSymType, uint8(class)) + } + + if ctxt.LinkMode == LinkExternal { + // Include section symbols as external, because + // .ctors and .debug_* section relocations refer to it. + for _, pesect := range f.sections { + s := ldr.LookupOrCreateSym(pesect.name, 0) + f.writeSymbol(ctxt.Out, ldr, s, pesect.name, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC) + } + } + + // Add special runtime.text and runtime.etext symbols. + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + + // Add text symbols. + for _, s := range ctxt.Textp { + addsym(s) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + name := ldr.SymName(s) // TODO: try not to read the name + if name == "" || name[0] == '.' { + return false + } + return true + } + + // Add data symbols and external references. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata + if t == sym.STLSBSS { + continue + } + if !shouldBeInSymbolTable(s) { + continue + } + addsym(s) + } + + switch t { + case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: + addsym(s) + default: + if len(isLabel) > 0 && isLabel[s] { + addsym(s) + } + } + } +} + +// writeSymbolTableAndStringTable writes out symbol and string tables for peFile f. +func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) { + f.symtabOffset = ctxt.Out.Offset() + + // write COFF symbol table + if !*FlagS || ctxt.LinkMode == LinkExternal { + f.writeSymbols(ctxt) + } + + // update COFF file header and section table + size := f.stringTable.size() + 18*f.symbolCount + var h *peSection + if ctxt.LinkMode != LinkExternal { + // We do not really need .symtab for go.o, and if we have one, ld + // will also include it in the exe, and that will confuse windows. + h = f.addSection(".symtab", size, size) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + h.checkOffset(f.symtabOffset) + } + + // write COFF string table + f.stringTable.write(ctxt.Out) + if ctxt.LinkMode != LinkExternal { + h.pad(ctxt.Out, uint32(size)) + } +} + +// writeFileHeader writes COFF file header for peFile f. +func (f *peFile) writeFileHeader(ctxt *Link) { + var fh pe.FileHeader + + switch ctxt.Arch.Family { + default: + Exitf("unknown PE architecture: %v", ctxt.Arch.Family) + case sys.AMD64: + fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64 + case sys.I386: + fh.Machine = pe.IMAGE_FILE_MACHINE_I386 + case sys.ARM: + fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT + case sys.ARM64: + fh.Machine = pe.IMAGE_FILE_MACHINE_ARM64 + } + + fh.NumberOfSections = uint16(len(f.sections)) + + // Being able to produce identical output for identical input is + // much more beneficial than having build timestamp in the header. + fh.TimeDateStamp = 0 + + if ctxt.LinkMode == LinkExternal { + fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED + } else { + fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: + if ctxt.BuildMode != BuildModePIE { + fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED + } + } + } + if pe64 != 0 { + var oh64 pe.OptionalHeader64 + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) + fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE + } else { + var oh pe.OptionalHeader32 + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) + fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE + } + + fh.PointerToSymbolTable = uint32(f.symtabOffset) + fh.NumberOfSymbols = uint32(f.symbolCount) + + binary.Write(ctxt.Out, binary.LittleEndian, &fh) +} + +// writeOptionalHeader writes COFF optional header for peFile f. +func (f *peFile) writeOptionalHeader(ctxt *Link) { + var oh pe.OptionalHeader32 + var oh64 pe.OptionalHeader64 + + if pe64 != 0 { + oh64.Magic = 0x20b // PE32+ + } else { + oh.Magic = 0x10b // PE32 + oh.BaseOfData = f.dataSect.virtualAddress + } + + // Fill out both oh64 and oh. We only use one. Oh well. + oh64.MajorLinkerVersion = 3 + oh.MajorLinkerVersion = 3 + oh64.MinorLinkerVersion = 0 + oh.MinorLinkerVersion = 0 + oh64.SizeOfCode = f.textSect.sizeOfRawData + oh.SizeOfCode = f.textSect.sizeOfRawData + oh64.SizeOfInitializedData = f.dataSect.sizeOfRawData + oh.SizeOfInitializedData = f.dataSect.sizeOfRawData + oh64.SizeOfUninitializedData = 0 + oh.SizeOfUninitializedData = 0 + if ctxt.LinkMode != LinkExternal { + oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE) + oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE) + } + oh64.BaseOfCode = f.textSect.virtualAddress + oh.BaseOfCode = f.textSect.virtualAddress + oh64.ImageBase = uint64(PEBASE) + oh.ImageBase = uint32(PEBASE) + oh64.SectionAlignment = uint32(PESECTALIGN) + oh.SectionAlignment = uint32(PESECTALIGN) + oh64.FileAlignment = uint32(PEFILEALIGN) + oh.FileAlignment = uint32(PEFILEALIGN) + oh64.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion + oh.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion + oh64.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion + oh.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion + oh64.MajorImageVersion = 1 + oh.MajorImageVersion = 1 + oh64.MinorImageVersion = 0 + oh.MinorImageVersion = 0 + oh64.MajorSubsystemVersion = PeMinimumTargetMajorVersion + oh.MajorSubsystemVersion = PeMinimumTargetMajorVersion + oh64.MinorSubsystemVersion = PeMinimumTargetMinorVersion + oh.MinorSubsystemVersion = PeMinimumTargetMinorVersion + oh64.SizeOfImage = f.nextSectOffset + oh.SizeOfImage = f.nextSectOffset + oh64.SizeOfHeaders = uint32(PEFILEHEADR) + oh.SizeOfHeaders = uint32(PEFILEHEADR) + if windowsgui { + oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI + oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI + } else { + oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI + oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI + } + + // Mark as having awareness of terminal services, to avoid ancient compatibility hacks. + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE + + // Enable DEP + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT + + // The DLL can be relocated at load time. + if needPEBaseReloc(ctxt) { + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + } + + // Image can handle a high entropy 64-bit virtual address space. + if ctxt.BuildMode == BuildModePIE { + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA + } + + // Disable stack growth as we don't want Windows to + // fiddle with the thread stack limits, which we set + // ourselves to circumvent the stack checks in the + // Windows exception dispatcher. + // Commit size must be strictly less than reserve + // size otherwise reserve will be rounded up to a + // larger size, as verified with VMMap. + + // On 64-bit, we always reserve 2MB stacks. "Pure" Go code is + // okay with much smaller stacks, but the syscall package + // makes it easy to call into arbitrary C code without cgo, + // and system calls even in "pure" Go code are actually C + // calls that may need more stack than we think. + // + // The default stack reserve size directly affects only the main + // thread. + // + // For other threads, the runtime explicitly asks the kernel + // to use the default stack size so that all stacks are + // consistent. + // + // At thread start, in minit, the runtime queries the OS for + // the actual stack bounds so that the stack size doesn't need + // to be hard-coded into the runtime. + oh64.SizeOfStackReserve = 0x00200000 + if !iscgo { + oh64.SizeOfStackCommit = 0x00001000 + } else { + // TODO(brainman): Maybe remove optional header writing altogether for cgo. + // For cgo it is the external linker that is building final executable. + // And it probably does not use any information stored in optional header. + oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages + } + + oh.SizeOfStackReserve = 0x00100000 + if !iscgo { + oh.SizeOfStackCommit = 0x00001000 + } else { + oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages + } + + oh64.SizeOfHeapReserve = 0x00100000 + oh.SizeOfHeapReserve = 0x00100000 + oh64.SizeOfHeapCommit = 0x00001000 + oh.SizeOfHeapCommit = 0x00001000 + oh64.NumberOfRvaAndSizes = 16 + oh.NumberOfRvaAndSizes = 16 + + if pe64 != 0 { + oh64.DataDirectory = f.dataDirectory + } else { + oh.DataDirectory = f.dataDirectory + } + + if pe64 != 0 { + binary.Write(ctxt.Out, binary.LittleEndian, &oh64) + } else { + binary.Write(ctxt.Out, binary.LittleEndian, &oh) + } +} + +var pefile peFile + +func Peinit(ctxt *Link) { + var l int + + if ctxt.Arch.PtrSize == 8 { + // 64-bit architectures + pe64 = 1 + PEBASE = 1 << 32 + if ctxt.Arch.Family == sys.AMD64 { + // TODO(rsc): For cgo we currently use 32-bit relocations + // that fail when PEBASE is too large. + // We need to fix this, but for now, use a smaller PEBASE. + PEBASE = 1 << 22 + } + var oh64 pe.OptionalHeader64 + l = binary.Size(&oh64) + } else { + // 32-bit architectures + PEBASE = 1 << 22 + var oh pe.OptionalHeader32 + l = binary.Size(&oh) + } + + if ctxt.LinkMode == LinkExternal { + // .rdata section will contain "masks" and "shifts" symbols, and they + // need to be aligned to 16-bytes. So make all sections aligned + // to 32-byte and mark them all IMAGE_SCN_ALIGN_32BYTES so external + // linker will honour that requirement. + PESECTALIGN = 32 + PEFILEALIGN = 0 + // We are creating an object file. The absolute address is irrelevant. + PEBASE = 0 + } + + var sh [16]pe.SectionHeader32 + var fh pe.FileHeader + PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN)) + if ctxt.LinkMode != LinkExternal { + PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN)) + } else { + PESECTHEADR = 0 + } + pefile.nextSectOffset = uint32(PESECTHEADR) + pefile.nextFileOffset = uint32(PEFILEHEADR) + + if ctxt.LinkMode == LinkInternal { + // some mingw libs depend on this symbol, for example, FindPESectionByName + for _, name := range [2]string{"__image_base__", "_image_base__"} { + sb := ctxt.loader.CreateSymForUpdate(name, 0) + sb.SetType(sym.SDATA) + sb.SetValue(PEBASE) + ctxt.loader.SetAttrSpecial(sb.Sym(), true) + ctxt.loader.SetAttrLocal(sb.Sym(), true) + } + } + + HEADR = PEFILEHEADR + if *FlagTextAddr == -1 { + *FlagTextAddr = PEBASE + int64(PESECTHEADR) + } + if *FlagRound == -1 { + *FlagRound = int(PESECTALIGN) + } +} + +func pewrite(ctxt *Link) { + ctxt.Out.SeekSet(0) + if ctxt.LinkMode != LinkExternal { + ctxt.Out.Write(dosstub) + ctxt.Out.WriteStringN("PE", 4) + } + + pefile.writeFileHeader(ctxt) + + pefile.writeOptionalHeader(ctxt) + + for _, sect := range pefile.sections { + sect.write(ctxt.Out, ctxt.LinkMode) + } +} + +func strput(out *OutBuf, s string) { + out.WriteString(s) + out.Write8(0) + // string must be padded to even size + if (len(s)+1)%2 != 0 { + out.Write8(0) + } +} + +func initdynimport(ctxt *Link) *Dll { + ldr := ctxt.loader + var d *Dll + + dr = nil + var m *Imp + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT { + continue + } + dynlib := ldr.SymDynimplib(s) + for d = dr; d != nil; d = d.next { + if d.name == dynlib { + m = new(Imp) + break + } + } + + if d == nil { + d = new(Dll) + d.name = dynlib + d.next = dr + dr = d + m = new(Imp) + } + + // Because external link requires properly stdcall decorated name, + // all external symbols in runtime use %n to denote that the number + // of uinptrs this function consumes. Store the argsize and discard + // the %n suffix if any. + m.argsize = -1 + extName := ldr.SymExtname(s) + if i := strings.IndexByte(extName, '%'); i >= 0 { + var err error + m.argsize, err = strconv.Atoi(extName[i+1:]) + if err != nil { + ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err) + } + m.argsize *= ctxt.Arch.PtrSize + ldr.SetSymExtname(s, extName[:i]) + } + + m.s = s + m.next = d.ms + d.ms = m + } + + if ctxt.IsExternal() { + // Add real symbol name + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + sb := ldr.MakeSymbolUpdater(m.s) + sb.SetType(sym.SDATA) + sb.Grow(int64(ctxt.Arch.PtrSize)) + dynName := sb.Extname() + // only windows/386 requires stdcall decoration + if ctxt.Is386() && m.argsize >= 0 { + dynName += fmt.Sprintf("@%d", m.argsize) + } + dynSym := ldr.CreateSymForUpdate(dynName, 0) + dynSym.SetType(sym.SHOSTOBJ) + r, _ := sb.AddRel(objabi.R_ADDR) + r.SetSym(dynSym.Sym()) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + } + } + } else { + dynamic := ldr.CreateSymForUpdate(".windynamic", 0) + dynamic.SetType(sym.SWINDOWS) + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + sb := ldr.MakeSymbolUpdater(m.s) + sb.SetType(sym.SWINDOWS) + sb.SetValue(dynamic.Size()) + dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) + dynamic.AddInteriorSym(m.s) + } + + dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) + } + } + + return dr +} + +// peimporteddlls returns the gcc command line argument to link all imported +// DLLs. +func peimporteddlls() []string { + var dlls []string + + for d := dr; d != nil; d = d.next { + dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll")) + } + + return dlls +} + +func addimports(ctxt *Link, datsect *peSection) { + ldr := ctxt.loader + startoff := ctxt.Out.Offset() + dynamic := ldr.LookupOrCreateSym(".windynamic", 0) + + // skip import descriptor table (will write it later) + n := uint64(0) + + for d := dr; d != nil; d = d.next { + n++ + } + ctxt.Out.SeekSet(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1)) + + // write dll names + for d := dr; d != nil; d = d.next { + d.nameoff = uint64(ctxt.Out.Offset()) - uint64(startoff) + strput(ctxt.Out, d.name) + } + + // write function names + for d := dr; d != nil; d = d.next { + for m := d.ms; m != nil; m = m.next { + m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff) + ctxt.Out.Write16(0) // hint + strput(ctxt.Out, ldr.SymExtname(m.s)) + } + } + + // write OriginalFirstThunks + oftbase := uint64(ctxt.Out.Offset()) - uint64(startoff) + + n = uint64(ctxt.Out.Offset()) + for d := dr; d != nil; d = d.next { + d.thunkoff = uint64(ctxt.Out.Offset()) - n + for m := d.ms; m != nil; m = m.next { + if pe64 != 0 { + ctxt.Out.Write64(m.off) + } else { + ctxt.Out.Write32(uint32(m.off)) + } + } + + if pe64 != 0 { + ctxt.Out.Write64(0) + } else { + ctxt.Out.Write32(0) + } + } + + // add pe section and pad it at the end + n = uint64(ctxt.Out.Offset()) - uint64(startoff) + + isect := pefile.addSection(".idata", int(n), int(n)) + isect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + isect.checkOffset(startoff) + isect.pad(ctxt.Out, uint32(n)) + endoff := ctxt.Out.Offset() + + // write FirstThunks (allocated in .data section) + ftbase := uint64(ldr.SymValue(dynamic)) - uint64(datsect.virtualAddress) - uint64(PEBASE) + + ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase)) + for d := dr; d != nil; d = d.next { + for m := d.ms; m != nil; m = m.next { + if pe64 != 0 { + ctxt.Out.Write64(m.off) + } else { + ctxt.Out.Write32(uint32(m.off)) + } + } + + if pe64 != 0 { + ctxt.Out.Write64(0) + } else { + ctxt.Out.Write32(0) + } + } + + // finally write import descriptor table + out := ctxt.Out + out.SeekSet(startoff) + + for d := dr; d != nil; d = d.next { + out.Write32(uint32(uint64(isect.virtualAddress) + oftbase + d.thunkoff)) + out.Write32(0) + out.Write32(0) + out.Write32(uint32(uint64(isect.virtualAddress) + d.nameoff)) + out.Write32(uint32(uint64(datsect.virtualAddress) + ftbase + d.thunkoff)) + } + + out.Write32(0) //end + out.Write32(0) + out.Write32(0) + out.Write32(0) + out.Write32(0) + + // update data directory + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(ldr.SymValue(dynamic) - PEBASE) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(ldr.SymSize(dynamic)) + + out.SeekSet(endoff) +} + +func initdynexport(ctxt *Link) { + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) { + continue + } + if len(dexport)+1 > cap(dexport) { + ctxt.Errorf(s, "pe dynexport table is full") + errorexit() + } + + dexport = append(dexport, s) + } + + sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) }) +} + +func addexports(ctxt *Link) { + ldr := ctxt.loader + var e IMAGE_EXPORT_DIRECTORY + + nexport := len(dexport) + size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1 + for _, s := range dexport { + size += len(ldr.SymExtname(s)) + 1 + } + + if nexport == 0 { + return + } + + sect := pefile.addSection(".edata", size, size) + sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + sect.checkOffset(ctxt.Out.Offset()) + va := int(sect.virtualAddress) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize + + vaName := va + binary.Size(&e) + nexport*4 + vaAddr := va + binary.Size(&e) + vaNa := va + binary.Size(&e) + nexport*8 + + e.Characteristics = 0 + e.MajorVersion = 0 + e.MinorVersion = 0 + e.NumberOfFunctions = uint32(nexport) + e.NumberOfNames = uint32(nexport) + e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names. + e.Base = 1 + e.AddressOfFunctions = uint32(vaAddr) + e.AddressOfNames = uint32(vaName) + e.AddressOfNameOrdinals = uint32(vaNa) + + out := ctxt.Out + + // put IMAGE_EXPORT_DIRECTORY + binary.Write(out, binary.LittleEndian, &e) + + // put EXPORT Address Table + for _, s := range dexport { + out.Write32(uint32(ldr.SymValue(s) - PEBASE)) + } + + // put EXPORT Name Pointer Table + v := int(e.Name + uint32(len(*flagOutfile)) + 1) + + for _, s := range dexport { + out.Write32(uint32(v)) + v += len(ldr.SymExtname(s)) + 1 + } + + // put EXPORT Ordinal Table + for i := 0; i < nexport; i++ { + out.Write16(uint16(i)) + } + + // put Names + out.WriteStringN(*flagOutfile, len(*flagOutfile)+1) + + for _, s := range dexport { + name := ldr.SymExtname(s) + out.WriteStringN(name, len(name)+1) + } + sect.pad(out, uint32(size)) +} + +// peBaseRelocEntry represents a single relocation entry. +type peBaseRelocEntry struct { + typeOff uint16 +} + +// peBaseRelocBlock represents a Base Relocation Block. A block +// is a collection of relocation entries in a page, where each +// entry describes a single relocation. +// The block page RVA (Relative Virtual Address) is the index +// into peBaseRelocTable.blocks. +type peBaseRelocBlock struct { + entries []peBaseRelocEntry +} + +// pePages is a type used to store the list of pages for which there +// are base relocation blocks. This is defined as a type so that +// it can be sorted. +type pePages []uint32 + +func (p pePages) Len() int { return len(p) } +func (p pePages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p pePages) Less(i, j int) bool { return p[i] < p[j] } + +// A PE base relocation table is a list of blocks, where each block +// contains relocation information for a single page. The blocks +// must be emitted in order of page virtual address. +// See https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-reloc-section-image-only +type peBaseRelocTable struct { + blocks map[uint32]peBaseRelocBlock + + // pePages is a list of keys into blocks map. + // It is stored separately for ease of sorting. + pages pePages +} + +func (rt *peBaseRelocTable) init(ctxt *Link) { + rt.blocks = make(map[uint32]peBaseRelocBlock) +} + +func (rt *peBaseRelocTable) addentry(ldr *loader.Loader, s loader.Sym, r *loader.Reloc) { + // pageSize is the size in bytes of a page + // described by a base relocation block. + const pageSize = 0x1000 + const pageMask = pageSize - 1 + + addr := ldr.SymValue(s) + int64(r.Off()) - int64(PEBASE) + page := uint32(addr &^ pageMask) + off := uint32(addr & pageMask) + + b, ok := rt.blocks[page] + if !ok { + rt.pages = append(rt.pages, page) + } + + e := peBaseRelocEntry{ + typeOff: uint16(off & 0xFFF), + } + + // Set entry type + switch r.Siz() { + default: + Exitf("unsupported relocation size %d\n", r.Siz) + case 4: + e.typeOff |= uint16(IMAGE_REL_BASED_HIGHLOW << 12) + case 8: + e.typeOff |= uint16(IMAGE_REL_BASED_DIR64 << 12) + } + + b.entries = append(b.entries, e) + rt.blocks[page] = b +} + +func (rt *peBaseRelocTable) write(ctxt *Link) { + out := ctxt.Out + + // sort the pages array + sort.Sort(rt.pages) + + for _, p := range rt.pages { + b := rt.blocks[p] + const sizeOfPEbaseRelocBlock = 8 // 2 * sizeof(uint32) + blockSize := uint32(sizeOfPEbaseRelocBlock + len(b.entries)*2) + out.Write32(p) + out.Write32(blockSize) + + for _, e := range b.entries { + out.Write16(e.typeOff) + } + } +} + +func addPEBaseRelocSym(ldr *loader.Loader, s loader.Sym, rt *peBaseRelocTable) { + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Type() >= objabi.ElfRelocOffset { + continue + } + if r.Siz() == 0 { // informational relocation + continue + } + if r.Type() == objabi.R_DWARFFILEREF { + continue + } + rs := r.Sym() + if rs == 0 { + continue + } + if !ldr.AttrReachable(s) { + continue + } + + switch r.Type() { + default: + case objabi.R_ADDR: + rt.addentry(ldr, s, &r) + } + } +} + +func needPEBaseReloc(ctxt *Link) bool { + // Non-PIE x86 binaries don't need the base relocation table. + // Everyone else does. + if (ctxt.Arch.Family == sys.I386 || ctxt.Arch.Family == sys.AMD64) && ctxt.BuildMode != BuildModePIE { + return false + } + return true +} + +func addPEBaseReloc(ctxt *Link) { + if !needPEBaseReloc(ctxt) { + return + } + + var rt peBaseRelocTable + rt.init(ctxt) + + // Get relocation information + ldr := ctxt.loader + for _, s := range ctxt.Textp { + addPEBaseRelocSym(ldr, s, &rt) + } + for _, s := range ctxt.datap { + addPEBaseRelocSym(ldr, s, &rt) + } + + // Write relocation information + startoff := ctxt.Out.Offset() + rt.write(ctxt) + size := ctxt.Out.Offset() - startoff + + // Add a PE section and pad it at the end + rsect := pefile.addSection(".reloc", int(size), int(size)) + rsect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + rsect.checkOffset(startoff) + rsect.pad(ctxt.Out, uint32(size)) + + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize +} + +func (ctxt *Link) dope() { + initdynimport(ctxt) + initdynexport(ctxt) +} + +func setpersrc(ctxt *Link, syms []loader.Sym) { + if len(rsrcsyms) != 0 { + Errorf(nil, "too many .rsrc sections") + } + rsrcsyms = syms +} + +func addpersrc(ctxt *Link) { + if len(rsrcsyms) == 0 { + return + } + + var size int64 + for _, rsrcsym := range rsrcsyms { + size += ctxt.loader.SymSize(rsrcsym) + } + h := pefile.addSection(".rsrc", int(size), int(size)) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA + h.checkOffset(ctxt.Out.Offset()) + + for _, rsrcsym := range rsrcsyms { + // A split resource happens when the actual resource data and its relocations are + // split across multiple sections, denoted by a $01 or $02 at the end of the .rsrc + // section name. + splitResources := strings.Contains(ctxt.loader.SymName(rsrcsym), ".rsrc$") + relocs := ctxt.loader.Relocs(rsrcsym) + data := ctxt.loader.Data(rsrcsym) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + p := data[r.Off():] + val := uint32(int64(h.virtualAddress) + r.Add()) + if splitResources { + // If we're a split resource section, and that section has relocation + // symbols, then the data that it points to doesn't actually begin at + // the virtual address listed in this current section, but rather + // begins at the section immediately after this one. So, in order to + // calculate the proper virtual address of the data it's pointing to, + // we have to add the length of this section to the virtual address. + // This works because .rsrc sections are divided into two (but not more) + // of these sections. + val += uint32(len(data)) + } + binary.LittleEndian.PutUint32(p, val) + } + ctxt.Out.Write(data) + } + h.pad(ctxt.Out, uint32(size)) + + // update data directory + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize +} + +func asmbPe(ctxt *Link) { + t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length)) + t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ + if ctxt.LinkMode == LinkExternal { + // some data symbols (e.g. masks) end up in the .text section, and they normally + // expect larger alignment requirement than the default text section alignment. + t.characteristics |= IMAGE_SCN_ALIGN_32BYTES + } + t.checkSegment(&Segtext) + pefile.textSect = t + + ro := pefile.addSection(".rdata", int(Segrodata.Length), int(Segrodata.Length)) + ro.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + if ctxt.LinkMode == LinkExternal { + // some data symbols (e.g. masks) end up in the .rdata section, and they normally + // expect larger alignment requirement than the default text section alignment. + ro.characteristics |= IMAGE_SCN_ALIGN_32BYTES + } + ro.checkSegment(&Segrodata) + pefile.rdataSect = ro + + var d *peSection + if ctxt.LinkMode != LinkExternal { + d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen)) + d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + d.checkSegment(&Segdata) + pefile.dataSect = d + } else { + d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen)) + d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES + d.checkSegment(&Segdata) + pefile.dataSect = d + + b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0) + b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES + b.pointerToRawData = 0 + pefile.bssSect = b + } + + pefile.addDWARF() + + if ctxt.LinkMode == LinkExternal { + pefile.ctorsSect = pefile.addInitArray(ctxt) + } + + ctxt.Out.SeekSet(int64(pefile.nextFileOffset)) + if ctxt.LinkMode != LinkExternal { + addimports(ctxt, d) + addexports(ctxt) + addPEBaseReloc(ctxt) + } + pefile.writeSymbolTableAndStringTable(ctxt) + addpersrc(ctxt) + if ctxt.LinkMode == LinkExternal { + pefile.emitRelocations(ctxt) + } + + pewrite(ctxt) +} diff --git a/src/cmd/link/internal/ld/stackcheck.go b/src/cmd/link/internal/ld/stackcheck.go new file mode 100644 index 0000000..c82dafe --- /dev/null +++ b/src/cmd/link/internal/ld/stackcheck.go @@ -0,0 +1,421 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/link/internal/loader" + "fmt" + "internal/buildcfg" + "sort" + "strings" +) + +type stackCheck struct { + ctxt *Link + ldr *loader.Loader + morestack loader.Sym + callSize int // The number of bytes added by a CALL + + // height records the maximum number of bytes a function and + // its callees can add to the stack without a split check. + height map[loader.Sym]int16 + + // graph records the out-edges from each symbol. This is only + // populated on a second pass if the first pass reveals an + // over-limit function. + graph map[loader.Sym][]stackCheckEdge +} + +type stackCheckEdge struct { + growth int // Stack growth in bytes at call to target + target loader.Sym // 0 for stack growth without a call +} + +// stackCheckCycle is a sentinel stored in the height map to detect if +// we've found a cycle. This is effectively an "infinite" stack +// height, so we use the closest value to infinity that we can. +const stackCheckCycle int16 = 1<<15 - 1 + +// stackCheckIndirect is a sentinel Sym value used to represent the +// target of an indirect/closure call. +const stackCheckIndirect loader.Sym = -1 + +// doStackCheck walks the call tree to check that there is always +// enough stack space for call frames, especially for a chain of +// nosplit functions. +// +// It walks all functions to accumulate the number of bytes they can +// grow the stack by without a split check and checks this against the +// limit. +func (ctxt *Link) doStackCheck() { + sc := newStackCheck(ctxt, false) + + // limit is number of bytes a splittable function ensures are + // available on the stack. If any call chain exceeds this + // depth, the stack check test fails. + // + // The call to morestack in every splittable function ensures + // that there are at least StackLimit bytes available below SP + // when morestack returns. + limit := objabi.StackLimit(*flagRace) - sc.callSize + if buildcfg.GOARCH == "arm64" { + // Need an extra 8 bytes below SP to save FP. + limit -= 8 + } + + // Compute stack heights without any back-tracking information. + // This will almost certainly succeed and we can simply + // return. If it fails, we do a second pass with back-tracking + // to produce a good error message. + // + // This accumulates stack heights bottom-up so it only has to + // visit every function once. + var failed []loader.Sym + for _, s := range ctxt.Textp { + if sc.check(s) > limit { + failed = append(failed, s) + } + } + + if len(failed) > 0 { + // Something was over-limit, so now we do the more + // expensive work to report a good error. First, for + // the over-limit functions, redo the stack check but + // record the graph this time. + sc = newStackCheck(ctxt, true) + for _, s := range failed { + sc.check(s) + } + + // Find the roots of the graph (functions that are not + // called by any other function). + roots := sc.findRoots() + + // Find and report all paths that go over the limit. + // This accumulates stack depths top-down. This is + // much less efficient because we may have to visit + // the same function multiple times at different + // depths, but lets us find all paths. + for _, root := range roots { + ctxt.Errorf(root, "nosplit stack over %d byte limit", limit) + chain := []stackCheckChain{{stackCheckEdge{0, root}, false}} + sc.report(root, limit, &chain) + } + } +} + +func newStackCheck(ctxt *Link, graph bool) *stackCheck { + sc := &stackCheck{ + ctxt: ctxt, + ldr: ctxt.loader, + morestack: ctxt.loader.Lookup("runtime.morestack", 0), + height: make(map[loader.Sym]int16, len(ctxt.Textp)), + } + // Compute stack effect of a CALL operation. 0 on LR machines. + // 1 register pushed on non-LR machines. + if !ctxt.Arch.HasLR { + sc.callSize = ctxt.Arch.RegSize + } + + if graph { + // We're going to record the call graph. + sc.graph = make(map[loader.Sym][]stackCheckEdge) + } + + return sc +} + +func (sc *stackCheck) symName(sym loader.Sym) string { + switch sym { + case stackCheckIndirect: + return "indirect" + case 0: + return "leaf" + } + return fmt.Sprintf("%s<%d>", sc.ldr.SymName(sym), sc.ldr.SymVersion(sym)) +} + +// check returns the stack height of sym. It populates sc.height and +// sc.graph for sym and every function in its call tree. +func (sc *stackCheck) check(sym loader.Sym) int { + if h, ok := sc.height[sym]; ok { + // We've already visited this symbol or we're in a cycle. + return int(h) + } + // Store the sentinel so we can detect cycles. + sc.height[sym] = stackCheckCycle + // Compute and record the height and optionally edges. + h, edges := sc.computeHeight(sym, *flagDebugNosplit || sc.graph != nil) + if h > int(stackCheckCycle) { // Prevent integer overflow + h = int(stackCheckCycle) + } + sc.height[sym] = int16(h) + if sc.graph != nil { + sc.graph[sym] = edges + } + + if *flagDebugNosplit { + for _, edge := range edges { + fmt.Printf("nosplit: %s +%d", sc.symName(sym), edge.growth) + if edge.target == 0 { + // Local stack growth or leaf function. + fmt.Printf("\n") + } else { + fmt.Printf(" -> %s\n", sc.symName(edge.target)) + } + } + } + + return h +} + +// computeHeight returns the stack height of sym. If graph is true, it +// also returns the out-edges of sym. +// +// Caching is applied to this in check. Call check instead of calling +// this directly. +func (sc *stackCheck) computeHeight(sym loader.Sym, graph bool) (int, []stackCheckEdge) { + ldr := sc.ldr + + // Check special cases. + if sym == sc.morestack { + // morestack looks like it calls functions, but they + // either happen only when already on the system stack + // (where there is ~infinite space), or after + // switching to the system stack. Hence, its stack + // height on the user stack is 0. + return 0, nil + } + if sym == stackCheckIndirect { + // Assume that indirect/closure calls are always to + // splittable functions, so they just need enough room + // to call morestack. + return sc.callSize, []stackCheckEdge{{sc.callSize, sc.morestack}} + } + + // Ignore calls to external functions. Assume that these calls + // are only ever happening on the system stack, where there's + // plenty of room. + if ldr.AttrExternal(sym) { + return 0, nil + } + if info := ldr.FuncInfo(sym); !info.Valid() { // also external + return 0, nil + } + + // Track the maximum height of this function and, if we're + // recording the graph, its out-edges. + var edges []stackCheckEdge + maxHeight := 0 + ctxt := sc.ctxt + // addEdge adds a stack growth out of this function to + // function "target" or, if target == 0, a local stack growth + // within the function. + addEdge := func(growth int, target loader.Sym) { + if graph { + edges = append(edges, stackCheckEdge{growth, target}) + } + height := growth + if target != 0 { // Don't walk into the leaf "edge" + height += sc.check(target) + } + if height > maxHeight { + maxHeight = height + } + } + + if !ldr.IsNoSplit(sym) { + // Splittable functions start with a call to + // morestack, after which their height is 0. Account + // for the height of the call to morestack. + addEdge(sc.callSize, sc.morestack) + return maxHeight, edges + } + + // This function is nosplit, so it adjusts SP without a split + // check. + // + // Walk through SP adjustments in function, consuming relocs + // and following calls. + maxLocalHeight := 0 + relocs, ri := ldr.Relocs(sym), 0 + pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) + for pcsp.Init(ldr.Data(ldr.Pcsp(sym))); !pcsp.Done; pcsp.Next() { + // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). + height := int(pcsp.Value) + if height > maxLocalHeight { + maxLocalHeight = height + } + + // Process calls in this span. + for ; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if uint32(r.Off()) >= pcsp.NextPC { + break + } + t := r.Type() + if t.IsDirectCall() || t == objabi.R_CALLIND { + growth := height + sc.callSize + var target loader.Sym + if t == objabi.R_CALLIND { + target = stackCheckIndirect + } else { + target = r.Sym() + } + addEdge(growth, target) + } + } + } + if maxLocalHeight > maxHeight { + // This is either a leaf function, or the function + // grew its stack to larger than the maximum call + // height between calls. Either way, record that local + // stack growth. + addEdge(maxLocalHeight, 0) + } + + return maxHeight, edges +} + +func (sc *stackCheck) findRoots() []loader.Sym { + // Collect all nodes. + nodes := make(map[loader.Sym]struct{}) + for k := range sc.graph { + nodes[k] = struct{}{} + } + + // Start a DFS from each node and delete all reachable + // children. If we encounter an unrooted cycle, this will + // delete everything in that cycle, so we detect this case and + // track the lowest-numbered node encountered in the cycle and + // put that node back as a root. + var walk func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym) + walk = func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym) { + if _, ok := nodes[sym]; !ok { + // We already deleted this node. + return false, 0 + } + delete(nodes, sym) + + if origin == sym { + // We found an unrooted cycle. We already + // deleted all children of this node. Walk + // back up, tracking the lowest numbered + // symbol in this cycle. + return true, sym + } + + // Delete children of this node. + for _, out := range sc.graph[sym] { + if c, l := walk(origin, out.target); c { + cycle = true + if lowest == 0 { + // On first cycle detection, + // add sym to the set of + // lowest-numbered candidates. + lowest = sym + } + if l < lowest { + lowest = l + } + } + } + return + } + for k := range nodes { + // Delete all children of k. + for _, out := range sc.graph[k] { + if cycle, lowest := walk(k, out.target); cycle { + // This is an unrooted cycle so we + // just deleted everything. Put back + // the lowest-numbered symbol. + nodes[lowest] = struct{}{} + } + } + } + + // Sort roots by height. This makes the result deterministic + // and also improves the error reporting. + var roots []loader.Sym + for k := range nodes { + roots = append(roots, k) + } + sort.Slice(roots, func(i, j int) bool { + h1, h2 := sc.height[roots[i]], sc.height[roots[j]] + if h1 != h2 { + return h1 > h2 + } + // Secondary sort by Sym. + return roots[i] < roots[j] + }) + return roots +} + +type stackCheckChain struct { + stackCheckEdge + printed bool +} + +func (sc *stackCheck) report(sym loader.Sym, depth int, chain *[]stackCheckChain) { + // Walk the out-edges of sym. We temporarily pull the edges + // out of the graph to detect cycles and prevent infinite + // recursion. + edges, ok := sc.graph[sym] + isCycle := !(ok || sym == 0) + delete(sc.graph, sym) + for _, out := range edges { + *chain = append(*chain, stackCheckChain{out, false}) + sc.report(out.target, depth-out.growth, chain) + *chain = (*chain)[:len(*chain)-1] + } + sc.graph[sym] = edges + + // If we've reached the end of a chain and it went over the + // stack limit or was a cycle that would eventually go over, + // print the whole chain. + // + // We should either be in morestack (which has no out-edges) + // or the sentinel 0 Sym "called" from a leaf function (which + // has no out-edges), or we came back around a cycle (possibly + // to ourselves) and edges was temporarily nil'd. + if len(edges) == 0 && (depth < 0 || isCycle) { + var indent string + for i := range *chain { + ent := &(*chain)[i] + if ent.printed { + // Already printed on an earlier part + // of this call tree. + continue + } + ent.printed = true + + if i == 0 { + // chain[0] is just the root function, + // not a stack growth. + fmt.Printf("%s\n", sc.symName(ent.target)) + continue + } + + indent = strings.Repeat(" ", i) + fmt.Print(indent) + // Grows the stack X bytes and (maybe) calls Y. + fmt.Printf("grows %d bytes", ent.growth) + if ent.target == 0 { + // Not a call, just a leaf. Print nothing. + } else { + fmt.Printf(", calls %s", sc.symName(ent.target)) + } + fmt.Printf("\n") + } + // Print how far over this chain went. + if isCycle { + fmt.Printf("%sinfinite cycle\n", indent) + } else { + fmt.Printf("%s%d bytes over limit\n", indent, -depth) + } + } +} diff --git a/src/cmd/link/internal/ld/stackcheck_test.go b/src/cmd/link/internal/ld/stackcheck_test.go new file mode 100644 index 0000000..dd7e205 --- /dev/null +++ b/src/cmd/link/internal/ld/stackcheck_test.go @@ -0,0 +1,87 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "fmt" + "internal/testenv" + "os" + "regexp" + "strconv" + "testing" +) + +// See also $GOROOT/test/nosplit.go for multi-platform edge case tests. + +func TestStackCheckOutput(t *testing.T) { + testenv.MustHaveGoBuild(t) + t.Parallel() + + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", os.DevNull, "./testdata/stackcheck") + // The rules for computing frame sizes on all of the + // architectures are complicated, so just do this on amd64. + cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux") + outB, err := cmd.CombinedOutput() + + if err == nil { + t.Fatalf("expected link to fail") + } + out := string(outB) + + t.Logf("linker output:\n%s", out) + + // Get expected limit. + limitRe := regexp.MustCompile(`nosplit stack over (\d+) byte limit`) + m := limitRe.FindStringSubmatch(out) + if m == nil { + t.Fatalf("no overflow errors in output") + } + limit, _ := strconv.Atoi(m[1]) + + wantMap := map[string]string{ + "main.startSelf": fmt.Sprintf( + `main.startSelf<0> + grows 1008 bytes + %d bytes over limit +`, 1008-limit), + "main.startChain": fmt.Sprintf( + `main.startChain<0> + grows 32 bytes, calls main.chain0<0> + grows 48 bytes, calls main.chainEnd<0> + grows 1008 bytes + %d bytes over limit + grows 32 bytes, calls main.chain2<0> + grows 80 bytes, calls main.chainEnd<0> + grows 1008 bytes + %d bytes over limit +`, 32+48+1008-limit, 32+80+1008-limit), + "main.startRec": `main.startRec<0> + grows 8 bytes, calls main.startRec0<0> + grows 8 bytes, calls main.startRec<0> + infinite cycle +`, + } + + // Parse stanzas + stanza := regexp.MustCompile(`^(.*): nosplit stack over \d+ byte limit\n(.*\n(?: .*\n)*)`) + // Strip comments from cmd/go + out = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(out, "") + for len(out) > 0 { + m := stanza.FindStringSubmatch(out) + if m == nil { + t.Fatalf("unexpected output:\n%s", out) + } + out = out[len(m[0]):] + fn := m[1] + got := m[2] + + want, ok := wantMap[fn] + if !ok { + t.Errorf("unexpected function: %s", fn) + } else if want != got { + t.Errorf("want:\n%sgot:\n%s", want, got) + } + } +} diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go new file mode 100644 index 0000000..d51a59e --- /dev/null +++ b/src/cmd/link/internal/ld/sym.go @@ -0,0 +1,117 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "internal/buildcfg" + "log" + "runtime" +) + +func linknew(arch *sys.Arch) *Link { + ler := loader.ErrorReporter{AfterErrorAction: afterErrorAction} + ctxt := &Link{ + Target: Target{Arch: arch}, + version: sym.SymVerStatic, + outSem: make(chan int, 2*runtime.GOMAXPROCS(0)), + Out: NewOutBuf(arch), + LibraryByPkg: make(map[string]*sym.Library), + numelfsym: 1, + ErrorReporter: ErrorReporter{ErrorReporter: ler}, + generatorSyms: make(map[loader.Sym]generatorFunc), + } + + if buildcfg.GOARCH != arch.Name { + log.Fatalf("invalid buildcfg.GOARCH %s (want %s)", buildcfg.GOARCH, arch.Name) + } + + AtExit(func() { + if nerrors > 0 { + ctxt.Out.ErrorClose() + mayberemoveoutfile() + } + }) + + return ctxt +} + +// computeTLSOffset records the thread-local storage offset. +// Not used for Android where the TLS offset is determined at runtime. +func (ctxt *Link) computeTLSOffset() { + switch ctxt.HeadType { + default: + log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType) + + case objabi.Hplan9, objabi.Hwindows, objabi.Hjs, objabi.Haix: + break + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd, + objabi.Hdragonfly, + objabi.Hsolaris: + /* + * ELF uses TLS offset negative from FS. + * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). + * Known to low-level assembly in package runtime and runtime/cgo. + */ + ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize + + case objabi.Hdarwin: + /* + * OS X system constants - offset from 0(GS) to our TLS. + */ + switch ctxt.Arch.Family { + default: + log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) + + /* + * For x86, Apple has reserved a slot in the TLS for Go. See issue 23617. + * That slot is at offset 0x30 on amd64. + * The slot will hold the G pointer. + * These constants should match those in runtime/sys_darwin_amd64.s + * and runtime/cgo/gcc_darwin_amd64.c. + */ + case sys.AMD64: + ctxt.Tlsoffset = 0x30 + + case sys.ARM64: + ctxt.Tlsoffset = 0 // dummy value, not needed + } + } + +} diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go new file mode 100644 index 0000000..21a1466 --- /dev/null +++ b/src/cmd/link/internal/ld/symtab.go @@ -0,0 +1,890 @@ +// Inferno utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "fmt" + "internal/buildcfg" + "path/filepath" + "strings" +) + +// Symbol table. + +func putelfstr(s string) int { + if len(Elfstrdat) == 0 && s != "" { + // first entry must be empty string + putelfstr("") + } + + off := len(Elfstrdat) + Elfstrdat = append(Elfstrdat, s...) + Elfstrdat = append(Elfstrdat, 0) + return off +} + +func putelfsyment(out *OutBuf, off int, addr int64, size int64, info uint8, shndx elf.SectionIndex, other int) { + if elf64 { + out.Write32(uint32(off)) + out.Write8(info) + out.Write8(uint8(other)) + out.Write16(uint16(shndx)) + out.Write64(uint64(addr)) + out.Write64(uint64(size)) + symSize += ELF64SYMSIZE + } else { + out.Write32(uint32(off)) + out.Write32(uint32(addr)) + out.Write32(uint32(size)) + out.Write8(info) + out.Write8(uint8(other)) + out.Write16(uint16(shndx)) + symSize += ELF32SYMSIZE + } +} + +func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) { + ldr := ctxt.loader + addr := ldr.SymValue(x) + size := ldr.SymSize(x) + + xo := x + if ldr.OuterSym(x) != 0 { + xo = ldr.OuterSym(x) + } + xot := ldr.SymType(xo) + xosect := ldr.SymSect(xo) + + var elfshnum elf.SectionIndex + if xot == sym.SDYNIMPORT || xot == sym.SHOSTOBJ || xot == sym.SUNDEFEXT { + elfshnum = elf.SHN_UNDEF + size = 0 + } else { + if xosect == nil { + ldr.Errorf(x, "missing section in putelfsym") + return + } + if xosect.Elfsect == nil { + ldr.Errorf(x, "missing ELF section in putelfsym") + return + } + elfshnum = xosect.Elfsect.(*ElfShdr).shnum + } + + sname := ldr.SymExtname(x) + sname = mangleABIName(ctxt, ldr, x, sname) + + // One pass for each binding: elf.STB_LOCAL, elf.STB_GLOBAL, + // maybe one day elf.STB_WEAK. + bind := elf.STB_GLOBAL + if ldr.IsFileLocal(x) && !isStaticTmp(sname) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) { + // Static tmp is package local, but a package can be shared among multiple DSOs. + // They need to have a single view of the static tmp that are writable. + bind = elf.STB_LOCAL + } + + // In external linking mode, we have to invoke gcc with -rdynamic + // to get the exported symbols put into the dynamic symbol table. + // To avoid filling the dynamic table with lots of unnecessary symbols, + // mark all Go symbols local (not global) in the final executable. + // But when we're dynamically linking, we need all those global symbols. + if !ctxt.DynlinkingGo() && ctxt.IsExternal() && !ldr.AttrCgoExportStatic(x) && elfshnum != elf.SHN_UNDEF { + bind = elf.STB_LOCAL + } + + if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF { + addr -= int64(xosect.Vaddr) + } + other := int(elf.STV_DEFAULT) + if ldr.AttrVisibilityHidden(x) { + // TODO(mwhudson): We only set AttrVisibilityHidden in ldelf, i.e. when + // internally linking. But STV_HIDDEN visibility only matters in object + // files and shared libraries, and as we are a long way from implementing + // internal linking for shared libraries and only create object files when + // externally linking, I don't think this makes a lot of sense. + other = int(elf.STV_HIDDEN) + } + if ctxt.IsPPC64() && typ == elf.STT_FUNC && ldr.AttrShared(x) && ldr.SymName(x) != "runtime.duffzero" && ldr.SymName(x) != "runtime.duffcopy" { + // On ppc64 the top three bits of the st_other field indicate how + // many instructions separate the global and local entry points. In + // our case it is two instructions, indicated by the value 3. + // The conditions here match those in preprocess in + // cmd/internal/obj/ppc64/obj9.go, which is where the + // instructions are inserted. + other |= 3 << 5 + } + + // When dynamically linking, we create Symbols by reading the names from + // the symbol tables of the shared libraries and so the names need to + // match exactly. Tools like DTrace will have to wait for now. + if !ctxt.DynlinkingGo() { + // Rewrite · to . for ASCII-only tools like DTrace (sigh) + sname = strings.Replace(sname, "·", ".", -1) + } + + if ctxt.DynlinkingGo() && bind == elf.STB_GLOBAL && curbind == elf.STB_LOCAL && ldr.SymType(x) == sym.STEXT { + // When dynamically linking, we want references to functions defined + // in this module to always be to the function object, not to the + // PLT. We force this by writing an additional local symbol for every + // global function symbol and making all relocations against the + // global symbol refer to this local symbol instead (see + // (*sym.Symbol).ElfsymForReloc). This is approximately equivalent to the + // ELF linker -Bsymbolic-functions option, but that is buggy on + // several platforms. + putelfsyment(ctxt.Out, putelfstr("local."+sname), addr, size, elf.ST_INFO(elf.STB_LOCAL, typ), elfshnum, other) + ldr.SetSymLocalElfSym(x, int32(ctxt.numelfsym)) + ctxt.numelfsym++ + return + } else if bind != curbind { + return + } + + putelfsyment(ctxt.Out, putelfstr(sname), addr, size, elf.ST_INFO(bind, typ), elfshnum, other) + ldr.SetSymElfSym(x, int32(ctxt.numelfsym)) + ctxt.numelfsym++ +} + +func putelfsectionsym(ctxt *Link, out *OutBuf, s loader.Sym, shndx elf.SectionIndex) { + putelfsyment(out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_SECTION), shndx, 0) + ctxt.loader.SetSymElfSym(s, int32(ctxt.numelfsym)) + ctxt.numelfsym++ +} + +func genelfsym(ctxt *Link, elfbind elf.SymBind) { + ldr := ctxt.loader + + // runtime.text marker symbol(s). + s := ldr.Lookup("runtime.text", 0) + putelfsym(ctxt, s, elf.STT_FUNC, elfbind) + for k, sect := range Segtext.Sections[1:] { + n := k + 1 + if sect.Name != ".text" || (ctxt.IsAIX() && ctxt.IsExternal()) { + // On AIX, runtime.text.X are symbols already in the symtab. + break + } + s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0) + if s == 0 { + break + } + if ldr.SymType(s) != sym.STEXT { + panic("unexpected type for runtime.text symbol") + } + putelfsym(ctxt, s, elf.STT_FUNC, elfbind) + } + + // Text symbols. + for _, s := range ctxt.Textp { + putelfsym(ctxt, s, elf.STT_FUNC, elfbind) + } + + // runtime.etext marker symbol. + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + putelfsym(ctxt, s, elf.STT_FUNC, elfbind) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + // FIXME: avoid having to do name inspections here. + // NB: the restrictions below on file local symbols are a bit + // arbitrary -- if it turns out we need nameless static + // symbols they could be relaxed/removed. + sn := ldr.SymName(s) + if (sn == "" || sn[0] == '.') && ldr.IsFileLocal(s) { + panic(fmt.Sprintf("unexpected file local symbol %d %s<%d>\n", + s, sn, ldr.SymVersion(s))) + } + if (sn == "" || sn[0] == '.') && !ldr.IsFileLocal(s) { + return false + } + return true + } + + // Data symbols. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + st := ldr.SymType(s) + if st >= sym.SELFRXSECT && st < sym.SXREF { + typ := elf.STT_OBJECT + if st == sym.STLSBSS { + if ctxt.IsInternal() { + continue + } + typ = elf.STT_TLS + } + if !shouldBeInSymbolTable(s) { + continue + } + putelfsym(ctxt, s, typ, elfbind) + continue + } + if st == sym.SHOSTOBJ || st == sym.SDYNIMPORT || st == sym.SUNDEFEXT { + putelfsym(ctxt, s, ldr.SymElfType(s), elfbind) + } + } +} + +func asmElfSym(ctxt *Link) { + + // the first symbol entry is reserved + putelfsyment(ctxt.Out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE), 0, 0) + + dwarfaddelfsectionsyms(ctxt) + + // Some linkers will add a FILE sym if one is not present. + // Avoid having the working directory inserted into the symbol table. + // It is added with a name to avoid problems with external linking + // encountered on some versions of Solaris. See issue #14957. + putelfsyment(ctxt.Out, putelfstr("go.go"), 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_FILE), elf.SHN_ABS, 0) + ctxt.numelfsym++ + + bindings := []elf.SymBind{elf.STB_LOCAL, elf.STB_GLOBAL} + for _, elfbind := range bindings { + if elfbind == elf.STB_GLOBAL { + elfglobalsymndx = ctxt.numelfsym + } + genelfsym(ctxt, elfbind) + } +} + +func putplan9sym(ctxt *Link, ldr *loader.Loader, s loader.Sym, char SymbolType) { + t := int(char) + if ldr.IsFileLocal(s) { + t += 'a' - 'A' + } + l := 4 + addr := ldr.SymValue(s) + if ctxt.IsAMD64() && !flag8 { + ctxt.Out.Write32b(uint32(addr >> 32)) + l = 8 + } + + ctxt.Out.Write32b(uint32(addr)) + ctxt.Out.Write8(uint8(t + 0x80)) /* 0x80 is variable length */ + + name := ldr.SymName(s) + name = mangleABIName(ctxt, ldr, s, name) + ctxt.Out.WriteString(name) + ctxt.Out.Write8(0) + + symSize += int32(l) + 1 + int32(len(name)) + 1 +} + +func asmbPlan9Sym(ctxt *Link) { + ldr := ctxt.loader + + // Add special runtime.text and runtime.etext symbols. + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + putplan9sym(ctxt, ldr, s, TextSym) + } + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + putplan9sym(ctxt, ldr, s, TextSym) + } + + // Add text symbols. + for _, s := range ctxt.Textp { + putplan9sym(ctxt, ldr, s, TextSym) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + name := ldr.SymName(s) // TODO: try not to read the name + if name == "" || name[0] == '.' { + return false + } + return true + } + + // Add data symbols and external references. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata + if t == sym.STLSBSS { + continue + } + if !shouldBeInSymbolTable(s) { + continue + } + char := DataSym + if t == sym.SBSS || t == sym.SNOPTRBSS { + char = BSSSym + } + putplan9sym(ctxt, ldr, s, char) + } + } +} + +type byPkg []*sym.Library + +func (libs byPkg) Len() int { + return len(libs) +} + +func (libs byPkg) Less(a, b int) bool { + return libs[a].Pkg < libs[b].Pkg +} + +func (libs byPkg) Swap(a, b int) { + libs[a], libs[b] = libs[b], libs[a] +} + +// Create a table with information on the text sections. +// Return the symbol of the table, and number of sections. +func textsectionmap(ctxt *Link) (loader.Sym, uint32) { + ldr := ctxt.loader + t := ldr.CreateSymForUpdate("runtime.textsectionmap", 0) + t.SetType(sym.SRODATA) + nsections := int64(0) + + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + nsections++ + } else { + break + } + } + t.Grow(3 * nsections * int64(ctxt.Arch.PtrSize)) + + off := int64(0) + n := 0 + + // The vaddr for each text section is the difference between the section's + // Vaddr and the Vaddr for the first text section as determined at compile + // time. + + // The symbol for the first text section is named runtime.text as before. + // Additional text sections are named runtime.text.n where n is the + // order of creation starting with 1. These symbols provide the section's + // address after relocation by the linker. + + textbase := Segtext.Sections[0].Vaddr + for _, sect := range Segtext.Sections { + if sect.Name != ".text" { + break + } + // The fields written should match runtime/symtab.go:textsect. + // They are designed to minimize runtime calculations. + vaddr := sect.Vaddr - textbase + off = t.SetUint(ctxt.Arch, off, vaddr) // field vaddr + end := vaddr + sect.Length + off = t.SetUint(ctxt.Arch, off, end) // field end + name := "runtime.text" + if n != 0 { + name = fmt.Sprintf("runtime.text.%d", n) + } + s := ldr.Lookup(name, 0) + if s == 0 { + ctxt.Errorf(s, "Unable to find symbol %s\n", name) + } + off = t.SetAddr(ctxt.Arch, off, s) // field baseaddr + n++ + } + return t.Sym(), uint32(n) +} + +func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { + ldr := ctxt.loader + + if !ctxt.IsAIX() { + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared: + s := ldr.Lookup(*flagEntrySymbol, sym.SymVerABI0) + if s != 0 { + addinitarrdata(ctxt, ldr, s) + } + } + } + + // Define these so that they'll get put into the symbol table. + // data.c:/^address will provide the actual values. + ctxt.xdefine("runtime.rodata", sym.SRODATA, 0) + ctxt.xdefine("runtime.erodata", sym.SRODATA, 0) + ctxt.xdefine("runtime.types", sym.SRODATA, 0) + ctxt.xdefine("runtime.etypes", sym.SRODATA, 0) + ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, 0) + ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, 0) + ctxt.xdefine("runtime.data", sym.SDATA, 0) + ctxt.xdefine("runtime.edata", sym.SDATA, 0) + ctxt.xdefine("runtime.bss", sym.SBSS, 0) + ctxt.xdefine("runtime.ebss", sym.SBSS, 0) + ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, 0) + ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, 0) + ctxt.xdefine("runtime.covctrs", sym.SNOPTRBSS, 0) + ctxt.xdefine("runtime.ecovctrs", sym.SNOPTRBSS, 0) + ctxt.xdefine("runtime.end", sym.SBSS, 0) + ctxt.xdefine("runtime.epclntab", sym.SRODATA, 0) + ctxt.xdefine("runtime.esymtab", sym.SRODATA, 0) + + // garbage collection symbols + s := ldr.CreateSymForUpdate("runtime.gcdata", 0) + s.SetType(sym.SRODATA) + s.SetSize(0) + ctxt.xdefine("runtime.egcdata", sym.SRODATA, 0) + + s = ldr.CreateSymForUpdate("runtime.gcbss", 0) + s.SetType(sym.SRODATA) + s.SetSize(0) + ctxt.xdefine("runtime.egcbss", sym.SRODATA, 0) + + // pseudo-symbols to mark locations of type, string, and go string data. + var symtype, symtyperel loader.Sym + if !ctxt.DynlinkingGo() { + if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) { + s = ldr.CreateSymForUpdate("type:*", 0) + s.SetType(sym.STYPE) + s.SetSize(0) + s.SetAlign(int32(ctxt.Arch.PtrSize)) + symtype = s.Sym() + + s = ldr.CreateSymForUpdate("typerel.*", 0) + s.SetType(sym.STYPERELRO) + s.SetSize(0) + s.SetAlign(int32(ctxt.Arch.PtrSize)) + symtyperel = s.Sym() + } else { + s = ldr.CreateSymForUpdate("type:*", 0) + s.SetType(sym.STYPE) + s.SetSize(0) + s.SetAlign(int32(ctxt.Arch.PtrSize)) + symtype = s.Sym() + symtyperel = s.Sym() + } + setCarrierSym(sym.STYPE, symtype) + setCarrierSym(sym.STYPERELRO, symtyperel) + } + + groupSym := func(name string, t sym.SymKind) loader.Sym { + s := ldr.CreateSymForUpdate(name, 0) + s.SetType(t) + s.SetSize(0) + s.SetAlign(int32(ctxt.Arch.PtrSize)) + s.SetLocal(true) + setCarrierSym(t, s.Sym()) + return s.Sym() + } + var ( + symgostring = groupSym("go:string.*", sym.SGOSTRING) + symgofunc = groupSym("go:func.*", sym.SGOFUNC) + symgcbits = groupSym("runtime.gcbits.*", sym.SGCBITS) + ) + + symgofuncrel := symgofunc + if ctxt.UseRelro() { + symgofuncrel = groupSym("go:funcrel.*", sym.SGOFUNCRELRO) + } + + symt := ldr.CreateSymForUpdate("runtime.symtab", 0) + symt.SetType(sym.SSYMTAB) + symt.SetSize(0) + symt.SetLocal(true) + + // assign specific types so that they sort together. + // within a type they sort by size, so the .* symbols + // just defined above will be first. + // hide the specific symbols. + // Some of these symbol section conditions are duplicated + // in cmd/internal/obj.contentHashSection. + nsym := loader.Sym(ldr.NSym()) + symGroupType := make([]sym.SymKind, nsym) + for s := loader.Sym(1); s < nsym; s++ { + if (!ctxt.IsExternal() && ldr.IsFileLocal(s) && !ldr.IsFromAssembly(s) && ldr.SymPkg(s) != "") || (ctxt.LinkMode == LinkInternal && ldr.SymType(s) == sym.SCOVERAGE_COUNTER) { + ldr.SetAttrNotInSymbolTable(s, true) + } + if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || (ldr.SymType(s) != sym.SRODATA && ldr.SymType(s) != sym.SGOFUNC) { + continue + } + + name := ldr.SymName(s) + switch { + case strings.HasPrefix(name, "go:string."): + symGroupType[s] = sym.SGOSTRING + ldr.SetAttrNotInSymbolTable(s, true) + ldr.SetCarrierSym(s, symgostring) + + case strings.HasPrefix(name, "runtime.gcbits."), + strings.HasPrefix(name, "type:.gcprog."): + symGroupType[s] = sym.SGCBITS + ldr.SetAttrNotInSymbolTable(s, true) + ldr.SetCarrierSym(s, symgcbits) + + case strings.HasSuffix(name, "·f"): + if !ctxt.DynlinkingGo() { + ldr.SetAttrNotInSymbolTable(s, true) + } + if ctxt.UseRelro() { + symGroupType[s] = sym.SGOFUNCRELRO + if !ctxt.DynlinkingGo() { + ldr.SetCarrierSym(s, symgofuncrel) + } + } else { + symGroupType[s] = sym.SGOFUNC + ldr.SetCarrierSym(s, symgofunc) + } + + case strings.HasPrefix(name, "gcargs."), + strings.HasPrefix(name, "gclocals."), + strings.HasPrefix(name, "gclocals·"), + ldr.SymType(s) == sym.SGOFUNC && s != symgofunc, // inltree, see pcln.go + strings.HasSuffix(name, ".opendefer"), + strings.HasSuffix(name, ".arginfo0"), + strings.HasSuffix(name, ".arginfo1"), + strings.HasSuffix(name, ".argliveinfo"), + strings.HasSuffix(name, ".wrapinfo"), + strings.HasSuffix(name, ".args_stackmap"), + strings.HasSuffix(name, ".stkobj"): + ldr.SetAttrNotInSymbolTable(s, true) + symGroupType[s] = sym.SGOFUNC + ldr.SetCarrierSym(s, symgofunc) + if ctxt.Debugvlog != 0 { + align := ldr.SymAlign(s) + liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1) + } + + // Note: Check for "type:" prefix after checking for .arginfo1 suffix. + // That way symbols like "type:.eq.[2]interface {}.arginfo1" that belong + // in go:func.* end up there. + case strings.HasPrefix(name, "type:"): + if !ctxt.DynlinkingGo() { + ldr.SetAttrNotInSymbolTable(s, true) + } + if ctxt.UseRelro() { + symGroupType[s] = sym.STYPERELRO + if symtyperel != 0 { + ldr.SetCarrierSym(s, symtyperel) + } + } else { + symGroupType[s] = sym.STYPE + if symtyperel != 0 { + ldr.SetCarrierSym(s, symtype) + } + } + } + } + + if ctxt.BuildMode == BuildModeShared { + abihashgostr := ldr.CreateSymForUpdate("go:link.abihash."+filepath.Base(*flagOutfile), 0) + abihashgostr.SetType(sym.SRODATA) + hashsym := ldr.LookupOrCreateSym("go:link.abihashbytes", 0) + abihashgostr.AddAddr(ctxt.Arch, hashsym) + abihashgostr.AddUint(ctxt.Arch, uint64(ldr.SymSize(hashsym))) + } + if ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() { + for _, l := range ctxt.Library { + s := ldr.CreateSymForUpdate("go:link.pkghashbytes."+l.Pkg, 0) + s.SetType(sym.SRODATA) + s.SetSize(int64(len(l.Fingerprint))) + s.SetData(l.Fingerprint[:]) + str := ldr.CreateSymForUpdate("go:link.pkghash."+l.Pkg, 0) + str.SetType(sym.SRODATA) + str.AddAddr(ctxt.Arch, s.Sym()) + str.AddUint(ctxt.Arch, uint64(len(l.Fingerprint))) + } + } + + textsectionmapSym, nsections := textsectionmap(ctxt) + + // Information about the layout of the executable image for the + // runtime to use. Any changes here must be matched by changes to + // the definition of moduledata in runtime/symtab.go. + // This code uses several global variables that are set by pcln.go:pclntab. + moduledata := ldr.MakeSymbolUpdater(ctxt.Moduledata) + // The pcHeader + moduledata.AddAddr(ctxt.Arch, pcln.pcheader) + // The function name slice + moduledata.AddAddr(ctxt.Arch, pcln.funcnametab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab))) + // The cutab slice + moduledata.AddAddr(ctxt.Arch, pcln.cutab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab))) + // The filetab slice + moduledata.AddAddr(ctxt.Arch, pcln.filetab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab))) + // The pctab slice + moduledata.AddAddr(ctxt.Arch, pcln.pctab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab))) + // The pclntab slice + moduledata.AddAddr(ctxt.Arch, pcln.pclntab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab))) + // The ftab slice + moduledata.AddAddr(ctxt.Arch, pcln.pclntab) + moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) + moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) + // findfunctab + moduledata.AddAddr(ctxt.Arch, pcln.findfunctab) + // minpc, maxpc + moduledata.AddAddr(ctxt.Arch, pcln.firstFunc) + moduledata.AddAddrPlus(ctxt.Arch, pcln.lastFunc, ldr.SymSize(pcln.lastFunc)) + // pointers to specific parts of the module + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.text", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etext", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.noptrdata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.enoptrdata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.data", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.edata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.bss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.ebss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.noptrbss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.enoptrbss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.covctrs", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.ecovctrs", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.end", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcdata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcbss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.types", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etypes", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.rodata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("go:func.*", 0)) + + if ctxt.IsAIX() && ctxt.IsExternal() { + // Add R_XCOFFREF relocation to prevent ld's garbage collection of + // the following symbols. They might not be referenced in the program. + addRef := func(name string) { + s := ldr.Lookup(name, 0) + if s == 0 { + return + } + r, _ := moduledata.AddRel(objabi.R_XCOFFREF) + r.SetSym(s) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + } + addRef("runtime.rodata") + addRef("runtime.erodata") + addRef("runtime.epclntab") + // As we use relative addressing for text symbols in functab, it is + // important that the offsets we computed stay unchanged by the external + // linker, i.e. all symbols in Textp should not be removed. + // Most of them are actually referenced (our deadcode pass ensures that), + // except go:buildid which is generated late and not used by the program. + addRef("go:buildid") + } + + // text section information + moduledata.AddAddr(ctxt.Arch, textsectionmapSym) + moduledata.AddUint(ctxt.Arch, uint64(nsections)) + moduledata.AddUint(ctxt.Arch, uint64(nsections)) + + // The typelinks slice + typelinkSym := ldr.Lookup("runtime.typelink", 0) + ntypelinks := uint64(ldr.SymSize(typelinkSym)) / 4 + moduledata.AddAddr(ctxt.Arch, typelinkSym) + moduledata.AddUint(ctxt.Arch, ntypelinks) + moduledata.AddUint(ctxt.Arch, ntypelinks) + // The itablinks slice + itablinkSym := ldr.Lookup("runtime.itablink", 0) + nitablinks := uint64(ldr.SymSize(itablinkSym)) / uint64(ctxt.Arch.PtrSize) + moduledata.AddAddr(ctxt.Arch, itablinkSym) + moduledata.AddUint(ctxt.Arch, nitablinks) + moduledata.AddUint(ctxt.Arch, nitablinks) + // The ptab slice + if ptab := ldr.Lookup("go:plugin.tabs", 0); ptab != 0 && ldr.AttrReachable(ptab) { + ldr.SetAttrLocal(ptab, true) + if ldr.SymType(ptab) != sym.SRODATA { + panic(fmt.Sprintf("go:plugin.tabs is %v, not SRODATA", ldr.SymType(ptab))) + } + nentries := uint64(len(ldr.Data(ptab)) / 8) // sizeof(nameOff) + sizeof(typeOff) + moduledata.AddAddr(ctxt.Arch, ptab) + moduledata.AddUint(ctxt.Arch, nentries) + moduledata.AddUint(ctxt.Arch, nentries) + } else { + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + if ctxt.BuildMode == BuildModePlugin { + addgostring(ctxt, ldr, moduledata, "go:link.thispluginpath", objabi.PathToPrefix(*flagPluginPath)) + + pkghashes := ldr.CreateSymForUpdate("go:link.pkghashes", 0) + pkghashes.SetLocal(true) + pkghashes.SetType(sym.SRODATA) + + for i, l := range ctxt.Library { + // pkghashes[i].name + addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go:link.pkgname.%d", i), l.Pkg) + // pkghashes[i].linktimehash + addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go:link.pkglinkhash.%d", i), string(l.Fingerprint[:])) + // pkghashes[i].runtimehash + hash := ldr.Lookup("go:link.pkghash."+l.Pkg, 0) + pkghashes.AddAddr(ctxt.Arch, hash) + } + moduledata.AddAddr(ctxt.Arch, pkghashes.Sym()) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library))) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library))) + } else { + moduledata.AddUint(ctxt.Arch, 0) // pluginpath + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) // pkghashes slice + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + if len(ctxt.Shlibs) > 0 { + thismodulename := filepath.Base(*flagOutfile) + switch ctxt.BuildMode { + case BuildModeExe, BuildModePIE: + // When linking an executable, outfile is just "a.out". Make + // it something slightly more comprehensible. + thismodulename = "the executable" + } + addgostring(ctxt, ldr, moduledata, "go:link.thismodulename", thismodulename) + + modulehashes := ldr.CreateSymForUpdate("go:link.abihashes", 0) + modulehashes.SetLocal(true) + modulehashes.SetType(sym.SRODATA) + + for i, shlib := range ctxt.Shlibs { + // modulehashes[i].modulename + modulename := filepath.Base(shlib.Path) + addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go:link.libname.%d", i), modulename) + + // modulehashes[i].linktimehash + addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go:link.linkhash.%d", i), string(shlib.Hash)) + + // modulehashes[i].runtimehash + abihash := ldr.LookupOrCreateSym("go:link.abihash."+modulename, 0) + ldr.SetAttrReachable(abihash, true) + modulehashes.AddAddr(ctxt.Arch, abihash) + } + + moduledata.AddAddr(ctxt.Arch, modulehashes.Sym()) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs))) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs))) + } else { + moduledata.AddUint(ctxt.Arch, 0) // modulename + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) // moduleshashes slice + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + + hasmain := ctxt.BuildMode == BuildModeExe || ctxt.BuildMode == BuildModePIE + if hasmain { + moduledata.AddUint8(1) + } else { + moduledata.AddUint8(0) + } + + // The rest of moduledata is zero initialized. + // When linking an object that does not contain the runtime we are + // creating the moduledata from scratch and it does not have a + // compiler-provided size, so read it from the type data. + moduledatatype := ldr.Lookup("type:runtime.moduledata", 0) + moduledata.SetSize(decodetypeSize(ctxt.Arch, ldr.Data(moduledatatype))) + moduledata.Grow(moduledata.Size()) + + lastmoduledatap := ldr.CreateSymForUpdate("runtime.lastmoduledatap", 0) + if lastmoduledatap.Type() != sym.SDYNIMPORT { + lastmoduledatap.SetType(sym.SNOPTRDATA) + lastmoduledatap.SetSize(0) // overwrite existing value + lastmoduledatap.SetData(nil) + lastmoduledatap.AddAddr(ctxt.Arch, moduledata.Sym()) + } + return symGroupType +} + +// CarrierSymByType tracks carrier symbols and their sizes. +var CarrierSymByType [sym.SXREF]struct { + Sym loader.Sym + Size int64 +} + +func setCarrierSym(typ sym.SymKind, s loader.Sym) { + if CarrierSymByType[typ].Sym != 0 { + panic(fmt.Sprintf("carrier symbol for type %v already set", typ)) + } + CarrierSymByType[typ].Sym = s +} + +func setCarrierSize(typ sym.SymKind, sz int64) { + if CarrierSymByType[typ].Size != 0 { + panic(fmt.Sprintf("carrier symbol size for type %v already set", typ)) + } + CarrierSymByType[typ].Size = sz +} + +func isStaticTmp(name string) bool { + return strings.Contains(name, "."+obj.StaticNamePref) +} + +// Mangle function name with ABI information. +func mangleABIName(ctxt *Link, ldr *loader.Loader, x loader.Sym, name string) string { + // For functions with ABI wrappers, we have to make sure that we + // don't wind up with two symbol table entries with the same + // name (since this will generated an error from the external + // linker). If we have wrappers, keep the ABIInternal name + // unmangled since we want cross-load-module calls to target + // ABIInternal, and rename other symbols. + // + // TODO: avoid the ldr.Lookup calls below by instead using an aux + // sym or marker relocation to associate the wrapper with the + // wrapped function. + if !buildcfg.Experiment.RegabiWrappers { + return name + } + + if ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) != sym.SymVerABIInternal && ldr.SymVersion(x) < sym.SymVerStatic { + if s2 := ldr.Lookup(name, sym.SymVerABIInternal); s2 != 0 && ldr.SymType(s2) == sym.STEXT { + name = fmt.Sprintf("%s.abi%d", name, ldr.SymVersion(x)) + } + } + + // When loading a shared library, if a symbol has only one ABI, + // and the name is not mangled, we don't know what ABI it is. + // So we always mangle ABIInternal function name in shared linkage, + // except symbols that are exported to C. Type symbols are always + // ABIInternal so they are not mangled. + if ctxt.IsShared() { + if ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) == sym.SymVerABIInternal && !ldr.AttrCgoExport(x) && !strings.HasPrefix(name, "type:") { + name = fmt.Sprintf("%s.abiinternal", name) + } + } + + return name +} diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go new file mode 100644 index 0000000..d0ce99f --- /dev/null +++ b/src/cmd/link/internal/ld/target.go @@ -0,0 +1,206 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "encoding/binary" +) + +// Target holds the configuration we're building for. +type Target struct { + Arch *sys.Arch + + HeadType objabi.HeadType + + LinkMode LinkMode + BuildMode BuildMode + + linkShared bool + canUsePlugins bool + IsELF bool +} + +// +// Target type functions +// + +func (t *Target) IsExe() bool { + return t.BuildMode == BuildModeExe +} + +func (t *Target) IsShared() bool { + return t.BuildMode == BuildModeShared +} + +func (t *Target) IsPlugin() bool { + return t.BuildMode == BuildModePlugin +} + +func (t *Target) IsInternal() bool { + return t.LinkMode == LinkInternal +} + +func (t *Target) IsExternal() bool { + return t.LinkMode == LinkExternal +} + +func (t *Target) IsPIE() bool { + return t.BuildMode == BuildModePIE +} + +func (t *Target) IsSharedGoLink() bool { + return t.linkShared +} + +func (t *Target) CanUsePlugins() bool { + return t.canUsePlugins +} + +func (t *Target) IsElf() bool { + t.mustSetHeadType() + return t.IsELF +} + +func (t *Target) IsDynlinkingGo() bool { + return t.IsShared() || t.IsSharedGoLink() || t.IsPlugin() || t.CanUsePlugins() +} + +// UseRelro reports whether to make use of "read only relocations" aka +// relro. +func (t *Target) UseRelro() bool { + switch t.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin: + return t.IsELF || t.HeadType == objabi.Haix || t.HeadType == objabi.Hdarwin + default: + if t.HeadType == objabi.Hdarwin && t.IsARM64() { + // On darwin/ARM64, everything is PIE. + return true + } + return t.linkShared || (t.HeadType == objabi.Haix && t.LinkMode == LinkExternal) + } +} + +// +// Processor functions +// + +func (t *Target) Is386() bool { + return t.Arch.Family == sys.I386 +} + +func (t *Target) IsARM() bool { + return t.Arch.Family == sys.ARM +} + +func (t *Target) IsARM64() bool { + return t.Arch.Family == sys.ARM64 +} + +func (t *Target) IsAMD64() bool { + return t.Arch.Family == sys.AMD64 +} + +func (t *Target) IsMIPS() bool { + return t.Arch.Family == sys.MIPS +} + +func (t *Target) IsMIPS64() bool { + return t.Arch.Family == sys.MIPS64 +} + +func (t *Target) IsLOONG64() bool { + return t.Arch.Family == sys.Loong64 +} + +func (t *Target) IsPPC64() bool { + return t.Arch.Family == sys.PPC64 +} + +func (t *Target) IsRISCV64() bool { + return t.Arch.Family == sys.RISCV64 +} + +func (t *Target) IsS390X() bool { + return t.Arch.Family == sys.S390X +} + +func (t *Target) IsWasm() bool { + return t.Arch.Family == sys.Wasm +} + +// +// OS Functions +// + +func (t *Target) IsLinux() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hlinux +} + +func (t *Target) IsDarwin() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hdarwin +} + +func (t *Target) IsWindows() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hwindows +} + +func (t *Target) IsPlan9() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hplan9 +} + +func (t *Target) IsAIX() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Haix +} + +func (t *Target) IsSolaris() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hsolaris +} + +func (t *Target) IsNetbsd() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hnetbsd +} + +func (t *Target) IsOpenbsd() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hopenbsd +} + +func (t *Target) IsFreebsd() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hfreebsd +} + +func (t *Target) mustSetHeadType() { + if t.HeadType == objabi.Hunknown { + panic("HeadType is not set") + } +} + +// +// MISC +// + +func (t *Target) IsBigEndian() bool { + return t.Arch.ByteOrder == binary.BigEndian +} + +func (t *Target) UsesLibc() bool { + t.mustSetHeadType() + switch t.HeadType { + case objabi.Haix, objabi.Hdarwin, objabi.Hopenbsd, objabi.Hsolaris, objabi.Hwindows: + // platforms where we use libc for syscalls. + return true + } + return false +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go new file mode 100644 index 0000000..32a24cf --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a method of a reachable type is not necessarily +// live even if it matches an interface method, as long as +// the type is never converted to an interface. + +package main + +type I interface{ M() } + +type T int + +func (T) M() { println("XXX") } + +var p *T +var e interface{} + +func main() { + p = new(T) // used T, but never converted to interface in any reachable code + e.(I).M() // used I and I.M +} + +func Unused() { // convert T to interface, but this function is not reachable + var i I = T(0) + i.M() +} + +var Unused2 interface{} = T(1) // convert T to interface, in an unreachable global initializer diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go new file mode 100644 index 0000000..48ba55d --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a method *is* live if it matches an interface +// method and the type is "indirectly" converted to an +// interface through reflection. + +package main + +import "reflect" + +type I interface{ M() } + +type T int + +func (T) M() { println("XXX") } + +func main() { + e := reflect.ValueOf([]T{1}).Index(0).Interface() + e.(I).M() +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go new file mode 100644 index 0000000..37c8937 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go @@ -0,0 +1,29 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Like ifacemethod2.go, this tests that a method *is* live +// if the type is "indirectly" converted to an interface +// using reflection with a method descriptor as intermediate. + +package main + +import "reflect" + +type S int + +func (s S) M() { println("S.M") } + +type I interface{ M() } + +type T float64 + +func (t T) F(s S) {} + +func main() { + var t T + ft := reflect.TypeOf(t).Method(0).Type + at := ft.In(1) + v := reflect.New(at).Elem() + v.Interface().(I).M() +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go new file mode 100644 index 0000000..4af47ad --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go @@ -0,0 +1,25 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a live type's method is not live even if +// it matches an interface method, as long as the interface +// method is not used. + +package main + +type T int + +//go:noinline +func (T) M() {} + +type I interface{ M() } + +var p *T +var pp *I + +func main() { + p = new(T) // use type T + pp = new(I) // use type I + *pp = *p // convert T to I, build itab +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go b/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go new file mode 100644 index 0000000..af95e46 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go @@ -0,0 +1,24 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This example uses reflect.Value.Call, but not +// reflect.{Value,Type}.Method. This should not +// need to bring all methods live. + +package main + +import "reflect" + +func f() { println("call") } + +type T int + +func (T) M() {} + +func main() { + v := reflect.ValueOf(f) + v.Call(nil) + i := interface{}(T(1)) + println(i) +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go b/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go new file mode 100644 index 0000000..8246093 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a live variable doesn't bring its type +// descriptor live. + +package main + +type T [10]string + +var t T + +func main() { + println(t[8]) +} diff --git a/src/cmd/link/internal/ld/testdata/httptest/main/main.go b/src/cmd/link/internal/ld/testdata/httptest/main/main.go new file mode 100644 index 0000000..1bce301 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/httptest/main/main.go @@ -0,0 +1,22 @@ +// A small test program that uses the net/http package. There is +// nothing special about net/http here, this is just a convenient way +// to pull in a lot of code. + +package main + +import ( + "net/http" + "net/http/httptest" +) + +type statusHandler int + +func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(int(*h)) +} + +func main() { + status := statusHandler(http.StatusNotFound) + s := httptest.NewServer(&status) + defer s.Close() +} diff --git a/src/cmd/link/internal/ld/testdata/issue10978/main.go b/src/cmd/link/internal/ld/testdata/issue10978/main.go new file mode 100644 index 0000000..5e8c097 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue10978/main.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func undefined() + +func defined1() int { + // To check multiple errors for a single symbol, + // reference undefined more than once. + undefined() + undefined() + return 0 +} + +func defined2() { + undefined() + undefined() +} + +func init() { + _ = defined1() + defined2() +} + +// The "main" function remains undeclared. diff --git a/src/cmd/link/internal/ld/testdata/issue10978/main.s b/src/cmd/link/internal/ld/testdata/issue10978/main.s new file mode 100644 index 0000000..1d00e76 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue10978/main.s @@ -0,0 +1 @@ +// This file is needed to make "go build" work for package with external functions.
diff --git a/src/cmd/link/internal/ld/testdata/issue25459/a/a.go b/src/cmd/link/internal/ld/testdata/issue25459/a/a.go new file mode 100644 index 0000000..6032d76 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue25459/a/a.go @@ -0,0 +1,27 @@ +package a + +const Always = true + +var Count int + +type FuncReturningInt func() int + +var PointerToConstIf FuncReturningInt + +func ConstIf() int { + if Always { + return 1 + } + var imdead [4]int + imdead[Count] = 1 + return imdead[0] +} + +func CallConstIf() int { + Count += 3 + return ConstIf() +} + +func Another() { + defer func() { PointerToConstIf = ConstIf; Count += 1 }() +} diff --git a/src/cmd/link/internal/ld/testdata/issue25459/main/main.go b/src/cmd/link/internal/ld/testdata/issue25459/main/main.go new file mode 100644 index 0000000..7b5796d --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue25459/main/main.go @@ -0,0 +1,10 @@ +package main + +import "cmd/link/internal/ld/testdata/issue25459/a" + +var Glob int + +func main() { + a.Another() + Glob += a.ConstIf() + a.CallConstIf() +} diff --git a/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go b/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go new file mode 100644 index 0000000..ca57749 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go @@ -0,0 +1,16 @@ +package b + +var q int + +func Top(x int) int { + q += 1 + if q != x { + return 3 + } + return 4 +} + +func OOO(x int) int { + defer func() { q += x & 7 }() + return Top(x + 1) +} diff --git a/src/cmd/link/internal/ld/testdata/issue26237/main/main.go b/src/cmd/link/internal/ld/testdata/issue26237/main/main.go new file mode 100644 index 0000000..fdb1223 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue26237/main/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + + b "cmd/link/internal/ld/testdata/issue26237/b.dir" +) + +var skyx int + +func main() { + skyx += b.OOO(skyx) + if b.Top(1) == 99 { + fmt.Printf("Beware the Jabberwock, my son!\n") + } +} diff --git a/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m b/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m new file mode 100644 index 0000000..9462788 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m @@ -0,0 +1,16 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#import <Foundation/Foundation.h> +#import <AppKit/NSAppearance.h> + +BOOL function(void) { +#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED > 101300) + NSAppearance *darkAppearance; + if (@available(macOS 10.14, *)) { + darkAppearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; + } +#endif + return NO; +} diff --git a/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go b/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go new file mode 100644 index 0000000..514b9b9 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go @@ -0,0 +1,19 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lib + +/* +#cgo darwin CFLAGS: -D__MAC_OS_X_VERSION_MAX_ALLOWED=101450 +#cgo darwin LDFLAGS: -framework Foundation -framework AppKit +#include "stdlib.h" +int function(void); +*/ +import "C" +import "fmt" + +func DoC() { + C.function() + fmt.Println("called c function") +} diff --git a/src/cmd/link/internal/ld/testdata/issue32233/main/main.go b/src/cmd/link/internal/ld/testdata/issue32233/main/main.go new file mode 100644 index 0000000..ed0838a --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue32233/main/main.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "cmd/link/internal/ld/testdata/issue32233/lib" + +func main() { + lib.DoC() +} diff --git a/src/cmd/link/internal/ld/testdata/issue38192/main.go b/src/cmd/link/internal/ld/testdata/issue38192/main.go new file mode 100644 index 0000000..3b7df60 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue38192/main.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func singleInstruction() + +func main() { + singleInstruction() +} diff --git a/src/cmd/link/internal/ld/testdata/issue38192/oneline.s b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s new file mode 100644 index 0000000..f5290d7 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s @@ -0,0 +1,8 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·singleInstruction(SB),NOSPLIT,$0 + RET diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.go b/src/cmd/link/internal/ld/testdata/issue39256/x.go new file mode 100644 index 0000000..97bc1cc --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue39256/x.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + _ "unsafe" +) + +//go:cgo_import_dynamic libc_getpid getpid "libc.so" +//go:cgo_import_dynamic libc_kill kill "libc.so" +//go:cgo_import_dynamic libc_close close "libc.so" +//go:cgo_import_dynamic libc_open open "libc.so" + +//go:cgo_import_dynamic _ _ "libc.so" + +func trampoline() + +func main() { + trampoline() +} diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.s b/src/cmd/link/internal/ld/testdata/issue39256/x.s new file mode 100644 index 0000000..41a54b2 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue39256/x.s @@ -0,0 +1,10 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +TEXT ·trampoline(SB),0,$0 + CALL libc_getpid(SB) + CALL libc_kill(SB) + CALL libc_open(SB) + CALL libc_close(SB) + RET diff --git a/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go new file mode 100644 index 0000000..76e0ea1 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go @@ -0,0 +1,15 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +var G int + +func main() { + if G != 101 { + println("not 101") + } else { + println("well now that's interesting") + } +} diff --git a/src/cmd/link/internal/ld/testdata/issue42484/main.go b/src/cmd/link/internal/ld/testdata/issue42484/main.go new file mode 100644 index 0000000..60fc110 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue42484/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" +) + +func main() { + a := 0 + a++ + b := 0 + f1(a, b) +} + +func f1(a, b int) { + fmt.Printf("%d %d\n", a, b) +} diff --git a/src/cmd/link/internal/ld/testdata/stackcheck/main.go b/src/cmd/link/internal/ld/testdata/stackcheck/main.go new file mode 100644 index 0000000..b708cc5 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/stackcheck/main.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { asmMain() } + +func asmMain() + +func startSelf() + +func startChain() +func chain0() +func chain1() +func chain2() +func chainEnd() + +func startRec() +func startRec0() diff --git a/src/cmd/link/internal/ld/testdata/stackcheck/main.s b/src/cmd/link/internal/ld/testdata/stackcheck/main.s new file mode 100644 index 0000000..10f6a3f --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/stackcheck/main.s @@ -0,0 +1,40 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#define NOSPLIT 7 + +TEXT ·asmMain(SB),0,$0-0 + CALL ·startSelf(SB) + CALL ·startChain(SB) + CALL ·startRec(SB) + RET + +// Test reporting of basic over-the-limit +TEXT ·startSelf(SB),NOSPLIT,$1000-0 + RET + +// Test reporting of multiple over-the-limit chains +TEXT ·startChain(SB),NOSPLIT,$16-0 + CALL ·chain0(SB) + CALL ·chain1(SB) + CALL ·chain2(SB) + RET +TEXT ·chain0(SB),NOSPLIT,$32-0 + CALL ·chainEnd(SB) + RET +TEXT ·chain1(SB),NOSPLIT,$48-0 // Doesn't go over + RET +TEXT ·chain2(SB),NOSPLIT,$64-0 + CALL ·chainEnd(SB) + RET +TEXT ·chainEnd(SB),NOSPLIT,$1000-0 // Should be reported twice + RET + +// Test reporting of rootless recursion +TEXT ·startRec(SB),NOSPLIT,$0-0 + CALL ·startRec0(SB) + RET +TEXT ·startRec0(SB),NOSPLIT,$0-0 + CALL ·startRec(SB) + RET diff --git a/src/cmd/link/internal/ld/typelink.go b/src/cmd/link/internal/ld/typelink.go new file mode 100644 index 0000000..5eca6e0 --- /dev/null +++ b/src/cmd/link/internal/ld/typelink.go @@ -0,0 +1,72 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "sort" +) + +type byTypeStr []typelinkSortKey + +type typelinkSortKey struct { + TypeStr string + Type loader.Sym +} + +func (s byTypeStr) Less(i, j int) bool { return s[i].TypeStr < s[j].TypeStr } +func (s byTypeStr) Len() int { return len(s) } +func (s byTypeStr) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// typelink generates the typelink table which is used by reflect.typelinks(). +// Types that should be added to the typelinks table are marked with the +// MakeTypelink attribute by the compiler. +func (ctxt *Link) typelink() { + ldr := ctxt.loader + typelinks := byTypeStr{} + var itabs []loader.Sym + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + if ldr.IsTypelink(s) { + typelinks = append(typelinks, typelinkSortKey{decodetypeStr(ldr, ctxt.Arch, s), s}) + } else if ldr.IsItab(s) { + itabs = append(itabs, s) + } + } + sort.Sort(typelinks) + + tl := ldr.CreateSymForUpdate("runtime.typelink", 0) + tl.SetType(sym.STYPELINK) + ldr.SetAttrLocal(tl.Sym(), true) + tl.SetSize(int64(4 * len(typelinks))) + tl.Grow(tl.Size()) + relocs := tl.AddRelocs(len(typelinks)) + for i, s := range typelinks { + r := relocs.At(i) + r.SetSym(s.Type) + r.SetOff(int32(i * 4)) + r.SetSiz(4) + r.SetType(objabi.R_ADDROFF) + } + + ptrsize := ctxt.Arch.PtrSize + il := ldr.CreateSymForUpdate("runtime.itablink", 0) + il.SetType(sym.SITABLINK) + ldr.SetAttrLocal(il.Sym(), true) + il.SetSize(int64(ptrsize * len(itabs))) + il.Grow(il.Size()) + relocs = il.AddRelocs(len(itabs)) + for i, s := range itabs { + r := relocs.At(i) + r.SetSym(s) + r.SetOff(int32(i * ptrsize)) + r.SetSiz(uint8(ptrsize)) + r.SetType(objabi.R_ADDR) + } +} diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go new file mode 100644 index 0000000..779f498 --- /dev/null +++ b/src/cmd/link/internal/ld/util.go @@ -0,0 +1,114 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/link/internal/loader" + "encoding/binary" + "fmt" + "os" +) + +var atExitFuncs []func() + +func AtExit(f func()) { + atExitFuncs = append(atExitFuncs, f) +} + +// runAtExitFuncs runs the queued set of AtExit functions. +func runAtExitFuncs() { + for i := len(atExitFuncs) - 1; i >= 0; i-- { + atExitFuncs[i]() + } + atExitFuncs = nil +} + +// Exit exits with code after executing all atExitFuncs. +func Exit(code int) { + runAtExitFuncs() + os.Exit(code) +} + +// Exitf logs an error message then calls Exit(2). +func Exitf(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...) + nerrors++ + Exit(2) +} + +// afterErrorAction updates 'nerrors' on error and invokes exit or +// panics in the proper circumstances. +func afterErrorAction() { + nerrors++ + if *flagH { + panic("error") + } + if nerrors > 20 { + Exitf("too many errors") + } +} + +// Errorf logs an error message. +// +// If more than 20 errors have been printed, exit with an error. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +// +// TODO: remove. Use ctxt.Errorf instead. +// All remaining calls use nil as first arg. +func Errorf(dummy *int, format string, args ...interface{}) { + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + afterErrorAction() +} + +// Errorf method logs an error message. +// +// If more than 20 errors have been printed, exit with an error. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +func (ctxt *Link) Errorf(s loader.Sym, format string, args ...interface{}) { + if ctxt.loader != nil { + ctxt.loader.Errorf(s, format, args...) + return + } + // Note: this is not expected to happen very often. + format = fmt.Sprintf("sym %d: %s", s, format) + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + afterErrorAction() +} + +func artrim(x []byte) string { + i := 0 + j := len(x) + for i < len(x) && x[i] == ' ' { + i++ + } + for j > i && x[j-1] == ' ' { + j-- + } + return string(x[i:j]) +} + +func stringtouint32(x []uint32, s string) { + for i := 0; len(s) > 0; i++ { + var buf [4]byte + s = s[copy(buf[:], s):] + x[i] = binary.LittleEndian.Uint32(buf[:]) + } +} + +// contains reports whether v is in s. +func contains(s []string, v string) bool { + for _, x := range s { + if x == v { + return true + } + } + return false +} diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go new file mode 100644 index 0000000..1265c45 --- /dev/null +++ b/src/cmd/link/internal/ld/xcoff.go @@ -0,0 +1,1815 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/bits" + "os" + "path/filepath" + "sort" + "strings" + "sync" + + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" +) + +// This file handles all algorithms related to XCOFF files generation. +// Most of them are adaptations of the ones in cmd/link/internal/pe.go +// as PE and XCOFF are based on COFF files. +// XCOFF files generated are 64 bits. + +const ( + // Total amount of space to reserve at the start of the file + // for File Header, Auxiliary Header, and Section Headers. + // May waste some. + XCOFFHDRRESERVE = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23 + + // base on dump -o, then rounded from 32B to 64B to + // match worst case elf text section alignment on ppc64. + XCOFFSECTALIGN int64 = 64 + + // XCOFF binaries should normally have all its sections position-independent. + // However, this is not yet possible for .text because of some R_ADDR relocations + // inside RODATA symbols. + // .data and .bss are position-independent so their address start inside a unreachable + // segment during execution to force segfault if something is wrong. + XCOFFTEXTBASE = 0x100000000 // Start of text address + XCOFFDATABASE = 0x200000000 // Start of data address +) + +// File Header +type XcoffFileHdr64 struct { + Fmagic uint16 // Target machine + Fnscns uint16 // Number of sections + Ftimedat int32 // Time and date of file creation + Fsymptr uint64 // Byte offset to symbol table start + Fopthdr uint16 // Number of bytes in optional header + Fflags uint16 // Flags + Fnsyms int32 // Number of entries in symbol table +} + +const ( + U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF +) + +// Flags that describe the type of the object file. +const ( + F_RELFLG = 0x0001 + F_EXEC = 0x0002 + F_LNNO = 0x0004 + F_FDPR_PROF = 0x0010 + F_FDPR_OPTI = 0x0020 + F_DSA = 0x0040 + F_VARPG = 0x0100 + F_DYNLOAD = 0x1000 + F_SHROBJ = 0x2000 + F_LOADONLY = 0x4000 +) + +// Auxiliary Header +type XcoffAoutHdr64 struct { + Omagic int16 // Flags - Ignored If Vstamp Is 1 + Ovstamp int16 // Version + Odebugger uint32 // Reserved For Debugger + Otextstart uint64 // Virtual Address Of Text + Odatastart uint64 // Virtual Address Of Data + Otoc uint64 // Toc Address + Osnentry int16 // Section Number For Entry Point + Osntext int16 // Section Number For Text + Osndata int16 // Section Number For Data + Osntoc int16 // Section Number For Toc + Osnloader int16 // Section Number For Loader + Osnbss int16 // Section Number For Bss + Oalgntext int16 // Max Text Alignment + Oalgndata int16 // Max Data Alignment + Omodtype [2]byte // Module Type Field + Ocpuflag uint8 // Bit Flags - Cputypes Of Objects + Ocputype uint8 // Reserved for CPU type + Otextpsize uint8 // Requested text page size + Odatapsize uint8 // Requested data page size + Ostackpsize uint8 // Requested stack page size + Oflags uint8 // Flags And TLS Alignment + Otsize uint64 // Text Size In Bytes + Odsize uint64 // Data Size In Bytes + Obsize uint64 // Bss Size In Bytes + Oentry uint64 // Entry Point Address + Omaxstack uint64 // Max Stack Size Allowed + Omaxdata uint64 // Max Data Size Allowed + Osntdata int16 // Section Number For Tdata Section + Osntbss int16 // Section Number For Tbss Section + Ox64flags uint16 // Additional Flags For 64-Bit Objects + Oresv3a int16 // Reserved + Oresv3 [2]int32 // Reserved +} + +// Section Header +type XcoffScnHdr64 struct { + Sname [8]byte // Section Name + Spaddr uint64 // Physical Address + Svaddr uint64 // Virtual Address + Ssize uint64 // Section Size + Sscnptr uint64 // File Offset To Raw Data + Srelptr uint64 // File Offset To Relocation + Slnnoptr uint64 // File Offset To Line Numbers + Snreloc uint32 // Number Of Relocation Entries + Snlnno uint32 // Number Of Line Number Entries + Sflags uint32 // flags +} + +// Flags defining the section type. +const ( + STYP_DWARF = 0x0010 + STYP_TEXT = 0x0020 + STYP_DATA = 0x0040 + STYP_BSS = 0x0080 + STYP_EXCEPT = 0x0100 + STYP_INFO = 0x0200 + STYP_TDATA = 0x0400 + STYP_TBSS = 0x0800 + STYP_LOADER = 0x1000 + STYP_DEBUG = 0x2000 + STYP_TYPCHK = 0x4000 + STYP_OVRFLO = 0x8000 +) +const ( + SSUBTYP_DWINFO = 0x10000 // DWARF info section + SSUBTYP_DWLINE = 0x20000 // DWARF line-number section + SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section + SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section + SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section + SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section + SSUBTYP_DWSTR = 0x70000 // DWARF strings section + SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section + SSUBTYP_DWLOC = 0x90000 // DWARF location lists section + SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section + SSUBTYP_DWMAC = 0xB0000 // DWARF macros section +) + +// Headers size +const ( + FILHSZ_32 = 20 + FILHSZ_64 = 24 + AOUTHSZ_EXEC32 = 72 + AOUTHSZ_EXEC64 = 120 + SCNHSZ_32 = 40 + SCNHSZ_64 = 72 + LDHDRSZ_32 = 32 + LDHDRSZ_64 = 56 + LDSYMSZ_64 = 24 + RELSZ_64 = 14 +) + +// Type representing all XCOFF symbols. +type xcoffSym interface { +} + +// Symbol Table Entry +type XcoffSymEnt64 struct { + Nvalue uint64 // Symbol value + Noffset uint32 // Offset of the name in string table or .debug section + Nscnum int16 // Section number of symbol + Ntype uint16 // Basic and derived type specification + Nsclass uint8 // Storage class of symbol + Nnumaux int8 // Number of auxiliary entries +} + +const SYMESZ = 18 + +const ( + // Nscnum + N_DEBUG = -2 + N_ABS = -1 + N_UNDEF = 0 + + //Ntype + SYM_V_INTERNAL = 0x1000 + SYM_V_HIDDEN = 0x2000 + SYM_V_PROTECTED = 0x3000 + SYM_V_EXPORTED = 0x4000 + SYM_TYPE_FUNC = 0x0020 // is function +) + +// Storage Class. +const ( + C_NULL = 0 // Symbol table entry marked for deletion + C_EXT = 2 // External symbol + C_STAT = 3 // Static symbol + C_BLOCK = 100 // Beginning or end of inner block + C_FCN = 101 // Beginning or end of function + C_FILE = 103 // Source file name and compiler information + C_HIDEXT = 107 // Unnamed external symbol + C_BINCL = 108 // Beginning of include file + C_EINCL = 109 // End of include file + C_WEAKEXT = 111 // Weak external symbol + C_DWARF = 112 // DWARF symbol + C_GSYM = 128 // Global variable + C_LSYM = 129 // Automatic variable allocated on stack + C_PSYM = 130 // Argument to subroutine allocated on stack + C_RSYM = 131 // Register variable + C_RPSYM = 132 // Argument to function or procedure stored in register + C_STSYM = 133 // Statically allocated symbol + C_BCOMM = 135 // Beginning of common block + C_ECOML = 136 // Local member of common block + C_ECOMM = 137 // End of common block + C_DECL = 140 // Declaration of object + C_ENTRY = 141 // Alternate entry + C_FUN = 142 // Function or procedure + C_BSTAT = 143 // Beginning of static block + C_ESTAT = 144 // End of static block + C_GTLS = 145 // Global thread-local variable + C_STTLS = 146 // Static thread-local variable +) + +// File Auxiliary Entry +type XcoffAuxFile64 struct { + Xzeroes uint32 // The name is always in the string table + Xoffset uint32 // Offset in the string table + X_pad1 [6]byte + Xftype uint8 // Source file string type + X_pad2 [2]byte + Xauxtype uint8 // Type of auxiliary entry +} + +// Function Auxiliary Entry +type XcoffAuxFcn64 struct { + Xlnnoptr uint64 // File pointer to line number + Xfsize uint32 // Size of function in bytes + Xendndx uint32 // Symbol table index of next entry + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +// csect Auxiliary Entry. +type XcoffAuxCSect64 struct { + Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index + Xparmhash uint32 // Offset of parameter type-check string + Xsnhash uint16 // .typchk section number + Xsmtyp uint8 // Symbol alignment and type + Xsmclas uint8 // Storage-mapping class + Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +// DWARF Auxiliary Entry +type XcoffAuxDWARF64 struct { + Xscnlen uint64 // Length of this symbol section + X_pad [9]byte + Xauxtype uint8 // Type of auxiliary entry +} + +// Auxiliary type +const ( + _AUX_EXCEPT = 255 + _AUX_FCN = 254 + _AUX_SYM = 253 + _AUX_FILE = 252 + _AUX_CSECT = 251 + _AUX_SECT = 250 +) + +// Xftype field +const ( + XFT_FN = 0 // Source File Name + XFT_CT = 1 // Compile Time Stamp + XFT_CV = 2 // Compiler Version Number + XFT_CD = 128 // Compiler Defined Information/ + +) + +// Symbol type field. +const ( + XTY_ER = 0 // External reference + XTY_SD = 1 // Section definition + XTY_LD = 2 // Label definition + XTY_CM = 3 // Common csect definition + XTY_WK = 0x8 // Weak symbol + XTY_EXP = 0x10 // Exported symbol + XTY_ENT = 0x20 // Entry point symbol + XTY_IMP = 0x40 // Imported symbol +) + +// Storage-mapping class. +const ( + XMC_PR = 0 // Program code + XMC_RO = 1 // Read-only constant + XMC_DB = 2 // Debug dictionary table + XMC_TC = 3 // TOC entry + XMC_UA = 4 // Unclassified + XMC_RW = 5 // Read/Write data + XMC_GL = 6 // Global linkage + XMC_XO = 7 // Extended operation + XMC_SV = 8 // 32-bit supervisor call descriptor + XMC_BS = 9 // BSS class + XMC_DS = 10 // Function descriptor + XMC_UC = 11 // Unnamed FORTRAN common + XMC_TC0 = 15 // TOC anchor + XMC_TD = 16 // Scalar data entry in the TOC + XMC_SV64 = 17 // 64-bit supervisor call descriptor + XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit + XMC_TL = 20 // Read/Write thread-local data + XMC_UL = 21 // Read/Write thread-local data (.tbss) + XMC_TE = 22 // TOC entry +) + +// Loader Header +type XcoffLdHdr64 struct { + Lversion int32 // Loader section version number + Lnsyms int32 // Number of symbol table entries + Lnreloc int32 // Number of relocation table entries + Listlen uint32 // Length of import file ID string table + Lnimpid int32 // Number of import file IDs + Lstlen uint32 // Length of string table + Limpoff uint64 // Offset to start of import file IDs + Lstoff uint64 // Offset to start of string table + Lsymoff uint64 // Offset to start of symbol table + Lrldoff uint64 // Offset to start of relocation entries +} + +// Loader Symbol +type XcoffLdSym64 struct { + Lvalue uint64 // Address field + Loffset uint32 // Byte offset into string table of symbol name + Lscnum int16 // Section number containing symbol + Lsmtype int8 // Symbol type, export, import flags + Lsmclas int8 // Symbol storage class + Lifile int32 // Import file ID; ordinal of import file IDs + Lparm uint32 // Parameter type-check field +} + +type xcoffLoaderSymbol struct { + sym loader.Sym + smtype int8 + smclas int8 +} + +type XcoffLdImportFile64 struct { + Limpidpath string + Limpidbase string + Limpidmem string +} + +type XcoffLdRel64 struct { + Lvaddr uint64 // Address Field + Lrtype uint16 // Relocation Size and Type + Lrsecnm int16 // Section Number being relocated + Lsymndx int32 // Loader-Section symbol table index +} + +// xcoffLoaderReloc holds information about a relocation made by the loader. +type xcoffLoaderReloc struct { + sym loader.Sym + roff int32 + rtype uint16 + symndx int32 +} + +const ( + XCOFF_R_POS = 0x00 // A(sym) Positive Relocation + XCOFF_R_NEG = 0x01 // -A(sym) Negative Relocation + XCOFF_R_REL = 0x02 // A(sym-*) Relative to self + XCOFF_R_TOC = 0x03 // A(sym-TOC) Relative to TOC + XCOFF_R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load. + + XCOFF_R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst + XCOFF_R_GL = 0x05 // A(external TOC of sym) Global Linkage + XCOFF_R_TCL = 0x06 // A(local TOC of sym) Local object TOC address + XCOFF_R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction + XCOFF_R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction + XCOFF_R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect + XCOFF_R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction + XCOFF_R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction + XCOFF_R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable + XCOFF_R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr + + XCOFF_R_TLS = 0x20 // General-dynamic reference to TLS symbol + XCOFF_R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol + XCOFF_R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol + XCOFF_R_TLS_LE = 0x23 // Local-exec reference to TLS symbol + XCOFF_R_TLSM = 0x24 // Module reference to TLS symbol + XCOFF_R_TLSML = 0x25 // Module reference to local (own) module + + XCOFF_R_TOCU = 0x30 // Relative to TOC - high order bits + XCOFF_R_TOCL = 0x31 // Relative to TOC - low order bits +) + +type XcoffLdStr64 struct { + size uint16 + name string +} + +// xcoffFile is used to build XCOFF file. +type xcoffFile struct { + xfhdr XcoffFileHdr64 + xahdr XcoffAoutHdr64 + sections []*XcoffScnHdr64 + sectText *XcoffScnHdr64 + sectData *XcoffScnHdr64 + sectBss *XcoffScnHdr64 + stringTable xcoffStringTable + sectNameToScnum map[string]int16 + loaderSize uint64 + symtabOffset int64 // offset to the start of symbol table + symbolCount uint32 // number of symbol table records written + symtabSym []xcoffSym // XCOFF symbols for the symbol table + dynLibraries map[string]int // Dynamic libraries in .loader section. The integer represents its import file number (- 1) + loaderSymbols []*xcoffLoaderSymbol // symbols inside .loader symbol table + loaderReloc []*xcoffLoaderReloc // Reloc that must be made inside loader + sync.Mutex // currently protect loaderReloc +} + +// Var used by XCOFF Generation algorithms +var ( + xfile xcoffFile +) + +// xcoffStringTable is a XCOFF string table. +type xcoffStringTable struct { + strings []string + stringsLen int +} + +// size returns size of string table t. +func (t *xcoffStringTable) size() int { + // string table starts with 4-byte length at the beginning + return t.stringsLen + 4 +} + +// add adds string str to string table t. +func (t *xcoffStringTable) add(str string) int { + off := t.size() + t.strings = append(t.strings, str) + t.stringsLen += len(str) + 1 // each string will have 0 appended to it + return off +} + +// write writes string table t into the output file. +func (t *xcoffStringTable) write(out *OutBuf) { + out.Write32(uint32(t.size())) + for _, s := range t.strings { + out.WriteString(s) + out.Write8(0) + } +} + +// write writes XCOFF section sect into the output file. +func (sect *XcoffScnHdr64) write(ctxt *Link) { + binary.Write(ctxt.Out, binary.BigEndian, sect) + ctxt.Out.Write32(0) // Add 4 empty bytes at the end to match alignment +} + +// addSection adds section to the XCOFF file f. +func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 { + sect := &XcoffScnHdr64{ + Spaddr: addr, + Svaddr: addr, + Ssize: size, + Sscnptr: fileoff, + Sflags: flags, + } + copy(sect.Sname[:], name) // copy string to [8]byte + f.sections = append(f.sections, sect) + f.sectNameToScnum[name] = int16(len(f.sections)) + return sect +} + +// addDwarfSection adds a dwarf section to the XCOFF file f. +// This function is similar to addSection, but Dwarf section names +// must be modified to conventional names and they are various subtypes. +func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 { + newName, subtype := xcoffGetDwarfSubtype(s.Name) + return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype) +} + +// xcoffGetDwarfSubtype returns the XCOFF name of the DWARF section str +// and its subtype constant. +func xcoffGetDwarfSubtype(str string) (string, uint32) { + switch str { + default: + Exitf("unknown DWARF section name for XCOFF: %s", str) + case ".debug_abbrev": + return ".dwabrev", SSUBTYP_DWABREV + case ".debug_info": + return ".dwinfo", SSUBTYP_DWINFO + case ".debug_frame": + return ".dwframe", SSUBTYP_DWFRAME + case ".debug_line": + return ".dwline", SSUBTYP_DWLINE + case ".debug_loc": + return ".dwloc", SSUBTYP_DWLOC + case ".debug_pubnames": + return ".dwpbnms", SSUBTYP_DWPBNMS + case ".debug_pubtypes": + return ".dwpbtyp", SSUBTYP_DWPBTYP + case ".debug_ranges": + return ".dwrnges", SSUBTYP_DWRNGES + } + // never used + return "", 0 +} + +// getXCOFFscnum returns the XCOFF section number of a Go section. +func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 { + switch sect.Seg { + case &Segtext: + return f.sectNameToScnum[".text"] + case &Segdata: + if sect.Name == ".noptrbss" || sect.Name == ".bss" { + return f.sectNameToScnum[".bss"] + } + if sect.Name == ".tbss" { + return f.sectNameToScnum[".tbss"] + } + return f.sectNameToScnum[".data"] + case &Segdwarf: + name, _ := xcoffGetDwarfSubtype(sect.Name) + return f.sectNameToScnum[name] + case &Segrelrodata: + return f.sectNameToScnum[".data"] + } + Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name) + return -1 +} + +// Xcoffinit initialised some internal value and setups +// already known header information. +func Xcoffinit(ctxt *Link) { + xfile.dynLibraries = make(map[string]int) + + HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN)) + if *FlagTextAddr != -1 { + Errorf(nil, "-T not available on AIX") + } + *FlagTextAddr = XCOFFTEXTBASE + int64(HEADR) + if *FlagRound != -1 { + Errorf(nil, "-R not available on AIX") + } + *FlagRound = int(XCOFFSECTALIGN) + +} + +// SYMBOL TABLE + +// type records C_FILE information needed for genasmsym in XCOFF. +type xcoffSymSrcFile struct { + name string + file *XcoffSymEnt64 // Symbol of this C_FILE + csectAux *XcoffAuxCSect64 // Symbol for the current .csect + csectSymNb uint64 // Symbol number for the current .csect + csectVAStart int64 + csectVAEnd int64 +} + +var ( + currDwscnoff = make(map[string]uint64) // Needed to create C_DWARF symbols + currSymSrcFile xcoffSymSrcFile + outerSymSize = make(map[string]int64) +) + +// xcoffUpdateOuterSize stores the size of outer symbols in order to have it +// in the symbol table. +func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) { + if size == 0 { + return + } + // TODO: use CarrierSymByType + + ldr := ctxt.loader + switch stype { + default: + Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String()) + case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING: + // Nothing to do + case sym.STYPERELRO: + if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) { + // runtime.types size must be removed, as it's a real symbol. + tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0)) + outerSymSize["typerel.*"] = size - tsize + return + } + fallthrough + case sym.STYPE: + if !ctxt.DynlinkingGo() { + // runtime.types size must be removed, as it's a real symbol. + tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0)) + outerSymSize["type:*"] = size - tsize + } + case sym.SGOSTRING: + outerSymSize["go:string.*"] = size + case sym.SGOFUNC: + if !ctxt.DynlinkingGo() { + outerSymSize["go:func.*"] = size + } + case sym.SGOFUNCRELRO: + outerSymSize["go:funcrel.*"] = size + case sym.SGCBITS: + outerSymSize["runtime.gcbits.*"] = size + case sym.SPCLNTAB: + outerSymSize["runtime.pclntab"] = size + } +} + +// addSymbol writes a symbol or an auxiliary symbol entry on ctxt.out. +func (f *xcoffFile) addSymbol(sym xcoffSym) { + f.symtabSym = append(f.symtabSym, sym) + f.symbolCount++ +} + +// xcoffAlign returns the log base 2 of the symbol's alignment. +func xcoffAlign(ldr *loader.Loader, x loader.Sym, t SymbolType) uint8 { + align := ldr.SymAlign(x) + if align == 0 { + if t == TextSym { + align = int32(Funcalign) + } else { + align = symalign(ldr, x) + } + } + return logBase2(int(align)) +} + +// logBase2 returns the log in base 2 of a. +func logBase2(a int) uint8 { + return uint8(bits.Len(uint(a)) - 1) +} + +// Write symbols needed when a new file appeared: +// - a C_FILE with one auxiliary entry for its name +// - C_DWARF symbols to provide debug information +// - a C_HIDEXT which will be a csect containing all of its functions +// It needs several parameters to create .csect symbols such as its entry point and its section number. +// +// Currently, a new file is in fact a new package. It seems to be OK, but it might change +// in the future. +func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint64, extnum int16) { + ldr := ctxt.loader + /* C_FILE */ + s := &XcoffSymEnt64{ + Noffset: uint32(f.stringTable.add(".file")), + Nsclass: C_FILE, + Nscnum: N_DEBUG, + Ntype: 0, // Go isn't inside predefined language. + Nnumaux: 1, + } + f.addSymbol(s) + currSymSrcFile.file = s + + // Auxiliary entry for file name. + auxf := &XcoffAuxFile64{ + Xoffset: uint32(f.stringTable.add(name)), + Xftype: XFT_FN, + Xauxtype: _AUX_FILE, + } + f.addSymbol(auxf) + + /* Dwarf */ + for _, sect := range Segdwarf.Sections { + var dwsize uint64 + if ctxt.LinkMode == LinkInternal { + // Find the size of this corresponding package DWARF compilation unit. + // This size is set during DWARF generation (see dwarf.go). + dwsize = getDwsectCUSize(sect.Name, name) + // .debug_abbrev is common to all packages and not found with the previous function + if sect.Name == ".debug_abbrev" { + dwsize = uint64(ldr.SymSize(loader.Sym(sect.Sym))) + + } + } else { + // There is only one .FILE with external linking. + dwsize = sect.Length + } + + // get XCOFF name + name, _ := xcoffGetDwarfSubtype(sect.Name) + s := &XcoffSymEnt64{ + Nvalue: currDwscnoff[sect.Name], + Noffset: uint32(f.stringTable.add(name)), + Nsclass: C_DWARF, + Nscnum: f.getXCOFFscnum(sect), + Nnumaux: 1, + } + + if currSymSrcFile.csectAux == nil { + // Dwarf relocations need the symbol number of .dw* symbols. + // It doesn't need to know it for each package, one is enough. + // currSymSrcFile.csectAux == nil means first package. + ldr.SetSymDynid(loader.Sym(sect.Sym), int32(f.symbolCount)) + + if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal { + // CIE size must be added to the first package. + dwsize += 48 + } + } + + f.addSymbol(s) + + // update the DWARF section offset in this file + if sect.Name != ".debug_abbrev" { + currDwscnoff[sect.Name] += dwsize + } + + // Auxiliary dwarf section + auxd := &XcoffAuxDWARF64{ + Xscnlen: dwsize, + Xauxtype: _AUX_SECT, + } + + f.addSymbol(auxd) + } + + /* .csect */ + // Check if extnum is in text. + // This is temporary and only here to check if this algorithm is correct. + if extnum != 1 { + Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text") + } + + currSymSrcFile.csectSymNb = uint64(f.symbolCount) + + // No offset because no name + s = &XcoffSymEnt64{ + Nvalue: firstEntry, + Nscnum: extnum, + Nsclass: C_HIDEXT, + Ntype: 0, // check visibility ? + Nnumaux: 1, + } + f.addSymbol(s) + + aux := &XcoffAuxCSect64{ + Xsmclas: XMC_PR, + Xsmtyp: XTY_SD | logBase2(Funcalign)<<3, + Xauxtype: _AUX_CSECT, + } + f.addSymbol(aux) + + currSymSrcFile.csectAux = aux + currSymSrcFile.csectVAStart = int64(firstEntry) + currSymSrcFile.csectVAEnd = int64(firstEntry) +} + +// Update values for the previous package. +// - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1 +// - Xsclen of the csect symbol. +func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) { + // first file + if currSymSrcFile.file == nil { + return + } + + // Update C_FILE + cfile := currSymSrcFile.file + if last { + cfile.Nvalue = 0xFFFFFFFFFFFFFFFF + } else { + cfile.Nvalue = uint64(f.symbolCount) + } + + // update csect scnlen in this auxiliary entry + aux := currSymSrcFile.csectAux + csectSize := currSymSrcFile.csectVAEnd - currSymSrcFile.csectVAStart + aux.Xscnlenlo = uint32(csectSize & 0xFFFFFFFF) + aux.Xscnlenhi = uint32(csectSize >> 32) +} + +// Write symbol representing a .text function. +// The symbol table is split with C_FILE corresponding to each package +// and not to each source file as it should be. +func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x loader.Sym) []xcoffSym { + // New XCOFF symbols which will be written. + syms := []xcoffSym{} + + // Check if a new file is detected. + ldr := ctxt.loader + name := ldr.SymName(x) + if strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") { + // Trampoline don't have a FILE so there are considered + // in the current file. + // Same goes for runtime.text.X symbols. + } else if ldr.SymPkg(x) == "" { // Undefined global symbol + // If this happens, the algorithm must be redone. + if currSymSrcFile.name != "" { + Exitf("undefined global symbol found inside another file") + } + } else { + // Current file has changed. New C_FILE, C_DWARF, etc must be generated. + if currSymSrcFile.name != ldr.SymPkg(x) { + if ctxt.LinkMode == LinkInternal { + // update previous file values + xfile.updatePreviousFile(ctxt, false) + currSymSrcFile.name = ldr.SymPkg(x) + f.writeSymbolNewFile(ctxt, ldr.SymPkg(x), uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x))) + } else { + // With external linking, ld will crash if there is several + // .FILE and DWARF debugging enable, somewhere during + // the relocation phase. + // Therefore, all packages are merged under a fake .FILE + // "go_functions". + // TODO(aix); remove once ld has been fixed or the triggering + // relocation has been found and fixed. + if currSymSrcFile.name == "" { + currSymSrcFile.name = ldr.SymPkg(x) + f.writeSymbolNewFile(ctxt, "go_functions", uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x))) + } + } + + } + } + + name = ldr.SymExtname(x) + name = mangleABIName(ctxt, ldr, x, name) + + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: f.getXCOFFscnum(ldr.SymSect(x)), + Ntype: SYM_TYPE_FUNC, + Nnumaux: 2, + } + + if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) { + s.Nsclass = C_HIDEXT + } + + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + // Keep track of the section size by tracking the VA range. Individual + // alignment differences may introduce a few extra bytes of padding + // which are not fully accounted for by ldr.SymSize(x). + sv := ldr.SymValue(x) + ldr.SymSize(x) + if currSymSrcFile.csectVAEnd < sv { + currSymSrcFile.csectVAEnd = sv + } + + // create auxiliary entries + a2 := &XcoffAuxFcn64{ + Xfsize: uint32(ldr.SymSize(x)), + Xlnnoptr: 0, // TODO + Xendndx: xfile.symbolCount + 3, // this symbol + 2 aux entries + Xauxtype: _AUX_FCN, + } + syms = append(syms, a2) + + a4 := &XcoffAuxCSect64{ + Xscnlenlo: uint32(currSymSrcFile.csectSymNb & 0xFFFFFFFF), + Xscnlenhi: uint32(currSymSrcFile.csectSymNb >> 32), + Xsmclas: XMC_PR, // Program Code + Xsmtyp: XTY_LD, // label definition (based on C) + Xauxtype: _AUX_CSECT, + } + a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3) + + syms = append(syms, a4) + return syms +} + +// put function used by genasmsym to write symbol table. +func putaixsym(ctxt *Link, x loader.Sym, t SymbolType) { + // All XCOFF symbols generated by this GO symbols + // Can be a symbol entry or a auxiliary entry + syms := []xcoffSym{} + + ldr := ctxt.loader + name := ldr.SymName(x) + if t == UndefinedSym { + name = ldr.SymExtname(x) + } + + switch t { + default: + return + + case TextSym: + if ldr.SymPkg(x) != "" || strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") { + // Function within a file + syms = xfile.writeSymbolFunc(ctxt, x) + } else { + // Only runtime.text and runtime.etext come through this way + if name != "runtime.text" && name != "runtime.etext" && name != "go:buildid" { + Exitf("putaixsym: unknown text symbol %s", name) + } + s := &XcoffSymEnt64{ + Nsclass: C_HIDEXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Ntype: SYM_TYPE_FUNC, + Nnumaux: 1, + } + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + size := uint64(ldr.SymSize(x)) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + Xsmclas: XMC_PR, + Xsmtyp: XTY_SD, + } + a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3) + syms = append(syms, a4) + } + + case DataSym, BSSSym: + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Nnumaux: 1, + } + + if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) { + // There is more symbols in the case of a global data + // which are related to the assembly generated + // to access such symbols. + // But as Golang as its own way to check if a symbol is + // global or local (the capital letter), we don't need to + // implement them yet. + s.Nsclass = C_HIDEXT + } + + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + // Create auxiliary entry + + // Normally, size should be the size of csect containing all + // the data and bss symbols of one file/package. + // However, it's easier to just have a csect for each symbol. + // It might change + size := uint64(ldr.SymSize(x)) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + } + + if ty := ldr.SymType(x); ty >= sym.STYPE && ty <= sym.SPCLNTAB { + if ctxt.IsExternal() && strings.HasPrefix(ldr.SymSect(x).Name, ".data.rel.ro") { + // During external linking, read-only datas with relocation + // must be in .data. + a4.Xsmclas = XMC_RW + } else { + // Read only data + a4.Xsmclas = XMC_RO + } + } else if /*ty == sym.SDATA &&*/ strings.HasPrefix(ldr.SymName(x), "TOC.") && ctxt.IsExternal() { + a4.Xsmclas = XMC_TC + } else if ldr.SymName(x) == "TOC" { + a4.Xsmclas = XMC_TC0 + } else { + a4.Xsmclas = XMC_RW + } + if t == DataSym { + a4.Xsmtyp |= XTY_SD + } else { + a4.Xsmtyp |= XTY_CM + } + + a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, t) << 3) + + syms = append(syms, a4) + + case UndefinedSym: + if ty := ldr.SymType(x); ty != sym.SDYNIMPORT && ty != sym.SHOSTOBJ && ty != sym.SUNDEFEXT { + return + } + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nnumaux: 1, + } + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xsmclas: XMC_DS, + Xsmtyp: XTY_ER | XTY_IMP, + } + + if ldr.SymName(x) == "__n_pthreads" { + // Currently, all imported symbols made by cgo_import_dynamic are + // syscall functions, except __n_pthreads which is a variable. + // TODO(aix): Find a way to detect variables imported by cgo. + a4.Xsmclas = XMC_RW + } + + syms = append(syms, a4) + + case TLSSym: + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Nvalue: uint64(ldr.SymValue(x)), + Nnumaux: 1, + } + + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + size := uint64(ldr.SymSize(x)) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xsmclas: XMC_UL, + Xsmtyp: XTY_CM, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + } + + syms = append(syms, a4) + } + + for _, s := range syms { + xfile.addSymbol(s) + } +} + +// Generate XCOFF Symbol table. +// It will be written in out file in Asmbxcoff, because it must be +// at the very end, especially after relocation sections which needs symbols' index. +func (f *xcoffFile) asmaixsym(ctxt *Link) { + ldr := ctxt.loader + // Get correct size for symbols wrapping others symbols like go.string.* + // sym.Size can be used directly as the symbols have already been written. + for name, size := range outerSymSize { + sym := ldr.Lookup(name, 0) + if sym == 0 { + Errorf(nil, "unknown outer symbol with name %s", name) + } else { + s := ldr.MakeSymbolUpdater(sym) + s.SetSize(size) + } + } + + // These symbols won't show up in the first loop below because we + // skip sym.STEXT symbols. Normal sym.STEXT symbols are emitted by walking textp. + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + // We've already included this symbol in ctxt.Textp on AIX with external linker. + // See data.go:/textaddress + if !ctxt.IsExternal() { + putaixsym(ctxt, s, TextSym) + } + } + + n := 1 + // Generate base addresses for all text sections if there are multiple + for _, sect := range Segtext.Sections[1:] { + if sect.Name != ".text" || ctxt.IsExternal() { + // On AIX, runtime.text.X are symbols already in the symtab. + break + } + s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0) + if s == 0 { + break + } + if ldr.SymType(s) == sym.STEXT { + putaixsym(ctxt, s, TextSym) + } + n++ + } + + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + // We've already included this symbol in ctxt.Textp + // on AIX with external linker. + // See data.go:/textaddress + if !ctxt.IsExternal() { + putaixsym(ctxt, s, TextSym) + } + } + + shouldBeInSymbolTable := func(s loader.Sym, name string) bool { + if name == ".go.buildinfo" { + // On AIX, .go.buildinfo must be in the symbol table as + // it has relocations. + return true + } + if ldr.AttrNotInSymbolTable(s) { + return false + } + if (name == "" || name[0] == '.') && !ldr.IsFileLocal(s) && name != ".TOC." { + return false + } + return true + } + + for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ { + if !shouldBeInSymbolTable(s, ldr.SymName(s)) { + continue + } + st := ldr.SymType(s) + switch { + case st == sym.STLSBSS: + if ctxt.IsExternal() { + putaixsym(ctxt, s, TLSSym) + } + + case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_8BIT_COUNTER, st == sym.SCOVERAGE_COUNTER: + if ldr.AttrReachable(s) { + data := ldr.Data(s) + if len(data) > 0 { + ldr.Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(data), ldr.SymType(s), ldr.AttrSpecial(s)) + } + putaixsym(ctxt, s, BSSSym) + } + + case st >= sym.SELFRXSECT && st < sym.SXREF: // data sections handled in dodata + if ldr.AttrReachable(s) { + putaixsym(ctxt, s, DataSym) + } + + case st == sym.SUNDEFEXT: + putaixsym(ctxt, s, UndefinedSym) + + case st == sym.SDYNIMPORT: + if ldr.AttrReachable(s) { + putaixsym(ctxt, s, UndefinedSym) + } + } + } + + for _, s := range ctxt.Textp { + putaixsym(ctxt, s, TextSym) + } + + if ctxt.Debugvlog != 0 || *flagN { + ctxt.Logf("symsize = %d\n", uint32(symSize)) + } + xfile.updatePreviousFile(ctxt, true) +} + +func (f *xcoffFile) genDynSym(ctxt *Link) { + ldr := ctxt.loader + var dynsyms []loader.Sym + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT { + continue + } + dynsyms = append(dynsyms, s) + } + + for _, s := range dynsyms { + f.adddynimpsym(ctxt, s) + + if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok { + f.dynLibraries[ldr.SymDynimplib(s)] = len(f.dynLibraries) + } + } +} + +// (*xcoffFile)adddynimpsym adds the dynamic symbol "s" to a XCOFF file. +// A new symbol named s.Extname() is created to be the actual dynamic symbol +// in the .loader section and in the symbol table as an External Reference. +// The symbol "s" is transformed to SXCOFFTOC to end up in .data section. +// However, there is no writing protection on those symbols and +// it might need to be added. +// TODO(aix): Handles dynamic symbols without library. +func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) { + // Check that library name is given. + // Pattern is already checked when compiling. + ldr := ctxt.loader + if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" { + ctxt.Errorf(s, "imported symbol must have a given library") + } + + sb := ldr.MakeSymbolUpdater(s) + sb.SetReachable(true) + sb.SetType(sym.SXCOFFTOC) + + // Create new dynamic symbol + extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0) + extsym.SetType(sym.SDYNIMPORT) + extsym.SetDynimplib(ldr.SymDynimplib(s)) + extsym.SetExtname(ldr.SymExtname(s)) + extsym.SetDynimpvers(ldr.SymDynimpvers(s)) + + // Add loader symbol + lds := &xcoffLoaderSymbol{ + sym: extsym.Sym(), + smtype: XTY_IMP, + smclas: XMC_DS, + } + if ldr.SymName(s) == "__n_pthreads" { + // Currently, all imported symbols made by cgo_import_dynamic are + // syscall functions, except __n_pthreads which is a variable. + // TODO(aix): Find a way to detect variables imported by cgo. + lds.smclas = XMC_RW + } + f.loaderSymbols = append(f.loaderSymbols, lds) + + // Relocation to retrieve the external address + sb.AddBytes(make([]byte, 8)) + r, _ := sb.AddRel(objabi.R_ADDR) + r.SetSym(extsym.Sym()) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + // TODO: maybe this could be + // sb.SetSize(0) + // sb.SetData(nil) + // sb.AddAddr(ctxt.Arch, extsym.Sym()) + // If the size is not 0 to begin with, I don't think the added 8 bytes + // of zeros are necessary. +} + +// Xcoffadddynrel adds a dynamic relocation in a XCOFF file. +// This relocation will be made by the loader. +func Xcoffadddynrel(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + if target.IsExternal() { + return true + } + if ldr.SymType(s) <= sym.SPCLNTAB { + ldr.Errorf(s, "cannot have a relocation to %s in a text section symbol", ldr.SymName(r.Sym())) + return false + } + + xldr := &xcoffLoaderReloc{ + sym: s, + roff: r.Off(), + } + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { + default: + ldr.Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", ldr.SymName(targ), r.Type().String()) + return false + case objabi.R_ADDR: + if ldr.SymType(s) == sym.SXCOFFTOC && targType == sym.SDYNIMPORT { + // Imported symbol relocation + for i, dynsym := range xfile.loaderSymbols { + if ldr.SymName(dynsym.sym) == ldr.SymName(targ) { + xldr.symndx = int32(i + 3) // +3 because of 3 section symbols + break + } + } + } else if t := ldr.SymType(s); t == sym.SDATA || t == sym.SNOPTRDATA || t == sym.SBUILDINFO || t == sym.SXCOFFTOC { + switch ldr.SymSect(targ).Seg { + default: + ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ)) + case &Segtext: + case &Segrodata: + xldr.symndx = 0 // .text + case &Segdata: + if targType == sym.SBSS || targType == sym.SNOPTRBSS { + xldr.symndx = 2 // .bss + } else { + xldr.symndx = 1 // .data + } + } + + } else { + ldr.Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", ldr.SymName(targ), ldr.SymType(s), ldr.SymType(targ)) + return false + } + + xldr.rtype = 0x3F<<8 + XCOFF_R_POS + } + + xfile.Lock() + xfile.loaderReloc = append(xfile.loaderReloc, xldr) + xfile.Unlock() + return true +} + +func (ctxt *Link) doxcoff() { + ldr := ctxt.loader + + // TOC + toc := ldr.CreateSymForUpdate("TOC", 0) + toc.SetType(sym.SXCOFFTOC) + toc.SetVisibilityHidden(true) + + // Add entry point to .loader symbols. + ep := ldr.Lookup(*flagEntrySymbol, 0) + if ep == 0 || !ldr.AttrReachable(ep) { + Exitf("wrong entry point") + } + + xfile.loaderSymbols = append(xfile.loaderSymbols, &xcoffLoaderSymbol{ + sym: ep, + smtype: XTY_ENT | XTY_SD, + smclas: XMC_DS, + }) + + xfile.genDynSym(ctxt) + + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if strings.HasPrefix(ldr.SymName(s), "TOC.") { + sb := ldr.MakeSymbolUpdater(s) + sb.SetType(sym.SXCOFFTOC) + } + } + + if ctxt.IsExternal() { + // Change rt0_go name to match name in runtime/cgo:main(). + rt0 := ldr.Lookup("runtime.rt0_go", 0) + ldr.SetSymExtname(rt0, "runtime_rt0_go") + + nsym := loader.Sym(ldr.NSym()) + for s := loader.Sym(1); s < nsym; s++ { + if !ldr.AttrCgoExport(s) { + continue + } + if ldr.IsFileLocal(s) { + panic("cgo_export on static symbol") + } + + if ldr.SymType(s) == sym.STEXT { + // On AIX, a exported function must have two symbols: + // - a .text symbol which must start with a ".". + // - a .data symbol which is a function descriptor. + name := ldr.SymExtname(s) + ldr.SetSymExtname(s, "."+name) + + desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0)) + desc.SetReachable(true) + desc.SetType(sym.SNOPTRDATA) + desc.AddAddr(ctxt.Arch, s) + desc.AddAddr(ctxt.Arch, toc.Sym()) + desc.AddUint64(ctxt.Arch, 0) + } + } + } +} + +// Loader section +// Currently, this section is created from scratch when assembling the XCOFF file +// according to information retrieved in xfile object. + +// Create loader section and returns its size. +func Loaderblk(ctxt *Link, off uint64) { + xfile.writeLdrScn(ctxt, off) +} + +func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) { + var symtab []*XcoffLdSym64 + var strtab []*XcoffLdStr64 + var importtab []*XcoffLdImportFile64 + var reloctab []*XcoffLdRel64 + var dynimpreloc []*XcoffLdRel64 + + // As the string table is updated in any loader subsection, + // its length must be computed at the same time. + stlen := uint32(0) + + // Loader Header + hdr := &XcoffLdHdr64{ + Lversion: 2, + Lsymoff: LDHDRSZ_64, + } + + ldr := ctxt.loader + /* Symbol table */ + for _, s := range f.loaderSymbols { + lds := &XcoffLdSym64{ + Loffset: uint32(stlen + 2), + Lsmtype: s.smtype, + Lsmclas: s.smclas, + } + sym := s.sym + switch s.smtype { + default: + ldr.Errorf(sym, "unexpected loader symbol type: 0x%x", s.smtype) + case XTY_ENT | XTY_SD: + lds.Lvalue = uint64(ldr.SymValue(sym)) + lds.Lscnum = f.getXCOFFscnum(ldr.SymSect(sym)) + case XTY_IMP: + lds.Lifile = int32(f.dynLibraries[ldr.SymDynimplib(sym)] + 1) + } + ldstr := &XcoffLdStr64{ + size: uint16(len(ldr.SymName(sym)) + 1), // + null terminator + name: ldr.SymName(sym), + } + stlen += uint32(2 + ldstr.size) // 2 = sizeof ldstr.size + symtab = append(symtab, lds) + strtab = append(strtab, ldstr) + + } + + hdr.Lnsyms = int32(len(symtab)) + hdr.Lrldoff = hdr.Lsymoff + uint64(24*hdr.Lnsyms) // 24 = sizeof one symbol + off := hdr.Lrldoff // current offset is the same of reloc offset + + /* Reloc */ + // Ensure deterministic order + sort.Slice(f.loaderReloc, func(i, j int) bool { + r1, r2 := f.loaderReloc[i], f.loaderReloc[j] + if r1.sym != r2.sym { + return r1.sym < r2.sym + } + if r1.roff != r2.roff { + return r1.roff < r2.roff + } + if r1.rtype != r2.rtype { + return r1.rtype < r2.rtype + } + return r1.symndx < r2.symndx + }) + + ep := ldr.Lookup(*flagEntrySymbol, 0) + xldr := &XcoffLdRel64{ + Lvaddr: uint64(ldr.SymValue(ep)), + Lrtype: 0x3F00, + Lrsecnm: f.getXCOFFscnum(ldr.SymSect(ep)), + Lsymndx: 0, + } + off += 16 + reloctab = append(reloctab, xldr) + + off += uint64(16 * len(f.loaderReloc)) + for _, r := range f.loaderReloc { + symp := r.sym + if symp == 0 { + panic("unexpected 0 sym value") + } + xldr = &XcoffLdRel64{ + Lvaddr: uint64(ldr.SymValue(symp) + int64(r.roff)), + Lrtype: r.rtype, + Lsymndx: r.symndx, + } + + if ldr.SymSect(symp) != nil { + xldr.Lrsecnm = f.getXCOFFscnum(ldr.SymSect(symp)) + } + + reloctab = append(reloctab, xldr) + } + + off += uint64(16 * len(dynimpreloc)) + reloctab = append(reloctab, dynimpreloc...) + + hdr.Lnreloc = int32(len(reloctab)) + hdr.Limpoff = off + + /* Import */ + // Default import: /usr/lib:/lib + ldimpf := &XcoffLdImportFile64{ + Limpidpath: "/usr/lib:/lib", + } + off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter + importtab = append(importtab, ldimpf) + + // The map created by adddynimpsym associates the name to a number + // This number represents the librairie index (- 1) in this import files section + // Therefore, they must be sorted before being put inside the section + libsOrdered := make([]string, len(f.dynLibraries)) + for key, val := range f.dynLibraries { + if libsOrdered[val] != "" { + continue + } + libsOrdered[val] = key + } + + for _, lib := range libsOrdered { + // lib string is defined as base.a/mem.o or path/base.a/mem.o + n := strings.Split(lib, "/") + path := "" + base := n[len(n)-2] + mem := n[len(n)-1] + if len(n) > 2 { + path = lib[:len(lib)-len(base)-len(mem)-2] + + } + ldimpf = &XcoffLdImportFile64{ + Limpidpath: path, + Limpidbase: base, + Limpidmem: mem, + } + off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter + importtab = append(importtab, ldimpf) + } + + hdr.Lnimpid = int32(len(importtab)) + hdr.Listlen = uint32(off - hdr.Limpoff) + hdr.Lstoff = off + hdr.Lstlen = stlen + + /* Writing */ + ctxt.Out.SeekSet(int64(globalOff)) + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, hdr) + + for _, s := range symtab { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s) + + } + for _, r := range reloctab { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, r) + } + for _, f := range importtab { + ctxt.Out.WriteString(f.Limpidpath) + ctxt.Out.Write8(0) + ctxt.Out.WriteString(f.Limpidbase) + ctxt.Out.Write8(0) + ctxt.Out.WriteString(f.Limpidmem) + ctxt.Out.Write8(0) + } + for _, s := range strtab { + ctxt.Out.Write16(s.size) + ctxt.Out.WriteString(s.name) + ctxt.Out.Write8(0) // null terminator + } + + f.loaderSize = off + uint64(stlen) +} + +// XCOFF assembling and writing file + +func (f *xcoffFile) writeFileHeader(ctxt *Link) { + // File header + f.xfhdr.Fmagic = U64_TOCMAGIC + f.xfhdr.Fnscns = uint16(len(f.sections)) + f.xfhdr.Ftimedat = 0 + + if !*FlagS { + f.xfhdr.Fsymptr = uint64(f.symtabOffset) + f.xfhdr.Fnsyms = int32(f.symbolCount) + } + + if ctxt.BuildMode == BuildModeExe && ctxt.LinkMode == LinkInternal { + ldr := ctxt.loader + f.xfhdr.Fopthdr = AOUTHSZ_EXEC64 + f.xfhdr.Fflags = F_EXEC + + // auxiliary header + f.xahdr.Ovstamp = 1 // based on dump -o + f.xahdr.Omagic = 0x10b + copy(f.xahdr.Omodtype[:], "1L") + entry := ldr.Lookup(*flagEntrySymbol, 0) + f.xahdr.Oentry = uint64(ldr.SymValue(entry)) + f.xahdr.Osnentry = f.getXCOFFscnum(ldr.SymSect(entry)) + toc := ldr.Lookup("TOC", 0) + f.xahdr.Otoc = uint64(ldr.SymValue(toc)) + f.xahdr.Osntoc = f.getXCOFFscnum(ldr.SymSect(toc)) + + f.xahdr.Oalgntext = int16(logBase2(int(XCOFFSECTALIGN))) + f.xahdr.Oalgndata = 0x5 + + binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr) + binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr) + } else { + f.xfhdr.Fopthdr = 0 + binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr) + } + +} + +func xcoffwrite(ctxt *Link) { + ctxt.Out.SeekSet(0) + + xfile.writeFileHeader(ctxt) + + for _, sect := range xfile.sections { + sect.write(ctxt) + } +} + +// Generate XCOFF assembly file. +func asmbXcoff(ctxt *Link) { + ctxt.Out.SeekSet(0) + fileoff := int64(Segdwarf.Fileoff + Segdwarf.Filelen) + fileoff = int64(Rnd(int64(fileoff), int64(*FlagRound))) + + xfile.sectNameToScnum = make(map[string]int16) + + // Add sections + s := xfile.addSection(".text", Segtext.Vaddr, Segtext.Length, Segtext.Fileoff, STYP_TEXT) + xfile.xahdr.Otextstart = s.Svaddr + xfile.xahdr.Osntext = xfile.sectNameToScnum[".text"] + xfile.xahdr.Otsize = s.Ssize + xfile.sectText = s + + segdataVaddr := Segdata.Vaddr + segdataFilelen := Segdata.Filelen + segdataFileoff := Segdata.Fileoff + segbssFilelen := Segdata.Length - Segdata.Filelen + if len(Segrelrodata.Sections) > 0 { + // Merge relro segment to data segment as + // relro data are inside data segment on AIX. + segdataVaddr = Segrelrodata.Vaddr + segdataFileoff = Segrelrodata.Fileoff + segdataFilelen = Segdata.Vaddr + Segdata.Filelen - Segrelrodata.Vaddr + } + + s = xfile.addSection(".data", segdataVaddr, segdataFilelen, segdataFileoff, STYP_DATA) + xfile.xahdr.Odatastart = s.Svaddr + xfile.xahdr.Osndata = xfile.sectNameToScnum[".data"] + xfile.xahdr.Odsize = s.Ssize + xfile.sectData = s + + s = xfile.addSection(".bss", segdataVaddr+segdataFilelen, segbssFilelen, 0, STYP_BSS) + xfile.xahdr.Osnbss = xfile.sectNameToScnum[".bss"] + xfile.xahdr.Obsize = s.Ssize + xfile.sectBss = s + + if ctxt.LinkMode == LinkExternal { + var tbss *sym.Section + for _, s := range Segdata.Sections { + if s.Name == ".tbss" { + tbss = s + break + } + } + s = xfile.addSection(".tbss", tbss.Vaddr, tbss.Length, 0, STYP_TBSS) + } + + // add dwarf sections + for _, sect := range Segdwarf.Sections { + xfile.addDwarfSection(sect) + } + + // add and write remaining sections + if ctxt.LinkMode == LinkInternal { + // Loader section + if ctxt.BuildMode == BuildModeExe { + Loaderblk(ctxt, uint64(fileoff)) + s = xfile.addSection(".loader", 0, xfile.loaderSize, uint64(fileoff), STYP_LOADER) + xfile.xahdr.Osnloader = xfile.sectNameToScnum[".loader"] + + // Update fileoff for symbol table + fileoff += int64(xfile.loaderSize) + } + } + + // Create Symbol table + xfile.asmaixsym(ctxt) + + if ctxt.LinkMode == LinkExternal { + xfile.emitRelocations(ctxt, fileoff) + } + + // Write Symbol table + xfile.symtabOffset = ctxt.Out.Offset() + for _, s := range xfile.symtabSym { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s) + } + // write string table + xfile.stringTable.write(ctxt.Out) + + // write headers + xcoffwrite(ctxt) +} + +// emitRelocations emits relocation entries for go.o in external linking. +func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) { + ctxt.Out.SeekSet(fileoff) + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + ldr := ctxt.loader + // relocsect relocates symbols from first in section sect, and returns + // the total number of relocations emitted. + relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) uint32 { + // ctxt.Logf("%s 0x%x\n", sect.Name, sect.Vaddr) + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return 0 + } + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + eaddr := int64(sect.Vaddr + sect.Length) + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= int64(eaddr) { + break + } + + // Compute external relocations on the go, and pass to Xcoffreloc1 to stream out. + // Relocation must be ordered by address, so create a list of sorted indices. + relocs := ldr.Relocs(s) + sorted := make([]int, relocs.Count()) + for i := 0; i < relocs.Count(); i++ { + sorted[i] = i + } + sort.Slice(sorted, func(i, j int) bool { + return relocs.At(sorted[i]).Off() < relocs.At(sorted[j]).Off() + }) + + for _, ri := range sorted { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ldr.Errorf(s, "missing xsym in relocation") + continue + } + if ldr.SymDynid(rr.Xsym) < 0 { + ldr.Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymDynid(rr.Xsym)) + } + if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) { + ldr.Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type(), r.Type(), r.Siz(), ldr.SymName(r.Sym())) + } + } + } + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff + return uint32(sect.Rellen) / RELSZ_64 + } + sects := []struct { + xcoffSect *XcoffScnHdr64 + segs []*sym.Segment + }{ + {f.sectText, []*sym.Segment{&Segtext}}, + {f.sectData, []*sym.Segment{&Segrelrodata, &Segdata}}, + } + for _, s := range sects { + s.xcoffSect.Srelptr = uint64(ctxt.Out.Offset()) + n := uint32(0) + for _, seg := range s.segs { + for _, sect := range seg.Sections { + if sect.Name == ".text" { + n += relocsect(sect, ctxt.Textp, 0) + } else { + n += relocsect(sect, ctxt.datap, 0) + } + } + } + s.xcoffSect.Snreloc += n + } + +dwarfLoop: + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ldr.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + for _, xcoffSect := range f.sections { + _, subtyp := xcoffGetDwarfSubtype(sect.Name) + if xcoffSect.Sflags&0xF0000 == subtyp { + xcoffSect.Srelptr = uint64(ctxt.Out.Offset()) + xcoffSect.Snreloc = relocsect(sect, si.syms, sect.Vaddr) + continue dwarfLoop + } + } + Errorf(nil, "emitRelocations: could not find %q section", sect.Name) + } +} + +// xcoffCreateExportFile creates a file with exported symbols for +// -Wl,-bE option. +// ld won't export symbols unless they are listed in an export file. +func xcoffCreateExportFile(ctxt *Link) (fname string) { + fname = filepath.Join(*flagTmpdir, "export_file.exp") + var buf bytes.Buffer + + ldr := ctxt.loader + for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ { + if !ldr.AttrCgoExport(s) { + continue + } + extname := ldr.SymExtname(s) + if !strings.HasPrefix(extname, "._cgoexp_") { + continue + } + if ldr.IsFileLocal(s) { + continue // Only export non-static symbols + } + + // Retrieve the name of the initial symbol + // exported by cgo. + // The corresponding Go symbol is: + // _cgoexp_hashcode_symname. + name := strings.SplitN(extname, "_", 4)[3] + + buf.Write([]byte(name + "\n")) + } + + err := os.WriteFile(fname, buf.Bytes(), 0666) + if err != nil { + Errorf(nil, "WriteFile %s failed: %v", fname, err) + } + + return fname +} diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go new file mode 100644 index 0000000..7ac7699 --- /dev/null +++ b/src/cmd/link/internal/loadelf/ldelf.go @@ -0,0 +1,1151 @@ +// 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 +https://github.com/9fans/plan9port/tree/master/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 := l.LookupOrCreateCgoExport + errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) { + return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...)) + } + + ehdrFlags = initEhdrFlags + + 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.Loong64: + if mach != elf.EM_LOONGARCH || class != elf.ELFCLASS64 { + return errorf("elf object but not loong64") + } + + 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 (%s): ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.name, 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 + switch flag { + case 0: + // No local entry. R2 is preserved. + case 1: + // These require R2 be saved and restored by the caller. This isn't supported today. + return errorf("%s: unable to handle local entry type 1", sb.Name()) + case 7: + return errorf("%s: invalid sym.other 0x%x", sb.Name(), elfsym.other) + default: + l.SetSymLocalentry(s, 4<<uint(flag-2)) + } + } + } + + // 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, addendSize, 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 addendSize == 2 { + rAdd = int64(int16(rAdd)) + } + if addendSize == 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 + } + } + + if s != 0 && l.SymType(s) == 0 && elfsym.type_ != elf.STT_SECTION { + sb := l.MakeSymbolUpdater(s) + sb.SetType(sym.SXREF) + } + elfsym.sym = s + + return nil +} + +// Return the size of the relocated field, and the size of the addend as the first +// and second values. Note, the addend may be larger than the relocation field in +// some cases when a relocated value is split across multiple relocations. +func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, 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) + LOONG64 = uint32(sys.Loong64) + 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, 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, + MIPS64 | uint32(elf.R_MIPS_CALL16)<<16, + MIPS64 | uint32(elf.R_MIPS_GPREL32)<<16, + MIPS64 | uint32(elf.R_MIPS_64)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT_DISP)<<16: + return 4, 4, nil + + case LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_PCREL)<<16, + LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_GPREL)<<16, + LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_ABSOLUTE)<<16, + LOONG64 | uint32(elf.R_LARCH_MARK_LA)<<16, + LOONG64 | uint32(elf.R_LARCH_SOP_POP_32_S_0_10_10_16_S2)<<16, + LOONG64 | uint32(elf.R_LARCH_64)<<16, + LOONG64 | uint32(elf.R_LARCH_MARK_PCREL)<<16, + LOONG64 | uint32(elf.R_LARCH_32_PCREL)<<16: + return 4, 4, nil + + case S390X | uint32(elf.R_390_8)<<16: + return 1, 1, nil + + case PPC64 | uint32(elf.R_PPC64_TOC16)<<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, 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, 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, 8, nil + + case RISCV64 | uint32(elf.R_RISCV_SET6)<<16, + RISCV64 | uint32(elf.R_RISCV_SUB6)<<16, + RISCV64 | uint32(elf.R_RISCV_SET8)<<16, + RISCV64 | uint32(elf.R_RISCV_SUB8)<<16: + return 1, 1, nil + + case RISCV64 | uint32(elf.R_RISCV_RVC_BRANCH)<<16, + RISCV64 | uint32(elf.R_RISCV_RVC_JUMP)<<16, + RISCV64 | uint32(elf.R_RISCV_SET16)<<16, + RISCV64 | uint32(elf.R_RISCV_SUB16)<<16: + return 2, 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_ADD32)<<16, + RISCV64 | uint32(elf.R_RISCV_SET32)<<16, + RISCV64 | uint32(elf.R_RISCV_SUB32)<<16, + RISCV64 | uint32(elf.R_RISCV_32_PCREL)<<16, + RISCV64 | uint32(elf.R_RISCV_RELAX)<<16: + return 4, 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, 8, nil + + case 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, + PPC64 | uint32(elf.R_PPC64_PLT16_HA)<<16, + PPC64 | uint32(elf.R_PPC64_PLT16_LO_DS)<<16: + return 2, 4, nil + + // PPC64 inline PLT sequence hint relocations (-fno-plt) + // These are informational annotations to assist linker optimizations. + case PPC64 | uint32(elf.R_PPC64_PLTSEQ)<<16, + PPC64 | uint32(elf.R_PPC64_PLTCALL)<<16, + PPC64 | uint32(elf.R_PPC64_PLTCALL_NOTOC)<<16, + PPC64 | uint32(elf.R_PPC64_PLTSEQ_NOTOC)<<16: + return 0, 0, 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..8e1575a --- /dev/null +++ b/src/cmd/link/internal/loader/loader.go @@ -0,0 +1,2601 @@ +// 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" + "io" + "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 +} + +func (rel Reloc) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc.Type()) &^ objabi.R_WEAK } +func (rel Reloc) Weak() bool { return objabi.RelocType(rel.Reloc.Type())&objabi.R_WEAK != 0 } +func (rel Reloc) SetType(t objabi.RelocType) { rel.Reloc.SetType(uint16(t)) } +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 } + +// 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 + 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 + 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 + + // CgoExports records cgo-exported symbols by SymName. + CgoExports map[string]Sym + + flags uint32 + + 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 + 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), + 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}) + 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 %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg) + } + 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 %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg) + } + } + 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 +} + +// AddCgoExport records a cgo-exported symbol in l.CgoExports. +// This table is used to identify the correct Go symbol ABI to use +// to resolve references from host objects (which don't have ABIs). +func (l *Loader) AddCgoExport(s Sym) { + if l.CgoExports == nil { + l.CgoExports = make(map[string]Sym) + } + l.CgoExports[l.SymName(s)] = s +} + +// LookupOrCreateCgoExport is like LookupOrCreateSym, but if ver +// indicates a global symbol, it uses the CgoExport table to determine +// the appropriate symbol version (ABI) to use. ver must be either 0 +// or a static symbol version. +func (l *Loader) LookupOrCreateCgoExport(name string, ver int) Sym { + if ver >= sym.SymVerStatic { + return l.LookupOrCreateSym(name, ver) + } + if ver != 0 { + panic("ver must be 0 or a static version") + } + // Look for a cgo-exported symbol from Go. + if s, ok := l.CgoExports[name]; ok { + return s + } + // Otherwise, this must just be a symbol in the host object. + // Create a version 0 symbol for it. + return l.LookupOrCreateSym(name, 0) +} + +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) + reason := "same length but different contents" + if len(p) != len(pdup) { + reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup)) + } else if bytes.Equal(p, pdup) { + // For BSS symbols, we need to check size as well, see issue 46653. + szdup := l.SymSize(dup) + sz := int64(r.Sym(li).Siz()) + if szdup == sz { + return + } + reason = fmt.Sprintf("different sizes: new size %d != old size %d", + sz, szdup) + } + 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() +} + +// Returns the 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) + if r == nil { + return "?" + } + return r.Sym(li).Name(r.Reader) +} + +// 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.SymName(i))) + } + if v { + l.attrExternal.Set(l.extIndex(i)) + } else { + l.attrExternal.Unset(l.extIndex(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() +} + +// Returns whether this symbol is a dictionary symbol. +func (l *Loader) IsDict(i Sym) bool { + if l.IsExternal(i) { + return false + } + r, li := l.toLocal(i) + return r.Sym(li).IsDict() +} + +// 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 offset of symbol s. +func (l *Loader) SymPlt(s Sym) int32 { + if v, ok := l.plt[s]; ok { + return v + } + return -1 +} + +// SetPlt sets the PLT offset of symbol i. +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 offset of symbol s. +func (l *Loader) SymGot(s Sym) int32 { + if v, ok := l.got[s]; ok { + return v + } + return -1 +} + +// SetGot sets the GOT offset of symbol i. +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 { return l.aux1(i, goobj.AuxGotype) } + +// 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 corresponds 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 an offset in bytes of the "local entry" of a symbol. +func (l *Loader) SymLocalentry(i Sym) uint8 { + return l.localentry[i] +} + +// SetSymLocalentry sets the "local entry" offset 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()) + } + r, auxs := l.auxs(fnSymIdx) + + 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) { + return Reloc{&relocs.rs[j], relocs.r, relocs.l} + } + return Reloc{&relocs.rs[j], relocs.r, relocs.l} +} + +// 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, + } +} + +func (l *Loader) auxs(i Sym) (*oReader, []goobj.Aux) { + if l.IsExternal(i) { + pp := l.getPayload(i) + return l.objs[pp.objidx].r, pp.auxs + } else { + r, li := l.toLocal(i) + return r, r.Auxs(li) + } +} + +// Returns a specific aux symbol of type t for symbol i. +func (l *Loader) aux1(i Sym, t uint8) Sym { + r, auxs := l.auxs(i) + for j := range auxs { + a := &auxs[j] + if a.Type() == t { + return l.resolve(r, a.Sym()) + } + } + return 0 +} + +func (l *Loader) Pcsp(i Sym) Sym { return l.aux1(i, goobj.AuxPcsp) } + +// Returns all aux symbols of per-PC data for symbol i. +// tmp is a scratch space for the pcdata slice. +func (l *Loader) PcdataAuxs(i Sym, tmp []Sym) (pcsp, pcfile, pcline, pcinline Sym, pcdata []Sym) { + pcdata = tmp[:0] + r, auxs := l.auxs(i) + for j := range auxs { + a := &auxs[j] + switch a.Type() { + case goobj.AuxPcsp: + pcsp = l.resolve(r, a.Sym()) + case goobj.AuxPcline: + pcline = l.resolve(r, a.Sym()) + case goobj.AuxPcfile: + pcfile = l.resolve(r, a.Sym()) + case goobj.AuxPcinline: + pcinline = l.resolve(r, a.Sym()) + case goobj.AuxPcdata: + pcdata = append(pcdata, l.resolve(r, a.Sym())) + } + } + return +} + +// Returns the number of pcdata for symbol i. +func (l *Loader) NumPcdata(i Sym) int { + n := 0 + _, auxs := l.auxs(i) + for j := range auxs { + a := &auxs[j] + if a.Type() == goobj.AuxPcdata { + n++ + } + } + return n +} + +// Returns all funcdata symbols of symbol i. +// tmp is a scratch space. +func (l *Loader) Funcdata(i Sym, tmp []Sym) []Sym { + fd := tmp[:0] + r, auxs := l.auxs(i) + for j := range auxs { + a := &auxs[j] + if a.Type() == goobj.AuxFuncdata { + fd = append(fd, l.resolve(r, a.Sym())) + } + } + return fd +} + +// Returns the number of funcdata for symbol i. +func (l *Loader) NumFuncdata(i Sym) int { + n := 0 + _, auxs := l.auxs(i) + for j := range auxs { + a := &auxs[j] + if a.Type() == goobj.AuxFuncdata { + n++ + } + } + return n +} + +// FuncInfo provides hooks to access goobj.FuncInfo in the objects. +type FuncInfo struct { + l *Loader + r *oReader + data []byte + 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 (*goobj.FuncInfo)(nil).ReadFuncID(fi.data) +} + +func (fi *FuncInfo) FuncFlag() objabi.FuncFlag { + return (*goobj.FuncInfo)(nil).ReadFuncFlag(fi.data) +} + +func (fi *FuncInfo) StartLine() int32 { + return (*goobj.FuncInfo)(nil).ReadStartLine(fi.data) +} + +// 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) 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)) +} + +// TopFrame returns true if the function associated with this FuncInfo +// is an entry point, meaning that unwinders should stop when they hit +// this function. +func (fi *FuncInfo) TopFrame() bool { + return (fi.FuncFlag() & objabi.FuncFlag_TOPFRAME) != 0 +} + +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 { + r, auxs := l.auxs(i) + for j := range auxs { + a := &auxs[j] + if a.Type() == goobj.AuxFuncInfo { + b := r.Data(a.Sym().SymIdx) + return FuncInfo{l, r, b, 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, + pkgprefix: pkgprefix, + syms: make([]Sym, ndef+nhashed64def+nhasheddef+r.NNonpkgdef()+r.NNonpkgref()), + ndef: ndef, + nhasheddef: nhasheddef, + nhashed64def: nhashed64def, + objidx: uint32(len(l.objs)), + } + + if r.Unlinkable() { + log.Fatalf("link: unlinkable object (from package %s) - compiler requires -p flag", lib.Pkg) + } + + // 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, io.SeekCurrent) + + 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) + 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)) + 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) + v = abiToVer(osym.ABI(), r.version) + } + gi := st.addSym(name, v, r, i, kind, osym) + r.syms[i] = gi + 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, int(osym.ABI())); 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) + + 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) + } + l.npkgsyms = l.NSym() + 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()) + for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ { + osym := r.Sym(ndef + i) + name := osym.Name(r.Reader) + 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("%v: reference to nonexistent package %s", r.unit.Lib, pkg) + } + 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 +} + +// 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.SymName(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) + 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()) + 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(), uint16(rel.Type()), rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())}) + } + + // 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) + + // Some attributes were encoded in the object file. Copy them over. + l.SetAttrDuplicateOK(symIdx, r.Sym(li).Dupok()) + l.SetAttrShared(symIdx, r.Shared()) +} + +// 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? +} + +// 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 pair of lists of loader.Sym's. First list corresponds to the +// corresponding to the undefined symbols themselves, the second list +// is the symbol that is making a reference to the undef. 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, []Sym) { + result, fromr := []Sym{}, []Sym{} +outerloop: + 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.SymName(rs) != ".got" { + result = append(result, rs) + fromr = append(fromr, si) + if limit != -1 && len(result) >= limit { + break outerloop + } + } + } + } + return result, fromr +} + +// 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 !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) != "" { + // Note: Replace is needed here because symbol names might have % in them, + // due to the use of LinkString for names of instantiating types. + format = strings.Replace(reporter.ldr.SymName(s), "%", "%%", -1) + ": " + 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..7d1031e --- /dev/null +++ b/src/cmd/link/internal/loader/loader_test.go @@ -0,0 +1,455 @@ +// 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.SymName(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} + r.SetType(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..558c0a7 --- /dev/null +++ b/src/cmd/link/internal/loader/symbolbuilder.go @@ -0,0 +1,446 @@ +// 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] +} + +// 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(uint16(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)...) + 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.relocs[j].SetType(uint16(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] +} + +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 > int64(len(sb.data)) { + panic("attempt to write past end of buffer") + } + copy(sb.data[off:off+strLen], str) + return off + strLen +} + +// AddCStringAt adds str plus a null terminating byte. +func (sb *SymbolBuilder) AddCStringAt(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..6e78392 --- /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 +https://github.com/9fans/plan9port/tree/master/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.LookupOrCreateCgoExport(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.SymName(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 := §.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..0d33823 --- /dev/null +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -0,0 +1,777 @@ +// 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 + IMAGE_REL_ARM64_ABSOLUTE = 0x0000 + IMAGE_REL_ARM64_ADDR32 = 0x0001 + IMAGE_REL_ARM64_ADDR32NB = 0x0002 + IMAGE_REL_ARM64_BRANCH26 = 0x0003 + IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004 + IMAGE_REL_ARM64_REL21 = 0x0005 + IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006 + IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007 + IMAGE_REL_ARM64_SECREL = 0x0008 + IMAGE_REL_ARM64_SECREL_LOW12A = 0x0009 + IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A + IMAGE_REL_ARM64_SECREL_LOW12L = 0x000B + IMAGE_REL_ARM64_TOKEN = 0x000C + IMAGE_REL_ARM64_SECTION = 0x000D + IMAGE_REL_ARM64_ADDR64 = 0x000E + IMAGE_REL_ARM64_BRANCH19 = 0x000F + IMAGE_REL_ARM64_BRANCH14 = 0x0010 + IMAGE_REL_ARM64_REL32 = 0x0011 +) + +const ( + // When stored into the PLT value for a symbol, this token tells + // windynrelocsym to redirect direct references to this symbol to a stub + // that loads from the corresponding import symbol and then does + // a jump to the loaded value. + CreateImportStubPltToken = -2 + + // When stored into the GOT value for a import symbol __imp_X this + // token tells windynrelocsym to redirect references to the + // underlying DYNIMPORT symbol X. + RedirectToDynImportGotToken = -2 +) + +// 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 +} + +// peImportSymsState tracks the set of DLL import symbols we've seen +// while reading host objects. We create a singleton instance of this +// type, which will persist across multiple host objects. +type peImportSymsState struct { + + // Text and non-text sections read in by the host object loader. + secSyms []loader.Sym + + // SDYNIMPORT symbols encountered along the way + dynimports map[loader.Sym]struct{} + + // Loader and arch, for use in postprocessing. + l *loader.Loader + arch *sys.Arch +} + +var importSymsState *peImportSymsState + +func createImportSymsState(l *loader.Loader, arch *sys.Arch) { + if importSymsState != nil { + return + } + importSymsState = &peImportSymsState{ + dynimports: make(map[loader.Sym]struct{}), + l: l, + arch: arch, + } +} + +// peLoaderState holds various bits of useful state information needed +// while loading a single PE object file. +type peLoaderState struct { + l *loader.Loader + arch *sys.Arch + f *pe.File + pn string + sectsyms map[*pe.Section]loader.Sym + comdats map[uint16]int64 // key is section index, val is size + sectdata map[*pe.Section][]byte + localSymVersion int +} + +// comdatDefinitions records the names of symbols for which we've +// previously seen a definition in COMDAT. Key is symbol name, value +// is symbol size (or -1 if we're using the "any" strategy). +var comdatDefinitions = make(map[string]int64) + +// Load loads the PE file pn from input. +// Symbols from the object file are created via the loader 'l', and +// 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) { + state := &peLoaderState{ + l: l, + arch: arch, + sectsyms: make(map[*pe.Section]loader.Sym), + sectdata: make(map[*pe.Section][]byte), + localSymVersion: localSymVersion, + pn: pn, + } + createImportSymsState(state.l, state.arch) + + // 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() + state.f = f + + // TODO return error if found .cormeta + + // create symbols for mapped sections + for _, sect := range f.Sections { + if sect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + + if sect.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.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) + s := state.l.LookupOrCreateCgoExport(name, localSymVersion) + bld := l.MakeSymbolUpdater(s) + + switch sect.Characteristics & (pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE | pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE) { + case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ: //.rdata + bld.SetType(sym.SRODATA) + + case pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.bss + bld.SetType(sym.SNOPTRBSS) + + case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.data + bld.SetType(sym.SNOPTRDATA) + + case pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE | pe.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 + } + state.sectdata[sect] = data + bld.SetData(data) + } + bld.SetSize(int64(sect.Size)) + state.sectsyms[sect] = s + if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") { + rsrc = append(rsrc, s) + } + } + + // Make a prepass over the symbols to collect info about COMDAT symbols. + if err := state.preprocessSymbols(); err != nil { + return nil, nil, err + } + + // load relocations + for _, rsect := range f.Sections { + if _, found := state.sectsyms[rsect]; !found { + continue + } + if rsect.NumberOfRelocations == 0 { + continue + } + if rsect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + if rsect.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.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(state.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 := state.readpesym(pesym) + 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, state.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(state.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(state.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(state.sectdata[rsect][rOff:])) + } + + case sys.ARM: + switch r.Type { + default: + return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, state.sectsyms[rsect], r.Type) + + case IMAGE_REL_ARM_SECREL: + rType = objabi.R_PCREL + + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) + + case IMAGE_REL_ARM_ADDR32, IMAGE_REL_ARM_ADDR32NB: + rType = objabi.R_ADDR + + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) + + case IMAGE_REL_ARM_BRANCH24: + rType = objabi.R_CALLARM + + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) + } + + case sys.ARM64: + switch r.Type { + default: + return nil, nil, fmt.Errorf("%s: %v: unknown ARM64 relocation type %v", pn, state.sectsyms[rsect], r.Type) + + case IMAGE_REL_ARM64_ADDR32, IMAGE_REL_ARM64_ADDR32NB: + rType = objabi.R_ADDR + + rAdd = int64(int32(binary.LittleEndian.Uint32(state.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 := state.sectsyms[sect]; !found { + continue + } + } + + bld, s, err := state.readpesym(pesym) + if err != nil { + return nil, nil, err + } + + if pesym.SectionNumber == 0 { // extern + 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 := state.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 + } + + // Check for COMDAT symbol. + if sz, ok1 := state.comdats[uint16(pesym.SectionNumber-1)]; ok1 { + if psz, ok2 := comdatDefinitions[l.SymName(s)]; ok2 { + if sz == psz { + // OK to discard, we've seen an instance + // already. + continue + } + } + } + if l.OuterSym(s) != 0 { + if l.AttrDuplicateOK(s) { + continue + } + outerName := l.SymName(l.OuterSym(s)) + sectName := l.SymName(state.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 := state.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) + } + if sz, ok := state.comdats[uint16(pesym.SectionNumber-1)]; ok { + // This is a COMDAT definition. Record that we're picking + // this instance so that we can ignore future defs. + if _, ok := comdatDefinitions[l.SymName(s)]; ok { + return nil, nil, fmt.Errorf("internal error: preexisting COMDAT definition for %q", name) + } + comdatDefinitions[l.SymName(s)] = sz + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for _, sect := range f.Sections { + s := state.sectsyms[sect] + if s == 0 { + continue + } + l.SortSub(s) + importSymsState.secSyms = append(importSymsState.secSyms, 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 +} + +// PostProcessImports works to resolve inconsistencies with DLL import +// symbols; it is needed when building with more "modern" C compilers +// with internal linkage. +// +// Background: DLL import symbols are data (SNOPTRDATA) symbols whose +// name is of the form "__imp_XXX", which contain a pointer/reference +// to symbol XXX. It's possible to have import symbols for both data +// symbols ("__imp__fmode") and text symbols ("__imp_CreateEventA"). +// In some case import symbols are just references to some external +// thing, and in other cases we see actual definitions of import +// symbols when reading host objects. +// +// Previous versions of the linker would in most cases immediately +// "forward" import symbol references, e.g. treat a references to +// "__imp_XXX" a references to "XXX", however this doesn't work well +// with more modern compilers, where you can sometimes see import +// symbols that are defs (as opposed to external refs). +// +// The main actions taken below are to search for references to +// SDYNIMPORT symbols in host object text/data sections and flag the +// symbols for later fixup. When we see a reference to an import +// symbol __imp_XYZ where XYZ corresponds to some SDYNIMPORT symbol, +// we flag the symbol (via GOT setting) so that it can be redirected +// to XYZ later in windynrelocsym. When we see a direct reference to +// an SDYNIMPORT symbol XYZ, we also flag the symbol (via PLT setting) +// to indicated that the reference will need to be redirected to a +// stub. +func PostProcessImports() error { + ldr := importSymsState.l + arch := importSymsState.arch + keeprelocneeded := make(map[loader.Sym]loader.Sym) + for _, s := range importSymsState.secSyms { + isText := ldr.SymType(s) == sym.STEXT + relocs := ldr.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + rs := r.Sym() + if ldr.SymType(rs) == sym.SDYNIMPORT { + // Tag the symbol for later stub generation. + ldr.SetPlt(rs, CreateImportStubPltToken) + continue + } + isym, err := LookupBaseFromImport(rs, ldr, arch) + if err != nil { + return err + } + if isym == 0 { + continue + } + if ldr.SymType(isym) != sym.SDYNIMPORT { + continue + } + // For non-text symbols, forward the reference from __imp_X to + // X immediately. + if !isText { + r.SetSym(isym) + continue + } + // Flag this imp symbol to be processed later in windynrelocsym. + ldr.SetGot(rs, RedirectToDynImportGotToken) + // Consistency check: should be no PLT token here. + splt := ldr.SymPlt(rs) + if splt != -1 { + return fmt.Errorf("internal error: import symbol %q has invalid PLT setting %d", ldr.SymName(rs), splt) + } + // Flag for dummy relocation. + keeprelocneeded[rs] = isym + } + } + for k, v := range keeprelocneeded { + sb := ldr.MakeSymbolUpdater(k) + r, _ := sb.AddRel(objabi.R_KEEP) + r.SetSym(v) + } + importSymsState = nil + return nil +} + +func issect(s *pe.COFFSymbol) bool { + return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.' +} + +func (state *peLoaderState) readpesym(pesym *pe.COFFSymbol) (*loader.SymbolBuilder, loader.Sym, error) { + symname, err := pesym.FullName(state.f.StringTable) + if err != nil { + return nil, 0, err + } + var name string + if issect(pesym) { + name = state.l.SymName(state.sectsyms[state.f.Sections[pesym.SectionNumber-1]]) + } else { + name = symname + // A note on the "_main" exclusion below: the main routine + // defined by the Go runtime is named "_main", not "main", so + // when reading references to _main from a host object we want + // to avoid rewriting "_main" to "main" in this specific + // instance. See #issuecomment-1143698749 on #35006 for more + // details on this problem. + if state.arch.Family == sys.I386 && name[0] == '_' && name != "_main" && !strings.HasPrefix(name, "__imp_") { + 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 = state.l.LookupOrCreateCgoExport(name, 0) + + case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL: + s = state.l.LookupOrCreateCgoExport(name, state.localSymVersion) + bld = makeUpdater(state.l, bld, s) + bld.SetDuplicateOK(true) + + default: + return nil, 0, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass) + } + } + + if s != 0 && state.l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) { + bld = makeUpdater(state.l, bld, s) + bld.SetType(sym.SXREF) + } + + return bld, s, nil +} + +// preprocessSymbols walks the COFF symbols for the PE file we're +// reading and looks for cases where we have both a symbol definition +// for "XXX" and an "__imp_XXX" symbol, recording these cases in a map +// in the state struct. This information will be used in readpesym() +// above to give such symbols special treatment. This function also +// gathers information about COMDAT sections/symbols for later use +// in readpesym(). +func (state *peLoaderState) preprocessSymbols() error { + + // Locate comdat sections. + state.comdats = make(map[uint16]int64) + for i, s := range state.f.Sections { + if s.Characteristics&uint32(pe.IMAGE_SCN_LNK_COMDAT) != 0 { + state.comdats[uint16(i)] = int64(s.Size) + } + } + + // Examine symbol defs. + for i, numaux := 0, 0; i < len(state.f.COFFSymbols); i += numaux + 1 { + pesym := &state.f.COFFSymbols[i] + numaux = int(pesym.NumberOfAuxSymbols) + if pesym.SectionNumber == 0 { // extern + continue + } + symname, err := pesym.FullName(state.f.StringTable) + if err != nil { + return err + } + if _, isc := state.comdats[uint16(pesym.SectionNumber-1)]; !isc { + continue + } + if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) { + continue + } + // This symbol corresponds to a COMDAT section. Read the + // aux data for it. + auxsymp, err := state.f.COFFSymbolReadSectionDefAux(i) + if err != nil { + return fmt.Errorf("unable to read aux info for section def symbol %d %s: pe.COFFSymbolReadComdatInfo returns %v", i, symname, err) + } + if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_SAME_SIZE { + // This is supported. + } else if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_ANY { + // Also supported. + state.comdats[uint16(pesym.SectionNumber-1)] = int64(-1) + } else { + // We don't support any of the other strategies at the + // moment. I suspect that we may need to also support + // "associative", we'll see. + return fmt.Errorf("internal error: unsupported COMDAT selection strategy found in path=%s sec=%d strategy=%d idx=%d, please file a bug", state.pn, auxsymp.SecNum, auxsymp.Selection, i) + } + } + return nil +} + +// LookupBaseFromImport examines the symbol "s" to see if it +// corresponds to an import symbol (name of the form "__imp_XYZ") and +// if so, it looks up the underlying target of the import symbol and +// returns it. An error is returned if the symbol is of the form +// "__imp_XYZ" but no XYZ can be found. +func LookupBaseFromImport(s loader.Sym, ldr *loader.Loader, arch *sys.Arch) (loader.Sym, error) { + sname := ldr.SymName(s) + if !strings.HasPrefix(sname, "__imp_") { + return 0, nil + } + basename := sname[len("__imp_"):] + if arch.Family == sys.I386 && basename[0] == '_' { + basename = basename[1:] // _Name => Name + } + isym := ldr.Lookup(basename, 0) + if isym == 0 { + return 0, fmt.Errorf("internal error: import symbol %q with no underlying sym", sname) + } + return isym, 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..920e1c8 --- /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.LookupOrCreateCgoExport(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/loong64/asm.go b/src/cmd/link/internal/loong64/asm.go new file mode 100644 index 0000000..0eb3a81 --- /dev/null +++ b/src/cmd/link/internal/loong64/asm.go @@ -0,0 +1,240 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loong64 + +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) {} + +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + log.Fatalf("adddynrel not implemented") + return false +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + // loong64 ELF relocation (endian neutral) + // offset uint64 + // symreloc uint64 // The high 32-bit is the symbol, the low 32-bit is the relocation type. + // addend int64 + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + switch r.Type { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + switch r.Size { + case 4: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_32) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + case 8: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_64) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + default: + return false + } + case objabi.R_ADDRLOONG64TLS: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_TLS_TPREL) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE)) + out.Write64(uint64(0xfff)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_AND)) + out.Write64(uint64(0x0)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_POP_32_U_10_12)) + out.Write64(uint64(0x0)) + + case objabi.R_ADDRLOONG64TLSU: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_TLS_TPREL) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE)) + out.Write64(uint64(0xc)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_SR)) + out.Write64(uint64(0x0)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_POP_32_S_5_20) | uint64(0)<<32) + out.Write64(uint64(0x0)) + + case objabi.R_CALLLOONG64: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_PLT_PCREL) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_POP_32_S_0_10_10_16_S2)) + out.Write64(uint64(0x0)) + // The pcaddu12i + addi.d instructions is used to obtain address of a symbol on Loong64. + // The low 12-bit of the symbol address need to be added. The addi.d instruction have + // signed 12-bit immediate operand. The 0x800 (addr+U12 <=> addr+0x800+S12) is introduced + // to do sign extending from 12 bits. The 0x804 is 0x800 + 4, 4 is instruction bit + // width on Loong64 and is used to correct the PC of the addi.d instruction. + case objabi.R_ADDRLOONG64: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_PCREL) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd + 0x4)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_PCREL) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd + 0x804)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE)) + out.Write64(uint64(0xc)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_SR)) + out.Write64(uint64(0x0)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE)) + out.Write64(uint64(0xc)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_SL)) + out.Write64(uint64(0x0)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_SUB)) + out.Write64(uint64(0x0)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_POP_32_S_10_12)) + out.Write64(uint64(0x0)) + + case objabi.R_ADDRLOONG64U: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_PCREL) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd + 0x800)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE)) + out.Write64(uint64(0xc)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_SR)) + out.Write64(uint64(0x0)) + + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_SOP_POP_32_S_5_20) | uint64(0)<<32) + out.Write64(uint64(0x0)) + } + + 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) { + rs := r.Sym() + if target.IsExternal() { + nExtReloc := 0 + switch r.Type() { + default: + return val, 0, false + case objabi.R_ADDRLOONG64, + objabi.R_ADDRLOONG64U: + // set up addend for eventual relocation via outer symbol. + rs, _ := ld.FoldSubSymbolOffset(ldr, rs) + 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 = 8 // need 8 ELF relocations. see elfreloc1 + if r.Type() == objabi.R_ADDRLOONG64U { + nExtReloc = 4 + } + return val, nExtReloc, true + case objabi.R_ADDRLOONG64TLS, + objabi.R_ADDRLOONG64TLSU, + objabi.R_CALLLOONG64, + objabi.R_JMPLOONG64: + nExtReloc = 4 + if r.Type() == objabi.R_CALLLOONG64 || r.Type() == objabi.R_JMPLOONG64 { + nExtReloc = 2 + } + return val, nExtReloc, true + } + } + + const isOk = true + const noExtReloc = 0 + + switch r.Type() { + case objabi.R_CONST: + return r.Add(), noExtReloc, isOk + case objabi.R_GOTOFF: + return ldr.SymValue(r.Sym()) + r.Add() - ldr.SymValue(syms.GOT), noExtReloc, isOk + case objabi.R_ADDRLOONG64, + objabi.R_ADDRLOONG64U: + pc := ldr.SymValue(s) + int64(r.Off()) + t := ldr.SymAddr(rs) + r.Add() - pc + if r.Type() == objabi.R_ADDRLOONG64 { + return int64(val&0xffc003ff | (((t + 4 - ((t + 4 + 1<<11) >> 12 << 12)) << 10) & 0x3ffc00)), noExtReloc, isOk + } + return int64(val&0xfe00001f | (((t + 1<<11) >> 12 << 5) & 0x1ffffe0)), noExtReloc, isOk + case objabi.R_ADDRLOONG64TLS, + objabi.R_ADDRLOONG64TLSU: + t := ldr.SymAddr(rs) + r.Add() + if r.Type() == objabi.R_ADDRLOONG64TLS { + return int64(val&0xffc003ff | ((t & 0xfff) << 10)), noExtReloc, isOk + } + return int64(val&0xfe00001f | (((t) >> 12 << 5) & 0x1ffffe0)), noExtReloc, isOk + case objabi.R_CALLLOONG64, + objabi.R_JMPLOONG64: + pc := ldr.SymValue(s) + int64(r.Off()) + t := ldr.SymAddr(rs) + r.Add() - pc + return int64(val&0xfc000000 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x3ff0000) >> 16)), noExtReloc, isOk + } + + return val, 0, false +} + +func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) 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_ADDRLOONG64, + objabi.R_ADDRLOONG64U: + return ld.ExtrelocViaOuterSym(ldr, r, s), true + + case objabi.R_ADDRLOONG64TLS, + objabi.R_ADDRLOONG64TLSU, + objabi.R_CONST, + objabi.R_GOTOFF, + objabi.R_CALLLOONG64, + objabi.R_JMPLOONG64: + return ld.ExtrelocSimple(ldr, r), true + } + return loader.ExtReloc{}, false +} diff --git a/src/cmd/link/internal/loong64/l.go b/src/cmd/link/internal/loong64/l.go new file mode 100644 index 0000000..e97a868 --- /dev/null +++ b/src/cmd/link/internal/loong64/l.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loong64 + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 8 +) + +/* Used by ../../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 3 + dwarfRegLR = 1 +) diff --git a/src/cmd/link/internal/loong64/obj.go b/src/cmd/link/internal/loong64/obj.go new file mode 100644 index 0000000..0a5bb0a --- /dev/null +++ b/src/cmd/link/internal/loong64/obj.go @@ -0,0 +1,59 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loong64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchLoong64 + + 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, + Machoreloc1: machoreloc1, + Gentext: gentext, + + Linuxdynld: "/lib64/ld.so.1", + LinuxdynldMusl: "/lib64/ld-musl-loongarch.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: /* loong64 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/mips/asm.go b/src/cmd/link/internal/mips/asm.go new file mode 100644 index 0000000..5891d35 --- /dev/null +++ b/src/cmd/link/internal/mips/asm.go @@ -0,0 +1,155 @@ +// 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() + 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, []byte) 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..f03c9ab --- /dev/null +++ b/src/cmd/link/internal/mips/obj.go @@ -0,0 +1,92 @@ +// 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" + "internal/buildcfg" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchMIPS + musl := "/lib/ld-musl-mips.so.1" + if buildcfg.GOARCH == "mipsle" { + arch = sys.ArchMIPSLE + musl = "/lib/ld-musl-mipsel.so.1" + } + + 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", + LinuxdynldMusl: musl, + + 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..0e64af3 --- /dev/null +++ b/src/cmd/link/internal/mips64/asm.go @@ -0,0 +1,169 @@ +// 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 + + addend := r.Xadd + + 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)) + if ctxt.Target.IsOpenbsd() { + // OpenBSD mips64 does not currently offset TLS by 0x7000, + // as such we need to add this back to get the correct offset + // via the external linker. + addend += 0x7000 + } + case objabi.R_CALLMIPS, + objabi.R_JMPMIPS: + out.Write8(uint8(elf.R_MIPS_26)) + } + out.Write64(uint64(addend)) + + 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() + 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 target.IsOpenbsd() { + // OpenBSD mips64 does not currently offset TLS by 0x7000, + // as such we need to add this back to get the correct offset. + t += 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, []byte) 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..557d799 --- /dev/null +++ b/src/cmd/link/internal/mips64/obj.go @@ -0,0 +1,102 @@ +// 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" + "internal/buildcfg" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchMIPS64 + musl := "/lib/ld-musl-mips64.so.1" + if buildcfg.GOARCH == "mips64le" { + arch = sys.ArchMIPS64LE + musl = "/lib/ld-musl-mips64el.so.1" + } + + 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", + LinuxdynldMusl: musl, + 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..7413599 --- /dev/null +++ b/src/cmd/link/internal/ppc64/asm.go @@ -0,0 +1,1529 @@ +// 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" + "strconv" + "strings" +) + +func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (sym loader.Sym, firstUse bool) { + // 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. + + // 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)) + firstUse = stub.Size() == 0 + if firstUse { + gencallstub(ctxt, ldr, 1, stub, r.Sym()) + } + + // Update the relocation to use the call stub + r.SetSym(stub.Sym()) + + // Make the symbol writeable so we can fixup toc. + su := ldr.MakeSymbolUpdater(s) + su.MakeWritable() + p := su.Data() + + // Check for toc restore slot (a nop), and replace with toc restore. + var nop uint32 + if len(p) >= int(r.Off()+8) { + nop = ctxt.Arch.ByteOrder.Uint32(p[r.Off()+4:]) + } + if nop != 0x60000000 { + ldr.Errorf(s, "Symbol %s is missing toc restoration slot at offset %d", ldr.SymName(s), r.Off()+4) + } + const o1 = 0xe8410018 // ld r2,24(r1) + ctxt.Arch.ByteOrder.PutUint32(p[r.Off()+4:], o1) + + return stub.Sym(), firstUse +} + +// Scan relocs and generate PLT stubs and generate/fixup ABI defined functions created by the linker. +func genstubs(ctxt *ld.Link, ldr *loader.Loader) { + var stubs []loader.Sym + var abifuncs []loader.Sym + for _, s := range ctxt.Textp { + relocs := ldr.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + switch r.Type() { + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24): + switch ldr.SymType(r.Sym()) { + case sym.SDYNIMPORT: + // This call goes through the PLT, generate and call through a PLT stub. + if sym, firstUse := genpltstub(ctxt, ldr, r, s); firstUse { + stubs = append(stubs, sym) + } + + case sym.SXREF: + // Is this an ELF ABI defined function which is (in practice) + // generated by the linker to save/restore callee save registers? + // These are defined similarly for both PPC64 ELF and ELFv2. + targName := ldr.SymName(r.Sym()) + if strings.HasPrefix(targName, "_save") || strings.HasPrefix(targName, "_rest") { + if sym, firstUse := rewriteABIFuncReloc(ctxt, ldr, targName, r); firstUse { + abifuncs = append(abifuncs, sym) + } + } + } + + // Handle objects compiled with -fno-plt. Rewrite local calls to avoid indirect calling. + // These are 0 sized relocs. They mark the mtctr r12, or bctrl + ld r2,24(r1). + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLTSEQ): + if ldr.SymType(r.Sym()) == sym.STEXT { + // This should be an mtctr instruction. Turn it into a nop. + su := ldr.MakeSymbolUpdater(s) + const OP_MTCTR = 31<<26 | 0x9<<16 | 467<<1 + const MASK_OP_MTCTR = 63<<26 | 0x3FF<<11 | 0x1FF<<1 + rewritetonop(&ctxt.Target, ldr, su, int64(r.Off()), MASK_OP_MTCTR, OP_MTCTR) + } + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLTCALL): + if ldr.SymType(r.Sym()) == sym.STEXT { + // This relocation should point to a bctrl followed by a ld r2, 24(41) + const OP_BL = 0x48000001 // bl 0 + const OP_TOCRESTORE = 0xe8410018 // ld r2,24(r1) + const OP_BCTRL = 0x4e800421 // bctrl + + // Convert the bctrl into a bl. + su := ldr.MakeSymbolUpdater(s) + rewritetoinsn(&ctxt.Target, ldr, su, int64(r.Off()), 0xFFFFFFFF, OP_BCTRL, OP_BL) + + // Turn this reloc into an R_CALLPOWER, and convert the TOC restore into a nop. + su.SetRelocType(i, objabi.R_CALLPOWER) + su.SetRelocAdd(i, r.Add()+int64(ldr.SymLocalentry(r.Sym()))) + r.SetSiz(4) + rewritetonop(&ctxt.Target, ldr, su, int64(r.Off()+4), 0xFFFFFFFF, OP_TOCRESTORE) + } + } + } + } + + // Append any usage of the go versions of ELF save/restore + // functions to the end of the callstub list to minimize + // chances a trampoline might be needed. + stubs = append(stubs, abifuncs...) + + // Put 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) +} + +// Rewrite ELF (v1 or v2) calls to _savegpr0_n, _savegpr1_n, _savefpr_n, _restfpr_n, _savevr_m, or +// _restvr_m (14<=n<=31, 20<=m<=31). Redirect them to runtime.elf_restgpr0+(n-14)*4, +// runtime.elf_restvr+(m-20)*8, and similar. +// +// These functions are defined in the ELFv2 ABI (generated when using gcc -Os option) to save and +// restore callee-saved registers (as defined in the PPC64 ELF ABIs) from registers n or m to 31 of +// the named type. R12 and R0 are sometimes used in exceptional ways described in the ABI. +// +// Final note, this is only needed when linking internally. The external linker will generate these +// functions if they are used. +func rewriteABIFuncReloc(ctxt *ld.Link, ldr *loader.Loader, tname string, r loader.Reloc) (sym loader.Sym, firstUse bool) { + s := strings.Split(tname, "_") + // A valid call will split like {"", "savegpr0", "20"} + if len(s) != 3 { + return 0, false // Not an abi func. + } + minReg := 14 // _savegpr0_{n}, _savegpr1_{n}, _savefpr_{n}, 14 <= n <= 31 + offMul := 4 // 1 instruction per register op. + switch s[1] { + case "savegpr0", "savegpr1", "savefpr": + case "restgpr0", "restgpr1", "restfpr": + case "savevr", "restvr": + minReg = 20 // _savevr_{n} or _restvr_{n}, 20 <= n <= 31 + offMul = 8 // 2 instructions per register op. + default: + return 0, false // Not an abi func + } + n, e := strconv.Atoi(s[2]) + if e != nil || n < minReg || n > 31 || r.Add() != 0 { + return 0, false // Invalid register number, or non-zero addend. Not an abi func. + } + + // tname is a valid relocation to an ABI defined register save/restore function. Re-relocate + // them to a go version of these functions in runtime/asm_ppc64x.s + ts := ldr.LookupOrCreateSym("runtime.elf_"+s[1], 0) + r.SetSym(ts) + r.SetAdd(int64((n - minReg) * offMul)) + firstUse = !ldr.AttrReachable(ts) + if firstUse { + ldr.SetAttrReachable(ts, true) + // This function only becomes reachable now. It has been dropped from + // the text section (it was unreachable until now), it needs included. + // + // Similarly, TOC regeneration should not happen for these functions, + // remove it from this save/restore function. + if ldr.AttrShared(ts) { + sb := ldr.MakeSymbolUpdater(ts) + sb.SetData(sb.Data()[8:]) + sb.SetSize(sb.Size() - 8) + relocs := sb.Relocs() + // Only one PCREL reloc to .TOC. should be present. + if relocs.Count() != 1 { + log.Fatalf("Unexpected number of relocs in %s\n", ldr.SymName(ts)) + } + sb.ResetRelocs() + + } + } + return ts, firstUse +} + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + if ctxt.DynlinkingGo() { + genaddmoduledata(ctxt, ldr) + } + + if ctxt.LinkMode == ld.LinkInternal { + genstubs(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 +} + +// Rewrite the instruction at offset into newinsn. Also, verify the +// existing instruction under mask matches the check value. +func rewritetoinsn(target *ld.Target, ldr *loader.Loader, su *loader.SymbolBuilder, offset int64, mask, check, newinsn uint32) { + su.MakeWritable() + op := target.Arch.ByteOrder.Uint32(su.Data()[offset:]) + if op&mask != check { + ldr.Errorf(su.Sym(), "Rewrite offset 0x%x to 0x%08X failed check (0x%08X&0x%08X != 0x%08X)", offset, newinsn, op, mask, check) + } + su.SetUint32(target.Arch, offset, newinsn) +} + +// Rewrite the instruction at offset into a hardware nop instruction. Also, verify the +// existing instruction under mask matches the check value. +func rewritetonop(target *ld.Target, ldr *loader.Loader, su *loader.SymbolBuilder, offset int64, mask, check uint32) { + const NOP = 0x60000000 + rewritetoinsn(target, ldr, su, offset, mask, check, NOP) +} + +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))) + + 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 + } else 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_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 + + // When compiling with gcc's -fno-plt option (no PLT), the following code and relocation + // sequences may be present to call an external function: + // + // 1. addis Rx,foo@R_PPC64_PLT16_HA + // 2. ld 12,foo@R_PPC64_PLT16_LO_DS(Rx) + // 3. mtctr 12 ; foo@R_PPC64_PLTSEQ + // 4. bctrl ; foo@R_PPC64_PLTCALL + // 5. ld r2,24(r1) + // + // Note, 5 is required to follow the R_PPC64_PLTCALL. Similarly, relocations targeting + // instructions 3 and 4 are zero sized informational relocations. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLT16_HA), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLT16_LO_DS): + su := ldr.MakeSymbolUpdater(s) + isPLT16_LO_DS := r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_PPC64_PLT16_LO_DS) + if isPLT16_LO_DS { + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS) + } else { + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW) + } + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + if targType == sym.SDYNIMPORT { + // This is an external symbol, make space in the GOT and retarget the reloc. + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_PPC64_GLOB_DAT)) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + } else if targType == sym.STEXT { + if isPLT16_LO_DS { + // Expect an ld opcode to nop + const MASK_OP_LD = 63<<26 | 0x3 + const OP_LD = 58 << 26 + rewritetonop(target, ldr, su, int64(r.Off()), MASK_OP_LD, OP_LD) + } else { + // Expect an addis opcode to nop + const MASK_OP_ADDIS = 63 << 26 + const OP_ADDIS = 15 << 26 + rewritetonop(target, ldr, su, int64(r.Off()), MASK_OP_ADDIS, OP_ADDIS) + } + // And we can ignore this reloc now. + su.SetRelocType(rIdx, objabi.ElfRelocOffset) + } else { + ldr.Errorf(s, "unexpected PLT relocation target symbol type %s", targType.String()) + } + return true + } + + // Handle references to ELF symbols from our own object files. + relocs := ldr.Relocs(s) + r = relocs.At(rIdx) + + switch r.Type() { + case objabi.R_ADDR: + if ldr.SymType(s) == sym.STEXT { + log.Fatalf("R_ADDR relocation in text symbol %s is unsupported\n", ldr.SymName(s)) + } + 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 + } + } + // Generate R_PPC64_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_PPC64_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_PPC64_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 + } + + 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: + // This only supports 16b relocations. It is fixed up in archreloc. + 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_ADDRPOWER_D34: + out.Write64(uint64(elf.R_PPC64_D34) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_PCREL34: + out.Write64(uint64(elf.R_PPC64_PCREL34) | uint64(elfsym)<<32) + 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_HA) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_PPC64_TPREL16_LO) | uint64(elfsym)<<32) + case objabi.R_POWER_TLS_LE_TPREL34: + out.Write64(uint64(elf.R_PPC64_TPREL34) | uint64(elfsym)<<32) + case objabi.R_POWER_TLS_IE_PCREL34: + out.Write64(uint64(elf.R_PPC64_GOT_TPREL_PCREL34) | 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. +func archreloctoc(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) int64 { + rs := r.Sym() + var o1, o2 uint32 + var t int64 + useAddi := false + + if target.IsBigEndian() { + o1 = uint32(val >> 32) + o2 = uint32(val) + } else { + o1 = uint32(val) + o2 = uint32(val >> 32) + } + + // On AIX, TOC data accesses are always made indirectly against R2 (a sequence of addis+ld+load/store). If the + // The target of the load is known, the sequence can be written into addis+addi+load/store. On Linux, + // TOC data accesses are always made directly against R2 (e.g addis+load/store). + if target.IsAIX() { + if !strings.HasPrefix(ldr.SymName(rs), "TOC.") { + ldr.Errorf(s, "archreloctoc called for a symbol without TOC anchor") + } + relocs := ldr.Relocs(rs) + tarSym := 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) + } + } else { + t = ldr.SymValue(rs) + r.Add() - symtoc(ldr, syms, s) + } + + 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 + } + case objabi.R_ADDRPOWER_TOCREL: + o2 |= uint32(t) & 0xffff + default: + return -1 + } + + if target.IsBigEndian() { + return int64(o1)<<32 | int64(o2) + } + return int64(o2)<<32 | int64(o1) +} + +// archrelocaddr relocates a symbol address. +// This code is for linux only. +func archrelocaddr(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) int64 { + rs := r.Sym() + if target.IsAIX() { + ldr.Errorf(s, "archrelocaddr called for %s relocation\n", ldr.SymName(rs)) + } + o1, o2 := unpackInstPair(target, val) + + // Verify resulting address fits within a 31 bit (2GB) address space. + // This is a restriction arising from the usage of lis (HA) + d-form + // (LO) instruction sequences used to implement absolute relocations + // on PPC64 prior to ISA 3.1 (P10). For consistency, maintain this + // restriction for ISA 3.1 unless it becomes problematic. + 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)) + } + + switch r.Type() { + case objabi.R_ADDRPOWER_PCREL34: + // S + A - P + t -= (ldr.SymValue(s) + int64(r.Off())) + o1 |= computePrefix34HI(t) + o2 |= computeLO(int32(t)) + case objabi.R_ADDRPOWER_D34: + o1 |= computePrefix34HI(t) + o2 |= computeLO(int32(t)) + case objabi.R_ADDRPOWER: + o1 |= computeHA(int32(t)) + o2 |= computeLO(int32(t)) + case objabi.R_ADDRPOWER_DS: + o1 |= computeHA(int32(t)) + o2 |= computeLO(int32(t)) + if t&3 != 0 { + ldr.Errorf(s, "bad DS reloc for %s: %d", ldr.SymName(s), ldr.SymValue(rs)) + } + default: + return -1 + } + + return packInstPair(target, o1, o2) +} + +// 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) { + // The TOC pointer is valid. The external linker will insert trampolines. + 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 + } + // Note, the trampoline is always called directly. The addend of the original relocation is accounted for in the + // trampoline itself. + t = ldr.SymValue(tramp) - (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 { + 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()) + 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(0x3c000000) | 12<<21 | 2<<16 // addis r12, r2, toctargetaddr hi + o2 = uint32(0xe8000000) | 12<<21 | 12<<16 // ld r12, r12, 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(0x3c000000) | 12<<21 // lis r12,targetaddr hi + o2 = uint32(0x38000000) | 12<<21 | 12<<16 // addi r12,r12,targetaddr lo + + // ELFv2 save/restore functions use R0/R12 in special ways, therefore trampolines + // as generated here will not always work correctly. + if strings.HasPrefix(ldr.SymName(target), "runtime.elf_") { + log.Fatalf("Internal linker does not support trampolines to ELFv2 ABI"+ + " register save/restore function %s", ldr.SymName(target)) + } + + t := ldr.SymValue(target) + if t == 0 || r2Valid(ctxt) || ctxt.IsExternal() { + // Target address is unknown, generate relocations + r, _ := tramp.AddRel(objabi.R_ADDRPOWER) + if r2Valid(ctxt) { + // Use a TOC relative address if R2 holds the TOC pointer + o1 |= uint32(2 << 16) // Transform lis r31,ha into addis r31,r2,ha + r.SetType(objabi.R_ADDRPOWER_TOCREL) + } + r.SetOff(0) + r.SetSiz(8) // generates 2 relocations: HA + LO + r.SetSym(target) + r.SetAdd(offset) + } else { + // The target address is known, resolve it + t += offset + o1 |= (uint32(t) + 0x8000) >> 16 // HA + o2 |= uint32(t) & 0xFFFF // LO + } + } + + o3 := uint32(0x7c0903a6) | 12<<21 // mtctr r12 + 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) +} + +// Unpack a pair of 32 bit instruction words from +// a 64 bit relocation into instN and instN+1 in endian order. +func unpackInstPair(target *ld.Target, r int64) (uint32, uint32) { + if target.IsBigEndian() { + return uint32(r >> 32), uint32(r) + } + return uint32(r), uint32(r >> 32) +} + +// Pack a pair of 32 bit instruction words o1, o2 into 64 bit relocation +// in endian order. +func packInstPair(target *ld.Target, o1, o2 uint32) int64 { + if target.IsBigEndian() { + return (int64(o1) << 32) | int64(o2) + } + return int64(o1) | (int64(o2) << 32) +} + +// Compute the high-adjusted value (always a signed 32b value) per the ELF ABI. +// The returned value is always 0 <= x <= 0xFFFF. +func computeHA(val int32) uint32 { + return uint32(uint16((val + 0x8000) >> 16)) +} + +// Compute the low value (the lower 16 bits of any 32b value) per the ELF ABI. +// The returned value is always 0 <= x <= 0xFFFF. +func computeLO(val int32) uint32 { + return uint32(uint16(val)) +} + +// Compute the high 18 bits of a signed 34b constant. Used to pack the high 18 bits +// of a prefix34 relocation field. This assumes the input is already restricted to +// 34 bits. +func computePrefix34HI(val int64) uint32 { + return uint32((val >> 16) & 0x3FFFF) +} + +func computeTLSLEReloc(target *ld.Target, ldr *loader.Loader, rs, s loader.Sym) int64 { + // 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(int32(v)) != v { + ldr.Errorf(s, "TLS offset out of range %d", v) + } + return v +} + +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 := 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_IE_PCREL34, objabi.R_POWER_TLS_LE_TPREL34: + nExtReloc = 1 + return val, nExtReloc, true + case objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE: + if target.IsAIX() && rt == objabi.R_POWER_TLS_LE { + // Fixup val, an addis/addi pair of instructions, which generate a 32b displacement + // from the threadpointer (R13), into a 16b relocation. XCOFF only supports 16b + // TLS LE relocations. Likewise, verify this is an addis/addi sequence. + const expectedOpcodes = 0x3C00000038000000 + const expectedOpmasks = 0xFC000000FC000000 + if uint64(val)&expectedOpmasks != expectedOpcodes { + ldr.Errorf(s, "relocation for %s+%d is not an addis/addi pair: %16x", ldr.SymName(rs), r.Off(), uint64(val)) + } + nval := (int64(uint32(0x380d0000)) | val&0x03e00000) << 32 // addi rX, r13, $0 + nval |= int64(0x60000000) // nop + val = nval + nExtReloc = 1 + } else { + nExtReloc = 2 + } + 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, objabi.R_ADDRPOWER_D34, objabi.R_ADDRPOWER_PCREL34: + 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, objabi.R_ADDRPOWER_D34, objabi.R_ADDRPOWER_PCREL34: + 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())) + + tgtName := ldr.SymName(rs) + + // If we are linking PIE or shared code, all golang generated object files have an extra 2 instruction prologue + // to regenerate the TOC pointer from R12. The exception are two special case functions tested below. Note, + // local call offsets for externally generated objects are accounted for when converting into golang relocs. + if !ldr.AttrExternal(rs) && ldr.AttrShared(rs) && tgtName != "runtime.duffzero" && tgtName != "runtime.duffcopy" { + // Furthermore, only apply the offset if the target looks like the start of a function call. + if r.Add() == 0 && ldr.SymType(rs) == sym.STEXT { + t += 8 + } + } + + 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_ADDRPOWER_PCREL: // S + A - P + t := ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off())) + ha, l := unpackInstPair(target, val) + l |= computeLO(int32(t)) + ha |= computeHA(int32(t)) + return packInstPair(target, ha, l), nExtReloc, true + + case objabi.R_POWER_TLS: + const OP_ADD = 31<<26 | 266<<1 + const MASK_OP_ADD = 0x3F<<26 | 0x1FF<<1 + if val&MASK_OP_ADD != OP_ADD { + ldr.Errorf(s, "R_POWER_TLS reloc only supports XO form ADD, not %08X", val) + } + // Verify RB is R13 in ADD RA,RB,RT. + if (val>>11)&0x1F != 13 { + // If external linking is made to support this, it may expect the linker to rewrite RB. + ldr.Errorf(s, "R_POWER_TLS reloc requires R13 in RB (%08X).", uint32(val)) + } + return val, nExtReloc, true + + case objabi.R_POWER_TLS_IE: + // Convert TLS_IE relocation to TLS_LE if supported. + if !(target.IsPIE() && target.IsElf()) { + log.Fatalf("cannot handle R_POWER_TLS_IE (sym %s) when linking non-PIE, non-ELF binaries internally", ldr.SymName(s)) + } + + // We are an ELF binary, we can safely convert to TLS_LE from: + // addis to, r2, x@got@tprel@ha + // ld to, to, x@got@tprel@l(to) + // + // to TLS_LE by converting to: + // addis to, r0, x@tprel@ha + // addi to, to, x@tprel@l(to) + + const OP_ADDI = 14 << 26 + const OP_MASK = 0x3F << 26 + const OP_RA_MASK = 0x1F << 16 + // convert r2 to r0, and ld to addi + mask := packInstPair(target, OP_RA_MASK, OP_MASK) + addi_op := packInstPair(target, 0, OP_ADDI) + val &^= mask + val |= addi_op + fallthrough + + case objabi.R_POWER_TLS_LE: + v := computeTLSLEReloc(target, ldr, rs, s) + o1, o2 := unpackInstPair(target, val) + o1 |= computeHA(int32(v)) + o2 |= computeLO(int32(v)) + return packInstPair(target, o1, o2), nExtReloc, true + + case objabi.R_POWER_TLS_IE_PCREL34: + // Convert TLS_IE relocation to TLS_LE if supported. + if !(target.IsPIE() && target.IsElf()) { + log.Fatalf("cannot handle R_POWER_TLS_IE (sym %s) when linking non-PIE, non-ELF binaries internally", ldr.SymName(s)) + } + + // We are an ELF binary, we can safely convert to TLS_LE_TPREL34 from: + // pld rX, x@got@tprel@pcrel + // + // to TLS_LE_TPREL32 by converting to: + // pla rX, x@tprel + + const OP_MASK_PFX = 0xFFFFFFFF // Discard prefix word + const OP_MASK = (0x3F << 26) | 0xFFFF // Preserve RT, RA + const OP_PFX = 1<<26 | 2<<24 + const OP_PLA = 14 << 26 + mask := packInstPair(target, OP_MASK_PFX, OP_MASK) + pla_op := packInstPair(target, OP_PFX, OP_PLA) + val &^= mask + val |= pla_op + fallthrough + + case objabi.R_POWER_TLS_LE_TPREL34: + v := computeTLSLEReloc(target, ldr, rs, s) + o1, o2 := unpackInstPair(target, val) + o1 |= computePrefix34HI(v) + o2 |= computeLO(int32(v)) + return packInstPair(target, o1, o2), 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, p []byte) (relocatedOffset int64) { + rs := 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(p[r.Off()-2:]) + + } else { + o1 = binary.LittleEndian.Uint32(p[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(p[r.Off()-2:]) + } else { + o1 = binary.LittleEndian.Uint32(p[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(p[r.Off():])) + } else { + o1 = uint32(binary.LittleEndian.Uint16(p[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_POWER_TLS_IE_PCREL34, objabi.R_POWER_TLS_LE_TPREL34, 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, + objabi.R_ADDRPOWER_D34, + objabi.R_ADDRPOWER_PCREL34: + 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 ELFv2 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. + // + // PC-rel offsets are computed once the final codesize of the + // resolver is known. + // + // This stub is PIC, so first get the PC of label 1 into r11. + 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) // mtlr r0 + + // Compute the .plt array index from the entry point address + // into r0. This is computed relative to label 1 above. + glink.AddUint32(ctxt.Arch, 0x38000000) // li r0,-(res_0-1b) + 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 + + // Load the PC-rel offset of ".plt - 1b", and add it to 1b. + // This is stored after this stub and before the resolvers. + glink.AddUint32(ctxt.Arch, 0xe98b0000) // ld r12,res_0-1b-8(r11) + glink.AddUint32(ctxt.Arch, 0x7d6b6214) // add r11,r11,r12 + + // 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 + + // Store the PC-rel offset to the PLT + r, _ := glink.AddRel(objabi.R_PCREL) + r.SetSym(ctxt.PLT) + r.SetSiz(8) + r.SetOff(int32(glink.Size())) + r.SetAdd(glink.Size()) // Adjust the offset to be relative to label 1 above. + glink.AddUint64(ctxt.Arch, 0) // The offset to the PLT. + + // Resolve PC-rel offsets above now the final size of the stub is known. + res0m1b := glink.Size() - 8 // res_0 - 1b + glink.SetUint32(ctxt.Arch, 16, 0x38000000|uint32(uint16(-res0m1b))) + glink.SetUint32(ctxt.Arch, 32, 0xe98b0000|uint32(uint16(res0m1b-8))) + + // 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..f580c55 --- /dev/null +++ b/src/cmd/link/internal/ppc64/obj.go @@ -0,0 +1,113 @@ +// 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" + "internal/buildcfg" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchPPC64LE + dynld := "/lib64/ld64.so.2" + musl := "/lib/ld-musl-powerpc64le.so.1" + + if buildcfg.GOARCH == "ppc64" { + arch = sys.ArchPPC64 + dynld = "/lib64/ld64.so.1" + musl = "/lib/ld-musl-powerpc64.so.1" + } + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + TrampLimit: 0x1c00000, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Extreloc: extreloc, + Elfreloc1: elfreloc1, + ElfrelocSize: 24, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Trampoline: trampoline, + Machoreloc1: machoreloc1, + Xcoffreloc1: xcoffreloc1, + + Linuxdynld: dynld, + LinuxdynldMusl: musl, + + 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..cb53a60 --- /dev/null +++ b/src/cmd/link/internal/riscv64/asm.go @@ -0,0 +1,361 @@ +// 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_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_RISCV_JAL) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + + 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) { + rs := r.Sym() + pc := ldr.SymValue(s) + int64(r.Off()) + + // If the call points to a trampoline, see if we can reach the symbol + // directly. This situation can occur when the relocation symbol is + // not assigned an address until after the trampolines are generated. + if r.Type() == objabi.R_RISCV_CALL_TRAMP { + relocs := ldr.Relocs(rs) + if relocs.Count() != 1 { + ldr.Errorf(s, "trampoline %v has %d relocations", ldr.SymName(rs), relocs.Count()) + } + tr := relocs.At(0) + if tr.Type() != objabi.R_RISCV_PCREL_ITYPE { + ldr.Errorf(s, "trampoline %v has unexpected relocation %v", ldr.SymName(rs), tr.Type()) + } + trs := tr.Sym() + if ldr.SymValue(trs) != 0 && ldr.SymType(trs) != sym.SDYNIMPORT && ldr.SymType(trs) != sym.SUNDEFEXT { + trsOff := ldr.SymValue(trs) + tr.Add() - pc + if trsOff >= -(1<<20) && trsOff < (1<<20) { + r.SetType(objabi.R_RISCV_CALL) + r.SetSym(trs) + r.SetAdd(tr.Add()) + rs = trs + } + } + + } + + if target.IsExternal() { + switch r.Type() { + case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP: + return val, 1, 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 + } + + off := ldr.SymValue(rs) + r.Add() - pc + + switch r.Type() { + case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP: + // Generate instruction immediates. + imm, err := riscv.EncodeJImmediate(off) + if err != nil { + ldr.Errorf(s, "cannot encode R_RISCV_CALL relocation offset for %s: %v", ldr.SymName(rs), err) + } + immMask := int64(riscv.JTypeImmMask) + + val = (val &^ immMask) | int64(imm) + + 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: + // 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, []byte) 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_CALL, objabi.R_RISCV_CALL_TRAMP: + return ld.ExtrelocSimple(ldr, r), 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 ld.ExtrelocViaOuterSym(ldr, r, s), true + } + return loader.ExtReloc{}, false +} + +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_RISCV_CALL: + pc := ldr.SymValue(s) + int64(r.Off()) + off := ldr.SymValue(rs) + r.Add() - pc + + // Relocation symbol has an address and is directly reachable, + // therefore there is no need for a trampoline. + if ldr.SymValue(rs) != 0 && off >= -(1<<20) && off < (1<<20) && (*ld.FlagDebugTramp <= 1 || ldr.SymPkg(s) == ldr.SymPkg(rs)) { + break + } + + // Relocation symbol is too far for a direct call or has not + // yet been given an address. See if an existing trampoline is + // reachable and if so, reuse it. Otherwise we need to create + // a new trampoline. + var tramp loader.Sym + for i := 0; ; i++ { + oName := ldr.SymName(rs) + name := fmt.Sprintf("%s-tramp%d", oName, i) + if r.Add() != 0 { + name = fmt.Sprintf("%s%+x-tramp%d", oName, r.Add(), i) + } + tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs))) + ldr.SetAttrReachable(tramp, true) + if ldr.SymType(tramp) == sym.SDYNIMPORT { + // Do not reuse trampoline defined in other module. + continue + } + if oName == "runtime.deferreturn" { + ldr.SetIsDeferReturnTramp(tramp, true) + } + if ldr.SymValue(tramp) == 0 { + // Either trampoline does not exist or we found one + // that does not have an address assigned and will be + // laid down immediately after the current function. + break + } + + trampOff := ldr.SymValue(tramp) - (ldr.SymValue(s) + int64(r.Off())) + if trampOff >= -(1<<20) && trampOff < (1<<20) { + // An existing trampoline that is reachable. + break + } + } + if ldr.SymType(tramp) == 0 { + trampb := ldr.MakeSymbolUpdater(tramp) + ctxt.AddTramp(trampb) + genCallTramp(ctxt.Arch, ctxt.LinkMode, ldr, trampb, rs, int64(r.Add())) + } + sb := ldr.MakeSymbolUpdater(s) + if ldr.SymValue(rs) == 0 { + // In this case the target symbol has not yet been assigned an + // address, so we have to assume a trampoline is required. Mark + // this as a call via a trampoline so that we can potentially + // switch to a direct call during relocation. + sb.SetRelocType(ri, objabi.R_RISCV_CALL_TRAMP) + } + relocs := sb.Relocs() + r := relocs.At(ri) + r.SetSym(tramp) + r.SetAdd(0) + + default: + ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type())) + } +} + +func genCallTramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { + tramp.AddUint32(arch, 0x00000f97) // AUIPC $0, X31 + tramp.AddUint32(arch, 0x000f8067) // JALR X0, (X31) + + r, _ := tramp.AddRel(objabi.R_RISCV_PCREL_ITYPE) + r.SetSiz(8) + r.SetSym(target) + r.SetAdd(offset) +} 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..8e4e41f --- /dev/null +++ b/src/cmd/link/internal/riscv64/obj.go @@ -0,0 +1,68 @@ +// 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, + + // TrampLimit is set such that we always run the trampoline + // generation code. This is necessary since calls to external + // symbols require the use of trampolines, regardless of the + // text size. + TrampLimit: 1, + Trampoline: trampoline, + + Gentext: gentext, + GenSymsLate: genSymsLate, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib/ld.so.1", + + Freebsddynld: "/usr/libexec/ld-elf.so.1", + Netbsddynld: "XXX", + Openbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + case objabi.Hlinux, objabi.Hfreebsd: + 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..2d9f750 --- /dev/null +++ b/src/cmd/link/internal/s390x/asm.go @@ -0,0 +1,449 @@ +// 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)) + } + 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()+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. + return targType != sym.SDYNIMPORT +} + +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, p []byte) 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..3aa8948 --- /dev/null +++ b/src/cmd/link/internal/s390x/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 © 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", + LinuxdynldMusl: "/lib/ld-musl-s390x.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..3bad5bf --- /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 { + 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 + VarDIEs []LoaderSym // Global variable DIEs + 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..a44dcdd --- /dev/null +++ b/src/cmd/link/internal/sym/reloc.go @@ -0,0 +1,76 @@ +// 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.Loong64: + return elf.R_LARCH(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..c889e71 --- /dev/null +++ b/src/cmd/link/internal/sym/segment.go @@ -0,0 +1,68 @@ +// 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 + + Compressed bool +} diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go new file mode 100644 index 0000000..2f2c839 --- /dev/null +++ b/src/cmd/link/internal/sym/symbol.go @@ -0,0 +1,42 @@ +// 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" + "internal/buildcfg" +) + +const ( + SymVerABI0 = 0 + SymVerABIInternal = 1 + SymVerABICount = 2 // Number of internal ABIs + 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: + if !buildcfg.Experiment.RegabiWrappers { + // If wrappers are not enabled, ABI0 and ABIInternal are actually same + // so we normalize everything to ABI0. + return SymVerABI0 + } + 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..2f8e8fe --- /dev/null +++ b/src/cmd/link/internal/sym/symkind.go @@ -0,0 +1,181 @@ +// 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 + +import "cmd/internal/objabi" + +// 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_8BIT_COUNTER + SCOVERAGE_COUNTER + SCOVERAGE_AUXVAR + 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 +) + +// AbiSymKindToSymKind maps values read from object files (which are +// of type cmd/internal/objabi.SymKind) to values of type SymKind. +var AbiSymKindToSymKind = [...]SymKind{ + objabi.Sxxx: Sxxx, + objabi.STEXT: STEXT, + objabi.SRODATA: SRODATA, + objabi.SNOPTRDATA: SNOPTRDATA, + objabi.SDATA: SDATA, + objabi.SBSS: SBSS, + objabi.SNOPTRBSS: SNOPTRBSS, + objabi.STLSBSS: STLSBSS, + objabi.SDWARFCUINFO: SDWARFCUINFO, + objabi.SDWARFCONST: SDWARFCONST, + objabi.SDWARFFCN: SDWARFFCN, + objabi.SDWARFABSFCN: SDWARFABSFCN, + objabi.SDWARFTYPE: SDWARFTYPE, + objabi.SDWARFVAR: SDWARFVAR, + objabi.SDWARFRANGE: SDWARFRANGE, + objabi.SDWARFLOC: SDWARFLOC, + objabi.SDWARFLINES: SDWARFLINES, + objabi.SLIBFUZZER_8BIT_COUNTER: SLIBFUZZER_8BIT_COUNTER, + objabi.SCOVERAGE_COUNTER: SCOVERAGE_COUNTER, + objabi.SCOVERAGE_AUXVAR: SCOVERAGE_AUXVAR, +} + +// 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..1cd7ab1 --- /dev/null +++ b/src/cmd/link/internal/sym/symkind_string.go @@ -0,0 +1,81 @@ +// Code generated by "stringer -type=SymKind symkind.go"; 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_8BIT_COUNTER-36] + _ = x[SCOVERAGE_COUNTER-37] + _ = x[SCOVERAGE_AUXVAR-38] + _ = x[STLSBSS-39] + _ = x[SXREF-40] + _ = x[SMACHOSYMSTR-41] + _ = x[SMACHOSYMTAB-42] + _ = x[SMACHOINDIRECTPLT-43] + _ = x[SMACHOINDIRECTGOT-44] + _ = x[SFILEPATH-45] + _ = x[SDYNIMPORT-46] + _ = x[SHOSTOBJ-47] + _ = x[SUNDEFEXT-48] + _ = x[SDWARFSECT-49] + _ = x[SDWARFCUINFO-50] + _ = x[SDWARFCONST-51] + _ = x[SDWARFFCN-52] + _ = x[SDWARFABSFCN-53] + _ = x[SDWARFTYPE-54] + _ = x[SDWARFVAR-55] + _ = x[SDWARFRANGE-56] + _ = x[SDWARFLOC-57] + _ = x[SDWARFLINES-58] +} + +const _SymKind_name = "SxxxSTEXTSELFRXSECTSMACHOPLTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINES" + +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, 336, 353, 369, 376, 381, 393, 405, 422, 439, 448, 458, 466, 475, 485, 497, 508, 517, 529, 539, 548, 559, 568, 579} + +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..99018c8 --- /dev/null +++ b/src/cmd/link/internal/wasm/asm.go @@ -0,0 +1,604 @@ +// 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" + "internal/buildcfg" + "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.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 := 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) +} + +// writeCodeSec 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, buildcfg.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, buildcfg.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..3a33201 --- /dev/null +++ b/src/cmd/link/internal/x86/asm.go @@ -0,0 +1,490 @@ +// 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)) + } + 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()+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, []byte) 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..b0a129e --- /dev/null +++ b/src/cmd/link/internal/x86/obj.go @@ -0,0 +1,117 @@ +// 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", + LinuxdynldMusl: "/lib/ld-musl-i386.so.1", + 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..a770c91 --- /dev/null +++ b/src/cmd/link/link_test.go @@ -0,0 +1,1152 @@ +// 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" + "debug/macho" + "internal/buildcfg" + "internal/platform" + "internal/testenv" + "os" + "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 := t.TempDir() + + importcfgfile := filepath.Join(tmpdir, "importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + + err := os.WriteFile(filepath.Join(tmpdir, "main.go"), []byte(source), 0666) + if err != nil { + t.Fatalf("failed to write main.go: %v\n", err) + } + + cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "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 = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "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 := t.TempDir() + + write := func(name, content string) { + err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666) + if err != nil { + t.Fatal(err) + } + } + + runGo := func(args ...string) { + cmd := testenv.Command(t, 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) + } + } + + importcfgfile := filepath.Join(tmpdir, "importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + + // Compile a main package. + write("main.go", "package main; func main() {}") + runGo("tool", "compile", "-importcfg="+importcfgfile, "-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", "-importcfg="+importcfgfile, "main.a") +} + +func TestUnresolved(t *testing.T) { + testenv.MustHaveGoBuild(t) + + t.Parallel() + + tmpdir := t.TempDir() + + write := func(name, content string) { + err := os.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 := testenv.Command(t, 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 "loong64": + t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH) + case "mips", "mipsle", "mips64", "mips64le": + t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH) + } + if runtime.GOOS == "aix" || + runtime.GOOS == "windows" && runtime.GOARCH == "arm64" { + t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH) + } + + t.Parallel() + + tmpdir := t.TempDir() + + write := func(name, content string) { + err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666) + if err != nil { + t.Fatal(err) + } + } + + run := func(name string, args ...string) string { + cmd := testenv.Command(t, 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")) + + importcfgfile := filepath.Join(tmpdir, "importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + + // Compile, assemble and pack the Go and C code. + runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s") + runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go") + runGo("tool", "asm", "-p=main", "-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 := testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-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 := testenv.Command(t, "xcrun", "--help").Run(); err != nil { + t.Skipf("error running xcrun, required for iOS cross build: %v", err) + } + + t.Parallel() + + sdkPath, err := testenv.Command(t, "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", + } + CGO_LDFLAGS := []string{"-framework", "CoreFoundation"} + lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go") + tmpDir := t.TempDir() + + ar := filepath.Join(tmpDir, "lib.a") + cmd := testenv.Command(t, 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 + "CGO_LDFLAGS="+strings.Join(CGO_LDFLAGS, " "), + ) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("%v: %v:\n%s", cmd.Args, err, out) + } + + link := testenv.Command(t, CC[0], CC[1:]...) + link.Args = append(link.Args, CGO_LDFLAGS...) + 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 := t.TempDir() + + src := filepath.Join(tmpdir, "main.go") + err := os.WriteFile(src, []byte(testXFlagSrc), 0666) + if err != nil { + t.Fatal(err) + } + + cmd := testenv.Command(t, 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 testMachOBuildVersionSrc = ` +package main +func main() { } +` + +func TestMachOBuildVersion(t *testing.T) { + testenv.MustHaveGoBuild(t) + + t.Parallel() + + tmpdir := t.TempDir() + + src := filepath.Join(tmpdir, "main.go") + err := os.WriteFile(src, []byte(testMachOBuildVersionSrc), 0666) + if err != nil { + t.Fatal(err) + } + + exe := filepath.Join(tmpdir, "main") + cmd := testenv.Command(t, 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) + } + defer exef.Close() + exem, err := macho.NewFile(exef) + if err != nil { + t.Fatal(err) + } + found := false + const LC_BUILD_VERSION = 0x32 + checkMin := func(ver uint32) { + major, minor := (ver>>16)&0xff, (ver>>8)&0xff + if major != 10 || minor < 9 { + t.Errorf("LC_BUILD_VERSION version %d.%d < 10.9", major, minor) + } + } + for _, cmd := range exem.Loads { + raw := cmd.Raw() + type_ := exem.ByteOrder.Uint32(raw) + if type_ != LC_BUILD_VERSION { + continue + } + osVer := exem.ByteOrder.Uint32(raw[12:]) + checkMin(osVer) + sdkVer := exem.ByteOrder.Uint32(raw[16:]) + checkMin(sdkVer) + found = true + break + } + if !found { + t.Errorf("no LC_BUILD_VERSION 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 := t.TempDir() + + src := filepath.Join(tmpdir, "blah.go") + err := os.WriteFile(src, []byte(Issue34788src), 0666) + if err != nil { + t.Fatal(err) + } + + obj := filepath.Join(tmpdir, "blah.o") + cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-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 = testenv.Command(t, 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) +` + +const testStrictDupAsmSrc3 = ` +#include "textflag.h" +GLOBL ·rcon(SB), RODATA|DUPOK, $64 +` + +const testStrictDupAsmSrc4 = ` +#include "textflag.h" +GLOBL ·rcon(SB), RODATA|DUPOK, $32 +` + +func TestStrictDup(t *testing.T) { + // Check that -strictdups flag works. + testenv.MustHaveGoBuild(t) + + asmfiles := []struct { + fname string + payload string + }{ + {"a", testStrictDupAsmSrc1}, + {"b", testStrictDupAsmSrc2}, + {"c", testStrictDupAsmSrc3}, + {"d", testStrictDupAsmSrc4}, + } + + t.Parallel() + + tmpdir := t.TempDir() + + src := filepath.Join(tmpdir, "x.go") + err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666) + if err != nil { + t.Fatal(err) + } + for _, af := range asmfiles { + src = filepath.Join(tmpdir, af.fname+".s") + err = os.WriteFile(src, []byte(af.payload), 0666) + if err != nil { + t.Fatal(err) + } + } + src = filepath.Join(tmpdir, "go.mod") + err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666) + if err != nil { + t.Fatal(err) + } + + cmd := testenv.Command(t, 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\n%s", err, string(out)) + } + if !bytes.Contains(out, []byte("mismatched payload")) { + t.Errorf("unexpected output:\n%s", out) + } + + cmd = testenv.Command(t, 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") + } + // NB: on amd64 we get the 'new length' error, on arm64 the 'different + // contents' error. + if !(bytes.Contains(out, []byte("mismatched payload: new length")) || + bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) || + !bytes.Contains(out, []byte("mismatched payload: different sizes")) { + t.Errorf("unexpected output:\n%s", out) + } +} + +const testFuncAlignSrc = ` +package main +import ( + "fmt" +) +func alignPc() +var alignPcFnAddr uintptr + +func main() { + if alignPcFnAddr % 512 != 0 { + fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr) + } else { + fmt.Printf("PASS") + } +} +` + +const testFuncAlignAsmSrc = ` +#include "textflag.h" + +TEXT ·alignPc(SB),NOSPLIT, $0-0 + MOVD $2, R0 + PCALIGN $512 + MOVD $3, R1 + RET + +GLOBL ·alignPcFnAddr(SB),RODATA,$8 +DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB) +` + +// 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 := t.TempDir() + + src := filepath.Join(tmpdir, "go.mod") + err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666) + if err != nil { + t.Fatal(err) + } + src = filepath.Join(tmpdir, "falign.go") + err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666) + if err != nil { + t.Fatal(err) + } + src = filepath.Join(tmpdir, "falign.s") + err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666) + if err != nil { + t.Fatal(err) + } + + // Build and run with old object file format. + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "falign") + cmd.Dir = tmpdir + out, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("build failed: %v", err) + } + cmd = testenv.Command(t, 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. + buildmodes := []string{"default"} + switch runtime.GOARCH { + case "arm", "arm64", "ppc64": + case "ppc64le": + // Trampolines are generated differently when internal linking PIE, test them too. + buildmodes = append(buildmodes, "pie") + default: + t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH) + } + + testenv.MustHaveGoBuild(t) + + t.Parallel() + + tmpdir := t.TempDir() + + src := filepath.Join(tmpdir, "hello.go") + err := os.WriteFile(src, []byte(testTrampSrc), 0666) + if err != nil { + t.Fatal(err) + } + exe := filepath.Join(tmpdir, "hello.exe") + + for _, mode := range buildmodes { + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) + } + cmd = testenv.Command(t, exe) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) + } + if string(out) != "hello\n" { + t.Errorf("unexpected output (%s):\n%s", mode, out) + } + } +} + +const testTrampCgoSrc = ` +package main + +// #include <stdio.h> +// void CHello() { printf("hello\n"); fflush(stdout); } +import "C" + +func main() { + C.CHello() +} +` + +func TestTrampolineCgo(t *testing.T) { + // Test that trampoline insertion works for cgo code. + // 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. + buildmodes := []string{"default"} + switch runtime.GOARCH { + case "arm", "arm64", "ppc64": + case "ppc64le": + // Trampolines are generated differently when internal linking PIE, test them too. + buildmodes = append(buildmodes, "pie") + default: + t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH) + } + + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + t.Parallel() + + tmpdir := t.TempDir() + + src := filepath.Join(tmpdir, "hello.go") + err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666) + if err != nil { + t.Fatal(err) + } + exe := filepath.Join(tmpdir, "hello.exe") + + for _, mode := range buildmodes { + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) + } + cmd = testenv.Command(t, exe) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) + } + if string(out) != "hello\n" && string(out) != "hello\r\n" { + t.Errorf("unexpected output (%s):\n%s", mode, out) + } + + // Test internal linking mode. + + if runtime.GOARCH == "ppc64" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() { + return // internal linking cgo is not supported + } + cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) + } + cmd = testenv.Command(t, exe) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) + } + if string(out) != "hello\n" && string(out) != "hello\r\n" { + t.Errorf("unexpected output (%s):\n%s", mode, 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 := t.TempDir() + + 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") + + importcfgFile := filepath.Join(tmpdir, "stdlib.importcfg") + testenv.WriteImportcfg(t, importcfgFile, nil) + importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg") + testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}) + + // Build a program with main package importing package a. + cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-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 = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-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 = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-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 = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-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 = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-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 TestPErsrcBinutils(t *testing.T) { + // Test that PE rsrc section is handled correctly (issue 39658). + testenv.MustHaveGoBuild(t) + + if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" { + // This test is limited to amd64 and 386, because binutils is limited as such + t.Skipf("this is only for windows/amd64 and windows/386") + } + + t.Parallel() + + tmpdir := t.TempDir() + + pkgdir := filepath.Join("testdata", "pe-binutils") + exe := filepath.Join(tmpdir, "a.exe") + cmd := testenv.Command(t, 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 := os.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") + } +} + +func TestPErsrcLLVM(t *testing.T) { + // Test that PE rsrc section is handled correctly (issue 39658). + testenv.MustHaveGoBuild(t) + + if runtime.GOOS != "windows" { + t.Skipf("this is a windows-only test") + } + + t.Parallel() + + tmpdir := t.TempDir() + + pkgdir := filepath.Join("testdata", "pe-llvm") + exe := filepath.Join(tmpdir, "a.exe") + cmd := testenv.Command(t, 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 := os.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() + + src := filepath.Join("testdata", "testHashedSyms", "p.go") + cmd := testenv.Command(t, 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 := testenv.Command(t, 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 := t.TempDir() + + src := filepath.Join(tmpdir, "x.go") + err := os.WriteFile(src, []byte(testIssue38554Src), 0666) + if err != nil { + t.Fatalf("failed to write source file: %v", err) + } + exe := filepath.Join(tmpdir, "x.exe") + cmd := testenv.Command(t, 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 !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) { + t.Skip("no race detector support") + } + + t.Parallel() + + tmpdir := t.TempDir() + + src := filepath.Join(tmpdir, "main.go") + err := os.WriteFile(src, []byte(testIssue42396src), 0666) + if err != nil { + t.Fatalf("failed to write source file: %v", err) + } + exe := filepath.Join(tmpdir, "main.exe") + cmd := testenv.Command(t, 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} + +var addr = [...]*byte{ + &x[1<<23-1], + &x[1<<23], + &x[1<<23+1], + &x[1<<24-1], + &x[1<<24], + &x[1<<24+1], +} + +func main() { + // check relocations in instructions + 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) + + // check absolute address relocations in data + check(*addr[0], 0) + check(*addr[1], 23) + check(*addr[2], 0) + check(*addr[3], 0) + check(*addr[4], 24) + check(*addr[5], 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 := t.TempDir() + + src := filepath.Join(tmpdir, "x.go") + err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666) + if err != nil { + t.Fatalf("failed to write source file: %v", err) + } + cmd := testenv.Command(t, 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 = testenv.Command(t, 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) + } + } +} + +func TestUnlinkableObj(t *testing.T) { + // Test that the linker emits an error with unlinkable object. + testenv.MustHaveGoBuild(t) + t.Parallel() + + if buildcfg.Experiment.Unified { + t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified") + } + + tmpdir := t.TempDir() + + xSrc := filepath.Join(tmpdir, "x.go") + pSrc := filepath.Join(tmpdir, "p.go") + xObj := filepath.Join(tmpdir, "x.o") + pObj := filepath.Join(tmpdir, "p.o") + exe := filepath.Join(tmpdir, "x.exe") + importcfgfile := filepath.Join(tmpdir, "importcfg") + testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj}) + err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666) + if err != nil { + t.Fatalf("failed to write source file: %v", err) + } + err = os.WriteFile(pSrc, []byte("package p\n"), 0666) + if err != nil { + t.Fatalf("failed to write source file: %v", err) + } + cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc) // without -p + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("compile p.go failed: %v. output:\n%s", err, out) + } + cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("compile x.go failed: %v. output:\n%s", err, out) + } + cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj) + out, err = cmd.CombinedOutput() + if err == nil { + t.Fatalf("link did not fail") + } + if !bytes.Contains(out, []byte("unlinkable object")) { + t.Errorf("did not see expected error message. out:\n%s", out) + } + + // It is okay to omit -p for (only) main package. + cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("compile p.go failed: %v. output:\n%s", err, out) + } + cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc) // without -p + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("compile failed: %v. output:\n%s", err, out) + } + + cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("link 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..45cb1b3 --- /dev/null +++ b/src/cmd/link/linkbig_test.go @@ -0,0 +1,111 @@ +// 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" + "fmt" + "internal/buildcfg" + "internal/testenv" + "os" + "testing" +) + +func TestLargeText(t *testing.T) { + if testing.Short() || (buildcfg.GOARCH != "ppc64le" && buildcfg.GOARCH != "ppc64" && buildcfg.GOARCH != "arm") { + t.Skipf("Skipping large text section test in short mode or on %s", buildcfg.GOARCH) + } + testenv.MustHaveGoBuild(t) + + var w bytes.Buffer + const FN = 4 + tmpdir := t.TempDir() + + if err := os.WriteFile(tmpdir+"/go.mod", []byte("module big_test\n"), 0666); err != nil { + t.Fatal(err) + } + + // 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[buildcfg.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 := os.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 := os.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. + cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "bigtext") + cmd.Dir = tmpdir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Build failed for big text program with internal linking: %v, output: %s", err, out) + } + cmd = testenv.Command(t, "./bigtext") + cmd.Dir = tmpdir + 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 + cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "bigtext", "-ldflags", "-linkmode=external") + cmd.Dir = tmpdir + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("Build failed for big text program with external linking: %v, output: %s", err, out) + } + cmd = testenv.Command(t, "./bigtext") + cmd.Dir = tmpdir + 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..16e5a01 --- /dev/null +++ b/src/cmd/link/main.go @@ -0,0 +1,73 @@ +// 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/sys" + "cmd/link/internal/amd64" + "cmd/link/internal/arm" + "cmd/link/internal/arm64" + "cmd/link/internal/ld" + "cmd/link/internal/loong64" + "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" + "internal/buildcfg" + "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 + + buildcfg.Check() + switch buildcfg.GOARCH { + default: + fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", buildcfg.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 "loong64": + arch, theArch = loong64.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/pe-binutils/main.go b/src/cmd/link/testdata/pe-binutils/main.go new file mode 100644 index 0000000..14ea6f9 --- /dev/null +++ b/src/cmd/link/testdata/pe-binutils/main.go @@ -0,0 +1,18 @@ +// 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 using binutils with: +// {x86_64,i686}-w64-mingw32-windres -i a.rc -o rsrc_$GOARCH.syso -O coff +// 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/pe-binutils/rsrc_386.syso b/src/cmd/link/testdata/pe-binutils/rsrc_386.syso Binary files differnew file mode 100644 index 0000000..b4abc58 --- /dev/null +++ b/src/cmd/link/testdata/pe-binutils/rsrc_386.syso diff --git a/src/cmd/link/testdata/pe-binutils/rsrc_amd64.syso b/src/cmd/link/testdata/pe-binutils/rsrc_amd64.syso Binary files differnew file mode 100644 index 0000000..0d9699d --- /dev/null +++ b/src/cmd/link/testdata/pe-binutils/rsrc_amd64.syso diff --git a/src/cmd/link/testdata/pe-llvm/main.go b/src/cmd/link/testdata/pe-llvm/main.go new file mode 100644 index 0000000..099a71a --- /dev/null +++ b/src/cmd/link/testdata/pe-llvm/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 using llvm with: +// {i686,x86_64,armv7,arm64}-w64-mingw32-windres -i a.rc -o rsrc_$GOARCH.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/pe-llvm/rsrc_386.syso b/src/cmd/link/testdata/pe-llvm/rsrc_386.syso Binary files differnew file mode 100644 index 0000000..21126c9 --- /dev/null +++ b/src/cmd/link/testdata/pe-llvm/rsrc_386.syso diff --git a/src/cmd/link/testdata/pe-llvm/rsrc_amd64.syso b/src/cmd/link/testdata/pe-llvm/rsrc_amd64.syso Binary files differnew file mode 100644 index 0000000..56f9260 --- /dev/null +++ b/src/cmd/link/testdata/pe-llvm/rsrc_amd64.syso diff --git a/src/cmd/link/testdata/pe-llvm/rsrc_arm.syso b/src/cmd/link/testdata/pe-llvm/rsrc_arm.syso Binary files differnew file mode 100644 index 0000000..c93a1e9 --- /dev/null +++ b/src/cmd/link/testdata/pe-llvm/rsrc_arm.syso diff --git a/src/cmd/link/testdata/pe-llvm/rsrc_arm64.syso b/src/cmd/link/testdata/pe-llvm/rsrc_arm64.syso Binary files differnew file mode 100644 index 0000000..7849638 --- /dev/null +++ b/src/cmd/link/testdata/pe-llvm/rsrc_arm64.syso 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/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) +} |