]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd/oldlink: add back the old linker
authorCherry Zhang <cherryyz@google.com>
Fri, 20 Mar 2020 15:54:24 +0000 (11:54 -0400)
committerCherry Zhang <cherryyz@google.com>
Mon, 23 Mar 2020 14:38:38 +0000 (14:38 +0000)
Basically a "cp -r" from tip's cmd/link (at commit
776a9d0958973bff6a66e61baa6a9a240ada4255), and fix up import
paths.

It is not used for now. A flag will be introduced in followup
CLs, which will allow to switch between the old and new linker.

Some adjustments to make it still build:
- Adjust accessor of NoSplit attribute to match the change in
  goobj2 package (cmd/oldlink/internal/loader/loader.go:1206).
- Change sym.Symbol's Len method to Length to match the new
  interface in cmd/internal/dwarf
  (cmd/oldlink/internal/sym/symbol.go:102).
- Add a TODO for deletion (cmd/oldlink/main.go:5).

Change-Id: I76e62f259cb6b494280f5511f75913bc37874746
Reviewed-on: https://go-review.googlesource.com/c/go/+/224622
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
96 files changed:
src/cmd/oldlink/doc.go [new file with mode: 0644]
src/cmd/oldlink/dwarf_test.go [new file with mode: 0644]
src/cmd/oldlink/elf_test.go [new file with mode: 0644]
src/cmd/oldlink/internal/amd64/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/amd64/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/amd64/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/arm/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/arm/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/arm/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/arm64/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/arm64/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/arm64/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/ar.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/config.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/data.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/deadcode.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/deadcode2.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/decodesym.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/dwarf.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/dwarf_test.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/elf.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/elf_test.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/execarchive.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/execarchive_noexec.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/go.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/issue33808_test.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/ld.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/ld_test.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/lib.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/link.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/macho.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/macho_combine_dwarf.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/main.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/nooptcgolink_test.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/outbuf.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/outbuf_mmap.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/outbuf_nommap.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/outbuf_windows.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/pcln.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/pe.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/sym.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/symtab.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/httptest/main/main.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue10978/main.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue10978/main.s [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue26237/b.dir/b.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue32233/lib/ObjC.m [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/typelink.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/util.go [new file with mode: 0644]
src/cmd/oldlink/internal/ld/xcoff.go [new file with mode: 0644]
src/cmd/oldlink/internal/loadelf/ldelf.go [new file with mode: 0644]
src/cmd/oldlink/internal/loader/loader.go [new file with mode: 0644]
src/cmd/oldlink/internal/loadmacho/ldmacho.go [new file with mode: 0644]
src/cmd/oldlink/internal/loadpe/ldpe.go [new file with mode: 0644]
src/cmd/oldlink/internal/loadxcoff/ldxcoff.go [new file with mode: 0644]
src/cmd/oldlink/internal/mips/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/mips/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/mips/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/mips64/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/mips64/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/mips64/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/objfile/objfile.go [new file with mode: 0644]
src/cmd/oldlink/internal/ppc64/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/ppc64/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/ppc64/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/riscv64/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/riscv64/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/riscv64/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/s390x/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/s390x/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/s390x/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/attribute.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/compilation_unit.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/library.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/reloc.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/segment.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/sizeof_test.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/symbol.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/symbols.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/symkind.go [new file with mode: 0644]
src/cmd/oldlink/internal/sym/symkind_string.go [new file with mode: 0644]
src/cmd/oldlink/internal/wasm/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/wasm/obj.go [new file with mode: 0644]
src/cmd/oldlink/internal/x86/asm.go [new file with mode: 0644]
src/cmd/oldlink/internal/x86/l.go [new file with mode: 0644]
src/cmd/oldlink/internal/x86/obj.go [new file with mode: 0644]
src/cmd/oldlink/link_test.go [new file with mode: 0644]
src/cmd/oldlink/linkbig_test.go [new file with mode: 0644]
src/cmd/oldlink/main.go [new file with mode: 0644]
src/cmd/oldlink/testdata/lib.go [new file with mode: 0644]
src/cmd/oldlink/testdata/main.m [new file with mode: 0644]

diff --git a/src/cmd/oldlink/doc.go b/src/cmd/oldlink/doc.go
new file mode 100644 (file)
index 0000000..219499b
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Link, typically invoked as ``go tool link,'' reads the Go archive or object
+for a package main, along with its dependencies, and combines them
+into an executable binary.
+
+Command Line
+
+Usage:
+
+       go tool link [flags] main.a
+
+Flags:
+
+       -B note
+               Add an ELF_NT_GNU_BUILD_ID note when using ELF.
+               The value should start with 0x and be an even number of hex digits.
+       -D address
+               Set data segment address.
+       -E entry
+               Set entry symbol name.
+       -H type
+               Set executable format type.
+               The default format is inferred from GOOS and GOARCH.
+               On Windows, -H windowsgui writes a "GUI binary" instead of a "console binary."
+       -I interpreter
+               Set the ELF dynamic linker to use.
+       -L dir1 -L dir2
+               Search for imported packages in dir1, dir2, etc,
+               after consulting $GOROOT/pkg/$GOOS_$GOARCH.
+       -R quantum
+               Set address rounding quantum.
+       -T address
+               Set text segment address.
+       -V
+               Print linker version and exit.
+       -X importpath.name=value
+               Set the value of the string variable in importpath named name to value.
+               This is only effective if the variable is declared in the source code either uninitialized
+               or initialized to a constant string expression. -X will not work if the initializer makes
+               a function call or refers to other variables.
+               Note that before Go 1.5 this option took two separate arguments.
+       -a
+               Disassemble output.
+       -buildid id
+               Record id as Go toolchain build id.
+       -buildmode mode
+               Set build mode (default exe).
+       -c
+               Dump call graphs.
+       -compressdwarf
+               Compress DWARF if possible (default true).
+       -cpuprofile file
+               Write CPU profile to file.
+       -d
+               Disable generation of dynamic executables.
+               The emitted code is the same in either case; the option
+               controls only whether a dynamic header is included.
+               The dynamic header is on by default, even without any
+               references to dynamic libraries, because many common
+               system tools now assume the presence of the header.
+       -debugtramp int
+               Debug trampolines.
+       -dumpdep
+               Dump symbol dependency graph.
+       -extar ar
+               Set the external archive program (default "ar").
+               Used only for -buildmode=c-archive.
+       -extld linker
+               Set the external linker (default "clang" or "gcc").
+       -extldflags flags
+               Set space-separated flags to pass to the external linker.
+       -f
+               Ignore version mismatch in the linked archives.
+       -g
+               Disable Go package data checks.
+       -importcfg file
+               Read import configuration from file.
+               In the file, set packagefile, packageshlib to specify import resolution.
+       -installsuffix suffix
+               Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix
+               instead of $GOROOT/pkg/$GOOS_$GOARCH.
+       -k symbol
+               Set field tracking symbol. Use this flag when GOEXPERIMENT=fieldtrack is set.
+       -libgcc file
+               Set name of compiler support library.
+               This is only used in internal link mode.
+               If not set, default value comes from running the compiler,
+               which may be set by the -extld option.
+               Set to "none" to use no support library.
+       -linkmode mode
+               Set link mode (internal, external, auto).
+               This sets the linking mode as described in cmd/cgo/doc.go.
+       -linkshared
+               Link against installed Go shared libraries (experimental).
+       -memprofile file
+               Write memory profile to file.
+       -memprofilerate rate
+               Set runtime.MemProfileRate to rate.
+       -msan
+               Link with C/C++ memory sanitizer support.
+       -n
+               Dump symbol table.
+       -o file
+               Write output to file (default a.out, or a.out.exe on Windows).
+       -pluginpath path
+               The path name used to prefix exported plugin symbols.
+       -r dir1:dir2:...
+               Set the ELF dynamic linker search path.
+       -race
+               Link with race detection libraries.
+       -s
+               Omit the symbol table and debug information.
+       -shared
+               Generated shared object (implies -linkmode external; experimental).
+       -tmpdir dir
+               Write temporary files to dir.
+               Temporary files are only used in external linking mode.
+       -u
+               Reject unsafe packages.
+       -v
+               Print trace of linker operations.
+       -w
+               Omit the DWARF symbol table.
+*/
+package main
diff --git a/src/cmd/oldlink/dwarf_test.go b/src/cmd/oldlink/dwarf_test.go
new file mode 100644 (file)
index 0000000..d4bb303
--- /dev/null
@@ -0,0 +1,201 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+       "bytes"
+       cmddwarf "cmd/internal/dwarf"
+       "cmd/internal/objfile"
+       "debug/dwarf"
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path"
+       "path/filepath"
+       "runtime"
+       "strings"
+       "testing"
+)
+
+func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string) {
+       testenv.MustHaveCGO(t)
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       out, err := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "cmd/link").CombinedOutput()
+       if err != nil {
+               t.Fatalf("go list: %v\n%s", err, out)
+       }
+       if string(out) != "false\n" {
+               if os.Getenv("GOROOT_FINAL_OLD") != "" {
+                       t.Skip("cmd/link is stale, but $GOROOT_FINAL_OLD is set")
+               }
+               t.Fatalf("cmd/link is stale - run go install cmd/link")
+       }
+
+       for _, prog := range []string{"testprog", "testprogcgo"} {
+               prog := prog
+               expectDWARF := expectDWARF
+               if runtime.GOOS == "aix" && prog == "testprogcgo" {
+                       extld := os.Getenv("CC")
+                       if extld == "" {
+                               extld = "gcc"
+                       }
+                       expectDWARF, err = cmddwarf.IsDWARFEnabledOnAIXLd(extld)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+
+               }
+
+               t.Run(prog, func(t *testing.T) {
+                       t.Parallel()
+
+                       tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF")
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       defer os.RemoveAll(tmpDir)
+
+                       exe := filepath.Join(tmpDir, prog+".exe")
+                       dir := "../../runtime/testdata/" + prog
+                       cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
+                       if buildmode != "" {
+                               cmd.Args = append(cmd.Args, "-buildmode", buildmode)
+                       }
+                       cmd.Args = append(cmd.Args, dir)
+                       if env != nil {
+                               env = append(env, "CGO_CFLAGS=") // ensure CGO_CFLAGS does not contain any flags. Issue #35459
+                               cmd.Env = append(os.Environ(), env...)
+                       }
+                       out, err := cmd.CombinedOutput()
+                       if err != nil {
+                               t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
+                       }
+
+                       if buildmode == "c-archive" {
+                               // Extract the archive and use the go.o object within.
+                               cmd := exec.Command("ar", "-x", exe)
+                               cmd.Dir = tmpDir
+                               if out, err := cmd.CombinedOutput(); err != nil {
+                                       t.Fatalf("ar -x %s: %v\n%s", exe, err, out)
+                               }
+                               exe = filepath.Join(tmpDir, "go.o")
+                       }
+
+                       if runtime.GOOS == "darwin" {
+                               if _, err = exec.LookPath("symbols"); err == nil {
+                                       // Ensure Apple's tooling can parse our object for symbols.
+                                       out, err = exec.Command("symbols", exe).CombinedOutput()
+                                       if err != nil {
+                                               t.Fatalf("symbols %v: %v: %s", filepath.Base(exe), err, out)
+                                       } else {
+                                               if bytes.HasPrefix(out, []byte("Unable to find file")) {
+                                                       // This failure will cause the App Store to reject our binaries.
+                                                       t.Fatalf("symbols %v: failed to parse file", filepath.Base(exe))
+                                               } else if bytes.Contains(out, []byte(", Empty]")) {
+                                                       t.Fatalf("symbols %v: parsed as empty", filepath.Base(exe))
+                                               }
+                                       }
+                               }
+                       }
+
+                       f, err := objfile.Open(exe)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       defer f.Close()
+
+                       syms, err := f.Symbols()
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+
+                       var addr uint64
+                       for _, sym := range syms {
+                               if sym.Name == "main.main" {
+                                       addr = sym.Addr
+                                       break
+                               }
+                       }
+                       if addr == 0 {
+                               t.Fatal("cannot find main.main in symbols")
+                       }
+
+                       d, err := f.DWARF()
+                       if err != nil {
+                               if expectDWARF {
+                                       t.Fatal(err)
+                               }
+                               return
+                       } else {
+                               if !expectDWARF {
+                                       t.Fatal("unexpected DWARF section")
+                               }
+                       }
+
+                       // TODO: We'd like to use filepath.Join here.
+                       // Also related: golang.org/issue/19784.
+                       wantFile := path.Join(prog, "main.go")
+                       wantLine := 24
+                       r := d.Reader()
+                       entry, err := r.SeekPC(addr)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       lr, err := d.LineReader(entry)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       var line dwarf.LineEntry
+                       if err := lr.SeekPC(addr, &line); err == dwarf.ErrUnknownPC {
+                               t.Fatalf("did not find file:line for %#x (main.main)", addr)
+                       } else if err != nil {
+                               t.Fatal(err)
+                       }
+                       if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
+                               t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
+                       }
+               })
+       }
+}
+
+func TestDWARF(t *testing.T) {
+       testDWARF(t, "", true)
+       if !testing.Short() {
+               if runtime.GOOS == "windows" {
+                       t.Skip("skipping Windows/c-archive; see Issue 35512 for more.")
+               }
+               t.Run("c-archive", func(t *testing.T) {
+                       testDWARF(t, "c-archive", true)
+               })
+       }
+}
+
+func TestDWARFiOS(t *testing.T) {
+       // Normally we run TestDWARF on native platform. But on iOS we don't have
+       // go build, so we do this test with a cross build.
+       // Only run this on darwin/amd64, where we can cross build for iOS.
+       if testing.Short() {
+               t.Skip("skipping in short mode")
+       }
+       if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
+               t.Skip("skipping on non-darwin/amd64 platform")
+       }
+       if err := exec.Command("xcrun", "--help").Run(); err != nil {
+               t.Skipf("error running xcrun, required for iOS cross build: %v", err)
+       }
+       cc := "CC=" + runtime.GOROOT() + "/misc/ios/clangwrap.sh"
+       // iOS doesn't allow unmapped segments, so iOS executables don't have DWARF.
+       testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
+       testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
+       // However, c-archive iOS objects have embedded DWARF.
+       testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
+       testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
+}
diff --git a/src/cmd/oldlink/elf_test.go b/src/cmd/oldlink/elf_test.go
new file mode 100644 (file)
index 0000000..2fb4dd8
--- /dev/null
@@ -0,0 +1,416 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build dragonfly freebsd linux netbsd openbsd
+
+package main
+
+import (
+       "cmd/internal/sys"
+       "debug/elf"
+       "fmt"
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "runtime"
+       "strings"
+       "sync"
+       "testing"
+       "text/template"
+)
+
+func getCCAndCCFLAGS(t *testing.T, env []string) (string, []string) {
+       goTool := testenv.GoToolPath(t)
+       cmd := exec.Command(goTool, "env", "CC")
+       cmd.Env = env
+       ccb, err := cmd.Output()
+       if err != nil {
+               t.Fatal(err)
+       }
+       cc := strings.TrimSpace(string(ccb))
+
+       cmd = exec.Command(goTool, "env", "GOGCCFLAGS")
+       cmd.Env = env
+       cflagsb, err := cmd.Output()
+       if err != nil {
+               t.Fatal(err)
+       }
+       cflags := strings.Fields(string(cflagsb))
+
+       return cc, cflags
+}
+
+var asmSource = `
+       .section .text1,"ax"
+s1:
+       .byte 0
+       .section .text2,"ax"
+s2:
+       .byte 0
+`
+
+var goSource = `
+package main
+func main() {}
+`
+
+// The linker used to crash if an ELF input file had multiple text sections
+// with the same name.
+func TestSectionsWithSameName(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       testenv.MustHaveCGO(t)
+       t.Parallel()
+
+       objcopy, err := exec.LookPath("objcopy")
+       if err != nil {
+               t.Skipf("can't find objcopy: %v", err)
+       }
+
+       dir, err := ioutil.TempDir("", "go-link-TestSectionsWithSameName")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       gopath := filepath.Join(dir, "GOPATH")
+       env := append(os.Environ(), "GOPATH="+gopath)
+
+       if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
+               t.Fatal(err)
+       }
+
+       asmFile := filepath.Join(dir, "x.s")
+       if err := ioutil.WriteFile(asmFile, []byte(asmSource), 0444); err != nil {
+               t.Fatal(err)
+       }
+
+       goTool := testenv.GoToolPath(t)
+       cc, cflags := getCCAndCCFLAGS(t, env)
+
+       asmObj := filepath.Join(dir, "x.o")
+       t.Logf("%s %v -c -o %s %s", cc, cflags, asmObj, asmFile)
+       if out, err := exec.Command(cc, append(cflags, "-c", "-o", asmObj, asmFile)...).CombinedOutput(); err != nil {
+               t.Logf("%s", out)
+               t.Fatal(err)
+       }
+
+       asm2Obj := filepath.Join(dir, "x2.syso")
+       t.Logf("%s --rename-section .text2=.text1 %s %s", objcopy, asmObj, asm2Obj)
+       if out, err := exec.Command(objcopy, "--rename-section", ".text2=.text1", asmObj, asm2Obj).CombinedOutput(); err != nil {
+               t.Logf("%s", out)
+               t.Fatal(err)
+       }
+
+       for _, s := range []string{asmFile, asmObj} {
+               if err := os.Remove(s); err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       goFile := filepath.Join(dir, "main.go")
+       if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
+               t.Fatal(err)
+       }
+
+       cmd := exec.Command(goTool, "build")
+       cmd.Dir = dir
+       cmd.Env = env
+       t.Logf("%s build", goTool)
+       if out, err := cmd.CombinedOutput(); err != nil {
+               t.Logf("%s", out)
+               t.Fatal(err)
+       }
+}
+
+var cSources35779 = []string{`
+static int blah() { return 42; }
+int Cfunc1() { return blah(); }
+`, `
+static int blah() { return 42; }
+int Cfunc2() { return blah(); }
+`,
+}
+
+// TestMinusRSymsWithSameName tests a corner case in the new
+// loader. Prior to the fix this failed with the error 'loadelf:
+// $WORK/b001/_pkg_.a(ldr.syso): duplicate symbol reference: blah in
+// both main(.text) and main(.text)'. See issue #35779.
+func TestMinusRSymsWithSameName(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       testenv.MustHaveCGO(t)
+       t.Parallel()
+
+       dir, err := ioutil.TempDir("", "go-link-TestMinusRSymsWithSameName")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       gopath := filepath.Join(dir, "GOPATH")
+       env := append(os.Environ(), "GOPATH="+gopath)
+
+       if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
+               t.Fatal(err)
+       }
+
+       goTool := testenv.GoToolPath(t)
+       cc, cflags := getCCAndCCFLAGS(t, env)
+
+       objs := []string{}
+       csrcs := []string{}
+       for i, content := range cSources35779 {
+               csrcFile := filepath.Join(dir, fmt.Sprintf("x%d.c", i))
+               csrcs = append(csrcs, csrcFile)
+               if err := ioutil.WriteFile(csrcFile, []byte(content), 0444); err != nil {
+                       t.Fatal(err)
+               }
+
+               obj := filepath.Join(dir, fmt.Sprintf("x%d.o", i))
+               objs = append(objs, obj)
+               t.Logf("%s %v -c -o %s %s", cc, cflags, obj, csrcFile)
+               if out, err := exec.Command(cc, append(cflags, "-c", "-o", obj, csrcFile)...).CombinedOutput(); err != nil {
+                       t.Logf("%s", out)
+                       t.Fatal(err)
+               }
+       }
+
+       sysoObj := filepath.Join(dir, "ldr.syso")
+       t.Logf("%s %v -nostdlib -r -o %s %v", cc, cflags, sysoObj, objs)
+       if out, err := exec.Command(cc, append(cflags, "-nostdlib", "-r", "-o", sysoObj, objs[0], objs[1])...).CombinedOutput(); err != nil {
+               t.Logf("%s", out)
+               t.Fatal(err)
+       }
+
+       cruft := [][]string{objs, csrcs}
+       for _, sl := range cruft {
+               for _, s := range sl {
+                       if err := os.Remove(s); err != nil {
+                               t.Fatal(err)
+                       }
+               }
+       }
+
+       goFile := filepath.Join(dir, "main.go")
+       if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
+               t.Fatal(err)
+       }
+
+       t.Logf("%s build", goTool)
+       cmd := exec.Command(goTool, "build")
+       cmd.Dir = dir
+       cmd.Env = env
+       if out, err := cmd.CombinedOutput(); err != nil {
+               t.Logf("%s", out)
+               t.Fatal(err)
+       }
+}
+
+const pieSourceTemplate = `
+package main
+
+import "fmt"
+
+// Force the creation of a lot of type descriptors that will go into
+// the .data.rel.ro section.
+{{range $index, $element := .}}var V{{$index}} interface{} = [{{$index}}]int{}
+{{end}}
+
+func main() {
+{{range $index, $element := .}}        fmt.Println(V{{$index}})
+{{end}}
+}
+`
+
+func TestPIESize(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       if !sys.BuildModeSupported(runtime.Compiler, "pie", runtime.GOOS, runtime.GOARCH) {
+               t.Skip("-buildmode=pie not supported")
+       }
+
+       tmpl := template.Must(template.New("pie").Parse(pieSourceTemplate))
+
+       writeGo := func(t *testing.T, dir string) {
+               f, err := os.Create(filepath.Join(dir, "pie.go"))
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               // Passing a 100-element slice here will cause
+               // pieSourceTemplate to create 100 variables with
+               // different types.
+               if err := tmpl.Execute(f, make([]byte, 100)); err != nil {
+                       t.Fatal(err)
+               }
+
+               if err := f.Close(); err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       for _, external := range []bool{false, true} {
+               external := external
+
+               name := "TestPieSize-"
+               if external {
+                       name += "external"
+               } else {
+                       name += "internal"
+               }
+               t.Run(name, func(t *testing.T) {
+                       t.Parallel()
+
+                       dir, err := ioutil.TempDir("", "go-link-"+name)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       defer os.RemoveAll(dir)
+
+                       writeGo(t, dir)
+
+                       binexe := filepath.Join(dir, "exe")
+                       binpie := filepath.Join(dir, "pie")
+                       if external {
+                               binexe += "external"
+                               binpie += "external"
+                       }
+
+                       build := func(bin, mode string) error {
+                               cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", bin, "-buildmode="+mode)
+                               if external {
+                                       cmd.Args = append(cmd.Args, "-ldflags=-linkmode=external")
+                               }
+                               cmd.Args = append(cmd.Args, "pie.go")
+                               cmd.Dir = dir
+                               t.Logf("%v", cmd.Args)
+                               out, err := cmd.CombinedOutput()
+                               if len(out) > 0 {
+                                       t.Logf("%s", out)
+                               }
+                               if err != nil {
+                                       t.Error(err)
+                               }
+                               return err
+                       }
+
+                       var errexe, errpie error
+                       var wg sync.WaitGroup
+                       wg.Add(2)
+                       go func() {
+                               defer wg.Done()
+                               errexe = build(binexe, "exe")
+                       }()
+                       go func() {
+                               defer wg.Done()
+                               errpie = build(binpie, "pie")
+                       }()
+                       wg.Wait()
+                       if errexe != nil || errpie != nil {
+                               t.Fatal("link failed")
+                       }
+
+                       var sizeexe, sizepie uint64
+                       if fi, err := os.Stat(binexe); err != nil {
+                               t.Fatal(err)
+                       } else {
+                               sizeexe = uint64(fi.Size())
+                       }
+                       if fi, err := os.Stat(binpie); err != nil {
+                               t.Fatal(err)
+                       } else {
+                               sizepie = uint64(fi.Size())
+                       }
+
+                       elfexe, err := elf.Open(binexe)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       defer elfexe.Close()
+
+                       elfpie, err := elf.Open(binpie)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       defer elfpie.Close()
+
+                       // The difference in size between exe and PIE
+                       // should be approximately the difference in
+                       // size of the .text section plus the size of
+                       // the PIE dynamic data sections plus the
+                       // difference in size of the .got and .plt
+                       // sections if they exist.
+                       // We ignore unallocated sections.
+                       // There may be gaps between non-writeable and
+                       // writable PT_LOAD segments. We also skip those
+                       // gaps (see issue #36023).
+
+                       textsize := func(ef *elf.File, name string) uint64 {
+                               for _, s := range ef.Sections {
+                                       if s.Name == ".text" {
+                                               return s.Size
+                                       }
+                               }
+                               t.Fatalf("%s: no .text section", name)
+                               return 0
+                       }
+                       textexe := textsize(elfexe, binexe)
+                       textpie := textsize(elfpie, binpie)
+
+                       dynsize := func(ef *elf.File) uint64 {
+                               var ret uint64
+                               for _, s := range ef.Sections {
+                                       if s.Flags&elf.SHF_ALLOC == 0 {
+                                               continue
+                                       }
+                                       switch s.Type {
+                                       case elf.SHT_DYNSYM, elf.SHT_STRTAB, elf.SHT_REL, elf.SHT_RELA, elf.SHT_HASH, elf.SHT_GNU_HASH, elf.SHT_GNU_VERDEF, elf.SHT_GNU_VERNEED, elf.SHT_GNU_VERSYM:
+                                               ret += s.Size
+                                       }
+                                       if s.Flags&elf.SHF_WRITE != 0 && (strings.Contains(s.Name, ".got") || strings.Contains(s.Name, ".plt")) {
+                                               ret += s.Size
+                                       }
+                               }
+                               return ret
+                       }
+
+                       dynexe := dynsize(elfexe)
+                       dynpie := dynsize(elfpie)
+
+                       extrasize := func(ef *elf.File) uint64 {
+                               var ret uint64
+                               // skip unallocated sections
+                               for _, s := range ef.Sections {
+                                       if s.Flags&elf.SHF_ALLOC == 0 {
+                                               ret += s.Size
+                                       }
+                               }
+                               // also skip gaps between PT_LOAD segments
+                               var prev *elf.Prog
+                               for _, seg := range ef.Progs {
+                                       if seg.Type != elf.PT_LOAD {
+                                               continue
+                                       }
+                                       if prev != nil {
+                                               ret += seg.Off - prev.Off - prev.Filesz
+                                       }
+                                       prev = seg
+                               }
+                               return ret
+                       }
+
+                       extraexe := extrasize(elfexe)
+                       extrapie := extrasize(elfpie)
+
+                       diffReal := (sizepie - extrapie) - (sizeexe - extraexe)
+                       diffExpected := (textpie + dynpie) - (textexe + dynexe)
+
+                       t.Logf("real size difference %#x, expected %#x", diffReal, diffExpected)
+
+                       if diffReal > (diffExpected + diffExpected/10) {
+                               t.Errorf("PIE unexpectedly large: got difference of %d (%d - %d), expected difference %d", diffReal, sizepie, sizeexe, diffExpected)
+                       }
+               })
+       }
+}
diff --git a/src/cmd/oldlink/internal/amd64/asm.go b/src/cmd/oldlink/internal/amd64/asm.go
new file mode 100644 (file)
index 0000000..c120e4e
--- /dev/null
@@ -0,0 +1,874 @@
+// Inferno utils/6l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "log"
+)
+
+func PADDR(x uint32) uint32 {
+       return x &^ 0x80000000
+}
+
+func Addcall(ctxt *ld.Link, s *sym.Symbol, t *sym.Symbol) int64 {
+       s.Attr |= sym.AttrReachable
+       i := s.Size
+       s.Size += 4
+       s.Grow(s.Size)
+       r := s.AddRel()
+       r.Sym = t
+       r.Off = int32(i)
+       r.Type = objabi.R_CALL
+       r.Siz = 4
+       return i + int64(r.Siz)
+}
+
+func gentext(ctxt *ld.Link) {
+       if !ctxt.DynlinkingGo() {
+               return
+       }
+       addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+       if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin {
+               // we're linking a module containing the runtime -> no need for
+               // an init function
+               return
+       }
+       addmoduledata.Attr |= sym.AttrReachable
+       initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
+       initfunc.Type = sym.STEXT
+       initfunc.Attr |= sym.AttrLocal
+       initfunc.Attr |= sym.AttrReachable
+       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)
+       Addcall(ctxt, initfunc, addmoduledata)
+       //    c:        c3                      retq
+       o(0xc3)
+       if ctxt.BuildMode == ld.BuildModePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
+       ctxt.Textp = append(ctxt.Textp, initfunc)
+       initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
+       initarray_entry.Attr |= sym.AttrReachable
+       initarray_entry.Attr |= sym.AttrLocal
+       initarray_entry.Type = sym.SINITARR
+       initarray_entry.AddAddr(ctxt.Arch, initfunc)
+}
+
+func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       targ := r.Sym
+
+       switch r.Type {
+       default:
+               if r.Type >= objabi.ElfRelocOffset {
+                       ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
+                       return false
+               }
+
+               // Handle relocations found in ELF object files.
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC32):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name)
+               }
+               // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+               // sense and should be removed when someone has thought about it properly.
+               if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() {
+                       ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
+               }
+               r.Type = objabi.R_PCREL
+               r.Add += 4
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC64):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_X86_64_PC64 relocation for dynamic symbol %s", targ.Name)
+               }
+               if targ.Type == 0 || targ.Type == sym.SXREF {
+                       ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
+               }
+               r.Type = objabi.R_PCREL
+               r.Add += 8
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PLT32):
+               r.Type = objabi.R_PCREL
+               r.Add += 4
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add += int64(targ.Plt())
+               }
+
+               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):
+               if targ.Type != sym.SDYNIMPORT {
+                       // have symbol
+                       if r.Off >= 2 && s.P[r.Off-2] == 0x8b {
+                               // turn MOVQ of GOT entry into LEAQ of symbol itself
+                               s.P[r.Off-2] = 0x8d
+
+                               r.Type = objabi.R_PCREL
+                               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
+               addgotsym(ctxt, targ)
+
+               r.Type = objabi.R_PCREL
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += 4
+               r.Add += int64(targ.Got())
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_64):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ADDR
+               if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal {
+                       // 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:
+               // TODO: What is the difference between all these?
+               r.Type = objabi.R_ADDR
+
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected reloc for dynamic symbol %s", targ.Name)
+               }
+               return true
+
+       case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1:
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add = int64(targ.Plt())
+                       r.Type = objabi.R_PCREL
+                       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:
+               r.Type = objabi.R_PCREL
+
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected pc-relative reloc for dynamic symbol %s", targ.Name)
+               }
+               return true
+
+       case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1:
+               if targ.Type != sym.SDYNIMPORT {
+                       // have symbol
+                       // turn MOVQ of GOT entry into LEAQ of symbol itself
+                       if r.Off < 2 || s.P[r.Off-2] != 0x8b {
+                               ld.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ.Name)
+                               return false
+                       }
+
+                       s.P[r.Off-2] = 0x8d
+                       r.Type = objabi.R_PCREL
+                       return true
+               }
+               fallthrough
+
+       case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT*2 + 1:
+               if targ.Type != sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
+               }
+               addgotsym(ctxt, targ)
+               r.Type = objabi.R_PCREL
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += int64(targ.Got())
+               return true
+       }
+
+       switch r.Type {
+       case objabi.R_CALL,
+               objabi.R_PCREL:
+               if targ.Type != sym.SDYNIMPORT {
+                       // nothing to do, the relocation will be laid out in reloc
+                       return true
+               }
+               if ctxt.LinkMode == ld.LinkExternal {
+                       // 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(ctxt, targ)
+               r.Sym = ctxt.Syms.Lookup(".plt", 0)
+               r.Add = int64(targ.Plt())
+               return true
+
+       case objabi.R_ADDR:
+               if s.Type == sym.STEXT && ctxt.IsELF {
+                       if ctxt.HeadType == objabi.Hsolaris {
+                               addpltsym(ctxt, targ)
+                               r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                               r.Add += int64(targ.Plt())
+                               return true
+                       }
+                       // The code is asking for the address of an external
+                       // function. We provide it with the address of the
+                       // correspondent GOT symbol.
+                       addgotsym(ctxt, targ)
+
+                       r.Sym = ctxt.Syms.Lookup(".got", 0)
+                       r.Add += int64(targ.Got())
+                       return true
+               }
+
+               // Process dynamic relocations for the data sections.
+               if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal {
+                       // 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 s.Name {
+                       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 s.Type != sym.SDATA && s.Type != sym.SRODATA {
+                               break
+                       }
+               }
+
+               if ctxt.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 := ctxt.Syms.Lookup(".rela", 0)
+                       rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off))
+                       if r.Siz == 8 {
+                               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_X86_64_RELATIVE)))
+                       } else {
+                               ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name)
+                       }
+                       rela.AddAddrPlus(ctxt.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 ctxt.HeadType == objabi.Hdarwin && s.Size == int64(ctxt.Arch.PtrSize) && r.Off == 0 {
+                       // Mach-O relocations are a royal pain to lay out.
+                       // They use a compact stateful bytecode representation
+                       // that is too much bother to deal with.
+                       // Instead, interpret the C declaration
+                       //      void *_Cvar_stderr = &stderr;
+                       // as making _Cvar_stderr the name of a GOT entry
+                       // for stderr. This is separate from the usual GOT entry,
+                       // just in case the C code assigns to the variable,
+                       // and of course it only works for single pointers,
+                       // but we only need to support cgo and that's all it needs.
+                       ld.Adddynsym(ctxt, targ)
+
+                       got := ctxt.Syms.Lookup(".got", 0)
+                       s.Type = got.Type
+                       s.Attr |= sym.AttrSubSymbol
+                       s.Outer = got
+                       s.Sub = got.Sub
+                       got.Sub = s
+                       s.Value = got.Size
+                       got.AddUint64(ctxt.Arch, 0)
+                       ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(targ.Dynid))
+                       r.Type = objabi.ElfRelocOffset // ignore during relocsym
+                       return true
+               }
+       }
+
+       return false
+}
+
+func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
+       ctxt.Out.Write64(uint64(sectoff))
+
+       elfsym := r.Xsym.ElfsymForReloc()
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               if r.Siz == 4 {
+                       ctxt.Out.Write64(uint64(elf.R_X86_64_32) | uint64(elfsym)<<32)
+               } else if r.Siz == 8 {
+                       ctxt.Out.Write64(uint64(elf.R_X86_64_64) | uint64(elfsym)<<32)
+               } else {
+                       return false
+               }
+       case objabi.R_TLS_LE:
+               if r.Siz == 4 {
+                       ctxt.Out.Write64(uint64(elf.R_X86_64_TPOFF32) | uint64(elfsym)<<32)
+               } else {
+                       return false
+               }
+       case objabi.R_TLS_IE:
+               if r.Siz == 4 {
+                       ctxt.Out.Write64(uint64(elf.R_X86_64_GOTTPOFF) | uint64(elfsym)<<32)
+               } else {
+                       return false
+               }
+       case objabi.R_CALL:
+               if r.Siz == 4 {
+                       if r.Xsym.Type == sym.SDYNIMPORT {
+                               if ctxt.DynlinkingGo() {
+                                       ctxt.Out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32)
+                               } else {
+                                       ctxt.Out.Write64(uint64(elf.R_X86_64_GOTPCREL) | uint64(elfsym)<<32)
+                               }
+                       } else {
+                               ctxt.Out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32)
+                       }
+               } else {
+                       return false
+               }
+       case objabi.R_PCREL:
+               if r.Siz == 4 {
+                       if r.Xsym.Type == sym.SDYNIMPORT && r.Xsym.ElfType() == elf.STT_FUNC {
+                               ctxt.Out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32)
+                       } else {
+                               ctxt.Out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32)
+                       }
+               } else {
+                       return false
+               }
+       case objabi.R_GOTPCREL:
+               if r.Siz == 4 {
+                       ctxt.Out.Write64(uint64(elf.R_X86_64_GOTPCREL) | uint64(elfsym)<<32)
+               } else {
+                       return false
+               }
+       }
+
+       ctxt.Out.Write64(uint64(r.Xadd))
+       return true
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       var v uint32
+
+       rs := r.Xsym
+
+       if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_PCREL || r.Type == objabi.R_GOTPCREL || r.Type == objabi.R_CALL {
+               if rs.Dynid < 0 {
+                       ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
+                       return false
+               }
+
+               v = uint32(rs.Dynid)
+               v |= 1 << 27 // external relocation
+       } else {
+               v = uint32(rs.Sect.Extnum)
+               if v == 0 {
+                       ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type)
+                       return false
+               }
+       }
+
+       switch r.Type {
+       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.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, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       var v uint32
+
+       rs := r.Xsym
+
+       if rs.Dynid < 0 {
+               ld.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
+               return false
+       }
+
+       out.Write32(uint32(sectoff))
+       out.Write32(uint32(rs.Dynid))
+
+       switch r.Type {
+       default:
+               return false
+
+       case objabi.R_DWARFSECREF:
+               v = ld.IMAGE_REL_AMD64_SECREL
+
+       case objabi.R_ADDR:
+               if r.Siz == 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(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       log.Fatalf("unexpected relocation variant")
+       return t
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       plt := ctxt.Syms.Lookup(".plt", 0)
+       got := ctxt.Syms.Lookup(".got.plt", 0)
+       if plt.Size == 0 {
+               // pushq got+8(IP)
+               plt.AddUint8(0xff)
+
+               plt.AddUint8(0x35)
+               plt.AddPCRelPlus(ctxt.Arch, got, 8)
+
+               // jmpq got+16(IP)
+               plt.AddUint8(0xff)
+
+               plt.AddUint8(0x25)
+               plt.AddPCRelPlus(ctxt.Arch, got, 16)
+
+               // nopl 0(AX)
+               plt.AddUint32(ctxt.Arch, 0x00401f0f)
+
+               // assume got->size == 0 too
+               got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+
+               got.AddUint64(ctxt.Arch, 0)
+               got.AddUint64(ctxt.Arch, 0)
+       }
+}
+
+func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Plt() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+
+       if ctxt.IsELF {
+               plt := ctxt.Syms.Lookup(".plt", 0)
+               got := ctxt.Syms.Lookup(".got.plt", 0)
+               rela := ctxt.Syms.Lookup(".rela.plt", 0)
+               if plt.Size == 0 {
+                       elfsetupplt(ctxt)
+               }
+
+               // jmpq *got+size(IP)
+               plt.AddUint8(0xff)
+
+               plt.AddUint8(0x25)
+               plt.AddPCRelPlus(ctxt.Arch, got, got.Size)
+
+               // add to got: pointer to current pos in plt
+               got.AddAddrPlus(ctxt.Arch, plt, plt.Size)
+
+               // pushq $x
+               plt.AddUint8(0x68)
+
+               plt.AddUint32(ctxt.Arch, uint32((got.Size-24-8)/8))
+
+               // jmpq .plt
+               plt.AddUint8(0xe9)
+
+               plt.AddUint32(ctxt.Arch, uint32(-(plt.Size + 4)))
+
+               // rela
+               rela.AddAddrPlus(ctxt.Arch, got, got.Size-8)
+
+               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_JMP_SLOT)))
+               rela.AddUint64(ctxt.Arch, 0)
+
+               s.SetPlt(int32(plt.Size - 16))
+       } else if ctxt.HeadType == objabi.Hdarwin {
+               // To do lazy symbol lookup right, we're supposed
+               // to tell the dynamic loader which library each
+               // symbol comes from and format the link info
+               // section just so. I'm too lazy (ha!) to do that
+               // so for now we'll just use non-lazy pointers,
+               // which don't need to be told which library to use.
+               //
+               // https://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html
+               // has details about what we're avoiding.
+
+               addgotsym(ctxt, s)
+               plt := ctxt.Syms.Lookup(".plt", 0)
+
+               ctxt.Syms.Lookup(".linkedit.plt", 0).AddUint32(ctxt.Arch, uint32(s.Dynid))
+
+               // jmpq *got+size(IP)
+               s.SetPlt(int32(plt.Size))
+
+               plt.AddUint8(0xff)
+               plt.AddUint8(0x25)
+               plt.AddPCRelPlus(ctxt.Arch, ctxt.Syms.Lookup(".got", 0), int64(s.Got()))
+       } else {
+               ld.Errorf(s, "addpltsym: unsupported binary format")
+       }
+}
+
+func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Got() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+       got := ctxt.Syms.Lookup(".got", 0)
+       s.SetGot(int32(got.Size))
+       got.AddUint64(ctxt.Arch, 0)
+
+       if ctxt.IsELF {
+               rela := ctxt.Syms.Lookup(".rela", 0)
+               rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got()))
+               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_GLOB_DAT)))
+               rela.AddUint64(ctxt.Arch, 0)
+       } else if ctxt.HeadType == objabi.Hdarwin {
+               ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(s.Dynid))
+       } else {
+               ld.Errorf(s, "addgotsym: unsupported binary format")
+       }
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       sect := ld.Segtext.Sections[0]
+       ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+       // 0xCC is INT $3 - breakpoint instruction
+       ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC})
+       for _, sect = range ld.Segtext.Sections[1:] {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+       if ld.Segrelrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       machlink := int64(0)
+       if ctxt.HeadType == objabi.Hdarwin {
+               machlink = ld.Domacholink(ctxt)
+       }
+
+       switch ctxt.HeadType {
+       default:
+               ld.Errorf(nil, "unknown header type %v", ctxt.HeadType)
+               fallthrough
+
+       case objabi.Hplan9:
+               break
+
+       case objabi.Hdarwin:
+               ld.Flag8 = true /* 64-bit addresses */
+
+       case objabi.Hlinux,
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd,
+               objabi.Hdragonfly,
+               objabi.Hsolaris:
+               ld.Flag8 = true /* 64-bit addresses */
+
+       case objabi.Hwindows:
+               break
+       }
+
+       ld.Symsize = 0
+       ld.Spsize = 0
+       ld.Lcsize = 0
+       symo := int64(0)
+       if !*ld.FlagS {
+               switch ctxt.HeadType {
+               default:
+               case objabi.Hplan9:
+                       *ld.FlagS = true
+                       symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+
+               case objabi.Hdarwin:
+                       symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
+
+               case objabi.Hlinux,
+                       objabi.Hfreebsd,
+                       objabi.Hnetbsd,
+                       objabi.Hopenbsd,
+                       objabi.Hdragonfly,
+                       objabi.Hsolaris:
+                       symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                       symo = ld.Rnd(symo, int64(*ld.FlagRound))
+
+               case objabi.Hwindows:
+                       symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                       symo = ld.Rnd(symo, ld.PEFILEALIGN)
+               }
+
+               ctxt.Out.SeekSet(symo)
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               ctxt.Out.SeekSet(symo)
+                               ld.Asmelfsym(ctxt)
+                               ctxt.Out.Flush()
+                               ctxt.Out.Write(ld.Elfstrdat)
+
+                               if ctxt.LinkMode == ld.LinkExternal {
+                                       ld.Elfemitreloc(ctxt)
+                               }
+                       }
+
+               case objabi.Hplan9:
+                       ld.Asmplan9sym(ctxt)
+                       ctxt.Out.Flush()
+
+                       sym := ctxt.Syms.Lookup("pclntab", 0)
+                       if sym != nil {
+                               ld.Lcsize = int32(len(sym.P))
+                               ctxt.Out.Write(sym.P)
+                               ctxt.Out.Flush()
+                       }
+
+               case objabi.Hwindows:
+                       // Do nothing
+
+               case objabi.Hdarwin:
+                       if ctxt.LinkMode == ld.LinkExternal {
+                               ld.Machoemitreloc(ctxt)
+                       }
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       default:
+       case objabi.Hplan9: /* plan9 */
+               magic := int32(4*26*26 + 7)
+
+               magic |= 0x00008000                           /* fat header */
+               ctxt.Out.Write32b(uint32(magic))              /* magic */
+               ctxt.Out.Write32b(uint32(ld.Segtext.Filelen)) /* sizes */
+               ctxt.Out.Write32b(uint32(ld.Segdata.Filelen))
+               ctxt.Out.Write32b(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
+               ctxt.Out.Write32b(uint32(ld.Symsize)) /* nsyms */
+               vl := ld.Entryvalue(ctxt)
+               ctxt.Out.Write32b(PADDR(uint32(vl))) /* va of entry */
+               ctxt.Out.Write32b(uint32(ld.Spsize)) /* sp offsets */
+               ctxt.Out.Write32b(uint32(ld.Lcsize)) /* line offsets */
+               ctxt.Out.Write64b(uint64(vl))        /* va of entry */
+
+       case objabi.Hdarwin:
+               ld.Asmbmacho(ctxt)
+
+       case objabi.Hlinux,
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd,
+               objabi.Hdragonfly,
+               objabi.Hsolaris:
+               ld.Asmbelf(ctxt, symo)
+
+       case objabi.Hwindows:
+               ld.Asmbpe(ctxt)
+       }
+
+       ctxt.Out.Flush()
+}
+
+func tlsIEtoLE(s *sym.Symbol, 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 := s.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/oldlink/internal/amd64/l.go b/src/cmd/oldlink/internal/amd64/l.go
new file mode 100644 (file)
index 0000000..393da6b
--- /dev/null
@@ -0,0 +1,43 @@
+// Inferno utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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 = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+       dwarfRegSP = 7
+       dwarfRegLR = 16
+)
diff --git a/src/cmd/oldlink/internal/amd64/obj.go b/src/cmd/oldlink/internal/amd64/obj.go
new file mode 100644 (file)
index 0000000..655d9f1
--- /dev/null
@@ -0,0 +1,117 @@
+// Inferno utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+       arch := sys.ArchAMD64
+
+       theArch := ld.Arch{
+               Funcalign:  funcAlign,
+               Maxalign:   maxAlign,
+               Minalign:   minAlign,
+               Dwarfregsp: dwarfRegSP,
+               Dwarfreglr: dwarfRegLR,
+
+               Adddynrel:        adddynrel,
+               Archinit:         archinit,
+               Archreloc:        archreloc,
+               Archrelocvariant: archrelocvariant,
+               Asmb:             asmb,
+               Asmb2:            asmb2,
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Machoreloc1:      machoreloc1,
+               PEreloc1:         pereloc1,
+               TLSIEtoLE:        tlsIEtoLE,
+
+               Linuxdynld:     "/lib64/ld-linux-x86-64.so.2",
+               Freebsddynld:   "/libexec/ld-elf.so.1",
+               Openbsddynld:   "/usr/libexec/ld.so",
+               Netbsddynld:    "/libexec/ld.elf_so",
+               Dragonflydynld: "/usr/libexec/ld-elf.so.2",
+               Solarisdynld:   "/lib/amd64/ld.so.1",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+       case objabi.Hplan9: /* plan 9 */
+               ld.HEADR = 32 + 8
+
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x200000 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 0x200000
+               }
+
+       case objabi.Hdarwin: /* apple MACH */
+               ld.HEADR = ld.INITIAL_MACHO_HEADR
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x1000000 + int64(ld.HEADR)
+               }
+
+       case objabi.Hlinux, /* elf64 executable */
+               objabi.Hfreebsd,   /* freebsd */
+               objabi.Hnetbsd,    /* netbsd */
+               objabi.Hopenbsd,   /* openbsd */
+               objabi.Hdragonfly, /* dragonfly */
+               objabi.Hsolaris:   /* solaris */
+               ld.Elfinit(ctxt)
+
+               ld.HEADR = ld.ELFRESERVE
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = (1 << 22) + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+
+       case objabi.Hwindows: /* PE executable */
+               // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+               return
+       }
+}
diff --git a/src/cmd/oldlink/internal/arm/asm.go b/src/cmd/oldlink/internal/arm/asm.go
new file mode 100644 (file)
index 0000000..8db0bc3
--- /dev/null
@@ -0,0 +1,891 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+       "cmd/oldlink/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) {
+       if !ctxt.DynlinkingGo() {
+               return
+       }
+       addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+       if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin {
+               // we're linking a module containing the runtime -> no need for
+               // an init function
+               return
+       }
+       addmoduledata.Attr |= sym.AttrReachable
+       initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
+       initfunc.Type = sym.STEXT
+       initfunc.Attr |= sym.AttrLocal
+       initfunc.Attr |= sym.AttrReachable
+       o := func(op uint32) {
+               initfunc.AddUint32(ctxt.Arch, op)
+       }
+       o(0xe59f0004)
+       o(0xe08f0000)
+
+       o(0xeafffffe)
+       rel := initfunc.AddRel()
+       rel.Off = 8
+       rel.Siz = 4
+       rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+       rel.Type = objabi.R_CALLARM
+       rel.Add = 0xeafffffe // vomit
+
+       o(0x00000000)
+       rel = initfunc.AddRel()
+       rel.Off = 12
+       rel.Siz = 4
+       rel.Sym = ctxt.Moduledata
+       rel.Type = objabi.R_PCREL
+       rel.Add = 4
+
+       if ctxt.BuildMode == ld.BuildModePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
+       ctxt.Textp = append(ctxt.Textp, initfunc)
+       initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
+       initarray_entry.Attr |= sym.AttrReachable
+       initarray_entry.Attr |= sym.AttrLocal
+       initarray_entry.Type = sym.SINITARR
+       initarray_entry.AddAddr(ctxt.Arch, initfunc)
+}
+
+// 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(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       targ := r.Sym
+
+       switch r.Type {
+       default:
+               if r.Type >= objabi.ElfRelocOffset {
+                       ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
+                       return false
+               }
+
+               // Handle relocations found in ELF object files.
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PLT32):
+               r.Type = objabi.R_CALLARM
+
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add = int64(braddoff(int32(r.Add), targ.Plt()/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 targ.Type != sym.SDYNIMPORT {
+                       addgotsyminternal(ctxt, targ)
+               } else {
+                       addgotsym(ctxt, targ)
+               }
+
+               r.Type = objabi.R_CONST // write r->add during relocsym
+               r.Sym = nil
+               r.Add += int64(targ.Got())
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil
+               if targ.Type != sym.SDYNIMPORT {
+                       addgotsyminternal(ctxt, targ)
+               } else {
+                       addgotsym(ctxt, targ)
+               }
+
+               r.Type = objabi.R_PCREL
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += int64(targ.Got()) + 4
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTOFF): // R_ARM_GOTOFF32
+               r.Type = objabi.R_GOTOFF
+
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTPC): // R_ARM_BASE_PREL
+               r.Type = objabi.R_PCREL
+
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += 4
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL):
+               r.Type = objabi.R_CALLARM
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4))
+               }
+
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_REL32): // R_ARM_REL32
+               r.Type = objabi.R_PCREL
+
+               r.Add += 4
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_ABS32):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ADDR
+               return true
+
+               // we can just ignore this, because we are targeting ARM V5+ anyway
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_V4BX):
+               if r.Sym != nil {
+                       // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it
+                       r.Sym.Type = 0
+               }
+
+               r.Sym = nil
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24),
+               objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24):
+               r.Type = objabi.R_CALLARM
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4))
+               }
+
+               return true
+       }
+
+       // Handle references to ELF symbols from our own object files.
+       if targ.Type != sym.SDYNIMPORT {
+               return true
+       }
+
+       switch r.Type {
+       case objabi.R_CALLARM:
+               if ctxt.LinkMode == ld.LinkExternal {
+                       // External linker will do this relocation.
+                       return true
+               }
+               addpltsym(ctxt, targ)
+               r.Sym = ctxt.Syms.Lookup(".plt", 0)
+               r.Add = int64(targ.Plt())
+               return true
+
+       case objabi.R_ADDR:
+               if s.Type != sym.SDATA {
+                       break
+               }
+               if ctxt.IsELF {
+                       ld.Adddynsym(ctxt, targ)
+                       rel := ctxt.Syms.Lookup(".rel", 0)
+                       rel.AddAddrPlus(ctxt.Arch, s, int64(r.Off))
+                       rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc
+                       r.Type = objabi.R_CONST                                                                   // write r->add during relocsym
+                       r.Sym = nil
+                       return true
+               }
+       }
+
+       return false
+}
+
+func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
+       ctxt.Out.Write32(uint32(sectoff))
+
+       elfsym := r.Xsym.ElfsymForReloc()
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               if r.Siz == 4 {
+                       ctxt.Out.Write32(uint32(elf.R_ARM_ABS32) | uint32(elfsym)<<8)
+               } else {
+                       return false
+               }
+       case objabi.R_PCREL:
+               if r.Siz == 4 {
+                       ctxt.Out.Write32(uint32(elf.R_ARM_REL32) | uint32(elfsym)<<8)
+               } else {
+                       return false
+               }
+       case objabi.R_CALLARM:
+               if r.Siz == 4 {
+                       if r.Add&0xff000000 == 0xeb000000 { // BL
+                               ctxt.Out.Write32(uint32(elf.R_ARM_CALL) | uint32(elfsym)<<8)
+                       } else {
+                               ctxt.Out.Write32(uint32(elf.R_ARM_JUMP24) | uint32(elfsym)<<8)
+                       }
+               } else {
+                       return false
+               }
+       case objabi.R_TLS_LE:
+               ctxt.Out.Write32(uint32(elf.R_ARM_TLS_LE32) | uint32(elfsym)<<8)
+       case objabi.R_TLS_IE:
+               ctxt.Out.Write32(uint32(elf.R_ARM_TLS_IE32) | uint32(elfsym)<<8)
+       case objabi.R_GOTPCREL:
+               if r.Siz == 4 {
+                       ctxt.Out.Write32(uint32(elf.R_ARM_GOT_PREL) | uint32(elfsym)<<8)
+               } else {
+                       return false
+               }
+       }
+
+       return true
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       plt := ctxt.Syms.Lookup(".plt", 0)
+       got := ctxt.Syms.Lookup(".got.plt", 0)
+       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, 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(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       var v uint32
+
+       rs := r.Xsym
+
+       if r.Type == objabi.R_PCREL {
+               if rs.Type == sym.SHOSTOBJ {
+                       ld.Errorf(s, "pc-relative relocation of external symbol is not supported")
+                       return false
+               }
+               if r.Siz != 4 {
+                       return false
+               }
+
+               // emit a pair of "scattered" relocations that
+               // resolve to the difference of section addresses of
+               // the symbol and the instruction
+               // this value is added to the field being relocated
+               o1 := uint32(sectoff)
+               o1 |= 1 << 31 // scattered bit
+               o1 |= ld.MACHO_ARM_RELOC_SECTDIFF << 24
+               o1 |= 2 << 28 // size = 4
+
+               o2 := uint32(0)
+               o2 |= 1 << 31 // scattered bit
+               o2 |= ld.MACHO_ARM_RELOC_PAIR << 24
+               o2 |= 2 << 28 // size = 4
+
+               out.Write32(o1)
+               out.Write32(uint32(ld.Symaddr(rs)))
+               out.Write32(o2)
+               out.Write32(uint32(s.Value + int64(r.Off)))
+               return true
+       }
+
+       if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALLARM {
+               if rs.Dynid < 0 {
+                       ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
+                       return false
+               }
+
+               v = uint32(rs.Dynid)
+               v |= 1 << 27 // external relocation
+       } else {
+               v = uint32(rs.Sect.Extnum)
+               if v == 0 {
+                       ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type)
+                       return false
+               }
+       }
+
+       switch r.Type {
+       default:
+               return false
+
+       case objabi.R_ADDR:
+               v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
+
+       case objabi.R_CALLARM:
+               v |= 1 << 24 // pc-relative bit
+               v |= ld.MACHO_ARM_RELOC_BR24 << 28
+       }
+
+       switch r.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, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       rs := r.Xsym
+
+       if rs.Dynid < 0 {
+               ld.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
+               return false
+       }
+
+       out.Write32(uint32(sectoff))
+       out.Write32(uint32(rs.Dynid))
+
+       var v uint32
+       switch r.Type {
+       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, r *sym.Reloc, s *sym.Symbol) {
+       switch r.Type {
+       case objabi.R_CALLARM:
+               // r.Add is the instruction
+               // low 24-bit encodes the target address
+               t := (ld.Symaddr(r.Sym) + int64(signext24(r.Add&0xffffff)*4) - (s.Value + int64(r.Off))) / 4
+               if t > 0x7fffff || t < -0x800000 || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) {
+                       // 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 *sym.Symbol
+                       for i := 0; ; i++ {
+                               name := r.Sym.Name + fmt.Sprintf("%+d-tramp%d", offset, i)
+                               tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
+                               if tramp.Type == sym.SDYNIMPORT {
+                                       // don't reuse trampoline defined in other module
+                                       continue
+                               }
+                               if tramp.Value == 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 = (ld.Symaddr(tramp) - 8 - (s.Value + 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 tramp.Type == 0 {
+                               // trampoline does not exist, create one
+                               ctxt.AddTramp(tramp)
+                               if ctxt.DynlinkingGo() {
+                                       if immrot(uint32(offset)) == 0 {
+                                               ld.Errorf(s, "odd offset in dynlink direct call: %v+%d", r.Sym, offset)
+                                       }
+                                       gentrampdyn(ctxt.Arch, tramp, r.Sym, int64(offset))
+                               } else if ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE {
+                                       gentramppic(ctxt.Arch, tramp, r.Sym, int64(offset))
+                               } else {
+                                       gentramp(ctxt.Arch, ctxt.LinkMode, tramp, r.Sym, int64(offset))
+                               }
+                       }
+                       // modify reloc to point to tramp, which will be resolved later
+                       r.Sym = tramp
+                       r.Add = r.Add&0xff000000 | 0xfffffe // clear the offset embedded in the instruction
+                       r.Done = false
+               }
+       default:
+               ld.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, tramp, target *sym.Symbol, offset int64) {
+       tramp.Size = 12 // 3 instructions
+       tramp.P = make([]byte, tramp.Size)
+       t := ld.Symaddr(target) + offset
+       o1 := uint32(0xe5900000 | 11<<12 | 15<<16) // MOVW (R15), R11 // R15 is actual pc + 8
+       o2 := uint32(0xe12fff10 | 11)              // JMP  (R11)
+       o3 := uint32(t)                            // WORD $target
+       arch.ByteOrder.PutUint32(tramp.P, o1)
+       arch.ByteOrder.PutUint32(tramp.P[4:], o2)
+       arch.ByteOrder.PutUint32(tramp.P[8:], o3)
+
+       if linkmode == ld.LinkExternal {
+               r := tramp.AddRel()
+               r.Off = 8
+               r.Type = objabi.R_ADDR
+               r.Siz = 4
+               r.Sym = target
+               r.Add = offset
+       }
+}
+
+// generate a trampoline to target+offset in position independent code
+func gentramppic(arch *sys.Arch, tramp, target *sym.Symbol, offset int64) {
+       tramp.Size = 16 // 4 instructions
+       tramp.P = make([]byte, tramp.Size)
+       o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 4)  // MOVW 4(R15), R11 // R15 is actual pc + 8
+       o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11
+       o3 := uint32(0xe12fff10 | 11)                   // JMP  (R11)
+       o4 := uint32(0)                                 // WORD $(target-pc) // filled in with relocation
+       arch.ByteOrder.PutUint32(tramp.P, o1)
+       arch.ByteOrder.PutUint32(tramp.P[4:], o2)
+       arch.ByteOrder.PutUint32(tramp.P[8:], o3)
+       arch.ByteOrder.PutUint32(tramp.P[12:], o4)
+
+       r := tramp.AddRel()
+       r.Off = 12
+       r.Type = objabi.R_PCREL
+       r.Siz = 4
+       r.Sym = target
+       r.Add = offset + 4
+}
+
+// generate a trampoline to target+offset in dynlink mode (using GOT)
+func gentrampdyn(arch *sys.Arch, tramp, target *sym.Symbol, offset int64) {
+       tramp.Size = 20                                 // 5 instructions
+       o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 8)  // MOVW 8(R15), R11 // R15 is actual pc + 8
+       o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11
+       o3 := uint32(0xe5900000 | 11<<12 | 11<<16)      // MOVW (R11), R11
+       o4 := uint32(0xe12fff10 | 11)                   // JMP  (R11)
+       o5 := uint32(0)                                 // WORD $target@GOT // filled in with relocation
+       o6 := uint32(0)
+       if offset != 0 {
+               // insert an instruction to add offset
+               tramp.Size = 24 // 6 instructions
+               o6 = o5
+               o5 = o4
+               o4 = 0xe2800000 | 11<<12 | 11<<16 | immrot(uint32(offset)) // ADD $offset, R11, R11
+               o1 = uint32(0xe5900000 | 11<<12 | 15<<16 | 12)             // MOVW 12(R15), R11
+       }
+       tramp.P = make([]byte, tramp.Size)
+       arch.ByteOrder.PutUint32(tramp.P, o1)
+       arch.ByteOrder.PutUint32(tramp.P[4:], o2)
+       arch.ByteOrder.PutUint32(tramp.P[8:], o3)
+       arch.ByteOrder.PutUint32(tramp.P[12:], o4)
+       arch.ByteOrder.PutUint32(tramp.P[16:], o5)
+       if offset != 0 {
+               arch.ByteOrder.PutUint32(tramp.P[20:], o6)
+       }
+
+       r := tramp.AddRel()
+       r.Off = 16
+       r.Type = objabi.R_GOTPCREL
+       r.Siz = 4
+       r.Sym = target
+       r.Add = 8
+       if offset != 0 {
+               // increase reloc offset by 4 as we inserted an ADD instruction
+               r.Off = 20
+               r.Add = 12
+       }
+}
+
+func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       if ctxt.LinkMode == ld.LinkExternal {
+               switch r.Type {
+               case objabi.R_CALLARM:
+                       r.Done = false
+
+                       // set up addend for eventual relocation via outer symbol.
+                       rs := r.Sym
+
+                       r.Xadd = int64(signext24(r.Add & 0xffffff))
+                       r.Xadd *= 4
+                       for rs.Outer != nil {
+                               r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer)
+                               rs = rs.Outer
+                       }
+
+                       if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Type != sym.SUNDEFEXT && rs.Sect == nil {
+                               ld.Errorf(s, "missing section for %s", rs.Name)
+                       }
+                       r.Xsym = rs
+
+                       // ld64 for arm seems to want the symbol table to contain offset
+                       // into the section rather than pseudo virtual address that contains
+                       // the section load address.
+                       // we need to compensate that by removing the instruction's address
+                       // from addend.
+                       if ctxt.HeadType == objabi.Hdarwin {
+                               r.Xadd -= ld.Symaddr(s) + int64(r.Off)
+                       }
+
+                       if r.Xadd/4 > 0x7fffff || r.Xadd/4 < -0x800000 {
+                               ld.Errorf(s, "direct call too far %d", r.Xadd/4)
+                       }
+
+                       return int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32(r.Xadd/4)))), true
+               }
+
+               return -1, false
+       }
+
+       switch r.Type {
+       case objabi.R_CONST:
+               return r.Add, true
+       case objabi.R_GOTOFF:
+               return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+
+       // 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 ld.Symaddr(ctxt.Syms.Lookup(".got.plt", 0)) < ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) {
+                       ld.Errorf(s, ".got.plt should be placed after .plt section.")
+               }
+               return 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add)) >> 20)), true
+       case objabi.R_PLT1: // add ip, ip, #0xYY000
+               return 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+4)) >> 12)), true
+       case objabi.R_PLT2: // ldr pc, [ip, #0xZZZ]!
+               return 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+8))), true
+       case objabi.R_CALLARM: // bl XXXXXX or b YYYYYY
+               // r.Add is the instruction
+               // low 24-bit encodes the target address
+               t := (ld.Symaddr(r.Sym) + int64(signext24(r.Add&0xffffff)*4) - (s.Value + int64(r.Off))) / 4
+               if t > 0x7fffff || t < -0x800000 {
+                       ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t)
+               }
+               return int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&t))), true
+       }
+
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       log.Fatalf("unexpected relocation variant")
+       return t
+}
+
+func addpltreloc(ctxt *ld.Link, plt *sym.Symbol, got *sym.Symbol, s *sym.Symbol, typ objabi.RelocType) {
+       r := plt.AddRel()
+       r.Sym = got
+       r.Off = int32(plt.Size)
+       r.Siz = 4
+       r.Type = typ
+       r.Add = int64(s.Got()) - 8
+
+       plt.Attr |= sym.AttrReachable
+       plt.Size += 4
+       plt.Grow(plt.Size)
+}
+
+func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Plt() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+
+       if ctxt.IsELF {
+               plt := ctxt.Syms.Lookup(".plt", 0)
+               got := ctxt.Syms.Lookup(".got.plt", 0)
+               rel := ctxt.Syms.Lookup(".rel.plt", 0)
+               if plt.Size == 0 {
+                       elfsetupplt(ctxt)
+               }
+
+               // .got entry
+               s.SetGot(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(ctxt.Arch, plt, 0)
+
+               // .plt entry, this depends on the .got entry
+               s.SetPlt(int32(plt.Size))
+
+               addpltreloc(ctxt, plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000
+               addpltreloc(ctxt, plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000
+               addpltreloc(ctxt, plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]!
+
+               // rel
+               rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got()))
+
+               rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_JUMP_SLOT)))
+       } else {
+               ld.Errorf(s, "addpltsym: unsupported binary format")
+       }
+}
+
+func addgotsyminternal(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Got() >= 0 {
+               return
+       }
+
+       got := ctxt.Syms.Lookup(".got", 0)
+       s.SetGot(int32(got.Size))
+
+       got.AddAddrPlus(ctxt.Arch, s, 0)
+
+       if ctxt.IsELF {
+       } else {
+               ld.Errorf(s, "addgotsyminternal: unsupported binary format")
+       }
+}
+
+func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Got() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+       got := ctxt.Syms.Lookup(".got", 0)
+       s.SetGot(int32(got.Size))
+       got.AddUint32(ctxt.Arch, 0)
+
+       if ctxt.IsELF {
+               rel := ctxt.Syms.Lookup(".rel", 0)
+               rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got()))
+               rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_GLOB_DAT)))
+       } else {
+               ld.Errorf(s, "addgotsym: unsupported binary format")
+       }
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       sect := ld.Segtext.Sections[0]
+       ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+       ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       for _, sect = range ld.Segtext.Sections[1:] {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+       if ld.Segrelrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       machlink := uint32(0)
+       if ctxt.HeadType == objabi.Hdarwin {
+               machlink = uint32(ld.Domacholink(ctxt))
+       }
+
+       /* output symbol table */
+       ld.Symsize = 0
+
+       ld.Lcsize = 0
+       symo := uint32(0)
+       if !*ld.FlagS {
+               // TODO: rationalize
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                               symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+                       }
+
+               case objabi.Hplan9:
+                       symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+
+               case objabi.Hdarwin:
+                       symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
+
+               case objabi.Hwindows:
+                       symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                       symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN))
+               }
+
+               ctxt.Out.SeekSet(int64(symo))
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               ld.Asmelfsym(ctxt)
+                               ctxt.Out.Flush()
+                               ctxt.Out.Write(ld.Elfstrdat)
+
+                               if ctxt.LinkMode == ld.LinkExternal {
+                                       ld.Elfemitreloc(ctxt)
+                               }
+                       }
+
+               case objabi.Hplan9:
+                       ld.Asmplan9sym(ctxt)
+                       ctxt.Out.Flush()
+
+                       sym := ctxt.Syms.Lookup("pclntab", 0)
+                       if sym != nil {
+                               ld.Lcsize = int32(len(sym.P))
+                               ctxt.Out.Write(sym.P)
+                               ctxt.Out.Flush()
+                       }
+
+               case objabi.Hwindows:
+                       // Do nothing
+
+               case objabi.Hdarwin:
+                       if ctxt.LinkMode == ld.LinkExternal {
+                               ld.Machoemitreloc(ctxt)
+                       }
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       default:
+       case objabi.Hplan9: /* plan 9 */
+               ctxt.Out.Write32b(0x647)                      /* magic */
+               ctxt.Out.Write32b(uint32(ld.Segtext.Filelen)) /* sizes */
+               ctxt.Out.Write32b(uint32(ld.Segdata.Filelen))
+               ctxt.Out.Write32b(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
+               ctxt.Out.Write32b(uint32(ld.Symsize))          /* nsyms */
+               ctxt.Out.Write32b(uint32(ld.Entryvalue(ctxt))) /* va of entry */
+               ctxt.Out.Write32b(0)
+               ctxt.Out.Write32b(uint32(ld.Lcsize))
+
+       case objabi.Hlinux,
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd:
+               ld.Asmbelf(ctxt, int64(symo))
+
+       case objabi.Hdarwin:
+               ld.Asmbmacho(ctxt)
+
+       case objabi.Hwindows:
+               ld.Asmbpe(ctxt)
+       }
+
+       ctxt.Out.Flush()
+       if *ld.FlagC {
+               fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
+               fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
+               fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
+               fmt.Printf("symsize=%d\n", ld.Symsize)
+               fmt.Printf("lcsize=%d\n", ld.Lcsize)
+               fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize))
+       }
+}
diff --git a/src/cmd/oldlink/internal/arm/l.go b/src/cmd/oldlink/internal/arm/l.go
new file mode 100644 (file)
index 0000000..a83d26b
--- /dev/null
@@ -0,0 +1,75 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/default/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/oldlink/internal/arm/obj.go b/src/cmd/oldlink/internal/arm/obj.go
new file mode 100644 (file)
index 0000000..1fe4b9c
--- /dev/null
@@ -0,0 +1,116 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+       arch := sys.ArchARM
+
+       theArch := ld.Arch{
+               Funcalign:  funcAlign,
+               Maxalign:   maxAlign,
+               Minalign:   minAlign,
+               Dwarfregsp: dwarfRegSP,
+               Dwarfreglr: dwarfRegLR,
+
+               Adddynrel:        adddynrel,
+               Archinit:         archinit,
+               Archreloc:        archreloc,
+               Archrelocvariant: archrelocvariant,
+               Trampoline:       trampoline,
+               Asmb:             asmb,
+               Asmb2:            asmb2,
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Machoreloc1:      machoreloc1,
+               PEreloc1:         pereloc1,
+
+               Linuxdynld:     "/lib/ld-linux.so.3", // 2 for OABI, 3 for EABI
+               Freebsddynld:   "/usr/libexec/ld-elf.so.1",
+               Openbsddynld:   "/usr/libexec/ld.so",
+               Netbsddynld:    "/libexec/ld.elf_so",
+               Dragonflydynld: "XXX",
+               Solarisdynld:   "XXX",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+       case objabi.Hplan9: /* plan 9 */
+               ld.HEADR = 32
+
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 4128
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+
+       case objabi.Hlinux, /* arm elf */
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd:
+               *ld.FlagD = false
+               // with dynamic linking
+               ld.Elfinit(ctxt)
+               ld.HEADR = ld.ELFRESERVE
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 0x10000
+               }
+
+       case objabi.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.Hwindows: /* PE executable */
+               // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+               return
+       }
+}
diff --git a/src/cmd/oldlink/internal/arm64/asm.go b/src/cmd/oldlink/internal/arm64/asm.go
new file mode 100644 (file)
index 0000000..a976712
--- /dev/null
@@ -0,0 +1,946 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "encoding/binary"
+       "fmt"
+       "log"
+)
+
+func gentext(ctxt *ld.Link) {
+       if !ctxt.DynlinkingGo() {
+               return
+       }
+       addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+       if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin {
+               // we're linking a module containing the runtime -> no need for
+               // an init function
+               return
+       }
+       addmoduledata.Attr |= sym.AttrReachable
+       initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
+       initfunc.Type = sym.STEXT
+       initfunc.Attr |= sym.AttrLocal
+       initfunc.Attr |= sym.AttrReachable
+       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()
+       rel.Off = 0
+       rel.Siz = 8
+       rel.Sym = ctxt.Moduledata
+       rel.Type = objabi.R_ADDRARM64
+
+       // 8:   14000000        b       0 <runtime.addmoduledata>
+       //      8: R_AARCH64_CALL26     runtime.addmoduledata
+       o(0x14000000)
+       rel = initfunc.AddRel()
+       rel.Off = 8
+       rel.Siz = 4
+       rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+       rel.Type = objabi.R_CALLARM64 // Really should be R_AARCH64_JUMP26 but doesn't seem to make any difference
+
+       if ctxt.BuildMode == ld.BuildModePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
+       ctxt.Textp = append(ctxt.Textp, initfunc)
+       initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
+       initarray_entry.Attr |= sym.AttrReachable
+       initarray_entry.Attr |= sym.AttrLocal
+       initarray_entry.Type = sym.SINITARR
+       initarray_entry.AddAddr(ctxt.Arch, initfunc)
+}
+
+func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       targ := r.Sym
+
+       switch r.Type {
+       default:
+               if r.Type >= objabi.ElfRelocOffset {
+                       ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
+                       return false
+               }
+
+       // Handle relocations found in ELF object files.
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL32):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_AARCH64_PREL32 relocation for dynamic symbol %s", targ.Name)
+               }
+               // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+               // sense and should be removed when someone has thought about it properly.
+               if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() {
+                       ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
+               }
+               r.Type = objabi.R_PCREL
+               r.Add += 4
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL64):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_AARCH64_PREL64 relocation for dynamic symbol %s", targ.Name)
+               }
+               if targ.Type == 0 || targ.Type == sym.SXREF {
+                       ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
+               }
+               r.Type = objabi.R_PCREL
+               r.Add += 8
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26),
+               objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26):
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add += int64(targ.Plt())
+               }
+               if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() {
+                       ld.Errorf(s, "unknown symbol %s in callarm64", targ.Name)
+               }
+               r.Type = 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 targ.Type != 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
+               addgotsym(ctxt, targ)
+
+               r.Type = objabi.R_ARM64_GOT
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += int64(targ.Got())
+               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 targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name)
+               }
+               if targ.Type == 0 || targ.Type == sym.SXREF {
+                       ld.Errorf(s, "unknown symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ARM64_PCREL
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ABS64):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_AARCH64_ABS64 relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ADDR
+               if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal {
+                       // 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 targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ARM64_LDST8
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST32_ABS_LO12_NC):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ARM64_LDST32
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST64_ABS_LO12_NC):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ARM64_LDST64
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST128_ABS_LO12_NC):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ARM64_LDST128
+               return true
+       }
+
+       switch r.Type {
+       case objabi.R_CALL,
+               objabi.R_PCREL,
+               objabi.R_CALLARM64:
+               if targ.Type != sym.SDYNIMPORT {
+                       // nothing to do, the relocation will be laid out in reloc
+                       return true
+               }
+               if ctxt.LinkMode == ld.LinkExternal {
+                       // External linker will do this relocation.
+                       return true
+               }
+
+       case objabi.R_ADDR:
+               if s.Type == sym.STEXT && ctxt.IsELF {
+                       // The code is asking for the address of an external
+                       // function. We provide it with the address of the
+                       // correspondent GOT symbol.
+                       addgotsym(ctxt, targ)
+
+                       r.Sym = ctxt.Syms.Lookup(".got", 0)
+                       r.Add += int64(targ.Got())
+                       return true
+               }
+
+               // Process dynamic relocations for the data sections.
+               if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal {
+                       // 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 s.Name {
+                       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 s.Type != sym.SDATA && s.Type != sym.SRODATA {
+                               break
+                       }
+               }
+
+               if ctxt.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 := ctxt.Syms.Lookup(".rela", 0)
+                       rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off))
+                       if r.Siz == 8 {
+                               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_AARCH64_RELATIVE)))
+                       } else {
+                               ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name)
+                       }
+                       rela.AddAddrPlus(ctxt.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 elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
+       ctxt.Out.Write64(uint64(sectoff))
+
+       elfsym := r.Xsym.ElfsymForReloc()
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               switch r.Siz {
+               case 4:
+                       ctxt.Out.Write64(uint64(elf.R_AARCH64_ABS32) | uint64(elfsym)<<32)
+               case 8:
+                       ctxt.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
+               ctxt.Out.Write64(uint64(elf.R_AARCH64_ADR_PREL_PG_HI21) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_AARCH64_ADD_ABS_LO12_NC) | uint64(elfsym)<<32)
+       case objabi.R_ARM64_TLS_LE:
+               ctxt.Out.Write64(uint64(elf.R_AARCH64_TLSLE_MOVW_TPREL_G0) | uint64(elfsym)<<32)
+       case objabi.R_ARM64_TLS_IE:
+               ctxt.Out.Write64(uint64(elf.R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) | uint64(elfsym)<<32)
+       case objabi.R_ARM64_GOTPCREL:
+               ctxt.Out.Write64(uint64(elf.R_AARCH64_ADR_GOT_PAGE) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_AARCH64_LD64_GOT_LO12_NC) | uint64(elfsym)<<32)
+       case objabi.R_CALLARM64:
+               if r.Siz != 4 {
+                       return false
+               }
+               ctxt.Out.Write64(uint64(elf.R_AARCH64_CALL26) | uint64(elfsym)<<32)
+
+       }
+       ctxt.Out.Write64(uint64(r.Xadd))
+
+       return true
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       var v uint32
+
+       rs := r.Xsym
+
+       if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALLARM64 || r.Type == objabi.R_ADDRARM64 {
+               if rs.Dynid < 0 {
+                       ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
+                       return false
+               }
+
+               v = uint32(rs.Dynid)
+               v |= 1 << 27 // external relocation
+       } else {
+               v = uint32(rs.Sect.Extnum)
+               if v == 0 {
+                       ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type)
+                       return false
+               }
+       }
+
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28
+       case objabi.R_CALLARM64:
+               if r.Xadd != 0 {
+                       ld.Errorf(s, "ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", rs.Name, r.Xadd)
+               }
+
+               v |= 1 << 24 // pc-relative bit
+               v |= ld.MACHO_ARM64_RELOC_BRANCH26 << 28
+       case objabi.R_ADDRARM64:
+               r.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(r.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(r.Xadd&0xffffff))
+               }
+               v |= 1 << 24 // pc-relative bit
+               v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
+       }
+
+       switch r.Siz {
+       default:
+               return false
+       case 1:
+               v |= 0 << 25
+       case 2:
+               v |= 1 << 25
+       case 4:
+               v |= 2 << 25
+       case 8:
+               v |= 3 << 25
+       }
+
+       out.Write32(uint32(sectoff))
+       out.Write32(v)
+       return true
+}
+
+func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       if ctxt.LinkMode == ld.LinkExternal {
+               switch r.Type {
+               default:
+                       return val, false
+               case objabi.R_ARM64_GOTPCREL:
+                       var o1, o2 uint32
+                       if ctxt.Arch.ByteOrder == binary.BigEndian {
+                               o1 = uint32(val >> 32)
+                               o2 = uint32(val)
+                       } else {
+                               o1 = uint32(val)
+                               o2 = uint32(val >> 32)
+                       }
+                       // Any relocation against a function symbol is redirected to
+                       // be against a local symbol instead (see putelfsym in
+                       // symtab.go) but unfortunately the system linker was buggy
+                       // when confronted with a R_AARCH64_ADR_GOT_PAGE relocation
+                       // against a local symbol until May 2015
+                       // (https://sourceware.org/bugzilla/show_bug.cgi?id=18270). So
+                       // we convert the adrp; ld64 + R_ARM64_GOTPCREL into adrp;
+                       // add + R_ADDRARM64.
+                       if !(r.Sym.IsFileLocal() || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && ctxt.DynlinkingGo() {
+                               if o2&0xffc00000 != 0xf9400000 {
+                                       ld.Errorf(s, "R_ARM64_GOTPCREL against unexpected instruction %x", o2)
+                               }
+                               o2 = 0x91000000 | (o2 & 0x000003ff)
+                               r.Type = objabi.R_ADDRARM64
+                       }
+                       if ctxt.Arch.ByteOrder == binary.BigEndian {
+                               val = int64(o1)<<32 | int64(o2)
+                       } else {
+                               val = int64(o2)<<32 | int64(o1)
+                       }
+                       fallthrough
+               case objabi.R_ADDRARM64:
+                       r.Done = false
+
+                       // set up addend for eventual relocation via outer symbol.
+                       rs := r.Sym
+                       r.Xadd = r.Add
+                       for rs.Outer != nil {
+                               r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer)
+                               rs = rs.Outer
+                       }
+
+                       if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil {
+                               ld.Errorf(s, "missing section for %s", rs.Name)
+                       }
+                       r.Xsym = rs
+
+                       // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation
+                       // will make the linking fail because it thinks the code is not PIC even though
+                       // the BR26 relocation should be fully resolved at link time.
+                       // That is the reason why the next if block is disabled. When the bug in ld64
+                       // is fixed, we can enable this block and also enable duff's device in cmd/7g.
+                       if false && ctxt.HeadType == objabi.Hdarwin {
+                               var o0, o1 uint32
+
+                               if ctxt.Arch.ByteOrder == binary.BigEndian {
+                                       o0 = uint32(val >> 32)
+                                       o1 = uint32(val)
+                               } else {
+                                       o0 = uint32(val)
+                                       o1 = uint32(val >> 32)
+                               }
+                               // Mach-O wants the addend to be encoded in the instruction
+                               // Note that although Mach-O supports ARM64_RELOC_ADDEND, it
+                               // can only encode 24-bit of signed addend, but the instructions
+                               // supports 33-bit of signed addend, so we always encode the
+                               // addend in place.
+                               o0 |= (uint32((r.Xadd>>12)&3) << 29) | (uint32((r.Xadd>>12>>2)&0x7ffff) << 5)
+                               o1 |= uint32(r.Xadd&0xfff) << 10
+                               r.Xadd = 0
+
+                               // when laid out, the instruction order must always be o1, o2.
+                               if ctxt.Arch.ByteOrder == binary.BigEndian {
+                                       val = int64(o0)<<32 | int64(o1)
+                               } else {
+                                       val = int64(o1)<<32 | int64(o0)
+                               }
+                       }
+
+                       return val, true
+               case objabi.R_CALLARM64,
+                       objabi.R_ARM64_TLS_LE,
+                       objabi.R_ARM64_TLS_IE:
+                       r.Done = false
+                       r.Xsym = r.Sym
+                       r.Xadd = r.Add
+                       return val, true
+               }
+       }
+
+       switch r.Type {
+       case objabi.R_CONST:
+               return r.Add, true
+
+       case objabi.R_GOTOFF:
+               return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+
+       case objabi.R_ADDRARM64:
+               t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+               if t >= 1<<32 || t < -1<<32 {
+                       ld.Errorf(s, "program too large, address relocation distance = %d", t)
+               }
+
+               var o0, o1 uint32
+
+               if ctxt.Arch.ByteOrder == binary.BigEndian {
+                       o0 = uint32(val >> 32)
+                       o1 = uint32(val)
+               } else {
+                       o0 = uint32(val)
+                       o1 = uint32(val >> 32)
+               }
+
+               o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5)
+               o1 |= uint32(t&0xfff) << 10
+
+               // when laid out, the instruction order must always be o1, o2.
+               if ctxt.Arch.ByteOrder == binary.BigEndian {
+                       return int64(o0)<<32 | int64(o1), true
+               }
+               return int64(o1)<<32 | int64(o0), true
+
+       case objabi.R_ARM64_TLS_LE:
+               r.Done = false
+               if ctxt.HeadType == objabi.Hdarwin {
+                       ld.Errorf(s, "TLS reloc on unsupported OS %v", ctxt.HeadType)
+               }
+               // The TCB is two pointers. This is not documented anywhere, but is
+               // de facto part of the ABI.
+               v := r.Sym.Value + int64(2*ctxt.Arch.PtrSize)
+               if v < 0 || v >= 32678 {
+                       ld.Errorf(s, "TLS offset out of range %d", v)
+               }
+               return val | (v << 5), true
+
+       case objabi.R_ARM64_TLS_IE:
+               if ctxt.BuildMode == ld.BuildModePIE && ctxt.IsELF {
+                       // We are linking the final executable, so we
+                       // can optimize any TLS IE relocation to LE.
+                       r.Done = false
+                       if ctxt.HeadType != objabi.Hlinux {
+                               ld.Errorf(s, "TLS reloc on unsupported OS %v", ctxt.HeadType)
+                       }
+
+                       // The TCB is two pointers. This is not documented anywhere, but is
+                       // de facto part of the ABI.
+                       v := ld.Symaddr(r.Sym) + int64(2*ctxt.Arch.PtrSize) + r.Add
+                       if v < 0 || v >= 32678 {
+                               ld.Errorf(s, "TLS offset out of range %d", v)
+                       }
+
+                       var o0, o1 uint32
+                       if ctxt.Arch.ByteOrder == binary.BigEndian {
+                               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 {
+                               ld.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 ctxt.Arch.ByteOrder == binary.BigEndian {
+                               return int64(o0)<<32 | int64(o1), true
+                       }
+                       return int64(o1)<<32 | int64(o0), true
+               } else {
+                       log.Fatalf("cannot handle R_ARM64_TLS_IE (sym %s) when linking internally", s.Name)
+               }
+
+       case objabi.R_CALLARM64:
+               var t int64
+               if r.Sym.Type == sym.SDYNIMPORT {
+                       t = (ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) + r.Add) - (s.Value + int64(r.Off))
+               } else {
+                       t = (ld.Symaddr(r.Sym) + r.Add) - (s.Value + int64(r.Off))
+               }
+               if t >= 1<<27 || t < -1<<27 {
+                       ld.Errorf(s, "program too large, call relocation distance = %d", t)
+               }
+               return val | ((t >> 2) & 0x03ffffff), true
+
+       case objabi.R_ARM64_GOT:
+               if s.P[r.Off+3]&0x9f == 0x90 {
+                       // R_AARCH64_ADR_GOT_PAGE
+                       // patch instruction: adrp
+                       t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+                       if t >= 1<<32 || t < -1<<32 {
+                               ld.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), true
+               } else if s.P[r.Off+3] == 0xf9 {
+                       // R_AARCH64_LD64_GOT_LO12_NC
+                       // patch instruction: ldr
+                       t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+                       if t&7 != 0 {
+                               ld.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)), true
+               } else {
+                       ld.Errorf(s, "unsupported instruction for %v R_GOTARM64", s.P[r.Off:r.Off+4])
+               }
+
+       case objabi.R_ARM64_PCREL:
+               if s.P[r.Off+3]&0x9f == 0x90 {
+                       // R_AARCH64_ADR_PREL_PG_HI21
+                       // patch instruction: adrp
+                       t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+                       if t >= 1<<32 || t < -1<<32 {
+                               ld.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), true
+               } else if s.P[r.Off+3]&0x91 == 0x91 {
+                       // R_AARCH64_ADD_ABS_LO12_NC
+                       // patch instruction: add
+                       t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+                       o1 := uint32(t&0xfff) << 10
+                       return val | int64(o1), true
+               } else {
+                       ld.Errorf(s, "unsupported instruction for %v R_PCRELARM64", s.P[r.Off:r.Off+4])
+               }
+
+       case objabi.R_ARM64_LDST8:
+               t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+               o0 := uint32(t&0xfff) << 10
+               return val | int64(o0), true
+
+       case objabi.R_ARM64_LDST32:
+               t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+               if t&3 != 0 {
+                       ld.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), true
+
+       case objabi.R_ARM64_LDST64:
+               t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+               if t&7 != 0 {
+                       ld.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), true
+
+       case objabi.R_ARM64_LDST128:
+               t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
+               if t&15 != 0 {
+                       ld.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), true
+       }
+
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       log.Fatalf("unexpected relocation variant")
+       return -1
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       plt := ctxt.Syms.Lookup(".plt", 0)
+       gotplt := ctxt.Syms.Lookup(".got.plt", 0)
+       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.AddAddrPlus4(gotplt, 16)
+               plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010)
+               plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT
+
+               // <imm> is the offset value of &got[2] to &got[0], the same below
+               // ldr     x17, [x16, <imm>]
+               plt.AddAddrPlus4(gotplt, 16)
+               plt.SetUint32(ctxt.Arch, plt.Size-4, 0xf9400211)
+               plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT
+
+               // add     x16, x16, <imm>
+               plt.AddAddrPlus4(gotplt, 16)
+               plt.SetUint32(ctxt.Arch, plt.Size-4, 0x91000210)
+               plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL
+
+               // 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 {
+                       ld.Errorf(gotplt, "got.plt is not empty at the very beginning")
+               }
+               gotplt.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+
+               gotplt.AddUint64(ctxt.Arch, 0)
+               gotplt.AddUint64(ctxt.Arch, 0)
+       }
+}
+
+func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Plt() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+
+       if ctxt.IsELF {
+               plt := ctxt.Syms.Lookup(".plt", 0)
+               gotplt := ctxt.Syms.Lookup(".got.plt", 0)
+               rela := ctxt.Syms.Lookup(".rela.plt", 0)
+               if plt.Size == 0 {
+                       elfsetupplt(ctxt)
+               }
+
+               // adrp    x16, &got.plt[0]
+               plt.AddAddrPlus4(gotplt, gotplt.Size)
+               plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010)
+               plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT
+
+               // <offset> is the offset value of &got.plt[n] to &got.plt[0]
+               // ldr     x17, [x16, <offset>]
+               plt.AddAddrPlus4(gotplt, gotplt.Size)
+               plt.SetUint32(ctxt.Arch, plt.Size-4, 0xf9400211)
+               plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT
+
+               // add     x16, x16, <offset>
+               plt.AddAddrPlus4(gotplt, gotplt.Size)
+               plt.SetUint32(ctxt.Arch, plt.Size-4, 0x91000210)
+               plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL
+
+               // br      x17
+               plt.AddUint32(ctxt.Arch, 0xd61f0220)
+
+               // add to got.plt: pointer to plt[0]
+               gotplt.AddAddrPlus(ctxt.Arch, plt, 0)
+
+               // rela
+               rela.AddAddrPlus(ctxt.Arch, gotplt, gotplt.Size-8)
+               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_JUMP_SLOT)))
+               rela.AddUint64(ctxt.Arch, 0)
+
+               s.SetPlt(int32(plt.Size - 16))
+       } else {
+               ld.Errorf(s, "addpltsym: unsupported binary format")
+       }
+}
+
+func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Got() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+       got := ctxt.Syms.Lookup(".got", 0)
+       s.SetGot(int32(got.Size))
+       got.AddUint64(ctxt.Arch, 0)
+
+       if ctxt.IsELF {
+               rela := ctxt.Syms.Lookup(".rela", 0)
+               rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got()))
+               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_GLOB_DAT)))
+               rela.AddUint64(ctxt.Arch, 0)
+       } else {
+               ld.Errorf(s, "addgotsym: unsupported binary format")
+       }
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       sect := ld.Segtext.Sections[0]
+       ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+       ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       for _, sect = range ld.Segtext.Sections[1:] {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+       if ld.Segrelrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       machlink := uint32(0)
+       if ctxt.HeadType == objabi.Hdarwin {
+               machlink = uint32(ld.Domacholink(ctxt))
+       }
+
+       /* output symbol table */
+       ld.Symsize = 0
+
+       ld.Lcsize = 0
+       symo := uint32(0)
+       if !*ld.FlagS {
+               // TODO: rationalize
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                               symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+                       }
+
+               case objabi.Hplan9:
+                       symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+
+               case objabi.Hdarwin:
+                       symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
+               }
+
+               ctxt.Out.SeekSet(int64(symo))
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               ld.Asmelfsym(ctxt)
+                               ctxt.Out.Flush()
+                               ctxt.Out.Write(ld.Elfstrdat)
+
+                               if ctxt.LinkMode == ld.LinkExternal {
+                                       ld.Elfemitreloc(ctxt)
+                               }
+                       }
+
+               case objabi.Hplan9:
+                       ld.Asmplan9sym(ctxt)
+                       ctxt.Out.Flush()
+
+                       sym := ctxt.Syms.Lookup("pclntab", 0)
+                       if sym != nil {
+                               ld.Lcsize = int32(len(sym.P))
+                               ctxt.Out.Write(sym.P)
+                               ctxt.Out.Flush()
+                       }
+
+               case objabi.Hdarwin:
+                       if ctxt.LinkMode == ld.LinkExternal {
+                               ld.Machoemitreloc(ctxt)
+                       }
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       default:
+       case objabi.Hplan9: /* plan 9 */
+               ctxt.Out.Write32(0x647)                      /* magic */
+               ctxt.Out.Write32(uint32(ld.Segtext.Filelen)) /* sizes */
+               ctxt.Out.Write32(uint32(ld.Segdata.Filelen))
+               ctxt.Out.Write32(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
+               ctxt.Out.Write32(uint32(ld.Symsize))          /* nsyms */
+               ctxt.Out.Write32(uint32(ld.Entryvalue(ctxt))) /* va of entry */
+               ctxt.Out.Write32(0)
+               ctxt.Out.Write32(uint32(ld.Lcsize))
+
+       case objabi.Hlinux,
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd:
+               ld.Asmbelf(ctxt, int64(symo))
+
+       case objabi.Hdarwin:
+               ld.Asmbmacho(ctxt)
+       }
+
+       ctxt.Out.Flush()
+       if *ld.FlagC {
+               fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
+               fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
+               fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
+               fmt.Printf("symsize=%d\n", ld.Symsize)
+               fmt.Printf("lcsize=%d\n", ld.Lcsize)
+               fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize))
+       }
+}
diff --git a/src/cmd/oldlink/internal/arm64/l.go b/src/cmd/oldlink/internal/arm64/l.go
new file mode 100644 (file)
index 0000000..50b88e4
--- /dev/null
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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 = 8
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+       dwarfRegSP = 31
+       dwarfRegLR = 30
+)
diff --git a/src/cmd/oldlink/internal/arm64/obj.go b/src/cmd/oldlink/internal/arm64/obj.go
new file mode 100644 (file)
index 0000000..2dcc999
--- /dev/null
@@ -0,0 +1,110 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+       arch := sys.ArchARM64
+
+       theArch := ld.Arch{
+               Funcalign:  funcAlign,
+               Maxalign:   maxAlign,
+               Minalign:   minAlign,
+               Dwarfregsp: dwarfRegSP,
+               Dwarfreglr: dwarfRegLR,
+
+               Adddynrel:        adddynrel,
+               Archinit:         archinit,
+               Archreloc:        archreloc,
+               Archrelocvariant: archrelocvariant,
+               Asmb:             asmb,
+               Asmb2:            asmb2,
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Machoreloc1:      machoreloc1,
+
+               Androiddynld: "/system/bin/linker64",
+               Linuxdynld:   "/lib/ld-linux-aarch64.so.1",
+
+               Freebsddynld:   "/usr/libexec/ld-elf.so.1",
+               Openbsddynld:   "/usr/libexec/ld.so",
+               Netbsddynld:    "/libexec/ld.elf_so",
+               Dragonflydynld: "XXX",
+               Solarisdynld:   "XXX",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+       case objabi.Hplan9: /* plan 9 */
+               ld.HEADR = 32
+
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+
+       case objabi.Hlinux, /* arm64 elf */
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd:
+               ld.Elfinit(ctxt)
+               ld.HEADR = ld.ELFRESERVE
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 0x10000
+               }
+
+       case objabi.Hdarwin: /* apple MACH */
+               ld.HEADR = ld.INITIAL_MACHO_HEADR
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/ar.go b/src/cmd/oldlink/internal/ld/ar.go
new file mode 100644 (file)
index 0000000..8df859f
--- /dev/null
@@ -0,0 +1,193 @@
+// Inferno utils/include/ar.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/include/ar.h
+//
+//     Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//     Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+//     Portions Copyright © 1997-1999 Vita Nuova Limited
+//     Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//     Portions Copyright © 2004,2006 Bruce Ellis
+//     Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+//     Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//     Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+       "cmd/internal/bio"
+       "cmd/internal/objabi"
+       "cmd/oldlink/internal/sym"
+       "encoding/binary"
+       "fmt"
+       "io"
+       "os"
+)
+
+const (
+       SARMAG  = 8
+       SAR_HDR = 16 + 44
+)
+
+const (
+       ARMAG = "!<arch>\n"
+)
+
+type ArHdr struct {
+       name string
+       date string
+       uid  string
+       gid  string
+       mode string
+       size string
+       fmag string
+}
+
+// hostArchive reads an archive file holding host objects and links in
+// required objects. The general format is the same as a Go archive
+// file, but it has an armap listing symbols and the objects that
+// define them. This is used for the compiler support library
+// libgcc.a.
+func hostArchive(ctxt *Link, name string) {
+       f, err := bio.Open(name)
+       if err != nil {
+               if os.IsNotExist(err) {
+                       // It's OK if we don't have a libgcc file at all.
+                       if ctxt.Debugvlog != 0 {
+                               ctxt.Logf("skipping libgcc file: %v\n", err)
+                       }
+                       return
+               }
+               Exitf("cannot open file %s: %v", name, err)
+       }
+       defer f.Close()
+
+       var magbuf [len(ARMAG)]byte
+       if _, err := io.ReadFull(f, magbuf[:]); err != nil {
+               Exitf("file %s too short", name)
+       }
+
+       if string(magbuf[:]) != ARMAG {
+               Exitf("%s is not an archive file", name)
+       }
+
+       var arhdr ArHdr
+       l := nextar(f, f.Offset(), &arhdr)
+       if l <= 0 {
+               Exitf("%s missing armap", name)
+       }
+
+       var armap archiveMap
+       if arhdr.name == "/" || arhdr.name == "/SYM64/" {
+               armap = readArmap(name, f, arhdr)
+       } else {
+               Exitf("%s missing armap", name)
+       }
+
+       loaded := make(map[uint64]bool)
+       any := true
+       for any {
+               var load []uint64
+               for _, s := range ctxt.Syms.Allsym {
+                       for i := range s.R {
+                               r := &s.R[i] // Copying sym.Reloc has measurable impact on performance
+                               if r.Sym != nil && r.Sym.Type == sym.SXREF {
+                                       if off := armap[r.Sym.Name]; off != 0 && !loaded[off] {
+                                               load = append(load, off)
+                                               loaded[off] = true
+                                       }
+                               }
+                       }
+               }
+
+               for _, off := range load {
+                       l := nextar(f, int64(off), &arhdr)
+                       if l <= 0 {
+                               Exitf("%s missing archive entry at offset %d", name, off)
+                       }
+                       pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
+                       l = atolwhex(arhdr.size)
+
+                       libgcc := sym.Library{Pkg: "libgcc"}
+                       h := ldobj(ctxt, f, &libgcc, l, pname, name)
+                       f.MustSeek(h.off, 0)
+                       h.ld(ctxt, f, h.pkg, h.length, h.pn)
+               }
+
+               any = len(load) > 0
+       }
+}
+
+// archiveMap is an archive symbol map: a mapping from symbol name to
+// offset within the archive file.
+type archiveMap map[string]uint64
+
+// readArmap reads the archive symbol map.
+func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap {
+       is64 := arhdr.name == "/SYM64/"
+       wordSize := 4
+       if is64 {
+               wordSize = 8
+       }
+
+       contents := make([]byte, atolwhex(arhdr.size))
+       if _, err := io.ReadFull(f, contents); err != nil {
+               Exitf("short read from %s", filename)
+       }
+
+       var c uint64
+       if is64 {
+               c = binary.BigEndian.Uint64(contents)
+       } else {
+               c = uint64(binary.BigEndian.Uint32(contents))
+       }
+       contents = contents[wordSize:]
+
+       ret := make(archiveMap)
+
+       names := contents[c*uint64(wordSize):]
+       for i := uint64(0); i < c; i++ {
+               n := 0
+               for names[n] != 0 {
+                       n++
+               }
+               name := string(names[:n])
+               names = names[n+1:]
+
+               // For Mach-O and PE/386 files we strip a leading
+               // underscore from the symbol name.
+               if objabi.GOOS == "darwin" || (objabi.GOOS == "windows" && objabi.GOARCH == "386") {
+                       if name[0] == '_' && len(name) > 1 {
+                               name = name[1:]
+                       }
+               }
+
+               var off uint64
+               if is64 {
+                       off = binary.BigEndian.Uint64(contents)
+               } else {
+                       off = uint64(binary.BigEndian.Uint32(contents))
+               }
+               contents = contents[wordSize:]
+
+               ret[name] = off
+       }
+
+       return ret
+}
diff --git a/src/cmd/oldlink/internal/ld/config.go b/src/cmd/oldlink/internal/ld/config.go
new file mode 100644 (file)
index 0000000..2373b50
--- /dev/null
@@ -0,0 +1,272 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "fmt"
+       "log"
+)
+
+// A BuildMode indicates the sort of object we are building.
+//
+// Possible build modes are the same as those for the -buildmode flag
+// in cmd/go, and are documented in 'go help buildmode'.
+type BuildMode uint8
+
+const (
+       BuildModeUnset BuildMode = iota
+       BuildModeExe
+       BuildModePIE
+       BuildModeCArchive
+       BuildModeCShared
+       BuildModeShared
+       BuildModePlugin
+)
+
+func (mode *BuildMode) Set(s string) error {
+       badmode := func() error {
+               return fmt.Errorf("buildmode %s not supported on %s/%s", s, objabi.GOOS, objabi.GOARCH)
+       }
+       switch s {
+       default:
+               return fmt.Errorf("invalid buildmode: %q", s)
+       case "exe":
+               *mode = BuildModeExe
+       case "pie":
+               switch objabi.GOOS {
+               case "aix", "android", "linux", "windows":
+               case "darwin", "freebsd":
+                       switch objabi.GOARCH {
+                       case "amd64":
+                       default:
+                               return badmode()
+                       }
+               default:
+                       return badmode()
+               }
+               *mode = BuildModePIE
+       case "c-archive":
+               switch objabi.GOOS {
+               case "aix", "darwin", "linux":
+               case "freebsd":
+                       switch objabi.GOARCH {
+                       case "amd64":
+                       default:
+                               return badmode()
+                       }
+               case "windows":
+                       switch objabi.GOARCH {
+                       case "amd64", "386", "arm":
+                       default:
+                               return badmode()
+                       }
+               default:
+                       return badmode()
+               }
+               *mode = BuildModeCArchive
+       case "c-shared":
+               switch objabi.GOARCH {
+               case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
+               default:
+                       return badmode()
+               }
+               *mode = BuildModeCShared
+       case "shared":
+               switch objabi.GOOS {
+               case "linux":
+                       switch objabi.GOARCH {
+                       case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
+                       default:
+                               return badmode()
+                       }
+               default:
+                       return badmode()
+               }
+               *mode = BuildModeShared
+       case "plugin":
+               switch objabi.GOOS {
+               case "linux":
+                       switch objabi.GOARCH {
+                       case "386", "amd64", "arm", "arm64", "s390x", "ppc64le":
+                       default:
+                               return badmode()
+                       }
+               case "darwin", "freebsd":
+                       switch objabi.GOARCH {
+                       case "amd64":
+                       default:
+                               return badmode()
+                       }
+               default:
+                       return badmode()
+               }
+               *mode = BuildModePlugin
+       }
+       return nil
+}
+
+func (mode *BuildMode) String() string {
+       switch *mode {
+       case BuildModeUnset:
+               return "" // avoid showing a default in usage message
+       case BuildModeExe:
+               return "exe"
+       case BuildModePIE:
+               return "pie"
+       case BuildModeCArchive:
+               return "c-archive"
+       case BuildModeCShared:
+               return "c-shared"
+       case BuildModeShared:
+               return "shared"
+       case BuildModePlugin:
+               return "plugin"
+       }
+       return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
+}
+
+// LinkMode indicates whether an external linker is used for the final link.
+type LinkMode uint8
+
+const (
+       LinkAuto LinkMode = iota
+       LinkInternal
+       LinkExternal
+)
+
+func (mode *LinkMode) Set(s string) error {
+       switch s {
+       default:
+               return fmt.Errorf("invalid linkmode: %q", s)
+       case "auto":
+               *mode = LinkAuto
+       case "internal":
+               *mode = LinkInternal
+       case "external":
+               *mode = LinkExternal
+       }
+       return nil
+}
+
+func (mode *LinkMode) String() string {
+       switch *mode {
+       case LinkAuto:
+               return "auto"
+       case LinkInternal:
+               return "internal"
+       case LinkExternal:
+               return "external"
+       }
+       return fmt.Sprintf("LinkMode(%d)", uint8(*mode))
+}
+
+// mustLinkExternal reports whether the program being linked requires
+// the external linker be used to complete the link.
+func mustLinkExternal(ctxt *Link) (res bool, reason string) {
+       if ctxt.Debugvlog > 1 {
+               defer func() {
+                       if res {
+                               log.Printf("external linking is forced by: %s\n", reason)
+                       }
+               }()
+       }
+
+       if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) {
+               return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH)
+       }
+
+       if *flagMsan {
+               return true, "msan"
+       }
+
+       // Internally linking cgo is incomplete on some architectures.
+       // https://golang.org/issue/14449
+       // https://golang.org/issue/21961
+       if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64) {
+               return true, objabi.GOARCH + " does not support internal cgo"
+       }
+       if iscgo && objabi.GOOS == "android" {
+               return true, objabi.GOOS + " does not support internal cgo"
+       }
+
+       // When the race flag is set, the LLVM tsan relocatable file is linked
+       // into the final binary, which means external linking is required because
+       // internal linking does not support it.
+       if *flagRace && ctxt.Arch.InFamily(sys.PPC64) {
+               return true, "race on " + objabi.GOARCH
+       }
+
+       // Some build modes require work the internal linker cannot do (yet).
+       switch ctxt.BuildMode {
+       case BuildModeCArchive:
+               return true, "buildmode=c-archive"
+       case BuildModeCShared:
+               return true, "buildmode=c-shared"
+       case BuildModePIE:
+               switch objabi.GOOS + "/" + objabi.GOARCH {
+               case "linux/amd64", "linux/arm64", "android/arm64":
+               case "windows/386", "windows/amd64", "windows/arm":
+               default:
+                       // Internal linking does not support TLS_IE.
+                       return true, "buildmode=pie"
+               }
+       case BuildModePlugin:
+               return true, "buildmode=plugin"
+       case BuildModeShared:
+               return true, "buildmode=shared"
+       }
+       if ctxt.linkShared {
+               return true, "dynamically linking with a shared library"
+       }
+
+       return false, ""
+}
+
+// determineLinkMode sets ctxt.LinkMode.
+//
+// It is called after flags are processed and inputs are processed,
+// so the ctxt.LinkMode variable has an initial value from the -linkmode
+// flag and the iscgo externalobj variables are set.
+func determineLinkMode(ctxt *Link) {
+       extNeeded, extReason := mustLinkExternal(ctxt)
+       via := ""
+
+       if ctxt.LinkMode == LinkAuto {
+               // The environment variable GO_EXTLINK_ENABLED controls the
+               // default value of -linkmode. If it is not set when the
+               // linker is called we take the value it was set to when
+               // cmd/link was compiled. (See make.bash.)
+               switch objabi.Getgoextlinkenabled() {
+               case "0":
+                       ctxt.LinkMode = LinkInternal
+                       via = "via GO_EXTLINK_ENABLED "
+               case "1":
+                       ctxt.LinkMode = LinkExternal
+                       via = "via GO_EXTLINK_ENABLED "
+               default:
+                       if extNeeded || (iscgo && externalobj) {
+                               ctxt.LinkMode = LinkExternal
+                       } else {
+                               ctxt.LinkMode = LinkInternal
+                       }
+               }
+       }
+
+       switch ctxt.LinkMode {
+       case LinkInternal:
+               if extNeeded {
+                       Exitf("internal linking requested %sbut external linking required: %s", via, extReason)
+               }
+       case LinkExternal:
+               switch {
+               case objabi.GOARCH == "riscv64":
+                       Exitf("external linking not supported for %s/riscv64", objabi.GOOS)
+               case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
+                       Exitf("external linking not supported for %s/ppc64", objabi.GOOS)
+               }
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/data.go b/src/cmd/oldlink/internal/ld/data.go
new file mode 100644 (file)
index 0000000..3c78896
--- /dev/null
@@ -0,0 +1,2505 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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 (
+       "bufio"
+       "bytes"
+       "cmd/internal/gcprog"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "compress/zlib"
+       "encoding/binary"
+       "fmt"
+       "log"
+       "os"
+       "sort"
+       "strconv"
+       "strings"
+       "sync"
+)
+
+// isRuntimeDepPkg reports whether pkg is the runtime package or its dependency
+func isRuntimeDepPkg(pkg string) bool {
+       switch pkg {
+       case "runtime",
+               "sync/atomic",      // runtime may call to sync/atomic, due to go:linkname
+               "internal/bytealg", // for IndexByte
+               "internal/cpu":     // for cpu features
+               return true
+       }
+       return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test")
+}
+
+// Estimate the max size needed to hold any new trampolines created for this function. This
+// is used to determine when the section can be split if it becomes too large, to ensure that
+// the trampolines are in the same section as the function that uses them.
+func maxSizeTrampolinesPPC64(s *sym.Symbol, 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)
+       for ri := range s.R {
+               r := &s.R[ri]
+               if r.Type.IsDirectCallOrJump() {
+                       n++
+               }
+       }
+       // Trampolines in ppc64 are 4 instructions.
+       return n * 16
+}
+
+// detect too-far jumps in function s, and add trampolines if necessary
+// ARM, PPC64 & PPC64LE support trampoline insertion for internal and external linking
+// On PPC64 & PPC64LE the text sections might be split but will still insert trampolines
+// where necessary.
+func trampoline(ctxt *Link, s *sym.Symbol) {
+       if thearch.Trampoline == nil {
+               return // no need or no support of trampolines on this arch
+       }
+
+       for ri := range s.R {
+               r := &s.R[ri]
+               if !r.Type.IsDirectCallOrJump() {
+                       continue
+               }
+               if Symaddr(r.Sym) == 0 && (r.Sym.Type != sym.SDYNIMPORT && r.Sym.Type != sym.SUNDEFEXT) {
+                       if r.Sym.File != s.File {
+                               if !isRuntimeDepPkg(s.File) || !isRuntimeDepPkg(r.Sym.File) {
+                                       ctxt.ErrorUnresolved(s, r)
+                               }
+                               // runtime and its dependent packages may call to each other.
+                               // they are fine, as they will be laid down together.
+                       }
+                       continue
+               }
+
+               thearch.Trampoline(ctxt, r, s)
+       }
+
+}
+
+// relocsym resolve relocations in "s". 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 relocsym(ctxt *Link, s *sym.Symbol) {
+       if len(s.R) == 0 {
+               return
+       }
+       if s.Attr.ReadOnly() {
+               // The symbol's content is backed by read-only memory.
+               // Copy it to writable memory to apply relocations.
+               s.P = append([]byte(nil), s.P...)
+               s.Attr.Set(sym.AttrReadOnly, false)
+       }
+       for ri := int32(0); ri < int32(len(s.R)); ri++ {
+               r := &s.R[ri]
+               if r.Done {
+                       // Relocation already processed by an earlier phase.
+                       continue
+               }
+               r.Done = true
+               off := r.Off
+               siz := int32(r.Siz)
+               if off < 0 || off+siz > int32(len(s.P)) {
+                       rname := ""
+                       if r.Sym != nil {
+                               rname = r.Sym.Name
+                       }
+                       Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(s.P))
+                       continue
+               }
+
+               if r.Sym != nil && ((r.Sym.Type == sym.Sxxx && !r.Sym.Attr.VisibilityHidden()) || r.Sym.Type == sym.SXREF) {
+                       // When putting the runtime but not main into a shared library
+                       // these symbols are undefined and that's OK.
+                       if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin {
+                               if r.Sym.Name == "main.main" || (ctxt.BuildMode != BuildModePlugin && r.Sym.Name == "main..inittask") {
+                                       r.Sym.Type = sym.SDYNIMPORT
+                               } else if strings.HasPrefix(r.Sym.Name, "go.info.") {
+                                       // Skip go.info symbols. They are only needed to communicate
+                                       // DWARF info between the compiler and linker.
+                                       continue
+                               }
+                       } else {
+                               ctxt.ErrorUnresolved(s, r)
+                               continue
+                       }
+               }
+
+               if r.Type >= objabi.ElfRelocOffset {
+                       continue
+               }
+               if r.Siz == 0 { // informational relocation - no work to do
+                       continue
+               }
+
+               // We need to be able to reference dynimport symbols when linking against
+               // shared libraries, and Solaris, Darwin and AIX need it always
+               if ctxt.HeadType != objabi.Hsolaris && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Haix && r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT && !ctxt.DynlinkingGo() && !r.Sym.Attr.SubSymbol() {
+                       if !(ctxt.Arch.Family == sys.PPC64 && ctxt.LinkMode == LinkExternal && r.Sym.Name == ".TOC.") {
+                               Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", r.Sym.Name, r.Sym.Type, r.Sym.Type, r.Type, sym.RelocName(ctxt.Arch, r.Type))
+                       }
+               }
+               if r.Sym != nil && r.Sym.Type != sym.STLSBSS && r.Type != objabi.R_WEAKADDROFF && !r.Sym.Attr.Reachable() {
+                       Errorf(s, "unreachable sym in relocation: %s", r.Sym.Name)
+               }
+
+               if ctxt.LinkMode == LinkExternal {
+                       r.InitExt()
+               }
+
+               // TODO(mundaym): remove this special case - see issue 14218.
+               if ctxt.Arch.Family == sys.S390X {
+                       switch r.Type {
+                       case objabi.R_PCRELDBL:
+                               r.InitExt()
+                               r.Type = objabi.R_PCREL
+                               r.Variant = sym.RV_390_DBL
+                       case objabi.R_CALL:
+                               r.InitExt()
+                               r.Variant = sym.RV_390_DBL
+                       }
+               }
+
+               var o int64
+               switch r.Type {
+               default:
+                       switch siz {
+                       default:
+                               Errorf(s, "bad reloc size %#x for %s", uint32(siz), r.Sym.Name)
+                       case 1:
+                               o = int64(s.P[off])
+                       case 2:
+                               o = int64(ctxt.Arch.ByteOrder.Uint16(s.P[off:]))
+                       case 4:
+                               o = int64(ctxt.Arch.ByteOrder.Uint32(s.P[off:]))
+                       case 8:
+                               o = int64(ctxt.Arch.ByteOrder.Uint64(s.P[off:]))
+                       }
+                       if offset, ok := thearch.Archreloc(ctxt, r, s, o); ok {
+                               o = offset
+                       } else {
+                               Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type))
+                       }
+               case objabi.R_TLS_LE:
+                       if ctxt.LinkMode == LinkExternal && ctxt.IsELF {
+                               r.Done = false
+                               if r.Sym == nil {
+                                       r.Sym = ctxt.Tlsg
+                               }
+                               r.Xsym = r.Sym
+                               r.Xadd = r.Add
+                               o = 0
+                               if ctxt.Arch.Family != sys.AMD64 {
+                                       o = r.Add
+                               }
+                               break
+                       }
+
+                       if ctxt.IsELF && ctxt.Arch.Family == sys.ARM {
+                               // 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 + r.Sym.Value
+                       } else if ctxt.IsELF || ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hdarwin {
+                               o = int64(ctxt.Tlsoffset) + r.Add
+                       } else if ctxt.HeadType == objabi.Hwindows {
+                               o = r.Add
+                       } else {
+                               log.Fatalf("unexpected R_TLS_LE relocation for %v", ctxt.HeadType)
+                       }
+               case objabi.R_TLS_IE:
+                       if ctxt.LinkMode == LinkExternal && ctxt.IsELF {
+                               r.Done = false
+                               if r.Sym == nil {
+                                       r.Sym = ctxt.Tlsg
+                               }
+                               r.Xsym = r.Sym
+                               r.Xadd = r.Add
+                               o = 0
+                               if ctxt.Arch.Family != sys.AMD64 {
+                                       o = r.Add
+                               }
+                               break
+                       }
+                       if ctxt.BuildMode == BuildModePIE && ctxt.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", ctxt.Arch.Family)
+                               }
+                               thearch.TLSIEtoLE(s, int(off), int(r.Siz))
+                               o = int64(ctxt.Tlsoffset)
+                               // TODO: o += r.Add when ctxt.Arch.Family != sys.AMD64?
+                               // Why do we treat r.Add differently on AMD64?
+                               // Is the external linker using Xadd at all?
+                       } else {
+                               log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", s.Name)
+                       }
+               case objabi.R_ADDR:
+                       if ctxt.LinkMode == LinkExternal && r.Sym.Type != sym.SCONST {
+                               r.Done = false
+
+                               // set up addend for eventual relocation via outer symbol.
+                               rs := r.Sym
+
+                               r.Xadd = r.Add
+                               for rs.Outer != nil {
+                                       r.Xadd += Symaddr(rs) - Symaddr(rs.Outer)
+                                       rs = rs.Outer
+                               }
+
+                               if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Type != sym.SUNDEFEXT && rs.Sect == nil {
+                                       Errorf(s, "missing section for relocation target %s", rs.Name)
+                               }
+                               r.Xsym = rs
+
+                               o = r.Xadd
+                               if ctxt.IsELF {
+                                       if ctxt.Arch.Family == sys.AMD64 {
+                                               o = 0
+                                       }
+                               } else if ctxt.HeadType == objabi.Hdarwin {
+                                       if rs.Type != sym.SHOSTOBJ {
+                                               o += Symaddr(rs)
+                                       }
+                               } else if ctxt.HeadType == objabi.Hwindows {
+                                       // nothing to do
+                               } else if ctxt.HeadType == objabi.Haix {
+                                       o = Symaddr(r.Sym) + r.Add
+                               } else {
+                                       Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.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 ctxt.HeadType == objabi.Haix && r.Sym.Type != 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 s.Sect.Seg == &Segdata {
+                                       Xcoffadddynrel(ctxt, s, r)
+                               }
+                       }
+
+                       o = Symaddr(r.Sym) + 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 && ctxt.Arch.PtrSize > 4 && siz == 4 {
+                               Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", r.Sym.Name, uint64(o), Symaddr(r.Sym), r.Add)
+                               errorexit()
+                       }
+               case objabi.R_DWARFSECREF:
+                       if r.Sym.Sect == nil {
+                               Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name)
+                       }
+
+                       if ctxt.LinkMode == LinkExternal {
+                               r.Done = false
+
+                               // 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 ctxt.HeadType == objabi.Hdarwin {
+                                       r.Done = true
+                               }
+
+                               // PE code emits IMAGE_REL_I386_SECREL and IMAGE_REL_AMD64_SECREL
+                               // for R_DWARFSECREF relocations, while R_ADDR is replaced with
+                               // IMAGE_REL_I386_DIR32, IMAGE_REL_AMD64_ADDR64 and IMAGE_REL_AMD64_ADDR32.
+                               // Do not replace R_DWARFSECREF with R_ADDR for windows -
+                               // let PE code emit correct relocations.
+                               if ctxt.HeadType != objabi.Hwindows {
+                                       r.Type = objabi.R_ADDR
+                               }
+
+                               r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0)
+                               r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr)
+
+                               o = r.Xadd
+                               if ctxt.IsELF && ctxt.Arch.Family == sys.AMD64 {
+                                       o = 0
+                               }
+                               break
+                       }
+                       o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
+               case objabi.R_WEAKADDROFF:
+                       if !r.Sym.Attr.Reachable() {
+                               continue
+                       }
+                       fallthrough
+               case objabi.R_ADDROFF:
+                       // The method offset tables using this relocation expect the offset to be relative
+                       // to the start of the first text section, even if there are multiple.
+                       if r.Sym.Sect.Name == ".text" {
+                               o = Symaddr(r.Sym) - int64(Segtext.Sections[0].Vaddr) + r.Add
+                       } else {
+                               o = Symaddr(r.Sym) - int64(r.Sym.Sect.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 = Symaddr(r.Sym) + r.Add - Symaddr(r.Sym.Unit.Textp[0])
+
+                       // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
+               case objabi.R_GOTPCREL:
+                       if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin && r.Sym != nil && r.Sym.Type != sym.SCONST {
+                               r.Done = false
+                               r.Xadd = r.Add
+                               r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk
+                               r.Xsym = r.Sym
+
+                               o = r.Xadd
+                               o += int64(r.Siz)
+                               break
+                       }
+                       fallthrough
+               case objabi.R_CALL, objabi.R_PCREL:
+                       if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT {
+                               // pass through to the external linker.
+                               r.Done = false
+                               r.Xadd = 0
+                               if ctxt.IsELF {
+                                       r.Xadd -= int64(r.Siz)
+                               }
+                               r.Xsym = r.Sym
+                               o = 0
+                               break
+                       }
+                       if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type != sym.SCONST && (r.Sym.Sect != s.Sect || r.Type == objabi.R_GOTPCREL) {
+                               r.Done = false
+
+                               // set up addend for eventual relocation via outer symbol.
+                               rs := r.Sym
+
+                               r.Xadd = r.Add
+                               for rs.Outer != nil {
+                                       r.Xadd += Symaddr(rs) - Symaddr(rs.Outer)
+                                       rs = rs.Outer
+                               }
+
+                               r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk
+                               if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil {
+                                       Errorf(s, "missing section for relocation target %s", rs.Name)
+                               }
+                               r.Xsym = rs
+
+                               o = r.Xadd
+                               if ctxt.IsELF {
+                                       if ctxt.Arch.Family == sys.AMD64 {
+                                               o = 0
+                                       }
+                               } else if ctxt.HeadType == objabi.Hdarwin {
+                                       if r.Type == objabi.R_CALL {
+                                               if ctxt.LinkMode == LinkExternal && rs.Type == sym.SDYNIMPORT {
+                                                       switch ctxt.Arch.Family {
+                                                       case sys.AMD64:
+                                                               // AMD64 dynamic relocations are relative to the end of the relocation.
+                                                               o += int64(r.Siz)
+                                                       case sys.I386:
+                                                               // I386 dynamic relocations are relative to the start of the section.
+                                                               o -= int64(r.Off)                         // offset in symbol
+                                                               o -= int64(s.Value - int64(s.Sect.Vaddr)) // offset of symbol in section
+                                                       }
+                                               } else {
+                                                       if rs.Type != sym.SHOSTOBJ {
+                                                               o += int64(uint64(Symaddr(rs)) - rs.Sect.Vaddr)
+                                                       }
+                                                       o -= int64(r.Off) // relative to section offset, not symbol
+                                               }
+                                       } else if ctxt.Arch.Family == sys.ARM {
+                                               // see ../arm/asm.go:/machoreloc1
+                                               o += Symaddr(rs) - s.Value - int64(r.Off)
+                                       } else {
+                                               o += int64(r.Siz)
+                                       }
+                               } else if ctxt.HeadType == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 { // 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(r.Siz)
+                               } else {
+                                       Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType)
+                               }
+
+                               break
+                       }
+
+                       o = 0
+                       if r.Sym != nil {
+                               o += Symaddr(r.Sym)
+                       }
+
+                       o += r.Add - (s.Value + int64(r.Off) + int64(r.Siz))
+               case objabi.R_SIZE:
+                       o = r.Sym.Size + r.Add
+
+               case objabi.R_XCOFFREF:
+                       if ctxt.HeadType != objabi.Haix {
+                               Errorf(s, "find XCOFF R_REF on non-XCOFF files")
+                       }
+                       if ctxt.LinkMode != LinkExternal {
+                               Errorf(s, "find XCOFF R_REF with internal linking")
+                       }
+                       r.Xsym = r.Sym
+                       r.Xadd = r.Add
+                       r.Done = false
+
+                       // This isn't a real relocation so it must not update
+                       // its offset value.
+                       continue
+
+               case objabi.R_DWARFFILEREF:
+                       // The final file index is saved in r.Add in dwarf.go:writelines.
+                       o = r.Add
+               }
+
+               if ctxt.Arch.Family == sys.PPC64 || ctxt.Arch.Family == sys.S390X {
+                       r.InitExt()
+                       if r.Variant != sym.RV_NONE {
+                               o = thearch.Archrelocvariant(ctxt, r, s, o)
+                       }
+               }
+
+               if false {
+                       nam := "<nil>"
+                       var addr int64
+                       if r.Sym != nil {
+                               nam = r.Sym.Name
+                               addr = Symaddr(r.Sym)
+                       }
+                       xnam := "<nil>"
+                       if r.Xsym != nil {
+                               xnam = r.Xsym.Name
+                       }
+                       fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x (xsym: %s +%#x) [type %d (%s)/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, addr, r.Add, xnam, r.Xadd, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Variant, o)
+               }
+               switch siz {
+               default:
+                       Errorf(s, "bad reloc size %#x for %s", uint32(siz), r.Sym.Name)
+                       fallthrough
+
+                       // TODO(rsc): Remove.
+               case 1:
+                       s.P[off] = byte(int8(o))
+               case 2:
+                       if o != int64(int16(o)) {
+                               Errorf(s, "relocation address for %s is too big: %#x", r.Sym.Name, o)
+                       }
+                       i16 := int16(o)
+                       ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16))
+               case 4:
+                       if r.Type == objabi.R_PCREL || r.Type == objabi.R_CALL {
+                               if o != int64(int32(o)) {
+                                       Errorf(s, "pc-relative relocation address for %s is too big: %#x", r.Sym.Name, o)
+                               }
+                       } else {
+                               if o != int64(int32(o)) && o != int64(uint32(o)) {
+                                       Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", r.Sym.Name, uint64(o))
+                               }
+                       }
+
+                       fl := int32(o)
+                       ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl))
+               case 8:
+                       ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o))
+               }
+       }
+}
+
+func (ctxt *Link) reloc() {
+       for _, s := range ctxt.Textp {
+               relocsym(ctxt, s)
+       }
+       for _, s := range datap {
+               relocsym(ctxt, s)
+       }
+       for _, s := range dwarfp {
+               relocsym(ctxt, s)
+       }
+}
+
+func windynrelocsym(ctxt *Link, rel, s *sym.Symbol) {
+       for ri := range s.R {
+               r := &s.R[ri]
+               targ := r.Sym
+               if targ == nil {
+                       continue
+               }
+               if !targ.Attr.Reachable() {
+                       if r.Type == objabi.R_WEAKADDROFF {
+                               continue
+                       }
+                       Errorf(s, "dynamic relocation to unreachable symbol %s", targ.Name)
+               }
+               if r.Sym.Plt() == -2 && r.Sym.Got() != -2 { // make dynimport JMP table for PE object files.
+                       targ.SetPlt(int32(rel.Size))
+                       r.Sym = rel
+                       r.Add = int64(targ.Plt())
+
+                       // jmp *addr
+                       switch ctxt.Arch.Family {
+                       default:
+                               Errorf(s, "unsupported arch %v", ctxt.Arch.Family)
+                               return
+                       case sys.I386:
+                               rel.AddUint8(0xff)
+                               rel.AddUint8(0x25)
+                               rel.AddAddr(ctxt.Arch, targ)
+                               rel.AddUint8(0x90)
+                               rel.AddUint8(0x90)
+                       case sys.AMD64:
+                               rel.AddUint8(0xff)
+                               rel.AddUint8(0x24)
+                               rel.AddUint8(0x25)
+                               rel.AddAddrPlus4(targ, 0)
+                               rel.AddUint8(0x90)
+                       }
+               } else if r.Sym.Plt() >= 0 {
+                       r.Sym = rel
+                       r.Add = int64(targ.Plt())
+               }
+       }
+}
+
+// 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.HeadType == objabi.Hwindows && iscgo && ctxt.LinkMode == LinkInternal) {
+               return
+       }
+
+       /* relocation table */
+       rel := ctxt.Syms.Lookup(".rel", 0)
+       rel.Attr |= sym.AttrReachable
+       rel.Type = sym.STEXT
+       ctxt.Textp = append(ctxt.Textp, rel)
+
+       for _, s := range ctxt.Textp {
+               if s == rel {
+                       continue
+               }
+               windynrelocsym(ctxt, rel, s)
+       }
+}
+
+func dynrelocsym(ctxt *Link, s *sym.Symbol) {
+       for ri := range s.R {
+               r := &s.R[ri]
+               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(ctxt, s, r)
+                       continue
+               }
+
+               if r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT || r.Type >= objabi.ElfRelocOffset {
+                       if r.Sym != nil && !r.Sym.Attr.Reachable() {
+                               Errorf(s, "dynamic relocation to unreachable symbol %s", r.Sym.Name)
+                       }
+                       if !thearch.Adddynrel(ctxt, s, r) {
+                               Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Sym.Type, r.Sym.Type)
+                       }
+               }
+       }
+}
+
+func dynreloc(ctxt *Link, data *[sym.SXREF][]*sym.Symbol) {
+       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 data {
+               for _, s := range syms {
+                       dynrelocsym(ctxt, s)
+               }
+       }
+       if ctxt.IsELF {
+               elfdynhash(ctxt)
+       }
+}
+
+func Codeblk(ctxt *Link, addr int64, size int64) {
+       CodeblkPad(ctxt, addr, size, zeros[:])
+}
+func CodeblkPad(ctxt *Link, addr int64, size int64, pad []byte) {
+       if *flagA {
+               ctxt.Logf("codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset())
+       }
+
+       blk(ctxt.Out, ctxt.Textp, addr, size, pad)
+
+       /* again for printing */
+       if !*flagA {
+               return
+       }
+
+       syms := ctxt.Textp
+       for i, s := range syms {
+               if !s.Attr.Reachable() {
+                       continue
+               }
+               if s.Value >= addr {
+                       syms = syms[i:]
+                       break
+               }
+       }
+
+       eaddr := addr + size
+       for _, s := range syms {
+               if !s.Attr.Reachable() {
+                       continue
+               }
+               if s.Value >= eaddr {
+                       break
+               }
+
+               if addr < s.Value {
+                       ctxt.Logf("%-20s %.8x|", "_", uint64(addr))
+                       for ; addr < s.Value; addr++ {
+                               ctxt.Logf(" %.2x", 0)
+                       }
+                       ctxt.Logf("\n")
+               }
+
+               ctxt.Logf("%.6x\t%-20s\n", uint64(addr), s.Name)
+               q := s.P
+
+               for len(q) >= 16 {
+                       ctxt.Logf("%.6x\t% x\n", uint64(addr), q[:16])
+                       addr += 16
+                       q = q[16:]
+               }
+
+               if len(q) > 0 {
+                       ctxt.Logf("%.6x\t% x\n", uint64(addr), q)
+                       addr += int64(len(q))
+               }
+       }
+
+       if addr < eaddr {
+               ctxt.Logf("%-20s %.8x|", "_", uint64(addr))
+               for ; addr < eaddr; addr++ {
+                       ctxt.Logf(" %.2x", 0)
+               }
+       }
+}
+
+func blk(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) {
+       for i, s := range syms {
+               if !s.Attr.SubSymbol() && s.Value >= addr {
+                       syms = syms[i:]
+                       break
+               }
+       }
+
+       // 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 s.Attr.SubSymbol() {
+                       continue
+               }
+               if s.Value >= eaddr {
+                       break
+               }
+               if s.Value < addr {
+                       Errorf(s, "phase error: addr=%#x but sym=%#x type=%d", addr, s.Value, s.Type)
+                       errorexit()
+               }
+               if addr < s.Value {
+                       out.WriteStringPad("", int(s.Value-addr), pad)
+                       addr = s.Value
+               }
+               out.WriteSym(s)
+               addr += int64(len(s.P))
+               if addr < s.Value+s.Size {
+                       out.WriteStringPad("", int(s.Value+s.Size-addr), pad)
+                       addr = s.Value + s.Size
+               }
+               if addr != s.Value+s.Size {
+                       Errorf(s, "phase error: addr=%#x value+size=%#x", addr, s.Value+s.Size)
+                       errorexit()
+               }
+               if s.Value+s.Size >= eaddr {
+                       break
+               }
+       }
+
+       if addr < eaddr {
+               out.WriteStringPad("", int(eaddr-addr), pad)
+       }
+       out.Flush()
+}
+
+func Datblk(ctxt *Link, addr int64, size int64) {
+       writeDatblkToOutBuf(ctxt, ctxt.Out, addr, size)
+}
+
+// Used only on Wasm for now.
+func DatblkBytes(ctxt *Link, addr int64, size int64) []byte {
+       buf := bytes.NewBuffer(make([]byte, 0, size))
+       out := &OutBuf{w: bufio.NewWriter(buf)}
+       writeDatblkToOutBuf(ctxt, out, addr, size)
+       out.Flush()
+       return buf.Bytes()
+}
+
+func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) {
+       if *flagA {
+               ctxt.Logf("datblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset())
+       }
+
+       blk(out, datap, addr, size, zeros[:])
+
+       /* again for printing */
+       if !*flagA {
+               return
+       }
+
+       syms := datap
+       for i, sym := range syms {
+               if sym.Value >= addr {
+                       syms = syms[i:]
+                       break
+               }
+       }
+
+       eaddr := addr + size
+       for _, sym := range syms {
+               if sym.Value >= eaddr {
+                       break
+               }
+               if addr < sym.Value {
+                       ctxt.Logf("\t%.8x| 00 ...\n", uint64(addr))
+                       addr = sym.Value
+               }
+
+               ctxt.Logf("%s\n\t%.8x|", sym.Name, uint64(addr))
+               for i, b := range sym.P {
+                       if i > 0 && i%16 == 0 {
+                               ctxt.Logf("\n\t%.8x|", uint64(addr)+uint64(i))
+                       }
+                       ctxt.Logf(" %.2x", b)
+               }
+
+               addr += int64(len(sym.P))
+               for ; addr < sym.Value+sym.Size; addr++ {
+                       ctxt.Logf(" %.2x", 0)
+               }
+               ctxt.Logf("\n")
+
+               if ctxt.LinkMode != LinkExternal {
+                       continue
+               }
+               for i := range sym.R {
+                       r := &sym.R[i] // Copying sym.Reloc has measurable impact on performance
+                       rsname := ""
+                       rsval := int64(0)
+                       if r.Sym != nil {
+                               rsname = r.Sym.Name
+                               rsval = r.Sym.Value
+                       }
+                       typ := "?"
+                       switch r.Type {
+                       case objabi.R_ADDR:
+                               typ = "addr"
+                       case objabi.R_PCREL:
+                               typ = "pcrel"
+                       case objabi.R_CALL:
+                               typ = "call"
+                       }
+                       ctxt.Logf("\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, r.Add, rsval+r.Add)
+               }
+       }
+
+       if addr < eaddr {
+               ctxt.Logf("\t%.8x| 00 ...\n", uint(addr))
+       }
+       ctxt.Logf("\t%.8x|\n", uint(eaddr))
+}
+
+func Dwarfblk(ctxt *Link, addr int64, size int64) {
+       if *flagA {
+               ctxt.Logf("dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset())
+       }
+
+       blk(ctxt.Out, dwarfp, addr, size, zeros[:])
+}
+
+var zeros [512]byte
+
+var (
+       strdata  = make(map[string]string)
+       strnames []string
+)
+
+func addstrdata1(ctxt *Link, arg string) {
+       eq := strings.Index(arg, "=")
+       dot := strings.LastIndex(arg[:eq+1], ".")
+       if eq < 0 || dot < 0 {
+               Exitf("-X flag requires argument of the form importpath.name=value")
+       }
+       pkg := arg[:dot]
+       if ctxt.BuildMode == BuildModePlugin && pkg == "main" {
+               pkg = *flagPluginPath
+       }
+       pkg = objabi.PathToPrefix(pkg)
+       name := pkg + arg[dot:eq]
+       value := arg[eq+1:]
+       if _, ok := strdata[name]; !ok {
+               strnames = append(strnames, name)
+       }
+       strdata[name] = value
+}
+
+// addstrdata sets the initial value of the string variable name to value.
+func addstrdata(ctxt *Link, name, value string) {
+       s := ctxt.Syms.ROLookup(name, 0)
+       if s == nil || s.Gotype == nil {
+               // Not defined in the loaded packages.
+               return
+       }
+       if s.Gotype.Name != "type.string" {
+               Errorf(s, "cannot set with -X: not a var of type string (%s)", s.Gotype.Name)
+               return
+       }
+       if s.Type == sym.SBSS {
+               s.Type = sym.SDATA
+       }
+
+       p := fmt.Sprintf("%s.str", s.Name)
+       sp := ctxt.Syms.Lookup(p, 0)
+
+       Addstring(sp, value)
+       sp.Type = sym.SRODATA
+
+       s.Size = 0
+       s.P = s.P[:0]
+       if s.Attr.ReadOnly() {
+               s.P = make([]byte, 0, ctxt.Arch.PtrSize*2)
+               s.Attr.Set(sym.AttrReadOnly, false)
+       }
+       s.R = s.R[:0]
+       reachable := s.Attr.Reachable()
+       s.AddAddr(ctxt.Arch, sp)
+       s.AddUint(ctxt.Arch, uint64(len(value)))
+
+       // addstring, addaddr, etc., mark the symbols as reachable.
+       // In this case that is not necessarily true, so stick to what
+       // we know before entering this function.
+       s.Attr.Set(sym.AttrReachable, reachable)
+
+       sp.Attr.Set(sym.AttrReachable, reachable)
+}
+
+func (ctxt *Link) dostrdata() {
+       for _, name := range strnames {
+               addstrdata(ctxt, name, strdata[name])
+       }
+}
+
+func Addstring(s *sym.Symbol, str string) int64 {
+       if s.Type == 0 {
+               s.Type = sym.SNOPTRDATA
+       }
+       s.Attr |= sym.AttrReachable
+       r := s.Size
+       if s.Name == ".shstrtab" {
+               elfsetstring(s, str, int(r))
+       }
+       s.P = append(s.P, str...)
+       s.P = append(s.P, 0)
+       s.Size = int64(len(s.P))
+       return r
+}
+
+// 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, s *sym.Symbol, symname, str string) {
+       sdata := ctxt.Syms.Lookup(symname, 0)
+       if sdata.Type != sym.Sxxx {
+               Errorf(s, "duplicate symname in addgostring: %s", symname)
+       }
+       sdata.Attr |= sym.AttrReachable
+       sdata.Attr |= sym.AttrLocal
+       sdata.Type = sym.SRODATA
+       sdata.Size = int64(len(str))
+       sdata.P = []byte(str)
+       s.AddAddr(ctxt.Arch, sdata)
+       s.AddUint(ctxt.Arch, uint64(len(str)))
+}
+
+func addinitarrdata(ctxt *Link, s *sym.Symbol) {
+       p := s.Name + ".ptr"
+       sp := ctxt.Syms.Lookup(p, 0)
+       sp.Type = sym.SINITARR
+       sp.Size = 0
+       sp.Attr |= sym.AttrDuplicateOK
+       sp.AddAddr(ctxt.Arch, s)
+}
+
+// symalign returns the required alignment for the given symbol s.
+func symalign(s *sym.Symbol) int32 {
+       min := int32(thearch.Minalign)
+       if s.Align >= min {
+               return s.Align
+       } else if s.Align != 0 {
+               return min
+       }
+       if strings.HasPrefix(s.Name, "go.string.") || strings.HasPrefix(s.Name, "type..namedata.") {
+               // String data is just bytes.
+               // If we align it, we waste a lot of space to padding.
+               return min
+       }
+       align := int32(thearch.Maxalign)
+       for int64(align) > s.Size && align > min {
+               align >>= 1
+       }
+       s.Align = align
+       return align
+}
+
+func aligndatsize(datsize int64, s *sym.Symbol) int64 {
+       return Rnd(datsize, int64(symalign(s)))
+}
+
+const debugGCProg = false
+
+type GCProg struct {
+       ctxt *Link
+       sym  *sym.Symbol
+       w    gcprog.Writer
+}
+
+func (p *GCProg) Init(ctxt *Link, name string) {
+       p.ctxt = ctxt
+       p.sym = ctxt.Syms.Lookup(name, 0)
+       p.w.Init(p.writeByte(ctxt))
+       if debugGCProg {
+               fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name)
+               p.w.Debug(os.Stderr)
+       }
+}
+
+func (p *GCProg) writeByte(ctxt *Link) 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 *sym.Symbol) {
+       typ := s.Gotype
+       // 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 == nil {
+               switch s.Name {
+               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
+               }
+               Errorf(s, "missing Go type information for global symbol: size %d", s.Size)
+               return
+       }
+
+       ptrsize := int64(p.ctxt.Arch.PtrSize)
+       nptr := decodetypePtrdata(p.ctxt.Arch, typ.P) / ptrsize
+
+       if debugGCProg {
+               fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr)
+       }
+
+       if decodetypeUsegcprog(p.ctxt.Arch, typ.P) == 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(s.Value/ptrsize + i)
+                       }
+               }
+               return
+       }
+
+       // Copy program.
+       prog := decodetypeGcprog(p.ctxt, typ)
+       p.w.ZeroUntil(s.Value / ptrsize)
+       p.w.Append(prog[4:], nptr)
+}
+
+// dataSortKey is used to sort a slice of data symbol *sym.Symbol pointers.
+// The sort keys are kept inline to improve cache behavior while sorting.
+type dataSortKey struct {
+       size int64
+       name string
+       sym  *sym.Symbol
+}
+
+type bySizeAndName []dataSortKey
+
+func (d bySizeAndName) Len() int      { return len(d) }
+func (d bySizeAndName) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
+func (d bySizeAndName) Less(i, j int) bool {
+       s1, s2 := d[i], d[j]
+       if s1.size != s2.size {
+               return s1.size < s2.size
+       }
+       return s1.name < s2.name
+}
+
+// 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 checkdatsize(ctxt *Link, datsize int64, symn sym.SymKind) {
+       if datsize > cutoff {
+               Errorf(nil, "too much data in section %v (over %v bytes)", symn, cutoff)
+       }
+}
+
+// datap is a collection of reachable data symbols in address order.
+// Generated by dodata.
+var datap []*sym.Symbol
+
+func (ctxt *Link) dodata() {
+       if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+               // 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.
+               bss := ctxt.Syms.Lookup("runtime.bss", 0)
+               bss.Size = 8
+               bss.Attr.Set(sym.AttrSpecial, false)
+
+               ctxt.Syms.Lookup("runtime.ebss", 0).Attr.Set(sym.AttrSpecial, false)
+
+               data := ctxt.Syms.Lookup("runtime.data", 0)
+               data.Size = 8
+               data.Attr.Set(sym.AttrSpecial, false)
+
+               edata := ctxt.Syms.Lookup("runtime.edata", 0)
+               edata.Attr.Set(sym.AttrSpecial, false)
+               if ctxt.HeadType == objabi.Haix {
+                       // XCOFFTOC symbols are part of .data section.
+                       edata.Type = sym.SXCOFFTOC
+               }
+
+               types := ctxt.Syms.Lookup("runtime.types", 0)
+               types.Type = sym.STYPE
+               types.Size = 8
+               types.Attr.Set(sym.AttrSpecial, false)
+
+               etypes := ctxt.Syms.Lookup("runtime.etypes", 0)
+               etypes.Type = sym.SFUNCTAB
+               etypes.Attr.Set(sym.AttrSpecial, false)
+
+               if ctxt.HeadType == objabi.Haix {
+                       rodata := ctxt.Syms.Lookup("runtime.rodata", 0)
+                       rodata.Type = sym.SSTRING
+                       rodata.Size = 8
+                       rodata.Attr.Set(sym.AttrSpecial, false)
+
+                       ctxt.Syms.Lookup("runtime.erodata", 0).Attr.Set(sym.AttrSpecial, false)
+
+               }
+       }
+
+       // Collect data symbols by type into data.
+       var data [sym.SXREF][]*sym.Symbol
+       for _, s := range ctxt.Syms.Allsym {
+               if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() {
+                       continue
+               }
+               if s.Type <= sym.STEXT || s.Type >= sym.SXREF {
+                       continue
+               }
+               data[s.Type] = append(data[s.Type], s)
+       }
+
+       // 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)
+       }
+       dynreloc(ctxt, &data)
+
+       if ctxt.UseRelro() {
+               // "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.
+               for _, symnro := range sym.ReadOnly {
+                       symnrelro := sym.RelROMap[symnro]
+
+                       ro := []*sym.Symbol{}
+                       relro := data[symnrelro]
+
+                       for _, s := range data[symnro] {
+                               isRelro := len(s.R) > 0
+                               switch s.Type {
+                               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 ctxt.HeadType == objabi.Haix && s.Name == "runtime.etypes" {
+                                               // runtime.etypes must be at the end of
+                                               // the relro datas.
+                                               isRelro = true
+                                       }
+                               }
+                               if isRelro {
+                                       s.Type = symnrelro
+                                       if s.Outer != nil {
+                                               s.Outer.Type = s.Type
+                                       }
+                                       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 s.Outer != nil && s.Outer.Type != s.Type {
+                                       Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)",
+                                               s.Outer.Name, s.Type, s.Outer.Type)
+                               }
+                       }
+
+                       data[symnro] = ro
+                       data[symnrelro] = relro
+               }
+       }
+
+       // Sort symbols.
+       var dataMaxAlign [sym.SXREF]int32
+       var wg sync.WaitGroup
+       for symn := range data {
+               symn := sym.SymKind(symn)
+               wg.Add(1)
+               go func() {
+                       data[symn], dataMaxAlign[symn] = dodataSect(ctxt, symn, data[symn])
+                       wg.Done()
+               }()
+       }
+       wg.Wait()
+
+       if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal {
+               // These symbols must have the same alignment as their section.
+               // Otherwize, ld might change the layout of Go sections.
+               ctxt.Syms.ROLookup("runtime.data", 0).Align = dataMaxAlign[sym.SDATA]
+               ctxt.Syms.ROLookup("runtime.bss", 0).Align = dataMaxAlign[sym.SBSS]
+       }
+
+       // 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.
+       datsize := int64(0)
+
+       // 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 {
+               for _, s := range data[symn] {
+                       sect := addsection(ctxt.Arch, &Segdata, s.Name, 06)
+                       sect.Align = symalign(s)
+                       datsize = Rnd(datsize, int64(sect.Align))
+                       sect.Vaddr = uint64(datsize)
+                       s.Sect = sect
+                       s.Type = sym.SDATA
+                       s.Value = int64(uint64(datsize) - sect.Vaddr)
+                       datsize += s.Size
+                       sect.Length = uint64(datsize) - sect.Vaddr
+               }
+               checkdatsize(ctxt, datsize, symn)
+       }
+
+       // .got (and .toc on ppc64)
+       if len(data[sym.SELFGOT]) > 0 {
+               sect := addsection(ctxt.Arch, &Segdata, ".got", 06)
+               sect.Align = dataMaxAlign[sym.SELFGOT]
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               for _, s := range data[sym.SELFGOT] {
+                       datsize = aligndatsize(datsize, s)
+                       s.Sect = sect
+                       s.Type = sym.SDATA
+                       s.Value = int64(uint64(datsize) - sect.Vaddr)
+
+                       // Resolve .TOC. symbol for this object file (ppc64)
+                       toc := ctxt.Syms.ROLookup(".TOC.", int(s.Version))
+                       if toc != nil {
+                               toc.Sect = sect
+                               toc.Outer = s
+                               toc.Sub = s.Sub
+                               s.Sub = toc
+
+                               toc.Value = 0x8000
+                       }
+
+                       datsize += s.Size
+               }
+               checkdatsize(ctxt, datsize, sym.SELFGOT)
+               sect.Length = uint64(datsize) - sect.Vaddr
+       }
+
+       /* pointer-free data */
+       sect := addsection(ctxt.Arch, &Segdata, ".noptrdata", 06)
+       sect.Align = dataMaxAlign[sym.SNOPTRDATA]
+       datsize = Rnd(datsize, int64(sect.Align))
+       sect.Vaddr = uint64(datsize)
+       ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect = sect
+       ctxt.Syms.Lookup("runtime.enoptrdata", 0).Sect = sect
+       for _, s := range data[sym.SNOPTRDATA] {
+               datsize = aligndatsize(datsize, s)
+               s.Sect = sect
+               s.Type = sym.SDATA
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+       }
+       checkdatsize(ctxt, datsize, sym.SNOPTRDATA)
+       sect.Length = uint64(datsize) - sect.Vaddr
+
+       hasinitarr := ctxt.linkShared
+
+       /* shared library initializer */
+       switch ctxt.BuildMode {
+       case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin:
+               hasinitarr = true
+       }
+
+       if ctxt.HeadType == objabi.Haix {
+               if len(data[sym.SINITARR]) > 0 {
+                       Errorf(nil, "XCOFF format doesn't allow .init_array section")
+               }
+       }
+
+       if hasinitarr && len(data[sym.SINITARR]) > 0 {
+               sect := addsection(ctxt.Arch, &Segdata, ".init_array", 06)
+               sect.Align = dataMaxAlign[sym.SINITARR]
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               for _, s := range data[sym.SINITARR] {
+                       datsize = aligndatsize(datsize, s)
+                       s.Sect = sect
+                       s.Value = int64(uint64(datsize) - sect.Vaddr)
+                       datsize += s.Size
+               }
+               sect.Length = uint64(datsize) - sect.Vaddr
+               checkdatsize(ctxt, datsize, sym.SINITARR)
+       }
+
+       /* data */
+       sect = addsection(ctxt.Arch, &Segdata, ".data", 06)
+       sect.Align = dataMaxAlign[sym.SDATA]
+       datsize = Rnd(datsize, int64(sect.Align))
+       sect.Vaddr = uint64(datsize)
+       ctxt.Syms.Lookup("runtime.data", 0).Sect = sect
+       ctxt.Syms.Lookup("runtime.edata", 0).Sect = sect
+       var gc GCProg
+       gc.Init(ctxt, "runtime.gcdata")
+       for _, s := range data[sym.SDATA] {
+               s.Sect = sect
+               s.Type = sym.SDATA
+               datsize = aligndatsize(datsize, s)
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               gc.AddSym(s)
+               datsize += s.Size
+       }
+       gc.End(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.
+       for _, s := range data[sym.SXCOFFTOC] {
+               s.Sect = sect
+               s.Type = sym.SDATA
+               datsize = aligndatsize(datsize, s)
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+       }
+       checkdatsize(ctxt, datsize, sym.SDATA)
+       sect.Length = uint64(datsize) - sect.Vaddr
+
+       /* bss */
+       sect = addsection(ctxt.Arch, &Segdata, ".bss", 06)
+       sect.Align = dataMaxAlign[sym.SBSS]
+       datsize = Rnd(datsize, int64(sect.Align))
+       sect.Vaddr = uint64(datsize)
+       ctxt.Syms.Lookup("runtime.bss", 0).Sect = sect
+       ctxt.Syms.Lookup("runtime.ebss", 0).Sect = sect
+       gc = GCProg{}
+       gc.Init(ctxt, "runtime.gcbss")
+       for _, s := range data[sym.SBSS] {
+               s.Sect = sect
+               datsize = aligndatsize(datsize, s)
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               gc.AddSym(s)
+               datsize += s.Size
+       }
+       checkdatsize(ctxt, datsize, sym.SBSS)
+       sect.Length = uint64(datsize) - sect.Vaddr
+       gc.End(int64(sect.Length))
+
+       /* pointer-free bss */
+       sect = addsection(ctxt.Arch, &Segdata, ".noptrbss", 06)
+       sect.Align = dataMaxAlign[sym.SNOPTRBSS]
+       datsize = Rnd(datsize, int64(sect.Align))
+       sect.Vaddr = uint64(datsize)
+       ctxt.Syms.Lookup("runtime.noptrbss", 0).Sect = sect
+       ctxt.Syms.Lookup("runtime.enoptrbss", 0).Sect = sect
+       for _, s := range data[sym.SNOPTRBSS] {
+               datsize = aligndatsize(datsize, s)
+               s.Sect = sect
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+       }
+       sect.Length = uint64(datsize) - sect.Vaddr
+       ctxt.Syms.Lookup("runtime.end", 0).Sect = sect
+       checkdatsize(ctxt, datsize, sym.SNOPTRBSS)
+
+       // Coverage instrumentation counters for libfuzzer.
+       if len(data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 {
+               sect := addsection(ctxt.Arch, &Segdata, "__libfuzzer_extra_counters", 06)
+               sect.Align = dataMaxAlign[sym.SLIBFUZZER_EXTRA_COUNTER]
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               for _, s := range data[sym.SLIBFUZZER_EXTRA_COUNTER] {
+                       datsize = aligndatsize(datsize, s)
+                       s.Sect = sect
+                       s.Value = int64(uint64(datsize) - sect.Vaddr)
+                       datsize += s.Size
+               }
+               sect.Length = uint64(datsize) - sect.Vaddr
+               checkdatsize(ctxt, datsize, sym.SLIBFUZZER_EXTRA_COUNTER)
+       }
+
+       if len(data[sym.STLSBSS]) > 0 {
+               var sect *sym.Section
+               if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) {
+                       sect = addsection(ctxt.Arch, &Segdata, ".tbss", 06)
+                       sect.Align = int32(ctxt.Arch.PtrSize)
+                       sect.Vaddr = 0
+               }
+               datsize = 0
+
+               for _, s := range data[sym.STLSBSS] {
+                       datsize = aligndatsize(datsize, s)
+                       s.Sect = sect
+                       s.Value = datsize
+                       datsize += s.Size
+               }
+               checkdatsize(ctxt, datsize, sym.STLSBSS)
+
+               if sect != nil {
+                       sect.Length = uint64(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
+       }
+
+       datsize = 0
+
+       /* read-only executable ELF, Mach-O sections */
+       if len(data[sym.STEXT]) != 0 {
+               Errorf(nil, "dodata found an sym.STEXT symbol: %s", data[sym.STEXT][0].Name)
+       }
+       for _, s := range data[sym.SELFRXSECT] {
+               sect := addsection(ctxt.Arch, &Segtext, s.Name, 04)
+               sect.Align = symalign(s)
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               s.Sect = sect
+               s.Type = sym.SRODATA
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+               sect.Length = uint64(datsize) - sect.Vaddr
+               checkdatsize(ctxt, datsize, sym.SELFRXSECT)
+       }
+
+       /* read-only data */
+       sect = addsection(ctxt.Arch, segro, ".rodata", 04)
+
+       sect.Vaddr = 0
+       ctxt.Syms.Lookup("runtime.rodata", 0).Sect = sect
+       ctxt.Syms.Lookup("runtime.erodata", 0).Sect = sect
+       if !ctxt.UseRelro() {
+               ctxt.Syms.Lookup("runtime.types", 0).Sect = sect
+               ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect
+       }
+       for _, symn := range sym.ReadOnly {
+               align := dataMaxAlign[symn]
+               if sect.Align < align {
+                       sect.Align = align
+               }
+       }
+       datsize = Rnd(datsize, int64(sect.Align))
+       for _, symn := range sym.ReadOnly {
+               symnStartValue := datsize
+               for _, s := range data[symn] {
+                       datsize = aligndatsize(datsize, s)
+                       s.Sect = sect
+                       s.Type = sym.SRODATA
+                       s.Value = int64(uint64(datsize) - sect.Vaddr)
+                       datsize += s.Size
+               }
+               checkdatsize(ctxt, datsize, symn)
+               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, datsize-symnStartValue, symn)
+               }
+       }
+       sect.Length = uint64(datsize) - sect.Vaddr
+
+       /* read-only ELF, Mach-O sections */
+       for _, s := range data[sym.SELFROSECT] {
+               sect = addsection(ctxt.Arch, segro, s.Name, 04)
+               sect.Align = symalign(s)
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               s.Sect = sect
+               s.Type = sym.SRODATA
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+               sect.Length = uint64(datsize) - sect.Vaddr
+       }
+       checkdatsize(ctxt, datsize, sym.SELFROSECT)
+
+       for _, s := range data[sym.SMACHOPLT] {
+               sect = addsection(ctxt.Arch, segro, s.Name, 04)
+               sect.Align = symalign(s)
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               s.Sect = sect
+               s.Type = sym.SRODATA
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+               sect.Length = uint64(datsize) - sect.Vaddr
+       }
+       checkdatsize(ctxt, datsize, sym.SMACHOPLT)
+
+       // 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.
+       addrelrosection := func(suffix string) *sym.Section {
+               return addsection(ctxt.Arch, segro, suffix, 04)
+       }
+
+       if ctxt.UseRelro() {
+               segrelro := &Segrelrodata
+               if ctxt.LinkMode == LinkExternal && ctxt.HeadType != objabi.Haix {
+                       // 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.
+                       datsize = 0
+               }
+
+               addrelrosection = func(suffix string) *sym.Section {
+                       return addsection(ctxt.Arch, segrelro, ".data.rel.ro"+suffix, 06)
+               }
+
+               /* data only written by relocations */
+               sect = addrelrosection("")
+
+               ctxt.Syms.Lookup("runtime.types", 0).Sect = sect
+               ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect
+
+               for _, symnro := range sym.ReadOnly {
+                       symn := sym.RelROMap[symnro]
+                       align := dataMaxAlign[symn]
+                       if sect.Align < align {
+                               sect.Align = align
+                       }
+               }
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+
+               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.
+                               datsize++
+                       }
+
+                       symn := sym.RelROMap[symnro]
+                       symnStartValue := datsize
+                       for _, s := range data[symn] {
+                               datsize = aligndatsize(datsize, s)
+                               if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect {
+                                       Errorf(s, "s.Outer (%s) in different section from s, %s != %s", s.Outer.Name, s.Outer.Sect.Name, sect.Name)
+                               }
+                               s.Sect = sect
+                               s.Type = sym.SRODATA
+                               s.Value = int64(uint64(datsize) - sect.Vaddr)
+                               datsize += s.Size
+                       }
+                       checkdatsize(ctxt, datsize, symn)
+                       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, datsize-symnStartValue, symn)
+                       }
+               }
+
+               sect.Length = uint64(datsize) - sect.Vaddr
+       }
+
+       /* typelink */
+       sect = addrelrosection(".typelink")
+       sect.Align = dataMaxAlign[sym.STYPELINK]
+       datsize = Rnd(datsize, int64(sect.Align))
+       sect.Vaddr = uint64(datsize)
+       typelink := ctxt.Syms.Lookup("runtime.typelink", 0)
+       typelink.Sect = sect
+       typelink.Type = sym.SRODATA
+       datsize += typelink.Size
+       checkdatsize(ctxt, datsize, sym.STYPELINK)
+       sect.Length = uint64(datsize) - sect.Vaddr
+
+       /* itablink */
+       sect = addrelrosection(".itablink")
+       sect.Align = dataMaxAlign[sym.SITABLINK]
+       datsize = Rnd(datsize, int64(sect.Align))
+       sect.Vaddr = uint64(datsize)
+       ctxt.Syms.Lookup("runtime.itablink", 0).Sect = sect
+       ctxt.Syms.Lookup("runtime.eitablink", 0).Sect = sect
+       for _, s := range data[sym.SITABLINK] {
+               datsize = aligndatsize(datsize, s)
+               s.Sect = sect
+               s.Type = sym.SRODATA
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+       }
+       checkdatsize(ctxt, datsize, sym.SITABLINK)
+       sect.Length = uint64(datsize) - sect.Vaddr
+       if ctxt.HeadType == objabi.Haix {
+               // Store .itablink size because its symbols are wrapped
+               // under an outer symbol: runtime.itablink.
+               xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SITABLINK)
+       }
+
+       /* gosymtab */
+       sect = addrelrosection(".gosymtab")
+       sect.Align = dataMaxAlign[sym.SSYMTAB]
+       datsize = Rnd(datsize, int64(sect.Align))
+       sect.Vaddr = uint64(datsize)
+       ctxt.Syms.Lookup("runtime.symtab", 0).Sect = sect
+       ctxt.Syms.Lookup("runtime.esymtab", 0).Sect = sect
+       for _, s := range data[sym.SSYMTAB] {
+               datsize = aligndatsize(datsize, s)
+               s.Sect = sect
+               s.Type = sym.SRODATA
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+       }
+       checkdatsize(ctxt, datsize, sym.SSYMTAB)
+       sect.Length = uint64(datsize) - sect.Vaddr
+
+       /* gopclntab */
+       sect = addrelrosection(".gopclntab")
+       sect.Align = dataMaxAlign[sym.SPCLNTAB]
+       datsize = Rnd(datsize, int64(sect.Align))
+       sect.Vaddr = uint64(datsize)
+       ctxt.Syms.Lookup("runtime.pclntab", 0).Sect = sect
+       ctxt.Syms.Lookup("runtime.epclntab", 0).Sect = sect
+       for _, s := range data[sym.SPCLNTAB] {
+               datsize = aligndatsize(datsize, s)
+               s.Sect = sect
+               s.Type = sym.SRODATA
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+       }
+       checkdatsize(ctxt, datsize, sym.SRODATA)
+       sect.Length = uint64(datsize) - sect.Vaddr
+
+       // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits.
+       if datsize != int64(uint32(datsize)) {
+               Errorf(nil, "read-only data segment too large: %d", datsize)
+       }
+
+       for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ {
+               datap = append(datap, data[symn]...)
+       }
+
+       dwarfGenerateDebugSyms(ctxt)
+
+       var i int
+       for ; i < len(dwarfp); i++ {
+               s := dwarfp[i]
+               if s.Type != sym.SDWARFSECT {
+                       break
+               }
+
+               sect = addsection(ctxt.Arch, &Segdwarf, s.Name, 04)
+               sect.Align = 1
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               s.Sect = sect
+               s.Type = sym.SRODATA
+               s.Value = int64(uint64(datsize) - sect.Vaddr)
+               datsize += s.Size
+               sect.Length = uint64(datsize) - sect.Vaddr
+       }
+       checkdatsize(ctxt, datsize, sym.SDWARFSECT)
+
+       for i < len(dwarfp) {
+               curType := dwarfp[i].Type
+               var sect *sym.Section
+               switch curType {
+               case sym.SDWARFINFO:
+                       sect = addsection(ctxt.Arch, &Segdwarf, ".debug_info", 04)
+               case sym.SDWARFRANGE:
+                       sect = addsection(ctxt.Arch, &Segdwarf, ".debug_ranges", 04)
+               case sym.SDWARFLOC:
+                       sect = addsection(ctxt.Arch, &Segdwarf, ".debug_loc", 04)
+               default:
+                       // Error is unrecoverable, so panic.
+                       panic(fmt.Sprintf("unknown DWARF section %v", curType))
+               }
+
+               sect.Align = 1
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               for ; i < len(dwarfp); i++ {
+                       s := dwarfp[i]
+                       if s.Type != curType {
+                               break
+                       }
+                       s.Sect = sect
+                       s.Type = sym.SRODATA
+                       s.Value = int64(uint64(datsize) - sect.Vaddr)
+                       s.Attr |= sym.AttrLocal
+                       datsize += s.Size
+
+                       if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC {
+                               // Update the size of .debug_loc for this symbol's
+                               // package.
+                               addDwsectCUSize(".debug_loc", s.File, uint64(s.Size))
+                       }
+               }
+               sect.Length = uint64(datsize) - sect.Vaddr
+               checkdatsize(ctxt, datsize, curType)
+       }
+
+       /* number the sections */
+       n := int32(1)
+
+       for _, sect := range Segtext.Sections {
+               sect.Extnum = int16(n)
+               n++
+       }
+       for _, sect := range Segrodata.Sections {
+               sect.Extnum = int16(n)
+               n++
+       }
+       for _, sect := range Segrelrodata.Sections {
+               sect.Extnum = int16(n)
+               n++
+       }
+       for _, sect := range Segdata.Sections {
+               sect.Extnum = int16(n)
+               n++
+       }
+       for _, sect := range Segdwarf.Sections {
+               sect.Extnum = int16(n)
+               n++
+       }
+}
+
+func dodataSect(ctxt *Link, symn sym.SymKind, syms []*sym.Symbol) (result []*sym.Symbol, maxAlign int32) {
+       if ctxt.HeadType == objabi.Hdarwin {
+               // Some symbols may no longer belong in syms
+               // due to movement in machosymorder.
+               newSyms := make([]*sym.Symbol, 0, len(syms))
+               for _, s := range syms {
+                       if s.Type == symn {
+                               newSyms = append(newSyms, s)
+                       }
+               }
+               syms = newSyms
+       }
+
+       var head, tail *sym.Symbol
+       symsSort := make([]dataSortKey, 0, len(syms))
+       for _, s := range syms {
+               if s.Attr.OnList() {
+                       log.Fatalf("symbol %s listed multiple times", s.Name)
+               }
+               s.Attr |= sym.AttrOnList
+               switch {
+               case s.Size < int64(len(s.P)):
+                       Errorf(s, "initialize bounds (%d < %d)", s.Size, len(s.P))
+               case s.Size < 0:
+                       Errorf(s, "negative size (%d bytes)", s.Size)
+               case s.Size > cutoff:
+                       Errorf(s, "symbol too large (%d bytes)", s.Size)
+               }
+
+               // 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 s.Name {
+                       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
+                       }
+               }
+
+               key := dataSortKey{
+                       size: s.Size,
+                       name: s.Name,
+                       sym:  s,
+               }
+
+               switch s.Type {
+               case sym.SELFGOT:
+                       // 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).
+                       key.size = 0
+               }
+
+               symsSort = append(symsSort, key)
+       }
+
+       sort.Sort(bySizeAndName(symsSort))
+
+       off := 0
+       if head != nil {
+               syms[0] = head
+               off++
+       }
+       for i, symSort := range symsSort {
+               syms[i+off] = symSort.sym
+               align := symalign(symSort.sym)
+               if maxAlign < align {
+                       maxAlign = align
+               }
+       }
+       if tail != nil {
+               syms[len(syms)-1] = tail
+       }
+
+       if ctxt.IsELF && symn == sym.SELFROSECT {
+               // Make .rela and .rela.plt contiguous, the ELF ABI requires this
+               // and Solaris actually cares.
+               reli, plti := -1, -1
+               for i, s := range syms {
+                       switch s.Name {
+                       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.
+                       rel.Align = int32(ctxt.Arch.RegSize)
+                       plt.Align = int32(ctxt.Arch.RegSize)
+               }
+       }
+
+       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
+       }
+
+       s := ctxt.Syms.Lookup("go.buildid", 0)
+       s.Attr |= sym.AttrReachable
+       // 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.Type = sym.STEXT
+       s.P = []byte(data)
+       s.Size = int64(len(s.P))
+
+       ctxt.Textp = append(ctxt.Textp, nil)
+       copy(ctxt.Textp[1:], ctxt.Textp)
+       ctxt.Textp[0] = s
+}
+
+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
+       }
+
+       s := ctxt.Syms.Lookup(".go.buildinfo", 0)
+       s.Attr |= sym.AttrReachable
+       s.Type = sym.SBUILDINFO
+       s.Align = 16
+       // The \xff is invalid UTF-8, meant to make it less likely
+       // to find one of these accidentally.
+       const prefix = "\xff Go buildinf:" // 14 bytes, plus 2 data bytes filled in below
+       data := make([]byte, 32)
+       copy(data, prefix)
+       data[len(prefix)] = byte(ctxt.Arch.PtrSize)
+       data[len(prefix)+1] = 0
+       if ctxt.Arch.ByteOrder == binary.BigEndian {
+               data[len(prefix)+1] = 1
+       }
+       s.P = data
+       s.Size = int64(len(s.P))
+       s1 := ctxt.Syms.Lookup("runtime.buildVersion", 0)
+       s2 := ctxt.Syms.Lookup("runtime.modinfo", 0)
+       s.R = []sym.Reloc{
+               {Off: 16, Siz: uint8(ctxt.Arch.PtrSize), Type: objabi.R_ADDR, Sym: s1},
+               {Off: 16 + int32(ctxt.Arch.PtrSize), Siz: uint8(ctxt.Arch.PtrSize), Type: objabi.R_ADDR, Sym: s2},
+       }
+}
+
+// assign addresses to text
+func (ctxt *Link) textaddress() {
+       addsection(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)
+
+       text := ctxt.Syms.Lookup("runtime.text", 0)
+       text.Sect = sect
+       if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal {
+               // Setting runtime.text has a real symbol prevents ld to
+               // change its base address resulting in wrong offsets for
+               // reflect methods.
+               text.Align = sect.Align
+               text.Size = 0x8
+       }
+
+       if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+               etext := ctxt.Syms.Lookup("runtime.etext", 0)
+               etext.Sect = sect
+
+               ctxt.Textp = append(ctxt.Textp, etext, nil)
+               copy(ctxt.Textp[1:], ctxt.Textp)
+               ctxt.Textp[0] = text
+       }
+
+       va := uint64(*FlagTextAddr)
+       n := 1
+       sect.Vaddr = va
+       ntramps := 0
+       for _, s := range ctxt.Textp {
+               sect, n, va = assignAddress(ctxt, sect, n, s, va, false)
+
+               trampoline(ctxt, s) // resolve jumps, may add trampolines if jump too far
+
+               // lay down trampolines after each function
+               for ; ntramps < len(ctxt.tramps); ntramps++ {
+                       tramp := ctxt.tramps[ntramps]
+                       if ctxt.HeadType == objabi.Haix && strings.HasPrefix(tramp.Name, "runtime.text.") {
+                               // Already set in assignAddress
+                               continue
+                       }
+                       sect, n, va = assignAddress(ctxt, sect, n, tramp, va, true)
+               }
+       }
+
+       sect.Length = va - sect.Vaddr
+       ctxt.Syms.Lookup("runtime.etext", 0).Sect = sect
+
+       // merge tramps into Textp, keeping Textp in address order
+       if ntramps != 0 {
+               newtextp := make([]*sym.Symbol, 0, len(ctxt.Textp)+ntramps)
+               i := 0
+               for _, s := range ctxt.Textp {
+                       for ; i < ntramps && ctxt.tramps[i].Value < s.Value; i++ {
+                               newtextp = append(newtextp, ctxt.tramps[i])
+                       }
+                       newtextp = append(newtextp, s)
+               }
+               newtextp = append(newtextp, ctxt.tramps[i:ntramps]...)
+
+               ctxt.Textp = newtextp
+       }
+}
+
+// assigns address for a text symbol, returns (possibly new) section, its number, and the address
+// Note: once we have trampoline insertion support for external linking, this function
+// will not need to create new text sections, and so no need to return sect and n.
+func assignAddress(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) {
+       if thearch.AssignAddress != nil {
+               return thearch.AssignAddress(ctxt, sect, n, s, va, isTramp)
+       }
+
+       s.Sect = sect
+       if s.Attr.SubSymbol() {
+               return sect, n, va
+       }
+       if s.Align != 0 {
+               va = uint64(Rnd(int64(va), int64(s.Align)))
+       } else {
+               va = uint64(Rnd(int64(va), int64(Funcalign)))
+       }
+
+       funcsize := uint64(MINFUNC) // spacing required for findfunctab
+       if s.Size > MINFUNC {
+               funcsize = uint64(s.Size)
+       }
+
+       // On ppc64x a text section should not be larger than 2^26 bytes due to the size of
+       // call target offset field in the bl instruction.  Splitting into smaller text
+       // sections smaller than this limit allows the GNU linker to modify the long calls
+       // appropriately.  The limit allows for the space needed for tables inserted by the linker.
+
+       // If this function doesn't fit in the current text section, then create a new one.
+
+       // Only break at outermost syms.
+
+       if ctxt.Arch.InFamily(sys.PPC64) && s.Outer == nil && ctxt.LinkMode == LinkExternal && va-sect.Vaddr+funcsize+maxSizeTrampolinesPPC64(s, isTramp) > 0x1c00000 {
+               // Set the length for the previous text section
+               sect.Length = va - sect.Vaddr
+
+               // Create new section, set the starting Vaddr
+               sect = addsection(ctxt.Arch, &Segtext, ".text", 05)
+               sect.Vaddr = va
+               s.Sect = sect
+
+               // Create a symbol for the start of the secondary text sections
+               ntext := ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+               ntext.Sect = sect
+               if ctxt.HeadType == objabi.Haix {
+                       // 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.Type = sym.STEXT
+                       ntext.Size = int64(MINFUNC)
+                       ntext.Attr |= sym.AttrReachable
+                       ntext.Attr |= sym.AttrOnList
+                       ctxt.tramps = append(ctxt.tramps, ntext)
+
+                       ntext.Value = int64(va)
+                       va += uint64(ntext.Size)
+
+                       if s.Align != 0 {
+                               va = uint64(Rnd(int64(va), int64(s.Align)))
+                       } else {
+                               va = uint64(Rnd(int64(va), int64(Funcalign)))
+                       }
+               }
+               n++
+       }
+
+       s.Value = 0
+       for sub := s; sub != nil; sub = sub.Sub {
+               sub.Value += int64(va)
+       }
+
+       va += funcsize
+
+       return sect, n, va
+}
+
+// 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 _, s := range Segtext.Sections {
+               va = uint64(Rnd(int64(va), int64(s.Align)))
+               s.Vaddr = va
+               va += s.Length
+       }
+
+       Segtext.Length = va - uint64(*FlagTextAddr)
+
+       if len(Segrodata.Sections) > 0 {
+               // align to page boundary so as not to mix
+               // rodata and executable text.
+               //
+               // Note: gold or GNU ld will reduce the size of the executable
+               // file by arranging for the relro segment to end at a page
+               // boundary, and overlap the end of the text segment with the
+               // start of the relro segment in the file.  The PT_LOAD segments
+               // will be such that the last page of the text segment will be
+               // mapped twice, once r-x and once starting out rw- and, after
+               // relocation processing, changed to r--.
+               //
+               // Ideally the last page of the text segment would not be
+               // writable even for this short period.
+               va = uint64(Rnd(int64(va), int64(*FlagRound)))
+
+               order = append(order, &Segrodata)
+               Segrodata.Rwx = 04
+               Segrodata.Vaddr = va
+               for _, s := range Segrodata.Sections {
+                       va = uint64(Rnd(int64(va), int64(s.Align)))
+                       s.Vaddr = va
+                       va += s.Length
+               }
+
+               Segrodata.Length = va - Segrodata.Vaddr
+       }
+       if len(Segrelrodata.Sections) > 0 {
+               // align to page boundary so as not to mix
+               // rodata, rel-ro data, and executable text.
+               va = uint64(Rnd(int64(va), int64(*FlagRound)))
+               if ctxt.HeadType == objabi.Haix {
+                       // Relro data are inside data segment on AIX.
+                       va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE)
+               }
+
+               order = append(order, &Segrelrodata)
+               Segrelrodata.Rwx = 06
+               Segrelrodata.Vaddr = va
+               for _, s := range Segrelrodata.Sections {
+                       va = uint64(Rnd(int64(va), int64(s.Align)))
+                       s.Vaddr = va
+                       va += s.Length
+               }
+
+               Segrelrodata.Length = va - Segrelrodata.Vaddr
+       }
+
+       va = uint64(Rnd(int64(va), int64(*FlagRound)))
+       if ctxt.HeadType == objabi.Haix && len(Segrelrodata.Sections) == 0 {
+               // Data sections are moved to an unreachable segment
+               // to ensure that they are position-independent.
+               // Already done if relro sections exist.
+               va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE)
+       }
+       order = append(order, &Segdata)
+       Segdata.Rwx = 06
+       Segdata.Vaddr = va
+       var data *sym.Section
+       var noptr *sym.Section
+       var bss *sym.Section
+       var noptrbss *sym.Section
+       for i, s := range Segdata.Sections {
+               if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && s.Name == ".tbss" {
+                       continue
+               }
+               vlen := int64(s.Length)
+               if i+1 < len(Segdata.Sections) && !((ctxt.IsELF || ctxt.HeadType == objabi.Haix) && Segdata.Sections[i+1].Name == ".tbss") {
+                       vlen = int64(Segdata.Sections[i+1].Vaddr - s.Vaddr)
+               }
+               s.Vaddr = va
+               va += uint64(vlen)
+               Segdata.Length = va - Segdata.Vaddr
+               if s.Name == ".data" {
+                       data = s
+               }
+               if s.Name == ".noptrdata" {
+                       noptr = s
+               }
+               if s.Name == ".bss" {
+                       bss = s
+               }
+               if s.Name == ".noptrbss" {
+                       noptrbss = s
+               }
+       }
+
+       // Assign Segdata's Filelen omitting the BSS. We do this here
+       // simply because right now we know where the BSS starts.
+       Segdata.Filelen = bss.Vaddr - Segdata.Vaddr
+
+       va = uint64(Rnd(int64(va), int64(*FlagRound)))
+       order = append(order, &Segdwarf)
+       Segdwarf.Rwx = 06
+       Segdwarf.Vaddr = va
+       for i, s := range Segdwarf.Sections {
+               vlen := int64(s.Length)
+               if i+1 < len(Segdwarf.Sections) {
+                       vlen = int64(Segdwarf.Sections[i+1].Vaddr - s.Vaddr)
+               }
+               s.Vaddr = va
+               va += uint64(vlen)
+               if ctxt.HeadType == objabi.Hwindows {
+                       va = uint64(Rnd(int64(va), PEFILEALIGN))
+               }
+               Segdwarf.Length = va - Segdwarf.Vaddr
+       }
+
+       var (
+               text     = Segtext.Sections[0]
+               rodata   = ctxt.Syms.Lookup("runtime.rodata", 0).Sect
+               itablink = ctxt.Syms.Lookup("runtime.itablink", 0).Sect
+               symtab   = ctxt.Syms.Lookup("runtime.symtab", 0).Sect
+               pclntab  = ctxt.Syms.Lookup("runtime.pclntab", 0).Sect
+               types    = ctxt.Syms.Lookup("runtime.types", 0).Sect
+       )
+       lasttext := text
+       // Could be multiple .text sections
+       for _, sect := range Segtext.Sections {
+               if sect.Name == ".text" {
+                       lasttext = sect
+               }
+       }
+
+       for _, s := range datap {
+               if s.Sect != nil {
+                       s.Value += int64(s.Sect.Vaddr)
+               }
+               for sub := s.Sub; sub != nil; sub = sub.Sub {
+                       sub.Value += s.Value
+               }
+       }
+
+       for _, s := range dwarfp {
+               if s.Sect != nil {
+                       s.Value += int64(s.Sect.Vaddr)
+               }
+               for sub := s.Sub; sub != nil; sub = sub.Sub {
+                       sub.Value += s.Value
+               }
+       }
+
+       if ctxt.BuildMode == BuildModeShared {
+               s := ctxt.Syms.Lookup("go.link.abihashbytes", 0)
+               sectSym := ctxt.Syms.Lookup(".note.go.abihash", 0)
+               s.Sect = sectSym.Sect
+               s.Value = int64(sectSym.Sect.Vaddr + 16)
+       }
+
+       ctxt.xdefine("runtime.text", sym.STEXT, int64(text.Vaddr))
+       ctxt.xdefine("runtime.etext", sym.STEXT, int64(lasttext.Vaddr+lasttext.Length))
+
+       // 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))
+       ctxt.xdefine("runtime.itablink", sym.SRODATA, int64(itablink.Vaddr))
+       ctxt.xdefine("runtime.eitablink", sym.SRODATA, int64(itablink.Vaddr+itablink.Length))
+
+       s := ctxt.Syms.Lookup("runtime.gcdata", 0)
+       s.Attr |= sym.AttrLocal
+       ctxt.xdefine("runtime.egcdata", sym.SRODATA, Symaddr(s)+s.Size)
+       ctxt.Syms.Lookup("runtime.egcdata", 0).Sect = s.Sect
+
+       s = ctxt.Syms.Lookup("runtime.gcbss", 0)
+       s.Attr |= sym.AttrLocal
+       ctxt.xdefine("runtime.egcbss", sym.SRODATA, Symaddr(s)+s.Size)
+       ctxt.Syms.Lookup("runtime.egcbss", 0).Sect = s.Sect
+
+       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.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
+       ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
+       ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length))
+       ctxt.xdefine("runtime.bss", sym.SBSS, int64(bss.Vaddr))
+       ctxt.xdefine("runtime.ebss", sym.SBSS, int64(bss.Vaddr+bss.Length))
+       ctxt.xdefine("runtime.data", sym.SDATA, int64(data.Vaddr))
+       ctxt.xdefine("runtime.edata", sym.SDATA, int64(data.Vaddr+data.Length))
+       ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr))
+       ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length))
+       ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length))
+
+       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 *sym.Symbol) {
+       s.Type = sym.STEXT
+       s.Attr |= sym.AttrReachable
+       s.Attr |= sym.AttrOnList
+       ctxt.tramps = append(ctxt.tramps, s)
+       if *FlagDebugTramp > 0 && ctxt.Debugvlog > 0 {
+               ctxt.Logf("trampoline %s inserted\n", s)
+       }
+}
+
+// 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 []*sym.Symbol) []byte {
+       var total int64
+       for _, sym := range syms {
+               total += sym.Size
+       }
+
+       var buf bytes.Buffer
+       buf.Write([]byte("ZLIB"))
+       var sizeBytes [8]byte
+       binary.BigEndian.PutUint64(sizeBytes[:], uint64(total))
+       buf.Write(sizeBytes[:])
+
+       // 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)
+       }
+       for _, s := range syms {
+               // s.P may be read-only. Apply relocations in a
+               // temporary buffer, and immediately write it out.
+               oldP := s.P
+               wasReadOnly := s.Attr.ReadOnly()
+               if len(s.R) != 0 && wasReadOnly {
+                       ctxt.relocbuf = append(ctxt.relocbuf[:0], s.P...)
+                       s.P = ctxt.relocbuf
+                       s.Attr.Set(sym.AttrReadOnly, false)
+               }
+               relocsym(ctxt, s)
+               if _, err := z.Write(s.P); err != nil {
+                       log.Fatalf("compression failed: %s", err)
+               }
+               for i := s.Size - int64(len(s.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)
+               }
+               // Restore s.P if a temporary buffer was used. If compression
+               // is not beneficial, we'll go back to use the uncompressed
+               // contents, in which case we still need s.P.
+               if len(s.R) != 0 && wasReadOnly {
+                       s.P = oldP
+                       s.Attr.Set(sym.AttrReadOnly, wasReadOnly)
+                       for i := range s.R {
+                               s.R[i].Done = false
+                       }
+               }
+       }
+       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/oldlink/internal/ld/deadcode.go b/src/cmd/oldlink/internal/ld/deadcode.go
new file mode 100644 (file)
index 0000000..1bdac31
--- /dev/null
@@ -0,0 +1,408 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "fmt"
+       "strings"
+       "unicode"
+)
+
+// 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.Call, .Method, or reflect.Method.Func
+//
+// 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.Call is reachable
+//     - reflect.Value.Method is reachable
+//     - reflect.Type.Method or MethodByName is called.
+// 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) {
+       if ctxt.Debugvlog != 0 {
+               ctxt.Logf("deadcode\n")
+       }
+
+       if *flagNewobj {
+               deadcode2(ctxt)
+               return
+       }
+
+       d := &deadcodepass{
+               ctxt:        ctxt,
+               ifaceMethod: make(map[methodsig]bool),
+       }
+
+       // First, flood fill any symbols directly reachable in the call
+       // graph from *flagEntrySymbol. Ignore all methods not directly called.
+       d.init()
+       d.flood()
+
+       callSym := ctxt.Syms.ROLookup("reflect.Value.Call", sym.SymVerABIInternal)
+       methSym := ctxt.Syms.ROLookup("reflect.Value.Method", sym.SymVerABIInternal)
+       reflectSeen := false
+
+       if ctxt.DynlinkingGo() {
+               // Exported methods may satisfy interfaces we don't know
+               // about yet when dynamically linking.
+               reflectSeen = true
+       }
+
+       for {
+               if !reflectSeen {
+                       if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) {
+                               // Methods might be called via reflection. Give up on
+                               // static analysis, mark all exported methods of
+                               // all reachable types as reachable.
+                               reflectSeen = true
+                       }
+               }
+
+               // 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.
+               var rem []methodref
+               for _, m := range d.markableMethods {
+                       if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
+                               d.markMethod(m)
+                       } else {
+                               rem = append(rem, m)
+                       }
+               }
+               d.markableMethods = rem
+
+               if len(d.markQueue) == 0 {
+                       // No new work was discovered. Done.
+                       break
+               }
+               d.flood()
+       }
+
+       // Remove all remaining unreached R_METHODOFF relocations.
+       for _, m := range d.markableMethods {
+               for _, r := range m.r {
+                       d.cleanupReloc(r)
+               }
+       }
+
+       if ctxt.BuildMode != BuildModeShared {
+               // Keep a itablink if the symbol it points at is being kept.
+               // (When BuildModeShared, always keep itablinks.)
+               for _, s := range ctxt.Syms.Allsym {
+                       if strings.HasPrefix(s.Name, "go.itablink.") {
+                               s.Attr.Set(sym.AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable())
+                       }
+               }
+       }
+
+       addToTextp(ctxt)
+}
+
+func addToTextp(ctxt *Link) {
+       // Remove dead text but keep file information (z symbols).
+       textp := []*sym.Symbol{}
+       for _, s := range ctxt.Textp {
+               if s.Attr.Reachable() {
+                       textp = append(textp, s)
+               }
+       }
+
+       // Put reachable text symbols into Textp.
+       // do it in postorder so that packages are laid down in dependency order
+       // internal first, then everything else
+       ctxt.Library = postorder(ctxt.Library)
+       for _, doInternal := range [2]bool{true, false} {
+               for _, lib := range ctxt.Library {
+                       if isRuntimeDepPkg(lib.Pkg) != doInternal {
+                               continue
+                       }
+                       libtextp := lib.Textp[:0]
+                       for _, s := range lib.Textp {
+                               if s.Attr.Reachable() {
+                                       textp = append(textp, s)
+                                       libtextp = append(libtextp, s)
+                                       if s.Unit != nil {
+                                               s.Unit.Textp = append(s.Unit.Textp, s)
+                                       }
+                               }
+                       }
+                       for _, s := range lib.DupTextSyms {
+                               if s.Attr.Reachable() && !s.Attr.OnList() {
+                                       textp = append(textp, s)
+                                       libtextp = append(libtextp, s)
+                                       if s.Unit != nil {
+                                               s.Unit.Textp = append(s.Unit.Textp, s)
+                                       }
+                                       s.Attr |= sym.AttrOnList
+                                       // dupok symbols may be defined in multiple packages. its
+                                       // associated package is chosen sort of arbitrarily (the
+                                       // first containing package that the linker loads). canonicalize
+                                       // it here to the package with which it will be laid down
+                                       // in text.
+                                       s.File = objabi.PathToPrefix(lib.Pkg)
+                               }
+                       }
+                       lib.Textp = libtextp
+               }
+       }
+       ctxt.Textp = textp
+
+       if len(ctxt.Shlibs) > 0 {
+               // We might have overwritten some functions above (this tends to happen for the
+               // autogenerated type equality/hashing functions) and we don't want to generated
+               // pcln table entries for these any more so remove them from Textp.
+               textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
+               for _, s := range ctxt.Textp {
+                       if s.Type != sym.SDYNIMPORT {
+                               textp = append(textp, s)
+                       }
+               }
+               ctxt.Textp = textp
+       }
+}
+
+// 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 *sym.Symbol   // receiver type symbol
+       r   [3]*sym.Reloc // R_METHODOFF relocations to fields of runtime.method
+}
+
+func (m methodref) ifn() *sym.Symbol { return m.r[1].Sym }
+
+func (m methodref) isExported() bool {
+       for _, r := range m.m {
+               return unicode.IsUpper(r)
+       }
+       panic("methodref has no signature")
+}
+
+// deadcodepass holds state for the deadcode flood fill.
+type deadcodepass struct {
+       ctxt            *Link
+       markQueue       []*sym.Symbol      // symbols to flood fill in next pass
+       ifaceMethod     map[methodsig]bool // methods declared in reached interfaces
+       markableMethods []methodref        // methods of reached types
+       reflectMethod   bool
+}
+
+func (d *deadcodepass) cleanupReloc(r *sym.Reloc) {
+       if r.Sym.Attr.Reachable() {
+               r.Type = objabi.R_ADDROFF
+       } else {
+               if d.ctxt.Debugvlog > 1 {
+                       d.ctxt.Logf("removing method %s\n", r.Sym.Name)
+               }
+               r.Sym = nil
+               r.Siz = 0
+       }
+}
+
+// mark appends a symbol to the mark queue for flood filling.
+func (d *deadcodepass) mark(s, parent *sym.Symbol) {
+       if s == nil || s.Attr.Reachable() {
+               return
+       }
+       if s.Attr.ReflectMethod() {
+               d.reflectMethod = true
+       }
+       if *flagDumpDep {
+               p := "_"
+               if parent != nil {
+                       p = parent.Name
+               }
+               fmt.Printf("%s -> %s\n", p, s.Name)
+       }
+       s.Attr |= sym.AttrReachable
+       if d.ctxt.Reachparent != nil {
+               d.ctxt.Reachparent[s] = parent
+       }
+       d.markQueue = append(d.markQueue, s)
+}
+
+// markMethod marks a method as reachable.
+func (d *deadcodepass) markMethod(m methodref) {
+       for _, r := range m.r {
+               d.mark(r.Sym, m.src)
+               r.Type = objabi.R_ADDROFF
+       }
+}
+
+// init marks all initial symbols as reachable.
+// In a typical binary, this is *flagEntrySymbol.
+func (d *deadcodepass) init() {
+       var names []string
+
+       if d.ctxt.BuildMode == BuildModeShared {
+               // Mark all symbols defined in this library as reachable when
+               // building a shared library.
+               for _, s := range d.ctxt.Syms.Allsym {
+                       if s.Type != 0 && s.Type != sym.SDYNIMPORT {
+                               d.mark(s, nil)
+                       }
+               }
+       } else {
+               // In a normal binary, start at main.main and the init
+               // functions and mark what is reachable from there.
+
+               if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+                       names = append(names, "main.main", "main..inittask")
+               } else {
+                       // The external linker refers main symbol directly.
+                       if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+                               if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
+                                       *flagEntrySymbol = "_main"
+                               } else {
+                                       *flagEntrySymbol = "main"
+                               }
+                       }
+                       names = append(names, *flagEntrySymbol)
+                       if d.ctxt.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.
+                               exports := d.ctxt.Syms.ROLookup("go.plugin.exports", 0)
+                               if exports != nil {
+                                       for i := range exports.R {
+                                               d.mark(exports.R[i].Sym, nil)
+                                       }
+                               }
+                       }
+               }
+               for _, s := range dynexp {
+                       d.mark(s, nil)
+               }
+       }
+
+       for _, name := range names {
+               // Mark symbol as a data/ABI0 symbol.
+               d.mark(d.ctxt.Syms.ROLookup(name, 0), nil)
+               // Also mark any Go functions (internal ABI).
+               d.mark(d.ctxt.Syms.ROLookup(name, sym.SymVerABIInternal), nil)
+       }
+}
+
+// flood fills symbols reachable from the markQueue symbols.
+// As it goes, it collects methodref and interface method declarations.
+func (d *deadcodepass) flood() {
+       for len(d.markQueue) > 0 {
+               s := d.markQueue[0]
+               d.markQueue = d.markQueue[1:]
+               if s.Type == sym.STEXT {
+                       if d.ctxt.Debugvlog > 1 {
+                               d.ctxt.Logf("marktext %s\n", s.Name)
+                       }
+               }
+
+               if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' {
+                       if len(s.P) == 0 {
+                               // Probably a bug. The undefined symbol check
+                               // later will give a better error than deadcode.
+                               continue
+                       }
+                       if decodetypeKind(d.ctxt.Arch, s.P)&kindMask == kindInterface {
+                               for _, sig := range decodeIfaceMethods(d.ctxt.Arch, s) {
+                                       if d.ctxt.Debugvlog > 1 {
+                                               d.ctxt.Logf("reached iface method: %s\n", sig)
+                                       }
+                                       d.ifaceMethod[sig] = true
+                               }
+                       }
+               }
+
+               mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype
+               var methods []methodref
+               for i := range s.R {
+                       r := &s.R[i]
+                       if r.Sym == nil {
+                               continue
+                       }
+                       if r.Type == objabi.R_WEAKADDROFF {
+                               // An R_WEAKADDROFF relocation is not reason
+                               // enough to mark the pointed-to symbol as
+                               // reachable.
+                               continue
+                       }
+                       if r.Sym.Type == sym.SABIALIAS {
+                               // Patch this relocation through the
+                               // ABI alias before marking.
+                               r.Sym = resolveABIAlias(r.Sym)
+                       }
+                       if r.Type != objabi.R_METHODOFF {
+                               d.mark(r.Sym, s)
+                               continue
+                       }
+                       // Collect rtype pointers to methods for
+                       // later processing in deadcode.
+                       if mpos == 0 {
+                               m := methodref{src: s}
+                               m.r[0] = r
+                               methods = append(methods, m)
+                       } else {
+                               methods[len(methods)-1].r[mpos] = r
+                       }
+                       mpos++
+                       if mpos == len(methodref{}.r) {
+                               mpos = 0
+                       }
+               }
+               if len(methods) > 0 {
+                       // Decode runtime type information for type methods
+                       // to help work out which methods can be called
+                       // dynamically via interfaces.
+                       methodsigs := decodetypeMethods(d.ctxt.Arch, s)
+                       if len(methods) != len(methodsigs) {
+                               panic(fmt.Sprintf("%q has %d method relocations for %d methods", s.Name, len(methods), len(methodsigs)))
+                       }
+                       for i, m := range methodsigs {
+                               name := string(m)
+                               name = name[:strings.Index(name, "(")]
+                               if !strings.HasSuffix(methods[i].ifn().Name, name) {
+                                       panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, methods[i].ifn().Name, name))
+                               }
+                               methods[i].m = m
+                       }
+                       d.markableMethods = append(d.markableMethods, methods...)
+               }
+
+               if s.FuncInfo != nil {
+                       for i := range s.FuncInfo.Funcdata {
+                               d.mark(s.FuncInfo.Funcdata[i], s)
+                       }
+               }
+               d.mark(s.Gotype, s)
+               d.mark(s.Sub, s)
+               d.mark(s.Outer, s)
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/deadcode2.go b/src/cmd/oldlink/internal/ld/deadcode2.go
new file mode 100644 (file)
index 0000000..82bfd60
--- /dev/null
@@ -0,0 +1,441 @@
+// 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 (
+       "bytes"
+       "cmd/internal/dwarf"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/loader"
+       "cmd/oldlink/internal/sym"
+       "container/heap"
+       "fmt"
+       "unicode"
+)
+
+var _ = fmt.Print
+
+type workQueue []loader.Sym
+
+// Implement container/heap.Interface.
+func (q *workQueue) Len() int           { return len(*q) }
+func (q *workQueue) Less(i, j int) bool { return (*q)[i] < (*q)[j] }
+func (q *workQueue) Swap(i, j int)      { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
+func (q *workQueue) Push(i interface{}) { *q = append(*q, i.(loader.Sym)) }
+func (q *workQueue) Pop() interface{}   { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
+
+// Functions for deadcode pass to use.
+// Deadcode pass should call push/pop, not Push/Pop.
+func (q *workQueue) push(i loader.Sym) { heap.Push(q, i) }
+func (q *workQueue) pop() loader.Sym   { return heap.Pop(q).(loader.Sym) }
+func (q *workQueue) empty() bool       { return len(*q) == 0 }
+
+type deadcodePass2 struct {
+       ctxt *Link
+       ldr  *loader.Loader
+       wq   workQueue
+       rtmp []loader.Reloc
+
+       ifaceMethod     map[methodsig]bool // methods declared in reached interfaces
+       markableMethods []methodref2       // methods of reached types
+       reflectSeen     bool               // whether we have seen a reflect method call
+}
+
+func (d *deadcodePass2) init() {
+       d.ldr.InitReachable()
+       d.ifaceMethod = make(map[methodsig]bool)
+       if d.ctxt.Reachparent != nil {
+               d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
+       }
+       heap.Init(&d.wq)
+
+       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)
+                       if !d.ldr.IsDup(s) {
+                               d.mark(s, 0)
+                       }
+               }
+               return
+       }
+
+       var names []string
+
+       // In a normal binary, start at main.main and the init
+       // functions and mark what is reachable from there.
+       if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+               names = append(names, "main.main", "main..inittask")
+       } else {
+               // The external linker refers main symbol directly.
+               if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+                       if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
+                               *flagEntrySymbol = "_main"
+                       } else {
+                               *flagEntrySymbol = "main"
+                       }
+               }
+               names = append(names, *flagEntrySymbol)
+               if d.ctxt.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 {
+                               d.ReadRelocs(exportsIdx)
+                               for i := 0; i < len(d.rtmp); i++ {
+                                       d.mark(d.rtmp[i].Sym, 0)
+                               }
+                       }
+               }
+       }
+
+       dynexpMap := d.ctxt.cgo_export_dynamic
+       if d.ctxt.LinkMode == LinkExternal {
+               dynexpMap = d.ctxt.cgo_export_static
+       }
+       for exp := range dynexpMap {
+               names = append(names, exp)
+       }
+
+       // DWARF constant DIE symbols are not referenced, but needed by
+       // the dwarf pass.
+       if !*FlagW {
+               for _, lib := range d.ctxt.Library {
+                       names = append(names, dwarf.ConstInfoPrefix+lib.Pkg)
+               }
+       }
+
+       for _, name := range names {
+               // Mark symbol as a data/ABI0 symbol.
+               d.mark(d.ldr.Lookup(name, 0), 0)
+               // Also mark any Go functions (internal ABI).
+               d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal), 0)
+       }
+}
+
+func (d *deadcodePass2) flood() {
+       symRelocs := []loader.Reloc{}
+       auxSyms := []loader.Sym{}
+       for !d.wq.empty() {
+               symIdx := d.wq.pop()
+
+               d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
+
+               relocs := d.ldr.Relocs(symIdx)
+               symRelocs = relocs.ReadAll(symRelocs)
+
+               if d.ldr.IsGoType(symIdx) {
+                       p := d.ldr.Data(symIdx)
+                       if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
+                               for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) {
+                                       if d.ctxt.Debugvlog > 1 {
+                                               d.ctxt.Logf("reached iface method: %s\n", sig)
+                                       }
+                                       d.ifaceMethod[sig] = true
+                               }
+                       }
+               }
+
+               var methods []methodref2
+               for i := 0; i < relocs.Count; i++ {
+                       r := symRelocs[i]
+                       if r.Type == objabi.R_WEAKADDROFF {
+                               continue
+                       }
+                       if r.Type == objabi.R_METHODOFF {
+                               if i+2 >= relocs.Count {
+                                       panic("expect three consecutive R_METHODOFF relocs")
+                               }
+                               methods = append(methods, methodref2{src: symIdx, r: i})
+                               i += 2
+                               continue
+                       }
+                       if r.Type == 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
+                       }
+                       d.mark(r.Sym, symIdx)
+               }
+               auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms)
+               for i := 0; i < len(auxSyms); i++ {
+                       d.mark(auxSyms[i], 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.
+               d.mark(d.ldr.OuterSym(symIdx), symIdx)
+               d.mark(d.ldr.SubSym(symIdx), symIdx)
+
+               if len(methods) != 0 {
+                       // Decode runtime type information for type methods
+                       // to help work out which methods can be called
+                       // dynamically via interfaces.
+                       methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs)
+                       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
+                       }
+                       d.markableMethods = append(d.markableMethods, methods...)
+               }
+       }
+}
+
+func (d *deadcodePass2) mark(symIdx, parent loader.Sym) {
+       if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) {
+               d.wq.push(symIdx)
+               d.ldr.Reachable.Set(symIdx)
+               if d.ctxt.Reachparent != nil {
+                       d.ldr.Reachparent[symIdx] = parent
+               }
+               if *flagDumpDep {
+                       to := d.ldr.SymName(symIdx)
+                       if to != "" {
+                               from := "_"
+                               if parent != 0 {
+                                       from = d.ldr.SymName(parent)
+                               }
+                               fmt.Printf("%s -> %s\n", from, to)
+                       }
+               }
+       }
+}
+
+func (d *deadcodePass2) markMethod(m methodref2) {
+       d.ReadRelocs(m.src)
+       d.mark(d.rtmp[m.r].Sym, m.src)
+       d.mark(d.rtmp[m.r+1].Sym, m.src)
+       d.mark(d.rtmp[m.r+2].Sym, m.src)
+}
+
+func deadcode2(ctxt *Link) {
+       ldr := ctxt.loader
+       d := deadcodePass2{ctxt: ctxt, ldr: ldr}
+       d.init()
+       d.flood()
+
+       callSym := ldr.Lookup("reflect.Value.Call", sym.SymVerABIInternal)
+       methSym := ldr.Lookup("reflect.Value.Method", sym.SymVerABIInternal)
+       if ctxt.DynlinkingGo() {
+               // Exported methods may satisfy interfaces we don't know
+               // about yet when dynamically linking.
+               d.reflectSeen = true
+       }
+
+       for {
+               // Methods might be called via reflection. Give up on
+               // static analysis, mark all exported methods of
+               // all reachable types as reachable.
+               d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.Reachable.Has(callSym)) || (methSym != 0 && ldr.Reachable.Has(methSym))
+
+               // Mark all methods that could satisfy a discovered
+               // interface as reachable. We recheck old marked interfaces
+               // as new types (with new methods) may have been discovered
+               // in the last pass.
+               rem := d.markableMethods[:0]
+               for _, m := range d.markableMethods {
+                       if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
+                               d.markMethod(m)
+                       } else {
+                               rem = append(rem, m)
+                       }
+               }
+               d.markableMethods = rem
+
+               if d.wq.empty() {
+                       // No new work was discovered. Done.
+                       break
+               }
+               d.flood()
+       }
+
+       n := ldr.NSym()
+
+       if ctxt.BuildMode != BuildModeShared {
+               // Keep a itablink if the symbol it points at is being kept.
+               // (When BuildModeShared, always keep itablinks.)
+               for i := 1; i < n; i++ {
+                       s := loader.Sym(i)
+                       if ldr.IsItabLink(s) {
+                               relocs := ldr.Relocs(s)
+                               if relocs.Count > 0 && ldr.Reachable.Has(relocs.At(0).Sym) {
+                                       ldr.Reachable.Set(s)
+                               }
+                       }
+               }
+       }
+}
+
+// methodref2 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 methodref2 struct {
+       m   methodsig
+       src loader.Sym // receiver type symbol
+       r   int        // the index of R_METHODOFF relocations
+}
+
+func (m methodref2) isExported() bool {
+       for _, r := range m.m {
+               return unicode.IsUpper(r)
+       }
+       panic("methodref has no signature")
+}
+
+// decodeMethodSig2 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 *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, off, size, count int) []methodsig {
+       var buf bytes.Buffer
+       var methods []methodsig
+       for i := 0; i < count; i++ {
+               buf.WriteString(decodetypeName2(ldr, symIdx, symRelocs, off))
+               mtypSym := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off+4))
+               // FIXME: add some sort of caching here, since we may see some of the
+               // same symbols over time for param types.
+               d.ReadRelocs(mtypSym)
+               mp := ldr.Data(mtypSym)
+
+               buf.WriteRune('(')
+               inCount := decodetypeFuncInCount(arch, mp)
+               for i := 0; i < inCount; i++ {
+                       if i > 0 {
+                               buf.WriteString(", ")
+                       }
+                       a := d.decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i)
+                       buf.WriteString(ldr.SymName(a))
+               }
+               buf.WriteString(") (")
+               outCount := decodetypeFuncOutCount(arch, mp)
+               for i := 0; i < outCount; i++ {
+                       if i > 0 {
+                               buf.WriteString(", ")
+                       }
+                       a := d.decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i)
+                       buf.WriteString(ldr.SymName(a))
+               }
+               buf.WriteRune(')')
+
+               off += size
+               methods = append(methods, methodsig(buf.String()))
+               buf.Reset()
+       }
+       return methods
+}
+
+func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
+       p := ldr.Data(symIdx)
+       if decodetypeKind(arch, p)&kindMask != kindInterface {
+               panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
+       }
+       rel := decodeReloc2(ldr, symIdx, symRelocs, int32(commonsize(arch)+arch.PtrSize))
+       if rel.Sym == 0 {
+               return nil
+       }
+       if rel.Sym != symIdx {
+               panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx)))
+       }
+       off := int(rel.Add) // array of reflect.imethod values
+       numMethods := int(decodetypeIfaceMethodCount(arch, p))
+       sizeofIMethod := 4 + 4
+       return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofIMethod, numMethods)
+}
+
+func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []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.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofMethod, mcount)
+}
+
+func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Reloc {
+       for j := 0; j < len(symRelocs); j++ {
+               rel := symRelocs[j]
+               if rel.Off == off {
+                       return rel
+               }
+       }
+       return loader.Reloc{}
+}
+
+func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym {
+       return decodeReloc2(ldr, symIdx, symRelocs, off).Sym
+}
+
+// decodetypeName2 decodes the name from a reflect.name.
+func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string {
+       r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off))
+       if r == 0 {
+               return ""
+       }
+
+       data := ldr.Data(r)
+       namelen := int(uint16(data[1])<<8 | uint16(data[2]))
+       return string(data[3 : 3+namelen])
+}
+
+func (d *deadcodePass2) decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
+       uadd := commonsize(arch) + 4
+       if arch.PtrSize == 8 {
+               uadd += 4
+       }
+       if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
+               uadd += uncommonSize()
+       }
+       return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize))
+}
+
+func (d *deadcodePass2) decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
+       return d.decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
+}
+
+// readRelocs reads the relocations for the specified symbol into the
+// deadcode relocs work array. Use with care, since the work array
+// is a singleton.
+func (d *deadcodePass2) ReadRelocs(symIdx loader.Sym) {
+       relocs := d.ldr.Relocs(symIdx)
+       d.rtmp = relocs.ReadAll(d.rtmp)
+}
diff --git a/src/cmd/oldlink/internal/ld/decodesym.go b/src/cmd/oldlink/internal/ld/decodesym.go
new file mode 100644 (file)
index 0000000..0676e94
--- /dev/null
@@ -0,0 +1,374 @@
+// 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 (
+       "bytes"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "fmt"
+)
+
+// Decoding the type.* symbols.         This has to be in sync with
+// ../../runtime/type.go, or more specifically, with what
+// cmd/compile/internal/gc/reflect.go stuffs in these.
+
+// tflag is documented in reflect/type.go.
+//
+// tflag values must be kept in sync with copies in:
+//     cmd/compile/internal/gc/reflect.go
+//     cmd/oldlink/internal/ld/decodesym.go
+//     reflect/type.go
+//     runtime/type.go
+const (
+       tflagUncommon  = 1 << 0
+       tflagExtraStar = 1 << 1
+)
+
+func decodeReloc(s *sym.Symbol, off int32) *sym.Reloc {
+       for i := range s.R {
+               if s.R[i].Off == off {
+                       return &s.R[i]
+               }
+       }
+       return nil
+}
+
+func decodeRelocSym(s *sym.Symbol, off int32) *sym.Symbol {
+       r := decodeReloc(s, off)
+       if r == nil {
+               return nil
+       }
+       return r.Sym
+}
+
+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
+}
+
+// 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 {
+                               if sect.Addr <= addr && addr <= sect.Addr+sect.Size {
+                                       return sect
+                               }
+                       }
+               }
+       }
+       return nil
+}
+
+// Type.commonType.gc
+func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte {
+       if s.Type == sym.SDYNIMPORT {
+               addr := decodetypeGcprogShlib(ctxt, s)
+               sect := findShlibSection(ctxt, s.File, addr)
+               if sect != nil {
+                       // A gcprog is a 4-byte uint32 indicating length, followed by
+                       // the actual program.
+                       progsize := make([]byte, 4)
+                       sect.ReadAt(progsize, int64(addr-sect.Addr))
+                       progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize))
+                       sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
+                       return append(progsize, progbytes...)
+               }
+               Exitf("cannot find gcprog for %s", s.Name)
+               return nil
+       }
+       return decodeRelocSym(s, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize)).P
+}
+
+func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 {
+       if ctxt.Arch.Family == sys.ARM64 {
+               for _, shlib := range ctxt.Shlibs {
+                       if shlib.Path == s.File {
+                               return shlib.gcdataAddresses[s]
+                       }
+               }
+               return 0
+       }
+       return decodeInuxi(ctxt.Arch, s.P[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
+}
+
+func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte {
+       if s.Type == sym.SDYNIMPORT {
+               addr := decodetypeGcprogShlib(ctxt, s)
+               ptrdata := decodetypePtrdata(ctxt.Arch, s.P)
+               sect := findShlibSection(ctxt, s.File, addr)
+               if sect != nil {
+                       r := make([]byte, ptrdata/int64(ctxt.Arch.PtrSize))
+                       sect.ReadAt(r, int64(addr-sect.Addr))
+                       return r
+               }
+               Exitf("cannot find gcmask for %s", s.Name)
+               return nil
+       }
+       mask := decodeRelocSym(s, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize))
+       return mask.P
+}
+
+// Type.ArrayType.elem and Type.SliceType.Elem
+func decodetypeArrayElem(arch *sys.Arch, s *sym.Symbol) *sym.Symbol {
+       return decodeRelocSym(s, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeArrayLen(arch *sys.Arch, s *sym.Symbol) int64 {
+       return int64(decodeInuxi(arch, s.P[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+// Type.PtrType.elem
+func decodetypePtrElem(arch *sys.Arch, s *sym.Symbol) *sym.Symbol {
+       return decodeRelocSym(s, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+// Type.MapType.key, elem
+func decodetypeMapKey(arch *sys.Arch, s *sym.Symbol) *sym.Symbol {
+       return decodeRelocSym(s, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapValue(arch *sys.Arch, s *sym.Symbol) *sym.Symbol {
+       return decodeRelocSym(s, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38
+}
+
+// Type.ChanType.elem
+func decodetypeChanElem(arch *sys.Arch, s *sym.Symbol) *sym.Symbol {
+       return decodeRelocSym(s, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+// 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))
+}
+
+func decodetypeFuncInType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
+       uadd := commonsize(arch) + 4
+       if arch.PtrSize == 8 {
+               uadd += 4
+       }
+       if decodetypeHasUncommon(arch, s.P) {
+               uadd += uncommonSize()
+       }
+       return decodeRelocSym(s, int32(uadd+i*arch.PtrSize))
+}
+
+func decodetypeFuncOutType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
+       return decodetypeFuncInType(arch, s, i+decodetypeFuncInCount(arch, s.P))
+}
+
+// Type.StructType.fields.Slice::length
+func decodetypeStructFieldCount(arch *sys.Arch, s *sym.Symbol) int {
+       return int(decodeInuxi(arch, s.P[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeStructFieldArrayOff(arch *sys.Arch, s *sym.Symbol, i int) int {
+       off := commonsize(arch) + 4*arch.PtrSize
+       if decodetypeHasUncommon(arch, s.P) {
+               off += uncommonSize()
+       }
+       off += i * structfieldSize(arch)
+       return off
+}
+
+// decodetypeStr returns the contents of an rtype's str field (a nameOff).
+func decodetypeStr(arch *sys.Arch, s *sym.Symbol) string {
+       str := decodetypeName(s, 4*arch.PtrSize+8)
+       if s.P[2*arch.PtrSize+4]&tflagExtraStar != 0 {
+               return str[1:]
+       }
+       return str
+}
+
+// decodetypeName decodes the name from a reflect.name.
+func decodetypeName(s *sym.Symbol, off int) string {
+       r := decodeReloc(s, int32(off))
+       if r == nil {
+               return ""
+       }
+
+       data := r.Sym.P
+       namelen := int(uint16(data[1])<<8 | uint16(data[2]))
+       return string(data[3 : 3+namelen])
+}
+
+func decodetypeStructFieldName(arch *sys.Arch, s *sym.Symbol, i int) string {
+       off := decodetypeStructFieldArrayOff(arch, s, i)
+       return decodetypeName(s, off)
+}
+
+func decodetypeStructFieldType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
+       off := decodetypeStructFieldArrayOff(arch, s, i)
+       return decodeRelocSym(s, int32(off+arch.PtrSize))
+}
+
+func decodetypeStructFieldOffs(arch *sys.Arch, s *sym.Symbol, i int) int64 {
+       return decodetypeStructFieldOffsAnon(arch, s, i) >> 1
+}
+
+func decodetypeStructFieldOffsAnon(arch *sys.Arch, s *sym.Symbol, i int) int64 {
+       off := decodetypeStructFieldArrayOff(arch, s, i)
+       return int64(decodeInuxi(arch, s.P[off+2*arch.PtrSize:], arch.PtrSize))
+}
+
+// InterfaceType.methods.length
+func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 {
+       return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+// methodsig is a fully qualified typed method signature, like
+// "Visit(type.go/ast.Node) (type.go/ast.Visitor)".
+type methodsig string
+
+// 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
+)
+
+// 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 decodeMethodSig(arch *sys.Arch, s *sym.Symbol, off, size, count int) []methodsig {
+       var buf bytes.Buffer
+       var methods []methodsig
+       for i := 0; i < count; i++ {
+               buf.WriteString(decodetypeName(s, off))
+               mtypSym := decodeRelocSym(s, int32(off+4))
+
+               buf.WriteRune('(')
+               inCount := decodetypeFuncInCount(arch, mtypSym.P)
+               for i := 0; i < inCount; i++ {
+                       if i > 0 {
+                               buf.WriteString(", ")
+                       }
+                       buf.WriteString(decodetypeFuncInType(arch, mtypSym, i).Name)
+               }
+               buf.WriteString(") (")
+               outCount := decodetypeFuncOutCount(arch, mtypSym.P)
+               for i := 0; i < outCount; i++ {
+                       if i > 0 {
+                               buf.WriteString(", ")
+                       }
+                       buf.WriteString(decodetypeFuncOutType(arch, mtypSym, i).Name)
+               }
+               buf.WriteRune(')')
+
+               off += size
+               methods = append(methods, methodsig(buf.String()))
+               buf.Reset()
+       }
+       return methods
+}
+
+func decodeIfaceMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
+       if decodetypeKind(arch, s.P)&kindMask != kindInterface {
+               panic(fmt.Sprintf("symbol %q is not an interface", s.Name))
+       }
+       r := decodeReloc(s, int32(commonsize(arch)+arch.PtrSize))
+       if r == nil {
+               return nil
+       }
+       if r.Sym != s {
+               panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", s.Name))
+       }
+       off := int(r.Add) // array of reflect.imethod values
+       numMethods := int(decodetypeIfaceMethodCount(arch, s.P))
+       sizeofIMethod := 4 + 4
+       return decodeMethodSig(arch, s, off, sizeofIMethod, numMethods)
+}
+
+func decodetypeMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
+       if !decodetypeHasUncommon(arch, s.P) {
+               panic(fmt.Sprintf("no methods on %q", s.Name))
+       }
+       off := commonsize(arch) // reflect.rtype
+       switch decodetypeKind(arch, s.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, s.P[off+4:], 2))
+       moff := int(decodeInuxi(arch, s.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 decodeMethodSig(arch, s, off, sizeofMethod, mcount)
+}
diff --git a/src/cmd/oldlink/internal/ld/dwarf.go b/src/cmd/oldlink/internal/ld/dwarf.go
new file mode 100644 (file)
index 0000000..3d5220c
--- /dev/null
@@ -0,0 +1,2044 @@
+// 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.
+
+// 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/oldlink/internal/sym"
+       "fmt"
+       "log"
+       "sort"
+       "strings"
+)
+
+type dwctxt struct {
+       linkctxt *Link
+}
+
+func (c dwctxt) PtrSize() int {
+       return c.linkctxt.Arch.PtrSize
+}
+func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) {
+       ls := s.(*sym.Symbol)
+       ls.AddUintXX(c.linkctxt.Arch, uint64(i), size)
+}
+func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) {
+       ls := s.(*sym.Symbol)
+       ls.AddBytes(b)
+}
+func (c dwctxt) AddString(s dwarf.Sym, v string) {
+       Addstring(s.(*sym.Symbol), v)
+}
+
+func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+       if value != 0 {
+               value -= (data.(*sym.Symbol)).Value
+       }
+       s.(*sym.Symbol).AddAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value)
+}
+
+func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
+       if value != 0 {
+               value -= (data.(*sym.Symbol)).Value
+       }
+       s.(*sym.Symbol).AddCURelativeAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value)
+}
+
+func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+       ls := s.(*sym.Symbol)
+       switch size {
+       default:
+               Errorf(ls, "invalid size %d in adddwarfref\n", size)
+               fallthrough
+       case c.linkctxt.Arch.PtrSize:
+               ls.AddAddr(c.linkctxt.Arch, t.(*sym.Symbol))
+       case 4:
+               ls.AddAddrPlus4(t.(*sym.Symbol), 0)
+       }
+       r := &ls.R[len(ls.R)-1]
+       r.Type = objabi.R_ADDROFF
+       r.Add = ofs
+}
+
+func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
+       size := 4
+       if isDwarf64(c.linkctxt) {
+               size = 8
+       }
+
+       c.AddSectionOffset(s, size, t, ofs)
+       ls := s.(*sym.Symbol)
+       ls.R[len(ls.R)-1].Type = objabi.R_DWARFSECREF
+}
+
+func (c dwctxt) Logf(format string, args ...interface{}) {
+       c.linkctxt.Logf(format, args...)
+}
+
+// At the moment these interfaces are only used in the compiler.
+
+func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) {
+       panic("should be used only in the compiler")
+}
+
+func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 {
+       panic("should be used only in the compiler")
+}
+
+func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) {
+       panic("should be used only in the compiler")
+}
+
+func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
+       panic("should be used only in the compiler")
+}
+
+func isDwarf64(ctxt *Link) bool {
+       return ctxt.HeadType == objabi.Haix
+}
+
+var gdbscript string
+
+var dwarfp []*sym.Symbol
+
+func writeabbrev(ctxt *Link) *sym.Symbol {
+       s := ctxt.Syms.Lookup(".debug_abbrev", 0)
+       s.Type = sym.SDWARFSECT
+       s.AddBytes(dwarf.GetAbbrev())
+       return s
+}
+
+var dwtypes dwarf.DWDie
+
+func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr {
+       a := new(dwarf.DWAttr)
+       a.Link = die.Attr
+       die.Attr = a
+       a.Atr = attr
+       a.Cls = uint8(cls)
+       a.Value = value
+       a.Data = data
+       return a
+}
+
+// Each DIE (except the root ones) has at least 1 attribute: its
+// name. getattr moves the desired one to the front so
+// frequently searched ones are found faster.
+func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr {
+       if die.Attr.Atr == attr {
+               return die.Attr
+       }
+
+       a := die.Attr
+       b := a.Link
+       for b != nil {
+               if b.Atr == attr {
+                       a.Link = b.Link
+                       b.Link = die.Attr
+                       die.Attr = b
+                       return b
+               }
+
+               a = b
+               b = b.Link
+       }
+
+       return nil
+}
+
+// Every DIE manufactured by the linker has at least an AT_name
+// attribute (but it will only be written out if it is listed in the abbrev).
+// The compiler does create nameless DWARF DIEs (ex: concrete subprogram
+// instance).
+func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie {
+       die := new(dwarf.DWDie)
+       die.Abbrev = abbrev
+       die.Link = parent.Child
+       parent.Child = die
+
+       newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name)
+
+       if name != "" && (abbrev <= dwarf.DW_ABRV_VARIABLE || abbrev >= dwarf.DW_ABRV_NULLTYPE) {
+               if abbrev != dwarf.DW_ABRV_VARIABLE || version == 0 {
+                       if abbrev == dwarf.DW_ABRV_COMPUNIT {
+                               // Avoid collisions with "real" symbol names.
+                               name = fmt.Sprintf(".pkg.%s.%d", name, len(ctxt.compUnits))
+                       }
+                       s := ctxt.Syms.Lookup(dwarf.InfoPrefix+name, version)
+                       s.Attr |= sym.AttrNotInSymbolTable
+                       s.Type = sym.SDWARFINFO
+                       die.Sym = s
+               }
+       }
+
+       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 walksymtypedef(ctxt *Link, s *sym.Symbol) *sym.Symbol {
+       if t := ctxt.Syms.ROLookup(s.Name+"..def", int(s.Version)); t != nil {
+               return t
+       }
+       return s
+}
+
+// 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
+}
+
+// Used to avoid string allocation when looking up dwarf symbols
+var prefixBuf = []byte(dwarf.InfoPrefix)
+
+func find(ctxt *Link, name string) *sym.Symbol {
+       n := append(prefixBuf, name...)
+       // The string allocation below is optimized away because it is only used in a map lookup.
+       s := ctxt.Syms.ROLookup(string(n), 0)
+       prefixBuf = n[:len(dwarf.InfoPrefix)]
+       if s != nil && s.Type == sym.SDWARFINFO {
+               return s
+       }
+       return nil
+}
+
+func mustFind(ctxt *Link, name string) *sym.Symbol {
+       r := find(ctxt, name)
+       if r == nil {
+               Exitf("dwarf find: cannot find %s", name)
+       }
+       return r
+}
+
+func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 {
+       var result int64
+       switch size {
+       default:
+               Errorf(s, "invalid size %d in adddwarfref\n", size)
+               fallthrough
+       case ctxt.Arch.PtrSize:
+               result = s.AddAddr(ctxt.Arch, t)
+       case 4:
+               result = s.AddAddrPlus4(t, 0)
+       }
+       r := &s.R[len(s.R)-1]
+       r.Type = objabi.R_DWARFSECREF
+       return result
+}
+
+func newrefattr(die *dwarf.DWDie, attr uint16, ref *sym.Symbol) *dwarf.DWAttr {
+       if ref == nil {
+               return nil
+       }
+       return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, ref)
+}
+
+func dtolsym(s dwarf.Sym) *sym.Symbol {
+       if s == nil {
+               return nil
+       }
+       return s.(*sym.Symbol)
+}
+
+func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*sym.Symbol, die *dwarf.DWDie) []*sym.Symbol {
+       s := dtolsym(die.Sym)
+       if s == nil {
+               s = syms[len(syms)-1]
+       } else {
+               if s.Attr.OnList() {
+                       log.Fatalf("symbol %s listed multiple times", s.Name)
+               }
+               s.Attr |= sym.AttrOnList
+               syms = append(syms, s)
+       }
+       dwarf.Uleb128put(ctxt, s, int64(die.Abbrev))
+       dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr)
+       if dwarf.HasChildren(die) {
+               for die := die.Child; die != nil; die = die.Link {
+                       syms = putdie(linkctxt, ctxt, syms, die)
+               }
+               syms[len(syms)-1].AddUint8(0)
+       }
+       return syms
+}
+
+func reverselist(list **dwarf.DWDie) {
+       curr := *list
+       var prev *dwarf.DWDie
+       for curr != nil {
+               next := curr.Link
+               curr.Link = prev
+               prev = curr
+               curr = next
+       }
+
+       *list = prev
+}
+
+func reversetree(list **dwarf.DWDie) {
+       reverselist(list)
+       for die := *list; die != nil; die = die.Link {
+               if dwarf.HasChildren(die) {
+                       reversetree(&die.Child)
+               }
+       }
+}
+
+func newmemberoffsetattr(die *dwarf.DWDie, offs int32) {
+       newattr(die, dwarf.DW_AT_data_member_location, dwarf.DW_CLS_CONSTANT, int64(offs), nil)
+}
+
+// GDB doesn't like FORM_addr for AT_location, so emit a
+// location expression that evals to a const.
+func newabslocexprattr(die *dwarf.DWDie, addr int64, sym *sym.Symbol) {
+       newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, sym)
+       // below
+}
+
+// Lookup predefined types
+func lookupOrDiag(ctxt *Link, n string) *sym.Symbol {
+       s := ctxt.Syms.ROLookup(n, 0)
+       if s == nil || s.Size == 0 {
+               Exitf("dwarf: missing type: %s", n)
+       }
+
+       return s
+}
+
+// dwarfFuncSym looks up a DWARF metadata symbol for function symbol s.
+// If the symbol does not exist, it creates it if create is true,
+// or returns nil otherwise.
+func dwarfFuncSym(ctxt *Link, s *sym.Symbol, meta string, create bool) *sym.Symbol {
+       // All function ABIs use symbol version 0 for the DWARF data.
+       //
+       // TODO(austin): It may be useful to have DWARF info for ABI
+       // wrappers, in which case we may want these versions to
+       // align. Better yet, replace these name lookups with a
+       // general way to attach metadata to a symbol.
+       ver := 0
+       if s.IsFileLocal() {
+               ver = int(s.Version)
+       }
+       if create {
+               return ctxt.Syms.Lookup(meta+s.Name, ver)
+       }
+       return ctxt.Syms.ROLookup(meta+s.Name, ver)
+}
+
+func dotypedef(ctxt *Link, 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
+       }
+       if strings.HasPrefix(name, "chan ") {
+               return nil
+       }
+       if name[0] == '[' || name[0] == '*' {
+               return nil
+       }
+       if def == nil {
+               Errorf(nil, "dwarf: bad def in dotypedef")
+       }
+
+       s := ctxt.Syms.Lookup(dtolsym(def.Sym).Name+"..def", 0)
+       s.Attr |= sym.AttrNotInSymbolTable
+       s.Type = sym.SDWARFINFO
+       def.Sym = s
+
+       // 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 := newdie(ctxt, parent, dwarf.DW_ABRV_TYPEDECL, name, 0)
+
+       newrefattr(die, dwarf.DW_AT_type, s)
+
+       return die
+}
+
+// Define gotype, for composite ones recurse into constituents.
+func defgotype(ctxt *Link, gotype *sym.Symbol) *sym.Symbol {
+       if gotype == nil {
+               return mustFind(ctxt, "<unspecified>")
+       }
+
+       if !strings.HasPrefix(gotype.Name, "type.") {
+               Errorf(gotype, "dwarf: type name doesn't start with \"type.\"")
+               return mustFind(ctxt, "<unspecified>")
+       }
+
+       name := gotype.Name[5:] // could also decode from Type.string
+
+       sdie := find(ctxt, name)
+
+       if sdie != nil {
+               return sdie
+       }
+
+       return newtype(ctxt, gotype).Sym.(*sym.Symbol)
+}
+
+func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
+       name := gotype.Name[5:] // could also decode from Type.string
+       kind := decodetypeKind(ctxt.Arch, gotype.P)
+       bytesize := decodetypeSize(ctxt.Arch, gotype.P)
+
+       var die, typedefdie *dwarf.DWDie
+       switch kind {
+       case objabi.KindBool:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+               newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+       case objabi.KindInt,
+               objabi.KindInt8,
+               objabi.KindInt16,
+               objabi.KindInt32,
+               objabi.KindInt64:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+               newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+       case objabi.KindUint,
+               objabi.KindUint8,
+               objabi.KindUint16,
+               objabi.KindUint32,
+               objabi.KindUint64,
+               objabi.KindUintptr:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+               newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+       case objabi.KindFloat32,
+               objabi.KindFloat64:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+               newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+       case objabi.KindComplex64,
+               objabi.KindComplex128:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+               newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+       case objabi.KindArray:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0)
+               typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+               s := decodetypeArrayElem(ctxt.Arch, gotype)
+               newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
+               fld := newdie(ctxt, die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0)
+
+               // use actual length not upper bound; correct for 0-length arrays.
+               newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(ctxt.Arch, gotype), 0)
+
+               newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+
+       case objabi.KindChan:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0)
+               s := decodetypeChanElem(ctxt.Arch, gotype)
+               newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s))
+               // Save elem type for synthesizechantypes. We could synthesize here
+               // but that would change the order of DIEs we output.
+               newrefattr(die, dwarf.DW_AT_type, s)
+
+       case objabi.KindFunc:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+               typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+               nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P)
+               for i := 0; i < nfields; i++ {
+                       s := decodetypeFuncInType(ctxt.Arch, gotype, i)
+                       fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
+                       newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
+               }
+
+               if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) {
+                       newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
+               }
+               nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P)
+               for i := 0; i < nfields; i++ {
+                       s := decodetypeFuncOutType(ctxt.Arch, gotype, i)
+                       fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
+                       newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, defgotype(ctxt, s)))
+               }
+
+       case objabi.KindInterface:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
+               typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+               nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P))
+               var s *sym.Symbol
+               if nfields == 0 {
+                       s = lookupOrDiag(ctxt, "type.runtime.eface")
+               } else {
+                       s = lookupOrDiag(ctxt, "type.runtime.iface")
+               }
+               newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
+
+       case objabi.KindMap:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0)
+               s := decodetypeMapKey(ctxt.Arch, gotype)
+               newrefattr(die, dwarf.DW_AT_go_key, defgotype(ctxt, s))
+               s = decodetypeMapValue(ctxt.Arch, gotype)
+               newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s))
+               // Save gotype for use in synthesizemaptypes. We could synthesize here,
+               // but that would change the order of the DIEs.
+               newrefattr(die, dwarf.DW_AT_type, gotype)
+
+       case objabi.KindPtr:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0)
+               typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+               s := decodetypePtrElem(ctxt.Arch, gotype)
+               newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
+
+       case objabi.KindSlice:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0)
+               typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+               s := decodetypeArrayElem(ctxt.Arch, gotype)
+               elem := defgotype(ctxt, s)
+               newrefattr(die, dwarf.DW_AT_go_elem, elem)
+
+       case objabi.KindString:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+       case objabi.KindStruct:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0)
+               typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+               newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+               nfields := decodetypeStructFieldCount(ctxt.Arch, gotype)
+               for i := 0; i < nfields; i++ {
+                       f := decodetypeStructFieldName(ctxt.Arch, gotype, i)
+                       s := decodetypeStructFieldType(ctxt.Arch, gotype, i)
+                       if f == "" {
+                               f = s.Name[5:] // skip "type."
+                       }
+                       fld := newdie(ctxt, die, dwarf.DW_ABRV_STRUCTFIELD, f, 0)
+                       newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
+                       offsetAnon := decodetypeStructFieldOffsAnon(ctxt.Arch, gotype, i)
+                       newmemberoffsetattr(fld, int32(offsetAnon>>1))
+                       if offsetAnon&1 != 0 { // is embedded field
+                               newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0)
+                       }
+               }
+
+       case objabi.KindUnsafePointer:
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0)
+
+       default:
+               Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
+               die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0)
+               newrefattr(die, dwarf.DW_AT_type, mustFind(ctxt, "<unspecified>"))
+       }
+
+       newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0)
+       if gotype.Attr.Reachable() {
+               newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype)
+       }
+
+       if _, ok := prototypedies[gotype.Name]; ok {
+               prototypedies[gotype.Name] = die
+       }
+
+       if typedefdie != nil {
+               return typedefdie
+       }
+       return die
+}
+
+func nameFromDIESym(dwtype *sym.Symbol) string {
+       return strings.TrimSuffix(dwtype.Name[len(dwarf.InfoPrefix):], "..def")
+}
+
+// Find or construct *T given T.
+func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol {
+       ptrname := "*" + nameFromDIESym(dwtype)
+       if die := find(ctxt, ptrname); die != nil {
+               return die
+       }
+
+       pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
+       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.
+       gotype := ctxt.Syms.ROLookup("type."+ptrname, 0)
+       if gotype != nil && gotype.Attr.Reachable() {
+               newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype)
+       }
+       return 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 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 := newdie(ctxt, dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0)
+               for a := src.Attr; a != nil; a = a.Link {
+                       newattr(c, a.Atr, int(a.Cls), a.Value, a.Data)
+               }
+               copychildrenexcept(ctxt, c, src, nil)
+       }
+
+       reverselist(&dst.Child)
+}
+
+func copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) {
+       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 substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) {
+       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 = dwtype
+       } else {
+               newrefattr(child, dwarf.DW_AT_type, dwtype)
+       }
+}
+
+func findprotodie(ctxt *Link, name string) *dwarf.DWDie {
+       die, ok := prototypedies[name]
+       if ok && die == nil {
+               defgotype(ctxt, lookupOrDiag(ctxt, name))
+               die = prototypedies[name]
+       }
+       return die
+}
+
+func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
+       prototype := walktypedef(findprotodie(ctxt, "type.runtime.stringStructDWARF"))
+       if prototype == nil {
+               return
+       }
+
+       for ; die != nil; die = die.Link {
+               if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE {
+                       continue
+               }
+               copychildren(ctxt, die, prototype)
+       }
+}
+
+func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
+       prototype := walktypedef(findprotodie(ctxt, "type.runtime.slice"))
+       if prototype == nil {
+               return
+       }
+
+       for ; die != nil; die = die.Link {
+               if die.Abbrev != dwarf.DW_ABRV_SLICETYPE {
+                       continue
+               }
+               copychildren(ctxt, die, prototype)
+               elem := getattr(die, dwarf.DW_AT_go_elem).Data.(*sym.Symbol)
+               substitutetype(die, "array", defptrto(ctxt, 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 mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) *sym.Symbol {
+       name := mkinternaltypename(typename, keyname, valname)
+       symname := dwarf.InfoPrefix + name
+       s := ctxt.Syms.ROLookup(symname, 0)
+       if s != nil && s.Type == sym.SDWARFINFO {
+               return s
+       }
+       die := newdie(ctxt, &dwtypes, abbrev, name, 0)
+       f(die)
+       return dtolsym(die.Sym)
+}
+
+func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
+       hash := walktypedef(findprotodie(ctxt, "type.runtime.hmap"))
+       bucket := walktypedef(findprotodie(ctxt, "type.runtime.bmap"))
+
+       if hash == nil {
+               return
+       }
+
+       for ; die != nil; die = die.Link {
+               if die.Abbrev != dwarf.DW_ABRV_MAPTYPE {
+                       continue
+               }
+               gotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol)
+               keytype := decodetypeMapKey(ctxt.Arch, gotype)
+               valtype := decodetypeMapValue(ctxt.Arch, gotype)
+               keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P)
+               keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype))
+
+               // compute size info like hashmap.c does.
+               indirectKey, indirectVal := false, false
+               if keysize > MaxKeySize {
+                       keysize = int64(ctxt.Arch.PtrSize)
+                       indirectKey = true
+               }
+               if valsize > MaxValSize {
+                       valsize = int64(ctxt.Arch.PtrSize)
+                       indirectVal = true
+               }
+
+               // Construct type to represent an array of BucketSize keys
+               keyname := nameFromDIESym(keytype)
+               dwhks := 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 = defptrto(ctxt, keytype)
+                       }
+                       newrefattr(dwhk, dwarf.DW_AT_type, t)
+                       fld := newdie(ctxt, dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+                       newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
+                       newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+               })
+
+               // Construct type to represent an array of BucketSize values
+               valname := nameFromDIESym(valtype)
+               dwhvs := 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 = defptrto(ctxt, valtype)
+                       }
+                       newrefattr(dwhv, dwarf.DW_AT_type, t)
+                       fld := newdie(ctxt, dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+                       newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
+                       newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+               })
+
+               // Construct bucket<K,V>
+               dwhbs := 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.
+                       copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
+
+                       fld := newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0)
+                       newrefattr(fld, dwarf.DW_AT_type, dwhks)
+                       newmemberoffsetattr(fld, BucketSize)
+                       fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0)
+                       newrefattr(fld, dwarf.DW_AT_type, dwhvs)
+                       newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
+                       fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0)
+                       newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, dtolsym(dwhb.Sym)))
+                       newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize)))
+                       if ctxt.Arch.RegSize > ctxt.Arch.PtrSize {
+                               fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0)
+                               newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+                               newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(ctxt.Arch.PtrSize))
+                       }
+
+                       newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(ctxt.Arch.RegSize), 0)
+               })
+
+               // Construct hash<K,V>
+               dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
+                       copychildren(ctxt, dwh, hash)
+                       substitutetype(dwh, "buckets", defptrto(ctxt, dwhbs))
+                       substitutetype(dwh, "oldbuckets", defptrto(ctxt, 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>
+               newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs))
+       }
+}
+
+func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) {
+       sudog := walktypedef(findprotodie(ctxt, "type.runtime.sudog"))
+       waitq := walktypedef(findprotodie(ctxt, "type.runtime.waitq"))
+       hchan := walktypedef(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 := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol)
+               elemname := elemgotype.Name[5:]
+               elemtype := walksymtypedef(ctxt, defgotype(ctxt, elemgotype))
+
+               // sudog<T>
+               dwss := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) {
+                       copychildren(ctxt, dws, sudog)
+                       substitutetype(dws, "elem", defptrto(ctxt, elemtype))
+                       newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil)
+               })
+
+               // waitq<T>
+               dwws := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) {
+
+                       copychildren(ctxt, dww, waitq)
+                       substitutetype(dww, "first", defptrto(ctxt, dwss))
+                       substitutetype(dww, "last", defptrto(ctxt, dwss))
+                       newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil)
+               })
+
+               // hchan<T>
+               dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) {
+                       copychildren(ctxt, dwh, hchan)
+                       substitutetype(dwh, "recvq", dwws)
+                       substitutetype(dwh, "sendq", dwws)
+                       newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil)
+               })
+
+               newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs))
+       }
+}
+
+func dwarfDefineGlobal(ctxt *Link, s *sym.Symbol, str string, v int64, gotype *sym.Symbol) {
+       // Find a suitable CU DIE to include the global.
+       // One would think it's as simple as just looking at the unit, but that might
+       // not have any reachable code. So, we go to the runtime's CU if our unit
+       // isn't otherwise reachable.
+       var unit *sym.CompilationUnit
+       if s.Unit != nil {
+               unit = s.Unit
+       } else {
+               unit = ctxt.runtimeCU
+       }
+       dv := newdie(ctxt, unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(s.Version))
+       newabslocexprattr(dv, v, s)
+       if !s.IsFileLocal() {
+               newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0)
+       }
+       dt := defgotype(ctxt, gotype)
+       newrefattr(dv, dwarf.DW_AT_type, dt)
+}
+
+// For use with pass.c::genasmsym
+func defdwsymb(ctxt *Link, s *sym.Symbol, str string, t SymbolType, v int64, gotype *sym.Symbol) {
+       if strings.HasPrefix(str, "go.string.") {
+               return
+       }
+       if strings.HasPrefix(str, "runtime.gcbits.") {
+               return
+       }
+
+       switch t {
+       case DataSym, BSSSym:
+               switch s.Type {
+               case sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS:
+                       // ok
+               case sym.SRODATA:
+                       if gotype != nil {
+                               defgotype(ctxt, gotype)
+                       }
+                       return
+               default:
+                       return
+               }
+               if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) {
+                       return
+               }
+               dwarfDefineGlobal(ctxt, s, str, v, gotype)
+
+       case AutoSym, ParamSym, DeletedAutoSym:
+               defgotype(ctxt, gotype)
+       }
+}
+
+// createUnitLength creates the initial length field with value v and update
+// offset of unit_length if needed.
+func createUnitLength(ctxt *Link, s *sym.Symbol, v uint64) {
+       if isDwarf64(ctxt) {
+               s.AddUint32(ctxt.Arch, 0xFFFFFFFF)
+       }
+       addDwarfAddrField(ctxt, s, v)
+}
+
+// addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits.
+func addDwarfAddrField(ctxt *Link, s *sym.Symbol, v uint64) {
+       if isDwarf64(ctxt) {
+               s.AddUint(ctxt.Arch, v)
+       } else {
+               s.AddUint32(ctxt.Arch, uint32(v))
+       }
+}
+
+// addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits.
+func addDwarfAddrRef(ctxt *Link, s *sym.Symbol, t *sym.Symbol) {
+       if isDwarf64(ctxt) {
+               adddwarfref(ctxt, s, t, 8)
+       } else {
+               adddwarfref(ctxt, s, t, 4)
+       }
+}
+
+// calcCompUnitRanges calculates the PC ranges of the compilation units.
+func calcCompUnitRanges(ctxt *Link) {
+       var prevUnit *sym.CompilationUnit
+       for _, s := range ctxt.Textp {
+               if s.FuncInfo == nil {
+                       continue
+               }
+               // Skip linker-created functions (ex: runtime.addmoduledata), since they
+               // don't have DWARF to begin with.
+               if s.Unit == nil {
+                       continue
+               }
+               unit := s.Unit
+               // 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.
+               if prevUnit != unit {
+                       unit.PCs = append(unit.PCs, dwarf.Range{Start: s.Value - unit.Textp[0].Value})
+                       prevUnit = unit
+               }
+               unit.PCs[len(unit.PCs)-1].End = s.Value - unit.Textp[0].Value + s.Size
+       }
+}
+
+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
+}
+
+// If the pcln table contains runtime/proc.go, use that to set gdbscript path.
+func finddebugruntimepath(s *sym.Symbol) {
+       if gdbscript != "" {
+               return
+       }
+
+       for i := range s.FuncInfo.File {
+               f := s.FuncInfo.File[i]
+               // 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(f.Name, "runtime/proc.go"); i >= 0 {
+                       gdbscript = f.Name[:i] + "runtime/runtime-gdb.py"
+                       break
+               }
+       }
+}
+
+/*
+ * 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 importInfoSymbol(ctxt *Link, dsym *sym.Symbol) {
+       dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable
+       dsym.Type = sym.SDWARFINFO
+       for i := range dsym.R {
+               r := &dsym.R[i] // Copying sym.Reloc has measurable impact on performance
+               if r.Type == objabi.R_DWARFSECREF && r.Sym.Size == 0 {
+                       n := nameFromDIESym(r.Sym)
+                       defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0))
+               }
+       }
+}
+
+func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
+
+       var dwarfctxt dwarf.Context = dwctxt{ctxt}
+       is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles.
+
+       unitstart := int64(-1)
+       headerstart := int64(-1)
+       headerend := int64(-1)
+
+       newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, ls.Size, ls)
+
+       // Write .debug_line Line Number Program Header (sec 6.2.4)
+       // Fields marked with (*) must be changed for 64-bit dwarf
+       unitLengthOffset := ls.Size
+       createUnitLength(ctxt, ls, 0) // unit_length (*), filled in at end
+       unitstart = ls.Size
+       ls.AddUint16(ctxt.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 := ls.Size
+       addDwarfAddrField(ctxt, ls, 0) // header_length (*), filled in at end
+       headerstart = ls.Size
+
+       // cpos == unitstart + 4 + 2 + 4
+       ls.AddUint8(1)                // minimum_instruction_length
+       ls.AddUint8(is_stmt)          // default_is_stmt
+       ls.AddUint8(LINE_BASE & 0xFF) // line_base
+       ls.AddUint8(LINE_RANGE)       // line_range
+       ls.AddUint8(OPCODE_BASE)      // opcode_base
+       ls.AddUint8(0)                // standard_opcode_lengths[1]
+       ls.AddUint8(1)                // standard_opcode_lengths[2]
+       ls.AddUint8(1)                // standard_opcode_lengths[3]
+       ls.AddUint8(1)                // standard_opcode_lengths[4]
+       ls.AddUint8(1)                // standard_opcode_lengths[5]
+       ls.AddUint8(0)                // standard_opcode_lengths[6]
+       ls.AddUint8(0)                // standard_opcode_lengths[7]
+       ls.AddUint8(0)                // standard_opcode_lengths[8]
+       ls.AddUint8(1)                // standard_opcode_lengths[9]
+       ls.AddUint8(0)                // standard_opcode_lengths[10]
+       ls.AddUint8(0)                // include_directories  (empty)
+
+       // Copy over the file table.
+       fileNums := make(map[string]int)
+       for i, name := range unit.DWARFFileTable {
+               if len(name) != 0 {
+                       if strings.HasPrefix(name, src.FileSymPrefix) {
+                               name = name[len(src.FileSymPrefix):]
+                       }
+                       name = expandGoroot(name)
+               } else {
+                       // Can't have empty filenames, and having a unique filename is quite useful
+                       // for debugging.
+                       name = fmt.Sprintf("<missing>_%d", i)
+               }
+               fileNums[name] = i + 1
+               dwarfctxt.AddString(ls, name)
+               ls.AddUint8(0)
+               ls.AddUint8(0)
+               ls.AddUint8(0)
+       }
+       // Grab files for inlined functions.
+       // TODO: With difficulty, this could be moved into the compiler.
+       for _, s := range unit.Textp {
+               dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true)
+               for ri := 0; ri < len(dsym.R); ri++ {
+                       r := &dsym.R[ri]
+                       if r.Type != objabi.R_DWARFFILEREF {
+                               continue
+                       }
+                       name := r.Sym.Name
+                       if _, ok := fileNums[name]; ok {
+                               continue
+                       }
+                       fileNums[name] = len(fileNums) + 1
+                       dwarfctxt.AddString(ls, name)
+                       ls.AddUint8(0)
+                       ls.AddUint8(0)
+                       ls.AddUint8(0)
+               }
+       }
+
+       // 4 zeros: the string termination + 3 fields.
+       ls.AddUint8(0)
+       // terminate file_names.
+       headerend = ls.Size
+
+       // Output the state machine for each function remaining.
+       var lastAddr int64
+       for _, s := range unit.Textp {
+               finddebugruntimepath(s)
+
+               // Set the PC.
+               ls.AddUint8(0)
+               dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize))
+               ls.AddUint8(dwarf.DW_LNE_set_address)
+               addr := ls.AddAddr(ctxt.Arch, s)
+               // Make sure the units are sorted.
+               if addr < lastAddr {
+                       Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr)
+               }
+               lastAddr = addr
+
+               // Output the line table.
+               // TODO: Now that we have all the debug information in separate
+               // symbols, it would make sense to use a rope, and concatenate them all
+               // together rather then the append() below. This would allow us to have
+               // the compiler emit the DW_LNE_set_address and a rope data structure
+               // to concat them all together in the output.
+               lines := dwarfFuncSym(ctxt, s, dwarf.DebugLinesPrefix, false)
+               if lines != nil {
+                       ls.P = append(ls.P, lines.P...)
+               }
+       }
+
+       ls.AddUint8(0) // start extended opcode
+       dwarf.Uleb128put(dwarfctxt, ls, 1)
+       ls.AddUint8(dwarf.DW_LNE_end_sequence)
+
+       if ctxt.HeadType == objabi.Haix {
+               saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(ls.Size-unitLengthOffset))
+       }
+       if isDwarf64(ctxt) {
+               ls.SetUint(ctxt.Arch, unitLengthOffset+4, uint64(ls.Size-unitstart)) // +4 because of 0xFFFFFFFF
+               ls.SetUint(ctxt.Arch, headerLengthOffset, uint64(headerend-headerstart))
+       } else {
+               ls.SetUint32(ctxt.Arch, unitLengthOffset, uint32(ls.Size-unitstart))
+               ls.SetUint32(ctxt.Arch, headerLengthOffset, uint32(headerend-headerstart))
+       }
+
+       // Process any R_DWARFFILEREF relocations, since we now know the
+       // line table file indices for this compilation unit. Note that
+       // this loop visits only subprogram DIEs: if the compiler is
+       // changed to generate DW_AT_decl_file attributes for other
+       // DIE flavors (ex: variables) then those DIEs would need to
+       // be included below.
+       missing := make(map[int]interface{})
+       s := unit.Textp[0]
+       for _, f := range unit.FuncDIEs {
+               for ri := range f.R {
+                       r := &f.R[ri]
+                       if r.Type != objabi.R_DWARFFILEREF {
+                               continue
+                       }
+                       idx, ok := fileNums[r.Sym.Name]
+                       if ok {
+                               if int(int32(idx)) != idx {
+                                       Errorf(f, "bad R_DWARFFILEREF relocation: file index overflow")
+                               }
+                               if r.Siz != 4 {
+                                       Errorf(f, "bad R_DWARFFILEREF relocation: has size %d, expected 4", r.Siz)
+                               }
+                               if r.Off < 0 || r.Off+4 > int32(len(f.P)) {
+                                       Errorf(f, "bad R_DWARFFILEREF relocation offset %d + 4 would write past length %d", r.Off, len(s.P))
+                                       continue
+                               }
+                               if r.Add != 0 {
+                                       Errorf(f, "bad R_DWARFFILEREF relocation: addend not zero")
+                               }
+                               r.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable
+                               r.Add = int64(idx) // record the index in r.Add, we'll apply it in the reloc phase.
+                       } else {
+                               _, found := missing[int(r.Sym.Value)]
+                               if !found {
+                                       Errorf(f, "R_DWARFFILEREF relocation file missing: %v idx %d", r.Sym, r.Sym.Value)
+                                       missing[int(r.Sym.Value)] = nil
+                               }
+                       }
+               }
+       }
+}
+
+// writepcranges generates the DW_AT_ranges table for compilation unit cu.
+func writepcranges(ctxt *Link, unit *sym.CompilationUnit, base *sym.Symbol, pcs []dwarf.Range, ranges *sym.Symbol) {
+       var dwarfctxt dwarf.Context = dwctxt{ctxt}
+
+       unitLengthOffset := ranges.Size
+
+       // Create PC ranges for this CU.
+       newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, ranges.Size, ranges)
+       newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, base.Value, base)
+       dwarf.PutBasedRanges(dwarfctxt, ranges, pcs)
+
+       if ctxt.HeadType == objabi.Haix {
+               addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(ranges.Size-unitLengthOffset))
+       }
+
+}
+
+/*
+ *  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 writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
+       var dwarfctxt dwarf.Context = dwctxt{ctxt}
+       fs := ctxt.Syms.Lookup(".debug_frame", 0)
+       fs.Type = sym.SDWARFSECT
+       syms = append(syms, fs)
+
+       // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64
+       lengthFieldSize := int64(4)
+       if isDwarf64(ctxt) {
+               lengthFieldSize += 8
+       }
+
+       // Emit the CIE, Section 6.4.1
+       cieReserve := uint32(16)
+       if haslinkregister(ctxt) {
+               cieReserve = 32
+       }
+       if isDwarf64(ctxt) {
+               cieReserve += 4 // 4 bytes added for cid
+       }
+       createUnitLength(ctxt, fs, uint64(cieReserve))             // initial length, must be multiple of thearch.ptrsize
+       addDwarfAddrField(ctxt, fs, ^uint64(0))                    // cid
+       fs.AddUint8(3)                                             // dwarf version (appendix F)
+       fs.AddUint8(0)                                             // augmentation ""
+       dwarf.Uleb128put(dwarfctxt, fs, 1)                         // code_alignment_factor
+       dwarf.Sleb128put(dwarfctxt, fs, dataAlignmentFactor)       // all CFI offset calculations include multiplication with this factor
+       dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // return_address_register
+
+       fs.AddUint8(dwarf.DW_CFA_def_cfa)                          // Set the current frame address..
+       dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
+       if haslinkregister(ctxt) {
+               dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...plus a 0 offset.
+
+               fs.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
+               dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr))
+
+               fs.AddUint8(dwarf.DW_CFA_val_offset)                       // The previous value...
+               dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...of the platform's SP register...
+               dwarf.Uleb128put(dwarfctxt, fs, int64(0))                  // ...is CFA+0.
+       } else {
+               dwarf.Uleb128put(dwarfctxt, fs, int64(ctxt.Arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
+
+               fs.AddUint8(dwarf.DW_CFA_offset_extended)                                      // The previous value...
+               dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr))                     // ...of the return address...
+               dwarf.Uleb128put(dwarfctxt, fs, int64(-ctxt.Arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
+       }
+
+       pad := int64(cieReserve) + lengthFieldSize - fs.Size
+
+       if pad < 0 {
+               Exitf("dwarf: cieReserve too small by %d bytes.", -pad)
+       }
+
+       fs.AddBytes(zeros[:pad])
+
+       var deltaBuf []byte
+       pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
+       for _, s := range ctxt.Textp {
+               if s.FuncInfo == nil {
+                       continue
+               }
+
+               // Emit a FDE, Section 6.4.1.
+               // First build the section contents into a byte buffer.
+               deltaBuf = deltaBuf[:0]
+               if haslinkregister(ctxt) && s.Attr.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(s.FuncInfo.Pcsp.P); !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) == s.Size {
+                               nextpc--
+                               if nextpc < pcsp.PC {
+                                       continue
+                               }
+                       }
+
+                       spdelta := int64(pcsp.Value)
+                       if !haslinkregister(ctxt) {
+                               // Return address has been pushed onto stack.
+                               spdelta += int64(ctxt.Arch.PtrSize)
+                       }
+
+                       if haslinkregister(ctxt) && !s.Attr.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(ctxt.Arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta)
+               }
+               pad := int(Rnd(int64(len(deltaBuf)), int64(ctxt.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*ctxt.Arch.PtrSize + len(deltaBuf))
+               if isDwarf64(ctxt) {
+                       fdeLength += 4 // 4 bytes added for CIE pointer
+               }
+               createUnitLength(ctxt, fs, fdeLength)
+
+               if ctxt.LinkMode == LinkExternal {
+                       addDwarfAddrRef(ctxt, fs, fs)
+               } else {
+                       addDwarfAddrField(ctxt, fs, 0) // CIE offset
+               }
+               fs.AddAddr(ctxt.Arch, s)
+               fs.AddUintXX(ctxt.Arch, uint64(s.Size), ctxt.Arch.PtrSize) // address range
+               fs.AddBytes(deltaBuf)
+
+               if ctxt.HeadType == objabi.Haix {
+                       addDwsectCUSize(".debug_frame", s.File, fdeLength+uint64(lengthFieldSize))
+               }
+       }
+       return syms
+}
+
+/*
+ *  Walk DWarfDebugInfoEntries, and emit .debug_info
+ */
+const (
+       COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
+)
+
+func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abbrevsym *sym.Symbol, pubNames, pubTypes *pubWriter) []*sym.Symbol {
+       infosec := ctxt.Syms.Lookup(".debug_info", 0)
+       infosec.Type = sym.SDWARFINFO
+       infosec.Attr |= sym.AttrReachable
+       syms = append(syms, infosec)
+
+       var dwarfctxt dwarf.Context = dwctxt{ctxt}
+
+       for _, u := range units {
+               compunit := u.DWInfo
+               s := dtolsym(compunit.Sym)
+
+               if len(u.Textp) == 0 && u.DWInfo.Child == nil {
+                       continue
+               }
+
+               pubNames.beginCompUnit(compunit)
+               pubTypes.beginCompUnit(compunit)
+
+               // 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.
+               createUnitLength(ctxt, s, 0) // unit_length (*), will be filled in later.
+               s.AddUint16(ctxt.Arch, 4)    // dwarf version (appendix F)
+
+               // debug_abbrev_offset (*)
+               addDwarfAddrRef(ctxt, s, abbrevsym)
+
+               s.AddUint8(uint8(ctxt.Arch.PtrSize)) // address_size
+
+               dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev))
+               dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr)
+
+               cu := []*sym.Symbol{s}
+               cu = append(cu, u.AbsFnDIEs...)
+               cu = append(cu, u.FuncDIEs...)
+               if u.Consts != nil {
+                       cu = append(cu, u.Consts)
+               }
+               var cusize int64
+               for _, child := range cu {
+                       cusize += child.Size
+               }
+
+               for die := compunit.Child; die != nil; die = die.Link {
+                       l := len(cu)
+                       lastSymSz := cu[l-1].Size
+                       cu = putdie(ctxt, dwarfctxt, cu, die)
+                       if ispubname(die) {
+                               pubNames.add(die, cusize)
+                       }
+                       if ispubtype(die) {
+                               pubTypes.add(die, cusize)
+                       }
+                       if lastSymSz != cu[l-1].Size {
+                               // putdie will sometimes append directly to the last symbol of the list
+                               cusize = cusize - lastSymSz + cu[l-1].Size
+                       }
+                       for _, child := range cu[l:] {
+                               cusize += child.Size
+                       }
+               }
+               cu[len(cu)-1].AddUint8(0) // closes compilation unit DIE
+               cusize++
+
+               // Save size for AIX symbol table.
+               if ctxt.HeadType == objabi.Haix {
+                       saveDwsectCUSize(".debug_info", getPkgFromCUSym(s), uint64(cusize))
+               }
+               if isDwarf64(ctxt) {
+                       cusize -= 12                            // exclude the length field.
+                       s.SetUint(ctxt.Arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF
+               } else {
+                       cusize -= 4 // exclude the length field.
+                       s.SetUint32(ctxt.Arch, 0, uint32(cusize))
+               }
+               pubNames.endCompUnit(compunit, uint32(cusize)+4)
+               pubTypes.endCompUnit(compunit, uint32(cusize)+4)
+               syms = append(syms, cu...)
+       }
+       return syms
+}
+
+/*
+ *  Emit .debug_pubnames/_types.  _info must have been written before,
+ *  because we need die->offs and infoo/infosize;
+ */
+func ispubname(die *dwarf.DWDie) bool {
+       switch die.Abbrev {
+       case dwarf.DW_ABRV_FUNCTION, dwarf.DW_ABRV_VARIABLE:
+               a := getattr(die, dwarf.DW_AT_external)
+               return a != nil && a.Value != 0
+       }
+
+       return false
+}
+
+func ispubtype(die *dwarf.DWDie) bool {
+       return die.Abbrev >= dwarf.DW_ABRV_NULLTYPE
+}
+
+type pubWriter struct {
+       ctxt  *Link
+       s     *sym.Symbol
+       sname string
+
+       sectionstart int64
+       culengthOff  int64
+}
+
+func newPubWriter(ctxt *Link, sname string) *pubWriter {
+       s := ctxt.Syms.Lookup(sname, 0)
+       s.Type = sym.SDWARFSECT
+       return &pubWriter{ctxt: ctxt, s: s, sname: sname}
+}
+
+func (pw *pubWriter) beginCompUnit(compunit *dwarf.DWDie) {
+       pw.sectionstart = pw.s.Size
+
+       // Write .debug_pubnames/types  Header (sec 6.1.1)
+       createUnitLength(pw.ctxt, pw.s, 0)                    // unit_length (*), will be filled in later.
+       pw.s.AddUint16(pw.ctxt.Arch, 2)                       // dwarf version (appendix F)
+       addDwarfAddrRef(pw.ctxt, pw.s, dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header)
+       pw.culengthOff = pw.s.Size
+       addDwarfAddrField(pw.ctxt, pw.s, uint64(0)) // debug_info_length, will be filled in later.
+
+}
+
+func (pw *pubWriter) add(die *dwarf.DWDie, offset int64) {
+       dwa := getattr(die, dwarf.DW_AT_name)
+       name := dwa.Data.(string)
+       if die.Sym == nil {
+               fmt.Println("Missing sym for ", name)
+       }
+       addDwarfAddrField(pw.ctxt, pw.s, uint64(offset))
+       Addstring(pw.s, name)
+}
+
+func (pw *pubWriter) endCompUnit(compunit *dwarf.DWDie, culength uint32) {
+       addDwarfAddrField(pw.ctxt, pw.s, 0) // Null offset
+
+       // On AIX, save the current size of this compilation unit.
+       if pw.ctxt.HeadType == objabi.Haix {
+               saveDwsectCUSize(pw.sname, getPkgFromCUSym(dtolsym(compunit.Sym)), uint64(pw.s.Size-pw.sectionstart))
+       }
+       if isDwarf64(pw.ctxt) {
+               pw.s.SetUint(pw.ctxt.Arch, pw.sectionstart+4, uint64(pw.s.Size-pw.sectionstart)-12) // exclude the length field.
+               pw.s.SetUint(pw.ctxt.Arch, pw.culengthOff, uint64(culength))
+       } else {
+               pw.s.SetUint32(pw.ctxt.Arch, pw.sectionstart, uint32(pw.s.Size-pw.sectionstart)-4) // exclude the length field.
+               pw.s.SetUint32(pw.ctxt.Arch, pw.culengthOff, culength)
+       }
+}
+
+func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
+       // TODO (aix): make it available
+       if ctxt.HeadType == objabi.Haix {
+               return syms
+       }
+       if ctxt.LinkMode == LinkExternal && ctxt.HeadType == objabi.Hwindows && ctxt.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 syms
+       }
+
+       if gdbscript != "" {
+               s := ctxt.Syms.Lookup(".debug_gdb_scripts", 0)
+               s.Type = sym.SDWARFSECT
+               syms = append(syms, s)
+               s.AddUint8(1) // magic 1 byte?
+               Addstring(s, gdbscript)
+       }
+
+       return syms
+}
+
+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
+}
+
+// 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
+       }
+
+       if ctxt.HeadType == objabi.Haix {
+               // Initial map used to store package size for each DWARF section.
+               dwsectCUSize = make(map[string]uint64)
+       }
+
+       // Forctxt.Diagnostic messages.
+       newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes")
+
+       // Some types that must exist to define other ones.
+       newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0)
+
+       newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "void", 0)
+       newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0)
+
+       die := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size
+       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(ctxt.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, lookupOrDiag(ctxt, "type.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"} {
+               defgotype(ctxt, lookupOrDiag(ctxt, typ))
+       }
+
+       // fake root DIE for compile unit DIEs
+       var dwroot dwarf.DWDie
+       flagVariants := make(map[string]bool)
+
+       for _, lib := range ctxt.Library {
+               consts := ctxt.Syms.ROLookup(dwarf.ConstInfoPrefix+lib.Pkg, 0)
+               for _, unit := range lib.Units {
+                       // We drop the constants into the first CU.
+                       if consts != nil {
+                               importInfoSymbol(ctxt, consts)
+                               unit.Consts = consts
+                               consts = nil
+                       }
+
+                       ctxt.compUnits = append(ctxt.compUnits, unit)
+
+                       // We need at least one runtime unit.
+                       if unit.Lib.Pkg == "runtime" {
+                               ctxt.runtimeCU = unit
+                       }
+
+                       unit.DWInfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0)
+                       newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0)
+                       // OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
+                       compDir := getCompilationDir()
+                       // TODO: Make this be the actual compilation directory, not
+                       // the linker directory. If we move CU construction into the
+                       // compiler, this should happen naturally.
+                       newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir)
+                       producerExtra := ctxt.Syms.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0)
+                       producer := "Go cmd/compile " + objabi.Version
+                       if len(producerExtra.P) > 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(producerExtra.P)
+                               flagVariants[string(producerExtra.P)] = true
+                       } else {
+                               flagVariants[""] = true
+                       }
+
+                       newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer)
+
+                       var pkgname string
+                       if s := ctxt.Syms.ROLookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); s != nil {
+                               pkgname = string(s.P)
+                       }
+                       newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname)
+
+                       if len(unit.Textp) == 0 {
+                               unit.DWInfo.Abbrev = dwarf.DW_ABRV_COMPUNIT_TEXTLESS
+                       }
+
+                       // Scan all functions in this compilation unit, create DIEs for all
+                       // referenced types, create the file table for debug_line, find all
+                       // referenced abstract functions.
+                       // Collect all debug_range symbols in unit.rangeSyms
+                       for _, s := range unit.Textp { // textp has been dead-code-eliminated already.
+                               dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, false)
+                               dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable
+                               dsym.Type = sym.SDWARFINFO
+                               unit.FuncDIEs = append(unit.FuncDIEs, dsym)
+
+                               rangeSym := dwarfFuncSym(ctxt, s, dwarf.RangePrefix, false)
+                               if rangeSym != nil && rangeSym.Size > 0 {
+                                       rangeSym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable
+                                       rangeSym.Type = sym.SDWARFRANGE
+                                       if ctxt.HeadType == objabi.Haix {
+                                               addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rangeSym.Size))
+                                       }
+                                       unit.RangeSyms = append(unit.RangeSyms, rangeSym)
+                               }
+
+                               for ri := 0; ri < len(dsym.R); ri++ {
+                                       r := &dsym.R[ri]
+                                       if r.Type == objabi.R_DWARFSECREF {
+                                               rsym := r.Sym
+                                               if strings.HasPrefix(rsym.Name, dwarf.InfoPrefix) && strings.HasSuffix(rsym.Name, dwarf.AbstractFuncSuffix) && !rsym.Attr.OnList() {
+                                                       // abstract function
+                                                       rsym.Attr |= sym.AttrOnList
+                                                       unit.AbsFnDIEs = append(unit.AbsFnDIEs, rsym)
+                                                       importInfoSymbol(ctxt, rsym)
+                                               } else if rsym.Size == 0 {
+                                                       // a type we do not have a DIE for
+                                                       n := nameFromDIESym(rsym)
+                                                       defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0))
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       // Fix for 31034: if the objects feeding into this link were compiled
+       // with different sets of flags, then don't issue an error if
+       // the -strictdups checks fail.
+       if checkStrictDups > 1 && len(flagVariants) > 1 {
+               checkStrictDups = 1
+       }
+
+       // Create DIEs for global variables and the types they use.
+       genasmsym(ctxt, defdwsymb)
+
+       // Create DIEs for variable types indirectly referenced by function
+       // autos (which may not appear directly as param/var DIEs).
+       for _, lib := range ctxt.Library {
+               for _, unit := range lib.Units {
+                       lists := [][]*sym.Symbol{unit.AbsFnDIEs, unit.FuncDIEs}
+                       for _, list := range lists {
+                               for _, s := range list {
+                                       for i := 0; i < len(s.R); i++ {
+                                               r := &s.R[i]
+                                               if r.Type == objabi.R_USETYPE {
+                                                       defgotype(ctxt, r.Sym)
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       synthesizestringtypes(ctxt, dwtypes.Child)
+       synthesizeslicetypes(ctxt, dwtypes.Child)
+       synthesizemaptypes(ctxt, dwtypes.Child)
+       synthesizechantypes(ctxt, dwtypes.Child)
+}
+
+// dwarfGenerateDebugSyms constructs debug_line, debug_frame, debug_loc,
+// debug_pubnames and debug_pubtypes. It also writes out the debug_info
+// section using symbols generated in dwarfGenerateDebugInfo.
+func dwarfGenerateDebugSyms(ctxt *Link) {
+       if !dwarfEnabled(ctxt) {
+               return
+       }
+
+       abbrev := writeabbrev(ctxt)
+       syms := []*sym.Symbol{abbrev}
+
+       calcCompUnitRanges(ctxt)
+       sort.Sort(compilationUnitByStartPC(ctxt.compUnits))
+
+       // Write per-package line and range tables and start their CU DIEs.
+       debugLine := ctxt.Syms.Lookup(".debug_line", 0)
+       debugLine.Type = sym.SDWARFSECT
+       debugRanges := ctxt.Syms.Lookup(".debug_ranges", 0)
+       debugRanges.Type = sym.SDWARFRANGE
+       debugRanges.Attr |= sym.AttrReachable
+       syms = append(syms, debugLine)
+       for _, u := range ctxt.compUnits {
+               reversetree(&u.DWInfo.Child)
+               if u.DWInfo.Abbrev == dwarf.DW_ABRV_COMPUNIT_TEXTLESS {
+                       continue
+               }
+               writelines(ctxt, u, debugLine)
+               writepcranges(ctxt, u, u.Textp[0], u.PCs, debugRanges)
+       }
+
+       // 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.
+       reversetree(&dwtypes.Child)
+       movetomodule(ctxt, &dwtypes)
+
+       pubNames := newPubWriter(ctxt, ".debug_pubnames")
+       pubTypes := newPubWriter(ctxt, ".debug_pubtypes")
+
+       // Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT
+       infosyms := writeinfo(ctxt, nil, ctxt.compUnits, abbrev, pubNames, pubTypes)
+
+       syms = writeframes(ctxt, syms)
+       syms = append(syms, pubNames.s, pubTypes.s)
+       syms = writegdbscript(ctxt, syms)
+       // Now we're done writing SDWARFSECT symbols, so we can write
+       // other SDWARF* symbols.
+       syms = append(syms, infosyms...)
+       syms = collectlocs(ctxt, syms, ctxt.compUnits)
+       syms = append(syms, debugRanges)
+       for _, unit := range ctxt.compUnits {
+               syms = append(syms, unit.RangeSyms...)
+       }
+       dwarfp = syms
+}
+
+func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) []*sym.Symbol {
+       empty := true
+       for _, u := range units {
+               for _, fn := range u.FuncDIEs {
+                       for i := range fn.R {
+                               reloc := &fn.R[i] // Copying sym.Reloc has measurable impact on performance
+                               if reloc.Type == objabi.R_DWARFSECREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) {
+                                       reloc.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable
+                                       syms = append(syms, reloc.Sym)
+                                       empty = false
+                                       // One location list entry per function, but many relocations to it. Don't duplicate.
+                                       break
+                               }
+                       }
+               }
+       }
+       // Don't emit .debug_loc if it's empty -- it makes the ARM linker mad.
+       if !empty {
+               locsym := ctxt.Syms.Lookup(".debug_loc", 0)
+               locsym.Type = sym.SDWARFLOC
+               locsym.Attr |= sym.AttrReachable
+               syms = append(syms, locsym)
+       }
+       return syms
+}
+
+// Read a pointer-sized uint from the beginning of buf.
+func readPtr(ctxt *Link, buf []byte) uint64 {
+       switch ctxt.Arch.PtrSize {
+       case 4:
+               return uint64(ctxt.Arch.ByteOrder.Uint32(buf))
+       case 8:
+               return ctxt.Arch.ByteOrder.Uint64(buf)
+       default:
+               panic("unexpected pointer size")
+       }
+}
+
+/*
+ *  Elf.
+ */
+func dwarfaddshstrings(ctxt *Link, shstrtab *sym.Symbol) {
+       if *FlagW { // disable dwarf
+               return
+       }
+
+       secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"}
+       for _, sec := range secs {
+               Addstring(shstrtab, ".debug_"+sec)
+               if ctxt.LinkMode == LinkExternal {
+                       Addstring(shstrtab, elfRelType+".debug_"+sec)
+               } else {
+                       Addstring(shstrtab, ".zdebug_"+sec)
+               }
+       }
+}
+
+// Add section symbols for DWARF debug info.  This is called before
+// dwarfaddelfheaders.
+func dwarfaddelfsectionsyms(ctxt *Link) {
+       if *FlagW { // disable dwarf
+               return
+       }
+       if ctxt.LinkMode != LinkExternal {
+               return
+       }
+
+       s := ctxt.Syms.Lookup(".debug_info", 0)
+       putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+       s = ctxt.Syms.Lookup(".debug_abbrev", 0)
+       putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+       s = ctxt.Syms.Lookup(".debug_line", 0)
+       putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+       s = ctxt.Syms.Lookup(".debug_frame", 0)
+       putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+       s = ctxt.Syms.Lookup(".debug_loc", 0)
+       if s.Sect != nil {
+               putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum)
+       }
+       s = ctxt.Syms.Lookup(".debug_ranges", 0)
+       if s.Sect != nil {
+               putelfsectionsym(ctxt.Out, s, 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) {
+       supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin
+       if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal {
+               return
+       }
+
+       var start int
+       var newDwarfp []*sym.Symbol
+       Segdwarf.Sections = Segdwarf.Sections[:0]
+       for i, s := range dwarfp {
+               // Find the boundaries between sections and compress
+               // the whole section once we've found the last of its
+               // symbols.
+               if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect {
+                       s1 := compressSyms(ctxt, dwarfp[start:i+1])
+                       if s1 == nil {
+                               // Compression didn't help.
+                               newDwarfp = append(newDwarfp, dwarfp[start:i+1]...)
+                               Segdwarf.Sections = append(Segdwarf.Sections, s.Sect)
+                       } else {
+                               compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):]
+                               sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04)
+                               sect.Length = uint64(len(s1))
+                               newSym := ctxt.Syms.Lookup(compressedSegName, 0)
+                               newSym.P = s1
+                               newSym.Size = int64(len(s1))
+                               newSym.Sect = sect
+                               newDwarfp = append(newDwarfp, newSym)
+                       }
+                       start = i + 1
+               }
+       }
+       dwarfp = newDwarfp
+       ctxt.relocbuf = nil // no longer needed, don't hold it live
+
+       // 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 _, s := range dwarfp {
+               s.Value = int64(pos)
+               if s.Sect != prevSect {
+                       s.Sect.Vaddr = uint64(s.Value)
+                       prevSect = s.Sect
+               }
+               if s.Sub != nil {
+                       log.Fatalf("%s: unexpected sub-symbols", s)
+               }
+               pos += uint64(s.Size)
+               if ctxt.HeadType == objabi.Hwindows {
+                       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].Textp[0].Value < v[j].Textp[0].Value
+       }
+}
+
+// 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 dwsectCUSize map[string]uint64
+
+// getDwsectCUSize retrieves the corresponding package size inside the current section.
+func getDwsectCUSize(sname string, pkgname string) uint64 {
+       return dwsectCUSize[sname+"."+pkgname]
+}
+
+func saveDwsectCUSize(sname string, pkgname string, size uint64) {
+       dwsectCUSize[sname+"."+pkgname] = size
+}
+
+func addDwsectCUSize(sname string, pkgname string, size uint64) {
+       dwsectCUSize[sname+"."+pkgname] += size
+}
+
+// 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 getPkgFromCUSym(s *sym.Symbol) string {
+       return strings.TrimPrefix(s.Name, dwarf.InfoPrefix+".pkg.")
+}
diff --git a/src/cmd/oldlink/internal/ld/dwarf_test.go b/src/cmd/oldlink/internal/ld/dwarf_test.go
new file mode 100644 (file)
index 0000000..cf6bec8
--- /dev/null
@@ -0,0 +1,1365 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+       intdwarf "cmd/internal/dwarf"
+       objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function
+       "debug/dwarf"
+       "debug/pe"
+       "errors"
+       "fmt"
+       "internal/testenv"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "reflect"
+       "runtime"
+       "strconv"
+       "strings"
+       "testing"
+)
+
+const (
+       DefaultOpt = "-gcflags="
+       NoOpt      = "-gcflags=-l -N"
+       OptInl4    = "-gcflags=-l=4"
+       OptAllInl4 = "-gcflags=all=-l=4"
+)
+
+func TestRuntimeTypesPresent(t *testing.T) {
+       t.Parallel()
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       dir, err := ioutil.TempDir("", "TestRuntimeTypesPresent")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       f := gobuild(t, dir, `package main; func main() { }`, NoOpt)
+       defer f.Close()
+
+       dwarf, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       want := map[string]bool{
+               "runtime._type":         true,
+               "runtime.arraytype":     true,
+               "runtime.chantype":      true,
+               "runtime.functype":      true,
+               "runtime.maptype":       true,
+               "runtime.ptrtype":       true,
+               "runtime.slicetype":     true,
+               "runtime.structtype":    true,
+               "runtime.interfacetype": true,
+               "runtime.itab":          true,
+               "runtime.imethod":       true,
+       }
+
+       found := findTypes(t, dwarf, want)
+       if len(found) != len(want) {
+               t.Errorf("found %v, want %v", found, want)
+       }
+}
+
+func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) {
+       found = make(map[string]bool)
+       rdr := dw.Reader()
+       for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+               if err != nil {
+                       t.Fatalf("error reading DWARF: %v", err)
+               }
+               switch entry.Tag {
+               case dwarf.TagTypedef:
+                       if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] {
+                               found[name] = true
+                       }
+               }
+       }
+       return
+}
+
+type builtFile struct {
+       *objfilepkg.File
+       path string
+}
+
+func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile {
+       src := filepath.Join(dir, "test.go")
+       dst := filepath.Join(dir, "out.exe")
+
+       if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil {
+               t.Fatal(err)
+       }
+
+       cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst, src)
+       if b, err := cmd.CombinedOutput(); err != nil {
+               t.Logf("build: %s\n", b)
+               t.Fatalf("build error: %v", err)
+       }
+
+       f, err := objfilepkg.Open(dst)
+       if err != nil {
+               t.Fatal(err)
+       }
+       return &builtFile{f, dst}
+}
+
+// Similar to gobuild() above, but uses a main package instead of a test.go file.
+
+func gobuildTestdata(t *testing.T, tdir string, pkgDir string, gcflags string) *builtFile {
+       dst := filepath.Join(tdir, "out.exe")
+
+       // Run a build with an updated GOPATH
+       cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst)
+       cmd.Dir = pkgDir
+       if b, err := cmd.CombinedOutput(); err != nil {
+               t.Logf("build: %s\n", b)
+               t.Fatalf("build error: %v", err)
+       }
+
+       f, err := objfilepkg.Open(dst)
+       if err != nil {
+               t.Fatal(err)
+       }
+       return &builtFile{f, dst}
+}
+
+func TestEmbeddedStructMarker(t *testing.T) {
+       t.Parallel()
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       const prog = `
+package main
+
+import "fmt"
+
+type Foo struct { v int }
+type Bar struct {
+       Foo
+       name string
+}
+type Baz struct {
+       *Foo
+       name string
+}
+
+func main() {
+       bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"}
+       baz := Baz{ Foo: &bar.Foo, name: "123" }
+       fmt.Println(bar, baz)
+}`
+
+       want := map[string]map[string]bool{
+               "main.Foo": {"v": false},
+               "main.Bar": {"Foo": true, "name": false},
+               "main.Baz": {"Foo": true, "name": false},
+       }
+
+       dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       f := gobuild(t, dir, prog, NoOpt)
+
+       defer f.Close()
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       rdr := d.Reader()
+       for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+               if err != nil {
+                       t.Fatalf("error reading DWARF: %v", err)
+               }
+               switch entry.Tag {
+               case dwarf.TagStructType:
+                       name := entry.Val(dwarf.AttrName).(string)
+                       wantMembers := want[name]
+                       if wantMembers == nil {
+                               continue
+                       }
+                       gotMembers, err := findMembers(rdr)
+                       if err != nil {
+                               t.Fatalf("error reading DWARF: %v", err)
+                       }
+
+                       if !reflect.DeepEqual(gotMembers, wantMembers) {
+                               t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers)
+                       }
+                       delete(want, name)
+               }
+       }
+       if len(want) != 0 {
+               t.Errorf("failed to check all expected types: missing types = %+v", want)
+       }
+}
+
+func findMembers(rdr *dwarf.Reader) (map[string]bool, error) {
+       memberEmbedded := map[string]bool{}
+       // TODO(hyangah): define in debug/dwarf package
+       const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field)
+       for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+               if err != nil {
+                       return nil, err
+               }
+               switch entry.Tag {
+               case dwarf.TagMember:
+                       name := entry.Val(dwarf.AttrName).(string)
+                       embedded := entry.Val(goEmbeddedStruct).(bool)
+                       memberEmbedded[name] = embedded
+               case 0:
+                       return memberEmbedded, nil
+               }
+       }
+       return memberEmbedded, nil
+}
+
+func TestSizes(t *testing.T) {
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+       t.Parallel()
+
+       // DWARF sizes should never be -1.
+       // See issue #21097
+       const prog = `
+package main
+var x func()
+var y [4]func()
+func main() {
+       x = nil
+       y[0] = nil
+}
+`
+       dir, err := ioutil.TempDir("", "TestSizes")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+       f := gobuild(t, dir, prog, NoOpt)
+       defer f.Close()
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+       rdr := d.Reader()
+       for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+               if err != nil {
+                       t.Fatalf("error reading DWARF: %v", err)
+               }
+               switch entry.Tag {
+               case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
+               default:
+                       continue
+               }
+               typ, err := d.Type(entry.Offset)
+               if err != nil {
+                       t.Fatalf("can't read type: %v", err)
+               }
+               if typ.Size() < 0 {
+                       t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ)
+               }
+       }
+}
+
+func TestFieldOverlap(t *testing.T) {
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+       t.Parallel()
+
+       // This test grew out of issue 21094, where specific sudog<T> DWARF types
+       // had elem fields set to values instead of pointers.
+       const prog = `
+package main
+
+var c chan string
+
+func main() {
+       c <- "foo"
+}
+`
+       dir, err := ioutil.TempDir("", "TestFieldOverlap")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       f := gobuild(t, dir, prog, NoOpt)
+       defer f.Close()
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       rdr := d.Reader()
+       for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+               if err != nil {
+                       t.Fatalf("error reading DWARF: %v", err)
+               }
+               if entry.Tag != dwarf.TagStructType {
+                       continue
+               }
+               typ, err := d.Type(entry.Offset)
+               if err != nil {
+                       t.Fatalf("can't read type: %v", err)
+               }
+               s := typ.(*dwarf.StructType)
+               for i := 0; i < len(s.Field); i++ {
+                       end := s.Field[i].ByteOffset + s.Field[i].Type.Size()
+                       var limit int64
+                       if i == len(s.Field)-1 {
+                               limit = s.Size()
+                       } else {
+                               limit = s.Field[i+1].ByteOffset
+                       }
+                       if end > limit {
+                               name := entry.Val(dwarf.AttrName).(string)
+                               t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name)
+                       }
+               }
+       }
+}
+
+func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) {
+       t.Parallel()
+
+       prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive)
+
+       dir, err := ioutil.TempDir("", testpoint)
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       f := gobuild(t, dir, prog, NoOpt)
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       rdr := d.Reader()
+       ex := examiner{}
+       if err := ex.populate(rdr); err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       // Locate the main.main DIE
+       mains := ex.Named("main.main")
+       if len(mains) == 0 {
+               t.Fatalf("unable to locate DIE for main.main")
+       }
+       if len(mains) != 1 {
+               t.Fatalf("more than one main.main DIE")
+       }
+       maindie := mains[0]
+
+       // Vet the main.main DIE
+       if maindie.Tag != dwarf.TagSubprogram {
+               t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
+       }
+
+       // Walk main's children and select variable "i".
+       mainIdx := ex.idxFromOffset(maindie.Offset)
+       childDies := ex.Children(mainIdx)
+       var iEntry *dwarf.Entry
+       for _, child := range childDies {
+               if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" {
+                       iEntry = child
+                       break
+               }
+       }
+       if iEntry == nil {
+               t.Fatalf("didn't find DW_TAG_variable for i in main.main")
+       }
+
+       // Verify line/file attributes.
+       line := iEntry.Val(dwarf.AttrDeclLine)
+       if line == nil || line.(int64) != int64(expectLine) {
+               t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine)
+       }
+
+       fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64)
+       if !fileIdxOK {
+               t.Errorf("missing or invalid DW_AT_decl_file for main")
+       }
+       file := ex.FileRef(t, d, mainIdx, fileIdx)
+       base := filepath.Base(file)
+       if base != expectFile {
+               t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile)
+       }
+}
+
+func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", "test.go", 5, "")
+}
+
+func TestVarDeclCoordsWithLineDirective(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective",
+               "foobar.go", 202, "//line /foobar.go:200")
+}
+
+// Helper class for supporting queries on DIEs within a DWARF .debug_info
+// section. Invoke the populate() method below passing in a dwarf.Reader,
+// which will read in all DIEs and keep track of parent/child
+// relationships. Queries can then be made to ask for DIEs by name or
+// by offset. This will hopefully reduce boilerplate for future test
+// writing.
+
+type examiner struct {
+       dies        []*dwarf.Entry
+       idxByOffset map[dwarf.Offset]int
+       kids        map[int][]int
+       parent      map[int]int
+       byname      map[string][]int
+}
+
+// Populate the examiner using the DIEs read from rdr.
+func (ex *examiner) populate(rdr *dwarf.Reader) error {
+       ex.idxByOffset = make(map[dwarf.Offset]int)
+       ex.kids = make(map[int][]int)
+       ex.parent = make(map[int]int)
+       ex.byname = make(map[string][]int)
+       var nesting []int
+       for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+               if err != nil {
+                       return err
+               }
+               if entry.Tag == 0 {
+                       // terminator
+                       if len(nesting) == 0 {
+                               return errors.New("nesting stack underflow")
+                       }
+                       nesting = nesting[:len(nesting)-1]
+                       continue
+               }
+               idx := len(ex.dies)
+               ex.dies = append(ex.dies, entry)
+               if _, found := ex.idxByOffset[entry.Offset]; found {
+                       return errors.New("DIE clash on offset")
+               }
+               ex.idxByOffset[entry.Offset] = idx
+               if name, ok := entry.Val(dwarf.AttrName).(string); ok {
+                       ex.byname[name] = append(ex.byname[name], idx)
+               }
+               if len(nesting) > 0 {
+                       parent := nesting[len(nesting)-1]
+                       ex.kids[parent] = append(ex.kids[parent], idx)
+                       ex.parent[idx] = parent
+               }
+               if entry.Children {
+                       nesting = append(nesting, idx)
+               }
+       }
+       if len(nesting) > 0 {
+               return errors.New("unterminated child sequence")
+       }
+       return nil
+}
+
+func indent(ilevel int) {
+       for i := 0; i < ilevel; i++ {
+               fmt.Printf("  ")
+       }
+}
+
+// For debugging new tests
+func (ex *examiner) dumpEntry(idx int, dumpKids bool, ilevel int) error {
+       if idx >= len(ex.dies) {
+               msg := fmt.Sprintf("bad DIE %d: index out of range\n", idx)
+               return errors.New(msg)
+       }
+       entry := ex.dies[idx]
+       indent(ilevel)
+       fmt.Printf("0x%x: %v\n", idx, entry.Tag)
+       for _, f := range entry.Field {
+               indent(ilevel)
+               fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val)
+       }
+       if dumpKids {
+               ksl := ex.kids[idx]
+               for _, k := range ksl {
+                       ex.dumpEntry(k, true, ilevel+2)
+               }
+       }
+       return nil
+}
+
+// Given a DIE offset, return the previously read dwarf.Entry, or nil
+func (ex *examiner) entryFromOffset(off dwarf.Offset) *dwarf.Entry {
+       if idx, found := ex.idxByOffset[off]; found && idx != -1 {
+               return ex.entryFromIdx(idx)
+       }
+       return nil
+}
+
+// Return the ID that examiner uses to refer to the DIE at offset off
+func (ex *examiner) idxFromOffset(off dwarf.Offset) int {
+       if idx, found := ex.idxByOffset[off]; found {
+               return idx
+       }
+       return -1
+}
+
+// Return the dwarf.Entry pointer for the DIE with id 'idx'
+func (ex *examiner) entryFromIdx(idx int) *dwarf.Entry {
+       if idx >= len(ex.dies) || idx < 0 {
+               return nil
+       }
+       return ex.dies[idx]
+}
+
+// Returns a list of child entries for a die with ID 'idx'
+func (ex *examiner) Children(idx int) []*dwarf.Entry {
+       sl := ex.kids[idx]
+       ret := make([]*dwarf.Entry, len(sl))
+       for i, k := range sl {
+               ret[i] = ex.entryFromIdx(k)
+       }
+       return ret
+}
+
+// Returns parent DIE for DIE 'idx', or nil if the DIE is top level
+func (ex *examiner) Parent(idx int) *dwarf.Entry {
+       p, found := ex.parent[idx]
+       if !found {
+               return nil
+       }
+       return ex.entryFromIdx(p)
+}
+
+// ParentCU returns the enclosing compilation unit DIE for the DIE
+// with a given index, or nil if for some reason we can't establish a
+// parent.
+func (ex *examiner) ParentCU(idx int) *dwarf.Entry {
+       for {
+               parentDie := ex.Parent(idx)
+               if parentDie == nil {
+                       return nil
+               }
+               if parentDie.Tag == dwarf.TagCompileUnit {
+                       return parentDie
+               }
+               idx = ex.idxFromOffset(parentDie.Offset)
+       }
+}
+
+// FileRef takes a given DIE by index and a numeric file reference
+// (presumably from a decl_file or call_file attribute), looks up the
+// reference in the .debug_line file table, and returns the proper
+// string for it. We need to know which DIE is making the reference
+// so as find the right compilation unit.
+func (ex *examiner) FileRef(t *testing.T, dw *dwarf.Data, dieIdx int, fileRef int64) string {
+
+       // Find the parent compilation unit DIE for the specified DIE.
+       cuDie := ex.ParentCU(dieIdx)
+       if cuDie == nil {
+               t.Fatalf("no parent CU DIE for DIE with idx %d?", dieIdx)
+               return ""
+       }
+       // Construct a line reader and then use it to get the file string.
+       lr, lrerr := dw.LineReader(cuDie)
+       if lrerr != nil {
+               t.Fatal("d.LineReader: ", lrerr)
+               return ""
+       }
+       files := lr.Files()
+       if fileRef < 0 || int(fileRef) > len(files)-1 {
+               t.Fatalf("examiner.FileRef: malformed file reference %d", fileRef)
+               return ""
+       }
+       return files[fileRef].Name
+}
+
+// Return a list of all DIEs with name 'name'. When searching for DIEs
+// by name, keep in mind that the returned results will include child
+// DIEs such as params/variables. For example, asking for all DIEs named
+// "p" for even a small program will give you 400-500 entries.
+func (ex *examiner) Named(name string) []*dwarf.Entry {
+       sl := ex.byname[name]
+       ret := make([]*dwarf.Entry, len(sl))
+       for i, k := range sl {
+               ret[i] = ex.entryFromIdx(k)
+       }
+       return ret
+}
+
+func TestInlinedRoutineRecords(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+       if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" {
+               t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168")
+       }
+
+       t.Parallel()
+
+       const prog = `
+package main
+
+var G int
+
+func noinline(x int) int {
+       defer func() { G += x }()
+       return x
+}
+
+func cand(x, y int) int {
+       return noinline(x+y) ^ (y - x)
+}
+
+func main() {
+    x := cand(G*G,G|7%G)
+    G = x
+}
+`
+       dir, err := ioutil.TempDir("", "TestInlinedRoutineRecords")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       // Note: this is a build with "-l=4", as opposed to "-l -N". The
+       // test is intended to verify DWARF that is only generated when
+       // the inliner is active. We're only going to look at the DWARF for
+       // main.main, however, hence we build with "-gcflags=-l=4" as opposed
+       // to "-gcflags=all=-l=4".
+       f := gobuild(t, dir, prog, OptInl4)
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       // The inlined subroutines we expect to visit
+       expectedInl := []string{"main.cand"}
+
+       rdr := d.Reader()
+       ex := examiner{}
+       if err := ex.populate(rdr); err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       // Locate the main.main DIE
+       mains := ex.Named("main.main")
+       if len(mains) == 0 {
+               t.Fatalf("unable to locate DIE for main.main")
+       }
+       if len(mains) != 1 {
+               t.Fatalf("more than one main.main DIE")
+       }
+       maindie := mains[0]
+
+       // Vet the main.main DIE
+       if maindie.Tag != dwarf.TagSubprogram {
+               t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
+       }
+
+       // Walk main's children and pick out the inlined subroutines
+       mainIdx := ex.idxFromOffset(maindie.Offset)
+       childDies := ex.Children(mainIdx)
+       exCount := 0
+       for _, child := range childDies {
+               if child.Tag == dwarf.TagInlinedSubroutine {
+                       // Found an inlined subroutine, locate abstract origin.
+                       ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+                       if !originOK {
+                               t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
+                       }
+                       originDIE := ex.entryFromOffset(ooff)
+                       if originDIE == nil {
+                               t.Fatalf("can't locate origin DIE at off %v", ooff)
+                       }
+
+                       // Walk the children of the abstract subroutine. We expect
+                       // to see child variables there, even if (perhaps due to
+                       // optimization) there are no references to them from the
+                       // inlined subroutine DIE.
+                       absFcnIdx := ex.idxFromOffset(ooff)
+                       absFcnChildDies := ex.Children(absFcnIdx)
+                       if len(absFcnChildDies) != 2 {
+                               t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies))
+                       }
+                       formalCount := 0
+                       for _, absChild := range absFcnChildDies {
+                               if absChild.Tag == dwarf.TagFormalParameter {
+                                       formalCount += 1
+                                       continue
+                               }
+                               t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag)
+                       }
+                       if formalCount != 2 {
+                               t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount)
+                       }
+
+                       if exCount >= len(expectedInl) {
+                               t.Fatalf("too many inlined subroutines found in main.main")
+                       }
+
+                       // Name should check out.
+                       expected := expectedInl[exCount]
+                       if name, ok := originDIE.Val(dwarf.AttrName).(string); ok {
+                               if name != expected {
+                                       t.Fatalf("expected inlined routine %s got %s", name, expected)
+                               }
+                       }
+                       exCount++
+
+                       // Verify that the call_file attribute for the inlined
+                       // instance is ok. In this case it should match the file
+                       // for the main routine. To do this we need to locate the
+                       // compilation unit DIE that encloses what we're looking
+                       // at; this can be done with the examiner.
+                       cf, cfOK := child.Val(dwarf.AttrCallFile).(int64)
+                       if !cfOK {
+                               t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset)
+                       }
+                       file := ex.FileRef(t, d, mainIdx, cf)
+                       base := filepath.Base(file)
+                       if base != "test.go" {
+                               t.Errorf("bad call_file attribute, found '%s', want '%s'",
+                                       file, "test.go")
+                       }
+
+                       omap := make(map[dwarf.Offset]bool)
+
+                       // Walk the child variables of the inlined routine. Each
+                       // of them should have a distinct abstract origin-- if two
+                       // vars point to the same origin things are definitely broken.
+                       inlIdx := ex.idxFromOffset(child.Offset)
+                       inlChildDies := ex.Children(inlIdx)
+                       for _, k := range inlChildDies {
+                               ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+                               if !originOK {
+                                       t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset)
+                               }
+                               if _, found := omap[ooff]; found {
+                                       t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset)
+                               }
+                               omap[ooff] = true
+                       }
+               }
+       }
+       if exCount != len(expectedInl) {
+               t.Fatalf("not enough inlined subroutines found in main.main")
+       }
+}
+
+func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
+       t.Parallel()
+
+       dir, err := ioutil.TempDir("", "TestAbstractOriginSanity")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       // Build with inlining, to exercise DWARF inlining support.
+       f := gobuildTestdata(t, dir, filepath.Join(pkgDir, "main"), flags)
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+       rdr := d.Reader()
+       ex := examiner{}
+       if err := ex.populate(rdr); err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       // Make a pass through all DIEs looking for abstract origin
+       // references.
+       abscount := 0
+       for i, die := range ex.dies {
+               // Does it have an abstract origin?
+               ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+               if !originOK {
+                       continue
+               }
+
+               // All abstract origin references should be resolvable.
+               abscount += 1
+               originDIE := ex.entryFromOffset(ooff)
+               if originDIE == nil {
+                       ex.dumpEntry(i, false, 0)
+                       t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset)
+               }
+
+               // Suppose that DIE X has parameter/variable children {K1,
+               // K2, ... KN}. If X has an abstract origin of A, then for
+               // each KJ, the abstract origin of KJ should be a child of A.
+               // Note that this same rule doesn't hold for non-variable DIEs.
+               pidx := ex.idxFromOffset(die.Offset)
+               if pidx < 0 {
+                       t.Fatalf("can't locate DIE id")
+               }
+               kids := ex.Children(pidx)
+               for _, kid := range kids {
+                       if kid.Tag != dwarf.TagVariable &&
+                               kid.Tag != dwarf.TagFormalParameter {
+                               continue
+                       }
+                       kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+                       if !originOK {
+                               continue
+                       }
+                       childOriginDIE := ex.entryFromOffset(kooff)
+                       if childOriginDIE == nil {
+                               ex.dumpEntry(i, false, 0)
+                               t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset)
+                       }
+                       coidx := ex.idxFromOffset(childOriginDIE.Offset)
+                       childOriginParent := ex.Parent(coidx)
+                       if childOriginParent != originDIE {
+                               ex.dumpEntry(i, false, 0)
+                               t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset)
+                       }
+               }
+       }
+       if abscount == 0 {
+               t.Fatalf("no abstract origin refs found, something is wrong")
+       }
+}
+
+func TestAbstractOriginSanity(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       if testing.Short() {
+               t.Skip("skipping test in short mode.")
+       }
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+       if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" {
+               t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168")
+       }
+
+       if wd, err := os.Getwd(); err == nil {
+               gopathdir := filepath.Join(wd, "testdata", "httptest")
+               abstractOriginSanity(t, gopathdir, OptAllInl4)
+       } else {
+               t.Fatalf("os.Getwd() failed %v", err)
+       }
+}
+
+func TestAbstractOriginSanityIssue25459(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+       if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" {
+               t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168")
+       }
+       if runtime.GOARCH != "amd64" && runtime.GOARCH != "x86" {
+               t.Skip("skipping on not-amd64 not-x86; location lists not supported")
+       }
+
+       if wd, err := os.Getwd(); err == nil {
+               gopathdir := filepath.Join(wd, "testdata", "issue25459")
+               abstractOriginSanity(t, gopathdir, DefaultOpt)
+       } else {
+               t.Fatalf("os.Getwd() failed %v", err)
+       }
+}
+
+func TestAbstractOriginSanityIssue26237(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+       if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" {
+               t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168")
+       }
+       if wd, err := os.Getwd(); err == nil {
+               gopathdir := filepath.Join(wd, "testdata", "issue26237")
+               abstractOriginSanity(t, gopathdir, DefaultOpt)
+       } else {
+               t.Fatalf("os.Getwd() failed %v", err)
+       }
+}
+
+func TestRuntimeTypeAttrInternal(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       if runtime.GOOS == "windows" && runtime.GOARCH == "arm" {
+               t.Skip("skipping on windows/arm; 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")
+       }
+       testRuntimeTypeAttr(t, "-ldflags=-linkmode=external")
+}
+
+func testRuntimeTypeAttr(t *testing.T, flags string) {
+       t.Parallel()
+
+       const prog = `
+package main
+
+import "unsafe"
+
+type X struct{ _ int }
+
+func main() {
+       var x interface{} = &X{}
+       p := *(*uintptr)(unsafe.Pointer(&x))
+       print(p)
+}
+`
+       dir, err := ioutil.TempDir("", "TestRuntimeType")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       f := gobuild(t, dir, prog, flags)
+       out, err := exec.Command(f.path).CombinedOutput()
+       if err != nil {
+               t.Fatalf("could not run test program: %v", err)
+       }
+       addr, err := strconv.ParseUint(string(out), 10, 64)
+       if err != nil {
+               t.Fatalf("could not parse type address from program output %q: %v", out, err)
+       }
+
+       symbols, err := f.Symbols()
+       if err != nil {
+               t.Fatalf("error reading symbols: %v", err)
+       }
+       var types *objfilepkg.Sym
+       for _, sym := range symbols {
+               if sym.Name == "runtime.types" {
+                       types = &sym
+                       break
+               }
+       }
+       if types == nil {
+               t.Fatal("couldn't find runtime.types in symbols")
+       }
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       rdr := d.Reader()
+       ex := examiner{}
+       if err := ex.populate(rdr); err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+       dies := ex.Named("*main.X")
+       if len(dies) != 1 {
+               t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
+       }
+       rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type)
+       if rtAttr == nil {
+               t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
+       }
+
+       if rtAttr.(uint64)+types.Addr != addr {
+               t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
+       }
+}
+
+func TestIssue27614(t *testing.T) {
+       // Type references in debug_info should always use the DW_TAG_typedef_type
+       // for the type, when that's generated.
+
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       t.Parallel()
+
+       dir, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       const prog = `package main
+
+import "fmt"
+
+type astruct struct {
+       X int
+}
+
+type bstruct struct {
+       X float32
+}
+
+var globalptr *astruct
+var globalvar astruct
+var bvar0, bvar1, bvar2 bstruct
+
+func main() {
+       fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
+}
+`
+
+       f := gobuild(t, dir, prog, NoOpt)
+
+       defer f.Close()
+
+       data, err := f.DWARF()
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       rdr := data.Reader()
+
+       var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
+       var globalptrDIE, globalvarDIE *dwarf.Entry
+       var bvarDIE [3]*dwarf.Entry
+
+       for {
+               e, err := rdr.Next()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if e == nil {
+                       break
+               }
+
+               name, _ := e.Val(dwarf.AttrName).(string)
+
+               switch e.Tag {
+               case dwarf.TagTypedef:
+                       switch name {
+                       case "main.astruct":
+                               astructTypeDIE = e
+                       case "main.bstruct":
+                               bstructTypeDIE = e
+                       }
+               case dwarf.TagPointerType:
+                       if name == "*main.astruct" {
+                               ptrastructTypeDIE = e
+                       }
+               case dwarf.TagVariable:
+                       switch name {
+                       case "main.globalptr":
+                               globalptrDIE = e
+                       case "main.globalvar":
+                               globalvarDIE = e
+                       default:
+                               const bvarprefix = "main.bvar"
+                               if strings.HasPrefix(name, bvarprefix) {
+                                       i, _ := strconv.Atoi(name[len(bvarprefix):])
+                                       bvarDIE[i] = e
+                               }
+                       }
+               }
+       }
+
+       typedieof := func(e *dwarf.Entry) dwarf.Offset {
+               return e.Val(dwarf.AttrType).(dwarf.Offset)
+       }
+
+       if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
+               t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
+       }
+
+       if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
+               t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
+       }
+
+       if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
+               t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
+       }
+
+       for i := range bvarDIE {
+               if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
+                       t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
+               }
+       }
+}
+
+func TestStaticTmp(t *testing.T) {
+       // Checks that statictmp variables do not appear in debug_info or the
+       // symbol table.
+       // Also checks that statictmp variables do not collide with user defined
+       // variables (issue #25113)
+
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       t.Parallel()
+
+       dir, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       const prog = `package main
+
+var stmp_0 string
+var a []int
+
+func init() {
+       a = []int{ 7 }
+}
+
+func main() {
+       println(a[0])
+}
+`
+
+       f := gobuild(t, dir, prog, NoOpt)
+
+       defer f.Close()
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       rdr := d.Reader()
+       for {
+               e, err := rdr.Next()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if e == nil {
+                       break
+               }
+               if e.Tag != dwarf.TagVariable {
+                       continue
+               }
+               name, ok := e.Val(dwarf.AttrName).(string)
+               if !ok {
+                       continue
+               }
+               if strings.Contains(name, "stmp") {
+                       t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset)
+               }
+       }
+
+       syms, err := f.Symbols()
+       if err != nil {
+               t.Fatalf("error reading symbols: %v", err)
+       }
+       for _, sym := range syms {
+               if strings.Contains(sym.Name, "stmp") {
+                       t.Errorf("statictmp variable found in symbol table: %s", sym.Name)
+               }
+       }
+}
+
+func TestPackageNameAttr(t *testing.T) {
+       const dwarfAttrGoPackageName = dwarf.Attr(0x2905)
+       const dwarfGoLanguage = 22
+
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+
+       t.Parallel()
+
+       dir, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n"
+
+       f := gobuild(t, dir, prog, NoOpt)
+
+       defer f.Close()
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       rdr := d.Reader()
+       runtimeUnitSeen := false
+       for {
+               e, err := rdr.Next()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if e == nil {
+                       break
+               }
+               if e.Tag != dwarf.TagCompileUnit {
+                       continue
+               }
+               if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage {
+                       continue
+               }
+
+               pn, ok := e.Val(dwarfAttrGoPackageName).(string)
+               if !ok {
+                       name, _ := e.Val(dwarf.AttrName).(string)
+                       t.Errorf("found compile unit without package name: %s", name)
+
+               }
+               if pn == "" {
+                       name, _ := e.Val(dwarf.AttrName).(string)
+                       t.Errorf("found compile unit with empty package name: %s", name)
+               } else {
+                       if pn == "runtime" {
+                               runtimeUnitSeen = true
+                       }
+               }
+       }
+
+       // Something is wrong if there's no runtime compilation unit.
+       if !runtimeUnitSeen {
+               t.Errorf("no package name for runtime unit")
+       }
+}
+
+func TestMachoIssue32233(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       testenv.MustHaveCGO(t)
+
+       if runtime.GOOS != "darwin" {
+               t.Skip("skipping; test only interesting on darwin")
+       }
+
+       tmpdir, err := ioutil.TempDir("", "TestMachoIssue32233")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       wd, err2 := os.Getwd()
+       if err2 != nil {
+               t.Fatalf("where am I? %v", err)
+       }
+       pdir := filepath.Join(wd, "testdata", "issue32233", "main")
+       f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+       f.Close()
+}
+
+func TestWindowsIssue36495(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       if runtime.GOOS != "windows" {
+               t.Skip("skipping: test only on windows")
+       }
+
+       dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       prog := `
+package main
+
+import "fmt"
+
+func main() {
+  fmt.Println("Hello World")
+}`
+       f := gobuild(t, dir, prog, NoOpt)
+       exe, err := pe.Open(f.path)
+       if err != nil {
+               t.Fatalf("error opening pe file: %v", err)
+       }
+       dw, err := exe.DWARF()
+       if err != nil {
+               t.Fatalf("error parsing DWARF: %v", err)
+       }
+       rdr := dw.Reader()
+       for {
+               e, err := rdr.Next()
+               if err != nil {
+                       t.Fatalf("error reading DWARF: %v", err)
+               }
+               if e == nil {
+                       break
+               }
+               if e.Tag != dwarf.TagCompileUnit {
+                       continue
+               }
+               lnrdr, err := dw.LineReader(e)
+               if err != nil {
+                       t.Fatalf("error creating DWARF line reader: %v", err)
+               }
+               if lnrdr != nil {
+                       var lne dwarf.LineEntry
+                       for {
+                               err := lnrdr.Next(&lne)
+                               if err == io.EOF {
+                                       break
+                               }
+                               if err != nil {
+                                       t.Fatalf("error reading next DWARF line: %v", err)
+                               }
+                               if strings.Contains(lne.File.Name, `\`) {
+                                       t.Errorf("filename should not contain backslash: %v", lne.File.Name)
+                               }
+                       }
+               }
+               rdr.SkipChildren()
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/elf.go b/src/cmd/oldlink/internal/ld/elf.go
new file mode 100644 (file)
index 0000000..6109721
--- /dev/null
@@ -0,0 +1,2415 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "crypto/sha1"
+       "encoding/binary"
+       "encoding/hex"
+       "io"
+       "path/filepath"
+       "sort"
+       "strings"
+)
+
+/*
+ * Derived from:
+ * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $
+ * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $
+ * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $
+ * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $
+ * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $
+ * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $
+ *
+ * Copyright (c) 1996-1998 John D. Polstra.  All rights reserved.
+ * Copyright (c) 2001 David E. O'Brien
+ * Portions Copyright 2009 The Go Authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * ELF definitions that are independent of architecture or word size.
+ */
+
+/*
+ * Note header.  The ".note" section contains an array of notes.  Each
+ * begins with this header, aligned to a word boundary.  Immediately
+ * following the note header is n_namesz bytes of name, padded to the
+ * next word boundary.  Then comes n_descsz bytes of descriptor, again
+ * padded to a word boundary.  The values of n_namesz and n_descsz do
+ * not include the padding.
+ */
+type elfNote struct {
+       nNamesz uint32
+       nDescsz uint32
+       nType   uint32
+}
+
+const (
+       EI_MAG0              = 0
+       EI_MAG1              = 1
+       EI_MAG2              = 2
+       EI_MAG3              = 3
+       EI_CLASS             = 4
+       EI_DATA              = 5
+       EI_VERSION           = 6
+       EI_OSABI             = 7
+       EI_ABIVERSION        = 8
+       OLD_EI_BRAND         = 8
+       EI_PAD               = 9
+       EI_NIDENT            = 16
+       ELFMAG0              = 0x7f
+       ELFMAG1              = 'E'
+       ELFMAG2              = 'L'
+       ELFMAG3              = 'F'
+       SELFMAG              = 4
+       EV_NONE              = 0
+       EV_CURRENT           = 1
+       ELFCLASSNONE         = 0
+       ELFCLASS32           = 1
+       ELFCLASS64           = 2
+       ELFDATANONE          = 0
+       ELFDATA2LSB          = 1
+       ELFDATA2MSB          = 2
+       ELFOSABI_NONE        = 0
+       ELFOSABI_HPUX        = 1
+       ELFOSABI_NETBSD      = 2
+       ELFOSABI_LINUX       = 3
+       ELFOSABI_HURD        = 4
+       ELFOSABI_86OPEN      = 5
+       ELFOSABI_SOLARIS     = 6
+       ELFOSABI_AIX         = 7
+       ELFOSABI_IRIX        = 8
+       ELFOSABI_FREEBSD     = 9
+       ELFOSABI_TRU64       = 10
+       ELFOSABI_MODESTO     = 11
+       ELFOSABI_OPENBSD     = 12
+       ELFOSABI_OPENVMS     = 13
+       ELFOSABI_NSK         = 14
+       ELFOSABI_ARM         = 97
+       ELFOSABI_STANDALONE  = 255
+       ELFOSABI_SYSV        = ELFOSABI_NONE
+       ELFOSABI_MONTEREY    = ELFOSABI_AIX
+       ET_NONE              = 0
+       ET_REL               = 1
+       ET_EXEC              = 2
+       ET_DYN               = 3
+       ET_CORE              = 4
+       ET_LOOS              = 0xfe00
+       ET_HIOS              = 0xfeff
+       ET_LOPROC            = 0xff00
+       ET_HIPROC            = 0xffff
+       EM_NONE              = 0
+       EM_M32               = 1
+       EM_SPARC             = 2
+       EM_386               = 3
+       EM_68K               = 4
+       EM_88K               = 5
+       EM_860               = 7
+       EM_MIPS              = 8
+       EM_S370              = 9
+       EM_MIPS_RS3_LE       = 10
+       EM_PARISC            = 15
+       EM_VPP500            = 17
+       EM_SPARC32PLUS       = 18
+       EM_960               = 19
+       EM_PPC               = 20
+       EM_PPC64             = 21
+       EM_S390              = 22
+       EM_V800              = 36
+       EM_FR20              = 37
+       EM_RH32              = 38
+       EM_RCE               = 39
+       EM_ARM               = 40
+       EM_SH                = 42
+       EM_SPARCV9           = 43
+       EM_TRICORE           = 44
+       EM_ARC               = 45
+       EM_H8_300            = 46
+       EM_H8_300H           = 47
+       EM_H8S               = 48
+       EM_H8_500            = 49
+       EM_IA_64             = 50
+       EM_MIPS_X            = 51
+       EM_COLDFIRE          = 52
+       EM_68HC12            = 53
+       EM_MMA               = 54
+       EM_PCP               = 55
+       EM_NCPU              = 56
+       EM_NDR1              = 57
+       EM_STARCORE          = 58
+       EM_ME16              = 59
+       EM_ST100             = 60
+       EM_TINYJ             = 61
+       EM_X86_64            = 62
+       EM_AARCH64           = 183
+       EM_486               = 6
+       EM_MIPS_RS4_BE       = 10
+       EM_ALPHA_STD         = 41
+       EM_ALPHA             = 0x9026
+       EM_RISCV             = 243
+       SHN_UNDEF            = 0
+       SHN_LORESERVE        = 0xff00
+       SHN_LOPROC           = 0xff00
+       SHN_HIPROC           = 0xff1f
+       SHN_LOOS             = 0xff20
+       SHN_HIOS             = 0xff3f
+       SHN_ABS              = 0xfff1
+       SHN_COMMON           = 0xfff2
+       SHN_XINDEX           = 0xffff
+       SHN_HIRESERVE        = 0xffff
+       SHT_NULL             = 0
+       SHT_PROGBITS         = 1
+       SHT_SYMTAB           = 2
+       SHT_STRTAB           = 3
+       SHT_RELA             = 4
+       SHT_HASH             = 5
+       SHT_DYNAMIC          = 6
+       SHT_NOTE             = 7
+       SHT_NOBITS           = 8
+       SHT_REL              = 9
+       SHT_SHLIB            = 10
+       SHT_DYNSYM           = 11
+       SHT_INIT_ARRAY       = 14
+       SHT_FINI_ARRAY       = 15
+       SHT_PREINIT_ARRAY    = 16
+       SHT_GROUP            = 17
+       SHT_SYMTAB_SHNDX     = 18
+       SHT_LOOS             = 0x60000000
+       SHT_HIOS             = 0x6fffffff
+       SHT_GNU_VERDEF       = 0x6ffffffd
+       SHT_GNU_VERNEED      = 0x6ffffffe
+       SHT_GNU_VERSYM       = 0x6fffffff
+       SHT_LOPROC           = 0x70000000
+       SHT_ARM_ATTRIBUTES   = 0x70000003
+       SHT_HIPROC           = 0x7fffffff
+       SHT_LOUSER           = 0x80000000
+       SHT_HIUSER           = 0xffffffff
+       SHF_WRITE            = 0x1
+       SHF_ALLOC            = 0x2
+       SHF_EXECINSTR        = 0x4
+       SHF_MERGE            = 0x10
+       SHF_STRINGS          = 0x20
+       SHF_INFO_LINK        = 0x40
+       SHF_LINK_ORDER       = 0x80
+       SHF_OS_NONCONFORMING = 0x100
+       SHF_GROUP            = 0x200
+       SHF_TLS              = 0x400
+       SHF_MASKOS           = 0x0ff00000
+       SHF_MASKPROC         = 0xf0000000
+       PT_NULL              = 0
+       PT_LOAD              = 1
+       PT_DYNAMIC           = 2
+       PT_INTERP            = 3
+       PT_NOTE              = 4
+       PT_SHLIB             = 5
+       PT_PHDR              = 6
+       PT_TLS               = 7
+       PT_LOOS              = 0x60000000
+       PT_HIOS              = 0x6fffffff
+       PT_LOPROC            = 0x70000000
+       PT_HIPROC            = 0x7fffffff
+       PT_GNU_STACK         = 0x6474e551
+       PT_GNU_RELRO         = 0x6474e552
+       PT_PAX_FLAGS         = 0x65041580
+       PT_SUNWSTACK         = 0x6ffffffb
+       PF_X                 = 0x1
+       PF_W                 = 0x2
+       PF_R                 = 0x4
+       PF_MASKOS            = 0x0ff00000
+       PF_MASKPROC          = 0xf0000000
+       DT_NULL              = 0
+       DT_NEEDED            = 1
+       DT_PLTRELSZ          = 2
+       DT_PLTGOT            = 3
+       DT_HASH              = 4
+       DT_STRTAB            = 5
+       DT_SYMTAB            = 6
+       DT_RELA              = 7
+       DT_RELASZ            = 8
+       DT_RELAENT           = 9
+       DT_STRSZ             = 10
+       DT_SYMENT            = 11
+       DT_INIT              = 12
+       DT_FINI              = 13
+       DT_SONAME            = 14
+       DT_RPATH             = 15
+       DT_SYMBOLIC          = 16
+       DT_REL               = 17
+       DT_RELSZ             = 18
+       DT_RELENT            = 19
+       DT_PLTREL            = 20
+       DT_DEBUG             = 21
+       DT_TEXTREL           = 22
+       DT_JMPREL            = 23
+       DT_BIND_NOW          = 24
+       DT_INIT_ARRAY        = 25
+       DT_FINI_ARRAY        = 26
+       DT_INIT_ARRAYSZ      = 27
+       DT_FINI_ARRAYSZ      = 28
+       DT_RUNPATH           = 29
+       DT_FLAGS             = 30
+       DT_ENCODING          = 32
+       DT_PREINIT_ARRAY     = 32
+       DT_PREINIT_ARRAYSZ   = 33
+       DT_LOOS              = 0x6000000d
+       DT_HIOS              = 0x6ffff000
+       DT_LOPROC            = 0x70000000
+       DT_HIPROC            = 0x7fffffff
+       DT_VERNEED           = 0x6ffffffe
+       DT_VERNEEDNUM        = 0x6fffffff
+       DT_VERSYM            = 0x6ffffff0
+       DT_PPC64_GLINK       = DT_LOPROC + 0
+       DT_PPC64_OPT         = DT_LOPROC + 3
+       DF_ORIGIN            = 0x0001
+       DF_SYMBOLIC          = 0x0002
+       DF_TEXTREL           = 0x0004
+       DF_BIND_NOW          = 0x0008
+       DF_STATIC_TLS        = 0x0010
+       NT_PRSTATUS          = 1
+       NT_FPREGSET          = 2
+       NT_PRPSINFO          = 3
+       STB_LOCAL            = 0
+       STB_GLOBAL           = 1
+       STB_WEAK             = 2
+       STB_LOOS             = 10
+       STB_HIOS             = 12
+       STB_LOPROC           = 13
+       STB_HIPROC           = 15
+       STT_NOTYPE           = 0
+       STT_OBJECT           = 1
+       STT_FUNC             = 2
+       STT_SECTION          = 3
+       STT_FILE             = 4
+       STT_COMMON           = 5
+       STT_TLS              = 6
+       STT_LOOS             = 10
+       STT_HIOS             = 12
+       STT_LOPROC           = 13
+       STT_HIPROC           = 15
+       STV_DEFAULT          = 0x0
+       STV_INTERNAL         = 0x1
+       STV_HIDDEN           = 0x2
+       STV_PROTECTED        = 0x3
+       STN_UNDEF            = 0
+)
+
+/* 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 struct {
+       ident     [EI_NIDENT]uint8
+       type_     uint16
+       machine   uint16
+       version   uint32
+       entry     uint64
+       phoff     uint64
+       shoff     uint64
+       flags     uint32
+       ehsize    uint16
+       phentsize uint16
+       phnum     uint16
+       shentsize uint16
+       shnum     uint16
+       shstrndx  uint16
+}
+
+/*
+ * Section header.
+ */
+type ElfShdr struct {
+       name      uint32
+       type_     uint32
+       flags     uint64
+       addr      uint64
+       off       uint64
+       size      uint64
+       link      uint32
+       info      uint32
+       addralign uint64
+       entsize   uint64
+       shnum     int
+}
+
+/*
+ * Program header.
+ */
+type ElfPhdr struct {
+       type_  uint32
+       flags  uint32
+       off    uint64
+       vaddr  uint64
+       paddr  uint64
+       filesz uint64
+       memsz  uint64
+       align  uint64
+}
+
+/* For accessing the fields of r_info. */
+
+/* For constructing r_info from field values. */
+
+/*
+ * Symbol table entries.
+ */
+
+/* For accessing the fields of st_info. */
+
+/* For constructing st_info from field values. */
+
+/* For accessing the fields of st_other. */
+
+/*
+ * Go linker interface
+ */
+const (
+       ELF64HDRSIZE  = 64
+       ELF64PHDRSIZE = 56
+       ELF64SHDRSIZE = 64
+       ELF64RELSIZE  = 16
+       ELF64RELASIZE = 24
+       ELF64SYMSIZE  = 24
+       ELF32HDRSIZE  = 52
+       ELF32PHDRSIZE = 32
+       ELF32SHDRSIZE = 40
+       ELF32SYMSIZE  = 16
+       ELF32RELSIZE  = 8
+)
+
+/*
+ * The interface uses the 64-bit structures always,
+ * to avoid code duplication.  The writers know how to
+ * marshal a 32-bit representation from the 64-bit structure.
+ */
+
+var Elfstrdat []byte
+
+/*
+ * Total amount of space to reserve at the start of the file
+ * for Header, PHeaders, SHeaders, and interp.
+ * May waste some.
+ * On FreeBSD, cannot be larger than a page.
+ */
+const (
+       ELFRESERVE = 4096
+)
+
+/*
+ * We use the 64-bit data structures on both 32- and 64-bit machines
+ * in order to write the code just once.  The 64-bit data structure is
+ * written in the 32-bit format on the 32-bit machines.
+ */
+const (
+       NSECT = 400
+)
+
+var (
+       Nelfsym = 1
+
+       elf64 bool
+       // Either ".rel" or ".rela" depending on which type of relocation the
+       // target platform uses.
+       elfRelType string
+
+       ehdr ElfEhdr
+       phdr [NSECT]*ElfPhdr
+       shdr [NSECT]*ElfShdr
+
+       interp string
+)
+
+type Elfstring struct {
+       s   string
+       off int
+}
+
+var elfstr [100]Elfstring
+
+var nelfstr int
+
+var buildinfo []byte
+
+/*
+ Initialize the global variable that describes the ELF header. It will be updated as
+ we write section and prog headers.
+*/
+func Elfinit(ctxt *Link) {
+       ctxt.IsELF = true
+
+       if ctxt.Arch.InFamily(sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) {
+               elfRelType = ".rela"
+       } else {
+               elfRelType = ".rel"
+       }
+
+       switch ctxt.Arch.Family {
+       // 64-bit architectures
+       case sys.PPC64, sys.S390X:
+               if ctxt.Arch.ByteOrder == binary.BigEndian {
+                       ehdr.flags = 1 /* Version 1 ABI */
+               } else {
+                       ehdr.flags = 2 /* Version 2 ABI */
+               }
+               fallthrough
+       case sys.AMD64, sys.ARM64, sys.MIPS64, sys.RISCV64:
+               if ctxt.Arch.Family == sys.MIPS64 {
+                       ehdr.flags = 0x20000004 /* MIPS 3 CPIC */
+               }
+               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_ == PT_LOAD {
+               fixElfPhdr(e)
+       }
+
+       out.Write32(e.type_)
+       out.Write32(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_ == PT_LOAD {
+               fixElfPhdr(e)
+       }
+
+       out.Write32(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(e.flags)
+       out.Write32(uint32(e.align))
+}
+
+func elf64shdr(out *OutBuf, e *ElfShdr) {
+       out.Write32(e.name)
+       out.Write32(e.type_)
+       out.Write64(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(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(s *sym.Symbol, str string, off int) {
+       if nelfstr >= len(elfstr) {
+               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 = int(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(ehdr.type_)
+       out.Write16(ehdr.machine)
+       out.Write32(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(ehdr.type_)
+       out.Write16(ehdr.machine)
+       out.Write32(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 Elfwritedynent(ctxt *Link, s *sym.Symbol, tag int, val uint64) {
+       if elf64 {
+               s.AddUint64(ctxt.Arch, uint64(tag))
+               s.AddUint64(ctxt.Arch, val)
+       } else {
+               s.AddUint32(ctxt.Arch, uint32(tag))
+               s.AddUint32(ctxt.Arch, uint32(val))
+       }
+}
+
+func elfwritedynentsym(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) {
+       Elfwritedynentsymplus(ctxt, s, tag, t, 0)
+}
+
+func Elfwritedynentsymplus(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol, 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 *sym.Symbol, tag int, t *sym.Symbol) {
+       if elf64 {
+               s.AddUint64(ctxt.Arch, uint64(tag))
+       } else {
+               s.AddUint32(ctxt.Arch, uint32(tag))
+       }
+       s.AddSize(ctxt.Arch, t)
+}
+
+func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int {
+       interp = p
+       n := len(interp) + 1
+       sh.addr = startva + resoff - uint64(n)
+       sh.off = resoff - uint64(n)
+       sh.size = uint64(n)
+
+       return n
+}
+
+func elfwriteinterp(out *OutBuf) int {
+       sh := elfshname(".interp")
+       out.SeekSet(int64(sh.off))
+       out.WriteString(interp)
+       out.Write8(0)
+       return int(sh.size)
+}
+
+func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int) int {
+       n := 3*4 + uint64(sz) + resoff%4
+
+       sh.type_ = SHT_NOTE
+       sh.flags = 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)
+}
+
+// OpenBSD Signature
+const (
+       ELF_NOTE_OPENBSD_NAMESZ  = 8
+       ELF_NOTE_OPENBSD_DESCSZ  = 4
+       ELF_NOTE_OPENBSD_TAG     = 1
+       ELF_NOTE_OPENBSD_VERSION = 0
+)
+
+var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00")
+
+func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
+       n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ
+       return elfnote(sh, startva, resoff, n)
+}
+
+func elfwriteopenbsdsig(out *OutBuf) int {
+       // Write Elf_Note header.
+       sh := elfwritenotehdr(out, ".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG)
+
+       if sh == nil {
+               return 0
+       }
+
+       // Followed by OpenBSD string and version.
+       out.Write(ELF_NOTE_OPENBSD_NAME)
+
+       out.Write32(ELF_NOTE_OPENBSD_VERSION)
+
+       return int(sh.size)
+}
+
+func addbuildinfo(val string) {
+       if !strings.HasPrefix(val, "0x") {
+               Exitf("-B argument must start with 0x: %s", val)
+       }
+
+       ov := val
+       val = val[2:]
+
+       const maxLen = 32
+       if hex.DecodedLen(len(val)) > maxLen {
+               Exitf("-B option too long (max %d digits): %s", maxLen, ov)
+       }
+
+       b, err := hex.DecodeString(val)
+       if err != nil {
+               if err == hex.ErrLength {
+                       Exitf("-B argument must have even number of digits: %s", ov)
+               }
+               if inv, ok := err.(hex.InvalidByteError); ok {
+                       Exitf("-B argument contains invalid hex digit %c: %s", byte(inv), ov)
+               }
+               Exitf("-B argument contains invalid hex: %s", ov)
+       }
+
+       buildinfo = b
+}
+
+// Build info note
+const (
+       ELF_NOTE_BUILDINFO_NAMESZ = 4
+       ELF_NOTE_BUILDINFO_TAG    = 3
+)
+
+var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00")
+
+func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int {
+       n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4))
+       return elfnote(sh, startva, resoff, n)
+}
+
+func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int {
+       n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(*flagBuildid)), 4))
+       return elfnote(sh, startva, resoff, n)
+}
+
+func elfwritebuildinfo(out *OutBuf) int {
+       sh := elfwritenotehdr(out, ".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG)
+       if sh == nil {
+               return 0
+       }
+
+       out.Write(ELF_NOTE_BUILDINFO_NAME)
+       out.Write(buildinfo)
+       var zero = make([]byte, 4)
+       out.Write(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))])
+
+       return int(sh.size)
+}
+
+func elfwritegobuildid(out *OutBuf) int {
+       sh := elfwritenotehdr(out, ".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(*flagBuildid)), ELF_NOTE_GOBUILDID_TAG)
+       if sh == nil {
+               return 0
+       }
+
+       out.Write(ELF_NOTE_GO_NAME)
+       out.Write([]byte(*flagBuildid))
+       var zero = make([]byte, 4)
+       out.Write(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))])
+
+       return int(sh.size)
+}
+
+// Go specific notes
+const (
+       ELF_NOTE_GOPKGLIST_TAG = 1
+       ELF_NOTE_GOABIHASH_TAG = 2
+       ELF_NOTE_GODEPS_TAG    = 3
+       ELF_NOTE_GOBUILDID_TAG = 4
+)
+
+var ELF_NOTE_GO_NAME = []byte("Go\x00\x00")
+
+var elfverneed int
+
+type Elfaux struct {
+       next *Elfaux
+       num  int
+       vers string
+}
+
+type Elflib struct {
+       next *Elflib
+       aux  *Elfaux
+       file string
+}
+
+func addelflib(list **Elflib, file string, vers string) *Elfaux {
+       var lib *Elflib
+
+       for lib = *list; lib != nil; lib = lib.next {
+               if lib.file == file {
+                       goto havelib
+               }
+       }
+       lib = new(Elflib)
+       lib.next = *list
+       lib.file = file
+       *list = lib
+
+havelib:
+       for aux := lib.aux; aux != nil; aux = aux.next {
+               if aux.vers == vers {
+                       return aux
+               }
+       }
+       aux := new(Elfaux)
+       aux.next = lib.aux
+       aux.vers = vers
+       lib.aux = aux
+
+       return aux
+}
+
+func elfdynhash(ctxt *Link) {
+       if !ctxt.IsELF {
+               return
+       }
+
+       nsym := Nelfsym
+       s := ctxt.Syms.Lookup(".hash", 0)
+       s.Type = sym.SELFROSECT
+       s.Attr |= sym.AttrReachable
+
+       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 ctxt.Syms.Allsym {
+               if sy.Dynid <= 0 {
+                       continue
+               }
+
+               if sy.Dynimpvers() != "" {
+                       need[sy.Dynid] = addelflib(&needlib, sy.Dynimplib(), sy.Dynimpvers())
+               }
+
+               name := sy.Extname()
+               hc := elfhash(name)
+
+               b := hc % uint32(nbucket)
+               chain[sy.Dynid] = buckets[b]
+               buckets[b] = uint32(sy.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])
+               }
+       }
+
+       // version symbols
+       dynstr := ctxt.Syms.Lookup(".dynstr", 0)
+
+       s = ctxt.Syms.Lookup(".gnu.version_r", 0)
+       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(Addstring(dynstr, 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(Addstring(dynstr, 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
+       s = ctxt.Syms.Lookup(".gnu.version", 0)
+
+       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 = ctxt.Syms.Lookup(".dynamic", 0)
+       elfverneed = nfile
+       if elfverneed != 0 {
+               elfwritedynentsym(ctxt, s, DT_VERNEED, ctxt.Syms.Lookup(".gnu.version_r", 0))
+               Elfwritedynent(ctxt, s, DT_VERNEEDNUM, uint64(nfile))
+               elfwritedynentsym(ctxt, s, DT_VERSYM, ctxt.Syms.Lookup(".gnu.version", 0))
+       }
+
+       sy := ctxt.Syms.Lookup(elfRelType+".plt", 0)
+       if sy.Size > 0 {
+               if elfRelType == ".rela" {
+                       Elfwritedynent(ctxt, s, DT_PLTREL, DT_RELA)
+               } else {
+                       Elfwritedynent(ctxt, s, DT_PLTREL, DT_REL)
+               }
+               elfwritedynentsymsize(ctxt, s, DT_PLTRELSZ, sy)
+               elfwritedynentsym(ctxt, s, DT_JMPREL, sy)
+       }
+
+       Elfwritedynent(ctxt, s, DT_NULL, 0)
+}
+
+func elfphload(seg *sym.Segment) *ElfPhdr {
+       ph := newElfPhdr()
+       ph.type_ = PT_LOAD
+       if seg.Rwx&4 != 0 {
+               ph.flags |= PF_R
+       }
+       if seg.Rwx&2 != 0 {
+               ph.flags |= PF_W
+       }
+       if seg.Rwx&1 != 0 {
+               ph.flags |= 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_ = 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_ == SHT_NOTE {
+               if linkmode != LinkExternal {
+                       // TODO(mwhudson): the approach here will work OK when
+                       // linking internally for notes that we want to be included
+                       // in a loadable segment (e.g. the abihash note) but not for
+                       // notes that we do not want to be mapped (e.g. the package
+                       // list note). The real fix is probably to define new values
+                       // for Symbol.Type corresponding to mapped and unmapped notes
+                       // and handle them in dodata().
+                       Errorf(nil, "sh.type_ == SHT_NOTE in elfshbits when linking internally")
+               }
+               sh.addralign = uint64(sect.Align)
+               sh.size = sect.Length
+               sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+               return sh
+       }
+       if sh.type_ > 0 {
+               return sh
+       }
+
+       if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
+               sh.type_ = SHT_PROGBITS
+       } else {
+               sh.type_ = SHT_NOBITS
+       }
+       sh.flags = SHF_ALLOC
+       if sect.Rwx&1 != 0 {
+               sh.flags |= SHF_EXECINSTR
+       }
+       if sect.Rwx&2 != 0 {
+               sh.flags |= SHF_WRITE
+       }
+       if sect.Name == ".tbss" {
+               sh.flags |= SHF_TLS
+               sh.type_ = SHT_NOBITS
+       }
+       if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") {
+               sh.flags = 0
+       }
+
+       if linkmode != LinkExternal {
+               sh.addr = sect.Vaddr
+       }
+       sh.addralign = uint64(sect.Align)
+       sh.size = sect.Length
+       if sect.Name != ".tbss" {
+               sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+       }
+
+       return sh
+}
+
+func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr {
+       // If main section is SHT_NOBITS, nothing to relocate.
+       // Also nothing to relocate in .shstrtab or notes.
+       if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+               return nil
+       }
+       if sect.Name == ".shstrtab" || sect.Name == ".tbss" {
+               return nil
+       }
+       if sect.Elfsect.(*ElfShdr).type_ == SHT_NOTE {
+               return nil
+       }
+
+       typ := SHT_REL
+       if elfRelType == ".rela" {
+               typ = 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 == 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, sect *sym.Section, syms []*sym.Symbol) {
+       // 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
+       }
+
+       sect.Reloff = uint64(ctxt.Out.Offset())
+       for i, s := range syms {
+               if !s.Attr.Reachable() {
+                       continue
+               }
+               if uint64(s.Value) >= sect.Vaddr {
+                       syms = syms[i:]
+                       break
+               }
+       }
+
+       eaddr := int32(sect.Vaddr + sect.Length)
+       for _, s := range syms {
+               if !s.Attr.Reachable() {
+                       continue
+               }
+               if s.Value >= int64(eaddr) {
+                       break
+               }
+               for ri := range s.R {
+                       r := &s.R[ri]
+                       if r.Done {
+                               continue
+                       }
+                       if r.Xsym == nil {
+                               Errorf(s, "missing xsym in relocation %#v %#v", r.Sym.Name, s)
+                               continue
+                       }
+                       if r.Xsym.ElfsymForReloc() == 0 {
+                               Errorf(s, "reloc %d (%s) to non-elf symbol %s (outer=%s) %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Sym.Name, r.Xsym.Name, r.Sym.Type, r.Sym.Type)
+                       }
+                       if !r.Xsym.Attr.Reachable() {
+                               Errorf(s, "unreachable reloc %d (%s) target %v", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Xsym.Name)
+                       }
+                       if !thearch.Elfreloc1(ctxt, r, int64(uint64(s.Value+int64(r.Off))-sect.Vaddr)) {
+                               Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Siz, r.Sym.Name)
+                       }
+               }
+       }
+
+       sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
+}
+
+func Elfemitreloc(ctxt *Link) {
+       for ctxt.Out.Offset()&7 != 0 {
+               ctxt.Out.Write8(0)
+       }
+
+       for _, sect := range Segtext.Sections {
+               if sect.Name == ".text" {
+                       elfrelocsect(ctxt, sect, ctxt.Textp)
+               } else {
+                       elfrelocsect(ctxt, sect, datap)
+               }
+       }
+
+       for _, sect := range Segrodata.Sections {
+               elfrelocsect(ctxt, sect, datap)
+       }
+       for _, sect := range Segrelrodata.Sections {
+               elfrelocsect(ctxt, sect, datap)
+       }
+       for _, sect := range Segdata.Sections {
+               elfrelocsect(ctxt, sect, datap)
+       }
+       for _, sect := range Segdwarf.Sections {
+               elfrelocsect(ctxt, sect, dwarfp)
+       }
+}
+
+func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
+       s := ctxt.Syms.Lookup(sectionName, 0)
+       s.Attr |= sym.AttrReachable
+       s.Type = 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.P = append(s.P, ELF_NOTE_GO_NAME...)
+       for len(s.P)%4 != 0 {
+               s.P = append(s.P, 0)
+       }
+       // desc + padding
+       s.P = append(s.P, desc...)
+       for len(s.P)%4 != 0 {
+               s.P = append(s.P, 0)
+       }
+       s.Size = int64(len(s.P))
+       s.Align = 4
+}
+
+func (ctxt *Link) doelf() {
+       if !ctxt.IsELF {
+               return
+       }
+
+       /* predefine strings we need for section headers */
+       shstrtab := ctxt.Syms.Lookup(".shstrtab", 0)
+
+       shstrtab.Type = sym.SELFROSECT
+       shstrtab.Attr |= sym.AttrReachable
+
+       Addstring(shstrtab, "")
+       Addstring(shstrtab, ".text")
+       Addstring(shstrtab, ".noptrdata")
+       Addstring(shstrtab, ".data")
+       Addstring(shstrtab, ".bss")
+       Addstring(shstrtab, ".noptrbss")
+       Addstring(shstrtab, "__libfuzzer_extra_counters")
+       Addstring(shstrtab, ".go.buildinfo")
+
+       // generate .tbss section for dynamic internal linker or external
+       // linking, so that various binutils could correctly calculate
+       // PT_TLS size. See https://golang.org/issue/5200.
+       if !*FlagD || ctxt.LinkMode == LinkExternal {
+               Addstring(shstrtab, ".tbss")
+       }
+       if ctxt.HeadType == objabi.Hnetbsd {
+               Addstring(shstrtab, ".note.netbsd.ident")
+       }
+       if ctxt.HeadType == objabi.Hopenbsd {
+               Addstring(shstrtab, ".note.openbsd.ident")
+       }
+       if len(buildinfo) > 0 {
+               Addstring(shstrtab, ".note.gnu.build-id")
+       }
+       if *flagBuildid != "" {
+               Addstring(shstrtab, ".note.go.buildid")
+       }
+       Addstring(shstrtab, ".elfdata")
+       Addstring(shstrtab, ".rodata")
+       // See the comment about data.rel.ro.FOO section names in data.go.
+       relro_prefix := ""
+       if ctxt.UseRelro() {
+               Addstring(shstrtab, ".data.rel.ro")
+               relro_prefix = ".data.rel.ro"
+       }
+       Addstring(shstrtab, relro_prefix+".typelink")
+       Addstring(shstrtab, relro_prefix+".itablink")
+       Addstring(shstrtab, relro_prefix+".gosymtab")
+       Addstring(shstrtab, relro_prefix+".gopclntab")
+
+       if ctxt.LinkMode == LinkExternal {
+               *FlagD = true
+
+               Addstring(shstrtab, elfRelType+".text")
+               Addstring(shstrtab, elfRelType+".rodata")
+               Addstring(shstrtab, elfRelType+relro_prefix+".typelink")
+               Addstring(shstrtab, elfRelType+relro_prefix+".itablink")
+               Addstring(shstrtab, elfRelType+relro_prefix+".gosymtab")
+               Addstring(shstrtab, elfRelType+relro_prefix+".gopclntab")
+               Addstring(shstrtab, elfRelType+".noptrdata")
+               Addstring(shstrtab, elfRelType+".data")
+               if ctxt.UseRelro() {
+                       Addstring(shstrtab, elfRelType+".data.rel.ro")
+               }
+               Addstring(shstrtab, elfRelType+".go.buildinfo")
+
+               // add a .note.GNU-stack section to mark the stack as non-executable
+               Addstring(shstrtab, ".note.GNU-stack")
+
+               if ctxt.BuildMode == BuildModeShared {
+                       Addstring(shstrtab, ".note.go.abihash")
+                       Addstring(shstrtab, ".note.go.pkg-list")
+                       Addstring(shstrtab, ".note.go.deps")
+               }
+       }
+
+       hasinitarr := ctxt.linkShared
+
+       /* shared library initializer */
+       switch ctxt.BuildMode {
+       case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin:
+               hasinitarr = true
+       }
+
+       if hasinitarr {
+               Addstring(shstrtab, ".init_array")
+               Addstring(shstrtab, elfRelType+".init_array")
+       }
+
+       if !*FlagS {
+               Addstring(shstrtab, ".symtab")
+               Addstring(shstrtab, ".strtab")
+               dwarfaddshstrings(ctxt, shstrtab)
+       }
+
+       Addstring(shstrtab, ".shstrtab")
+
+       if !*FlagD { /* -d suppresses dynamic loader format */
+               Addstring(shstrtab, ".interp")
+               Addstring(shstrtab, ".hash")
+               Addstring(shstrtab, ".got")
+               if ctxt.Arch.Family == sys.PPC64 {
+                       Addstring(shstrtab, ".glink")
+               }
+               Addstring(shstrtab, ".got.plt")
+               Addstring(shstrtab, ".dynamic")
+               Addstring(shstrtab, ".dynsym")
+               Addstring(shstrtab, ".dynstr")
+               Addstring(shstrtab, elfRelType)
+               Addstring(shstrtab, elfRelType+".plt")
+
+               Addstring(shstrtab, ".plt")
+               Addstring(shstrtab, ".gnu.version")
+               Addstring(shstrtab, ".gnu.version_r")
+
+               /* dynamic symbol table - first entry all zeros */
+               s := ctxt.Syms.Lookup(".dynsym", 0)
+
+               s.Type = sym.SELFROSECT
+               s.Attr |= sym.AttrReachable
+               if elf64 {
+                       s.Size += ELF64SYMSIZE
+               } else {
+                       s.Size += ELF32SYMSIZE
+               }
+
+               /* dynamic string table */
+               s = ctxt.Syms.Lookup(".dynstr", 0)
+
+               s.Type = sym.SELFROSECT
+               s.Attr |= sym.AttrReachable
+               if s.Size == 0 {
+                       Addstring(s, "")
+               }
+               dynstr := s
+
+               /* relocation table */
+               s = ctxt.Syms.Lookup(elfRelType, 0)
+               s.Attr |= sym.AttrReachable
+               s.Type = sym.SELFROSECT
+
+               /* global offset table */
+               s = ctxt.Syms.Lookup(".got", 0)
+
+               s.Attr |= sym.AttrReachable
+               s.Type = sym.SELFGOT // writable
+
+               /* ppc64 glink resolver */
+               if ctxt.Arch.Family == sys.PPC64 {
+                       s := ctxt.Syms.Lookup(".glink", 0)
+                       s.Attr |= sym.AttrReachable
+                       s.Type = sym.SELFRXSECT
+               }
+
+               /* hash */
+               s = ctxt.Syms.Lookup(".hash", 0)
+
+               s.Attr |= sym.AttrReachable
+               s.Type = sym.SELFROSECT
+
+               s = ctxt.Syms.Lookup(".got.plt", 0)
+               s.Attr |= sym.AttrReachable
+               s.Type = sym.SELFSECT // writable
+
+               s = ctxt.Syms.Lookup(".plt", 0)
+
+               s.Attr |= sym.AttrReachable
+               if ctxt.Arch.Family == sys.PPC64 {
+                       // In the ppc64 ABI, .plt is a data section
+                       // written by the dynamic linker.
+                       s.Type = sym.SELFSECT
+               } else {
+                       s.Type = sym.SELFRXSECT
+               }
+
+               thearch.Elfsetupplt(ctxt)
+
+               s = ctxt.Syms.Lookup(elfRelType+".plt", 0)
+               s.Attr |= sym.AttrReachable
+               s.Type = sym.SELFROSECT
+
+               s = ctxt.Syms.Lookup(".gnu.version", 0)
+               s.Attr |= sym.AttrReachable
+               s.Type = sym.SELFROSECT
+
+               s = ctxt.Syms.Lookup(".gnu.version_r", 0)
+               s.Attr |= sym.AttrReachable
+               s.Type = sym.SELFROSECT
+
+               /* define dynamic elf table */
+               s = ctxt.Syms.Lookup(".dynamic", 0)
+
+               s.Attr |= sym.AttrReachable
+               s.Type = sym.SELFSECT // writable
+
+               /*
+                * .dynamic table
+                */
+               elfwritedynentsym(ctxt, s, DT_HASH, ctxt.Syms.Lookup(".hash", 0))
+
+               elfwritedynentsym(ctxt, s, DT_SYMTAB, ctxt.Syms.Lookup(".dynsym", 0))
+               if elf64 {
+                       Elfwritedynent(ctxt, s, DT_SYMENT, ELF64SYMSIZE)
+               } else {
+                       Elfwritedynent(ctxt, s, DT_SYMENT, ELF32SYMSIZE)
+               }
+               elfwritedynentsym(ctxt, s, DT_STRTAB, ctxt.Syms.Lookup(".dynstr", 0))
+               elfwritedynentsymsize(ctxt, s, DT_STRSZ, ctxt.Syms.Lookup(".dynstr", 0))
+               if elfRelType == ".rela" {
+                       elfwritedynentsym(ctxt, s, DT_RELA, ctxt.Syms.Lookup(".rela", 0))
+                       elfwritedynentsymsize(ctxt, s, DT_RELASZ, ctxt.Syms.Lookup(".rela", 0))
+                       Elfwritedynent(ctxt, s, DT_RELAENT, ELF64RELASIZE)
+               } else {
+                       elfwritedynentsym(ctxt, s, DT_REL, ctxt.Syms.Lookup(".rel", 0))
+                       elfwritedynentsymsize(ctxt, s, DT_RELSZ, ctxt.Syms.Lookup(".rel", 0))
+                       Elfwritedynent(ctxt, s, DT_RELENT, ELF32RELSIZE)
+               }
+
+               if rpath.val != "" {
+                       Elfwritedynent(ctxt, s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val)))
+               }
+
+               if ctxt.Arch.Family == sys.PPC64 {
+                       elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".plt", 0))
+               } else if ctxt.Arch.Family == sys.S390X {
+                       elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got", 0))
+               } else {
+                       elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got.plt", 0))
+               }
+
+               if ctxt.Arch.Family == sys.PPC64 {
+                       Elfwritedynent(ctxt, s, 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 DT_PLTREL,
+               // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the
+               // size of .rel(a).plt section.
+               Elfwritedynent(ctxt, s, DT_DEBUG, 0)
+       }
+
+       if ctxt.BuildMode == BuildModeShared {
+               // The go.link.abihashbytes symbol will be pointed at the appropriate
+               // part of the .note.go.abihash section in data.go:func address().
+               s := ctxt.Syms.Lookup("go.link.abihashbytes", 0)
+               s.Attr |= sym.AttrLocal
+               s.Type = sym.SRODATA
+               s.Attr |= sym.AttrSpecial
+               s.Attr |= sym.AttrReachable
+               s.Size = int64(sha1.Size)
+
+               sort.Sort(byPkg(ctxt.Library))
+               h := sha1.New()
+               for _, l := range ctxt.Library {
+                       io.WriteString(h, l.Hash)
+               }
+               addgonote(ctxt, ".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{}))
+               addgonote(ctxt, ".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote)
+               var deplist []string
+               for _, shlib := range ctxt.Shlibs {
+                       deplist = append(deplist, filepath.Base(shlib.Path))
+               }
+               addgonote(ctxt, ".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n")))
+       }
+
+       if ctxt.LinkMode == LinkExternal && *flagBuildid != "" {
+               addgonote(ctxt, ".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(*flagBuildid))
+       }
+}
+
+// Do not write DT_NULL.  elfdynhash will finish it.
+func shsym(sh *ElfShdr, s *sym.Symbol) {
+       addr := Symaddr(s)
+       if sh.flags&SHF_ALLOC != 0 {
+               sh.addr = uint64(addr)
+       }
+       sh.off = uint64(datoff(s, addr))
+       sh.size = uint64(s.Size)
+}
+
+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, symo int64) {
+       eh := getElfEhdr()
+       switch ctxt.Arch.Family {
+       default:
+               Exitf("unknown architecture in asmbelf: %v", ctxt.Arch.Family)
+       case sys.MIPS, sys.MIPS64:
+               eh.machine = EM_MIPS
+       case sys.ARM:
+               eh.machine = EM_ARM
+       case sys.AMD64:
+               eh.machine = EM_X86_64
+       case sys.ARM64:
+               eh.machine = EM_AARCH64
+       case sys.I386:
+               eh.machine = EM_386
+       case sys.PPC64:
+               eh.machine = EM_PPC64
+       case sys.RISCV64:
+               eh.machine = EM_RISCV
+       case sys.S390X:
+               eh.machine = EM_S390
+       }
+
+       elfreserve := int64(ELFRESERVE)
+
+       numtext := int64(0)
+       for _, sect := range Segtext.Sections {
+               if sect.Name == ".text" {
+                       numtext++
+               }
+       }
+
+       // If there are multiple text sections, extra space is needed
+       // in the elfreserve for the additional .text and .rela.text
+       // section headers.  It can handle 4 extra now. Headers are
+       // 64 bytes.
+
+       if numtext > 4 {
+               elfreserve += elfreserve + numtext*64*2
+       }
+
+       startva := *FlagTextAddr - int64(HEADR)
+       resoff := elfreserve
+
+       var pph *ElfPhdr
+       var pnote *ElfPhdr
+       if ctxt.LinkMode == LinkExternal {
+               /* skip program headers */
+               eh.phoff = 0
+
+               eh.phentsize = 0
+
+               if ctxt.BuildMode == BuildModeShared {
+                       sh := elfshname(".note.go.pkg-list")
+                       sh.type_ = SHT_NOTE
+                       sh = elfshname(".note.go.abihash")
+                       sh.type_ = SHT_NOTE
+                       sh.flags = SHF_ALLOC
+                       sh = elfshname(".note.go.deps")
+                       sh.type_ = SHT_NOTE
+               }
+
+               if *flagBuildid != "" {
+                       sh := elfshname(".note.go.buildid")
+                       sh.type_ = SHT_NOTE
+                       sh.flags = SHF_ALLOC
+               }
+
+               goto elfobj
+       }
+
+       /* program header info */
+       pph = newElfPhdr()
+
+       pph.type_ = PT_PHDR
+       pph.flags = 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_ = SHT_PROGBITS
+               sh.flags = SHF_ALLOC
+               sh.addralign = 1
+
+               if interpreter == "" && objabi.GO_LDSO != "" {
+                       interpreter = objabi.GO_LDSO
+               }
+
+               if interpreter == "" {
+                       switch ctxt.HeadType {
+                       case objabi.Hlinux:
+                               if objabi.GOOS == "android" {
+                                       interpreter = thearch.Androiddynld
+                                       if interpreter == "" {
+                                               Exitf("ELF interpreter not set")
+                                       }
+                               } else {
+                                       interpreter = thearch.Linuxdynld
+                               }
+
+                       case objabi.Hfreebsd:
+                               interpreter = thearch.Freebsddynld
+
+                       case objabi.Hnetbsd:
+                               interpreter = thearch.Netbsddynld
+
+                       case objabi.Hopenbsd:
+                               interpreter = thearch.Openbsddynld
+
+                       case objabi.Hdragonfly:
+                               interpreter = thearch.Dragonflydynld
+
+                       case objabi.Hsolaris:
+                               interpreter = thearch.Solarisdynld
+                       }
+               }
+
+               resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter))
+
+               ph := newElfPhdr()
+               ph.type_ = PT_INTERP
+               ph.flags = PF_R
+               phsh(ph, sh)
+       }
+
+       pnote = nil
+       if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd {
+               var sh *ElfShdr
+               switch ctxt.HeadType {
+               case objabi.Hnetbsd:
+                       sh = elfshname(".note.netbsd.ident")
+                       resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff)))
+
+               case objabi.Hopenbsd:
+                       sh = elfshname(".note.openbsd.ident")
+                       resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff)))
+               }
+
+               pnote = newElfPhdr()
+               pnote.type_ = PT_NOTE
+               pnote.flags = PF_R
+               phsh(pnote, sh)
+       }
+
+       if len(buildinfo) > 0 {
+               sh := elfshname(".note.gnu.build-id")
+               resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff)))
+
+               if pnote == nil {
+                       pnote = newElfPhdr()
+                       pnote.type_ = PT_NOTE
+                       pnote.flags = PF_R
+               }
+
+               phsh(pnote, sh)
+       }
+
+       if *flagBuildid != "" {
+               sh := elfshname(".note.go.buildid")
+               resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff)))
+
+               pnote := newElfPhdr()
+               pnote.type_ = PT_NOTE
+               pnote.flags = PF_R
+               phsh(pnote, sh)
+       }
+
+       // Additions to the reserved area must be above this line.
+
+       elfphload(&Segtext)
+       if len(Segrodata.Sections) > 0 {
+               elfphload(&Segrodata)
+       }
+       if len(Segrelrodata.Sections) > 0 {
+               elfphload(&Segrelrodata)
+               elfphrelro(&Segrelrodata)
+       }
+       elfphload(&Segdata)
+
+       /* Dynamic linking sections */
+       if !*FlagD {
+               sh := elfshname(".dynsym")
+               sh.type_ = SHT_DYNSYM
+               sh.flags = 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 := ctxt.Syms.Lookup(".dynsym", 0)
+               i := uint32(0)
+               for sub := s; sub != nil; sub = sub.Sub {
+                       i++
+                       if !sub.Attr.Local() {
+                               break
+                       }
+               }
+               sh.info = i
+               shsym(sh, s)
+
+               sh = elfshname(".dynstr")
+               sh.type_ = SHT_STRTAB
+               sh.flags = SHF_ALLOC
+               sh.addralign = 1
+               shsym(sh, ctxt.Syms.Lookup(".dynstr", 0))
+
+               if elfverneed != 0 {
+                       sh := elfshname(".gnu.version")
+                       sh.type_ = SHT_GNU_VERSYM
+                       sh.flags = SHF_ALLOC
+                       sh.addralign = 2
+                       sh.link = uint32(elfshname(".dynsym").shnum)
+                       sh.entsize = 2
+                       shsym(sh, ctxt.Syms.Lookup(".gnu.version", 0))
+
+                       sh = elfshname(".gnu.version_r")
+                       sh.type_ = SHT_GNU_VERNEED
+                       sh.flags = SHF_ALLOC
+                       sh.addralign = uint64(ctxt.Arch.RegSize)
+                       sh.info = uint32(elfverneed)
+                       sh.link = uint32(elfshname(".dynstr").shnum)
+                       shsym(sh, ctxt.Syms.Lookup(".gnu.version_r", 0))
+               }
+
+               if elfRelType == ".rela" {
+                       sh := elfshname(".rela.plt")
+                       sh.type_ = SHT_RELA
+                       sh.flags = 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, ctxt.Syms.Lookup(".rela.plt", 0))
+
+                       sh = elfshname(".rela")
+                       sh.type_ = SHT_RELA
+                       sh.flags = SHF_ALLOC
+                       sh.entsize = ELF64RELASIZE
+                       sh.addralign = 8
+                       sh.link = uint32(elfshname(".dynsym").shnum)
+                       shsym(sh, ctxt.Syms.Lookup(".rela", 0))
+               } else {
+                       sh := elfshname(".rel.plt")
+                       sh.type_ = SHT_REL
+                       sh.flags = SHF_ALLOC
+                       sh.entsize = ELF32RELSIZE
+                       sh.addralign = 4
+                       sh.link = uint32(elfshname(".dynsym").shnum)
+                       shsym(sh, ctxt.Syms.Lookup(".rel.plt", 0))
+
+                       sh = elfshname(".rel")
+                       sh.type_ = SHT_REL
+                       sh.flags = SHF_ALLOC
+                       sh.entsize = ELF32RELSIZE
+                       sh.addralign = 4
+                       sh.link = uint32(elfshname(".dynsym").shnum)
+                       shsym(sh, ctxt.Syms.Lookup(".rel", 0))
+               }
+
+               if eh.machine == EM_PPC64 {
+                       sh := elfshname(".glink")
+                       sh.type_ = SHT_PROGBITS
+                       sh.flags = SHF_ALLOC + SHF_EXECINSTR
+                       sh.addralign = 4
+                       shsym(sh, ctxt.Syms.Lookup(".glink", 0))
+               }
+
+               sh = elfshname(".plt")
+               sh.type_ = SHT_PROGBITS
+               sh.flags = SHF_ALLOC + SHF_EXECINSTR
+               if eh.machine == EM_X86_64 {
+                       sh.entsize = 16
+               } else if eh.machine == EM_S390 {
+                       sh.entsize = 32
+               } else if eh.machine == EM_PPC64 {
+                       // On ppc64, this is just a table of addresses
+                       // filled by the dynamic linker
+                       sh.type_ = SHT_NOBITS
+
+                       sh.flags = SHF_ALLOC + SHF_WRITE
+                       sh.entsize = 8
+               } else {
+                       sh.entsize = 4
+               }
+               sh.addralign = sh.entsize
+               shsym(sh, ctxt.Syms.Lookup(".plt", 0))
+
+               // On ppc64, .got comes from the input files, so don't
+               // create it here, and .got.plt is not used.
+               if eh.machine != EM_PPC64 {
+                       sh := elfshname(".got")
+                       sh.type_ = SHT_PROGBITS
+                       sh.flags = SHF_ALLOC + SHF_WRITE
+                       sh.entsize = uint64(ctxt.Arch.RegSize)
+                       sh.addralign = uint64(ctxt.Arch.RegSize)
+                       shsym(sh, ctxt.Syms.Lookup(".got", 0))
+
+                       sh = elfshname(".got.plt")
+                       sh.type_ = SHT_PROGBITS
+                       sh.flags = SHF_ALLOC + SHF_WRITE
+                       sh.entsize = uint64(ctxt.Arch.RegSize)
+                       sh.addralign = uint64(ctxt.Arch.RegSize)
+                       shsym(sh, ctxt.Syms.Lookup(".got.plt", 0))
+               }
+
+               sh = elfshname(".hash")
+               sh.type_ = SHT_HASH
+               sh.flags = SHF_ALLOC
+               sh.entsize = 4
+               sh.addralign = uint64(ctxt.Arch.RegSize)
+               sh.link = uint32(elfshname(".dynsym").shnum)
+               shsym(sh, ctxt.Syms.Lookup(".hash", 0))
+
+               /* sh and PT_DYNAMIC for .dynamic section */
+               sh = elfshname(".dynamic")
+
+               sh.type_ = SHT_DYNAMIC
+               sh.flags = SHF_ALLOC + SHF_WRITE
+               sh.entsize = 2 * uint64(ctxt.Arch.RegSize)
+               sh.addralign = uint64(ctxt.Arch.RegSize)
+               sh.link = uint32(elfshname(".dynstr").shnum)
+               shsym(sh, ctxt.Syms.Lookup(".dynamic", 0))
+               ph := newElfPhdr()
+               ph.type_ = PT_DYNAMIC
+               ph.flags = PF_R + 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_ = PT_TLS
+                       ph.flags = PF_R
+                       ph.memsz = tlssize
+                       ph.align = uint64(ctxt.Arch.RegSize)
+               }
+       }
+
+       if ctxt.HeadType == objabi.Hlinux {
+               ph := newElfPhdr()
+               ph.type_ = PT_GNU_STACK
+               ph.flags = PF_W + PF_R
+               ph.align = uint64(ctxt.Arch.RegSize)
+
+               ph = newElfPhdr()
+               ph.type_ = 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_ = PT_SUNWSTACK
+               ph.flags = PF_W + PF_R
+       }
+
+elfobj:
+       sh := elfshname(".shstrtab")
+       sh.type_ = SHT_STRTAB
+       sh.addralign = 1
+       shsym(sh, ctxt.Syms.Lookup(".shstrtab", 0))
+       eh.shstrndx = uint16(sh.shnum)
+
+       // put these sections early in the list
+       if !*FlagS {
+               elfshname(".symtab")
+               elfshname(".strtab")
+       }
+
+       for _, sect := range Segtext.Sections {
+               elfshbits(ctxt.LinkMode, sect)
+       }
+       for _, sect := range Segrodata.Sections {
+               elfshbits(ctxt.LinkMode, sect)
+       }
+       for _, sect := range Segrelrodata.Sections {
+               elfshbits(ctxt.LinkMode, sect)
+       }
+       for _, sect := range Segdata.Sections {
+               elfshbits(ctxt.LinkMode, sect)
+       }
+       for _, sect := range Segdwarf.Sections {
+               elfshbits(ctxt.LinkMode, sect)
+       }
+
+       if ctxt.LinkMode == LinkExternal {
+               for _, sect := range Segtext.Sections {
+                       elfshreloc(ctxt.Arch, sect)
+               }
+               for _, sect := range Segrodata.Sections {
+                       elfshreloc(ctxt.Arch, sect)
+               }
+               for _, sect := range Segrelrodata.Sections {
+                       elfshreloc(ctxt.Arch, sect)
+               }
+               for _, sect := range Segdata.Sections {
+                       elfshreloc(ctxt.Arch, sect)
+               }
+               for _, s := range dwarfp {
+                       if len(s.R) > 0 || s.Type == sym.SDWARFINFO || s.Type == sym.SDWARFLOC {
+                               elfshreloc(ctxt.Arch, s.Sect)
+                       }
+               }
+               // add a .note.GNU-stack section to mark the stack as non-executable
+               sh := elfshname(".note.GNU-stack")
+
+               sh.type_ = SHT_PROGBITS
+               sh.addralign = 1
+               sh.flags = 0
+       }
+
+       if !*FlagS {
+               sh := elfshname(".symtab")
+               sh.type_ = 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_ = SHT_STRTAB
+               sh.off = uint64(symo) + uint64(Symsize)
+               sh.size = uint64(len(Elfstrdat))
+               sh.addralign = 1
+       }
+
+       /* Main header */
+       eh.ident[EI_MAG0] = '\177'
+
+       eh.ident[EI_MAG1] = 'E'
+       eh.ident[EI_MAG2] = 'L'
+       eh.ident[EI_MAG3] = 'F'
+       if ctxt.HeadType == objabi.Hfreebsd {
+               eh.ident[EI_OSABI] = ELFOSABI_FREEBSD
+       } else if ctxt.HeadType == objabi.Hnetbsd {
+               eh.ident[EI_OSABI] = ELFOSABI_NETBSD
+       } else if ctxt.HeadType == objabi.Hopenbsd {
+               eh.ident[EI_OSABI] = ELFOSABI_OPENBSD
+       } else if ctxt.HeadType == objabi.Hdragonfly {
+               eh.ident[EI_OSABI] = ELFOSABI_NONE
+       }
+       if elf64 {
+               eh.ident[EI_CLASS] = ELFCLASS64
+       } else {
+               eh.ident[EI_CLASS] = ELFCLASS32
+       }
+       if ctxt.Arch.ByteOrder == binary.BigEndian {
+               eh.ident[EI_DATA] = ELFDATA2MSB
+       } else {
+               eh.ident[EI_DATA] = ELFDATA2LSB
+       }
+       eh.ident[EI_VERSION] = EV_CURRENT
+
+       if ctxt.LinkMode == LinkExternal {
+               eh.type_ = ET_REL
+       } else if ctxt.BuildMode == BuildModePIE {
+               eh.type_ = ET_DYN
+       } else {
+               eh.type_ = ET_EXEC
+       }
+
+       if ctxt.LinkMode != LinkExternal {
+               eh.entry = uint64(Entryvalue(ctxt))
+       }
+
+       eh.version = EV_CURRENT
+
+       if pph != nil {
+               pph.filesz = uint64(eh.phnum) * uint64(eh.phentsize)
+               pph.memsz = pph.filesz
+       }
+
+       ctxt.Out.SeekSet(0)
+       a := int64(0)
+       a += int64(elfwritehdr(ctxt.Out))
+       a += int64(elfwritephdrs(ctxt.Out))
+       a += int64(elfwriteshdrs(ctxt.Out))
+       if !*FlagD {
+               a += int64(elfwriteinterp(ctxt.Out))
+       }
+       if ctxt.LinkMode != LinkExternal {
+               if ctxt.HeadType == objabi.Hnetbsd {
+                       a += int64(elfwritenetbsdsig(ctxt.Out))
+               }
+               if ctxt.HeadType == objabi.Hopenbsd {
+                       a += int64(elfwriteopenbsdsig(ctxt.Out))
+               }
+               if len(buildinfo) > 0 {
+                       a += int64(elfwritebuildinfo(ctxt.Out))
+               }
+               if *flagBuildid != "" {
+                       a += int64(elfwritegobuildid(ctxt.Out))
+               }
+       }
+
+       if a > elfreserve {
+               Errorf(nil, "ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext)
+       }
+}
+
+func elfadddynsym(ctxt *Link, s *sym.Symbol) {
+       if elf64 {
+               s.Dynid = int32(Nelfsym)
+               Nelfsym++
+
+               d := ctxt.Syms.Lookup(".dynsym", 0)
+
+               name := s.Extname()
+               d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name)))
+
+               /* type */
+               t := STB_GLOBAL << 4
+
+               if s.Attr.CgoExport() && s.Type == sym.STEXT {
+                       t |= STT_FUNC
+               } else {
+                       t |= STT_OBJECT
+               }
+               d.AddUint8(uint8(t))
+
+               /* reserved */
+               d.AddUint8(0)
+
+               /* section where symbol is defined */
+               if s.Type == sym.SDYNIMPORT {
+                       d.AddUint16(ctxt.Arch, SHN_UNDEF)
+               } else {
+                       d.AddUint16(ctxt.Arch, 1)
+               }
+
+               /* value */
+               if s.Type == sym.SDYNIMPORT {
+                       d.AddUint64(ctxt.Arch, 0)
+               } else {
+                       d.AddAddr(ctxt.Arch, s)
+               }
+
+               /* size of object */
+               d.AddUint64(ctxt.Arch, uint64(s.Size))
+
+               if ctxt.Arch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib() != "" && !seenlib[s.Dynimplib()] {
+                       Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(ctxt.Syms.Lookup(".dynstr", 0), s.Dynimplib())))
+               }
+       } else {
+               s.Dynid = int32(Nelfsym)
+               Nelfsym++
+
+               d := ctxt.Syms.Lookup(".dynsym", 0)
+
+               /* name */
+               name := s.Extname()
+
+               d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name)))
+
+               /* value */
+               if s.Type == sym.SDYNIMPORT {
+                       d.AddUint32(ctxt.Arch, 0)
+               } else {
+                       d.AddAddr(ctxt.Arch, s)
+               }
+
+               /* size of object */
+               d.AddUint32(ctxt.Arch, uint32(s.Size))
+
+               /* type */
+               t := STB_GLOBAL << 4
+
+               // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386.
+               if ctxt.Arch.Family == sys.I386 && s.Attr.CgoExport() && s.Type == sym.STEXT {
+                       t |= STT_FUNC
+               } else if ctxt.Arch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type == sym.STEXT {
+                       t |= STT_FUNC
+               } else {
+                       t |= STT_OBJECT
+               }
+               d.AddUint8(uint8(t))
+               d.AddUint8(0)
+
+               /* shndx */
+               if s.Type == sym.SDYNIMPORT {
+                       d.AddUint16(ctxt.Arch, SHN_UNDEF)
+               } else {
+                       d.AddUint16(ctxt.Arch, 1)
+               }
+       }
+}
+
+func ELF32_R_SYM(info uint32) uint32 {
+       return info >> 8
+}
+
+func ELF32_R_TYPE(info uint32) uint32 {
+       return uint32(uint8(info))
+}
+
+func ELF32_R_INFO(sym uint32, type_ uint32) uint32 {
+       return sym<<8 | type_
+}
+
+func ELF32_ST_BIND(info uint8) uint8 {
+       return info >> 4
+}
+
+func ELF32_ST_TYPE(info uint8) uint8 {
+       return info & 0xf
+}
+
+func ELF32_ST_INFO(bind uint8, type_ uint8) uint8 {
+       return bind<<4 | type_&0xf
+}
+
+func ELF32_ST_VISIBILITY(oth uint8) uint8 {
+       return oth & 3
+}
+
+func ELF64_R_SYM(info uint64) uint32 {
+       return uint32(info >> 32)
+}
+
+func ELF64_R_TYPE(info uint64) uint32 {
+       return uint32(info)
+}
+
+func ELF64_R_INFO(sym uint32, type_ uint32) uint64 {
+       return uint64(sym)<<32 | uint64(type_)
+}
+
+func ELF64_ST_BIND(info uint8) uint8 {
+       return info >> 4
+}
+
+func ELF64_ST_TYPE(info uint8) uint8 {
+       return info & 0xf
+}
+
+func ELF64_ST_INFO(bind uint8, type_ uint8) uint8 {
+       return bind<<4 | type_&0xf
+}
+
+func ELF64_ST_VISIBILITY(oth uint8) uint8 {
+       return oth & 3
+}
diff --git a/src/cmd/oldlink/internal/ld/elf_test.go b/src/cmd/oldlink/internal/ld/elf_test.go
new file mode 100644 (file)
index 0000000..8e86beb
--- /dev/null
@@ -0,0 +1,79 @@
+// +build cgo
+
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+       "debug/elf"
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "testing"
+)
+
+func TestDynSymShInfo(t *testing.T) {
+       t.Parallel()
+       testenv.MustHaveGoBuild(t)
+       dir, err := ioutil.TempDir("", "go-build-issue33358")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       const prog = `
+package main
+
+import "net"
+
+func main() {
+       net.Dial("", "")
+}
+`
+       src := filepath.Join(dir, "issue33358.go")
+       if err := ioutil.WriteFile(src, []byte(prog), 0666); err != nil {
+               t.Fatal(err)
+       }
+
+       binFile := filepath.Join(dir, "issue33358")
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", binFile, src)
+       if out, err := cmd.CombinedOutput(); err != nil {
+               t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+       }
+
+       fi, err := os.Open(binFile)
+       if err != nil {
+               t.Fatalf("failed to open built file: %v", err)
+       }
+
+       elfFile, err := elf.NewFile(fi)
+       if err != nil {
+               t.Skip("The system may not support ELF, skipped.")
+       }
+
+       section := elfFile.Section(".dynsym")
+       if section == nil {
+               t.Fatal("no dynsym")
+       }
+
+       symbols, err := elfFile.DynamicSymbols()
+       if err != nil {
+               t.Fatalf("failed to get dynamic symbols: %v", err)
+       }
+
+       var numLocalSymbols uint32
+       for i, s := range symbols {
+               if elf.ST_BIND(s.Info) != elf.STB_LOCAL {
+                       numLocalSymbols = uint32(i + 1)
+                       break
+               }
+       }
+
+       if section.Info != numLocalSymbols {
+               t.Fatalf("Unexpected sh info, want greater than 0, got: %d", section.Info)
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/execarchive.go b/src/cmd/oldlink/internal/ld/execarchive.go
new file mode 100644 (file)
index 0000000..fe5cc40
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !wasm,!windows
+
+package ld
+
+import (
+       "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/oldlink/internal/ld/execarchive_noexec.go b/src/cmd/oldlink/internal/ld/execarchive_noexec.go
new file mode 100644 (file)
index 0000000..a70dea9
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build wasm windows
+
+package ld
+
+const syscallExecSupported = false
+
+func (ctxt *Link) execArchive(argv []string) {
+       panic("should never arrive here")
+}
diff --git a/src/cmd/oldlink/internal/ld/go.go b/src/cmd/oldlink/internal/ld/go.go
new file mode 100644 (file)
index 0000000..b052655
--- /dev/null
@@ -0,0 +1,442 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// go-specific code shared across loaders (5l, 6l, 8l).
+
+package ld
+
+import (
+       "bytes"
+       "cmd/internal/bio"
+       "cmd/internal/objabi"
+       "cmd/oldlink/internal/sym"
+       "encoding/json"
+       "fmt"
+       "io"
+       "os"
+       "strings"
+)
+
+// go-specific code shared across loaders (5l, 6l, 8l).
+
+// replace all "". with pkg.
+func expandpkg(t0 string, pkg string) string {
+       return strings.Replace(t0, `"".`, pkg+".", -1)
+}
+
+func resolveABIAlias(s *sym.Symbol) *sym.Symbol {
+       if s.Type != sym.SABIALIAS {
+               return s
+       }
+       target := s.R[0].Sym
+       if target.Type == sym.SABIALIAS {
+               panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", s, target))
+       }
+       return target
+}
+
+// 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)
+               if *flagU {
+                       errorexit()
+               }
+               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)
+               if *flagU {
+                       errorexit()
+               }
+               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 == "safe" {
+                       lib.Safe = true
+               }
+               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)
+                       if *flagU {
+                               errorexit()
+                       }
+                       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)
+                       if *flagU {
+                               errorexit()
+                       }
+                       return
+               }
+               p1 += p0
+               loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
+       }
+}
+
+func loadcgo(ctxt *Link, file string, pkg string, p string) {
+       var directives [][]string
+       if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
+               fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
+               nerrors++
+               return
+       }
+
+       // Find cgo_export symbols. They are roots in the deadcode pass.
+       for _, f := range directives {
+               switch f[0] {
+               case "cgo_export_static", "cgo_export_dynamic":
+                       if len(f) < 2 || len(f) > 3 {
+                               continue
+                       }
+                       local := f[1]
+                       switch ctxt.BuildMode {
+                       case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
+                               if local == "main" {
+                                       continue
+                               }
+                       }
+                       local = expandpkg(local, pkg)
+                       if f[0] == "cgo_export_static" {
+                               ctxt.cgo_export_static[local] = true
+                       } else {
+                               ctxt.cgo_export_dynamic[local] = true
+                       }
+               }
+       }
+
+       if *flagNewobj {
+               // Record the directives. We'll process them later after Symbols are created.
+               ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
+       } else {
+               setCgoAttr(ctxt, ctxt.Syms.Lookup, file, pkg, directives)
+       }
+}
+
+// Set symbol attributes or flags based on cgo directives.
+func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, pkg string, directives [][]string) {
+       for _, f := range directives {
+               switch f[0] {
+               case "cgo_import_dynamic":
+                       if len(f) < 2 || len(f) > 4 {
+                               break
+                       }
+
+                       local := f[1]
+                       remote := local
+                       if len(f) > 2 {
+                               remote = f[2]
+                       }
+                       lib := ""
+                       if len(f) > 3 {
+                               lib = f[3]
+                       }
+
+                       if *FlagD {
+                               fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
+                               nerrors++
+                               return
+                       }
+
+                       if local == "_" && remote == "_" {
+                               // allow #pragma dynimport _ _ "foo.so"
+                               // to force a link of foo.so.
+                               havedynamic = 1
+
+                               if ctxt.HeadType == objabi.Hdarwin {
+                                       machoadddynlib(lib, ctxt.LinkMode)
+                               } else {
+                                       dynlib = append(dynlib, lib)
+                               }
+                               continue
+                       }
+
+                       local = expandpkg(local, pkg)
+                       q := ""
+                       if i := strings.Index(remote, "#"); i >= 0 {
+                               remote, q = remote[:i], remote[i+1:]
+                       }
+                       s := lookup(local, 0)
+                       if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS || s.Type == sym.SHOSTOBJ {
+                               s.SetDynimplib(lib)
+                               s.SetExtname(remote)
+                               s.SetDynimpvers(q)
+                               if s.Type != sym.SHOSTOBJ {
+                                       s.Type = sym.SDYNIMPORT
+                               }
+                               havedynamic = 1
+                       }
+
+                       continue
+
+               case "cgo_import_static":
+                       if len(f) != 2 {
+                               break
+                       }
+                       local := f[1]
+
+                       s := lookup(local, 0)
+                       s.Type = sym.SHOSTOBJ
+                       s.Size = 0
+                       continue
+
+               case "cgo_export_static", "cgo_export_dynamic":
+                       if len(f) < 2 || len(f) > 3 {
+                               break
+                       }
+                       local := f[1]
+                       remote := local
+                       if len(f) > 2 {
+                               remote = f[2]
+                       }
+                       local = expandpkg(local, pkg)
+
+                       // The compiler arranges for an ABI0 wrapper
+                       // to be available for all cgo-exported
+                       // functions. Link.loadlib will resolve any
+                       // ABI aliases we find here (since we may not
+                       // yet know it's an alias).
+                       s := lookup(local, 0)
+
+                       switch ctxt.BuildMode {
+                       case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
+                               if s == lookup("main", 0) {
+                                       continue
+                               }
+                       }
+
+                       // export overrides import, for openbsd/cgo.
+                       // see issue 4878.
+                       if s.Dynimplib() != "" {
+                               s.ResetDyninfo()
+                               s.SetExtname("")
+                               s.Type = 0
+                       }
+
+                       if !s.Attr.CgoExport() {
+                               s.SetExtname(remote)
+                       } else if s.Extname() != remote {
+                               fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname(), remote)
+                               nerrors++
+                               return
+                       }
+
+                       if f[0] == "cgo_export_static" {
+                               s.Attr |= sym.AttrCgoExportStatic
+                       } else {
+                               s.Attr |= sym.AttrCgoExportDynamic
+                       }
+                       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++
+       }
+}
+
+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 {
+               s := ctxt.Syms.Lookup(".dynstr", 0)
+               if s.Size == 0 {
+                       Addstring(s, "")
+               }
+               Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
+       } else {
+               Errorf(nil, "adddynlib: unsupported binary format")
+       }
+}
+
+func Adddynsym(ctxt *Link, s *sym.Symbol) {
+       if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal {
+               return
+       }
+
+       if ctxt.IsELF {
+               elfadddynsym(ctxt, s)
+       } else if ctxt.HeadType == objabi.Hdarwin {
+               Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname())
+       } else if ctxt.HeadType == objabi.Hwindows {
+               // already taken care of
+       } else {
+               Errorf(s, "adddynsym: unsupported binary format")
+       }
+}
+
+func fieldtrack(ctxt *Link) {
+       // record field tracking references
+       var buf bytes.Buffer
+       for _, s := range ctxt.Syms.Allsym {
+               if strings.HasPrefix(s.Name, "go.track.") {
+                       s.Attr |= sym.AttrSpecial // do not lay out in data segment
+                       s.Attr |= sym.AttrNotInSymbolTable
+                       if s.Attr.Reachable() {
+                               buf.WriteString(s.Name[9:])
+                               for p := ctxt.Reachparent[s]; p != nil; p = ctxt.Reachparent[p] {
+                                       buf.WriteString("\t")
+                                       buf.WriteString(p.Name)
+                               }
+                               buf.WriteString("\n")
+                       }
+
+                       s.Type = sym.SCONST
+                       s.Value = 0
+               }
+       }
+
+       if *flagFieldTrack == "" {
+               return
+       }
+       s := ctxt.Syms.ROLookup(*flagFieldTrack, 0)
+       if s == nil || !s.Attr.Reachable() {
+               return
+       }
+       s.Type = sym.SDATA
+       addstrdata(ctxt, *flagFieldTrack, buf.String())
+}
+
+func (ctxt *Link) addexport() {
+       // Track undefined external symbols during external link.
+       if ctxt.LinkMode == LinkExternal {
+               for _, s := range ctxt.Syms.Allsym {
+                       if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() {
+                               continue
+                       }
+                       if s.Type != sym.STEXT {
+                               continue
+                       }
+                       for i := range s.R {
+                               r := &s.R[i]
+                               if r.Sym != nil && r.Sym.Type == sym.Sxxx {
+                                       r.Sym.Type = sym.SUNDEFEXT
+                               }
+                       }
+               }
+       }
+
+       // TODO(aix)
+       if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
+               return
+       }
+
+       for _, exp := range dynexp {
+               Adddynsym(ctxt, exp)
+       }
+       for _, lib := range 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/oldlink/internal/ld/issue33808_test.go b/src/cmd/oldlink/internal/ld/issue33808_test.go
new file mode 100644 (file)
index 0000000..77eaeb4
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "runtime"
+       "strings"
+       "testing"
+)
+
+const prog = `
+package main
+
+import "log"
+
+func main() {
+       log.Fatalf("HERE")
+}
+`
+
+func TestIssue33808(t *testing.T) {
+       if runtime.GOOS != "darwin" {
+               return
+       }
+       testenv.MustHaveGoBuild(t)
+       testenv.MustHaveCGO(t)
+
+       dir, err := ioutil.TempDir("", "TestIssue33808")
+       if err != nil {
+               t.Fatalf("could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       f := gobuild(t, dir, prog, "-ldflags=-linkmode=external")
+       f.Close()
+
+       syms, err := f.Symbols()
+       if err != nil {
+               t.Fatalf("Error reading symbols: %v", err)
+       }
+
+       name := "log.Fatalf"
+       for _, sym := range syms {
+               if strings.Contains(sym.Name, name) {
+                       return
+               }
+       }
+       t.Fatalf("Didn't find %v", name)
+}
diff --git a/src/cmd/oldlink/internal/ld/ld.go b/src/cmd/oldlink/internal/ld/ld.go
new file mode 100644 (file)
index 0000000..7420dce
--- /dev/null
@@ -0,0 +1,217 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/sym"
+       "io/ioutil"
+       "log"
+       "os"
+       "path"
+       "path/filepath"
+       "strconv"
+       "strings"
+)
+
+func (ctxt *Link) readImportCfg(file string) {
+       ctxt.PackageFile = make(map[string]string)
+       ctxt.PackageShlib = make(map[string]string)
+       data, err := ioutil.ReadFile(file)
+       if err != nil {
+               log.Fatalf("-importcfg: %v", err)
+       }
+
+       for lineNum, line := range strings.Split(string(data), "\n") {
+               lineNum++ // 1-based
+               line = strings.TrimSpace(line)
+               if line == "" {
+                       continue
+               }
+               if line == "" || strings.HasPrefix(line, "#") {
+                       continue
+               }
+
+               var verb, args string
+               if i := strings.Index(line, " "); i < 0 {
+                       verb = line
+               } else {
+                       verb, args = line[:i], strings.TrimSpace(line[i+1:])
+               }
+               var before, after string
+               if i := strings.Index(args, "="); i >= 0 {
+                       before, after = args[:i], args[i+1:]
+               }
+               switch verb {
+               default:
+                       log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
+               case "packagefile":
+                       if before == "" || after == "" {
+                               log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
+                       }
+                       ctxt.PackageFile[before] = after
+               case "packageshlib":
+                       if before == "" || after == "" {
+                               log.Fatalf(`%s:%d: invalid packageshlib: syntax is "packageshlib path=filename"`, file, lineNum)
+                       }
+                       ctxt.PackageShlib[before] = after
+               }
+       }
+}
+
+func pkgname(ctxt *Link, lib string) string {
+       name := path.Clean(lib)
+
+       // When using importcfg, we have the final package name.
+       if ctxt.PackageFile != nil {
+               return name
+       }
+
+       // runtime.a -> runtime, runtime.6 -> runtime
+       pkg := name
+       if len(pkg) >= 2 && pkg[len(pkg)-2] == '.' {
+               pkg = pkg[:len(pkg)-2]
+       }
+       return pkg
+}
+
+func findlib(ctxt *Link, lib string) (string, bool) {
+       name := path.Clean(lib)
+
+       var pname string
+       isshlib := false
+
+       if ctxt.linkShared && ctxt.PackageShlib[name] != "" {
+               pname = ctxt.PackageShlib[name]
+               isshlib = true
+       } else if ctxt.PackageFile != nil {
+               pname = ctxt.PackageFile[name]
+               if pname == "" {
+                       ctxt.Logf("cannot find package %s (using -importcfg)\n", name)
+                       return "", false
+               }
+       } else {
+               if filepath.IsAbs(name) {
+                       pname = name
+               } else {
+                       pkg := pkgname(ctxt, lib)
+                       // Add .a if needed; the new -importcfg modes
+                       // do not put .a into the package name anymore.
+                       // This only matters when people try to mix
+                       // compiles using -importcfg with links not using -importcfg,
+                       // such as when running quick things like
+                       // 'go tool compile x.go && go tool link x.o'
+                       // by hand against a standard library built using -importcfg.
+                       if !strings.HasSuffix(name, ".a") && !strings.HasSuffix(name, ".o") {
+                               name += ".a"
+                       }
+                       // try dot, -L "libdir", and then goroot.
+                       for _, dir := range ctxt.Libdir {
+                               if ctxt.linkShared {
+                                       pname = filepath.Join(dir, pkg+".shlibname")
+                                       if _, err := os.Stat(pname); err == nil {
+                                               isshlib = true
+                                               break
+                                       }
+                               }
+                               pname = filepath.Join(dir, name)
+                               if _, err := os.Stat(pname); err == nil {
+                                       break
+                               }
+                       }
+               }
+               pname = filepath.Clean(pname)
+       }
+
+       return pname, isshlib
+}
+
+func addlib(ctxt *Link, src string, obj string, lib string) *sym.Library {
+       pkg := pkgname(ctxt, lib)
+
+       // already loaded?
+       if l := ctxt.LibraryByPkg[pkg]; l != nil {
+               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)
+       }
+       return addlibpath(ctxt, src, obj, pname, pkg, "")
+}
+
+/*
+ * 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
+ */
+func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlib string) *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\n", srcref, objref, file, pkg, shlib)
+       }
+
+       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
+       if shlib != "" {
+               if strings.HasSuffix(shlib, ".shlibname") {
+                       data, err := ioutil.ReadFile(shlib)
+                       if err != nil {
+                               Errorf(nil, "cannot read %s: %v", shlib, err)
+                       }
+                       shlib = strings.TrimSpace(string(data))
+               }
+               l.Shlib = shlib
+       }
+       return l
+}
+
+func atolwhex(s string) int64 {
+       n, _ := strconv.ParseInt(s, 0, 64)
+       return n
+}
diff --git a/src/cmd/oldlink/internal/ld/ld_test.go b/src/cmd/oldlink/internal/ld/ld_test.go
new file mode 100644 (file)
index 0000000..4dbe09d
--- /dev/null
@@ -0,0 +1,136 @@
+// 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 (
+       "fmt"
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "runtime"
+       "strings"
+       "testing"
+)
+
+func TestUndefinedRelocErrors(t *testing.T) {
+       t.Parallel()
+       testenv.MustHaveGoBuild(t)
+       dir, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       out, err := exec.Command(testenv.GoToolPath(t), "build", "./testdata/issue10978").CombinedOutput()
+       if err == nil {
+               t.Fatal("expected build to fail")
+       }
+
+       wantErrors := map[string]int{
+               // Main function has dedicated error message.
+               "function main is undeclared in the main package": 1,
+
+               // Single error reporting per each symbol.
+               // This way, duplicated messages are not reported for
+               // multiple relocations with a same name.
+               "main.defined1: relocation target main.undefined not defined": 1,
+               "main.defined2: relocation target main.undefined not defined": 1,
+       }
+       unexpectedErrors := map[string]int{}
+
+       for _, l := range strings.Split(string(out), "\n") {
+               if strings.HasPrefix(l, "#") || l == "" {
+                       continue
+               }
+               matched := ""
+               for want := range wantErrors {
+                       if strings.Contains(l, want) {
+                               matched = want
+                               break
+                       }
+               }
+               if matched != "" {
+                       wantErrors[matched]--
+               } else {
+                       unexpectedErrors[l]++
+               }
+       }
+
+       for want, n := range wantErrors {
+               switch {
+               case n > 0:
+                       t.Errorf("unmatched error: %s (x%d)", want, n)
+               case n < 0:
+                       t.Errorf("extra errors: %s (x%d)", want, -n)
+               }
+       }
+       for unexpected, n := range unexpectedErrors {
+               t.Errorf("unexpected error: %s (x%d)", unexpected, n)
+       }
+}
+
+const carchiveSrcText = `
+package main
+
+//export GoFunc
+func GoFunc() {
+       println(42)
+}
+
+func main() {
+}
+`
+
+func TestArchiveBuildInvokeWithExec(t *testing.T) {
+       t.Parallel()
+       testenv.MustHaveGoBuild(t)
+       testenv.MustHaveCGO(t)
+
+       // run this test on just a small set of platforms (no need to test it
+       // across the board given the nature of the test).
+       pair := runtime.GOOS + "-" + runtime.GOARCH
+       switch pair {
+       case "darwin-amd64", "darwin-arm64", "linux-amd64", "freebsd-amd64":
+       default:
+               t.Skip("no need for test on " + pair)
+       }
+       switch runtime.GOOS {
+       case "openbsd", "windows":
+               t.Skip("c-archive unsupported")
+       }
+       dir, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       srcfile := filepath.Join(dir, "test.go")
+       arfile := filepath.Join(dir, "test.a")
+       if err := ioutil.WriteFile(srcfile, []byte(carchiveSrcText), 0666); err != nil {
+               t.Fatal(err)
+       }
+
+       ldf := fmt.Sprintf("-ldflags=-v -tmpdir=%s", dir)
+       argv := []string{"build", "-buildmode=c-archive", "-o", arfile, ldf, srcfile}
+       out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
+       if err != nil {
+               t.Fatalf("build failure: %s\n%s\n", err, string(out))
+       }
+
+       found := false
+       const want = "invoking archiver with syscall.Exec"
+       for _, l := range strings.Split(string(out), "\n") {
+               if strings.HasPrefix(l, want) {
+                       found = true
+                       break
+               }
+       }
+
+       if !found {
+               t.Errorf("expected '%s' in -v output, got:\n%s\n", want, string(out))
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/lib.go b/src/cmd/oldlink/internal/ld/lib.go
new file mode 100644 (file)
index 0000000..a6c86af
--- /dev/null
@@ -0,0 +1,2749 @@
+// Inferno utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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 (
+       "bufio"
+       "bytes"
+       "cmd/internal/bio"
+       "cmd/internal/obj"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/loadelf"
+       "cmd/oldlink/internal/loader"
+       "cmd/oldlink/internal/loadmacho"
+       "cmd/oldlink/internal/loadpe"
+       "cmd/oldlink/internal/loadxcoff"
+       "cmd/oldlink/internal/objfile"
+       "cmd/oldlink/internal/sym"
+       "crypto/sha1"
+       "debug/elf"
+       "debug/macho"
+       "encoding/base64"
+       "encoding/binary"
+       "encoding/hex"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "log"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "runtime"
+       "sort"
+       "strings"
+       "sync"
+)
+
+// Data layout and relocation.
+
+// Derived from Inferno utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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.
+
+type Arch struct {
+       Funcalign      int
+       Maxalign       int
+       Minalign       int
+       Dwarfregsp     int
+       Dwarfreglr     int
+       Androiddynld   string
+       Linuxdynld     string
+       Freebsddynld   string
+       Netbsddynld    string
+       Openbsddynld   string
+       Dragonflydynld string
+       Solarisdynld   string
+       Adddynrel      func(*Link, *sym.Symbol, *sym.Reloc) 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) and a boolean indicating
+       // success/failure (a failing value indicates a fatal error).
+       Archreloc func(link *Link, rel *sym.Reloc, sym *sym.Symbol,
+               offset int64) (relocatedOffset int64, success 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(link *Link, rel *sym.Reloc, sym *sym.Symbol,
+               offset int64) (relocatedOffset int64)
+       Trampoline func(*Link, *sym.Reloc, *sym.Symbol)
+
+       // Asmb and Asmb2 are arch-specific routines that write the output
+       // file. Typically, Asmb writes most of the content (sections and
+       // segments), for which we have computed the size and offset. Asmb2
+       // writes the rest.
+       Asmb  func(*Link)
+       Asmb2 func(*Link)
+
+       Elfreloc1   func(*Link, *sym.Reloc, int64) bool
+       Elfsetupplt func(*Link)
+       Gentext     func(*Link)
+       Machoreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool
+       PEreloc1    func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool
+       Xcoffreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool
+
+       // 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(s *sym.Symbol, off, size int)
+
+       // optional override for assignAddress
+       AssignAddress func(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64)
+}
+
+var (
+       thearch Arch
+       Lcsize  int32
+       rpath   Rpath
+       Spsize  int32
+       Symsize int32
+)
+
+const (
+       MINFUNC = 16 // minimum size for a function
+)
+
+// DynlinkingGo reports whether we are producing Go code that can live
+// in separate shared libraries linked together at runtime.
+func (ctxt *Link) DynlinkingGo() bool {
+       if !ctxt.Loaded {
+               panic("DynlinkingGo called before all symbols loaded")
+       }
+       return ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin || ctxt.canUsePlugins
+}
+
+// CanUsePlugins reports whether a plugins can be used
+func (ctxt *Link) CanUsePlugins() bool {
+       if !ctxt.Loaded {
+               panic("CanUsePlugins called before all symbols loaded")
+       }
+       return ctxt.canUsePlugins
+}
+
+// UseRelro reports whether to make use of "read only relocations" aka
+// relro.
+func (ctxt *Link) UseRelro() bool {
+       switch ctxt.BuildMode {
+       case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin:
+               return ctxt.IsELF || ctxt.HeadType == objabi.Haix
+       default:
+               return ctxt.linkShared || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal)
+       }
+}
+
+var (
+       dynexp          []*sym.Symbol
+       dynlib          []string
+       ldflag          []string
+       havedynamic     int
+       Funcalign       int
+       iscgo           bool
+       elfglobalsymndx int
+       interpreter     string
+
+       debug_s bool // backup old value of debug['s']
+       HEADR   int32
+
+       nerrors  int
+       liveness int64
+
+       // See -strictdups command line flag.
+       checkStrictDups   int // 0=off 1=warning 2=error
+       strictDupMsgCount int
+)
+
+var (
+       Segtext      sym.Segment
+       Segrodata    sym.Segment
+       Segrelrodata sym.Segment
+       Segdata      sym.Segment
+       Segdwarf     sym.Segment
+)
+
+const pkgdef = "__.PKGDEF"
+
+var (
+       // Set if we see an object compiled by the host compiler that is not
+       // from a package that is known to support internal linking mode.
+       externalobj = false
+       theline     string
+)
+
+func Lflag(ctxt *Link, arg string) {
+       ctxt.Libdir = append(ctxt.Libdir, arg)
+}
+
+/*
+ * Unix doesn't like it when we write to a running (or, sometimes,
+ * recently run) binary, so remove the output file before writing it.
+ * On Windows 7, remove() can force a subsequent create() to fail.
+ * S_ISREG() does not exist on Plan 9.
+ */
+func mayberemoveoutfile() {
+       if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() {
+               return
+       }
+       os.Remove(*flagOutfile)
+}
+
+func libinit(ctxt *Link) {
+       Funcalign = thearch.Funcalign
+
+       // add goroot to the end of the libdir list.
+       suffix := ""
+
+       suffixsep := ""
+       if *flagInstallSuffix != "" {
+               suffixsep = "_"
+               suffix = *flagInstallSuffix
+       } else if *flagRace {
+               suffixsep = "_"
+               suffix = "race"
+       } else if *flagMsan {
+               suffixsep = "_"
+               suffix = "msan"
+       }
+
+       Lflag(ctxt, filepath.Join(objabi.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", objabi.GOOS, objabi.GOARCH, suffixsep, suffix)))
+
+       mayberemoveoutfile()
+       f, err := os.OpenFile(*flagOutfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
+       if err != nil {
+               Exitf("cannot create %s: %v", *flagOutfile, err)
+       }
+
+       ctxt.Out.w = bufio.NewWriter(f)
+       ctxt.Out.f = f
+
+       if *flagEntrySymbol == "" {
+               switch ctxt.BuildMode {
+               case BuildModeCShared, BuildModeCArchive:
+                       *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", objabi.GOARCH, objabi.GOOS)
+               case BuildModeExe, BuildModePIE:
+                       *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", objabi.GOARCH, objabi.GOOS)
+               case BuildModeShared, BuildModePlugin:
+                       // No *flagEntrySymbol for -buildmode=shared and plugin
+               default:
+                       Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode)
+               }
+       }
+}
+
+func exitIfErrors() {
+       if nerrors != 0 || checkStrictDups > 1 && strictDupMsgCount > 0 {
+               mayberemoveoutfile()
+               Exit(2)
+       }
+
+}
+
+func errorexit() {
+       exitIfErrors()
+       Exit(0)
+}
+
+func loadinternal(ctxt *Link, name string) *sym.Library {
+       if ctxt.linkShared && ctxt.PackageShlib != nil {
+               if shlib := ctxt.PackageShlib[name]; shlib != "" {
+                       return addlibpath(ctxt, "internal", "internal", "", name, shlib)
+               }
+       }
+       if ctxt.PackageFile != nil {
+               if pname := ctxt.PackageFile[name]; pname != "" {
+                       return addlibpath(ctxt, "internal", "internal", pname, name, "")
+               }
+               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)
+                       }
+               }
+               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, "")
+               }
+       }
+
+       ctxt.Logf("warning: unable to find %s.a\n", name)
+       return nil
+}
+
+// extld returns the current external linker.
+func (ctxt *Link) extld() string {
+       if *flagExtld == "" {
+               *flagExtld = "gcc"
+       }
+       return *flagExtld
+}
+
+// findLibPathCmd uses cmd command to find gcc library libname.
+// It returns library full path if found, or "none" if not found.
+func (ctxt *Link) findLibPathCmd(cmd, libname string) string {
+       extld := ctxt.extld()
+       args := hostlinkArchArgs(ctxt.Arch)
+       args = append(args, cmd)
+       if ctxt.Debugvlog != 0 {
+               ctxt.Logf("%s %v\n", extld, args)
+       }
+       out, err := exec.Command(extld, args...).Output()
+       if err != nil {
+               if ctxt.Debugvlog != 0 {
+                       ctxt.Logf("not using a %s file because compiler failed\n%v\n%s\n", libname, err, out)
+               }
+               return "none"
+       }
+       return strings.TrimSpace(string(out))
+}
+
+// findLibPath searches for library libname.
+// It returns library full path if found, or "none" if not found.
+func (ctxt *Link) findLibPath(libname string) string {
+       return ctxt.findLibPathCmd("--print-file-name="+libname, libname)
+}
+
+func (ctxt *Link) loadlib() {
+       if *flagNewobj {
+               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)
+               }
+               ctxt.loader = loader.NewLoader(flags)
+       }
+
+       ctxt.cgo_export_static = make(map[string]bool)
+       ctxt.cgo_export_dynamic = make(map[string]bool)
+
+       // ctxt.Library grows during the loop, so not a range loop.
+       i := 0
+       for ; i < len(ctxt.Library); i++ {
+               lib := ctxt.Library[i]
+               if lib.Shlib == "" {
+                       if ctxt.Debugvlog > 1 {
+                               ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref)
+                       }
+                       loadobjfile(ctxt, lib)
+               }
+       }
+
+       // load internal packages, if not already
+       if *flagRace {
+               loadinternal(ctxt, "runtime/race")
+       }
+       if *flagMsan {
+               loadinternal(ctxt, "runtime/msan")
+       }
+       loadinternal(ctxt, "runtime")
+       for ; i < len(ctxt.Library); i++ {
+               lib := ctxt.Library[i]
+               if lib.Shlib == "" {
+                       loadobjfile(ctxt, lib)
+               }
+       }
+
+       if *flagNewobj {
+               iscgo = ctxt.loader.Lookup("x_cgo_init", 0) != 0
+               ctxt.canUsePlugins = ctxt.loader.Lookup("plugin.Open", sym.SymVerABIInternal) != 0
+       } else {
+               iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
+               ctxt.canUsePlugins = ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil
+       }
+
+       // We now have enough information to determine the link mode.
+       determineLinkMode(ctxt)
+
+       if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) {
+               // 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 {
+                       if lib.Shlib != "" {
+                               ldshlibsyms(ctxt, lib.Shlib)
+                       } else {
+                               if ctxt.BuildMode == BuildModeShared || ctxt.linkShared {
+                                       Exitf("cannot implicitly include runtime/cgo in a shared library")
+                               }
+                               loadobjfile(ctxt, lib)
+                       }
+               }
+       }
+
+       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)
+               }
+       }
+
+       if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
+               if *flagNewobj {
+                       // In newobj mode, we typically create sym.Symbols later therefore
+                       // also set cgo attributes later. However, for internal cgo linking,
+                       // the host object loaders still work with sym.Symbols (for now),
+                       // and they need cgo attributes set to work properly. So process
+                       // them now.
+                       lookup := func(name string, ver int) *sym.Symbol { return ctxt.loader.LookupOrCreate(name, ver, ctxt.Syms) }
+                       for _, d := range ctxt.cgodata {
+                               setCgoAttr(ctxt, lookup, d.file, d.pkg, d.directives)
+                       }
+                       ctxt.cgodata = nil
+               }
+
+               // Drop all the cgo_import_static declarations.
+               // Turns out we won't be needing them.
+               for _, s := range ctxt.Syms.Allsym {
+                       if s.Type == 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.
+                               if s.Extname() != "" && s.Dynimplib() != "" && !s.Attr.CgoExport() {
+                                       s.Type = sym.SDYNIMPORT
+                               } else {
+                                       s.Type = 0
+                               }
+                       }
+               }
+       }
+
+       // Conditionally load host objects, or setup for external linking.
+       hostobjs(ctxt)
+       hostlinksetup(ctxt)
+
+       if *flagNewobj {
+               // Add references of externally defined symbols.
+               ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms)
+       }
+
+       // Now that we know the link mode, set the dynexp list.
+       if !*flagNewobj { // set this later in newobj mode
+               setupdynexp(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
+               for _, s := range ctxt.Syms.Allsym {
+                       for i := range s.R {
+                               r := &s.R[i] // Copying sym.Reloc has measurable impact on performance
+                               if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" {
+                                       any = true
+                                       break
+                               }
+                       }
+               }
+               if any {
+                       if *flagLibGCC == "" {
+                               *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc")
+                       }
+                       if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" {
+                               // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a".
+                               // In this case we fail to load libgcc.a and can encounter link
+                               // errors - see if we can find libcompiler_rt.a instead.
+                               *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
+                       }
+                       if *flagLibGCC != "none" {
+                               hostArchive(ctxt, *flagLibGCC)
+                       }
+                       if ctxt.HeadType == objabi.Hwindows {
+                               if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
+                                       hostArchive(ctxt, p)
+                               }
+                               if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
+                                       hostArchive(ctxt, p)
+                               }
+                               // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol
+                               // (see https://golang.org/issue/23649 for details).
+                               if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" {
+                                       hostArchive(ctxt, p)
+                               }
+                               // TODO: maybe do something similar to peimporteddlls to collect all lib names
+                               // and try link them all to final exe just like libmingwex.a and libmingw32.a:
+                               /*
+                                       for:
+                                       #cgo windows LDFLAGS: -lmsvcrt -lm
+                                       import:
+                                       libmsvcrt.a libm.a
+                               */
+                       }
+               }
+       }
+
+       // We've loaded all the code now.
+       ctxt.Loaded = true
+
+       importcycles()
+
+       if *flagNewobj {
+               strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
+       }
+}
+
+// Set up dynexp list.
+func setupdynexp(ctxt *Link) {
+       dynexpMap := ctxt.cgo_export_dynamic
+       if ctxt.LinkMode == LinkExternal {
+               dynexpMap = ctxt.cgo_export_static
+       }
+       dynexp = make([]*sym.Symbol, 0, len(dynexpMap))
+       for exp := range dynexpMap {
+               s := ctxt.Syms.Lookup(exp, 0)
+               dynexp = append(dynexp, s)
+       }
+       sort.Sort(byName(dynexp))
+
+       // Resolve ABI aliases in the list of cgo-exported functions.
+       // This is necessary because we load the ABI0 symbol for all
+       // cgo exports.
+       for i, s := range dynexp {
+               if s.Type != sym.SABIALIAS {
+                       continue
+               }
+               t := resolveABIAlias(s)
+               t.Attr |= s.Attr
+               t.SetExtname(s.Extname())
+               dynexp[i] = t
+       }
+
+       ctxt.cgo_export_static = nil
+       ctxt.cgo_export_dynamic = nil
+}
+
+// Set up flags and special symbols depending on the platform build mode.
+func (ctxt *Link) linksetup() {
+       switch ctxt.BuildMode {
+       case BuildModeCShared, BuildModePlugin:
+               s := ctxt.Syms.Lookup("runtime.islibrary", 0)
+               s.Type = sym.SNOPTRDATA
+               s.Attr |= sym.AttrDuplicateOK
+               s.AddUint8(1)
+       case BuildModeCArchive:
+               s := ctxt.Syms.Lookup("runtime.isarchive", 0)
+               s.Type = sym.SNOPTRDATA
+               s.Attr |= sym.AttrDuplicateOK
+               s.AddUint8(1)
+       }
+
+       // Recalculate pe parameters now that we have ctxt.LinkMode set.
+       if ctxt.HeadType == objabi.Hwindows {
+               Peinit(ctxt)
+       }
+
+       if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
+               *FlagTextAddr = 0
+       }
+
+       // If there are no dynamic libraries needed, gcc disables dynamic linking.
+       // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
+       // assumes that a dynamic binary always refers to at least one dynamic library.
+       // Rather than be a source of test cases for glibc, disable dynamic linking
+       // the same way that gcc would.
+       //
+       // Exception: on OS X, programs such as Shark only work with dynamic
+       // binaries, so leave it enabled on OS X (Mach-O) binaries.
+       // Also leave it enabled on Solaris which doesn't support
+       // statically linked binaries.
+       if ctxt.BuildMode == BuildModeExe {
+               if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris {
+                       *FlagD = true
+               }
+       }
+
+       if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
+               toc := ctxt.Syms.Lookup(".TOC.", 0)
+               toc.Type = 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 dont't
+       // generate it.
+       if objabi.GOOS != "android" {
+               tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0)
+
+               // runtime.tlsg is used for external linking on platforms that do not define
+               // a variable to hold g in assembly (currently only intel).
+               if tlsg.Type == 0 {
+                       tlsg.Type = sym.STLSBSS
+                       tlsg.Size = int64(ctxt.Arch.PtrSize)
+               } else if tlsg.Type != sym.SDYNIMPORT {
+                       Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type)
+               }
+               tlsg.Attr |= sym.AttrReachable
+               ctxt.Tlsg = tlsg
+       }
+
+       var moduledata *sym.Symbol
+       if ctxt.BuildMode == BuildModePlugin {
+               moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0)
+               moduledata.Attr |= sym.AttrLocal
+       } else {
+               moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
+       }
+       if moduledata.Type != 0 && moduledata.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().
+               moduledata.Size = 0
+
+               // In addition, on ARM, the runtime depends on the linker
+               // recording the value of GOARM.
+               if ctxt.Arch.Family == sys.ARM {
+                       s := ctxt.Syms.Lookup("runtime.goarm", 0)
+                       s.Type = sym.SDATA
+                       s.Size = 0
+                       s.AddUint8(uint8(objabi.GOARM))
+               }
+
+               if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
+                       s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0)
+                       s.Type = sym.SDATA
+                       s.Size = 0
+                       s.AddUint8(1)
+               }
+       } else {
+               // If OTOH the module does not contain the runtime package,
+               // create a local symbol for the moduledata.
+               moduledata = ctxt.Syms.Lookup("local.moduledata", 0)
+               moduledata.Attr |= sym.AttrLocal
+       }
+       // In all cases way we mark the moduledata as noptrdata to hide it from
+       // the GC.
+       moduledata.Type = sym.SNOPTRDATA
+       moduledata.Attr |= sym.AttrReachable
+       ctxt.Moduledata = moduledata
+
+       // If package versioning is required, generate a hash of the
+       // packages used in the link.
+       if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
+               for _, lib := range ctxt.Library {
+                       if lib.Shlib == "" {
+                               genhash(ctxt, lib)
+                       }
+               }
+       }
+
+       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.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0)
+                       got.Type = sym.SDYNIMPORT
+                       got.Attr |= sym.AttrReachable
+               }
+       }
+}
+
+// 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
+       }
+
+       for _, s := range ctxt.Syms.Allsym {
+               newName := typeSymbolMangle(s.Name)
+               if newName != s.Name {
+                       ctxt.Syms.Rename(s.Name, newName, int(s.Version), ctxt.Reachparent)
+               }
+       }
+}
+
+// typeSymbolMangle mangles the given symbol name into something shorter.
+//
+// Keep the type.. prefix, which parts of the linker (like the
+// DWARF generator) know means the symbol is not decodable.
+// Leave type.runtime. symbols alone, because other parts of
+// the linker manipulates them.
+func typeSymbolMangle(name string) string {
+       if !strings.HasPrefix(name, "type.") {
+               return name
+       }
+       if strings.HasPrefix(name, "type.runtime.") {
+               return name
+       }
+       if len(name) <= 14 && !strings.Contains(name, "@") { // Issue 19529
+               return name
+       }
+       hash := sha1.Sum([]byte(name))
+       prefix := "type."
+       if name[5] == '.' {
+               prefix = "type.."
+       }
+       return prefix + base64.StdEncoding.EncodeToString(hash[:6])
+}
+
+/*
+ * look for the next file in an archive.
+ * adapted from libmach.
+ */
+func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 {
+       if off&1 != 0 {
+               off++
+       }
+       bp.MustSeek(off, 0)
+       var buf [SAR_HDR]byte
+       if n, err := io.ReadFull(bp, buf[:]); err != nil {
+               if n == 0 && err != io.EOF {
+                       return -1
+               }
+               return 0
+       }
+
+       a.name = artrim(buf[0:16])
+       a.date = artrim(buf[16:28])
+       a.uid = artrim(buf[28:34])
+       a.gid = artrim(buf[34:40])
+       a.mode = artrim(buf[40:48])
+       a.size = artrim(buf[48:58])
+       a.fmag = artrim(buf[58:60])
+
+       arsize := atolwhex(a.size)
+       if arsize&1 != 0 {
+               arsize++
+       }
+       return arsize + SAR_HDR
+}
+
+func genhash(ctxt *Link, lib *sym.Library) {
+       f, err := bio.Open(lib.File)
+       if err != nil {
+               Errorf(nil, "cannot open file %s for hash generation: %v", lib.File, err)
+               return
+       }
+       defer f.Close()
+
+       var magbuf [len(ARMAG)]byte
+       if _, err := io.ReadFull(f, magbuf[:]); err != nil {
+               Exitf("file %s too short", lib.File)
+       }
+
+       if string(magbuf[:]) != ARMAG {
+               Exitf("%s is not an archive file", lib.File)
+       }
+
+       var arhdr ArHdr
+       l := nextar(f, f.Offset(), &arhdr)
+       if l <= 0 {
+               Errorf(nil, "%s: short read on archive file symbol header", lib.File)
+               return
+       }
+       if arhdr.name != pkgdef {
+               Errorf(nil, "%s: missing package data entry", lib.File)
+               return
+       }
+
+       h := sha1.New()
+
+       // To compute the hash of a package, we hash the first line of
+       // __.PKGDEF (which contains the toolchain version and any
+       // GOEXPERIMENT flags) and the export data (which is between
+       // the first two occurrences of "\n$$").
+
+       pkgDefBytes := make([]byte, atolwhex(arhdr.size))
+       _, err = io.ReadFull(f, pkgDefBytes)
+       if err != nil {
+               Errorf(nil, "%s: error reading package data: %v", lib.File, err)
+               return
+       }
+       firstEOL := bytes.IndexByte(pkgDefBytes, '\n')
+       if firstEOL < 0 {
+               Errorf(nil, "cannot parse package data of %s for hash generation, no newline found", lib.File)
+               return
+       }
+       firstDoubleDollar := bytes.Index(pkgDefBytes, []byte("\n$$"))
+       if firstDoubleDollar < 0 {
+               Errorf(nil, "cannot parse package data of %s for hash generation, no \\n$$ found", lib.File)
+               return
+       }
+       secondDoubleDollar := bytes.Index(pkgDefBytes[firstDoubleDollar+1:], []byte("\n$$"))
+       if secondDoubleDollar < 0 {
+               Errorf(nil, "cannot parse package data of %s for hash generation, only one \\n$$ found", lib.File)
+               return
+       }
+       h.Write(pkgDefBytes[0:firstEOL])
+       h.Write(pkgDefBytes[firstDoubleDollar : firstDoubleDollar+secondDoubleDollar])
+       lib.Hash = hex.EncodeToString(h.Sum(nil))
+}
+
+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)
+               }
+
+               // Ideally, we'd check that *all* object files within
+               // the archive were marked safe, but here we settle
+               // for *any*.
+               //
+               // Historically, cmd/link only checked the __.PKGDEF
+               // file, which in turn came from the first object
+               // file, typically produced by cmd/compile. The
+               // remaining object files are normally produced by
+               // cmd/asm, which doesn't support marking files as
+               // safe anyway. So at least in practice, this matches
+               // how safe mode has always worked.
+               if *flagU && !lib.Safe {
+                       Exitf("%s: load of unsafe package %s", lib.File, pkg)
+               }
+       }()
+
+       for i := 0; i < len(ARMAG); i++ {
+               if c, err := f.ReadByte(); err == nil && c == ARMAG[i] {
+                       continue
+               }
+
+               /* load it as a regular file */
+               l := f.MustSeek(0, 2)
+               f.MustSeek(0, 0)
+               ldobj(ctxt, f, lib, l, lib.File, lib.File)
+               return
+       }
+
+       /*
+        * load all the object files from the archive now.
+        * this gives us sequential file access and keeps us
+        * from needing to come back later to pick up more
+        * objects.  it breaks the usual C archive model, but
+        * this is Go, not C.  the common case in Go is that
+        * we need to load all the objects, and then we throw away
+        * the individual symbols that are unused.
+        *
+        * loading every object will also make it possible to
+        * load foreign objects not referenced by __.PKGDEF.
+        */
+       var arhdr ArHdr
+       off := f.Offset()
+       for {
+               l := nextar(f, off, &arhdr)
+               if l == 0 {
+                       break
+               }
+               if l < 0 {
+                       Exitf("%s: malformed archive", lib.File)
+               }
+               off += l
+
+               // __.PKGDEF isn't a real Go object file, and it's
+               // absent in -linkobj builds anyway. Skipping it
+               // ensures consistency between -linkobj and normal
+               // build modes.
+               if arhdr.name == pkgdef {
+                       continue
+               }
+
+               // Skip other special (non-object-file) sections that
+               // build tools may have added. Such sections must have
+               // short names so that the suffix is not truncated.
+               if len(arhdr.name) < 16 {
+                       if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" {
+                               continue
+                       }
+               }
+
+               pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name)
+               l = atolwhex(arhdr.size)
+               ldobj(ctxt, f, lib, l, pname, lib.File)
+       }
+}
+
+type Hostobj struct {
+       ld     func(*Link, *bio.Reader, string, int64, string)
+       pkg    string
+       pn     string
+       file   string
+       off    int64
+       length int64
+}
+
+var hostobj []Hostobj
+
+// These packages can use internal linking mode.
+// Others trigger external mode.
+var internalpkg = []string{
+       "crypto/x509",
+       "net",
+       "os/user",
+       "runtime/cgo",
+       "runtime/race",
+       "runtime/msan",
+}
+
+func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj {
+       isinternal := false
+       for _, intpkg := range internalpkg {
+               if pkg == intpkg {
+                       isinternal = true
+                       break
+               }
+       }
+
+       // DragonFly declares errno with __thread, which results in a symbol
+       // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not
+       // currently know how to handle TLS relocations, hence we have to
+       // force external linking for any libraries that link in code that
+       // uses errno. This can be removed if the Go linker ever supports
+       // these relocation types.
+       if headType == objabi.Hdragonfly {
+               if pkg == "net" || pkg == "os/user" {
+                       isinternal = false
+               }
+       }
+
+       if !isinternal {
+               externalobj = true
+       }
+
+       hostobj = append(hostobj, Hostobj{})
+       h := &hostobj[len(hostobj)-1]
+       h.ld = ld
+       h.pkg = pkg
+       h.pn = pn
+       h.file = file
+       h.off = f.Offset()
+       h.length = length
+       return h
+}
+
+func hostobjs(ctxt *Link) {
+       if ctxt.LinkMode != LinkInternal {
+               return
+       }
+       var h *Hostobj
+
+       for i := 0; i < len(hostobj); i++ {
+               h = &hostobj[i]
+               f, err := bio.Open(h.file)
+               if err != nil {
+                       Exitf("cannot reopen %s: %v", h.pn, err)
+               }
+
+               f.MustSeek(h.off, 0)
+               h.ld(ctxt, f, h.pkg, h.length, h.pn)
+               f.Close()
+       }
+}
+
+func hostlinksetup(ctxt *Link) {
+       if ctxt.LinkMode != LinkExternal {
+               return
+       }
+
+       // For external link, record that we need to tell the external linker -s,
+       // and turn off -s internally: the external linker needs the symbol
+       // information for its final link.
+       debug_s = *FlagS
+       *FlagS = false
+
+       // create temporary directory and arrange cleanup
+       if *flagTmpdir == "" {
+               dir, err := ioutil.TempDir("", "go-link-")
+               if err != nil {
+                       log.Fatal(err)
+               }
+               *flagTmpdir = dir
+               ownTmpDir = true
+               AtExit(func() {
+                       ctxt.Out.f.Close()
+                       os.RemoveAll(*flagTmpdir)
+               })
+       }
+
+       // change our output to temporary object file
+       ctxt.Out.f.Close()
+       mayberemoveoutfile()
+
+       p := filepath.Join(*flagTmpdir, "go.o")
+       var err error
+       f, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
+       if err != nil {
+               Exitf("cannot create %s: %v", p, err)
+       }
+
+       ctxt.Out.w = bufio.NewWriter(f)
+       ctxt.Out.f = f
+       ctxt.Out.off = 0
+}
+
+// hostobjCopy creates a copy of the object files in hostobj in a
+// temporary directory.
+func hostobjCopy() (paths []string) {
+       var wg sync.WaitGroup
+       sema := make(chan struct{}, runtime.NumCPU()) // limit open file descriptors
+       for i, h := range hostobj {
+               h := h
+               dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i))
+               paths = append(paths, dst)
+
+               wg.Add(1)
+               go func() {
+                       sema <- struct{}{}
+                       defer func() {
+                               <-sema
+                               wg.Done()
+                       }()
+                       f, err := os.Open(h.file)
+                       if err != nil {
+                               Exitf("cannot reopen %s: %v", h.pn, err)
+                       }
+                       defer f.Close()
+                       if _, err := f.Seek(h.off, 0); err != nil {
+                               Exitf("cannot seek %s: %v", h.pn, err)
+                       }
+
+                       w, err := os.Create(dst)
+                       if err != nil {
+                               Exitf("cannot create %s: %v", dst, err)
+                       }
+                       if _, err := io.CopyN(w, f, h.length); err != nil {
+                               Exitf("cannot write %s: %v", dst, err)
+                       }
+                       if err := w.Close(); err != nil {
+                               Exitf("cannot close %s: %v", dst, err)
+                       }
+               }()
+       }
+       wg.Wait()
+       return paths
+}
+
+// writeGDBLinkerScript creates gcc linker script file in temp
+// directory. writeGDBLinkerScript returns created file path.
+// The script is used to work around gcc bug
+// (see https://golang.org/issue/20183 for details).
+func writeGDBLinkerScript() string {
+       name := "fix_debug_gdb_scripts.ld"
+       path := filepath.Join(*flagTmpdir, name)
+       src := `SECTIONS
+{
+  .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) :
+  {
+    *(.debug_gdb_scripts)
+  }
+}
+INSERT AFTER .debug_types;
+`
+       err := ioutil.WriteFile(path, []byte(src), 0666)
+       if err != nil {
+               Errorf(nil, "WriteFile %s failed: %v", name, err)
+       }
+       return path
+}
+
+// archive builds a .a archive from the hostobj object files.
+func (ctxt *Link) archive() {
+       if ctxt.BuildMode != BuildModeCArchive {
+               return
+       }
+
+       exitIfErrors()
+
+       if *flagExtar == "" {
+               *flagExtar = "ar"
+       }
+
+       mayberemoveoutfile()
+
+       // Force the buffer to flush here so that external
+       // tools will see a complete file.
+       ctxt.Out.Flush()
+       if err := ctxt.Out.f.Close(); err != nil {
+               Exitf("close: %v", err)
+       }
+       ctxt.Out.f = nil
+
+       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")
+               }
+       }
+
+       switch ctxt.HeadType {
+       case objabi.Hdarwin:
+               if machoPlatform == PLATFORM_MACOS {
+                       // -headerpad is incompatible with -fembed-bitcode.
+                       argv = append(argv, "-Wl,-headerpad,1144")
+               }
+               if ctxt.DynlinkingGo() && !ctxt.Arch.InFamily(sys.ARM, sys.ARM64) {
+                       argv = append(argv, "-Wl,-flat_namespace")
+               }
+       case objabi.Hopenbsd:
+               argv = append(argv, "-Wl,-nopie")
+       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")
+       }
+
+       switch ctxt.BuildMode {
+       case BuildModeExe:
+               if ctxt.HeadType == objabi.Hdarwin {
+                       if machoPlatform == PLATFORM_MACOS {
+                               argv = append(argv, "-Wl,-no_pie")
+                               argv = append(argv, "-Wl,-pagezero_size,4000000")
+                       }
+               }
+       case BuildModePIE:
+               switch ctxt.HeadType {
+               case objabi.Hdarwin, objabi.Haix:
+               case objabi.Hwindows:
+                       // Enable ASLR.
+                       argv = append(argv, "-Wl,--dynamicbase")
+                       // enable high-entropy ASLR on 64-bit.
+                       if ctxt.Arch.PtrSize >= 8 {
+                               argv = append(argv, "-Wl,--high-entropy-va")
+                       }
+                       // Work around binutils limitation that strips relocation table for dynamicbase.
+                       // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
+                       argv = append(argv, "-Wl,--export-all-symbols")
+               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")
+                       if ctxt.Arch.Family != sys.AMD64 {
+                               argv = append(argv, "-Wl,-read_only_relocs,suppress")
+                       }
+               } else {
+                       // ELF.
+                       argv = append(argv, "-Wl,-Bsymbolic")
+                       if ctxt.UseRelro() {
+                               argv = append(argv, "-Wl,-z,relro")
+                       }
+                       argv = append(argv, "-shared")
+                       if ctxt.HeadType != objabi.Hwindows {
+                               // Pass -z nodelete to mark the shared library as
+                               // non-closeable: a dlclose will do nothing.
+                               argv = append(argv, "-Wl,-z,nodelete")
+                       }
+               }
+       case BuildModeShared:
+               if ctxt.UseRelro() {
+                       argv = append(argv, "-Wl,-z,relro")
+               }
+               argv = append(argv, "-shared")
+       case BuildModePlugin:
+               if ctxt.HeadType == objabi.Hdarwin {
+                       argv = append(argv, "-dynamiclib")
+               } else {
+                       if ctxt.UseRelro() {
+                               argv = append(argv, "-Wl,-z,relro")
+                       }
+                       argv = append(argv, "-shared")
+               }
+       }
+
+       if ctxt.IsELF && ctxt.DynlinkingGo() {
+               // We force all symbol resolution to be done at program startup
+               // because lazy PLT resolution can use large amounts of stack at
+               // times we cannot allow it to do so.
+               argv = append(argv, "-Wl,-znow")
+
+               // Do not let the host linker generate COPY relocations. These
+               // can move symbols out of sections that rely on stable offsets
+               // from the beginning of the section (like sym.STYPE).
+               argv = append(argv, "-Wl,-znocopyreloc")
+
+               if ctxt.Arch.InFamily(sys.ARM, sys.ARM64) && objabi.GOOS == "linux" {
+                       // On ARM, the GNU linker will generate COPY relocations
+                       // even with -znocopyreloc set.
+                       // https://sourceware.org/bugzilla/show_bug.cgi?id=19962
+                       //
+                       // On ARM64, the GNU linker will fail instead of
+                       // generating COPY relocations.
+                       //
+                       // In both cases, switch to gold.
+                       argv = append(argv, "-fuse-ld=gold")
+
+                       // If gold is not installed, gcc will silently switch
+                       // back to ld.bfd. So we parse the version information
+                       // and provide a useful error if gold is missing.
+                       cmd := exec.Command(*flagExtld, "-fuse-ld=gold", "-Wl,--version")
+                       if out, err := cmd.CombinedOutput(); err == nil {
+                               if !bytes.Contains(out, []byte("GNU gold")) {
+                                       log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out)
+                               }
+                       }
+               }
+       }
+
+       if ctxt.Arch.Family == sys.ARM64 && objabi.GOOS == "freebsd" {
+               // Switch to ld.bfd on freebsd/arm64.
+               argv = append(argv, "-fuse-ld=bfd")
+
+               // Provide a useful error if ld.bfd is missing.
+               cmd := exec.Command(*flagExtld, "-fuse-ld=bfd", "-Wl,--version")
+               if out, err := cmd.CombinedOutput(); err == nil {
+                       if !bytes.Contains(out, []byte("GNU ld")) {
+                               log.Fatalf("ARM64 external linker must be ld.bfd (issue #35197), please install devel/binutils")
+                       }
+               }
+       }
+
+       if ctxt.IsELF && len(buildinfo) > 0 {
+               argv = append(argv, fmt.Sprintf("-Wl,--build-id=0x%x", buildinfo))
+       }
+
+       // On Windows, given -o foo, GCC will append ".exe" to produce
+       // "foo.exe".  We have decided that we want to honor the -o
+       // option. To make this work, we append a '.' so that GCC
+       // will decide that the file already has an extension. We
+       // only want to do this when producing a Windows output file
+       // on a Windows host.
+       outopt := *flagOutfile
+       if objabi.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" {
+               outopt += "."
+       }
+       argv = append(argv, "-o")
+       argv = append(argv, outopt)
+
+       if rpath.val != "" {
+               argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val))
+       }
+
+       // Force global symbols to be exported for dlopen, etc.
+       if ctxt.IsELF {
+               argv = append(argv, "-rdynamic")
+       }
+       if ctxt.HeadType == objabi.Haix {
+               fileName := xcoffCreateExportFile(ctxt)
+               argv = append(argv, "-Wl,-bE:"+fileName)
+       }
+
+       if strings.Contains(argv[0], "clang") {
+               argv = append(argv, "-Qunused-arguments")
+       }
+
+       const compressDWARF = "-Wl,--compress-debug-sections=zlib-gnu"
+       if ctxt.compressDWARF && linkerFlagSupported(argv[0], compressDWARF) {
+               argv = append(argv, compressDWARF)
+       }
+
+       argv = append(argv, filepath.Join(*flagTmpdir, "go.o"))
+       argv = append(argv, hostobjCopy()...)
+       if ctxt.HeadType == objabi.Haix {
+               // We want to have C files after Go files to remove
+               // trampolines csects made by ld.
+               argv = append(argv, "-nostartfiles")
+               argv = append(argv, "/lib/crt0_64.o")
+
+               extld := ctxt.extld()
+               // Get starting files.
+               getPathFile := func(file string) string {
+                       args := []string{"-maix64", "--print-file-name=" + file}
+                       out, err := exec.Command(extld, args...).CombinedOutput()
+                       if err != nil {
+                               log.Fatalf("running %s failed: %v\n%s", extld, err, out)
+                       }
+                       return strings.Trim(string(out), "\n")
+               }
+               argv = append(argv, getPathFile("crtcxa.o"))
+               argv = append(argv, getPathFile("crtdbase.o"))
+       }
+
+       if ctxt.linkShared {
+               seenDirs := make(map[string]bool)
+               seenLibs := make(map[string]bool)
+               addshlib := func(path string) {
+                       dir, base := filepath.Split(path)
+                       if !seenDirs[dir] {
+                               argv = append(argv, "-L"+dir)
+                               if !rpath.set {
+                                       argv = append(argv, "-Wl,-rpath="+dir)
+                               }
+                               seenDirs[dir] = true
+                       }
+                       base = strings.TrimSuffix(base, ".so")
+                       base = strings.TrimPrefix(base, "lib")
+                       if !seenLibs[base] {
+                               argv = append(argv, "-l"+base)
+                               seenLibs[base] = true
+                       }
+               }
+               for _, shlib := range ctxt.Shlibs {
+                       addshlib(shlib.Path)
+                       for _, dep := range shlib.Deps {
+                               if dep == "" {
+                                       continue
+                               }
+                               libpath := findshlib(ctxt, dep)
+                               if libpath != "" {
+                                       addshlib(libpath)
+                               }
+                       }
+               }
+       }
+
+       // clang, unlike GCC, passes -rdynamic to the linker
+       // even when linking with -static, causing a linker
+       // error when using GNU ld. So take out -rdynamic if
+       // we added it. We do it in this order, rather than
+       // only adding -rdynamic later, so that -*extldflags
+       // can override -rdynamic without using -static.
+       checkStatic := func(arg string) {
+               if ctxt.IsELF && arg == "-static" {
+                       for i := range argv {
+                               if argv[i] == "-rdynamic" {
+                                       argv[i] = "-static"
+                               }
+                       }
+               }
+       }
+
+       for _, p := range ldflag {
+               argv = append(argv, p)
+               checkStatic(p)
+       }
+
+       // When building a program with the default -buildmode=exe the
+       // gc compiler generates code requires DT_TEXTREL in a
+       // position independent executable (PIE). On systems where the
+       // toolchain creates PIEs by default, and where DT_TEXTREL
+       // does not work, the resulting programs will not run. See
+       // issue #17847. To avoid this problem pass -no-pie to the
+       // toolchain if it is supported.
+       if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared {
+               // GCC uses -no-pie, clang uses -nopie.
+               for _, nopie := range []string{"-no-pie", "-nopie"} {
+                       if linkerFlagSupported(argv[0], nopie) {
+                               argv = append(argv, nopie)
+                               break
+                       }
+               }
+       }
+
+       for _, p := range strings.Fields(*flagExtldflags) {
+               argv = append(argv, p)
+               checkStatic(p)
+       }
+       if ctxt.HeadType == objabi.Hwindows {
+               // use gcc linker script to work around gcc bug
+               // (see https://golang.org/issue/20183 for details).
+               p := writeGDBLinkerScript()
+               argv = append(argv, "-Wl,-T,"+p)
+               // libmingw32 and libmingwex have some inter-dependencies,
+               // so must use linker groups.
+               argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group")
+               argv = append(argv, peimporteddlls()...)
+       }
+
+       if ctxt.Debugvlog != 0 {
+               ctxt.Logf("host link:")
+               for _, v := range argv {
+                       ctxt.Logf(" %q", v)
+               }
+               ctxt.Logf("\n")
+       }
+
+       out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput()
+       if err != nil {
+               Exitf("running %s failed: %v\n%s", argv[0], err, out)
+       }
+
+       // Filter out useless linker warnings caused by bugs outside Go.
+       // See also cmd/go/internal/work/exec.go's gccld method.
+       var save [][]byte
+       var skipLines int
+       for _, line := range bytes.SplitAfter(out, []byte("\n")) {
+               // golang.org/issue/26073 - Apple Xcode bug
+               if bytes.Contains(line, []byte("ld: warning: text-based stub file")) {
+                       continue
+               }
+
+               if skipLines > 0 {
+                       skipLines--
+                       continue
+               }
+
+               // Remove TOC overflow warning on AIX.
+               if bytes.Contains(line, []byte("ld: 0711-783")) {
+                       skipLines = 2
+                       continue
+               }
+
+               save = append(save, line)
+       }
+       out = bytes.Join(save, nil)
+
+       if len(out) > 0 {
+               // always print external output even if the command is successful, so that we don't
+               // swallow linker warnings (see https://golang.org/issue/17935).
+               ctxt.Logf("%s", out)
+       }
+
+       if !*FlagS && !*FlagW && !debug_s && ctxt.HeadType == objabi.Hdarwin {
+               dsym := filepath.Join(*flagTmpdir, "go.dwarf")
+               if out, err := exec.Command("dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil {
+                       Exitf("%s: running dsymutil 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)
+               }
+               // Only macOS supports unmapped segments such as our __DWARF segment.
+               if machoPlatform == PLATFORM_MACOS {
+                       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)
+                       }
+               }
+       }
+}
+
+var createTrivialCOnce sync.Once
+
+func linkerFlagSupported(linker, flag string) bool {
+       createTrivialCOnce.Do(func() {
+               src := filepath.Join(*flagTmpdir, "trivial.c")
+               if err := ioutil.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil {
+                       Errorf(nil, "WriteFile trivial.c failed: %v", err)
+               }
+       })
+
+       flagsWithNextArgSkip := []string{
+               "-F",
+               "-l",
+               "-L",
+               "-framework",
+               "-Wl,-framework",
+               "-Wl,-rpath",
+               "-Wl,-undefined",
+       }
+       flagsWithNextArgKeep := []string{
+               "-arch",
+               "-isysroot",
+               "--sysroot",
+               "-target",
+       }
+       prefixesToKeep := []string{
+               "-f",
+               "-m",
+               "-p",
+               "-Wl,",
+               "-arch",
+               "-isysroot",
+               "--sysroot",
+               "-target",
+       }
+
+       var flags []string
+       keep := false
+       skip := false
+       extldflags := strings.Fields(*flagExtldflags)
+       for _, f := range append(extldflags, ldflag...) {
+               if keep {
+                       flags = append(flags, f)
+                       keep = false
+               } else if skip {
+                       skip = false
+               } else if f == "" || f[0] != '-' {
+               } else if contains(flagsWithNextArgSkip, f) {
+                       skip = true
+               } else if contains(flagsWithNextArgKeep, f) {
+                       flags = append(flags, f)
+                       keep = true
+               } else {
+                       for _, p := range prefixesToKeep {
+                               if strings.HasPrefix(f, p) {
+                                       flags = append(flags, f)
+                                       break
+                               }
+                       }
+               }
+       }
+
+       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, sys.S390X:
+               return []string{"-m64"}
+       case sys.ARM:
+               return []string{"-marm"}
+       case sys.ARM64:
+               // nothing needed
+       case sys.MIPS64:
+               return []string{"-mabi=64"}
+       case sys.MIPS:
+               return []string{"-mabi=32"}
+       case sys.PPC64:
+               if objabi.GOOS == "aix" {
+                       return []string{"-maix64"}
+               } else {
+                       return []string{"-m64"}
+               }
+
+       }
+       return nil
+}
+
+// ldobj loads an input object. If it is a host object (an object
+// compiled by a non-Go compiler) it returns the Hostobj pointer. If
+// it is a Go object, it returns nil.
+func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj {
+       pkg := objabi.PathToPrefix(lib.Pkg)
+
+       eof := f.Offset() + length
+       start := f.Offset()
+       c1 := bgetc(f)
+       c2 := bgetc(f)
+       c3 := bgetc(f)
+       c4 := bgetc(f)
+       f.MustSeek(start, 0)
+
+       unit := &sym.CompilationUnit{Lib: lib}
+       lib.Units = append(lib.Units, unit)
+
+       magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
+       if magic == 0x7f454c46 { // \x7F E L F
+               if *flagNewobj {
+                       ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+                               textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, 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)
+               } else {
+                       ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+                               textp, flags, err := loadelf.LoadOld(ctxt.Arch, ctxt.Syms, 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 {
+               if *flagNewobj {
+                       ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+                               textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, 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)
+               } else {
+                       ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+                               textp, err := loadmacho.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+                               if err != nil {
+                                       Errorf(nil, "%v", err)
+                                       return
+                               }
+                               ctxt.Textp = append(ctxt.Textp, textp...)
+                       }
+                       return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
+               }
+       }
+
+       if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 {
+               if *flagNewobj {
+                       ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+                               textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+                               if err != nil {
+                                       Errorf(nil, "%v", err)
+                                       return
+                               }
+                               if rsrc != nil {
+                                       setpersrc(ctxt, rsrc)
+                               }
+                               ctxt.Textp = append(ctxt.Textp, textp...)
+                       }
+                       return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
+               } else {
+                       ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+                               textp, rsrc, err := loadpe.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+                               if err != nil {
+                                       Errorf(nil, "%v", err)
+                                       return
+                               }
+                               if rsrc != nil {
+                                       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) {
+               if *flagNewobj {
+                       ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+                               textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, 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)
+               } else {
+                       ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+                               textp, err := loadxcoff.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+                               if err != nil {
+                                       Errorf(nil, "%v", err)
+                                       return
+                               }
+                               ctxt.Textp = append(ctxt.Textp, textp...)
+                       }
+                       return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
+               }
+       }
+
+       /* check the header */
+       line, err := f.ReadString('\n')
+       if err != nil {
+               Errorf(nil, "truncated object file: %s: %v", pn, err)
+               return nil
+       }
+
+       if !strings.HasPrefix(line, "go object ") {
+               if strings.HasSuffix(pn, ".go") {
+                       Exitf("%s: uncompiled .go source file", pn)
+                       return nil
+               }
+
+               if line == ctxt.Arch.Name {
+                       // old header format: just $GOOS
+                       Errorf(nil, "%s: stale object file", pn)
+                       return nil
+               }
+
+               Errorf(nil, "%s: not an object file", pn)
+               return nil
+       }
+
+       // First, check that the basic GOOS, GOARCH, and Version match.
+       t := fmt.Sprintf("%s %s %s ", objabi.GOOS, objabi.GOARCH, objabi.Version)
+
+       line = strings.TrimRight(line, "\n")
+       if !strings.HasPrefix(line[10:]+" ", t) && !*flagF {
+               Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], t)
+               return nil
+       }
+
+       // Second, check that longer lines match each other exactly,
+       // so that the Go compiler and write additional information
+       // that must be the same from run to run.
+       if len(line) >= len(t)+10 {
+               if theline == "" {
+                       theline = line[10:]
+               } else if theline != line[10:] {
+                       Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], theline)
+                       return nil
+               }
+       }
+
+       // Skip over exports and other info -- ends with \n!\n.
+       //
+       // Note: It's possible for "\n!\n" to appear within the binary
+       // package export data format. To avoid truncating the package
+       // definition prematurely (issue 21703), we keep track of
+       // how many "$$" delimiters we've seen.
+
+       import0 := f.Offset()
+
+       c1 = '\n' // the last line ended in \n
+       c2 = bgetc(f)
+       c3 = bgetc(f)
+       markers := 0
+       for {
+               if c1 == '\n' {
+                       if markers%2 == 0 && c2 == '!' && c3 == '\n' {
+                               break
+                       }
+                       if c2 == '$' && c3 == '$' {
+                               markers++
+                       }
+               }
+
+               c1 = c2
+               c2 = c3
+               c3 = bgetc(f)
+               if c3 == -1 {
+                       Errorf(nil, "truncated object file: %s", pn)
+                       return nil
+               }
+       }
+
+       import1 := f.Offset()
+
+       f.MustSeek(import0, 0)
+       ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n
+       f.MustSeek(import1, 0)
+
+       flags := 0
+       switch *FlagStrictDups {
+       case 0:
+               break
+       case 1:
+               flags = objfile.StrictDupsWarnFlag
+       case 2:
+               flags = objfile.StrictDupsErrFlag
+       default:
+               log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
+       }
+       var c int
+       if *flagNewobj {
+               ctxt.loader.Preload(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
+       } else {
+               c = objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
+       }
+       strictDupMsgCount += c
+       addImports(ctxt, lib, pn)
+       return nil
+}
+
+func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte {
+       data := make([]byte, sym.Size)
+       sect := f.Sections[sym.Section]
+       if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE {
+               Errorf(nil, "reading %s from non-data section", sym.Name)
+       }
+       n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr))
+       if uint64(n) != sym.Size {
+               Errorf(nil, "reading contents of %s: %v", sym.Name, err)
+       }
+       return data
+}
+
+func readwithpad(r io.Reader, sz int32) ([]byte, error) {
+       data := make([]byte, Rnd(int64(sz), 4))
+       _, err := io.ReadFull(r, data)
+       if err != nil {
+               return nil, err
+       }
+       data = data[:sz]
+       return data, nil
+}
+
+func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) {
+       for _, sect := range f.Sections {
+               if sect.Type != elf.SHT_NOTE {
+                       continue
+               }
+               r := sect.Open()
+               for {
+                       var namesize, descsize, noteType int32
+                       err := binary.Read(r, f.ByteOrder, &namesize)
+                       if err != nil {
+                               if err == io.EOF {
+                                       break
+                               }
+                               return nil, fmt.Errorf("read namesize failed: %v", err)
+                       }
+                       err = binary.Read(r, f.ByteOrder, &descsize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read descsize failed: %v", err)
+                       }
+                       err = binary.Read(r, f.ByteOrder, &noteType)
+                       if err != nil {
+                               return nil, fmt.Errorf("read type failed: %v", err)
+                       }
+                       noteName, err := readwithpad(r, namesize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read name failed: %v", err)
+                       }
+                       desc, err := readwithpad(r, descsize)
+                       if err != nil {
+                               return nil, fmt.Errorf("read desc failed: %v", err)
+                       }
+                       if string(name) == string(noteName) && typ == noteType {
+                               return desc, nil
+                       }
+               }
+       }
+       return nil, nil
+}
+
+func findshlib(ctxt *Link, shlib string) string {
+       if filepath.IsAbs(shlib) {
+               return shlib
+       }
+       for _, libdir := range ctxt.Libdir {
+               libpath := filepath.Join(libdir, shlib)
+               if _, err := os.Stat(libpath); err == nil {
+                       return libpath
+               }
+       }
+       Errorf(nil, "cannot find shared library: %s", shlib)
+       return ""
+}
+
+func ldshlibsyms(ctxt *Link, shlib string) {
+       var libpath string
+       if filepath.IsAbs(shlib) {
+               libpath = shlib
+               shlib = filepath.Base(shlib)
+       } else {
+               libpath = findshlib(ctxt, shlib)
+               if libpath == "" {
+                       return
+               }
+       }
+       for _, processedlib := range ctxt.Shlibs {
+               if processedlib.Path == libpath {
+                       return
+               }
+       }
+       if ctxt.Debugvlog > 1 {
+               ctxt.Logf("ldshlibsyms: found library with name %s at %s\n", shlib, libpath)
+       }
+
+       f, err := elf.Open(libpath)
+       if err != nil {
+               Errorf(nil, "cannot open shared library: %s", libpath)
+               return
+       }
+       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
+       }
+       gcdataLocations := make(map[uint64]*sym.Symbol)
+       for _, elfsym := range syms {
+               if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION {
+                       continue
+               }
+
+               // Symbols whose names start with "type." are compiler
+               // generated, so make functions with that prefix internal.
+               ver := 0
+               if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type.") {
+                       ver = sym.SymVerABIInternal
+               }
+
+               var lsym *sym.Symbol
+               if *flagNewobj {
+                       i := ctxt.loader.AddExtSym(elfsym.Name, ver)
+                       if i == 0 {
+                               continue
+                       }
+                       lsym = ctxt.Syms.Newsym(elfsym.Name, ver)
+                       ctxt.loader.Syms[i] = lsym
+               } else {
+                       lsym = ctxt.Syms.Lookup(elfsym.Name, ver)
+               }
+               // Because loadlib above loads all .a files before loading any shared
+               // libraries, any non-dynimport symbols we find that duplicate symbols
+               // already loaded should be ignored (the symbols from the .a files
+               // "win").
+               if lsym.Type != 0 && lsym.Type != sym.SDYNIMPORT {
+                       continue
+               }
+               lsym.Type = sym.SDYNIMPORT
+               lsym.SetElfType(elf.ST_TYPE(elfsym.Info))
+               lsym.Size = int64(elfsym.Size)
+               if elfsym.Section != elf.SHN_UNDEF {
+                       // Set .File for the library that actually defines the symbol.
+                       lsym.File = libpath
+                       // The decodetype_* functions in decodetype.go need access to
+                       // the type data.
+                       if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") {
+                               lsym.P = readelfsymboldata(ctxt, f, &elfsym)
+                               gcdataLocations[elfsym.Value+2*uint64(ctxt.Arch.PtrSize)+8+1*uint64(ctxt.Arch.PtrSize)] = lsym
+                       }
+               }
+               // For function symbols, we don't know what ABI is
+               // available, so alias it under both ABIs.
+               //
+               // TODO(austin): This is almost certainly wrong once
+               // the ABIs are actually different. We might have to
+               // mangle Go function names in the .so to include the
+               // ABI.
+               if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
+                       var alias *sym.Symbol
+                       if *flagNewobj {
+                               i := ctxt.loader.AddExtSym(elfsym.Name, sym.SymVerABIInternal)
+                               if i == 0 {
+                                       continue
+                               }
+                               alias = ctxt.Syms.Newsym(elfsym.Name, sym.SymVerABIInternal)
+                               ctxt.loader.Syms[i] = alias
+                       } else {
+                               alias = ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal)
+                       }
+                       if alias.Type != 0 {
+                               continue
+                       }
+                       alias.Type = sym.SABIALIAS
+                       alias.R = []sym.Reloc{{Sym: lsym}}
+               }
+       }
+       gcdataAddresses := make(map[*sym.Symbol]uint64)
+       if ctxt.Arch.Family == sys.ARM64 {
+               for _, sect := range f.Sections {
+                       if sect.Type == elf.SHT_RELA {
+                               var rela elf.Rela64
+                               rdr := sect.Open()
+                               for {
+                                       err := binary.Read(rdr, f.ByteOrder, &rela)
+                                       if err == io.EOF {
+                                               break
+                                       } else if err != nil {
+                                               Errorf(nil, "reading relocation failed %v", err)
+                                               return
+                                       }
+                                       t := elf.R_AARCH64(rela.Info & 0xffff)
+                                       if t != elf.R_AARCH64_RELATIVE {
+                                               continue
+                                       }
+                                       if lsym, ok := gcdataLocations[rela.Off]; ok {
+                                               gcdataAddresses[lsym] = uint64(rela.Addend)
+                                       }
+                               }
+                       }
+               }
+       }
+
+       ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdataAddresses: gcdataAddresses})
+}
+
+func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section {
+       sect := new(sym.Section)
+       sect.Rwx = uint8(rwx)
+       sect.Name = name
+       sect.Seg = seg
+       sect.Align = int32(arch.PtrSize) // everything is at least pointer-aligned
+       seg.Sections = append(seg.Sections, sect)
+       return sect
+}
+
+type chain struct {
+       sym   *sym.Symbol
+       up    *chain
+       limit int // limit on entry to sym
+}
+
+var morestack *sym.Symbol
+
+// TODO: Record enough information in new object files to
+// allow stack checks here.
+
+func haslinkregister(ctxt *Link) bool {
+       return ctxt.FixedFrameSize() != 0
+}
+
+func callsize(ctxt *Link) int {
+       if haslinkregister(ctxt) {
+               return 0
+       }
+       return ctxt.Arch.RegSize
+}
+
+func (ctxt *Link) dostkcheck() {
+       var ch chain
+
+       morestack = ctxt.Syms.Lookup("runtime.morestack", 0)
+
+       // Every splitting function ensures that there are at least StackLimit
+       // bytes available below SP when the splitting prologue finishes.
+       // If the splitting function calls F, then F begins execution with
+       // at least StackLimit - callsize() bytes available.
+       // Check that every function behaves correctly with this amount
+       // of stack, following direct calls in order to piece together chains
+       // of non-splitting functions.
+       ch.up = nil
+
+       ch.limit = objabi.StackLimit - callsize(ctxt)
+       if objabi.GOARCH == "arm64" {
+               // need extra 8 bytes below SP to save FP
+               ch.limit -= 8
+       }
+
+       // Check every function, but do the nosplit functions in a first pass,
+       // to make the printed failure chains as short as possible.
+       for _, s := range ctxt.Textp {
+               // runtime.racesymbolizethunk is called from gcc-compiled C
+               // code running on the operating system thread stack.
+               // It uses more than the usual amount of stack but that's okay.
+               if s.Name == "runtime.racesymbolizethunk" {
+                       continue
+               }
+
+               if s.Attr.NoSplit() {
+                       ch.sym = s
+                       stkcheck(ctxt, &ch, 0)
+               }
+       }
+
+       for _, s := range ctxt.Textp {
+               if !s.Attr.NoSplit() {
+                       ch.sym = s
+                       stkcheck(ctxt, &ch, 0)
+               }
+       }
+}
+
+func stkcheck(ctxt *Link, up *chain, depth int) int {
+       limit := up.limit
+       s := up.sym
+
+       // Don't duplicate work: only need to consider each
+       // function at top of safe zone once.
+       top := limit == objabi.StackLimit-callsize(ctxt)
+       if top {
+               if s.Attr.StackCheck() {
+                       return 0
+               }
+               s.Attr |= sym.AttrStackCheck
+       }
+
+       if depth > 500 {
+               Errorf(s, "nosplit stack check too deep")
+               stkbroke(ctxt, up, 0)
+               return -1
+       }
+
+       if s.Attr.External() || s.FuncInfo == nil {
+               // external function.
+               // should never be called directly.
+               // onlyctxt.Diagnose the direct caller.
+               // TODO(mwhudson): actually think about this.
+               // TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack.
+               // See the trampolines in src/runtime/sys_darwin_$ARCH.go.
+               if depth == 1 && s.Type != sym.SXREF && !ctxt.DynlinkingGo() &&
+                       ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin {
+                       //Errorf(s, "call to external function")
+               }
+               return -1
+       }
+
+       if limit < 0 {
+               stkbroke(ctxt, up, limit)
+               return -1
+       }
+
+       // morestack looks like it calls functions,
+       // but it switches the stack pointer first.
+       if s == morestack {
+               return 0
+       }
+
+       var ch chain
+       ch.up = up
+
+       if !s.Attr.NoSplit() {
+               // Ensure we have enough stack to call morestack.
+               ch.limit = limit - callsize(ctxt)
+               ch.sym = morestack
+               if stkcheck(ctxt, &ch, depth+1) < 0 {
+                       return -1
+               }
+               if !top {
+                       return 0
+               }
+               // Raise limit to allow frame.
+               locals := int32(0)
+               if s.FuncInfo != nil {
+                       locals = s.FuncInfo.Locals
+               }
+               limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize())
+       }
+
+       // Walk through sp adjustments in function, consuming relocs.
+       ri := 0
+
+       endr := len(s.R)
+       var ch1 chain
+       pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
+       var r *sym.Reloc
+       for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() {
+               // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
+
+               // Check stack size in effect for this span.
+               if int32(limit)-pcsp.Value < 0 {
+                       stkbroke(ctxt, up, int(int32(limit)-pcsp.Value))
+                       return -1
+               }
+
+               // Process calls in this span.
+               for ; ri < endr && uint32(s.R[ri].Off) < pcsp.NextPC; ri++ {
+                       r = &s.R[ri]
+                       switch {
+                       case r.Type.IsDirectCall():
+                               ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
+                               ch.sym = r.Sym
+                               if stkcheck(ctxt, &ch, depth+1) < 0 {
+                                       return -1
+                               }
+
+                       // Indirect call. Assume it is a call to a splitting function,
+                       // so we have to make sure it can call morestack.
+                       // Arrange the data structures to report both calls, so that
+                       // if there is an error, stkprint shows all the steps involved.
+                       case r.Type == objabi.R_CALLIND:
+                               ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
+                               ch.sym = nil
+                               ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue
+                               ch1.up = &ch
+                               ch1.sym = morestack
+                               if stkcheck(ctxt, &ch1, depth+2) < 0 {
+                                       return -1
+                               }
+                       }
+               }
+       }
+
+       return 0
+}
+
+func stkbroke(ctxt *Link, ch *chain, limit int) {
+       Errorf(ch.sym, "nosplit stack overflow")
+       stkprint(ctxt, ch, limit)
+}
+
+func stkprint(ctxt *Link, ch *chain, limit int) {
+       var name string
+
+       if ch.sym != nil {
+               name = ch.sym.Name
+               if ch.sym.Attr.NoSplit() {
+                       name += " (nosplit)"
+               }
+       } else {
+               name = "function pointer"
+       }
+
+       if ch.up == nil {
+               // top of chain.  ch->sym != nil.
+               if ch.sym.Attr.NoSplit() {
+                       fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name)
+               } else {
+                       fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name)
+               }
+       } else {
+               stkprint(ctxt, ch.up, ch.limit+callsize(ctxt))
+               if !haslinkregister(ctxt) {
+                       fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name)
+               }
+       }
+
+       if ch.limit != limit {
+               fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit)
+       }
+}
+
+func usage() {
+       fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n")
+       objabi.Flagprint(os.Stderr)
+       Exit(2)
+}
+
+type SymbolType int8
+
+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'
+)
+
+func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int64, *sym.Symbol)) {
+       // 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 := ctxt.Syms.Lookup("runtime.text", 0)
+       if s.Type == sym.STEXT {
+               // We've already included this symbol in ctxt.Textp
+               // if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin or
+               // on AIX with external linker.
+               // See data.go:/textaddress
+               if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+                       put(ctxt, s, s.Name, TextSym, s.Value, nil)
+               }
+       }
+
+       n := 0
+
+       // Generate base addresses for all text sections if there are multiple
+       for _, sect := range Segtext.Sections {
+               if n == 0 {
+                       n++
+                       continue
+               }
+               if sect.Name != ".text" || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+                       // On AIX, runtime.text.X are symbols already in the symtab.
+                       break
+               }
+               s = ctxt.Syms.ROLookup(fmt.Sprintf("runtime.text.%d", n), 0)
+               if s == nil {
+                       break
+               }
+               if s.Type == sym.STEXT {
+                       put(ctxt, s, s.Name, TextSym, s.Value, nil)
+               }
+               n++
+       }
+
+       s = ctxt.Syms.Lookup("runtime.etext", 0)
+       if s.Type == sym.STEXT {
+               // We've already included this symbol in ctxt.Textp
+               // if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin or
+               // on AIX with external linker.
+               // See data.go:/textaddress
+               if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+                       put(ctxt, s, s.Name, TextSym, s.Value, nil)
+               }
+       }
+
+       shouldBeInSymbolTable := func(s *sym.Symbol) bool {
+               if s.Attr.NotInSymbolTable() {
+                       return false
+               }
+               if ctxt.HeadType == objabi.Haix && s.Name == ".go.buildinfo" {
+                       // On AIX, .go.buildinfo must be in the symbol table as
+                       // it has relocations.
+                       return true
+               }
+               if (s.Name == "" || s.Name[0] == '.') && !s.IsFileLocal() && s.Name != ".rathole" && s.Name != ".TOC." {
+                       return false
+               }
+               return true
+       }
+
+       for _, s := range ctxt.Syms.Allsym {
+               if !shouldBeInSymbolTable(s) {
+                       continue
+               }
+               switch s.Type {
+               case sym.SCONST,
+                       sym.SRODATA,
+                       sym.SSYMTAB,
+                       sym.SPCLNTAB,
+                       sym.SINITARR,
+                       sym.SDATA,
+                       sym.SNOPTRDATA,
+                       sym.SELFROSECT,
+                       sym.SMACHOGOT,
+                       sym.STYPE,
+                       sym.SSTRING,
+                       sym.SGOSTRING,
+                       sym.SGOFUNC,
+                       sym.SGCBITS,
+                       sym.STYPERELRO,
+                       sym.SSTRINGRELRO,
+                       sym.SGOSTRINGRELRO,
+                       sym.SGOFUNCRELRO,
+                       sym.SGCBITSRELRO,
+                       sym.SRODATARELRO,
+                       sym.STYPELINK,
+                       sym.SITABLINK,
+                       sym.SWINDOWS:
+                       if !s.Attr.Reachable() {
+                               continue
+                       }
+                       put(ctxt, s, s.Name, DataSym, Symaddr(s), s.Gotype)
+
+               case sym.SBSS, sym.SNOPTRBSS, sym.SLIBFUZZER_EXTRA_COUNTER:
+                       if !s.Attr.Reachable() {
+                               continue
+                       }
+                       if len(s.P) > 0 {
+                               Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(s.P), s.Type, s.Attr.Special())
+                       }
+                       put(ctxt, s, s.Name, BSSSym, Symaddr(s), s.Gotype)
+
+               case sym.SUNDEFEXT:
+                       if ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Haix || ctxt.IsELF {
+                               put(ctxt, s, s.Name, UndefinedSym, s.Value, nil)
+                       }
+
+               case sym.SHOSTOBJ:
+                       if !s.Attr.Reachable() {
+                               continue
+                       }
+                       if ctxt.HeadType == objabi.Hwindows || ctxt.IsELF {
+                               put(ctxt, s, s.Name, UndefinedSym, s.Value, nil)
+                       }
+
+               case sym.SDYNIMPORT:
+                       if !s.Attr.Reachable() {
+                               continue
+                       }
+                       put(ctxt, s, s.Extname(), UndefinedSym, 0, nil)
+
+               case sym.STLSBSS:
+                       if ctxt.LinkMode == LinkExternal {
+                               put(ctxt, s, s.Name, TLSSym, Symaddr(s), s.Gotype)
+                       }
+               }
+       }
+
+       for _, s := range ctxt.Textp {
+               put(ctxt, s, s.Name, TextSym, s.Value, s.Gotype)
+
+               locals := int32(0)
+               if s.FuncInfo != nil {
+                       locals = s.FuncInfo.Locals
+               }
+               // NOTE(ality): acid can't produce a stack trace without .frame symbols
+               put(ctxt, nil, ".frame", FrameSym, int64(locals)+int64(ctxt.Arch.PtrSize), nil)
+
+               if s.FuncInfo == nil {
+                       continue
+               }
+       }
+
+       if ctxt.Debugvlog != 0 || *flagN {
+               ctxt.Logf("symsize = %d\n", uint32(Symsize))
+       }
+}
+
+func Symaddr(s *sym.Symbol) int64 {
+       if !s.Attr.Reachable() {
+               Errorf(s, "unreachable symbol in symaddr")
+       }
+       return s.Value
+}
+
+func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) {
+       s := ctxt.Syms.Lookup(p, 0)
+       s.Type = t
+       s.Value = v
+       s.Attr |= sym.AttrReachable
+       s.Attr |= sym.AttrSpecial
+       s.Attr |= sym.AttrLocal
+}
+
+func datoff(s *sym.Symbol, 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)
+       }
+       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)
+       }
+       s := ctxt.Syms.Lookup(a, 0)
+       if s.Type == 0 {
+               return *FlagTextAddr
+       }
+       if ctxt.HeadType != objabi.Haix && s.Type != sym.STEXT {
+               Errorf(s, "entry not text")
+       }
+       return s.Value
+}
+
+func undefsym(ctxt *Link, s *sym.Symbol) {
+       var r *sym.Reloc
+
+       for i := 0; i < len(s.R); i++ {
+               r = &s.R[i]
+               if r.Sym == nil { // happens for some external ARM relocs
+                       continue
+               }
+               // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+               // sense and should be removed when someone has thought about it properly.
+               if (r.Sym.Type == sym.Sxxx || r.Sym.Type == sym.SXREF) && !r.Sym.Attr.VisibilityHidden() {
+                       Errorf(s, "undefined: %q", r.Sym.Name)
+               }
+               if !r.Sym.Attr.Reachable() && r.Type != objabi.R_WEAKADDROFF {
+                       Errorf(s, "relocation target %q", r.Sym.Name)
+               }
+       }
+}
+
+func (ctxt *Link) undef() {
+       // undefsym performs checks (almost) identical to checks
+       // that report undefined relocations in relocsym.
+       // Both undefsym and relocsym can report same symbol as undefined,
+       // which results in error message duplication (see #10978).
+       //
+       // The undef is run after Arch.Asmb and could detect some
+       // programming errors there, but if object being linked is already
+       // failed with errors, it is better to avoid duplicated errors.
+       if nerrors > 0 {
+               return
+       }
+
+       for _, s := range ctxt.Textp {
+               undefsym(ctxt, s)
+       }
+       for _, s := range datap {
+               undefsym(ctxt, s)
+       }
+       if nerrors > 0 {
+               errorexit()
+       }
+}
+
+func (ctxt *Link) callgraph() {
+       if !*FlagC {
+               return
+       }
+
+       var i int
+       var r *sym.Reloc
+       for _, s := range ctxt.Textp {
+               for i = 0; i < len(s.R); i++ {
+                       r = &s.R[i]
+                       if r.Sym == nil {
+                               continue
+                       }
+                       if r.Type.IsDirectCall() && r.Sym.Type == sym.STEXT {
+                               ctxt.Logf("%s calls %s\n", s.Name, r.Sym.Name)
+                       }
+               }
+       }
+}
+
+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 (ctxt *Link) loadlibfull() {
+       // Load full symbol contents, resolve indexed references.
+       ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms)
+
+       // Pull the symbols out.
+       ctxt.loader.ExtractSymbols(ctxt.Syms)
+
+       // Load cgo directives.
+       for _, d := range ctxt.cgodata {
+               setCgoAttr(ctxt, ctxt.Syms.Lookup, d.file, d.pkg, d.directives)
+       }
+
+       setupdynexp(ctxt)
+
+       // Populate ctxt.Reachparent if appropriate.
+       if ctxt.Reachparent != nil {
+               for i := 0; i < len(ctxt.loader.Reachparent); i++ {
+                       p := ctxt.loader.Reachparent[i]
+                       if p == 0 {
+                               continue
+                       }
+                       if p == loader.Sym(i) {
+                               panic("self-cycle in reachparent")
+                       }
+                       sym := ctxt.loader.Syms[i]
+                       psym := ctxt.loader.Syms[p]
+                       ctxt.Reachparent[sym] = psym
+               }
+       }
+
+       // Drop the reference.
+       ctxt.loader = nil
+       ctxt.cgodata = nil
+
+       addToTextp(ctxt)
+}
+
+func (ctxt *Link) dumpsyms() {
+       for _, s := range ctxt.Syms.Allsym {
+               fmt.Printf("%s %s %p %v %v\n", s, s.Type, s, s.Attr.Reachable(), s.Attr.OnList())
+               for i := range s.R {
+                       fmt.Println("\t", s.R[i].Type, s.R[i].Sym)
+               }
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/link.go b/src/cmd/oldlink/internal/ld/link.go
new file mode 100644 (file)
index 0000000..4020f8d
--- /dev/null
@@ -0,0 +1,187 @@
+// Derived from Inferno utils/6l/l.h and related files.
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/obj"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/loader"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "fmt"
+)
+
+type Shlib struct {
+       Path            string
+       Hash            []byte
+       Deps            []string
+       File            *elf.File
+       gcdataAddresses map[*sym.Symbol]uint64
+}
+
+// Link holds the context for writing object code from a compiler
+// or for reading that input into the linker.
+type Link struct {
+       Out *OutBuf
+
+       Syms *sym.Symbols
+
+       Arch      *sys.Arch
+       Debugvlog int
+       Bso       *bufio.Writer
+
+       Loaded bool // set after all inputs have been loaded as symbols
+
+       IsELF    bool
+       HeadType objabi.HeadType
+
+       linkShared    bool // link against installed Go shared libraries
+       LinkMode      LinkMode
+       BuildMode     BuildMode
+       canUsePlugins bool // initialized when Loaded is set to true
+       compressDWARF bool
+
+       Tlsg         *sym.Symbol
+       Libdir       []string
+       Library      []*sym.Library
+       LibraryByPkg map[string]*sym.Library
+       Shlibs       []Shlib
+       Tlsoffset    int
+       Textp        []*sym.Symbol
+       Filesyms     []*sym.Symbol
+       Moduledata   *sym.Symbol
+
+       PackageFile  map[string]string
+       PackageShlib map[string]string
+
+       tramps []*sym.Symbol // trampolines
+
+       // unresolvedSymSet is a set of erroneous unresolved references.
+       // Used to avoid duplicated error messages.
+       unresolvedSymSet map[unresolvedSymKey]bool
+
+       // Used to implement field tracking.
+       Reachparent map[*sym.Symbol]*sym.Symbol
+
+       compUnits []*sym.CompilationUnit // DWARF compilation units
+       runtimeCU *sym.CompilationUnit   // One of the runtime CUs, the last one seen.
+
+       relocbuf []byte // temporary buffer for applying relocations
+
+       loader  *loader.Loader
+       cgodata []cgodata // cgo directives to load, three strings are args for loadcgo
+
+       cgo_export_static  map[string]bool
+       cgo_export_dynamic map[string]bool
+}
+
+type cgodata struct {
+       file       string
+       pkg        string
+       directives [][]string
+}
+
+type unresolvedSymKey struct {
+       from *sym.Symbol // Symbol that referenced unresolved "to"
+       to   *sym.Symbol // Unresolved symbol referenced by "from"
+}
+
+// ErrorUnresolved prints unresolved symbol error for r.Sym that is referenced from s.
+func (ctxt *Link) ErrorUnresolved(s *sym.Symbol, r *sym.Reloc) {
+       if ctxt.unresolvedSymSet == nil {
+               ctxt.unresolvedSymSet = make(map[unresolvedSymKey]bool)
+       }
+
+       k := unresolvedSymKey{from: s, to: r.Sym}
+       if !ctxt.unresolvedSymSet[k] {
+               ctxt.unresolvedSymSet[k] = true
+
+               // Try to find symbol under another ABI.
+               var reqABI, haveABI obj.ABI
+               haveABI = ^obj.ABI(0)
+               reqABI, ok := sym.VersionToABI(int(r.Sym.Version))
+               if ok {
+                       for abi := obj.ABI(0); abi < obj.ABICount; abi++ {
+                               v := sym.ABIToVersion(abi)
+                               if v == -1 {
+                                       continue
+                               }
+                               if rs := ctxt.Syms.ROLookup(r.Sym.Name, v); rs != nil && rs.Type != sym.Sxxx {
+                                       haveABI = abi
+                               }
+                       }
+               }
+
+               // Give a special error message for main symbol (see #24809).
+               if r.Sym.Name == "main.main" {
+                       Errorf(s, "function main is undeclared in the main package")
+               } else if haveABI != ^obj.ABI(0) {
+                       Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", r.Sym.Name, reqABI, haveABI)
+               } else {
+                       Errorf(s, "relocation target %s not defined", r.Sym.Name)
+               }
+       }
+}
+
+// The smallest possible offset from the hardware stack pointer to a local
+// variable on the stack. Architectures that use a link register save its value
+// on the stack in the function prologue and so always have a pointer between
+// the hardware stack pointer and the local variable area.
+func (ctxt *Link) FixedFrameSize() int64 {
+       switch ctxt.Arch.Family {
+       case sys.AMD64, sys.I386:
+               return 0
+       case sys.PPC64:
+               // PIC code on ppc64le requires 32 bytes of stack, and it's easier to
+               // just use that much stack always on ppc64x.
+               return int64(4 * ctxt.Arch.PtrSize)
+       default:
+               return int64(ctxt.Arch.PtrSize)
+       }
+}
+
+func (ctxt *Link) Logf(format string, args ...interface{}) {
+       fmt.Fprintf(ctxt.Bso, format, args...)
+       ctxt.Bso.Flush()
+}
+
+func addImports(ctxt *Link, l *sym.Library, pn string) {
+       pkg := objabi.PathToPrefix(l.Pkg)
+       for _, importStr := range l.ImportStrings {
+               lib := addlib(ctxt, pkg, pn, importStr)
+               if lib != nil {
+                       l.Imports = append(l.Imports, lib)
+               }
+       }
+       l.ImportStrings = nil
+}
diff --git a/src/cmd/oldlink/internal/ld/macho.go b/src/cmd/oldlink/internal/ld/macho.go
new file mode 100644 (file)
index 0000000..960ed29
--- /dev/null
@@ -0,0 +1,1119 @@
+// 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/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "debug/macho"
+       "encoding/binary"
+       "fmt"
+       "io"
+       "os"
+       "sort"
+       "strings"
+)
+
+type MachoHdr struct {
+       cpu    uint32
+       subcpu uint32
+}
+
+type MachoSect struct {
+       name    string
+       segname string
+       addr    uint64
+       size    uint64
+       off     uint32
+       align   uint32
+       reloc   uint32
+       nreloc  uint32
+       flag    uint32
+       res1    uint32
+       res2    uint32
+}
+
+type MachoSeg struct {
+       name       string
+       vsize      uint64
+       vaddr      uint64
+       fileoffset uint64
+       filesize   uint64
+       prot1      uint32
+       prot2      uint32
+       nsect      uint32
+       msect      uint32
+       sect       []MachoSect
+       flag       uint32
+}
+
+// MachoPlatformLoad represents a LC_VERSION_MIN_* or
+// LC_BUILD_VERSION load command.
+type MachoPlatformLoad struct {
+       platform MachoPlatform // One of PLATFORM_* constants.
+       cmd      MachoLoad
+}
+
+type MachoLoad struct {
+       type_ uint32
+       data  []uint32
+}
+
+type MachoPlatform int
+
+/*
+ * Total amount of space to reserve at the start of the file
+ * for Header, PHeaders, and SHeaders.
+ * May waste some.
+ */
+const (
+       INITIAL_MACHO_HEADR = 4 * 1024
+)
+
+const (
+       MACHO_CPU_AMD64               = 1<<24 | 7
+       MACHO_CPU_386                 = 7
+       MACHO_SUBCPU_X86              = 3
+       MACHO_CPU_ARM                 = 12
+       MACHO_SUBCPU_ARM              = 0
+       MACHO_SUBCPU_ARMV7            = 9
+       MACHO_CPU_ARM64               = 1<<24 | 12
+       MACHO_SUBCPU_ARM64_ALL        = 0
+       MACHO32SYMSIZE                = 12
+       MACHO64SYMSIZE                = 16
+       MACHO_X86_64_RELOC_UNSIGNED   = 0
+       MACHO_X86_64_RELOC_SIGNED     = 1
+       MACHO_X86_64_RELOC_BRANCH     = 2
+       MACHO_X86_64_RELOC_GOT_LOAD   = 3
+       MACHO_X86_64_RELOC_GOT        = 4
+       MACHO_X86_64_RELOC_SUBTRACTOR = 5
+       MACHO_X86_64_RELOC_SIGNED_1   = 6
+       MACHO_X86_64_RELOC_SIGNED_2   = 7
+       MACHO_X86_64_RELOC_SIGNED_4   = 8
+       MACHO_ARM_RELOC_VANILLA       = 0
+       MACHO_ARM_RELOC_PAIR          = 1
+       MACHO_ARM_RELOC_SECTDIFF      = 2
+       MACHO_ARM_RELOC_BR24          = 5
+       MACHO_ARM64_RELOC_UNSIGNED    = 0
+       MACHO_ARM64_RELOC_BRANCH26    = 2
+       MACHO_ARM64_RELOC_PAGE21      = 3
+       MACHO_ARM64_RELOC_PAGEOFF12   = 4
+       MACHO_ARM64_RELOC_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
+)
+
+const (
+       LC_SEGMENT                  = 0x1
+       LC_SYMTAB                   = 0x2
+       LC_SYMSEG                   = 0x3
+       LC_THREAD                   = 0x4
+       LC_UNIXTHREAD               = 0x5
+       LC_LOADFVMLIB               = 0x6
+       LC_IDFVMLIB                 = 0x7
+       LC_IDENT                    = 0x8
+       LC_FVMFILE                  = 0x9
+       LC_PREPAGE                  = 0xa
+       LC_DYSYMTAB                 = 0xb
+       LC_LOAD_DYLIB               = 0xc
+       LC_ID_DYLIB                 = 0xd
+       LC_LOAD_DYLINKER            = 0xe
+       LC_ID_DYLINKER              = 0xf
+       LC_PREBOUND_DYLIB           = 0x10
+       LC_ROUTINES                 = 0x11
+       LC_SUB_FRAMEWORK            = 0x12
+       LC_SUB_UMBRELLA             = 0x13
+       LC_SUB_CLIENT               = 0x14
+       LC_SUB_LIBRARY              = 0x15
+       LC_TWOLEVEL_HINTS           = 0x16
+       LC_PREBIND_CKSUM            = 0x17
+       LC_LOAD_WEAK_DYLIB          = 0x80000018
+       LC_SEGMENT_64               = 0x19
+       LC_ROUTINES_64              = 0x1a
+       LC_UUID                     = 0x1b
+       LC_RPATH                    = 0x8000001c
+       LC_CODE_SIGNATURE           = 0x1d
+       LC_SEGMENT_SPLIT_INFO       = 0x1e
+       LC_REEXPORT_DYLIB           = 0x8000001f
+       LC_LAZY_LOAD_DYLIB          = 0x20
+       LC_ENCRYPTION_INFO          = 0x21
+       LC_DYLD_INFO                = 0x22
+       LC_DYLD_INFO_ONLY           = 0x80000022
+       LC_LOAD_UPWARD_DYLIB        = 0x80000023
+       LC_VERSION_MIN_MACOSX       = 0x24
+       LC_VERSION_MIN_IPHONEOS     = 0x25
+       LC_FUNCTION_STARTS          = 0x26
+       LC_DYLD_ENVIRONMENT         = 0x27
+       LC_MAIN                     = 0x80000028
+       LC_DATA_IN_CODE             = 0x29
+       LC_SOURCE_VERSION           = 0x2A
+       LC_DYLIB_CODE_SIGN_DRS      = 0x2B
+       LC_ENCRYPTION_INFO_64       = 0x2C
+       LC_LINKER_OPTION            = 0x2D
+       LC_LINKER_OPTIMIZATION_HINT = 0x2E
+       LC_VERSION_MIN_TVOS         = 0x2F
+       LC_VERSION_MIN_WATCHOS      = 0x30
+       LC_VERSION_NOTE             = 0x31
+       LC_BUILD_VERSION            = 0x32
+)
+
+const (
+       S_REGULAR                  = 0x0
+       S_ZEROFILL                 = 0x1
+       S_NON_LAZY_SYMBOL_POINTERS = 0x6
+       S_SYMBOL_STUBS             = 0x8
+       S_MOD_INIT_FUNC_POINTERS   = 0x9
+       S_ATTR_PURE_INSTRUCTIONS   = 0x80000000
+       S_ATTR_DEBUG               = 0x02000000
+       S_ATTR_SOME_INSTRUCTIONS   = 0x00000400
+)
+
+const (
+       PLATFORM_MACOS    MachoPlatform = 1
+       PLATFORM_IOS      MachoPlatform = 2
+       PLATFORM_TVOS     MachoPlatform = 3
+       PLATFORM_WATCHOS  MachoPlatform = 4
+       PLATFORM_BRIDGEOS MachoPlatform = 5
+)
+
+// 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 []*sym.Symbol
+
+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(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))
+       if nkind[SymKindUndef] == 0 {
+               out.Write32(MH_NOUNDEFS) /* flags - no undefines */
+       } else {
+               out.Write32(0) /* flags */
+       }
+       if arch.PtrSize == 8 {
+               out.Write32(0) /* reserved */
+       }
+
+       for i := 0; i < nseg; i++ {
+               s := &seg[i]
+               if arch.PtrSize == 8 {
+                       out.Write32(LC_SEGMENT_64)
+                       out.Write32(72 + 80*s.nsect)
+                       out.WriteStringN(s.name, 16)
+                       out.Write64(s.vaddr)
+                       out.Write64(s.vsize)
+                       out.Write64(s.fileoffset)
+                       out.Write64(s.filesize)
+                       out.Write32(s.prot1)
+                       out.Write32(s.prot2)
+                       out.Write32(s.nsect)
+                       out.Write32(s.flag)
+               } else {
+                       out.Write32(LC_SEGMENT)
+                       out.Write32(56 + 68*s.nsect)
+                       out.WriteStringN(s.name, 16)
+                       out.Write32(uint32(s.vaddr))
+                       out.Write32(uint32(s.vsize))
+                       out.Write32(uint32(s.fileoffset))
+                       out.Write32(uint32(s.filesize))
+                       out.Write32(s.prot1)
+                       out.Write32(s.prot2)
+                       out.Write32(s.nsect)
+                       out.Write32(s.flag)
+               }
+
+               for j := uint32(0); j < s.nsect; j++ {
+                       t := &s.sect[j]
+                       if arch.PtrSize == 8 {
+                               out.WriteStringN(t.name, 16)
+                               out.WriteStringN(t.segname, 16)
+                               out.Write64(t.addr)
+                               out.Write64(t.size)
+                               out.Write32(t.off)
+                               out.Write32(t.align)
+                               out.Write32(t.reloc)
+                               out.Write32(t.nreloc)
+                               out.Write32(t.flag)
+                               out.Write32(t.res1) /* reserved */
+                               out.Write32(t.res2) /* reserved */
+                               out.Write32(0)      /* reserved */
+                       } else {
+                               out.WriteStringN(t.name, 16)
+                               out.WriteStringN(t.segname, 16)
+                               out.Write32(uint32(t.addr))
+                               out.Write32(uint32(t.size))
+                               out.Write32(t.off)
+                               out.Write32(t.align)
+                               out.Write32(t.reloc)
+                               out.Write32(t.nreloc)
+                               out.Write32(t.flag)
+                               out.Write32(t.res1) /* reserved */
+                               out.Write32(t.res2) /* reserved */
+                       }
+               }
+       }
+
+       for i := range load {
+               l := &load[i]
+               out.Write32(l.type_)
+               out.Write32(4 * (uint32(len(l.data)) + 2))
+               for j := 0; j < len(l.data); j++ {
+                       out.Write32(l.data[j])
+               }
+       }
+
+       return int(out.Offset() - o1)
+}
+
+func (ctxt *Link) domacho() {
+       if *FlagD {
+               return
+       }
+
+       // Copy platform load command.
+       for _, h := range hostobj {
+               load, err := hostobjMachoPlatform(&h)
+               if err != nil {
+                       Exitf("%v", err)
+               }
+               if load != nil {
+                       machoPlatform = load.platform
+                       ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data)))
+                       copy(ml.data, load.cmd.data)
+                       break
+               }
+       }
+       if machoPlatform == 0 {
+               switch ctxt.Arch.Family {
+               default:
+                       machoPlatform = PLATFORM_MACOS
+                       if ctxt.LinkMode == LinkInternal {
+                               // For lldb, must say LC_VERSION_MIN_MACOSX or else
+                               // it won't know that this Mach-O binary is from OS X
+                               // (could be iOS or WatchOS instead).
+                               // Go on iOS uses linkmode=external, and linkmode=external
+                               // adds this itself. So we only need this code for linkmode=internal
+                               // and we can assume OS X.
+                               //
+                               // See golang.org/issues/12941.
+                               //
+                               // The version must be at least 10.9; see golang.org/issues/30488.
+                               ml := newMachoLoad(ctxt.Arch, LC_VERSION_MIN_MACOSX, 2)
+                               ml.data[0] = 10<<16 | 9<<8 | 0<<0 // OS X version 10.9.0
+                               ml.data[1] = 10<<16 | 9<<8 | 0<<0 // SDK 10.9.0
+                       }
+               case sys.ARM, sys.ARM64:
+                       machoPlatform = PLATFORM_IOS
+               }
+       }
+
+       // empirically, string table must begin with " \x00".
+       s := ctxt.Syms.Lookup(".machosymstr", 0)
+
+       s.Type = sym.SMACHOSYMSTR
+       s.Attr |= sym.AttrReachable
+       s.AddUint8(' ')
+       s.AddUint8('\x00')
+
+       s = ctxt.Syms.Lookup(".machosymtab", 0)
+       s.Type = sym.SMACHOSYMTAB
+       s.Attr |= sym.AttrReachable
+
+       if ctxt.LinkMode != LinkExternal {
+               s := ctxt.Syms.Lookup(".plt", 0) // will be __symbol_stub
+               s.Type = sym.SMACHOPLT
+               s.Attr |= sym.AttrReachable
+
+               s = ctxt.Syms.Lookup(".got", 0) // will be __nl_symbol_ptr
+               s.Type = sym.SMACHOGOT
+               s.Attr |= sym.AttrReachable
+               s.Align = 4
+
+               s = ctxt.Syms.Lookup(".linkedit.plt", 0) // indirect table for .plt
+               s.Type = sym.SMACHOINDIRECTPLT
+               s.Attr |= sym.AttrReachable
+
+               s = ctxt.Syms.Lookup(".linkedit.got", 0) // indirect table for .got
+               s.Type = sym.SMACHOINDIRECTGOT
+               s.Attr |= sym.AttrReachable
+       }
+
+       // Add a dummy symbol that will become the __asm marker section.
+       if ctxt.LinkMode == LinkExternal {
+               s := ctxt.Syms.Lookup(".llvmasm", 0)
+               s.Type = sym.SMACHO
+               s.Attr |= sym.AttrReachable
+               s.AddUint8(0)
+       }
+}
+
+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)
+
+       var msect *MachoSect
+       if sect.Rwx&1 == 0 && segname != "__DWARF" && (ctxt.Arch.Family == sys.ARM64 ||
+               ctxt.Arch.Family == sys.ARM ||
+               (ctxt.Arch.Family == sys.AMD64 && ctxt.BuildMode != BuildModeExe)) {
+               // Darwin external linker on arm and arm64, and on amd64 in c-shared/c-archive buildmode
+               // complains about absolute relocs in __TEXT, so if the section is not
+               // executable, put it in __DATA segment.
+               msect = newMachoSect(mseg, buf, "__DATA")
+       } else {
+               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.Syms.Lookup(".linkedit.plt", 0).Size / 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) {
+       /* apple MACH */
+       va := *FlagTextAddr - int64(HEADR)
+
+       mh := getMachoHdr()
+       switch ctxt.Arch.Family {
+       default:
+               Exitf("unknown macho architecture: %v", ctxt.Arch.Family)
+
+       case sys.ARM:
+               mh.cpu = MACHO_CPU_ARM
+               mh.subcpu = MACHO_SUBCPU_ARMV7
+
+       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
+
+       case sys.I386:
+               mh.cpu = MACHO_CPU_386
+               mh.subcpu = MACHO_SUBCPU_X86
+       }
+
+       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")
+       }
+
+       /* data */
+       if ctxt.LinkMode != LinkExternal {
+               w := int64(Segdata.Length)
+               ms = newMachoSeg("__DATA", 20)
+               ms.vaddr = uint64(va) + uint64(v)
+               ms.vsize = uint64(w)
+               ms.fileoffset = uint64(v)
+               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.ARM:
+                       ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 17+2)
+                       ml.data[0] = 1                           /* thread type */
+                       ml.data[1] = 17                          /* word count */
+                       ml.data[2+15] = uint32(Entryvalue(ctxt)) /* start pc */
+
+               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_UNIXTHREAD, 68+2)
+                       ml.data[0] = 6                           /* thread type */
+                       ml.data[1] = 68                          /* word count */
+                       ml.data[2+64] = uint32(Entryvalue(ctxt)) /* start pc */
+                       ml.data[2+64+1] = uint32(Entryvalue(ctxt) >> 32)
+
+               case sys.I386:
+                       ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 16+2)
+                       ml.data[0] = 1                           /* thread type */
+                       ml.data[1] = 16                          /* word count */
+                       ml.data[2+10] = uint32(Entryvalue(ctxt)) /* start pc */
+               }
+       }
+
+       if !*FlagD {
+               // must match domacholink below
+               s1 := ctxt.Syms.Lookup(".machosymtab", 0)
+               s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
+               s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
+               s4 := ctxt.Syms.Lookup(".machosymstr", 0)
+
+               if ctxt.LinkMode != LinkExternal {
+                       ms := newMachoSeg("__LINKEDIT", 0)
+                       ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(*FlagRound)))
+                       ms.vsize = uint64(s1.Size) + uint64(s2.Size) + uint64(s3.Size) + uint64(s4.Size)
+                       ms.fileoffset = uint64(linkoff)
+                       ms.filesize = ms.vsize
+                       ms.prot1 = 7
+                       ms.prot2 = 3
+               }
+
+               ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4)
+               ml.data[0] = uint32(linkoff)                               /* symoff */
+               ml.data[1] = uint32(nsortsym)                              /* nsyms */
+               ml.data[2] = uint32(linkoff + s1.Size + s2.Size + s3.Size) /* stroff */
+               ml.data[3] = uint32(s4.Size)                               /* strsize */
+
+               machodysymtab(ctxt)
+
+               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)
+                       }
+               }
+       }
+
+       a := machowrite(ctxt.Arch, ctxt.Out, ctxt.LinkMode)
+       if int32(a) > HEADR {
+               Exitf("HEADR too small: %d > %d", a, HEADR)
+       }
+}
+
+func symkind(s *sym.Symbol) int {
+       if s.Type == sym.SDYNIMPORT {
+               return SymKindUndef
+       }
+       if s.Attr.CgoExport() {
+               return SymKindExtdef
+       }
+       return SymKindLocal
+}
+
+func addsym(ctxt *Link, s *sym.Symbol, name string, type_ SymbolType, addr int64, gotype *sym.Symbol) {
+       if s == nil {
+               return
+       }
+
+       switch type_ {
+       default:
+               return
+
+       case DataSym, BSSSym, TextSym:
+               break
+       }
+
+       if sortsym != nil {
+               sortsym[nsortsym] = s
+               nkind[symkind(s)]++
+       }
+
+       nsortsym++
+}
+
+type machoscmp []*sym.Symbol
+
+func (x machoscmp) Len() int {
+       return len(x)
+}
+
+func (x machoscmp) Swap(i, j int) {
+       x[i], x[j] = x[j], x[i]
+}
+
+func (x machoscmp) Less(i, j int) bool {
+       s1 := x[i]
+       s2 := x[j]
+
+       k1 := symkind(s1)
+       k2 := symkind(s2)
+       if k1 != k2 {
+               return k1 < k2
+       }
+
+       return s1.Extname() < s2.Extname()
+}
+
+func machogenasmsym(ctxt *Link) {
+       genasmsym(ctxt, addsym)
+       for _, s := range ctxt.Syms.Allsym {
+               // Some 64-bit functions have a "$INODE64" or "$INODE64$UNIX2003" suffix.
+               if s.Type == sym.SDYNIMPORT && s.Dynimplib() == "/usr/lib/libSystem.B.dylib" {
+                       // But only on macOS.
+                       if machoPlatform == PLATFORM_MACOS {
+                               switch n := s.Extname(); n {
+                               case "fdopendir":
+                                       switch objabi.GOARCH {
+                                       case "amd64":
+                                               s.SetExtname(n + "$INODE64")
+                                       case "386":
+                                               s.SetExtname(n + "$INODE64$UNIX2003")
+                                       }
+                               case "readdir_r", "getfsstat":
+                                       switch objabi.GOARCH {
+                                       case "amd64", "386":
+                                               s.SetExtname(n + "$INODE64")
+                                       }
+                               }
+                       }
+               }
+
+               if s.Type == sym.SDYNIMPORT || s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT {
+                       if s.Attr.Reachable() {
+                               addsym(ctxt, s, "", DataSym, 0, nil)
+                       }
+               }
+       }
+}
+
+func machosymorder(ctxt *Link) {
+       // 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 i := range dynexp {
+               dynexp[i].Attr |= sym.AttrReachable
+       }
+       machogenasmsym(ctxt)
+       sortsym = make([]*sym.Symbol, nsortsym)
+       nsortsym = 0
+       machogenasmsym(ctxt)
+       sort.Sort(machoscmp(sortsym[:nsortsym]))
+       for i := 0; i < nsortsym; i++ {
+               sortsym[i].Dynid = int32(i)
+       }
+}
+
+// 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, s *sym.Symbol) bool {
+       if !ctxt.DynlinkingGo() || s.Attr.Local() {
+               return false
+       }
+       if ctxt.BuildMode == BuildModePlugin && strings.HasPrefix(s.Extname(), objabi.PathToPrefix(*flagPluginPath)) {
+               return true
+       }
+       if strings.HasPrefix(s.Name, "go.itab.") {
+               return true
+       }
+       if strings.HasPrefix(s.Name, "type.") && !strings.HasPrefix(s.Name, "type..") {
+               // reduce runtime typemap pressure, but do not
+               // export alg functions (type..*), as these
+               // appear in pclntable.
+               return true
+       }
+       if strings.HasPrefix(s.Name, "go.link.pkghash") {
+               return true
+       }
+       return s.Type >= sym.SFirstWritable // only writable sections
+}
+
+func machosymtab(ctxt *Link) {
+       symtab := ctxt.Syms.Lookup(".machosymtab", 0)
+       symstr := ctxt.Syms.Lookup(".machosymstr", 0)
+
+       for i := 0; i < nsortsym; i++ {
+               s := sortsym[i]
+               symtab.AddUint32(ctxt.Arch, uint32(symstr.Size))
+
+               export := machoShouldExport(ctxt, s)
+               isGoSymbol := strings.Contains(s.Extname(), ".")
+
+               // In normal buildmodes, only add _ to C symbols, as
+               // Go symbols have dot in the name.
+               //
+               // Do not export C symbols in plugins, as runtime C
+               // symbols like crosscall2 are in pclntab and end up
+               // pointing at the host binary, breaking unwinding.
+               // See Issue #18190.
+               cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s))
+               if cexport || export || isGoSymbol {
+                       symstr.AddUint8('_')
+               }
+
+               // replace "·" as ".", because DTrace cannot handle it.
+               Addstring(symstr, strings.Replace(s.Extname(), "·", ".", -1))
+
+               if s.Type == sym.SDYNIMPORT || s.Type == sym.SHOSTOBJ || s.Type == 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 s.Attr.CgoExport() || export {
+                               symtab.AddUint8(0x0f)
+                       } else {
+                               symtab.AddUint8(0x0e)
+                       }
+                       o := s
+                       for o.Outer != nil {
+                               o = o.Outer
+                       }
+                       if o.Sect == nil {
+                               Errorf(s, "missing section for symbol")
+                               symtab.AddUint8(0)
+                       } else {
+                               symtab.AddUint8(uint8(o.Sect.Extnum))
+                       }
+                       symtab.AddUint16(ctxt.Arch, 0) // desc
+                       symtab.AddUintXX(ctxt.Arch, uint64(Symaddr(s)), ctxt.Arch.PtrSize)
+               }
+       }
+}
+
+func machodysymtab(ctxt *Link) {
+       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 */
+
+       // must match domacholink below
+       s1 := ctxt.Syms.Lookup(".machosymtab", 0)
+
+       s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
+       s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
+       ml.data[12] = uint32(linkoff + s1.Size)       /* indirectsymoff */
+       ml.data[13] = uint32((s2.Size + s3.Size) / 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)
+
+       // write data that will be linkedit section
+       s1 := ctxt.Syms.Lookup(".machosymtab", 0)
+
+       s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
+       s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
+       s4 := ctxt.Syms.Lookup(".machosymstr", 0)
+
+       // 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.
+       for s4.Size%16 != 0 {
+               s4.AddUint8(0)
+       }
+
+       size := int(s1.Size + s2.Size + s3.Size + s4.Size)
+
+       if size > 0 {
+               linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
+               ctxt.Out.SeekSet(linkoff)
+
+               ctxt.Out.Write(s1.P[:s1.Size])
+               ctxt.Out.Write(s2.P[:s2.Size])
+               ctxt.Out.Write(s3.P[:s3.Size])
+               ctxt.Out.Write(s4.P[:s4.Size])
+       }
+
+       return Rnd(int64(size), int64(*FlagRound))
+}
+
+func machorelocsect(ctxt *Link, sect *sym.Section, syms []*sym.Symbol) {
+       // If main section has no bits, nothing to relocate.
+       if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+               return
+       }
+
+       sect.Reloff = uint64(ctxt.Out.Offset())
+       for i, s := range syms {
+               if !s.Attr.Reachable() {
+                       continue
+               }
+               if uint64(s.Value) >= sect.Vaddr {
+                       syms = syms[i:]
+                       break
+               }
+       }
+
+       eaddr := int32(sect.Vaddr + sect.Length)
+       for _, s := range syms {
+               if !s.Attr.Reachable() {
+                       continue
+               }
+               if s.Value >= int64(eaddr) {
+                       break
+               }
+               for ri := range s.R {
+                       r := &s.R[ri]
+                       if r.Done {
+                               continue
+                       }
+                       if r.Xsym == nil {
+                               Errorf(s, "missing xsym in relocation")
+                               continue
+                       }
+                       if !r.Xsym.Attr.Reachable() {
+                               Errorf(s, "unreachable reloc %d (%s) target %v", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Xsym.Name)
+                       }
+                       if !thearch.Machoreloc1(ctxt.Arch, ctxt.Out, s, r, int64(uint64(s.Value+int64(r.Off))-sect.Vaddr)) {
+                               Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Siz, r.Sym.Name)
+                       }
+               }
+       }
+
+       sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
+}
+
+func Machoemitreloc(ctxt *Link) {
+       for ctxt.Out.Offset()&7 != 0 {
+               ctxt.Out.Write8(0)
+       }
+
+       machorelocsect(ctxt, Segtext.Sections[0], ctxt.Textp)
+       for _, sect := range Segtext.Sections[1:] {
+               machorelocsect(ctxt, sect, datap)
+       }
+       for _, sect := range Segdata.Sections {
+               machorelocsect(ctxt, sect, datap)
+       }
+       for _, sect := range Segdwarf.Sections {
+               machorelocsect(ctxt, sect, dwarfp)
+       }
+}
+
+// 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
+}
diff --git a/src/cmd/oldlink/internal/ld/macho_combine_dwarf.go b/src/cmd/oldlink/internal/ld/macho_combine_dwarf.go
new file mode 100644 (file)
index 0000000..9d9f916
--- /dev/null
@@ -0,0 +1,462 @@
+// 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"
+)
+
+const (
+       pageAlign = 12 // 4096 = 1 << 12
+)
+
+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 := machoCalcStart(realdwarf.Offset, linkseg.Offset, pageAlign)
+       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 := machoCalcStart(linkseg.Offset, uint64(dwarfstart)+dwarfsize, pageAlign)
+       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, &macho.Segment64{}, &macho.Section64{})
+               case macho.LoadCmdSegment:
+                       err = machoUpdateSegment(reader, linkseg, linkoffset, &macho.Segment32{}, &macho.Section32{})
+               case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
+                       err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
+               case macho.LoadCmdSymtab:
+                       err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.SymtabCmd{}, "Symoff", "Stroff")
+               case macho.LoadCmdDysymtab:
+                       err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
+               case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS:
+                       err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &linkEditDataCmd{}, "DataOff")
+               case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
+                       err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &encryptionInfoCmd{}, "CryptOff")
+               case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH, LC_ID_DYLIB, LC_SYMSEG, LC_LOADFVMLIB, LC_IDFVMLIB, LC_IDENT, LC_FVMFILE, LC_PREPAGE, LC_ID_DYLINKER, LC_ROUTINES, LC_SUB_FRAMEWORK, LC_SUB_UMBRELLA, LC_SUB_CLIENT, LC_SUB_LIBRARY, LC_TWOLEVEL_HINTS, LC_PREBIND_CKSUM, LC_ROUTINES_64, LC_LAZY_LOAD_DYLIB, LC_LOAD_UPWARD_DYLIB, LC_DYLD_ENVIRONMENT, LC_LINKER_OPTION, LC_LINKER_OPTIMIZATION_HINT, LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS, LC_VERSION_NOTE, LC_BUILD_VERSION:
+                       // Nothing to update
+               default:
+                       err = fmt.Errorf("unknown load command 0x%x (%s)", int(cmd.Cmd), cmd.Cmd)
+               }
+               if err != nil {
+                       return err
+               }
+       }
+       // Do the final update of the DWARF segment's load command.
+       return machoUpdateDwarfHeader(&reader, compressedSects, dwarfsize, dwarfstart, realdwarf)
+}
+
+// machoCompressSections tries to compress the DWARF segments in dwarfm,
+// returning the updated sections and segment contents, nils if the sections
+// weren't compressed, or an error if there was a problem reading dwarfm.
+func machoCompressSections(ctxt *Link, dwarfm *macho.File) ([]*macho.Section, []byte, error) {
+       if !ctxt.compressDWARF {
+               return nil, nil, nil
+       }
+
+       dwarfseg := dwarfm.Segment("__DWARF")
+       var sects []*macho.Section
+       var buf bytes.Buffer
+
+       for _, sect := range dwarfm.Sections {
+               if sect.Seg != "__DWARF" {
+                       continue
+               }
+
+               // As of writing, there are no relocations in dsymutil's output
+               // so there's no point in worrying about them. Bail out if that
+               // changes.
+               if sect.Nreloc != 0 {
+                       return nil, nil, nil
+               }
+
+               data, err := sect.Data()
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               compressed, contents, err := machoCompressSection(data)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               newSec := *sect
+               newSec.Offset = uint32(dwarfseg.Offset) + uint32(buf.Len())
+               newSec.Addr = dwarfseg.Addr + uint64(buf.Len())
+               if compressed {
+                       newSec.Name = "__z" + sect.Name[2:]
+                       newSec.Size = uint64(len(contents))
+               }
+               sects = append(sects, &newSec)
+               buf.Write(contents)
+       }
+       return sects, buf.Bytes(), nil
+}
+
+// machoCompressSection compresses secBytes if it results in less data.
+func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, err error) {
+       var buf bytes.Buffer
+       buf.WriteString("ZLIB")
+       var sizeBytes [8]byte
+       binary.BigEndian.PutUint64(sizeBytes[:], uint64(len(sectBytes)))
+       buf.Write(sizeBytes[:])
+
+       z := zlib.NewWriter(&buf)
+       if _, err := z.Write(sectBytes); err != nil {
+               return false, nil, err
+       }
+       if err := z.Close(); err != nil {
+               return false, nil, err
+       }
+       if buf.Len() >= len(sectBytes) {
+               return false, sectBytes, nil
+       }
+       return true, buf.Bytes(), nil
+}
+
+// machoUpdateSegment updates the load command for a moved segment.
+// Only the linkedit segment should move, and it should have 0 sections.
+// seg should be a macho.Segment32 or macho.Segment64 as appropriate.
+// sect should be a macho.Section32 or macho.Section64 as appropriate.
+func machoUpdateSegment(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64, seg, sect interface{}) error {
+       if err := r.ReadAt(0, seg); err != nil {
+               return err
+       }
+       segValue := reflect.ValueOf(seg)
+       offset := reflect.Indirect(segValue).FieldByName("Offset")
+
+       // Only the linkedit segment moved, anything before that is fine.
+       if offset.Uint() < linkseg.Offset {
+               return nil
+       }
+       offset.SetUint(offset.Uint() + 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, segValue, reflect.ValueOf(sect), linkoffset, nil)
+}
+
+func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset uint64, compressedSects []*macho.Section) error {
+       iseg := reflect.Indirect(seg)
+       nsect := iseg.FieldByName("Nsect").Uint()
+       if nsect == 0 {
+               return nil
+       }
+       sectOffset := int64(iseg.Type().Size())
+
+       isect := reflect.Indirect(sect)
+       offsetField := isect.FieldByName("Offset")
+       reloffField := isect.FieldByName("Reloff")
+       addrField := isect.FieldByName("Addr")
+       nameField := isect.FieldByName("Name")
+       sizeField := isect.FieldByName("Size")
+       sectSize := int64(isect.Type().Size())
+       for i := uint64(0); i < nsect; i++ {
+               if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
+                       return err
+               }
+               if compressedSects != nil {
+                       cSect := compressedSects[i]
+                       var name [16]byte
+                       copy(name[:], []byte(cSect.Name))
+                       nameField.Set(reflect.ValueOf(name))
+                       sizeField.SetUint(cSect.Size)
+                       if cSect.Offset != 0 {
+                               offsetField.SetUint(uint64(cSect.Offset) + deltaOffset)
+                       }
+                       if cSect.Addr != 0 {
+                               addrField.SetUint(cSect.Addr)
+                       }
+               } else {
+                       if offsetField.Uint() != 0 {
+                               offsetField.SetUint(offsetField.Uint() + deltaOffset)
+                       }
+                       if reloffField.Uint() != 0 {
+                               reloffField.SetUint(reloffField.Uint() + deltaOffset)
+                       }
+                       if addrField.Uint() != 0 {
+                               addrField.SetUint(addrField.Uint())
+                       }
+               }
+               if err := r.WriteAt(sectOffset, sect.Interface()); 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 {
+       var seg, sect interface{}
+       cmd, err := r.Next()
+       if err != nil {
+               return err
+       }
+       if cmd.Cmd == macho.LoadCmdSegment64 {
+               seg = new(macho.Segment64)
+               sect = new(macho.Section64)
+       } else {
+               seg = new(macho.Segment32)
+               sect = new(macho.Section32)
+       }
+       if err := r.ReadAt(0, seg); err != nil {
+               return err
+       }
+       segv := reflect.ValueOf(seg).Elem()
+       segv.FieldByName("Offset").SetUint(uint64(dwarfstart))
+
+       if compressedSects != nil {
+               var segSize uint64
+               for _, newSect := range compressedSects {
+                       segSize += newSect.Size
+               }
+               segv.FieldByName("Filesz").SetUint(segSize)
+       } else {
+               segv.FieldByName("Filesz").SetUint(dwarfsize)
+       }
+
+       // We want the DWARF segment to be considered non-loadable, so
+       // force vmaddr and vmsize to zero. In addition, set the initial
+       // protection to zero so as to make the dynamic loader happy,
+       // since otherwise it may complain that that the vm size and file
+       // size don't match for the segment. See issues 21647 and 32673
+       // for more context. Also useful to refer to the Apple dynamic
+       // loader source, specifically ImageLoaderMachO::sniffLoadCommands
+       // in ImageLoaderMachO.cpp (various versions can be found online, see
+       // https://opensource.apple.com/source/dyld/dyld-519.2.2/src/ImageLoaderMachO.cpp.auto.html
+       // as one example).
+       segv.FieldByName("Addr").SetUint(0)
+       segv.FieldByName("Memsz").SetUint(0)
+       segv.FieldByName("Prot").SetUint(0)
+
+       if err := r.WriteAt(0, seg); err != nil {
+               return err
+       }
+       return machoUpdateSections(*r, segv, reflect.ValueOf(sect), 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
+}
+
+func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 {
+       align := uint64(1 << alignExp)
+       origMod, newMod := origAddr%align, newAddr%align
+       if origMod == newMod {
+               return int64(newAddr)
+       }
+       return int64(newAddr + align + origMod - newMod)
+}
diff --git a/src/cmd/oldlink/internal/ld/main.go b/src/cmd/oldlink/internal/ld/main.go
new file mode 100644 (file)
index 0000000..f7f3700
--- /dev/null
@@ -0,0 +1,338 @@
+// Inferno utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "flag"
+       "log"
+       "os"
+       "runtime"
+       "runtime/pprof"
+       "strings"
+)
+
+var (
+       pkglistfornote []byte
+       windowsgui     bool // writes a "GUI binary" instead of a "console binary"
+       ownTmpDir      bool // set to true if tmp dir created by linker (e.g. no -tmpdir)
+)
+
+func init() {
+       flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...")
+}
+
+// Flags used by the linker. The exported flags are used by the architecture-specific packages.
+var (
+       flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
+
+       flagOutfile    = flag.String("o", "", "write output to `file`")
+       flagPluginPath = flag.String("pluginpath", "", "full path name for plugin")
+
+       flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`")
+       flagDumpDep       = flag.Bool("dumpdep", false, "dump symbol dependency graph")
+       flagRace          = flag.Bool("race", false, "enable race detector")
+       flagMsan          = flag.Bool("msan", false, "enable MSan interface")
+
+       flagFieldTrack = flag.String("k", "", "set field tracking `symbol`")
+       flagLibGCC     = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable")
+       flagTmpdir     = flag.String("tmpdir", "", "use `directory` for temporary files")
+
+       flagExtld      = flag.String("extld", "", "use `linker` when linking in external mode")
+       flagExtldflags = flag.String("extldflags", "", "pass `flags` to external linker")
+       flagExtar      = flag.String("extar", "", "archive program for buildmode=c-archive")
+
+       flagA           = flag.Bool("a", false, "disassemble output")
+       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")
+       flagU           = flag.Bool("u", false, "reject unsafe packages")
+       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")
+       FlagStrictDups  = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
+       flagNewobj      = flag.Bool("newobj", false, "use new object file format")
+
+       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`")
+)
+
+// Main is the main entry point for the linker code.
+func Main(arch *sys.Arch, theArch Arch) {
+       thearch = theArch
+       ctxt := linknew(arch)
+       ctxt.Bso = bufio.NewWriter(os.Stdout)
+
+       // For testing behavior of go command when tools crash silently.
+       // Undocumented, not in standard flag parser to avoid
+       // exposing in usage message.
+       for _, arg := range os.Args {
+               if arg == "-crash_for_testing" {
+                       os.Exit(2)
+               }
+       }
+
+       final := gorootFinal()
+       addstrdata1(ctxt, "runtime/internal/sys.DefaultGoroot="+final)
+       addstrdata1(ctxt, "cmd/internal/objabi.defaultGOROOT="+final)
+
+       // TODO(matloob): define these above and then check flag values here
+       if ctxt.Arch.Family == sys.AMD64 && objabi.GOOS == "plan9" {
+               flag.BoolVar(&Flag8, "8", false, "use 64-bit addresses in symbol table")
+       }
+       flagHeadType := flag.String("H", "", "set header `type`")
+       flag.BoolVar(&ctxt.linkShared, "linkshared", false, "link against installed Go shared libraries")
+       flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
+       flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
+       flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
+       objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo)
+       objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
+       objabi.AddVersionFlag() // -V
+       objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
+       objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog)
+       objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg)
+
+       objabi.Flagparse(usage)
+
+       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 objabi.Fieldtrack_enabled != 0 {
+               ctxt.Reachparent = make(map[*sym.Symbol]*sym.Symbol)
+       }
+       checkStrictDups = *FlagStrictDups
+
+       startProfile()
+       if ctxt.BuildMode == BuildModeUnset {
+               ctxt.BuildMode = BuildModeExe
+       }
+
+       if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 {
+               usage()
+       }
+
+       if *flagOutfile == "" {
+               *flagOutfile = "a.out"
+               if ctxt.HeadType == objabi.Hwindows {
+                       *flagOutfile += ".exe"
+               }
+       }
+
+       interpreter = *flagInterpreter
+
+       libinit(ctxt) // creates outfile
+
+       if ctxt.HeadType == objabi.Hunknown {
+               ctxt.HeadType.Set(objabi.GOOS)
+       }
+
+       ctxt.computeTLSOffset()
+       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))
+       }
+
+       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, "")
+               }
+       case BuildModePlugin:
+               addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "")
+       default:
+               addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "")
+       }
+       ctxt.loadlib()
+
+       deadcode(ctxt)
+       if *flagNewobj {
+               ctxt.loadlibfull() // XXX do it here for now
+       }
+       ctxt.linksetup()
+       ctxt.dostrdata()
+
+       dwarfGenerateDebugInfo(ctxt)
+       if objabi.Fieldtrack_enabled != 0 {
+               fieldtrack(ctxt)
+       }
+       ctxt.mangleTypeSym()
+       ctxt.callgraph()
+
+       ctxt.doelf()
+       if ctxt.HeadType == objabi.Hdarwin {
+               ctxt.domacho()
+       }
+       ctxt.dostkcheck()
+       if ctxt.HeadType == objabi.Hwindows {
+               ctxt.dope()
+               ctxt.windynrelocsyms()
+       }
+       if ctxt.HeadType == objabi.Haix {
+               ctxt.doxcoff()
+       }
+
+       ctxt.addexport()
+       thearch.Gentext(ctxt) // trampolines, call stubs, etc.
+       ctxt.textbuildid()
+       ctxt.textaddress()
+       ctxt.pclntab()
+       ctxt.findfunctab()
+       ctxt.typelink()
+       ctxt.symtab()
+       ctxt.buildinfo()
+       ctxt.dodata()
+       order := ctxt.address()
+       dwarfcompress(ctxt)
+       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.
+       var outputMmapped bool
+       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.
+               err := ctxt.Out.Mmap(filesize)
+               outputMmapped = err == nil
+       }
+       if outputMmapped {
+               // Asmb will redirect symbols to the output file mmap, and relocations
+               // will be applied directly there.
+               thearch.Asmb(ctxt)
+               ctxt.reloc()
+               ctxt.Out.Munmap()
+       } else {
+               // If we don't mmap, we need to apply relocations before
+               // writing out.
+               ctxt.reloc()
+               thearch.Asmb(ctxt)
+       }
+       thearch.Asmb2(ctxt)
+
+       ctxt.undef()
+       ctxt.hostlink()
+       if ctxt.Debugvlog != 0 {
+               ctxt.Logf("%d symbols\n", len(ctxt.Syms.Allsym))
+               ctxt.Logf("%d liveness data\n", liveness)
+       }
+       ctxt.Bso.Flush()
+       ctxt.archive()
+
+       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/oldlink/internal/ld/nooptcgolink_test.go b/src/cmd/oldlink/internal/ld/nooptcgolink_test.go
new file mode 100644 (file)
index 0000000..4d2ff1a
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "runtime"
+       "testing"
+)
+
+func TestNooptCgoBuild(t *testing.T) {
+       if testing.Short() {
+               t.Skip("skipping test in short mode.")
+       }
+       t.Parallel()
+
+       testenv.MustHaveGoBuild(t)
+       testenv.MustHaveCGO(t)
+       dir, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out"))
+       cmd.Dir = filepath.Join(runtime.GOROOT(), "src", "runtime", "testdata", "testprogcgo")
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Logf("go build output: %s", out)
+               t.Fatal(err)
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/outbuf.go b/src/cmd/oldlink/internal/ld/outbuf.go
new file mode 100644 (file)
index 0000000..596d239
--- /dev/null
@@ -0,0 +1,177 @@
+// 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 (
+       "bufio"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "encoding/binary"
+       "log"
+       "os"
+)
+
+// OutBuf is a buffered file writer.
+//
+// It is simlar to the Writer in cmd/internal/bio with a few small differences.
+//
+// First, it tracks the output architecture and uses it to provide
+// endian helpers.
+//
+// Second, it provides a very cheap offset counter that doesn't require
+// any system calls to read the value.
+//
+// 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
+// - Munmap the output file
+// - possibly write more content to the file, which will not be edited later.
+type OutBuf struct {
+       arch   *sys.Arch
+       off    int64
+       w      *bufio.Writer
+       buf    []byte // backing store of mmap'd output file
+       f      *os.File
+       encbuf [8]byte // temp buffer used by WriteN methods
+}
+
+func (out *OutBuf) SeekSet(p int64) {
+       if p == out.off {
+               return
+       }
+       if out.buf == nil {
+               out.Flush()
+               if _, err := out.f.Seek(p, 0); err != nil {
+                       Exitf("seeking to %d in %s: %v", p, out.f.Name(), err)
+               }
+       }
+       out.off = p
+}
+
+func (out *OutBuf) Offset() int64 {
+       return out.off
+}
+
+// Write writes the contents of v to the buffer.
+//
+// As Write is backed by a bufio.Writer, callers do not have
+// to explicitly handle the returned error as long as Flush is
+// eventually called.
+func (out *OutBuf) Write(v []byte) (int, error) {
+       if out.buf != nil {
+               n := copy(out.buf[out.off:], v)
+               out.off += int64(n)
+               return n, nil
+       }
+       n, err := out.w.Write(v)
+       out.off += int64(n)
+       return n, err
+}
+
+func (out *OutBuf) Write8(v uint8) {
+       if out.buf != nil {
+               out.buf[out.off] = v
+               out.off++
+               return
+       }
+       if err := out.w.WriteByte(v); err == nil {
+               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) {
+       if out.buf != nil {
+               n := copy(out.buf[out.off:], 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)
+               return
+       }
+       n, _ := out.w.WriteString(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, then changes the Symbol's content
+// to point to the output buffer that we just wrote, so we can apply further
+// edit to the symbol content.
+// If the output file is not Mmap'd, just writes the content.
+func (out *OutBuf) WriteSym(s *sym.Symbol) {
+       if out.buf != nil {
+               start := out.off
+               out.Write(s.P)
+               s.P = out.buf[start:out.off]
+               s.Attr.Set(sym.AttrReadOnly, false)
+       } else {
+               out.Write(s.P)
+       }
+}
+
+func (out *OutBuf) Flush() {
+       var err error
+       if out.buf != nil {
+               err = out.Msync()
+       } else {
+               err = out.w.Flush()
+       }
+       if err != nil {
+               Exitf("flushing %s: %v", out.f.Name(), err)
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/outbuf_mmap.go b/src/cmd/oldlink/internal/ld/outbuf_mmap.go
new file mode 100644 (file)
index 0000000..4075141
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux openbsd
+
+package ld
+
+import (
+       "syscall"
+       "unsafe"
+)
+
+func (out *OutBuf) Mmap(filesize uint64) error {
+       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)
+       return err
+}
+
+func (out *OutBuf) Munmap() {
+       err := out.Msync()
+       if err != nil {
+               Exitf("msync output file failed: %v", err)
+       }
+       syscall.Munmap(out.buf)
+       out.buf = nil
+       _, err = out.f.Seek(out.off, 0)
+       if err != nil {
+               Exitf("seek output file failed: %v", err)
+       }
+}
+
+func (out *OutBuf) Msync() error {
+       // TODO: netbsd supports mmap and msync, but the syscall package doesn't define MSYNC.
+       // It is excluded from the build tag for now.
+       _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(len(out.buf)), syscall.MS_SYNC)
+       if errno != 0 {
+               return errno
+       }
+       return nil
+}
diff --git a/src/cmd/oldlink/internal/ld/outbuf_nommap.go b/src/cmd/oldlink/internal/ld/outbuf_nommap.go
new file mode 100644 (file)
index 0000000..fba8cd8
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !darwin,!dragonfly,!freebsd,!linux,!openbsd,!windows
+
+package ld
+
+import "errors"
+
+var errNotSupported = errors.New("mmap not supported")
+
+func (out *OutBuf) Mmap(filesize uint64) error { return errNotSupported }
+func (out *OutBuf) Munmap()                    { panic("unreachable") }
+func (out *OutBuf) Msync() error               { panic("unreachable") }
diff --git a/src/cmd/oldlink/internal/ld/outbuf_windows.go b/src/cmd/oldlink/internal/ld/outbuf_windows.go
new file mode 100644 (file)
index 0000000..1cb05c3
--- /dev/null
@@ -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 (
+       "reflect"
+       "syscall"
+       "unsafe"
+)
+
+func (out *OutBuf) Mmap(filesize uint64) error {
+       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_READONLY, 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
+       }
+       *(*reflect.SliceHeader)(unsafe.Pointer(&out.buf)) = reflect.SliceHeader{Data: ptr, Len: int(filesize), Cap: int(filesize)}
+       return nil
+}
+
+func (out *OutBuf) Munmap() {
+       if out.buf == nil {
+               return
+       }
+       err := syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])))
+       if err != nil {
+               Exitf("UnmapViewOfFile failed: %v", err)
+       }
+}
+
+func (out *OutBuf) Msync() error {
+       if out.buf == nil {
+               return nil
+       }
+       return syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0)
+}
diff --git a/src/cmd/oldlink/internal/ld/pcln.go b/src/cmd/oldlink/internal/ld/pcln.go
new file mode 100644 (file)
index 0000000..7d53ab8
--- /dev/null
@@ -0,0 +1,530 @@
+// 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/obj"
+       "cmd/internal/objabi"
+       "cmd/internal/src"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "encoding/binary"
+       "fmt"
+       "log"
+       "os"
+       "path/filepath"
+       "strings"
+)
+
+func ftabaddstring(ftab *sym.Symbol, s string) int32 {
+       start := len(ftab.P)
+       ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL
+       copy(ftab.P[start:], s)
+       return int32(start)
+}
+
+// numberfile assigns a file number to the file if it hasn't been assigned already.
+func numberfile(ctxt *Link, file *sym.Symbol) {
+       if file.Type != sym.SFILEPATH {
+               ctxt.Filesyms = append(ctxt.Filesyms, file)
+               file.Value = int64(len(ctxt.Filesyms))
+               file.Type = sym.SFILEPATH
+               path := file.Name[len(src.FileSymPrefix):]
+               file.Name = expandGoroot(path)
+       }
+}
+
+func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) {
+       // Give files numbers.
+       for _, f := range files {
+               numberfile(ctxt, f)
+       }
+
+       buf := make([]byte, binary.MaxVarintLen32)
+       newval := int32(-1)
+       var out sym.Pcdata
+       it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
+       for it.Init(d.P); !it.Done; it.Next() {
+               // value delta
+               oldval := it.Value
+
+               var val int32
+               if oldval == -1 {
+                       val = -1
+               } else {
+                       if oldval < 0 || oldval >= int32(len(files)) {
+                               log.Fatalf("bad pcdata %d", oldval)
+                       }
+                       val = int32(files[oldval].Value)
+               }
+
+               dv := val - newval
+               newval = val
+
+               // value
+               n := binary.PutVarint(buf, int64(dv))
+               out.P = append(out.P, buf[:n]...)
+
+               // pc delta
+               pc := (it.NextPC - it.PC) / it.PCScale
+               n = binary.PutUvarint(buf, uint64(pc))
+               out.P = append(out.P, buf[:n]...)
+       }
+
+       // terminating value delta
+       // we want to write varint-encoded 0, which is just 0
+       out.P = append(out.P, 0)
+
+       *d = out
+}
+
+// onlycsymbol reports whether this is a symbol that is referenced by C code.
+func onlycsymbol(s *sym.Symbol) bool {
+       switch s.Name {
+       case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2":
+               return true
+       }
+       if strings.HasPrefix(s.Name, "_cgoexp_") {
+               return true
+       }
+       return false
+}
+
+func emitPcln(ctxt *Link, s *sym.Symbol) bool {
+       if s == nil {
+               return true
+       }
+       if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) {
+               return false
+       }
+       // We want to generate func table entries only for the "lowest level" symbols,
+       // not containers of subsymbols.
+       return !s.Attr.Container()
+}
+
+// pclntab initializes the pclntab symbol with
+// runtime function and file name information.
+
+var pclntabZpcln sym.FuncInfo
+
+// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
+var pclntabNfunc int32
+var pclntabFiletabOffset int32
+var pclntabPclntabOffset int32
+var pclntabFirstFunc *sym.Symbol
+var pclntabLastFunc *sym.Symbol
+
+func (ctxt *Link) pclntab() {
+       funcdataBytes := int64(0)
+       ftab := ctxt.Syms.Lookup("runtime.pclntab", 0)
+       ftab.Type = sym.SPCLNTAB
+       ftab.Attr |= sym.AttrReachable
+
+       // See golang.org/s/go12symtab for the format. Briefly:
+       //      8-byte header
+       //      nfunc [thearch.ptrsize bytes]
+       //      function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
+       //      end PC [thearch.ptrsize bytes]
+       //      offset to file table [4 bytes]
+
+       // Find container symbols and mark them as such.
+       for _, s := range ctxt.Textp {
+               if s.Outer != nil {
+                       s.Outer.Attr |= sym.AttrContainer
+               }
+       }
+
+       // Gather some basic stats and info.
+       var nfunc int32
+       prevSect := ctxt.Textp[0].Sect
+       for _, s := range ctxt.Textp {
+               if !emitPcln(ctxt, s) {
+                       continue
+               }
+               nfunc++
+               if pclntabFirstFunc == nil {
+                       pclntabFirstFunc = s
+               }
+               if s.Sect != prevSect {
+                       // With multiple text sections, the external linker may insert functions
+                       // between the sections, which are not known by Go. This leaves holes in
+                       // the PC range covered by the func table. We need to generate an entry
+                       // to mark the hole.
+                       nfunc++
+                       prevSect = s.Sect
+               }
+       }
+
+       pclntabNfunc = nfunc
+       ftab.Grow(8 + int64(ctxt.Arch.PtrSize) + int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
+       ftab.SetUint32(ctxt.Arch, 0, 0xfffffffb)
+       ftab.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
+       ftab.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
+       ftab.SetUint(ctxt.Arch, 8, uint64(nfunc))
+       pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize)
+
+       funcnameoff := make(map[string]int32)
+       nameToOffset := func(name string) int32 {
+               nameoff, ok := funcnameoff[name]
+               if !ok {
+                       nameoff = ftabaddstring(ftab, name)
+                       funcnameoff[name] = nameoff
+               }
+               return nameoff
+       }
+
+       pctaboff := make(map[string]uint32)
+       writepctab := func(off int32, p []byte) int32 {
+               start, ok := pctaboff[string(p)]
+               if !ok {
+                       if len(p) > 0 {
+                               start = uint32(len(ftab.P))
+                               ftab.AddBytes(p)
+                       }
+                       pctaboff[string(p)] = start
+               }
+               newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start))
+               return newoff
+       }
+
+       nfunc = 0 // repurpose nfunc as a running index
+       prevFunc := ctxt.Textp[0]
+       for _, s := range ctxt.Textp {
+               if !emitPcln(ctxt, s) {
+                       continue
+               }
+
+               if s.Sect != prevFunc.Sect {
+                       // With multiple text sections, there may be a hole here in the address
+                       // space (see the comment above). We use an invalid funcoff value to
+                       // mark the hole.
+                       // See also runtime/symtab.go:findfunc
+                       ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFunc.Size)
+                       ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0))
+                       nfunc++
+               }
+               prevFunc = s
+
+               pcln := s.FuncInfo
+               if pcln == nil {
+                       pcln = &pclntabZpcln
+               }
+
+               if len(pcln.InlTree) > 0 {
+                       if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex {
+                               // Create inlining pcdata table.
+                               pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
+                               copy(pcdata, pcln.Pcdata)
+                               pcln.Pcdata = pcdata
+                       }
+
+                       if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree {
+                               // Create inline tree funcdata.
+                               funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1)
+                               funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1)
+                               copy(funcdata, pcln.Funcdata)
+                               copy(funcdataoff, pcln.Funcdataoff)
+                               pcln.Funcdata = funcdata
+                               pcln.Funcdataoff = funcdataoff
+                       }
+               }
+
+               funcstart := int32(len(ftab.P))
+               funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize
+
+               ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s)
+               ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
+
+               // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
+               // and package debug/gosym.
+
+               // fixed size of struct, checked below
+               off := funcstart
+
+               end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize)
+               if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
+                       end += 4
+               }
+               ftab.Grow(int64(end))
+
+               // entry uintptr
+               off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s))
+
+               // name int32
+               nameoff := nameToOffset(s.Name)
+               off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
+
+               // args int32
+               // TODO: Move into funcinfo.
+               args := uint32(0)
+               if s.FuncInfo != nil {
+                       args = uint32(s.FuncInfo.Args)
+               }
+               off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
+
+               // deferreturn
+               deferreturn := uint32(0)
+               lastWasmAddr := uint32(0)
+               for _, r := range s.R {
+                       if ctxt.Arch.Family == sys.Wasm && r.Type == objabi.R_ADDR {
+                               // Wasm does not have a live variable set at the deferreturn
+                               // call itself. Instead it has one identified by the
+                               // resumption point immediately preceding the deferreturn.
+                               // The wasm code has a R_ADDR relocation which is used to
+                               // set the resumption point to PC_B.
+                               lastWasmAddr = uint32(r.Add)
+                       }
+                       if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
+                               if ctxt.Arch.Family == sys.Wasm {
+                                       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 ctxt.Arch.Family {
+                                       case sys.AMD64, sys.I386:
+                                               deferreturn--
+                                       case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64:
+                                               // no change
+                                       case sys.RISCV64:
+                                               // TODO(jsing): The JALR instruction is marked with
+                                               // R_CALLRISCV, whereas the actual reloc is currently
+                                               // one instruction earlier starting with the AUIPC.
+                                               deferreturn -= 4
+                                       case sys.S390X:
+                                               deferreturn -= 2
+                                       default:
+                                               panic(fmt.Sprint("Unhandled architecture:", ctxt.Arch.Family))
+                                       }
+                               }
+                               break // only need one
+                       }
+               }
+               off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
+
+               if pcln != &pclntabZpcln {
+                       renumberfiles(ctxt, pcln.File, &pcln.Pcfile)
+                       if false {
+                               // Sanity check the new numbering
+                               it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
+                               for it.Init(pcln.Pcfile.P); !it.Done; it.Next() {
+                                       if it.Value < 1 || it.Value > int32(len(ctxt.Filesyms)) {
+                                               Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(ctxt.Filesyms))
+                                               errorexit()
+                                       }
+                               }
+                       }
+               }
+
+               if len(pcln.InlTree) > 0 {
+                       inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0)
+                       inlTreeSym.Type = sym.SRODATA
+                       inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK
+
+                       for i, call := range pcln.InlTree {
+                               // Usually, call.File is already numbered since the file
+                               // shows up in the Pcfile table. However, two inlined calls
+                               // might overlap exactly so that only the innermost file
+                               // appears in the Pcfile table. In that case, this assigns
+                               // the outer file a number.
+                               numberfile(ctxt, call.File)
+                               nameoff := nameToOffset(call.Func)
+
+                               inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent))
+                               inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func, "")))
+                               // byte 3 is unused
+                               inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value))
+                               inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line))
+                               inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff))
+                               inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC))
+                       }
+
+                       pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym
+                       pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline
+               }
+
+               // pcdata
+               off = writepctab(off, pcln.Pcsp.P)
+               off = writepctab(off, pcln.Pcfile.P)
+               off = writepctab(off, pcln.Pcline.P)
+               off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata))))
+
+               // funcID uint8
+               var file string
+               if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 {
+                       file = s.FuncInfo.File[0].Name
+               }
+               funcID := objabi.GetFuncID(s.Name, file)
+
+               off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
+
+               // unused
+               off += 2
+
+               // nfuncdata must be the final entry.
+               off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(pcln.Funcdata))))
+               for i := range pcln.Pcdata {
+                       off = writepctab(off, pcln.Pcdata[i].P)
+               }
+
+               // funcdata, must be pointer-aligned and we're only int32-aligned.
+               // Missing funcdata will be 0 (nil pointer).
+               if len(pcln.Funcdata) > 0 {
+                       if off&int32(ctxt.Arch.PtrSize-1) != 0 {
+                               off += 4
+                       }
+                       for i := range pcln.Funcdata {
+                               dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i)
+                               if pcln.Funcdata[i] == nil {
+                                       ftab.SetUint(ctxt.Arch, dataoff, uint64(pcln.Funcdataoff[i]))
+                                       continue
+                               }
+                               // TODO: Dedup.
+                               funcdataBytes += pcln.Funcdata[i].Size
+                               ftab.SetAddrPlus(ctxt.Arch, dataoff, pcln.Funcdata[i], pcln.Funcdataoff[i])
+                       }
+                       off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize)
+               }
+
+               if off != end {
+                       Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), ctxt.Arch.PtrSize)
+                       errorexit()
+               }
+
+               nfunc++
+       }
+
+       last := ctxt.Textp[len(ctxt.Textp)-1]
+       pclntabLastFunc = last
+       // Final entry of table is just end pc.
+       ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size)
+
+       // Start file table.
+       start := int32(len(ftab.P))
+
+       start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1)
+       pclntabFiletabOffset = start
+       ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
+
+       ftab.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4)
+       ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1))
+       for i := len(ctxt.Filesyms) - 1; i >= 0; i-- {
+               s := ctxt.Filesyms[i]
+               ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
+       }
+
+       ftab.Size = int64(len(ftab.P))
+
+       if ctxt.Debugvlog != 0 {
+               ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size, funcdataBytes)
+       }
+}
+
+func gorootFinal() string {
+       root := objabi.GOROOT
+       if final := os.Getenv("GOROOT_FINAL"); final != "" {
+               root = final
+       }
+       return root
+}
+
+func expandGoroot(s string) string {
+       const n = len("$GOROOT")
+       if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
+               return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:]))
+       }
+       return s
+}
+
+const (
+       BUCKETSIZE    = 256 * MINFUNC
+       SUBBUCKETS    = 16
+       SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
+       NOIDX         = 0x7fffffff
+)
+
+// findfunctab generates a lookup table to quickly find the containing
+// function for a pc. See src/runtime/symtab.go:findfunc for details.
+func (ctxt *Link) findfunctab() {
+       t := ctxt.Syms.Lookup("runtime.findfunctab", 0)
+       t.Type = sym.SRODATA
+       t.Attr |= sym.AttrReachable
+       t.Attr |= sym.AttrLocal
+
+       // find min and max address
+       min := ctxt.Textp[0].Value
+       lastp := ctxt.Textp[len(ctxt.Textp)-1]
+       max := lastp.Value + lastp.Size
+
+       // for each subbucket, compute the minimum of all symbol indexes
+       // that map to that subbucket.
+       n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
+
+       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) {
+                       continue
+               }
+               p := s.Value
+               var e *sym.Symbol
+               i++
+               if i < len(ctxt.Textp) {
+                       e = ctxt.Textp[i]
+               }
+               for !emitPcln(ctxt, e) && i < len(ctxt.Textp) {
+                       e = ctxt.Textp[i]
+                       i++
+               }
+               q := max
+               if e != nil {
+                       q = e.Value
+               }
+
+               //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++
+       }
+
+       // allocate table
+       nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
+
+       t.Grow(4*int64(nbuckets) + int64(n))
+
+       // 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))
+               }
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/pe.go b/src/cmd/oldlink/internal/ld/pe.go
new file mode 100644 (file)
index 0000000..b40557f
--- /dev/null
@@ -0,0 +1,1562 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// PE (Portable Executable) file writing
+// https://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
+
+package ld
+
+import (
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "debug/pe"
+       "encoding/binary"
+       "fmt"
+       "sort"
+       "strconv"
+       "strings"
+)
+
+type IMAGE_IMPORT_DESCRIPTOR struct {
+       OriginalFirstThunk uint32
+       TimeDateStamp      uint32
+       ForwarderChain     uint32
+       Name               uint32
+       FirstThunk         uint32
+}
+
+type IMAGE_EXPORT_DIRECTORY struct {
+       Characteristics       uint32
+       TimeDateStamp         uint32
+       MajorVersion          uint16
+       MinorVersion          uint16
+       Name                  uint32
+       Base                  uint32
+       NumberOfFunctions     uint32
+       NumberOfNames         uint32
+       AddressOfFunctions    uint32
+       AddressOfNames        uint32
+       AddressOfNameOrdinals uint32
+}
+
+const (
+       PEBASE = 0x00400000
+)
+
+var (
+       // SectionAlignment must be greater than or equal to FileAlignment.
+       // The default is the page size for the architecture.
+       PESECTALIGN int64 = 0x1000
+
+       // FileAlignment should be a power of 2 between 512 and 64 K, inclusive.
+       // The default is 512. If the SectionAlignment is less than
+       // the architecture's page size, then FileAlignment must match SectionAlignment.
+       PEFILEALIGN int64 = 2 << 8
+)
+
+const (
+       IMAGE_SCN_CNT_CODE               = 0x00000020
+       IMAGE_SCN_CNT_INITIALIZED_DATA   = 0x00000040
+       IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
+       IMAGE_SCN_MEM_EXECUTE            = 0x20000000
+       IMAGE_SCN_MEM_READ               = 0x40000000
+       IMAGE_SCN_MEM_WRITE              = 0x80000000
+       IMAGE_SCN_MEM_DISCARDABLE        = 0x2000000
+       IMAGE_SCN_LNK_NRELOC_OVFL        = 0x1000000
+       IMAGE_SCN_ALIGN_32BYTES          = 0x600000
+)
+
+// TODO(crawshaw): add these constants to debug/pe.
+const (
+       // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2
+       IMAGE_SYM_TYPE_NULL      = 0
+       IMAGE_SYM_TYPE_STRUCT    = 8
+       IMAGE_SYM_DTYPE_FUNCTION = 0x20
+       IMAGE_SYM_DTYPE_ARRAY    = 0x30
+       IMAGE_SYM_CLASS_EXTERNAL = 2
+       IMAGE_SYM_CLASS_STATIC   = 3
+
+       IMAGE_REL_I386_DIR32  = 0x0006
+       IMAGE_REL_I386_SECREL = 0x000B
+       IMAGE_REL_I386_REL32  = 0x0014
+
+       IMAGE_REL_AMD64_ADDR64 = 0x0001
+       IMAGE_REL_AMD64_ADDR32 = 0x0002
+       IMAGE_REL_AMD64_REL32  = 0x0004
+       IMAGE_REL_AMD64_SECREL = 0x000B
+
+       IMAGE_REL_ARM_ABSOLUTE = 0x0000
+       IMAGE_REL_ARM_ADDR32   = 0x0001
+       IMAGE_REL_ARM_ADDR32NB = 0x0002
+       IMAGE_REL_ARM_BRANCH24 = 0x0003
+       IMAGE_REL_ARM_BRANCH11 = 0x0004
+       IMAGE_REL_ARM_SECREL   = 0x000F
+
+       IMAGE_REL_BASED_HIGHLOW = 3
+       IMAGE_REL_BASED_DIR64   = 10
+)
+
+const (
+       PeMinimumTargetMajorVersion = 6
+       PeMinimumTargetMinorVersion = 1
+)
+
+// DOS stub that prints out
+// "This program cannot be run in DOS mode."
+var dosstub = []uint8{
+       0x4d,
+       0x5a,
+       0x90,
+       0x00,
+       0x03,
+       0x00,
+       0x04,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0xff,
+       0xff,
+       0x00,
+       0x00,
+       0x8b,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x40,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x80,
+       0x00,
+       0x00,
+       0x00,
+       0x0e,
+       0x1f,
+       0xba,
+       0x0e,
+       0x00,
+       0xb4,
+       0x09,
+       0xcd,
+       0x21,
+       0xb8,
+       0x01,
+       0x4c,
+       0xcd,
+       0x21,
+       0x54,
+       0x68,
+       0x69,
+       0x73,
+       0x20,
+       0x70,
+       0x72,
+       0x6f,
+       0x67,
+       0x72,
+       0x61,
+       0x6d,
+       0x20,
+       0x63,
+       0x61,
+       0x6e,
+       0x6e,
+       0x6f,
+       0x74,
+       0x20,
+       0x62,
+       0x65,
+       0x20,
+       0x72,
+       0x75,
+       0x6e,
+       0x20,
+       0x69,
+       0x6e,
+       0x20,
+       0x44,
+       0x4f,
+       0x53,
+       0x20,
+       0x6d,
+       0x6f,
+       0x64,
+       0x65,
+       0x2e,
+       0x0d,
+       0x0d,
+       0x0a,
+       0x24,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+       0x00,
+}
+
+type Imp struct {
+       s       *sym.Symbol
+       off     uint64
+       next    *Imp
+       argsize int
+}
+
+type Dll struct {
+       name     string
+       nameoff  uint64
+       thunkoff uint64
+       ms       *Imp
+       next     *Dll
+}
+
+var (
+       rsrcsym     *sym.Symbol
+       PESECTHEADR int32
+       PEFILEHEADR int32
+       pe64        int
+       dr          *Dll
+       dexport     [1024]*sym.Symbol
+       nexport     int
+)
+
+// peStringTable is a COFF string table.
+type peStringTable struct {
+       strings    []string
+       stringsLen int
+}
+
+// size returns size of string table t.
+func (t *peStringTable) size() int {
+       // string table starts with 4-byte length at the beginning
+       return t.stringsLen + 4
+}
+
+// add adds string str to string table t.
+func (t *peStringTable) add(str string) int {
+       off := t.size()
+       t.strings = append(t.strings, str)
+       t.stringsLen += len(str) + 1 // each string will have 0 appended to it
+       return off
+}
+
+// write writes string table t into the output file.
+func (t *peStringTable) write(out *OutBuf) {
+       out.Write32(uint32(t.size()))
+       for _, s := range t.strings {
+               out.WriteString(s)
+               out.Write8(0)
+       }
+}
+
+// peSection represents section from COFF section table.
+type peSection struct {
+       name                 string
+       shortName            string
+       index                int // one-based index into the Section Table
+       virtualSize          uint32
+       virtualAddress       uint32
+       sizeOfRawData        uint32
+       pointerToRawData     uint32
+       pointerToRelocations uint32
+       numberOfRelocations  uint16
+       characteristics      uint32
+}
+
+// checkOffset verifies COFF section sect offset in the file.
+func (sect *peSection) checkOffset(off int64) {
+       if off != int64(sect.pointerToRawData) {
+               Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off))
+               errorexit()
+       }
+}
+
+// checkSegment verifies COFF section sect matches address
+// and file offset provided in segment seg.
+func (sect *peSection) checkSegment(seg *sym.Segment) {
+       if seg.Vaddr-PEBASE != uint64(sect.virtualAddress) {
+               Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-PEBASE)))
+               errorexit()
+       }
+       if seg.Fileoff != uint64(sect.pointerToRawData) {
+               Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff)))
+               errorexit()
+       }
+}
+
+// pad adds zeros to the section sect. It writes as many bytes
+// as necessary to make section sect.SizeOfRawData bytes long.
+// It assumes that n bytes are already written to the file.
+func (sect *peSection) pad(out *OutBuf, n uint32) {
+       out.WriteStringN("", int(sect.sizeOfRawData-n))
+}
+
+// write writes COFF section sect into the output file.
+func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error {
+       h := pe.SectionHeader32{
+               VirtualSize:          sect.virtualSize,
+               SizeOfRawData:        sect.sizeOfRawData,
+               PointerToRawData:     sect.pointerToRawData,
+               PointerToRelocations: sect.pointerToRelocations,
+               NumberOfRelocations:  sect.numberOfRelocations,
+               Characteristics:      sect.characteristics,
+       }
+       if linkmode != LinkExternal {
+               h.VirtualAddress = sect.virtualAddress
+       }
+       copy(h.Name[:], sect.shortName)
+       return binary.Write(out, binary.LittleEndian, h)
+}
+
+// emitRelocations emits the relocation entries for the sect.
+// The actual relocations are emitted by relocfn.
+// This updates the corresponding PE section table entry
+// with the relocation offset and count.
+func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) {
+       sect.pointerToRelocations = uint32(out.Offset())
+       // first entry: extended relocs
+       out.Write32(0) // placeholder for number of relocation + 1
+       out.Write32(0)
+       out.Write16(0)
+
+       n := relocfn() + 1
+
+       cpos := out.Offset()
+       out.SeekSet(int64(sect.pointerToRelocations))
+       out.Write32(uint32(n))
+       out.SeekSet(cpos)
+       if n > 0x10000 {
+               n = 0x10000
+               sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL
+       } else {
+               sect.pointerToRelocations += 10 // skip the extend reloc entry
+       }
+       sect.numberOfRelocations = uint16(n - 1)
+}
+
+// peFile is used to build COFF file.
+type peFile struct {
+       sections       []*peSection
+       stringTable    peStringTable
+       textSect       *peSection
+       rdataSect      *peSection
+       dataSect       *peSection
+       bssSect        *peSection
+       ctorsSect      *peSection
+       nextSectOffset uint32
+       nextFileOffset uint32
+       symtabOffset   int64 // offset to the start of symbol table
+       symbolCount    int   // number of symbol table records written
+       dataDirectory  [16]pe.DataDirectory
+}
+
+// addSection adds section to the COFF file f.
+func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection {
+       sect := &peSection{
+               name:             name,
+               shortName:        name,
+               index:            len(f.sections) + 1,
+               virtualSize:      uint32(sectsize),
+               virtualAddress:   f.nextSectOffset,
+               pointerToRawData: f.nextFileOffset,
+       }
+       f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN))
+       if filesize > 0 {
+               sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN))
+               f.nextFileOffset += sect.sizeOfRawData
+       }
+       f.sections = append(f.sections, sect)
+       return sect
+}
+
+// addDWARFSection adds DWARF section to the COFF file f.
+// This function is similar to addSection, but DWARF section names are
+// longer than 8 characters, so they need to be stored in the string table.
+func (f *peFile) addDWARFSection(name string, size int) *peSection {
+       if size == 0 {
+               Exitf("DWARF section %q is empty", name)
+       }
+       // DWARF section names are longer than 8 characters.
+       // PE format requires such names to be stored in string table,
+       // and section names replaced with slash (/) followed by
+       // correspondent string table index.
+       // see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx
+       // for details
+       off := f.stringTable.add(name)
+       h := f.addSection(name, size, size)
+       h.shortName = fmt.Sprintf("/%d", off)
+       h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
+       return h
+}
+
+// addDWARF adds DWARF information to the COFF file f.
+func (f *peFile) addDWARF() {
+       if *FlagS { // disable symbol table
+               return
+       }
+       if *FlagW { // disable dwarf
+               return
+       }
+       for _, sect := range Segdwarf.Sections {
+               h := f.addDWARFSection(sect.Name, int(sect.Length))
+               fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff
+               if uint64(h.pointerToRawData) != fileoff {
+                       Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff)
+               }
+       }
+}
+
+// addInitArray adds .ctors COFF section to the file f.
+func (f *peFile) addInitArray(ctxt *Link) *peSection {
+       // The size below was determined by the specification for array relocations,
+       // and by observing what GCC writes here. If the initarray section grows to
+       // contain more than one constructor entry, the size will need to be 8 * constructor_count.
+       // However, the entire Go runtime is initialized from just one function, so it is unlikely
+       // that this will need to grow in the future.
+       var size int
+       switch objabi.GOARCH {
+       default:
+               Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", objabi.GOARCH)
+       case "386":
+               size = 4
+       case "amd64":
+               size = 8
+       case "arm":
+               size = 4
+       }
+       sect := f.addSection(".ctors", size, size)
+       sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+       sect.sizeOfRawData = uint32(size)
+       ctxt.Out.SeekSet(int64(sect.pointerToRawData))
+       sect.checkOffset(ctxt.Out.Offset())
+
+       init_entry := ctxt.Syms.Lookup(*flagEntrySymbol, 0)
+       addr := uint64(init_entry.Value) - init_entry.Sect.Vaddr
+       switch objabi.GOARCH {
+       case "386", "arm":
+               ctxt.Out.Write32(uint32(addr))
+       case "amd64":
+               ctxt.Out.Write64(addr)
+       }
+       return sect
+}
+
+// emitRelocations emits relocation entries for go.o in external linking.
+func (f *peFile) emitRelocations(ctxt *Link) {
+       for ctxt.Out.Offset()&7 != 0 {
+               ctxt.Out.Write8(0)
+       }
+
+       // relocsect relocates symbols from first in section sect, and returns
+       // the total number of relocations emitted.
+       relocsect := func(sect *sym.Section, syms []*sym.Symbol, base uint64) int {
+               // If main section has no bits, nothing to relocate.
+               if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+                       return 0
+               }
+               relocs := 0
+               sect.Reloff = uint64(ctxt.Out.Offset())
+               for i, s := range syms {
+                       if !s.Attr.Reachable() {
+                               continue
+                       }
+                       if uint64(s.Value) >= sect.Vaddr {
+                               syms = syms[i:]
+                               break
+                       }
+               }
+               eaddr := int32(sect.Vaddr + sect.Length)
+               for _, sym := range syms {
+                       if !sym.Attr.Reachable() {
+                               continue
+                       }
+                       if sym.Value >= int64(eaddr) {
+                               break
+                       }
+                       for ri := range sym.R {
+                               r := &sym.R[ri]
+                               if r.Done {
+                                       continue
+                               }
+                               if r.Xsym == nil {
+                                       Errorf(sym, "missing xsym in relocation")
+                                       continue
+                               }
+                               if r.Xsym.Dynid < 0 {
+                                       Errorf(sym, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type)
+                               }
+                               if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, sym, r, int64(uint64(sym.Value+int64(r.Off))-base)) {
+                                       Errorf(sym, "unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
+                               }
+                               relocs++
+                       }
+               }
+               sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
+               return relocs
+       }
+
+       sects := []struct {
+               peSect *peSection
+               seg    *sym.Segment
+               syms   []*sym.Symbol
+       }{
+               {f.textSect, &Segtext, ctxt.Textp},
+               {f.rdataSect, &Segrodata, datap},
+               {f.dataSect, &Segdata, 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 _, sect := range Segdwarf.Sections {
+               for _, pesect := range f.sections {
+                       if sect.Name == pesect.name {
+                               pesect.emitRelocations(ctxt.Out, func() int {
+                                       return relocsect(sect, dwarfp, sect.Vaddr)
+                               })
+                               continue dwarfLoop
+                       }
+               }
+               Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
+       }
+
+       f.ctorsSect.emitRelocations(ctxt.Out, func() int {
+               dottext := ctxt.Syms.Lookup(".text", 0)
+               ctxt.Out.Write32(0)
+               ctxt.Out.Write32(uint32(dottext.Dynid))
+               switch objabi.GOARCH {
+               default:
+                       Errorf(dottext, "unknown architecture for PE: %q\n", objabi.GOARCH)
+               case "386":
+                       ctxt.Out.Write16(IMAGE_REL_I386_DIR32)
+               case "amd64":
+                       ctxt.Out.Write16(IMAGE_REL_AMD64_ADDR64)
+               case "arm":
+                       ctxt.Out.Write16(IMAGE_REL_ARM_ADDR32)
+               }
+               return 1
+       })
+}
+
+// writeSymbol appends symbol s to file f symbol table.
+// It also sets s.Dynid to written symbol number.
+func (f *peFile) writeSymbol(out *OutBuf, s *sym.Symbol, value int64, sectidx int, typ uint16, class uint8) {
+       if len(s.Name) > 8 {
+               out.Write32(0)
+               out.Write32(uint32(f.stringTable.add(s.Name)))
+       } else {
+               out.WriteStringN(s.Name, 8)
+       }
+       out.Write32(uint32(value))
+       out.Write16(uint16(sectidx))
+       out.Write16(typ)
+       out.Write8(class)
+       out.Write8(0) // no aux entries
+
+       s.Dynid = 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(s *sym.Symbol, linkmode LinkMode) (pesectidx int, offset int64, err error) {
+       if s.Sect == nil {
+               return 0, 0, fmt.Errorf("could not map %s symbol with no section", s.Name)
+       }
+       if s.Sect.Seg == &Segtext {
+               return f.textSect.index, int64(uint64(s.Value) - Segtext.Vaddr), nil
+       }
+       if s.Sect.Seg == &Segrodata {
+               return f.rdataSect.index, int64(uint64(s.Value) - Segrodata.Vaddr), nil
+       }
+       if s.Sect.Seg != &Segdata {
+               return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", s.Name)
+       }
+       v := uint64(s.Value) - Segdata.Vaddr
+       if linkmode != LinkExternal {
+               return f.dataSect.index, int64(v), nil
+       }
+       if s.Type == sym.SDATA {
+               return f.dataSect.index, int64(v), nil
+       }
+       // Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section
+       // it still belongs to the .data section, not the .bss section.
+       if v < Segdata.Filelen {
+               return f.dataSect.index, int64(v), nil
+       }
+       return f.bssSect.index, int64(v - Segdata.Filelen), nil
+}
+
+// writeSymbols writes all COFF symbol table records.
+func (f *peFile) writeSymbols(ctxt *Link) {
+
+       put := func(ctxt *Link, s *sym.Symbol, name string, type_ SymbolType, addr int64, gotype *sym.Symbol) {
+               if s == nil {
+                       return
+               }
+               if s.Sect == nil && type_ != UndefinedSym {
+                       return
+               }
+               switch type_ {
+               default:
+                       return
+               case DataSym, BSSSym, TextSym, UndefinedSym:
+               }
+
+               // Only windows/386 requires underscore prefix on external symbols.
+               if ctxt.Arch.Family == sys.I386 &&
+                       ctxt.LinkMode == LinkExternal &&
+                       (s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT || s.Attr.CgoExport()) {
+                       s.Name = "_" + s.Name
+               }
+
+               var typ uint16
+               if ctxt.LinkMode == LinkExternal {
+                       typ = IMAGE_SYM_TYPE_NULL
+               } else {
+                       // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308
+                       typ = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT
+                       typ = 0x0308 // "array of structs"
+               }
+               sect, value, err := f.mapToPESection(s, ctxt.LinkMode)
+               if err != nil {
+                       if type_ == UndefinedSym {
+                               typ = IMAGE_SYM_DTYPE_FUNCTION
+                       } else {
+                               Errorf(s, "addpesym: %v", err)
+                       }
+               }
+               class := IMAGE_SYM_CLASS_EXTERNAL
+               if s.IsFileLocal() || s.Attr.VisibilityHidden() || s.Attr.Local() {
+                       class = IMAGE_SYM_CLASS_STATIC
+               }
+               f.writeSymbol(ctxt.Out, s, value, sect, typ, 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 {
+                       sym := ctxt.Syms.Lookup(pesect.name, 0)
+                       f.writeSymbol(ctxt.Out, sym, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC)
+               }
+       }
+
+       genasmsym(ctxt, put)
+}
+
+// writeSymbolTableAndStringTable writes out symbol and string tables for peFile f.
+func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) {
+       f.symtabOffset = ctxt.Out.Offset()
+
+       // write COFF symbol table
+       if !*FlagS || ctxt.LinkMode == LinkExternal {
+               f.writeSymbols(ctxt)
+       }
+
+       // update COFF file header and section table
+       size := f.stringTable.size() + 18*f.symbolCount
+       var h *peSection
+       if ctxt.LinkMode != LinkExternal {
+               // We do not really need .symtab for go.o, and if we have one, ld
+               // will also include it in the exe, and that will confuse windows.
+               h = f.addSection(".symtab", size, size)
+               h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
+               h.checkOffset(f.symtabOffset)
+       }
+
+       // write COFF string table
+       f.stringTable.write(ctxt.Out)
+       if ctxt.LinkMode != LinkExternal {
+               h.pad(ctxt.Out, uint32(size))
+       }
+}
+
+// writeFileHeader writes COFF file header for peFile f.
+func (f *peFile) writeFileHeader(ctxt *Link) {
+       var fh pe.FileHeader
+
+       switch ctxt.Arch.Family {
+       default:
+               Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
+       case sys.AMD64:
+               fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64
+       case sys.I386:
+               fh.Machine = pe.IMAGE_FILE_MACHINE_I386
+       case sys.ARM:
+               fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT
+       }
+
+       fh.NumberOfSections = uint16(len(f.sections))
+
+       // Being able to produce identical output for identical input is
+       // much more beneficial than having build timestamp in the header.
+       fh.TimeDateStamp = 0
+
+       if ctxt.LinkMode == LinkExternal {
+               fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED
+       } else {
+               fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED
+               switch ctxt.Arch.Family {
+               case sys.AMD64, sys.I386:
+                       if ctxt.BuildMode != BuildModePIE {
+                               fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED
+                       }
+               }
+       }
+       if pe64 != 0 {
+               var oh64 pe.OptionalHeader64
+               fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64))
+               fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE
+       } else {
+               var oh pe.OptionalHeader32
+               fh.SizeOfOptionalHeader = uint16(binary.Size(&oh))
+               fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE
+       }
+
+       fh.PointerToSymbolTable = uint32(f.symtabOffset)
+       fh.NumberOfSymbols = uint32(f.symbolCount)
+
+       binary.Write(ctxt.Out, binary.LittleEndian, &fh)
+}
+
+// writeOptionalHeader writes COFF optional header for peFile f.
+func (f *peFile) writeOptionalHeader(ctxt *Link) {
+       var oh pe.OptionalHeader32
+       var oh64 pe.OptionalHeader64
+
+       if pe64 != 0 {
+               oh64.Magic = 0x20b // PE32+
+       } else {
+               oh.Magic = 0x10b // PE32
+               oh.BaseOfData = f.dataSect.virtualAddress
+       }
+
+       // Fill out both oh64 and oh. We only use one. Oh well.
+       oh64.MajorLinkerVersion = 3
+       oh.MajorLinkerVersion = 3
+       oh64.MinorLinkerVersion = 0
+       oh.MinorLinkerVersion = 0
+       oh64.SizeOfCode = f.textSect.sizeOfRawData
+       oh.SizeOfCode = f.textSect.sizeOfRawData
+       oh64.SizeOfInitializedData = f.dataSect.sizeOfRawData
+       oh.SizeOfInitializedData = f.dataSect.sizeOfRawData
+       oh64.SizeOfUninitializedData = 0
+       oh.SizeOfUninitializedData = 0
+       if ctxt.LinkMode != LinkExternal {
+               oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
+               oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
+       }
+       oh64.BaseOfCode = f.textSect.virtualAddress
+       oh.BaseOfCode = f.textSect.virtualAddress
+       oh64.ImageBase = PEBASE
+       oh.ImageBase = PEBASE
+       oh64.SectionAlignment = uint32(PESECTALIGN)
+       oh.SectionAlignment = uint32(PESECTALIGN)
+       oh64.FileAlignment = uint32(PEFILEALIGN)
+       oh.FileAlignment = uint32(PEFILEALIGN)
+       oh64.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion
+       oh.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion
+       oh64.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion
+       oh.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion
+       oh64.MajorImageVersion = 1
+       oh.MajorImageVersion = 1
+       oh64.MinorImageVersion = 0
+       oh.MinorImageVersion = 0
+       oh64.MajorSubsystemVersion = PeMinimumTargetMajorVersion
+       oh.MajorSubsystemVersion = PeMinimumTargetMajorVersion
+       oh64.MinorSubsystemVersion = PeMinimumTargetMinorVersion
+       oh.MinorSubsystemVersion = PeMinimumTargetMinorVersion
+       oh64.SizeOfImage = f.nextSectOffset
+       oh.SizeOfImage = f.nextSectOffset
+       oh64.SizeOfHeaders = uint32(PEFILEHEADR)
+       oh.SizeOfHeaders = uint32(PEFILEHEADR)
+       if windowsgui {
+               oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
+               oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
+       } else {
+               oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
+               oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
+       }
+
+       // Mark as having awareness of terminal services, to avoid ancient compatibility hacks.
+       oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
+       oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
+
+       // Enable DEP
+       oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
+       oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
+
+       // The DLL can be relocated at load time.
+       switch ctxt.Arch.Family {
+       case sys.AMD64, sys.I386:
+               if ctxt.BuildMode == BuildModePIE {
+                       oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+                       oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+               }
+       case sys.ARM:
+               oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+               oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+       }
+
+       // Image can handle a high entropy 64-bit virtual address space.
+       if ctxt.BuildMode == BuildModePIE {
+               oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
+       }
+
+       // Disable stack growth as we don't want Windows to
+       // fiddle with the thread stack limits, which we set
+       // ourselves to circumvent the stack checks in the
+       // Windows exception dispatcher.
+       // Commit size must be strictly less than reserve
+       // size otherwise reserve will be rounded up to a
+       // larger size, as verified with VMMap.
+
+       // On 64-bit, we always reserve 2MB stacks. "Pure" Go code is
+       // okay with much smaller stacks, but the syscall package
+       // makes it easy to call into arbitrary C code without cgo,
+       // and system calls even in "pure" Go code are actually C
+       // calls that may need more stack than we think.
+       //
+       // The default stack reserve size directly affects only the main
+       // thread, ctrlhandler thread, and profileloop thread. For
+       // these, it must be greater than the stack size assumed by
+       // externalthreadhandler.
+       //
+       // For other threads, the runtime explicitly asks the kernel
+       // to use the default stack size so that all stacks are
+       // consistent.
+       //
+       // At thread start, in minit, the runtime queries the OS for
+       // the actual stack bounds so that the stack size doesn't need
+       // to be hard-coded into the runtime.
+       oh64.SizeOfStackReserve = 0x00200000
+       if !iscgo {
+               oh64.SizeOfStackCommit = 0x00001000
+       } else {
+               // TODO(brainman): Maybe remove optional header writing altogether for cgo.
+               // For cgo it is the external linker that is building final executable.
+               // And it probably does not use any information stored in optional header.
+               oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages
+       }
+
+       oh.SizeOfStackReserve = 0x00100000
+       if !iscgo {
+               oh.SizeOfStackCommit = 0x00001000
+       } else {
+               oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages
+       }
+
+       oh64.SizeOfHeapReserve = 0x00100000
+       oh.SizeOfHeapReserve = 0x00100000
+       oh64.SizeOfHeapCommit = 0x00001000
+       oh.SizeOfHeapCommit = 0x00001000
+       oh64.NumberOfRvaAndSizes = 16
+       oh.NumberOfRvaAndSizes = 16
+
+       if pe64 != 0 {
+               oh64.DataDirectory = f.dataDirectory
+       } else {
+               oh.DataDirectory = f.dataDirectory
+       }
+
+       if pe64 != 0 {
+               binary.Write(ctxt.Out, binary.LittleEndian, &oh64)
+       } else {
+               binary.Write(ctxt.Out, binary.LittleEndian, &oh)
+       }
+}
+
+var pefile peFile
+
+func Peinit(ctxt *Link) {
+       var l int
+
+       switch ctxt.Arch.Family {
+       // 64-bit architectures
+       case sys.AMD64:
+               pe64 = 1
+               var oh64 pe.OptionalHeader64
+               l = binary.Size(&oh64)
+
+       // 32-bit architectures
+       default:
+               var oh pe.OptionalHeader32
+               l = binary.Size(&oh)
+
+       }
+
+       if ctxt.LinkMode == LinkExternal {
+               // .rdata section will contain "masks" and "shifts" symbols, and they
+               // need to be aligned to 16-bytes. So make all sections aligned
+               // to 32-byte and mark them all IMAGE_SCN_ALIGN_32BYTES so external
+               // linker will honour that requirement.
+               PESECTALIGN = 32
+               PEFILEALIGN = 0
+       }
+
+       var sh [16]pe.SectionHeader32
+       var fh pe.FileHeader
+       PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN))
+       if ctxt.LinkMode != LinkExternal {
+               PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN))
+       } else {
+               PESECTHEADR = 0
+       }
+       pefile.nextSectOffset = uint32(PESECTHEADR)
+       pefile.nextFileOffset = uint32(PEFILEHEADR)
+
+       if ctxt.LinkMode == LinkInternal {
+               // some mingw libs depend on this symbol, for example, FindPESectionByName
+               ctxt.xdefine("__image_base__", sym.SDATA, PEBASE)
+               ctxt.xdefine("_image_base__", sym.SDATA, PEBASE)
+       }
+
+       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 {
+       var d *Dll
+
+       dr = nil
+       var m *Imp
+       for _, s := range ctxt.Syms.Allsym {
+               if !s.Attr.Reachable() || s.Type != sym.SDYNIMPORT {
+                       continue
+               }
+               for d = dr; d != nil; d = d.next {
+                       if d.name == s.Dynimplib() {
+                               m = new(Imp)
+                               break
+                       }
+               }
+
+               if d == nil {
+                       d = new(Dll)
+                       d.name = s.Dynimplib()
+                       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 := s.Extname()
+               if i := strings.IndexByte(extName, '%'); i >= 0 {
+                       var err error
+                       m.argsize, err = strconv.Atoi(extName[i+1:])
+                       if err != nil {
+                               Errorf(s, "failed to parse stdcall decoration: %v", err)
+                       }
+                       m.argsize *= ctxt.Arch.PtrSize
+                       s.SetExtname(extName[:i])
+               }
+
+               m.s = s
+               m.next = d.ms
+               d.ms = m
+       }
+
+       if ctxt.LinkMode == LinkExternal {
+               // Add real symbol name
+               for d := dr; d != nil; d = d.next {
+                       for m = d.ms; m != nil; m = m.next {
+                               m.s.Type = sym.SDATA
+                               m.s.Grow(int64(ctxt.Arch.PtrSize))
+                               dynName := m.s.Extname()
+                               // only windows/386 requires stdcall decoration
+                               if ctxt.Arch.Family == sys.I386 && m.argsize >= 0 {
+                                       dynName += fmt.Sprintf("@%d", m.argsize)
+                               }
+                               dynSym := ctxt.Syms.Lookup(dynName, 0)
+                               dynSym.Attr |= sym.AttrReachable
+                               dynSym.Type = sym.SHOSTOBJ
+                               r := m.s.AddRel()
+                               r.Sym = dynSym
+                               r.Off = 0
+                               r.Siz = uint8(ctxt.Arch.PtrSize)
+                               r.Type = objabi.R_ADDR
+                       }
+               }
+       } else {
+               dynamic := ctxt.Syms.Lookup(".windynamic", 0)
+               dynamic.Attr |= sym.AttrReachable
+               dynamic.Type = sym.SWINDOWS
+               for d := dr; d != nil; d = d.next {
+                       for m = d.ms; m != nil; m = m.next {
+                               m.s.Type = sym.SWINDOWS
+                               m.s.Attr |= sym.AttrSubSymbol
+                               m.s.Sub = dynamic.Sub
+                               dynamic.Sub = m.s
+                               m.s.Value = dynamic.Size
+                               dynamic.Size += int64(ctxt.Arch.PtrSize)
+                       }
+
+                       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) {
+       startoff := ctxt.Out.Offset()
+       dynamic := ctxt.Syms.Lookup(".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, m.s.Extname())
+               }
+       }
+
+       // 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(dynamic.Value) - uint64(datsect.virtualAddress) - PEBASE
+
+       ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase))
+       for d := dr; d != nil; d = d.next {
+               for m := d.ms; m != nil; m = m.next {
+                       if pe64 != 0 {
+                               ctxt.Out.Write64(m.off)
+                       } else {
+                               ctxt.Out.Write32(uint32(m.off))
+                       }
+               }
+
+               if pe64 != 0 {
+                       ctxt.Out.Write64(0)
+               } else {
+                       ctxt.Out.Write32(0)
+               }
+       }
+
+       // finally write import descriptor table
+       out := ctxt.Out
+       out.SeekSet(startoff)
+
+       for d := dr; d != nil; d = d.next {
+               out.Write32(uint32(uint64(isect.virtualAddress) + oftbase + d.thunkoff))
+               out.Write32(0)
+               out.Write32(0)
+               out.Write32(uint32(uint64(isect.virtualAddress) + d.nameoff))
+               out.Write32(uint32(uint64(datsect.virtualAddress) + ftbase + d.thunkoff))
+       }
+
+       out.Write32(0) //end
+       out.Write32(0)
+       out.Write32(0)
+       out.Write32(0)
+       out.Write32(0)
+
+       // update data directory
+       pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress
+       pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize
+       pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE)
+       pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size)
+
+       out.SeekSet(endoff)
+}
+
+type byExtname []*sym.Symbol
+
+func (s byExtname) Len() int           { return len(s) }
+func (s byExtname) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s byExtname) Less(i, j int) bool { return s[i].Extname() < s[j].Extname() }
+
+func initdynexport(ctxt *Link) {
+       nexport = 0
+       for _, s := range ctxt.Syms.Allsym {
+               if !s.Attr.Reachable() || !s.Attr.CgoExportDynamic() {
+                       continue
+               }
+               if nexport+1 > len(dexport) {
+                       Errorf(s, "pe dynexport table is full")
+                       errorexit()
+               }
+
+               dexport[nexport] = s
+               nexport++
+       }
+
+       sort.Sort(byExtname(dexport[:nexport]))
+}
+
+func addexports(ctxt *Link) {
+       var e IMAGE_EXPORT_DIRECTORY
+
+       size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1
+       for i := 0; i < nexport; i++ {
+               size += len(dexport[i].Extname()) + 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 i := 0; i < nexport; i++ {
+               out.Write32(uint32(dexport[i].Value - PEBASE))
+       }
+
+       // put EXPORT Name Pointer Table
+       v := int(e.Name + uint32(len(*flagOutfile)) + 1)
+
+       for i := 0; i < nexport; i++ {
+               out.Write32(uint32(v))
+               v += len(dexport[i].Extname()) + 1
+       }
+
+       // put EXPORT Ordinal Table
+       for i := 0; i < nexport; i++ {
+               out.Write16(uint16(i))
+       }
+
+       // put Names
+       out.WriteStringN(*flagOutfile, len(*flagOutfile)+1)
+
+       for i := 0; i < nexport; i++ {
+               out.WriteStringN(dexport[i].Extname(), len(dexport[i].Extname())+1)
+       }
+       sect.pad(out, uint32(size))
+}
+
+// peBaseRelocEntry represents a single relocation entry.
+type peBaseRelocEntry struct {
+       typeOff uint16
+       rel     *sym.Reloc
+       sym     *sym.Symbol // For debug
+}
+
+// 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(ctxt *Link, s *sym.Symbol, r *sym.Reloc) {
+       // pageSize is the size in bytes of a page
+       // described by a base relocation block.
+       const pageSize = 0x1000
+       const pageMask = pageSize - 1
+
+       addr := s.Value + 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),
+               rel:     r,
+               sym:     s,
+       }
+
+       // 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(ctxt *Link, s *sym.Symbol, rt *peBaseRelocTable) {
+       for ri := 0; ri < len(s.R); ri++ {
+               r := &s.R[ri]
+
+               if r.Sym == nil {
+                       continue
+               }
+               if !r.Sym.Attr.Reachable() {
+                       continue
+               }
+               if r.Type >= objabi.ElfRelocOffset {
+                       continue
+               }
+               if r.Siz == 0 { // informational relocation
+                       continue
+               }
+               if r.Type == objabi.R_DWARFFILEREF {
+                       continue
+               }
+
+               switch r.Type {
+               default:
+               case objabi.R_ADDR:
+                       rt.addentry(ctxt, s, r)
+               }
+       }
+}
+
+func addPEBaseReloc(ctxt *Link) {
+       // Arm does not work without base relocation table.
+       // 386 and amd64 will only require the table for BuildModePIE.
+       switch ctxt.Arch.Family {
+       default:
+               return
+       case sys.I386, sys.AMD64:
+               if ctxt.BuildMode != BuildModePIE {
+                       return
+               }
+       case sys.ARM:
+       }
+
+       var rt peBaseRelocTable
+       rt.init(ctxt)
+
+       // Get relocation information
+       for _, s := range ctxt.Textp {
+               addPEBaseRelocSym(ctxt, s, &rt)
+       }
+       for _, s := range datap {
+               addPEBaseRelocSym(ctxt, 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, sym *sym.Symbol) {
+       if rsrcsym != nil {
+               Errorf(sym, "too many .rsrc sections")
+       }
+
+       rsrcsym = sym
+}
+
+func addpersrc(ctxt *Link) {
+       if rsrcsym == nil {
+               return
+       }
+
+       h := pefile.addSection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size))
+       h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA
+       h.checkOffset(ctxt.Out.Offset())
+
+       // relocation
+       for ri := range rsrcsym.R {
+               r := &rsrcsym.R[ri]
+               p := rsrcsym.P[r.Off:]
+               val := uint32(int64(h.virtualAddress) + r.Add)
+
+               // 32-bit little-endian
+               p[0] = byte(val)
+
+               p[1] = byte(val >> 8)
+               p[2] = byte(val >> 16)
+               p[3] = byte(val >> 24)
+       }
+
+       ctxt.Out.Write(rsrcsym.P)
+       h.pad(ctxt.Out, uint32(rsrcsym.Size))
+
+       // update data directory
+       pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
+
+       pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize
+}
+
+func Asmbpe(ctxt *Link) {
+       switch ctxt.Arch.Family {
+       default:
+               Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
+       case sys.AMD64, sys.I386, sys.ARM:
+       }
+
+       t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length))
+       t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
+       if ctxt.LinkMode == LinkExternal {
+               // some data symbols (e.g. masks) end up in the .text section, and they normally
+               // expect larger alignment requirement than the default text section alignment.
+               t.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+       }
+       t.checkSegment(&Segtext)
+       pefile.textSect = t
+
+       ro := pefile.addSection(".rdata", int(Segrodata.Length), int(Segrodata.Length))
+       ro.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+       if ctxt.LinkMode == LinkExternal {
+               // some data symbols (e.g. masks) end up in the .rdata section, and they normally
+               // expect larger alignment requirement than the default text section alignment.
+               ro.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+       }
+       ro.checkSegment(&Segrodata)
+       pefile.rdataSect = ro
+
+       var d *peSection
+       if ctxt.LinkMode != LinkExternal {
+               d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen))
+               d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
+               d.checkSegment(&Segdata)
+               pefile.dataSect = d
+       } else {
+               d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen))
+               d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
+               d.checkSegment(&Segdata)
+               pefile.dataSect = d
+
+               b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0)
+               b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
+               b.pointerToRawData = 0
+               pefile.bssSect = b
+       }
+
+       pefile.addDWARF()
+
+       if ctxt.LinkMode == LinkExternal {
+               pefile.ctorsSect = pefile.addInitArray(ctxt)
+       }
+
+       ctxt.Out.SeekSet(int64(pefile.nextFileOffset))
+       if ctxt.LinkMode != LinkExternal {
+               addimports(ctxt, d)
+               addexports(ctxt)
+               addPEBaseReloc(ctxt)
+       }
+       pefile.writeSymbolTableAndStringTable(ctxt)
+       addpersrc(ctxt)
+       if ctxt.LinkMode == LinkExternal {
+               pefile.emitRelocations(ctxt)
+       }
+
+       pewrite(ctxt)
+}
diff --git a/src/cmd/oldlink/internal/ld/sym.go b/src/cmd/oldlink/internal/ld/sym.go
new file mode 100644 (file)
index 0000000..c0f725c
--- /dev/null
@@ -0,0 +1,115 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/sym"
+       "log"
+)
+
+func linknew(arch *sys.Arch) *Link {
+       ctxt := &Link{
+               Syms:         sym.NewSymbols(),
+               Out:          &OutBuf{arch: arch},
+               Arch:         arch,
+               LibraryByPkg: make(map[string]*sym.Library),
+       }
+
+       if objabi.GOARCH != arch.Name {
+               log.Fatalf("invalid objabi.GOARCH %s (want %s)", objabi.GOARCH, arch.Name)
+       }
+
+       AtExit(func() {
+               if nerrors > 0 && ctxt.Out.f != nil {
+                       ctxt.Out.f.Close()
+                       mayberemoveoutfile()
+               }
+       })
+
+       return ctxt
+}
+
+// computeTLSOffset records the thread-local storage offset.
+// Not used for Android where the TLS offset is determined at runtime.
+func (ctxt *Link) computeTLSOffset() {
+       switch ctxt.HeadType {
+       default:
+               log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType)
+
+       case objabi.Hplan9, objabi.Hwindows, objabi.Hjs, objabi.Haix:
+               break
+
+       case objabi.Hlinux,
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd,
+               objabi.Hdragonfly,
+               objabi.Hsolaris:
+               /*
+                * ELF uses TLS offset negative from FS.
+                * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS).
+                * Known to low-level assembly in package runtime and runtime/cgo.
+                */
+               ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize
+
+       case objabi.Hdarwin:
+               /*
+                * OS X system constants - offset from 0(GS) to our TLS.
+                */
+               switch ctxt.Arch.Family {
+               default:
+                       log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name)
+
+                       /*
+                        * For x86, Apple has reserved a slot in the TLS for Go. See issue 23617.
+                        * That slot is at offset 0x30 on amd64, and 0x18 on 386.
+                        * The slot will hold the G pointer.
+                        * These constants should match those in runtime/sys_darwin_{386,amd64}.s
+                        * and runtime/cgo/gcc_darwin_{386,amd64}.c.
+                        */
+               case sys.I386:
+                       ctxt.Tlsoffset = 0x18
+
+               case sys.AMD64:
+                       ctxt.Tlsoffset = 0x30
+
+               case sys.ARM:
+                       ctxt.Tlsoffset = 0 // dummy value, not needed
+
+               case sys.ARM64:
+                       ctxt.Tlsoffset = 0 // dummy value, not needed
+               }
+       }
+
+}
diff --git a/src/cmd/oldlink/internal/ld/symtab.go b/src/cmd/oldlink/internal/ld/symtab.go
new file mode 100644 (file)
index 0000000..a324fdf
--- /dev/null
@@ -0,0 +1,713 @@
+// Inferno utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/sym"
+       "fmt"
+       "path/filepath"
+       "strings"
+)
+
+// Symbol table.
+
+func putelfstr(s string) int {
+       if len(Elfstrdat) == 0 && s != "" {
+               // first entry must be empty string
+               putelfstr("")
+       }
+
+       off := len(Elfstrdat)
+       Elfstrdat = append(Elfstrdat, s...)
+       Elfstrdat = append(Elfstrdat, 0)
+       return off
+}
+
+func putelfsyment(out *OutBuf, off int, addr int64, size int64, info int, shndx int, other int) {
+       if elf64 {
+               out.Write32(uint32(off))
+               out.Write8(uint8(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(uint8(info))
+               out.Write8(uint8(other))
+               out.Write16(uint16(shndx))
+               Symsize += ELF32SYMSIZE
+       }
+}
+
+var numelfsym = 1 // 0 is reserved
+
+var elfbind int
+
+func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64, go_ *sym.Symbol) {
+       var typ int
+
+       switch t {
+       default:
+               return
+
+       case TextSym:
+               typ = STT_FUNC
+
+       case DataSym, BSSSym:
+               typ = STT_OBJECT
+
+       case UndefinedSym:
+               // ElfType is only set for symbols read from Go shared libraries, but
+               // for other symbols it is left as STT_NOTYPE which is fine.
+               typ = int(x.ElfType())
+
+       case TLSSym:
+               typ = STT_TLS
+       }
+
+       size := x.Size
+       if t == UndefinedSym {
+               size = 0
+       }
+
+       xo := x
+       for xo.Outer != nil {
+               xo = xo.Outer
+       }
+
+       var elfshnum int
+       if xo.Type == sym.SDYNIMPORT || xo.Type == sym.SHOSTOBJ || xo.Type == sym.SUNDEFEXT {
+               elfshnum = SHN_UNDEF
+       } else {
+               if xo.Sect == nil {
+                       Errorf(x, "missing section in putelfsym")
+                       return
+               }
+               if xo.Sect.Elfsect == nil {
+                       Errorf(x, "missing ELF section in putelfsym")
+                       return
+               }
+               elfshnum = xo.Sect.Elfsect.(*ElfShdr).shnum
+       }
+
+       // One pass for each binding: STB_LOCAL, STB_GLOBAL,
+       // maybe one day STB_WEAK.
+       bind := STB_GLOBAL
+
+       if x.IsFileLocal() || x.Attr.VisibilityHidden() || x.Attr.Local() {
+               bind = 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.LinkMode == LinkExternal && !x.Attr.CgoExportStatic() && elfshnum != SHN_UNDEF {
+               bind = STB_LOCAL
+       }
+
+       if ctxt.LinkMode == LinkExternal && elfshnum != SHN_UNDEF {
+               addr -= int64(xo.Sect.Vaddr)
+       }
+       other := STV_DEFAULT
+       if x.Attr.VisibilityHidden() {
+               // 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 = STV_HIDDEN
+       }
+       if ctxt.Arch.Family == sys.PPC64 && typ == STT_FUNC && x.Attr.Shared() && x.Name != "runtime.duffzero" && x.Name != "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)
+               s = strings.Replace(s, "·", ".", -1)
+       }
+
+       if ctxt.DynlinkingGo() && bind == STB_GLOBAL && elfbind == STB_LOCAL && x.Type == 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."+s), addr, size, STB_LOCAL<<4|typ&0xf, elfshnum, other)
+               x.LocalElfsym = int32(numelfsym)
+               numelfsym++
+               return
+       } else if bind != elfbind {
+               return
+       }
+
+       putelfsyment(ctxt.Out, putelfstr(s), addr, size, bind<<4|typ&0xf, elfshnum, other)
+       x.Elfsym = int32(numelfsym)
+       numelfsym++
+}
+
+func putelfsectionsym(out *OutBuf, s *sym.Symbol, shndx int) {
+       putelfsyment(out, 0, 0, 0, STB_LOCAL<<4|STT_SECTION, shndx, 0)
+       s.Elfsym = int32(numelfsym)
+       numelfsym++
+}
+
+func Asmelfsym(ctxt *Link) {
+       // the first symbol entry is reserved
+       putelfsyment(ctxt.Out, 0, 0, 0, STB_LOCAL<<4|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, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0)
+       numelfsym++
+
+       elfbind = STB_LOCAL
+       genasmsym(ctxt, putelfsym)
+
+       elfbind = STB_GLOBAL
+       elfglobalsymndx = numelfsym
+       genasmsym(ctxt, putelfsym)
+}
+
+func putplan9sym(ctxt *Link, x *sym.Symbol, s string, typ SymbolType, addr int64, go_ *sym.Symbol) {
+       t := int(typ)
+       switch typ {
+       case TextSym, DataSym, BSSSym:
+               if x.IsFileLocal() {
+                       t += 'a' - 'A'
+               }
+               fallthrough
+
+       case AutoSym, ParamSym, FrameSym:
+               l := 4
+               if ctxt.HeadType == objabi.Hplan9 && ctxt.Arch.Family == sys.AMD64 && !Flag8 {
+                       ctxt.Out.Write32b(uint32(addr >> 32))
+                       l = 8
+               }
+
+               ctxt.Out.Write32b(uint32(addr))
+               ctxt.Out.Write8(uint8(t + 0x80)) /* 0x80 is variable length */
+
+               ctxt.Out.WriteString(s)
+               ctxt.Out.Write8(0)
+
+               Symsize += int32(l) + 1 + int32(len(s)) + 1
+
+       default:
+               return
+       }
+}
+
+func Asmplan9sym(ctxt *Link) {
+       genasmsym(ctxt, putplan9sym)
+}
+
+var symt *sym.Symbol
+
+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.
+
+func textsectionmap(ctxt *Link) uint32 {
+
+       t := ctxt.Syms.Lookup("runtime.textsectionmap", 0)
+       t.Type = sym.SRODATA
+       t.Attr |= sym.AttrReachable
+       nsections := int64(0)
+
+       for _, sect := range Segtext.Sections {
+               if sect.Name == ".text" {
+                       nsections++
+               } else {
+                       break
+               }
+       }
+       t.Grow(3 * nsections * int64(ctxt.Arch.PtrSize))
+
+       off := int64(0)
+       n := 0
+
+       // The vaddr for each text section is the difference between the section's
+       // Vaddr and the Vaddr for the first text section as determined at compile
+       // time.
+
+       // The symbol for the first text section is named runtime.text as before.
+       // Additional text sections are named runtime.text.n where n is the
+       // order of creation starting with 1. These symbols provide the section's
+       // address after relocation by the linker.
+
+       textbase := Segtext.Sections[0].Vaddr
+       for _, sect := range Segtext.Sections {
+               if sect.Name != ".text" {
+                       break
+               }
+               off = t.SetUint(ctxt.Arch, off, sect.Vaddr-textbase)
+               off = t.SetUint(ctxt.Arch, off, sect.Length)
+               if n == 0 {
+                       s := ctxt.Syms.ROLookup("runtime.text", 0)
+                       if s == nil {
+                               Errorf(nil, "Unable to find symbol runtime.text\n")
+                       }
+                       off = t.SetAddr(ctxt.Arch, off, s)
+
+               } else {
+                       s := ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+                       if s == nil {
+                               Errorf(nil, "Unable to find symbol runtime.text.%d\n", n)
+                       }
+                       off = t.SetAddr(ctxt.Arch, off, s)
+               }
+               n++
+       }
+       return uint32(n)
+}
+
+func (ctxt *Link) symtab() {
+       switch ctxt.BuildMode {
+       case BuildModeCArchive, BuildModeCShared:
+               for _, s := range ctxt.Syms.Allsym {
+                       // Create a new entry in the .init_array section that points to the
+                       // library initializer function.
+                       if s.Name == *flagEntrySymbol && ctxt.HeadType != objabi.Haix {
+                               addinitarrdata(ctxt, s)
+                       }
+               }
+       }
+
+       // Define these so that they'll get put into the symbol table.
+       // data.c:/^address will provide the actual values.
+       ctxt.xdefine("runtime.text", sym.STEXT, 0)
+
+       ctxt.xdefine("runtime.etext", sym.STEXT, 0)
+       ctxt.xdefine("runtime.itablink", sym.SRODATA, 0)
+       ctxt.xdefine("runtime.eitablink", sym.SRODATA, 0)
+       ctxt.xdefine("runtime.rodata", sym.SRODATA, 0)
+       ctxt.xdefine("runtime.erodata", sym.SRODATA, 0)
+       ctxt.xdefine("runtime.types", sym.SRODATA, 0)
+       ctxt.xdefine("runtime.etypes", sym.SRODATA, 0)
+       ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, 0)
+       ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, 0)
+       ctxt.xdefine("runtime.data", sym.SDATA, 0)
+       ctxt.xdefine("runtime.edata", sym.SDATA, 0)
+       ctxt.xdefine("runtime.bss", sym.SBSS, 0)
+       ctxt.xdefine("runtime.ebss", sym.SBSS, 0)
+       ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, 0)
+       ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, 0)
+       ctxt.xdefine("runtime.end", sym.SBSS, 0)
+       ctxt.xdefine("runtime.epclntab", sym.SRODATA, 0)
+       ctxt.xdefine("runtime.esymtab", sym.SRODATA, 0)
+
+       // garbage collection symbols
+       s := ctxt.Syms.Lookup("runtime.gcdata", 0)
+
+       s.Type = sym.SRODATA
+       s.Size = 0
+       s.Attr |= sym.AttrReachable
+       ctxt.xdefine("runtime.egcdata", sym.SRODATA, 0)
+
+       s = ctxt.Syms.Lookup("runtime.gcbss", 0)
+       s.Type = sym.SRODATA
+       s.Size = 0
+       s.Attr |= sym.AttrReachable
+       ctxt.xdefine("runtime.egcbss", sym.SRODATA, 0)
+
+       // pseudo-symbols to mark locations of type, string, and go string data.
+       var symtype *sym.Symbol
+       var symtyperel *sym.Symbol
+       if !ctxt.DynlinkingGo() {
+               if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
+                       s = ctxt.Syms.Lookup("type.*", 0)
+
+                       s.Type = sym.STYPE
+                       s.Size = 0
+                       s.Attr |= sym.AttrReachable
+                       symtype = s
+
+                       s = ctxt.Syms.Lookup("typerel.*", 0)
+
+                       s.Type = sym.STYPERELRO
+                       s.Size = 0
+                       s.Attr |= sym.AttrReachable
+                       symtyperel = s
+               } else {
+                       s = ctxt.Syms.Lookup("type.*", 0)
+
+                       s.Type = sym.STYPE
+                       s.Size = 0
+                       s.Attr |= sym.AttrReachable
+                       symtype = s
+                       symtyperel = s
+               }
+       }
+
+       groupSym := func(name string, t sym.SymKind) *sym.Symbol {
+               s := ctxt.Syms.Lookup(name, 0)
+               s.Type = t
+               s.Size = 0
+               s.Attr |= sym.AttrLocal | sym.AttrReachable
+               return s
+       }
+       var (
+               symgostring = groupSym("go.string.*", sym.SGOSTRING)
+               symgofunc   = groupSym("go.func.*", sym.SGOFUNC)
+               symgcbits   = groupSym("runtime.gcbits.*", sym.SGCBITS)
+       )
+
+       var symgofuncrel *sym.Symbol
+       if !ctxt.DynlinkingGo() {
+               if ctxt.UseRelro() {
+                       symgofuncrel = groupSym("go.funcrel.*", sym.SGOFUNCRELRO)
+               } else {
+                       symgofuncrel = symgofunc
+               }
+       }
+
+       symitablink := ctxt.Syms.Lookup("runtime.itablink", 0)
+       symitablink.Type = sym.SITABLINK
+
+       symt = ctxt.Syms.Lookup("runtime.symtab", 0)
+       symt.Attr |= sym.AttrLocal
+       symt.Type = sym.SSYMTAB
+       symt.Size = 0
+       symt.Attr |= sym.AttrReachable
+
+       nitablinks := 0
+
+       // 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.
+       for _, s := range ctxt.Syms.Allsym {
+               if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) {
+                       s.Attr |= sym.AttrNotInSymbolTable
+               }
+
+               if !s.Attr.Reachable() || s.Attr.Special() || s.Type != sym.SRODATA {
+                       continue
+               }
+
+               switch {
+               case strings.HasPrefix(s.Name, "type."):
+                       if !ctxt.DynlinkingGo() {
+                               s.Attr |= sym.AttrNotInSymbolTable
+                       }
+                       if ctxt.UseRelro() {
+                               s.Type = sym.STYPERELRO
+                               s.Outer = symtyperel
+                       } else {
+                               s.Type = sym.STYPE
+                               s.Outer = symtype
+                       }
+
+               case strings.HasPrefix(s.Name, "go.importpath.") && ctxt.UseRelro():
+                       // Keep go.importpath symbols in the same section as types and
+                       // names, as they can be referred to by a section offset.
+                       s.Type = sym.STYPERELRO
+
+               case strings.HasPrefix(s.Name, "go.itablink."):
+                       nitablinks++
+                       s.Type = sym.SITABLINK
+                       s.Attr |= sym.AttrNotInSymbolTable
+                       s.Outer = symitablink
+
+               case strings.HasPrefix(s.Name, "go.string."):
+                       s.Type = sym.SGOSTRING
+                       s.Attr |= sym.AttrNotInSymbolTable
+                       s.Outer = symgostring
+
+               case strings.HasPrefix(s.Name, "runtime.gcbits."):
+                       s.Type = sym.SGCBITS
+                       s.Attr |= sym.AttrNotInSymbolTable
+                       s.Outer = symgcbits
+
+               case strings.HasSuffix(s.Name, "·f"):
+                       if !ctxt.DynlinkingGo() {
+                               s.Attr |= sym.AttrNotInSymbolTable
+                       }
+                       if ctxt.UseRelro() {
+                               s.Type = sym.SGOFUNCRELRO
+                               s.Outer = symgofuncrel
+                       } else {
+                               s.Type = sym.SGOFUNC
+                               s.Outer = symgofunc
+                       }
+
+               case strings.HasPrefix(s.Name, "gcargs."),
+                       strings.HasPrefix(s.Name, "gclocals."),
+                       strings.HasPrefix(s.Name, "gclocals·"),
+                       strings.HasPrefix(s.Name, "inltree."),
+                       strings.HasSuffix(s.Name, ".opendefer"):
+                       s.Type = sym.SGOFUNC
+                       s.Attr |= sym.AttrNotInSymbolTable
+                       s.Outer = symgofunc
+                       s.Align = 4
+                       liveness += (s.Size + int64(s.Align) - 1) &^ (int64(s.Align) - 1)
+               }
+       }
+
+       if ctxt.BuildMode == BuildModeShared {
+               abihashgostr := ctxt.Syms.Lookup("go.link.abihash."+filepath.Base(*flagOutfile), 0)
+               abihashgostr.Attr |= sym.AttrReachable
+               abihashgostr.Type = sym.SRODATA
+               hashsym := ctxt.Syms.Lookup("go.link.abihashbytes", 0)
+               abihashgostr.AddAddr(ctxt.Arch, hashsym)
+               abihashgostr.AddUint(ctxt.Arch, uint64(hashsym.Size))
+       }
+       if ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
+               for _, l := range ctxt.Library {
+                       s := ctxt.Syms.Lookup("go.link.pkghashbytes."+l.Pkg, 0)
+                       s.Attr |= sym.AttrReachable
+                       s.Type = sym.SRODATA
+                       s.Size = int64(len(l.Hash))
+                       s.P = []byte(l.Hash)
+                       str := ctxt.Syms.Lookup("go.link.pkghash."+l.Pkg, 0)
+                       str.Attr |= sym.AttrReachable
+                       str.Type = sym.SRODATA
+                       str.AddAddr(ctxt.Arch, s)
+                       str.AddUint(ctxt.Arch, uint64(len(l.Hash)))
+               }
+       }
+
+       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 := ctxt.Moduledata
+       // The pclntab slice
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0))
+       moduledata.AddUint(ctxt.Arch, uint64(ctxt.Syms.Lookup("runtime.pclntab", 0).Size))
+       moduledata.AddUint(ctxt.Arch, uint64(ctxt.Syms.Lookup("runtime.pclntab", 0).Size))
+       // The ftab slice
+       moduledata.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabPclntabOffset))
+       moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
+       moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
+       // The filetab slice
+       moduledata.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabFiletabOffset))
+       moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Filesyms))+1)
+       moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Filesyms))+1)
+       // findfunctab
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.findfunctab", 0))
+       // minpc, maxpc
+       moduledata.AddAddr(ctxt.Arch, pclntabFirstFunc)
+       moduledata.AddAddrPlus(ctxt.Arch, pclntabLastFunc, pclntabLastFunc.Size)
+       // pointers to specific parts of the module
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.text", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.etext", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.noptrdata", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.enoptrdata", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.data", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.edata", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.bss", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.ebss", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.noptrbss", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.enoptrbss", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.end", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.gcdata", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.gcbss", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.types", 0))
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.etypes", 0))
+
+       if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal {
+               // Add R_REF relocation to prevent ld's garbage collection of
+               // runtime.rodata, runtime.erodata and runtime.epclntab.
+               addRef := func(name string) {
+                       r := moduledata.AddRel()
+                       r.Sym = ctxt.Syms.Lookup(name, 0)
+                       r.Type = objabi.R_XCOFFREF
+                       r.Siz = uint8(ctxt.Arch.PtrSize)
+               }
+               addRef("runtime.rodata")
+               addRef("runtime.erodata")
+               addRef("runtime.epclntab")
+       }
+
+       // text section information
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.textsectionmap", 0))
+       moduledata.AddUint(ctxt.Arch, uint64(nsections))
+       moduledata.AddUint(ctxt.Arch, uint64(nsections))
+
+       // The typelinks slice
+       typelinkSym := ctxt.Syms.Lookup("runtime.typelink", 0)
+       ntypelinks := uint64(typelinkSym.Size) / 4
+       moduledata.AddAddr(ctxt.Arch, typelinkSym)
+       moduledata.AddUint(ctxt.Arch, ntypelinks)
+       moduledata.AddUint(ctxt.Arch, ntypelinks)
+       // The itablinks slice
+       moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.itablink", 0))
+       moduledata.AddUint(ctxt.Arch, uint64(nitablinks))
+       moduledata.AddUint(ctxt.Arch, uint64(nitablinks))
+       // The ptab slice
+       if ptab := ctxt.Syms.ROLookup("go.plugin.tabs", 0); ptab != nil && ptab.Attr.Reachable() {
+               ptab.Attr |= sym.AttrLocal
+               ptab.Type = sym.SRODATA
+
+               nentries := uint64(len(ptab.P) / 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, moduledata, "go.link.thispluginpath", objabi.PathToPrefix(*flagPluginPath))
+
+               pkghashes := ctxt.Syms.Lookup("go.link.pkghashes", 0)
+               pkghashes.Attr |= sym.AttrReachable
+               pkghashes.Attr |= sym.AttrLocal
+               pkghashes.Type = sym.SRODATA
+
+               for i, l := range ctxt.Library {
+                       // pkghashes[i].name
+                       addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkgname.%d", i), l.Pkg)
+                       // pkghashes[i].linktimehash
+                       addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkglinkhash.%d", i), l.Hash)
+                       // pkghashes[i].runtimehash
+                       hash := ctxt.Syms.ROLookup("go.link.pkghash."+l.Pkg, 0)
+                       pkghashes.AddAddr(ctxt.Arch, hash)
+               }
+               moduledata.AddAddr(ctxt.Arch, pkghashes)
+               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, moduledata, "go.link.thismodulename", thismodulename)
+
+               modulehashes := ctxt.Syms.Lookup("go.link.abihashes", 0)
+               modulehashes.Attr |= sym.AttrReachable
+               modulehashes.Attr |= sym.AttrLocal
+               modulehashes.Type = sym.SRODATA
+
+               for i, shlib := range ctxt.Shlibs {
+                       // modulehashes[i].modulename
+                       modulename := filepath.Base(shlib.Path)
+                       addgostring(ctxt, modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename)
+
+                       // modulehashes[i].linktimehash
+                       addgostring(ctxt, modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash))
+
+                       // modulehashes[i].runtimehash
+                       abihash := ctxt.Syms.Lookup("go.link.abihash."+modulename, 0)
+                       abihash.Attr |= sym.AttrReachable
+                       modulehashes.AddAddr(ctxt.Arch, abihash)
+               }
+
+               moduledata.AddAddr(ctxt.Arch, modulehashes)
+               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 := ctxt.Syms.ROLookup("type.runtime.moduledata", 0)
+       moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype.P)
+       moduledata.Grow(moduledata.Size)
+
+       lastmoduledatap := ctxt.Syms.Lookup("runtime.lastmoduledatap", 0)
+       if lastmoduledatap.Type != sym.SDYNIMPORT {
+               lastmoduledatap.Type = sym.SNOPTRDATA
+               lastmoduledatap.Size = 0 // overwrite existing value
+               lastmoduledatap.AddAddr(ctxt.Arch, moduledata)
+       }
+}
+
+func isStaticTemp(name string) bool {
+       if i := strings.LastIndex(name, "/"); i >= 0 {
+               name = name[i:]
+       }
+       return strings.Contains(name, "..stmp_")
+}
diff --git a/src/cmd/oldlink/internal/ld/testdata/httptest/main/main.go b/src/cmd/oldlink/internal/ld/testdata/httptest/main/main.go
new file mode 100644 (file)
index 0000000..1bce301
--- /dev/null
@@ -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/oldlink/internal/ld/testdata/issue10978/main.go b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.go
new file mode 100644 (file)
index 0000000..5e8c097
--- /dev/null
@@ -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/oldlink/internal/ld/testdata/issue10978/main.s b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.s
new file mode 100644 (file)
index 0000000..1d00e76
--- /dev/null
@@ -0,0 +1 @@
+// This file is needed to make "go build" work for package with external functions.\r
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go b/src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go
new file mode 100644 (file)
index 0000000..6032d76
--- /dev/null
@@ -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/oldlink/internal/ld/testdata/issue25459/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go
new file mode 100644 (file)
index 0000000..3f5f365
--- /dev/null
@@ -0,0 +1,10 @@
+package main
+
+import "cmd/oldlink/internal/ld/testdata/issue25459/a"
+
+var Glob int
+
+func main() {
+       a.Another()
+       Glob += a.ConstIf() + a.CallConstIf()
+}
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue26237/b.dir/b.go b/src/cmd/oldlink/internal/ld/testdata/issue26237/b.dir/b.go
new file mode 100644 (file)
index 0000000..ca57749
--- /dev/null
@@ -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/oldlink/internal/ld/testdata/issue26237/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go
new file mode 100644 (file)
index 0000000..88b54f1
--- /dev/null
@@ -0,0 +1,16 @@
+package main
+
+import (
+       "fmt"
+
+       b "cmd/oldlink/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/oldlink/internal/ld/testdata/issue32233/lib/ObjC.m b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/ObjC.m
new file mode 100644 (file)
index 0000000..9462788
--- /dev/null
@@ -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/oldlink/internal/ld/testdata/issue32233/lib/lib.go b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go
new file mode 100644 (file)
index 0000000..514b9b9
--- /dev/null
@@ -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/oldlink/internal/ld/testdata/issue32233/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go
new file mode 100644 (file)
index 0000000..9eec6cc
--- /dev/null
@@ -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/oldlink/internal/ld/testdata/issue32233/lib"
+
+func main() {
+       lib.DoC()
+}
diff --git a/src/cmd/oldlink/internal/ld/typelink.go b/src/cmd/oldlink/internal/ld/typelink.go
new file mode 100644 (file)
index 0000000..07d04bb
--- /dev/null
@@ -0,0 +1,49 @@
+// 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/oldlink/internal/sym"
+       "sort"
+)
+
+type byTypeStr []typelinkSortKey
+
+type typelinkSortKey struct {
+       TypeStr string
+       Type    *sym.Symbol
+}
+
+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() {
+       typelinks := byTypeStr{}
+       for _, s := range ctxt.Syms.Allsym {
+               if s.Attr.Reachable() && s.Attr.MakeTypelink() {
+                       typelinks = append(typelinks, typelinkSortKey{decodetypeStr(ctxt.Arch, s), s})
+               }
+       }
+       sort.Sort(typelinks)
+
+       tl := ctxt.Syms.Lookup("runtime.typelink", 0)
+       tl.Type = sym.STYPELINK
+       tl.Attr |= sym.AttrReachable | sym.AttrLocal
+       tl.Size = int64(4 * len(typelinks))
+       tl.P = make([]byte, tl.Size)
+       tl.R = make([]sym.Reloc, len(typelinks))
+       for i, s := range typelinks {
+               r := &tl.R[i]
+               r.Sym = s.Type
+               r.Off = int32(i * 4)
+               r.Siz = 4
+               r.Type = objabi.R_ADDROFF
+       }
+}
diff --git a/src/cmd/oldlink/internal/ld/util.go b/src/cmd/oldlink/internal/ld/util.go
new file mode 100644 (file)
index 0000000..97c9a44
--- /dev/null
@@ -0,0 +1,97 @@
+// 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/oldlink/internal/sym"
+       "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)
+}
+
+// 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.
+func Errorf(s *sym.Symbol, format string, args ...interface{}) {
+       if s != nil {
+               format = s.Name + ": " + format
+       }
+       format += "\n"
+       fmt.Fprintf(os.Stderr, format, args...)
+       nerrors++
+       if *flagH {
+               panic("error")
+       }
+       if nerrors > 20 {
+               Exitf("too many errors")
+       }
+}
+
+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
+}
+
+// implements sort.Interface, for sorting symbols by name.
+type byName []*sym.Symbol
+
+func (s byName) Len() int           { return len(s) }
+func (s byName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name }
diff --git a/src/cmd/oldlink/internal/ld/xcoff.go b/src/cmd/oldlink/internal/ld/xcoff.go
new file mode 100644 (file)
index 0000000..4d66d6d
--- /dev/null
@@ -0,0 +1,1685 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+       "bytes"
+       "cmd/internal/objabi"
+       "cmd/oldlink/internal/sym"
+       "encoding/binary"
+       "io/ioutil"
+       "math/bits"
+       "path/filepath"
+       "sort"
+       "strings"
+)
+
+// This file handles all algorithms related to XCOFF files generation.
+// Most of them are adaptations of the ones in  cmd/oldlink/internal/pe.go
+// as PE and XCOFF are based on COFF files.
+// XCOFF files generated are 64 bits.
+
+const (
+       // Total amount of space to reserve at the start of the file
+       // for File Header, Auxiliary Header, and Section Headers.
+       // May waste some.
+       XCOFFHDRRESERVE       = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23
+       XCOFFSECTALIGN  int64 = 32 // base on dump -o
+
+       // XCOFF binaries should normally have all its sections position-independent.
+       // However, this is not yet possible for .text because of some R_ADDR relocations
+       // inside RODATA symbols.
+       // .data and .bss are position-independent so their address start inside a unreachable
+       // segment during execution to force segfault if something is wrong.
+       XCOFFTEXTBASE = 0x100000000 // Start of text address
+       XCOFFDATABASE = 0x200000000 // Start of data address
+)
+
+// File Header
+type XcoffFileHdr64 struct {
+       Fmagic   uint16 // Target machine
+       Fnscns   uint16 // Number of sections
+       Ftimedat int32  // Time and date of file creation
+       Fsymptr  uint64 // Byte offset to symbol table start
+       Fopthdr  uint16 // Number of bytes in optional header
+       Fflags   uint16 // Flags
+       Fnsyms   int32  // Number of entries in symbol table
+}
+
+const (
+       U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF
+)
+
+// Flags that describe the type of the object file.
+const (
+       F_RELFLG    = 0x0001
+       F_EXEC      = 0x0002
+       F_LNNO      = 0x0004
+       F_FDPR_PROF = 0x0010
+       F_FDPR_OPTI = 0x0020
+       F_DSA       = 0x0040
+       F_VARPG     = 0x0100
+       F_DYNLOAD   = 0x1000
+       F_SHROBJ    = 0x2000
+       F_LOADONLY  = 0x4000
+)
+
+// Auxiliary Header
+type XcoffAoutHdr64 struct {
+       Omagic      int16    // Flags - Ignored If Vstamp Is 1
+       Ovstamp     int16    // Version
+       Odebugger   uint32   // Reserved For Debugger
+       Otextstart  uint64   // Virtual Address Of Text
+       Odatastart  uint64   // Virtual Address Of Data
+       Otoc        uint64   // Toc Address
+       Osnentry    int16    // Section Number For Entry Point
+       Osntext     int16    // Section Number For Text
+       Osndata     int16    // Section Number For Data
+       Osntoc      int16    // Section Number For Toc
+       Osnloader   int16    // Section Number For Loader
+       Osnbss      int16    // Section Number For Bss
+       Oalgntext   int16    // Max Text Alignment
+       Oalgndata   int16    // Max Data Alignment
+       Omodtype    [2]byte  // Module Type Field
+       Ocpuflag    uint8    // Bit Flags - Cputypes Of Objects
+       Ocputype    uint8    // Reserved for CPU type
+       Otextpsize  uint8    // Requested text page size
+       Odatapsize  uint8    // Requested data page size
+       Ostackpsize uint8    // Requested stack page size
+       Oflags      uint8    // Flags And TLS Alignment
+       Otsize      uint64   // Text Size In Bytes
+       Odsize      uint64   // Data Size In Bytes
+       Obsize      uint64   // Bss Size In Bytes
+       Oentry      uint64   // Entry Point Address
+       Omaxstack   uint64   // Max Stack Size Allowed
+       Omaxdata    uint64   // Max Data Size Allowed
+       Osntdata    int16    // Section Number For Tdata Section
+       Osntbss     int16    // Section Number For Tbss Section
+       Ox64flags   uint16   // Additional Flags For 64-Bit Objects
+       Oresv3a     int16    // Reserved
+       Oresv3      [2]int32 // Reserved
+}
+
+// Section Header
+type XcoffScnHdr64 struct {
+       Sname    [8]byte // Section Name
+       Spaddr   uint64  // Physical Address
+       Svaddr   uint64  // Virtual Address
+       Ssize    uint64  // Section Size
+       Sscnptr  uint64  // File Offset To Raw Data
+       Srelptr  uint64  // File Offset To Relocation
+       Slnnoptr uint64  // File Offset To Line Numbers
+       Snreloc  uint32  // Number Of Relocation Entries
+       Snlnno   uint32  // Number Of Line Number Entries
+       Sflags   uint32  // flags
+}
+
+// Flags defining the section type.
+const (
+       STYP_DWARF  = 0x0010
+       STYP_TEXT   = 0x0020
+       STYP_DATA   = 0x0040
+       STYP_BSS    = 0x0080
+       STYP_EXCEPT = 0x0100
+       STYP_INFO   = 0x0200
+       STYP_TDATA  = 0x0400
+       STYP_TBSS   = 0x0800
+       STYP_LOADER = 0x1000
+       STYP_DEBUG  = 0x2000
+       STYP_TYPCHK = 0x4000
+       STYP_OVRFLO = 0x8000
+)
+const (
+       SSUBTYP_DWINFO  = 0x10000 // DWARF info section
+       SSUBTYP_DWLINE  = 0x20000 // DWARF line-number section
+       SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section
+       SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section
+       SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section
+       SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section
+       SSUBTYP_DWSTR   = 0x70000 // DWARF strings section
+       SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section
+       SSUBTYP_DWLOC   = 0x90000 // DWARF location lists section
+       SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section
+       SSUBTYP_DWMAC   = 0xB0000 // DWARF macros section
+)
+
+// Headers size
+const (
+       FILHSZ_32      = 20
+       FILHSZ_64      = 24
+       AOUTHSZ_EXEC32 = 72
+       AOUTHSZ_EXEC64 = 120
+       SCNHSZ_32      = 40
+       SCNHSZ_64      = 72
+       LDHDRSZ_32     = 32
+       LDHDRSZ_64     = 56
+       LDSYMSZ_64     = 24
+       RELSZ_64       = 14
+)
+
+// Type representing all XCOFF symbols.
+type xcoffSym interface {
+}
+
+// Symbol Table Entry
+type XcoffSymEnt64 struct {
+       Nvalue  uint64 // Symbol value
+       Noffset uint32 // Offset of the name in string table or .debug section
+       Nscnum  int16  // Section number of symbol
+       Ntype   uint16 // Basic and derived type specification
+       Nsclass uint8  // Storage class of symbol
+       Nnumaux int8   // Number of auxiliary entries
+}
+
+const SYMESZ = 18
+
+const (
+       // Nscnum
+       N_DEBUG = -2
+       N_ABS   = -1
+       N_UNDEF = 0
+
+       //Ntype
+       SYM_V_INTERNAL  = 0x1000
+       SYM_V_HIDDEN    = 0x2000
+       SYM_V_PROTECTED = 0x3000
+       SYM_V_EXPORTED  = 0x4000
+       SYM_TYPE_FUNC   = 0x0020 // is function
+)
+
+// Storage Class.
+const (
+       C_NULL    = 0   // Symbol table entry marked for deletion
+       C_EXT     = 2   // External symbol
+       C_STAT    = 3   // Static symbol
+       C_BLOCK   = 100 // Beginning or end of inner block
+       C_FCN     = 101 // Beginning or end of function
+       C_FILE    = 103 // Source file name and compiler information
+       C_HIDEXT  = 107 // Unnamed external symbol
+       C_BINCL   = 108 // Beginning of include file
+       C_EINCL   = 109 // End of include file
+       C_WEAKEXT = 111 // Weak external symbol
+       C_DWARF   = 112 // DWARF symbol
+       C_GSYM    = 128 // Global variable
+       C_LSYM    = 129 // Automatic variable allocated on stack
+       C_PSYM    = 130 // Argument to subroutine allocated on stack
+       C_RSYM    = 131 // Register variable
+       C_RPSYM   = 132 // Argument to function or procedure stored in register
+       C_STSYM   = 133 // Statically allocated symbol
+       C_BCOMM   = 135 // Beginning of common block
+       C_ECOML   = 136 // Local member of common block
+       C_ECOMM   = 137 // End of common block
+       C_DECL    = 140 // Declaration of object
+       C_ENTRY   = 141 // Alternate entry
+       C_FUN     = 142 // Function or procedure
+       C_BSTAT   = 143 // Beginning of static block
+       C_ESTAT   = 144 // End of static block
+       C_GTLS    = 145 // Global thread-local variable
+       C_STTLS   = 146 // Static thread-local variable
+)
+
+// File Auxiliary Entry
+type XcoffAuxFile64 struct {
+       Xzeroes  uint32 // The name is always in the string table
+       Xoffset  uint32 // Offset in the string table
+       X_pad1   [6]byte
+       Xftype   uint8 // Source file string type
+       X_pad2   [2]byte
+       Xauxtype uint8 // Type of auxiliary entry
+}
+
+// Function Auxiliary Entry
+type XcoffAuxFcn64 struct {
+       Xlnnoptr uint64 // File pointer to line number
+       Xfsize   uint32 // Size of function in bytes
+       Xendndx  uint32 // Symbol table index of next entry
+       Xpad     uint8  // Unused
+       Xauxtype uint8  // Type of auxiliary entry
+}
+
+// csect Auxiliary Entry.
+type XcoffAuxCSect64 struct {
+       Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index
+       Xparmhash uint32 // Offset of parameter type-check string
+       Xsnhash   uint16 // .typchk section number
+       Xsmtyp    uint8  // Symbol alignment and type
+       Xsmclas   uint8  // Storage-mapping class
+       Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index
+       Xpad      uint8  // Unused
+       Xauxtype  uint8  // Type of auxiliary entry
+}
+
+// DWARF Auxiliary Entry
+type XcoffAuxDWARF64 struct {
+       Xscnlen  uint64 // Length of this symbol section
+       X_pad    [9]byte
+       Xauxtype uint8 // Type of auxiliary entry
+}
+
+// Auxiliary type
+const (
+       _AUX_EXCEPT = 255
+       _AUX_FCN    = 254
+       _AUX_SYM    = 253
+       _AUX_FILE   = 252
+       _AUX_CSECT  = 251
+       _AUX_SECT   = 250
+)
+
+// Xftype field
+const (
+       XFT_FN = 0   // Source File Name
+       XFT_CT = 1   // Compile Time Stamp
+       XFT_CV = 2   // Compiler Version Number
+       XFT_CD = 128 // Compiler Defined Information/
+
+)
+
+// Symbol type field.
+const (
+       XTY_ER  = 0    // External reference
+       XTY_SD  = 1    // Section definition
+       XTY_LD  = 2    // Label definition
+       XTY_CM  = 3    // Common csect definition
+       XTY_WK  = 0x8  // Weak symbol
+       XTY_EXP = 0x10 // Exported symbol
+       XTY_ENT = 0x20 // Entry point symbol
+       XTY_IMP = 0x40 // Imported symbol
+)
+
+// Storage-mapping class.
+const (
+       XMC_PR     = 0  // Program code
+       XMC_RO     = 1  // Read-only constant
+       XMC_DB     = 2  // Debug dictionary table
+       XMC_TC     = 3  // TOC entry
+       XMC_UA     = 4  // Unclassified
+       XMC_RW     = 5  // Read/Write data
+       XMC_GL     = 6  // Global linkage
+       XMC_XO     = 7  // Extended operation
+       XMC_SV     = 8  // 32-bit supervisor call descriptor
+       XMC_BS     = 9  // BSS class
+       XMC_DS     = 10 // Function descriptor
+       XMC_UC     = 11 // Unnamed FORTRAN common
+       XMC_TC0    = 15 // TOC anchor
+       XMC_TD     = 16 // Scalar data entry in the TOC
+       XMC_SV64   = 17 // 64-bit supervisor call descriptor
+       XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit
+       XMC_TL     = 20 // Read/Write thread-local data
+       XMC_UL     = 21 // Read/Write thread-local data (.tbss)
+       XMC_TE     = 22 // TOC entry
+)
+
+// Loader Header
+type XcoffLdHdr64 struct {
+       Lversion int32  // Loader section version number
+       Lnsyms   int32  // Number of symbol table entries
+       Lnreloc  int32  // Number of relocation table entries
+       Listlen  uint32 // Length of import file ID string table
+       Lnimpid  int32  // Number of import file IDs
+       Lstlen   uint32 // Length of string table
+       Limpoff  uint64 // Offset to start of import file IDs
+       Lstoff   uint64 // Offset to start of string table
+       Lsymoff  uint64 // Offset to start of symbol table
+       Lrldoff  uint64 // Offset to start of relocation entries
+}
+
+// Loader Symbol
+type XcoffLdSym64 struct {
+       Lvalue  uint64 // Address field
+       Loffset uint32 // Byte offset into string table of symbol name
+       Lscnum  int16  // Section number containing symbol
+       Lsmtype int8   // Symbol type, export, import flags
+       Lsmclas int8   // Symbol storage class
+       Lifile  int32  // Import file ID; ordinal of import file IDs
+       Lparm   uint32 // Parameter type-check field
+}
+
+type xcoffLoaderSymbol struct {
+       sym    *sym.Symbol
+       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    *sym.Symbol
+       rel    *sym.Reloc
+       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
+}
+
+// Var used by XCOFF Generation algorithms
+var (
+       xfile xcoffFile
+)
+
+// xcoffStringTable is a XCOFF string table.
+type xcoffStringTable struct {
+       strings    []string
+       stringsLen int
+}
+
+// size returns size of string table t.
+func (t *xcoffStringTable) size() int {
+       // string table starts with 4-byte length at the beginning
+       return t.stringsLen + 4
+}
+
+// add adds string str to string table t.
+func (t *xcoffStringTable) add(str string) int {
+       off := t.size()
+       t.strings = append(t.strings, str)
+       t.stringsLen += len(str) + 1 // each string will have 0 appended to it
+       return off
+}
+
+// write writes string table t into the output file.
+func (t *xcoffStringTable) write(out *OutBuf) {
+       out.Write32(uint32(t.size()))
+       for _, s := range t.strings {
+               out.WriteString(s)
+               out.Write8(0)
+       }
+}
+
+// write writes XCOFF section sect into the output file.
+func (sect *XcoffScnHdr64) write(ctxt *Link) {
+       binary.Write(ctxt.Out, binary.BigEndian, sect)
+       ctxt.Out.Write32(0) // Add 4 empty bytes at the end to match alignment
+}
+
+// addSection adds section to the XCOFF file f.
+func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 {
+       sect := &XcoffScnHdr64{
+               Spaddr:  addr,
+               Svaddr:  addr,
+               Ssize:   size,
+               Sscnptr: fileoff,
+               Sflags:  flags,
+       }
+       copy(sect.Sname[:], name) // copy string to [8]byte
+       f.sections = append(f.sections, sect)
+       f.sectNameToScnum[name] = int16(len(f.sections))
+       return sect
+}
+
+// addDwarfSection adds a dwarf section to the XCOFF file f.
+// This function is similar to addSection, but Dwarf section names
+// must be modified to conventional names and they are various subtypes.
+func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 {
+       newName, subtype := xcoffGetDwarfSubtype(s.Name)
+       return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype)
+}
+
+// xcoffGetDwarfSubtype returns the XCOFF name of the DWARF section str
+// and its subtype constant.
+func xcoffGetDwarfSubtype(str string) (string, uint32) {
+       switch str {
+       default:
+               Exitf("unknown DWARF section name for XCOFF: %s", str)
+       case ".debug_abbrev":
+               return ".dwabrev", SSUBTYP_DWABREV
+       case ".debug_info":
+               return ".dwinfo", SSUBTYP_DWINFO
+       case ".debug_frame":
+               return ".dwframe", SSUBTYP_DWFRAME
+       case ".debug_line":
+               return ".dwline", SSUBTYP_DWLINE
+       case ".debug_loc":
+               return ".dwloc", SSUBTYP_DWLOC
+       case ".debug_pubnames":
+               return ".dwpbnms", SSUBTYP_DWPBNMS
+       case ".debug_pubtypes":
+               return ".dwpbtyp", SSUBTYP_DWPBTYP
+       case ".debug_ranges":
+               return ".dwrnges", SSUBTYP_DWRNGES
+       }
+       // never used
+       return "", 0
+}
+
+// getXCOFFscnum returns the XCOFF section number of a Go section.
+func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 {
+       switch sect.Seg {
+       case &Segtext:
+               return f.sectNameToScnum[".text"]
+       case &Segdata:
+               if sect.Name == ".noptrbss" || sect.Name == ".bss" {
+                       return f.sectNameToScnum[".bss"]
+               }
+               if sect.Name == ".tbss" {
+                       return f.sectNameToScnum[".tbss"]
+               }
+               return f.sectNameToScnum[".data"]
+       case &Segdwarf:
+               name, _ := xcoffGetDwarfSubtype(sect.Name)
+               return f.sectNameToScnum[name]
+       case &Segrelrodata:
+               return f.sectNameToScnum[".data"]
+       }
+       Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name)
+       return -1
+}
+
+// Xcoffinit initialised some internal value and setups
+// already known header information
+func Xcoffinit(ctxt *Link) {
+       xfile.dynLibraries = make(map[string]int)
+
+       HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN))
+       if *FlagTextAddr != -1 {
+               Errorf(nil, "-T not available on AIX")
+       }
+       *FlagTextAddr = XCOFFTEXTBASE + int64(HEADR)
+       if *FlagRound != -1 {
+               Errorf(nil, "-R not available on AIX")
+       }
+       *FlagRound = int(XCOFFSECTALIGN)
+
+}
+
+// SYMBOL TABLE
+
+// type records C_FILE information needed for genasmsym in XCOFF.
+type xcoffSymSrcFile struct {
+       name       string
+       file       *XcoffSymEnt64   // Symbol of this C_FILE
+       csectAux   *XcoffAuxCSect64 // Symbol for the current .csect
+       csectSymNb uint64           // Symbol number for the current .csect
+       csectSize  int64
+}
+
+var (
+       currDwscnoff   = make(map[string]uint64) // Needed to create C_DWARF symbols
+       currSymSrcFile xcoffSymSrcFile
+       outerSymSize   = make(map[string]int64)
+)
+
+// xcoffUpdateOuterSize stores the size of outer symbols in order to have it
+// in the symbol table.
+func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
+       if size == 0 {
+               return
+       }
+
+       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.
+                       outerSymSize["typerel.*"] = size - ctxt.Syms.ROLookup("runtime.types", 0).Size
+                       return
+               }
+               fallthrough
+       case sym.STYPE:
+               if !ctxt.DynlinkingGo() {
+                       // runtime.types size must be removed, as it's a real symbol.
+                       outerSymSize["type.*"] = size - ctxt.Syms.ROLookup("runtime.types", 0).Size
+               }
+       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.SITABLINK:
+               outerSymSize["runtime.itablink"] = 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(x *sym.Symbol, t SymbolType) uint8 {
+       align := x.Align
+       if align == 0 {
+               if t == TextSym {
+                       align = int32(Funcalign)
+               } else {
+                       align = symalign(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) {
+       /* 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" {
+                               s := ctxt.Syms.ROLookup(sect.Name, 0)
+                               dwsize = uint64(s.Size)
+
+                       }
+               } 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.
+                       dws := ctxt.Syms.Lookup(sect.Name, 0)
+                       dws.Dynid = int32(f.symbolCount)
+
+                       if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal {
+                               // CIE size must be added to the first package.
+                               dwsize += 48
+                       }
+               }
+
+               f.addSymbol(s)
+
+               // update the DWARF section offset in this file
+               if sect.Name != ".debug_abbrev" {
+                       currDwscnoff[sect.Name] += dwsize
+               }
+
+               // Auxiliary dwarf section
+               auxd := &XcoffAuxDWARF64{
+                       Xscnlen:  dwsize,
+                       Xauxtype: _AUX_SECT,
+               }
+
+               f.addSymbol(auxd)
+       }
+
+       /* .csect */
+       // Check if extnum is in text.
+       // This is temporary and only here to check if this algorithm is correct.
+       if extnum != 1 {
+               Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text")
+       }
+
+       currSymSrcFile.csectSymNb = uint64(f.symbolCount)
+
+       // No offset because no name
+       s = &XcoffSymEnt64{
+               Nvalue:  firstEntry,
+               Nscnum:  extnum,
+               Nsclass: C_HIDEXT,
+               Ntype:   0, // check visibility ?
+               Nnumaux: 1,
+       }
+       f.addSymbol(s)
+
+       aux := &XcoffAuxCSect64{
+               Xsmclas:  XMC_PR,
+               Xsmtyp:   XTY_SD | logBase2(Funcalign)<<3,
+               Xauxtype: _AUX_CSECT,
+       }
+       f.addSymbol(aux)
+
+       currSymSrcFile.csectAux = aux
+       currSymSrcFile.csectSize = 0
+}
+
+// Update values for the previous package.
+//  - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1
+//  - Xsclen of the csect symbol.
+func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) {
+       // first file
+       if currSymSrcFile.file == nil {
+               return
+       }
+
+       // Update C_FILE
+       cfile := currSymSrcFile.file
+       if last {
+               cfile.Nvalue = 0xFFFFFFFFFFFFFFFF
+       } else {
+               cfile.Nvalue = uint64(f.symbolCount)
+       }
+
+       // update csect scnlen in this auxiliary entry
+       aux := currSymSrcFile.csectAux
+       aux.Xscnlenlo = uint32(currSymSrcFile.csectSize & 0xFFFFFFFF)
+       aux.Xscnlenhi = uint32(currSymSrcFile.csectSize >> 32)
+}
+
+// Write symbol representing a .text function.
+// The symbol table is split with C_FILE corresponding to each package
+// and not to each source file as it should be.
+func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x *sym.Symbol) []xcoffSym {
+       // New XCOFF symbols which will be written.
+       syms := []xcoffSym{}
+
+       // Check if a new file is detected.
+       if strings.Contains(x.Name, "-tramp") || strings.HasPrefix(x.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 x.File == "" { // 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 != x.File {
+                       if ctxt.LinkMode == LinkInternal {
+                               // update previous file values
+                               xfile.updatePreviousFile(ctxt, false)
+                               currSymSrcFile.name = x.File
+                               f.writeSymbolNewFile(ctxt, x.File, uint64(x.Value), xfile.getXCOFFscnum(x.Sect))
+                       } 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 = x.File
+                                       f.writeSymbolNewFile(ctxt, "go_functions", uint64(x.Value), xfile.getXCOFFscnum(x.Sect))
+                               }
+                       }
+
+               }
+       }
+
+       s := &XcoffSymEnt64{
+               Nsclass: C_EXT,
+               Noffset: uint32(xfile.stringTable.add(x.Extname())),
+               Nvalue:  uint64(x.Value),
+               Nscnum:  f.getXCOFFscnum(x.Sect),
+               Ntype:   SYM_TYPE_FUNC,
+               Nnumaux: 2,
+       }
+
+       if x.Version != 0 || x.Attr.VisibilityHidden() || x.Attr.Local() {
+               s.Nsclass = C_HIDEXT
+       }
+
+       x.Dynid = int32(xfile.symbolCount)
+       syms = append(syms, s)
+
+       // Update current csect size
+       currSymSrcFile.csectSize += x.Size
+
+       // create auxiliary entries
+       a2 := &XcoffAuxFcn64{
+               Xfsize:   uint32(x.Size),
+               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(x, TextSym) << 3)
+
+       syms = append(syms, a4)
+       return syms
+}
+
+// put function used by genasmsym to write symbol table
+func putaixsym(ctxt *Link, x *sym.Symbol, str string, t SymbolType, addr int64, go_ *sym.Symbol) {
+
+       // All XCOFF symbols generated by this GO symbols
+       // Can be a symbol entry or a auxiliary entry
+       syms := []xcoffSym{}
+
+       switch t {
+       default:
+               return
+
+       case TextSym:
+               if x.FuncInfo != nil || strings.Contains(x.Name, "-tramp") || strings.HasPrefix(x.Name, "runtime.text.") {
+                       // Function within a file
+                       syms = xfile.writeSymbolFunc(ctxt, x)
+               } else {
+                       // Only runtime.text and runtime.etext come through this way
+                       if x.Name != "runtime.text" && x.Name != "runtime.etext" && x.Name != "go.buildid" {
+                               Exitf("putaixsym: unknown text symbol %s", x.Name)
+                       }
+                       s := &XcoffSymEnt64{
+                               Nsclass: C_HIDEXT,
+                               Noffset: uint32(xfile.stringTable.add(str)),
+                               Nvalue:  uint64(x.Value),
+                               Nscnum:  xfile.getXCOFFscnum(x.Sect),
+                               Ntype:   SYM_TYPE_FUNC,
+                               Nnumaux: 1,
+                       }
+                       x.Dynid = int32(xfile.symbolCount)
+                       syms = append(syms, s)
+
+                       size := uint64(x.Size)
+                       a4 := &XcoffAuxCSect64{
+                               Xauxtype:  _AUX_CSECT,
+                               Xscnlenlo: uint32(size & 0xFFFFFFFF),
+                               Xscnlenhi: uint32(size >> 32),
+                               Xsmclas:   XMC_PR,
+                               Xsmtyp:    XTY_SD,
+                       }
+                       a4.Xsmtyp |= uint8(xcoffAlign(x, TextSym) << 3)
+                       syms = append(syms, a4)
+
+               }
+
+       case DataSym, BSSSym:
+               s := &XcoffSymEnt64{
+                       Nsclass: C_EXT,
+                       Noffset: uint32(xfile.stringTable.add(str)),
+                       Nvalue:  uint64(x.Value),
+                       Nscnum:  xfile.getXCOFFscnum(x.Sect),
+                       Nnumaux: 1,
+               }
+
+               if x.Version != 0 || x.Attr.VisibilityHidden() || x.Attr.Local() {
+                       // 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
+               }
+
+               x.Dynid = 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(x.Size)
+               a4 := &XcoffAuxCSect64{
+                       Xauxtype:  _AUX_CSECT,
+                       Xscnlenlo: uint32(size & 0xFFFFFFFF),
+                       Xscnlenhi: uint32(size >> 32),
+               }
+
+               if x.Type >= sym.STYPE && x.Type <= sym.SPCLNTAB {
+                       if ctxt.LinkMode == LinkExternal && strings.HasPrefix(x.Sect.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 x.Type == sym.SDATA && strings.HasPrefix(x.Name, "TOC.") && ctxt.LinkMode == LinkExternal {
+                       a4.Xsmclas = XMC_TC
+               } else if x.Name == "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(x, t) << 3)
+
+               syms = append(syms, a4)
+
+       case UndefinedSym:
+               if x.Type != sym.SDYNIMPORT && x.Type != sym.SHOSTOBJ && x.Type != sym.SUNDEFEXT {
+                       return
+               }
+               s := &XcoffSymEnt64{
+                       Nsclass: C_EXT,
+                       Noffset: uint32(xfile.stringTable.add(str)),
+                       Nnumaux: 1,
+               }
+               x.Dynid = int32(xfile.symbolCount)
+               syms = append(syms, s)
+
+               a4 := &XcoffAuxCSect64{
+                       Xauxtype: _AUX_CSECT,
+                       Xsmclas:  XMC_DS,
+                       Xsmtyp:   XTY_ER | XTY_IMP,
+               }
+
+               if x.Name == "__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(str)),
+                       Nscnum:  xfile.getXCOFFscnum(x.Sect),
+                       Nvalue:  uint64(x.Value),
+                       Nnumaux: 1,
+               }
+
+               x.Dynid = int32(xfile.symbolCount)
+               syms = append(syms, s)
+
+               size := uint64(x.Size)
+               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) {
+       // 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 := ctxt.Syms.ROLookup(name, 0)
+               if sym == nil {
+                       Errorf(nil, "unknown outer symbol with name %s", name)
+               } else {
+                       sym.Size = size
+               }
+       }
+
+       genasmsym(ctxt, putaixsym)
+       xfile.updatePreviousFile(ctxt, true)
+}
+
+func (f *xcoffFile) genDynSym(ctxt *Link) {
+       var dynsyms []*sym.Symbol
+       for _, s := range ctxt.Syms.Allsym {
+               if s.Type != sym.SHOSTOBJ && s.Type != sym.SDYNIMPORT {
+                       continue
+               }
+               dynsyms = append(dynsyms, s)
+       }
+
+       for _, s := range dynsyms {
+               f.adddynimpsym(ctxt, s)
+
+               if _, ok := f.dynLibraries[s.Dynimplib()]; !ok {
+                       f.dynLibraries[s.Dynimplib()] = 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 *sym.Symbol) {
+       // Check that library name is given.
+       // Pattern is already checked when compiling.
+       if ctxt.LinkMode == LinkInternal && s.Dynimplib() == "" {
+               Errorf(s, "imported symbol must have a given library")
+       }
+
+       s.Type = sym.SXCOFFTOC
+
+       // Create new dynamic symbol
+       extsym := ctxt.Syms.Lookup(s.Extname(), 0)
+       extsym.Type = sym.SDYNIMPORT
+       extsym.Attr |= sym.AttrReachable
+       extsym.SetDynimplib(s.Dynimplib())
+       extsym.SetExtname(s.Extname())
+       extsym.SetDynimpvers(s.Dynimpvers())
+
+       // Add loader symbol
+       lds := &xcoffLoaderSymbol{
+               sym:    extsym,
+               smtype: XTY_IMP,
+               smclas: XMC_DS,
+       }
+       if s.Name == "__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
+       s.AddBytes(make([]byte, 8))
+       s.SetAddr(ctxt.Arch, 0, extsym)
+
+}
+
+// Xcoffadddynrel adds a dynamic relocation in a XCOFF file.
+// This relocation will be made by the loader.
+func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
+       if ctxt.LinkMode == LinkExternal {
+               return true
+       }
+       if s.Type <= sym.SPCLNTAB {
+               Errorf(s, "cannot have a relocation to %s in a text section symbol", r.Sym.Name)
+               return false
+       }
+
+       ldr := &xcoffLoaderReloc{
+               sym: s,
+               rel: r,
+       }
+
+       switch r.Type {
+       default:
+               Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", r.Sym.Name, r.Type.String())
+               return false
+       case objabi.R_ADDR:
+               if s.Type == sym.SXCOFFTOC && r.Sym.Type == sym.SDYNIMPORT {
+                       // Imported symbol relocation
+                       for i, dynsym := range xfile.loaderSymbols {
+                               if dynsym.sym.Name == r.Sym.Name {
+                                       ldr.symndx = int32(i + 3) // +3 because of 3 section symbols
+                                       break
+                               }
+                       }
+               } else if s.Type == sym.SDATA {
+                       switch r.Sym.Sect.Seg {
+                       default:
+                               Errorf(s, "unknown segment for .loader relocation with symbol %s", r.Sym.Name)
+                       case &Segtext:
+                       case &Segrodata:
+                               ldr.symndx = 0 // .text
+                       case &Segdata:
+                               if r.Sym.Type == sym.SBSS || r.Sym.Type == sym.SNOPTRBSS {
+                                       ldr.symndx = 2 // .bss
+                               } else {
+                                       ldr.symndx = 1 // .data
+                               }
+
+                       }
+
+               } else {
+                       Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", r.Sym.Name, s.Type, r.Sym.Type)
+                       return false
+               }
+
+               ldr.rtype = 0x3F<<8 + XCOFF_R_POS
+       }
+
+       xfile.loaderReloc = append(xfile.loaderReloc, ldr)
+       return true
+}
+
+func (ctxt *Link) doxcoff() {
+       if *FlagD {
+               // All XCOFF files have dynamic symbols because of the syscalls.
+               Exitf("-d is not available on AIX")
+       }
+
+       // TOC
+       toc := ctxt.Syms.Lookup("TOC", 0)
+       toc.Type = sym.SXCOFFTOC
+       toc.Attr |= sym.AttrReachable
+       toc.Attr |= sym.AttrVisibilityHidden
+
+       // Add entry point to .loader symbols.
+       ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0)
+       if !ep.Attr.Reachable() {
+               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 := range ctxt.Syms.Allsym {
+               if strings.HasPrefix(s.Name, "TOC.") {
+                       s.Type = sym.SXCOFFTOC
+               }
+       }
+
+       if ctxt.LinkMode == LinkExternal {
+               // Change rt0_go name to match name in runtime/cgo:main().
+               rt0 := ctxt.Syms.ROLookup("runtime.rt0_go", 0)
+               ctxt.Syms.Rename(rt0.Name, "runtime_rt0_go", 0, ctxt.Reachparent)
+
+               for _, s := range ctxt.Syms.Allsym {
+                       if !s.Attr.CgoExport() {
+                               continue
+                       }
+
+                       name := s.Extname()
+                       if s.Type == 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.
+                               ctxt.Syms.Rename(s.Name, "."+name, 0, ctxt.Reachparent)
+
+                               desc := ctxt.Syms.Lookup(name, 0)
+                               desc.Type = sym.SNOPTRDATA
+                               desc.AddAddr(ctxt.Arch, s)
+                               desc.AddAddr(ctxt.Arch, toc)
+                               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,
+       }
+
+       /* Symbol table */
+       for _, s := range f.loaderSymbols {
+               lds := &XcoffLdSym64{
+                       Loffset: uint32(stlen + 2),
+                       Lsmtype: s.smtype,
+                       Lsmclas: s.smclas,
+               }
+               switch s.smtype {
+               default:
+                       Errorf(s.sym, "unexpected loader symbol type: 0x%x", s.smtype)
+               case XTY_ENT | XTY_SD:
+                       lds.Lvalue = uint64(s.sym.Value)
+                       lds.Lscnum = f.getXCOFFscnum(s.sym.Sect)
+               case XTY_IMP:
+                       lds.Lifile = int32(f.dynLibraries[s.sym.Dynimplib()] + 1)
+               }
+               ldstr := &XcoffLdStr64{
+                       size: uint16(len(s.sym.Name) + 1), // + null terminator
+                       name: s.sym.Name,
+               }
+               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 */
+       ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0)
+       ldr := &XcoffLdRel64{
+               Lvaddr:  uint64(ep.Value),
+               Lrtype:  0x3F00,
+               Lrsecnm: f.getXCOFFscnum(ep.Sect),
+               Lsymndx: 0,
+       }
+       off += 16
+       reloctab = append(reloctab, ldr)
+
+       off += uint64(16 * len(f.loaderReloc))
+       for _, r := range f.loaderReloc {
+               ldr = &XcoffLdRel64{
+                       Lvaddr:  uint64(r.sym.Value + int64(r.rel.Off)),
+                       Lrtype:  r.rtype,
+                       Lsymndx: r.symndx,
+               }
+
+               if r.sym.Sect != nil {
+                       ldr.Lrsecnm = f.getXCOFFscnum(r.sym.Sect)
+               }
+
+               reloctab = append(reloctab, ldr)
+       }
+
+       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)
+       ctxt.Out.Flush()
+
+       /* again for printing */
+       if !*flagA {
+               return
+       }
+
+       ctxt.Logf("\n.loader section")
+       // write in buf
+       var buf bytes.Buffer
+
+       binary.Write(&buf, ctxt.Arch.ByteOrder, hdr)
+       for _, s := range symtab {
+               binary.Write(&buf, ctxt.Arch.ByteOrder, s)
+
+       }
+       for _, f := range importtab {
+               buf.WriteString(f.Limpidpath)
+               buf.WriteByte(0)
+               buf.WriteString(f.Limpidbase)
+               buf.WriteByte(0)
+               buf.WriteString(f.Limpidmem)
+               buf.WriteByte(0)
+       }
+       for _, s := range strtab {
+               binary.Write(&buf, ctxt.Arch.ByteOrder, s.size)
+               buf.WriteString(s.name)
+               buf.WriteByte(0) // null terminator
+       }
+
+       // Log buffer
+       ctxt.Logf("\n\t%.8x|", globalOff)
+       for i, b := range buf.Bytes() {
+               if i > 0 && i%16 == 0 {
+                       ctxt.Logf("\n\t%.8x|", uint64(globalOff)+uint64(i))
+               }
+               ctxt.Logf(" %.2x", b)
+       }
+       ctxt.Logf("\n")
+
+}
+
+// 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 {
+               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 := ctxt.Syms.ROLookup(*flagEntrySymbol, 0)
+               f.xahdr.Oentry = uint64(entry.Value)
+               f.xahdr.Osnentry = f.getXCOFFscnum(entry.Sect)
+               toc := ctxt.Syms.ROLookup("TOC", 0)
+               f.xahdr.Otoc = uint64(toc.Value)
+               f.xahdr.Osntoc = f.getXCOFFscnum(toc.Sect)
+
+               f.xahdr.Oalgntext = int16(logBase2(int(Funcalign)))
+               f.xahdr.Oalgndata = 0x5
+
+               binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
+               binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr)
+       } else {
+               f.xfhdr.Fopthdr = 0
+               binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
+       }
+
+}
+
+func xcoffwrite(ctxt *Link) {
+       ctxt.Out.SeekSet(0)
+
+       xfile.writeFileHeader(ctxt)
+
+       for _, sect := range xfile.sections {
+               sect.write(ctxt)
+       }
+}
+
+// Generate XCOFF assembly file
+func Asmbxcoff(ctxt *Link, fileoff int64) {
+       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)
+
+       ctxt.Out.Flush()
+
+       // write headers
+       xcoffwrite(ctxt)
+}
+
+// byOffset is used to sort relocations by offset
+type byOffset []sym.Reloc
+
+func (x byOffset) Len() int { return len(x) }
+
+func (x byOffset) Swap(i, j int) {
+       x[i], x[j] = x[j], x[i]
+}
+
+func (x byOffset) Less(i, j int) bool {
+       return x[i].Off < x[j].Off
+}
+
+// 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)
+       }
+
+       // relocsect relocates symbols from first in section sect, and returns
+       // the total number of relocations emitted.
+       relocsect := func(sect *sym.Section, syms []*sym.Symbol, 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 !s.Attr.Reachable() {
+                               continue
+                       }
+                       if uint64(s.Value) >= sect.Vaddr {
+                               syms = syms[i:]
+                               break
+                       }
+               }
+               eaddr := int64(sect.Vaddr + sect.Length)
+               for _, s := range syms {
+                       if !s.Attr.Reachable() {
+                               continue
+                       }
+                       if s.Value >= int64(eaddr) {
+                               break
+                       }
+
+                       // Relocation must be ordered by address, so s.R is ordered by Off.
+                       sort.Sort(byOffset(s.R))
+
+                       for ri := range s.R {
+
+                               r := &s.R[ri]
+
+                               if r.Done {
+                                       continue
+                               }
+                               if r.Xsym == nil {
+                                       Errorf(s, "missing xsym in relocation")
+                                       continue
+                               }
+                               if r.Xsym.Dynid < 0 {
+                                       Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type.String(), r.Sym.Name, r.Xsym.Name, r.Sym.Type, r.Xsym.Dynid)
+                               }
+                               if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, s, r, int64(uint64(s.Value+int64(r.Off))-base)) {
+                                       Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type, r.Type.String(), r.Siz, r.Sym.Name)
+                               }
+                       }
+               }
+               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, datap, 0)
+                               }
+                       }
+               }
+               s.xcoffSect.Snreloc += n
+       }
+
+dwarfLoop:
+       for _, sect := range Segdwarf.Sections {
+               for _, xcoffSect := range f.sections {
+                       _, subtyp := xcoffGetDwarfSubtype(sect.Name)
+                       if xcoffSect.Sflags&0xF0000 == subtyp {
+                               xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
+                               xcoffSect.Snreloc = relocsect(sect, dwarfp, 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
+
+       for _, s := range ctxt.Syms.Allsym {
+               if !s.Attr.CgoExport() {
+                       continue
+               }
+               if !strings.HasPrefix(s.String(), "_cgoexp_") {
+                       continue
+               }
+
+               // Retrieve the name of the initial symbol
+               // exported by cgo.
+               // The corresponding Go symbol is:
+               // _cgoexp_hashcode_symname.
+               name := strings.SplitN(s.Extname(), "_", 4)[3]
+
+               buf.Write([]byte(name + "\n"))
+       }
+
+       err := ioutil.WriteFile(fname, buf.Bytes(), 0666)
+       if err != nil {
+               Errorf(nil, "WriteFile %s failed: %v", fname, err)
+       }
+
+       return fname
+
+}
diff --git a/src/cmd/oldlink/internal/loadelf/ldelf.go b/src/cmd/oldlink/internal/loadelf/ldelf.go
new file mode 100644 (file)
index 0000000..db37db9
--- /dev/null
@@ -0,0 +1,1282 @@
+// 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 loadelf implements an ELF file reader.
+package loadelf
+
+import (
+       "bytes"
+       "cmd/internal/bio"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/loader"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "encoding/binary"
+       "fmt"
+       "io"
+       "log"
+       "sort"
+       "strings"
+)
+
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+http://code.swtch.com/plan9port/src/tip/src/libmach/
+
+       Copyright © 2004 Russ Cox.
+       Portions Copyright © 2008-2010 Google Inc.
+       Portions Copyright © 2010 The Go Authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+const (
+       ElfClassNone = 0
+       ElfClass32   = 1
+       ElfClass64   = 2
+)
+
+const (
+       ElfDataNone = 0
+       ElfDataLsb  = 1
+       ElfDataMsb  = 2
+)
+
+const (
+       ElfTypeNone         = 0
+       ElfTypeRelocatable  = 1
+       ElfTypeExecutable   = 2
+       ElfTypeSharedObject = 3
+       ElfTypeCore         = 4
+)
+
+const (
+       ElfMachNone        = 0
+       ElfMach32100       = 1
+       ElfMachSparc       = 2
+       ElfMach386         = 3
+       ElfMach68000       = 4
+       ElfMach88000       = 5
+       ElfMach486         = 6
+       ElfMach860         = 7
+       ElfMachMips        = 8
+       ElfMachS370        = 9
+       ElfMachMipsLe      = 10
+       ElfMachParisc      = 15
+       ElfMachVpp500      = 17
+       ElfMachSparc32Plus = 18
+       ElfMach960         = 19
+       ElfMachPower       = 20
+       ElfMachPower64     = 21
+       ElfMachS390        = 22
+       ElfMachV800        = 36
+       ElfMachFr20        = 37
+       ElfMachRh32        = 38
+       ElfMachRce         = 39
+       ElfMachArm         = 40
+       ElfMachAlpha       = 41
+       ElfMachSH          = 42
+       ElfMachSparc9      = 43
+       ElfMachAmd64       = 62
+       ElfMachArm64       = 183
+)
+
+const (
+       ElfAbiNone     = 0
+       ElfAbiSystemV  = 0
+       ElfAbiHPUX     = 1
+       ElfAbiNetBSD   = 2
+       ElfAbiLinux    = 3
+       ElfAbiSolaris  = 6
+       ElfAbiAix      = 7
+       ElfAbiIrix     = 8
+       ElfAbiFreeBSD  = 9
+       ElfAbiTru64    = 10
+       ElfAbiModesto  = 11
+       ElfAbiOpenBSD  = 12
+       ElfAbiARM      = 97
+       ElfAbiEmbedded = 255
+)
+
+const (
+       ElfSectNone      = 0
+       ElfSectProgbits  = 1
+       ElfSectSymtab    = 2
+       ElfSectStrtab    = 3
+       ElfSectRela      = 4
+       ElfSectHash      = 5
+       ElfSectDynamic   = 6
+       ElfSectNote      = 7
+       ElfSectNobits    = 8
+       ElfSectRel       = 9
+       ElfSectShlib     = 10
+       ElfSectDynsym    = 11
+       ElfSectFlagWrite = 0x1
+       ElfSectFlagAlloc = 0x2
+       ElfSectFlagExec  = 0x4
+)
+
+const (
+       ElfSymBindLocal  = 0
+       ElfSymBindGlobal = 1
+       ElfSymBindWeak   = 2
+)
+
+const (
+       ElfSymTypeNone    = 0
+       ElfSymTypeObject  = 1
+       ElfSymTypeFunc    = 2
+       ElfSymTypeSection = 3
+       ElfSymTypeFile    = 4
+       ElfSymTypeCommon  = 5
+       ElfSymTypeTLS     = 6
+)
+
+const (
+       ElfSymShnNone   = 0
+       ElfSymShnAbs    = 0xFFF1
+       ElfSymShnCommon = 0xFFF2
+)
+
+const (
+       ElfProgNone      = 0
+       ElfProgLoad      = 1
+       ElfProgDynamic   = 2
+       ElfProgInterp    = 3
+       ElfProgNote      = 4
+       ElfProgShlib     = 5
+       ElfProgPhdr      = 6
+       ElfProgFlagExec  = 0x1
+       ElfProgFlagWrite = 0x2
+       ElfProgFlagRead  = 0x4
+)
+
+const (
+       ElfNotePrStatus     = 1
+       ElfNotePrFpreg      = 2
+       ElfNotePrPsinfo     = 3
+       ElfNotePrTaskstruct = 4
+       ElfNotePrAuxv       = 6
+       ElfNotePrXfpreg     = 0x46e62b7f
+)
+
+// TODO(crawshaw): de-duplicate with cmd/oldlink/internal/ld/elf.go.
+const (
+       ELF64SYMSIZE = 24
+       ELF32SYMSIZE = 16
+
+       SHT_ARM_ATTRIBUTES = 0x70000003
+)
+
+type ElfHdrBytes struct {
+       Ident     [16]uint8
+       Type      [2]uint8
+       Machine   [2]uint8
+       Version   [4]uint8
+       Entry     [4]uint8
+       Phoff     [4]uint8
+       Shoff     [4]uint8
+       Flags     [4]uint8
+       Ehsize    [2]uint8
+       Phentsize [2]uint8
+       Phnum     [2]uint8
+       Shentsize [2]uint8
+       Shnum     [2]uint8
+       Shstrndx  [2]uint8
+}
+
+type ElfSectBytes struct {
+       Name    [4]uint8
+       Type    [4]uint8
+       Flags   [4]uint8
+       Addr    [4]uint8
+       Off     [4]uint8
+       Size    [4]uint8
+       Link    [4]uint8
+       Info    [4]uint8
+       Align   [4]uint8
+       Entsize [4]uint8
+}
+
+type ElfProgBytes struct {
+}
+
+type ElfSymBytes struct {
+       Name  [4]uint8
+       Value [4]uint8
+       Size  [4]uint8
+       Info  uint8
+       Other uint8
+       Shndx [2]uint8
+}
+
+type ElfHdrBytes64 struct {
+       Ident     [16]uint8
+       Type      [2]uint8
+       Machine   [2]uint8
+       Version   [4]uint8
+       Entry     [8]uint8
+       Phoff     [8]uint8
+       Shoff     [8]uint8
+       Flags     [4]uint8
+       Ehsize    [2]uint8
+       Phentsize [2]uint8
+       Phnum     [2]uint8
+       Shentsize [2]uint8
+       Shnum     [2]uint8
+       Shstrndx  [2]uint8
+}
+
+type ElfSectBytes64 struct {
+       Name    [4]uint8
+       Type    [4]uint8
+       Flags   [8]uint8
+       Addr    [8]uint8
+       Off     [8]uint8
+       Size    [8]uint8
+       Link    [4]uint8
+       Info    [4]uint8
+       Align   [8]uint8
+       Entsize [8]uint8
+}
+
+type ElfProgBytes64 struct {
+}
+
+type ElfSymBytes64 struct {
+       Name  [4]uint8
+       Info  uint8
+       Other uint8
+       Shndx [2]uint8
+       Value [8]uint8
+       Size  [8]uint8
+}
+
+type ElfSect struct {
+       name    string
+       nameoff uint32
+       type_   uint32
+       flags   uint64
+       addr    uint64
+       off     uint64
+       size    uint64
+       link    uint32
+       info    uint32
+       align   uint64
+       entsize uint64
+       base    []byte
+       sym     *sym.Symbol
+}
+
+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  uint8
+       type_ uint8
+       other uint8
+       shndx uint16
+       sym   *sym.Symbol
+}
+
+var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'}
+
+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 == 64: // Tag_nodefaults has no argument
+
+       case attr.tag == 65: // Tag_also_compatible_with
+               // 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
+}
+
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) {
+       newSym := func(name string, version int) *sym.Symbol {
+               return l.Create(name, syms)
+       }
+       lookup := func(name string, version int) *sym.Symbol {
+               return l.LookupOrCreate(name, version, syms)
+       }
+       return load(arch, syms.IncVersion(), newSym, lookup, f, pkg, length, pn, flags)
+}
+
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) {
+       return load(arch, syms.IncVersion(), syms.Newsym, syms.Lookup, f, pkg, length, pn, flags)
+}
+
+type lookupFunc func(string, int) *sym.Symbol
+
+// load loads the ELF file pn from f.
+// Symbols are written into syms, 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(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []*sym.Symbol, ehdrFlags uint32, err error) {
+       errorf := func(str string, args ...interface{}) ([]*sym.Symbol, uint32, error) {
+               return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
+       }
+
+       base := f.Offset()
+
+       var hdrbuf [64]uint8
+       if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
+               return errorf("malformed elf file: %v", err)
+       }
+       hdr := new(ElfHdrBytes)
+       binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter
+       if string(hdr.Ident[:4]) != "\x7FELF" {
+               return errorf("malformed elf file, bad header")
+       }
+       var e binary.ByteOrder
+       switch hdr.Ident[5] {
+       case ElfDataLsb:
+               e = binary.LittleEndian
+
+       case ElfDataMsb:
+               e = binary.BigEndian
+
+       default:
+               return errorf("malformed elf file, unknown header")
+       }
+
+       // read header
+       elfobj := new(ElfObj)
+
+       elfobj.e = e
+       elfobj.f = f
+       elfobj.base = base
+       elfobj.length = length
+       elfobj.name = pn
+
+       is64 := 0
+       if hdr.Ident[4] == ElfClass64 {
+               is64 = 1
+               hdr := new(ElfHdrBytes64)
+               binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter
+               elfobj.type_ = uint32(e.Uint16(hdr.Type[:]))
+               elfobj.machine = uint32(e.Uint16(hdr.Machine[:]))
+               elfobj.version = e.Uint32(hdr.Version[:])
+               elfobj.phoff = e.Uint64(hdr.Phoff[:])
+               elfobj.shoff = e.Uint64(hdr.Shoff[:])
+               elfobj.flags = e.Uint32(hdr.Flags[:])
+               elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:]))
+               elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:]))
+               elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:]))
+               elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:]))
+               elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:]))
+               elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:]))
+       } else {
+               elfobj.type_ = uint32(e.Uint16(hdr.Type[:]))
+               elfobj.machine = uint32(e.Uint16(hdr.Machine[:]))
+               elfobj.version = e.Uint32(hdr.Version[:])
+               elfobj.entry = uint64(e.Uint32(hdr.Entry[:]))
+               elfobj.phoff = uint64(e.Uint32(hdr.Phoff[:]))
+               elfobj.shoff = uint64(e.Uint32(hdr.Shoff[:]))
+               elfobj.flags = e.Uint32(hdr.Flags[:])
+               elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:]))
+               elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:]))
+               elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:]))
+               elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:]))
+               elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:]))
+               elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:]))
+       }
+
+       elfobj.is64 = is64
+
+       if v := uint32(hdr.Ident[6]); v != elfobj.version {
+               return errorf("malformed elf version: got %d, want %d", v, elfobj.version)
+       }
+
+       if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable {
+               return errorf("elf but not elf relocatable object")
+       }
+
+       switch arch.Family {
+       default:
+               return errorf("elf %s unimplemented", arch.Name)
+
+       case sys.MIPS:
+               if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass32 {
+                       return errorf("elf object but not mips")
+               }
+
+       case sys.MIPS64:
+               if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 {
+                       return errorf("elf object but not mips64")
+               }
+
+       case sys.ARM:
+               if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 {
+                       return errorf("elf object but not arm")
+               }
+
+       case sys.AMD64:
+               if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 {
+                       return errorf("elf object but not amd64")
+               }
+
+       case sys.ARM64:
+               if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 {
+                       return errorf("elf object but not arm64")
+               }
+
+       case sys.I386:
+               if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 {
+                       return errorf("elf object but not 386")
+               }
+
+       case sys.PPC64:
+               if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 {
+                       return errorf("elf object but not ppc64")
+               }
+
+       case sys.S390X:
+               if elfobj.machine != ElfMachS390 || hdr.Ident[4] != 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 ElfSectBytes64
+
+                       if err := binary.Read(f, e, &b); err != nil {
+                               return errorf("malformed elf file: %v", err)
+                       }
+
+                       sect.nameoff = e.Uint32(b.Name[:])
+                       sect.type_ = e.Uint32(b.Type[:])
+                       sect.flags = e.Uint64(b.Flags[:])
+                       sect.addr = e.Uint64(b.Addr[:])
+                       sect.off = e.Uint64(b.Off[:])
+                       sect.size = e.Uint64(b.Size[:])
+                       sect.link = e.Uint32(b.Link[:])
+                       sect.info = e.Uint32(b.Info[:])
+                       sect.align = e.Uint64(b.Align[:])
+                       sect.entsize = e.Uint64(b.Entsize[:])
+               } else {
+                       var b ElfSectBytes
+
+                       if err := binary.Read(f, e, &b); err != nil {
+                               return errorf("malformed elf file: %v", err)
+                       }
+
+                       sect.nameoff = e.Uint32(b.Name[:])
+                       sect.type_ = e.Uint32(b.Type[:])
+                       sect.flags = uint64(e.Uint32(b.Flags[:]))
+                       sect.addr = uint64(e.Uint32(b.Addr[:]))
+                       sect.off = uint64(e.Uint32(b.Off[:]))
+                       sect.size = uint64(e.Uint32(b.Size[:]))
+                       sect.link = e.Uint32(b.Link[:])
+                       sect.info = e.Uint32(b.Info[:])
+                       sect.align = uint64(e.Uint32(b.Align[:]))
+                       sect.entsize = uint64(e.Uint32(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 / ELF64SYMSIZE)
+       } else {
+               elfobj.nsymtab = int(elfobj.symtab.size / ELF32SYMSIZE)
+       }
+
+       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_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 {
+                       continue
+               }
+               if sect.type_ != ElfSectNobits {
+                       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
+
+               s := lookup(name, localSymVersion)
+
+               switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) {
+               default:
+                       return errorf("%s: unexpected flags for ELF section %s", pn, sect.name)
+
+               case ElfSectFlagAlloc:
+                       s.Type = sym.SRODATA
+
+               case ElfSectFlagAlloc + ElfSectFlagWrite:
+                       if sect.type_ == ElfSectNobits {
+                               s.Type = sym.SNOPTRBSS
+                       } else {
+                               s.Type = sym.SNOPTRDATA
+                       }
+
+               case ElfSectFlagAlloc + ElfSectFlagExec:
+                       s.Type = sym.STEXT
+               }
+
+               if sect.name == ".got" || sect.name == ".toc" {
+                       s.Type = sym.SELFGOT
+               }
+               if sect.type_ == ElfSectProgbits {
+                       s.P = sect.base
+                       s.P = s.P[:sect.size]
+               }
+
+               s.Size = int64(sect.size)
+               s.Align = int32(sect.align)
+               sect.sym = s
+       }
+
+       // enter sub-symbols into symbol table.
+       // symbol 0 is the null symbol.
+       symbols := make([]*sym.Symbol, elfobj.nsymtab)
+
+       for i := 1; i < elfobj.nsymtab; i++ {
+               var elfsym ElfSym
+               if err := readelfsym(newSym, lookup, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
+                       return errorf("%s: malformed elf file: %v", pn, err)
+               }
+               symbols[i] = elfsym.sym
+               if elfsym.type_ != ElfSymTypeFunc && elfsym.type_ != ElfSymTypeObject && elfsym.type_ != ElfSymTypeNone && elfsym.type_ != ElfSymTypeCommon {
+                       continue
+               }
+               if elfsym.shndx == ElfSymShnCommon || elfsym.type_ == ElfSymTypeCommon {
+                       s := elfsym.sym
+                       if uint64(s.Size) < elfsym.size {
+                               s.Size = int64(elfsym.size)
+                       }
+                       if s.Type == 0 || s.Type == sym.SXREF {
+                               s.Type = 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 == nil {
+                       continue
+               }
+               sect = &elfobj.sect[elfsym.shndx]
+               if sect.sym == nil {
+                       if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this
+                               continue
+                       }
+
+                       if elfsym.name == "" && elfsym.type_ == 0 && sect.name == ".debug_str" {
+                               // This reportedly happens with clang 3.7 on ARM.
+                               // See issue 13139.
+                               continue
+                       }
+
+                       if strings.HasPrefix(elfsym.name, "$d") && elfsym.type_ == 0 && sect.name == ".debug_frame" {
+                               // "$d" is a marker, not a real symbol.
+                               // This happens with gcc on ARM64.
+                               // See https://sourceware.org/bugzilla/show_bug.cgi?id=21809
+                               continue
+                       }
+
+                       if strings.HasPrefix(elfsym.name, ".LASF") { // gcc on s390x does this
+                               continue
+                       }
+                       return errorf("%v: sym#%d: ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.shndx, elfsym.type_)
+               }
+
+               s := elfsym.sym
+               if s.Outer != nil {
+                       if s.Attr.DuplicateOK() {
+                               continue
+                       }
+                       return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name)
+               }
+
+               s.Sub = sect.sym.Sub
+               sect.sym.Sub = s
+               s.Type = sect.sym.Type
+               s.Attr |= sym.AttrSubSymbol
+               if !s.Attr.CgoExportDynamic() {
+                       s.SetDynimplib("") // satisfy dynimport
+               }
+               s.Value = int64(elfsym.value)
+               s.Size = int64(elfsym.size)
+               s.Outer = sect.sym
+               if sect.sym.Type == sym.STEXT {
+                       if s.Attr.External() && !s.Attr.DuplicateOK() {
+                               return errorf("%v: duplicate symbol definition", s)
+                       }
+                       s.Attr |= sym.AttrExternal
+               }
+
+               if elfobj.machine == ElfMachPower64 {
+                       flag := int(elfsym.other) >> 5
+                       if 2 <= flag && flag <= 6 {
+                               s.SetLocalentry(1 << uint(flag-2))
+                       } else if flag == 7 {
+                               return errorf("%v: invalid sym.other 0x%x", s, elfsym.other)
+                       }
+               }
+       }
+
+       // Sort outer lists by address, adding to textp.
+       // This keeps textp in increasing address order.
+       for i := uint(0); i < elfobj.nsect; i++ {
+               s := elfobj.sect[i].sym
+               if s == nil {
+                       continue
+               }
+               if s.Sub != nil {
+                       s.Sub = sym.SortSub(s.Sub)
+               }
+               if s.Type == sym.STEXT {
+                       if s.Attr.OnList() {
+                               return errorf("symbol %s listed multiple times", s.Name)
+                       }
+                       s.Attr |= sym.AttrOnList
+                       textp = append(textp, s)
+                       for s = s.Sub; s != nil; s = s.Sub {
+                               if s.Attr.OnList() {
+                                       return errorf("symbol %s listed multiple times", s.Name)
+                               }
+                               s.Attr |= sym.AttrOnList
+                               textp = append(textp, s)
+                       }
+               }
+       }
+
+       // load relocations
+       for i := uint(0); i < elfobj.nsect; i++ {
+               rsect := &elfobj.sect[i]
+               if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel {
+                       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_ == ElfSectRela {
+                       rela = 1
+               }
+               n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela))
+               r := make([]sym.Reloc, n)
+               p := rsect.base
+               for j := 0; j < n; j++ {
+                       var add uint64
+                       var symIdx int
+                       var relocType uint64
+
+                       rp := &r[j]
+                       if is64 != 0 {
+                               // 64-bit rel/rela
+                               rp.Off = 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
+                               rp.Off = 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
+                               rp.Sym = nil
+                       } else {
+                               var elfsym ElfSym
+                               if err := readelfsym(newSym, lookup, arch, elfobj, symIdx, &elfsym, 0, 0); err != nil {
+                                       return errorf("malformed elf file: %v", err)
+                               }
+                               elfsym.sym = symbols[symIdx]
+                               if elfsym.sym == nil {
+                                       return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, symIdx, elfsym.name, elfsym.shndx, elfsym.type_)
+                               }
+
+                               rp.Sym = elfsym.sym
+                       }
+
+                       rp.Type = objabi.ElfRelocOffset + objabi.RelocType(relocType)
+                       rp.Siz, err = relSize(arch, pn, uint32(relocType))
+                       if err != nil {
+                               return nil, 0, err
+                       }
+                       if rela != 0 {
+                               rp.Add = int64(add)
+                       } else {
+                               // load addend from image
+                               if rp.Siz == 4 {
+                                       rp.Add = int64(e.Uint32(sect.base[rp.Off:]))
+                               } else if rp.Siz == 8 {
+                                       rp.Add = int64(e.Uint64(sect.base[rp.Off:]))
+                               } else {
+                                       return errorf("invalid rela size %d", rp.Siz)
+                               }
+                       }
+
+                       if rp.Siz == 2 {
+                               rp.Add = int64(int16(rp.Add))
+                       }
+                       if rp.Siz == 4 {
+                               rp.Add = int64(int32(rp.Add))
+                       }
+               }
+
+               //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add);
+               sort.Sort(sym.RelocByOff(r[:n]))
+               // just in case
+
+               s := sect.sym
+               s.R = r
+               s.R = s.R[:n]
+       }
+
+       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
+       }
+
+       sect.base = make([]byte, sect.size)
+       elfobj.f.MustSeek(int64(uint64(elfobj.base)+sect.off), 0)
+       if _, err := io.ReadFull(elfobj.f, sect.base); err != nil {
+               return fmt.Errorf("short read: %v", err)
+       }
+
+       return nil
+}
+
+func readelfsym(newSym, lookup lookupFunc, 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(ElfSymBytes64)
+               binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF64SYMSIZE:(i+1)*ELF64SYMSIZE]), elfobj.e, b)
+               elfsym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):])
+               elfsym.value = elfobj.e.Uint64(b.Value[:])
+               elfsym.size = elfobj.e.Uint64(b.Size[:])
+               elfsym.shndx = elfobj.e.Uint16(b.Shndx[:])
+               elfsym.bind = b.Info >> 4
+               elfsym.type_ = b.Info & 0xf
+               elfsym.other = b.Other
+       } else {
+               b := new(ElfSymBytes)
+               binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF32SYMSIZE:(i+1)*ELF32SYMSIZE]), elfobj.e, b)
+               elfsym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):])
+               elfsym.value = uint64(elfobj.e.Uint32(b.Value[:]))
+               elfsym.size = uint64(elfobj.e.Uint32(b.Size[:]))
+               elfsym.shndx = elfobj.e.Uint16(b.Shndx[:])
+               elfsym.bind = b.Info >> 4
+               elfsym.type_ = b.Info & 0xf
+               elfsym.other = b.Other
+       }
+
+       var s *sym.Symbol
+       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 = ElfSymBindLocal
+       }
+
+       switch elfsym.type_ {
+       case ElfSymTypeSection:
+               s = elfobj.sect[elfsym.shndx].sym
+
+       case ElfSymTypeObject, ElfSymTypeFunc, ElfSymTypeNone, ElfSymTypeCommon:
+               switch elfsym.bind {
+               case ElfSymBindGlobal:
+                       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 != nil && elfsym.other == 2 {
+                                       s.Attr |= sym.AttrDuplicateOK | sym.AttrVisibilityHidden
+                               }
+                       }
+
+               case ElfSymBindLocal:
+                       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)
+                                       s.Attr |= sym.AttrVisibilityHidden
+                               }
+
+                               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)
+
+                               s.Attr |= sym.AttrVisibilityHidden
+                       }
+
+               case ElfSymBindWeak:
+                       if needSym != 0 {
+                               s = lookup(elfsym.name, 0)
+                               if elfsym.other == 2 {
+                                       s.Attr |= sym.AttrVisibilityHidden
+                               }
+
+                               // Allow weak symbols to be duplicated when already defined.
+                               if s.Outer != nil {
+                                       s.Attr |= sym.AttrDuplicateOK
+                               }
+                       }
+
+               default:
+                       err = fmt.Errorf("%s: invalid symbol binding %d", elfsym.name, elfsym.bind)
+                       return err
+               }
+       }
+
+       // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+       // sense and should be removed when someone has thought about it properly.
+       if s != nil && s.Type == 0 && !s.Attr.VisibilityHidden() && elfsym.type_ != ElfSymTypeSection {
+               s.Type = sym.SXREF
+       }
+       elfsym.sym = s
+
+       return nil
+}
+
+func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) {
+       // TODO(mdempsky): Replace this with a struct-valued switch statement
+       // once golang.org/issue/15164 is fixed or found to not impair cmd/link
+       // performance.
+
+       const (
+               AMD64  = uint32(sys.AMD64)
+               ARM    = uint32(sys.ARM)
+               ARM64  = uint32(sys.ARM64)
+               I386   = uint32(sys.I386)
+               PPC64  = uint32(sys.PPC64)
+               S390X  = uint32(sys.S390X)
+               MIPS   = uint32(sys.MIPS)
+               MIPS64 = uint32(sys.MIPS64)
+       )
+
+       switch uint32(arch.Family) | elftype<<16 {
+       default:
+               return 0, fmt.Errorf("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype)
+
+       case MIPS | uint32(elf.R_MIPS_HI16)<<16,
+               MIPS | uint32(elf.R_MIPS_LO16)<<16,
+               MIPS | uint32(elf.R_MIPS_GOT16)<<16,
+               MIPS | uint32(elf.R_MIPS_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_GPREL16)<<16,
+               MIPS64 | uint32(elf.R_MIPS_GOT_PAGE)<<16,
+               MIPS64 | uint32(elf.R_MIPS_JALR)<<16,
+               MIPS64 | uint32(elf.R_MIPS_GOT_OFST)<<16:
+               return 4, nil
+
+       case S390X | uint32(elf.R_390_8)<<16:
+               return 1, nil
+
+       case PPC64 | uint32(elf.R_PPC64_TOC16)<<16,
+               PPC64 | uint32(elf.R_PPC64_TOC16_LO)<<16,
+               PPC64 | uint32(elf.R_PPC64_TOC16_HI)<<16,
+               PPC64 | uint32(elf.R_PPC64_TOC16_HA)<<16,
+               PPC64 | uint32(elf.R_PPC64_TOC16_DS)<<16,
+               PPC64 | uint32(elf.R_PPC64_TOC16_LO_DS)<<16,
+               PPC64 | uint32(elf.R_PPC64_REL16_LO)<<16,
+               PPC64 | uint32(elf.R_PPC64_REL16_HI)<<16,
+               PPC64 | uint32(elf.R_PPC64_REL16_HA)<<16,
+               S390X | uint32(elf.R_390_16)<<16,
+               S390X | uint32(elf.R_390_GOT16)<<16,
+               S390X | uint32(elf.R_390_PC16)<<16,
+               S390X | uint32(elf.R_390_PC16DBL)<<16,
+               S390X | uint32(elf.R_390_PLT16DBL)<<16:
+               return 2, nil
+
+       case ARM | uint32(elf.R_ARM_ABS32)<<16,
+               ARM | uint32(elf.R_ARM_GOT32)<<16,
+               ARM | uint32(elf.R_ARM_PLT32)<<16,
+               ARM | uint32(elf.R_ARM_GOTOFF)<<16,
+               ARM | uint32(elf.R_ARM_GOTPC)<<16,
+               ARM | uint32(elf.R_ARM_THM_PC22)<<16,
+               ARM | uint32(elf.R_ARM_REL32)<<16,
+               ARM | uint32(elf.R_ARM_CALL)<<16,
+               ARM | uint32(elf.R_ARM_V4BX)<<16,
+               ARM | uint32(elf.R_ARM_GOT_PREL)<<16,
+               ARM | uint32(elf.R_ARM_PC24)<<16,
+               ARM | uint32(elf.R_ARM_JUMP24)<<16,
+               ARM64 | uint32(elf.R_AARCH64_CALL26)<<16,
+               ARM64 | uint32(elf.R_AARCH64_ADR_GOT_PAGE)<<16,
+               ARM64 | uint32(elf.R_AARCH64_LD64_GOT_LO12_NC)<<16,
+               ARM64 | uint32(elf.R_AARCH64_ADR_PREL_PG_HI21)<<16,
+               ARM64 | uint32(elf.R_AARCH64_ADD_ABS_LO12_NC)<<16,
+               ARM64 | uint32(elf.R_AARCH64_LDST8_ABS_LO12_NC)<<16,
+               ARM64 | uint32(elf.R_AARCH64_LDST32_ABS_LO12_NC)<<16,
+               ARM64 | uint32(elf.R_AARCH64_LDST64_ABS_LO12_NC)<<16,
+               ARM64 | uint32(elf.R_AARCH64_LDST128_ABS_LO12_NC)<<16,
+               ARM64 | uint32(elf.R_AARCH64_PREL32)<<16,
+               ARM64 | uint32(elf.R_AARCH64_JUMP26)<<16,
+               AMD64 | uint32(elf.R_X86_64_PC32)<<16,
+               AMD64 | uint32(elf.R_X86_64_PLT32)<<16,
+               AMD64 | uint32(elf.R_X86_64_GOTPCREL)<<16,
+               AMD64 | uint32(elf.R_X86_64_GOTPCRELX)<<16,
+               AMD64 | uint32(elf.R_X86_64_REX_GOTPCRELX)<<16,
+               I386 | uint32(elf.R_386_32)<<16,
+               I386 | uint32(elf.R_386_PC32)<<16,
+               I386 | uint32(elf.R_386_GOT32)<<16,
+               I386 | uint32(elf.R_386_PLT32)<<16,
+               I386 | uint32(elf.R_386_GOTOFF)<<16,
+               I386 | uint32(elf.R_386_GOTPC)<<16,
+               I386 | uint32(elf.R_386_GOT32X)<<16,
+               PPC64 | uint32(elf.R_PPC64_REL24)<<16,
+               PPC64 | uint32(elf.R_PPC_REL32)<<16,
+               S390X | uint32(elf.R_390_32)<<16,
+               S390X | uint32(elf.R_390_PC32)<<16,
+               S390X | uint32(elf.R_390_GOT32)<<16,
+               S390X | uint32(elf.R_390_PLT32)<<16,
+               S390X | uint32(elf.R_390_PC32DBL)<<16,
+               S390X | uint32(elf.R_390_PLT32DBL)<<16,
+               S390X | uint32(elf.R_390_GOTPCDBL)<<16,
+               S390X | uint32(elf.R_390_GOTENT)<<16:
+               return 4, nil
+
+       case AMD64 | uint32(elf.R_X86_64_64)<<16,
+               AMD64 | uint32(elf.R_X86_64_PC64)<<16,
+               ARM64 | uint32(elf.R_AARCH64_ABS64)<<16,
+               ARM64 | uint32(elf.R_AARCH64_PREL64)<<16,
+               PPC64 | uint32(elf.R_PPC64_ADDR64)<<16,
+               S390X | uint32(elf.R_390_GLOB_DAT)<<16,
+               S390X | uint32(elf.R_390_RELATIVE)<<16,
+               S390X | uint32(elf.R_390_GOTOFF)<<16,
+               S390X | uint32(elf.R_390_GOTPC)<<16,
+               S390X | uint32(elf.R_390_64)<<16,
+               S390X | uint32(elf.R_390_PC64)<<16,
+               S390X | uint32(elf.R_390_GOT64)<<16,
+               S390X | uint32(elf.R_390_PLT64)<<16:
+               return 8, nil
+       }
+}
+
+func cstring(x []byte) string {
+       i := bytes.IndexByte(x, '\x00')
+       if i >= 0 {
+               x = x[:i]
+       }
+       return string(x)
+}
diff --git a/src/cmd/oldlink/internal/loader/loader.go b/src/cmd/oldlink/internal/loader/loader.go
new file mode 100644 (file)
index 0000000..ed533c9
--- /dev/null
@@ -0,0 +1,1350 @@
+// 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/dwarf"
+       "cmd/internal/goobj2"
+       "cmd/internal/obj"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "fmt"
+       "log"
+       "os"
+       "sort"
+       "strconv"
+       "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 {
+       Count int // number of relocs
+
+       li int      // local index of symbol whose relocs we're examining
+       r  *oReader // object reader for containing package
+       l  *Loader  // loader
+
+       ext *sym.Symbol // external symbol if not nil
+}
+
+// Reloc contains the payload for a specific relocation.
+// TODO: replace this with sym.Reloc, once we change the
+// relocation target from "*sym.Symbol" to "loader.Sym" in sym.Reloc.
+type Reloc struct {
+       Off  int32            // offset to rewrite
+       Size uint8            // number of bytes to rewrite: 0, 1, 2, or 4
+       Type objabi.RelocType // the relocation type
+       Add  int64            // addend
+       Sym  Sym              // global index of symbol the reloc addresses
+}
+
+// oReader is a wrapper type of obj.Reader, along with some
+// extra information.
+// TODO: rename to objReader once the old one is gone?
+type oReader struct {
+       *goobj2.Reader
+       unit      *sym.CompilationUnit
+       version   int    // version of static symbol
+       flags     uint32 // read from object file
+       pkgprefix string
+       rcache    []Sym // cache mapping local PkgNone symbol to resolved Sym
+}
+
+type objIdx struct {
+       r *oReader
+       i Sym // start index
+       e Sym // end 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
+}
+
+// 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
+}
+
+func makeBitmap(n int) bitmap {
+       return make(bitmap, (n+31)/32)
+}
+
+// A Loader loads new object files and resolves indexed symbol references.
+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)
+       max         Sym              // current max index
+       extStart    Sym              // from this index on, the symbols are externally defined
+       extSyms     []nameVer        // externally defined symbols
+       builtinSyms []Sym            // global index of builtin symbols
+       ocache      int              // index (into 'objs') of most recent lookup
+
+       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
+       overwrite     map[Sym]Sym       // overwrite[i]=j if symbol j overwrites symbol i
+
+       itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
+
+       objByPkg map[string]*oReader // map package path to its Go object reader
+
+       Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now.
+
+       anonVersion int // most recently assigned ext static sym pseudo-version
+
+       Reachable bitmap // bitmap of reachable symbols, indexed by global index
+
+       // 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
+
+       relocBatch []sym.Reloc // for bulk allocation of relocations
+
+       flags uint32
+
+       strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
+}
+
+const (
+       // Loader.flags
+       FlagStrictDups = 1 << iota
+)
+
+func NewLoader(flags uint32) *Loader {
+       nbuiltin := goobj2.NBuiltin()
+       return &Loader{
+               start:         make(map[*oReader]Sym),
+               objs:          []objIdx{{nil, 0, 0}},
+               symsByName:    [2]map[string]Sym{make(map[string]Sym), make(map[string]Sym)},
+               objByPkg:      make(map[string]*oReader),
+               overwrite:     make(map[Sym]Sym),
+               itablink:      make(map[Sym]struct{}),
+               extStaticSyms: make(map[nameVer]Sym),
+               builtinSyms:   make([]Sym, nbuiltin),
+               flags:         flags,
+       }
+}
+
+// Return the start index in the global index space for a given object file.
+func (l *Loader) startIndex(r *oReader) Sym {
+       return l.start[r]
+}
+
+// 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
+       }
+       n := r.NSym() + r.NNonpkgdef()
+       i := l.max + 1
+       l.start[r] = i
+       l.objs = append(l.objs, objIdx{r, i, i + Sym(n) - 1})
+       l.max += Sym(n)
+       return i
+}
+
+// Add a symbol with a given index, return if it is added.
+func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ sym.SymKind) bool {
+       if l.extStart != 0 {
+               panic("AddSym called after AddExtSym is called")
+       }
+       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.
+               return true
+       }
+       if oldi, ok := l.symsByName[ver][name]; ok {
+               if dupok {
+                       if l.flags&FlagStrictDups != 0 {
+                               l.checkdup(name, i, r, oldi)
+                       }
+                       return false
+               }
+               oldr, li := l.toLocal(oldi)
+               oldsym := goobj2.Sym{}
+               oldsym.Read(oldr.Reader, oldr.SymOff(li))
+               if oldsym.Dupok() {
+                       return false
+               }
+               overwrite := r.DataSize(int(i-l.startIndex(r))) != 0
+               if overwrite {
+                       // new symbol overwrites old symbol.
+                       oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type)]
+                       if !oldtyp.IsData() && r.DataSize(li) == 0 {
+                               log.Fatalf("duplicated definition of symbol " + name)
+                       }
+                       l.overwrite[oldi] = i
+               } else {
+                       // old symbol overwrites new symbol.
+                       if typ != sym.SDATA && typ != sym.SNOPTRDATA && typ != sym.SBSS && typ != sym.SNOPTRBSS { // only allow overwriting data symbol
+                               log.Fatalf("duplicated definition of symbol " + name)
+                       }
+                       l.overwrite[i] = oldi
+                       return false
+               }
+       }
+       l.symsByName[ver][name] = i
+       return true
+}
+
+// Add an external symbol (without index). Return the index of newly added
+// symbol, or 0 if not added.
+func (l *Loader) AddExtSym(name string, ver int) Sym {
+       static := ver >= sym.SymVerStatic
+       if static {
+               if _, ok := l.extStaticSyms[nameVer{name, ver}]; ok {
+                       return 0
+               }
+       } else {
+               if _, ok := l.symsByName[ver][name]; ok {
+                       return 0
+               }
+       }
+       i := l.max + 1
+       if static {
+               l.extStaticSyms[nameVer{name, ver}] = i
+       } else {
+               l.symsByName[ver][name] = i
+       }
+       l.max++
+       if l.extStart == 0 {
+               l.extStart = i
+       }
+       l.extSyms = append(l.extSyms, nameVer{name, ver})
+       l.growSyms(int(i))
+       return i
+}
+
+func (l *Loader) IsExternal(i Sym) bool {
+       return l.extStart != 0 && i >= l.extStart
+}
+
+// Ensure Syms slice has enough space.
+func (l *Loader) growSyms(i int) {
+       n := len(l.Syms)
+       if n > i {
+               return
+       }
+       l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...)
+}
+
+// Convert a local index to a global index.
+func (l *Loader) toGlobal(r *oReader, i int) Sym {
+       g := l.startIndex(r) + Sym(i)
+       if ov, ok := l.overwrite[g]; ok {
+               return ov
+       }
+       return g
+}
+
+// Convert a global index to a local index.
+func (l *Loader) toLocal(i Sym) (*oReader, int) {
+       if ov, ok := l.overwrite[i]; ok {
+               i = ov
+       }
+       if l.IsExternal(i) {
+               return nil, int(i - l.extStart)
+       }
+       oc := l.ocache
+       if oc != 0 && i >= l.objs[oc].i && i <= l.objs[oc].e {
+               return l.objs[oc].r, int(i - l.objs[oc].i)
+       }
+       // Search for the local object holding index i.
+       // Below k is the first one that has its start index > i,
+       // so k-1 is the one we want.
+       k := sort.Search(len(l.objs), func(k int) bool {
+               return l.objs[k].i > i
+       })
+       l.ocache = k - 1
+       return l.objs[k-1].r, int(i - l.objs[k-1].i)
+}
+
+// rcacheGet checks for a valid entry for 's' in the readers cache,
+// where 's' is a local PkgIdxNone ref or def, or zero if
+// the cache is empty or doesn't contain a value for 's'.
+func (or *oReader) rcacheGet(symIdx uint32) Sym {
+       if len(or.rcache) > 0 {
+               return or.rcache[symIdx]
+       }
+       return 0
+}
+
+// rcacheSet installs a new entry in the oReader's PkgNone
+// resolver cache for the specified PkgIdxNone ref or def,
+// allocating a new cache if needed.
+func (or *oReader) rcacheSet(symIdx uint32, gsym Sym) {
+       if len(or.rcache) == 0 {
+               or.rcache = make([]Sym, or.NNonpkgdef()+or.NNonpkgref())
+       }
+       or.rcache[symIdx] = gsym
+}
+
+// Resolve a local symbol reference. Return global index.
+func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym {
+       var rr *oReader
+       switch p := s.PkgIdx; p {
+       case goobj2.PkgIdxInvalid:
+               if s.SymIdx != 0 {
+                       panic("bad sym ref")
+               }
+               return 0
+       case goobj2.PkgIdxNone:
+               // Check for cached version first
+               if cached := r.rcacheGet(s.SymIdx); cached != 0 {
+                       return cached
+               }
+               // Resolve by name
+               i := int(s.SymIdx) + r.NSym()
+               osym := goobj2.Sym{}
+               osym.Read(r.Reader, r.SymOff(i))
+               name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+               v := abiToVer(osym.ABI, r.version)
+               gsym := l.Lookup(name, v)
+               // Add to cache, then return.
+               r.rcacheSet(s.SymIdx, gsym)
+               return gsym
+       case goobj2.PkgIdxBuiltin:
+               return l.builtinSyms[s.SymIdx]
+       case goobj2.PkgIdxSelf:
+               rr = r
+       default:
+               pkg := r.Pkg(int(p))
+               var ok bool
+               rr, ok = l.objByPkg[pkg]
+               if !ok {
+                       log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
+               }
+       }
+       return l.toGlobal(rr, int(s.SymIdx))
+}
+
+// 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]
+}
+
+// Returns whether i is a dup of another symbol, and i is not
+// "primary", i.e. Lookup i by name will not return i.
+func (l *Loader) IsDup(i Sym) bool {
+       if _, ok := l.overwrite[i]; ok {
+               return true
+       }
+       if l.IsExternal(i) {
+               return false
+       }
+       r, li := l.toLocal(i)
+       osym := goobj2.Sym{}
+       osym.Read(r.Reader, r.SymOff(li))
+       if !osym.Dupok() {
+               return false
+       }
+       if osym.Name == "" {
+               return false // Unnamed aux symbol cannot be dup.
+       }
+       if osym.ABI == goobj2.SymABIstatic {
+               return false // Static symbol cannot be dup.
+       }
+       name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+       ver := abiToVer(osym.ABI, r.version)
+       return l.symsByName[ver][name] != i
+}
+
+// Check that duplicate symbols have same contents.
+func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) {
+       li := int(i - l.startIndex(r))
+       p := r.Data(li)
+       if strings.HasPrefix(name, "go.info.") {
+               p, _ = patchDWARFName1(p, r)
+       }
+       rdup, ldup := l.toLocal(dup)
+       pdup := rdup.Data(ldup)
+       if strings.HasPrefix(name, "go.info.") {
+               pdup, _ = patchDWARFName1(pdup, rdup)
+       }
+       if bytes.Equal(p, pdup) {
+               return
+       }
+       reason := "same length but different contents"
+       if len(p) != len(pdup) {
+               reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
+       }
+       fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)
+
+       // For the moment, whitelist 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.
+       whitelist := strings.HasPrefix(name, "go.info.go.interface") ||
+               strings.HasPrefix(name, "go.info.go.builtin") ||
+               strings.HasPrefix(name, "go.debuglines")
+       if !whitelist {
+               l.strictDupMsgs++
+       }
+}
+
+func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
+
+// Number of total symbols.
+func (l *Loader) NSym() int {
+       return int(l.max + 1)
+}
+
+// Number of defined Go symbols.
+func (l *Loader) NDef() int {
+       return int(l.extStart)
+}
+
+// Returns the raw (unpatched) name of the i-th symbol.
+func (l *Loader) RawSymName(i Sym) string {
+       if l.IsExternal(i) {
+               if s := l.Syms[i]; s != nil {
+                       return s.Name
+               }
+               return ""
+       }
+       r, li := l.toLocal(i)
+       osym := goobj2.Sym{}
+       osym.Read(r.Reader, r.SymOff(li))
+       return osym.Name
+}
+
+// Returns the (patched) name of the i-th symbol.
+func (l *Loader) SymName(i Sym) string {
+       if l.IsExternal(i) {
+               if s := l.Syms[i]; s != nil {
+                       return s.Name // external name should already be patched?
+               }
+               return ""
+       }
+       r, li := l.toLocal(i)
+       osym := goobj2.Sym{}
+       osym.Read(r.Reader, r.SymOff(li))
+       return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+}
+
+// Returns the type of the i-th symbol.
+func (l *Loader) SymType(i Sym) sym.SymKind {
+       if l.IsExternal(i) {
+               if s := l.Syms[i]; s != nil {
+                       return s.Type
+               }
+               return 0
+       }
+       r, li := l.toLocal(i)
+       osym := goobj2.Sym{}
+       osym.Read(r.Reader, r.SymOff(li))
+       return sym.AbiSymKindToSymKind[objabi.SymKind(osym.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 is the only thing matters and it cannot be set by external symbol.
+               return 0
+       }
+       r, li := l.toLocal(i)
+       osym := goobj2.Sym{}
+       osym.Read(r.Reader, r.SymOff(li))
+       return osym.Flag
+}
+
+// Returns whether the i-th symbol has ReflectMethod attribute set.
+func (l *Loader) IsReflectMethod(i Sym) bool {
+       return l.SymAttr(i)&goobj2.SymFlagReflectMethod != 0
+}
+
+// Returns whether this is a Go type symbol.
+func (l *Loader) IsGoType(i Sym) bool {
+       return l.SymAttr(i)&goobj2.SymFlagGoType != 0
+}
+
+// Returns whether this is a "go.itablink.*" symbol.
+func (l *Loader) IsItabLink(i Sym) bool {
+       if _, ok := l.itablink[i]; ok {
+               return true
+       }
+       return false
+}
+
+// Returns the symbol content of the i-th symbol. i is global index.
+func (l *Loader) Data(i Sym) []byte {
+       if l.IsExternal(i) {
+               if s := l.Syms[i]; s != nil {
+                       return s.P
+               }
+               return nil
+       }
+       r, li := l.toLocal(i)
+       return r.Data(li)
+}
+
+// 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 referred symbol of the j-th aux symbol of the i-th
+// symbol.
+func (l *Loader) AuxSym(i Sym, j int) Sym {
+       if l.IsExternal(i) {
+               return 0
+       }
+       r, li := l.toLocal(i)
+       a := goobj2.Aux{}
+       a.Read(r.Reader, r.AuxOff(li, j))
+       return l.resolve(r, a.Sym)
+}
+
+// ReadAuxSyms reads the aux symbol ids for the specified symbol into the
+// slice passed as a parameter. If the slice capacity is not large enough, a new
+// larger slice will be allocated. Final slice is returned.
+func (l *Loader) ReadAuxSyms(symIdx Sym, dst []Sym) []Sym {
+       if l.IsExternal(symIdx) {
+               return dst[:0]
+       }
+       naux := l.NAux(symIdx)
+       if naux == 0 {
+               return dst[:0]
+       }
+
+       if cap(dst) < naux {
+               dst = make([]Sym, naux)
+       }
+       dst = dst[:0]
+
+       r, li := l.toLocal(symIdx)
+       for i := 0; i < naux; i++ {
+               a := goobj2.Aux{}
+               a.Read(r.Reader, r.AuxOff(li, i))
+               dst = append(dst, l.resolve(r, a.Sym))
+       }
+
+       return dst
+}
+
+// OuterSym gets the outer symbol for host object loaded symbols.
+func (l *Loader) OuterSym(i Sym) Sym {
+       sym := l.Syms[i]
+       if sym != nil && sym.Outer != nil {
+               outer := sym.Outer
+               return l.Lookup(outer.Name, int(outer.Version))
+       }
+       return 0
+}
+
+// SubSym gets the subsymbol for host object loaded symbols.
+func (l *Loader) SubSym(i Sym) Sym {
+       sym := l.Syms[i]
+       if sym != nil && sym.Sub != nil {
+               sub := sym.Sub
+               return l.Lookup(sub.Name, int(sub.Version))
+       }
+       return 0
+}
+
+// Initialize Reachable bitmap for running deadcode pass.
+func (l *Loader) InitReachable() {
+       l.Reachable = makeBitmap(l.NSym())
+}
+
+// At method returns the j-th reloc for a global symbol.
+func (relocs *Relocs) At(j int) Reloc {
+       if relocs.ext != nil {
+               rel := &relocs.ext.R[j]
+               return Reloc{
+                       Off:  rel.Off,
+                       Size: rel.Siz,
+                       Type: rel.Type,
+                       Add:  rel.Add,
+                       Sym:  relocs.l.Lookup(rel.Sym.Name, int(rel.Sym.Version)),
+               }
+       }
+       rel := goobj2.Reloc{}
+       rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j))
+       target := relocs.l.resolve(relocs.r, rel.Sym)
+       return Reloc{
+               Off:  rel.Off,
+               Size: rel.Siz,
+               Type: objabi.RelocType(rel.Type),
+               Add:  rel.Add,
+               Sym:  target,
+       }
+}
+
+// ReadAll method reads all relocations for a symbol into the
+// specified slice. If the slice capacity is not large enough, a new
+// larger slice will be allocated. Final slice is returned.
+func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc {
+       if relocs.Count == 0 {
+               return dst[:0]
+       }
+
+       if cap(dst) < relocs.Count {
+               dst = make([]Reloc, relocs.Count)
+       }
+       dst = dst[:0]
+
+       if relocs.ext != nil {
+               for i := 0; i < relocs.Count; i++ {
+                       erel := &relocs.ext.R[i]
+                       rel := Reloc{
+                               Off:  erel.Off,
+                               Size: erel.Siz,
+                               Type: erel.Type,
+                               Add:  erel.Add,
+                               Sym:  relocs.l.Lookup(erel.Sym.Name, int(erel.Sym.Version)),
+                       }
+                       dst = append(dst, rel)
+               }
+               return dst
+       }
+
+       off := relocs.r.RelocOff(relocs.li, 0)
+       for i := 0; i < relocs.Count; i++ {
+               rel := goobj2.Reloc{}
+               rel.Read(relocs.r.Reader, off)
+               off += uint32(rel.Size())
+               target := relocs.l.resolve(relocs.r, rel.Sym)
+               dst = append(dst, Reloc{
+                       Off:  rel.Off,
+                       Size: rel.Siz,
+                       Type: objabi.RelocType(rel.Type),
+                       Add:  rel.Add,
+                       Sym:  target,
+               })
+       }
+       return dst
+}
+
+// Relocs returns a Relocs object for the given global sym.
+func (l *Loader) Relocs(i Sym) Relocs {
+       if l.IsExternal(i) {
+               if s := l.Syms[i]; s != nil {
+                       return Relocs{Count: len(s.R), l: l, ext: s}
+               }
+               return Relocs{}
+       }
+       r, li := l.toLocal(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 int) Relocs {
+       return Relocs{
+               Count: r.NReloc(li),
+               li:    li,
+               r:     r,
+               l:     l,
+       }
+}
+
+// Preload a package: add autolibs, add symbols to the symbol table.
+// Does not read symbol data yet.
+func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
+       roObject, readonly, err := f.Slice(uint64(length))
+       if err != nil {
+               log.Fatal("cannot read object file:", err)
+       }
+       r := goobj2.NewReaderFromBytes(roObject, readonly)
+       if r == nil {
+               panic("cannot read object file")
+       }
+       localSymVersion := syms.IncVersion()
+       pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+       or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, nil}
+
+       // Autolib
+       lib.ImportStrings = append(lib.ImportStrings, r.Autolib()...)
+
+       // DWARF file table
+       nfile := r.NDwarfFile()
+       unit.DWARFFileTable = make([]string, nfile)
+       for i := range unit.DWARFFileTable {
+               unit.DWARFFileTable[i] = r.DwarfFile(i)
+       }
+
+       istart := l.addObj(lib.Pkg, or)
+
+       ndef := r.NSym()
+       nnonpkgdef := r.NNonpkgdef()
+       for i, n := 0, ndef+nnonpkgdef; i < n; i++ {
+               osym := goobj2.Sym{}
+               osym.Read(r, r.SymOff(i))
+               name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1)
+               if name == "" {
+                       continue // don't add unnamed aux symbol
+               }
+               v := abiToVer(osym.ABI, localSymVersion)
+               dupok := osym.Dupok()
+               added := l.AddSym(name, v, istart+Sym(i), or, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
+               if added && strings.HasPrefix(name, "go.itablink.") {
+                       l.itablink[istart+Sym(i)] = struct{}{}
+               }
+               if added && strings.HasPrefix(name, "runtime.") {
+                       if bi := goobj2.BuiltinIdx(name, v); bi != -1 {
+                               // This is a definition of a builtin symbol. Record where it is.
+                               l.builtinSyms[bi] = istart + Sym(i)
+                       }
+               }
+       }
+
+       // The caller expects us consuming all the data
+       f.MustSeek(length, os.SEEK_CUR)
+}
+
+// Make sure referenced symbols are added. Most of them should already be added.
+// This should only be needed for referenced external symbols.
+func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) {
+       for _, o := range l.objs[1:] {
+               loadObjRefs(l, o.r, arch, syms)
+       }
+}
+
+func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) {
+       ndef := r.NSym() + r.NNonpkgdef()
+       for i, n := 0, r.NNonpkgref(); i < n; i++ {
+               osym := goobj2.Sym{}
+               osym.Read(r.Reader, r.SymOff(ndef+i))
+               name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+               v := abiToVer(osym.ABI, r.version)
+               l.AddExtSym(name, v)
+       }
+}
+
+func abiToVer(abi uint16, localSymVersion int) int {
+       var v int
+       if abi == goobj2.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
+}
+
+func preprocess(arch *sys.Arch, s *sym.Symbol) {
+       if s.Name != "" && s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
+               x, err := strconv.ParseUint(s.Name[5:], 16, 64)
+               if err != nil {
+                       log.Panicf("failed to parse $-symbol %s: %v", s.Name, err)
+               }
+               s.Type = sym.SRODATA
+               s.Attr |= sym.AttrLocal
+               switch s.Name[:5] {
+               case "$f32.":
+                       if uint64(uint32(x)) != x {
+                               log.Panicf("$-symbol %s too large: %d", s.Name, x)
+                       }
+                       s.AddUint32(arch, uint32(x))
+               case "$f64.", "$i64.":
+                       s.AddUint64(arch, x)
+               default:
+                       log.Panicf("unrecognized $-symbol: %s", s.Name)
+               }
+       }
+}
+
+// Load full contents.
+func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
+       // create all Symbols first.
+       l.growSyms(l.NSym())
+
+       nr := 0 // total number of sym.Reloc's we'll need
+       for _, o := range l.objs[1:] {
+               nr += loadObjSyms(l, syms, o.r)
+       }
+
+       // allocate a single large slab of relocations for all live symbols
+       l.relocBatch = make([]sym.Reloc, nr)
+
+       // external symbols
+       for i := l.extStart; i <= l.max; i++ {
+               if s := l.Syms[i]; s != nil {
+                       s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
+                       continue // already loaded from external object
+               }
+               nv := l.extSyms[i-l.extStart]
+               if l.Reachable.Has(i) || strings.HasPrefix(nv.name, "gofile..") { // XXX file symbols are used but not marked
+                       s := syms.Newsym(nv.name, nv.v)
+                       preprocess(arch, s)
+                       s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
+                       l.Syms[i] = s
+               }
+       }
+
+       // load contents of defined symbols
+       for _, o := range l.objs[1:] {
+               loadObjFull(l, o.r)
+       }
+
+       // Resolve ABI aliases for external symbols. This is only
+       // needed for internal cgo linking.
+       // (The old code does this in deadcode, but deadcode2 doesn't
+       // do this.)
+       for i := l.extStart; i <= l.max; i++ {
+               if s := l.Syms[i]; s != nil && s.Attr.Reachable() {
+                       for ri := range s.R {
+                               r := &s.R[ri]
+                               if r.Sym != nil && r.Sym.Type == sym.SABIALIAS {
+                                       r.Sym = r.Sym.R[0].Sym
+                               }
+                       }
+               }
+       }
+}
+
+// ExtractSymbols grabs the symbols out of the loader for work that hasn't been
+// ported to the new symbol type.
+func (l *Loader) ExtractSymbols(syms *sym.Symbols) {
+       // Nil out overwritten symbols.
+       // Overwritten Go symbols aren't a problem (as they're lazy loaded), but
+       // symbols loaded from host object loaders are fully loaded, and we might
+       // have multiple symbols with the same name. This loop nils them out.
+       for oldI := range l.overwrite {
+               l.Syms[oldI] = nil
+       }
+
+       // Add symbols to the ctxt.Syms lookup table. This explicitly
+       // skips things created via loader.Create (marked with versions
+       // less than zero), since if we tried to add these we'd wind up
+       // with collisions. Along the way, update the version from the
+       // negative anon version to something larger than sym.SymVerStatic
+       // (needed so that sym.symbol.IsFileLocal() works properly).
+       anonVerReplacement := syms.IncVersion()
+       for _, s := range l.Syms {
+               if s == nil {
+                       continue
+               }
+               if s.Name != "" && s.Version >= 0 {
+                       syms.Add(s)
+               }
+               if s.Version < 0 {
+                       s.Version = int16(anonVerReplacement)
+               }
+       }
+}
+
+// addNewSym adds a new sym.Symbol to the i-th index in the list of symbols.
+func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
+       s := syms.Newsym(name, ver)
+       if s.Type != 0 && s.Type != sym.SXREF {
+               fmt.Println("symbol already processed:", unit.Lib, i, s)
+               panic("symbol already processed")
+       }
+       if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
+               t = s.Type
+       }
+       s.Type = t
+       s.Unit = unit
+       l.growSyms(int(i))
+       l.Syms[i] = s
+       return s
+}
+
+// loadObjSyms creates sym.Symbol objects for the live Syms in the
+// object corresponding to object reader "r". Return value is the
+// number of sym.Reloc entries required for all the new symbols.
+func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
+       istart := l.startIndex(r)
+       nr := 0
+
+       for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
+               // If it's been previously loaded in host object loading, we don't need to do it again.
+               if s := l.Syms[istart+Sym(i)]; s != nil {
+                       // Mark symbol as reachable as it wasn't marked as such before.
+                       s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i)))
+                       nr += r.NReloc(i)
+                       continue
+               }
+               osym := goobj2.Sym{}
+               osym.Read(r.Reader, r.SymOff(i))
+               name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+               if name == "" {
+                       continue
+               }
+               ver := abiToVer(osym.ABI, r.version)
+               if osym.ABI != goobj2.SymABIstatic && l.symsByName[ver][name] != istart+Sym(i) {
+                       continue
+               }
+
+               t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+               if t == sym.SXREF {
+                       log.Fatalf("bad sxref")
+               }
+               if t == 0 {
+                       log.Fatalf("missing type for %s in %s", name, r.unit.Lib)
+               }
+               if !l.Reachable.Has(istart+Sym(i)) && !(t == sym.SRODATA && strings.HasPrefix(name, "type.")) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" {
+                       // No need to load unreachable symbols.
+                       // XXX some type symbol's content may be needed in DWARF code, but they are not marked.
+                       // XXX reference to runtime.addmoduledata may be generated later by the linker in plugin mode.
+                       continue
+               }
+
+               s := l.addNewSym(istart+Sym(i), syms, name, ver, r.unit, t)
+               s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i)))
+               nr += r.NReloc(i)
+       }
+       return nr
+}
+
+// funcInfoSym records the sym.Symbol for a function, along with a copy
+// of the corresponding goobj2.Sym and the index of its FuncInfo aux sym.
+// We use this to delay populating FuncInfo until we can batch-allocate
+// slices for their sub-objects.
+type funcInfoSym struct {
+       s    *sym.Symbol // sym.Symbol for a live function
+       osym goobj2.Sym  // object file symbol data for that function
+       isym int         // global symbol index of FuncInfo aux sym for func
+}
+
+// funcAllocInfo records totals/counts for all functions in an objfile;
+// used to help with bulk allocation of sym.Symbol sub-objects.
+type funcAllocInfo struct {
+       symPtr  uint32 // number of *sym.Symbol's needed in file slices
+       inlCall uint32 // number of sym.InlinedCall's needed in inltree slices
+       pcData  uint32 // number of sym.Pcdata's needed in pdata slices
+       fdOff   uint32 // number of int64's needed in all Funcdataoff slices
+}
+
+// LoadSymbol loads a single symbol by name.
+// This function should only be used by the host object loaders.
+// NB: This function does NOT set the symbol as reachable.
+func (l *Loader) LoadSymbol(name string, version int, syms *sym.Symbols) *sym.Symbol {
+       global := l.Lookup(name, version)
+
+       // If we're already loaded, bail.
+       if global != 0 && int(global) < len(l.Syms) && l.Syms[global] != nil {
+               return l.Syms[global]
+       }
+
+       // Read the symbol.
+       r, i := l.toLocal(global)
+       istart := l.startIndex(r)
+
+       osym := goobj2.Sym{}
+       osym.Read(r.Reader, r.SymOff(int(i)))
+       if l.symsByName[version][name] != istart+Sym(i) {
+               return nil
+       }
+
+       return l.addNewSym(istart+Sym(i), syms, name, version, r.unit, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
+}
+
+// LookupOrCreate looks up a symbol by name, and creates one if not found.
+// Either way, it will also create a sym.Symbol for it, if not already.
+// This should only be called when interacting with parts of the linker
+// that still works on sym.Symbols (i.e. internal cgo linking, for now).
+func (l *Loader) LookupOrCreate(name string, version int, syms *sym.Symbols) *sym.Symbol {
+       i := l.Lookup(name, version)
+       if i != 0 {
+               // symbol exists
+               if int(i) < len(l.Syms) && l.Syms[i] != nil {
+                       return l.Syms[i] // already loaded
+               }
+               if l.IsExternal(i) {
+                       panic("Can't load an external symbol.")
+               }
+               return l.LoadSymbol(name, version, syms)
+       }
+       i = l.AddExtSym(name, version)
+       s := syms.Newsym(name, version)
+       l.Syms[i] = s
+       return s
+}
+
+// Create creates a symbol with the specified name, returning a
+// sym.Symbol object for it. This method is intended for static/hidden
+// symbols discovered while loading host objects. We can see more than
+// one instance of a given static symbol with the same name/version,
+// so we can't add them to the lookup tables "as is". Instead assign
+// them fictitious (unique) versions, starting at -1 and decreasing by
+// one for each newly created symbol, and record them in the
+// extStaticSyms hash.
+func (l *Loader) Create(name string, syms *sym.Symbols) *sym.Symbol {
+       i := l.max + 1
+       l.max++
+       if l.extStart == 0 {
+               l.extStart = i
+       }
+
+       // Assign a new unique negative version -- this is to mark the
+       // symbol so that it can be skipped when ExtractSymbols is adding
+       // ext syms to the sym.Symbols hash.
+       l.anonVersion--
+       ver := l.anonVersion
+       l.extSyms = append(l.extSyms, nameVer{name, ver})
+       l.growSyms(int(i))
+       s := syms.Newsym(name, ver)
+       l.Syms[i] = s
+       l.extStaticSyms[nameVer{name, ver}] = i
+
+       return s
+}
+
+func loadObjFull(l *Loader, r *oReader) {
+       lib := r.unit.Lib
+       istart := l.startIndex(r)
+
+       resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
+               i := l.resolve(r, s)
+               return l.Syms[i]
+       }
+
+       funcs := []funcInfoSym{}
+       fdsyms := []*sym.Symbol{}
+       var funcAllocCounts funcAllocInfo
+       pcdataBase := r.PcdataBase()
+       rslice := []Reloc{}
+       for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
+               osym := goobj2.Sym{}
+               osym.Read(r.Reader, r.SymOff(i))
+               name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+               if name == "" {
+                       continue
+               }
+               ver := abiToVer(osym.ABI, r.version)
+               dupok := osym.Dupok()
+               if dupok {
+                       if dupsym := l.symsByName[ver][name]; dupsym != istart+Sym(i) {
+                               if l.Reachable.Has(dupsym) {
+                                       // A dupok 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.
+                                       s := l.Syms[dupsym]
+                                       if s.Type == sym.STEXT {
+                                               lib.DupTextSyms = append(lib.DupTextSyms, s)
+                                       }
+                               }
+                               continue
+                       }
+               }
+
+               s := l.Syms[istart+Sym(i)]
+               if s == nil {
+                       continue
+               }
+               if s.Name != name { // Sanity check. We can remove it in the final version.
+                       fmt.Println("name mismatch:", lib, i, s.Name, name)
+                       panic("name mismatch")
+               }
+
+               local := osym.Local()
+               makeTypelink := osym.Typelink()
+               size := osym.Siz
+
+               // Symbol data
+               s.P = r.Data(i)
+               s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
+
+               // Relocs
+               relocs := l.relocs(r, i)
+               rslice = relocs.ReadAll(rslice)
+               batch := l.relocBatch
+               s.R = batch[:relocs.Count:relocs.Count]
+               l.relocBatch = batch[relocs.Count:]
+               for j := range s.R {
+                       r := rslice[j]
+                       rs := r.Sym
+                       sz := r.Size
+                       rt := r.Type
+                       if rt == objabi.R_METHODOFF {
+                               if l.Reachable.Has(rs) {
+                                       rt = objabi.R_ADDROFF
+                               } else {
+                                       sz = 0
+                                       rs = 0
+                               }
+                       }
+                       if rt == objabi.R_WEAKADDROFF && !l.Reachable.Has(rs) {
+                               rs = 0
+                               sz = 0
+                       }
+                       if rs != 0 && l.SymType(rs) == sym.SABIALIAS {
+                               rsrelocs := l.Relocs(rs)
+                               rs = rsrelocs.At(0).Sym
+                       }
+                       s.R[j] = sym.Reloc{
+                               Off:  r.Off,
+                               Siz:  sz,
+                               Type: rt,
+                               Add:  r.Add,
+                               Sym:  l.Syms[rs],
+                       }
+               }
+
+               // Aux symbol info
+               isym := -1
+               naux := r.NAux(i)
+               for j := 0; j < naux; j++ {
+                       a := goobj2.Aux{}
+                       a.Read(r.Reader, r.AuxOff(i, j))
+                       switch a.Type {
+                       case goobj2.AuxGotype:
+                               typ := resolveSymRef(a.Sym)
+                               if typ != nil {
+                                       s.Gotype = typ
+                               }
+                       case goobj2.AuxFuncdata:
+                               fdsyms = append(fdsyms, resolveSymRef(a.Sym))
+                       case goobj2.AuxFuncInfo:
+                               if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
+                                       panic("funcinfo symbol not defined in current package")
+                               }
+                               isym = int(a.Sym.SymIdx)
+                       case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
+                               // ignored for now
+                       default:
+                               panic("unknown aux type")
+                       }
+               }
+
+               s.File = r.pkgprefix[:len(r.pkgprefix)-1]
+               if dupok {
+                       s.Attr |= sym.AttrDuplicateOK
+               }
+               if s.Size < int64(size) {
+                       s.Size = int64(size)
+               }
+               s.Attr.Set(sym.AttrLocal, local)
+               s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
+
+               if s.Type == sym.SDWARFINFO {
+                       // For DWARF symbols, replace `"".` to actual package prefix
+                       // in the symbol content.
+                       // TODO: maybe we should do this in the compiler and get rid
+                       // of this.
+                       patchDWARFName(s, r)
+               }
+
+               if s.Type != sym.STEXT {
+                       continue
+               }
+
+               if isym == -1 {
+                       continue
+               }
+
+               // Record function sym and associated info for additional
+               // processing in the loop below.
+               fwis := funcInfoSym{s: s, isym: isym, osym: osym}
+               funcs = append(funcs, fwis)
+
+               // Read the goobj2.FuncInfo for this text symbol so that we can
+               // collect allocation counts. We'll read it again in the loop
+               // below.
+               b := r.Data(isym)
+               info := goobj2.FuncInfo{}
+               info.Read(b)
+               funcAllocCounts.symPtr += uint32(len(info.File))
+               funcAllocCounts.pcData += uint32(len(info.Pcdata))
+               funcAllocCounts.inlCall += uint32(len(info.InlTree))
+               funcAllocCounts.fdOff += uint32(len(info.Funcdataoff))
+       }
+
+       // At this point we can do batch allocation of the sym.FuncInfo's,
+       // along with the slices of sub-objects they use.
+       fiBatch := make([]sym.FuncInfo, len(funcs))
+       inlCallBatch := make([]sym.InlinedCall, funcAllocCounts.inlCall)
+       symPtrBatch := make([]*sym.Symbol, funcAllocCounts.symPtr)
+       pcDataBatch := make([]sym.Pcdata, funcAllocCounts.pcData)
+       fdOffBatch := make([]int64, funcAllocCounts.fdOff)
+
+       // Populate FuncInfo contents for func symbols.
+       for fi := 0; fi < len(funcs); fi++ {
+               s := funcs[fi].s
+               isym := funcs[fi].isym
+               osym := funcs[fi].osym
+
+               s.FuncInfo = &fiBatch[0]
+               fiBatch = fiBatch[1:]
+
+               b := r.Data(isym)
+               info := goobj2.FuncInfo{}
+               info.Read(b)
+
+               if osym.NoSplit() {
+                       s.Attr |= sym.AttrNoSplit
+               }
+               if osym.ReflectMethod() {
+                       s.Attr |= sym.AttrReflectMethod
+               }
+               if r.Flags()&goobj2.ObjFlagShared != 0 {
+                       s.Attr |= sym.AttrShared
+               }
+               if osym.TopFrame() {
+                       s.Attr |= sym.AttrTopFrame
+               }
+
+               pc := s.FuncInfo
+
+               if len(info.Funcdataoff) != 0 {
+                       nfd := len(info.Funcdataoff)
+                       pc.Funcdata = fdsyms[:nfd:nfd]
+                       fdsyms = fdsyms[nfd:]
+               }
+
+               info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
+               pc.Args = int32(info.Args)
+               pc.Locals = int32(info.Locals)
+
+               npc := len(info.Pcdata) - 1 // -1 as we appended one above
+               pc.Pcdata = pcDataBatch[:npc:npc]
+               pcDataBatch = pcDataBatch[npc:]
+
+               nfd := len(info.Funcdataoff)
+               pc.Funcdataoff = fdOffBatch[:nfd:nfd]
+               fdOffBatch = fdOffBatch[nfd:]
+
+               nsp := len(info.File)
+               pc.File = symPtrBatch[:nsp:nsp]
+               symPtrBatch = symPtrBatch[nsp:]
+
+               nic := len(info.InlTree)
+               pc.InlTree = inlCallBatch[:nic:nic]
+               inlCallBatch = inlCallBatch[nic:]
+
+               pc.Pcsp.P = r.BytesAt(pcdataBase+info.Pcsp, int(info.Pcfile-info.Pcsp))
+               pc.Pcfile.P = r.BytesAt(pcdataBase+info.Pcfile, int(info.Pcline-info.Pcfile))
+               pc.Pcline.P = r.BytesAt(pcdataBase+info.Pcline, int(info.Pcinline-info.Pcline))
+               pc.Pcinline.P = r.BytesAt(pcdataBase+info.Pcinline, int(info.Pcdata[0]-info.Pcinline))
+               for k := range pc.Pcdata {
+                       pc.Pcdata[k].P = r.BytesAt(pcdataBase+info.Pcdata[k], int(info.Pcdata[k+1]-info.Pcdata[k]))
+               }
+               for k := range pc.Funcdataoff {
+                       pc.Funcdataoff[k] = int64(info.Funcdataoff[k])
+               }
+               for k := range pc.File {
+                       pc.File[k] = resolveSymRef(info.File[k])
+               }
+               for k := range pc.InlTree {
+                       inl := &info.InlTree[k]
+                       pc.InlTree[k] = sym.InlinedCall{
+                               Parent:   inl.Parent,
+                               File:     resolveSymRef(inl.File),
+                               Line:     inl.Line,
+                               Func:     l.SymName(l.resolve(r, inl.Func)),
+                               ParentPC: inl.ParentPC,
+                       }
+               }
+
+               dupok := osym.Dupok()
+               if !dupok {
+                       if s.Attr.OnList() {
+                               log.Fatalf("symbol %s listed multiple times", s.Name)
+                       }
+                       s.Attr.Set(sym.AttrOnList, true)
+                       lib.Textp = append(lib.Textp, s)
+               } else {
+                       // there may be a dup in another package
+                       // put into a temp list and add to text later
+                       lib.DupTextSyms = append(lib.DupTextSyms, s)
+               }
+       }
+}
+
+var emptyPkg = []byte(`"".`)
+
+func patchDWARFName1(p []byte, r *oReader) ([]byte, int) {
+       // This is kind of ugly. Really the package name should not
+       // even be included here.
+       if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION {
+               return p, -1
+       }
+       e := bytes.IndexByte(p, 0)
+       if e == -1 {
+               return p, -1
+       }
+       if !bytes.Contains(p[:e], emptyPkg) {
+               return p, -1
+       }
+       pkgprefix := []byte(r.pkgprefix)
+       patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1)
+       return append(patched, p[e:]...), e
+}
+
+func patchDWARFName(s *sym.Symbol, r *oReader) {
+       patched, e := patchDWARFName1(s.P, r)
+       if e == -1 {
+               return
+       }
+       s.P = patched
+       s.Attr.Set(sym.AttrReadOnly, false)
+       delta := int64(len(s.P)) - s.Size
+       s.Size = int64(len(s.P))
+       for i := range s.R {
+               r := &s.R[i]
+               if r.Off > int32(e) {
+                       r.Off += int32(delta)
+               }
+       }
+}
+
+// For debugging.
+func (l *Loader) Dump() {
+       fmt.Println("objs")
+       for _, obj := range l.objs {
+               if obj.r != nil {
+                       fmt.Println(obj.i, obj.r.unit.Lib)
+               }
+       }
+       fmt.Println("syms")
+       for i, s := range l.Syms {
+               if i == 0 {
+                       continue
+               }
+               if s != nil {
+                       fmt.Println(i, s, s.Type)
+               } else {
+                       fmt.Println(i, l.SymName(Sym(i)), "<not loaded>")
+               }
+       }
+       fmt.Println("overwrite:", l.overwrite)
+       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)
+       }
+}
diff --git a/src/cmd/oldlink/internal/loadmacho/ldmacho.go b/src/cmd/oldlink/internal/loadmacho/ldmacho.go
new file mode 100644 (file)
index 0000000..8982180
--- /dev/null
@@ -0,0 +1,891 @@
+// 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/oldlink/internal/loader"
+       "cmd/oldlink/internal/sym"
+       "encoding/binary"
+       "fmt"
+       "io"
+       "sort"
+)
+
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+http://code.swtch.com/plan9port/src/tip/src/libmach/
+
+       Copyright © 2004 Russ Cox.
+       Portions Copyright © 2008-2010 Google Inc.
+       Portions Copyright © 2010 The Go Authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld
+const (
+       MACHO_X86_64_RELOC_UNSIGNED = 0
+       MACHO_X86_64_RELOC_SIGNED   = 1
+       MACHO_FAKE_GOTPCREL         = 100
+)
+
+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     *sym.Symbol
+       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     *sym.Symbol
+}
+
+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       = 0x1000007
+       LdMachoCpuMips        = 8
+       LdMachoCpu98000       = 10
+       LdMachoCpuHppa        = 11
+       LdMachoCpuArm         = 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)
+       n := int(sect.nreloc * 8)
+       buf := make([]byte, n)
+       m.f.MustSeek(m.base+int64(sect.reloff), 0)
+       if _, err := io.ReadFull(m.f, buf); 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)
+
+       p := make([]byte, n*4)
+       m.f.MustSeek(m.base+int64(d.indirectsymoff), 0)
+       if _, err := io.ReadFull(m.f, p); 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
+       }
+
+       strbuf := make([]byte, symtab.strsize)
+       m.f.MustSeek(m.base+int64(symtab.stroff), 0)
+       if _, err := io.ReadFull(m.f, strbuf); err != nil {
+               return -1
+       }
+
+       symsize := 12
+       if m.is64 {
+               symsize = 16
+       }
+       n := int(symtab.nsym * uint32(symsize))
+       symbuf := make([]byte, n)
+       m.f.MustSeek(m.base+int64(symtab.symoff), 0)
+       if _, err := io.ReadFull(m.f, symbuf); 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
+}
+
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) ([]*sym.Symbol, error) {
+       newSym := func(name string, version int) *sym.Symbol {
+               return l.LookupOrCreate(name, version, syms)
+       }
+       return load(arch, syms.IncVersion(), newSym, f, pkg, length, pn)
+}
+
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+       return load(arch, syms.IncVersion(), syms.Lookup, f, pkg, length, pn)
+}
+
+// load the Mach-O file pn from f.
+// Symbols are written into syms, and a slice of the text symbols is returned.
+func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Symbol, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+       errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
+               return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
+       }
+
+       base := f.Offset()
+
+       var hdr [7 * 4]uint8
+       if _, err := io.ReadFull(f, hdr[:]); 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.I386:
+               if e != binary.LittleEndian || m.cputype != LdMachoCpu386 {
+                       return errorf("mach-o object but not 386")
+               }
+       }
+
+       m.cmd = make([]ldMachoCmd, ncmd)
+       cmdp := make([]byte, cmdsz)
+       if _, err := io.ReadFull(f, cmdp); 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 := make([]byte, c.seg.filesz)
+       if _, err := io.ReadFull(f, dat); 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 := lookup(name, localSymVersion)
+               if s.Type != 0 {
+                       return errorf("duplicate %s/%s", sect.segname, sect.name)
+               }
+
+               if sect.flags&0xff == 1 { // S_ZEROFILL
+                       s.P = make([]byte, sect.size)
+               } else {
+                       s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size]
+               }
+               s.Size = int64(len(s.P))
+
+               if sect.segname == "__TEXT" {
+                       if sect.name == "__text" {
+                               s.Type = sym.STEXT
+                       } else {
+                               s.Type = sym.SRODATA
+                       }
+               } else {
+                       if sect.name == "__bss" {
+                               s.Type = sym.SNOPTRBSS
+                               s.P = s.P[:0]
+                       } else {
+                               s.Type = 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 := lookup(name, v)
+               if machsym.type_&N_EXT == 0 {
+                       s.Attr |= sym.AttrDuplicateOK
+               }
+               if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
+                       s.Attr |= sym.AttrDuplicateOK
+               }
+               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]
+               outer := sect.sym
+               if outer == nil {
+                       continue // ignore reference to invalid section
+               }
+
+               if s.Outer != nil {
+                       if s.Attr.DuplicateOK() {
+                               continue
+                       }
+                       return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name)
+               }
+
+               s.Type = outer.Type
+               s.Attr |= sym.AttrSubSymbol
+               s.Sub = outer.Sub
+               outer.Sub = s
+               s.Outer = outer
+               s.Value = int64(machsym.value - sect.addr)
+               if !s.Attr.CgoExportDynamic() {
+                       s.SetDynimplib("") // satisfy dynimport
+               }
+               if outer.Type == sym.STEXT {
+                       if s.Attr.External() && !s.Attr.DuplicateOK() {
+                               return errorf("%v: duplicate symbol definition", s)
+                       }
+                       s.Attr |= sym.AttrExternal
+               }
+
+               machsym.sym = s
+       }
+
+       // 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 == nil {
+                       continue
+               }
+               if s.Sub != nil {
+                       s.Sub = sym.SortSub(s.Sub)
+
+                       // assign sizes, now that we know symbols in sorted order.
+                       for s1 := s.Sub; s1 != nil; s1 = s1.Sub {
+                               if s1.Sub != nil {
+                                       s1.Size = s1.Sub.Value - s1.Value
+                               } else {
+                                       s1.Size = s.Value + s.Size - s1.Value
+                               }
+                       }
+               }
+
+               if s.Type == sym.STEXT {
+                       if s.Attr.OnList() {
+                               return errorf("symbol %s listed multiple times", s.Name)
+                       }
+                       s.Attr |= sym.AttrOnList
+                       textp = append(textp, s)
+                       for s1 := s.Sub; s1 != nil; s1 = s1.Sub {
+                               if s1.Attr.OnList() {
+                                       return errorf("symbol %s listed multiple times", s1.Name)
+                               }
+                               s1.Attr |= sym.AttrOnList
+                               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 == nil {
+                       continue
+               }
+               macholoadrel(m, sect)
+               if sect.rel == nil {
+                       continue
+               }
+               r := make([]sym.Reloc, sect.nreloc)
+               rpi := 0
+       Reloc:
+               for j := uint32(0); j < sect.nreloc; j++ {
+                       rp := &r[rpi]
+                       rel := &sect.rel[j]
+                       if rel.scattered != 0 {
+                               if arch.Family != sys.I386 {
+                                       // mach-o only uses scattered relocation on 32-bit platforms
+                                       return errorf("%v: unexpected scattered relocation", s)
+                               }
+
+                               // on 386, rewrite scattered 4/1 relocation and some
+                               // scattered 2/1 relocation into the pseudo-pc-relative
+                               // reference that it is.
+                               // assume that the second in the pair is in this section
+                               // and use that as the pc-relative base.
+                               if j+1 >= sect.nreloc {
+                                       return errorf("unsupported scattered relocation %d", int(rel.type_))
+                               }
+
+                               if sect.rel[j+1].scattered == 0 || sect.rel[j+1].type_ != 1 || (rel.type_ != 4 && rel.type_ != 2) || uint64(sect.rel[j+1].value) < sect.addr || uint64(sect.rel[j+1].value) >= sect.addr+sect.size {
+                                       return errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_))
+                               }
+
+                               rp.Siz = rel.length
+                               rp.Off = int32(rel.addr)
+
+                               // NOTE(rsc): I haven't worked out why (really when)
+                               // we should ignore the addend on a
+                               // scattered relocation, but it seems that the
+                               // common case is we ignore it.
+                               // It's likely that this is not strictly correct
+                               // and that the math should look something
+                               // like the non-scattered case below.
+                               rp.Add = 0
+
+                               // want to make it pc-relative aka relative to rp->off+4
+                               // but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr.
+                               // adjust rp->add accordingly.
+                               rp.Type = objabi.R_PCREL
+
+                               rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr))
+
+                               // now consider the desired symbol.
+                               // find the section where it lives.
+                               for k := 0; uint32(k) < c.seg.nsect; k++ {
+                                       ks := &c.seg.sect[k]
+                                       if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size {
+                                               if ks.sym != nil {
+                                                       rp.Sym = ks.sym
+                                                       rp.Add += int64(uint64(rel.value) - ks.addr)
+                                               } else if ks.segname == "__IMPORT" && ks.name == "__pointers" {
+                                                       // handle reference to __IMPORT/__pointers.
+                                                       // how much worse can this get?
+                                                       // why are we supporting 386 on the mac anyway?
+                                                       rp.Type = objabi.MachoRelocOffset + MACHO_FAKE_GOTPCREL
+
+                                                       // figure out which pointer this is a reference to.
+                                                       k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4)
+
+                                                       // load indirect table for __pointers
+                                                       // fetch symbol number
+                                                       if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil {
+                                                               return errorf("invalid scattered relocation: indirect symbol reference out of range")
+                                                       }
+
+                                                       k = int(dsymtab.indir[k])
+                                                       if k < 0 || uint32(k) >= symtab.nsym {
+                                                               return errorf("invalid scattered relocation: symbol reference out of range")
+                                                       }
+
+                                                       rp.Sym = symtab.sym[k].sym
+                                               } else {
+                                                       return errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name)
+                                               }
+
+                                               rpi++
+
+                                               // skip #1 of 2 rel; continue skips #2 of 2.
+                                               j++
+
+                                               continue Reloc
+                                       }
+                               }
+
+                               return errorf("unsupported scattered relocation: invalid address %#x", rel.addr)
+                       }
+
+                       rp.Siz = rel.length
+                       rp.Type = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel)
+                       rp.Off = int32(rel.addr)
+
+                       // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0).
+                       if arch.Family == sys.AMD64 && 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
+
+                               rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr)
+                       } else {
+                               rp.Add = int64(int32(e.Uint32(s.P[rp.Off:])))
+                       }
+
+                       // 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
+                               rp.Add -= int64(secaddr)
+                       }
+
+                       // For i386 Mach-O PC-relative, the addend is written such that
+                       // it *is* the PC being subtracted. Use that to make
+                       // it match our version of PC-relative.
+                       if rel.pcrel != 0 && arch.Family == sys.I386 {
+                               rp.Add += int64(rp.Off) + int64(rp.Siz)
+                       }
+                       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)
+                               }
+
+                               rp.Sym = c.seg.sect[rel.symnum-1].sym
+                               if rp.Sym == nil {
+                                       return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
+                               }
+
+                               // References to symbols in other sections
+                               // include that information in the addend.
+                               // We only care about the delta from the
+                               // section base.
+                               if arch.Family == sys.I386 {
+                                       rp.Add -= int64(c.seg.sect[rel.symnum-1].addr)
+                               }
+                       } else {
+                               if rel.symnum >= symtab.nsym {
+                                       return errorf("invalid relocation: symbol reference out of range")
+                               }
+
+                               rp.Sym = symtab.sym[rel.symnum].sym
+                       }
+
+                       rpi++
+               }
+
+               sort.Sort(sym.RelocByOff(r[:rpi]))
+               s.R = r
+               s.R = s.R[:rpi]
+       }
+
+       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/oldlink/internal/loadpe/ldpe.go b/src/cmd/oldlink/internal/loadpe/ldpe.go
new file mode 100644 (file)
index 0000000..f7df774
--- /dev/null
@@ -0,0 +1,513 @@
+// 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 (
+       "cmd/internal/bio"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/loader"
+       "cmd/oldlink/internal/sym"
+       "debug/pe"
+       "encoding/binary"
+       "errors"
+       "fmt"
+       "io"
+       "sort"
+       "strings"
+)
+
+const (
+       // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 (same with IMAGE_SYM_DTYPE_POINTER and IMAGE_SYM_DTYPE_FUNCTION)
+       IMAGE_SYM_UNDEFINED              = 0
+       IMAGE_SYM_ABSOLUTE               = -1
+       IMAGE_SYM_DEBUG                  = -2
+       IMAGE_SYM_TYPE_NULL              = 0
+       IMAGE_SYM_TYPE_VOID              = 1
+       IMAGE_SYM_TYPE_CHAR              = 2
+       IMAGE_SYM_TYPE_SHORT             = 3
+       IMAGE_SYM_TYPE_INT               = 4
+       IMAGE_SYM_TYPE_LONG              = 5
+       IMAGE_SYM_TYPE_FLOAT             = 6
+       IMAGE_SYM_TYPE_DOUBLE            = 7
+       IMAGE_SYM_TYPE_STRUCT            = 8
+       IMAGE_SYM_TYPE_UNION             = 9
+       IMAGE_SYM_TYPE_ENUM              = 10
+       IMAGE_SYM_TYPE_MOE               = 11
+       IMAGE_SYM_TYPE_BYTE              = 12
+       IMAGE_SYM_TYPE_WORD              = 13
+       IMAGE_SYM_TYPE_UINT              = 14
+       IMAGE_SYM_TYPE_DWORD             = 15
+       IMAGE_SYM_TYPE_PCODE             = 32768
+       IMAGE_SYM_DTYPE_NULL             = 0
+       IMAGE_SYM_DTYPE_POINTER          = 0x10
+       IMAGE_SYM_DTYPE_FUNCTION         = 0x20
+       IMAGE_SYM_DTYPE_ARRAY            = 0x30
+       IMAGE_SYM_CLASS_END_OF_FUNCTION  = -1
+       IMAGE_SYM_CLASS_NULL             = 0
+       IMAGE_SYM_CLASS_AUTOMATIC        = 1
+       IMAGE_SYM_CLASS_EXTERNAL         = 2
+       IMAGE_SYM_CLASS_STATIC           = 3
+       IMAGE_SYM_CLASS_REGISTER         = 4
+       IMAGE_SYM_CLASS_EXTERNAL_DEF     = 5
+       IMAGE_SYM_CLASS_LABEL            = 6
+       IMAGE_SYM_CLASS_UNDEFINED_LABEL  = 7
+       IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8
+       IMAGE_SYM_CLASS_ARGUMENT         = 9
+       IMAGE_SYM_CLASS_STRUCT_TAG       = 10
+       IMAGE_SYM_CLASS_MEMBER_OF_UNION  = 11
+       IMAGE_SYM_CLASS_UNION_TAG        = 12
+       IMAGE_SYM_CLASS_TYPE_DEFINITION  = 13
+       IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14
+       IMAGE_SYM_CLASS_ENUM_TAG         = 15
+       IMAGE_SYM_CLASS_MEMBER_OF_ENUM   = 16
+       IMAGE_SYM_CLASS_REGISTER_PARAM   = 17
+       IMAGE_SYM_CLASS_BIT_FIELD        = 18
+       IMAGE_SYM_CLASS_FAR_EXTERNAL     = 68 /* Not in PECOFF v8 spec */
+       IMAGE_SYM_CLASS_BLOCK            = 100
+       IMAGE_SYM_CLASS_FUNCTION         = 101
+       IMAGE_SYM_CLASS_END_OF_STRUCT    = 102
+       IMAGE_SYM_CLASS_FILE             = 103
+       IMAGE_SYM_CLASS_SECTION          = 104
+       IMAGE_SYM_CLASS_WEAK_EXTERNAL    = 105
+       IMAGE_SYM_CLASS_CLR_TOKEN        = 107
+       IMAGE_REL_I386_ABSOLUTE          = 0x0000
+       IMAGE_REL_I386_DIR16             = 0x0001
+       IMAGE_REL_I386_REL16             = 0x0002
+       IMAGE_REL_I386_DIR32             = 0x0006
+       IMAGE_REL_I386_DIR32NB           = 0x0007
+       IMAGE_REL_I386_SEG12             = 0x0009
+       IMAGE_REL_I386_SECTION           = 0x000A
+       IMAGE_REL_I386_SECREL            = 0x000B
+       IMAGE_REL_I386_TOKEN             = 0x000C
+       IMAGE_REL_I386_SECREL7           = 0x000D
+       IMAGE_REL_I386_REL32             = 0x0014
+       IMAGE_REL_AMD64_ABSOLUTE         = 0x0000
+       IMAGE_REL_AMD64_ADDR64           = 0x0001
+       IMAGE_REL_AMD64_ADDR32           = 0x0002
+       IMAGE_REL_AMD64_ADDR32NB         = 0x0003
+       IMAGE_REL_AMD64_REL32            = 0x0004
+       IMAGE_REL_AMD64_REL32_1          = 0x0005
+       IMAGE_REL_AMD64_REL32_2          = 0x0006
+       IMAGE_REL_AMD64_REL32_3          = 0x0007
+       IMAGE_REL_AMD64_REL32_4          = 0x0008
+       IMAGE_REL_AMD64_REL32_5          = 0x0009
+       IMAGE_REL_AMD64_SECTION          = 0x000A
+       IMAGE_REL_AMD64_SECREL           = 0x000B
+       IMAGE_REL_AMD64_SECREL7          = 0x000C
+       IMAGE_REL_AMD64_TOKEN            = 0x000D
+       IMAGE_REL_AMD64_SREL32           = 0x000E
+       IMAGE_REL_AMD64_PAIR             = 0x000F
+       IMAGE_REL_AMD64_SSPAN32          = 0x0010
+       IMAGE_REL_ARM_ABSOLUTE           = 0x0000
+       IMAGE_REL_ARM_ADDR32             = 0x0001
+       IMAGE_REL_ARM_ADDR32NB           = 0x0002
+       IMAGE_REL_ARM_BRANCH24           = 0x0003
+       IMAGE_REL_ARM_BRANCH11           = 0x0004
+       IMAGE_REL_ARM_SECTION            = 0x000E
+       IMAGE_REL_ARM_SECREL             = 0x000F
+       IMAGE_REL_ARM_MOV32              = 0x0010
+       IMAGE_REL_THUMB_MOV32            = 0x0011
+       IMAGE_REL_THUMB_BRANCH20         = 0x0012
+       IMAGE_REL_THUMB_BRANCH24         = 0x0014
+       IMAGE_REL_THUMB_BLX23            = 0x0015
+       IMAGE_REL_ARM_PAIR               = 0x0016
+)
+
+// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld, ideally in debug/pe.
+const (
+       IMAGE_SCN_CNT_CODE               = 0x00000020
+       IMAGE_SCN_CNT_INITIALIZED_DATA   = 0x00000040
+       IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
+       IMAGE_SCN_MEM_DISCARDABLE        = 0x02000000
+       IMAGE_SCN_MEM_EXECUTE            = 0x20000000
+       IMAGE_SCN_MEM_READ               = 0x40000000
+       IMAGE_SCN_MEM_WRITE              = 0x80000000
+)
+
+// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf
+
+// peBiobuf makes bio.Reader look like io.ReaderAt.
+type peBiobuf bio.Reader
+
+func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) {
+       ret := ((*bio.Reader)(f)).MustSeek(off, 0)
+       if ret < 0 {
+               return 0, errors.New("fail to seek")
+       }
+       n, err := f.Read(p)
+       if err != nil {
+               return 0, err
+       }
+       return n, nil
+}
+
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
+       lookup := func(name string, version int) *sym.Symbol {
+               return l.LookupOrCreate(name, version, syms)
+       }
+       return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
+}
+
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
+       return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn)
+}
+
+// load loads the PE file pn from input.
+// Symbols are written into syms, and a slice of the text symbols is returned.
+// If an .rsrc section is found, its symbol is returned as rsrc.
+func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
+       sectsyms := make(map[*pe.Section]*sym.Symbol)
+       sectdata := make(map[*pe.Section][]byte)
+
+       // Some input files are archives containing multiple of
+       // object files, and pe.NewFile seeks to the start of
+       // input file and get confused. Create section reader
+       // to stop pe.NewFile looking before current position.
+       sr := io.NewSectionReader((*peBiobuf)(input), input.Offset(), 1<<63-1)
+
+       // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
+       f, err := pe.NewFile(sr)
+       if err != nil {
+               return nil, nil, err
+       }
+       defer f.Close()
+
+       // TODO return error if found .cormeta
+
+       // create symbols for mapped sections
+       for _, sect := range f.Sections {
+               if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+                       continue
+               }
+
+               if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+                       // This has been seen for .idata sections, which we
+                       // want to ignore. See issues 5106 and 5273.
+                       continue
+               }
+
+               name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
+               s := lookup(name, localSymVersion)
+
+               switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
+               case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
+                       s.Type = sym.SRODATA
+
+               case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss
+                       s.Type = sym.SNOPTRBSS
+
+               case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data
+                       s.Type = sym.SNOPTRDATA
+
+               case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text
+                       s.Type = sym.STEXT
+
+               default:
+                       return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
+               }
+
+               if s.Type != sym.SNOPTRBSS {
+                       data, err := sect.Data()
+                       if err != nil {
+                               return nil, nil, err
+                       }
+                       sectdata[sect] = data
+                       s.P = data
+               }
+               s.Size = int64(sect.Size)
+               sectsyms[sect] = s
+               if sect.Name == ".rsrc" {
+                       rsrc = s
+               }
+       }
+
+       // load relocations
+       for _, rsect := range f.Sections {
+               if _, found := sectsyms[rsect]; !found {
+                       continue
+               }
+               if rsect.NumberOfRelocations == 0 {
+                       continue
+               }
+               if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+                       continue
+               }
+               if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+                       // This has been seen for .idata sections, which we
+                       // want to ignore. See issues 5106 and 5273.
+                       continue
+               }
+
+               rs := make([]sym.Reloc, rsect.NumberOfRelocations)
+               for j, r := range rsect.Relocs {
+                       rp := &rs[j]
+                       if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
+                               return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
+                       }
+                       pesym := &f.COFFSymbols[r.SymbolTableIndex]
+                       gosym, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
+                       if err != nil {
+                               return nil, nil, err
+                       }
+                       if gosym == nil {
+                               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)
+                       }
+
+                       rp.Sym = gosym
+                       rp.Siz = 4
+                       rp.Off = int32(r.VirtualAddress)
+                       switch arch.Family {
+                       default:
+                               return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
+                       case sys.I386, sys.AMD64:
+                               switch r.Type {
+                               default:
+                                       return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
+
+                               case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
+                                       IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
+                                       IMAGE_REL_AMD64_ADDR32NB:
+                                       rp.Type = objabi.R_PCREL
+
+                                       rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+
+                               case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
+                                       rp.Type = objabi.R_ADDR
+
+                                       // load addend from image
+                                       rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+
+                               case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
+                                       rp.Siz = 8
+
+                                       rp.Type = objabi.R_ADDR
+
+                                       // load addend from image
+                                       rp.Add = int64(binary.LittleEndian.Uint64(sectdata[rsect][rp.Off:]))
+                               }
+
+                       case sys.ARM:
+                               switch r.Type {
+                               default:
+                                       return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
+
+                               case IMAGE_REL_ARM_SECREL:
+                                       rp.Type = objabi.R_PCREL
+
+                                       rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+
+                               case IMAGE_REL_ARM_ADDR32:
+                                       rp.Type = objabi.R_ADDR
+
+                                       rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+
+                               case IMAGE_REL_ARM_BRANCH24:
+                                       rp.Type = objabi.R_CALLARM
+
+                                       rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+                               }
+                       }
+
+                       // ld -r could generate multiple section symbols for the
+                       // same section but with different values, we have to take
+                       // that into account
+                       if issect(pesym) {
+                               rp.Add += int64(pesym.Value)
+                       }
+               }
+
+               sort.Sort(sym.RelocByOff(rs[:rsect.NumberOfRelocations]))
+
+               s := sectsyms[rsect]
+               s.R = rs
+               s.R = s.R[:rsect.NumberOfRelocations]
+       }
+
+       // 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
+               }
+               var sect *pe.Section
+               if pesym.SectionNumber > 0 {
+                       sect = f.Sections[pesym.SectionNumber-1]
+                       if _, found := sectsyms[sect]; !found {
+                               continue
+                       }
+               }
+
+               s, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               if pesym.SectionNumber == 0 { // extern
+                       if s.Type == sym.SDYNIMPORT {
+                               s.SetPlt(-2) // flag for dynimport in PE object files.
+                       }
+                       if s.Type == sym.SXREF && pesym.Value > 0 { // global data
+                               s.Type = sym.SNOPTRDATA
+                               s.Size = int64(pesym.Value)
+                       }
+
+                       continue
+               } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
+                       sect = f.Sections[pesym.SectionNumber-1]
+                       if _, found := sectsyms[sect]; !found {
+                               return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
+                       }
+               } else {
+                       return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
+               }
+
+               if sect == nil {
+                       return nil, rsrc, nil
+               }
+
+               if s.Outer != nil {
+                       if s.Attr.DuplicateOK() {
+                               continue
+                       }
+                       return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sectsyms[sect].Name)
+               }
+
+               sectsym := sectsyms[sect]
+               s.Sub = sectsym.Sub
+               sectsym.Sub = s
+               s.Type = sectsym.Type
+               s.Attr |= sym.AttrSubSymbol
+               s.Value = int64(pesym.Value)
+               s.Size = 4
+               s.Outer = sectsym
+               if sectsym.Type == sym.STEXT {
+                       if s.Attr.External() && !s.Attr.DuplicateOK() {
+                               return nil, nil, fmt.Errorf("%s: duplicate symbol definition", s.Name)
+                       }
+                       s.Attr |= sym.AttrExternal
+               }
+       }
+
+       // Sort outer lists by address, adding to textp.
+       // This keeps textp in increasing address order.
+       for _, sect := range f.Sections {
+               s := sectsyms[sect]
+               if s == nil {
+                       continue
+               }
+               if s.Sub != nil {
+                       s.Sub = sym.SortSub(s.Sub)
+               }
+               if s.Type == sym.STEXT {
+                       if s.Attr.OnList() {
+                               return nil, nil, fmt.Errorf("symbol %s listed multiple times", s.Name)
+                       }
+                       s.Attr |= sym.AttrOnList
+                       textp = append(textp, s)
+                       for s = s.Sub; s != nil; s = s.Sub {
+                               if s.Attr.OnList() {
+                                       return nil, nil, fmt.Errorf("symbol %s listed multiple times", s.Name)
+                               }
+                               s.Attr |= sym.AttrOnList
+                               textp = append(textp, s)
+                       }
+               }
+       }
+
+       return textp, rsrc, nil
+}
+
+func issect(s *pe.COFFSymbol) bool {
+       return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
+}
+
+func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]*sym.Symbol, localSymVersion int) (*sym.Symbol, error) {
+       symname, err := pesym.FullName(f.StringTable)
+       if err != nil {
+               return nil, err
+       }
+       var name string
+       if issect(pesym) {
+               name = sectsyms[f.Sections[pesym.SectionNumber-1]].Name
+       } else {
+               name = symname
+               switch arch.Family {
+               case sys.AMD64:
+                       if name == "__imp___acrt_iob_func" {
+                               // Do not rename __imp___acrt_iob_func into __acrt_iob_func,
+                               // because __imp___acrt_iob_func symbol is real
+                               // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details).
+                       } else {
+                               name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
+                       }
+               case sys.I386:
+                       if name == "__imp____acrt_iob_func" {
+                               // Do not rename __imp____acrt_iob_func into ___acrt_iob_func,
+                               // because __imp____acrt_iob_func symbol is real
+                               // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details).
+                       } else {
+                               name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
+                       }
+                       if name[0] == '_' {
+                               name = name[1:] // _Name => Name
+                       }
+               }
+       }
+
+       // remove last @XXX
+       if i := strings.LastIndex(name, "@"); i >= 0 {
+               name = name[:i]
+       }
+
+       var s *sym.Symbol
+       switch pesym.Type {
+       default:
+               return nil, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type)
+
+       case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
+               switch pesym.StorageClass {
+               case IMAGE_SYM_CLASS_EXTERNAL: //global
+                       s = lookup(name, 0)
+
+               case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
+                       s = lookup(name, localSymVersion)
+                       s.Attr |= sym.AttrDuplicateOK
+
+               default:
+                       return nil, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass)
+               }
+       }
+
+       if s != nil && s.Type == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) {
+               s.Type = sym.SXREF
+       }
+       if strings.HasPrefix(symname, "__imp_") {
+               s.SetGot(-2) // flag for __imp_
+       }
+
+       return s, nil
+}
diff --git a/src/cmd/oldlink/internal/loadxcoff/ldxcoff.go b/src/cmd/oldlink/internal/loadxcoff/ldxcoff.go
new file mode 100644 (file)
index 0000000..832b168
--- /dev/null
@@ -0,0 +1,238 @@
+// 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/oldlink/internal/loader"
+       "cmd/oldlink/internal/sym"
+       "errors"
+       "fmt"
+       "internal/xcoff"
+)
+
+// ldSection is an XCOFF section with its symbols.
+type ldSection struct {
+       xcoff.Section
+       sym *sym.Symbol
+}
+
+// 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
+}
+
+// Load loads xcoff files with the indexed object files.
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+       lookup := func(name string, version int) *sym.Symbol {
+               return l.LookupOrCreate(name, version, syms)
+       }
+       return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
+}
+
+// LoadOld uses the old version of object loading.
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+       return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn)
+}
+
+// loads the Xcoff file pn from f.
+// Symbols are written into syms, and a slice of the text symbols is returned.
+func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+       errorf := func(str string, args ...interface{}) ([]*sym.Symbol, 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)
+               s := lookup(name, localSymVersion)
+
+               switch lds.Type {
+               default:
+                       return errorf("unrecognized section type 0x%x", lds.Type)
+               case xcoff.STYP_TEXT:
+                       s.Type = sym.STEXT
+               case xcoff.STYP_DATA:
+                       s.Type = sym.SNOPTRDATA
+               case xcoff.STYP_BSS:
+                       s.Type = sym.SNOPTRBSS
+               }
+
+               s.Size = int64(lds.Size)
+               if s.Type != sym.SNOPTRBSS {
+                       data, err := lds.Section.Data()
+                       if err != nil {
+                               return nil, err
+                       }
+                       s.P = data
+               }
+
+               lds.sym = s
+               ldSections = append(ldSections, lds)
+       }
+
+       // sx = symbol from file
+       // s = symbol for syms
+       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 := lookup(sx.Name, 0)
+
+               // Text symbol
+               if s.Type == sym.STEXT {
+                       if s.Attr.OnList() {
+                               return errorf("symbol %s listed multiple times", s.Name)
+                       }
+                       s.Attr |= sym.AttrOnList
+                       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
+               }
+               rs := make([]sym.Reloc, sect.Nreloc)
+               for i, rx := range sect.Relocs {
+                       r := &rs[i]
+
+                       r.Sym = lookup(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)
+                       }
+                       r.Off = int32(rx.VirtualAddress)
+                       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)
+                               }
+                               r.Siz = 8
+                               r.Type = objabi.R_CONST
+                               r.Add = int64(rx.Symbol.Value)
+
+                       case xcoff.R_RBR:
+                               r.Siz = 4
+                               r.Type = objabi.R_CALLPOWER
+                               r.Add = 0 //
+
+                       }
+               }
+               s := sect.sym
+               s.R = rs
+               s.R = s.R[:sect.Nreloc]
+       }
+       return textp, nil
+
+}
+
+// Convert symbol xcoff type to sym.SymKind
+// Returns nil if this shouldn't be added into syms (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/oldlink/internal/mips/asm.go b/src/cmd/oldlink/internal/mips/asm.go
new file mode 100644 (file)
index 0000000..48a2324
--- /dev/null
@@ -0,0 +1,230 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "fmt"
+       "log"
+)
+
+func gentext(ctxt *ld.Link) {
+       return
+}
+
+func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       log.Fatalf("adddynrel not implemented")
+       return false
+}
+
+func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
+       ctxt.Out.Write32(uint32(sectoff))
+
+       elfsym := r.Xsym.ElfsymForReloc()
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               if r.Siz != 4 {
+                       return false
+               }
+               ctxt.Out.Write32(uint32(elf.R_MIPS_32) | uint32(elfsym)<<8)
+       case objabi.R_ADDRMIPS:
+               ctxt.Out.Write32(uint32(elf.R_MIPS_LO16) | uint32(elfsym)<<8)
+       case objabi.R_ADDRMIPSU:
+               ctxt.Out.Write32(uint32(elf.R_MIPS_HI16) | uint32(elfsym)<<8)
+       case objabi.R_ADDRMIPSTLS:
+               ctxt.Out.Write32(uint32(elf.R_MIPS_TLS_TPREL_LO16) | uint32(elfsym)<<8)
+       case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+               ctxt.Out.Write32(uint32(elf.R_MIPS_26) | uint32(elfsym)<<8)
+       }
+
+       return true
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       return
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       return false
+}
+
+func applyrel(arch *sys.Arch, r *sym.Reloc, s *sym.Symbol, val int64, t int64) int64 {
+       o := arch.ByteOrder.Uint32(s.P[r.Off:])
+       switch r.Type {
+       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(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       if ctxt.LinkMode == ld.LinkExternal {
+               switch r.Type {
+               default:
+                       return val, false
+               case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
+                       r.Done = false
+
+                       // set up addend for eventual relocation via outer symbol.
+                       rs := r.Sym
+                       r.Xadd = r.Add
+                       for rs.Outer != nil {
+                               r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer)
+                               rs = rs.Outer
+                       }
+
+                       if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil {
+                               ld.Errorf(s, "missing section for %s", rs.Name)
+                       }
+                       r.Xsym = rs
+                       return applyrel(ctxt.Arch, r, s, val, r.Xadd), true
+               case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+                       r.Done = false
+                       r.Xsym = r.Sym
+                       r.Xadd = r.Add
+                       return applyrel(ctxt.Arch, r, s, val, r.Add), true
+               }
+       }
+
+       switch r.Type {
+       case objabi.R_CONST:
+               return r.Add, true
+       case objabi.R_GOTOFF:
+               return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+       case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
+               t := ld.Symaddr(r.Sym) + r.Add
+               return applyrel(ctxt.Arch, r, s, val, t), true
+       case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+               t := ld.Symaddr(r.Sym) + r.Add
+
+               if t&3 != 0 {
+                       ld.Errorf(s, "direct call is not aligned: %s %x", r.Sym.Name, t)
+               }
+
+               // check if target address is in the same 256 MB region as the next instruction
+               if (s.Value+int64(r.Off)+4)&0xf0000000 != (t & 0xf0000000) {
+                       ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t)
+               }
+
+               return applyrel(ctxt.Arch, r, s, val, t), true
+       case objabi.R_ADDRMIPSTLS:
+               // thread pointer is at 0x7000 offset from the start of TLS data area
+               t := ld.Symaddr(r.Sym) + r.Add - 0x7000
+               if t < -32768 || t >= 32678 {
+                       ld.Errorf(s, "TLS offset out of range %d", t)
+               }
+               return applyrel(ctxt.Arch, r, s, val, t), true
+       }
+
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       return -1
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       sect := ld.Segtext.Sections[0]
+       ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+       ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       for _, sect = range ld.Segtext.Sections[1:] {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       /* output symbol table */
+       ld.Symsize = 0
+
+       ld.Lcsize = 0
+       symo := uint32(0)
+       if !*ld.FlagS {
+               if !ctxt.IsELF {
+                       ld.Errorf(nil, "unsupported executable format")
+               }
+               symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+               symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+
+               ctxt.Out.SeekSet(int64(symo))
+               ld.Asmelfsym(ctxt)
+               ctxt.Out.Flush()
+               ctxt.Out.Write(ld.Elfstrdat)
+
+               if ctxt.LinkMode == ld.LinkExternal {
+                       ld.Elfemitreloc(ctxt)
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       default:
+               ld.Errorf(nil, "unsupported operating system")
+       case objabi.Hlinux:
+               ld.Asmbelf(ctxt, int64(symo))
+       }
+
+       ctxt.Out.Flush()
+       if *ld.FlagC {
+               fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
+               fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
+               fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
+               fmt.Printf("symsize=%d\n", ld.Symsize)
+               fmt.Printf("lcsize=%d\n", ld.Lcsize)
+               fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize))
+       }
+}
diff --git a/src/cmd/oldlink/internal/mips/l.go b/src/cmd/oldlink/internal/mips/l.go
new file mode 100644 (file)
index 0000000..adbde40
--- /dev/null
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/mips/obj.go b/src/cmd/oldlink/internal/mips/obj.go
new file mode 100644 (file)
index 0000000..c80824f
--- /dev/null
@@ -0,0 +1,89 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+       arch := sys.ArchMIPS
+       if objabi.GOARCH == "mipsle" {
+               arch = sys.ArchMIPSLE
+       }
+
+       theArch := ld.Arch{
+               Funcalign:  FuncAlign,
+               Maxalign:   MaxAlign,
+               Minalign:   MinAlign,
+               Dwarfregsp: DWARFREGSP,
+               Dwarfreglr: DWARFREGLR,
+
+               Adddynrel:        adddynrel,
+               Archinit:         archinit,
+               Archreloc:        archreloc,
+               Archrelocvariant: archrelocvariant,
+               Asmb:             asmb,
+               Asmb2:            asmb2,
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Machoreloc1:      machoreloc1,
+
+               Linuxdynld: "/lib/ld.so.1",
+
+               Freebsddynld:   "XXX",
+               Openbsddynld:   "XXX",
+               Netbsddynld:    "XXX",
+               Dragonflydynld: "XXX",
+               Solarisdynld:   "XXX",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+       case objabi.Hlinux: /* mips elf */
+               ld.Elfinit(ctxt)
+               ld.HEADR = ld.ELFRESERVE
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 0x10000
+               }
+       }
+}
diff --git a/src/cmd/oldlink/internal/mips64/asm.go b/src/cmd/oldlink/internal/mips64/asm.go
new file mode 100644 (file)
index 0000000..775b74e
--- /dev/null
@@ -0,0 +1,278 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "fmt"
+       "log"
+)
+
+func gentext(ctxt *ld.Link) {}
+
+func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       log.Fatalf("adddynrel not implemented")
+       return false
+}
+
+func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
+       // mips64 ELF relocation (endian neutral)
+       //              offset  uint64
+       //              sym             uint32
+       //              ssym    uint8
+       //              type3   uint8
+       //              type2   uint8
+       //              type    uint8
+       //              addend  int64
+
+       ctxt.Out.Write64(uint64(sectoff))
+
+       elfsym := r.Xsym.ElfsymForReloc()
+       ctxt.Out.Write32(uint32(elfsym))
+       ctxt.Out.Write8(0)
+       ctxt.Out.Write8(0)
+       ctxt.Out.Write8(0)
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               switch r.Siz {
+               case 4:
+                       ctxt.Out.Write8(uint8(elf.R_MIPS_32))
+               case 8:
+                       ctxt.Out.Write8(uint8(elf.R_MIPS_64))
+               default:
+                       return false
+               }
+       case objabi.R_ADDRMIPS:
+               ctxt.Out.Write8(uint8(elf.R_MIPS_LO16))
+       case objabi.R_ADDRMIPSU:
+               ctxt.Out.Write8(uint8(elf.R_MIPS_HI16))
+       case objabi.R_ADDRMIPSTLS:
+               ctxt.Out.Write8(uint8(elf.R_MIPS_TLS_TPREL_LO16))
+       case objabi.R_CALLMIPS,
+               objabi.R_JMPMIPS:
+               ctxt.Out.Write8(uint8(elf.R_MIPS_26))
+       }
+       ctxt.Out.Write64(uint64(r.Xadd))
+
+       return true
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       return
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       return false
+}
+
+func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       if ctxt.LinkMode == ld.LinkExternal {
+               switch r.Type {
+               default:
+                       return val, false
+               case objabi.R_ADDRMIPS,
+                       objabi.R_ADDRMIPSU:
+                       r.Done = false
+
+                       // set up addend for eventual relocation via outer symbol.
+                       rs := r.Sym
+                       r.Xadd = r.Add
+                       for rs.Outer != nil {
+                               r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer)
+                               rs = rs.Outer
+                       }
+
+                       if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil {
+                               ld.Errorf(s, "missing section for %s", rs.Name)
+                       }
+                       r.Xsym = rs
+
+                       return val, true
+               case objabi.R_ADDRMIPSTLS,
+                       objabi.R_CALLMIPS,
+                       objabi.R_JMPMIPS:
+                       r.Done = false
+                       r.Xsym = r.Sym
+                       r.Xadd = r.Add
+                       return val, true
+               }
+       }
+
+       switch r.Type {
+       case objabi.R_CONST:
+               return r.Add, true
+       case objabi.R_GOTOFF:
+               return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+       case objabi.R_ADDRMIPS,
+               objabi.R_ADDRMIPSU:
+               t := ld.Symaddr(r.Sym) + r.Add
+               o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:])
+               if r.Type == objabi.R_ADDRMIPS {
+                       return int64(o1&0xffff0000 | uint32(t)&0xffff), true
+               }
+               return int64(o1&0xffff0000 | uint32((t+1<<15)>>16)&0xffff), true
+       case objabi.R_ADDRMIPSTLS:
+               // thread pointer is at 0x7000 offset from the start of TLS data area
+               t := ld.Symaddr(r.Sym) + r.Add - 0x7000
+               if t < -32768 || t >= 32678 {
+                       ld.Errorf(s, "TLS offset out of range %d", t)
+               }
+               o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:])
+               return int64(o1&0xffff0000 | uint32(t)&0xffff), true
+       case objabi.R_CALLMIPS,
+               objabi.R_JMPMIPS:
+               // Low 26 bits = (S + A) >> 2
+               t := ld.Symaddr(r.Sym) + r.Add
+               o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:])
+               return int64(o1&0xfc000000 | uint32(t>>2)&^0xfc000000), true
+       }
+
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       return -1
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       sect := ld.Segtext.Sections[0]
+       ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+       ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       for _, sect = range ld.Segtext.Sections[1:] {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+       if ld.Segrelrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       /* output symbol table */
+       ld.Symsize = 0
+
+       ld.Lcsize = 0
+       symo := uint32(0)
+       if !*ld.FlagS {
+               // TODO: rationalize
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                               symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+                       }
+
+               case objabi.Hplan9:
+                       symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+               }
+
+               ctxt.Out.SeekSet(int64(symo))
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               ld.Asmelfsym(ctxt)
+                               ctxt.Out.Flush()
+                               ctxt.Out.Write(ld.Elfstrdat)
+
+                               if ctxt.LinkMode == ld.LinkExternal {
+                                       ld.Elfemitreloc(ctxt)
+                               }
+                       }
+
+               case objabi.Hplan9:
+                       ld.Asmplan9sym(ctxt)
+                       ctxt.Out.Flush()
+
+                       sym := ctxt.Syms.Lookup("pclntab", 0)
+                       if sym != nil {
+                               ld.Lcsize = int32(len(sym.P))
+                               ctxt.Out.Write(sym.P)
+                               ctxt.Out.Flush()
+                       }
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       default:
+       case objabi.Hplan9: /* plan 9 */
+               magic := uint32(4*18*18 + 7)
+               if ctxt.Arch == sys.ArchMIPS64LE {
+                       magic = uint32(4*26*26 + 7)
+               }
+               ctxt.Out.Write32(magic)                      /* magic */
+               ctxt.Out.Write32(uint32(ld.Segtext.Filelen)) /* sizes */
+               ctxt.Out.Write32(uint32(ld.Segdata.Filelen))
+               ctxt.Out.Write32(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
+               ctxt.Out.Write32(uint32(ld.Symsize))          /* nsyms */
+               ctxt.Out.Write32(uint32(ld.Entryvalue(ctxt))) /* va of entry */
+               ctxt.Out.Write32(0)
+               ctxt.Out.Write32(uint32(ld.Lcsize))
+
+       case objabi.Hlinux,
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd:
+               ld.Asmbelf(ctxt, int64(symo))
+       }
+
+       ctxt.Out.Flush()
+       if *ld.FlagC {
+               fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
+               fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
+               fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
+               fmt.Printf("symsize=%d\n", ld.Symsize)
+               fmt.Printf("lcsize=%d\n", ld.Lcsize)
+               fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize))
+       }
+}
diff --git a/src/cmd/oldlink/internal/mips64/l.go b/src/cmd/oldlink/internal/mips64/l.go
new file mode 100644 (file)
index 0000000..d794122
--- /dev/null
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/mips64/obj.go b/src/cmd/oldlink/internal/mips64/obj.go
new file mode 100644 (file)
index 0000000..1ddce45
--- /dev/null
@@ -0,0 +1,98 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+       arch := sys.ArchMIPS64
+       if objabi.GOARCH == "mips64le" {
+               arch = sys.ArchMIPS64LE
+       }
+
+       theArch := ld.Arch{
+               Funcalign:        funcAlign,
+               Maxalign:         maxAlign,
+               Minalign:         minAlign,
+               Dwarfregsp:       dwarfRegSP,
+               Dwarfreglr:       dwarfRegLR,
+               Adddynrel:        adddynrel,
+               Archinit:         archinit,
+               Archreloc:        archreloc,
+               Archrelocvariant: archrelocvariant,
+               Asmb:             asmb,
+               Asmb2:            asmb2,
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Machoreloc1:      machoreloc1,
+
+               Linuxdynld:     "/lib64/ld64.so.1",
+               Freebsddynld:   "XXX",
+               Openbsddynld:   "XXX",
+               Netbsddynld:    "XXX",
+               Dragonflydynld: "XXX",
+               Solarisdynld:   "XXX",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+       case objabi.Hplan9: /* plan 9 */
+               ld.HEADR = 32
+
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 16*1024 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 16 * 1024
+               }
+
+       case objabi.Hlinux: /* mips64 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/oldlink/internal/objfile/objfile.go b/src/cmd/oldlink/internal/objfile/objfile.go
new file mode 100644 (file)
index 0000000..83b931d
--- /dev/null
@@ -0,0 +1,659 @@
+// 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 objfile reads Go object files for the Go linker, cmd/link.
+//
+// This package is similar to cmd/internal/objfile which also reads
+// Go object files.
+package objfile
+
+import (
+       "bufio"
+       "bytes"
+       "cmd/internal/bio"
+       "cmd/internal/dwarf"
+       "cmd/internal/obj"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/sym"
+       "fmt"
+       "io"
+       "log"
+       "os"
+       "strconv"
+       "strings"
+       "unsafe"
+)
+
+const (
+       startmagic = "\x00go114ld"
+       endmagic   = "\xffgo114ld"
+)
+
+var emptyPkg = []byte(`"".`)
+
+// objReader reads Go object files.
+type objReader struct {
+       rd              *bio.Reader
+       arch            *sys.Arch
+       syms            *sym.Symbols
+       lib             *sym.Library
+       unit            *sym.CompilationUnit
+       pn              string
+       dupSym          *sym.Symbol
+       localSymVersion int
+       flags           int
+       strictDupMsgs   int
+       dataSize        int
+
+       // rdBuf is used by readString and readSymName as scratch for reading strings.
+       rdBuf []byte
+
+       // List of symbol references for the file being read.
+       refs        []*sym.Symbol
+       data        []byte
+       reloc       []sym.Reloc
+       pcdata      []sym.Pcdata
+       funcdata    []*sym.Symbol
+       funcdataoff []int64
+       file        []*sym.Symbol
+       pkgpref     string // objabi.PathToPrefix(r.lib.Pkg) + "."
+
+       roObject []byte // from read-only mmap of object file (may be nil)
+       roOffset int64  // offset into readonly object data examined so far
+
+       dataReadOnly bool // whether data is backed by read-only memory
+}
+
+// Flags to enable optional behavior during object loading/reading.
+
+const (
+       NoFlag int = iota
+
+       // Sanity-check duplicate symbol contents, issuing warning
+       // when duplicates have different lengths or contents.
+       StrictDupsWarnFlag
+
+       // Similar to StrictDupsWarnFlag, but issue fatal error.
+       StrictDupsErrFlag
+)
+
+// Load loads an object file f into library lib.
+// The symbols loaded are added to syms.
+func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) int {
+       start := f.Offset()
+       roObject := f.SliceRO(uint64(length))
+       if roObject != nil {
+               f.MustSeek(int64(-length), os.SEEK_CUR)
+       }
+       r := &objReader{
+               rd:              f,
+               lib:             lib,
+               unit:            unit,
+               arch:            arch,
+               syms:            syms,
+               pn:              pn,
+               dupSym:          &sym.Symbol{Name: ".dup"},
+               localSymVersion: syms.IncVersion(),
+               flags:           flags,
+               roObject:        roObject,
+               pkgpref:         objabi.PathToPrefix(lib.Pkg) + ".",
+       }
+       r.loadObjFile()
+       if roObject != nil {
+               if r.roOffset != length {
+                       log.Fatalf("%s: unexpected end at %d, want %d", pn, r.roOffset, start+length)
+               }
+               r.rd.MustSeek(int64(length), os.SEEK_CUR)
+       } else if f.Offset() != start+length {
+               log.Fatalf("%s: unexpected end at %d, want %d", pn, f.Offset(), start+length)
+       }
+       return r.strictDupMsgs
+}
+
+func (r *objReader) loadObjFile() {
+       // Magic header
+       var buf [8]uint8
+       r.readFull(buf[:])
+       if string(buf[:]) != startmagic {
+               log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", r.pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7])
+       }
+
+       // Version
+       c, err := r.readByte()
+       if err != nil || c != 1 {
+               log.Fatalf("%s: invalid file version number %d", r.pn, c)
+       }
+
+       // Autolib
+       for {
+               lib := r.readString()
+               if lib == "" {
+                       break
+               }
+               r.lib.ImportStrings = append(r.lib.ImportStrings, lib)
+       }
+
+       // DWARF strings
+       count := r.readInt()
+       r.unit.DWARFFileTable = make([]string, count)
+       for i := 0; i < count; i++ {
+               // TODO: This should probably be a call to mkROString.
+               r.unit.DWARFFileTable[i] = r.readString()
+       }
+
+       // Symbol references
+       r.refs = []*sym.Symbol{nil} // zeroth ref is nil
+       for {
+               c, err := r.peek(1)
+               if err != nil {
+                       log.Fatalf("%s: peeking: %v", r.pn, err)
+               }
+               if c[0] == 0xff {
+                       r.readByte()
+                       break
+               }
+               r.readRef()
+       }
+
+       // Lengths
+       r.readSlices()
+
+       // Data section
+       err = r.readDataSection()
+       if err != nil {
+               log.Fatalf("%s: error reading %s", r.pn, err)
+       }
+
+       // Defined symbols
+       for {
+               c, err := r.peek(1)
+               if err != nil {
+                       log.Fatalf("%s: peeking: %v", r.pn, err)
+               }
+               if c[0] == 0xff {
+                       break
+               }
+               r.readSym()
+       }
+
+       // Magic footer
+       buf = [8]uint8{}
+       r.readFull(buf[:])
+       if string(buf[:]) != endmagic {
+               log.Fatalf("%s: invalid file end", r.pn)
+       }
+}
+
+func (r *objReader) readSlices() {
+       r.dataSize = r.readInt()
+       n := r.readInt()
+       r.reloc = make([]sym.Reloc, n)
+       n = r.readInt()
+       r.pcdata = make([]sym.Pcdata, n)
+       _ = r.readInt() // TODO: remove on next object file rev (autom count)
+       n = r.readInt()
+       r.funcdata = make([]*sym.Symbol, n)
+       r.funcdataoff = make([]int64, n)
+       n = r.readInt()
+       r.file = make([]*sym.Symbol, n)
+}
+
+func (r *objReader) readDataSection() (err error) {
+       if r.roObject != nil {
+               r.data, r.dataReadOnly, err =
+                       r.roObject[r.roOffset:r.roOffset+int64(r.dataSize)], true, nil
+               r.roOffset += int64(r.dataSize)
+               return
+       }
+       r.data, r.dataReadOnly, err = r.rd.Slice(uint64(r.dataSize))
+       return
+}
+
+// Symbols are prefixed so their content doesn't get confused with the magic footer.
+const symPrefix = 0xfe
+
+func (r *objReader) readSym() {
+       var c byte
+       var err error
+       if c, err = r.readByte(); c != symPrefix || err != nil {
+               log.Fatalln("readSym out of sync")
+       }
+       if c, err = r.readByte(); err != nil {
+               log.Fatalln("error reading input: ", err)
+       }
+       t := sym.AbiSymKindToSymKind[c]
+       s := r.readSymIndex()
+       flags := r.readInt()
+       dupok := flags&1 != 0
+       local := flags&2 != 0
+       makeTypelink := flags&4 != 0
+       size := r.readInt()
+       typ := r.readSymIndex()
+       data := r.readData()
+       nreloc := r.readInt()
+       isdup := false
+
+       var dup *sym.Symbol
+       if s.Type != 0 && s.Type != sym.SXREF {
+               if (t == sym.SDATA || t == sym.SBSS || t == sym.SNOPTRBSS) && len(data) == 0 && nreloc == 0 {
+                       if s.Size < int64(size) {
+                               s.Size = int64(size)
+                       }
+                       if typ != nil && s.Gotype == nil {
+                               s.Gotype = typ
+                       }
+                       return
+               }
+
+               if (s.Type == sym.SDATA || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 {
+                       goto overwrite
+               }
+               if s.Type != sym.SBSS && s.Type != sym.SNOPTRBSS && !dupok && !s.Attr.DuplicateOK() {
+                       log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, r.pn)
+               }
+               if len(s.P) > 0 {
+                       dup = s
+                       s = r.dupSym
+                       isdup = true
+               }
+       }
+
+overwrite:
+       s.File = r.pkgpref[:len(r.pkgpref)-1]
+       s.Unit = r.unit
+       if dupok {
+               s.Attr |= sym.AttrDuplicateOK
+       }
+       if t == sym.SXREF {
+               log.Fatalf("bad sxref")
+       }
+       if t == 0 {
+               log.Fatalf("missing type for %s in %s", s.Name, r.pn)
+       }
+       if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
+               t = s.Type
+       }
+       s.Type = t
+       if s.Size < int64(size) {
+               s.Size = int64(size)
+       }
+       s.Attr.Set(sym.AttrLocal, local)
+       s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
+       if typ != nil {
+               s.Gotype = typ
+       }
+       if isdup && typ != nil { // if bss sym defined multiple times, take type from any one def
+               dup.Gotype = typ
+       }
+       s.P = data
+       s.Attr.Set(sym.AttrReadOnly, r.dataReadOnly)
+       if nreloc > 0 {
+               s.R = r.reloc[:nreloc:nreloc]
+               if !isdup {
+                       r.reloc = r.reloc[nreloc:]
+               }
+
+               for i := 0; i < nreloc; i++ {
+                       s.R[i] = sym.Reloc{
+                               Off:  r.readInt32(),
+                               Siz:  r.readUint8(),
+                               Type: objabi.RelocType(r.readInt32()),
+                               Add:  r.readInt64(),
+                               Sym:  r.readSymIndex(),
+                       }
+               }
+       }
+
+       if s.Type == sym.STEXT {
+               s.FuncInfo = new(sym.FuncInfo)
+               pc := s.FuncInfo
+
+               pc.Args = r.readInt32()
+               pc.Locals = r.readInt32()
+               if r.readUint8() != 0 {
+                       s.Attr |= sym.AttrNoSplit
+               }
+               flags := r.readInt()
+               if flags&(1<<2) != 0 {
+                       s.Attr |= sym.AttrReflectMethod
+               }
+               if flags&(1<<3) != 0 {
+                       s.Attr |= sym.AttrShared
+               }
+               if flags&(1<<4) != 0 {
+                       s.Attr |= sym.AttrTopFrame
+               }
+               n := r.readInt()
+               if n != 0 {
+                       log.Fatalf("stale object file: autom count nonzero")
+               }
+
+               pc.Pcsp.P = r.readData()
+               pc.Pcfile.P = r.readData()
+               pc.Pcline.P = r.readData()
+               pc.Pcinline.P = r.readData()
+               n = r.readInt()
+               pc.Pcdata = r.pcdata[:n:n]
+               if !isdup {
+                       r.pcdata = r.pcdata[n:]
+               }
+               for i := 0; i < n; i++ {
+                       pc.Pcdata[i].P = r.readData()
+               }
+               n = r.readInt()
+               pc.Funcdata = r.funcdata[:n:n]
+               pc.Funcdataoff = r.funcdataoff[:n:n]
+               if !isdup {
+                       r.funcdata = r.funcdata[n:]
+                       r.funcdataoff = r.funcdataoff[n:]
+               }
+               for i := 0; i < n; i++ {
+                       pc.Funcdata[i] = r.readSymIndex()
+               }
+               for i := 0; i < n; i++ {
+                       pc.Funcdataoff[i] = r.readInt64()
+               }
+               n = r.readInt()
+               pc.File = r.file[:n:n]
+               if !isdup {
+                       r.file = r.file[n:]
+               }
+               for i := 0; i < n; i++ {
+                       pc.File[i] = r.readSymIndex()
+               }
+               n = r.readInt()
+               pc.InlTree = make([]sym.InlinedCall, n)
+               for i := 0; i < n; i++ {
+                       pc.InlTree[i].Parent = r.readInt32()
+                       pc.InlTree[i].File = r.readSymIndex()
+                       pc.InlTree[i].Line = r.readInt32()
+                       pc.InlTree[i].Func = r.readSymIndex().Name
+                       pc.InlTree[i].ParentPC = r.readInt32()
+               }
+
+               if !dupok {
+                       if s.Attr.OnList() {
+                               log.Fatalf("symbol %s listed multiple times", s.Name)
+                       }
+                       s.Attr |= sym.AttrOnList
+                       r.lib.Textp = append(r.lib.Textp, s)
+               } else {
+                       // there may ba a dup in another package
+                       // put into a temp list and add to text later
+                       if !isdup {
+                               r.lib.DupTextSyms = append(r.lib.DupTextSyms, s)
+                       } else {
+                               r.lib.DupTextSyms = append(r.lib.DupTextSyms, dup)
+                       }
+               }
+       }
+       if s.Type == sym.SDWARFINFO {
+               r.patchDWARFName(s)
+       }
+
+       if isdup && r.flags&(StrictDupsWarnFlag|StrictDupsErrFlag) != 0 {
+               // Compare the just-read symbol with the previously read
+               // symbol of the same name, verifying that they have the same
+               // payload. If not, issue a warning and possibly an error.
+               if !bytes.Equal(s.P, dup.P) {
+                       reason := "same length but different contents"
+                       if len(s.P) != len(dup.P) {
+                               reason = fmt.Sprintf("new length %d != old length %d",
+                                       len(data), len(dup.P))
+                       }
+                       fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.lib, dup, dup.Unit.Lib, reason)
+
+                       // For the moment, whitelist 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.
+                       whitelist := (strings.HasPrefix(dup.Name, "go.info.go.interface") ||
+                               strings.HasPrefix(dup.Name, "go.info.go.builtin") ||
+                               strings.HasPrefix(dup.Name, "go.isstmt.go.builtin") ||
+                               strings.HasPrefix(dup.Name, "go.debuglines"))
+                       if !whitelist {
+                               r.strictDupMsgs++
+                       }
+               }
+       }
+}
+
+func (r *objReader) patchDWARFName(s *sym.Symbol) {
+       // This is kind of ugly. Really the package name should not
+       // even be included here.
+       if s.Size < 1 || s.P[0] != dwarf.DW_ABRV_FUNCTION {
+               return
+       }
+       e := bytes.IndexByte(s.P, 0)
+       if e == -1 {
+               return
+       }
+       p := bytes.Index(s.P[:e], emptyPkg)
+       if p == -1 {
+               return
+       }
+       pkgprefix := []byte(r.pkgpref)
+       patched := bytes.Replace(s.P[:e], emptyPkg, pkgprefix, -1)
+
+       s.P = append(patched, s.P[e:]...)
+       delta := int64(len(s.P)) - s.Size
+       s.Size = int64(len(s.P))
+       for i := range s.R {
+               r := &s.R[i]
+               if r.Off > int32(e) {
+                       r.Off += int32(delta)
+               }
+       }
+}
+
+func (r *objReader) readFull(b []byte) {
+       if r.roObject != nil {
+               copy(b, r.roObject[r.roOffset:])
+               r.roOffset += int64(len(b))
+               return
+       }
+       _, err := io.ReadFull(r.rd, b)
+       if err != nil {
+               log.Fatalf("%s: error reading %s", r.pn, err)
+       }
+}
+
+func (r *objReader) readByte() (byte, error) {
+       if r.roObject != nil {
+               b := r.roObject[r.roOffset]
+               r.roOffset++
+               return b, nil
+       }
+       return r.rd.ReadByte()
+}
+
+func (r *objReader) peek(n int) ([]byte, error) {
+       if r.roObject != nil {
+               return r.roObject[r.roOffset : r.roOffset+int64(n)], nil
+       }
+       return r.rd.Peek(n)
+}
+
+func (r *objReader) readRef() {
+       if c, err := r.readByte(); c != symPrefix || err != nil {
+               log.Fatalf("readSym out of sync")
+       }
+       name := r.readSymName()
+       var v int
+       if abi := r.readInt(); abi == -1 {
+               // Static
+               v = r.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 for %q: %d", name, abi)
+       }
+       s := r.syms.Lookup(name, v)
+       r.refs = append(r.refs, s)
+
+       if s == nil || v == r.localSymVersion {
+               return
+       }
+       if s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
+               x, err := strconv.ParseUint(s.Name[5:], 16, 64)
+               if err != nil {
+                       log.Panicf("failed to parse $-symbol %s: %v", s.Name, err)
+               }
+               s.Type = sym.SRODATA
+               s.Attr |= sym.AttrLocal
+               switch s.Name[:5] {
+               case "$f32.":
+                       if uint64(uint32(x)) != x {
+                               log.Panicf("$-symbol %s too large: %d", s.Name, x)
+                       }
+                       s.AddUint32(r.arch, uint32(x))
+               case "$f64.", "$i64.":
+                       s.AddUint64(r.arch, x)
+               default:
+                       log.Panicf("unrecognized $-symbol: %s", s.Name)
+               }
+               s.Attr.Set(sym.AttrReachable, false)
+       }
+       if strings.HasPrefix(s.Name, "runtime.gcbits.") {
+               s.Attr |= sym.AttrLocal
+       }
+}
+
+func (r *objReader) readInt64() int64 {
+       uv := uint64(0)
+       for shift := uint(0); ; shift += 7 {
+               if shift >= 64 {
+                       log.Fatalf("corrupt input")
+               }
+               c, err := r.readByte()
+               if err != nil {
+                       log.Fatalln("error reading input: ", err)
+               }
+               uv |= uint64(c&0x7F) << shift
+               if c&0x80 == 0 {
+                       break
+               }
+       }
+
+       return int64(uv>>1) ^ (int64(uv<<63) >> 63)
+}
+
+func (r *objReader) readInt() int {
+       n := r.readInt64()
+       if int64(int(n)) != n {
+               log.Panicf("%v out of range for int", n)
+       }
+       return int(n)
+}
+
+func (r *objReader) readInt32() int32 {
+       n := r.readInt64()
+       if int64(int32(n)) != n {
+               log.Panicf("%v out of range for int32", n)
+       }
+       return int32(n)
+}
+
+func (r *objReader) readInt16() int16 {
+       n := r.readInt64()
+       if int64(int16(n)) != n {
+               log.Panicf("%v out of range for int16", n)
+       }
+       return int16(n)
+}
+
+func (r *objReader) readUint8() uint8 {
+       n := r.readInt64()
+       if int64(uint8(n)) != n {
+               log.Panicf("%v out of range for uint8", n)
+       }
+       return uint8(n)
+}
+
+func (r *objReader) readString() string {
+       n := r.readInt()
+       if cap(r.rdBuf) < n {
+               r.rdBuf = make([]byte, 2*n)
+       }
+       r.readFull(r.rdBuf[:n])
+       return string(r.rdBuf[:n])
+}
+
+func (r *objReader) readData() []byte {
+       n := r.readInt()
+       p := r.data[:n:n]
+       r.data = r.data[n:]
+       return p
+}
+
+type stringHeader struct {
+       str unsafe.Pointer
+       len int
+}
+
+func mkROString(rodata []byte) string {
+       if len(rodata) == 0 {
+               return ""
+       }
+       ss := stringHeader{str: unsafe.Pointer(&rodata[0]), len: len(rodata)}
+       s := *(*string)(unsafe.Pointer(&ss))
+       return s
+}
+
+// readSymName reads a symbol name, replacing all "". with pkg.
+func (r *objReader) readSymName() string {
+       n := r.readInt()
+       if n == 0 {
+               r.readInt64()
+               return ""
+       }
+       if cap(r.rdBuf) < n {
+               r.rdBuf = make([]byte, 2*n)
+       }
+       sOffset := r.roOffset
+       origName, err := r.peek(n)
+       if err == bufio.ErrBufferFull {
+               // Long symbol names are rare but exist. One source is type
+               // symbols for types with long string forms. See #15104.
+               origName = make([]byte, n)
+               r.readFull(origName)
+       } else if err != nil {
+               log.Fatalf("%s: error reading symbol: %v", r.pn, err)
+       }
+       adjName := r.rdBuf[:0]
+       nPkgRefs := 0
+       for {
+               i := bytes.Index(origName, emptyPkg)
+               if i == -1 {
+                       var s string
+                       if r.roObject != nil && nPkgRefs == 0 {
+                               s = mkROString(r.roObject[sOffset : sOffset+int64(n)])
+                       } else {
+                               s = string(append(adjName, origName...))
+                       }
+                       // Read past the peeked origName, now that we're done with it,
+                       // using the rfBuf (also no longer used) as the scratch space.
+                       // TODO: use bufio.Reader.Discard if available instead?
+                       if err == nil {
+                               r.readFull(r.rdBuf[:n])
+                       }
+                       r.rdBuf = adjName[:0] // in case 2*n wasn't enough
+                       return s
+               }
+               nPkgRefs++
+               adjName = append(adjName, origName[:i]...)
+               adjName = append(adjName, r.pkgpref[:len(r.pkgpref)-1]...)
+               adjName = append(adjName, '.')
+               origName = origName[i+len(emptyPkg):]
+       }
+}
+
+// Reads the index of a symbol reference and resolves it to a symbol
+func (r *objReader) readSymIndex() *sym.Symbol {
+       i := r.readInt()
+       return r.refs[i]
+}
diff --git a/src/cmd/oldlink/internal/ppc64/asm.go b/src/cmd/oldlink/internal/ppc64/asm.go
new file mode 100644 (file)
index 0000000..608bdec
--- /dev/null
@@ -0,0 +1,1181 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "encoding/binary"
+       "fmt"
+       "log"
+       "strings"
+)
+
+func genplt(ctxt *ld.Link) {
+       // The ppc64 ABI PLT has similar concepts to other
+       // architectures, but is laid out quite differently. When we
+       // see an R_PPC64_REL24 relocation to a dynamic symbol
+       // (indicating that the call needs to go through the PLT), we
+       // generate up to three stubs and reserve a PLT slot.
+       //
+       // 1) The call site will be bl x; nop (where the relocation
+       //    applies to the bl).  We rewrite this to bl x_stub; ld
+       //    r2,24(r1).  The ld is necessary because x_stub will save
+       //    r2 (the TOC pointer) at 24(r1) (the "TOC save slot").
+       //
+       // 2) We reserve space for a pointer in the .plt section (once
+       //    per referenced dynamic function).  .plt is a data
+       //    section filled solely by the dynamic linker (more like
+       //    .plt.got on other architectures).  Initially, the
+       //    dynamic linker will fill each slot with a pointer to the
+       //    corresponding x@plt entry point.
+       //
+       // 3) We generate the "call stub" x_stub (once per dynamic
+       //    function/object file pair).  This saves the TOC in the
+       //    TOC save slot, reads the function pointer from x's .plt
+       //    slot and calls it like any other global entry point
+       //    (including setting r12 to the function address).
+       //
+       // 4) We generate the "symbol resolver stub" x@plt (once per
+       //    dynamic function).  This is solely a branch to the glink
+       //    resolver stub.
+       //
+       // 5) We generate the glink resolver stub (only once).  This
+       //    computes which symbol resolver stub we came through and
+       //    invokes the dynamic resolver via a pointer provided by
+       //    the dynamic linker. This will patch up the .plt slot to
+       //    point directly at the function so future calls go
+       //    straight from the call stub to the real function, and
+       //    then call the function.
+
+       // NOTE: It's possible we could make ppc64 closer to other
+       // architectures: ppc64's .plt is like .plt.got on other
+       // platforms and ppc64's .glink is like .plt on other
+       // platforms.
+
+       // Find all R_PPC64_REL24 relocations that reference dynamic
+       // imports. Reserve PLT entries for these symbols and
+       // generate call stubs. The call stubs need to live in .text,
+       // which is why we need to do this pass this early.
+       //
+       // This assumes "case 1" from the ABI, where the caller needs
+       // us to save and restore the TOC pointer.
+       var stubs []*sym.Symbol
+       for _, s := range ctxt.Textp {
+               for i := range s.R {
+                       r := &s.R[i]
+                       if r.Type != objabi.ElfRelocOffset+objabi.RelocType(elf.R_PPC64_REL24) || r.Sym.Type != sym.SDYNIMPORT {
+                               continue
+                       }
+
+                       // Reserve PLT entry and generate symbol
+                       // resolver
+                       addpltsym(ctxt, r.Sym)
+
+                       // Generate call stub
+                       n := fmt.Sprintf("%s.%s", s.Name, r.Sym.Name)
+
+                       stub := ctxt.Syms.Lookup(n, 0)
+                       if s.Attr.Reachable() {
+                               stub.Attr |= sym.AttrReachable
+                       }
+                       if stub.Size == 0 {
+                               // Need outer to resolve .TOC.
+                               stub.Outer = s
+                               stubs = append(stubs, stub)
+                               gencallstub(ctxt, 1, stub, r.Sym)
+                       }
+
+                       // Update the relocation to use the call stub
+                       r.Sym = stub
+
+                       // Restore TOC after bl. The compiler put a
+                       // nop here for us to overwrite.
+                       const o1 = 0xe8410018 // ld r2,24(r1)
+                       ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1)
+               }
+       }
+       // Put call stubs at the beginning (instead of the end).
+       // So when resolving the relocations to calls to the stubs,
+       // the addresses are known and trampolines can be inserted
+       // when necessary.
+       ctxt.Textp = append(stubs, ctxt.Textp...)
+}
+
+func genaddmoduledata(ctxt *ld.Link) {
+       addmoduledata := ctxt.Syms.ROLookup("runtime.addmoduledata", sym.SymVerABI0)
+       if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin {
+               return
+       }
+       addmoduledata.Attr |= sym.AttrReachable
+       initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
+       initfunc.Type = sym.STEXT
+       initfunc.Attr |= sym.AttrLocal
+       initfunc.Attr |= sym.AttrReachable
+       o := func(op uint32) {
+               initfunc.AddUint32(ctxt.Arch, op)
+       }
+       // addis r2, r12, .TOC.-func@ha
+       rel := initfunc.AddRel()
+       rel.Off = int32(initfunc.Size)
+       rel.Siz = 8
+       rel.Sym = ctxt.Syms.Lookup(".TOC.", 0)
+       rel.Sym.Attr |= sym.AttrReachable
+       rel.Type = objabi.R_ADDRPOWER_PCREL
+       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
+       rel = initfunc.AddRel()
+       rel.Off = int32(initfunc.Size)
+       rel.Siz = 8
+       if s := ctxt.Syms.ROLookup("local.moduledata", 0); s != nil {
+               rel.Sym = s
+       } else if s := ctxt.Syms.ROLookup("local.pluginmoduledata", 0); s != nil {
+               rel.Sym = s
+       } else {
+               rel.Sym = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
+       }
+       rel.Sym.Attr |= sym.AttrReachable
+       rel.Sym.Attr |= sym.AttrLocal
+       rel.Type = objabi.R_ADDRPOWER_GOT
+       o(0x3c620000)
+       // ld r3, local.moduledata@got@l(r3)
+       o(0xe8630000)
+       // bl runtime.addmoduledata
+       rel = initfunc.AddRel()
+       rel.Off = int32(initfunc.Size)
+       rel.Siz = 4
+       rel.Sym = addmoduledata
+       rel.Type = objabi.R_CALLPOWER
+       o(0x48000001)
+       // nop
+       o(0x60000000)
+       // ld r31, 0(r1)
+       o(0xe8010000)
+       // mtlr r31
+       o(0x7c0803a6)
+       // addi r1,r1,32
+       o(0x38210020)
+       // blr
+       o(0x4e800020)
+
+       if ctxt.BuildMode == ld.BuildModePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
+       initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
+       ctxt.Textp = append(ctxt.Textp, initfunc)
+       initarray_entry.Attr |= sym.AttrReachable
+       initarray_entry.Attr |= sym.AttrLocal
+       initarray_entry.Type = sym.SINITARR
+       initarray_entry.AddAddr(ctxt.Arch, initfunc)
+}
+
+func gentext(ctxt *ld.Link) {
+       if ctxt.DynlinkingGo() {
+               genaddmoduledata(ctxt)
+       }
+
+       if ctxt.LinkMode == ld.LinkInternal {
+               genplt(ctxt)
+       }
+}
+
+// Construct a call stub in stub that calls symbol targ via its PLT
+// entry.
+func gencallstub(ctxt *ld.Link, abicase int, stub *sym.Symbol, targ *sym.Symbol) {
+       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.Syms.Lookup(".plt", 0)
+
+       stub.Type = 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.
+       r := stub.AddRel()
+
+       r.Off = int32(stub.Size)
+       r.Sym = plt
+       r.Add = int64(targ.Plt())
+       r.Siz = 2
+       if ctxt.Arch.ByteOrder == binary.BigEndian {
+               r.Off += int32(r.Siz)
+       }
+       r.Type = objabi.R_POWER_TOC
+       r.Variant = sym.RV_POWER_HA
+       stub.AddUint32(ctxt.Arch, 0x3d820000) // addis r12,r2,targ@plt@toc@ha
+       r = stub.AddRel()
+       r.Off = int32(stub.Size)
+       r.Sym = plt
+       r.Add = int64(targ.Plt())
+       r.Siz = 2
+       if ctxt.Arch.ByteOrder == binary.BigEndian {
+               r.Off += int32(r.Siz)
+       }
+       r.Type = objabi.R_POWER_TOC
+       r.Variant = sym.RV_POWER_LO
+       stub.AddUint32(ctxt.Arch, 0xe98c0000) // ld r12,targ@plt@toc@l(r12)
+
+       // Jump to the loaded pointer
+       stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
+       stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+}
+
+func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       if ctxt.IsELF {
+               return addelfdynrel(ctxt, s, r)
+       } else if ctxt.HeadType == objabi.Haix {
+               return ld.Xcoffadddynrel(ctxt, s, r)
+       }
+       return false
+}
+func addelfdynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       targ := r.Sym
+       r.InitExt()
+
+       switch r.Type {
+       default:
+               if r.Type >= objabi.ElfRelocOffset {
+                       ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
+                       return false
+               }
+
+               // Handle relocations found in ELF object files.
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24):
+               r.Type = 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.)
+               r.Add += int64(r.Sym.Localentry()) * 4
+
+               if targ.Type == sym.SDYNIMPORT {
+                       // Should have been handled in elfsetupplt
+                       ld.Errorf(s, "unexpected R_PPC64_REL24 for dyn import")
+               }
+
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC_REL32):
+               r.Type = objabi.R_PCREL
+               r.Add += 4
+
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_PPC_REL32 for dyn import")
+               }
+
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_ADDR64):
+               r.Type = objabi.R_ADDR
+               if targ.Type == sym.SDYNIMPORT {
+                       // These happen in .toc sections
+                       ld.Adddynsym(ctxt, targ)
+
+                       rela := ctxt.Syms.Lookup(".rela", 0)
+                       rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off))
+                       rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(targ.Dynid), uint32(elf.R_PPC64_ADDR64)))
+                       rela.AddUint64(ctxt.Arch, uint64(r.Add))
+                       r.Type = objabi.ElfRelocOffset // ignore during relocsym
+               }
+
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16):
+               r.Type = objabi.R_POWER_TOC
+               r.Variant = sym.RV_POWER_LO | sym.RV_CHECK_OVERFLOW
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO):
+               r.Type = objabi.R_POWER_TOC
+               r.Variant = sym.RV_POWER_LO
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HA):
+               r.Type = objabi.R_POWER_TOC
+               r.Variant = sym.RV_POWER_HA | sym.RV_CHECK_OVERFLOW
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HI):
+               r.Type = objabi.R_POWER_TOC
+               r.Variant = sym.RV_POWER_HI | sym.RV_CHECK_OVERFLOW
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_DS):
+               r.Type = objabi.R_POWER_TOC
+               r.Variant = sym.RV_POWER_DS | sym.RV_CHECK_OVERFLOW
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO_DS):
+               r.Type = objabi.R_POWER_TOC
+               r.Variant = sym.RV_POWER_DS
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_LO):
+               r.Type = objabi.R_PCREL
+               r.Variant = sym.RV_POWER_LO
+               r.Add += 2 // Compensate for relocation size of 2
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HI):
+               r.Type = objabi.R_PCREL
+               r.Variant = sym.RV_POWER_HI | sym.RV_CHECK_OVERFLOW
+               r.Add += 2
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HA):
+               r.Type = objabi.R_PCREL
+               r.Variant = sym.RV_POWER_HA | sym.RV_CHECK_OVERFLOW
+               r.Add += 2
+               return true
+       }
+
+       // Handle references to ELF symbols from our own object files.
+       if targ.Type != sym.SDYNIMPORT {
+               return true
+       }
+
+       // TODO(austin): Translate our relocations to ELF
+
+       return false
+}
+
+func xcoffreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       rs := r.Xsym
+
+       emitReloc := func(v uint16, off uint64) {
+               out.Write64(uint64(sectoff) + off)
+               out.Write32(uint32(rs.Dynid))
+               out.Write16(v)
+       }
+
+       var v uint16
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               v = ld.XCOFF_R_POS
+               if r.Siz == 4 {
+                       v |= 0x1F << 8
+               } else {
+                       v |= 0x3F << 8
+               }
+               emitReloc(v, 0)
+       case objabi.R_ADDRPOWER_TOCREL:
+       case objabi.R_ADDRPOWER_TOCREL_DS:
+               emitReloc(ld.XCOFF_R_TOCU|(0x0F<<8), 2)
+               emitReloc(ld.XCOFF_R_TOCL|(0x0F<<8), 6)
+       case objabi.R_POWER_TLS_LE:
+               emitReloc(ld.XCOFF_R_TLS_LE|0x0F<<8, 2)
+       case objabi.R_CALLPOWER:
+               if r.Siz != 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, r *sym.Reloc, sectoff int64) bool {
+       // Beware that bit0~bit15 start from the third byte of a instruction in Big-Endian machines.
+       if r.Type == objabi.R_ADDR || r.Type == objabi.R_POWER_TLS || r.Type == objabi.R_CALLPOWER {
+       } else {
+               if ctxt.Arch.ByteOrder == binary.BigEndian {
+                       sectoff += 2
+               }
+       }
+       ctxt.Out.Write64(uint64(sectoff))
+
+       elfsym := r.Xsym.ElfsymForReloc()
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               switch r.Siz {
+               case 4:
+                       ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR32) | uint64(elfsym)<<32)
+               case 8:
+                       ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR64) | uint64(elfsym)<<32)
+               default:
+                       return false
+               }
+       case objabi.R_POWER_TLS:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_TLS) | uint64(elfsym)<<32)
+       case objabi.R_POWER_TLS_LE:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_TPREL16) | uint64(elfsym)<<32)
+       case objabi.R_POWER_TLS_IE:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_HA) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_LO_DS) | uint64(elfsym)<<32)
+       case objabi.R_ADDRPOWER:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR16_LO) | uint64(elfsym)<<32)
+       case objabi.R_ADDRPOWER_DS:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR16_LO_DS) | uint64(elfsym)<<32)
+       case objabi.R_ADDRPOWER_GOT:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_GOT16_HA) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_PPC64_GOT16_LO_DS) | uint64(elfsym)<<32)
+       case objabi.R_ADDRPOWER_PCREL:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_REL16_HA) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_PPC64_REL16_LO) | uint64(elfsym)<<32)
+               r.Xadd += 4
+       case objabi.R_ADDRPOWER_TOCREL:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_PPC64_TOC16_LO) | uint64(elfsym)<<32)
+       case objabi.R_ADDRPOWER_TOCREL_DS:
+               ctxt.Out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32)
+               ctxt.Out.Write64(uint64(r.Xadd))
+               ctxt.Out.Write64(uint64(sectoff + 4))
+               ctxt.Out.Write64(uint64(elf.R_PPC64_TOC16_LO_DS) | uint64(elfsym)<<32)
+       case objabi.R_CALLPOWER:
+               if r.Siz != 4 {
+                       return false
+               }
+               ctxt.Out.Write64(uint64(elf.R_PPC64_REL24) | uint64(elfsym)<<32)
+
+       }
+       ctxt.Out.Write64(uint64(r.Xadd))
+
+       return true
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       plt := ctxt.Syms.Lookup(".plt", 0)
+       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.Size = 16
+       }
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       return false
+}
+
+// Return the value of .TOC. for symbol s
+func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 {
+       var toc *sym.Symbol
+
+       if s.Outer != nil {
+               toc = ctxt.Syms.ROLookup(".TOC.", int(s.Outer.Version))
+       } else {
+               toc = ctxt.Syms.ROLookup(".TOC.", int(s.Version))
+       }
+
+       if toc == nil {
+               ld.Errorf(s, "TOC-relative relocation in object without .TOC.")
+               return 0
+       }
+
+       return toc.Value
+}
+
+// archreloctoc relocates a TOC relative symbol.
+// If the symbol pointed by this TOC relative symbol is in .data or .bss, the
+// default load instruction can be changed to an addi instruction and the
+// symbol address can be used directly.
+// This code is for AIX only.
+func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
+       if ctxt.HeadType == objabi.Hlinux {
+               ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name)
+       }
+       var o1, o2 uint32
+
+       o1 = uint32(val >> 32)
+       o2 = uint32(val)
+
+       var t int64
+       useAddi := false
+       const prefix = "TOC."
+       var tarSym *sym.Symbol
+       if strings.HasPrefix(r.Sym.Name, prefix) {
+               tarSym = r.Sym.R[0].Sym
+       } else {
+               ld.Errorf(s, "archreloctoc called for a symbol without TOC anchor")
+       }
+
+       if ctxt.LinkMode == ld.LinkInternal && tarSym != nil && tarSym.Attr.Reachable() && (tarSym.Sect.Seg == &ld.Segdata) {
+               t = ld.Symaddr(tarSym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value
+               // change ld to addi in the second instruction
+               o2 = (o2 & 0x03FF0000) | 0xE<<26
+               useAddi = true
+       } else {
+               t = ld.Symaddr(r.Sym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value
+       }
+
+       if t != int64(int32(t)) {
+               ld.Errorf(s, "TOC relocation for %s is too big to relocate %s: 0x%x", s.Name, r.Sym, 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 {
+                               ld.Errorf(s, "bad DS reloc for %s: %d", s.Name, ld.Symaddr(r.Sym))
+                       }
+                       o2 |= uint32(t) & 0xFFFC
+               }
+       default:
+               return -1
+       }
+
+       return int64(o1)<<32 | int64(o2)
+}
+
+// archrelocaddr relocates a symbol address.
+// This code is for AIX only.
+func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
+       if ctxt.HeadType == objabi.Haix {
+               ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name)
+       }
+       var o1, o2 uint32
+       if ctxt.Arch.ByteOrder == binary.BigEndian {
+               o1 = uint32(val >> 32)
+               o2 = uint32(val)
+       } else {
+               o1 = uint32(val)
+               o2 = uint32(val >> 32)
+       }
+
+       // We are spreading a 31-bit address across two instructions, putting the
+       // high (adjusted) part in the low 16 bits of the first instruction and the
+       // low part in the low 16 bits of the second instruction, or, in the DS case,
+       // bits 15-2 (inclusive) of the address into bits 15-2 of the second
+       // instruction (it is an error in this case if the low 2 bits of the address
+       // are non-zero).
+
+       t := ld.Symaddr(r.Sym) + r.Add
+       if t < 0 || t >= 1<<31 {
+               ld.Errorf(s, "relocation for %s is too big (>=2G): 0x%x", s.Name, ld.Symaddr(r.Sym))
+       }
+       if t&0x8000 != 0 {
+               t += 0x10000
+       }
+
+       switch r.Type {
+       case objabi.R_ADDRPOWER:
+               o1 |= (uint32(t) >> 16) & 0xffff
+               o2 |= uint32(t) & 0xffff
+       case objabi.R_ADDRPOWER_DS:
+               o1 |= (uint32(t) >> 16) & 0xffff
+               if t&3 != 0 {
+                       ld.Errorf(s, "bad DS reloc for %s: %d", s.Name, ld.Symaddr(r.Sym))
+               }
+               o2 |= uint32(t) & 0xfffc
+       default:
+               return -1
+       }
+
+       if ctxt.Arch.ByteOrder == binary.BigEndian {
+               return int64(o1)<<32 | int64(o2)
+       }
+       return int64(o2)<<32 | int64(o1)
+}
+
+// resolve direct jump relocation r in s, and add trampoline if necessary
+func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
+
+       // 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.LinkMode == ld.LinkExternal && (ctxt.DynlinkingGo() || ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE) {
+               // No trampolines needed since r2 contains the TOC
+               return
+       }
+
+       t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off))
+       switch r.Type {
+       case objabi.R_CALLPOWER:
+
+               // If branch offset is too far then create a trampoline.
+
+               if (ctxt.LinkMode == ld.LinkExternal && s.Sect != r.Sym.Sect) || (ctxt.LinkMode == ld.LinkInternal && int64(int32(t<<6)>>6) != t) || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) {
+                       var tramp *sym.Symbol
+                       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.
+
+                               name := r.Sym.Name
+                               if r.Add == 0 {
+                                       name = name + fmt.Sprintf("-tramp%d", i)
+                               } else {
+                                       name = name + fmt.Sprintf("%+x-tramp%d", r.Add, i)
+                               }
+
+                               // Look up the trampoline in case it already exists
+
+                               tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
+                               if tramp.Value == 0 {
+                                       break
+                               }
+
+                               t = ld.Symaddr(tramp) + r.Add - (s.Value + 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.LinkMode == ld.LinkInternal && int64(int32(t<<6)>>6) == t) || (ctxt.LinkMode == ld.LinkExternal && s.Sect == tramp.Sect) {
+                                       break
+                               }
+                       }
+                       if tramp.Type == 0 {
+                               if ctxt.DynlinkingGo() || ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE {
+                                       // Should have returned for above cases
+                                       ld.Errorf(s, "unexpected trampoline for shared or dynamic linking\n")
+                               } else {
+                                       ctxt.AddTramp(tramp)
+                                       gentramp(ctxt, tramp, r.Sym, r.Add)
+                               }
+                       }
+                       r.Sym = tramp
+                       r.Add = 0 // This was folded into the trampoline target address
+                       r.Done = false
+               }
+       default:
+               ld.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
+       }
+}
+
+func gentramp(ctxt *ld.Link, tramp, target *sym.Symbol, offset int64) {
+       tramp.Size = 16 // 4 instructions
+       tramp.P = make([]byte, tramp.Size)
+       t := ld.Symaddr(target) + offset
+       var o1, o2 uint32
+
+       if ctxt.HeadType == objabi.Haix {
+               // On AIX, the address is retrieved with a TOC symbol.
+               // For internal linking, the "Linux" way might still be used.
+               // However, all text symbols are accessed with a TOC symbol as
+               // text relocations aren't supposed to be possible.
+               // So, keep using the external linking way to be more AIX friendly.
+               o1 = uint32(0x3fe20000) // lis r2, toctargetaddr hi
+               o2 = uint32(0xebff0000) // ld r31, toctargetaddr lo
+
+               toctramp := ctxt.Syms.Lookup("TOC."+tramp.Name, 0)
+               toctramp.Type = sym.SXCOFFTOC
+               toctramp.Attr |= sym.AttrReachable
+               toctramp.AddAddr(ctxt.Arch, target)
+
+               tr := tramp.AddRel()
+               tr.Off = 0
+               tr.Type = objabi.R_ADDRPOWER_TOCREL_DS
+               tr.Siz = 8 // generates 2 relocations:  HA + LO
+               tr.Sym = toctramp
+               tr.Add = offset
+       } else {
+               // Used for default build mode for an executable
+               // Address of the call target is generated using
+               // relocation and doesn't depend on r2 (TOC).
+               o1 = uint32(0x3fe00000) // lis r31,targetaddr hi
+               o2 = uint32(0x3bff0000) // addi r31,targetaddr lo
+
+               // With external linking, the target address must be
+               // relocated using LO and HA
+               if ctxt.LinkMode == ld.LinkExternal {
+                       tr := tramp.AddRel()
+                       tr.Off = 0
+                       tr.Type = objabi.R_ADDRPOWER
+                       tr.Siz = 8 // generates 2 relocations:  HA + LO
+                       tr.Sym = target
+                       tr.Add = offset
+
+               } else {
+                       // adjustment needed if lo has sign bit set
+                       // when using addi to compute address
+                       val := uint32((t & 0xffff0000) >> 16)
+                       if t&0x8000 != 0 {
+                               val += 1
+                       }
+                       o1 |= val                // hi part of addr
+                       o2 |= uint32(t & 0xffff) // lo part of addr
+               }
+       }
+
+       o3 := uint32(0x7fe903a6) // mtctr r31
+       o4 := uint32(0x4e800420) // bctr
+       ctxt.Arch.ByteOrder.PutUint32(tramp.P, o1)
+       ctxt.Arch.ByteOrder.PutUint32(tramp.P[4:], o2)
+       ctxt.Arch.ByteOrder.PutUint32(tramp.P[8:], o3)
+       ctxt.Arch.ByteOrder.PutUint32(tramp.P[12:], o4)
+}
+
+func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       if ctxt.LinkMode == ld.LinkExternal {
+               // On AIX, relocations (except TLS ones) must be also done to the
+               // value with the current addresses.
+               switch r.Type {
+               default:
+                       if ctxt.HeadType != objabi.Haix {
+                               return val, false
+                       }
+               case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE:
+                       r.Done = false
+                       // check Outer is nil, Type is TLSBSS?
+                       r.Xadd = r.Add
+                       r.Xsym = r.Sym
+                       return val, 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:
+                       r.Done = false
+
+                       // set up addend for eventual relocation via outer symbol.
+                       rs := r.Sym
+                       r.Xadd = r.Add
+                       for rs.Outer != nil {
+                               r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer)
+                               rs = rs.Outer
+                       }
+
+                       if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Type != sym.SUNDEFEXT && rs.Sect == nil {
+                               ld.Errorf(s, "missing section for %s", rs.Name)
+                       }
+                       r.Xsym = rs
+
+                       if ctxt.HeadType != objabi.Haix {
+                               return val, true
+                       }
+               case objabi.R_CALLPOWER:
+                       r.Done = false
+                       r.Xsym = r.Sym
+                       r.Xadd = r.Add
+                       if ctxt.HeadType != objabi.Haix {
+                               return val, true
+                       }
+               }
+       }
+
+       switch r.Type {
+       case objabi.R_CONST:
+               return r.Add, true
+       case objabi.R_GOTOFF:
+               return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+       case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS:
+               return archreloctoc(ctxt, r, s, val), true
+       case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS:
+               return archrelocaddr(ctxt, r, s, val), true
+       case objabi.R_CALLPOWER:
+               // Bits 6 through 29 = (S + A - P) >> 2
+
+               t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off))
+
+               if t&3 != 0 {
+                       ld.Errorf(s, "relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t)
+               }
+               // If branch offset is too far then create a trampoline.
+
+               if int64(int32(t<<6)>>6) != t {
+                       ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t)
+               }
+               return val | int64(uint32(t)&^0xfc000003), true
+       case objabi.R_POWER_TOC: // S + A - .TOC.
+               return ld.Symaddr(r.Sym) + r.Add - symtoc(ctxt, s), true
+
+       case objabi.R_POWER_TLS_LE:
+               // The thread pointer points 0x7000 bytes after the start of the
+               // thread local storage area as documented in section "3.7.2 TLS
+               // Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI
+               // Specification".
+               v := r.Sym.Value - 0x7000
+               if ctxt.HeadType == objabi.Haix {
+                       // On AIX, the thread pointer points 0x7800 bytes after
+                       // the TLS.
+                       v -= 0x800
+               }
+               if int64(int16(v)) != v {
+                       ld.Errorf(s, "TLS offset out of range %d", v)
+               }
+               return (val &^ 0xffff) | (v & 0xffff), true
+       }
+
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       switch r.Variant & sym.RV_TYPE_MASK {
+       default:
+               ld.Errorf(s, "unexpected relocation variant %d", r.Variant)
+               fallthrough
+
+       case sym.RV_NONE:
+               return t
+
+       case sym.RV_POWER_LO:
+               if r.Variant&sym.RV_CHECK_OVERFLOW != 0 {
+                       // Whether to check for signed or unsigned
+                       // overflow depends on the instruction
+                       var o1 uint32
+                       if ctxt.Arch.ByteOrder == binary.BigEndian {
+                               o1 = binary.BigEndian.Uint32(s.P[r.Off-2:])
+                       } else {
+                               o1 = binary.LittleEndian.Uint32(s.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 r.Variant&sym.RV_CHECK_OVERFLOW != 0 {
+                       // Whether to check for signed or unsigned
+                       // overflow depends on the instruction
+                       var o1 uint32
+                       if ctxt.Arch.ByteOrder == binary.BigEndian {
+                               o1 = binary.BigEndian.Uint32(s.P[r.Off-2:])
+                       } else {
+                               o1 = binary.LittleEndian.Uint32(s.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 ctxt.Arch.ByteOrder == binary.BigEndian {
+                       o1 = uint32(binary.BigEndian.Uint16(s.P[r.Off:]))
+               } else {
+                       o1 = uint32(binary.LittleEndian.Uint16(s.P[r.Off:]))
+               }
+               if t&3 != 0 {
+                       ld.Errorf(s, "relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t)
+               }
+               if (r.Variant&sym.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t {
+                       goto overflow
+               }
+               return int64(o1)&0x3 | int64(int16(t))
+       }
+
+overflow:
+       ld.Errorf(s, "relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t)
+       return t
+}
+
+func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Plt() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+
+       if ctxt.IsELF {
+               plt := ctxt.Syms.Lookup(".plt", 0)
+               rela := ctxt.Syms.Lookup(".rela.plt", 0)
+               if plt.Size == 0 {
+                       elfsetupplt(ctxt)
+               }
+
+               // Create the glink resolver if necessary
+               glink := ensureglinkresolver(ctxt)
+
+               // Write symbol resolver stub (just a branch to the
+               // glink resolver stub)
+               r := glink.AddRel()
+
+               r.Sym = glink
+               r.Off = int32(glink.Size)
+               r.Siz = 4
+               r.Type = objabi.R_CALLPOWER
+               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
+               s.SetPlt(int32(plt.Size))
+
+               plt.Size += 8
+
+               rela.AddAddrPlus(ctxt.Arch, plt, int64(s.Plt()))
+               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_PPC64_JMP_SLOT)))
+               rela.AddUint64(ctxt.Arch, 0)
+       } else {
+               ld.Errorf(s, "addpltsym: unsupported binary format")
+       }
+}
+
+// Generate the glink resolver stub if necessary and return the .glink section
+func ensureglinkresolver(ctxt *ld.Link) *sym.Symbol {
+       glink := ctxt.Syms.Lookup(".glink", 0)
+       if glink.Size != 0 {
+               return glink
+       }
+
+       // This is essentially the resolver from the ppc64 ELF ABI.
+       // At entry, r12 holds the address of the symbol resolver stub
+       // for the target routine and the argument registers hold the
+       // arguments for the target routine.
+       //
+       // This stub is PIC, so first get the PC of label 1 into r11.
+       // Other things will be relative to this.
+       glink.AddUint32(ctxt.Arch, 0x7c0802a6) // mflr r0
+       glink.AddUint32(ctxt.Arch, 0x429f0005) // bcl 20,31,1f
+       glink.AddUint32(ctxt.Arch, 0x7d6802a6) // 1: mflr r11
+       glink.AddUint32(ctxt.Arch, 0x7c0803a6) // mtlf r0
+
+       // Compute the .plt array index from the entry point address.
+       // Because this is PIC, everything is relative to label 1b (in
+       // r11):
+       //   r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4
+       glink.AddUint32(ctxt.Arch, 0x3800ffd0) // li r0,-(res_0-1b)=-48
+       glink.AddUint32(ctxt.Arch, 0x7c006214) // add r0,r0,r12
+       glink.AddUint32(ctxt.Arch, 0x7c0b0050) // sub r0,r0,r11
+       glink.AddUint32(ctxt.Arch, 0x7800f082) // srdi r0,r0,2
+
+       // r11 = address of the first byte of the PLT
+       r := glink.AddRel()
+
+       r.Off = int32(glink.Size)
+       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+       r.Siz = 8
+       r.Type = objabi.R_ADDRPOWER
+
+       glink.AddUint32(ctxt.Arch, 0x3d600000) // addis r11,0,.plt@ha
+       glink.AddUint32(ctxt.Arch, 0x396b0000) // addi r11,r11,.plt@l
+
+       // Load r12 = dynamic resolver address and r11 = DSO
+       // identifier from the first two doublewords of the PLT.
+       glink.AddUint32(ctxt.Arch, 0xe98b0000) // ld r12,0(r11)
+       glink.AddUint32(ctxt.Arch, 0xe96b0008) // ld r11,8(r11)
+
+       // Jump to the dynamic resolver
+       glink.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
+       glink.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+
+       // The symbol resolvers must immediately follow.
+       //   res_0:
+
+       // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes
+       // before the first symbol resolver stub.
+       s := ctxt.Syms.Lookup(".dynamic", 0)
+
+       ld.Elfwritedynentsymplus(ctxt, s, ld.DT_PPC64_GLINK, glink, glink.Size-32)
+
+       return glink
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       for _, sect := range ld.Segtext.Sections {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               // Handle additional text sections with Codeblk
+               if sect.Name == ".text" {
+                       ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+               } else {
+                       ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+               }
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+       if ld.Segrelrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       /* output symbol table */
+       ld.Symsize = 0
+
+       ld.Lcsize = 0
+       symo := uint32(0)
+       if !*ld.FlagS {
+               // TODO: rationalize
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                               symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+                       }
+
+               case objabi.Hplan9:
+                       symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+
+               case objabi.Haix:
+                       // Nothing to do
+               }
+
+               ctxt.Out.SeekSet(int64(symo))
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               ld.Asmelfsym(ctxt)
+                               ctxt.Out.Flush()
+                               ctxt.Out.Write(ld.Elfstrdat)
+
+                               if ctxt.LinkMode == ld.LinkExternal {
+                                       ld.Elfemitreloc(ctxt)
+                               }
+                       }
+
+               case objabi.Hplan9:
+                       ld.Asmplan9sym(ctxt)
+                       ctxt.Out.Flush()
+
+                       sym := ctxt.Syms.Lookup("pclntab", 0)
+                       if sym != nil {
+                               ld.Lcsize = int32(len(sym.P))
+                               ctxt.Out.Write(sym.P)
+                               ctxt.Out.Flush()
+                       }
+
+               case objabi.Haix:
+                       // symtab must be added once sections have been created in ld.Asmbxcoff
+                       ctxt.Out.Flush()
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       default:
+       case objabi.Hplan9: /* plan 9 */
+               ctxt.Out.Write32(0x647)                      /* magic */
+               ctxt.Out.Write32(uint32(ld.Segtext.Filelen)) /* sizes */
+               ctxt.Out.Write32(uint32(ld.Segdata.Filelen))
+               ctxt.Out.Write32(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
+               ctxt.Out.Write32(uint32(ld.Symsize))          /* nsyms */
+               ctxt.Out.Write32(uint32(ld.Entryvalue(ctxt))) /* va of entry */
+               ctxt.Out.Write32(0)
+               ctxt.Out.Write32(uint32(ld.Lcsize))
+
+       case objabi.Hlinux,
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd:
+               ld.Asmbelf(ctxt, int64(symo))
+
+       case objabi.Haix:
+               fileoff := uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+               fileoff = uint32(ld.Rnd(int64(fileoff), int64(*ld.FlagRound)))
+               ld.Asmbxcoff(ctxt, int64(fileoff))
+       }
+
+       ctxt.Out.Flush()
+       if *ld.FlagC {
+               fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
+               fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
+               fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
+               fmt.Printf("symsize=%d\n", ld.Symsize)
+               fmt.Printf("lcsize=%d\n", ld.Lcsize)
+               fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize))
+       }
+}
diff --git a/src/cmd/oldlink/internal/ppc64/l.go b/src/cmd/oldlink/internal/ppc64/l.go
new file mode 100644 (file)
index 0000000..c78535b
--- /dev/null
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ppc64/obj.go b/src/cmd/oldlink/internal/ppc64/obj.go
new file mode 100644 (file)
index 0000000..e9da5a3
--- /dev/null
@@ -0,0 +1,106 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+       arch := sys.ArchPPC64
+       if objabi.GOARCH == "ppc64le" {
+               arch = sys.ArchPPC64LE
+       }
+
+       theArch := ld.Arch{
+               Funcalign:  funcAlign,
+               Maxalign:   maxAlign,
+               Minalign:   minAlign,
+               Dwarfregsp: dwarfRegSP,
+               Dwarfreglr: dwarfRegLR,
+
+               Adddynrel:        adddynrel,
+               Archinit:         archinit,
+               Archreloc:        archreloc,
+               Archrelocvariant: archrelocvariant,
+               Asmb:             asmb,
+               Asmb2:            asmb2,
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Trampoline:       trampoline,
+               Machoreloc1:      machoreloc1,
+               Xcoffreloc1:      xcoffreloc1,
+
+               // TODO(austin): ABI v1 uses /usr/lib/ld.so.1,
+               Linuxdynld: "/lib64/ld64.so.1",
+
+               Freebsddynld:   "XXX",
+               Openbsddynld:   "XXX",
+               Netbsddynld:    "XXX",
+               Dragonflydynld: "XXX",
+               Solarisdynld:   "XXX",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+       case objabi.Hplan9: /* plan 9 */
+               ld.HEADR = 32
+
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 4128
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+
+       case objabi.Hlinux: /* ppc64 elf */
+               ld.Elfinit(ctxt)
+               ld.HEADR = ld.ELFRESERVE
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 0x10000
+               }
+
+       case objabi.Haix:
+               ld.Xcoffinit(ctxt)
+       }
+}
diff --git a/src/cmd/oldlink/internal/riscv64/asm.go b/src/cmd/oldlink/internal/riscv64/asm.go
new file mode 100644 (file)
index 0000000..f4db32d
--- /dev/null
@@ -0,0 +1,168 @@
+// 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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "fmt"
+       "log"
+)
+
+func gentext(ctxt *ld.Link) {
+}
+
+func adddynrela(ctxt *ld.Link, rel *sym.Symbol, s *sym.Symbol, r *sym.Reloc) {
+       log.Fatalf("adddynrela not implemented")
+}
+
+func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       log.Fatalf("adddynrel not implemented")
+       return false
+}
+
+func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
+       log.Fatalf("elfreloc1")
+       return false
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       log.Fatalf("elfsetuplt")
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       log.Fatalf("machoreloc1 not implemented")
+       return false
+}
+
+func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       switch r.Type {
+       case objabi.R_CALLRISCV:
+               // Nothing to do.
+               return val, true
+
+       case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
+               pc := s.Value + int64(r.Off)
+               off := ld.Symaddr(r.Sym) + r.Add - pc
+
+               // Generate AUIPC and second instruction immediates.
+               low, high, err := riscv.Split32BitImmediate(off)
+               if err != nil {
+                       ld.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32-bits: %d", off)
+               }
+
+               auipcImm, err := riscv.EncodeUImmediate(high)
+               if err != nil {
+                       ld.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", r.Sym.Name, 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 {
+                               ld.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", r.Sym.Name, err)
+                       }
+               case objabi.R_RISCV_PCREL_STYPE:
+                       secondImmMask = riscv.STypeImmMask
+                       secondImm, err = riscv.EncodeSImmediate(low)
+                       if err != nil {
+                               ld.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", r.Sym.Name, 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, true
+       }
+
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       log.Fatalf("archrelocvariant")
+       return -1
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       sect := ld.Segtext.Sections[0]
+       ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+       ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       for _, sect = range ld.Segtext.Sections[1:] {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+       if ld.Segrelrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       ld.Symsize = 0
+       ld.Lcsize = 0
+       symo := uint32(0)
+
+       if !*ld.FlagS {
+               if !ctxt.IsELF {
+                       ld.Errorf(nil, "unsupported executable format")
+               }
+
+               symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+               symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+               ctxt.Out.SeekSet(int64(symo))
+
+               ld.Asmelfsym(ctxt)
+               ctxt.Out.Flush()
+               ctxt.Out.Write(ld.Elfstrdat)
+
+               if ctxt.LinkMode == ld.LinkExternal {
+                       ld.Elfemitreloc(ctxt)
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       case objabi.Hlinux:
+               ld.Asmbelf(ctxt, int64(symo))
+       default:
+               ld.Errorf(nil, "unsupported operating system")
+       }
+       ctxt.Out.Flush()
+
+       if *ld.FlagC {
+               fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
+               fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
+               fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
+               fmt.Printf("symsize=%d\n", ld.Symsize)
+               fmt.Printf("lcsize=%d\n", ld.Lcsize)
+               fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize))
+       }
+}
diff --git a/src/cmd/oldlink/internal/riscv64/l.go b/src/cmd/oldlink/internal/riscv64/l.go
new file mode 100644 (file)
index 0000000..a302657
--- /dev/null
@@ -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/oldlink/internal/riscv64/obj.go b/src/cmd/oldlink/internal/riscv64/obj.go
new file mode 100644 (file)
index 0000000..a6a5adb
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package riscv64
+
+import (
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+       arch := sys.ArchRISCV64
+
+       theArch := ld.Arch{
+               Funcalign:  funcAlign,
+               Maxalign:   maxAlign,
+               Minalign:   minAlign,
+               Dwarfregsp: dwarfRegSP,
+               Dwarfreglr: dwarfRegLR,
+
+               Adddynrel:        adddynrel,
+               Archinit:         archinit,
+               Archreloc:        archreloc,
+               Archrelocvariant: archrelocvariant,
+               Asmb:             asmb,
+               Asmb2:            asmb2,
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Machoreloc1:      machoreloc1,
+
+               Linuxdynld: "/lib/ld.so.1",
+
+               Freebsddynld:   "XXX",
+               Netbsddynld:    "XXX",
+               Openbsddynld:   "XXX",
+               Dragonflydynld: "XXX",
+               Solarisdynld:   "XXX",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       case objabi.Hlinux:
+               ld.Elfinit(ctxt)
+               ld.HEADR = ld.ELFRESERVE
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 0x10000
+               }
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+       }
+}
diff --git a/src/cmd/oldlink/internal/s390x/asm.go b/src/cmd/oldlink/internal/s390x/asm.go
new file mode 100644 (file)
index 0000000..6c5744d
--- /dev/null
@@ -0,0 +1,574 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "fmt"
+)
+
+// 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) {
+       if !ctxt.DynlinkingGo() {
+               return
+       }
+       addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+       if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin {
+               // we're linking a module containing the runtime -> no need for
+               // an init function
+               return
+       }
+       addmoduledata.Attr |= sym.AttrReachable
+       initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
+       initfunc.Type = sym.STEXT
+       initfunc.Attr |= sym.AttrLocal
+       initfunc.Attr |= sym.AttrReachable
+
+       // larl %r2, <local.moduledata>
+       initfunc.AddUint8(0xc0)
+       initfunc.AddUint8(0x20)
+       lmd := initfunc.AddRel()
+       lmd.InitExt()
+       lmd.Off = int32(initfunc.Size)
+       lmd.Siz = 4
+       lmd.Sym = ctxt.Moduledata
+       lmd.Type = objabi.R_PCREL
+       lmd.Variant = sym.RV_390_DBL
+       lmd.Add = 2 + int64(lmd.Siz)
+       initfunc.AddUint32(ctxt.Arch, 0)
+
+       // jg <runtime.addmoduledata[@plt]>
+       initfunc.AddUint8(0xc0)
+       initfunc.AddUint8(0xf4)
+       rel := initfunc.AddRel()
+       rel.InitExt()
+       rel.Off = int32(initfunc.Size)
+       rel.Siz = 4
+       rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+       rel.Type = objabi.R_CALL
+       rel.Variant = sym.RV_390_DBL
+       rel.Add = 2 + int64(rel.Siz)
+       initfunc.AddUint32(ctxt.Arch, 0)
+
+       // undef (for debugging)
+       initfunc.AddUint32(ctxt.Arch, 0)
+       if ctxt.BuildMode == ld.BuildModePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
+       ctxt.Textp = append(ctxt.Textp, initfunc)
+       initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
+       initarray_entry.Attr |= sym.AttrLocal
+       initarray_entry.Attr |= sym.AttrReachable
+       initarray_entry.Type = sym.SINITARR
+       initarray_entry.AddAddr(ctxt.Arch, initfunc)
+}
+
+func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       targ := r.Sym
+       r.InitExt()
+
+       switch r.Type {
+       default:
+               if r.Type >= objabi.ElfRelocOffset {
+                       ld.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):
+               ld.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 targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_390_nn relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = 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 targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_390_PCnn relocation for dynamic symbol %s", targ.Name)
+               }
+               // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+               // sense and should be removed when someone has thought about it properly.
+               if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() {
+                       ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
+               }
+               r.Type = objabi.R_PCREL
+               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):
+               ld.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):
+               r.Type = objabi.R_PCREL
+               r.Variant = sym.RV_390_DBL
+               r.Add += int64(r.Siz)
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add += int64(targ.Plt())
+               }
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32),
+               objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT64):
+               r.Type = objabi.R_PCREL
+               r.Add += int64(r.Siz)
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add += int64(targ.Plt())
+               }
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_COPY):
+               ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset)
+               return false
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GLOB_DAT):
+               ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset)
+               return false
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_JMP_SLOT):
+               ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset)
+               return false
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_RELATIVE):
+               ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset)
+               return false
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTOFF):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_390_GOTOFF relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_GOTOFF
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPC):
+               r.Type = objabi.R_PCREL
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += int64(r.Siz)
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16DBL),
+               objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32DBL):
+               r.Type = objabi.R_PCREL
+               r.Variant = sym.RV_390_DBL
+               r.Add += int64(r.Siz)
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_390_PCnnDBL relocation for dynamic symbol %s", targ.Name)
+               }
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPCDBL):
+               r.Type = objabi.R_PCREL
+               r.Variant = sym.RV_390_DBL
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += int64(r.Siz)
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT):
+               addgotsym(ctxt, targ)
+
+               r.Type = objabi.R_PCREL
+               r.Variant = sym.RV_390_DBL
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += int64(targ.Got())
+               r.Add += int64(r.Siz)
+               return true
+       }
+       // Handle references to ELF symbols from our own object files.
+       if targ.Type != sym.SDYNIMPORT {
+               return true
+       }
+
+       return false
+}
+
+func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
+       ctxt.Out.Write64(uint64(sectoff))
+
+       elfsym := r.Xsym.ElfsymForReloc()
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_TLS_LE:
+               switch r.Siz {
+               default:
+                       return false
+               case 4:
+                       // WARNING - silently ignored by linker in ELF64
+                       ctxt.Out.Write64(uint64(elf.R_390_TLS_LE32) | uint64(elfsym)<<32)
+               case 8:
+                       // WARNING - silently ignored by linker in ELF32
+                       ctxt.Out.Write64(uint64(elf.R_390_TLS_LE64) | uint64(elfsym)<<32)
+               }
+       case objabi.R_TLS_IE:
+               switch r.Siz {
+               default:
+                       return false
+               case 4:
+                       ctxt.Out.Write64(uint64(elf.R_390_TLS_IEENT) | uint64(elfsym)<<32)
+               }
+       case objabi.R_ADDR:
+               switch r.Siz {
+               default:
+                       return false
+               case 4:
+                       ctxt.Out.Write64(uint64(elf.R_390_32) | uint64(elfsym)<<32)
+               case 8:
+                       ctxt.Out.Write64(uint64(elf.R_390_64) | uint64(elfsym)<<32)
+               }
+       case objabi.R_GOTPCREL:
+               if r.Siz == 4 {
+                       ctxt.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
+               isdbl := r.Variant&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 r.Xsym.Type == sym.SDYNIMPORT && (r.Xsym.ElfType() == elf.STT_FUNC || r.Type == objabi.R_CALL) {
+                       if isdbl {
+                               switch r.Siz {
+                               case 2:
+                                       elfrel = elf.R_390_PLT16DBL
+                               case 4:
+                                       elfrel = elf.R_390_PLT32DBL
+                               }
+                       } else {
+                               switch r.Siz {
+                               case 4:
+                                       elfrel = elf.R_390_PLT32
+                               case 8:
+                                       elfrel = elf.R_390_PLT64
+                               }
+                       }
+               } else {
+                       if isdbl {
+                               switch r.Siz {
+                               case 2:
+                                       elfrel = elf.R_390_PC16DBL
+                               case 4:
+                                       elfrel = elf.R_390_PC32DBL
+                               }
+                       } else {
+                               switch r.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
+               }
+               ctxt.Out.Write64(uint64(elfrel) | uint64(elfsym)<<32)
+       }
+
+       ctxt.Out.Write64(uint64(r.Xadd))
+       return true
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       plt := ctxt.Syms.Lookup(".plt", 0)
+       got := ctxt.Syms.Lookup(".got", 0)
+       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.AddPCRelPlus(ctxt.Arch, got, 6)
+               // 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, ctxt.Syms.Lookup(".dynamic", 0), 0)
+
+               got.AddUint64(ctxt.Arch, 0)
+               got.AddUint64(ctxt.Arch, 0)
+       }
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       return false
+}
+
+func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       if ctxt.LinkMode == ld.LinkExternal {
+               return val, false
+       }
+
+       switch r.Type {
+       case objabi.R_CONST:
+               return r.Add, true
+       case objabi.R_GOTOFF:
+               return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+       }
+
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       switch r.Variant & sym.RV_TYPE_MASK {
+       default:
+               ld.Errorf(s, "unexpected relocation variant %d", r.Variant)
+               return t
+
+       case sym.RV_NONE:
+               return t
+
+       case sym.RV_390_DBL:
+               if (t & 1) != 0 {
+                       ld.Errorf(s, "%s+%v is not 2-byte aligned", r.Sym.Name, r.Sym.Value)
+               }
+               return t >> 1
+       }
+}
+
+func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Plt() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+
+       if ctxt.IsELF {
+               plt := ctxt.Syms.Lookup(".plt", 0)
+               got := ctxt.Syms.Lookup(".got", 0)
+               rela := ctxt.Syms.Lookup(".rela.plt", 0)
+               if plt.Size == 0 {
+                       elfsetupplt(ctxt)
+               }
+               // larl    %r1,_GLOBAL_OFFSET_TABLE_+index
+
+               plt.AddUint8(0xc0)
+               plt.AddUint8(0x10)
+               plt.AddPCRelPlus(ctxt.Arch, got, got.Size+6) // need variant?
+
+               // add to got: pointer to current pos in plt
+               got.AddAddrPlus(ctxt.Arch, plt, 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(ctxt.Arch, uint32(-((plt.Size - 2) >> 1))) // roll-your-own relocation
+               //.plt index
+               plt.AddUint32(ctxt.Arch, uint32(rela.Size)) // rela size before current entry
+
+               // rela
+               rela.AddAddrPlus(ctxt.Arch, got, got.Size-8)
+
+               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_JMP_SLOT)))
+               rela.AddUint64(ctxt.Arch, 0)
+
+               s.SetPlt(int32(plt.Size - 32))
+
+       } else {
+               ld.Errorf(s, "addpltsym: unsupported binary format")
+       }
+}
+
+func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Got() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+       got := ctxt.Syms.Lookup(".got", 0)
+       s.SetGot(int32(got.Size))
+       got.AddUint64(ctxt.Arch, 0)
+
+       if ctxt.IsELF {
+               rela := ctxt.Syms.Lookup(".rela", 0)
+               rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got()))
+               rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_GLOB_DAT)))
+               rela.AddUint64(ctxt.Arch, 0)
+       } else {
+               ld.Errorf(s, "addgotsym: unsupported binary format")
+       }
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       sect := ld.Segtext.Sections[0]
+       ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+       ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       for _, sect = range ld.Segtext.Sections[1:] {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+       if ld.Segrelrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       /* output symbol table */
+       ld.Symsize = 0
+
+       ld.Lcsize = 0
+       symo := uint32(0)
+       if !*ld.FlagS {
+               if !ctxt.IsELF {
+                       ld.Errorf(nil, "unsupported executable format")
+               }
+               symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+               symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+
+               ctxt.Out.SeekSet(int64(symo))
+               ld.Asmelfsym(ctxt)
+               ctxt.Out.Flush()
+               ctxt.Out.Write(ld.Elfstrdat)
+
+               if ctxt.LinkMode == ld.LinkExternal {
+                       ld.Elfemitreloc(ctxt)
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       default:
+               ld.Errorf(nil, "unsupported operating system")
+       case objabi.Hlinux:
+               ld.Asmbelf(ctxt, int64(symo))
+       }
+
+       ctxt.Out.Flush()
+       if *ld.FlagC {
+               fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
+               fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
+               fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
+               fmt.Printf("symsize=%d\n", ld.Symsize)
+               fmt.Printf("lcsize=%d\n", ld.Lcsize)
+               fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize))
+       }
+}
diff --git a/src/cmd/oldlink/internal/s390x/l.go b/src/cmd/oldlink/internal/s390x/l.go
new file mode 100644 (file)
index 0000000..87d10ee
--- /dev/null
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/s390x/obj.go b/src/cmd/oldlink/internal/s390x/obj.go
new file mode 100644 (file)
index 0000000..b4af86b
--- /dev/null
@@ -0,0 +1,88 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/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,
+               Asmb:             asmb,  // in asm.go
+               Asmb2:            asmb2, // in asm.go
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Machoreloc1:      machoreloc1,
+
+               Linuxdynld: "/lib64/ld64.so.1",
+
+               // not relevant for s390x
+               Freebsddynld:   "XXX",
+               Openbsddynld:   "XXX",
+               Netbsddynld:    "XXX",
+               Dragonflydynld: "XXX",
+               Solarisdynld:   "XXX",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+       case objabi.Hlinux: // s390x ELF
+               ld.Elfinit(ctxt)
+               ld.HEADR = ld.ELFRESERVE
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 0x10000
+               }
+       }
+}
diff --git a/src/cmd/oldlink/internal/sym/attribute.go b/src/cmd/oldlink/internal/sym/attribute.go
new file mode 100644 (file)
index 0000000..4b69bf3
--- /dev/null
@@ -0,0 +1,117 @@
+// 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
+
+// Attribute is a set of common symbol attributes.
+type Attribute int32
+
+const (
+       // AttrDuplicateOK marks a symbol that can be present in multiple object
+       // files.
+       AttrDuplicateOK Attribute = 1 << iota
+       // AttrExternal marks function symbols loaded from host object files.
+       AttrExternal
+       // AttrNoSplit marks functions that cannot split the stack; the linker
+       // cares because it checks that there are no call chains of nosplit
+       // functions that require more than StackLimit bytes (see
+       // lib.go:dostkcheck)
+       AttrNoSplit
+       // AttrReachable marks symbols that are transitively referenced from the
+       // entry points. Unreachable symbols are not written to the output.
+       AttrReachable
+       // AttrCgoExportDynamic and AttrCgoExportStatic mark symbols referenced
+       // by directives written by cgo (in response to //export directives in
+       // the source).
+       AttrCgoExportDynamic
+       AttrCgoExportStatic
+       // AttrSpecial marks symbols that do not have their address (i.e. Value)
+       // computed by the usual mechanism of data.go:dodata() &
+       // data.go:address().
+       AttrSpecial
+       // AttrStackCheck is used by dostkcheck to only check each NoSplit
+       // function's stack usage once.
+       AttrStackCheck
+       // AttrNotInSymbolTable marks symbols that are not written to the symbol table.
+       AttrNotInSymbolTable
+       // AttrOnList marks 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.
+       AttrOnList
+       // AttrLocal marks symbols that are only visible within the module
+       // (executable or shared library) being linked. Only relevant when
+       // dynamically linking Go code.
+       AttrLocal
+       // AttrReflectMethod marks certain methods from the reflect package that
+       // can be used to call arbitrary methods. If no symbol with this bit set
+       // is marked as reachable, more dead code elimination can be done.
+       AttrReflectMethod
+       // AttrMakeTypelink Amarks types that should be added to the typelink
+       // table. See typelinks.go:typelinks().
+       AttrMakeTypelink
+       // AttrShared marks symbols compiled with the -shared option.
+       AttrShared
+       // AttrVisibilityHidden symbols are 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.
+       AttrVisibilityHidden
+       // AttrSubSymbol mostly means that the symbol appears on the Sub list of some
+       // other symbol.  Unfortunately, it's not 100% reliable; at least, it's not set
+       // correctly for the .TOC. symbol in Link.dodata.  Usually the Outer field of the
+       // symbol points to the symbol whose list it is on, but that it is not set for the
+       // symbols added to .windynamic in initdynimport in pe.go.
+       //
+       // TODO(mwhudson): fix the inconsistencies noticed above.
+       //
+       // Sub lists are 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.
+       //
+       // TODO(mwhudson): perhaps a better name for this is AttrNonGoSymbol.
+       AttrSubSymbol
+       // AttrContainer is set on text symbols that are present as the .Outer for some
+       // other symbol.
+       AttrContainer
+       // AttrTopFrame means that the function is an entry point and unwinders
+       // should stop when they hit this function.
+       AttrTopFrame
+       // AttrReadOnly indicates whether the symbol's content (Symbol.P) is backed by
+       // read-only memory.
+       AttrReadOnly
+       // 19 attributes defined so far.
+)
+
+func (a Attribute) DuplicateOK() bool      { return a&AttrDuplicateOK != 0 }
+func (a Attribute) External() bool         { return a&AttrExternal != 0 }
+func (a Attribute) NoSplit() bool          { return a&AttrNoSplit != 0 }
+func (a Attribute) Reachable() bool        { return a&AttrReachable != 0 }
+func (a Attribute) CgoExportDynamic() bool { return a&AttrCgoExportDynamic != 0 }
+func (a Attribute) CgoExportStatic() bool  { return a&AttrCgoExportStatic != 0 }
+func (a Attribute) Special() bool          { return a&AttrSpecial != 0 }
+func (a Attribute) StackCheck() bool       { return a&AttrStackCheck != 0 }
+func (a Attribute) NotInSymbolTable() bool { return a&AttrNotInSymbolTable != 0 }
+func (a Attribute) OnList() bool           { return a&AttrOnList != 0 }
+func (a Attribute) Local() bool            { return a&AttrLocal != 0 }
+func (a Attribute) ReflectMethod() bool    { return a&AttrReflectMethod != 0 }
+func (a Attribute) MakeTypelink() bool     { return a&AttrMakeTypelink != 0 }
+func (a Attribute) Shared() bool           { return a&AttrShared != 0 }
+func (a Attribute) VisibilityHidden() bool { return a&AttrVisibilityHidden != 0 }
+func (a Attribute) SubSymbol() bool        { return a&AttrSubSymbol != 0 }
+func (a Attribute) Container() bool        { return a&AttrContainer != 0 }
+func (a Attribute) TopFrame() bool         { return a&AttrTopFrame != 0 }
+func (a Attribute) ReadOnly() bool         { return a&AttrReadOnly != 0 }
+
+func (a Attribute) CgoExport() bool {
+       return a.CgoExportDynamic() || a.CgoExportStatic()
+}
+
+func (a *Attribute) Set(flag Attribute, value bool) {
+       if value {
+               *a |= flag
+       } else {
+               *a &^= flag
+       }
+}
diff --git a/src/cmd/oldlink/internal/sym/compilation_unit.go b/src/cmd/oldlink/internal/sym/compilation_unit.go
new file mode 100644 (file)
index 0000000..02fb0cf
--- /dev/null
@@ -0,0 +1,23 @@
+// 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"
+
+// CompilationUnit is an abstraction used by DWARF to represent a chunk of
+// debug-related data. We create a CompilationUnit per Object file in a
+// library (so, one for all the Go code, one for each assembly file, etc.).
+type CompilationUnit struct {
+       Pkg            string        // The package name, eg ("fmt", or "runtime")
+       Lib            *Library      // Our library
+       Consts         *Symbol       // Package constants DIEs
+       PCs            []dwarf.Range // PC ranges, relative to Textp[0]
+       DWInfo         *dwarf.DWDie  // CU root DIE
+       FuncDIEs       []*Symbol     // Function DIE subtrees
+       AbsFnDIEs      []*Symbol     // Abstract function DIE subtrees
+       RangeSyms      []*Symbol     // Symbols for debug_range
+       Textp          []*Symbol     // Text symbols in this CU
+       DWARFFileTable []string      // The file table used to generate the .debug_lines
+}
diff --git a/src/cmd/oldlink/internal/sym/library.go b/src/cmd/oldlink/internal/sym/library.go
new file mode 100644 (file)
index 0000000..4f2023b
--- /dev/null
@@ -0,0 +1,25 @@
+// 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
+
+type Library struct {
+       Objref        string
+       Srcref        string
+       File          string
+       Pkg           string
+       Shlib         string
+       Hash          string
+       ImportStrings []string
+       Imports       []*Library
+       Textp         []*Symbol // text symbols defined in this library
+       DupTextSyms   []*Symbol // dupok text symbols defined in this library
+       Main          bool
+       Safe          bool
+       Units         []*CompilationUnit
+}
+
+func (l Library) String() string {
+       return l.Pkg
+}
diff --git a/src/cmd/oldlink/internal/sym/reloc.go b/src/cmd/oldlink/internal/sym/reloc.go
new file mode 100644 (file)
index 0000000..4809db8
--- /dev/null
@@ -0,0 +1,128 @@
+// 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"
+)
+
+// Reloc is a relocation.
+//
+// The typical Reloc rewrites part of a symbol at offset Off to address Sym.
+// A Reloc is stored in a slice on the Symbol it rewrites.
+//
+// Relocations are generated by the compiler as the type
+// cmd/internal/obj.Reloc, which is encoded into the object file wire
+// format and decoded by the linker into this type. A separate type is
+// used to hold linker-specific state about the relocation.
+//
+// Some relocations are created by cmd/link.
+type Reloc struct {
+       Off       int32            // offset to rewrite
+       Siz       uint8            // number of bytes to rewrite, 1, 2, or 4
+       Done      bool             // set to true when relocation is complete
+       Type      objabi.RelocType // the relocation type
+       Add       int64            // addend
+       Sym       *Symbol          // symbol the relocation addresses
+       *relocExt                  // extra fields (see below), may be nil, call InitExt before use
+}
+
+// relocExt contains extra fields in Reloc that are used only in
+// certain cases.
+type relocExt struct {
+       Xadd    int64        // addend passed to external linker
+       Xsym    *Symbol      // symbol passed to external linker
+       Variant RelocVariant // variation on Type, currently used only on PPC64 and S390X
+}
+
+func (r *Reloc) InitExt() {
+       if r.relocExt == nil {
+               r.relocExt = new(relocExt)
+       }
+}
+
+// RelocVariant is a linker-internal variation on a relocation.
+type RelocVariant uint8
+
+const (
+       RV_NONE RelocVariant = iota
+       RV_POWER_LO
+       RV_POWER_HI
+       RV_POWER_HA
+       RV_POWER_DS
+
+       // RV_390_DBL is a s390x-specific relocation variant that indicates that
+       // the value to be placed into the relocatable field should first be
+       // divided by 2.
+       RV_390_DBL
+
+       RV_CHECK_OVERFLOW RelocVariant = 1 << 7
+       RV_TYPE_MASK      RelocVariant = RV_CHECK_OVERFLOW - 1
+)
+
+func RelocName(arch *sys.Arch, r objabi.RelocType) string {
+       // We didn't have some relocation types at Go1.4.
+       // Uncomment code when we include those in bootstrap code.
+
+       switch {
+       case r >= objabi.MachoRelocOffset: // Mach-O
+               // nr := (r - objabi.MachoRelocOffset)>>1
+               // switch ctxt.Arch.Family {
+               // case sys.AMD64:
+               //      return macho.RelocTypeX86_64(nr).String()
+               // case sys.ARM:
+               //      return macho.RelocTypeARM(nr).String()
+               // case sys.ARM64:
+               //      return macho.RelocTypeARM64(nr).String()
+               // case sys.I386:
+               //      return macho.RelocTypeGeneric(nr).String()
+               // default:
+               //      panic("unreachable")
+               // }
+       case r >= objabi.ElfRelocOffset: // ELF
+               nr := r - objabi.ElfRelocOffset
+               switch arch.Family {
+               case sys.AMD64:
+                       return elf.R_X86_64(nr).String()
+               case sys.ARM:
+                       return elf.R_ARM(nr).String()
+               case sys.ARM64:
+                       return elf.R_AARCH64(nr).String()
+               case sys.I386:
+                       return elf.R_386(nr).String()
+               case sys.MIPS, sys.MIPS64:
+                       return elf.R_MIPS(nr).String()
+               case sys.PPC64:
+                       return elf.R_PPC64(nr).String()
+               case sys.S390X:
+                       return elf.R_390(nr).String()
+               default:
+                       panic("unreachable")
+               }
+       }
+
+       return r.String()
+}
+
+// RelocByOff implements sort.Interface for sorting relocations by offset.
+type RelocByOff []Reloc
+
+func (x RelocByOff) Len() int { return len(x) }
+
+func (x RelocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+func (x RelocByOff) Less(i, j int) bool {
+       a := &x[i]
+       b := &x[j]
+       if a.Off < b.Off {
+               return true
+       }
+       if a.Off > b.Off {
+               return false
+       }
+       return false
+}
diff --git a/src/cmd/oldlink/internal/sym/segment.go b/src/cmd/oldlink/internal/sym/segment.go
new file mode 100644 (file)
index 0000000..d5255bf
--- /dev/null
@@ -0,0 +1,58 @@
+// Inferno utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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
+}
diff --git a/src/cmd/oldlink/internal/sym/sizeof_test.go b/src/cmd/oldlink/internal/sym/sizeof_test.go
new file mode 100644 (file)
index 0000000..e6e3916
--- /dev/null
@@ -0,0 +1,37 @@
+// 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 sym
+
+import (
+       "reflect"
+       "testing"
+       "unsafe"
+)
+
+// Assert that the size of important structures do not change unexpectedly.
+
+func TestSizeof(t *testing.T) {
+       const nbit = unsafe.Sizeof(uintptr(0)) * 8
+       const _64bit = nbit == 64
+
+       var tests = []struct {
+               val    interface{} // type as a value
+               _32bit uintptr     // size on 32bit platforms
+               _64bit uintptr     // size on 64bit platforms
+       }{
+               {Symbol{}, 108, 176},
+       }
+
+       for _, tt := range tests {
+               want := tt._32bit
+               if _64bit {
+                       want = tt._64bit
+               }
+               got := reflect.TypeOf(tt.val).Size()
+               if want != got {
+                       t.Errorf("%d bit unsafe.Sizeof(%T) = %d, want %d", nbit, tt.val, got, want)
+               }
+       }
+}
diff --git a/src/cmd/oldlink/internal/sym/symbol.go b/src/cmd/oldlink/internal/sym/symbol.go
new file mode 100644 (file)
index 0000000..2756acd
--- /dev/null
@@ -0,0 +1,543 @@
+// 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"
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "debug/elf"
+       "fmt"
+       "log"
+)
+
+// Symbol is an entry in the symbol table.
+type Symbol struct {
+       Name        string
+       Type        SymKind
+       Version     int16
+       Attr        Attribute
+       Dynid       int32
+       Align       int32
+       Elfsym      int32
+       LocalElfsym int32
+       Value       int64
+       Size        int64
+       Sub         *Symbol
+       Outer       *Symbol
+       Gotype      *Symbol
+       File        string // actually package!
+       auxinfo     *AuxSymbol
+       Sect        *Section
+       FuncInfo    *FuncInfo
+       Unit        *CompilationUnit
+       // P contains the raw symbol data.
+       P []byte
+       R []Reloc
+}
+
+// AuxSymbol contains less-frequently used sym.Symbol fields.
+type AuxSymbol struct {
+       extname    string
+       dynimplib  string
+       dynimpvers string
+       localentry uint8
+       plt        int32
+       got        int32
+       // ElfType is set 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).
+       elftype elf.SymType
+}
+
+const (
+       SymVerABI0        = 0
+       SymVerABIInternal = 1
+       SymVerStatic      = 10 // Minimum version used by static (file-local) syms
+)
+
+func ABIToVersion(abi obj.ABI) int {
+       switch abi {
+       case obj.ABI0:
+               return SymVerABI0
+       case obj.ABIInternal:
+               return SymVerABIInternal
+       }
+       return -1
+}
+
+func VersionToABI(v int) (obj.ABI, bool) {
+       switch v {
+       case SymVerABI0:
+               return obj.ABI0, true
+       case SymVerABIInternal:
+               return obj.ABIInternal, true
+       }
+       return ^obj.ABI(0), false
+}
+
+func (s *Symbol) String() string {
+       if s.Version == 0 {
+               return s.Name
+       }
+       return fmt.Sprintf("%s<%d>", s.Name, s.Version)
+}
+
+func (s *Symbol) IsFileLocal() bool {
+       return s.Version >= SymVerStatic
+}
+
+func (s *Symbol) ElfsymForReloc() int32 {
+       // If putelfsym created a local version of this symbol, use that in all
+       // relocations.
+       if s.LocalElfsym != 0 {
+               return s.LocalElfsym
+       } else {
+               return s.Elfsym
+       }
+}
+
+func (s *Symbol) Length(_ interface{}) int64 {
+       return s.Size
+}
+
+func (s *Symbol) Grow(siz int64) {
+       if int64(int(siz)) != siz {
+               log.Fatalf("symgrow size %d too long", siz)
+       }
+       if int64(len(s.P)) >= siz {
+               return
+       }
+       if cap(s.P) < int(siz) {
+               p := make([]byte, 2*(siz+1))
+               s.P = append(p[:0], s.P...)
+       }
+       s.P = s.P[:siz]
+}
+
+func (s *Symbol) AddBytes(bytes []byte) int64 {
+       if s.Type == 0 {
+               s.Type = SDATA
+       }
+       s.Attr |= AttrReachable
+       s.P = append(s.P, bytes...)
+       s.Size = int64(len(s.P))
+
+       return s.Size
+}
+
+func (s *Symbol) AddUint8(v uint8) int64 {
+       off := s.Size
+       if s.Type == 0 {
+               s.Type = SDATA
+       }
+       s.Attr |= AttrReachable
+       s.Size++
+       s.P = append(s.P, v)
+
+       return off
+}
+
+func (s *Symbol) AddUint16(arch *sys.Arch, v uint16) int64 {
+       return s.AddUintXX(arch, uint64(v), 2)
+}
+
+func (s *Symbol) AddUint32(arch *sys.Arch, v uint32) int64 {
+       return s.AddUintXX(arch, uint64(v), 4)
+}
+
+func (s *Symbol) AddUint64(arch *sys.Arch, v uint64) int64 {
+       return s.AddUintXX(arch, v, 8)
+}
+
+func (s *Symbol) AddUint(arch *sys.Arch, v uint64) int64 {
+       return s.AddUintXX(arch, v, arch.PtrSize)
+}
+
+func (s *Symbol) SetUint8(arch *sys.Arch, r int64, v uint8) int64 {
+       return s.setUintXX(arch, r, uint64(v), 1)
+}
+
+func (s *Symbol) SetUint16(arch *sys.Arch, r int64, v uint16) int64 {
+       return s.setUintXX(arch, r, uint64(v), 2)
+}
+
+func (s *Symbol) SetUint32(arch *sys.Arch, r int64, v uint32) int64 {
+       return s.setUintXX(arch, r, uint64(v), 4)
+}
+
+func (s *Symbol) SetUint(arch *sys.Arch, r int64, v uint64) int64 {
+       return s.setUintXX(arch, r, v, int64(arch.PtrSize))
+}
+
+func (s *Symbol) addAddrPlus(arch *sys.Arch, t *Symbol, add int64, typ objabi.RelocType) int64 {
+       if s.Type == 0 {
+               s.Type = SDATA
+       }
+       s.Attr |= AttrReachable
+       i := s.Size
+       s.Size += int64(arch.PtrSize)
+       s.Grow(s.Size)
+       r := s.AddRel()
+       r.Sym = t
+       r.Off = int32(i)
+       r.Siz = uint8(arch.PtrSize)
+       r.Type = typ
+       r.Add = add
+       return i + int64(r.Siz)
+}
+
+func (s *Symbol) AddAddrPlus(arch *sys.Arch, t *Symbol, add int64) int64 {
+       return s.addAddrPlus(arch, t, add, objabi.R_ADDR)
+}
+
+func (s *Symbol) AddCURelativeAddrPlus(arch *sys.Arch, t *Symbol, add int64) int64 {
+       return s.addAddrPlus(arch, t, add, objabi.R_ADDRCUOFF)
+}
+
+func (s *Symbol) AddPCRelPlus(arch *sys.Arch, t *Symbol, add int64) int64 {
+       if s.Type == 0 {
+               s.Type = SDATA
+       }
+       s.Attr |= AttrReachable
+       i := s.Size
+       s.Size += 4
+       s.Grow(s.Size)
+       r := s.AddRel()
+       r.Sym = t
+       r.Off = int32(i)
+       r.Add = add
+       r.Type = objabi.R_PCREL
+       r.Siz = 4
+       if arch.Family == sys.S390X || arch.Family == sys.PPC64 {
+               r.InitExt()
+       }
+       if arch.Family == sys.S390X {
+               r.Variant = RV_390_DBL
+       }
+       return i + int64(r.Siz)
+}
+
+func (s *Symbol) AddAddr(arch *sys.Arch, t *Symbol) int64 {
+       return s.AddAddrPlus(arch, t, 0)
+}
+
+func (s *Symbol) SetAddrPlus(arch *sys.Arch, off int64, t *Symbol, add int64) int64 {
+       if s.Type == 0 {
+               s.Type = SDATA
+       }
+       s.Attr |= AttrReachable
+       if off+int64(arch.PtrSize) > s.Size {
+               s.Size = off + int64(arch.PtrSize)
+               s.Grow(s.Size)
+       }
+
+       r := s.AddRel()
+       r.Sym = t
+       r.Off = int32(off)
+       r.Siz = uint8(arch.PtrSize)
+       r.Type = objabi.R_ADDR
+       r.Add = add
+       return off + int64(r.Siz)
+}
+
+func (s *Symbol) SetAddr(arch *sys.Arch, off int64, t *Symbol) int64 {
+       return s.SetAddrPlus(arch, off, t, 0)
+}
+
+func (s *Symbol) AddSize(arch *sys.Arch, t *Symbol) int64 {
+       if s.Type == 0 {
+               s.Type = SDATA
+       }
+       s.Attr |= AttrReachable
+       i := s.Size
+       s.Size += int64(arch.PtrSize)
+       s.Grow(s.Size)
+       r := s.AddRel()
+       r.Sym = t
+       r.Off = int32(i)
+       r.Siz = uint8(arch.PtrSize)
+       r.Type = objabi.R_SIZE
+       return i + int64(r.Siz)
+}
+
+func (s *Symbol) AddAddrPlus4(t *Symbol, add int64) int64 {
+       if s.Type == 0 {
+               s.Type = SDATA
+       }
+       s.Attr |= AttrReachable
+       i := s.Size
+       s.Size += 4
+       s.Grow(s.Size)
+       r := s.AddRel()
+       r.Sym = t
+       r.Off = int32(i)
+       r.Siz = 4
+       r.Type = objabi.R_ADDR
+       r.Add = add
+       return i + int64(r.Siz)
+}
+
+func (s *Symbol) AddRel() *Reloc {
+       s.R = append(s.R, Reloc{})
+       return &s.R[len(s.R)-1]
+}
+
+func (s *Symbol) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 {
+       off := s.Size
+       s.setUintXX(arch, off, v, int64(wid))
+       return off
+}
+
+func (s *Symbol) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 {
+       if s.Type == 0 {
+               s.Type = SDATA
+       }
+       s.Attr |= AttrReachable
+       if s.Size < off+wid {
+               s.Size = off + wid
+               s.Grow(s.Size)
+       }
+
+       switch wid {
+       case 1:
+               s.P[off] = uint8(v)
+       case 2:
+               arch.ByteOrder.PutUint16(s.P[off:], uint16(v))
+       case 4:
+               arch.ByteOrder.PutUint32(s.P[off:], uint32(v))
+       case 8:
+               arch.ByteOrder.PutUint64(s.P[off:], v)
+       }
+
+       return off + wid
+}
+
+func (s *Symbol) makeAuxInfo() {
+       if s.auxinfo == nil {
+               s.auxinfo = &AuxSymbol{extname: s.Name, plt: -1, got: -1}
+       }
+}
+
+func (s *Symbol) Extname() string {
+       if s.auxinfo == nil {
+               return s.Name
+       }
+       return s.auxinfo.extname
+}
+
+func (s *Symbol) SetExtname(n string) {
+       if s.auxinfo == nil {
+               if s.Name == n {
+                       return
+               }
+               s.makeAuxInfo()
+       }
+       s.auxinfo.extname = n
+}
+
+func (s *Symbol) Dynimplib() string {
+       if s.auxinfo == nil {
+               return ""
+       }
+       return s.auxinfo.dynimplib
+}
+
+func (s *Symbol) Dynimpvers() string {
+       if s.auxinfo == nil {
+               return ""
+       }
+       return s.auxinfo.dynimpvers
+}
+
+func (s *Symbol) SetDynimplib(lib string) {
+       if s.auxinfo == nil {
+               s.makeAuxInfo()
+       }
+       s.auxinfo.dynimplib = lib
+}
+
+func (s *Symbol) SetDynimpvers(vers string) {
+       if s.auxinfo == nil {
+               s.makeAuxInfo()
+       }
+       s.auxinfo.dynimpvers = vers
+}
+
+func (s *Symbol) ResetDyninfo() {
+       if s.auxinfo != nil {
+               s.auxinfo.dynimplib = ""
+               s.auxinfo.dynimpvers = ""
+       }
+}
+
+func (s *Symbol) Localentry() uint8 {
+       if s.auxinfo == nil {
+               return 0
+       }
+       return s.auxinfo.localentry
+}
+
+func (s *Symbol) SetLocalentry(val uint8) {
+       if s.auxinfo == nil {
+               if val != 0 {
+                       return
+               }
+               s.makeAuxInfo()
+       }
+       s.auxinfo.localentry = val
+}
+
+func (s *Symbol) Plt() int32 {
+       if s.auxinfo == nil {
+               return -1
+       }
+       return s.auxinfo.plt
+}
+
+func (s *Symbol) SetPlt(val int32) {
+       if s.auxinfo == nil {
+               if val == -1 {
+                       return
+               }
+               s.makeAuxInfo()
+       }
+       s.auxinfo.plt = val
+}
+
+func (s *Symbol) Got() int32 {
+       if s.auxinfo == nil {
+               return -1
+       }
+       return s.auxinfo.got
+}
+
+func (s *Symbol) SetGot(val int32) {
+       if s.auxinfo == nil {
+               if val == -1 {
+                       return
+               }
+               s.makeAuxInfo()
+       }
+       s.auxinfo.got = val
+}
+
+func (s *Symbol) ElfType() elf.SymType {
+       if s.auxinfo == nil {
+               return elf.STT_NOTYPE
+       }
+       return s.auxinfo.elftype
+}
+
+func (s *Symbol) SetElfType(val elf.SymType) {
+       if s.auxinfo == nil {
+               if val == elf.STT_NOTYPE {
+                       return
+               }
+               s.makeAuxInfo()
+       }
+       s.auxinfo.elftype = val
+}
+
+// SortSub sorts a linked-list (by Sub) of *Symbol by Value.
+// Used for sub-symbols when loading host objects (see e.g. ldelf.go).
+func SortSub(l *Symbol) *Symbol {
+       if l == nil || l.Sub == nil {
+               return l
+       }
+
+       l1 := l
+       l2 := l
+       for {
+               l2 = l2.Sub
+               if l2 == nil {
+                       break
+               }
+               l2 = l2.Sub
+               if l2 == nil {
+                       break
+               }
+               l1 = l1.Sub
+       }
+
+       l2 = l1.Sub
+       l1.Sub = nil
+       l1 = SortSub(l)
+       l2 = SortSub(l2)
+
+       /* set up lead element */
+       if l1.Value < l2.Value {
+               l = l1
+               l1 = l1.Sub
+       } else {
+               l = l2
+               l2 = l2.Sub
+       }
+
+       le := l
+
+       for {
+               if l1 == nil {
+                       for l2 != nil {
+                               le.Sub = l2
+                               le = l2
+                               l2 = l2.Sub
+                       }
+
+                       le.Sub = nil
+                       break
+               }
+
+               if l2 == nil {
+                       for l1 != nil {
+                               le.Sub = l1
+                               le = l1
+                               l1 = l1.Sub
+                       }
+
+                       break
+               }
+
+               if l1.Value < l2.Value {
+                       le.Sub = l1
+                       le = l1
+                       l1 = l1.Sub
+               } else {
+                       le.Sub = l2
+                       le = l2
+                       l2 = l2.Sub
+               }
+       }
+
+       le.Sub = nil
+       return l
+}
+
+type FuncInfo struct {
+       Args        int32
+       Locals      int32
+       Pcsp        Pcdata
+       Pcfile      Pcdata
+       Pcline      Pcdata
+       Pcinline    Pcdata
+       Pcdata      []Pcdata
+       Funcdata    []*Symbol
+       Funcdataoff []int64
+       File        []*Symbol
+       InlTree     []InlinedCall
+}
+
+// InlinedCall is a node in a local inlining tree (FuncInfo.InlTree).
+type InlinedCall struct {
+       Parent   int32   // index of parent in InlTree
+       File     *Symbol // file of the inlined call
+       Line     int32   // line number of the inlined call
+       Func     string  // name of the function that was inlined
+       ParentPC int32   // PC of the instruction just before the inlined body (offset from function start)
+}
+
+type Pcdata struct {
+       P []byte
+}
diff --git a/src/cmd/oldlink/internal/sym/symbols.go b/src/cmd/oldlink/internal/sym/symbols.go
new file mode 100644 (file)
index 0000000..e772496
--- /dev/null
@@ -0,0 +1,135 @@
+// Derived from Inferno utils/6l/l.h and related files.
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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
+
+type Symbols struct {
+       symbolBatch []Symbol
+
+       // Symbol lookup based on name and indexed by version.
+       hash []map[string]*Symbol
+
+       Allsym []*Symbol
+}
+
+func NewSymbols() *Symbols {
+       hash := make([]map[string]*Symbol, SymVerStatic)
+       // Preallocate about 2mb for hash of non static symbols
+       hash[0] = make(map[string]*Symbol, 100000)
+       // And another 1mb for internal ABI text symbols.
+       hash[SymVerABIInternal] = make(map[string]*Symbol, 50000)
+       return &Symbols{
+               hash:   hash,
+               Allsym: make([]*Symbol, 0, 100000),
+       }
+}
+
+func (syms *Symbols) Newsym(name string, v int) *Symbol {
+       batch := syms.symbolBatch
+       if len(batch) == 0 {
+               batch = make([]Symbol, 1000)
+       }
+       s := &batch[0]
+       syms.symbolBatch = batch[1:]
+
+       s.Dynid = -1
+       s.Name = name
+       s.Version = int16(v)
+       syms.Allsym = append(syms.Allsym, s)
+
+       return s
+}
+
+// Look up the symbol with the given name and version, creating the
+// symbol if it is not found.
+func (syms *Symbols) Lookup(name string, v int) *Symbol {
+       m := syms.hash[v]
+       s := m[name]
+       if s != nil {
+               return s
+       }
+       s = syms.Newsym(name, v)
+       m[name] = s
+       return s
+}
+
+// Look up the symbol with the given name and version, returning nil
+// if it is not found.
+func (syms *Symbols) ROLookup(name string, v int) *Symbol {
+       return syms.hash[v][name]
+}
+
+// Add an existing symbol to the symbol table.
+func (syms *Symbols) Add(s *Symbol) {
+       name := s.Name
+       v := int(s.Version)
+       m := syms.hash[v]
+       if _, ok := m[name]; ok {
+               panic(name + " already added")
+       }
+       m[name] = s
+}
+
+// Allocate a new version (i.e. symbol namespace).
+func (syms *Symbols) IncVersion() int {
+       syms.hash = append(syms.hash, make(map[string]*Symbol))
+       return len(syms.hash) - 1
+}
+
+// Rename renames a symbol.
+func (syms *Symbols) Rename(old, new string, v int, reachparent map[*Symbol]*Symbol) {
+       s := syms.hash[v][old]
+       oldExtName := s.Extname()
+       s.Name = new
+       if oldExtName == old {
+               s.SetExtname(new)
+       }
+       delete(syms.hash[v], old)
+
+       dup := syms.hash[v][new]
+       if dup == nil {
+               syms.hash[v][new] = s
+       } else {
+               if s.Type == 0 {
+                       dup.Attr |= s.Attr
+                       if s.Attr.Reachable() && reachparent != nil {
+                               reachparent[dup] = reachparent[s]
+                       }
+                       *s = *dup
+               } else if dup.Type == 0 {
+                       s.Attr |= dup.Attr
+                       if dup.Attr.Reachable() && reachparent != nil {
+                               reachparent[s] = reachparent[dup]
+                       }
+                       *dup = *s
+                       syms.hash[v][new] = s
+               }
+       }
+}
diff --git a/src/cmd/oldlink/internal/sym/symkind.go b/src/cmd/oldlink/internal/sym/symkind.go
new file mode 100644 (file)
index 0000000..1933dd7
--- /dev/null
@@ -0,0 +1,168 @@
+// Derived from Inferno utils/6l/l.h and related files.
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h
+//
+//     Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//     Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+//     Portions Copyright © 1997-1999 Vita Nuova Limited
+//     Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//     Portions Copyright © 2004,2006 Bruce Ellis
+//     Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+//     Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//     Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package sym
+
+// A SymKind describes the kind of memory represented by a symbol.
+type SymKind uint8
+
+// Defined SymKind values.
+//
+// TODO(rsc): Give idiomatic Go names.
+//go:generate stringer -type=SymKind
+const (
+       Sxxx SymKind = iota
+       STEXT
+       SELFRXSECT
+
+       // Read-only sections.
+       STYPE
+       SSTRING
+       SGOSTRING
+       SGOFUNC
+       SGCBITS
+       SRODATA
+       SFUNCTAB
+
+       SELFROSECT
+       SMACHOPLT
+
+       // Read-only sections with relocations.
+       //
+       // Types STYPE-SFUNCTAB above are written to the .rodata section by default.
+       // When linking a shared object, some conceptually "read only" types need to
+       // be written to by relocations and putting them in a section called
+       // ".rodata" interacts poorly with the system linkers. The GNU linkers
+       // support this situation by arranging for sections of the name
+       // ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after
+       // relocations have applied, so when the Go linker is creating a shared
+       // object it checks all objects of the above types and bumps any object that
+       // has a relocation to it to the corresponding type below, which are then
+       // written to sections with appropriate magic names.
+       STYPERELRO
+       SSTRINGRELRO
+       SGOSTRINGRELRO
+       SGOFUNCRELRO
+       SGCBITSRELRO
+       SRODATARELRO
+       SFUNCTABRELRO
+
+       // Part of .data.rel.ro if it exists, otherwise part of .rodata.
+       STYPELINK
+       SITABLINK
+       SSYMTAB
+       SPCLNTAB
+
+       // Writable sections.
+       SFirstWritable
+       SBUILDINFO
+       SELFSECT
+       SMACHO
+       SMACHOGOT
+       SWINDOWS
+       SELFGOT
+       SNOPTRDATA
+       SINITARR
+       SDATA
+       SXCOFFTOC
+       SBSS
+       SNOPTRBSS
+       SLIBFUZZER_EXTRA_COUNTER
+       STLSBSS
+       SXREF
+       SMACHOSYMSTR
+       SMACHOSYMTAB
+       SMACHOINDIRECTPLT
+       SMACHOINDIRECTGOT
+       SFILEPATH
+       SCONST
+       SDYNIMPORT
+       SHOSTOBJ
+       SUNDEFEXT // Undefined symbol for resolution by external linker
+
+       // Sections for debugging information
+       SDWARFSECT
+       SDWARFINFO
+       SDWARFRANGE
+       SDWARFLOC
+       SDWARFLINES
+
+       // ABI aliases (these never appear in the output)
+       SABIALIAS
+)
+
+// AbiSymKindToSymKind maps values read from object files (which are
+// of type cmd/internal/objabi.SymKind) to values of type SymKind.
+var AbiSymKindToSymKind = [...]SymKind{
+       Sxxx,
+       STEXT,
+       SRODATA,
+       SNOPTRDATA,
+       SDATA,
+       SBSS,
+       SNOPTRBSS,
+       STLSBSS,
+       SDWARFINFO,
+       SDWARFRANGE,
+       SDWARFLOC,
+       SDWARFLINES,
+       SABIALIAS,
+       SLIBFUZZER_EXTRA_COUNTER,
+}
+
+// ReadOnly are the symbol kinds that form read-only sections. In some
+// cases, if they will require relocations, they are transformed into
+// rel-ro sections using relROMap.
+var ReadOnly = []SymKind{
+       STYPE,
+       SSTRING,
+       SGOSTRING,
+       SGOFUNC,
+       SGCBITS,
+       SRODATA,
+       SFUNCTAB,
+}
+
+// RelROMap describes the transformation of read-only symbols to rel-ro
+// symbols.
+var RelROMap = map[SymKind]SymKind{
+       STYPE:     STYPERELRO,
+       SSTRING:   SSTRINGRELRO,
+       SGOSTRING: SGOSTRINGRELRO,
+       SGOFUNC:   SGOFUNCRELRO,
+       SGCBITS:   SGCBITSRELRO,
+       SRODATA:   SRODATARELRO,
+       SFUNCTAB:  SFUNCTABRELRO,
+}
+
+// IsData returns true if the type is a data type.
+func (t SymKind) IsData() bool {
+       return t == SDATA || t == SNOPTRDATA || t == SBSS || t == SNOPTRBSS
+}
diff --git a/src/cmd/oldlink/internal/sym/symkind_string.go b/src/cmd/oldlink/internal/sym/symkind_string.go
new file mode 100644 (file)
index 0000000..97af992
--- /dev/null
@@ -0,0 +1,76 @@
+// 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[STYPE-3]
+       _ = x[SSTRING-4]
+       _ = x[SGOSTRING-5]
+       _ = x[SGOFUNC-6]
+       _ = x[SGCBITS-7]
+       _ = x[SRODATA-8]
+       _ = x[SFUNCTAB-9]
+       _ = x[SELFROSECT-10]
+       _ = x[SMACHOPLT-11]
+       _ = x[STYPERELRO-12]
+       _ = x[SSTRINGRELRO-13]
+       _ = x[SGOSTRINGRELRO-14]
+       _ = x[SGOFUNCRELRO-15]
+       _ = x[SGCBITSRELRO-16]
+       _ = x[SRODATARELRO-17]
+       _ = x[SFUNCTABRELRO-18]
+       _ = x[STYPELINK-19]
+       _ = x[SITABLINK-20]
+       _ = x[SSYMTAB-21]
+       _ = x[SPCLNTAB-22]
+       _ = x[SFirstWritable-23]
+       _ = x[SBUILDINFO-24]
+       _ = x[SELFSECT-25]
+       _ = x[SMACHO-26]
+       _ = x[SMACHOGOT-27]
+       _ = x[SWINDOWS-28]
+       _ = x[SELFGOT-29]
+       _ = x[SNOPTRDATA-30]
+       _ = x[SINITARR-31]
+       _ = x[SDATA-32]
+       _ = x[SXCOFFTOC-33]
+       _ = x[SBSS-34]
+       _ = x[SNOPTRBSS-35]
+       _ = x[SLIBFUZZER_EXTRA_COUNTER-36]
+       _ = x[STLSBSS-37]
+       _ = x[SXREF-38]
+       _ = x[SMACHOSYMSTR-39]
+       _ = x[SMACHOSYMTAB-40]
+       _ = x[SMACHOINDIRECTPLT-41]
+       _ = x[SMACHOINDIRECTGOT-42]
+       _ = x[SFILEPATH-43]
+       _ = x[SCONST-44]
+       _ = x[SDYNIMPORT-45]
+       _ = x[SHOSTOBJ-46]
+       _ = x[SUNDEFEXT-47]
+       _ = x[SDWARFSECT-48]
+       _ = x[SDWARFINFO-49]
+       _ = x[SDWARFRANGE-50]
+       _ = x[SDWARFLOC-51]
+       _ = x[SDWARFLINES-52]
+       _ = x[SABIALIAS-53]
+}
+
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_EXTRA_COUNTERSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSCONSTSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOCSDWARFLINESSABIALIAS"
+
+var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 337, 344, 349, 361, 373, 390, 407, 416, 422, 432, 440, 449, 459, 469, 480, 489, 500, 509}
+
+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/oldlink/internal/wasm/asm.go b/src/cmd/oldlink/internal/wasm/asm.go
new file mode 100644 (file)
index 0000000..35bc7b1
--- /dev/null
@@ -0,0 +1,583 @@
+// 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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "io"
+       "regexp"
+)
+
+const (
+       I32 = 0x7F
+       I64 = 0x7E
+       F32 = 0x7D
+       F64 = 0x7C
+)
+
+const (
+       sectionCustom   = 0
+       sectionType     = 1
+       sectionImport   = 2
+       sectionFunction = 3
+       sectionTable    = 4
+       sectionMemory   = 5
+       sectionGlobal   = 6
+       sectionExport   = 7
+       sectionStart    = 8
+       sectionElement  = 9
+       sectionCode     = 10
+       sectionData     = 11
+)
+
+// funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly
+const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses
+
+func gentext(ctxt *ld.Link) {
+}
+
+type wasmFunc struct {
+       Name string
+       Type uint32
+       Code []byte
+}
+
+type wasmFuncType struct {
+       Params  []byte
+       Results []byte
+}
+
+var wasmFuncTypes = map[string]*wasmFuncType{
+       "_rt0_wasm_js":           {Params: []byte{}},                                         //
+       "wasm_export_run":        {Params: []byte{I32, I32}},                                 // argc, argv
+       "wasm_export_resume":     {Params: []byte{}},                                         //
+       "wasm_export_getsp":      {Results: []byte{I32}},                                     // sp
+       "wasm_pc_f_loop":         {Params: []byte{}},                                         //
+       "runtime.wasmMove":       {Params: []byte{I32, I32, I32}},                            // dst, src, len
+       "runtime.wasmZero":       {Params: []byte{I32, I32}},                                 // ptr, len
+       "runtime.wasmDiv":        {Params: []byte{I64, I64}, Results: []byte{I64}},           // x, y -> x/y
+       "runtime.wasmTruncS":     {Params: []byte{F64}, Results: []byte{I64}},                // x -> int(x)
+       "runtime.wasmTruncU":     {Params: []byte{F64}, Results: []byte{I64}},                // x -> uint(x)
+       "runtime.gcWriteBarrier": {Params: []byte{I64, I64}},                                 // ptr, val
+       "cmpbody":                {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1
+       "memeqbody":              {Params: []byte{I64, I64, I64}, Results: []byte{I64}},      // a, b, len -> 0/1
+       "memcmp":                 {Params: []byte{I32, I32, I32}, Results: []byte{I32}},      // a, b, len -> <0/0/>0
+       "memchr":                 {Params: []byte{I32, I32, I32}, Results: []byte{I32}},      // s, c, len -> index
+}
+
+func assignAddress(ctxt *ld.Link, sect *sym.Section, n int, s *sym.Symbol, 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.
+       s.Sect = sect
+       s.Value = int64(funcValueOffset+va/ld.MINFUNC) << 16 // va starts at zero
+       va += uint64(ld.MINFUNC)
+       return sect, n, va
+}
+
+func asmb(ctxt *ld.Link) {} // dummy
+
+// asmb writes the final WebAssembly module binary.
+// Spec: https://webassembly.github.io/spec/core/binary/modules.html
+func asmb2(ctxt *ld.Link) {
+       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[*sym.Symbol]int64)
+       for _, fn := range ctxt.Textp {
+               for _, r := range fn.R {
+                       if r.Type == objabi.R_WASMIMPORT {
+                               hostImportMap[r.Sym] = int64(len(hostImports))
+                               hostImports = append(hostImports, &wasmFunc{
+                                       Name: r.Sym.Name,
+                                       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 fn.Name == "go.buildid" {
+                       writeUleb128(wfn, 0) // number of sets of locals
+                       writeI32Const(wfn, 0)
+                       wfn.WriteByte(0x0b) // end
+                       buildid = fn.P
+               } else {
+                       // Relocations have variable length, handle them here.
+                       off := int32(0)
+                       for _, r := range fn.R {
+                               wfn.Write(fn.P[off:r.Off])
+                               off = r.Off
+                               switch r.Type {
+                               case objabi.R_ADDR:
+                                       writeSleb128(wfn, r.Sym.Value+r.Add)
+                               case objabi.R_CALL:
+                                       writeSleb128(wfn, int64(len(hostImports))+r.Sym.Value>>16-funcValueOffset)
+                               case objabi.R_WASMIMPORT:
+                                       writeSleb128(wfn, hostImportMap[r.Sym])
+                               default:
+                                       ld.Errorf(fn, "bad reloc type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
+                                       continue
+                               }
+                       }
+                       wfn.Write(fn.P[off:])
+               }
+
+               typ := uint32(0)
+               if sig, ok := wasmFuncTypes[fn.Name]; ok {
+                       typ = lookupType(sig, &types)
+               }
+
+               name := nameRegexp.ReplaceAllString(fn.Name, "_")
+               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)
+       writeGlobalSec(ctxt)
+       writeExportSec(ctxt, 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)
+       }
+
+       ctxt.Out.Flush()
+}
+
+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) {
+       sizeOffset := writeSecHeader(ctxt, sectionMemory)
+
+       dataSection := ctxt.Syms.Lookup("runtime.data", 0).Sect
+       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, lenHostImports int) {
+       sizeOffset := writeSecHeader(ctxt, sectionExport)
+
+       writeUleb128(ctxt.Out, 4) // number of exports
+
+       for _, name := range []string{"run", "resume", "getsp"} {
+               idx := uint32(lenHostImports) + uint32(ctxt.Syms.ROLookup("wasm_export_"+name, 0).Value>>16) - funcValueOffset
+               writeName(ctxt.Out, name)           // inst.exports.run/resume/getsp in wasm_exec.js
+               ctxt.Out.WriteByte(0x00)            // func export
+               writeUleb128(ctxt.Out, uint64(idx)) // funcidx
+       }
+
+       writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
+       ctxt.Out.WriteByte(0x02)   // mem export
+       writeUleb128(ctxt.Out, 0)  // memidx
+
+       writeSecSize(ctxt, sizeOffset)
+}
+
+// writeElementSec writes the section that initializes the tables declared by the "table" section.
+// The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value)
+// maps linearly to the function index (numImports + PC_F).
+func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
+       sizeOffset := writeSecHeader(ctxt, sectionElement)
+
+       writeUleb128(ctxt.Out, 1) // number of element segments
+
+       writeUleb128(ctxt.Out, 0) // tableidx
+       writeI32Const(ctxt.Out, funcValueOffset)
+       ctxt.Out.WriteByte(0x0b) // end
+
+       writeUleb128(ctxt.Out, numFns) // number of entries
+       for i := uint64(0); i < numFns; i++ {
+               writeUleb128(ctxt.Out, numImports+i)
+       }
+
+       writeSecSize(ctxt, sizeOffset)
+}
+
+// writeElementSec writes the section that provides the function bodies for the functions
+// declared by the "func" section.
+func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
+       sizeOffset := writeSecHeader(ctxt, sectionCode)
+
+       writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries
+       for _, fn := range fns {
+               writeUleb128(ctxt.Out, uint64(len(fn.Code)))
+               ctxt.Out.Write(fn.Code)
+       }
+
+       writeSecSize(ctxt, sizeOffset)
+}
+
+// writeDataSec writes the section that provides data that will be used to initialize the linear memory.
+func writeDataSec(ctxt *ld.Link) {
+       sizeOffset := writeSecHeader(ctxt, sectionData)
+
+       sections := []*sym.Section{
+               ctxt.Syms.Lookup("runtime.rodata", 0).Sect,
+               ctxt.Syms.Lookup("runtime.typelink", 0).Sect,
+               ctxt.Syms.Lookup("runtime.itablink", 0).Sect,
+               ctxt.Syms.Lookup("runtime.symtab", 0).Sect,
+               ctxt.Syms.Lookup("runtime.pclntab", 0).Sect,
+               ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect,
+               ctxt.Syms.Lookup("runtime.data", 0).Sect,
+       }
+
+       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, sec := range sections {
+               data := ld.DatblkBytes(ctxt, int64(sec.Vaddr), int64(sec.Length))
+               offset := int32(sec.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(sections)-secIndex) == maxNumSegments {
+                               segmentEnd = dataLen
+                               zeroEnd = dataLen
+                       } else {
+                               for {
+                                       // look for beginning of zeroes
+                                       for segmentEnd < dataLen && data[segmentEnd] != 0 {
+                                               segmentEnd++
+                                       }
+                                       // look for end of zeroes
+                                       zeroEnd = segmentEnd
+                                       for zeroEnd < dataLen && data[zeroEnd] == 0 {
+                                               zeroEnd++
+                                       }
+                                       // emit segment if omitting zeroes reduces the output size
+                                       if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen {
+                                               break
+                                       }
+                                       segmentEnd = zeroEnd
+                               }
+                       }
+
+                       segments = append(segments, &dataSegment{
+                               offset: offset,
+                               data:   data[:segmentEnd],
+                       })
+                       data = data[zeroEnd:]
+                       offset += zeroEnd
+               }
+       }
+
+       writeUleb128(ctxt.Out, uint64(len(segments))) // number of data entries
+       for _, seg := range segments {
+               writeUleb128(ctxt.Out, 0) // memidx
+               writeI32Const(ctxt.Out, seg.offset)
+               ctxt.Out.WriteByte(0x0b) // end
+               writeUleb128(ctxt.Out, uint64(len(seg.data)))
+               ctxt.Out.Write(seg.data)
+       }
+
+       writeSecSize(ctxt, sizeOffset)
+}
+
+// writeProducerSec writes an optional section that reports the source language and compiler version.
+func writeProducerSec(ctxt *ld.Link) {
+       sizeOffset := writeSecHeader(ctxt, sectionCustom)
+       writeName(ctxt.Out, "producers")
+
+       writeUleb128(ctxt.Out, 2) // number of fields
+
+       writeName(ctxt.Out, "language")     // field name
+       writeUleb128(ctxt.Out, 1)           // number of values
+       writeName(ctxt.Out, "Go")           // value: name
+       writeName(ctxt.Out, objabi.Version) // value: version
+
+       writeName(ctxt.Out, "processed-by")   // field name
+       writeUleb128(ctxt.Out, 1)             // number of values
+       writeName(ctxt.Out, "Go cmd/compile") // value: name
+       writeName(ctxt.Out, objabi.Version)   // value: version
+
+       writeSecSize(ctxt, sizeOffset)
+}
+
+var nameRegexp = regexp.MustCompile(`[^\w\.]`)
+
+// writeNameSec writes an optional section that assigns names to the functions declared by the "func" section.
+// The names are only used by WebAssembly stack traces, debuggers and decompilers.
+// TODO(neelance): add symbol table of DATA symbols
+func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) {
+       sizeOffset := writeSecHeader(ctxt, sectionCustom)
+       writeName(ctxt.Out, "name")
+
+       sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names
+       writeUleb128(ctxt.Out, uint64(len(fns)))
+       for i, fn := range fns {
+               writeUleb128(ctxt.Out, uint64(firstFnIndex+i))
+               writeName(ctxt.Out, fn.Name)
+       }
+       writeSecSize(ctxt, sizeOffset2)
+
+       writeSecSize(ctxt, sizeOffset)
+}
+
+type nameWriter interface {
+       io.ByteWriter
+       io.Writer
+}
+
+func writeI32Const(w io.ByteWriter, v int32) {
+       w.WriteByte(0x41) // i32.const
+       writeSleb128(w, int64(v))
+}
+
+func writeI64Const(w io.ByteWriter, v int64) {
+       w.WriteByte(0x42) // i64.const
+       writeSleb128(w, v)
+}
+
+func writeName(w nameWriter, name string) {
+       writeUleb128(w, uint64(len(name)))
+       w.Write([]byte(name))
+}
+
+func writeUleb128(w io.ByteWriter, v uint64) {
+       if v < 128 {
+               w.WriteByte(uint8(v))
+               return
+       }
+       more := true
+       for more {
+               c := uint8(v & 0x7f)
+               v >>= 7
+               more = v != 0
+               if more {
+                       c |= 0x80
+               }
+               w.WriteByte(c)
+       }
+}
+
+func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) {
+       for i := 0; i < length; i++ {
+               c := uint8(v & 0x7f)
+               v >>= 7
+               if i < length-1 {
+                       c |= 0x80
+               }
+               w.WriteByte(c)
+       }
+       if v != 0 {
+               panic("writeUleb128FixedLength: length too small")
+       }
+}
+
+func writeSleb128(w io.ByteWriter, v int64) {
+       more := true
+       for more {
+               c := uint8(v & 0x7f)
+               s := uint8(v & 0x40)
+               v >>= 7
+               more = !((v == 0 && s == 0) || (v == -1 && s != 0))
+               if more {
+                       c |= 0x80
+               }
+               w.WriteByte(c)
+       }
+}
diff --git a/src/cmd/oldlink/internal/wasm/obj.go b/src/cmd/oldlink/internal/wasm/obj.go
new file mode 100644 (file)
index 0000000..fdc9fb7
--- /dev/null
@@ -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/oldlink/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/oldlink/internal/x86/asm.go b/src/cmd/oldlink/internal/x86/asm.go
new file mode 100644 (file)
index 0000000..3466806
--- /dev/null
@@ -0,0 +1,745 @@
+// Inferno utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+       "cmd/oldlink/internal/sym"
+       "debug/elf"
+       "log"
+)
+
+// Append 4 bytes to s and create a R_CALL relocation targeting t to fill them in.
+func addcall(ctxt *ld.Link, s *sym.Symbol, t *sym.Symbol) {
+       s.Attr |= sym.AttrReachable
+       i := s.Size
+       s.Size += 4
+       s.Grow(s.Size)
+       r := s.AddRel()
+       r.Sym = t
+       r.Off = int32(i)
+       r.Type = objabi.R_CALL
+       r.Siz = 4
+}
+
+func gentext(ctxt *ld.Link) {
+       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([]*sym.Symbol, 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 := ctxt.Syms.Lookup("__x86.get_pc_thunk."+r.name, 0)
+               thunkfunc.Type = sym.STEXT
+               thunkfunc.Attr |= sym.AttrLocal
+               thunkfunc.Attr |= sym.AttrReachable //TODO: remove?
+               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)
+       }
+       ctxt.Textp = append(thunks, ctxt.Textp...) // keep Textp in dependency order
+
+       addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+       if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin {
+               // we're linking a module containing the runtime -> no need for
+               // an init function
+               return
+       }
+
+       addmoduledata.Attr |= sym.AttrReachable
+
+       initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
+       initfunc.Type = sym.STEXT
+       initfunc.Attr |= sym.AttrLocal
+       initfunc.Attr |= sym.AttrReachable
+       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)
+       addcall(ctxt, initfunc, ctxt.Syms.Lookup("__x86.get_pc_thunk.cx", 0))
+
+       o(0x8d, 0x81)
+       initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 6)
+
+       o(0x8d, 0x99)
+       i := initfunc.Size
+       initfunc.Size += 4
+       initfunc.Grow(initfunc.Size)
+       r := initfunc.AddRel()
+       r.Sym = ctxt.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0)
+       r.Off = int32(i)
+       r.Type = objabi.R_PCREL
+       r.Add = 12
+       r.Siz = 4
+
+       o(0xe8)
+       addcall(ctxt, initfunc, addmoduledata)
+
+       o(0x5b)
+
+       o(0xc3)
+
+       if ctxt.BuildMode == ld.BuildModePlugin {
+               ctxt.Textp = append(ctxt.Textp, addmoduledata)
+       }
+       ctxt.Textp = append(ctxt.Textp, initfunc)
+       initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
+       initarray_entry.Attr |= sym.AttrReachable
+       initarray_entry.Attr |= sym.AttrLocal
+       initarray_entry.Type = sym.SINITARR
+       initarray_entry.AddAddr(ctxt.Arch, initfunc)
+}
+
+func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+       targ := r.Sym
+
+       switch r.Type {
+       default:
+               if r.Type >= objabi.ElfRelocOffset {
+                       ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
+                       return false
+               }
+
+               // Handle relocations found in ELF object files.
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PC32):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", targ.Name)
+               }
+               // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
+               // sense and should be removed when someone has thought about it properly.
+               if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() {
+                       ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
+               }
+               r.Type = objabi.R_PCREL
+               r.Add += 4
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PLT32):
+               r.Type = objabi.R_PCREL
+               r.Add += 4
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add += int64(targ.Plt())
+               }
+
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32),
+               objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32X):
+               if targ.Type != sym.SDYNIMPORT {
+                       // have symbol
+                       if r.Off >= 2 && s.P[r.Off-2] == 0x8b {
+                               // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT.
+                               s.P[r.Off-2] = 0x8d
+
+                               r.Type = objabi.R_GOTOFF
+                               return true
+                       }
+
+                       if r.Off >= 2 && s.P[r.Off-2] == 0xff && s.P[r.Off-1] == 0xb3 {
+                               // turn PUSHL of GOT entry into PUSHL of symbol itself.
+                               // use unnecessary SS prefix to keep instruction same length.
+                               s.P[r.Off-2] = 0x36
+
+                               s.P[r.Off-1] = 0x68
+                               r.Type = objabi.R_ADDR
+                               return true
+                       }
+
+                       ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
+                       return false
+               }
+
+               addgotsym(ctxt, targ)
+               r.Type = objabi.R_CONST // write r->add during relocsym
+               r.Sym = nil
+               r.Add += int64(targ.Got())
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTOFF):
+               r.Type = objabi.R_GOTOFF
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC):
+               r.Type = objabi.R_PCREL
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += 4
+               return true
+
+       case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_32):
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", targ.Name)
+               }
+               r.Type = objabi.R_ADDR
+               return true
+
+       case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0:
+               r.Type = objabi.R_ADDR
+               if targ.Type == sym.SDYNIMPORT {
+                       ld.Errorf(s, "unexpected reloc for dynamic symbol %s", targ.Name)
+               }
+               return true
+
+       case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1:
+               if targ.Type == sym.SDYNIMPORT {
+                       addpltsym(ctxt, targ)
+                       r.Sym = ctxt.Syms.Lookup(".plt", 0)
+                       r.Add = int64(targ.Plt())
+                       r.Type = objabi.R_PCREL
+                       return true
+               }
+
+               r.Type = objabi.R_PCREL
+               return true
+
+       case objabi.MachoRelocOffset + ld.MACHO_FAKE_GOTPCREL:
+               if targ.Type != sym.SDYNIMPORT {
+                       // have symbol
+                       // turn MOVL of GOT entry into LEAL of symbol itself
+                       if r.Off < 2 || s.P[r.Off-2] != 0x8b {
+                               ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
+                               return false
+                       }
+
+                       s.P[r.Off-2] = 0x8d
+                       r.Type = objabi.R_PCREL
+                       return true
+               }
+
+               addgotsym(ctxt, targ)
+               r.Sym = ctxt.Syms.Lookup(".got", 0)
+               r.Add += int64(targ.Got())
+               r.Type = objabi.R_PCREL
+               return true
+       }
+
+       // Handle references to ELF symbols from our own object files.
+       if targ.Type != sym.SDYNIMPORT {
+               return true
+       }
+       switch r.Type {
+       case objabi.R_CALL,
+               objabi.R_PCREL:
+               if ctxt.LinkMode == ld.LinkExternal {
+                       // External linker will do this relocation.
+                       return true
+               }
+               addpltsym(ctxt, targ)
+               r.Sym = ctxt.Syms.Lookup(".plt", 0)
+               r.Add = int64(targ.Plt())
+               return true
+
+       case objabi.R_ADDR:
+               if s.Type != sym.SDATA {
+                       break
+               }
+               if ctxt.IsELF {
+                       ld.Adddynsym(ctxt, targ)
+                       rel := ctxt.Syms.Lookup(".rel", 0)
+                       rel.AddAddrPlus(ctxt.Arch, s, int64(r.Off))
+                       rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_386_32)))
+                       r.Type = objabi.R_CONST // write r->add during relocsym
+                       r.Sym = nil
+                       return true
+               }
+
+               if ctxt.HeadType == objabi.Hdarwin && s.Size == int64(ctxt.Arch.PtrSize) && r.Off == 0 {
+                       // Mach-O relocations are a royal pain to lay out.
+                       // They use a compact stateful bytecode representation
+                       // that is too much bother to deal with.
+                       // Instead, interpret the C declaration
+                       //      void *_Cvar_stderr = &stderr;
+                       // as making _Cvar_stderr the name of a GOT entry
+                       // for stderr. This is separate from the usual GOT entry,
+                       // just in case the C code assigns to the variable,
+                       // and of course it only works for single pointers,
+                       // but we only need to support cgo and that's all it needs.
+                       ld.Adddynsym(ctxt, targ)
+
+                       got := ctxt.Syms.Lookup(".got", 0)
+                       s.Type = got.Type
+                       s.Attr |= sym.AttrSubSymbol
+                       s.Outer = got
+                       s.Sub = got.Sub
+                       got.Sub = s
+                       s.Value = got.Size
+                       got.AddUint32(ctxt.Arch, 0)
+                       ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(targ.Dynid))
+                       r.Type = objabi.ElfRelocOffset // ignore during relocsym
+                       return true
+               }
+       }
+
+       return false
+}
+
+func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
+       ctxt.Out.Write32(uint32(sectoff))
+
+       elfsym := r.Xsym.ElfsymForReloc()
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               if r.Siz == 4 {
+                       ctxt.Out.Write32(uint32(elf.R_386_32) | uint32(elfsym)<<8)
+               } else {
+                       return false
+               }
+       case objabi.R_GOTPCREL:
+               if r.Siz == 4 {
+                       ctxt.Out.Write32(uint32(elf.R_386_GOTPC))
+                       if r.Xsym.Name != "_GLOBAL_OFFSET_TABLE_" {
+                               ctxt.Out.Write32(uint32(sectoff))
+                               ctxt.Out.Write32(uint32(elf.R_386_GOT32) | uint32(elfsym)<<8)
+                       }
+               } else {
+                       return false
+               }
+       case objabi.R_CALL:
+               if r.Siz == 4 {
+                       if r.Xsym.Type == sym.SDYNIMPORT {
+                               ctxt.Out.Write32(uint32(elf.R_386_PLT32) | uint32(elfsym)<<8)
+                       } else {
+                               ctxt.Out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8)
+                       }
+               } else {
+                       return false
+               }
+       case objabi.R_PCREL:
+               if r.Siz == 4 {
+                       ctxt.Out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8)
+               } else {
+                       return false
+               }
+       case objabi.R_TLS_LE:
+               if r.Siz == 4 {
+                       ctxt.Out.Write32(uint32(elf.R_386_TLS_LE) | uint32(elfsym)<<8)
+               } else {
+                       return false
+               }
+       case objabi.R_TLS_IE:
+               if r.Siz == 4 {
+                       ctxt.Out.Write32(uint32(elf.R_386_GOTPC))
+                       ctxt.Out.Write32(uint32(sectoff))
+                       ctxt.Out.Write32(uint32(elf.R_386_TLS_GOTIE) | uint32(elfsym)<<8)
+               } else {
+                       return false
+               }
+       }
+
+       return true
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       var v uint32
+
+       rs := r.Xsym
+
+       if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALL {
+               if rs.Dynid < 0 {
+                       ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
+                       return false
+               }
+
+               v = uint32(rs.Dynid)
+               v |= 1 << 27 // external relocation
+       } else {
+               v = uint32(rs.Sect.Extnum)
+               if v == 0 {
+                       ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type)
+                       return false
+               }
+       }
+
+       switch r.Type {
+       default:
+               return false
+       case objabi.R_ADDR:
+               v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
+       case objabi.R_CALL,
+               objabi.R_PCREL:
+               v |= 1 << 24 // pc-relative bit
+               v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
+       }
+
+       switch r.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, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       var v uint32
+
+       rs := r.Xsym
+
+       if rs.Dynid < 0 {
+               ld.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
+               return false
+       }
+
+       out.Write32(uint32(sectoff))
+       out.Write32(uint32(rs.Dynid))
+
+       switch r.Type {
+       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(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+       if ctxt.LinkMode == ld.LinkExternal {
+               return val, false
+       }
+       switch r.Type {
+       case objabi.R_CONST:
+               return r.Add, true
+       case objabi.R_GOTOFF:
+               return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+       }
+
+       return val, false
+}
+
+func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+       log.Fatalf("unexpected relocation variant")
+       return t
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+       plt := ctxt.Syms.Lookup(".plt", 0)
+       got := ctxt.Syms.Lookup(".got.plt", 0)
+       if plt.Size == 0 {
+               // pushl got+4
+               plt.AddUint8(0xff)
+
+               plt.AddUint8(0x35)
+               plt.AddAddrPlus(ctxt.Arch, got, 4)
+
+               // jmp *got+8
+               plt.AddUint8(0xff)
+
+               plt.AddUint8(0x25)
+               plt.AddAddrPlus(ctxt.Arch, got, 8)
+
+               // zero pad
+               plt.AddUint32(ctxt.Arch, 0)
+
+               // assume got->size == 0 too
+               got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+
+               got.AddUint32(ctxt.Arch, 0)
+               got.AddUint32(ctxt.Arch, 0)
+       }
+}
+
+func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Plt() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+
+       if ctxt.IsELF {
+               plt := ctxt.Syms.Lookup(".plt", 0)
+               got := ctxt.Syms.Lookup(".got.plt", 0)
+               rel := ctxt.Syms.Lookup(".rel.plt", 0)
+               if plt.Size == 0 {
+                       elfsetupplt(ctxt)
+               }
+
+               // jmpq *got+size
+               plt.AddUint8(0xff)
+
+               plt.AddUint8(0x25)
+               plt.AddAddrPlus(ctxt.Arch, got, got.Size)
+
+               // add to got: pointer to current pos in plt
+               got.AddAddrPlus(ctxt.Arch, plt, plt.Size)
+
+               // pushl $x
+               plt.AddUint8(0x68)
+
+               plt.AddUint32(ctxt.Arch, uint32(rel.Size))
+
+               // jmp .plt
+               plt.AddUint8(0xe9)
+
+               plt.AddUint32(ctxt.Arch, uint32(-(plt.Size + 4)))
+
+               // rel
+               rel.AddAddrPlus(ctxt.Arch, got, got.Size-4)
+
+               rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_JMP_SLOT)))
+
+               s.SetPlt(int32(plt.Size - 16))
+       } else if ctxt.HeadType == objabi.Hdarwin {
+               // Same laziness as in 6l.
+
+               plt := ctxt.Syms.Lookup(".plt", 0)
+
+               addgotsym(ctxt, s)
+
+               ctxt.Syms.Lookup(".linkedit.plt", 0).AddUint32(ctxt.Arch, uint32(s.Dynid))
+
+               // jmpq *got+size(IP)
+               s.SetPlt(int32(plt.Size))
+
+               plt.AddUint8(0xff)
+               plt.AddUint8(0x25)
+               plt.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".got", 0), int64(s.Got()))
+       } else {
+               ld.Errorf(s, "addpltsym: unsupported binary format")
+       }
+}
+
+func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+       if s.Got() >= 0 {
+               return
+       }
+
+       ld.Adddynsym(ctxt, s)
+       got := ctxt.Syms.Lookup(".got", 0)
+       s.SetGot(int32(got.Size))
+       got.AddUint32(ctxt.Arch, 0)
+
+       if ctxt.IsELF {
+               rel := ctxt.Syms.Lookup(".rel", 0)
+               rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got()))
+               rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_GLOB_DAT)))
+       } else if ctxt.HeadType == objabi.Hdarwin {
+               ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(s.Dynid))
+       } else {
+               ld.Errorf(s, "addgotsym: unsupported binary format")
+       }
+}
+
+func asmb(ctxt *ld.Link) {
+       if ctxt.IsELF {
+               ld.Asmbelfsetup()
+       }
+
+       sect := ld.Segtext.Sections[0]
+       ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+       // 0xCC is INT $3 - breakpoint instruction
+       ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC})
+       for _, sect = range ld.Segtext.Sections[1:] {
+               ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+               ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+       }
+
+       if ld.Segrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+       }
+       if ld.Segrelrodata.Filelen > 0 {
+               ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff))
+               ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
+       }
+
+       ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
+       ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+       ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
+       ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+
+func asmb2(ctxt *ld.Link) {
+       machlink := uint32(0)
+       if ctxt.HeadType == objabi.Hdarwin {
+               machlink = uint32(ld.Domacholink(ctxt))
+       }
+
+       ld.Symsize = 0
+       ld.Spsize = 0
+       ld.Lcsize = 0
+       symo := uint32(0)
+       if !*ld.FlagS {
+               // TODO: rationalize
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                               symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+                       }
+
+               case objabi.Hplan9:
+                       symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
+
+               case objabi.Hdarwin:
+                       symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
+
+               case objabi.Hwindows:
+                       symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                       symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN))
+               }
+
+               ctxt.Out.SeekSet(int64(symo))
+               switch ctxt.HeadType {
+               default:
+                       if ctxt.IsELF {
+                               ld.Asmelfsym(ctxt)
+                               ctxt.Out.Flush()
+                               ctxt.Out.Write(ld.Elfstrdat)
+
+                               if ctxt.LinkMode == ld.LinkExternal {
+                                       ld.Elfemitreloc(ctxt)
+                               }
+                       }
+
+               case objabi.Hplan9:
+                       ld.Asmplan9sym(ctxt)
+                       ctxt.Out.Flush()
+
+                       sym := ctxt.Syms.Lookup("pclntab", 0)
+                       if sym != nil {
+                               ld.Lcsize = int32(len(sym.P))
+                               ctxt.Out.Write(sym.P)
+                               ctxt.Out.Flush()
+                       }
+
+               case objabi.Hwindows:
+                       // Do nothing
+
+               case objabi.Hdarwin:
+                       if ctxt.LinkMode == ld.LinkExternal {
+                               ld.Machoemitreloc(ctxt)
+                       }
+               }
+       }
+
+       ctxt.Out.SeekSet(0)
+       switch ctxt.HeadType {
+       default:
+       case objabi.Hplan9: /* plan9 */
+               magic := int32(4*11*11 + 7)
+
+               ctxt.Out.Write32b(uint32(magic))              /* magic */
+               ctxt.Out.Write32b(uint32(ld.Segtext.Filelen)) /* sizes */
+               ctxt.Out.Write32b(uint32(ld.Segdata.Filelen))
+               ctxt.Out.Write32b(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
+               ctxt.Out.Write32b(uint32(ld.Symsize))          /* nsyms */
+               ctxt.Out.Write32b(uint32(ld.Entryvalue(ctxt))) /* va of entry */
+               ctxt.Out.Write32b(uint32(ld.Spsize))           /* sp offsets */
+               ctxt.Out.Write32b(uint32(ld.Lcsize))           /* line offsets */
+
+       case objabi.Hdarwin:
+               ld.Asmbmacho(ctxt)
+
+       case objabi.Hlinux,
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd:
+               ld.Asmbelf(ctxt, int64(symo))
+
+       case objabi.Hwindows:
+               ld.Asmbpe(ctxt)
+       }
+
+       ctxt.Out.Flush()
+}
diff --git a/src/cmd/oldlink/internal/x86/l.go b/src/cmd/oldlink/internal/x86/l.go
new file mode 100644 (file)
index 0000000..0f104ea
--- /dev/null
@@ -0,0 +1,43 @@
+// Inferno utils/8l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/x86/obj.go b/src/cmd/oldlink/internal/x86/obj.go
new file mode 100644 (file)
index 0000000..b78ccba
--- /dev/null
@@ -0,0 +1,113 @@
+// Inferno utils/8l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/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/oldlink/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+       arch := sys.Arch386
+
+       theArch := ld.Arch{
+               Funcalign:  funcAlign,
+               Maxalign:   maxAlign,
+               Minalign:   minAlign,
+               Dwarfregsp: dwarfRegSP,
+               Dwarfreglr: dwarfRegLR,
+
+               Adddynrel:        adddynrel,
+               Archinit:         archinit,
+               Archreloc:        archreloc,
+               Archrelocvariant: archrelocvariant,
+               Asmb:             asmb,
+               Asmb2:            asmb2,
+               Elfreloc1:        elfreloc1,
+               Elfsetupplt:      elfsetupplt,
+               Gentext:          gentext,
+               Machoreloc1:      machoreloc1,
+               PEreloc1:         pereloc1,
+
+               Linuxdynld:   "/lib/ld-linux.so.2",
+               Freebsddynld: "/usr/libexec/ld-elf.so.1",
+               Openbsddynld: "/usr/libexec/ld.so",
+               Netbsddynld:  "/usr/libexec/ld.elf_so",
+               Solarisdynld: "/lib/ld.so.1",
+       }
+
+       return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+       switch ctxt.HeadType {
+       default:
+               ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+       case objabi.Hplan9: /* plan 9 */
+               ld.HEADR = 32
+
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+
+       case objabi.Hdarwin: /* apple MACH */
+               ld.HEADR = ld.INITIAL_MACHO_HEADR
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+
+       case objabi.Hlinux, /* elf32 executable */
+               objabi.Hfreebsd,
+               objabi.Hnetbsd,
+               objabi.Hopenbsd:
+               ld.Elfinit(ctxt)
+
+               ld.HEADR = ld.ELFRESERVE
+               if *ld.FlagTextAddr == -1 {
+                       *ld.FlagTextAddr = 0x08048000 + int64(ld.HEADR)
+               }
+               if *ld.FlagRound == -1 {
+                       *ld.FlagRound = 4096
+               }
+
+       case objabi.Hwindows: /* PE executable */
+               // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+               return
+       }
+}
diff --git a/src/cmd/oldlink/link_test.go b/src/cmd/oldlink/link_test.go
new file mode 100644 (file)
index 0000000..4f792bd
--- /dev/null
@@ -0,0 +1,449 @@
+package main
+
+import (
+       "bufio"
+       "bytes"
+       "debug/macho"
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "regexp"
+       "runtime"
+       "strings"
+       "testing"
+)
+
+var AuthorPaidByTheColumnInch struct {
+       fog int `text:"London. Michaelmas term lately over, and the Lord Chancellor sitting in Lincoln’s Inn Hall. Implacable November weather. As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill. Smoke lowering down from chimney-pots, making a soft black drizzle, with flakes of soot in it as big as full-grown snowflakes—gone into mourning, one might imagine, for the death of the sun. Dogs, undistinguishable in mire. Horses, scarcely better; splashed to their very blinkers. Foot passengers, jostling one another’s umbrellas in a general infection of ill temper, and losing their foot-hold at street-corners, where tens of thousands of other foot passengers have been slipping and sliding since the day broke (if this day ever broke), adding new deposits to the crust upon crust of mud, sticking at those points tenaciously to the pavement, and accumulating at compound interest.    Fog everywhere. Fog up the river, where it flows among green aits and meadows; fog down the river, where it rolls defiled among the tiers of shipping and the waterside pollutions of a great (and dirty) city. Fog on the Essex marshes, fog on the Kentish heights. Fog creeping into the cabooses of collier-brigs; fog lying out on the yards and hovering in the rigging of great ships; fog drooping on the gunwales of barges and small boats. Fog in the eyes and throats of ancient Greenwich pensioners, wheezing by the firesides of their wards; fog in the stem and bowl of the afternoon pipe of the wrathful skipper, down in his close cabin; fog cruelly pinching the toes and fingers of his shivering little ‘prentice boy on deck. Chance people on the bridges peeping over the parapets into a nether sky of fog, with fog all round them, as if they were up in a balloon and hanging in the misty clouds.     Gas looming through the fog in divers places in the streets, much as the sun may, from the spongey fields, be seen to loom by husbandman and ploughboy. Most of the shops lighted two hours before their time—as the gas seems to know, for it has a haggard and unwilling look.      The raw afternoon is rawest, and the dense fog is densest, and the muddy streets are muddiest near that leaden-headed old obstruction, appropriate ornament for the threshold of a leaden-headed old corporation, Temple Bar. And hard by Temple Bar, in Lincoln’s Inn Hall, at the very heart of the fog, sits the Lord High Chancellor in his High Court of Chancery."`
+
+       wind int `text:"It was grand to see how the wind awoke, and bent the trees, and drove the rain before it like a cloud of smoke; and to hear the solemn thunder, and to see the lightning; and while thinking with awe of the tremendous powers by which our little lives are encompassed, to consider how beneficent they are, and how upon the smallest flower and leaf there was already a freshness poured from all this seeming rage, which seemed to make creation new again."`
+
+       jarndyce int `text:"Jarndyce and Jarndyce drones on. This scarecrow of a suit has, over the course of time, become so complicated, that no man alive knows what it means. The parties to it understand it least; but it has been observed that no two Chancery lawyers can talk about it for five minutes, without coming to a total disagreement as to all the premises. Innumerable children have been born into the cause; innumerable young people have married into it; innumerable old people have died out of it. Scores of persons have deliriously found themselves made parties in Jarndyce and Jarndyce, without knowing how or why; whole families have inherited legendary hatreds with the suit. The little plaintiff or defendant, who was promised a new rocking-horse when Jarndyce and Jarndyce should be settled, has grown up, possessed himself of a real horse, and trotted away into the other world. Fair wards of court have faded into mothers and grandmothers; a long procession of Chancellors has come in and gone out; the legion of bills in the suit have been transformed into mere bills of mortality; there are not three Jarndyces left upon the earth perhaps, since old Tom Jarndyce in despair blew his brains out at a coffee-house in Chancery Lane; but Jarndyce and Jarndyce still drags its dreary length before the Court, perennially hopeless."`
+
+       principle int `text:"The one great principle of the English law is, to make business for itself. There is no other principle distinctly, certainly, and consistently maintained through all its narrow turnings. Viewed by this light it becomes a coherent scheme, and not the monstrous maze the laity are apt to think it. Let them but once clearly perceive that its grand principle is to make business for itself at their expense, and surely they will cease to grumble."`
+}
+
+func TestLargeSymName(t *testing.T) {
+       // The compiler generates a symbol name using the string form of the
+       // type. This tests that the linker can read symbol names larger than
+       // the bufio buffer. Issue #15104.
+       _ = AuthorPaidByTheColumnInch
+}
+
+func TestIssue21703(t *testing.T) {
+       t.Parallel()
+
+       testenv.MustHaveGoBuild(t)
+
+       const source = `
+package main
+const X = "\n!\n"
+func main() {}
+`
+
+       tmpdir, err := ioutil.TempDir("", "issue21703")
+       if err != nil {
+               t.Fatalf("failed to create temp dir: %v\n", err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       err = ioutil.WriteFile(filepath.Join(tmpdir, "main.go"), []byte(source), 0666)
+       if err != nil {
+               t.Fatalf("failed to write main.go: %v\n", err)
+       }
+
+       cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "main.go")
+       cmd.Dir = tmpdir
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
+       }
+
+       cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "main.o")
+       cmd.Dir = tmpdir
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
+       }
+}
+
+// TestIssue28429 ensures that the linker does not attempt to link
+// sections not named *.o. Such sections may be used by a build system
+// to, for example, save facts produced by a modular static analysis
+// such as golang.org/x/tools/go/analysis.
+func TestIssue28429(t *testing.T) {
+       t.Parallel()
+
+       testenv.MustHaveGoBuild(t)
+
+       tmpdir, err := ioutil.TempDir("", "issue28429-")
+       if err != nil {
+               t.Fatalf("failed to create temp dir: %v", err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       write := func(name, content string) {
+               err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       runGo := func(args ...string) {
+               cmd := exec.Command(testenv.GoToolPath(t), args...)
+               cmd.Dir = tmpdir
+               out, err := cmd.CombinedOutput()
+               if err != nil {
+                       t.Fatalf("'go %s' failed: %v, output: %s",
+                               strings.Join(args, " "), err, out)
+               }
+       }
+
+       // Compile a main package.
+       write("main.go", "package main; func main() {}")
+       runGo("tool", "compile", "-p", "main", "main.go")
+       runGo("tool", "pack", "c", "main.a", "main.o")
+
+       // Add an extra section with a short, non-.o name.
+       // This simulates an alternative build system.
+       write(".facts", "this is not an object file")
+       runGo("tool", "pack", "r", "main.a", ".facts")
+
+       // Verify that the linker does not attempt
+       // to compile the extra section.
+       runGo("tool", "link", "main.a")
+}
+
+func TestUnresolved(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       tmpdir, err := ioutil.TempDir("", "unresolved-")
+       if err != nil {
+               t.Fatalf("failed to create temp dir: %v", err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       write := func(name, content string) {
+               err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       // Test various undefined references. Because of issue #29852,
+       // this used to give confusing error messages because the
+       // linker would find an undefined reference to "zero" created
+       // by the runtime package.
+
+       write("go.mod", "module testunresolved\n")
+       write("main.go", `package main
+
+func main() {
+        x()
+}
+
+func x()
+`)
+       write("main.s", `
+TEXT ·x(SB),0,$0
+        MOVD zero<>(SB), AX
+        MOVD zero(SB), AX
+        MOVD ·zero(SB), AX
+        RET
+`)
+       cmd := exec.Command(testenv.GoToolPath(t), "build")
+       cmd.Dir = tmpdir
+       cmd.Env = append(os.Environ(),
+               "GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
+       out, err := cmd.CombinedOutput()
+       if err == nil {
+               t.Fatalf("expected build to fail, but it succeeded")
+       }
+       out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
+       got := string(out)
+       want := `main.x: relocation target zero not defined
+main.x: relocation target zero not defined
+main.x: relocation target main.zero not defined
+`
+       if want != got {
+               t.Fatalf("want:\n%sgot:\n%s", want, got)
+       }
+}
+
+func TestBuildForTvOS(t *testing.T) {
+       testenv.MustHaveCGO(t)
+       testenv.MustHaveGoBuild(t)
+
+       // Only run this on darwin/amd64, where we can cross build for tvOS.
+       if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
+               t.Skip("skipping on non-darwin/amd64 platform")
+       }
+       if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+               t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
+       }
+       if err := exec.Command("xcrun", "--help").Run(); err != nil {
+               t.Skipf("error running xcrun, required for iOS cross build: %v", err)
+       }
+
+       sdkPath, err := exec.Command("xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
+       if err != nil {
+               t.Skip("failed to locate appletvos SDK, skipping")
+       }
+       CC := []string{
+               "clang",
+               "-arch",
+               "arm64",
+               "-isysroot", strings.TrimSpace(string(sdkPath)),
+               "-mtvos-version-min=12.0",
+               "-fembed-bitcode",
+               "-framework", "CoreFoundation",
+       }
+       lib := filepath.Join("testdata", "lib.go")
+       tmpDir, err := ioutil.TempDir("", "go-link-TestBuildFortvOS")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpDir)
+
+       ar := filepath.Join(tmpDir, "lib.a")
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
+       cmd.Env = append(os.Environ(),
+               "CGO_ENABLED=1",
+               "GOOS=darwin",
+               "GOARCH=arm64",
+               "CC="+strings.Join(CC, " "),
+               "CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
+       )
+       if out, err := cmd.CombinedOutput(); err != nil {
+               t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+       }
+
+       link := exec.Command(CC[0], CC[1:]...)
+       link.Args = append(link.Args, ar, filepath.Join("testdata", "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)
+
+       tmpdir, err := ioutil.TempDir("", "TestXFlag")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       src := filepath.Join(tmpdir, "main.go")
+       err = ioutil.WriteFile(src, []byte(testXFlagSrc), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
+       if out, err := cmd.CombinedOutput(); err != nil {
+               t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
+       }
+}
+
+var testMacOSVersionSrc = `
+package main
+func main() { }
+`
+
+func TestMacOSVersion(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       tmpdir, err := ioutil.TempDir("", "TestMacOSVersion")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       src := filepath.Join(tmpdir, "main.go")
+       err = ioutil.WriteFile(src, []byte(testMacOSVersionSrc), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       exe := filepath.Join(tmpdir, "main")
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
+       cmd.Env = append(os.Environ(),
+               "CGO_ENABLED=0",
+               "GOOS=darwin",
+               "GOARCH=amd64",
+       )
+       if out, err := cmd.CombinedOutput(); err != nil {
+               t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+       }
+       exef, err := os.Open(exe)
+       if err != nil {
+               t.Fatal(err)
+       }
+       exem, err := macho.NewFile(exef)
+       if err != nil {
+               t.Fatal(err)
+       }
+       found := false
+       const LC_VERSION_MIN_MACOSX = 0x24
+       checkMin := func(ver uint32) {
+               major, minor := (ver>>16)&0xff, (ver>>8)&0xff
+               if major != 10 || minor < 9 {
+                       t.Errorf("LC_VERSION_MIN_MACOSX version %d.%d < 10.9", major, minor)
+               }
+       }
+       for _, cmd := range exem.Loads {
+               raw := cmd.Raw()
+               type_ := exem.ByteOrder.Uint32(raw)
+               if type_ != LC_VERSION_MIN_MACOSX {
+                       continue
+               }
+               osVer := exem.ByteOrder.Uint32(raw[8:])
+               checkMin(osVer)
+               sdkVer := exem.ByteOrder.Uint32(raw[12:])
+               checkMin(sdkVer)
+               found = true
+               break
+       }
+       if !found {
+               t.Errorf("no LC_VERSION_MIN_MACOSX load command found")
+       }
+}
+
+const Issue34788src = `
+
+package blah
+
+func Blah(i int) int {
+       a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
+       return a[i&7]
+}
+`
+
+func TestIssue34788Android386TLSSequence(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       // This is a cross-compilation test, so it doesn't make
+       // sense to run it on every GOOS/GOARCH combination. Limit
+       // the test to amd64 + darwin/linux.
+       if runtime.GOARCH != "amd64" ||
+               (runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
+               t.Skip("skipping on non-{linux,darwin}/amd64 platform")
+       }
+
+       tmpdir, err := ioutil.TempDir("", "TestIssue34788Android386TLSSequence")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       src := filepath.Join(tmpdir, "blah.go")
+       err = ioutil.WriteFile(src, []byte(Issue34788src), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       obj := filepath.Join(tmpdir, "blah.o")
+       cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", obj, src)
+       cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
+       if out, err := cmd.CombinedOutput(); err != nil {
+               if err != nil {
+                       t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
+               }
+       }
+
+       // Run objdump on the resulting object.
+       cmd = exec.Command(testenv.GoToolPath(t), "tool", "objdump", obj)
+       out, oerr := cmd.CombinedOutput()
+       if oerr != nil {
+               t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
+       }
+
+       // Sift through the output; we should not be seeing any R_TLS_LE relocs.
+       scanner := bufio.NewScanner(bytes.NewReader(out))
+       for scanner.Scan() {
+               line := scanner.Text()
+               if strings.Contains(line, "R_TLS_LE") {
+                       t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
+               }
+       }
+}
+
+const testStrictDupGoSrc = `
+package main
+func f()
+func main() { f() }
+`
+
+const testStrictDupAsmSrc1 = `
+#include "textflag.h"
+TEXT   ·f(SB), NOSPLIT|DUPOK, $0-0
+       RET
+`
+
+const testStrictDupAsmSrc2 = `
+#include "textflag.h"
+TEXT   ·f(SB), NOSPLIT|DUPOK, $0-0
+       JMP     0(PC)
+`
+
+func TestStrictDup(t *testing.T) {
+       // Check that -strictdups flag works.
+       testenv.MustHaveGoBuild(t)
+
+       tmpdir, err := ioutil.TempDir("", "TestStrictDup")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       src := filepath.Join(tmpdir, "x.go")
+       err = ioutil.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+       src = filepath.Join(tmpdir, "a.s")
+       err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc1), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+       src = filepath.Join(tmpdir, "b.s")
+       err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc2), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+       src = filepath.Join(tmpdir, "go.mod")
+       err = ioutil.WriteFile(src, []byte("module teststrictdup\n"), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
+       cmd.Dir = tmpdir
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Errorf("linking with -strictdups=1 failed: %v", err)
+       }
+       if !bytes.Contains(out, []byte("mismatched payload")) {
+               t.Errorf("unexpected output:\n%s", out)
+       }
+
+       cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
+       cmd.Dir = tmpdir
+       out, err = cmd.CombinedOutput()
+       if err == nil {
+               t.Errorf("linking with -strictdups=2 did not fail")
+       }
+       if !bytes.Contains(out, []byte("mismatched payload")) {
+               t.Errorf("unexpected output:\n%s", out)
+       }
+}
diff --git a/src/cmd/oldlink/linkbig_test.go b/src/cmd/oldlink/linkbig_test.go
new file mode 100644 (file)
index 0000000..78d2bc1
--- /dev/null
@@ -0,0 +1,112 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This program generates a test to verify that a program can be
+// successfully linked even when there are very large text
+// sections present.
+
+package main
+
+import (
+       "bytes"
+       "cmd/internal/objabi"
+       "fmt"
+       "internal/testenv"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "testing"
+)
+
+func TestLargeText(t *testing.T) {
+       if testing.Short() || (objabi.GOARCH != "ppc64le" && objabi.GOARCH != "ppc64" && objabi.GOARCH != "arm") {
+               t.Skipf("Skipping large text section test in short mode or on %s", objabi.GOARCH)
+       }
+       testenv.MustHaveGoBuild(t)
+
+       var w bytes.Buffer
+       const FN = 4
+       tmpdir, err := ioutil.TempDir("", "bigtext")
+       if err != nil {
+               t.Fatalf("can't create temp directory: %v\n", err)
+       }
+
+       defer os.RemoveAll(tmpdir)
+
+       // Generate the scenario where the total amount of text exceeds the
+       // limit for the jmp/call instruction, on RISC architectures like ppc64le,
+       // which is 2^26.  When that happens the call requires special trampolines or
+       // long branches inserted by the linker where supported.
+       // Multiple .s files are generated instead of one.
+       instOnArch := map[string]string{
+               "ppc64":   "\tMOVD\tR0,R3\n",
+               "ppc64le": "\tMOVD\tR0,R3\n",
+               "arm":     "\tMOVW\tR0,R1\n",
+       }
+       inst := instOnArch[objabi.GOARCH]
+       for j := 0; j < FN; j++ {
+               testname := fmt.Sprintf("bigfn%d", j)
+               fmt.Fprintf(&w, "TEXT ·%s(SB),$0\n", testname)
+               for i := 0; i < 2200000; i++ {
+                       fmt.Fprintf(&w, inst)
+               }
+               fmt.Fprintf(&w, "\tRET\n")
+               err := ioutil.WriteFile(tmpdir+"/"+testname+".s", w.Bytes(), 0666)
+               if err != nil {
+                       t.Fatalf("can't write output: %v\n", err)
+               }
+               w.Reset()
+       }
+       fmt.Fprintf(&w, "package main\n")
+       fmt.Fprintf(&w, "\nimport (\n")
+       fmt.Fprintf(&w, "\t\"os\"\n")
+       fmt.Fprintf(&w, "\t\"fmt\"\n")
+       fmt.Fprintf(&w, ")\n\n")
+
+       for i := 0; i < FN; i++ {
+               fmt.Fprintf(&w, "func bigfn%d()\n", i)
+       }
+       fmt.Fprintf(&w, "\nfunc main() {\n")
+
+       // There are lots of dummy code generated in the .s files just to generate a lot
+       // of text. Link them in but guard their call so their code is not executed but
+       // the main part of the program can be run.
+       fmt.Fprintf(&w, "\tif os.Getenv(\"LINKTESTARG\") != \"\" {\n")
+       for i := 0; i < FN; i++ {
+               fmt.Fprintf(&w, "\t\tbigfn%d()\n", i)
+       }
+       fmt.Fprintf(&w, "\t}\n")
+       fmt.Fprintf(&w, "\tfmt.Printf(\"PASS\\n\")\n")
+       fmt.Fprintf(&w, "}")
+       err = ioutil.WriteFile(tmpdir+"/bigfn.go", w.Bytes(), 0666)
+       if err != nil {
+               t.Fatalf("can't write output: %v\n", err)
+       }
+
+       // Build and run with internal linking.
+       os.Chdir(tmpdir)
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "bigtext")
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("Build failed for big text program with internal linking: %v, output: %s", err, out)
+       }
+       cmd = exec.Command(tmpdir + "/bigtext")
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("Program built with internal linking failed to run with err %v, output: %s", err, out)
+       }
+
+       // Build and run with external linking
+       os.Chdir(tmpdir)
+       cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", "bigtext", "-ldflags", "'-linkmode=external'")
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("Build failed for big text program with external linking: %v, output: %s", err, out)
+       }
+       cmd = exec.Command(tmpdir + "/bigtext")
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("Program built with external linking failed to run with err %v, output: %s", err, out)
+       }
+}
diff --git a/src/cmd/oldlink/main.go b/src/cmd/oldlink/main.go
new file mode 100644 (file)
index 0000000..be1d0fd
--- /dev/null
@@ -0,0 +1,71 @@
+// 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.
+
+// TODO(go115newobj): delete.
+
+package main
+
+import (
+       "cmd/internal/objabi"
+       "cmd/internal/sys"
+       "cmd/oldlink/internal/amd64"
+       "cmd/oldlink/internal/arm"
+       "cmd/oldlink/internal/arm64"
+       "cmd/oldlink/internal/ld"
+       "cmd/oldlink/internal/mips"
+       "cmd/oldlink/internal/mips64"
+       "cmd/oldlink/internal/ppc64"
+       "cmd/oldlink/internal/riscv64"
+       "cmd/oldlink/internal/s390x"
+       "cmd/oldlink/internal/wasm"
+       "cmd/oldlink/internal/x86"
+       "fmt"
+       "os"
+)
+
+// The bulk of the linker implementation lives in cmd/oldlink/internal/ld.
+// Architecture-specific code lives in cmd/oldlink/internal/GOARCH.
+//
+// Program initialization:
+//
+// Before any argument parsing is done, the Init function of relevant
+// architecture package is called. The only job done in Init is
+// configuration of the architecture-specific variables.
+//
+// Then control flow passes to ld.Main, which parses flags, makes
+// some configuration decisions, and then gives the architecture
+// packages a second chance to modify the linker's configuration
+// via the ld.Arch.Archinit function.
+
+func main() {
+       var arch *sys.Arch
+       var theArch ld.Arch
+
+       switch objabi.GOARCH {
+       default:
+               fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", objabi.GOARCH)
+               os.Exit(2)
+       case "386":
+               arch, theArch = x86.Init()
+       case "amd64":
+               arch, theArch = amd64.Init()
+       case "arm":
+               arch, theArch = arm.Init()
+       case "arm64":
+               arch, theArch = arm64.Init()
+       case "mips", "mipsle":
+               arch, theArch = mips.Init()
+       case "mips64", "mips64le":
+               arch, theArch = mips64.Init()
+       case "ppc64", "ppc64le":
+               arch, theArch = ppc64.Init()
+       case "riscv64":
+               arch, theArch = riscv64.Init()
+       case "s390x":
+               arch, theArch = s390x.Init()
+       case "wasm":
+               arch, theArch = wasm.Init()
+       }
+       ld.Main(arch, theArch)
+}
diff --git a/src/cmd/oldlink/testdata/lib.go b/src/cmd/oldlink/testdata/lib.go
new file mode 100644 (file)
index 0000000..bc6c699
--- /dev/null
@@ -0,0 +1,8 @@
+package main
+
+import "C"
+
+//export GoFunc
+func GoFunc() {}
+
+func main() {}
diff --git a/src/cmd/oldlink/testdata/main.m b/src/cmd/oldlink/testdata/main.m
new file mode 100644 (file)
index 0000000..1c8175f
--- /dev/null
@@ -0,0 +1,5 @@
+extern void GoFunc();
+
+int main(int argc, char **argv) {
+       GoFunc();
+}