aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/asm/internal/flags/flags.go5
-rw-r--r--src/cmd/asm/main.go4
-rw-r--r--src/cmd/compile/internal/gc/iexport.go2
-rw-r--r--src/cmd/compile/internal/gc/iimport.go2
-rw-r--r--src/cmd/compile/internal/gc/main.go4
-rw-r--r--src/cmd/dist/buildtool.go2
-rw-r--r--src/cmd/go/internal/work/gc.go21
-rw-r--r--src/cmd/internal/dwarf/dwarf.go11
-rw-r--r--src/cmd/internal/goobj/read.go3
-rw-r--r--src/cmd/internal/goobj/readnew.go56
-rw-r--r--src/cmd/internal/goobj2/funcinfo.go17
-rw-r--r--src/cmd/internal/goobj2/objfile.go210
-rw-r--r--src/cmd/internal/goobj2/objfile_test.go39
-rw-r--r--src/cmd/internal/obj/link.go2
-rw-r--r--src/cmd/internal/obj/objfile.go52
-rw-r--r--src/cmd/internal/obj/objfile2.go90
-rw-r--r--src/cmd/internal/obj/plist.go36
-rw-r--r--src/cmd/internal/obj/sym.go84
-rw-r--r--src/cmd/link/internal/amd64/asm.go184
-rw-r--r--src/cmd/link/internal/arm/asm.go152
-rw-r--r--src/cmd/link/internal/arm64/asm.go182
-rw-r--r--src/cmd/link/internal/benchmark/bench.go195
-rw-r--r--src/cmd/link/internal/benchmark/bench_test.go53
-rw-r--r--src/cmd/link/internal/ld/ar.go16
-rw-r--r--src/cmd/link/internal/ld/data.go708
-rw-r--r--src/cmd/link/internal/ld/deadcode.go380
-rw-r--r--src/cmd/link/internal/ld/deadcode2.go156
-rw-r--r--src/cmd/link/internal/ld/decodesym.go5
-rw-r--r--src/cmd/link/internal/ld/decodesym2.go120
-rw-r--r--src/cmd/link/internal/ld/dwarf.go1845
-rw-r--r--src/cmd/link/internal/ld/dwarf2.go172
-rw-r--r--src/cmd/link/internal/ld/elf.go507
-rw-r--r--src/cmd/link/internal/ld/errors.go87
-rw-r--r--src/cmd/link/internal/ld/go.go163
-rw-r--r--src/cmd/link/internal/ld/lib.go891
-rw-r--r--src/cmd/link/internal/ld/link.go81
-rw-r--r--src/cmd/link/internal/ld/macho.go77
-rw-r--r--src/cmd/link/internal/ld/main.go148
-rw-r--r--src/cmd/link/internal/ld/outbuf.go110
-rw-r--r--src/cmd/link/internal/ld/outbuf_mmap.go9
-rw-r--r--src/cmd/link/internal/ld/outbuf_test.go30
-rw-r--r--src/cmd/link/internal/ld/outbuf_windows.go3
-rw-r--r--src/cmd/link/internal/ld/pe.go152
-rw-r--r--src/cmd/link/internal/ld/sym.go10
-rw-r--r--src/cmd/link/internal/ld/symtab.go18
-rw-r--r--src/cmd/link/internal/ld/target.go144
-rw-r--r--src/cmd/link/internal/ld/util.go37
-rw-r--r--src/cmd/link/internal/ld/xcoff.go159
-rw-r--r--src/cmd/link/internal/loadelf/ldelf.go231
-rw-r--r--src/cmd/link/internal/loader/loader.go2530
-rw-r--r--src/cmd/link/internal/loader/loader_test.go441
-rw-r--r--src/cmd/link/internal/loader/symbolbuilder.go349
-rw-r--r--src/cmd/link/internal/loadmacho/ldmacho.go170
-rw-r--r--src/cmd/link/internal/loadpe/ldpe.go176
-rw-r--r--src/cmd/link/internal/loadxcoff/ldxcoff.go65
-rw-r--r--src/cmd/link/internal/mips/asm.go44
-rw-r--r--src/cmd/link/internal/mips64/asm.go46
-rw-r--r--src/cmd/link/internal/ppc64/asm.go122
-rw-r--r--src/cmd/link/internal/riscv64/asm.go38
-rw-r--r--src/cmd/link/internal/s390x/asm.go110
-rw-r--r--src/cmd/link/internal/sym/attribute.go60
-rw-r--r--src/cmd/link/internal/sym/compilation_unit.go10
-rw-r--r--src/cmd/link/internal/sym/library.go3
-rw-r--r--src/cmd/link/internal/sym/segment.go1
-rw-r--r--src/cmd/link/internal/sym/symbol.go4
-rw-r--r--src/cmd/link/internal/sym/symbols.go106
-rw-r--r--src/cmd/link/internal/x86/asm.go156
-rw-r--r--src/cmd/link/link_test.go24
-rw-r--r--src/cmd/oldlink/doc.go129
-rw-r--r--src/cmd/oldlink/internal/amd64/asm.go874
-rw-r--r--src/cmd/oldlink/internal/amd64/l.go43
-rw-r--r--src/cmd/oldlink/internal/amd64/obj.go117
-rw-r--r--src/cmd/oldlink/internal/arm/asm.go891
-rw-r--r--src/cmd/oldlink/internal/arm/l.go75
-rw-r--r--src/cmd/oldlink/internal/arm/obj.go116
-rw-r--r--src/cmd/oldlink/internal/arm64/asm.go946
-rw-r--r--src/cmd/oldlink/internal/arm64/l.go74
-rw-r--r--src/cmd/oldlink/internal/arm64/obj.go110
-rw-r--r--src/cmd/oldlink/internal/ld/ar.go193
-rw-r--r--src/cmd/oldlink/internal/ld/config.go272
-rw-r--r--src/cmd/oldlink/internal/ld/data.go2509
-rw-r--r--src/cmd/oldlink/internal/ld/deadcode.go408
-rw-r--r--src/cmd/oldlink/internal/ld/deadcode2.go441
-rw-r--r--src/cmd/oldlink/internal/ld/decodesym.go374
-rw-r--r--src/cmd/oldlink/internal/ld/dwarf.go2044
-rw-r--r--src/cmd/oldlink/internal/ld/elf.go2415
-rw-r--r--src/cmd/oldlink/internal/ld/execarchive.go37
-rw-r--r--src/cmd/oldlink/internal/ld/execarchive_noexec.go13
-rw-r--r--src/cmd/oldlink/internal/ld/go.go442
-rw-r--r--src/cmd/oldlink/internal/ld/ld.go217
-rw-r--r--src/cmd/oldlink/internal/ld/lib.go2749
-rw-r--r--src/cmd/oldlink/internal/ld/link.go187
-rw-r--r--src/cmd/oldlink/internal/ld/macho.go1119
-rw-r--r--src/cmd/oldlink/internal/ld/macho_combine_dwarf.go462
-rw-r--r--src/cmd/oldlink/internal/ld/main.go338
-rw-r--r--src/cmd/oldlink/internal/ld/outbuf.go177
-rw-r--r--src/cmd/oldlink/internal/ld/outbuf_mmap.go44
-rw-r--r--src/cmd/oldlink/internal/ld/outbuf_nommap.go15
-rw-r--r--src/cmd/oldlink/internal/ld/outbuf_windows.go49
-rw-r--r--src/cmd/oldlink/internal/ld/pcln.go530
-rw-r--r--src/cmd/oldlink/internal/ld/pe.go1562
-rw-r--r--src/cmd/oldlink/internal/ld/sym.go115
-rw-r--r--src/cmd/oldlink/internal/ld/symtab.go713
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/httptest/main/main.go22
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue10978/main.go27
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue10978/main.s1
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go27
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go10
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue26237/b.dir/b.go16
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go16
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue32233/lib/ObjC.m16
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go19
-rw-r--r--src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go11
-rw-r--r--src/cmd/oldlink/internal/ld/typelink.go49
-rw-r--r--src/cmd/oldlink/internal/ld/util.go97
-rw-r--r--src/cmd/oldlink/internal/ld/xcoff.go1685
-rw-r--r--src/cmd/oldlink/internal/loadelf/ldelf.go1282
-rw-r--r--src/cmd/oldlink/internal/loader/loader.go629
-rw-r--r--src/cmd/oldlink/internal/loadmacho/ldmacho.go891
-rw-r--r--src/cmd/oldlink/internal/loadpe/ldpe.go513
-rw-r--r--src/cmd/oldlink/internal/loadxcoff/ldxcoff.go238
-rw-r--r--src/cmd/oldlink/internal/mips/asm.go230
-rw-r--r--src/cmd/oldlink/internal/mips/l.go74
-rw-r--r--src/cmd/oldlink/internal/mips/obj.go89
-rw-r--r--src/cmd/oldlink/internal/mips64/asm.go278
-rw-r--r--src/cmd/oldlink/internal/mips64/l.go74
-rw-r--r--src/cmd/oldlink/internal/mips64/obj.go98
-rw-r--r--src/cmd/oldlink/internal/objfile/objfile.go (renamed from src/cmd/link/internal/objfile/objfile.go)6
-rw-r--r--src/cmd/oldlink/internal/ppc64/asm.go1181
-rw-r--r--src/cmd/oldlink/internal/ppc64/l.go74
-rw-r--r--src/cmd/oldlink/internal/ppc64/obj.go106
-rw-r--r--src/cmd/oldlink/internal/riscv64/asm.go168
-rw-r--r--src/cmd/oldlink/internal/riscv64/l.go14
-rw-r--r--src/cmd/oldlink/internal/riscv64/obj.go60
-rw-r--r--src/cmd/oldlink/internal/s390x/asm.go574
-rw-r--r--src/cmd/oldlink/internal/s390x/l.go74
-rw-r--r--src/cmd/oldlink/internal/s390x/obj.go88
-rw-r--r--src/cmd/oldlink/internal/sym/attribute.go117
-rw-r--r--src/cmd/oldlink/internal/sym/compilation_unit.go23
-rw-r--r--src/cmd/oldlink/internal/sym/library.go25
-rw-r--r--src/cmd/oldlink/internal/sym/reloc.go128
-rw-r--r--src/cmd/oldlink/internal/sym/segment.go58
-rw-r--r--src/cmd/oldlink/internal/sym/sizeof_test.go37
-rw-r--r--src/cmd/oldlink/internal/sym/symbol.go543
-rw-r--r--src/cmd/oldlink/internal/sym/symbols.go135
-rw-r--r--src/cmd/oldlink/internal/sym/symkind.go168
-rw-r--r--src/cmd/oldlink/internal/sym/symkind_string.go76
-rw-r--r--src/cmd/oldlink/internal/wasm/asm.go583
-rw-r--r--src/cmd/oldlink/internal/wasm/obj.go35
-rw-r--r--src/cmd/oldlink/internal/x86/asm.go745
-rw-r--r--src/cmd/oldlink/internal/x86/l.go43
-rw-r--r--src/cmd/oldlink/internal/x86/obj.go113
-rw-r--r--src/cmd/oldlink/main.go71
153 files changed, 40239 insertions, 4318 deletions
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index 618b08cc36..e8535ae9ac 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -23,9 +23,10 @@ var (
Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries")
AllErrors = flag.Bool("e", false, "no limit on number of errors reported")
SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble")
- Newobj = flag.Bool("newobj", false, "use new object file format")
+ Importpath = flag.String("p", "", "set expected package import to path")
+ Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)")
- Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)")
+ Go115Newobj = flag.Bool("go115newobj", true, "use new object file format")
)
var (
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index 21c8bd963a..a927de854b 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -40,7 +40,7 @@ func main() {
}
ctxt.Flag_dynlink = *flags.Dynlink
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
- ctxt.Flag_newobj = *flags.Newobj
+ ctxt.Flag_go115newobj = *flags.Go115Newobj
switch *flags.Spectre {
default:
log.Printf("unknown setting -spectre=%s", *flags.Spectre)
@@ -87,7 +87,7 @@ func main() {
pList.Firstpc, ok = parser.Parse()
// reports errors to parser.Errorf
if ok {
- obj.Flushplist(ctxt, pList, nil, "")
+ obj.Flushplist(ctxt, pList, nil, *flags.Importpath)
}
}
if !ok {
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go
index 7ee0876e8b..917bf2394a 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/gc/iexport.go
@@ -991,7 +991,7 @@ func (w *exportWriter) linkname(s *types.Sym) {
}
func (w *exportWriter) symIdx(s *types.Sym) {
- if Ctxt.Flag_newobj {
+ if Ctxt.Flag_go115newobj {
lsym := s.Linksym()
if lsym.PkgIdx > goobj2.PkgIdxSelf || (lsym.PkgIdx == goobj2.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
// Don't export index for non-package symbols, linkname'd symbols,
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
index f99b70ae27..f881a33ea7 100644
--- a/src/cmd/compile/internal/gc/iimport.go
+++ b/src/cmd/compile/internal/gc/iimport.go
@@ -687,7 +687,7 @@ func (r *importReader) linkname(s *types.Sym) {
}
func (r *importReader) symIdx(s *types.Sym) {
- if Ctxt.Flag_newobj {
+ if Ctxt.Flag_go115newobj {
lsym := s.Linksym()
idx := int32(r.int64())
if idx != -1 {
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 81d31c2007..d04c09c93e 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -280,7 +280,7 @@ func Main(archInit func(*Arch)) {
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
- flag.BoolVar(&Ctxt.Flag_newobj, "newobj", false, "use new object file format")
+ flag.BoolVar(&Ctxt.Flag_go115newobj, "go115newobj", true, "use new object file format")
flag.StringVar(&jsonLogOpt, "json", "", "version,destination for JSON compiler/optimizer logging")
objabi.Flagparse(usage)
@@ -314,7 +314,7 @@ func Main(archInit func(*Arch)) {
// Record flags that affect the build result. (And don't
// record flags that don't, since that would cause spurious
// changes in the binary.)
- recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre", "newobj")
+ recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre", "go115newobj")
if smallFrames {
maxStackVarSize = 128 * 1024
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index a07e64b472..5ec2381589 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -73,6 +73,7 @@ var bootstrapDirs = []string{
"cmd/link/internal/amd64",
"cmd/link/internal/arm",
"cmd/link/internal/arm64",
+ "cmd/link/internal/benchmark",
"cmd/link/internal/ld",
"cmd/link/internal/loadelf",
"cmd/link/internal/loader",
@@ -81,7 +82,6 @@ var bootstrapDirs = []string{
"cmd/link/internal/loadxcoff",
"cmd/link/internal/mips",
"cmd/link/internal/mips64",
- "cmd/link/internal/objfile",
"cmd/link/internal/ppc64",
"cmd/link/internal/riscv64",
"cmd/link/internal/s390x",
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index 7d17c0c01e..78db845ae7 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -37,6 +37,17 @@ func (gcToolchain) linker() string {
return base.Tool("link")
}
+func pkgPath(a *Action) string {
+ p := a.Package
+ ppath := p.ImportPath
+ if cfg.BuildBuildmode == "plugin" {
+ ppath = pluginPath(a)
+ } else if p.Name == "main" && !p.Internal.ForceLibrary {
+ ppath = "main"
+ }
+ return ppath
+}
+
func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
p := a.Package
objdir := a.Objdir
@@ -47,12 +58,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, s
ofile = objdir + out
}
- pkgpath := p.ImportPath
- if cfg.BuildBuildmode == "plugin" {
- pkgpath = pluginPath(a)
- } else if p.Name == "main" && !p.Internal.ForceLibrary {
- pkgpath = "main"
- }
+ pkgpath := pkgPath(a)
gcargs := []string{"-p", pkgpath}
if p.Module != nil && p.Module.GoVersion != "" && allowedVersion(p.Module.GoVersion) {
gcargs = append(gcargs, "-lang=go"+p.Module.GoVersion)
@@ -240,7 +246,8 @@ func (a *Action) trimpath() string {
func asmArgs(a *Action, p *load.Package) []interface{} {
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
inc := filepath.Join(cfg.GOROOT, "pkg", "include")
- args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
+ pkgpath := pkgPath(a)
+ args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
if p.ImportPath == "runtime" && cfg.Goarch == "386" {
for _, arg := range forcedAsmflags {
if arg == "-dynlink" {
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index 56b44a1ab5..a17b574cdd 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -18,6 +18,9 @@ import (
"strings"
)
+// TODO(go115newobj): clean up. Some constant prefixes here are no longer
+// needed in the new object files.
+
// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
const InfoPrefix = "go.info."
@@ -48,7 +51,7 @@ var logDwarf bool
// Sym represents a symbol.
type Sym interface {
- Len() int64
+ Length(dwarfContext interface{}) int64
}
// A Var represents a local variable or a function parameter.
@@ -1279,7 +1282,7 @@ func PutInlinedFunc(ctxt Context, s *FnState, callersym Sym, callIdx int) error
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, callee)
if abbrev == DW_ABRV_INLINED_SUBROUTINE_RANGES {
- putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Len(), s.Ranges)
+ putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges)
s.PutRanges(ctxt, ic.Ranges)
} else {
st := ic.Ranges[0].Start
@@ -1440,7 +1443,7 @@ func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev
putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, s.StartPC)
} else {
Uleb128put(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES)
- putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Len(), s.Ranges)
+ putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges)
s.PutRanges(ctxt, scope.Ranges)
}
@@ -1585,7 +1588,7 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int,
}
if abbrevUsesLoclist(abbrev) {
- putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Loc.Len(), s.Loc)
+ putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Loc.Length(ctxt), s.Loc)
v.PutLocationList(s.Loc, s.StartPC)
} else {
loc := encbuf[:0]
diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go
index 48537d2b1c..44e619cabb 100644
--- a/src/cmd/internal/goobj/read.go
+++ b/src/cmd/internal/goobj/read.go
@@ -11,6 +11,7 @@ package goobj
import (
"bufio"
"bytes"
+ "cmd/internal/goobj2"
"cmd/internal/objabi"
"errors"
"fmt"
@@ -507,7 +508,7 @@ func (r *objReader) parseObject(prefix []byte) error {
if err != nil {
return err
}
- if bytes.Equal(p, []byte("\x00go114LD")) {
+ if bytes.Equal(p, []byte(goobj2.Magic)) {
r.readNew()
return nil
}
diff --git a/src/cmd/internal/goobj/readnew.go b/src/cmd/internal/goobj/readnew.go
index 3f9d0d1db6..3a5a016514 100644
--- a/src/cmd/internal/goobj/readnew.go
+++ b/src/cmd/internal/goobj/readnew.go
@@ -57,9 +57,8 @@ func (r *objReader) readNew() {
pkg := pkglist[p]
return SymID{fmt.Sprintf("%s.<#%d>", pkg, s.SymIdx), 0}
}
- sym := goobj2.Sym{}
- sym.Read(rr, rr.SymOff(i))
- return SymID{sym.Name, abiToVer(sym.ABI)}
+ sym := rr.Sym2(i)
+ return SymID{sym.Name(rr), abiToVer(sym.ABI())}
}
// Read things for the current goobj API for now.
@@ -69,16 +68,15 @@ func (r *objReader) readNew() {
n := rr.NSym() + rr.NNonpkgdef() + rr.NNonpkgref()
ndef := rr.NSym() + rr.NNonpkgdef()
for i := 0; i < n; i++ {
- osym := goobj2.Sym{}
- osym.Read(rr, rr.SymOff(i))
- if osym.Name == "" {
+ osym := rr.Sym2(i)
+ if osym.Name(rr) == "" {
continue // not a real symbol
}
// In a symbol name in an object file, "". denotes the
// prefix for the package in which the object file has been found.
// Expand it.
- name := strings.ReplaceAll(osym.Name, `"".`, r.pkgprefix)
- symID := SymID{Name: name, Version: abiToVer(osym.ABI)}
+ name := strings.ReplaceAll(osym.Name(rr), `"".`, r.pkgprefix)
+ symID := SymID{Name: name, Version: abiToVer(osym.ABI())}
r.p.SymRefs = append(r.p.SymRefs, symID)
if i >= ndef {
@@ -91,45 +89,43 @@ func (r *objReader) readNew() {
sym := Sym{
SymID: symID,
- Kind: objabi.SymKind(osym.Type),
+ Kind: objabi.SymKind(osym.Type()),
DupOK: osym.Dupok(),
- Size: int64(osym.Siz),
+ Size: int64(osym.Siz()),
Data: Data{int64(start + dataOff), siz},
}
r.p.Syms = append(r.p.Syms, &sym)
// Reloc
- nreloc := rr.NReloc(i)
- sym.Reloc = make([]Reloc, nreloc)
- for j := 0; j < nreloc; j++ {
- rel := goobj2.Reloc{}
- rel.Read(rr, rr.RelocOff(i, j))
+ relocs := rr.Relocs2(i)
+ sym.Reloc = make([]Reloc, len(relocs))
+ for j := range relocs {
+ rel := &relocs[j]
sym.Reloc[j] = Reloc{
- Offset: int64(rel.Off),
- Size: int64(rel.Siz),
- Type: objabi.RelocType(rel.Type),
- Add: rel.Add,
- Sym: resolveSymRef(rel.Sym),
+ Offset: int64(rel.Off()),
+ Size: int64(rel.Siz()),
+ Type: objabi.RelocType(rel.Type()),
+ Add: rel.Add(),
+ Sym: resolveSymRef(rel.Sym()),
}
}
// Aux symbol info
isym := -1
funcdata := make([]goobj2.SymRef, 0, 4)
- naux := rr.NAux(i)
- for j := 0; j < naux; j++ {
- a := goobj2.Aux{}
- a.Read(rr, rr.AuxOff(i, j))
- switch a.Type {
+ auxs := rr.Auxs2(i)
+ for j := range auxs {
+ a := &auxs[j]
+ switch a.Type() {
case goobj2.AuxGotype:
- sym.Type = resolveSymRef(a.Sym)
+ sym.Type = resolveSymRef(a.Sym())
case goobj2.AuxFuncInfo:
- if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
+ if a.Sym().PkgIdx != goobj2.PkgIdxSelf {
panic("funcinfo symbol not defined in current package")
}
- isym = int(a.Sym.SymIdx)
+ isym = int(a.Sym().SymIdx)
case goobj2.AuxFuncdata:
- funcdata = append(funcdata, a.Sym)
+ funcdata = append(funcdata, a.Sym())
case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
// nothing to do
default:
@@ -149,7 +145,7 @@ func (r *objReader) readNew() {
f := &Func{
Args: int64(info.Args),
Frame: int64(info.Locals),
- NoSplit: info.NoSplit != 0,
+ NoSplit: osym.NoSplit(),
Leaf: osym.Leaf(),
TopFrame: osym.TopFrame(),
PCSP: Data{int64(pcdataBase + info.Pcsp), int64(info.Pcfile - info.Pcsp)},
diff --git a/src/cmd/internal/goobj2/funcinfo.go b/src/cmd/internal/goobj2/funcinfo.go
index 8620931970..053d7adc15 100644
--- a/src/cmd/internal/goobj2/funcinfo.go
+++ b/src/cmd/internal/goobj2/funcinfo.go
@@ -14,8 +14,6 @@ import (
//
// TODO: make each pcdata a separate symbol?
type FuncInfo struct {
- NoSplit uint8
-
Args uint32
Locals uint32
@@ -32,8 +30,6 @@ type FuncInfo struct {
}
func (a *FuncInfo) Write(w *bytes.Buffer) {
- w.WriteByte(a.NoSplit)
-
var b [4]byte
writeUint32 := func(x uint32) {
binary.LittleEndian.PutUint32(b[:], x)
@@ -68,9 +64,6 @@ func (a *FuncInfo) Write(w *bytes.Buffer) {
}
func (a *FuncInfo) Read(b []byte) {
- a.NoSplit = b[0]
- b = b[1:]
-
readUint32 := func() uint32 {
x := binary.LittleEndian.Uint32(b)
b = b[4:]
@@ -107,6 +100,16 @@ func (a *FuncInfo) Read(b []byte) {
}
}
+// Accessors reading only some fields.
+// TODO: more accessors.
+
+func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) }
+
+// return start and end offsets.
+func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) {
+ return binary.LittleEndian.Uint32(b[8:]), binary.LittleEndian.Uint32(b[12:])
+}
+
// InlTreeNode is the serialized form of FileInfo.InlTree.
type InlTreeNode struct {
Parent int32
diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go
index 4c364b0c54..52544bf773 100644
--- a/src/cmd/internal/goobj2/objfile.go
+++ b/src/cmd/internal/goobj2/objfile.go
@@ -19,24 +19,23 @@ import (
// New object file format.
//
// Header struct {
-// Magic [...]byte // "\x00go114LD"
+// Magic [...]byte // "\x00go115ld"
// Flags uint32
// // TODO: Fingerprint
// Offsets [...]uint32 // byte offset of each block below
// }
//
// Strings [...]struct {
-// Len uint32
// Data [...]byte
// }
//
-// Autolib [...]stringOff // imported packages (for file loading) // TODO: add fingerprints
-// PkgIndex [...]stringOff // referenced packages by index
+// Autolib [...]string // imported packages (for file loading) // TODO: add fingerprints
+// PkgIndex [...]string // referenced packages by index
//
-// DwarfFiles [...]stringOff
+// DwarfFiles [...]string
//
// SymbolDefs [...]struct {
-// Name stringOff
+// Name string
// ABI uint16
// Type uint8
// Flag uint8
@@ -69,8 +68,8 @@ import (
// Data [...]byte
// Pcdata [...]byte
//
-// stringOff is a uint32 (?) offset that points to the corresponding
-// string, which is a uint32 length followed by that number of bytes.
+// string is encoded as is a uint32 length followed by a uint32 offset
+// that points to the corresponding string bytes.
//
// symRef is struct { PkgIdx, SymIdx uint32 }.
//
@@ -118,6 +117,8 @@ import (
// Currently a symbol's Gotype and FuncInfo are auxiliary symbols. We
// may make use of aux symbols in more cases, e.g. DWARF symbols.
+const stringRefSize = 8 // two uint32s
+
// Package Index.
const (
PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols
@@ -153,7 +154,7 @@ type Header struct {
Offsets [NBlk]uint32
}
-const Magic = "\x00go114LD"
+const Magic = "\x00go115ld"
func (h *Header) Write(w *Writer) {
w.RawString(h.Magic)
@@ -185,11 +186,12 @@ func (h *Header) Size() int {
// Symbol definition.
type Sym struct {
- Name string
- ABI uint16
- Type uint8
- Flag uint8
- Siz uint32
+ Name string
+ ABI uint16
+ Type uint8
+ Flag uint8
+ Siz uint32
+ Align uint32
}
const SymABIstatic = ^uint16(0)
@@ -203,7 +205,7 @@ const (
SymFlagLocal
SymFlagTypelink
SymFlagLeaf
- SymFlagCFunc
+ SymFlagNoSplit
SymFlagReflectMethod
SymFlagGoType
SymFlagTopFrame
@@ -215,28 +217,33 @@ func (s *Sym) Write(w *Writer) {
w.Uint8(s.Type)
w.Uint8(s.Flag)
w.Uint32(s.Siz)
+ w.Uint32(s.Align)
}
-func (s *Sym) Read(r *Reader, off uint32) {
- s.Name = r.StringRef(off)
- s.ABI = r.uint16At(off + 4)
- s.Type = r.uint8At(off + 6)
- s.Flag = r.uint8At(off + 7)
- s.Siz = r.uint32At(off + 8)
-}
+const SymSize = stringRefSize + 2 + 1 + 1 + 4 + 4
+
+type Sym2 [SymSize]byte
-func (s *Sym) Size() int {
- return 4 + 2 + 1 + 1 + 4
+func (s *Sym2) Name(r *Reader) string {
+ len := binary.LittleEndian.Uint32(s[:])
+ off := binary.LittleEndian.Uint32(s[4:])
+ return r.StringAt(off, len)
}
-func (s *Sym) Dupok() bool { return s.Flag&SymFlagDupok != 0 }
-func (s *Sym) Local() bool { return s.Flag&SymFlagLocal != 0 }
-func (s *Sym) Typelink() bool { return s.Flag&SymFlagTypelink != 0 }
-func (s *Sym) Leaf() bool { return s.Flag&SymFlagLeaf != 0 }
-func (s *Sym) CFunc() bool { return s.Flag&SymFlagCFunc != 0 }
-func (s *Sym) ReflectMethod() bool { return s.Flag&SymFlagReflectMethod != 0 }
-func (s *Sym) IsGoType() bool { return s.Flag&SymFlagGoType != 0 }
-func (s *Sym) TopFrame() bool { return s.Flag&SymFlagTopFrame != 0 }
+func (s *Sym2) ABI() uint16 { return binary.LittleEndian.Uint16(s[8:]) }
+func (s *Sym2) Type() uint8 { return s[10] }
+func (s *Sym2) Flag() uint8 { return s[11] }
+func (s *Sym2) Siz() uint32 { return binary.LittleEndian.Uint32(s[12:]) }
+func (s *Sym2) Align() uint32 { return binary.LittleEndian.Uint32(s[16:]) }
+
+func (s *Sym2) Dupok() bool { return s.Flag()&SymFlagDupok != 0 }
+func (s *Sym2) Local() bool { return s.Flag()&SymFlagLocal != 0 }
+func (s *Sym2) Typelink() bool { return s.Flag()&SymFlagTypelink != 0 }
+func (s *Sym2) Leaf() bool { return s.Flag()&SymFlagLeaf != 0 }
+func (s *Sym2) NoSplit() bool { return s.Flag()&SymFlagNoSplit != 0 }
+func (s *Sym2) ReflectMethod() bool { return s.Flag()&SymFlagReflectMethod != 0 }
+func (s *Sym2) IsGoType() bool { return s.Flag()&SymFlagGoType != 0 }
+func (s *Sym2) TopFrame() bool { return s.Flag()&SymFlagTopFrame != 0 }
// Symbol reference.
type SymRef struct {
@@ -249,15 +256,6 @@ func (s *SymRef) Write(w *Writer) {
w.Uint32(s.SymIdx)
}
-func (s *SymRef) Read(r *Reader, off uint32) {
- s.PkgIdx = r.uint32At(off)
- s.SymIdx = r.uint32At(off + 4)
-}
-
-func (s *SymRef) Size() int {
- return 4 + 4
-}
-
// Relocation.
type Reloc struct {
Off int32
@@ -275,16 +273,33 @@ func (r *Reloc) Write(w *Writer) {
r.Sym.Write(w)
}
-func (o *Reloc) Read(r *Reader, off uint32) {
- o.Off = r.int32At(off)
- o.Siz = r.uint8At(off + 4)
- o.Type = r.uint8At(off + 5)
- o.Add = r.int64At(off + 6)
- o.Sym.Read(r, off+14)
+const RelocSize = 4 + 1 + 1 + 8 + 8
+
+type Reloc2 [RelocSize]byte
+
+func (r *Reloc2) Off() int32 { return int32(binary.LittleEndian.Uint32(r[:])) }
+func (r *Reloc2) Siz() uint8 { return r[4] }
+func (r *Reloc2) Type() uint8 { return r[5] }
+func (r *Reloc2) Add() int64 { return int64(binary.LittleEndian.Uint64(r[6:])) }
+func (r *Reloc2) Sym() SymRef {
+ return SymRef{binary.LittleEndian.Uint32(r[14:]), binary.LittleEndian.Uint32(r[18:])}
+}
+
+func (r *Reloc2) SetOff(x int32) { binary.LittleEndian.PutUint32(r[:], uint32(x)) }
+func (r *Reloc2) SetSiz(x uint8) { r[4] = x }
+func (r *Reloc2) SetType(x uint8) { r[5] = x }
+func (r *Reloc2) SetAdd(x int64) { binary.LittleEndian.PutUint64(r[6:], uint64(x)) }
+func (r *Reloc2) SetSym(x SymRef) {
+ binary.LittleEndian.PutUint32(r[14:], x.PkgIdx)
+ binary.LittleEndian.PutUint32(r[18:], x.SymIdx)
}
-func (r *Reloc) Size() int {
- return 4 + 1 + 1 + 8 + r.Sym.Size()
+func (r *Reloc2) Set(off int32, size uint8, typ uint8, add int64, sym SymRef) {
+ r.SetOff(off)
+ r.SetSiz(size)
+ r.SetType(typ)
+ r.SetAdd(add)
+ r.SetSym(sym)
}
// Aux symbol info.
@@ -311,13 +326,13 @@ func (a *Aux) Write(w *Writer) {
a.Sym.Write(w)
}
-func (a *Aux) Read(r *Reader, off uint32) {
- a.Type = r.uint8At(off)
- a.Sym.Read(r, off+1)
-}
+const AuxSize = 1 + 8
-func (a *Aux) Size() int {
- return 1 + a.Sym.Size()
+type Aux2 [AuxSize]byte
+
+func (a *Aux2) Type() uint8 { return a[0] }
+func (a *Aux2) Sym() SymRef {
+ return SymRef{binary.LittleEndian.Uint32(a[1:]), binary.LittleEndian.Uint32(a[5:])}
}
type Writer struct {
@@ -335,7 +350,6 @@ func (w *Writer) AddString(s string) {
return
}
w.stringMap[s] = w.off
- w.Uint32(uint32(len(s)))
w.RawString(s)
}
@@ -344,6 +358,7 @@ func (w *Writer) StringRef(s string) {
if !ok {
panic(fmt.Sprintf("writeStringRef: string not added: %q", s))
}
+ w.Uint32(uint32(len(s)))
w.Uint32(off)
}
@@ -441,9 +456,8 @@ func (r *Reader) uint8At(off uint32) uint8 {
return b[0]
}
-func (r *Reader) StringAt(off uint32) string {
- l := r.uint32At(off)
- b := r.b[off+4 : off+4+l]
+func (r *Reader) StringAt(off uint32, len uint32) string {
+ b := r.b[off : off+len]
if r.readonly {
return toString(b) // backed by RO memory, ok to make unsafe string
}
@@ -465,66 +479,69 @@ func toString(b []byte) string {
}
func (r *Reader) StringRef(off uint32) string {
- return r.StringAt(r.uint32At(off))
+ l := r.uint32At(off)
+ return r.StringAt(r.uint32At(off+4), l)
}
func (r *Reader) Autolib() []string {
- n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / 4
+ n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / stringRefSize
s := make([]string, n)
for i := range s {
- off := r.h.Offsets[BlkAutolib] + uint32(i)*4
+ off := r.h.Offsets[BlkAutolib] + uint32(i)*stringRefSize
s[i] = r.StringRef(off)
}
return s
}
func (r *Reader) Pkglist() []string {
- n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / 4
+ n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / stringRefSize
s := make([]string, n)
for i := range s {
- off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4
+ off := r.h.Offsets[BlkPkgIdx] + uint32(i)*stringRefSize
s[i] = r.StringRef(off)
}
return s
}
func (r *Reader) NPkg() int {
- return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / 4
+ return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / stringRefSize
}
func (r *Reader) Pkg(i int) string {
- off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4
+ off := r.h.Offsets[BlkPkgIdx] + uint32(i)*stringRefSize
return r.StringRef(off)
}
func (r *Reader) NDwarfFile() int {
- return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / 4
+ return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / stringRefSize
}
func (r *Reader) DwarfFile(i int) string {
- off := r.h.Offsets[BlkDwarfFile] + uint32(i)*4
+ off := r.h.Offsets[BlkDwarfFile] + uint32(i)*stringRefSize
return r.StringRef(off)
}
func (r *Reader) NSym() int {
- symsiz := (&Sym{}).Size()
- return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / symsiz
+ return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / SymSize
}
func (r *Reader) NNonpkgdef() int {
- symsiz := (&Sym{}).Size()
- return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / symsiz
+ return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / SymSize
}
func (r *Reader) NNonpkgref() int {
- symsiz := (&Sym{}).Size()
- return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / symsiz
+ return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / SymSize
}
// SymOff returns the offset of the i-th symbol.
func (r *Reader) SymOff(i int) uint32 {
- symsiz := (&Sym{}).Size()
- return r.h.Offsets[BlkSymdef] + uint32(i*symsiz)
+ return r.h.Offsets[BlkSymdef] + uint32(i*SymSize)
+}
+
+// Sym2 returns a pointer to the i-th symbol.
+func (r *Reader) Sym2(i int) *Sym2 {
+ off := r.SymOff(i)
+ return (*Sym2)(unsafe.Pointer(&r.b[off]))
}
// NReloc returns the number of relocations of the i-th symbol.
@@ -537,8 +554,20 @@ func (r *Reader) NReloc(i int) int {
func (r *Reader) RelocOff(i int, j int) uint32 {
relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
relocIdx := r.uint32At(relocIdxOff)
- relocsiz := (&Reloc{}).Size()
- return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(relocsiz)
+ return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(RelocSize)
+}
+
+// Reloc2 returns a pointer to the j-th relocation of the i-th symbol.
+func (r *Reader) Reloc2(i int, j int) *Reloc2 {
+ off := r.RelocOff(i, j)
+ return (*Reloc2)(unsafe.Pointer(&r.b[off]))
+}
+
+// Relocs2 returns a pointer to the relocations of the i-th symbol.
+func (r *Reader) Relocs2(i int) []Reloc2 {
+ off := r.RelocOff(i, 0)
+ n := r.NReloc(i)
+ return (*[1 << 20]Reloc2)(unsafe.Pointer(&r.b[off]))[:n:n]
}
// NAux returns the number of aux symbols of the i-th symbol.
@@ -551,8 +580,20 @@ func (r *Reader) NAux(i int) int {
func (r *Reader) AuxOff(i int, j int) uint32 {
auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4)
auxIdx := r.uint32At(auxIdxOff)
- auxsiz := (&Aux{}).Size()
- return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(auxsiz)
+ return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(AuxSize)
+}
+
+// Aux2 returns a pointer to the j-th aux symbol of the i-th symbol.
+func (r *Reader) Aux2(i int, j int) *Aux2 {
+ off := r.AuxOff(i, j)
+ return (*Aux2)(unsafe.Pointer(&r.b[off]))
+}
+
+// Auxs2 returns the aux symbols of the i-th symbol.
+func (r *Reader) Auxs2(i int) []Aux2 {
+ off := r.AuxOff(i, 0)
+ n := r.NAux(i)
+ return (*[1 << 20]Aux2)(unsafe.Pointer(&r.b[off]))[:n:n]
}
// DataOff returns the offset of the i-th symbol's data.
@@ -563,12 +604,17 @@ func (r *Reader) DataOff(i int) uint32 {
// DataSize returns the size of the i-th symbol's data.
func (r *Reader) DataSize(i int) int {
- return int(r.DataOff(i+1) - r.DataOff(i))
+ dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4)
+ return int(r.uint32At(dataIdxOff+4) - r.uint32At(dataIdxOff))
}
// Data returns the i-th symbol's data.
func (r *Reader) Data(i int) []byte {
- return r.BytesAt(r.DataOff(i), r.DataSize(i))
+ dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4)
+ base := r.h.Offsets[BlkData]
+ off := r.uint32At(dataIdxOff)
+ end := r.uint32At(dataIdxOff + 4)
+ return r.BytesAt(base+off, int(end-off))
}
// AuxDataBase returns the base offset of the aux data block.
diff --git a/src/cmd/internal/goobj2/objfile_test.go b/src/cmd/internal/goobj2/objfile_test.go
new file mode 100644
index 0000000000..ee15136cbe
--- /dev/null
+++ b/src/cmd/internal/goobj2/objfile_test.go
@@ -0,0 +1,39 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package goobj2
+
+import (
+ "bufio"
+ "bytes"
+ "cmd/internal/bio"
+ "testing"
+)
+
+func dummyWriter() *Writer {
+ var buf bytes.Buffer
+ wr := &bio.Writer{Writer: bufio.NewWriter(&buf)} // hacky: no file, so cannot seek
+ return NewWriter(wr)
+}
+
+func TestSize(t *testing.T) {
+ // This test checks that hard-coded sizes match the actual sizes
+ // in the object file format.
+ tests := []struct {
+ x interface{ Write(*Writer) }
+ want uint32
+ }{
+ {&Reloc{}, RelocSize},
+ {&Aux{}, AuxSize},
+ }
+ w := dummyWriter()
+ for _, test := range tests {
+ off0 := w.off
+ test.x.Write(w)
+ got := w.off - off0
+ if got != test.want {
+ t.Errorf("size(%T) mismatch: %d bytes written, but size=%d", test.x, got, test.want)
+ }
+ }
+}
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 0879c611ba..ac3621bf74 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -653,7 +653,7 @@ type Link struct {
Flag_linkshared bool
Flag_optimize bool
Flag_locationlists bool
- Flag_newobj bool // use new object file format
+ Flag_go115newobj bool // use new object file format
Retpoline bool // emit use of retpoline stubs for indirect jmp/call
Bso *bufio.Writer
Pathname string
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index 46e8a551ad..cb6b709066 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -82,7 +82,7 @@ func newObjWriter(ctxt *Link, b *bufio.Writer, pkgpath string) *objWriter {
}
func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) {
- if ctxt.Flag_newobj {
+ if ctxt.Flag_go115newobj {
WriteObjFile2(ctxt, bout, pkgpath)
return
}
@@ -229,7 +229,11 @@ func (w *objWriter) writeRefs(s *LSym) {
}
func (ctxt *Link) writeSymDebug(s *LSym) {
- fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
+ ctxt.writeSymDebugNamed(s, s.Name)
+}
+
+func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
+ fmt.Fprintf(ctxt.Bso, "%s ", name)
if s.Type != 0 {
fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
}
@@ -500,12 +504,21 @@ func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64)
r := &ls.R[len(ls.R)-1]
r.Type = objabi.R_DWARFSECREF
}
+
func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) {
ls := s.(*LSym)
rsym := f.(*LSym)
- ls.WriteAddr(c.Link, ls.Size, 4, rsym, 0)
- r := &ls.R[len(ls.R)-1]
- r.Type = objabi.R_DWARFFILEREF
+ if c.Link.Flag_go115newobj {
+ fidx := c.Link.PosTable.FileIndex(rsym.Name)
+ // Note the +1 here -- the value we're writing is going to be an
+ // index into the DWARF line table file section, whose entries
+ // are numbered starting at 1, not 0.
+ ls.WriteInt(c.Link, ls.Size, 4, int64(fidx+1))
+ } else {
+ ls.WriteAddr(c.Link, ls.Size, 4, rsym, 0)
+ r := &ls.R[len(ls.R)-1]
+ r.Type = objabi.R_DWARFFILEREF
+ }
}
func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 {
@@ -543,20 +556,37 @@ func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym,
ctxt.Diag("dwarfSym of non-TEXT %v", s)
}
if s.Func.dwarfInfoSym == nil {
- s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
- if ctxt.Flag_locationlists {
- s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name)
+ if ctxt.Flag_go115newobj {
+ s.Func.dwarfInfoSym = &LSym{
+ Type: objabi.SDWARFINFO,
+ }
+ if ctxt.Flag_locationlists {
+ s.Func.dwarfLocSym = &LSym{
+ Type: objabi.SDWARFLOC,
+ }
+ }
+ s.Func.dwarfRangesSym = &LSym{
+ Type: objabi.SDWARFRANGE,
+ }
+ s.Func.dwarfDebugLinesSym = &LSym{
+ Type: objabi.SDWARFLINES,
+ }
+ } else {
+ s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
+ if ctxt.Flag_locationlists {
+ s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name)
+ }
+ s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name)
+ s.Func.dwarfDebugLinesSym = ctxt.LookupDerived(s, dwarf.DebugLinesPrefix+s.Name)
}
- s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name)
if s.WasInlined() {
s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s)
}
- s.Func.dwarfDebugLinesSym = ctxt.LookupDerived(s, dwarf.DebugLinesPrefix+s.Name)
}
return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym
}
-func (s *LSym) Len() int64 {
+func (s *LSym) Length(dwarfContext interface{}) int64 {
return s.Size
}
diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go
index 69019e033d..6261924d0d 100644
--- a/src/cmd/internal/obj/objfile2.go
+++ b/src/cmd/internal/obj/objfile2.go
@@ -18,9 +18,8 @@ import (
// Entry point of writing new object file.
func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) {
- if ctxt.Debugasm > 0 {
- ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
- }
+
+ debugAsmEmit(ctxt)
genFuncInfoSyms(ctxt)
@@ -60,7 +59,7 @@ func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) {
// DWARF file table
h.Offsets[goobj2.BlkDwarfFile] = w.Offset()
for _, f := range ctxt.PosTable.DebugLinesFileTable() {
- w.StringRef(f)
+ w.StringRef(filepath.ToSlash(f))
}
// Symbol definitions
@@ -207,7 +206,7 @@ func (w *writer) StringTable() {
}
})
for _, f := range w.ctxt.PosTable.DebugLinesFileTable() {
- w.AddString(f)
+ w.AddString(filepath.ToSlash(f))
}
}
@@ -229,8 +228,8 @@ func (w *writer) Sym(s *LSym) {
if s.Leaf() {
flag |= goobj2.SymFlagLeaf
}
- if s.CFunc() {
- flag |= goobj2.SymFlagCFunc
+ if s.NoSplit() {
+ flag |= goobj2.SymFlagNoSplit
}
if s.ReflectMethod() {
flag |= goobj2.SymFlagReflectMethod
@@ -245,12 +244,17 @@ func (w *writer) Sym(s *LSym) {
if strings.HasPrefix(name, "gofile..") {
name = filepath.ToSlash(name)
}
+ var align uint32
+ if s.Func != nil {
+ align = uint32(s.Func.Align)
+ }
o := goobj2.Sym{
- Name: name,
- ABI: abi,
- Type: uint8(s.Type),
- Flag: flag,
- Siz: uint32(s.Size),
+ Name: name,
+ ABI: abi,
+ Type: uint8(s.Type),
+ Flag: flag,
+ Siz: uint32(s.Size),
+ Align: align,
}
o.Write(w.Writer)
}
@@ -300,28 +304,28 @@ func (w *writer) Aux(s *LSym) {
o.Write(w.Writer)
}
- if s.Func.dwarfInfoSym != nil {
+ if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 {
o := goobj2.Aux{
Type: goobj2.AuxDwarfInfo,
Sym: makeSymRef(s.Func.dwarfInfoSym),
}
o.Write(w.Writer)
}
- if s.Func.dwarfLocSym != nil {
+ if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
o := goobj2.Aux{
Type: goobj2.AuxDwarfLoc,
Sym: makeSymRef(s.Func.dwarfLocSym),
}
o.Write(w.Writer)
}
- if s.Func.dwarfRangesSym != nil {
+ if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
o := goobj2.Aux{
Type: goobj2.AuxDwarfRanges,
Sym: makeSymRef(s.Func.dwarfRangesSym),
}
o.Write(w.Writer)
}
- if s.Func.dwarfDebugLinesSym != nil {
+ if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
o := goobj2.Aux{
Type: goobj2.AuxDwarfLines,
Sym: makeSymRef(s.Func.dwarfDebugLinesSym),
@@ -340,16 +344,16 @@ func nAuxSym(s *LSym) int {
if s.Func != nil {
// FuncInfo is an aux symbol, each Funcdata is an aux symbol
n += 1 + len(s.Func.Pcln.Funcdata)
- if s.Func.dwarfInfoSym != nil {
+ if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 {
n++
}
- if s.Func.dwarfLocSym != nil {
+ if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
n++
}
- if s.Func.dwarfRangesSym != nil {
+ if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
n++
}
- if s.Func.dwarfDebugLinesSym != nil {
+ if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
n++
}
}
@@ -366,14 +370,9 @@ func genFuncInfoSyms(ctxt *Link) {
if s.Func == nil {
continue
}
- nosplit := uint8(0)
- if s.NoSplit() {
- nosplit = 1
- }
o := goobj2.FuncInfo{
- NoSplit: nosplit,
- Args: uint32(s.Func.Args),
- Locals: uint32(s.Func.Locals),
+ Args: uint32(s.Func.Args),
+ Locals: uint32(s.Func.Locals),
}
pc := &s.Func.Pcln
o.Pcsp = pcdataoff
@@ -424,6 +423,43 @@ func genFuncInfoSyms(ctxt *Link) {
infosyms = append(infosyms, isym)
s.Func.FuncInfoSym = isym
b.Reset()
+
+ dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym, s.Func.dwarfInfoSym}
+ for _, s := range dwsyms {
+ if s == nil || s.Size == 0 {
+ continue
+ }
+ s.PkgIdx = goobj2.PkgIdxSelf
+ s.SymIdx = symidx
+ s.Set(AttrIndexed, true)
+ symidx++
+ infosyms = append(infosyms, s)
+ }
}
ctxt.defs = append(ctxt.defs, infosyms...)
}
+
+// debugDumpAux is a dumper for selected aux symbols.
+func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) {
+ // Most aux symbols (ex: funcdata) are not interesting--
+ // pick out just the DWARF ones for now.
+ if aux.Type != objabi.SDWARFLOC &&
+ aux.Type != objabi.SDWARFINFO &&
+ aux.Type != objabi.SDWARFLINES &&
+ aux.Type != objabi.SDWARFRANGE {
+ return
+ }
+ ctxt.writeSymDebugNamed(aux, "aux for "+par.Name)
+}
+
+func debugAsmEmit(ctxt *Link) {
+ if ctxt.Debugasm > 0 {
+ ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
+ if ctxt.Debugasm > 1 {
+ fn := func(par *LSym, aux *LSym) {
+ writeAuxSymDebug(ctxt, par, aux)
+ }
+ ctxt.traverseAuxSyms(traverseAux, fn)
+ }
+ }
+}
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
index 7579dd0390..44ec4602de 100644
--- a/src/cmd/internal/obj/plist.go
+++ b/src/cmd/internal/obj/plist.go
@@ -109,7 +109,9 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string
continue
}
linkpcln(ctxt, s)
- ctxt.populateDWARF(plist.Curfn, s, myimportpath)
+ if myimportpath != "" {
+ ctxt.populateDWARF(plist.Curfn, s, myimportpath)
+ }
}
}
@@ -136,21 +138,27 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
s.Type = objabi.STEXT
ctxt.Text = append(ctxt.Text, s)
- // Set up DWARF entries for s.
+ // Set up DWARF entries for s
info, loc, ranges, _, lines := ctxt.dwarfSym(s)
- info.Type = objabi.SDWARFINFO
- info.Set(AttrDuplicateOK, s.DuplicateOK())
- if loc != nil {
- loc.Type = objabi.SDWARFLOC
- loc.Set(AttrDuplicateOK, s.DuplicateOK())
- ctxt.Data = append(ctxt.Data, loc)
+
+ // When using new object files, the DWARF symbols are unnamed aux
+ // symbols and don't need to be added to ctxt.Data.
+ // But the old object file still needs them.
+ if !ctxt.Flag_go115newobj {
+ info.Type = objabi.SDWARFINFO
+ info.Set(AttrDuplicateOK, s.DuplicateOK())
+ if loc != nil {
+ loc.Type = objabi.SDWARFLOC
+ loc.Set(AttrDuplicateOK, s.DuplicateOK())
+ ctxt.Data = append(ctxt.Data, loc)
+ }
+ ranges.Type = objabi.SDWARFRANGE
+ ranges.Set(AttrDuplicateOK, s.DuplicateOK())
+ ctxt.Data = append(ctxt.Data, info, ranges)
+ lines.Type = objabi.SDWARFLINES
+ lines.Set(AttrDuplicateOK, s.DuplicateOK())
+ ctxt.Data = append(ctxt.Data, lines)
}
- ranges.Type = objabi.SDWARFRANGE
- ranges.Set(AttrDuplicateOK, s.DuplicateOK())
- ctxt.Data = append(ctxt.Data, info, ranges)
- lines.Type = objabi.SDWARFLINES
- lines.Set(AttrDuplicateOK, s.DuplicateOK())
- ctxt.Data = append(ctxt.Data, lines)
}
func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go
index 3ef886651f..03ce8ddc5a 100644
--- a/src/cmd/internal/obj/sym.go
+++ b/src/cmd/internal/obj/sym.go
@@ -164,7 +164,7 @@ func (ctxt *Link) Int64Sym(i int64) *LSym {
// asm is set to true if this is called by the assembler (i.e. not the compiler),
// in which case all the symbols are non-package (for now).
func (ctxt *Link) NumberSyms(asm bool) {
- if !ctxt.Flag_newobj {
+ if !ctxt.Flag_go115newobj {
return
}
@@ -298,28 +298,72 @@ func (ctxt *Link) traverseSyms(flag traverseFlag, fn func(*LSym)) {
fn(s.Gotype)
}
if s.Type == objabi.STEXT {
- pc := &s.Func.Pcln
- for _, d := range pc.Funcdata {
- if d != nil {
- fn(d)
- }
- }
- for _, f := range pc.File {
- if fsym := ctxt.Lookup(f); fsym != nil {
- fn(fsym)
- }
- }
- for _, call := range pc.InlTree.nodes {
- if call.Func != nil {
- fn(call.Func)
- }
- f, _ := linkgetlineFromPos(ctxt, call.Pos)
- if fsym := ctxt.Lookup(f); fsym != nil {
- fn(fsym)
- }
+ f := func(parent *LSym, aux *LSym) {
+ fn(aux)
}
+ ctxt.traverseFuncAux(flag, s, f)
}
}
}
}
}
+
+func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent *LSym, aux *LSym)) {
+ pc := &fsym.Func.Pcln
+ if flag&traverseAux == 0 {
+ // NB: should it become necessary to walk aux sym reloc references
+ // without walking the aux syms themselves, this can be changed.
+ panic("should not be here")
+ }
+ for _, d := range pc.Funcdata {
+ if d != nil {
+ fn(fsym, d)
+ }
+ }
+ for _, f := range pc.File {
+ if filesym := ctxt.Lookup(f); filesym != nil {
+ fn(fsym, filesym)
+ }
+ }
+ for _, call := range pc.InlTree.nodes {
+ if call.Func != nil {
+ fn(fsym, call.Func)
+ }
+ f, _ := linkgetlineFromPos(ctxt, call.Pos)
+ if filesym := ctxt.Lookup(f); filesym != nil {
+ fn(fsym, filesym)
+ }
+ }
+ dwsyms := []*LSym{fsym.Func.dwarfRangesSym, fsym.Func.dwarfLocSym, fsym.Func.dwarfDebugLinesSym, fsym.Func.dwarfInfoSym}
+ for _, dws := range dwsyms {
+ if dws == nil || dws.Size == 0 {
+ continue
+ }
+ fn(fsym, dws)
+ if flag&traverseRefs != 0 {
+ for _, r := range dws.R {
+ if r.Sym != nil {
+ fn(dws, r.Sym)
+ }
+ }
+ }
+ }
+}
+
+// Traverse aux symbols, calling fn for each sym/aux pair.
+func (ctxt *Link) traverseAuxSyms(flag traverseFlag, fn func(parent *LSym, aux *LSym)) {
+ lists := [][]*LSym{ctxt.Text, ctxt.Data, ctxt.ABIAliases}
+ for _, list := range lists {
+ for _, s := range list {
+ if s.Gotype != nil {
+ if flag&traverseDefs != 0 {
+ fn(s, s.Gotype)
+ }
+ }
+ if s.Type != objabi.STEXT {
+ continue
+ }
+ ctxt.traverseFuncAux(flag, s, fn)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
index 26208cc619..cb1210422d 100644
--- a/src/cmd/link/internal/amd64/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -34,9 +34,11 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"log"
+ "sync"
)
func PADDR(x uint32) uint32 {
@@ -98,13 +100,21 @@ func gentext(ctxt *ld.Link) {
initarray_entry.AddAddr(ctxt.Arch, initfunc)
}
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+// makeWritable makes a readonly symbol writable if we do opcode rewriting.
+func makeWritable(s *sym.Symbol) {
+ if s.Attr.ReadOnly() {
+ s.Attr.Set(sym.AttrReadOnly, false)
+ s.P = append([]byte(nil), s.P...)
+ }
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, 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))
+ ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type))
return false
}
@@ -137,8 +147,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
r.Type = objabi.R_PCREL
r.Add += 4
if targ.Type == sym.SDYNIMPORT {
- addpltsym(ctxt, targ)
- r.Sym = ctxt.Syms.Lookup(".plt", 0)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add += int64(targ.Plt())
}
@@ -150,6 +160,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
if targ.Type != sym.SDYNIMPORT {
// have symbol
if r.Off >= 2 && s.P[r.Off-2] == 0x8b {
+ makeWritable(s)
// turn MOVQ of GOT entry into LEAQ of symbol itself
s.P[r.Off-2] = 0x8d
@@ -161,10 +172,10 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
// fall back to using GOT and hope for the best (CMOV*)
// TODO: just needs relocation, no need to put in .dynsym
- addgotsym(ctxt, targ)
+ addgotsym(target, syms, targ)
r.Type = objabi.R_PCREL
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += 4
r.Add += int64(targ.Got())
return true
@@ -174,7 +185,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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 {
+ if target.IsPIE() && target.IsInternal() {
// For internal linking PIE, this R_ADDR relocation cannot
// be resolved statically. We need to generate a dynamic
// relocation. Let the code below handle it.
@@ -196,8 +207,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add = int64(targ.Plt())
r.Type = objabi.R_PCREL
return true
@@ -225,6 +236,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
return false
}
+ makeWritable(s)
s.P[r.Off-2] = 0x8d
r.Type = objabi.R_PCREL
return true
@@ -235,9 +247,9 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
if targ.Type != sym.SDYNIMPORT {
ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
}
- addgotsym(ctxt, targ)
+ addgotsym(target, syms, targ)
r.Type = objabi.R_PCREL
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += int64(targ.Got())
return true
}
@@ -249,37 +261,37 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
// nothing to do, the relocation will be laid out in reloc
return true
}
- if ctxt.LinkMode == ld.LinkExternal {
+ if target.IsExternal() {
// External linker will do this relocation.
return true
}
// Internal linking, for both ELF and Mach-O.
// Build a PLT entry and change the relocation target to that entry.
- addpltsym(ctxt, targ)
- r.Sym = ctxt.Syms.Lookup(".plt", 0)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
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)
+ if s.Type == sym.STEXT && target.IsElf() {
+ if target.IsSolaris() {
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
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)
+ addgotsym(target, syms, targ)
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += int64(targ.Got())
return true
}
// Process dynamic relocations for the data sections.
- if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal {
+ if target.IsPIE() && target.IsInternal() {
// When internally linking, generate dynamic relocations
// for all typical R_ADDR relocations. The exception
// are those R_ADDR that are created as part of generating
@@ -327,7 +339,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
}
}
- if ctxt.IsELF {
+ if target.IsElf() {
// Generate R_X86_64_RELATIVE relocations for best
// efficiency in the dynamic linker.
//
@@ -345,14 +357,14 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
// 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))
+ rela := syms.Rela
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off))
if r.Siz == 8 {
- rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_X86_64_RELATIVE)))
+ rela.AddUint64(target.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))
+ rela.AddAddrPlus(target.Arch, targ, int64(r.Add))
// Not mark r done here. So we still apply it statically,
// so in the file content we'll also have the right offset
// to the relocation target. So it can be examined statically
@@ -360,7 +372,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
return true
}
- if ctxt.HeadType == objabi.Hdarwin && s.Size == int64(ctxt.Arch.PtrSize) && r.Off == 0 {
+ if target.IsDarwin() && s.Size == int64(target.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.
@@ -371,17 +383,17 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
// 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)
+ ld.Adddynsym(target, syms, targ)
- got := ctxt.Syms.Lookup(".got", 0)
+ got := syms.GOT
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))
+ got.AddUint64(target.Arch, 0)
+ syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid))
r.Type = objabi.ElfRelocOffset // ignore during relocsym
return true
}
@@ -553,84 +565,82 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, secto
return true
}
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, 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 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, 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 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
// pushq got+8(IP)
plt.AddUint8(0xff)
plt.AddUint8(0x35)
- plt.AddPCRelPlus(ctxt.Arch, got, 8)
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 8)
// jmpq got+16(IP)
plt.AddUint8(0xff)
plt.AddUint8(0x25)
- plt.AddPCRelPlus(ctxt.Arch, got, 16)
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 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.AddAddrPlus(ctxt.Arch, dynamic, 0)
got.AddUint64(ctxt.Arch, 0)
got.AddUint64(ctxt.Arch, 0)
}
}
-func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Plt() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
+ ld.Adddynsym(target, syms, 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 target.IsElf() {
+ plt := syms.PLT
+ got := syms.GOTPLT
+ rela := syms.RelaPLT
if plt.Size == 0 {
- elfsetupplt(ctxt)
+ panic("plt is not set up")
}
// jmpq *got+size(IP)
plt.AddUint8(0xff)
plt.AddUint8(0x25)
- plt.AddPCRelPlus(ctxt.Arch, got, got.Size)
+ plt.AddPCRelPlus(target.Arch, got, got.Size)
// add to got: pointer to current pos in plt
- got.AddAddrPlus(ctxt.Arch, plt, plt.Size)
+ got.AddAddrPlus(target.Arch, plt, plt.Size)
// pushq $x
plt.AddUint8(0x68)
- plt.AddUint32(ctxt.Arch, uint32((got.Size-24-8)/8))
+ plt.AddUint32(target.Arch, uint32((got.Size-24-8)/8))
// jmpq .plt
plt.AddUint8(0xe9)
- plt.AddUint32(ctxt.Arch, uint32(-(plt.Size + 4)))
+ plt.AddUint32(target.Arch, uint32(-(plt.Size + 4)))
// rela
- rela.AddAddrPlus(ctxt.Arch, got, got.Size-8)
+ rela.AddAddrPlus(target.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)
+ rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_JMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
s.SetPlt(int32(plt.Size - 16))
- } else if ctxt.HeadType == objabi.Hdarwin {
+ } else if target.IsDarwin() {
// 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
@@ -641,39 +651,39 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
// 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)
+ addgotsym(target, syms, s)
+ plt := syms.PLT
- ctxt.Syms.Lookup(".linkedit.plt", 0).AddUint32(ctxt.Arch, uint32(s.Dynid))
+ syms.LinkEditPLT.AddUint32(target.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()))
+ plt.AddPCRelPlus(target.Arch, syms.GOT, int64(s.Got()))
} else {
ld.Errorf(s, "addpltsym: unsupported binary format")
}
}
-func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Got() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
- got := ctxt.Syms.Lookup(".got", 0)
+ ld.Adddynsym(target, syms, s)
+ got := syms.GOT
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))
+ got.AddUint64(target.Arch, 0)
+
+ if target.IsElf() {
+ rela := syms.Rela
+ rela.AddAddrPlus(target.Arch, got, int64(s.Got()))
+ rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_GLOB_DAT)))
+ rela.AddUint64(target.Arch, 0)
+ } else if target.IsDarwin() {
+ syms.LinkEditGOT.AddUint32(target.Arch, uint32(s.Dynid))
} else {
ld.Errorf(s, "addgotsym: unsupported binary format")
}
@@ -684,29 +694,33 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ f := func(ctxt *ld.Link, out *ld.OutBuf, start, length int64) {
+ // 0xCC is INT $3 - breakpoint instruction
+ ld.CodeblkPad(ctxt, out, start, length, []byte{0xCC})
+ }
+ ld.WriteParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length)
+
+ for _, sect := range ld.Segtext.Sections[1:] {
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
+
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
index f2fb6543d0..43ee4bf200 100644
--- a/src/cmd/link/internal/arm/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -34,10 +34,12 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"fmt"
"log"
+ "sync"
)
// This assembler:
@@ -115,13 +117,13 @@ 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 {
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, 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))
+ ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type))
return false
}
@@ -130,8 +132,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
r.Type = objabi.R_CALLARM
if targ.Type == sym.SDYNIMPORT {
- addpltsym(ctxt, targ)
- r.Sym = ctxt.Syms.Lookup(".plt", 0)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4))
}
@@ -143,9 +145,9 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT32): // R_ARM_GOT_BREL
if targ.Type != sym.SDYNIMPORT {
- addgotsyminternal(ctxt, targ)
+ addgotsyminternal(target, syms, targ)
} else {
- addgotsym(ctxt, targ)
+ addgotsym(target, syms, targ)
}
r.Type = objabi.R_CONST // write r->add during relocsym
@@ -155,13 +157,13 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil
if targ.Type != sym.SDYNIMPORT {
- addgotsyminternal(ctxt, targ)
+ addgotsyminternal(target, syms, targ)
} else {
- addgotsym(ctxt, targ)
+ addgotsym(target, syms, targ)
}
r.Type = objabi.R_PCREL
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += int64(targ.Got()) + 4
return true
@@ -173,15 +175,15 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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.Sym = syms.GOT
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)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4))
}
@@ -214,8 +216,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4))
}
@@ -229,12 +231,12 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
switch r.Type {
case objabi.R_CALLARM:
- if ctxt.LinkMode == ld.LinkExternal {
+ if target.IsExternal() {
// External linker will do this relocation.
return true
}
- addpltsym(ctxt, targ)
- r.Sym = ctxt.Syms.Lookup(".plt", 0)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add = int64(targ.Plt())
return true
@@ -242,12 +244,12 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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
+ if target.IsElf() {
+ ld.Adddynsym(target, syms, targ)
+ rel := syms.Rel
+ rel.AddAddrPlus(target.Arch, s, int64(r.Off))
+ rel.AddUint32(target.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
}
@@ -300,10 +302,8 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
return true
}
-func elfsetupplt(ctxt *ld.Link) {
- plt := ctxt.Syms.Lookup(".plt", 0)
- got := ctxt.Syms.Lookup(".got.plt", 0)
- if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
// str lr, [sp, #-4]!
plt.AddUint32(ctxt.Arch, 0xe52de004)
@@ -317,7 +317,7 @@ func elfsetupplt(ctxt *ld.Link) {
plt.AddUint32(ctxt.Arch, 0xe5bef008)
// .word &GLOBAL_OFFSET_TABLE[0] - .
- plt.AddPCRelPlus(ctxt.Arch, got, 4)
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 4)
// the first .plt entry requires 3 .plt.got entries
got.AddUint32(ctxt.Arch, 0)
@@ -597,8 +597,8 @@ func gentrampdyn(arch *sys.Arch, tramp, target *sym.Symbol, offset int64) {
}
}
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
- if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+ if target.IsExternal() {
switch r.Type {
case objabi.R_CALLARM:
r.Done = false
@@ -623,7 +623,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
// the section load address.
// we need to compensate that by removing the instruction's address
// from addend.
- if ctxt.HeadType == objabi.Hdarwin {
+ if target.IsDarwin() {
r.Xadd -= ld.Symaddr(s) + int64(r.Off)
}
@@ -641,19 +641,19 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
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 ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), 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)) {
+ if ld.Symaddr(syms.GOTPLT) < ld.Symaddr(syms.PLT) {
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
+ return 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+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
+ return 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+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
+ return 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+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
@@ -667,12 +667,12 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
return val, false
}
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, 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) {
+func addpltreloc(plt *sym.Symbol, got *sym.Symbol, s *sym.Symbol, typ objabi.RelocType) {
r := plt.AddRel()
r.Sym = got
r.Off = int32(plt.Size)
@@ -685,19 +685,19 @@ func addpltreloc(ctxt *ld.Link, plt *sym.Symbol, got *sym.Symbol, s *sym.Symbol,
plt.Grow(plt.Size)
}
-func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Plt() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
+ ld.Adddynsym(target, syms, 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 target.IsElf() {
+ plt := syms.PLT
+ got := syms.GOTPLT
+ rel := syms.RelPLT
if plt.Size == 0 {
- elfsetupplt(ctxt)
+ panic("plt is not set up")
}
// .got entry
@@ -706,54 +706,54 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
// 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)
+ got.AddAddrPlus(target.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]!
+ addpltreloc(plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000
+ addpltreloc(plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000
+ addpltreloc(plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]!
// rel
- rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got()))
+ rel.AddAddrPlus(target.Arch, got, int64(s.Got()))
- rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_JUMP_SLOT)))
+ rel.AddUint32(target.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) {
+func addgotsyminternal(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Got() >= 0 {
return
}
- got := ctxt.Syms.Lookup(".got", 0)
+ got := syms.GOT
s.SetGot(int32(got.Size))
- got.AddAddrPlus(ctxt.Arch, s, 0)
+ got.AddAddrPlus(target.Arch, s, 0)
- if ctxt.IsELF {
+ if target.IsElf() {
} else {
ld.Errorf(s, "addgotsyminternal: unsupported binary format")
}
}
-func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Got() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
- got := ctxt.Syms.Lookup(".got", 0)
+ ld.Adddynsym(target, syms, s)
+ got := syms.GOT
s.SetGot(int32(got.Size))
- got.AddUint32(ctxt.Arch, 0)
+ got.AddUint32(target.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)))
+ if target.IsElf() {
+ rel := syms.Rel
+ rel.AddAddrPlus(target.Arch, got, int64(s.Got()))
+ rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_GLOB_DAT)))
} else {
ld.Errorf(s, "addgotsym: unsupported binary format")
}
@@ -764,28 +764,28 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length)
+
+ for _, sect := range ld.Segtext.Sections[1:] {
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index 9c3f442238..66fa1c3a56 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -34,11 +34,12 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
- "encoding/binary"
"fmt"
"log"
+ "sync"
)
func gentext(ctxt *ld.Link) {
@@ -92,13 +93,13 @@ func gentext(ctxt *ld.Link) {
initarray_entry.AddAddr(ctxt.Arch, initfunc)
}
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, 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))
+ ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type))
return false
}
@@ -130,8 +131,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add += int64(targ.Plt())
}
if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() {
@@ -149,10 +150,10 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
// fall back to using GOT
// TODO: just needs relocation, no need to put in .dynsym
- addgotsym(ctxt, targ)
+ addgotsym(target, syms, targ)
r.Type = objabi.R_ARM64_GOT
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += int64(targ.Got())
return true
@@ -172,7 +173,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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 {
+ if target.IsPIE() && target.IsInternal() {
// For internal linking PIE, this R_ADDR relocation cannot
// be resolved statically. We need to generate a dynamic
// relocation. Let the code below handle it.
@@ -217,25 +218,25 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
// nothing to do, the relocation will be laid out in reloc
return true
}
- if ctxt.LinkMode == ld.LinkExternal {
+ if target.IsExternal() {
// External linker will do this relocation.
return true
}
case objabi.R_ADDR:
- if s.Type == sym.STEXT && ctxt.IsELF {
+ if s.Type == sym.STEXT && target.IsElf() {
// The code is asking for the address of an external
// function. We provide it with the address of the
// correspondent GOT symbol.
- addgotsym(ctxt, targ)
+ addgotsym(target, syms, targ)
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += int64(targ.Got())
return true
}
// Process dynamic relocations for the data sections.
- if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal {
+ if target.IsPIE() && target.IsInternal() {
// When internally linking, generate dynamic relocations
// for all typical R_ADDR relocations. The exception
// are those R_ADDR that are created as part of generating
@@ -283,7 +284,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
}
}
- if ctxt.IsELF {
+ if target.IsElf() {
// Generate R_AARCH64_RELATIVE relocations for best
// efficiency in the dynamic linker.
//
@@ -301,14 +302,14 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
// 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))
+ rela := syms.Rela
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off))
if r.Siz == 8 {
- rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_AARCH64_RELATIVE)))
+ rela.AddUint64(target.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))
+ rela.AddAddrPlus(target.Arch, targ, int64(r.Add))
// Not mark r done here. So we still apply it statically,
// so in the file content we'll also have the right offset
// to the relocation target. So it can be examined statically
@@ -434,14 +435,14 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se
return true
}
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
- if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+ if target.IsExternal() {
switch r.Type {
default:
return val, false
case objabi.R_ARM64_GOTPCREL:
var o1, o2 uint32
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
o1 = uint32(val >> 32)
o2 = uint32(val)
} else {
@@ -456,14 +457,14 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
// (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 !(r.Sym.IsFileLocal() || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && target.IsDynlinkingGo() {
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 {
+ if target.IsBigEndian() {
val = int64(o1)<<32 | int64(o2)
} else {
val = int64(o2)<<32 | int64(o1)
@@ -490,10 +491,10 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
// 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 {
+ if false && target.IsDarwin() {
var o0, o1 uint32
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
o0 = uint32(val >> 32)
o1 = uint32(val)
} else {
@@ -510,7 +511,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
r.Xadd = 0
// when laid out, the instruction order must always be o1, o2.
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
val = int64(o0)<<32 | int64(o1)
} else {
val = int64(o1)<<32 | int64(o0)
@@ -533,7 +534,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
return r.Add, true
case objabi.R_GOTOFF:
- return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true
+ return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
case objabi.R_ADDRARM64:
t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
@@ -543,7 +544,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
var o0, o1 uint32
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
o0 = uint32(val >> 32)
o1 = uint32(val)
} else {
@@ -555,42 +556,42 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
o1 |= uint32(t&0xfff) << 10
// when laid out, the instruction order must always be o1, o2.
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
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)
+ if target.IsDarwin() {
+ ld.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
}
// The TCB is two pointers. This is not documented anywhere, but is
// de facto part of the ABI.
- v := r.Sym.Value + int64(2*ctxt.Arch.PtrSize)
+ v := r.Sym.Value + int64(2*target.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 {
+ if target.IsPIE() && target.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)
+ if !target.IsLinux() {
+ ld.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
}
// The TCB is two pointers. This is not documented anywhere, but is
// de facto part of the ABI.
- v := ld.Symaddr(r.Sym) + int64(2*ctxt.Arch.PtrSize) + r.Add
+ v := ld.Symaddr(r.Sym) + int64(2*target.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 {
+ if target.IsBigEndian() {
o0 = uint32(val >> 32)
o1 = uint32(val)
} else {
@@ -609,7 +610,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
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 {
+ if target.IsBigEndian() {
return int64(o0)<<32 | int64(o1), true
}
return int64(o1)<<32 | int64(o0), true
@@ -620,7 +621,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
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))
+ t = (ld.Symaddr(syms.PLT) + r.Add) - (s.Value + int64(r.Off))
} else {
t = (ld.Symaddr(r.Sym) + r.Add) - (s.Value + int64(r.Off))
}
@@ -707,35 +708,30 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
return val, false
}
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, 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 {
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
// stp x16, x30, [sp, #-16]!
// identifying information
plt.AddUint32(ctxt.Arch, 0xa9bf7bf0)
// the following two instructions (adrp + ldr) load *got[2] into x17
// adrp x16, &got[0]
- plt.AddAddrPlus4(gotplt, 16)
- plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010)
- plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x90000010)
// <imm> is the offset value of &got[2] to &got[0], the same below
// ldr x17, [x16, <imm>]
- plt.AddAddrPlus4(gotplt, 16)
- plt.SetUint32(ctxt.Arch, plt.Size-4, 0xf9400211)
- plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0xf9400211)
// add x16, x16, <imm>
- plt.AddAddrPlus4(gotplt, 16)
- plt.SetUint32(ctxt.Arch, plt.Size-4, 0x91000210)
- plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_PCREL, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x91000210)
// br x17
plt.AddUint32(ctxt.Arch, 0xd61f0220)
@@ -746,57 +742,57 @@ func elfsetupplt(ctxt *ld.Link) {
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")
+ if gotplt.Size() != 0 {
+ ctxt.Errorf(gotplt.Sym(), "got.plt is not empty at the very beginning")
}
- gotplt.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+ gotplt.AddAddrPlus(ctxt.Arch, dynamic, 0)
gotplt.AddUint64(ctxt.Arch, 0)
gotplt.AddUint64(ctxt.Arch, 0)
}
}
-func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Plt() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
+ ld.Adddynsym(target, syms, 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 target.IsElf() {
+ plt := syms.PLT
+ gotplt := syms.GOTPLT
+ rela := syms.RelaPLT
if plt.Size == 0 {
- elfsetupplt(ctxt)
+ panic("plt is not set up")
}
// adrp x16, &got.plt[0]
plt.AddAddrPlus4(gotplt, gotplt.Size)
- plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010)
+ plt.SetUint32(target.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.SetUint32(target.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.SetUint32(target.Arch, plt.Size-4, 0x91000210)
plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL
// br x17
- plt.AddUint32(ctxt.Arch, 0xd61f0220)
+ plt.AddUint32(target.Arch, 0xd61f0220)
// add to got.plt: pointer to plt[0]
- gotplt.AddAddrPlus(ctxt.Arch, plt, 0)
+ gotplt.AddAddrPlus(target.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)
+ rela.AddAddrPlus(target.Arch, gotplt, gotplt.Size-8)
+ rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_JUMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
s.SetPlt(int32(plt.Size - 16))
} else {
@@ -804,21 +800,21 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
}
}
-func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Got() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
- got := ctxt.Syms.Lookup(".got", 0)
+ ld.Adddynsym(target, syms, s)
+ got := syms.GOT
s.SetGot(int32(got.Size))
- got.AddUint64(ctxt.Arch, 0)
+ got.AddUint64(target.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)
+ if target.IsElf() {
+ rela := syms.Rela
+ rela.AddAddrPlus(target.Arch, got, int64(s.Got()))
+ rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_GLOB_DAT)))
+ rela.AddUint64(target.Arch, 0)
} else {
ld.Errorf(s, "addgotsym: unsupported binary format")
}
@@ -829,28 +825,28 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length)
+
+ for _, sect := range ld.Segtext.Sections[1:] {
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go
new file mode 100644
index 0000000000..6c163c801e
--- /dev/null
+++ b/src/cmd/link/internal/benchmark/bench.go
@@ -0,0 +1,195 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package benchmark provides a Metrics object that enables memory and CPU
+// profiling for the linker. The Metrics objects can be used to mark stages
+// of the code, and name the measurements during that stage. There is also
+// optional GCs that can be performed at the end of each stage, so you
+// can get an accurate measurement of how each stage changes live memory.
+package benchmark
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "time"
+ "unicode"
+)
+
+type Flags int
+
+const (
+ GC = 1 << iota
+ NoGC Flags = 0
+)
+
+type Metrics struct {
+ gc Flags
+ marks []*mark
+ curMark *mark
+ filebase string
+ pprofFile *os.File
+}
+
+type mark struct {
+ name string
+ startM, endM, gcM runtime.MemStats
+ startT, endT time.Time
+}
+
+// New creates a new Metrics object.
+//
+// Typical usage should look like:
+//
+// func main() {
+// filename := "" // Set to enable per-phase pprof file output.
+// bench := benchmark.New(benchmark.GC, filename)
+// defer bench.Report(os.Stdout)
+// // etc
+// bench.Start("foo")
+// foo()
+// bench.Start("bar")
+// bar()
+// }
+//
+// Note that a nil Metrics object won't cause any errors, so one could write
+// code like:
+//
+// func main() {
+// enableBenchmarking := flag.Bool("enable", true, "enables benchmarking")
+// flag.Parse()
+// var bench *benchmark.Metrics
+// if *enableBenchmarking {
+// bench = benchmark.New(benchmark.GC)
+// }
+// bench.Start("foo")
+// // etc.
+// }
+func New(gc Flags, filebase string) *Metrics {
+ if gc == GC {
+ runtime.GC()
+ }
+ return &Metrics{gc: gc, filebase: filebase}
+}
+
+// Report reports the metrics.
+// Closes the currently Start(ed) range, and writes the report to the given io.Writer.
+func (m *Metrics) Report(w io.Writer) {
+ if m == nil {
+ return
+ }
+
+ m.closeMark()
+
+ gcString := ""
+ if m.gc == GC {
+ gcString = "_GC"
+ }
+
+ var totTime time.Duration
+ for _, curMark := range m.marks {
+ dur := curMark.endT.Sub(curMark.startT)
+ totTime += dur
+ fmt.Fprintf(w, "%s 1 %d ns/op", makeBenchString(curMark.name+gcString), dur.Nanoseconds())
+ fmt.Fprintf(w, "\t%d B/op", curMark.endM.TotalAlloc-curMark.startM.TotalAlloc)
+ fmt.Fprintf(w, "\t%d allocs/op", curMark.endM.Mallocs-curMark.startM.Mallocs)
+ if m.gc == GC {
+ fmt.Fprintf(w, "\t%d live-B", curMark.gcM.HeapAlloc)
+ } else {
+ fmt.Fprintf(w, "\t%d heap-B", curMark.endM.HeapAlloc)
+ }
+ fmt.Fprintf(w, "\n")
+ }
+ fmt.Fprintf(w, "%s 1 %d ns/op\n", makeBenchString("total time"+gcString), totTime.Nanoseconds())
+}
+
+// Starts marks the beginning of a new measurement phase.
+// Once a metric is started, it continues until either a Report is issued, or another Start is called.
+func (m *Metrics) Start(name string) {
+ if m == nil {
+ return
+ }
+ m.closeMark()
+ m.curMark = &mark{name: name}
+ // Unlikely we need to a GC here, as one was likely just done in closeMark.
+ if m.shouldPProf() {
+ f, err := os.Create(makePProfFilename(m.filebase, name, "cpuprof"))
+ if err != nil {
+ panic(err)
+ }
+ m.pprofFile = f
+ if err = pprof.StartCPUProfile(m.pprofFile); err != nil {
+ panic(err)
+ }
+ }
+ runtime.ReadMemStats(&m.curMark.startM)
+ m.curMark.startT = time.Now()
+}
+
+func (m *Metrics) closeMark() {
+ if m == nil || m.curMark == nil {
+ return
+ }
+ m.curMark.endT = time.Now()
+ if m.shouldPProf() {
+ pprof.StopCPUProfile()
+ m.pprofFile.Close()
+ m.pprofFile = nil
+ }
+ runtime.ReadMemStats(&m.curMark.endM)
+ if m.gc == GC {
+ runtime.GC()
+ runtime.ReadMemStats(&m.curMark.gcM)
+ if m.shouldPProf() {
+ // Collect a profile of the live heap. Do a
+ // second GC to force sweep completion so we
+ // get a complete snapshot of the live heap at
+ // the end of this phase.
+ runtime.GC()
+ f, err := os.Create(makePProfFilename(m.filebase, m.curMark.name, "memprof"))
+ if err != nil {
+ panic(err)
+ }
+ err = pprof.WriteHeapProfile(f)
+ if err != nil {
+ panic(err)
+ }
+ err = f.Close()
+ if err != nil {
+ panic(err)
+ }
+ }
+ }
+ m.marks = append(m.marks, m.curMark)
+ m.curMark = nil
+}
+
+// shouldPProf returns true if we should be doing pprof runs.
+func (m *Metrics) shouldPProf() bool {
+ return m != nil && len(m.filebase) > 0
+}
+
+// makeBenchString makes a benchmark string consumable by Go's benchmarking tools.
+func makeBenchString(name string) string {
+ needCap := true
+ ret := []rune("Benchmark")
+ for _, r := range name {
+ if unicode.IsSpace(r) {
+ needCap = true
+ continue
+ }
+ if needCap {
+ r = unicode.ToUpper(r)
+ needCap = false
+ }
+ ret = append(ret, r)
+ }
+ return string(ret)
+}
+
+func makePProfFilename(filebase, name, typ string) string {
+ return fmt.Sprintf("%s_%s.%s", filebase, makeBenchString(name), typ)
+}
diff --git a/src/cmd/link/internal/benchmark/bench_test.go b/src/cmd/link/internal/benchmark/bench_test.go
new file mode 100644
index 0000000000..d8ec717c7c
--- /dev/null
+++ b/src/cmd/link/internal/benchmark/bench_test.go
@@ -0,0 +1,53 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package benchmark
+
+import (
+ "testing"
+)
+
+func TestMakeBenchString(t *testing.T) {
+ tests := []struct {
+ have, want string
+ }{
+ {"foo", "BenchmarkFoo"},
+ {" foo ", "BenchmarkFoo"},
+ {"foo bar", "BenchmarkFooBar"},
+ }
+ for i, test := range tests {
+ if v := makeBenchString(test.have); test.want != v {
+ t.Errorf("test[%d] makeBenchString(%q) == %q, want %q", i, test.have, v, test.want)
+ }
+ }
+}
+
+func TestPProfFlag(t *testing.T) {
+ tests := []struct {
+ name string
+ want bool
+ }{
+ {"", false},
+ {"foo", true},
+ }
+ for i, test := range tests {
+ b := New(GC, test.name)
+ if v := b.shouldPProf(); test.want != v {
+ t.Errorf("test[%d] shouldPProf() == %v, want %v", i, v, test.want)
+ }
+ }
+}
+
+func TestPProfNames(t *testing.T) {
+ want := "foo_BenchmarkTest.cpuprof"
+ if v := makePProfFilename("foo", "test", "cpuprof"); v != want {
+ t.Errorf("makePProfFilename() == %q, want %q", v, want)
+ }
+}
+
+// Ensure that public APIs work with a nil Metrics object.
+func TestNilBenchmarkObject(t *testing.T) {
+ var b *Metrics
+ b.Start("TEST")
+ b.Report(nil)
+}
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
index 4a20d96f96..268f40e92d 100644
--- a/src/cmd/link/internal/ld/ar.go
+++ b/src/cmd/link/internal/ld/ar.go
@@ -104,15 +104,13 @@ func hostArchive(ctxt *Link, name string) {
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
- }
- }
+ returnAllUndefs := -1
+ undefs := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
+ for _, symIdx := range undefs {
+ name := ctxt.loader.SymName(symIdx)
+ if off := armap[name]; off != 0 && !loaded[off] {
+ load = append(load, off)
+ loaded[off] = true
}
}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 31613e5cef..7c4b08a805 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -37,6 +37,7 @@ import (
"cmd/internal/gcprog"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"compress/zlib"
"encoding/binary"
@@ -99,7 +100,7 @@ func trampoline(ctxt *Link, s *sym.Symbol) {
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)
+ ctxt.errorUnresolved(s, r)
}
// runtime and its dependent packages may call to each other.
// they are fine, as they will be laid down together.
@@ -126,7 +127,9 @@ func trampoline(ctxt *Link, s *sym.Symbol) {
//
// 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) {
+// TODO: This function is called in parallel. When the Loader wavefront
+// reaches here, calls into the loader need to be parallel as well.
+func relocsym(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *ArchSyms, s *sym.Symbol) {
if len(s.R) == 0 {
return
}
@@ -157,8 +160,8 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
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") {
+ if target.IsShared() || target.IsPlugin() {
+ if r.Sym.Name == "main.main" || (!target.IsPlugin() && 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
@@ -166,7 +169,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
continue
}
} else {
- ctxt.ErrorUnresolved(s, r)
+ err.errorUnresolved(s, r)
continue
}
}
@@ -180,21 +183,21 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
// 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 !target.IsSolaris() && !target.IsDarwin() && !target.IsAIX() && r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !r.Sym.Attr.SubSymbol() {
+ if !(target.IsPPC64() && target.IsExternal() && 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(target.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 {
+ if target.IsExternal() {
r.InitExt()
}
// TODO(mundaym): remove this special case - see issue 14218.
- if ctxt.Arch.Family == sys.S390X {
+ if target.IsS390X() {
switch r.Type {
case objabi.R_PCRELDBL:
r.InitExt()
@@ -215,33 +218,33 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
case 1:
o = int64(s.P[off])
case 2:
- o = int64(ctxt.Arch.ByteOrder.Uint16(s.P[off:]))
+ o = int64(target.Arch.ByteOrder.Uint16(s.P[off:]))
case 4:
- o = int64(ctxt.Arch.ByteOrder.Uint32(s.P[off:]))
+ o = int64(target.Arch.ByteOrder.Uint32(s.P[off:]))
case 8:
- o = int64(ctxt.Arch.ByteOrder.Uint64(s.P[off:]))
+ o = int64(target.Arch.ByteOrder.Uint64(s.P[off:]))
}
- if offset, ok := thearch.Archreloc(ctxt, r, s, o); ok {
+ if offset, ok := thearch.Archreloc(target, syms, 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))
+ Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(target.Arch, r.Type))
}
case objabi.R_TLS_LE:
- if ctxt.LinkMode == LinkExternal && ctxt.IsELF {
+ if target.IsExternal() && target.IsElf() {
r.Done = false
if r.Sym == nil {
- r.Sym = ctxt.Tlsg
+ r.Sym = syms.Tlsg
}
r.Xsym = r.Sym
r.Xadd = r.Add
o = 0
- if ctxt.Arch.Family != sys.AMD64 {
+ if !target.IsAMD64() {
o = r.Add
}
break
}
- if ctxt.IsELF && ctxt.Arch.Family == sys.ARM {
+ if target.IsElf() && target.IsARM() {
// On ELF ARM, the thread pointer is 8 bytes before
// the start of the thread-local data block, so add 8
// to the actual TLS offset (r->sym->value).
@@ -250,43 +253,43 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
// 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 {
+ } else if target.IsElf() || target.IsPlan9() || target.IsDarwin() {
+ o = int64(syms.Tlsoffset) + r.Add
+ } else if target.IsWindows() {
o = r.Add
} else {
- log.Fatalf("unexpected R_TLS_LE relocation for %v", ctxt.HeadType)
+ log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType)
}
case objabi.R_TLS_IE:
- if ctxt.LinkMode == LinkExternal && ctxt.IsELF {
+ if target.IsExternal() && target.IsElf() {
r.Done = false
if r.Sym == nil {
- r.Sym = ctxt.Tlsg
+ r.Sym = syms.Tlsg
}
r.Xsym = r.Sym
r.Xadd = r.Add
o = 0
- if ctxt.Arch.Family != sys.AMD64 {
+ if !target.IsAMD64() {
o = r.Add
}
break
}
- if ctxt.BuildMode == BuildModePIE && ctxt.IsELF {
+ if target.IsPIE() && target.IsElf() {
// We are linking the final executable, so we
// can optimize any TLS IE relocation to LE.
if thearch.TLSIEtoLE == nil {
- log.Fatalf("internal linking of TLS IE not supported on %v", ctxt.Arch.Family)
+ log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family)
}
thearch.TLSIEtoLE(s, int(off), int(r.Siz))
- o = int64(ctxt.Tlsoffset)
- // TODO: o += r.Add when ctxt.Arch.Family != sys.AMD64?
+ o = int64(syms.Tlsoffset)
+ // TODO: o += r.Add when !target.IsAmd64()?
// 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 {
+ if target.IsExternal() && r.Sym.Type != sym.SCONST {
r.Done = false
// set up addend for eventual relocation via outer symbol.
@@ -304,20 +307,20 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
r.Xsym = rs
o = r.Xadd
- if ctxt.IsELF {
- if ctxt.Arch.Family == sys.AMD64 {
+ if target.IsElf() {
+ if target.IsAMD64() {
o = 0
}
- } else if ctxt.HeadType == objabi.Hdarwin {
+ } else if target.IsDarwin() {
if rs.Type != sym.SHOSTOBJ {
o += Symaddr(rs)
}
- } else if ctxt.HeadType == objabi.Hwindows {
+ } else if target.IsWindows() {
// nothing to do
- } else if ctxt.HeadType == objabi.Haix {
+ } else if target.IsAIX() {
o = Symaddr(r.Sym) + r.Add
} else {
- Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType)
+ Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType)
}
break
@@ -327,14 +330,14 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
// 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 {
+ if target.IsAIX() && 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)
+ Xcoffadddynrel(target, ldr, s, r)
}
}
@@ -345,7 +348,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
// 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 {
+ if int32(o) < 0 && target.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()
}
@@ -354,7 +357,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name)
}
- if ctxt.LinkMode == LinkExternal {
+ if target.IsExternal() {
r.Done = false
// On most platforms, the external linker needs to adjust DWARF references
@@ -362,7 +365,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
// 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 {
+ if target.IsDarwin() {
r.Done = true
}
@@ -371,15 +374,15 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
// 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 {
+ if !target.IsWindows() {
r.Type = objabi.R_ADDR
}
- r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0)
+ r.Xsym = r.Sym.Sect.Sym
r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr)
o = r.Xadd
- if ctxt.IsELF && ctxt.Arch.Family == sys.AMD64 {
+ if target.IsElf() && target.IsAMD64() {
o = 0
}
break
@@ -406,7 +409,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
// 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 {
+ if target.IsDynlinkingGo() && target.IsDarwin() && 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
@@ -418,18 +421,18 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
}
fallthrough
case objabi.R_CALL, objabi.R_PCREL:
- if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT {
+ if target.IsExternal() && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT {
// pass through to the external linker.
r.Done = false
r.Xadd = 0
- if ctxt.IsELF {
+ if target.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) {
+ if target.IsExternal() && 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.
@@ -448,14 +451,14 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
r.Xsym = rs
o = r.Xadd
- if ctxt.IsELF {
- if ctxt.Arch.Family == sys.AMD64 {
+ if target.IsElf() {
+ if target.IsAMD64() {
o = 0
}
- } else if ctxt.HeadType == objabi.Hdarwin {
+ } else if target.IsDarwin() {
if r.Type == objabi.R_CALL {
- if ctxt.LinkMode == LinkExternal && rs.Type == sym.SDYNIMPORT {
- switch ctxt.Arch.Family {
+ if target.IsExternal() && rs.Type == sym.SDYNIMPORT {
+ switch target.Arch.Family {
case sys.AMD64:
// AMD64 dynamic relocations are relative to the end of the relocation.
o += int64(r.Siz)
@@ -470,18 +473,18 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
}
o -= int64(r.Off) // relative to section offset, not symbol
}
- } else if ctxt.Arch.Family == sys.ARM {
+ } else if target.IsARM() {
// 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
+ } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL
// PE/COFF's PC32 relocation uses the address after the relocated
// bytes as the base. Compensate by skewing the addend.
o += int64(r.Siz)
} else {
- Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType)
+ Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType)
}
break
@@ -497,10 +500,10 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
o = r.Sym.Size + r.Add
case objabi.R_XCOFFREF:
- if ctxt.HeadType != objabi.Haix {
+ if !target.IsAIX() {
Errorf(s, "find XCOFF R_REF on non-XCOFF files")
}
- if ctxt.LinkMode != LinkExternal {
+ if !target.IsExternal() {
Errorf(s, "find XCOFF R_REF with internal linking")
}
r.Xsym = r.Sym
@@ -516,10 +519,10 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
o = r.Add
}
- if ctxt.Arch.Family == sys.PPC64 || ctxt.Arch.Family == sys.S390X {
+ if target.IsPPC64() || target.IsS390X() {
r.InitExt()
if r.Variant != sym.RV_NONE {
- o = thearch.Archrelocvariant(ctxt, r, s, o)
+ o = thearch.Archrelocvariant(target, syms, r, s, o)
}
}
@@ -534,7 +537,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
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)
+ 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(target.Arch, r.Type), r.Variant, o)
}
switch siz {
default:
@@ -549,7 +552,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
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))
+ target.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)) {
@@ -562,64 +565,95 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
}
fl := int32(o)
- ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl))
+ target.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl))
case 8:
- ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o))
+ target.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)
- }
+ var wg sync.WaitGroup
+ target := &ctxt.Target
+ ldr := ctxt.loader
+ reporter := &ctxt.ErrorReporter
+ syms := &ctxt.ArchSyms
+ wg.Add(3)
+ go func() {
+ for _, s := range ctxt.Textp {
+ relocsym(target, ldr, reporter, syms, s)
+ }
+ wg.Done()
+ }()
+ go func() {
+ for _, s := range ctxt.datap {
+ relocsym(target, ldr, reporter, syms, s)
+ }
+ wg.Done()
+ }()
+ go func() {
+ for _, s := range dwarfp {
+ relocsym(target, ldr, reporter, syms, s)
+ }
+ wg.Done()
+ }()
+ wg.Wait()
}
-func windynrelocsym(ctxt *Link, rel, s *sym.Symbol) {
- for ri := range s.R {
- r := &s.R[ri]
- targ := r.Sym
- if targ == nil {
+func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) {
+ var su *loader.SymbolBuilder
+ relocs := ctxt.loader.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At2(ri)
+ targ := r.Sym()
+ if targ == 0 {
continue
}
- if !targ.Attr.Reachable() {
- if r.Type == objabi.R_WEAKADDROFF {
+ rt := r.Type()
+ if !ctxt.loader.AttrReachable(targ) {
+ if rt == objabi.R_WEAKADDROFF {
continue
}
- Errorf(s, "dynamic relocation to unreachable symbol %s", targ.Name)
+ ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s",
+ ctxt.loader.SymName(targ))
}
- 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())
+
+ tplt := ctxt.loader.SymPlt(targ)
+ tgot := ctxt.loader.SymGot(targ)
+ if tplt == -2 && tgot != -2 { // make dynimport JMP table for PE object files.
+ tplt := int32(rel.Size())
+ ctxt.loader.SetPlt(targ, tplt)
+
+ if su == nil {
+ su = ctxt.loader.MakeSymbolUpdater(s)
+ }
+ r.SetSym(rel.Sym())
+ r.SetAdd(int64(tplt))
// jmp *addr
switch ctxt.Arch.Family {
default:
- Errorf(s, "unsupported arch %v", ctxt.Arch.Family)
+ ctxt.Errorf(s, "unsupported arch %v", ctxt.Arch.Family)
return
case sys.I386:
rel.AddUint8(0xff)
rel.AddUint8(0x25)
- rel.AddAddr(ctxt.Arch, targ)
+ rel.AddAddrPlus(ctxt.Arch, targ, 0)
rel.AddUint8(0x90)
rel.AddUint8(0x90)
case sys.AMD64:
rel.AddUint8(0xff)
rel.AddUint8(0x24)
rel.AddUint8(0x25)
- rel.AddAddrPlus4(targ, 0)
+ rel.AddAddrPlus4(ctxt.Arch, targ, 0)
rel.AddUint8(0x90)
}
- } else if r.Sym.Plt() >= 0 {
- r.Sym = rel
- r.Add = int64(targ.Plt())
+ } else if tplt >= 0 {
+ if su == nil {
+ su = ctxt.loader.MakeSymbolUpdater(s)
+ }
+ r.SetSym(rel.Sym())
+ r.SetAdd(int64(tplt))
}
}
}
@@ -627,32 +661,32 @@ func windynrelocsym(ctxt *Link, rel, s *sym.Symbol) {
// 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) {
+ if !(ctxt.IsWindows() && iscgo && ctxt.IsInternal()) {
return
}
- /* relocation table */
- rel := ctxt.Syms.Lookup(".rel", 0)
- rel.Attr |= sym.AttrReachable
- rel.Type = sym.STEXT
- ctxt.Textp = append(ctxt.Textp, rel)
+ rel := ctxt.loader.LookupOrCreateSym(".rel", 0)
+ relu := ctxt.loader.MakeSymbolUpdater(rel)
+ relu.SetType(sym.STEXT)
- for _, s := range ctxt.Textp {
- if s == rel {
- continue
- }
- windynrelocsym(ctxt, rel, s)
+ for _, s := range ctxt.Textp2 {
+ windynrelocsym(ctxt, relu, s)
}
+
+ ctxt.Textp2 = append(ctxt.Textp2, rel)
}
func dynrelocsym(ctxt *Link, s *sym.Symbol) {
+ target := &ctxt.Target
+ ldr := ctxt.loader
+ syms := &ctxt.ArchSyms
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)
+ thearch.Adddynrel(target, ldr, syms, s, r)
continue
}
@@ -660,7 +694,7 @@ func dynrelocsym(ctxt *Link, s *sym.Symbol) {
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) {
+ if !thearch.Adddynrel(target, ldr, syms, 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)
}
}
@@ -690,15 +724,16 @@ func dynreloc(ctxt *Link, data *[sym.SXREF][]*sym.Symbol) {
}
}
-func Codeblk(ctxt *Link, addr int64, size int64) {
- CodeblkPad(ctxt, addr, size, zeros[:])
+func Codeblk(ctxt *Link, out *OutBuf, addr int64, size int64) {
+ CodeblkPad(ctxt, out, addr, size, zeros[:])
}
-func CodeblkPad(ctxt *Link, addr int64, size int64, pad []byte) {
+
+func CodeblkPad(ctxt *Link, out *OutBuf, 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)
+ writeBlocks(out, ctxt.outSem, ctxt.Textp, addr, size, pad)
/* again for printing */
if !*flagA {
@@ -756,9 +791,86 @@ func CodeblkPad(ctxt *Link, addr int64, size int64, pad []byte) {
}
}
-func blk(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) {
+const blockSize = 1 << 20 // 1MB chunks written at a time.
+
+// writeBlocks writes a specified chunk of symbols to the output buffer. It
+// breaks the write up into ≥blockSize chunks to write them out, and schedules
+// as many goroutines as necessary to accomplish this task. This call then
+// blocks, waiting on the writes to complete. Note that we use the sem parameter
+// to limit the number of concurrent writes taking place.
+func writeBlocks(out *OutBuf, sem chan int, syms []*sym.Symbol, addr, size int64, pad []byte) {
for i, s := range syms {
- if !s.Attr.SubSymbol() && s.Value >= addr {
+ if s.Value >= addr && !s.Attr.SubSymbol() {
+ syms = syms[i:]
+ break
+ }
+ }
+
+ var wg sync.WaitGroup
+ max, lastAddr, written := int64(blockSize), addr+size, int64(0)
+ for addr < lastAddr {
+ // Find the last symbol we'd write.
+ idx := -1
+ for i, s := range syms {
+ // If the next symbol's size would put us out of bounds on the total length,
+ // stop looking.
+ if s.Value+s.Size > lastAddr {
+ break
+ }
+
+ // We're gonna write this symbol.
+ idx = i
+
+ // If we cross over the max size, we've got enough symbols.
+ if s.Value+s.Size > addr+max {
+ break
+ }
+ }
+
+ // If we didn't find any symbols to write, we're done here.
+ if idx < 0 {
+ break
+ }
+
+ // Compute the length to write, including padding.
+ // We need to write to the end address (lastAddr), or the next symbol's
+ // start address, whichever comes first. If there is no more symbols,
+ // just write to lastAddr. This ensures we don't leave holes between the
+ // blocks or at the end.
+ length := int64(0)
+ if idx+1 < len(syms) {
+ length = syms[idx+1].Value - addr
+ }
+ if length == 0 || length > lastAddr-addr {
+ length = lastAddr - addr
+ }
+
+ // Start the block output operator.
+ if o, err := out.View(uint64(out.Offset() + written)); err == nil {
+ sem <- 1
+ wg.Add(1)
+ go func(o *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) {
+ writeBlock(o, syms, addr, size, pad)
+ wg.Done()
+ <-sem
+ }(o, syms, addr, length, pad)
+ } else { // output not mmaped, don't parallelize.
+ writeBlock(out, syms, addr, length, pad)
+ }
+
+ // Prepare for the next loop.
+ if idx != -1 {
+ syms = syms[idx+1:]
+ }
+ written += length
+ addr += length
+ }
+ wg.Wait()
+}
+
+func writeBlock(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) {
+ for i, s := range syms {
+ if s.Value >= addr && !s.Attr.SubSymbol() {
syms = syms[i:]
break
}
@@ -802,11 +914,26 @@ func blk(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) {
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)
+type writeFn func(*Link, *OutBuf, int64, int64)
+
+// WriteParallel handles scheduling parallel execution of data write functions.
+func WriteParallel(wg *sync.WaitGroup, fn writeFn, ctxt *Link, seek, vaddr, length uint64) {
+ if out, err := ctxt.Out.View(seek); err != nil {
+ ctxt.Out.SeekSet(int64(seek))
+ fn(ctxt, ctxt.Out, int64(vaddr), int64(length))
+ } else {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ fn(ctxt, out, int64(vaddr), int64(length))
+ }()
+ }
+}
+
+func Datblk(ctxt *Link, out *OutBuf, addr, size int64) {
+ writeDatblkToOutBuf(ctxt, out, addr, size)
}
// Used only on Wasm for now.
@@ -823,14 +950,14 @@ func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) {
ctxt.Logf("datblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset())
}
- blk(out, datap, addr, size, zeros[:])
+ writeBlocks(out, ctxt.outSem, ctxt.datap, addr, size, zeros[:])
/* again for printing */
if !*flagA {
return
}
- syms := datap
+ syms := ctxt.datap
for i, sym := range syms {
if sym.Value >= addr {
syms = syms[i:]
@@ -892,12 +1019,12 @@ func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) {
ctxt.Logf("\t%.8x|\n", uint(eaddr))
}
-func Dwarfblk(ctxt *Link, addr int64, size int64) {
+func Dwarfblk(ctxt *Link, out *OutBuf, 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[:])
+ writeBlocks(out, ctxt.outSem, dwarfp, addr, size, zeros[:])
}
var zeros [512]byte
@@ -927,48 +1054,43 @@ func addstrdata1(ctxt *Link, arg string) {
}
// 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.
+func addstrdata(arch *sys.Arch, l *loader.Loader, name, value string) {
+ s := l.Lookup(name, 0)
+ if s == 0 {
return
}
- if s.Gotype.Name != "type.string" {
- Errorf(s, "cannot set with -X: not a var of type string (%s)", s.Gotype.Name)
+ if goType := l.SymGoType(s); goType == 0 {
+ return
+ } else if typeName := l.SymName(goType); typeName != "type.string" {
+ Errorf(nil, "%s: cannot set with -X: not a var of type string (%s)", name, typeName)
return
}
- if s.Type == sym.SBSS {
- s.Type = sym.SDATA
+ if !l.AttrReachable(s) {
+ return // don't bother setting unreachable variable
}
-
- 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)
+ bld := l.MakeSymbolUpdater(s)
+ if bld.Type() == sym.SBSS {
+ bld.SetType(sym.SDATA)
}
- 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)
+ p := fmt.Sprintf("%s.str", name)
+ sp := l.LookupOrCreateSym(p, 0)
+ sbld := l.MakeSymbolUpdater(sp)
- sp.Attr.Set(sym.AttrReachable, reachable)
+ sbld.Addstring(value)
+ sbld.SetType(sym.SRODATA)
+
+ bld.SetSize(0)
+ bld.SetData(make([]byte, 0, arch.PtrSize*2))
+ bld.SetReadOnly(false)
+ bld.SetRelocs(nil)
+ bld.AddAddrPlus(arch, sp, 0)
+ bld.AddUint(arch, uint64(len(value)))
}
func (ctxt *Link) dostrdata() {
for _, name := range strnames {
- addstrdata(ctxt, name, strdata[name])
+ addstrdata(ctxt.Arch, ctxt.loader, name, strdata[name])
}
}
@@ -1139,71 +1261,131 @@ func checkdatsize(ctxt *Link, datsize int64, symn sym.SymKind) {
}
}
-// datap is a collection of reachable data symbols in address order.
-// Generated by dodata.
-var datap []*sym.Symbol
+// fixZeroSizedSymbols gives a few special symbols with zero size some space.
+func fixZeroSizedSymbols(ctxt *Link) {
+ // The values in moduledata are filled out by relocations
+ // pointing to the addresses of these special symbols.
+ // Typically these symbols have no size and are not laid
+ // out with their matching section.
+ //
+ // However on darwin, dyld will find the special symbol
+ // in the first loaded module, even though it is local.
+ //
+ // (An hypothesis, formed without looking in the dyld sources:
+ // these special symbols have no size, so their address
+ // matches a real symbol. The dynamic linker assumes we
+ // want the normal symbol with the same address and finds
+ // it in the other module.)
+ //
+ // To work around this we lay out the symbls whose
+ // addresses are vital for multi-module programs to work
+ // as normal symbols, and give them a little size.
+ //
+ // On AIX, as all DATA sections are merged together, ld might not put
+ // these symbols at the beginning of their respective section if there
+ // aren't real symbols, their alignment might not match the
+ // first symbol alignment. Therefore, there are explicitly put at the
+ // beginning of their section with the same alignment.
+ if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+ return
+ }
-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
- }
+ bss := ctxt.Syms.Lookup("runtime.bss", 0)
+ bss.Size = 8
+ bss.Attr.Set(sym.AttrSpecial, false)
- types := ctxt.Syms.Lookup("runtime.types", 0)
- types.Type = sym.STYPE
- types.Size = 8
- types.Attr.Set(sym.AttrSpecial, false)
+ ctxt.Syms.Lookup("runtime.ebss", 0).Attr.Set(sym.AttrSpecial, false)
- etypes := ctxt.Syms.Lookup("runtime.etypes", 0)
- etypes.Type = sym.SFUNCTAB
- etypes.Attr.Set(sym.AttrSpecial, false)
+ data := ctxt.Syms.Lookup("runtime.data", 0)
+ data.Size = 8
+ data.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)
+ 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)
+ ctxt.Syms.Lookup("runtime.erodata", 0).Attr.Set(sym.AttrSpecial, false)
+ }
+}
+
+// makeRelroForSharedLib creates a section of readonly data if necessary.
+func makeRelroForSharedLib(target *Link, data *[sym.SXREF][]*sym.Symbol) {
+ if !target.UseRelro() {
+ return
+ }
+
+ // "read only" data with relocations needs to go in its own section
+ // when building a shared library. We do this by boosting objects of
+ // type SXXX with relocations to type SXXXRELRO.
+ 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 target.IsAIX() && 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
}
+}
+
+func (ctxt *Link) dodata() {
+ // Give zeros sized symbols space if necessary.
+ fixZeroSizedSymbols(ctxt)
// Collect data symbols by type into data.
var data [sym.SXREF][]*sym.Symbol
@@ -1228,57 +1410,8 @@ func (ctxt *Link) dodata() {
}
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
- }
- }
+ // Move any RO data with relocations to a separate section.
+ makeRelroForSharedLib(ctxt, &data)
// Sort symbols.
var dataMaxAlign [sym.SXREF]int32
@@ -1767,7 +1900,7 @@ func (ctxt *Link) dodata() {
}
for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ {
- datap = append(datap, data[symn]...)
+ ctxt.datap = append(ctxt.datap, data[symn]...)
}
dwarfGenerateDebugSyms(ctxt)
@@ -1780,6 +1913,7 @@ func (ctxt *Link) dodata() {
}
sect = addsection(ctxt.Arch, &Segdwarf, s.Name, 04)
+ sect.Sym = s
sect.Align = 1
datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = uint64(datsize)
@@ -1794,18 +1928,20 @@ func (ctxt *Link) dodata() {
for i < len(dwarfp) {
curType := dwarfp[i].Type
var sect *sym.Section
+ var sectname string
switch curType {
case sym.SDWARFINFO:
- sect = addsection(ctxt.Arch, &Segdwarf, ".debug_info", 04)
+ sectname = ".debug_info"
case sym.SDWARFRANGE:
- sect = addsection(ctxt.Arch, &Segdwarf, ".debug_ranges", 04)
+ sectname = ".debug_ranges"
case sym.SDWARFLOC:
- sect = addsection(ctxt.Arch, &Segdwarf, ".debug_loc", 04)
+ sectname = ".debug_loc"
default:
// Error is unrecoverable, so panic.
panic(fmt.Sprintf("unknown DWARF section %v", curType))
}
-
+ sect = addsection(ctxt.Arch, &Segdwarf, sectname, 04)
+ sect.Sym = ctxt.Syms.ROLookup(sectname, 0)
sect.Align = 1
datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = uint64(datsize)
@@ -1980,18 +2116,19 @@ func (ctxt *Link) textbuildid() {
return
}
- s := ctxt.Syms.Lookup("go.buildid", 0)
- s.Attr |= sym.AttrReachable
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate("go.buildid", 0)
+ s.SetReachable(true)
// 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))
+ s.SetType(sym.STEXT)
+ s.SetData([]byte(data))
+ s.SetSize(int64(len(data)))
- ctxt.Textp = append(ctxt.Textp, nil)
- copy(ctxt.Textp[1:], ctxt.Textp)
- ctxt.Textp[0] = s
+ ctxt.Textp2 = append(ctxt.Textp2, 0)
+ copy(ctxt.Textp2[1:], ctxt.Textp2)
+ ctxt.Textp2[0] = s.Sym()
}
func (ctxt *Link) buildinfo() {
@@ -2317,7 +2454,7 @@ func (ctxt *Link) address() []*sym.Segment {
}
}
- for _, s := range datap {
+ for _, s := range ctxt.datap {
if s.Sect != nil {
s.Value += int64(s.Sect.Vaddr)
}
@@ -2392,6 +2529,24 @@ func (ctxt *Link) address() []*sym.Segment {
ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length))
ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length))
+ if ctxt.IsSolaris() {
+ // On Solaris, in the runtime it sets the external names of the
+ // end symbols. Unset them and define separate symbols, so we
+ // keep both.
+ etext := ctxt.Syms.ROLookup("runtime.etext", 0)
+ edata := ctxt.Syms.ROLookup("runtime.edata", 0)
+ end := ctxt.Syms.ROLookup("runtime.end", 0)
+ etext.SetExtname("runtime.etext")
+ edata.SetExtname("runtime.edata")
+ end.SetExtname("runtime.end")
+ ctxt.xdefine("_etext", etext.Type, etext.Value)
+ ctxt.xdefine("_edata", edata.Type, edata.Value)
+ ctxt.xdefine("_end", end.Type, end.Value)
+ ctxt.Syms.ROLookup("_etext", 0).Sect = etext.Sect
+ ctxt.Syms.ROLookup("_edata", 0).Sect = edata.Sect
+ ctxt.Syms.ROLookup("_end", 0).Sect = end.Sect
+ }
+
return order
}
@@ -2454,6 +2609,8 @@ func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte {
binary.BigEndian.PutUint64(sizeBytes[:], uint64(total))
buf.Write(sizeBytes[:])
+ var relocbuf []byte // temporary buffer for applying relocations
+
// Using zlib.BestSpeed achieves very nearly the same
// compression levels of zlib.DefaultCompression, but takes
// substantially less time. This is important because DWARF
@@ -2462,17 +2619,22 @@ func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte {
if err != nil {
log.Fatalf("NewWriterLevel failed: %s", err)
}
+ target := &ctxt.Target
+ ldr := ctxt.loader
+ reporter := &ctxt.ErrorReporter
+ archSyms := &ctxt.ArchSyms
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
+ relocbuf = append(relocbuf[:0], s.P...)
+ s.P = relocbuf
+ // TODO: This function call needs to be parallelized when the loader wavefront gets here.
s.Attr.Set(sym.AttrReadOnly, false)
}
- relocsym(ctxt, s)
+ relocsym(target, ldr, reporter, archSyms, s)
if _, err := z.Write(s.P); err != nil {
log.Fatalf("compression failed: %s", err)
}
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 6d96ae50d7..13ddcdac24 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -6,11 +6,7 @@ package ld
import (
"cmd/internal/objabi"
- "cmd/internal/sys"
"cmd/link/internal/sym"
- "fmt"
- "strings"
- "unicode"
)
// deadcode marks all reachable symbols.
@@ -46,363 +42,59 @@ import (
//
// 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)
+ deadcode2(ctxt)
}
+// addToTextp populates the context Textp slice (needed in various places
+// in the linker) and also the unit Textp slices (needed by the "old"
+// phase 2 DWARF generation).
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)
+
+ // First set up ctxt.Textp, based on ctxt.Textp2.
+ textp := make([]*sym.Symbol, 0, len(ctxt.Textp2))
+ haveshlibs := len(ctxt.Shlibs) > 0
+ for _, tsym := range ctxt.Textp2 {
+ sp := ctxt.loader.Syms[tsym]
+ if sp == nil || !ctxt.loader.AttrReachable(tsym) {
+ panic("should never happen")
}
+ if haveshlibs && sp.Type == sym.SDYNIMPORT {
+ continue
+ }
+ textp = append(textp, sp)
}
+ ctxt.Textp = textp
- // 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)
+ // Dupok symbols may be defined in multiple packages; the
+ // associated package for a dupok sym is chosen sort of
+ // arbitrarily (the first containing package that the linker
+ // loads). The loop below canonicalizes the File to the package
+ // with which it will be laid down in text. Assumes that
+ // ctxt.Library is already in postorder.
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 _, dsym := range lib.DupTextSyms2 {
+ tsp := ctxt.loader.Syms[dsym]
+ if !tsp.Attr.OnList() {
+ tsp.Attr |= sym.AttrOnList
+ tsp.File = objabi.PathToPrefix(lib.Pkg)
}
}
- 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)
+ // Finally, set up compilation unit Textp slices. Can be removed
+ // once loader-Sym DWARF-gen phase 2 is always enabled.
+ for _, lib := range ctxt.Library {
+ for _, unit := range lib.Units {
+ for _, usym := range unit.Textp2 {
+ usp := ctxt.loader.Syms[usym]
+ usp.Attr |= sym.AttrOnList
+ unit.Textp = append(unit.Textp, usp)
}
}
- d.mark(s.Gotype, s)
- d.mark(s.Sub, s)
- d.mark(s.Outer, s)
}
}
diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go
index 9197c700f5..3342efe39f 100644
--- a/src/cmd/link/internal/ld/deadcode2.go
+++ b/src/cmd/link/internal/ld/deadcode2.go
@@ -6,7 +6,6 @@ package ld
import (
"bytes"
- "cmd/internal/dwarf"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
@@ -37,7 +36,6 @@ 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
@@ -58,9 +56,7 @@ func (d *deadcodePass2) init() {
n := d.ldr.NDef()
for i := 1; i < n; i++ {
s := loader.Sym(i)
- if !d.ldr.IsDup(s) {
- d.mark(s, 0)
- }
+ d.mark(s, 0)
}
return
}
@@ -88,9 +84,9 @@ func (d *deadcodePass2) init() {
// 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)
+ relocs := d.ldr.Relocs(exportsIdx)
+ for i := 0; i < relocs.Count(); i++ {
+ d.mark(relocs.At2(i).Sym(), 0)
}
}
}
@@ -104,14 +100,6 @@ func (d *deadcodePass2) init() {
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)
@@ -121,20 +109,18 @@ func (d *deadcodePass2) init() {
}
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)
+ isgotype := d.ldr.IsGoType(symIdx)
relocs := d.ldr.Relocs(symIdx)
- symRelocs = relocs.ReadAll(symRelocs)
- if d.ldr.IsGoType(symIdx) {
+ if isgotype {
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) {
+ for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) {
if d.ctxt.Debugvlog > 1 {
d.ctxt.Logf("reached iface method: %s\n", sig)
}
@@ -144,30 +130,31 @@ func (d *deadcodePass2) flood() {
}
var methods []methodref2
- for i := 0; i < relocs.Count; i++ {
- r := symRelocs[i]
- if r.Type == objabi.R_WEAKADDROFF {
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At2(i)
+ t := r.Type()
+ if t == objabi.R_WEAKADDROFF {
continue
}
- if r.Type == objabi.R_METHODOFF {
- if i+2 >= relocs.Count {
+ if t == 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 {
+ if t == 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)
+ d.mark(r.Sym(), symIdx)
}
- auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms)
- for i := 0; i < len(auxSyms); i++ {
- d.mark(auxSyms[i], symIdx)
+ naux := d.ldr.NAux(symIdx)
+ for i := 0; i < naux; i++ {
+ d.mark(d.ldr.Aux2(symIdx, i).Sym(), symIdx)
}
// Some host object symbols have an outer object, which acts like a
// "carrier" symbol, or it holds all the symbols for a particular
@@ -175,14 +162,19 @@ func (d *deadcodePass2) flood() {
// 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 d.ldr.IsExternal(symIdx) {
+ d.mark(d.ldr.OuterSym(symIdx), symIdx)
+ d.mark(d.ldr.SubSym(symIdx), symIdx)
+ }
if len(methods) != 0 {
+ if !isgotype {
+ panic("method found on non-type symbol")
+ }
// Decode runtime type information for type methods
// to help work out which methods can be called
// dynamically via interfaces.
- methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs)
+ methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs)
if len(methods) != len(methodsigs) {
panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
}
@@ -195,9 +187,9 @@ func (d *deadcodePass2) flood() {
}
func (d *deadcodePass2) mark(symIdx, parent loader.Sym) {
- if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) {
+ if symIdx != 0 && !d.ldr.AttrReachable(symIdx) {
d.wq.push(symIdx)
- d.ldr.Reachable.Set(symIdx)
+ d.ldr.SetAttrReachable(symIdx, true)
if d.ctxt.Reachparent != nil {
d.ldr.Reachparent[symIdx] = parent
}
@@ -215,10 +207,10 @@ func (d *deadcodePass2) mark(symIdx, parent loader.Sym) {
}
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)
+ relocs := d.ldr.Relocs(m.src)
+ d.mark(relocs.At2(m.r).Sym(), m.src)
+ d.mark(relocs.At2(m.r+1).Sym(), m.src)
+ d.mark(relocs.At2(m.r+2).Sym(), m.src)
}
func deadcode2(ctxt *Link) {
@@ -239,7 +231,7 @@ func deadcode2(ctxt *Link) {
// 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))
+ d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.AttrReachable(callSym)) || (methSym != 0 && ldr.AttrReachable(methSym))
// Mark all methods that could satisfy a discovered
// interface as reachable. We recheck old marked interfaces
@@ -271,8 +263,8 @@ func deadcode2(ctxt *Link) {
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)
+ if relocs.Count() > 0 && ldr.AttrReachable(relocs.At2(0).Sym()) {
+ ldr.SetAttrReachable(s, true)
}
}
}
@@ -301,15 +293,15 @@ func (m methodref2) isExported() bool {
// 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 {
+func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, 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))
+ buf.WriteString(decodetypeName2(ldr, symIdx, relocs, off))
+ mtypSym := decodeRelocSym2(ldr, symIdx, relocs, 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)
+ mrelocs := ldr.Relocs(mtypSym)
mp := ldr.Data(mtypSym)
buf.WriteRune('(')
@@ -318,7 +310,7 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym
if i > 0 {
buf.WriteString(", ")
}
- a := d.decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i)
+ a := decodetypeFuncInType2(ldr, arch, mtypSym, &mrelocs, i)
buf.WriteString(ldr.SymName(a))
}
buf.WriteString(") (")
@@ -327,7 +319,7 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym
if i > 0 {
buf.WriteString(", ")
}
- a := d.decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i)
+ a := decodetypeFuncOutType2(ldr, arch, mtypSym, &mrelocs, i)
buf.WriteString(ldr.SymName(a))
}
buf.WriteRune(')')
@@ -339,25 +331,26 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym
return methods
}
-func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
+func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []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 {
+ rel := decodeReloc2(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize))
+ s := rel.Sym()
+ if s == 0 {
return nil
}
- if rel.Sym != symIdx {
+ if s != 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
+ 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)
+ return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods)
}
-func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
+func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
p := ldr.Data(symIdx)
if !decodetypeHasUncommon(arch, p) {
panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
@@ -388,54 +381,5 @@ func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, s
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)
+ return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
}
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index 3271c85157..50586081d3 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -125,11 +125,6 @@ func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte {
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)
diff --git a/src/cmd/link/internal/ld/decodesym2.go b/src/cmd/link/internal/ld/decodesym2.go
new file mode 100644
index 0000000000..318ce36594
--- /dev/null
+++ b/src/cmd/link/internal/ld/decodesym2.go
@@ -0,0 +1,120 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+)
+
+// This file contains utilities to decode type.* symbols, for
+// loader.Sym symbols (uses new loader interfaces).
+
+// At some point we'll want to migrate the contents of this file
+// to decodesym.go once the rouetines there have been decprecated + removed.
+
+func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc2 {
+ for j := 0; j < relocs.Count(); j++ {
+ rel := relocs.At2(j)
+ if rel.Off() == off {
+ return rel
+ }
+ }
+ return loader.Reloc2{}
+}
+
+func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym {
+ return decodeReloc2(ldr, symIdx, relocs, off).Sym()
+}
+
+// decodetypeName2 decodes the name from a reflect.name.
+func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string {
+ r := decodeRelocSym2(ldr, symIdx, relocs, int32(off))
+ if r == 0 {
+ return ""
+ }
+
+ data := ldr.Data(r)
+ namelen := int(uint16(data[1])<<8 | uint16(data[2]))
+ return string(data[3 : 3+namelen])
+}
+
+func decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
+ uadd := commonsize(arch) + 4
+ if arch.PtrSize == 8 {
+ uadd += 4
+ }
+ if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
+ uadd += uncommonSize()
+ }
+ return decodeRelocSym2(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize))
+}
+
+func decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
+ return decodetypeFuncInType2(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
+}
+
+func decodetypeArrayElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeArrayLen2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int64 {
+ data := ldr.Data(symIdx)
+ return int64(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeChanElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapKey2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapValue2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38
+}
+
+func decodetypePtrElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeStructFieldCount2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int {
+ data := ldr.Data(symIdx)
+ return int(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeStructFieldArrayOff2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int {
+ data := ldr.Data(symIdx)
+ off := commonsize(arch) + 4*arch.PtrSize
+ if decodetypeHasUncommon(arch, data) {
+ off += uncommonSize()
+ }
+ off += i * structfieldSize(arch)
+ return off
+}
+
+func decodetypeStructFieldName2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) string {
+ off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i)
+ relocs := ldr.Relocs(symIdx)
+ return decodetypeName2(ldr, symIdx, &relocs, off)
+}
+
+func decodetypeStructFieldType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym {
+ off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i)
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym2(ldr, symIdx, &relocs, int32(off+arch.PtrSize))
+}
+
+func decodetypeStructFieldOffsAnon2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 {
+ off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i)
+ data := ldr.Data(symIdx)
+ return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize))
+}
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 6eba39bcf7..65d3daeffb 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// 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.
@@ -19,6 +19,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"fmt"
"log"
@@ -26,105 +27,175 @@ import (
"strings"
)
-type dwctxt struct {
+// dwctxt2 is a wrapper intended to satisfy the method set of
+// dwarf.Context, so that functions like dwarf.PutAttrs will work with
+// DIEs that use loader.Sym as opposed to *sym.Symbol. It is also
+// being used as a place to store tables/maps that are useful as part
+// of type conversion (this is just a convenience; it would be easy to
+// split these things out into another type if need be).
+type dwctxt2 struct {
linkctxt *Link
+ ldr *loader.Loader
+ arch *sys.Arch
+
+ // This maps type name string (e.g. "uintptr") to loader symbol for
+ // the DWARF DIE for that type (e.g. "go.info.type.uintptr")
+ tmap map[string]loader.Sym
+
+ // This maps loader symbol for the DWARF DIE symbol generated for
+ // a type (e.g. "go.info.uintptr") to the type symbol itself
+ // ("type.uintptr").
+ // FIXME: try converting this map (and the next one) to a single
+ // array indexed by loader.Sym -- this may perform better.
+ rtmap map[loader.Sym]loader.Sym
+
+ // This maps Go type symbol (e.g. "type.XXX") to loader symbol for
+ // the typedef DIE for that type (e.g. "go.info.XXX..def")
+ tdmap map[loader.Sym]loader.Sym
+
+ // Cache these type symbols, so as to avoid repeatedly looking them up
+ typeRuntimeEface loader.Sym
+ typeRuntimeIface loader.Sym
+ uintptrInfoSym loader.Sym
}
-func (c dwctxt) PtrSize() int {
- return c.linkctxt.Arch.PtrSize
+func newdwctxt2(linkctxt *Link, forTypeGen bool) dwctxt2 {
+ d := dwctxt2{
+ linkctxt: linkctxt,
+ ldr: linkctxt.loader,
+ arch: linkctxt.Arch,
+ tmap: make(map[string]loader.Sym),
+ tdmap: make(map[loader.Sym]loader.Sym),
+ rtmap: make(map[loader.Sym]loader.Sym),
+ }
+ d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface")
+ d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface")
+ return d
}
-func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) {
- ls := s.(*sym.Symbol)
- ls.AddUintXX(c.linkctxt.Arch, uint64(i), size)
+
+// dwSym wraps a loader.Sym; this type is meant to obey the interface
+// rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and
+// DwAttr objects contain references to symbols via this type.
+type dwSym loader.Sym
+
+func (s dwSym) Length(dwarfContext interface{}) int64 {
+ l := dwarfContext.(dwctxt2).ldr
+ return int64(len(l.Data(loader.Sym(s))))
}
-func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) {
- ls := s.(*sym.Symbol)
- ls.AddBytes(b)
+
+func (c dwctxt2) PtrSize() int {
+ return c.arch.PtrSize
}
-func (c dwctxt) AddString(s dwarf.Sym, v string) {
- Addstring(s.(*sym.Symbol), v)
+
+func (c dwctxt2) AddInt(s dwarf.Sym, size int, i int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.AddUintXX(c.arch, uint64(i), size)
+}
+
+func (c dwctxt2) AddBytes(s dwarf.Sym, b []byte) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.AddBytes(b)
}
-func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+func (c dwctxt2) AddString(s dwarf.Sym, v string) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.Addstring(v)
+}
+
+func (c dwctxt2) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
if value != 0 {
- value -= (data.(*sym.Symbol)).Value
+ value -= dsu.Value()
}
- s.(*sym.Symbol).AddAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value)
+ tgtds := loader.Sym(data.(dwSym))
+ dsu.AddAddrPlus(c.arch, tgtds, value)
}
-func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
+func (c dwctxt2) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
if value != 0 {
- value -= (data.(*sym.Symbol)).Value
+ value -= dsu.Value()
}
- s.(*sym.Symbol).AddCURelativeAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value)
+ tgtds := loader.Sym(data.(dwSym))
+ dsu.AddCURelativeAddrPlus(c.arch, tgtds, value)
}
-func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
- ls := s.(*sym.Symbol)
+func (c dwctxt2) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ tds := loader.Sym(t.(dwSym))
switch size {
default:
- 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)
+ c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size)
+ case c.arch.PtrSize, 4:
}
- r := &ls.R[len(ls.R)-1]
- r.Type = objabi.R_ADDROFF
- r.Add = ofs
+ dsu.AddSymRef(c.arch, tds, ofs, objabi.R_ADDROFF, size)
}
-func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
+func (c dwctxt2) 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
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ tds := loader.Sym(t.(dwSym))
+ switch size {
+ default:
+ c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size)
+ case c.arch.PtrSize, 4:
+ }
+ dsu.AddSymRef(c.arch, tds, ofs, objabi.R_DWARFSECREF, size)
}
-func (c dwctxt) Logf(format string, args ...interface{}) {
+func (c dwctxt2) 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{}) {
+func (c dwctxt2) AddFileRef(s dwarf.Sym, f interface{}) {
panic("should be used only in the compiler")
}
-func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 {
+func (c dwctxt2) 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) {
+func (c dwctxt2) 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) {
+func (c dwctxt2) 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
+var dwarfp2 []loader.Sym
-func writeabbrev(ctxt *Link) *sym.Symbol {
- s := ctxt.Syms.Lookup(".debug_abbrev", 0)
- s.Type = sym.SDWARFSECT
- s.AddBytes(dwarf.GetAbbrev())
- return s
+func (d *dwctxt2) writeabbrev() loader.Sym {
+ abrvs := d.ldr.LookupOrCreateSym(".debug_abbrev", 0)
+ u := d.ldr.MakeSymbolUpdater(abrvs)
+ u.SetType(sym.SDWARFSECT)
+ u.AddBytes(dwarf.GetAbbrev())
+ return abrvs
}
var dwtypes dwarf.DWDie
+// newattr attaches a new attribute to the specified DIE.
+//
+// FIXME: at the moment attributes are stored in a linked list in a
+// fairly space-inefficient way -- it might be better to instead look
+// up all attrs in a single large table, then store indices into the
+// table in the DIE. This would allow us to common up storage for
+// attributes that are shared by many DIEs (ex: byte size of N).
func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr {
a := new(dwarf.DWAttr)
a.Link = die.Attr
@@ -165,7 +236,8 @@ func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr {
// 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 {
+// FIXME: it would be more efficient to bulk-allocate DIEs.
+func (d *dwctxt2) newdie(parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie {
die := new(dwarf.DWDie)
die.Abbrev = abbrev
die.Link = parent.Child
@@ -174,15 +246,22 @@ func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version in
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) {
+ // Q: do we need version here? My understanding is that all these
+ // symbols should be version 0.
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))
+ name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits))
+ }
+ ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, version)
+ dsu := d.ldr.MakeSymbolUpdater(ds)
+ dsu.SetType(sym.SDWARFINFO)
+ d.ldr.SetAttrNotInSymbolTable(ds, true)
+ d.ldr.SetAttrReachable(ds, true)
+ die.Sym = dwSym(ds)
+ if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL {
+ d.tmap[name] = ds
}
- s := ctxt.Syms.Lookup(dwarf.InfoPrefix+name, version)
- s.Attr |= sym.AttrNotInSymbolTable
- s.Type = sym.SDWARFINFO
- die.Sym = s
}
}
@@ -205,11 +284,22 @@ func walktypedef(die *dwarf.DWDie) *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
+func (d *dwctxt2) walksymtypedef(symIdx loader.Sym) loader.Sym {
+
+ // We're being given the loader symbol for the type DIE, e.g.
+ // "go.info.type.uintptr". Map that first to the type symbol (e.g.
+ // "type.uintptr") and then to the typedef DIE for the type.
+ // FIXME: this seems clunky, maybe there is a better way to do this.
+
+ if ts, ok := d.rtmap[symIdx]; ok {
+ if def, ok := d.tdmap[ts]; ok {
+ return def
+ }
+ d.linkctxt.Errorf(ts, "internal error: no entry for sym %d in tdmap\n", ts)
+ return 0
}
- return s
+ d.linkctxt.Errorf(symIdx, "internal error: no entry for sym %d in rtmap\n", symIdx)
+ return 0
}
// Find child by AT_name using hashtable if available or linear scan
@@ -230,73 +320,66 @@ func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie {
// 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
+// find looks up the loader symbol for the DWARF DIE generated for the
+// type with the specified name.
+func (d *dwctxt2) find(name string) loader.Sym {
+ return d.tmap[name]
}
-func mustFind(ctxt *Link, name string) *sym.Symbol {
- r := find(ctxt, name)
- if r == nil {
+func (d *dwctxt2) mustFind(name string) loader.Sym {
+ r := d.find(name)
+ if r == 0 {
Exitf("dwarf find: cannot find %s", name)
}
return r
}
-func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 {
+func (d *dwctxt2) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, 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
+ d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size)
+ case d.arch.PtrSize, 4:
+ }
+ result = sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size)
return result
}
-func newrefattr(die *dwarf.DWDie, attr uint16, ref *sym.Symbol) *dwarf.DWAttr {
- if ref == nil {
+func (d *dwctxt2) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) *dwarf.DWAttr {
+ if ref == 0 {
return nil
}
- return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, ref)
+ return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref))
}
-func dtolsym(s dwarf.Sym) *sym.Symbol {
+func (d *dwctxt2) dtolsym(s dwarf.Sym) loader.Sym {
if s == nil {
- return nil
+ return 0
}
- return s.(*sym.Symbol)
+ dws := loader.Sym(s.(dwSym))
+ return dws
}
-func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*sym.Symbol, die *dwarf.DWDie) []*sym.Symbol {
- s := dtolsym(die.Sym)
- if s == nil {
+func (d *dwctxt2) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym {
+ s := d.dtolsym(die.Sym)
+ if s == 0 {
s = syms[len(syms)-1]
} else {
- if s.Attr.OnList() {
- log.Fatalf("symbol %s listed multiple times", s.Name)
+ if d.ldr.AttrOnList(s) {
+ log.Fatalf("symbol %s listed multiple times", d.ldr.SymName(s))
}
- s.Attr |= sym.AttrOnList
+ d.ldr.SetAttrOnList(s, true)
syms = append(syms, s)
}
- dwarf.Uleb128put(ctxt, s, int64(die.Abbrev))
- dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr)
+ sDwsym := dwSym(s)
+ dwarf.Uleb128put(d, sDwsym, int64(die.Abbrev))
+ dwarf.PutAttrs(d, sDwsym, die.Abbrev, die.Attr)
if dwarf.HasChildren(die) {
for die := die.Child; die != nil; die = die.Link {
- syms = putdie(linkctxt, ctxt, syms, die)
+ syms = d.putdie(syms, die)
}
- syms[len(syms)-1].AddUint8(0)
+ dsu := d.ldr.MakeSymbolUpdater(syms[len(syms)-1])
+ dsu.AddUint8(0)
}
return syms
}
@@ -329,42 +412,23 @@ func newmemberoffsetattr(die *dwarf.DWDie, offs int32) {
// 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
+func (d *dwctxt2) newabslocexprattr(die *dwarf.DWDie, addr int64, symIdx loader.Sym) {
+ newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, dwSym(symIdx))
}
-// Lookup predefined types
-func lookupOrDiag(ctxt *Link, n string) *sym.Symbol {
- s := ctxt.Syms.ROLookup(n, 0)
- if s == nil || s.Size == 0 {
+func (d *dwctxt2) lookupOrDiag(n string) loader.Sym {
+ symIdx := d.ldr.Lookup(n, 0)
+ if symIdx == 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)
+ if len(d.ldr.Data(symIdx)) == 0 {
+ Exitf("dwarf: missing type (no data): %s", n)
}
- return ctxt.Syms.ROLookup(meta+s.Name, ver)
+
+ return symIdx
}
-func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie {
+func (d *dwctxt2) dotypedef(parent *dwarf.DWDie, gotype loader.Sym, name string, def *dwarf.DWDie) *dwarf.DWDie {
// Only emit typedefs for real names.
if strings.HasPrefix(name, "map[") {
return nil
@@ -382,53 +446,66 @@ func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) *
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
+ // Create a new loader symbol for the typedef. We no longer
+ // do lookups of typedef symbols by name, so this is going
+ // to be an anonymous symbol (we want this for perf reasons).
+ tds := d.ldr.CreateExtSym("", 0)
+ tdsu := d.ldr.MakeSymbolUpdater(tds)
+ tdsu.SetType(sym.SDWARFINFO)
+ def.Sym = dwSym(tds)
+ d.ldr.SetAttrNotInSymbolTable(tds, true)
+ d.ldr.SetAttrReachable(tds, true)
// The typedef entry must be created after the def,
// so that future lookups will find the typedef instead
// of the real definition. This hooks the typedef into any
// circular definition loops, so that gdb can understand them.
- die := newdie(ctxt, parent, dwarf.DW_ABRV_TYPEDECL, name, 0)
+ die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name, 0)
- newrefattr(die, dwarf.DW_AT_type, s)
+ d.newrefattr(die, dwarf.DW_AT_type, tds)
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>")
+func (d *dwctxt2) defgotype(gotype loader.Sym) loader.Sym {
+ if gotype == 0 {
+ return d.mustFind("<unspecified>")
}
- if !strings.HasPrefix(gotype.Name, "type.") {
- Errorf(gotype, "dwarf: type name doesn't start with \"type.\"")
- return mustFind(ctxt, "<unspecified>")
+ // If we already have a tdmap entry for the gotype, return it.
+ if ds, ok := d.tdmap[gotype]; ok {
+ return ds
}
- name := gotype.Name[5:] // could also decode from Type.string
-
- sdie := find(ctxt, name)
+ sn := d.ldr.SymName(gotype)
+ if !strings.HasPrefix(sn, "type.") {
+ d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type.\"")
+ return d.mustFind("<unspecified>")
+ }
+ name := sn[5:] // could also decode from Type.string
- if sdie != nil {
+ sdie := d.find(name)
+ if sdie != 0 {
return sdie
}
- return newtype(ctxt, gotype).Sym.(*sym.Symbol)
+ gtdwSym := d.newtype(gotype)
+ d.tdmap[gotype] = loader.Sym(gtdwSym.Sym.(dwSym))
+ return loader.Sym(gtdwSym.Sym.(dwSym))
}
-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)
+func (d *dwctxt2) newtype(gotype loader.Sym) *dwarf.DWDie {
+ sn := d.ldr.SymName(gotype)
+ name := sn[5:] // could also decode from Type.string
+ tdata := d.ldr.Data(gotype)
+ kind := decodetypeKind(d.arch, tdata)
+ bytesize := decodetypeSize(d.arch, tdata)
var die, typedefdie *dwarf.DWDie
switch kind {
case objabi.KindBool:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
@@ -437,7 +514,7 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
objabi.KindInt16,
objabi.KindInt32,
objabi.KindInt64:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
@@ -447,118 +524,125 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
objabi.KindUint32,
objabi.KindUint64,
objabi.KindUintptr:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
case objabi.KindFloat32,
objabi.KindFloat64:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
case objabi.KindComplex64,
objabi.KindComplex128:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
case objabi.KindArray:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0)
- typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
- s := decodetypeArrayElem(ctxt.Arch, gotype)
- newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
- fld := newdie(ctxt, die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0)
+ s := decodetypeArrayElem2(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
+ fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0)
// use actual length not upper bound; correct for 0-length arrays.
- newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(ctxt.Arch, gotype), 0)
+ newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen2(d.ldr, d.arch, gotype), 0)
- newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
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))
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0)
+ s := decodetypeChanElem2(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
// Save elem type for synthesizechantypes. We could synthesize here
// but that would change the order of DIEs we output.
- newrefattr(die, dwarf.DW_AT_type, s)
+ d.newrefattr(die, dwarf.DW_AT_type, s)
case objabi.KindFunc:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
+ die = d.newdie(&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)
+ typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+ data := d.ldr.Data(gotype)
+ // FIXME: add caching or reuse reloc slice.
+ relocs := d.ldr.Relocs(gotype)
+ nfields := decodetypeFuncInCount(d.arch, data)
for i := 0; i < nfields; i++ {
- s := decodetypeFuncInType(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))
+ s := decodetypeFuncInType2(d.ldr, d.arch, gotype, &relocs, i)
+ sn := d.ldr.SymName(s)
+ fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
}
- if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) {
- newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
+ if decodetypeFuncDotdotdot(d.arch, data) {
+ d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
}
- nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P)
+ nfields = decodetypeFuncOutCount(d.arch, data)
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)))
+ s := decodetypeFuncOutType2(d.ldr, d.arch, gotype, &relocs, i)
+ sn := d.ldr.SymName(s)
+ fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s)))
}
case objabi.KindInterface:
- die = 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
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+ data := d.ldr.Data(gotype)
+ nfields := int(decodetypeIfaceMethodCount(d.arch, data))
+ var s loader.Sym
if nfields == 0 {
- s = lookupOrDiag(ctxt, "type.runtime.eface")
+ s = d.typeRuntimeEface
} else {
- s = lookupOrDiag(ctxt, "type.runtime.iface")
+ s = d.typeRuntimeIface
}
- newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(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))
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0)
+ s := decodetypeMapKey2(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s))
+ s = decodetypeMapValue2(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
// Save gotype for use in synthesizemaptypes. We could synthesize here,
// but that would change the order of the DIEs.
- newrefattr(die, dwarf.DW_AT_type, gotype)
+ d.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))
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+ s := decodetypePtrElem2(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
case objabi.KindSlice:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0)
- typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
- s := decodetypeArrayElem(ctxt.Arch, gotype)
- elem := defgotype(ctxt, s)
- newrefattr(die, dwarf.DW_AT_go_elem, elem)
+ s := decodetypeArrayElem2(d.ldr, d.arch, gotype)
+ elem := d.defgotype(s)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, elem)
case objabi.KindString:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
case objabi.KindStruct:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0)
- typedefdie = dotypedef(ctxt, &dwtypes, name, die)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0)
+ typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
- nfields := decodetypeStructFieldCount(ctxt.Arch, gotype)
+ nfields := decodetypeStructFieldCount2(d.ldr, d.arch, gotype)
for i := 0; i < nfields; i++ {
- f := decodetypeStructFieldName(ctxt.Arch, gotype, i)
- s := decodetypeStructFieldType(ctxt.Arch, gotype, i)
+ f := decodetypeStructFieldName2(d.ldr, d.arch, gotype, i)
+ s := decodetypeStructFieldType2(d.ldr, d.arch, gotype, i)
if f == "" {
- f = s.Name[5:] // skip "type."
+ sn := d.ldr.SymName(s)
+ f = sn[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)
+ fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f, 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
+ offsetAnon := decodetypeStructFieldOffsAnon2(d.ldr, d.arch, gotype, i)
newmemberoffsetattr(fld, int32(offsetAnon>>1))
if offsetAnon&1 != 0 { // is embedded field
newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0)
@@ -566,21 +650,33 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
}
case objabi.KindUnsafePointer:
- die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0)
+ die = d.newdie(&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>"))
+ d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0)
+ d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("<unspecified>"))
}
newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0)
- if gotype.Attr.Reachable() {
- newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype)
+
+ if d.ldr.AttrReachable(gotype) {
+ newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gotype))
}
- if _, ok := prototypedies[gotype.Name]; ok {
- prototypedies[gotype.Name] = die
+ // Sanity check.
+ if _, ok := d.rtmap[gotype]; ok {
+ log.Fatalf("internal error: rtmap entry already installed\n")
+ }
+
+ ds := loader.Sym(die.Sym.(dwSym))
+ if typedefdie != nil {
+ ds = loader.Sym(typedefdie.Sym.(dwSym))
+ }
+ d.rtmap[ds] = gotype
+
+ if _, ok := prototypedies[sn]; ok {
+ prototypedies[sn] = die
}
if typedefdie != nil {
@@ -589,55 +685,67 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
return die
}
-func nameFromDIESym(dwtype *sym.Symbol) string {
- return strings.TrimSuffix(dwtype.Name[len(dwarf.InfoPrefix):], "..def")
+func (d *dwctxt2) nameFromDIESym(dwtypeDIESym loader.Sym) string {
+ sn := d.ldr.SymName(dwtypeDIESym)
+ return sn[len(dwarf.InfoPrefix):]
}
-// 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 {
+func (d *dwctxt2) defptrto(dwtype loader.Sym) loader.Sym {
+
+ // FIXME: it would be nice if the compiler attached an aux symbol
+ // ref from the element type to the pointer type -- it would be
+ // more efficient to do it this way as opposed to via name lookups.
+
+ ptrname := "*" + d.nameFromDIESym(dwtype)
+ if die := d.find(ptrname); die != 0 {
return die
}
- pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
- newrefattr(pdie, dwarf.DW_AT_type, dwtype)
+ pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
+ d.newrefattr(pdie, dwarf.DW_AT_type, dwtype)
// The DWARF info synthesizes pointer types that don't exist at the
// language level, like *hash<...> and *bucket<...>, and the data
// pointers of slices. Link to the ones we can find.
- 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)
+ gts := d.ldr.Lookup("type."+ptrname, 0)
+ if gts != 0 && d.ldr.AttrReachable(gts) {
+ newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts))
+ }
+
+ if gts != 0 {
+ ds := loader.Sym(pdie.Sym.(dwSym))
+ d.rtmap[ds] = gts
+ d.tdmap[gts] = ds
}
- return dtolsym(pdie.Sym)
+
+ return d.dtolsym(pdie.Sym)
}
// Copies src's children into dst. Copies attributes by value.
// DWAttr.data is copied as pointer only. If except is one of
// the top-level children, it will not be copied.
-func copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) {
+func (d *dwctxt2) 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)
+ c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0)
for a := src.Attr; a != nil; a = a.Link {
newattr(c, a.Atr, int(a.Cls), a.Value, a.Data)
}
- copychildrenexcept(ctxt, c, src, nil)
+ d.copychildrenexcept(ctxt, c, src, nil)
}
reverselist(&dst.Child)
}
-func copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) {
- copychildrenexcept(ctxt, dst, src, nil)
+func (d *dwctxt2) copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) {
+ d.copychildrenexcept(ctxt, dst, src, nil)
}
// Search children (assumed to have TAG_member) for the one named
// field and set its AT_type to dwtype
-func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) {
+func (d *dwctxt2) substitutetype(structdie *dwarf.DWDie, field string, dwtype loader.Sym) {
child := findchild(structdie, field)
if child == nil {
Exitf("dwarf substitutetype: %s does not have member %s",
@@ -647,23 +755,26 @@ func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) {
a := getattr(child, dwarf.DW_AT_type)
if a != nil {
- a.Data = dwtype
+ a.Data = dwSym(dwtype)
} else {
- newrefattr(child, dwarf.DW_AT_type, dwtype)
+ d.newrefattr(child, dwarf.DW_AT_type, dwtype)
}
}
-func findprotodie(ctxt *Link, name string) *dwarf.DWDie {
+func (d *dwctxt2) findprotodie(ctxt *Link, name string) *dwarf.DWDie {
die, ok := prototypedies[name]
if ok && die == nil {
- defgotype(ctxt, lookupOrDiag(ctxt, name))
+ d.defgotype(d.lookupOrDiag(name))
die = prototypedies[name]
}
+ if die == nil {
+ log.Fatalf("internal error: DIE generation failed for %s\n", name)
+ }
return die
}
-func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
- prototype := walktypedef(findprotodie(ctxt, "type.runtime.stringStructDWARF"))
+func (d *dwctxt2) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
+ prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.stringStructDWARF"))
if prototype == nil {
return
}
@@ -672,12 +783,12 @@ func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE {
continue
}
- copychildren(ctxt, die, prototype)
+ d.copychildren(ctxt, die, prototype)
}
}
-func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
- prototype := walktypedef(findprotodie(ctxt, "type.runtime.slice"))
+func (d *dwctxt2) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
+ prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.slice"))
if prototype == nil {
return
}
@@ -686,9 +797,9 @@ func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
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))
+ d.copychildren(ctxt, die, prototype)
+ elem := loader.Sym(getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym))
+ d.substitutetype(die, "array", d.defptrto(elem))
}
}
@@ -706,21 +817,21 @@ const (
BucketSize = 8
)
-func mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) *sym.Symbol {
+func (d *dwctxt2) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) loader.Sym {
name := mkinternaltypename(typename, keyname, valname)
symname := dwarf.InfoPrefix + name
- s := ctxt.Syms.ROLookup(symname, 0)
- if s != nil && s.Type == sym.SDWARFINFO {
+ s := d.ldr.Lookup(symname, 0)
+ if s != 0 && d.ldr.SymType(s) == sym.SDWARFINFO {
return s
}
- die := newdie(ctxt, &dwtypes, abbrev, name, 0)
+ die := d.newdie(&dwtypes, abbrev, name, 0)
f(die)
- return dtolsym(die.Sym)
+ return d.dtolsym(die.Sym)
}
-func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
- hash := walktypedef(findprotodie(ctxt, "type.runtime.hmap"))
- bucket := walktypedef(findprotodie(ctxt, "type.runtime.bmap"))
+func (d *dwctxt2) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
+ hash := walktypedef(d.findprotodie(ctxt, "type.runtime.hmap"))
+ bucket := walktypedef(d.findprotodie(ctxt, "type.runtime.bmap"))
if hash == nil {
return
@@ -730,92 +841,94 @@ func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
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))
+ gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
+ keytype := decodetypeMapKey2(d.ldr, d.arch, gotype)
+ valtype := decodetypeMapValue2(d.ldr, d.arch, gotype)
+ keydata := d.ldr.Data(keytype)
+ valdata := d.ldr.Data(valtype)
+ keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata)
+ keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype))
// compute size info like hashmap.c does.
indirectKey, indirectVal := false, false
if keysize > MaxKeySize {
- keysize = int64(ctxt.Arch.PtrSize)
+ keysize = int64(d.arch.PtrSize)
indirectKey = true
}
if valsize > MaxValSize {
- valsize = int64(ctxt.Arch.PtrSize)
+ valsize = int64(d.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) {
+ keyname := d.nameFromDIESym(keytype)
+ dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) {
newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0)
t := keytype
if indirectKey {
- t = defptrto(ctxt, keytype)
+ t = d.defptrto(keytype)
}
- newrefattr(dwhk, dwarf.DW_AT_type, t)
- fld := newdie(ctxt, dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+ d.newrefattr(dwhk, dwarf.DW_AT_type, t)
+ fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
- newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
})
// 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) {
+ valname := d.nameFromDIESym(valtype)
+ dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) {
newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0)
t := valtype
if indirectVal {
- t = defptrto(ctxt, valtype)
+ t = d.defptrto(valtype)
}
- newrefattr(dwhv, dwarf.DW_AT_type, t)
- fld := newdie(ctxt, dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+ d.newrefattr(dwhv, dwarf.DW_AT_type, t)
+ fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
- newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
})
// Construct bucket<K,V>
- dwhbs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
+ dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
// Copy over all fields except the field "data" from the generic
// bucket. "data" will be replaced with keys/values below.
- copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
+ d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
- fld := newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0)
- newrefattr(fld, dwarf.DW_AT_type, dwhks)
+ fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0)
+ d.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)
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, dwhvs)
newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
- fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0)
- newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, dtolsym(dwhb.Sym)))
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym)))
newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize)))
- if 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))
+ if d.arch.RegSize > d.arch.PtrSize {
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+ newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize))
}
- newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(ctxt.Arch.RegSize), 0)
+ newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(d.arch.RegSize), 0)
})
// Construct hash<K,V>
- dwhs := 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))
+ dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
+ d.copychildren(ctxt, dwh, hash)
+ d.substitutetype(dwh, "buckets", d.defptrto(dwhbs))
+ d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs))
newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil)
})
// make map type a pointer to hash<K,V>
- newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs))
+ d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(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"))
+func (d *dwctxt2) synthesizechantypes(ctxt *Link, die *dwarf.DWDie) {
+ sudog := walktypedef(d.findprotodie(ctxt, "type.runtime.sudog"))
+ waitq := walktypedef(d.findprotodie(ctxt, "type.runtime.waitq"))
+ hchan := walktypedef(d.findprotodie(ctxt, "type.runtime.hchan"))
if sudog == nil || waitq == nil || hchan == nil {
return
}
@@ -826,130 +939,103 @@ func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) {
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))
+ elemgotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
+ tname := d.ldr.SymName(elemgotype)
+ elemname := tname[5:]
+ elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname)))
// sudog<T>
- dwss := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) {
- copychildren(ctxt, dws, sudog)
- substitutetype(dws, "elem", defptrto(ctxt, elemtype))
+ dwss := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) {
+ d.copychildren(ctxt, dws, sudog)
+ d.substitutetype(dws, "elem", d.defptrto(elemtype))
newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil)
})
// waitq<T>
- dwws := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) {
+ dwws := d.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))
+ d.copychildren(ctxt, dww, waitq)
+ d.substitutetype(dww, "first", d.defptrto(dwss))
+ d.substitutetype(dww, "last", d.defptrto(dwss))
newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil)
})
// hchan<T>
- dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) {
- copychildren(ctxt, dwh, hchan)
- substitutetype(dwh, "recvq", dwws)
- substitutetype(dwh, "sendq", dwws)
+ dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) {
+ d.copychildren(ctxt, dwh, hchan)
+ d.substitutetype(dwh, "recvq", dwws)
+ d.substitutetype(dwh, "sendq", dwws)
newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil)
})
- newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs))
+ d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
}
}
-func dwarfDefineGlobal(ctxt *Link, s *sym.Symbol, str string, v int64, gotype *sym.Symbol) {
+func (d *dwctxt2) dwarfDefineGlobal(ctxt *Link, symIdx loader.Sym, str string, v int64, gotype loader.Sym) {
// Find a suitable CU DIE to include the global.
// One would think it's as simple as just looking at the unit, but that might
// not have any reachable code. So, we go to the runtime's CU if our unit
// isn't otherwise reachable.
- var unit *sym.CompilationUnit
- if s.Unit != nil {
- unit = s.Unit
- } else {
+ unit := d.ldr.SymUnit(symIdx)
+ if unit == nil {
unit = ctxt.runtimeCU
}
- dv := newdie(ctxt, unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(s.Version))
- newabslocexprattr(dv, v, s)
- if !s.IsFileLocal() {
+ ver := d.ldr.SymVersion(symIdx)
+ dv := d.newdie(unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(ver))
+ d.newabslocexprattr(dv, v, symIdx)
+ if d.ldr.SymVersion(symIdx) < sym.SymVerStatic {
newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0)
}
- dt := 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)
- }
+ dt := d.defgotype(gotype)
+ d.newrefattr(dv, dwarf.DW_AT_type, dt)
}
// createUnitLength creates the initial length field with value v and update
// offset of unit_length if needed.
-func createUnitLength(ctxt *Link, s *sym.Symbol, v uint64) {
- if isDwarf64(ctxt) {
- s.AddUint32(ctxt.Arch, 0xFFFFFFFF)
+func (d *dwctxt2) createUnitLength(su *loader.SymbolBuilder, v uint64) {
+ if isDwarf64(d.linkctxt) {
+ su.AddUint32(d.arch, 0xFFFFFFFF)
}
- addDwarfAddrField(ctxt, s, v)
+ d.addDwarfAddrField(su, 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)
+func (d *dwctxt2) addDwarfAddrField(sb *loader.SymbolBuilder, v uint64) {
+ if isDwarf64(d.linkctxt) {
+ sb.AddUint(d.arch, v)
} else {
- s.AddUint32(ctxt.Arch, uint32(v))
+ sb.AddUint32(d.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)
+func (d *dwctxt2) addDwarfAddrRef(sb *loader.SymbolBuilder, t loader.Sym) {
+ if isDwarf64(d.linkctxt) {
+ d.adddwarfref(sb, t, 8)
} else {
- adddwarfref(ctxt, s, t, 4)
+ d.adddwarfref(sb, t, 4)
}
}
// calcCompUnitRanges calculates the PC ranges of the compilation units.
-func calcCompUnitRanges(ctxt *Link) {
+func (d *dwctxt2) calcCompUnitRanges() {
var prevUnit *sym.CompilationUnit
- for _, s := range ctxt.Textp {
- if s.FuncInfo == nil {
+ for _, s := range d.linkctxt.Textp2 {
+ sym := loader.Sym(s)
+
+ fi := d.ldr.FuncInfo(sym)
+ if !fi.Valid() {
continue
}
+
// Skip linker-created functions (ex: runtime.addmoduledata), since they
// don't have DWARF to begin with.
- if s.Unit == nil {
+ unit := d.ldr.SymUnit(sym)
+ if unit == nil {
continue
}
- unit := s.Unit
+
// Update PC ranges.
//
// We don't simply compare the end of the previous
@@ -957,11 +1043,13 @@ func calcCompUnitRanges(ctxt *Link) {
// often a little padding between them. Instead, we
// only create boundaries between symbols from
// different units.
+ sval := d.ldr.SymValue(sym)
+ u0val := d.ldr.SymValue(loader.Sym(unit.Textp2[0]))
if prevUnit != unit {
- unit.PCs = append(unit.PCs, dwarf.Range{Start: s.Value - unit.Textp[0].Value})
+ unit.PCs = append(unit.PCs, dwarf.Range{Start: sval - u0val})
prevUnit = unit
}
- unit.PCs[len(unit.PCs)-1].End = s.Value - unit.Textp[0].Value + s.Size
+ unit.PCs[len(unit.PCs)-1].End = sval - u0val + int64(len(d.ldr.Data(sym)))
}
}
@@ -977,24 +1065,6 @@ func movetomodule(ctxt *Link, parent *dwarf.DWDie) {
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
@@ -1020,115 +1090,130 @@ func getCompilationDir() string {
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 (d *dwctxt2) importInfoSymbol(ctxt *Link, dsym loader.Sym) {
+ d.ldr.SetAttrReachable(dsym, true)
+ d.ldr.SetAttrNotInSymbolTable(dsym, true)
+ if d.ldr.SymType(dsym) != sym.SDWARFINFO {
+ log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String())
+ }
+ relocs := d.ldr.Relocs(dsym)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At2(i)
+ if r.Type() != objabi.R_DWARFSECREF {
+ continue
+ }
+ rsym := r.Sym()
+ // If there is an entry for the symbol in our rtmap, then it
+ // means we've processed the type already, and can skip this one.
+ if _, ok := d.rtmap[rsym]; ok {
+ // type already generated
+ continue
}
+ // FIXME: is there a way we could avoid materializing the
+ // symbol name here?
+ sn := d.ldr.SymName(rsym)
+ tn := sn[len(dwarf.InfoPrefix):]
+ ts := d.ldr.Lookup("type."+tn, 0)
+ d.defgotype(ts)
}
}
-func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
+func expandFile(fname string) string {
+ if strings.HasPrefix(fname, src.FileSymPrefix) {
+ fname = fname[len(src.FileSymPrefix):]
+ }
+ return expandGoroot(fname)
+}
+
+func expandFileSym(l *loader.Loader, fsym loader.Sym) string {
+ return expandFile(l.SymName(fsym))
+}
+
+func (d *dwctxt2) writelines(unit *sym.CompilationUnit, ls loader.Sym) {
- 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)
+ lsu := d.ldr.MakeSymbolUpdater(ls)
+ newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, lsu.Size(), dwSym(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
+ unitLengthOffset := lsu.Size()
+ d.createUnitLength(lsu, 0) // unit_length (*), filled in at end
+
+ unitstart = lsu.Size()
+ lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05
+ headerLengthOffset := lsu.Size()
+ d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end
+ headerstart = lsu.Size()
// cpos == unitstart + 4 + 2 + 4
- 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)
+ lsu.AddUint8(1) // minimum_instruction_length
+ lsu.AddUint8(is_stmt) // default_is_stmt
+ lsu.AddUint8(LINE_BASE & 0xFF) // line_base
+ lsu.AddUint8(LINE_RANGE) // line_range
+ lsu.AddUint8(OPCODE_BASE) // opcode_base
+ lsu.AddUint8(0) // standard_opcode_lengths[1]
+ lsu.AddUint8(1) // standard_opcode_lengths[2]
+ lsu.AddUint8(1) // standard_opcode_lengths[3]
+ lsu.AddUint8(1) // standard_opcode_lengths[4]
+ lsu.AddUint8(1) // standard_opcode_lengths[5]
+ lsu.AddUint8(0) // standard_opcode_lengths[6]
+ lsu.AddUint8(0) // standard_opcode_lengths[7]
+ lsu.AddUint8(0) // standard_opcode_lengths[8]
+ lsu.AddUint8(1) // standard_opcode_lengths[9]
+ lsu.AddUint8(0) // standard_opcode_lengths[10]
+ lsu.AddUint8(0) // include_directories (empty)
// Copy over the file table.
fileNums := make(map[string]int)
+ lsDwsym := dwSym(ls)
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 := expandFile(name)
+ if len(name) == 0 {
+ // Can't have empty filenames, and having a unique
+ // filename is quite useful for debugging.
name = fmt.Sprintf("<missing>_%d", i)
}
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
+ d.AddString(lsDwsym, name)
+ lsu.AddUint8(0)
+ lsu.AddUint8(0)
+ lsu.AddUint8(0)
+ if gdbscript == "" {
+ // We can't use something that may be dead-code
+ // eliminated from a binary here. proc.go contains
+ // main and the scheduler, so it's not going anywhere.
+ if i := strings.Index(name, "runtime/proc.go"); i >= 0 {
+ k := strings.Index(name, "runtime/proc.go")
+ gdbscript = name[:k] + "runtime/runtime-gdb.py"
}
- 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)
+ lsu.AddUint8(0)
// terminate file_names.
- headerend = ls.Size
+ headerend = lsu.Size()
// Output the state machine for each function remaining.
var lastAddr int64
- for _, s := range unit.Textp {
- finddebugruntimepath(s)
+ for _, s := range unit.Textp2 {
+ fnSym := loader.Sym(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)
+ lsu.AddUint8(0)
+ dwarf.Uleb128put(d, lsDwsym, 1+int64(d.arch.PtrSize))
+ lsu.AddUint8(dwarf.DW_LNE_set_address)
+ addr := lsu.AddAddrPlus(d.arch, fnSym, 0)
// Make sure the units are sorted.
if addr < lastAddr {
- Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr)
+ d.linkctxt.Errorf(fnSym, "address wasn't increasing %x < %x",
+ addr, lastAddr)
}
lastAddr = addr
@@ -1138,84 +1223,44 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
// 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...)
+ _, _, _, lines := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+ if lines != 0 {
+ lsu.AddBytes(d.ldr.Data(lines))
}
}
- ls.AddUint8(0) // start extended opcode
- dwarf.Uleb128put(dwarfctxt, ls, 1)
- ls.AddUint8(dwarf.DW_LNE_end_sequence)
+ lsu.AddUint8(0) // start extended opcode
+ dwarf.Uleb128put(d, lsDwsym, 1)
+ lsu.AddUint8(dwarf.DW_LNE_end_sequence)
- if ctxt.HeadType == objabi.Haix {
- saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(ls.Size-unitLengthOffset))
+ if d.linkctxt.HeadType == objabi.Haix {
+ saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(lsu.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))
+ if isDwarf64(d.linkctxt) {
+ lsu.SetUint(d.arch, unitLengthOffset+4, uint64(lsu.Size()-unitstart)) // +4 because of 0xFFFFFFFF
+ lsu.SetUint(d.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
- }
- }
- }
+ lsu.SetUint32(d.arch, unitLengthOffset, uint32(lsu.Size()-unitstart))
+ lsu.SetUint32(d.arch, headerLengthOffset, uint32(headerend-headerstart))
}
}
// 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}
+func (d *dwctxt2) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, ranges loader.Sym) {
+
+ rsu := d.ldr.MakeSymbolUpdater(ranges)
+ rDwSym := dwSym(ranges)
- unitLengthOffset := ranges.Size
+ unitLengthOffset := rsu.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)
+ newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym)
+ newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base))
+ dwarf.PutBasedRanges(d, rDwSym, pcs)
- if ctxt.HeadType == objabi.Haix {
- addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(ranges.Size-unitLengthOffset))
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rsu.Size()-unitLengthOffset))
}
-
}
/*
@@ -1246,84 +1291,91 @@ func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte {
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
+func (d *dwctxt2) writeframes(syms []loader.Sym) []loader.Sym {
+ fs := d.ldr.LookupOrCreateSym(".debug_frame", 0)
+ fsd := dwSym(fs)
+ fsu := d.ldr.MakeSymbolUpdater(fs)
+ fsu.SetType(sym.SDWARFSECT)
syms = append(syms, fs)
+ isdw64 := isDwarf64(d.linkctxt)
+ haslr := haslinkregister(d.linkctxt)
// Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64
lengthFieldSize := int64(4)
- if isDwarf64(ctxt) {
+ if isdw64 {
lengthFieldSize += 8
}
// Emit the CIE, Section 6.4.1
cieReserve := uint32(16)
- if haslinkregister(ctxt) {
+ if haslr {
cieReserve = 32
}
- if isDwarf64(ctxt) {
+ if isdw64 {
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.
+ d.createUnitLength(fsu, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize
+ d.addDwarfAddrField(fsu, ^uint64(0)) // cid
+ fsu.AddUint8(3) // dwarf version (appendix F)
+ fsu.AddUint8(0) // augmentation ""
+ dwarf.Uleb128put(d, fsd, 1) // code_alignment_factor
+ dwarf.Sleb128put(d, fsd, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // return_address_register
+
+ fsu.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address..
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
+ if haslr {
+ dwarf.Uleb128put(d, fsd, int64(0)) // ...plus a 0 offset.
+
+ fsu.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr))
+
+ fsu.AddUint8(dwarf.DW_CFA_val_offset) // The previous value...
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...of the platform's SP register...
+ dwarf.Uleb128put(d, fsd, int64(0)) // ...is CFA+0.
} else {
- dwarf.Uleb128put(dwarfctxt, fs, int64(ctxt.Arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
+ dwarf.Uleb128put(d, fsd, int64(d.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)].
+ fsu.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value...
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // ...of the return address...
+ dwarf.Uleb128put(d, fsd, int64(-d.arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
}
- pad := int64(cieReserve) + lengthFieldSize - fs.Size
+ pad := int64(cieReserve) + lengthFieldSize - int64(len(d.ldr.Data(fs)))
if pad < 0 {
Exitf("dwarf: cieReserve too small by %d bytes.", -pad)
}
- fs.AddBytes(zeros[:pad])
+ fsu.AddBytes(zeros[:pad])
var deltaBuf []byte
- pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- for _, s := range ctxt.Textp {
- if s.FuncInfo == nil {
+ pcsp := obj.NewPCIter(uint32(d.arch.MinLC))
+ for _, s := range d.linkctxt.Textp2 {
+ fn := loader.Sym(s)
+ fi := d.ldr.FuncInfo(fn)
+ if !fi.Valid() {
continue
}
+ fpcsp := fi.Pcsp()
// Emit a FDE, Section 6.4.1.
// First build the section contents into a byte buffer.
deltaBuf = deltaBuf[:0]
- if haslinkregister(ctxt) && s.Attr.TopFrame() {
+ if haslr && d.ldr.AttrTopFrame(fn) {
// Mark the link register as having an undefined value.
// This stops call stack unwinders progressing any further.
// TODO: similar mark on non-LR architectures.
deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined)
deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
}
- for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() {
+
+ for pcsp.Init(fpcsp); !pcsp.Done; pcsp.Next() {
nextpc := pcsp.NextPC
// pciterinit goes up to the end of the function,
// but DWARF expects us to stop just before the end.
- if int64(nextpc) == s.Size {
+ if int64(nextpc) == int64(len(d.ldr.Data(fn))) {
nextpc--
if nextpc < pcsp.PC {
continue
@@ -1331,12 +1383,12 @@ func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
}
spdelta := int64(pcsp.Value)
- if !haslinkregister(ctxt) {
+ if !haslr {
// Return address has been pushed onto stack.
- spdelta += int64(ctxt.Arch.PtrSize)
+ spdelta += int64(d.arch.PtrSize)
}
- if haslinkregister(ctxt) && !s.Attr.TopFrame() {
+ if haslr && !d.ldr.AttrTopFrame(fn) {
// TODO(bryanpkc): This is imprecise. In general, the instruction
// that stores the return address to the stack frame is not the
// same one that allocates the frame.
@@ -1354,9 +1406,9 @@ func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
}
}
- deltaBuf = appendPCDeltaCFA(ctxt.Arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta)
+ deltaBuf = appendPCDeltaCFA(d.arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta)
}
- pad := int(Rnd(int64(len(deltaBuf)), int64(ctxt.Arch.PtrSize))) - len(deltaBuf)
+ pad := int(Rnd(int64(len(deltaBuf)), int64(d.arch.PtrSize))) - len(deltaBuf)
deltaBuf = append(deltaBuf, zeros[:pad]...)
// Emit the FDE header, Section 6.4.1.
@@ -1365,48 +1417,61 @@ func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
// ptrsize: initial location
// ptrsize: address range
- fdeLength := uint64(4 + 2*ctxt.Arch.PtrSize + len(deltaBuf))
- if isDwarf64(ctxt) {
+ fdeLength := uint64(4 + 2*d.arch.PtrSize + len(deltaBuf))
+ if isdw64 {
fdeLength += 4 // 4 bytes added for CIE pointer
}
- createUnitLength(ctxt, fs, fdeLength)
+ d.createUnitLength(fsu, fdeLength)
- if ctxt.LinkMode == LinkExternal {
- addDwarfAddrRef(ctxt, fs, fs)
+ if d.linkctxt.LinkMode == LinkExternal {
+ d.addDwarfAddrRef(fsu, fs)
} else {
- addDwarfAddrField(ctxt, fs, 0) // CIE offset
+ d.addDwarfAddrField(fsu, 0) // CIE offset
}
- fs.AddAddr(ctxt.Arch, s)
- fs.AddUintXX(ctxt.Arch, uint64(s.Size), ctxt.Arch.PtrSize) // address range
- fs.AddBytes(deltaBuf)
+ fsu.AddAddrPlus(d.arch, s, 0)
+ fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range
+ fsu.AddBytes(deltaBuf)
- if ctxt.HeadType == objabi.Haix {
- addDwsectCUSize(".debug_frame", s.File, fdeLength+uint64(lengthFieldSize))
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_frame", d.ldr.SymFile(fn), 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)
+// appendSyms appends the syms from 'src' into 'syms' and returns the
+// result. This can go away once we do away with sym.LoaderSym
+// entirely.
+func appendSyms(syms []loader.Sym, src []sym.LoaderSym) []loader.Sym {
+ for _, s := range src {
+ syms = append(syms, loader.Sym(s))
+ }
+ return syms
+}
- var dwarfctxt dwarf.Context = dwctxt{ctxt}
+func (d *dwctxt2) writeinfo(syms []loader.Sym, units []*sym.CompilationUnit, abbrevsym loader.Sym, pubNames, pubTypes *pubWriter2) []loader.Sym {
+
+ infosec := d.ldr.LookupOrCreateSym(".debug_info", 0)
+ disu := d.ldr.MakeSymbolUpdater(infosec)
+ disu.SetType(sym.SDWARFINFO)
+ d.ldr.SetAttrReachable(infosec, true)
+ syms = append(syms, infosec)
for _, u := range units {
compunit := u.DWInfo
- s := dtolsym(compunit.Sym)
+ s := d.dtolsym(compunit.Sym)
+ su := d.ldr.MakeSymbolUpdater(s)
- if len(u.Textp) == 0 && u.DWInfo.Child == nil {
+ if len(u.Textp2) == 0 && u.DWInfo.Child == nil {
continue
}
@@ -1416,59 +1481,62 @@ func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abb
// 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)
+ d.createUnitLength(su, 0) // unit_length (*), will be filled in later.
+ su.AddUint16(d.arch, 4) // dwarf version (appendix F)
// debug_abbrev_offset (*)
- addDwarfAddrRef(ctxt, s, abbrevsym)
+ d.addDwarfAddrRef(su, abbrevsym)
- s.AddUint8(uint8(ctxt.Arch.PtrSize)) // address_size
+ su.AddUint8(uint8(d.arch.PtrSize)) // address_size
- dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev))
- dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr)
+ ds := dwSym(s)
+ dwarf.Uleb128put(d, ds, int64(compunit.Abbrev))
+ dwarf.PutAttrs(d, ds, 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)
+ cu := []loader.Sym{s}
+ cu = appendSyms(cu, u.AbsFnDIEs2)
+ cu = appendSyms(cu, u.FuncDIEs2)
+ if u.Consts2 != 0 {
+ cu = append(cu, loader.Sym(u.Consts2))
}
var cusize int64
for _, child := range cu {
- cusize += child.Size
+ cusize += int64(len(d.ldr.Data(child)))
}
for die := compunit.Child; die != nil; die = die.Link {
l := len(cu)
- lastSymSz := cu[l-1].Size
- cu = putdie(ctxt, dwarfctxt, cu, die)
+ lastSymSz := int64(len(d.ldr.Data(cu[l-1])))
+ cu = d.putdie(cu, die)
if ispubname(die) {
pubNames.add(die, cusize)
}
if ispubtype(die) {
pubTypes.add(die, cusize)
}
- if lastSymSz != cu[l-1].Size {
+ if lastSymSz != int64(len(d.ldr.Data(cu[l-1]))) {
// putdie will sometimes append directly to the last symbol of the list
- cusize = cusize - lastSymSz + cu[l-1].Size
+ cusize = cusize - lastSymSz + int64(len(d.ldr.Data(cu[l-1])))
}
for _, child := range cu[l:] {
- cusize += child.Size
+ cusize += int64(len(d.ldr.Data(child)))
}
}
- cu[len(cu)-1].AddUint8(0) // closes compilation unit DIE
+
+ culu := d.ldr.MakeSymbolUpdater(cu[len(cu)-1])
+ culu.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 d.linkctxt.HeadType == objabi.Haix {
+ saveDwsectCUSize(".debug_info", d.getPkgFromCUSym(s), uint64(cusize))
}
- if isDwarf64(ctxt) {
- cusize -= 12 // exclude the length field.
- s.SetUint(ctxt.Arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF
+ if isDwarf64(d.linkctxt) {
+ cusize -= 12 // exclude the length field.
+ su.SetUint(d.arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF
} else {
cusize -= 4 // exclude the length field.
- s.SetUint32(ctxt.Arch, 0, uint32(cusize))
+ su.SetUint32(d.arch, 0, uint32(cusize))
}
pubNames.endCompUnit(compunit, uint32(cusize)+4)
pubTypes.endCompUnit(compunit, uint32(cusize)+4)
@@ -1481,79 +1549,81 @@ func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abb
* 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
+type pubWriter2 struct {
+ d *dwctxt2
+ s loader.Sym
+ su *loader.SymbolBuilder
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 newPubWriter2(d *dwctxt2, sname string) *pubWriter2 {
+ s := d.ldr.LookupOrCreateSym(sname, 0)
+ u := d.ldr.MakeSymbolUpdater(s)
+ u.SetType(sym.SDWARFSECT)
+ return &pubWriter2{d: d, s: s, su: u, sname: sname}
}
-func (pw *pubWriter) beginCompUnit(compunit *dwarf.DWDie) {
- pw.sectionstart = pw.s.Size
+func (pw *pubWriter2) beginCompUnit(compunit *dwarf.DWDie) {
+ pw.sectionstart = pw.su.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.
-
+ pw.d.createUnitLength(pw.su, 0) // unit_length (*), will be filled in later.
+ pw.su.AddUint16(pw.d.arch, 2) // dwarf version (appendix F)
+ pw.d.addDwarfAddrRef(pw.su, pw.d.dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header)
+ pw.culengthOff = pw.su.Size()
+ pw.d.addDwarfAddrField(pw.su, uint64(0)) // debug_info_length, will be filled in later.
}
-func (pw *pubWriter) add(die *dwarf.DWDie, offset int64) {
+func (pw *pubWriter2) add(die *dwarf.DWDie, offset int64) {
dwa := getattr(die, dwarf.DW_AT_name)
name := dwa.Data.(string)
- if die.Sym == nil {
+ if pw.d.dtolsym(die.Sym) == 0 {
fmt.Println("Missing sym for ", name)
}
- addDwarfAddrField(pw.ctxt, pw.s, uint64(offset))
- Addstring(pw.s, name)
+ pw.d.addDwarfAddrField(pw.su, uint64(offset))
+ pw.su.Addstring(name)
}
-func (pw *pubWriter) endCompUnit(compunit *dwarf.DWDie, culength uint32) {
- addDwarfAddrField(pw.ctxt, pw.s, 0) // Null offset
+func (pw *pubWriter2) endCompUnit(compunit *dwarf.DWDie, culength uint32) {
+ pw.d.addDwarfAddrField(pw.su, 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 pw.d.linkctxt.HeadType == objabi.Haix {
+ saveDwsectCUSize(pw.sname, pw.d.getPkgFromCUSym(pw.d.dtolsym(compunit.Sym)), uint64(pw.su.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))
+ if isDwarf64(pw.d.linkctxt) {
+ pw.su.SetUint(pw.d.arch, pw.sectionstart+4, uint64(pw.su.Size()-pw.sectionstart)-12) // exclude the length field.
+ pw.su.SetUint(pw.d.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)
+ pw.su.SetUint32(pw.d.arch, pw.sectionstart, uint32(pw.su.Size()-pw.sectionstart)-4) // exclude the length field.
+ pw.su.SetUint32(pw.d.arch, pw.culengthOff, culength)
+ }
+}
+
+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
}
-func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
+func (d *dwctxt2) writegdbscript(syms []loader.Sym) []loader.Sym {
// TODO (aix): make it available
- if ctxt.HeadType == objabi.Haix {
+ if d.linkctxt.HeadType == objabi.Haix {
return syms
}
- if ctxt.LinkMode == LinkExternal && ctxt.HeadType == objabi.Hwindows && ctxt.BuildMode == BuildModeCArchive {
+ if d.linkctxt.LinkMode == LinkExternal && d.linkctxt.HeadType == objabi.Hwindows && d.linkctxt.BuildMode == BuildModeCArchive {
// gcc on Windows places .debug_gdb_scripts in the wrong location, which
// causes the program not to run. See https://golang.org/issue/20183
// Non c-archives can avoid this issue via a linker script
@@ -1564,16 +1634,22 @@ func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol {
}
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)
+ gs := d.ldr.LookupOrCreateSym(".debug_gdb_scripts", 0)
+ u := d.ldr.MakeSymbolUpdater(gs)
+ u.SetType(sym.SDWARFSECT)
+
+ syms = append(syms, gs)
+ u.AddUint8(1) // magic 1 byte?
+ u.Addstring(gdbscript)
}
return syms
+
}
+// FIXME: might be worth looking replacing this map with a function
+// that switches based on symbol instead.
+
var prototypedies map[string]*dwarf.DWDie
func dwarfEnabled(ctxt *Link) bool {
@@ -1606,11 +1682,30 @@ func dwarfEnabled(ctxt *Link) bool {
return true
}
+// mkBuiltinType populates the dwctxt2 sym lookup maps for the
+// newly created builtin type DIE 'typeDie'.
+func (d *dwctxt2) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie {
+ // create type DIE
+ die := d.newdie(&dwtypes, abrv, tname, 0)
+
+ // Look up type symbol.
+ gotype := d.lookupOrDiag("type." + tname)
+
+ // Map from die sym to type sym
+ ds := loader.Sym(die.Sym.(dwSym))
+ d.rtmap[ds] = gotype
+
+ // Map from type to def sym
+ d.tdmap[gotype] = ds
+
+ return die
+}
+
// 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
+// 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) {
@@ -1618,25 +1713,29 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
return
}
+ d := newdwctxt2(ctxt, true)
+
if ctxt.HeadType == objabi.Haix {
// Initial map used to store package size for each DWARF section.
dwsectCUSize = make(map[string]uint64)
}
- // Forctxt.Diagnostic messages.
+ // For ctxt.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)
+ // Unspecified type. There are no references to this in the symbol table.
+ d.newdie(&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
+ // Some types that must exist to define other ones (uintptr in particular
+ // is needed for array size)
+ d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer")
+ die := d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BASETYPE, "uintptr")
newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
- newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(ctxt.Arch.PtrSize), 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(d.arch.PtrSize), 0)
newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0)
- newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, lookupOrDiag(ctxt, "type.uintptr"))
+ newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, dwSym(d.lookupOrDiag("type.uintptr")))
+
+ d.uintptrInfoSym = d.mustFind("uintptr")
// Prototypes needed for type synthesis.
prototypedies = map[string]*dwarf.DWDie{
@@ -1662,7 +1761,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
"type.runtime.interfacetype",
"type.runtime.itab",
"type.runtime.imethod"} {
- defgotype(ctxt, lookupOrDiag(ctxt, typ))
+ d.defgotype(d.lookupOrDiag(typ))
}
// fake root DIE for compile unit DIEs
@@ -1670,15 +1769,15 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
flagVariants := make(map[string]bool)
for _, lib := range ctxt.Library {
- consts := ctxt.Syms.ROLookup(dwarf.ConstInfoPrefix+lib.Pkg, 0)
+
+ consts := d.ldr.Lookup(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
+ if consts != 0 {
+ unit.Consts2 = sym.LoaderSym(consts)
+ d.importInfoSymbol(ctxt, consts)
+ consts = 0
}
-
ctxt.compUnits = append(ctxt.compUnits, unit)
// We need at least one runtime unit.
@@ -1686,7 +1785,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
ctxt.runtimeCU = unit
}
- unit.DWInfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0)
+ unit.DWInfo = d.newdie(&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()
@@ -1694,17 +1793,21 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
// 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)
+
+ var peData []byte
+ if producerExtra := d.ldr.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0); producerExtra != 0 {
+ peData = d.ldr.Data(producerExtra)
+ }
producer := "Go cmd/compile " + objabi.Version
- if len(producerExtra.P) > 0 {
+ if len(peData) > 0 {
// We put a semicolon before the flags to clearly
// separate them from the version, which can be long
// and have lots of weird things in it in development
// versions. We promise not to put a semicolon in the
// version, so it should be safe for readers to scan
// forward to the semicolon.
- producer += "; " + string(producerExtra.P)
- flagVariants[string(producerExtra.P)] = true
+ producer += "; " + string(peData)
+ flagVariants[string(peData)] = true
} else {
flagVariants[""] = true
}
@@ -1712,12 +1815,13 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
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)
+ if pnSymIdx := d.ldr.Lookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); pnSymIdx != 0 {
+ pnsData := d.ldr.Data(pnSymIdx)
+ pkgname = string(pnsData)
}
newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname)
- if len(unit.Textp) == 0 {
+ if len(unit.Textp2) == 0 {
unit.DWInfo.Abbrev = dwarf.DW_ABRV_COMPUNIT_TEXTLESS
}
@@ -1725,36 +1829,50 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
// 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
+ for _, s := range unit.Textp2 { // textp2 has been dead-code-eliminated already.
+ fnSym := loader.Sym(s)
+ infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+ if infosym == 0 {
+ continue
+ }
+ d.ldr.SetAttrNotInSymbolTable(infosym, true)
+ d.ldr.SetAttrReachable(infosym, true)
+
+ unit.FuncDIEs2 = append(unit.FuncDIEs2, sym.LoaderSym(infosym))
+ if rangesym != 0 {
+ rs := len(d.ldr.Data(rangesym))
+ d.ldr.SetAttrNotInSymbolTable(rangesym, true)
+ d.ldr.SetAttrReachable(rangesym, true)
if ctxt.HeadType == objabi.Haix {
- addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rangeSym.Size))
+ addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rs))
}
- unit.RangeSyms = append(unit.RangeSyms, rangeSym)
+ unit.RangeSyms2 = append(unit.RangeSyms2, sym.LoaderSym(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() {
+ drelocs := d.ldr.Relocs(infosym)
+ for ri := 0; ri < drelocs.Count(); ri++ {
+ r := drelocs.At2(ri)
+ if r.Type() == objabi.R_DWARFSECREF {
+ rsym := r.Sym()
+ rsn := d.ldr.SymName(rsym)
+ if len(rsn) == 0 {
+ continue
+ }
+ // NB: there should be a better way to do this that doesn't involve materializing the symbol name and doing string prefix+suffix checks.
+ if strings.HasPrefix(rsn, dwarf.InfoPrefix) && strings.HasSuffix(rsn, dwarf.AbstractFuncSuffix) && !d.ldr.AttrOnList(rsym) {
// 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))
+ d.ldr.SetAttrOnList(rsym, true)
+ unit.AbsFnDIEs2 = append(unit.AbsFnDIEs2, sym.LoaderSym(rsym))
+ d.importInfoSymbol(ctxt, rsym)
+ continue
+ }
+ if _, ok := d.rtmap[rsym]; ok {
+ // type already generated
+ continue
}
+ tn := rsn[len(dwarf.InfoPrefix):]
+ ts := d.ldr.Lookup("type."+tn, 0)
+ d.defgotype(ts)
}
}
}
@@ -1769,19 +1887,55 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
}
// Create DIEs for global variables and the types they use.
- genasmsym(ctxt, defdwsymb)
+ // FIXME: ideally this should be done in the compiler, since
+ // for globals there isn't any abiguity about which package
+ // a global belongs to.
+ for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ {
+ if !d.ldr.AttrReachable(idx) ||
+ d.ldr.AttrNotInSymbolTable(idx) ||
+ d.ldr.SymVersion(idx) >= sym.SymVerStatic {
+ continue
+ }
+ t := d.ldr.SymType(idx)
+ switch t {
+ case sym.SRODATA, sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS:
+ // ok
+ default:
+ continue
+ }
+ // Skip things with no type
+ if d.ldr.SymGoType(idx) == 0 {
+ continue
+ }
+
+ sn := d.ldr.SymName(idx)
+ if ctxt.LinkMode != LinkExternal && isStaticTemp(sn) {
+ continue
+ }
+ if sn == "" {
+ // skip aux symbols
+ continue
+ }
+
+ // Create DIE for global.
+ sv := d.ldr.SymValue(idx)
+ gt := d.ldr.SymGoType(idx)
+ d.dwarfDefineGlobal(ctxt, idx, sn, sv, gt)
+ }
// 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}
+ lists := [][]sym.LoaderSym{unit.AbsFnDIEs2, unit.FuncDIEs2}
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)
+ symIdx := loader.Sym(s)
+ relocs := d.ldr.Relocs(symIdx)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At2(i)
+ if r.Type() == objabi.R_USETYPE {
+ d.defgotype(r.Sym())
}
}
}
@@ -1789,77 +1943,118 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
}
}
- synthesizestringtypes(ctxt, dwtypes.Child)
- synthesizeslicetypes(ctxt, dwtypes.Child)
- synthesizemaptypes(ctxt, dwtypes.Child)
- synthesizechantypes(ctxt, dwtypes.Child)
+ d.synthesizestringtypes(ctxt, dwtypes.Child)
+ d.synthesizeslicetypes(ctxt, dwtypes.Child)
+ d.synthesizemaptypes(ctxt, dwtypes.Child)
+ d.synthesizechantypes(ctxt, dwtypes.Child)
+
+ // NB: at this stage we have all the DIE objects constructed, but
+ // they have loader.Sym attributes and not sym.Symbol attributes.
+ // At the point when loadlibfull runs we will need to visit
+ // every DIE constructed and convert the symbols.
}
// 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.
+// section using symbols generated in dwarfGenerateDebugInfo2.
func dwarfGenerateDebugSyms(ctxt *Link) {
if !dwarfEnabled(ctxt) {
return
}
+ d := &dwctxt2{
+ linkctxt: ctxt,
+ ldr: ctxt.loader,
+ arch: ctxt.Arch,
+ }
+ d.dwarfGenerateDebugSyms()
+}
- abbrev := writeabbrev(ctxt)
- syms := []*sym.Symbol{abbrev}
+func (d *dwctxt2) dwarfGenerateDebugSyms() {
- calcCompUnitRanges(ctxt)
- sort.Sort(compilationUnitByStartPC(ctxt.compUnits))
+ // Hack: because the "wavefront" hasn't been pushed all the way
+ // up to dodata(), there will have been changes made to the sym.Symbol's
+ // that are not yet reflected in the loader. Call a temporary
+ // loader routine that copies any changes back.
+ // WARNING: changing a symbol's content will usually require
+ // calling the loader cloneToExternal method, meaning that there
+ // can be an increase in memory, so this is likely to mess up any
+ // benchmarking runs.
+ d.ldr.PropagateSymbolChangesBackToLoader()
- // 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
+ abbrev := d.writeabbrev()
+ syms := []loader.Sym{abbrev}
+
+ d.calcCompUnitRanges()
+ sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits))
+
+ // Create .debug_line and .debug_ranges section symbols
+ debugLine := d.ldr.LookupOrCreateSym(".debug_line", 0)
+ dlu := d.ldr.MakeSymbolUpdater(debugLine)
+ dlu.SetType(sym.SDWARFSECT)
+ d.ldr.SetAttrReachable(debugLine, true)
syms = append(syms, debugLine)
- for _, u := range ctxt.compUnits {
+
+ debugRanges := d.ldr.LookupOrCreateSym(".debug_ranges", 0)
+ dru := d.ldr.MakeSymbolUpdater(debugRanges)
+ dru.SetType(sym.SDWARFRANGE)
+ d.ldr.SetAttrReachable(debugRanges, true)
+
+ // Write per-package line and range tables and start their CU DIEs.
+ for _, u := range d.linkctxt.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)
+ d.writelines(u, debugLine)
+ base := loader.Sym(u.Textp2[0])
+ d.writepcranges(u, base, 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)
+ movetomodule(d.linkctxt, &dwtypes)
- pubNames := newPubWriter(ctxt, ".debug_pubnames")
- pubTypes := newPubWriter(ctxt, ".debug_pubtypes")
+ pubNames := newPubWriter2(d, ".debug_pubnames")
+ pubTypes := newPubWriter2(d, ".debug_pubtypes")
// Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT
- infosyms := writeinfo(ctxt, nil, ctxt.compUnits, abbrev, pubNames, pubTypes)
+ infosyms := d.writeinfo(nil, d.linkctxt.compUnits, abbrev, pubNames, pubTypes)
- syms = writeframes(ctxt, syms)
+ syms = d.writeframes(syms)
syms = append(syms, pubNames.s, pubTypes.s)
- syms = writegdbscript(ctxt, syms)
- // Now we're done writing SDWARFSECT symbols, so we can write
+ syms = d.writegdbscript(syms)
+ // We are now done writing SDWARFSECT symbols, so we can write
// other SDWARF* symbols.
syms = append(syms, infosyms...)
- syms = collectlocs(ctxt, syms, ctxt.compUnits)
+ syms = d.collectlocs(syms, d.linkctxt.compUnits)
syms = append(syms, debugRanges)
- for _, unit := range ctxt.compUnits {
- syms = append(syms, unit.RangeSyms...)
+ for _, unit := range d.linkctxt.compUnits {
+ for _, s := range unit.RangeSyms2 {
+ syms = append(syms, loader.Sym(s))
+ }
}
- dwarfp = syms
+ dwarfp2 = syms
+ anonVerReplacement := d.linkctxt.Syms.IncVersion()
+ dwarfp = d.ldr.PropagateLoaderChangesToSymbols(dwarfp2, anonVerReplacement)
}
-func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) []*sym.Symbol {
+func (d *dwctxt2) collectlocs(syms []loader.Sym, units []*sym.CompilationUnit) []loader.Sym {
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)
+ for _, fn := range u.FuncDIEs2 {
+ relocs := d.ldr.Relocs(loader.Sym(fn))
+ for i := 0; i < relocs.Count(); i++ {
+ reloc := relocs.At2(i)
+ if reloc.Type() != objabi.R_DWARFSECREF {
+ continue
+ }
+ rsym := reloc.Sym()
+ if d.ldr.SymType(rsym) == sym.SDWARFLOC {
+ d.ldr.SetAttrReachable(rsym, true)
+ d.ldr.SetAttrNotInSymbolTable(rsym, true)
+ syms = append(syms, rsym)
empty = false
// One location list entry per function, but many relocations to it. Don't duplicate.
break
@@ -1867,152 +2062,44 @@ func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) [
}
}
}
+
// 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
+ locsym := d.ldr.LookupOrCreateSym(".debug_loc", 0)
+ u := d.ldr.MakeSymbolUpdater(locsym)
+ u.SetType(sym.SDWARFLOC)
+ d.ldr.SetAttrReachable(locsym, true)
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)
- }
- }
+func (d *dwctxt2) dwarfaddshstrings(ctxt *Link, shstrtab loader.Sym) {
+ panic("not yet implemented")
}
// 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)
- }
+func (d *dwctxt2) dwarfaddelfsectionsyms(ctxt *Link) {
+ panic("not yet implemented")
}
// 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
+func (d *dwctxt2) dwarfcompress(ctxt *Link) {
+ panic("not yet implemented")
}
-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
- }
+// getPkgFromCUSym returns the package name for the compilation unit
+// represented by s.
+// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get
+// the package name.
+func (d *dwctxt2) getPkgFromCUSym(s loader.Sym) string {
+ return strings.TrimPrefix(d.ldr.SymName(s), dwarf.InfoPrefix+".pkg.")
}
// On AIX, the symbol table needs to know where are the compilation units parts
@@ -2034,11 +2121,3 @@ func saveDwsectCUSize(sname string, pkgname string, size uint64) {
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/link/internal/ld/dwarf2.go b/src/cmd/link/internal/ld/dwarf2.go
new file mode 100644
index 0000000000..233cd6a784
--- /dev/null
+++ b/src/cmd/link/internal/ld/dwarf2.go
@@ -0,0 +1,172 @@
+// 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/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "log"
+)
+
+func isDwarf64(ctxt *Link) bool {
+ return ctxt.HeadType == objabi.Haix
+}
+
+var dwarfp []*sym.Symbol
+
+/*
+ * Elf.
+ */
+func dwarfaddshstrings(ctxt *Link, shstrtab *loader.SymbolBuilder) {
+ if *FlagW { // disable dwarf
+ return
+ }
+
+ secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"}
+ for _, sec := range secs {
+ shstrtab.Addstring(".debug_" + sec)
+ if ctxt.IsExternal() {
+ shstrtab.Addstring(elfRelType + ".debug_" + sec)
+ } else {
+ shstrtab.Addstring(".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) {
+ // compressedSect is a helper type for parallelizing compression.
+ type compressedSect struct {
+ index int
+ compressed []byte
+ syms []*sym.Symbol
+ }
+
+ supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin
+ if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal {
+ return
+ }
+
+ var start, compressedCount int
+ resChannel := make(chan compressedSect)
+ 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 {
+ go func(resIndex int, syms []*sym.Symbol) {
+ resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms}
+ }(compressedCount, dwarfp[start:i+1])
+ compressedCount++
+ start = i + 1
+ }
+ }
+ res := make([]compressedSect, compressedCount)
+ for ; compressedCount > 0; compressedCount-- {
+ r := <-resChannel
+ res[r.index] = r
+ }
+
+ var newDwarfp []*sym.Symbol
+ Segdwarf.Sections = Segdwarf.Sections[:0]
+ for _, z := range res {
+ s := z.syms[0]
+ if z.compressed == nil {
+ // Compression didn't help.
+ newDwarfp = append(newDwarfp, z.syms...)
+ 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(z.compressed))
+ newSym := ctxt.Syms.Lookup(compressedSegName, 0)
+ newSym.P = z.compressed
+ newSym.Size = int64(len(z.compressed))
+ newSym.Sect = sect
+ newDwarfp = append(newDwarfp, newSym)
+ }
+ }
+ dwarfp = newDwarfp
+
+ // Re-compute the locations of the compressed DWARF symbols
+ // and sections, since the layout of these within the file is
+ // based on Section.Vaddr and Symbol.Value.
+ pos := Segdwarf.Vaddr
+ var prevSect *sym.Section
+ for _, 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].Textp2) == 0 && len(v[j].Textp2) == 0:
+ return v[i].Lib.Pkg < v[j].Lib.Pkg
+ case len(v[i].Textp2) != 0 && len(v[j].Textp2) == 0:
+ return true
+ case len(v[i].Textp2) == 0 && len(v[j].Textp2) != 0:
+ return false
+ default:
+ return v[i].PCs[0].Start < v[j].PCs[0].Start
+ }
+}
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index b7221f04b3..e15f94d5e0 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -7,6 +7,7 @@ package ld
import (
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"crypto/sha1"
"encoding/binary"
@@ -742,21 +743,54 @@ func elfhash(name string) uint32 {
return h
}
-func Elfwritedynent(ctxt *Link, s *sym.Symbol, tag int, val uint64) {
+func elfWriteDynEnt(arch *sys.Arch, s *sym.Symbol, tag int, val uint64) {
if elf64 {
- s.AddUint64(ctxt.Arch, uint64(tag))
- s.AddUint64(ctxt.Arch, val)
+ s.AddUint64(arch, uint64(tag))
+ s.AddUint64(arch, val)
} else {
- s.AddUint32(ctxt.Arch, uint32(tag))
- s.AddUint32(ctxt.Arch, uint32(val))
+ s.AddUint32(arch, uint32(tag))
+ s.AddUint32(arch, uint32(val))
+ }
+}
+
+func elfWriteDynEntSym(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) {
+ Elfwritedynentsymplus(arch, s, tag, t, 0)
+}
+
+func Elfwritedynentsymplus(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol, add int64) {
+ if elf64 {
+ s.AddUint64(arch, uint64(tag))
+ } else {
+ s.AddUint32(arch, uint32(tag))
+ }
+ s.AddAddrPlus(arch, t, add)
+}
+
+func elfWriteDynEntSymSize(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) {
+ if elf64 {
+ s.AddUint64(arch, uint64(tag))
+ } else {
+ s.AddUint32(arch, uint32(tag))
}
+ s.AddSize(arch, t)
}
-func elfwritedynentsym(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) {
- Elfwritedynentsymplus(ctxt, s, tag, t, 0)
+// temporary
+func Elfwritedynent2(arch *sys.Arch, s *loader.SymbolBuilder, tag int, val uint64) {
+ if elf64 {
+ s.AddUint64(arch, uint64(tag))
+ s.AddUint64(arch, val)
+ } else {
+ s.AddUint32(arch, uint32(tag))
+ s.AddUint32(arch, uint32(val))
+ }
}
-func Elfwritedynentsymplus(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol, add int64) {
+func elfwritedynentsym2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) {
+ Elfwritedynentsymplus2(ctxt, s, tag, t, 0)
+}
+
+func Elfwritedynentsymplus2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym, add int64) {
if elf64 {
s.AddUint64(ctxt.Arch, uint64(tag))
} else {
@@ -765,7 +799,7 @@ func Elfwritedynentsymplus(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol, ad
s.AddAddrPlus(ctxt.Arch, t, add)
}
-func elfwritedynentsymsize(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) {
+func elfwritedynentsymsize2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) {
if elf64 {
s.AddUint64(ctxt.Arch, uint64(tag))
} else {
@@ -1121,23 +1155,23 @@ func elfdynhash(ctxt *Link) {
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))
+ elfWriteDynEntSym(ctxt.Arch, s, DT_VERNEED, ctxt.Syms.Lookup(".gnu.version_r", 0))
+ elfWriteDynEnt(ctxt.Arch, s, DT_VERNEEDNUM, uint64(nfile))
+ elfWriteDynEntSym(ctxt.Arch, 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)
+ elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_RELA)
} else {
- Elfwritedynent(ctxt, s, DT_PLTREL, DT_REL)
+ elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_REL)
}
- elfwritedynentsymsize(ctxt, s, DT_PLTRELSZ, sy)
- elfwritedynentsym(ctxt, s, DT_JMPREL, sy)
+ elfWriteDynEntSymSize(ctxt.Arch, s, DT_PLTRELSZ, sy)
+ elfWriteDynEntSym(ctxt.Arch, s, DT_JMPREL, sy)
}
- Elfwritedynent(ctxt, s, DT_NULL, 0)
+ elfWriteDynEnt(ctxt.Arch, s, DT_NULL, 0)
}
func elfphload(seg *sym.Segment) *ElfPhdr {
@@ -1381,18 +1415,18 @@ func Elfemitreloc(ctxt *Link) {
if sect.Name == ".text" {
elfrelocsect(ctxt, sect, ctxt.Textp)
} else {
- elfrelocsect(ctxt, sect, datap)
+ elfrelocsect(ctxt, sect, ctxt.datap)
}
}
for _, sect := range Segrodata.Sections {
- elfrelocsect(ctxt, sect, datap)
+ elfrelocsect(ctxt, sect, ctxt.datap)
}
for _, sect := range Segrelrodata.Sections {
- elfrelocsect(ctxt, sect, datap)
+ elfrelocsect(ctxt, sect, ctxt.datap)
}
for _, sect := range Segdata.Sections {
- elfrelocsect(ctxt, sect, datap)
+ elfrelocsect(ctxt, sect, ctxt.datap)
}
for _, sect := range Segdwarf.Sections {
elfrelocsect(ctxt, sect, dwarfp)
@@ -1400,9 +1434,10 @@ func Elfemitreloc(ctxt *Link) {
}
func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
- s := ctxt.Syms.Lookup(sectionName, 0)
- s.Attr |= sym.AttrReachable
- s.Type = sym.SELFROSECT
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate(sectionName, 0)
+ s.SetReachable(true)
+ s.SetType(sym.SELFROSECT)
// namesz
s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME)))
// descsz
@@ -1410,93 +1445,91 @@ func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
// 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)
+ s.AddBytes(ELF_NOTE_GO_NAME)
+ for len(s.Data())%4 != 0 {
+ s.AddUint8(0)
}
// desc + padding
- s.P = append(s.P, desc...)
- for len(s.P)%4 != 0 {
- s.P = append(s.P, 0)
+ s.AddBytes(desc)
+ for len(s.Data())%4 != 0 {
+ s.AddUint8(0)
}
- s.Size = int64(len(s.P))
- s.Align = 4
+ s.SetSize(int64(len(s.Data())))
+ s.SetAlign(4)
}
func (ctxt *Link) doelf() {
- if !ctxt.IsELF {
- return
- }
+ ldr := ctxt.loader
/* predefine strings we need for section headers */
- shstrtab := ctxt.Syms.Lookup(".shstrtab", 0)
+ shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0)
- shstrtab.Type = sym.SELFROSECT
- shstrtab.Attr |= sym.AttrReachable
+ shstrtab.SetType(sym.SELFROSECT)
+ shstrtab.SetReachable(true)
- 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")
+ shstrtab.Addstring("")
+ shstrtab.Addstring(".text")
+ shstrtab.Addstring(".noptrdata")
+ shstrtab.Addstring(".data")
+ shstrtab.Addstring(".bss")
+ shstrtab.Addstring(".noptrbss")
+ shstrtab.Addstring("__libfuzzer_extra_counters")
+ shstrtab.Addstring(".go.buildinfo")
// generate .tbss section for dynamic internal linker or external
// linking, so that various binutils could correctly calculate
// PT_TLS size. See https://golang.org/issue/5200.
- if !*FlagD || ctxt.LinkMode == LinkExternal {
- Addstring(shstrtab, ".tbss")
+ if !*FlagD || ctxt.IsExternal() {
+ shstrtab.Addstring(".tbss")
}
- if ctxt.HeadType == objabi.Hnetbsd {
- Addstring(shstrtab, ".note.netbsd.ident")
+ if ctxt.IsNetbsd() {
+ shstrtab.Addstring(".note.netbsd.ident")
}
- if ctxt.HeadType == objabi.Hopenbsd {
- Addstring(shstrtab, ".note.openbsd.ident")
+ if ctxt.IsOpenbsd() {
+ shstrtab.Addstring(".note.openbsd.ident")
}
if len(buildinfo) > 0 {
- Addstring(shstrtab, ".note.gnu.build-id")
+ shstrtab.Addstring(".note.gnu.build-id")
}
if *flagBuildid != "" {
- Addstring(shstrtab, ".note.go.buildid")
+ shstrtab.Addstring(".note.go.buildid")
}
- Addstring(shstrtab, ".elfdata")
- Addstring(shstrtab, ".rodata")
+ shstrtab.Addstring(".elfdata")
+ shstrtab.Addstring(".rodata")
// See the comment about data.rel.ro.FOO section names in data.go.
relro_prefix := ""
if ctxt.UseRelro() {
- Addstring(shstrtab, ".data.rel.ro")
+ shstrtab.Addstring(".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")
+ shstrtab.Addstring(relro_prefix + ".typelink")
+ shstrtab.Addstring(relro_prefix + ".itablink")
+ shstrtab.Addstring(relro_prefix + ".gosymtab")
+ shstrtab.Addstring(relro_prefix + ".gopclntab")
- if ctxt.LinkMode == LinkExternal {
+ if ctxt.IsExternal() {
*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")
+ shstrtab.Addstring(elfRelType + ".text")
+ shstrtab.Addstring(elfRelType + ".rodata")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".typelink")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".itablink")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".gosymtab")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".gopclntab")
+ shstrtab.Addstring(elfRelType + ".noptrdata")
+ shstrtab.Addstring(elfRelType + ".data")
if ctxt.UseRelro() {
- Addstring(shstrtab, elfRelType+".data.rel.ro")
+ shstrtab.Addstring(elfRelType + ".data.rel.ro")
}
- Addstring(shstrtab, elfRelType+".go.buildinfo")
+ shstrtab.Addstring(elfRelType + ".go.buildinfo")
// add a .note.GNU-stack section to mark the stack as non-executable
- Addstring(shstrtab, ".note.GNU-stack")
+ shstrtab.Addstring(".note.GNU-stack")
- if ctxt.BuildMode == BuildModeShared {
- Addstring(shstrtab, ".note.go.abihash")
- Addstring(shstrtab, ".note.go.pkg-list")
- Addstring(shstrtab, ".note.go.deps")
+ if ctxt.IsShared() {
+ shstrtab.Addstring(".note.go.abihash")
+ shstrtab.Addstring(".note.go.pkg-list")
+ shstrtab.Addstring(".note.go.deps")
}
}
@@ -1509,171 +1542,171 @@ func (ctxt *Link) doelf() {
}
if hasinitarr {
- Addstring(shstrtab, ".init_array")
- Addstring(shstrtab, elfRelType+".init_array")
+ shstrtab.Addstring(".init_array")
+ shstrtab.Addstring(elfRelType + ".init_array")
}
if !*FlagS {
- Addstring(shstrtab, ".symtab")
- Addstring(shstrtab, ".strtab")
+ shstrtab.Addstring(".symtab")
+ shstrtab.Addstring(".strtab")
dwarfaddshstrings(ctxt, shstrtab)
}
- Addstring(shstrtab, ".shstrtab")
+ shstrtab.Addstring(".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")
+ shstrtab.Addstring(".interp")
+ shstrtab.Addstring(".hash")
+ shstrtab.Addstring(".got")
+ if ctxt.IsPPC64() {
+ shstrtab.Addstring(".glink")
+ }
+ shstrtab.Addstring(".got.plt")
+ shstrtab.Addstring(".dynamic")
+ shstrtab.Addstring(".dynsym")
+ shstrtab.Addstring(".dynstr")
+ shstrtab.Addstring(elfRelType)
+ shstrtab.Addstring(elfRelType + ".plt")
+
+ shstrtab.Addstring(".plt")
+ shstrtab.Addstring(".gnu.version")
+ shstrtab.Addstring(".gnu.version_r")
/* dynamic symbol table - first entry all zeros */
- s := ctxt.Syms.Lookup(".dynsym", 0)
+ dynsym := ldr.CreateSymForUpdate(".dynsym", 0)
- s.Type = sym.SELFROSECT
- s.Attr |= sym.AttrReachable
+ dynsym.SetType(sym.SELFROSECT)
+ dynsym.SetReachable(true)
if elf64 {
- s.Size += ELF64SYMSIZE
+ dynsym.SetSize(dynsym.Size() + ELF64SYMSIZE)
} else {
- s.Size += ELF32SYMSIZE
+ dynsym.SetSize(dynsym.Size() + ELF32SYMSIZE)
}
/* dynamic string table */
- s = ctxt.Syms.Lookup(".dynstr", 0)
+ dynstr := ldr.CreateSymForUpdate(".dynstr", 0)
- s.Type = sym.SELFROSECT
- s.Attr |= sym.AttrReachable
- if s.Size == 0 {
- Addstring(s, "")
+ dynstr.SetType(sym.SELFROSECT)
+ dynstr.SetReachable(true)
+ if dynstr.Size() == 0 {
+ dynstr.Addstring("")
}
- dynstr := s
/* relocation table */
- s = ctxt.Syms.Lookup(elfRelType, 0)
- s.Attr |= sym.AttrReachable
- s.Type = sym.SELFROSECT
+ s := ldr.CreateSymForUpdate(elfRelType, 0)
+ s.SetReachable(true)
+ s.SetType(sym.SELFROSECT)
/* global offset table */
- s = ctxt.Syms.Lookup(".got", 0)
-
- s.Attr |= sym.AttrReachable
- s.Type = sym.SELFGOT // writable
+ got := ldr.CreateSymForUpdate(".got", 0)
+ got.SetReachable(true)
+ got.SetType(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
+ if ctxt.IsPPC64() {
+ s := ldr.CreateSymForUpdate(".glink", 0)
+ s.SetReachable(true)
+ s.SetType(sym.SELFRXSECT)
}
/* hash */
- s = ctxt.Syms.Lookup(".hash", 0)
+ hash := ldr.CreateSymForUpdate(".hash", 0)
+ hash.SetReachable(true)
+ hash.SetType(sym.SELFROSECT)
- s.Attr |= sym.AttrReachable
- s.Type = sym.SELFROSECT
+ gotplt := ldr.CreateSymForUpdate(".got.plt", 0)
+ gotplt.SetReachable(true)
+ gotplt.SetType(sym.SELFSECT) // writable
- 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 {
+ plt := ldr.CreateSymForUpdate(".plt", 0)
+ plt.SetReachable(true)
+ if ctxt.IsPPC64() {
// In the ppc64 ABI, .plt is a data section
// written by the dynamic linker.
- s.Type = sym.SELFSECT
+ plt.SetType(sym.SELFSECT)
} else {
- s.Type = sym.SELFRXSECT
+ plt.SetType(sym.SELFRXSECT)
}
- thearch.Elfsetupplt(ctxt)
-
- s = ctxt.Syms.Lookup(elfRelType+".plt", 0)
- s.Attr |= sym.AttrReachable
- s.Type = sym.SELFROSECT
+ s = ldr.CreateSymForUpdate(elfRelType+".plt", 0)
+ s.SetReachable(true)
+ s.SetType(sym.SELFROSECT)
- s = ctxt.Syms.Lookup(".gnu.version", 0)
- s.Attr |= sym.AttrReachable
- s.Type = sym.SELFROSECT
+ s = ldr.CreateSymForUpdate(".gnu.version", 0)
+ s.SetReachable(true)
+ s.SetType(sym.SELFROSECT)
- s = ctxt.Syms.Lookup(".gnu.version_r", 0)
- s.Attr |= sym.AttrReachable
- s.Type = sym.SELFROSECT
+ s = ldr.CreateSymForUpdate(".gnu.version_r", 0)
+ s.SetReachable(true)
+ s.SetType(sym.SELFROSECT)
/* define dynamic elf table */
- s = ctxt.Syms.Lookup(".dynamic", 0)
+ dynamic := ldr.CreateSymForUpdate(".dynamic", 0)
+ dynamic.SetReachable(true)
+ dynamic.SetType(sym.SELFSECT) // writable
- s.Attr |= sym.AttrReachable
- s.Type = sym.SELFSECT // writable
+ if ctxt.IsS390X() {
+ // S390X uses .got instead of .got.plt
+ gotplt = got
+ }
+ thearch.Elfsetupplt(ctxt, plt, gotplt, dynamic.Sym())
/*
* .dynamic table
*/
- elfwritedynentsym(ctxt, s, DT_HASH, ctxt.Syms.Lookup(".hash", 0))
+ elfwritedynentsym2(ctxt, dynamic, DT_HASH, hash.Sym())
- elfwritedynentsym(ctxt, s, DT_SYMTAB, ctxt.Syms.Lookup(".dynsym", 0))
+ elfwritedynentsym2(ctxt, dynamic, DT_SYMTAB, dynsym.Sym())
if elf64 {
- Elfwritedynent(ctxt, s, DT_SYMENT, ELF64SYMSIZE)
+ Elfwritedynent2(ctxt.Arch, dynamic, DT_SYMENT, ELF64SYMSIZE)
} else {
- Elfwritedynent(ctxt, s, DT_SYMENT, ELF32SYMSIZE)
+ Elfwritedynent2(ctxt.Arch, dynamic, DT_SYMENT, ELF32SYMSIZE)
}
- elfwritedynentsym(ctxt, s, DT_STRTAB, ctxt.Syms.Lookup(".dynstr", 0))
- elfwritedynentsymsize(ctxt, s, DT_STRSZ, ctxt.Syms.Lookup(".dynstr", 0))
+ elfwritedynentsym2(ctxt, dynamic, DT_STRTAB, dynstr.Sym())
+ elfwritedynentsymsize2(ctxt, dynamic, DT_STRSZ, dynstr.Sym())
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)
+ rela := ldr.LookupOrCreateSym(".rela", 0)
+ elfwritedynentsym2(ctxt, dynamic, DT_RELA, rela)
+ elfwritedynentsymsize2(ctxt, dynamic, DT_RELASZ, rela)
+ Elfwritedynent2(ctxt.Arch, dynamic, 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)
+ rel := ldr.LookupOrCreateSym(".rel", 0)
+ elfwritedynentsym2(ctxt, dynamic, DT_REL, rel)
+ elfwritedynentsymsize2(ctxt, dynamic, DT_RELSZ, rel)
+ Elfwritedynent2(ctxt.Arch, dynamic, DT_RELENT, ELF32RELSIZE)
}
if rpath.val != "" {
- Elfwritedynent(ctxt, s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val)))
+ Elfwritedynent2(ctxt.Arch, dynamic, DT_RUNPATH, uint64(dynstr.Addstring(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))
+ if ctxt.IsPPC64() {
+ elfwritedynentsym2(ctxt, dynamic, DT_PLTGOT, plt.Sym())
} else {
- elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got.plt", 0))
+ elfwritedynentsym2(ctxt, dynamic, DT_PLTGOT, gotplt.Sym())
}
- if ctxt.Arch.Family == sys.PPC64 {
- Elfwritedynent(ctxt, s, DT_PPC64_OPT, 0)
+ if ctxt.IsPPC64() {
+ Elfwritedynent2(ctxt.Arch, dynamic, 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)
+ Elfwritedynent2(ctxt.Arch, dynamic, DT_DEBUG, 0)
}
- if ctxt.BuildMode == BuildModeShared {
+ if ctxt.IsShared() {
// The go.link.abihashbytes symbol will be pointed at the appropriate
// part of the .note.go.abihash section in data.go:func address().
- s := 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)
+ s := ldr.LookupOrCreateSym("go.link.abihashbytes", 0)
+ sb := ldr.MakeSymbolUpdater(s)
+ ldr.SetAttrLocal(s, true)
+ sb.SetType(sym.SRODATA)
+ ldr.SetAttrSpecial(s, true)
+ sb.SetReachable(true)
+ sb.SetSize(sha1.Size)
sort.Sort(byPkg(ctxt.Library))
h := sha1.New()
@@ -2271,15 +2304,15 @@ elfobj:
}
}
-func elfadddynsym(ctxt *Link, s *sym.Symbol) {
+func elfadddynsym(target *Target, syms *ArchSyms, s *sym.Symbol) {
if elf64 {
s.Dynid = int32(Nelfsym)
Nelfsym++
- d := ctxt.Syms.Lookup(".dynsym", 0)
+ d := syms.DynSym
name := s.Extname()
- d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name)))
+ d.AddUint32(target.Arch, uint32(Addstring(syms.DynStr, name)))
/* type */
t := STB_GLOBAL << 4
@@ -2296,52 +2329,52 @@ func elfadddynsym(ctxt *Link, s *sym.Symbol) {
/* section where symbol is defined */
if s.Type == sym.SDYNIMPORT {
- d.AddUint16(ctxt.Arch, SHN_UNDEF)
+ d.AddUint16(target.Arch, SHN_UNDEF)
} else {
- d.AddUint16(ctxt.Arch, 1)
+ d.AddUint16(target.Arch, 1)
}
/* value */
if s.Type == sym.SDYNIMPORT {
- d.AddUint64(ctxt.Arch, 0)
+ d.AddUint64(target.Arch, 0)
} else {
- d.AddAddr(ctxt.Arch, s)
+ d.AddAddr(target.Arch, s)
}
/* size of object */
- d.AddUint64(ctxt.Arch, uint64(s.Size))
+ d.AddUint64(target.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())))
+ if target.Arch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib() != "" && !seenlib[s.Dynimplib()] {
+ elfWriteDynEnt(target.Arch, syms.Dynamic, DT_NEEDED, uint64(Addstring(syms.DynStr, s.Dynimplib())))
}
} else {
s.Dynid = int32(Nelfsym)
Nelfsym++
- d := ctxt.Syms.Lookup(".dynsym", 0)
+ d := syms.DynSym
/* name */
name := s.Extname()
- d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name)))
+ d.AddUint32(target.Arch, uint32(Addstring(syms.DynStr, name)))
/* value */
if s.Type == sym.SDYNIMPORT {
- d.AddUint32(ctxt.Arch, 0)
+ d.AddUint32(target.Arch, 0)
} else {
- d.AddAddr(ctxt.Arch, s)
+ d.AddAddr(target.Arch, s)
}
/* size of object */
- d.AddUint32(ctxt.Arch, uint32(s.Size))
+ d.AddUint32(target.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 {
+ if target.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 {
+ } else if target.Arch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type == sym.STEXT {
t |= STT_FUNC
} else {
t |= STT_OBJECT
@@ -2351,9 +2384,95 @@ func elfadddynsym(ctxt *Link, s *sym.Symbol) {
/* shndx */
if s.Type == sym.SDYNIMPORT {
- d.AddUint16(ctxt.Arch, SHN_UNDEF)
+ d.AddUint16(target.Arch, SHN_UNDEF)
+ } else {
+ d.AddUint16(target.Arch, 1)
+ }
+ }
+}
+
+func elfadddynsym2(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
+ ldr.SetSymDynid(s, int32(Nelfsym))
+ Nelfsym++
+ d := ldr.MakeSymbolUpdater(syms.DynSym2)
+ name := ldr.SymExtname(s)
+ dstru := ldr.MakeSymbolUpdater(syms.DynStr2)
+ st := ldr.SymType(s)
+ cgoeStatic := ldr.AttrCgoExportStatic(s)
+ cgoeDynamic := ldr.AttrCgoExportDynamic(s)
+ cgoexp := (cgoeStatic || cgoeDynamic)
+
+ d.AddUint32(target.Arch, uint32(dstru.Addstring(name)))
+
+ if elf64 {
+
+ /* type */
+ t := STB_GLOBAL << 4
+
+ if cgoexp && st == sym.STEXT {
+ t |= STT_FUNC
+ } else {
+ t |= STT_OBJECT
+ }
+ d.AddUint8(uint8(t))
+
+ /* reserved */
+ d.AddUint8(0)
+
+ /* section where symbol is defined */
+ if st == sym.SDYNIMPORT {
+ d.AddUint16(target.Arch, SHN_UNDEF)
+ } else {
+ d.AddUint16(target.Arch, 1)
+ }
+
+ /* value */
+ if st == sym.SDYNIMPORT {
+ d.AddUint64(target.Arch, 0)
+ } else {
+ d.AddAddrPlus(target.Arch, s, 0)
+ }
+
+ /* size of object */
+ d.AddUint64(target.Arch, uint64(len(ldr.Data(s))))
+
+ dil := ldr.SymDynimplib(s)
+
+ if target.Arch.Family == sys.AMD64 && !cgoeDynamic && dil != "" && !seenlib[dil] {
+ du := ldr.MakeSymbolUpdater(syms.Dynamic2)
+ Elfwritedynent2(target.Arch, du, DT_NEEDED, uint64(dstru.Addstring(dil)))
+ }
+ } else {
+
+ /* value */
+ if st == sym.SDYNIMPORT {
+ d.AddUint32(target.Arch, 0)
+ } else {
+ d.AddAddrPlus(target.Arch, s, 0)
+ }
+
+ /* size of object */
+ d.AddUint32(target.Arch, uint32(len(ldr.Data(s))))
+
+ /* type */
+ t := STB_GLOBAL << 4
+
+ // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386.
+ if target.Arch.Family == sys.I386 && cgoexp && st == sym.STEXT {
+ t |= STT_FUNC
+ } else if target.Arch.Family == sys.ARM && cgoeDynamic && st == sym.STEXT {
+ t |= STT_FUNC
+ } else {
+ t |= STT_OBJECT
+ }
+ d.AddUint8(uint8(t))
+ d.AddUint8(0)
+
+ /* shndx */
+ if st == sym.SDYNIMPORT {
+ d.AddUint16(target.Arch, SHN_UNDEF)
} else {
- d.AddUint16(ctxt.Arch, 1)
+ d.AddUint16(target.Arch, 1)
}
}
}
diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go
new file mode 100644
index 0000000000..e66c518b85
--- /dev/null
+++ b/src/cmd/link/internal/ld/errors.go
@@ -0,0 +1,87 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package ld
+
+import (
+ "cmd/internal/obj"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "os"
+ "sync"
+)
+
+type unresolvedSymKey struct {
+ from *sym.Symbol // Symbol that referenced unresolved "to"
+ to *sym.Symbol // Unresolved symbol referenced by "from"
+}
+
+type lookupFn func(name string, version int) *sym.Symbol
+type symNameFn func(s loader.Sym) string
+
+// ErrorReporter is used to make error reporting thread safe.
+type ErrorReporter struct {
+ unresOnce sync.Once
+ unresSyms map[unresolvedSymKey]bool
+ unresMutex sync.Mutex
+ lookup lookupFn
+ SymName symNameFn
+}
+
+// errorUnresolved prints unresolved symbol error for r.Sym that is referenced from s.
+func (reporter *ErrorReporter) errorUnresolved(s *sym.Symbol, r *sym.Reloc) {
+ reporter.unresOnce.Do(func() { reporter.unresSyms = make(map[unresolvedSymKey]bool) })
+
+ k := unresolvedSymKey{from: s, to: r.Sym}
+ reporter.unresMutex.Lock()
+ defer reporter.unresMutex.Unlock()
+ if !reporter.unresSyms[k] {
+ reporter.unresSyms[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 := reporter.lookup(r.Sym.Name, v); rs != nil && rs.Type != sym.Sxxx && rs.Type != sym.SXREF {
+ 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)
+ }
+ }
+}
+
+// Errorf method logs an error message.
+//
+// If more than 20 errors have been printed, exit with an error.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+// TODO: consolidate the various different versions of Errorf (
+// function, Link method, and ErrorReporter method).
+func (reporter *ErrorReporter) Errorf(s loader.Sym, format string, args ...interface{}) {
+ if s != 0 && reporter.SymName != nil {
+ sn := reporter.SymName(s)
+ format = sn + ": " + format
+ } else {
+ format = fmt.Sprintf("sym %d: %s", s, format)
+ }
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ afterErrorAction()
+}
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
index 21457fdfc8..3b9d4ba7cc 100644
--- a/src/cmd/link/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -10,6 +10,8 @@ import (
"bytes"
"cmd/internal/bio"
"cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"encoding/json"
"fmt"
@@ -145,16 +147,14 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
}
}
- 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)
- }
+ // Record the directives. We'll process them later after Symbols are created.
+ ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
}
// Set symbol attributes or flags based on cgo directives.
-func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, pkg string, directives [][]string) {
+// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'.
+func setCgoAttr(ctxt *Link, lookup func(string, int) loader.Sym, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) {
+ l := ctxt.loader
for _, f := range directives {
switch f[0] {
case "cgo_import_dynamic":
@@ -197,12 +197,16 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p
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
+ st := l.SymType(s)
+ if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ {
+ l.SetSymDynimplib(s, lib)
+ l.SetSymExtname(s, remote)
+ l.SetSymDynimpvers(s, q)
+ if st != sym.SHOSTOBJ {
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SDYNIMPORT)
+ } else {
+ hostObjSyms[s] = struct{}{}
}
havedynamic = 1
}
@@ -216,8 +220,10 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p
local := f[1]
s := lookup(local, 0)
- s.Type = sym.SHOSTOBJ
- s.Size = 0
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SHOSTOBJ)
+ su.SetSize(0)
+ hostObjSyms[s] = struct{}{}
continue
case "cgo_export_static", "cgo_export_dynamic":
@@ -238,6 +244,10 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p
// yet know it's an alias).
s := lookup(local, 0)
+ if l.SymType(s) == sym.SHOSTOBJ {
+ hostObjSyms[s] = struct{}{}
+ }
+
switch ctxt.BuildMode {
case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
if s == lookup("main", 0) {
@@ -247,24 +257,27 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p
// export overrides import, for openbsd/cgo.
// see issue 4878.
- if s.Dynimplib() != "" {
- s.ResetDyninfo()
- s.SetExtname("")
- s.Type = 0
+ if l.SymDynimplib(s) != "" {
+ l.SetSymDynimplib(s, "")
+ l.SetSymDynimpvers(s, "")
+ l.SetSymExtname(s, "")
+ var su *loader.SymbolBuilder
+ su = l.MakeSymbolUpdater(s)
+ su.SetType(0)
}
- if !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)
+ if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) {
+ l.SetSymExtname(s, remote)
+ } else if l.SymExtname(s) != remote {
+ fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote)
nerrors++
return
}
if f[0] == "cgo_export_static" {
- s.Attr |= sym.AttrCgoExportStatic
+ l.SetAttrCgoExportStatic(s, true)
} else {
- s.Attr |= sym.AttrCgoExportDynamic
+ l.SetAttrCgoExportDynamic(s, true)
}
continue
@@ -295,6 +308,7 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p
fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
nerrors++
}
+ return
}
var seenlib = make(map[string]bool)
@@ -306,78 +320,99 @@ func adddynlib(ctxt *Link, lib string) {
seenlib[lib] = true
if ctxt.IsELF {
- s := ctxt.Syms.Lookup(".dynstr", 0)
- if s.Size == 0 {
- Addstring(s, "")
+ dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr2)
+ if dsu.Size() == 0 {
+ dsu.Addstring("")
}
- Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
+ du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic2)
+ Elfwritedynent2(ctxt.Arch, du, DT_NEEDED, uint64(dsu.Addstring(lib)))
} else {
Errorf(nil, "adddynlib: unsupported binary format")
}
}
-func Adddynsym(ctxt *Link, s *sym.Symbol) {
- if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal {
+func Adddynsym2(ldr *loader.Loader, reporter *ErrorReporter, target *Target, syms *ArchSyms, s loader.Sym) {
+ if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal {
return
}
- if ctxt.IsELF {
- elfadddynsym(ctxt, s)
- } else if ctxt.HeadType == objabi.Hdarwin {
+ if target.IsELF {
+ elfadddynsym2(ldr, target, syms, s)
+ } else if target.HeadType == objabi.Hdarwin {
+ reporter.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s))
+ } else if target.HeadType == objabi.Hwindows {
+ // already taken care of
+ } else {
+ reporter.Errorf(s, "adddynsym: unsupported binary format")
+ }
+}
+
+func Adddynsym(target *Target, syms *ArchSyms, s *sym.Symbol) {
+ if s.Dynid >= 0 || target.LinkMode == LinkExternal {
+ return
+ }
+
+ if target.IsELF {
+ elfadddynsym(target, syms, s)
+ } else if target.HeadType == objabi.Hdarwin {
Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname())
- } else if ctxt.HeadType == objabi.Hwindows {
+ } else if target.HeadType == objabi.Hwindows {
// already taken care of
} else {
Errorf(s, "adddynsym: unsupported binary format")
}
}
-func fieldtrack(ctxt *Link) {
- // record field tracking references
+func fieldtrack(arch *sys.Arch, l *loader.Loader) {
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] {
+ for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ {
+ if name := l.SymName(i); strings.HasPrefix(name, "go.track.") {
+ bld := l.MakeSymbolUpdater(i)
+ bld.SetSpecial(true)
+ bld.SetNotInSymbolTable(true)
+ if bld.Reachable() {
+ buf.WriteString(name[9:])
+ for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] {
buf.WriteString("\t")
- buf.WriteString(p.Name)
+ buf.WriteString(l.SymName(p))
}
buf.WriteString("\n")
- }
- s.Type = sym.SCONST
- s.Value = 0
+ bld.SetType(sym.SCONST)
+ bld.SetValue(0)
+ }
}
}
-
if *flagFieldTrack == "" {
return
}
- s := ctxt.Syms.ROLookup(*flagFieldTrack, 0)
- if s == nil || !s.Attr.Reachable() {
+ s := l.Lookup(*flagFieldTrack, 0)
+ if s == 0 || !l.AttrReachable(s) {
return
}
- s.Type = sym.SDATA
- addstrdata(ctxt, *flagFieldTrack, buf.String())
+ bld := l.MakeSymbolUpdater(s)
+ bld.SetType(sym.SDATA)
+ addstrdata(arch, l, *flagFieldTrack, buf.String())
}
func (ctxt *Link) addexport() {
// Track undefined external symbols during external link.
if ctxt.LinkMode == LinkExternal {
- for _, s := range ctxt.Syms.Allsym {
- if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() {
- continue
- }
- if s.Type != sym.STEXT {
+ for _, s := range ctxt.Textp2 {
+ if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) {
continue
}
- for i := range s.R {
- r := &s.R[i]
- if r.Sym != nil && r.Sym.Type == sym.Sxxx {
- r.Sym.Type = sym.SUNDEFEXT
+ relocs := ctxt.loader.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ if rs := relocs.At2(i).Sym(); rs != 0 {
+ if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) {
+ // sanity check
+ if len(ctxt.loader.Data(rs)) != 0 {
+ panic("expected no data on undef symbol")
+ }
+ su := ctxt.loader.MakeSymbolUpdater(rs)
+ su.SetType(sym.SUNDEFEXT)
+ }
}
}
}
@@ -388,8 +423,8 @@ func (ctxt *Link) addexport() {
return
}
- for _, exp := range dynexp {
- Adddynsym(ctxt, exp)
+ for _, exp := range ctxt.dynexp2 {
+ Adddynsym2(ctxt.loader, &ctxt.ErrorReporter, &ctxt.Target, &ctxt.ArchSyms, exp)
}
for _, lib := range dynlib {
adddynlib(ctxt, lib)
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index a4b4b60ca1..3f21fc54ef 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -31,7 +31,6 @@
package ld
import (
- "bufio"
"bytes"
"cmd/internal/bio"
"cmd/internal/obj"
@@ -42,7 +41,6 @@ import (
"cmd/link/internal/loadmacho"
"cmd/link/internal/loadpe"
"cmd/link/internal/loadxcoff"
- "cmd/link/internal/objfile"
"cmd/link/internal/sym"
"crypto/sha1"
"debug/elf"
@@ -95,6 +93,128 @@ import (
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+type LookupFn func(name string, version int) *sym.Symbol
+
+// ArchSyms holds a number of architecture specific symbols used during
+// relocation. Rather than allowing them universal access to all symbols,
+// we keep a subset for relocation application.
+type ArchSyms struct {
+ TOC *sym.Symbol
+ DotTOC []*sym.Symbol // for each version
+
+ GOT *sym.Symbol
+ PLT *sym.Symbol
+ GOTPLT *sym.Symbol
+
+ Tlsg *sym.Symbol
+ Tlsoffset int
+
+ Dynamic *sym.Symbol
+ DynSym *sym.Symbol
+ DynStr *sym.Symbol
+
+ // Elf specific
+ Rel *sym.Symbol
+ Rela *sym.Symbol
+ RelPLT *sym.Symbol
+ RelaPLT *sym.Symbol
+
+ // Darwin symbols
+ LinkEditGOT *sym.Symbol
+ LinkEditPLT *sym.Symbol
+
+ // ----- loader.Sym equivalents -----
+
+ Rel2 loader.Sym
+ Rela2 loader.Sym
+ RelPLT2 loader.Sym
+ RelaPLT2 loader.Sym
+
+ LinkEditGOT2 loader.Sym
+ LinkEditPLT2 loader.Sym
+
+ TOC2 loader.Sym
+ DotTOC2 []loader.Sym // for each version
+
+ GOT2 loader.Sym
+ PLT2 loader.Sym
+ GOTPLT2 loader.Sym
+
+ Tlsg2 loader.Sym
+
+ Dynamic2 loader.Sym
+ DynSym2 loader.Sym
+ DynStr2 loader.Sym
+}
+
+const BeforeLoadlibFull = 1
+const AfterLoadlibFull = 2
+
+func (ctxt *Link) mkArchSym(which int, name string, ls *loader.Sym, ss **sym.Symbol) {
+ if which == BeforeLoadlibFull {
+ *ls = ctxt.loader.LookupOrCreateSym(name, 0)
+ } else {
+ *ss = ctxt.loader.Syms[*ls]
+ }
+}
+
+func (ctxt *Link) mkArchSymVec(which int, name string, i int, ls []loader.Sym, ss []*sym.Symbol) {
+ if which == BeforeLoadlibFull {
+ ls[i] = ctxt.loader.LookupOrCreateSym(name, 0)
+ } else {
+ ss[i] = ctxt.loader.Syms[ls[i]]
+ }
+}
+
+// setArchSyms sets up the ArchSyms structure, and must be called before
+// relocations are applied. This function is invoked twice, once prior
+// to loadlibfull(), and once after the work of loadlibfull is complete.
+func (ctxt *Link) setArchSyms(which int) {
+ if which != BeforeLoadlibFull && which != AfterLoadlibFull {
+ panic("internal error")
+ }
+ ctxt.mkArchSym(which, ".got", &ctxt.GOT2, &ctxt.GOT)
+ ctxt.mkArchSym(which, ".plt", &ctxt.PLT2, &ctxt.PLT)
+ ctxt.mkArchSym(which, ".got.plt", &ctxt.GOTPLT2, &ctxt.GOTPLT)
+ ctxt.mkArchSym(which, ".dynamic", &ctxt.Dynamic2, &ctxt.Dynamic)
+ ctxt.mkArchSym(which, ".dynsym", &ctxt.DynSym2, &ctxt.DynSym)
+ ctxt.mkArchSym(which, ".dynstr", &ctxt.DynStr2, &ctxt.DynStr)
+
+ if ctxt.IsAIX() {
+ ctxt.mkArchSym(which, "TOC", &ctxt.TOC2, &ctxt.TOC)
+
+ // NB: note the +2 below for DotTOC2 compared to the +1 for
+ // DocTOC. This is because loadlibfull() creates an additional
+ // syms version during conversion of loader.Sym symbols to
+ // *sym.Symbol symbols. Symbols that are assigned this final
+ // version are not going to have TOC references, so it should
+ // be ok for them to inherit an invalid .TOC. symbol.
+ if which == BeforeLoadlibFull {
+ ctxt.DotTOC2 = make([]loader.Sym, ctxt.Syms.MaxVersion()+2)
+ } else {
+ ctxt.DotTOC = make([]*sym.Symbol, ctxt.Syms.MaxVersion()+1)
+ }
+ for i := 0; i <= ctxt.Syms.MaxVersion(); i++ {
+ if i >= 2 && i < sym.SymVerStatic { // these versions are not used currently
+ continue
+ }
+ if ctxt.DotTOC2[i] != 0 {
+ ctxt.mkArchSymVec(which, ".TOC.", i, ctxt.DotTOC2, ctxt.DotTOC)
+ }
+ }
+ }
+ if ctxt.IsElf() {
+ ctxt.mkArchSym(which, ".rel", &ctxt.Rel2, &ctxt.Rel)
+ ctxt.mkArchSym(which, ".rela", &ctxt.Rela2, &ctxt.Rela)
+ ctxt.mkArchSym(which, ".rel.plt", &ctxt.RelPLT2, &ctxt.RelPLT)
+ ctxt.mkArchSym(which, ".rela.plt", &ctxt.RelaPLT2, &ctxt.RelaPLT)
+ }
+ if ctxt.IsDarwin() {
+ ctxt.mkArchSym(which, ".linkedit.got", &ctxt.LinkEditGOT2, &ctxt.LinkEditGOT)
+ ctxt.mkArchSym(which, ".linkedit.plt", &ctxt.LinkEditPLT2, &ctxt.LinkEditPLT)
+ }
+}
+
type Arch struct {
Funcalign int
Maxalign int
@@ -108,7 +228,7 @@ type Arch struct {
Openbsddynld string
Dragonflydynld string
Solarisdynld string
- Adddynrel func(*Link, *sym.Symbol, *sym.Reloc) bool
+ Adddynrel func(*Target, *loader.Loader, *ArchSyms, *sym.Symbol, *sym.Reloc) bool
Archinit func(*Link)
// Archreloc is an arch-specific hook that assists in
// relocation processing (invoked by 'relocsym'); it handles
@@ -119,7 +239,7 @@ type Arch struct {
// 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,
+ Archreloc func(target *Target, syms *ArchSyms, 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
@@ -129,7 +249,7 @@ type Arch struct {
// 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,
+ Archrelocvariant func(target *Target, syms *ArchSyms, rel *sym.Reloc, sym *sym.Symbol,
offset int64) (relocatedOffset int64)
Trampoline func(*Link, *sym.Reloc, *sym.Symbol)
@@ -141,7 +261,7 @@ type Arch struct {
Asmb2 func(*Link)
Elfreloc1 func(*Link, *sym.Reloc, int64) bool
- Elfsetupplt func(*Link)
+ Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
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
@@ -188,17 +308,6 @@ func (ctxt *Link) CanUsePlugins() bool {
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
@@ -275,14 +384,11 @@ func libinit(ctxt *Link) {
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 {
+
+ if err := ctxt.Out.Open(*flagOutfile); 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:
@@ -381,17 +487,18 @@ func (ctxt *Link) findLibPath(libname string) string {
}
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)
+ 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, elfsetstring)
+ ctxt.ErrorReporter.SymName = func(s loader.Sym) string {
+ return ctxt.loader.SymName(s)
}
ctxt.cgo_export_static = make(map[string]bool)
@@ -423,35 +530,33 @@ func (ctxt *Link) loadlib() {
loadobjfile(ctxt, lib)
}
}
+ // At this point, the Go objects are "preloaded". Not all the symbols are
+ // added to the symbol table (only defined package symbols are). Looking
+ // up symbol by name may not get expected result.
- 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
- }
+ iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil
+ ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != 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)) {
+ if ctxt.LinkMode == LinkExternal && !iscgo && !(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)
+ if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" {
+ if ctxt.BuildMode == BuildModeShared || ctxt.linkShared {
+ Exitf("cannot implicitly include runtime/cgo in a shared library")
}
+ loadobjfile(ctxt, lib)
}
}
+ // Add non-package symbols and references of externally defined symbols.
+ ctxt.loader.LoadNonpkgSyms(ctxt.Syms)
+
+ // Load symbols from shared libraries, after all Go object symbols are loaded.
for _, lib := range ctxt.Library {
if lib.Shlib != "" {
if ctxt.Debugvlog > 1 {
@@ -461,63 +566,20 @@ func (ctxt *Link) loadlib() {
}
}
- 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
- }
- }
- }
- }
+ // Process cgo directives (has to be done before host object loading).
+ ctxt.loadcgodirectives()
// Conditionally load host objects, or setup for external linking.
hostobjs(ctxt)
hostlinksetup(ctxt)
- if *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
- }
- }
+ undefs := ctxt.loader.UndefinedRelocTargets(1)
+ if len(undefs) > 0 {
+ any = true
}
if any {
if *flagLibGCC == "" {
@@ -561,54 +623,99 @@ func (ctxt *Link) loadlib() {
importcycles()
- if *flagNewobj {
- strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
+ strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
+}
+
+// genSymsForDynexp constructs a *sym.Symbol version of ctxt.dynexp,
+// writing to the global variable 'dynexp'.
+func genSymsForDynexp(ctxt *Link) {
+ dynexp = make([]*sym.Symbol, len(ctxt.dynexp2))
+ for i, s := range ctxt.dynexp2 {
+ dynexp[i] = ctxt.loader.Syms[s]
}
}
-// Set up dynexp list.
+// setupdynexp constructs ctxt.dynexp, a list of loader.Sym.
func setupdynexp(ctxt *Link) {
dynexpMap := ctxt.cgo_export_dynamic
if ctxt.LinkMode == LinkExternal {
dynexpMap = ctxt.cgo_export_static
}
- dynexp = make([]*sym.Symbol, 0, len(dynexpMap))
+ d := make([]loader.Sym, 0, len(dynexpMap))
for exp := range dynexpMap {
- s := ctxt.Syms.Lookup(exp, 0)
- dynexp = append(dynexp, s)
+ s := ctxt.loader.LookupOrCreateSym(exp, 0)
+ d = append(d, s)
+ // sanity check
+ if !ctxt.loader.AttrReachable(s) {
+ panic("dynexp entry not reachable")
+ }
}
- sort.Sort(byName(dynexp))
+ sort.Slice(d, func(i, j int) bool {
+ return ctxt.loader.SymName(d[i]) < ctxt.loader.SymName(d[j])
+ })
// Resolve ABI aliases in the list of cgo-exported functions.
// This is necessary because we load the ABI0 symbol for all
// cgo exports.
- for i, s := range dynexp {
- if s.Type != sym.SABIALIAS {
+ for i, s := range d {
+ if ctxt.loader.SymType(s) != sym.SABIALIAS {
continue
}
- t := resolveABIAlias(s)
- t.Attr |= s.Attr
- t.SetExtname(s.Extname())
- dynexp[i] = t
+ t := ctxt.loader.ResolveABIAlias(s)
+ ctxt.loader.CopyAttributes(s, t)
+ ctxt.loader.SetSymExtname(t, ctxt.loader.SymExtname(s))
+ d[i] = t
}
+ ctxt.dynexp2 = d
ctxt.cgo_export_static = nil
ctxt.cgo_export_dynamic = nil
}
+// loadcgodirectives reads the previously discovered cgo directives, creating
+// symbols in preparation for host object loading or use later in the link.
+func (ctxt *Link) loadcgodirectives() {
+ l := ctxt.loader
+ hostObjSyms := make(map[loader.Sym]struct{})
+ for _, d := range ctxt.cgodata {
+ setCgoAttr(ctxt, ctxt.loader.LookupOrCreateSym, d.file, d.pkg, d.directives, hostObjSyms)
+ }
+ ctxt.cgodata = nil
+
+ if ctxt.LinkMode == LinkInternal {
+ // Drop all the cgo_import_static declarations.
+ // Turns out we won't be needing them.
+ for symIdx := range hostObjSyms {
+ if l.SymType(symIdx) == sym.SHOSTOBJ {
+ // If a symbol was marked both
+ // cgo_import_static and cgo_import_dynamic,
+ // then we want to make it cgo_import_dynamic
+ // now.
+ su := l.MakeSymbolUpdater(symIdx)
+ if l.SymExtname(symIdx) != "" && l.SymDynimplib(symIdx) != "" && !(l.AttrCgoExportStatic(symIdx) || l.AttrCgoExportDynamic(symIdx)) {
+ su.SetType(sym.SDYNIMPORT)
+ } else {
+ su.SetType(0)
+ }
+ }
+ }
+ }
+}
+
// Set up flags and special symbols depending on the platform build mode.
+// This version works with loader.Loader.
func (ctxt *Link) linksetup() {
switch ctxt.BuildMode {
case BuildModeCShared, BuildModePlugin:
- s := ctxt.Syms.Lookup("runtime.islibrary", 0)
- s.Type = sym.SNOPTRDATA
- s.Attr |= sym.AttrDuplicateOK
- s.AddUint8(1)
+ symIdx := ctxt.loader.LookupOrCreateSym("runtime.islibrary", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(symIdx)
+ sb.SetType(sym.SNOPTRDATA)
+ sb.AddUint8(1)
case BuildModeCArchive:
- s := ctxt.Syms.Lookup("runtime.isarchive", 0)
- s.Type = sym.SNOPTRDATA
- s.Attr |= sym.AttrDuplicateOK
- s.AddUint8(1)
+ symIdx := ctxt.loader.LookupOrCreateSym("runtime.isarchive", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(symIdx)
+ sb.SetType(sym.SNOPTRDATA)
+ sb.AddUint8(1)
}
// Recalculate pe parameters now that we have ctxt.LinkMode set.
@@ -637,69 +744,77 @@ func (ctxt *Link) linksetup() {
}
if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
- toc := ctxt.Syms.Lookup(".TOC.", 0)
- toc.Type = sym.SDYNIMPORT
+ toc := ctxt.loader.LookupOrCreateSym(".TOC.", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(toc)
+ sb.SetType(sym.SDYNIMPORT)
}
// The Android Q linker started to complain about underalignment of the our TLS
- // section. We don't actually use the section on android, so dont't
+ // section. We don't actually use the section on android, so don't
// generate it.
if objabi.GOOS != "android" {
- tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0)
+ tlsg := ctxt.loader.LookupOrCreateSym("runtime.tlsg", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(tlsg)
// runtime.tlsg is used for external linking on platforms that do not define
// a variable to hold g in assembly (currently only intel).
- if 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)
+ if sb.Type() == 0 {
+ sb.SetType(sym.STLSBSS)
+ sb.SetSize(int64(ctxt.Arch.PtrSize))
+ } else if sb.Type() != sym.SDYNIMPORT {
+ Errorf(nil, "runtime declared tlsg variable %v", sb.Type())
}
- tlsg.Attr |= sym.AttrReachable
- ctxt.Tlsg = tlsg
+ ctxt.loader.SetAttrReachable(tlsg, true)
+ ctxt.Tlsg2 = tlsg
}
- var moduledata *sym.Symbol
+ var moduledata loader.Sym
+ var mdsb *loader.SymbolBuilder
if ctxt.BuildMode == BuildModePlugin {
- moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0)
- moduledata.Attr |= sym.AttrLocal
+ moduledata = ctxt.loader.LookupOrCreateSym("local.pluginmoduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+ ctxt.loader.SetAttrLocal(moduledata, true)
} else {
- moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
+ moduledata = ctxt.loader.LookupOrCreateSym("runtime.firstmoduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
}
- if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT {
+ if mdsb.Type() != 0 && mdsb.Type() != sym.SDYNIMPORT {
// If the module (toolchain-speak for "executable or shared
// library") we are linking contains the runtime package, it
// will define the runtime.firstmoduledata symbol and we
// truncate it back to 0 bytes so we can define its entire
// contents in symtab.go:symtab().
- moduledata.Size = 0
+ mdsb.SetSize(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))
+ goarm := ctxt.loader.LookupOrCreateSym("runtime.goarm", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(goarm)
+ sb.SetType(sym.SDATA)
+ sb.SetSize(0)
+ sb.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)
+ fpe := ctxt.loader.LookupOrCreateSym("runtime.framepointer_enabled", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(fpe)
+ sb.SetType(sym.SNOPTRDATA)
+ sb.SetSize(0)
+ sb.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
+ moduledata = ctxt.loader.LookupOrCreateSym("local.moduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+ ctxt.loader.SetAttrLocal(moduledata, true)
}
// In all cases way we mark the moduledata as noptrdata to hide it from
// the GC.
- moduledata.Type = sym.SNOPTRDATA
- moduledata.Attr |= sym.AttrReachable
- ctxt.Moduledata = moduledata
+ mdsb.SetType(sym.SNOPTRDATA)
+ ctxt.loader.SetAttrReachable(moduledata, true)
+ ctxt.Moduledata2 = moduledata
// If package versioning is required, generate a hash of the
// packages used in the link.
@@ -713,11 +828,25 @@ func (ctxt *Link) linksetup() {
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
+ got := ctxt.loader.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(got)
+ sb.SetType(sym.SDYNIMPORT)
+ ctxt.loader.SetAttrReachable(got, true)
}
}
+
+ // DWARF-gen and other phases require that the unit Textp2 slices
+ // be populated, so that it can walk the functions in each unit.
+ // Call into the loader to do this (requires that we collect the
+ // set of internal libraries first). NB: might be simpler if we
+ // moved isRuntimeDepPkg to cmd/internal and then did the test in
+ // loader.AssignTextSymbolOrder.
+ ctxt.Library = postorder(ctxt.Library)
+ intlibs := []bool{}
+ for _, lib := range ctxt.Library {
+ intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg))
+ }
+ ctxt.Textp2 = ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs, ctxt.Textp2)
}
// mangleTypeSym shortens the names of symbols that represent Go types
@@ -738,10 +867,29 @@ func (ctxt *Link) mangleTypeSym() {
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)
+ ldr := ctxt.loader
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ name := ldr.SymName(s)
+ newName := typeSymbolMangle(name)
+ if newName != name {
+ ldr.SetSymExtname(s, newName)
+
+ // When linking against a shared library, the Go object file may
+ // have reference to the original symbol name whereas the shared
+ // library provides a symbol with the mangled name. We need to
+ // copy the payload of mangled to original.
+ // XXX maybe there is a better way to do this.
+ dup := ldr.Lookup(newName, ldr.SymVersion(s))
+ if dup != 0 {
+ st := ldr.SymType(s)
+ dt := ldr.SymType(dup)
+ if st == sym.Sxxx && dt != sym.Sxxx {
+ ldr.CopySym(dup, s)
+ }
+ }
}
}
}
@@ -1051,25 +1199,21 @@ func hostlinksetup(ctxt *Link) {
*flagTmpdir = dir
ownTmpDir = true
AtExit(func() {
- ctxt.Out.f.Close()
+ ctxt.Out.Close()
os.RemoveAll(*flagTmpdir)
})
}
// change our output to temporary object file
- ctxt.Out.f.Close()
+ if err := ctxt.Out.Close(); err != nil {
+ Exitf("error closing output file")
+ }
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 {
+ if err := ctxt.Out.Open(p); 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
@@ -1153,11 +1297,9 @@ func (ctxt *Link) archive() {
// 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)
+ if err := ctxt.Out.Close(); err != nil {
+ Exitf("error closing %v", *flagOutfile)
}
- ctxt.Out.f = nil
argv := []string{*flagExtar, "-q", "-c", "-s"}
if ctxt.HeadType == objabi.Haix {
@@ -1702,107 +1844,55 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
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...)
+ ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn, ehdr.flags)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
}
- return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
+ ehdr.flags = flags
+ ctxt.Textp2 = append(ctxt.Textp2, 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...)
+ ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
}
- return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
+ ctxt.Textp2 = append(ctxt.Textp2, 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...)
+ ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
}
- 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...)
+ if rsrc != 0 {
+ setpersrc(ctxt, rsrc)
}
- return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
+ ctxt.Textp2 = append(ctxt.Textp2, 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...)
+ ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
}
- return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
+ ctxt.Textp2 = append(ctxt.Textp2, textp...)
}
+ return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
/* check the header */
@@ -1887,24 +1977,7 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
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
+ ctxt.loader.Preload(ctxt.Syms, f, lib, unit, eof-f.Offset(), 0)
addImports(ctxt, lib, pn)
return nil
}
@@ -2045,7 +2118,7 @@ func ldshlibsyms(ctxt *Link, shlib string) {
Errorf(nil, "cannot read symbols from shared library: %s", libpath)
return
}
- gcdataLocations := make(map[uint64]*sym.Symbol)
+ gcdataLocations := make(map[uint64]loader.Sym)
for _, elfsym := range syms {
if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION {
continue
@@ -2058,37 +2131,44 @@ func ldshlibsyms(ctxt *Link, shlib string) {
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 {
+ l := ctxt.loader
+ s := l.LookupOrCreateSym(elfsym.Name, ver)
+
+ // Because loadlib above loads all .a files before loading
+ // any shared libraries, any non-dynimport symbols we find
+ // that duplicate symbols already loaded should be ignored
+ // (the symbols from the .a files "win").
+ if l.SymType(s) != 0 && l.SymType(s) != sym.SDYNIMPORT {
continue
}
- lsym.Type = sym.SDYNIMPORT
- lsym.SetElfType(elf.ST_TYPE(elfsym.Info))
- lsym.Size = int64(elfsym.Size)
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SDYNIMPORT)
+ l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info))
+ su.SetSize(int64(elfsym.Size))
if elfsym.Section != elf.SHN_UNDEF {
+ // If it's not undefined, mark the symbol as reachable
+ // so as to protect it from dead code elimination,
+ // even if there aren't any explicit references to it.
+ // Under the previous sym.Symbol based regime this
+ // wasn't necessary, but for the loader-based deadcode
+ // it is definitely needed.
+ //
+ // FIXME: have a more general/flexible mechanism for this?
+ //
+ l.SetAttrReachable(s, true)
+
// Set .File for the library that actually defines the symbol.
- lsym.File = libpath
+ l.SetSymFile(s, 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
+ sname := l.SymName(s)
+ if strings.HasPrefix(sname, "type.") && !strings.HasPrefix(sname, "type..") {
+ su.SetData(readelfsymboldata(ctxt, f, &elfsym))
+ gcdataLocations[elfsym.Value+2*uint64(ctxt.Arch.PtrSize)+8+1*uint64(ctxt.Arch.PtrSize)] = s
}
}
+
// For function symbols, we don't know what ABI is
// available, so alias it under both ABIs.
//
@@ -2097,25 +2177,15 @@ func ldshlibsyms(ctxt *Link, shlib string) {
// 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 {
+ alias := ctxt.loader.LookupOrCreateSym(elfsym.Name, sym.SymVerABIInternal)
+ if l.SymType(alias) != 0 {
continue
}
- alias.Type = sym.SABIALIAS
- alias.R = []sym.Reloc{{Sym: lsym}}
+ su := l.MakeSymbolUpdater(alias)
+ su.SetType(sym.SABIALIAS)
+ su.AddReloc(loader.Reloc{Sym: s})
}
}
- gcdataAddresses := make(map[*sym.Symbol]uint64)
if ctxt.Arch.Family == sys.ARM64 {
for _, sect := range f.Sections {
if sect.Type == elf.SHT_RELA {
@@ -2133,15 +2203,12 @@ func ldshlibsyms(ctxt *Link, shlib string) {
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})
+ ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
}
func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section {
@@ -2155,16 +2222,11 @@ func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Sec
}
type chain struct {
- sym *sym.Symbol
+ sym loader.Sym
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
}
@@ -2176,10 +2238,23 @@ func callsize(ctxt *Link) int {
return ctxt.Arch.RegSize
}
-func (ctxt *Link) dostkcheck() {
- var ch chain
+type stkChk struct {
+ ldr *loader.Loader
+ ctxt *Link
+ morestack loader.Sym
+ done loader.Bitmap
+}
- morestack = ctxt.Syms.Lookup("runtime.morestack", 0)
+// Walk the call tree and check that there is always enough stack space
+// for the call frames, especially for a chain of nosplit functions.
+func (ctxt *Link) dostkcheck() {
+ ldr := ctxt.loader
+ sc := stkChk{
+ ldr: ldr,
+ ctxt: ctxt,
+ morestack: ldr.Lookup("runtime.morestack", 0),
+ done: loader.MakeBitmap(ldr.NSym()),
+ }
// Every splitting function ensures that there are at least StackLimit
// bytes available below SP when the splitting prologue finishes.
@@ -2188,8 +2263,7 @@ func (ctxt *Link) dostkcheck() {
// 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
-
+ var ch chain
ch.limit = objabi.StackLimit - callsize(ctxt)
if objabi.GOARCH == "arm64" {
// need extra 8 bytes below SP to save FP
@@ -2198,118 +2272,115 @@ func (ctxt *Link) dostkcheck() {
// 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() {
+ for _, s := range ctxt.Textp2 {
+ if ldr.IsNoSplit(s) {
ch.sym = s
- stkcheck(ctxt, &ch, 0)
+ sc.check(&ch, 0)
}
}
- for _, s := range ctxt.Textp {
- if !s.Attr.NoSplit() {
+ for _, s := range ctxt.Textp2 {
+ if !ldr.IsNoSplit(s) {
ch.sym = s
- stkcheck(ctxt, &ch, 0)
+ sc.check(&ch, 0)
}
}
}
-func stkcheck(ctxt *Link, up *chain, depth int) int {
+func (sc *stkChk) check(up *chain, depth int) int {
limit := up.limit
s := up.sym
+ ldr := sc.ldr
+ ctxt := sc.ctxt
// Don't duplicate work: only need to consider each
// function at top of safe zone once.
top := limit == objabi.StackLimit-callsize(ctxt)
if top {
- if s.Attr.StackCheck() {
+ if sc.done.Has(s) {
return 0
}
- s.Attr |= sym.AttrStackCheck
+ sc.done.Set(s)
}
if depth > 500 {
- Errorf(s, "nosplit stack check too deep")
- stkbroke(ctxt, up, 0)
+ sc.ctxt.Errorf(s, "nosplit stack check too deep")
+ sc.broke(up, 0)
return -1
}
- if s.Attr.External() || s.FuncInfo == nil {
+ if ldr.AttrExternal(s) {
// external function.
// should never be called directly.
// onlyctxt.Diagnose the direct caller.
// TODO(mwhudson): actually think about this.
// TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack.
// See the trampolines in src/runtime/sys_darwin_$ARCH.go.
- if depth == 1 && s.Type != sym.SXREF && !ctxt.DynlinkingGo() &&
- ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin {
- //Errorf(s, "call to external function")
- }
+ //if depth == 1 && ldr.SymType(s) != sym.SXREF && !ctxt.DynlinkingGo() &&
+ // ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin {
+ // Errorf(s, "call to external function")
+ //}
+ return -1
+ }
+ info := ldr.FuncInfo(s)
+ if !info.Valid() { // external function. see above.
return -1
}
if limit < 0 {
- stkbroke(ctxt, up, limit)
+ sc.broke(up, limit)
return -1
}
// morestack looks like it calls functions,
// but it switches the stack pointer first.
- if s == morestack {
+ if s == sc.morestack {
return 0
}
var ch chain
ch.up = up
- if !s.Attr.NoSplit() {
+ if !ldr.IsNoSplit(s) {
// Ensure we have enough stack to call morestack.
ch.limit = limit - callsize(ctxt)
- ch.sym = morestack
- if stkcheck(ctxt, &ch, depth+1) < 0 {
+ ch.sym = sc.morestack
+ if sc.check(&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
- }
+ locals := info.Locals()
limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize())
}
// Walk through sp adjustments in function, consuming relocs.
- ri := 0
-
- endr := len(s.R)
+ relocs := ldr.Relocs(s)
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() {
+ for pcsp.Init(info.Pcsp()); !pcsp.Done; pcsp.Next() {
// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
// Check stack size in effect for this span.
if int32(limit)-pcsp.Value < 0 {
- stkbroke(ctxt, up, int(int32(limit)-pcsp.Value))
+ sc.broke(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]
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At2(i)
+ if uint32(r.Off()) >= pcsp.NextPC {
+ break
+ }
+ t := r.Type()
switch {
- case r.Type.IsDirectCall():
+ case t.IsDirectCall():
ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
- ch.sym = r.Sym
- if stkcheck(ctxt, &ch, depth+1) < 0 {
+ ch.sym = r.Sym()
+ if sc.check(&ch, depth+1) < 0 {
return -1
}
@@ -2317,13 +2388,13 @@ func stkcheck(ctxt *Link, up *chain, depth int) int {
// 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:
+ case t == objabi.R_CALLIND:
ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
- ch.sym = nil
+ ch.sym = 0
ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue
ch1.up = &ch
- ch1.sym = morestack
- if stkcheck(ctxt, &ch1, depth+2) < 0 {
+ ch1.sym = sc.morestack
+ if sc.check(&ch1, depth+2) < 0 {
return -1
}
}
@@ -2333,17 +2404,18 @@ func stkcheck(ctxt *Link, up *chain, depth int) int {
return 0
}
-func stkbroke(ctxt *Link, ch *chain, limit int) {
- Errorf(ch.sym, "nosplit stack overflow")
- stkprint(ctxt, ch, limit)
+func (sc *stkChk) broke(ch *chain, limit int) {
+ sc.ctxt.Errorf(ch.sym, "nosplit stack overflow")
+ sc.print(ch, limit)
}
-func stkprint(ctxt *Link, ch *chain, limit int) {
+func (sc *stkChk) print(ch *chain, limit int) {
+ ldr := sc.ldr
+ ctxt := sc.ctxt
var name string
-
- if ch.sym != nil {
- name = ch.sym.Name
- if ch.sym.Attr.NoSplit() {
+ if ch.sym != 0 {
+ name = ldr.SymName(ch.sym)
+ if ldr.IsNoSplit(ch.sym) {
name += " (nosplit)"
}
} else {
@@ -2351,14 +2423,14 @@ func stkprint(ctxt *Link, ch *chain, limit int) {
}
if ch.up == nil {
- // top of chain. ch->sym != nil.
- if ch.sym.Attr.NoSplit() {
+ // top of chain. ch.sym != 0.
+ if ldr.IsNoSplit(ch.sym) {
fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name)
} else {
fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name)
}
} else {
- stkprint(ctxt, ch.up, ch.limit+callsize(ctxt))
+ sc.print(ch.up, ch.limit+callsize(ctxt))
if !haslinkregister(ctxt) {
fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name)
}
@@ -2619,7 +2691,7 @@ func (ctxt *Link) undef() {
for _, s := range ctxt.Textp {
undefsym(ctxt, s)
}
- for _, s := range datap {
+ for _, s := range ctxt.datap {
undefsym(ctxt, s)
}
if nerrors > 0 {
@@ -2632,16 +2704,17 @@ func (ctxt *Link) callgraph() {
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 {
+ ldr := ctxt.loader
+ for _, s := range ctxt.Textp2 {
+ relocs := ldr.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At2(i)
+ rs := r.Sym()
+ if rs == 0 {
continue
}
- if r.Type.IsDirectCall() && r.Sym.Type == sym.STEXT {
- ctxt.Logf("%s calls %s\n", s.Name, r.Sym.Name)
+ if r.Type().IsDirectCall() && (ldr.SymType(rs) == sym.STEXT || ldr.SymType(rs) == sym.SABIALIAS) {
+ ctxt.Logf("%s calls %s\n", ldr.SymName(s), ldr.SymName(rs))
}
}
}
@@ -2703,40 +2776,30 @@ func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library
}
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)
+ // Convert ctxt.Moduledata2 to ctxt.Moduledata, etc
+ if ctxt.Moduledata2 != 0 {
+ ctxt.Moduledata = ctxt.loader.Syms[ctxt.Moduledata2]
+ ctxt.Tlsg = ctxt.loader.Syms[ctxt.Tlsg2]
}
- setupdynexp(ctxt)
+ // Pull the symbols out.
+ ctxt.loader.ExtractSymbols(ctxt.Syms, ctxt.Reachparent)
+ ctxt.lookup = ctxt.Syms.ROLookup
- // 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
- }
- }
+ // Recreate dynexp using *sym.Symbol instead of loader.Sym
+ genSymsForDynexp(ctxt)
- // Drop the reference.
- ctxt.loader = nil
+ // Drop the cgodata reference.
ctxt.cgodata = nil
addToTextp(ctxt)
+
+ // Set special global symbols.
+ ctxt.setArchSyms(AfterLoadlibFull)
}
func (ctxt *Link) dumpsyms() {
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index df3845fac3..24866d8e8c 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -32,7 +32,6 @@ package ld
import (
"bufio"
- "cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
@@ -42,67 +41,61 @@ import (
)
type Shlib struct {
- Path string
- Hash []byte
- Deps []string
- File *elf.File
- gcdataAddresses map[*sym.Symbol]uint64
+ Path string
+ Hash []byte
+ Deps []string
+ File *elf.File
}
// Link holds the context for writing object code from a compiler
// or for reading that input into the linker.
type Link struct {
- Out *OutBuf
+ Target
+ ErrorReporter
+ ArchSyms
+
+ outSem chan int // limits the number of output writers
+ 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
+ Tlsg2 loader.Sym
Libdir []string
Library []*sym.Library
LibraryByPkg map[string]*sym.Library
Shlibs []Shlib
- Tlsoffset int
Textp []*sym.Symbol
+ Textp2 []loader.Sym
Filesyms []*sym.Symbol
Moduledata *sym.Symbol
+ Moduledata2 loader.Sym
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
+
+ datap []*sym.Symbol
+ dynexp2 []loader.Sym
}
type cgodata struct {
@@ -111,48 +104,6 @@ type cgodata struct {
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 && rs.Type != sym.SXREF {
- 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
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index e50eddd96c..0f36cd0cbe 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -428,42 +428,49 @@ func (ctxt *Link) domacho() {
}
// 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
+ s := ctxt.loader.LookupOrCreateSym(".machosymstr", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ sb.SetType(sym.SMACHOSYMSTR)
+ sb.SetReachable(true)
+ sb.AddUint8(' ')
+ sb.AddUint8('\x00')
+
+ s = ctxt.loader.LookupOrCreateSym(".machosymtab", 0)
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOSYMTAB)
+ sb.SetReachable(true)
+
+ if ctxt.IsInternal() {
+ s = ctxt.loader.LookupOrCreateSym(".plt", 0) // will be __symbol_stub
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOPLT)
+ sb.SetReachable(true)
+
+ s = ctxt.loader.LookupOrCreateSym(".got", 0) // will be __nl_symbol_ptr
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOGOT)
+ sb.SetReachable(true)
+ sb.SetAlign(4)
+
+ s = ctxt.loader.LookupOrCreateSym(".linkedit.plt", 0) // indirect table for .plt
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOINDIRECTPLT)
+ sb.SetReachable(true)
+
+ s = ctxt.loader.LookupOrCreateSym(".linkedit.got", 0) // indirect table for .got
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOINDIRECTGOT)
+ sb.SetReachable(true)
}
// Add a dummy symbol that will become the __asm marker section.
- if ctxt.LinkMode == LinkExternal {
- s := ctxt.Syms.Lookup(".llvmasm", 0)
- s.Type = sym.SMACHO
- s.Attr |= sym.AttrReachable
- s.AddUint8(0)
+ if ctxt.IsExternal() {
+ s = ctxt.loader.LookupOrCreateSym(".llvmasm", 0)
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHO)
+ sb.SetReachable(true)
+ sb.AddUint8(0)
}
}
@@ -1053,10 +1060,10 @@ func Machoemitreloc(ctxt *Link) {
machorelocsect(ctxt, Segtext.Sections[0], ctxt.Textp)
for _, sect := range Segtext.Sections[1:] {
- machorelocsect(ctxt, sect, datap)
+ machorelocsect(ctxt, sect, ctxt.datap)
}
for _, sect := range Segdata.Sections {
- machorelocsect(ctxt, sect, datap)
+ machorelocsect(ctxt, sect, ctxt.datap)
}
for _, sect := range Segdwarf.Sections {
machorelocsect(ctxt, sect, dwarfp)
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index 9f9395b757..3772c5090c 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -34,10 +34,12 @@ import (
"bufio"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/benchmark"
"cmd/link/internal/sym"
"flag"
"log"
"os"
+ "os/exec"
"runtime"
"runtime/pprof"
"strings"
@@ -87,8 +89,6 @@ var (
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")
@@ -96,6 +96,11 @@ var (
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
memprofile = flag.String("memprofile", "", "write memory profile to `file`")
memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
+
+ benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
+ benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")
+
+ flagGo115Newobj = flag.Bool("go115newobj", true, "use new object file format")
)
// Main is the main entry point for the linker code.
@@ -135,6 +140,10 @@ func Main(arch *sys.Arch, theArch Arch) {
objabi.Flagparse(usage)
+ if !*flagGo115Newobj {
+ oldlink()
+ }
+
switch *flagHeadType {
case "":
case "windowsgui":
@@ -170,13 +179,29 @@ func Main(arch *sys.Arch, theArch Arch) {
interpreter = *flagInterpreter
+ // enable benchmarking
+ var bench *benchmark.Metrics
+ if len(*benchmarkFlag) != 0 {
+ if *benchmarkFlag == "mem" {
+ bench = benchmark.New(benchmark.GC, *benchmarkFileFlag)
+ } else if *benchmarkFlag == "cpu" {
+ bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag)
+ } else {
+ Errorf(nil, "unknown benchmark flag: %q", *benchmarkFlag)
+ usage()
+ }
+ }
+
+ bench.Start("libinit")
libinit(ctxt) // creates outfile
if ctxt.HeadType == objabi.Hunknown {
ctxt.HeadType.Set(objabi.GOOS)
}
+ bench.Start("computeTLSOffset")
ctxt.computeTLSOffset()
+ bench.Start("Archinit")
thearch.Archinit(ctxt)
if ctxt.linkShared && !ctxt.IsELF {
@@ -207,47 +232,84 @@ func Main(arch *sys.Arch, theArch Arch) {
default:
addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "")
}
+ bench.Start("loadlib")
ctxt.loadlib()
+ bench.Start("deadcode")
deadcode(ctxt)
- if *flagNewobj {
- ctxt.loadlibfull() // XXX do it here for now
- }
+
+ bench.Start("linksetup")
ctxt.linksetup()
- ctxt.dostrdata()
- dwarfGenerateDebugInfo(ctxt)
+ bench.Start("dostrdata")
+ ctxt.dostrdata()
if objabi.Fieldtrack_enabled != 0 {
- fieldtrack(ctxt)
+ bench.Start("fieldtrack")
+ fieldtrack(ctxt.Arch, ctxt.loader)
}
- ctxt.mangleTypeSym()
+
+ bench.Start("dwarfGenerateDebugInfo")
+ dwarfGenerateDebugInfo(ctxt)
+
+ bench.Start("callgraph")
ctxt.callgraph()
- ctxt.doelf()
- if ctxt.HeadType == objabi.Hdarwin {
+ bench.Start("dostkcheck")
+ ctxt.dostkcheck()
+
+ bench.Start("mangleTypeSym")
+ ctxt.mangleTypeSym()
+
+ if ctxt.IsELF {
+ bench.Start("doelf")
+ ctxt.doelf()
+ }
+ if ctxt.IsDarwin() {
+ bench.Start("domacho")
ctxt.domacho()
}
- ctxt.dostkcheck()
- if ctxt.HeadType == objabi.Hwindows {
+ if ctxt.IsWindows() {
+ bench.Start("dope")
ctxt.dope()
+ bench.Start("windynrelocsyms")
ctxt.windynrelocsyms()
}
- if ctxt.HeadType == objabi.Haix {
+ if ctxt.IsAIX() {
+ bench.Start("doxcoff")
ctxt.doxcoff()
}
+ bench.Start("textbuildid")
+ ctxt.textbuildid()
+ bench.Start("addexport")
+ setupdynexp(ctxt)
+ ctxt.setArchSyms(BeforeLoadlibFull)
ctxt.addexport()
+
+ bench.Start("loadlibfull")
+ ctxt.loadlibfull() // XXX do it here for now
+
+ bench.Start("Gentext")
thearch.Gentext(ctxt) // trampolines, call stubs, etc.
- ctxt.textbuildid()
+ bench.Start("textaddress")
ctxt.textaddress()
+ bench.Start("pclntab")
ctxt.pclntab()
+ bench.Start("findfunctab")
ctxt.findfunctab()
+ bench.Start("typelink")
ctxt.typelink()
+ bench.Start("symtab")
ctxt.symtab()
+ bench.Start("buildinfo")
ctxt.buildinfo()
+ bench.Start("dodata")
ctxt.dodata()
+ bench.Start("address")
order := ctxt.address()
+ bench.Start("dwarfcompress")
dwarfcompress(ctxt)
+ bench.Start("layout")
filesize := ctxt.layout(order)
// Write out the output file.
@@ -266,25 +328,36 @@ func Main(arch *sys.Arch, theArch Arch) {
if outputMmapped {
// Asmb will redirect symbols to the output file mmap, and relocations
// will be applied directly there.
+ bench.Start("Asmb")
thearch.Asmb(ctxt)
+ bench.Start("reloc")
ctxt.reloc()
+ bench.Start("Munmap")
ctxt.Out.Munmap()
} else {
// If we don't mmap, we need to apply relocations before
// writing out.
+ bench.Start("reloc")
ctxt.reloc()
+ bench.Start("Asmb")
thearch.Asmb(ctxt)
}
+ bench.Start("Asmb2")
thearch.Asmb2(ctxt)
+ bench.Start("undef")
ctxt.undef()
+ bench.Start("hostlink")
ctxt.hostlink()
if ctxt.Debugvlog != 0 {
ctxt.Logf("%d symbols\n", len(ctxt.Syms.Allsym))
ctxt.Logf("%d liveness data\n", liveness)
}
+ bench.Start("Flush")
ctxt.Bso.Flush()
+ bench.Start("archive")
ctxt.archive()
+ bench.Report(os.Stdout)
errorexit()
}
@@ -336,3 +409,48 @@ func startProfile() {
})
}
}
+
+// Invoke the old linker and exit.
+func oldlink() {
+ linker := os.Args[0]
+ if strings.HasSuffix(linker, "link") {
+ linker = linker[:len(linker)-4] + "oldlink"
+ } else if strings.HasSuffix(linker, "link.exe") {
+ linker = linker[:len(linker)-8] + "oldlink.exe"
+ } else {
+ log.Fatal("cannot find oldlink. arg0=", linker)
+ }
+
+ // Copy args, filter out -go115newobj flag
+ args := make([]string, 0, len(os.Args)-1)
+ skipNext := false
+ for i, a := range os.Args {
+ if i == 0 {
+ continue // skip arg0
+ }
+ if skipNext {
+ skipNext = false
+ continue
+ }
+ if a == "-go115newobj" {
+ skipNext = true
+ continue
+ }
+ if strings.HasPrefix(a, "-go115newobj=") {
+ continue
+ }
+ args = append(args, a)
+ }
+
+ cmd := exec.Command(linker, args...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ if err == nil {
+ os.Exit(0)
+ }
+ if _, ok := err.(*exec.ExitError); ok {
+ os.Exit(2) // would be nice to use ExitError.ExitCode(), but that is too new
+ }
+ log.Fatal("invoke oldlink failed:", err)
+}
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
index f8e65ef8ae..8e083477ec 100644
--- a/src/cmd/link/internal/ld/outbuf.go
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -9,6 +9,7 @@ import (
"cmd/internal/sys"
"cmd/link/internal/sym"
"encoding/binary"
+ "errors"
"log"
"os"
)
@@ -23,19 +24,100 @@ import (
// 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:
+// Third, it also mmaps the output file (if available). The intended usage is:
// - Mmap the output file
// - Write the content
// - possibly apply any edits in the output buffer
// - Munmap the output file
// - possibly write more content to the file, which will not be edited later.
+//
+// And finally, it provides a mechanism by which you can multithread the
+// writing of output files. This mechanism is accomplished by copying a OutBuf,
+// and using it in the thread/goroutine.
+//
+// Parallel OutBuf is intended to be used like:
+//
+// func write(out *OutBuf) {
+// var wg sync.WaitGroup
+// for i := 0; i < 10; i++ {
+// wg.Add(1)
+// view, err := out.View(start[i])
+// if err != nil {
+// // handle output
+// continue
+// }
+// go func(out *OutBuf, i int) {
+// // do output
+// wg.Done()
+// }(view, i)
+// }
+// wg.Wait()
+// }
type OutBuf struct {
- arch *sys.Arch
- off int64
- w *bufio.Writer
- buf []byte // backing store of mmap'd output file
- f *os.File
- encbuf [8]byte // temp buffer used by WriteN methods
+ arch *sys.Arch
+ off int64
+ w *bufio.Writer
+ buf []byte // backing store of mmap'd output file
+ name string
+ f *os.File
+ encbuf [8]byte // temp buffer used by WriteN methods
+ isView bool // true if created from View()
+ start, length uint64 // start and length mmaped data.
+}
+
+func (out *OutBuf) Open(name string) error {
+ if out.f != nil {
+ return errors.New("cannont open more than one file")
+ }
+ f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
+ if err != nil {
+ return err
+ }
+ out.off = 0
+ out.name = name
+ out.w = bufio.NewWriter(f)
+ out.f = f
+ return nil
+}
+
+func NewOutBuf(arch *sys.Arch) *OutBuf {
+ return &OutBuf{
+ arch: arch,
+ }
+}
+
+var viewError = errors.New("output not mmapped")
+
+func (out *OutBuf) View(start uint64) (*OutBuf, error) {
+ if out.buf == nil {
+ return nil, viewError
+ }
+ return &OutBuf{
+ arch: out.arch,
+ name: out.name,
+ buf: out.buf,
+ off: int64(start),
+ start: start,
+ length: out.length,
+ isView: true,
+ }, nil
+}
+
+var viewCloseError = errors.New("cannot Close OutBuf from View")
+
+func (out *OutBuf) Close() error {
+ if out.isView {
+ return viewCloseError
+ }
+ out.Flush()
+ if out.f == nil {
+ return nil
+ }
+ if err := out.f.Close(); err != nil {
+ return err
+ }
+ out.f = nil
+ return nil
}
func (out *OutBuf) SeekSet(p int64) {
@@ -45,7 +127,7 @@ func (out *OutBuf) SeekSet(p int64) {
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)
+ Exitf("seeking to %d in %s: %v", p, out.name, err)
}
}
out.off = p
@@ -154,13 +236,16 @@ func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) {
// edit to the symbol content.
// If the output file is not Mmap'd, just writes the content.
func (out *OutBuf) WriteSym(s *sym.Symbol) {
+ // NB: We inline the Write call for speediness.
if out.buf != nil {
start := out.off
- out.Write(s.P)
+ n := copy(out.buf[out.off:], s.P)
+ out.off += int64(n)
s.P = out.buf[start:out.off]
s.Attr.Set(sym.AttrReadOnly, false)
} else {
- out.Write(s.P)
+ n, _ := out.w.Write(s.P)
+ out.off += int64(n)
}
}
@@ -168,10 +253,11 @@ func (out *OutBuf) Flush() {
var err error
if out.buf != nil {
err = out.Msync()
- } else {
+ }
+ if out.w != nil {
err = out.w.Flush()
}
if err != nil {
- Exitf("flushing %s: %v", out.f.Name(), err)
+ Exitf("flushing %s: %v", out.name, err)
}
}
diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go
index 4075141171..c064e9686a 100644
--- a/src/cmd/link/internal/ld/outbuf_mmap.go
+++ b/src/cmd/link/internal/ld/outbuf_mmap.go
@@ -16,11 +16,15 @@ func (out *OutBuf) Mmap(filesize uint64) error {
if err != nil {
Exitf("resize output file failed: %v", err)
}
+ out.length = filesize
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() {
+ if out.buf == nil {
+ return
+ }
err := out.Msync()
if err != nil {
Exitf("msync output file failed: %v", err)
@@ -34,9 +38,12 @@ func (out *OutBuf) Munmap() {
}
func (out *OutBuf) Msync() error {
+ if out.buf == nil || out.length <= 0 {
+ return nil
+ }
// 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)
+ _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(out.length), syscall.MS_SYNC)
if errno != 0 {
return errno
}
diff --git a/src/cmd/link/internal/ld/outbuf_test.go b/src/cmd/link/internal/ld/outbuf_test.go
new file mode 100644
index 0000000000..512238f39a
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_test.go
@@ -0,0 +1,30 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "os"
+ "runtime"
+ "testing"
+)
+
+// TestMMap ensures that we can actually mmap on every supported platform.
+func TestMMap(t *testing.T) {
+ switch runtime.GOOS {
+ default:
+ t.Skip("unsupported OS")
+ case "darwin", "dragonfly", "freebsd", "linux", "openbsd", "windows":
+ }
+ filename := "foo.out"
+ ob := NewOutBuf(nil)
+ if err := ob.Open(filename); err != nil {
+ t.Errorf("error opening file: %v", err)
+ }
+ defer os.RemoveAll(filename)
+ defer ob.Close()
+ if err := ob.Mmap(1 << 20); err != nil {
+ t.Errorf("error mmapping file %v", err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go
index 1cb05c301f..f745a5cb22 100644
--- a/src/cmd/link/internal/ld/outbuf_windows.go
+++ b/src/cmd/link/internal/ld/outbuf_windows.go
@@ -17,7 +17,7 @@ func (out *OutBuf) Mmap(filesize uint64) error {
}
low, high := uint32(filesize), uint32(filesize>>32)
- fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READONLY, high, low, nil)
+ fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READWRITE, high, low, nil)
if err != nil {
return err
}
@@ -36,6 +36,7 @@ func (out *OutBuf) Munmap() {
return
}
err := syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])))
+ out.buf = nil
if err != nil {
Exitf("UnmapViewOfFile failed: %v", err)
}
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
index f775132393..364e757985 100644
--- a/src/cmd/link/internal/ld/pe.go
+++ b/src/cmd/link/internal/ld/pe.go
@@ -10,6 +10,7 @@ package ld
import (
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/pe"
"encoding/binary"
@@ -237,7 +238,7 @@ var dosstub = []uint8{
}
type Imp struct {
- s *sym.Symbol
+ s loader.Sym
off uint64
next *Imp
argsize int
@@ -252,13 +253,13 @@ type Dll struct {
}
var (
- rsrcsym *sym.Symbol
+ rsrcsym loader.Sym
PESECTHEADR int32
PEFILEHEADR int32
pe64 int
dr *Dll
- dexport [1024]*sym.Symbol
- nexport int
+
+ dexport = make([]loader.Sym, 0, 1024)
)
// peStringTable is a COFF string table.
@@ -542,8 +543,8 @@ func (f *peFile) emitRelocations(ctxt *Link) {
syms []*sym.Symbol
}{
{f.textSect, &Segtext, ctxt.Textp},
- {f.rdataSect, &Segrodata, datap},
- {f.dataSect, &Segdata, datap},
+ {f.rdataSect, &Segrodata, ctxt.datap},
+ {f.dataSect, &Segdata, ctxt.datap},
}
for _, s := range sects {
s.peSect.emitRelocations(ctxt.Out, func() int {
@@ -957,8 +958,15 @@ func Peinit(ctxt *Link) {
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)
+ for _, name := range [2]string{"__image_base__", "_image_base__"} {
+ s := ctxt.loader.LookupOrCreateSym(name, 0)
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SDATA)
+ sb.SetValue(PEBASE)
+ ctxt.loader.SetAttrReachable(s, true)
+ ctxt.loader.SetAttrSpecial(s, true)
+ ctxt.loader.SetAttrLocal(s, true)
+ }
}
HEADR = PEFILEHEADR
@@ -996,16 +1004,18 @@ func strput(out *OutBuf, s string) {
}
func initdynimport(ctxt *Link) *Dll {
+ ldr := ctxt.loader
var d *Dll
dr = nil
var m *Imp
- for _, s := range ctxt.Syms.Allsym {
- if !s.Attr.Reachable() || s.Type != sym.SDYNIMPORT {
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT {
continue
}
+ dynlib := ldr.SymDynimplib(s)
for d = dr; d != nil; d = d.next {
- if d.name == s.Dynimplib() {
+ if d.name == dynlib {
m = new(Imp)
break
}
@@ -1013,7 +1023,7 @@ func initdynimport(ctxt *Link) *Dll {
if d == nil {
d = new(Dll)
- d.name = s.Dynimplib()
+ d.name = dynlib
d.next = dr
dr = d
m = new(Imp)
@@ -1024,15 +1034,15 @@ func initdynimport(ctxt *Link) *Dll {
// of uinptrs this function consumes. Store the argsize and discard
// the %n suffix if any.
m.argsize = -1
- extName := s.Extname()
+ extName := ldr.SymExtname(s)
if i := strings.IndexByte(extName, '%'); i >= 0 {
var err error
m.argsize, err = strconv.Atoi(extName[i+1:])
if err != nil {
- Errorf(s, "failed to parse stdcall decoration: %v", err)
+ ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err)
}
m.argsize *= ctxt.Arch.PtrSize
- s.SetExtname(extName[:i])
+ ldr.SetSymExtname(s, extName[:i])
}
m.s = s
@@ -1040,42 +1050,38 @@ func initdynimport(ctxt *Link) *Dll {
d.ms = m
}
- if ctxt.LinkMode == LinkExternal {
+ if ctxt.IsExternal() {
// 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()
+ sb := ldr.MakeSymbolUpdater(m.s)
+ sb.SetType(sym.SDATA)
+ sb.Grow(int64(ctxt.Arch.PtrSize))
+ dynName := sb.Extname()
// only windows/386 requires stdcall decoration
- if ctxt.Arch.Family == sys.I386 && m.argsize >= 0 {
+ if ctxt.Is386() && 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
+ dynSym := ldr.CreateSymForUpdate(dynName, 0)
+ dynSym.SetReachable(true)
+ dynSym.SetType(sym.SHOSTOBJ)
+ sb.AddReloc(loader.Reloc{Sym: dynSym.Sym(), Type: objabi.R_ADDR, Off: 0, Size: uint8(ctxt.Arch.PtrSize)})
}
}
} else {
- dynamic := ctxt.Syms.Lookup(".windynamic", 0)
- dynamic.Attr |= sym.AttrReachable
- dynamic.Type = sym.SWINDOWS
+ dynamic := ldr.CreateSymForUpdate(".windynamic", 0)
+ dynamic.SetReachable(true)
+ dynamic.SetType(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)
+ sb := ldr.MakeSymbolUpdater(m.s)
+ sb.SetType(sym.SWINDOWS)
+ dynamic.PrependSub(m.s)
+ sb.SetValue(dynamic.Size())
+ dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize))
}
- dynamic.Size += int64(ctxt.Arch.PtrSize)
+ dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize))
}
}
@@ -1095,6 +1101,7 @@ func peimporteddlls() []string {
}
func addimports(ctxt *Link, datsect *peSection) {
+ ldr := ctxt.loader
startoff := ctxt.Out.Offset()
dynamic := ctxt.Syms.Lookup(".windynamic", 0)
@@ -1117,7 +1124,7 @@ func addimports(ctxt *Link, datsect *peSection) {
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())
+ strput(ctxt.Out, ldr.SymExtname(m.s))
}
}
@@ -1198,36 +1205,31 @@ func addimports(ctxt *Link, datsect *peSection) {
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() {
+ ldr := ctxt.loader
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) {
continue
}
- if nexport+1 > len(dexport) {
- Errorf(s, "pe dynexport table is full")
+ if len(dexport)+1 > cap(dexport) {
+ ctxt.Errorf(s, "pe dynexport table is full")
errorexit()
}
- dexport[nexport] = s
- nexport++
+ dexport = append(dexport, s)
}
- sort.Sort(byExtname(dexport[:nexport]))
+ sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) })
}
func addexports(ctxt *Link) {
+ ldr := ctxt.loader
var e IMAGE_EXPORT_DIRECTORY
+ nexport := len(dexport)
size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1
- for i := 0; i < nexport; i++ {
- size += len(dexport[i].Extname()) + 1
+ for _, s := range dexport {
+ size += len(ldr.Syms[s].Extname()) + 1
}
if nexport == 0 {
@@ -1262,16 +1264,16 @@ func addexports(ctxt *Link) {
binary.Write(out, binary.LittleEndian, &e)
// put EXPORT Address Table
- for i := 0; i < nexport; i++ {
- out.Write32(uint32(dexport[i].Value - PEBASE))
+ for _, s := range dexport {
+ out.Write32(uint32(ldr.Syms[s].Value - PEBASE))
}
// put EXPORT Name Pointer Table
v := int(e.Name + uint32(len(*flagOutfile)) + 1)
- for i := 0; i < nexport; i++ {
+ for _, s := range dexport {
out.Write32(uint32(v))
- v += len(dexport[i].Extname()) + 1
+ v += len(ldr.Syms[s].Extname()) + 1
}
// put EXPORT Ordinal Table
@@ -1282,8 +1284,9 @@ func addexports(ctxt *Link) {
// put Names
out.WriteStringN(*flagOutfile, len(*flagOutfile)+1)
- for i := 0; i < nexport; i++ {
- out.WriteStringN(dexport[i].Extname(), len(dexport[i].Extname())+1)
+ for _, s := range dexport {
+ ss := ldr.Syms[s]
+ out.WriteStringN(ss.Extname(), len(ss.Extname())+1)
}
sect.pad(out, uint32(size))
}
@@ -1431,7 +1434,7 @@ func addPEBaseReloc(ctxt *Link) {
for _, s := range ctxt.Textp {
addPEBaseRelocSym(ctxt, s, &rt)
}
- for _, s := range datap {
+ for _, s := range ctxt.datap {
addPEBaseRelocSym(ctxt, s, &rt)
}
@@ -1455,28 +1458,31 @@ func (ctxt *Link) dope() {
initdynexport(ctxt)
}
-func setpersrc(ctxt *Link, sym *sym.Symbol) {
- if rsrcsym != nil {
- Errorf(sym, "too many .rsrc sections")
+func setpersrc(ctxt *Link, sym loader.Sym) {
+ if rsrcsym != 0 {
+ Errorf(nil, "too many .rsrc sections")
}
rsrcsym = sym
}
func addpersrc(ctxt *Link) {
- if rsrcsym == nil {
+ if rsrcsym == 0 {
return
}
- h := pefile.addSection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size))
+ data := ctxt.loader.Data(rsrcsym)
+ size := len(data)
+ h := pefile.addSection(".rsrc", size, 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)
+ relocs := ctxt.loader.Relocs(rsrcsym)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At2(i)
+ p := data[r.Off():]
+ val := uint32(int64(h.virtualAddress) + r.Add())
// 32-bit little-endian
p[0] = byte(val)
@@ -1486,8 +1492,8 @@ func addpersrc(ctxt *Link) {
p[3] = byte(val >> 24)
}
- ctxt.Out.Write(rsrcsym.P)
- h.pad(ctxt.Out, uint32(rsrcsym.Size))
+ ctxt.Out.Write(data)
+ h.pad(ctxt.Out, uint32(size))
// update data directory
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
index eb48ac842a..97966ed7e4 100644
--- a/src/cmd/link/internal/ld/sym.go
+++ b/src/cmd/link/internal/ld/sym.go
@@ -36,13 +36,15 @@ import (
"cmd/internal/sys"
"cmd/link/internal/sym"
"log"
+ "runtime"
)
func linknew(arch *sys.Arch) *Link {
ctxt := &Link{
+ Target: Target{Arch: arch},
Syms: sym.NewSymbols(),
- Out: &OutBuf{arch: arch},
- Arch: arch,
+ outSem: make(chan int, 2*runtime.GOMAXPROCS(0)),
+ Out: NewOutBuf(arch),
LibraryByPkg: make(map[string]*sym.Library),
}
@@ -51,8 +53,8 @@ func linknew(arch *sys.Arch) *Link {
}
AtExit(func() {
- if nerrors > 0 && ctxt.Out.f != nil {
- ctxt.Out.f.Close()
+ if nerrors > 0 {
+ ctxt.Out.Close()
mayberemoveoutfile()
}
})
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index bba623eb48..97cbb5616e 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -163,6 +163,13 @@ func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64, go
other |= 3 << 5
}
+ if s == x.Name {
+ // We should use Extname for ELF symbol table.
+ // TODO: maybe genasmsym should have done this. That function is too
+ // overloaded and I would rather not change it for now.
+ s = x.Extname()
+ }
+
// 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.
@@ -326,12 +333,11 @@ func textsectionmap(ctxt *Link) uint32 {
}
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 {
+ if ctxt.HeadType != objabi.Haix {
+ switch ctxt.BuildMode {
+ case BuildModeCArchive, BuildModeCShared:
+ s := ctxt.Syms.ROLookup(*flagEntrySymbol, sym.SymVerABI0)
+ if s != nil {
addinitarrdata(ctxt, s)
}
}
diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go
new file mode 100644
index 0000000000..95f6ca17ab
--- /dev/null
+++ b/src/cmd/link/internal/ld/target.go
@@ -0,0 +1,144 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "encoding/binary"
+)
+
+// Target holds the configuration we're building for.
+type Target struct {
+ Arch *sys.Arch
+
+ HeadType objabi.HeadType
+
+ LinkMode LinkMode
+ BuildMode BuildMode
+
+ linkShared bool
+ canUsePlugins bool
+ IsELF bool
+}
+
+//
+// Target type functions
+//
+
+func (t *Target) IsShared() bool {
+ return t.BuildMode == BuildModeShared
+}
+
+func (t *Target) IsPlugin() bool {
+ return t.BuildMode == BuildModePlugin
+}
+
+func (t *Target) IsInternal() bool {
+ return t.LinkMode == LinkInternal
+}
+
+func (t *Target) IsExternal() bool {
+ return t.LinkMode == LinkExternal
+}
+
+func (t *Target) IsPIE() bool {
+ return t.BuildMode == BuildModePIE
+}
+
+func (t *Target) IsSharedGoLink() bool {
+ return t.linkShared
+}
+
+func (t *Target) CanUsePlugins() bool {
+ return t.canUsePlugins
+}
+
+func (t *Target) IsElf() bool {
+ return t.IsELF
+}
+
+func (t *Target) IsDynlinkingGo() bool {
+ return t.IsShared() || t.IsSharedGoLink() || t.IsPlugin() || t.CanUsePlugins()
+}
+
+// UseRelro reports whether to make use of "read only relocations" aka
+// relro.
+func (t *Target) UseRelro() bool {
+ switch t.BuildMode {
+ case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin:
+ return t.IsELF || t.HeadType == objabi.Haix
+ default:
+ return t.linkShared || (t.HeadType == objabi.Haix && t.LinkMode == LinkExternal)
+ }
+}
+
+//
+// Processor functions
+//
+
+func (t *Target) Is386() bool {
+ return t.Arch.Family == sys.I386
+}
+
+func (t *Target) IsARM() bool {
+ return t.Arch.Family == sys.ARM
+}
+
+func (t *Target) IsAMD64() bool {
+ return t.Arch.Family == sys.AMD64
+}
+
+func (t *Target) IsPPC64() bool {
+ return t.Arch.Family == sys.PPC64
+}
+
+func (t *Target) IsS390X() bool {
+ return t.Arch.Family == sys.S390X
+}
+
+//
+// OS Functions
+//
+
+func (t *Target) IsLinux() bool {
+ return t.HeadType == objabi.Hlinux
+}
+
+func (t *Target) IsDarwin() bool {
+ return t.HeadType == objabi.Hdarwin
+}
+
+func (t *Target) IsWindows() bool {
+ return t.HeadType == objabi.Hwindows
+}
+
+func (t *Target) IsPlan9() bool {
+ return t.HeadType == objabi.Hplan9
+}
+
+func (t *Target) IsAIX() bool {
+ return t.HeadType == objabi.Haix
+}
+
+func (t *Target) IsSolaris() bool {
+ return t.HeadType == objabi.Hsolaris
+}
+
+func (t *Target) IsNetbsd() bool {
+ return t.HeadType == objabi.Hnetbsd
+}
+
+func (t *Target) IsOpenbsd() bool {
+ return t.HeadType == objabi.Hopenbsd
+}
+
+//
+// MISC
+//
+
+func (t *Target) IsBigEndian() bool {
+ return t.Arch.ByteOrder == binary.BigEndian
+}
diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go
index 9d236db766..9f257b8fc0 100644
--- a/src/cmd/link/internal/ld/util.go
+++ b/src/cmd/link/internal/ld/util.go
@@ -5,6 +5,7 @@
package ld
import (
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"encoding/binary"
"fmt"
@@ -38,6 +39,18 @@ func Exitf(format string, a ...interface{}) {
Exit(2)
}
+// afterErrorAction updates 'nerrors' on error and invokes exit or
+// panics in the proper circumstances.
+func afterErrorAction() {
+ nerrors++
+ if *flagH {
+ panic("error")
+ }
+ if nerrors > 20 {
+ Exitf("too many errors")
+ }
+}
+
// Errorf logs an error message.
//
// If more than 20 errors have been printed, exit with an error.
@@ -50,13 +63,25 @@ func Errorf(s *sym.Symbol, format string, args ...interface{}) {
}
format += "\n"
fmt.Fprintf(os.Stderr, format, args...)
- nerrors++
- if *flagH {
- panic("error")
- }
- if nerrors > 20 {
- Exitf("too many errors")
+ afterErrorAction()
+}
+
+// Errorf method logs an error message.
+//
+// If more than 20 errors have been printed, exit with an error.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+func (ctxt *Link) Errorf(s loader.Sym, format string, args ...interface{}) {
+ if s != 0 && ctxt.loader != nil {
+ sn := ctxt.loader.SymName(s)
+ format = sn + ": " + format
+ } else {
+ format = fmt.Sprintf("sym %d: %s", s, format)
}
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ afterErrorAction()
}
func artrim(x []byte) string {
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go
index 8814bad4ae..5bdf863f14 100644
--- a/src/cmd/link/internal/ld/xcoff.go
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -7,6 +7,7 @@ package ld
import (
"bytes"
"cmd/internal/objabi"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"encoding/binary"
"io/ioutil"
@@ -338,7 +339,7 @@ type XcoffLdSym64 struct {
}
type xcoffLoaderSymbol struct {
- sym *sym.Symbol
+ sym loader.Sym
smtype int8
smclas int8
}
@@ -1031,9 +1032,13 @@ func (f *xcoffFile) asmaixsym(ctxt *Link) {
}
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 {
+ ldr := ctxt.loader
+ var dynsyms []loader.Sym
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT {
continue
}
dynsyms = append(dynsyms, s)
@@ -1042,12 +1047,10 @@ func (f *xcoffFile) genDynSym(ctxt *Link) {
for _, s := range dynsyms {
f.adddynimpsym(ctxt, s)
- if _, ok := f.dynLibraries[s.Dynimplib()]; !ok {
- f.dynLibraries[s.Dynimplib()] = len(f.dynLibraries)
+ if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok {
+ f.dynLibraries[ldr.SymDynimplib(s)] = len(f.dynLibraries)
}
-
}
-
}
// (*xcoffFile)adddynimpsym adds the dynamic symbol "s" to a XCOFF file.
@@ -1057,30 +1060,32 @@ func (f *xcoffFile) genDynSym(ctxt *Link) {
// 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) {
+func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) {
// 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")
+ ldr := ctxt.loader
+ if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" {
+ ctxt.Errorf(s, "imported symbol must have a given library")
}
- s.Type = sym.SXCOFFTOC
+ sb := ldr.MakeSymbolUpdater(s)
+ sb.SetType(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())
+ extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0)
+ extsym.SetType(sym.SDYNIMPORT)
+ extsym.SetReachable(true)
+ extsym.SetDynimplib(ldr.SymDynimplib(s))
+ extsym.SetExtname(ldr.SymExtname(s))
+ extsym.SetDynimpvers(ldr.SymDynimpvers(s))
// Add loader symbol
lds := &xcoffLoaderSymbol{
- sym: extsym,
+ sym: extsym.Sym(),
smtype: XTY_IMP,
smclas: XMC_DS,
}
- if s.Name == "__n_pthreads" {
+ if ldr.SymName(s) == "__n_pthreads" {
// Currently, all imported symbols made by cgo_import_dynamic are
// syscall functions, except __n_pthreads which is a variable.
// TODO(aix): Find a way to detect variables imported by cgo.
@@ -1089,15 +1094,20 @@ func (f *xcoffFile) adddynimpsym(ctxt *Link, s *sym.Symbol) {
f.loaderSymbols = append(f.loaderSymbols, lds)
// Relocation to retrieve the external address
- s.AddBytes(make([]byte, 8))
- s.SetAddr(ctxt.Arch, 0, extsym)
-
+ sb.AddBytes(make([]byte, 8))
+ sb.AddReloc(loader.Reloc{Off: 0, Size: uint8(ctxt.Arch.PtrSize), Type: objabi.R_ADDR, Sym: extsym.Sym()})
+ // TODO: maybe this could be
+ // sb.SetSize(0)
+ // sb.SetData(nil)
+ // sb.AddAddr(ctxt.Arch, extsym.Sym())
+ // If the size is not 0 to begin with, I don't think the added 8 bytes
+ // of zeros are necessary.
}
// Xcoffadddynrel adds a dynamic relocation in a XCOFF file.
// This relocation will be made by the loader.
-func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
- if ctxt.LinkMode == LinkExternal {
+func Xcoffadddynrel(target *Target, ldr *loader.Loader, s *sym.Symbol, r *sym.Reloc) bool {
+ if target.IsExternal() {
return true
}
if s.Type <= sym.SPCLNTAB {
@@ -1105,7 +1115,7 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
return false
}
- ldr := &xcoffLoaderReloc{
+ xldr := &xcoffLoaderReloc{
sym: s,
rel: r,
}
@@ -1118,8 +1128,8 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
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
+ if ldr.Syms[dynsym.sym].Name == r.Sym.Name {
+ xldr.symndx = int32(i + 3) // +3 because of 3 section symbols
break
}
}
@@ -1129,12 +1139,12 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
Errorf(s, "unknown segment for .loader relocation with symbol %s", r.Sym.Name)
case &Segtext:
case &Segrodata:
- ldr.symndx = 0 // .text
+ xldr.symndx = 0 // .text
case &Segdata:
if r.Sym.Type == sym.SBSS || r.Sym.Type == sym.SNOPTRBSS {
- ldr.symndx = 2 // .bss
+ xldr.symndx = 2 // .bss
} else {
- ldr.symndx = 1 // .data
+ xldr.symndx = 1 // .data
}
}
@@ -1144,10 +1154,10 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
return false
}
- ldr.rtype = 0x3F<<8 + XCOFF_R_POS
+ xldr.rtype = 0x3F<<8 + XCOFF_R_POS
}
- xfile.loaderReloc = append(xfile.loaderReloc, ldr)
+ xfile.loaderReloc = append(xfile.loaderReloc, xldr)
return true
}
@@ -1156,16 +1166,17 @@ func (ctxt *Link) doxcoff() {
// All XCOFF files have dynamic symbols because of the syscalls.
Exitf("-d is not available on AIX")
}
+ ldr := ctxt.loader
// TOC
- toc := ctxt.Syms.Lookup("TOC", 0)
- toc.Type = sym.SXCOFFTOC
- toc.Attr |= sym.AttrReachable
- toc.Attr |= sym.AttrVisibilityHidden
+ toc := ldr.CreateSymForUpdate("TOC", 0)
+ toc.SetType(sym.SXCOFFTOC)
+ toc.SetReachable(true)
+ toc.SetVisibilityHidden(true)
// Add entry point to .loader symbols.
- ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0)
- if !ep.Attr.Reachable() {
+ ep := ldr.Lookup(*flagEntrySymbol, 0)
+ if ep == 0 || !ldr.AttrReachable(ep) {
Exitf("wrong entry point")
}
@@ -1177,33 +1188,43 @@ func (ctxt *Link) doxcoff() {
xfile.genDynSym(ctxt)
- for _, s := range ctxt.Syms.Allsym {
- if strings.HasPrefix(s.Name, "TOC.") {
- s.Type = sym.SXCOFFTOC
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if strings.HasPrefix(ldr.SymName(s), "TOC.") {
+ sb := ldr.MakeSymbolUpdater(s)
+ sb.SetType(sym.SXCOFFTOC)
}
}
- if ctxt.LinkMode == LinkExternal {
+ if ctxt.IsExternal() {
// 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)
+ rt0 := ldr.Lookup("runtime.rt0_go", 0)
+ ldr.SetSymExtname(rt0, "runtime_rt0_go")
- for _, s := range ctxt.Syms.Allsym {
- if !s.Attr.CgoExport() {
+ nsym := loader.Sym(ldr.NSym())
+ for s := loader.Sym(1); s < nsym; s++ {
+ if !ldr.AttrCgoExport(s) {
continue
}
+ if ldr.SymVersion(s) != 0 { // sanity check
+ panic("cgo_export on non-version 0 symbol")
+ }
- name := s.Extname()
- if s.Type == sym.STEXT {
+ if ldr.SymType(s) == sym.STEXT || ldr.SymType(s) == sym.SABIALIAS {
// On AIX, a exported function must have two symbols:
// - a .text symbol which must start with a ".".
// - a .data symbol which is a function descriptor.
- ctxt.Syms.Rename(s.Name, "."+name, 0, ctxt.Reachparent)
-
- desc := ctxt.Syms.Lookup(name, 0)
- desc.Type = sym.SNOPTRDATA
+ //
+ // CgoExport attribute should only be set on a version 0
+ // symbol, which can be TEXT or ABIALIAS.
+ // (before, setupdynexp copies the attribute from the
+ // alias to the aliased. Now we are before setupdynexp.)
+ name := ldr.SymExtname(s)
+ ldr.SetSymExtname(s, "."+name)
+
+ desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0))
+ desc.SetType(sym.SNOPTRDATA)
desc.AddAddr(ctxt.Arch, s)
- desc.AddAddr(ctxt.Arch, toc)
+ desc.AddAddr(ctxt.Arch, toc.Sym())
desc.AddUint64(ctxt.Arch, 0)
}
}
@@ -1243,18 +1264,19 @@ func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) {
Lsmtype: s.smtype,
Lsmclas: s.smclas,
}
+ sym := ctxt.loader.Syms[s.sym]
switch s.smtype {
default:
- Errorf(s.sym, "unexpected loader symbol type: 0x%x", s.smtype)
+ Errorf(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)
+ lds.Lvalue = uint64(sym.Value)
+ lds.Lscnum = f.getXCOFFscnum(sym.Sect)
case XTY_IMP:
- lds.Lifile = int32(f.dynLibraries[s.sym.Dynimplib()] + 1)
+ lds.Lifile = int32(f.dynLibraries[sym.Dynimplib()] + 1)
}
ldstr := &XcoffLdStr64{
- size: uint16(len(s.sym.Name) + 1), // + null terminator
- name: s.sym.Name,
+ size: uint16(len(sym.Name) + 1), // + null terminator
+ name: sym.Name,
}
stlen += uint32(2 + ldstr.size) // 2 = sizeof ldstr.size
symtab = append(symtab, lds)
@@ -1268,28 +1290,28 @@ func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) {
/* Reloc */
ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0)
- ldr := &XcoffLdRel64{
+ xldr := &XcoffLdRel64{
Lvaddr: uint64(ep.Value),
Lrtype: 0x3F00,
Lrsecnm: f.getXCOFFscnum(ep.Sect),
Lsymndx: 0,
}
off += 16
- reloctab = append(reloctab, ldr)
+ reloctab = append(reloctab, xldr)
off += uint64(16 * len(f.loaderReloc))
for _, r := range f.loaderReloc {
- ldr = &XcoffLdRel64{
+ xldr = &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)
+ xldr.Lrsecnm = f.getXCOFFscnum(r.sym.Sect)
}
- reloctab = append(reloctab, ldr)
+ reloctab = append(reloctab, xldr)
}
off += uint64(16 * len(dynimpreloc))
@@ -1630,7 +1652,7 @@ func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) {
if sect.Name == ".text" {
n += relocsect(sect, ctxt.Textp, 0)
} else {
- n += relocsect(sect, datap, 0)
+ n += relocsect(sect, ctxt.datap, 0)
}
}
}
@@ -1662,9 +1684,12 @@ func xcoffCreateExportFile(ctxt *Link) (fname string) {
if !s.Attr.CgoExport() {
continue
}
- if !strings.HasPrefix(s.String(), "_cgoexp_") {
+ if !strings.HasPrefix(s.Extname(), "._cgoexp_") {
continue
}
+ if s.Version != 0 {
+ continue // Only export version 0 symbols. See the comment in doxcoff.
+ }
// Retrieve the name of the initial symbol
// exported by cgo.
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go
index 1962d76338..0d40940093 100644
--- a/src/cmd/link/internal/loadelf/ldelf.go
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The Go Authors. All rights reserved.
+// 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.
@@ -270,19 +270,20 @@ type ElfSymBytes64 struct {
}
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
+ name string
+ nameoff uint32
+ type_ uint32
+ flags uint64
+ addr uint64
+ off uint64
+ size uint64
+ link uint32
+ info uint32
+ align uint64
+ entsize uint64
+ base []byte
+ readOnlyMem bool // Is this section in readonly memory?
+ sym loader.Sym
}
type ElfObj struct {
@@ -320,7 +321,7 @@ type ElfSym struct {
type_ uint8
other uint8
shndx uint16
- sym *sym.Symbol
+ sym loader.Sym
}
var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'}
@@ -452,32 +453,22 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags
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.
+// Load loads the ELF file pn from f.
+// Symbols are installed into the loader, and a slice of the text symbols is returned.
//
// On ARM systems, Load will attempt to determine what ELF header flags to
// emit by scanning the attributes in the ELF file being loaded. The
// parameter initEhdrFlags contains the current header flags for the output
// object, and the returned ehdrFlags contains what this Load function computes.
// TODO: find a better place for this logic.
-func load(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) {
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []loader.Sym, ehdrFlags uint32, err error) {
+ newSym := func(name string, version int) loader.Sym {
+ return l.CreateStaticSym(name)
+ }
+ lookup := func(name string, version int) loader.Sym {
+ return l.LookupOrCreateSym(name, version)
+ }
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) {
return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
}
@@ -610,7 +601,6 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
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)
}
@@ -731,46 +721,47 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
}
sectsymNames[name] = true
- s := lookup(name, localSymVersion)
+ sb := l.MakeSymbolUpdater(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
+ sb.SetType(sym.SRODATA)
case ElfSectFlagAlloc + ElfSectFlagWrite:
if sect.type_ == ElfSectNobits {
- s.Type = sym.SNOPTRBSS
+ sb.SetType(sym.SNOPTRBSS)
} else {
- s.Type = sym.SNOPTRDATA
+ sb.SetType(sym.SNOPTRDATA)
}
case ElfSectFlagAlloc + ElfSectFlagExec:
- s.Type = sym.STEXT
+ sb.SetType(sym.STEXT)
}
if sect.name == ".got" || sect.name == ".toc" {
- s.Type = sym.SELFGOT
+ sb.SetType(sym.SELFGOT)
}
if sect.type_ == ElfSectProgbits {
- s.P = sect.base
- s.P = s.P[:sect.size]
+ sb.SetData(sect.base[:sect.size])
}
- s.Size = int64(sect.size)
- s.Align = int32(sect.align)
- sect.sym = s
+ sb.SetSize(int64(sect.size))
+ sb.SetAlign(int32(sect.align))
+ sb.SetReadOnly(sect.readOnlyMem)
+
+ sect.sym = sb.Sym()
}
// enter sub-symbols into symbol table.
// symbol 0 is the null symbol.
- symbols := make([]*sym.Symbol, elfobj.nsymtab)
+ symbols := make([]loader.Sym, 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 {
+ if err := readelfsym(newSym, lookup, l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
return errorf("%s: malformed elf file: %v", pn, err)
}
symbols[i] = elfsym.sym
@@ -778,12 +769,12 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
continue
}
if elfsym.shndx == ElfSymShnCommon || elfsym.type_ == ElfSymTypeCommon {
- s := elfsym.sym
- if uint64(s.Size) < elfsym.size {
- s.Size = int64(elfsym.size)
+ sb := l.MakeSymbolUpdater(elfsym.sym)
+ if uint64(sb.Size()) < elfsym.size {
+ sb.SetSize(int64(elfsym.size))
}
- if s.Type == 0 || s.Type == sym.SXREF {
- s.Type = sym.SNOPTRBSS
+ if sb.Type() == 0 || sb.Type() == sym.SXREF {
+ sb.SetType(sym.SNOPTRBSS)
}
continue
}
@@ -793,11 +784,11 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
}
// even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols
- if elfsym.sym == nil {
+ if elfsym.sym == 0 {
continue
}
sect = &elfobj.sect[elfsym.shndx]
- if sect.sym == nil {
+ if sect.sym == 0 {
if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this
continue
}
@@ -822,36 +813,37 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
}
s := elfsym.sym
- if s.Outer != nil {
- if s.Attr.DuplicateOK() {
+ if l.OuterSym(s) != 0 {
+ if l.AttrDuplicateOK(s) {
continue
}
- return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name)
+ return errorf("duplicate symbol reference: %s in both %s and %s",
+ l.SymName(s), l.SymName(l.OuterSym(s)), l.SymName(sect.sym))
}
- 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
+ sectsb := l.MakeSymbolUpdater(sect.sym)
+ sb := l.MakeSymbolUpdater(s)
+
+ sb.SetType(sectsb.Type())
+ sectsb.PrependSub(s)
+ if !l.AttrCgoExportDynamic(s) {
+ sb.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)
+ sb.SetValue(int64(elfsym.value))
+ sb.SetSize(int64(elfsym.size))
+ if sectsb.Type() == sym.STEXT {
+ if l.AttrExternal(s) && !l.AttrDuplicateOK(s) {
+ return errorf("%s: duplicate symbol definition", sb.Name())
}
- s.Attr |= sym.AttrExternal
+ l.SetAttrExternal(s, true)
}
if elfobj.machine == ElfMachPower64 {
flag := int(elfsym.other) >> 5
if 2 <= flag && flag <= 6 {
- s.SetLocalentry(1 << uint(flag-2))
+ l.SetSymLocalentry(s, 1<<uint(flag-2))
} else if flag == 7 {
- return errorf("%v: invalid sym.other 0x%x", s, elfsym.other)
+ return errorf("%s: invalid sym.other 0x%x", sb.Name(), elfsym.other)
}
}
}
@@ -860,24 +852,27 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
// This keeps textp in increasing address order.
for i := uint(0); i < elfobj.nsect; i++ {
s := elfobj.sect[i].sym
- if s == nil {
+ if s == 0 {
continue
}
- if s.Sub != nil {
- s.Sub = sym.SortSub(s.Sub)
+ sb := l.MakeSymbolUpdater(s)
+ if l.SubSym(s) != 0 {
+ sb.SortSub()
}
- if s.Type == sym.STEXT {
- if s.Attr.OnList() {
- return errorf("symbol %s listed multiple times", s.Name)
+ if sb.Type() == sym.STEXT {
+ if l.AttrOnList(s) {
+ return errorf("symbol %s listed multiple times",
+ l.SymName(s))
}
- s.Attr |= sym.AttrOnList
+ l.SetAttrOnList(s, true)
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)
+ for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) {
+ if l.AttrOnList(ss) {
+ return errorf("symbol %s listed multiple times",
+ l.SymName(ss))
}
- s.Attr |= sym.AttrOnList
- textp = append(textp, s)
+ l.SetAttrOnList(ss, true)
+ textp = append(textp, ss)
}
}
}
@@ -900,7 +895,7 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
rela = 1
}
n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela))
- r := make([]sym.Reloc, n)
+ r := make([]loader.Reloc, n)
p := rsect.base
for j := 0; j < n; j++ {
var add uint64
@@ -951,22 +946,22 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
}
if symIdx == 0 { // absolute relocation, don't bother reading the null symbol
- rp.Sym = nil
+ rp.Sym = 0
} else {
var elfsym ElfSym
- if err := readelfsym(newSym, lookup, arch, elfobj, symIdx, &elfsym, 0, 0); err != nil {
+ if err := readelfsym(newSym, lookup, l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil {
return errorf("malformed elf file: %v", err)
}
elfsym.sym = symbols[symIdx]
- if elfsym.sym == 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_)
+ if elfsym.sym == 0 {
+ return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", l.SymName(sect.sym), j, int(symIdx), elfsym.name, elfsym.shndx, elfsym.type_)
}
rp.Sym = elfsym.sym
}
rp.Type = objabi.ElfRelocOffset + objabi.RelocType(relocType)
- rp.Siz, err = relSize(arch, pn, uint32(relocType))
+ rp.Size, err = relSize(arch, pn, uint32(relocType))
if err != nil {
return nil, 0, err
}
@@ -974,30 +969,30 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio
rp.Add = int64(add)
} else {
// load addend from image
- if rp.Siz == 4 {
+ if rp.Size == 4 {
rp.Add = int64(e.Uint32(sect.base[rp.Off:]))
- } else if rp.Siz == 8 {
+ } else if rp.Size == 8 {
rp.Add = int64(e.Uint64(sect.base[rp.Off:]))
} else {
- return errorf("invalid rela size %d", rp.Siz)
+ return errorf("invalid rela size %d", rp.Size)
}
}
- if rp.Siz == 2 {
+ if rp.Size == 2 {
rp.Add = int64(int16(rp.Add))
}
- if rp.Siz == 4 {
+ if rp.Size == 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]))
+ sort.Sort(loader.RelocByOff(r[:n]))
// just in case
- s := sect.sym
- s.R = r
- s.R = s.R[:n]
+ sb := l.MakeSymbolUpdater(sect.sym)
+ r = r[:n]
+ sb.SetRelocs(r)
}
return textp, ehdrFlags, nil
@@ -1022,16 +1017,16 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
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 {
+ sect.base, sect.readOnlyMem, err = elfobj.f.Slice(uint64(sect.size))
+ if err != nil {
return fmt.Errorf("short read: %v", err)
}
return nil
}
-func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
+func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
if i >= elfobj.nsymtab || i < 0 {
err = fmt.Errorf("invalid elf symbol index")
return err
@@ -1063,7 +1058,8 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int
elfsym.other = b.Other
}
- var s *sym.Symbol
+ var s loader.Sym
+
if elfsym.name == "_GLOBAL_OFFSET_TABLE_" {
elfsym.name = ".got"
}
@@ -1090,8 +1086,12 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int
// 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
+ if s != 0 && elfsym.other == 2 {
+ if !l.IsExternal(s) {
+ l.MakeSymbolUpdater(s)
+ }
+ l.SetAttrDuplicateOK(s, true)
+ l.SetAttrVisibilityHidden(s, true)
}
}
@@ -1107,9 +1107,8 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int
// so put it in the hash table.
if needSym != 0 {
s = lookup(elfsym.name, localSymVersion)
- s.Attr |= sym.AttrVisibilityHidden
+ l.SetAttrVisibilityHidden(s, true)
}
-
break
}
@@ -1121,20 +1120,19 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int
// reduce mem use, but also (possibly) make it harder
// to debug problems.
s = newSym(elfsym.name, localSymVersion)
-
- s.Attr |= sym.AttrVisibilityHidden
+ l.SetAttrVisibilityHidden(s, true)
}
case ElfSymBindWeak:
if needSym != 0 {
s = lookup(elfsym.name, 0)
if elfsym.other == 2 {
- s.Attr |= sym.AttrVisibilityHidden
+ l.SetAttrVisibilityHidden(s, true)
}
// Allow weak symbols to be duplicated when already defined.
- if s.Outer != nil {
- s.Attr |= sym.AttrDuplicateOK
+ if l.OuterSym(s) != 0 {
+ l.SetAttrDuplicateOK(s, true)
}
}
@@ -1146,8 +1144,9 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int
// 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
+ if s != 0 && l.SymType(s) == 0 && !l.AttrVisibilityHidden(s) && elfsym.type_ != ElfSymTypeSection {
+ sb := l.MakeSymbolUpdater(s)
+ sb.SetType(sym.SXREF)
}
elfsym.sym = s
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 0adc395fef..7cc846a19e 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -7,14 +7,15 @@ package loader
import (
"bytes"
"cmd/internal/bio"
- "cmd/internal/dwarf"
"cmd/internal/goobj2"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/sym"
+ "debug/elf"
"fmt"
"log"
+ "math/bits"
"os"
"sort"
"strconv"
@@ -30,13 +31,11 @@ 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
+ rs []goobj2.Reloc2
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.
@@ -50,6 +49,42 @@ type Reloc struct {
Sym Sym // global index of symbol the reloc addresses
}
+// Reloc2 holds a "handle" to access a relocation record from an
+// object file.
+type Reloc2 struct {
+ *goobj2.Reloc2
+ r *oReader
+ l *Loader
+
+ // External reloc types may not fit into a uint8 which the Go object file uses.
+ // Store it here, instead of in the byte of goobj2.Reloc2.
+ // For Go symbols this will always be 0.
+ // goobj2.Reloc2.Type() + typ is always the right type, for both Go and external
+ // symbols.
+ typ objabi.RelocType
+}
+
+func (rel Reloc2) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc2.Type()) + rel.typ }
+func (rel Reloc2) Sym() Sym { return rel.l.resolve(rel.r, rel.Reloc2.Sym()) }
+func (rel Reloc2) SetSym(s Sym) { rel.Reloc2.SetSym(goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) }
+
+func (rel Reloc2) SetType(t objabi.RelocType) {
+ if t != objabi.RelocType(uint8(t)) {
+ panic("SetType: type doesn't fit into Reloc2")
+ }
+ rel.Reloc2.SetType(uint8(t))
+}
+
+// Aux2 holds a "handle" to access an aux symbol record from an
+// object file.
+type Aux2 struct {
+ *goobj2.Aux2
+ r *oReader
+ l *Loader
+}
+
+func (a Aux2) Sym() Sym { return a.l.resolve(a.r, a.Aux2.Sym()) }
+
// oReader is a wrapper type of obj.Reader, along with some
// extra information.
// TODO: rename to objReader once the old one is gone?
@@ -59,13 +94,24 @@ type oReader struct {
version int // version of static symbol
flags uint32 // read from object file
pkgprefix string
- rcache []Sym // cache mapping local PkgNone symbol to resolved Sym
+ syms []Sym // Sym's global index, indexed by local index
+ ndef int // cache goobj2.Reader.NSym()
+ objidx uint32 // index of this reader in the objs slice
}
type objIdx struct {
r *oReader
i Sym // start index
- e Sym // end index
+}
+
+// objSym represents a symbol in an object file. It is a tuple of
+// the object and the symbol's local index.
+// For external symbols, r is l.extReader, s is its index into the
+// payload array.
+// {nil, 0} represents the nil symbol.
+type objSym struct {
+ r *oReader
+ s int // local index
}
type nameVer struct {
@@ -73,47 +119,134 @@ type nameVer struct {
v int
}
-type bitmap []uint32
+type Bitmap []uint32
// set the i-th bit.
-func (bm bitmap) Set(i Sym) {
+func (bm Bitmap) Set(i Sym) {
n, r := uint(i)/32, uint(i)%32
bm[n] |= 1 << r
}
+// unset the i-th bit.
+func (bm Bitmap) Unset(i Sym) {
+ n, r := uint(i)/32, uint(i)%32
+ bm[n] &^= (1 << r)
+}
+
// whether the i-th bit is set.
-func (bm bitmap) Has(i Sym) bool {
+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)
+// return current length of bitmap in bits.
+func (bm Bitmap) Len() int {
+ return len(bm) * 32
+}
+func MakeBitmap(n int) Bitmap {
+ return make(Bitmap, (n+31)/32)
+}
+
+// growBitmap insures that the specified bitmap has enough capacity,
+// reallocating (doubling the size) if needed.
+func growBitmap(reqLen int, b Bitmap) Bitmap {
+ curLen := b.Len()
+ if reqLen > curLen {
+ b = append(b, MakeBitmap(reqLen+1-curLen)...)
+ }
+ return b
}
// A Loader loads new object files and resolves indexed symbol references.
+//
+// Notes on the layout of global symbol index space:
+//
+// - Go object files are read before host object files; each Go object
+// read adds its defined package symbols to the global index space.
+// Nonpackage symbols are not yet added.
+//
+// - In loader.LoadNonpkgSyms, add non-package defined symbols and
+// references in all object files to the global index space.
+//
+// - Host object file loading happens; the host object loader does a
+// name/version lookup for each symbol it finds; this can wind up
+// extending the external symbol index space range. The host object
+// loader stores symbol payloads in loader.payloads using SymbolBuilder.
+//
+// - For now, in loader.LoadFull we convert all symbols (Go + external)
+// to sym.Symbols.
+//
+// - At some point (when the wayfront is pushed through all of the
+// linker), all external symbols will be payload-based, and we can
+// get rid of the loader.Syms array.
+//
+// - Each symbol gets a unique global index. For duplicated and
+// overwriting/overwritten symbols, the second (or later) appearance
+// of the symbol gets the same global index as the first appearance.
type Loader struct {
start map[*oReader]Sym // map from object file to its start index
objs []objIdx // sorted by start index (i.e. objIdx.i)
- 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
+
+ objSyms []objSym // global index mapping to local index
symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal
extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name
- overwrite map[Sym]Sym // overwrite[i]=j if symbol j overwrites symbol i
+
+ extReader *oReader // a dummy oReader, for external symbols
+ payloadBatch []extSymPayload
+ payloads []*extSymPayload // contents of linker-materialized external syms
+ values []int64 // symbol values, indexed by global sym index
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.
+ Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now.
+ symBatch []sym.Symbol // batch of symbols.
anonVersion int // most recently assigned ext static sym pseudo-version
- Reachable bitmap // bitmap of reachable symbols, indexed by global index
+ // Bitmaps and other side structures used to store data used to store
+ // symbol flags/attributes; these are to be accessed via the
+ // corresponding loader "AttrXXX" and "SetAttrXXX" methods. Please
+ // visit the comments on these methods for more details on the
+ // semantics / interpretation of the specific flags or attribute.
+ attrReachable Bitmap // reachable symbols, indexed by global index
+ attrOnList Bitmap // "on list" symbols, indexed by global index
+ attrLocal Bitmap // "local" symbols, indexed by global index
+ attrNotInSymbolTable Bitmap // "not in symtab" symbols, indexed by glob idx
+ attrVisibilityHidden Bitmap // hidden symbols, indexed by ext sym index
+ attrDuplicateOK Bitmap // dupOK symbols, indexed by ext sym index
+ attrShared Bitmap // shared symbols, indexed by ext sym index
+ attrExternal Bitmap // external symbols, indexed by ext sym index
+
+ attrReadOnly map[Sym]bool // readonly data for this sym
+ attrTopFrame map[Sym]struct{} // top frame symbols
+ attrSpecial map[Sym]struct{} // "special" frame symbols
+ attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols
+ attrCgoExportStatic map[Sym]struct{} // "cgo_export_static" symbols
+
+ // Outer and Sub relations for symbols.
+ // TODO: figure out whether it's more efficient to just have these
+ // as fields on extSymPayload (note that this won't be a viable
+ // strategy if somewhere in the linker we set sub/outer for a
+ // non-external sym).
+ outer map[Sym]Sym
+ sub map[Sym]Sym
+
+ align map[Sym]int32 // stores alignment for symbols
+
+ dynimplib map[Sym]string // stores Dynimplib symbol attribute
+ dynimpvers map[Sym]string // stores Dynimpvers symbol attribute
+ localentry map[Sym]uint8 // stores Localentry symbol attribute
+ extname map[Sym]string // stores Extname symbol attribute
+ elfType map[Sym]elf.SymType // stores elf type symbol property
+ symFile map[Sym]string // stores file for shlib-derived syms
+ plt map[Sym]int32 // stores dynimport for pe objects
+ got map[Sym]int32 // stores got for pe objects
+ dynid map[Sym]int32 // stores Dynid for symbol
// Used to implement field tracking; created during deadcode if
// field tracking is enabled. Reachparent[K] contains the index of
@@ -125,6 +258,32 @@ type Loader struct {
flags uint32
strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
+
+ elfsetstring elfsetstringFunc
+
+ SymLookup func(name string, ver int) *sym.Symbol
+}
+
+const (
+ pkgDef = iota
+ nonPkgDef
+ nonPkgRef
+)
+
+type elfsetstringFunc func(s *sym.Symbol, str string, off int)
+
+// extSymPayload holds the payload (data + relocations) for linker-synthesized
+// external symbols (note that symbol value is stored in a separate slice).
+type extSymPayload struct {
+ name string // TODO: would this be better as offset into str table?
+ size int64
+ ver int
+ kind sym.SymKind
+ objidx uint32 // index of original object if sym made by cloneToExternal
+ gotype Sym // Gotype (0 if not present)
+ relocs []goobj2.Reloc2
+ reltypes []objabi.RelocType // relocation types
+ data []byte
}
const (
@@ -132,26 +291,40 @@ const (
FlagStrictDups = 1 << iota
)
-func NewLoader(flags uint32) *Loader {
+func NewLoader(flags uint32, elfsetstring elfsetstringFunc) *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,
+ start: make(map[*oReader]Sym),
+ objs: []objIdx{{}}, // reserve index 0 for nil symbol
+ objSyms: []objSym{{}}, // reserve index 0 for nil symbol
+ extReader: &oReader{},
+ symsByName: [2]map[string]Sym{make(map[string]Sym, 100000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols
+ objByPkg: make(map[string]*oReader),
+ outer: make(map[Sym]Sym),
+ sub: make(map[Sym]Sym),
+ align: make(map[Sym]int32),
+ dynimplib: make(map[Sym]string),
+ dynimpvers: make(map[Sym]string),
+ localentry: make(map[Sym]uint8),
+ extname: make(map[Sym]string),
+ attrReadOnly: make(map[Sym]bool),
+ elfType: make(map[Sym]elf.SymType),
+ symFile: make(map[Sym]string),
+ plt: make(map[Sym]int32),
+ got: make(map[Sym]int32),
+ dynid: make(map[Sym]int32),
+ attrTopFrame: make(map[Sym]struct{}),
+ attrSpecial: make(map[Sym]struct{}),
+ attrCgoExportDynamic: make(map[Sym]struct{}),
+ attrCgoExportStatic: make(map[Sym]struct{}),
+ itablink: make(map[Sym]struct{}),
+ extStaticSyms: make(map[nameVer]Sym),
+ builtinSyms: make([]Sym, nbuiltin),
+ flags: flags,
+ elfsetstring: elfsetstring,
}
}
-// 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 {
@@ -161,89 +334,176 @@ func (l *Loader) addObj(pkg string, r *oReader) Sym {
if _, ok := l.objByPkg[pkg]; !ok {
l.objByPkg[pkg] = r
}
- n := r.NSym() + r.NNonpkgdef()
- i := l.max + 1
+ i := Sym(len(l.objSyms))
l.start[r] = i
- l.objs = append(l.objs, objIdx{r, i, i + Sym(n) - 1})
- l.max += Sym(n)
+ l.objs = append(l.objs, objIdx{r, i})
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 {
+// Add a symbol from an object file, return the global index and whether it is added.
+// If the symbol already exist, it returns the index of that symbol.
+func (l *Loader) AddSym(name string, ver int, r *oReader, li int, kind int, dupok bool, typ sym.SymKind) (Sym, bool) {
if l.extStart != 0 {
- panic("AddSym called after AddExtSym is called")
+ panic("AddSym called after external symbol is created")
+ }
+ i := Sym(len(l.objSyms))
+ addToGlobal := func() {
+ l.objSyms = append(l.objSyms, objSym{r, li})
+ }
+ if name == "" {
+ addToGlobal()
+ return i, true // unnamed aux symbol
}
if ver == r.version {
// Static symbol. Add its global index but don't
// add to name lookup table, as it cannot be
// referenced by name.
- 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
- }
+ addToGlobal()
+ return i, true
+ }
+ if kind == pkgDef {
+ // Defined package symbols cannot be dup to each other.
+ // We load all the package symbols first, so we don't need
+ // to check dup here.
+ // We still add it to the lookup table, as it may still be
+ // referenced by name (e.g. through linkname).
+ l.symsByName[ver][name] = i
+ addToGlobal()
+ return i, true
}
- 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
+ // Non-package (named) symbol. Check if it already exists.
+ oldi, existed := l.symsByName[ver][name]
+ if !existed {
+ l.symsByName[ver][name] = i
+ addToGlobal()
+ return i, true
+ }
+ // symbol already exists
+ if dupok {
+ if l.flags&FlagStrictDups != 0 {
+ l.checkdup(name, r, li, oldi)
}
+ return oldi, false
+ }
+ oldr, oldli := l.toLocal(oldi)
+ oldsym := oldr.Sym2(oldli)
+ if oldsym.Dupok() {
+ return oldi, false
+ }
+ overwrite := r.DataSize(li) != 0
+ if overwrite {
+ // new symbol overwrites old symbol.
+ oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) {
+ log.Fatalf("duplicated definition of symbol " + name)
+ }
+ l.objSyms[oldi] = objSym{r, li}
} else {
- if _, ok := l.symsByName[ver][name]; ok {
- return 0
+ // old symbol overwrites new symbol.
+ if !typ.IsData() { // only allow overwriting data symbol
+ log.Fatalf("duplicated definition of symbol " + name)
}
}
- i := l.max + 1
+ return oldi, true
+}
+
+// newExtSym creates a new external sym with the specified
+// name/version.
+func (l *Loader) newExtSym(name string, ver int) Sym {
+ i := Sym(len(l.objSyms))
+ if l.extStart == 0 {
+ l.extStart = i
+ }
+ l.growSyms(int(i))
+ pi := l.newPayload(name, ver)
+ l.objSyms = append(l.objSyms, objSym{l.extReader, int(pi)})
+ l.extReader.syms = append(l.extReader.syms, i)
+ return i
+}
+
+// LookupOrCreateSym looks up the symbol with the specified name/version,
+// returning its Sym index if found. If the lookup fails, a new external
+// Sym will be created, entered into the lookup tables, and returned.
+func (l *Loader) LookupOrCreateSym(name string, ver int) Sym {
+ i := l.Lookup(name, ver)
+ if i != 0 {
+ return i
+ }
+ i = l.newExtSym(name, ver)
+ static := ver >= sym.SymVerStatic || ver < 0
if static {
l.extStaticSyms[nameVer{name, ver}] = i
} else {
l.symsByName[ver][name] = i
}
- 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
+ r, _ := l.toLocal(i)
+ return l.isExtReader(r)
+}
+
+func (l *Loader) isExtReader(r *oReader) bool {
+ return r == l.extReader
+}
+
+// For external symbol, return its index in the payloads array.
+// XXX result is actually not a global index. We (ab)use the Sym type
+// so we don't need conversion for accessing bitmaps.
+func (l *Loader) extIndex(i Sym) Sym {
+ _, li := l.toLocal(i)
+ return Sym(li)
+}
+
+// Get a new payload for external symbol, return its index in
+// the payloads array.
+func (l *Loader) newPayload(name string, ver int) int {
+ pi := len(l.payloads)
+ pp := l.allocPayload()
+ pp.name = name
+ pp.ver = ver
+ l.payloads = append(l.payloads, pp)
+ l.growExtAttrBitmaps()
+ return pi
+}
+
+// getPayload returns a pointer to the extSymPayload struct for an
+// external symbol if the symbol has a payload. Will panic if the
+// symbol in question is bogus (zero or not an external sym).
+func (l *Loader) getPayload(i Sym) *extSymPayload {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("bogus symbol index %d in getPayload", i))
+ }
+ pi := l.extIndex(i)
+ return l.payloads[pi]
+}
+
+// allocPayload allocates a new payload.
+func (l *Loader) allocPayload() *extSymPayload {
+ batch := l.payloadBatch
+ if len(batch) == 0 {
+ batch = make([]extSymPayload, 1000)
+ }
+ p := &batch[0]
+ l.payloadBatch = batch[1:]
+ return p
+}
+
+func (ms *extSymPayload) Grow(siz int64) {
+ if int64(int(siz)) != siz {
+ log.Fatalf("symgrow size %d too long", siz)
+ }
+ if int64(len(ms.data)) >= siz {
+ return
+ }
+ if cap(ms.data) < int(siz) {
+ cl := len(ms.data)
+ ms.data = append(ms.data, make([]byte, int(siz)+1-cl)...)
+ ms.data = ms.data[0:cl]
+ }
+ ms.data = ms.data[:siz]
}
// Ensure Syms slice has enough space.
@@ -253,57 +513,18 @@ func (l *Loader) growSyms(i int) {
return
}
l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...)
+ l.growValues(int(i) + 1)
+ l.growAttrBitmaps(int(i) + 1)
}
// 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
+ return r.syms[i]
}
// 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
+ return l.objSyms[i].r, int(l.objSyms[i].s)
}
// Resolve a local symbol reference. Return global index.
@@ -311,25 +532,19 @@ func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym {
var rr *oReader
switch p := s.PkgIdx; p {
case goobj2.PkgIdxInvalid:
+ // {0, X} with non-zero X is never a valid sym reference from a Go object.
+ // We steal this space for symbol references from external objects.
+ // In this case, X is just the global index.
+ if l.isExtReader(r) {
+ return Sym(s.SymIdx)
+ }
if s.SymIdx != 0 {
panic("bad sym ref")
}
return 0
case 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
+ i := int(s.SymIdx) + r.ndef
+ return r.syms[i]
case goobj2.PkgIdxBuiltin:
return l.builtinSyms[s.SymIdx]
case goobj2.PkgIdxSelf:
@@ -355,44 +570,11 @@ func (l *Loader) Lookup(name string, ver int) Sym {
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))
+func (l *Loader) checkdup(name string, r *oReader, li int, dup Sym) {
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
}
@@ -419,7 +601,7 @@ func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
// Number of total symbols.
func (l *Loader) NSym() int {
- return int(l.max + 1)
+ return len(l.objSyms)
}
// Number of defined Go symbols.
@@ -430,43 +612,44 @@ func (l *Loader) NDef() int {
// 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 ""
+ pp := l.getPayload(i)
+ return pp.name
}
r, li := l.toLocal(i)
- osym := goobj2.Sym{}
- osym.Read(r.Reader, r.SymOff(li))
- return osym.Name
+ return r.Sym2(li).Name(r.Reader)
}
// 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 ""
+ pp := l.getPayload(i)
+ return pp.name
+ }
+ r, li := l.toLocal(i)
+ return strings.Replace(r.Sym2(li).Name(r.Reader), "\"\".", r.pkgprefix, -1)
+}
+
+// Returns the version of the i-th symbol.
+func (l *Loader) SymVersion(i Sym) int {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.ver
}
r, li := l.toLocal(i)
- osym := goobj2.Sym{}
- osym.Read(r.Reader, r.SymOff(li))
- return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+ return int(abiToVer(r.Sym2(li).ABI(), r.version))
}
// 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
+ pp := l.getPayload(i)
+ if pp != nil {
+ return pp.kind
}
return 0
}
r, li := l.toLocal(i)
- osym := goobj2.Sym{}
- osym.Read(r.Reader, r.SymOff(li))
- return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+ return sym.AbiSymKindToSymKind[objabi.SymKind(r.Sym2(li).Type())]
}
// Returns the attributes of the i-th symbol.
@@ -476,16 +659,310 @@ func (l *Loader) SymAttr(i Sym) uint8 {
return 0
}
r, li := l.toLocal(i)
- osym := goobj2.Sym{}
- osym.Read(r.Reader, r.SymOff(li))
- return osym.Flag
+ return r.Sym2(li).Flag()
+}
+
+// AttrReachable returns true for symbols that are transitively
+// referenced from the entry points. Unreachable symbols are not
+// written to the output.
+func (l *Loader) AttrReachable(i Sym) bool {
+ return l.attrReachable.Has(i)
}
+// SetAttrReachable sets the reachability property for a symbol (see
+// AttrReachable).
+func (l *Loader) SetAttrReachable(i Sym, v bool) {
+ if v {
+ l.attrReachable.Set(i)
+ } else {
+ l.attrReachable.Unset(i)
+ }
+}
+
+// AttrOnList returns true for symbols that are on some list (such as
+// the list of all text symbols, or one of the lists of data symbols)
+// and is consulted to avoid bugs where a symbol is put on a list
+// twice.
+func (l *Loader) AttrOnList(i Sym) bool {
+ return l.attrOnList.Has(i)
+}
+
+// SetAttrOnList sets the "on list" property for a symbol (see
+// AttrOnList).
+func (l *Loader) SetAttrOnList(i Sym, v bool) {
+ if v {
+ l.attrOnList.Set(i)
+ } else {
+ l.attrOnList.Unset(i)
+ }
+}
+
+// AttrLocal returns true for symbols that are only visible within the
+// module (executable or shared library) being linked. This attribute
+// is applied to thunks and certain other linker-generated symbols.
+func (l *Loader) AttrLocal(i Sym) bool {
+ return l.attrLocal.Has(i)
+}
+
+// SetAttrLocal the "local" property for a symbol (see AttrLocal above).
+func (l *Loader) SetAttrLocal(i Sym, v bool) {
+ if v {
+ l.attrLocal.Set(i)
+ } else {
+ l.attrLocal.Unset(i)
+ }
+}
+
+// AttrNotInSymbolTable returns true for symbols that should not be
+// added to the symbol table of the final generated load module.
+func (l *Loader) AttrNotInSymbolTable(i Sym) bool {
+ return l.attrNotInSymbolTable.Has(i)
+}
+
+// SetAttrNotInSymbolTable the "not in symtab" property for a symbol
+// (see AttrNotInSymbolTable above).
+func (l *Loader) SetAttrNotInSymbolTable(i Sym, v bool) {
+ if v {
+ l.attrNotInSymbolTable.Set(i)
+ } else {
+ l.attrNotInSymbolTable.Unset(i)
+ }
+}
+
+// AttrVisibilityHidden symbols returns true for ELF symbols with
+// visibility set to STV_HIDDEN. They become local symbols in
+// the final executable. Only relevant when internally linking
+// on an ELF platform.
+func (l *Loader) AttrVisibilityHidden(i Sym) bool {
+ if !l.IsExternal(i) {
+ return false
+ }
+ return l.attrVisibilityHidden.Has(l.extIndex(i))
+}
+
+// SetAttrVisibilityHidden sets the "hidden visibility" property for a
+// symbol (see AttrVisibilityHidden).
+func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("tried to set visibility attr on non-external symbol")
+ }
+ if v {
+ l.attrVisibilityHidden.Set(l.extIndex(i))
+ } else {
+ l.attrVisibilityHidden.Unset(l.extIndex(i))
+ }
+}
+
+// AttrDuplicateOK returns true for a symbol that can be present in
+// multiple object files.
+func (l *Loader) AttrDuplicateOK(i Sym) bool {
+ if !l.IsExternal(i) {
+ // TODO: if this path winds up being taken frequently, it
+ // might make more sense to copy the flag value out of the object
+ // into a larger bitmap during preload.
+ r, li := l.toLocal(i)
+ return r.Sym2(li).Dupok()
+ }
+ return l.attrDuplicateOK.Has(l.extIndex(i))
+}
+
+// SetAttrDuplicateOK sets the "duplicate OK" property for an external
+// symbol (see AttrDuplicateOK).
+func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("tried to set dupok attr on non-external symbol")
+ }
+ if v {
+ l.attrDuplicateOK.Set(l.extIndex(i))
+ } else {
+ l.attrDuplicateOK.Unset(l.extIndex(i))
+ }
+}
+
+// AttrShared returns true for symbols compiled with the -shared option.
+func (l *Loader) AttrShared(i Sym) bool {
+ if !l.IsExternal(i) {
+ // TODO: if this path winds up being taken frequently, it
+ // might make more sense to copy the flag value out of the
+ // object into a larger bitmap during preload.
+ r, _ := l.toLocal(i)
+ return (r.Flags() & goobj2.ObjFlagShared) != 0
+ }
+ return l.attrShared.Has(l.extIndex(i))
+}
+
+// SetAttrShared sets the "shared" property for an external
+// symbol (see AttrShared).
+func (l *Loader) SetAttrShared(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("tried to set shared attr on non-external symbol %d %s", i, l.SymName(i)))
+ }
+ if v {
+ l.attrShared.Set(l.extIndex(i))
+ } else {
+ l.attrShared.Unset(l.extIndex(i))
+ }
+}
+
+// AttrExternal returns true for function symbols loaded from host
+// object files.
+func (l *Loader) AttrExternal(i Sym) bool {
+ if !l.IsExternal(i) {
+ return false
+ }
+ return l.attrExternal.Has(l.extIndex(i))
+}
+
+// SetAttrExternal sets the "external" property for an host object
+// symbol (see AttrExternal).
+func (l *Loader) SetAttrExternal(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("tried to set external attr on non-external symbol %q", l.RawSymName(i)))
+ }
+ if v {
+ l.attrExternal.Set(l.extIndex(i))
+ } else {
+ l.attrExternal.Unset(l.extIndex(i))
+ }
+}
+
+// AttrTopFrame returns true for a function symbol that is an entry
+// point, meaning that unwinders should stop when they hit this
+// function.
+func (l *Loader) AttrTopFrame(i Sym) bool {
+ _, ok := l.attrTopFrame[i]
+ return ok
+}
+
+// SetAttrTopFrame sets the "top frame" property for a symbol (see
+// AttrTopFrame).
+func (l *Loader) SetAttrTopFrame(i Sym, v bool) {
+ if v {
+ l.attrTopFrame[i] = struct{}{}
+ } else {
+ delete(l.attrTopFrame, i)
+ }
+}
+
+// AttrSpecial returns true for a symbols that do not have their
+// address (i.e. Value) computed by the usual mechanism of
+// data.go:dodata() & data.go:address().
+func (l *Loader) AttrSpecial(i Sym) bool {
+ _, ok := l.attrSpecial[i]
+ return ok
+}
+
+// SetAttrSpecial sets the "special" property for a symbol (see
+// AttrSpecial).
+func (l *Loader) SetAttrSpecial(i Sym, v bool) {
+ if v {
+ l.attrSpecial[i] = struct{}{}
+ } else {
+ delete(l.attrSpecial, i)
+ }
+}
+
+// AttrCgoExportDynamic returns true for a symbol that has been
+// specially marked via the "cgo_export_dynamic" compiler directive
+// written by cgo (in response to //export directives in the source).
+func (l *Loader) AttrCgoExportDynamic(i Sym) bool {
+ _, ok := l.attrCgoExportDynamic[i]
+ return ok
+}
+
+// SetAttrCgoExportDynamic sets the "cgo_export_dynamic" for a symbol
+// (see AttrCgoExportDynamic).
+func (l *Loader) SetAttrCgoExportDynamic(i Sym, v bool) {
+ if v {
+ l.attrCgoExportDynamic[i] = struct{}{}
+ } else {
+ delete(l.attrCgoExportDynamic, i)
+ }
+}
+
+// AttrCgoExportStatic returns true for a symbol that has been
+// specially marked via the "cgo_export_static" directive
+// written by cgo.
+func (l *Loader) AttrCgoExportStatic(i Sym) bool {
+ _, ok := l.attrCgoExportStatic[i]
+ return ok
+}
+
+// SetAttrCgoExportStatic sets the "cgo_export_static" for a symbol
+// (see AttrCgoExportStatic).
+func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
+ if v {
+ l.attrCgoExportStatic[i] = struct{}{}
+ } else {
+ delete(l.attrCgoExportStatic, i)
+ }
+}
+
+func (l *Loader) AttrCgoExport(i Sym) bool {
+ return l.AttrCgoExportDynamic(i) || l.AttrCgoExportStatic(i)
+}
+
+// AttrReadOnly returns true for a symbol whose underlying data
+// is stored via a read-only mmap.
+func (l *Loader) AttrReadOnly(i Sym) bool {
+ if v, ok := l.attrReadOnly[i]; ok {
+ return v
+ }
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ return l.objs[pp.objidx].r.ReadOnly()
+ }
+ return false
+ }
+ r, _ := l.toLocal(i)
+ return r.ReadOnly()
+}
+
+// SetAttrReadOnly sets the "data is read only" property for a symbol
+// (see AttrReadOnly).
+func (l *Loader) SetAttrReadOnly(i Sym, v bool) {
+ l.attrReadOnly[i] = v
+}
+
+// AttrSubSymbol returns true for symbols that are listed as a
+// sub-symbol of some other outer symbol. The sub/outer mechanism is
+// used when loading host objects (sections from the host object
+// become regular linker symbols and symbols go on the Sub list of
+// their section) and for constructing the global offset table when
+// internally linking a dynamic executable.
+func (l *Loader) AttrSubSymbol(i Sym) bool {
+ // we don't explicitly store this attribute any more -- return
+ // a value based on the sub-symbol setting.
+ return l.OuterSym(i) != 0
+}
+
+// AttrContainer returns true for symbols that are listed as a
+// sub-symbol of some other outer symbol. The sub/outer mechanism is
+// used when loading host objects (sections from the host object
+// become regular linker symbols and symbols go on the Sub list of
+// their section) and for constructing the global offset table when
+// internally linking a dynamic executable.
+func (l *Loader) AttrContainer(i Sym) bool {
+ // we don't explicitly store this attribute any more -- return
+ // a value based on the sub-symbol setting.
+ return l.SubSym(i) != 0
+}
+
+// Note that we don't have SetAttrSubSymbol' or 'SetAttrContainer' methods
+// in the loader; clients should just use methods like PrependSub
+// to establish these relationships
+
// 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 the i-th symbol is nosplit.
+func (l *Loader) IsNoSplit(i Sym) bool {
+ return l.SymAttr(i)&goobj2.SymFlagNoSplit != 0
+}
+
// Returns whether this is a Go type symbol.
func (l *Loader) IsGoType(i Sym) bool {
return l.SymAttr(i)&goobj2.SymFlagGoType != 0
@@ -499,11 +976,30 @@ func (l *Loader) IsItabLink(i Sym) bool {
return false
}
+// growValues grows the slice used to store symbol values.
+func (l *Loader) growValues(reqLen int) {
+ curLen := len(l.values)
+ if reqLen > curLen {
+ l.values = append(l.values, make([]int64, reqLen+1-curLen)...)
+ }
+}
+
+// SymValue returns the value of the i-th symbol. i is global index.
+func (l *Loader) SymValue(i Sym) int64 {
+ return l.values[i]
+}
+
+// SetSymValue sets the value of the i-th symbol. i is global index.
+func (l *Loader) SetSymValue(i Sym, val int64) {
+ l.values[i] = val
+}
+
// 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
+ pp := l.getPayload(i)
+ if pp != nil {
+ return pp.data
}
return nil
}
@@ -511,184 +1007,550 @@ func (l *Loader) Data(i Sym) []byte {
return r.Data(li)
}
-// Returns the number of aux symbols given a global index.
-func (l *Loader) NAux(i Sym) int {
+// SymAlign returns the alignment for a symbol.
+func (l *Loader) SymAlign(i Sym) int32 {
+ // If an alignment has been recorded, return that.
+ if align, ok := l.align[i]; ok {
+ return align
+ }
+ // TODO: would it make sense to return an arch-specific
+ // alignment depending on section type? E.g. STEXT => 32,
+ // SDATA => 1, etc?
+ return 0
+}
+
+// SetSymAlign sets the alignment for a symbol.
+func (l *Loader) SetSymAlign(i Sym, align int32) {
+ // reject bad synbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymAlign")
+ }
+ // Reject nonsense alignments.
+ // TODO: do we need this?
+ if align < 0 {
+ panic("bad alignment value")
+ }
+ if align == 0 {
+ delete(l.align, i)
+ } else {
+ // Alignment should be a power of 2.
+ if bits.OnesCount32(uint32(align)) != 1 {
+ panic("bad alignment value")
+ }
+ l.align[i] = align
+ }
+}
+
+// SymDynImplib returns the "dynimplib" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimplib(i Sym) string {
+ return l.dynimplib[i]
+}
+
+// SetSymDynimplib sets the "dynimplib" attribute for a symbol.
+func (l *Loader) SetSymDynimplib(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetDynimplib")
+ }
+ if value == "" {
+ delete(l.dynimplib, i)
+ } else {
+ l.dynimplib[i] = value
+ }
+}
+
+// SymDynimpvers returns the "dynimpvers" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimpvers(i Sym) string {
+ return l.dynimpvers[i]
+}
+
+// SetSymDynimpvers sets the "dynimpvers" attribute for a symbol.
+func (l *Loader) SetSymDynimpvers(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetDynimpvers")
+ }
+ if value == "" {
+ delete(l.dynimpvers, i)
+ } else {
+ l.dynimpvers[i] = value
+ }
+}
+
+// SymExtname returns the "extname" value for the specified
+// symbol.
+func (l *Loader) SymExtname(i Sym) string {
+ if s, ok := l.extname[i]; ok {
+ return s
+ }
+ return l.SymName(i)
+}
+
+// SetSymExtname sets the "extname" attribute for a symbol.
+func (l *Loader) SetSymExtname(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetExtname")
+ }
+ if value == "" {
+ delete(l.extname, i)
+ } else {
+ l.extname[i] = value
+ }
+}
+
+// SymElfType returns the previously recorded ELF type for a symbol
+// (used only for symbols read from shared libraries by ldshlibsyms).
+// It is not set for symbols defined by the packages being linked or
+// by symbols read by ldelf (and so is left as elf.STT_NOTYPE).
+func (l *Loader) SymElfType(i Sym) elf.SymType {
+ if et, ok := l.elfType[i]; ok {
+ return et
+ }
+ return elf.STT_NOTYPE
+}
+
+// SetSymElfType sets the elf type attribute for a symbol.
+func (l *Loader) SetSymElfType(i Sym, et elf.SymType) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymElfType")
+ }
+ if et == elf.STT_NOTYPE {
+ delete(l.elfType, i)
+ } else {
+ l.elfType[i] = et
+ }
+}
+
+// SymPlt returns the plt value for pe symbols.
+func (l *Loader) SymPlt(s Sym) int32 {
+ if v, ok := l.plt[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetPlt sets the plt value for pe symbols.
+func (l *Loader) SetPlt(i Sym, v int32) {
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol for SetPlt")
+ }
+ if v == -1 {
+ delete(l.plt, i)
+ } else {
+ l.plt[i] = v
+ }
+}
+
+// SymGot returns the got value for pe symbols.
+func (l *Loader) SymGot(s Sym) int32 {
+ if v, ok := l.got[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetGot sets the got value for pe symbols.
+func (l *Loader) SetGot(i Sym, v int32) {
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol for SetGot")
+ }
+ if v == -1 {
+ delete(l.got, i)
+ } else {
+ l.got[i] = v
+ }
+}
+
+// SymDynid returns the "dynid" property for the specified symbol.
+func (l *Loader) SymDynid(i Sym) int32 {
+ if s, ok := l.dynid[i]; ok {
+ return s
+ }
+ return -1
+}
+
+// SetSymDynid sets the "dynid" property for a symbol.
+func (l *Loader) SetSymDynid(i Sym, val int32) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymDynid")
+ }
+ if val == -1 {
+ delete(l.dynid, i)
+ } else {
+ l.dynid[i] = val
+ }
+}
+
+// SymGoType returns the 'Gotype' property for a given symbol (set by
+// the Go compiler for variable symbols). This version relies on
+// reading aux symbols for the target sym -- it could be that a faster
+// approach would be to check for gotype during preload and copy the
+// results in to a map (might want to try this at some point and see
+// if it helps speed things up).
+func (l *Loader) SymGoType(i Sym) Sym {
if l.IsExternal(i) {
- return 0
+ pp := l.getPayload(i)
+ return pp.gotype
}
r, li := l.toLocal(i)
- return r.NAux(li)
+ auxs := r.Auxs2(li)
+ for j := range auxs {
+ a := &auxs[j]
+ switch a.Type() {
+ case goobj2.AuxGotype:
+ return l.resolve(r, a.Sym())
+ }
+ }
+ return 0
+}
+
+// SymUnit returns the compilation unit for a given symbol (which will
+// typically be nil for external or linker-manufactured symbols).
+func (l *Loader) SymUnit(i Sym) *sym.CompilationUnit {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ r := l.objs[pp.objidx].r
+ return r.unit
+ }
+ return nil
+ }
+ r, _ := l.toLocal(i)
+ return r.unit
+}
+
+// SymFile returns the file for a symbol, which is normally the
+// package the symbol came from (for regular compiler-generated Go
+// symbols), but in the case of building with "-linkshared" (when a
+// symbol is read from a a shared library), will hold the library
+// name.
+func (l *Loader) SymFile(i Sym) string {
+ if l.IsExternal(i) {
+ if f, ok := l.symFile[i]; ok {
+ return f
+ }
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ r := l.objs[pp.objidx].r
+ return r.unit.Lib.File
+ }
+ return ""
+ }
+ r, _ := l.toLocal(i)
+ return r.unit.Lib.File
+}
+
+// SetSymFile sets the file attribute for a symbol. This is
+// needed mainly for external symbols, specifically those imported
+// from shared libraries.
+func (l *Loader) SetSymFile(i Sym, file string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymFile")
+ }
+ if !l.IsExternal(i) {
+ panic("can't set file for non-external sym")
+ }
+ l.symFile[i] = file
}
-// Returns the referred symbol of the j-th aux symbol of the i-th
+// SymLocalentry returns the "local entry" value for the specified
// symbol.
-func (l *Loader) AuxSym(i Sym, j int) Sym {
+func (l *Loader) SymLocalentry(i Sym) uint8 {
+ return l.localentry[i]
+}
+
+// SetSymLocalentry sets the "local entry" attribute for a symbol.
+func (l *Loader) SetSymLocalentry(i Sym, value uint8) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymLocalentry")
+ }
+ if value == 0 {
+ delete(l.localentry, i)
+ } else {
+ l.localentry[i] = value
+ }
+}
+
+// Returns the number of aux symbols given a global index.
+func (l *Loader) NAux(i Sym) int {
if l.IsExternal(i) {
return 0
}
r, li := l.toLocal(i)
- a := goobj2.Aux{}
- a.Read(r.Reader, r.AuxOff(li, j))
- return l.resolve(r, a.Sym)
+ return r.NAux(li)
}
-// 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]
+// Returns the "handle" to the j-th aux symbol of the i-th symbol.
+func (l *Loader) Aux2(i Sym, j int) Aux2 {
+ if l.IsExternal(i) {
+ return Aux2{}
}
- naux := l.NAux(symIdx)
- if naux == 0 {
- return dst[:0]
+ r, li := l.toLocal(i)
+ if j >= r.NAux(li) {
+ return Aux2{}
}
+ return Aux2{r.Aux2(li, j), r, l}
+}
- if cap(dst) < naux {
- dst = make([]Sym, naux)
+// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF
+// symbols associated with a given function symbol. Prior to the
+// introduction of the loader, this was done purely using name
+// lookups, e.f. for function with name XYZ we would then look up
+// go.info.XYZ, etc.
+// FIXME: once all of dwarfgen is converted over to the loader,
+// it would save some space to make these aux symbols nameless.
+func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) {
+ if l.SymType(fnSymIdx) != sym.STEXT {
+ log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
+ }
+ if l.IsExternal(fnSymIdx) {
+ // Current expectation is that any external function will
+ // not have auxsyms.
+ return
}
- 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))
+ r, li := l.toLocal(fnSymIdx)
+ auxs := r.Auxs2(li)
+ for i := range auxs {
+ a := &auxs[i]
+ switch a.Type() {
+ case goobj2.AuxDwarfInfo:
+ auxDwarfInfo = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfInfo) != sym.SDWARFINFO {
+ panic("aux dwarf info sym with wrong type")
+ }
+ case goobj2.AuxDwarfLoc:
+ auxDwarfLoc = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfLoc) != sym.SDWARFLOC {
+ panic("aux dwarf loc sym with wrong type")
+ }
+ case goobj2.AuxDwarfRanges:
+ auxDwarfRanges = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE {
+ panic("aux dwarf ranges sym with wrong type")
+ }
+ case goobj2.AuxDwarfLines:
+ auxDwarfLines = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfLines) != sym.SDWARFLINES {
+ panic("aux dwarf lines sym with wrong type")
+ }
+ }
}
+ return
+}
- return dst
+// PrependSub prepends 'sub' onto the sub list for outer symbol 'outer'.
+// Will panic if 'sub' already has an outer sym or sub sym.
+// FIXME: should this be instead a method on SymbolBuilder?
+func (l *Loader) PrependSub(outer Sym, sub Sym) {
+ // NB: this presupposes that an outer sym can't be a sub symbol of
+ // some other outer-outer sym (I'm assuming this is true, but I
+ // haven't tested exhaustively).
+ if l.OuterSym(outer) != 0 {
+ panic("outer has outer itself")
+ }
+ if l.SubSym(sub) != 0 {
+ panic("sub set for subsym")
+ }
+ if l.OuterSym(sub) != 0 {
+ panic("outer already set for subsym")
+ }
+ l.sub[sub] = l.sub[outer]
+ l.sub[outer] = sub
+ l.outer[sub] = outer
}
// 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
+ // FIXME: add check for isExternal?
+ return l.outer[i]
}
// SubSym gets the subsymbol for host object loaded symbols.
func (l *Loader) SubSym(i Sym) Sym {
- sym := l.Syms[i]
- if sym != nil && sym.Sub != nil {
- sub := sym.Sub
- return l.Lookup(sub.Name, int(sub.Version))
- }
- return 0
+ // NB: note -- no check for l.isExternal(), since I am pretty sure
+ // that later phases in the linker set subsym for "type." syms
+ return l.sub[i]
}
-// Initialize Reachable bitmap for running deadcode pass.
+// Initialize Reachable bitmap and its siblings 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
+ l.growAttrBitmaps(l.NSym() + 1)
+}
+
+type symWithVal struct {
+ s Sym
+ v int64
+}
+type bySymValue []symWithVal
+
+func (s bySymValue) Len() int { return len(s) }
+func (s bySymValue) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s bySymValue) Less(i, j int) bool { return s[i].v < s[j].v }
+
+// SortSub walks through the sub-symbols for 's' and sorts them
+// in place by increasing value. Return value is the new
+// sub symbol for the specified outer symbol.
+func (l *Loader) SortSub(s Sym) Sym {
+
+ if s == 0 || l.sub[s] == 0 {
+ return s
+ }
+
+ // Sort symbols using a slice first. Use a stable sort on the off
+ // chance that there's more than once symbol with the same value,
+ // so as to preserve reproducible builds.
+ sl := []symWithVal{}
+ for ss := l.sub[s]; ss != 0; ss = l.sub[ss] {
+ sl = append(sl, symWithVal{s: ss, v: l.SymValue(ss)})
}
+ sort.Stable(bySymValue(sl))
- 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,
- })
+ // Then apply any changes needed to the sub map.
+ ns := Sym(0)
+ for i := len(sl) - 1; i >= 0; i-- {
+ s := sl[i].s
+ l.sub[s] = ns
+ ns = s
}
- return dst
+
+ // Update sub for outer symbol, then return
+ l.sub[s] = sl[0].s
+ return sl[0].s
+}
+
+// Insure that reachable bitmap and its siblings have enough size.
+func (l *Loader) growAttrBitmaps(reqLen int) {
+ if reqLen > l.attrReachable.Len() {
+ // These are indexed by global symbol
+ l.attrReachable = growBitmap(reqLen, l.attrReachable)
+ l.attrOnList = growBitmap(reqLen, l.attrOnList)
+ l.attrLocal = growBitmap(reqLen, l.attrLocal)
+ l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable)
+ }
+ l.growExtAttrBitmaps()
+}
+
+func (l *Loader) growExtAttrBitmaps() {
+ // These are indexed by external symbol index (e.g. l.extIndex(i))
+ extReqLen := len(l.payloads)
+ if extReqLen > l.attrVisibilityHidden.Len() {
+ l.attrVisibilityHidden = growBitmap(extReqLen, l.attrVisibilityHidden)
+ l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK)
+ l.attrShared = growBitmap(extReqLen, l.attrShared)
+ l.attrExternal = growBitmap(extReqLen, l.attrExternal)
+ }
+}
+
+func (relocs *Relocs) Count() int { return len(relocs.rs) }
+
+// At2 returns the j-th reloc for a global symbol.
+func (relocs *Relocs) At2(j int) Reloc2 {
+ if relocs.l.isExtReader(relocs.r) {
+ pp := relocs.l.payloads[relocs.li]
+ return Reloc2{&relocs.rs[j], relocs.r, relocs.l, pp.reltypes[j]}
+ }
+ return Reloc2{&relocs.rs[j], relocs.r, relocs.l, 0}
}
// Relocs returns a Relocs object for the given global sym.
func (l *Loader) Relocs(i Sym) Relocs {
- 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)
+ if r == nil {
+ panic(fmt.Sprintf("trying to get oreader for invalid sym %d\n\n", i))
+ }
return l.relocs(r, li)
}
// Relocs returns a Relocs object given a local sym index and reader.
func (l *Loader) relocs(r *oReader, li int) Relocs {
+ var rs []goobj2.Reloc2
+ if l.isExtReader(r) {
+ pp := l.payloads[li]
+ rs = pp.relocs
+ } else {
+ rs = r.Relocs2(li)
+ }
return Relocs{
- Count: r.NReloc(li),
- li: li,
- r: r,
- l: l,
+ rs: rs,
+ 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) {
+// 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 { return x[i].Off < x[j].Off }
+
+// FuncInfo provides hooks to access goobj2.FuncInfo in the objects.
+type FuncInfo struct {
+ l *Loader
+ r *oReader
+ data []byte
+}
+
+func (fi *FuncInfo) Valid() bool { return fi.r != nil }
+
+func (fi *FuncInfo) Locals() int {
+ return int((*goobj2.FuncInfo)(nil).ReadLocals(fi.data))
+}
+
+func (fi *FuncInfo) Pcsp() []byte {
+ pcsp, end := (*goobj2.FuncInfo)(nil).ReadPcsp(fi.data)
+ return fi.r.BytesAt(fi.r.PcdataBase()+pcsp, int(end-pcsp))
+}
+
+// TODO: more accessors.
+
+func (l *Loader) FuncInfo(i Sym) FuncInfo {
+ if l.IsExternal(i) {
+ return FuncInfo{}
+ }
+ r, li := l.toLocal(i)
+ auxs := r.Auxs2(li)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj2.AuxFuncInfo {
+ b := r.Data(int(a.Sym().SymIdx))
+ return FuncInfo{l, r, b}
+ }
+ }
+ return FuncInfo{}
+}
+
+// Preload a package: add autolibs, add defined package symbols to the symbol table.
+// Does not add non-package symbols yet, which will be done in LoadNonpkgSyms.
+// Does not read symbol data.
+func (l *Loader) Preload(syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, 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 {
+ if len(roObject) >= 8 && bytes.Equal(roObject[:8], []byte("\x00go114ld")) {
+ log.Fatalf("found object file %s in old format, but -go115newobj is true\nset -go115newobj consistently in all -gcflags, -asmflags, and -ldflags", f.File().Name())
+ }
panic("cannot read object file")
}
localSymVersion := syms.IncVersion()
pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
- or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, nil}
+ ndef := r.NSym()
+ nnonpkgdef := r.NNonpkgdef()
+ or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, make([]Sym, ndef+nnonpkgdef+r.NNonpkgref()), ndef, uint32(len(l.objs))}
// Autolib
lib.ImportStrings = append(lib.ImportStrings, r.Autolib()...)
@@ -700,51 +1562,88 @@ func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *
unit.DWARFFileTable[i] = r.DwarfFile(i)
}
- istart := l.addObj(lib.Pkg, or)
+ l.addObj(lib.Pkg, or)
+ l.preloadSyms(or, pkgDef)
+ // The caller expects us consuming all the data
+ f.MustSeek(length, os.SEEK_CUR)
+}
+
+// Preload symbols of given kind from an object.
+func (l *Loader) preloadSyms(r *oReader, kind int) {
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)
+ var start, end int
+ switch kind {
+ case pkgDef:
+ start = 0
+ end = ndef
+ case nonPkgDef:
+ start = ndef
+ end = ndef + nnonpkgdef
+ default:
+ panic("preloadSyms: bad kind")
+ }
+ l.growSyms(len(l.objSyms) + end - start)
+ l.growAttrBitmaps(len(l.objSyms) + end - start)
+ for i := start; i < end; i++ {
+ osym := r.Sym2(i)
+ name := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1)
+ v := abiToVer(osym.ABI(), r.version)
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{}{}
+ gi, added := l.AddSym(name, v, r, i, kind, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())])
+ r.syms[i] = gi
+ if !added {
+ continue
+ }
+ if osym.TopFrame() {
+ l.SetAttrTopFrame(gi, true)
+ }
+ if osym.Local() {
+ l.SetAttrLocal(gi, true)
+ }
+ if strings.HasPrefix(name, "go.itablink.") {
+ l.itablink[gi] = struct{}{}
}
- if added && strings.HasPrefix(name, "runtime.") {
+ if 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)
+ l.builtinSyms[bi] = gi
}
}
+ if strings.HasPrefix(name, "go.string.") ||
+ strings.HasPrefix(name, "gclocals·") ||
+ strings.HasPrefix(name, "runtime.gcbits.") {
+ l.SetAttrNotInSymbolTable(gi, true)
+ }
+ if a := osym.Align(); a != 0 {
+ l.SetSymAlign(gi, int32(a))
+ }
}
-
- // 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) {
+// Add non-package symbols and references to external symbols (which are always
+// named).
+func (l *Loader) LoadNonpkgSyms(syms *sym.Symbols) {
+ for _, o := range l.objs[1:] {
+ l.preloadSyms(o.r, nonPkgDef)
+ }
for _, o := range l.objs[1:] {
- loadObjRefs(l, o.r, arch, syms)
+ loadObjRefs(l, o.r, syms)
}
}
-func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) {
+func loadObjRefs(l *Loader, r *oReader, 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)
+ osym := r.Sym2(ndef + i)
+ name := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1)
+ v := abiToVer(osym.ABI(), r.version)
+ r.syms[ndef+i] = l.LookupOrCreateSym(name, v)
+ if osym.Local() {
+ gi := r.syms[ndef+i]
+ l.SetAttrLocal(gi, true)
+ }
}
}
@@ -794,22 +1693,64 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
nr += loadObjSyms(l, syms, o.r)
}
+ // Make a first pass through the external symbols, making
+ // sure that each external symbol has a non-nil entry in
+ // l.Syms (note that relocations and symbol content will
+ // be copied in a later loop).
+ toConvert := make([]Sym, 0, len(l.payloads))
+ for _, i := range l.extReader.syms {
+ sname := l.RawSymName(i)
+ if !l.attrReachable.Has(i) && !strings.HasPrefix(sname, "gofile..") { // XXX file symbols are used but not marked
+ continue
+ }
+ pp := l.getPayload(i)
+ nr += len(pp.relocs)
+ // create and install the sym.Symbol here so that l.Syms will
+ // be fully populated when we do relocation processing and
+ // outer/sub processing below. Note that once we do this,
+ // we'll need to get at the payload for a symbol with direct
+ // reference to l.payloads[] as opposed to calling l.getPayload().
+ s := l.allocSym(sname, 0)
+ l.installSym(i, s)
+ toConvert = append(toConvert, i)
+ }
+
// 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
+ // convert payload-based external symbols into sym.Symbol-based
+ for _, i := range toConvert {
+
+ // Copy kind/size/value etc.
+ pp := l.payloads[l.extIndex(i)]
+ s := l.Syms[i]
+ s.Version = int16(pp.ver)
+ s.Type = pp.kind
+ s.Size = pp.size
+ if pp.gotype != 0 {
+ s.Gotype = l.Syms[pp.gotype]
}
- 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
+ if f, ok := l.symFile[i]; ok {
+ s.File = f
+ } else if pp.objidx != 0 {
+ s.File = l.objs[pp.objidx].r.unit.Lib.File
}
+
+ // Copy relocations
+ batch := l.relocBatch
+ s.R = batch[:len(pp.relocs):len(pp.relocs)]
+ l.relocBatch = batch[len(pp.relocs):]
+ relocs := l.Relocs(i)
+ l.convertRelocations(&relocs, s, false)
+
+ // Copy data
+ s.P = pp.data
+
+ // Transfer over attributes.
+ l.migrateAttributes(i, s)
+
+ // Preprocess symbol. May set 'AttrLocal'.
+ preprocess(arch, s)
}
// load contents of defined symbols
@@ -817,11 +1758,15 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
loadObjFull(l, o.r)
}
+ // Note: resolution of ABI aliases is now also handled in
+ // loader.convertRelocations, so once the host object loaders move
+ // completely to loader.Sym, we can remove the code below.
+
// 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++ {
+ for _, i := range l.extReader.syms {
if s := l.Syms[i]; s != nil && s.Attr.Reachable() {
for ri := range s.R {
r := &s.R[ri]
@@ -833,40 +1778,259 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
}
}
+// ResolveABIAlias given a symbol returns the ABI alias target of that
+// symbol. If the sym in question is not an alias, the sym itself is
+// returned.
+func (l *Loader) ResolveABIAlias(s Sym) Sym {
+ if l.SymType(s) != sym.SABIALIAS {
+ return s
+ }
+ relocs := l.Relocs(s)
+ target := relocs.At2(0).Sym()
+ if l.SymType(target) == sym.SABIALIAS {
+ panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", l.SymName(s), l.SymName(target)))
+ }
+ return target
+}
+
+// PropagateSymbolChangesBackToLoader is a temporary shim function
+// that copies over a given sym.Symbol into the equivalent representation
+// in the loader world. The intent is to enable converting a given
+// linker phase/pass from dealing with sym.Symbol's to a modernized
+// pass that works with loader.Sym, in cases where the "loader.Sym
+// wavefront" has not yet reached the pass in question. For such work
+// the recipe is to first call PropagateSymbolChangesBackToLoader(),
+// then exexute the pass working with the loader, then call
+// PropagateLoaderChangesToSymbols to copy the changes made by the
+// pass back to the sym.Symbol world.
+func (l *Loader) PropagateSymbolChangesBackToLoader() {
+
+ // For the moment we only copy symbol values, and we don't touch
+ // any new sym.Symbols created since loadlibfull() was run. This
+ // seems to be what's needed for DWARF gen.
+ for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
+ s := l.Syms[i]
+ if s != nil {
+ if s.Value != l.SymValue(i) {
+ l.SetSymValue(i, s.Value)
+ }
+ }
+ }
+}
+
+// PropagateLoaderChangesToSymbols is a temporary shim function that
+// takes a list of loader.Sym symbols and works to copy their contents
+// and attributes over to a corresponding sym.Symbol. The parameter
+// anonVerReplacement specifies a version number for any new anonymous
+// symbols encountered on the list, when creating sym.Symbols for them
+// (or zero if we don't expect to encounter any new anon symbols). See
+// the PropagateSymbolChangesBackToLoader header comment for more
+// info.
+//
+// WARNING: this function is brittle and depends heavily on loader
+// implementation. A key problem with doing this is that as things
+// stand at the moment, some sym.Symbol contents/attributes are
+// populated only when converting from loader.Sym to sym.Symbol in
+// loadlibfull, meaning we may wipe out some information when copying
+// back.
+
+func (l *Loader) PropagateLoaderChangesToSymbols(toconvert []Sym, anonVerReplacement int) []*sym.Symbol {
+
+ result := []*sym.Symbol{}
+ relocfixup := []Sym{}
+
+ // Note: this loop needs to allow for the possibility that we may
+ // see "new" symbols on the 'toconvert' list that come from object
+ // files (for example, DWARF location lists), as opposed to just
+ // newly manufactured symbols (ex: DWARF section symbols such as
+ // ".debug_info"). This means that we have to be careful not to
+ // stomp on sym.Symbol attributes/content that was set up in
+ // in loadlibfull().
+
+ // Also note that in order for the relocation fixup to work, we
+ // have to do this in two passes -- one pass to create the symbols,
+ // and then a second fix up the relocations once all necessary
+ // sym.Symbols are created.
+
+ // First pass, symbol creation and symbol data fixup.
+ for _, cand := range toconvert {
+
+ sn := l.SymName(cand)
+ sv := l.SymVersion(cand)
+ st := l.SymType(cand)
+ if sv < 0 {
+ if anonVerReplacement == 0 {
+ panic("expected valid anon version replacement")
+ }
+ sv = anonVerReplacement
+ }
+
+ s := l.Syms[cand]
+
+ isnew := false
+ if sn == "" {
+ // Don't install anonymous symbols in the lookup tab.
+ if s == nil {
+ s = l.allocSym(sn, sv)
+ l.installSym(cand, s)
+ }
+ isnew = true
+ } else {
+ if s != nil {
+ // Already have a symbol for this -- it must be
+ // something that was previously processed by
+ // loadObjFull. Note that the symbol in question may
+ // or may not be in the name lookup map.
+ } else {
+ isnew = true
+ s = l.SymLookup(sn, sv)
+ }
+ }
+ result = append(result, s)
+
+ // Always copy these from new to old.
+ s.Value = l.SymValue(cand)
+ s.Type = st
+
+ // If the data for a symbol has increased in size, make sure
+ // we bring the new content across.
+ relfix := isnew
+ if isnew || len(l.Data(cand)) > len(s.P) {
+ s.P = l.Data(cand)
+ s.Size = int64(len(s.P))
+ relfix = true
+ }
+
+ // For 'new' symbols, copy other content (such as Gotype,
+ // sym file, relocations, etc).
+ if isnew {
+ if gt := l.SymGoType(cand); gt != 0 {
+ s.Gotype = l.Syms[gt]
+ }
+ if f, ok := l.symFile[cand]; ok {
+ s.File = f
+ } else {
+ r, _ := l.toLocal(cand)
+ if r != nil && r != l.extReader {
+ s.File = l.SymFile(cand)
+ }
+ }
+ }
+
+ if relfix {
+ relocfixup = append(relocfixup, cand)
+ }
+
+ // If new symbol, call a helper to migrate attributes.
+ // Otherwise touch only not-in-symbol-table, since there are
+ // some attrs that are only set up at the point where we
+ // convert loader.Sym to sym.Symbol.
+ if isnew {
+ l.migrateAttributes(cand, s)
+ } else {
+ if l.AttrNotInSymbolTable(cand) {
+ s.Attr.Set(sym.AttrNotInSymbolTable, true)
+ }
+ }
+ }
+
+ // Second pass to fix up relocations.
+ for _, cand := range relocfixup {
+ s := l.Syms[cand]
+ relocs := l.Relocs(cand)
+ if len(s.R) != relocs.Count() {
+ s.R = make([]sym.Reloc, relocs.Count())
+ }
+ l.convertRelocations(&relocs, s, true)
+ }
+
+ return result
+}
+
// 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).
+func (l *Loader) ExtractSymbols(syms *sym.Symbols, rp map[*sym.Symbol]*sym.Symbol) {
+ // 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. We do, however,
+ // add these symbols to the list of global symbols so that other future
+ // steps (like pclntab generation) can find these symbols if neceassary.
+ // 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)
- }
+ syms.Allsym = append(syms.Allsym, s) // XXX still add to Allsym for now, as there are code looping through Allsym
if s.Version < 0 {
s.Version = int16(anonVerReplacement)
}
}
+
+ for i, s := range l.Reachparent {
+ if i == 0 {
+ continue
+ }
+ rp[l.Syms[i]] = l.Syms[s]
+ }
+
+ // Provide lookup functions for sym.Symbols.
+ l.SymLookup = func(name string, ver int) *sym.Symbol {
+ i := l.LookupOrCreateSym(name, ver)
+ if s := l.Syms[i]; s != nil {
+ return s
+ }
+ s := l.allocSym(name, ver)
+ l.installSym(i, s)
+ syms.Allsym = append(syms.Allsym, s) // XXX see above
+ return s
+ }
+ syms.Lookup = l.SymLookup
+ syms.ROLookup = func(name string, ver int) *sym.Symbol {
+ i := l.Lookup(name, ver)
+ return l.Syms[i]
+ }
+ syms.Newsym = func(name string, ver int) *sym.Symbol {
+ i := l.newExtSym(name, ver)
+ s := l.allocSym(name, ver)
+ l.installSym(i, s)
+ syms.Allsym = append(syms.Allsym, s) // XXX see above
+ return s
+ }
+}
+
+// allocSym allocates a new symbol backing.
+func (l *Loader) allocSym(name string, version int) *sym.Symbol {
+ batch := l.symBatch
+ if len(batch) == 0 {
+ batch = make([]sym.Symbol, 1000)
+ }
+ s := &batch[0]
+ l.symBatch = batch[1:]
+
+ s.Dynid = -1
+ s.Name = name
+ s.Version = int16(version)
+
+ return s
+}
+
+// installSym sets the underlying sym.Symbol for the specified sym index.
+func (l *Loader) installSym(i Sym, s *sym.Symbol) {
+ if s == nil {
+ panic("installSym nil symbol")
+ }
+ if l.Syms[i] != nil {
+ panic("sym already present in installSym")
+ }
+ l.Syms[i] = s
}
// 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)
+func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
+ s := l.allocSym(name, ver)
if s.Type != 0 && s.Type != sym.SXREF {
fmt.Println("symbol already processed:", unit.Lib, i, s)
panic("symbol already processed")
@@ -877,7 +2041,7 @@ func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit
s.Type = t
s.Unit = unit
l.growSyms(int(i))
- l.Syms[i] = s
+ l.installSym(i, s)
return s
}
@@ -885,44 +2049,39 @@ func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit
// 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
+ gi := r.syms[i]
+ if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+ continue // come from a different object
}
- ver := abiToVer(osym.ABI, r.version)
- if osym.ABI != goobj2.SymABIstatic && l.symsByName[ver][name] != istart+Sym(i) {
+ osym := r.Sym2(i)
+ name := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1)
+ t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+ // NB: for the test below, we can skip most anonymous symbols
+ // since they will never be turned into sym.Symbols (ex:
+ // funcdata), however DWARF subprogram DIE symbols (which are
+ // nameless) will eventually need to be turned into
+ // sym.Symbols (with relocations), so the simplest thing to do
+ // is include them as part of this loop.
+ if name == "" && t != sym.SDWARFINFO {
continue
}
-
- t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+ ver := abiToVer(osym.ABI(), r.version)
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" {
+ if !l.attrReachable.Has(gi) && 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)))
+ s := l.addNewSym(gi, name, ver, r.unit, t)
+ l.migrateAttributes(gi, s)
nr += r.NReloc(i)
}
return nr
@@ -933,9 +2092,9 @@ func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
// 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
+ s *sym.Symbol // sym.Symbol for a live function
+ osym *goobj2.Sym2 // 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;
@@ -947,85 +2106,201 @@ type funcAllocInfo struct {
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]
+// cloneToExternal takes the existing object file symbol (symIdx)
+// and creates a new external symbol payload that is a clone with
+// respect to name, version, type, relocations, etc. The idea here
+// is that if the linker decides it wants to update the contents of
+// a symbol originally discovered as part of an object file, it's
+// easier to do this if we make the updates to an external symbol
+// payload.
+// XXX maybe rename? makeExtPayload?
+func (l *Loader) cloneToExternal(symIdx Sym) {
+ if l.IsExternal(symIdx) {
+ panic("sym is already external, no need for clone")
}
+ l.growSyms(int(symIdx))
- // Read the symbol.
- r, i := l.toLocal(global)
- istart := l.startIndex(r)
+ // Read the particulars from object.
+ r, li := l.toLocal(symIdx)
+ osym := r.Sym2(li)
+ sname := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1)
+ sver := abiToVer(osym.ABI(), r.version)
+ skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+
+ // Create new symbol, update version and kind.
+ pi := l.newPayload(sname, sver)
+ pp := l.payloads[pi]
+ pp.kind = skind
+ pp.ver = sver
+ pp.size = int64(osym.Siz())
+ pp.objidx = r.objidx
+
+ // If this is a def, then copy the guts. We expect this case
+ // to be very rare (one case it may come up is with -X).
+ if li < (r.NSym() + r.NNonpkgdef()) {
+
+ // Copy relocations
+ relocs := l.Relocs(symIdx)
+ pp.relocs = make([]goobj2.Reloc2, relocs.Count())
+ pp.reltypes = make([]objabi.RelocType, relocs.Count())
+ for i := range pp.relocs {
+ // Copy the relocs slice.
+ // Convert local reference to global reference.
+ rel := relocs.At2(i)
+ pp.relocs[i].Set(rel.Off(), rel.Siz(), 0, rel.Add(), goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
+ pp.reltypes[i] = rel.Type()
+ }
- osym := goobj2.Sym{}
- osym.Read(r.Reader, r.SymOff(int(i)))
- if l.symsByName[version][name] != istart+Sym(i) {
- return nil
+ // Copy data
+ pp.data = r.Data(li)
}
- 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.")
+ // If we're overriding a data symbol, collect the associated
+ // Gotype, so as to propagate it to the new symbol.
+ auxs := r.Auxs2(li)
+ for j := range auxs {
+ a := &auxs[j]
+ switch a.Type() {
+ case goobj2.AuxGotype:
+ pp.gotype = l.resolve(r, a.Sym())
+ default:
+ log.Fatalf("internal error: cloneToExternal applied to %s symbol %s with non-gotype aux data %d", skind.String(), sname, a.Type())
}
- return l.LoadSymbol(name, version, syms)
}
- i = l.AddExtSym(name, version)
- s := syms.Newsym(name, version)
- l.Syms[i] = s
- return s
+
+ // Install new payload to global index space.
+ // (This needs to happen at the end, as the accessors above
+ // need to access the old symbol content.)
+ l.objSyms[symIdx] = objSym{l.extReader, pi}
+ l.extReader.syms = append(l.extReader.syms, symIdx)
}
-// 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
+// Copy the payload of symbol src to dst. Both src and dst must be external
+// symbols.
+// The intended use case is that when building/linking against a shared library,
+// where we do symbol name mangling, the Go object file may have reference to
+// the original symbol name whereas the shared library provides a symbol with
+// the mangled name. When we do mangling, we copy payload of mangled to original.
+func (l *Loader) CopySym(src, dst Sym) {
+ if !l.IsExternal(dst) {
+ panic("dst is not external") //l.newExtSym(l.SymName(dst), l.SymVersion(dst))
+ }
+ if !l.IsExternal(src) {
+ panic("src is not external") //l.cloneToExternal(src)
+ }
+ l.payloads[l.extIndex(dst)] = l.payloads[l.extIndex(src)]
+ l.SetSymFile(dst, l.SymFile(src))
+ // TODO: other attributes?
+}
+
+// CopyAttributes copies over all of the attributes of symbol 'src' to
+// symbol 'dst'.
+func (l *Loader) CopyAttributes(src Sym, dst Sym) {
+ l.SetAttrReachable(dst, l.AttrReachable(src))
+ l.SetAttrOnList(dst, l.AttrOnList(src))
+ l.SetAttrLocal(dst, l.AttrLocal(src))
+ l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src))
+ if l.IsExternal(dst) {
+ l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src))
+ l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src))
+ l.SetAttrShared(dst, l.AttrShared(src))
+ l.SetAttrExternal(dst, l.AttrExternal(src))
+ } else {
+ // Some attributes are modifiable only for external symbols.
+ // In such cases, don't try to transfer over the attribute
+ // from the source even if there is a clash. This comes up
+ // when copying attributes from a dupOK ABI wrapper symbol to
+ // the real target symbol (which may not be marked dupOK).
+ }
+ l.SetAttrTopFrame(dst, l.AttrTopFrame(src))
+ l.SetAttrSpecial(dst, l.AttrSpecial(src))
+ l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src))
+ l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src))
+ l.SetAttrReadOnly(dst, l.AttrReadOnly(src))
+}
+
+// migrateAttributes copies over all of the attributes of symbol 'src' to
+// sym.Symbol 'dst'.
+func (l *Loader) migrateAttributes(src Sym, dst *sym.Symbol) {
+ dst.Value = l.SymValue(src)
+ dst.Align = l.SymAlign(src)
+
+ dst.Attr.Set(sym.AttrReachable, l.AttrReachable(src))
+ dst.Attr.Set(sym.AttrOnList, l.AttrOnList(src))
+ dst.Attr.Set(sym.AttrLocal, l.AttrLocal(src))
+ dst.Attr.Set(sym.AttrNotInSymbolTable, l.AttrNotInSymbolTable(src))
+ dst.Attr.Set(sym.AttrNoSplit, l.IsNoSplit(src))
+ dst.Attr.Set(sym.AttrVisibilityHidden, l.AttrVisibilityHidden(src))
+ dst.Attr.Set(sym.AttrDuplicateOK, l.AttrDuplicateOK(src))
+ dst.Attr.Set(sym.AttrShared, l.AttrShared(src))
+ dst.Attr.Set(sym.AttrExternal, l.AttrExternal(src))
+ dst.Attr.Set(sym.AttrTopFrame, l.AttrTopFrame(src))
+ dst.Attr.Set(sym.AttrSpecial, l.AttrSpecial(src))
+ dst.Attr.Set(sym.AttrCgoExportDynamic, l.AttrCgoExportDynamic(src))
+ dst.Attr.Set(sym.AttrCgoExportStatic, l.AttrCgoExportStatic(src))
+ dst.Attr.Set(sym.AttrReadOnly, l.AttrReadOnly(src))
+
+ // Convert outer/sub relationships
+ if outer, ok := l.outer[src]; ok {
+ dst.Outer = l.Syms[outer]
+ }
+ if sub, ok := l.sub[src]; ok {
+ dst.Sub = l.Syms[sub]
}
+ // Set sub-symbol attribute. FIXME: would be better to do away
+ // with this and just use l.OuterSymbol() != 0 elsewhere within
+ // the linker.
+ dst.Attr.Set(sym.AttrSubSymbol, dst.Outer != nil)
+
+ // Copy over dynimplib, dynimpvers, extname.
+ if name, ok := l.extname[src]; ok {
+ dst.SetExtname(name)
+ }
+ if l.SymDynimplib(src) != "" {
+ dst.SetDynimplib(l.SymDynimplib(src))
+ }
+ if l.SymDynimpvers(src) != "" {
+ dst.SetDynimpvers(l.SymDynimpvers(src))
+ }
+
+ // Copy ELF type if set.
+ if et, ok := l.elfType[src]; ok {
+ dst.SetElfType(et)
+ }
+
+ // Copy pe objects values if set.
+ if plt, ok := l.plt[src]; ok {
+ dst.SetPlt(plt)
+ }
+ if got, ok := l.got[src]; ok {
+ dst.SetGot(got)
+ }
+
+ // Copy dynid
+ if dynid, ok := l.dynid[src]; ok {
+ dst.Dynid = dynid
+ }
+}
+
+// CreateExtSym creates a new external symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateExtSym(name string, ver int) Sym {
+ return l.newExtSym(name, ver)
+}
+
+// CreateStaticSym creates a new static symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateStaticSym(name string) Sym {
// Assign a new unique negative version -- this is to mark the
// symbol so that it 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
+ return l.newExtSym(name, l.anonVersion)
}
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]
@@ -1035,43 +2310,42 @@ func loadObjFull(l *Loader, r *oReader) {
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
+ // A symbol may be a dup or overwritten. In this case, its
+ // content will actually be provided by a different object
+ // (to which its global index points). Skip those symbols.
+ gi := l.toGlobal(r, i)
+ var isdup bool
+ if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+ isdup = true
}
- ver := abiToVer(osym.ABI, r.version)
+
+ osym := r.Sym2(i)
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)
- }
+ if dupok && isdup {
+ if l.attrReachable.Has(gi) {
+ // 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[gi]
+ if s.Type == sym.STEXT {
+ lib.DupTextSyms = append(lib.DupTextSyms, s)
}
- continue
}
+ continue
}
- s := l.Syms[istart+Sym(i)]
+ if isdup {
+ continue // come from a different object
+ }
+ s := l.Syms[gi]
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
+ size := osym.Siz()
// Symbol data
s.P = r.Data(i)
@@ -1079,59 +2353,29 @@ func loadObjFull(l *Loader, r *oReader) {
// 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],
- }
- }
+ s.R = batch[:relocs.Count():relocs.Count()]
+ l.relocBatch = batch[relocs.Count():]
+ l.convertRelocations(&relocs, s, false)
// 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 {
+ auxs := r.Auxs2(i)
+ for j := range auxs {
+ a := &auxs[j]
+ switch a.Type() {
case goobj2.AuxGotype:
- typ := resolveSymRef(a.Sym)
+ typ := resolveSymRef(a.Sym())
if typ != nil {
s.Gotype = typ
}
case goobj2.AuxFuncdata:
- fdsyms = append(fdsyms, resolveSymRef(a.Sym))
+ fdsyms = append(fdsyms, resolveSymRef(a.Sym()))
case goobj2.AuxFuncInfo:
- if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
+ if a.Sym().PkgIdx != goobj2.PkgIdxSelf {
panic("funcinfo symbol not defined in current package")
}
- isym = int(a.Sym.SymIdx)
+ isym = int(a.Sym().SymIdx)
case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
// ignored for now
default:
@@ -1149,14 +2393,6 @@ func loadObjFull(l *Loader, r *oReader) {
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
}
@@ -1203,7 +2439,7 @@ func loadObjFull(l *Loader, r *oReader) {
info := goobj2.FuncInfo{}
info.Read(b)
- if info.NoSplit != 0 {
+ if osym.NoSplit() {
s.Attr |= sym.AttrNoSplit
}
if osym.ReflectMethod() {
@@ -1283,41 +2519,164 @@ func loadObjFull(l *Loader, r *oReader) {
}
}
-var emptyPkg = []byte(`"".`)
+// convertRelocations takes a vector of loader.Reloc relocations and
+// translates them into an equivalent set of sym.Reloc relocations on
+// the symbol "dst", performing fixups along the way for ABI aliases,
+// etc. It is assumed that the caller has pre-allocated the dst symbol
+// relocations slice. If 'strict' is set, then this method will
+// panic if it finds a relocation targeting a nil symbol.
+func (l *Loader) convertRelocations(src *Relocs, dst *sym.Symbol, strict bool) {
+ for j := range dst.R {
+ r := src.At2(j)
+ rs := r.Sym()
+ sz := r.Siz()
+ rt := r.Type()
+ if rt == objabi.R_METHODOFF {
+ if l.attrReachable.Has(rs) {
+ rt = objabi.R_ADDROFF
+ } else {
+ sz = 0
+ rs = 0
+ }
+ }
+ if rt == objabi.R_WEAKADDROFF && !l.attrReachable.Has(rs) {
+ rs = 0
+ sz = 0
+ }
+ if rs != 0 && l.Syms[rs] != nil && l.Syms[rs].Type == sym.SABIALIAS {
+ rsrelocs := l.Relocs(rs)
+ rs = rsrelocs.At2(0).Sym()
+ }
+ if strict && rs != 0 && l.Syms[rs] == nil && rt != objabi.R_USETYPE {
+ panic("nil reloc target in convertRelocations")
+ }
+ dst.R[j] = sym.Reloc{
+ Off: r.Off(),
+ Siz: sz,
+ Type: rt,
+ Add: r.Add(),
+ Sym: l.Syms[rs],
+ }
+ }
+}
-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
+// UndefinedRelocTargets iterates through the global symbol index
+// space, looking for symbols with relocations targeting undefined
+// references. The linker's loadlib method uses this to determine if
+// there are unresolved references to functions in system libraries
+// (for example, libgcc.a), presumably due to CGO code. Return
+// value is a list of loader.Sym's corresponding to the undefined
+// cross-refs. The "limit" param controls the maximum number of
+// results returned; if "limit" is -1, then all undefs are returned.
+func (l *Loader) UndefinedRelocTargets(limit int) []Sym {
+ result := []Sym{}
+ for si := Sym(1); si < Sym(len(l.objSyms)); si++ {
+ relocs := l.Relocs(si)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At2(ri)
+ rs := r.Sym()
+ if rs != 0 && l.SymType(rs) == sym.SXREF && l.RawSymName(rs) != ".got" {
+ result = append(result, rs)
+ if limit != -1 && len(result) >= limit {
+ break
+ }
+ }
+ }
}
- e := bytes.IndexByte(p, 0)
- if e == -1 {
- return p, -1
+ return result
+}
+
+// AssignTextSymbolOrder populates the Textp2 slices within each
+// library and compilation unit, insuring that packages are laid down
+// in dependency order (internal first, then everything else). Return value
+// is a slice of all text syms.
+func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym {
+
+ // Library Textp2 lists should be empty at this point.
+ for _, lib := range libs {
+ if len(lib.Textp2) != 0 {
+ panic("expected empty Textp2 slice for library")
+ }
+ if len(lib.DupTextSyms2) != 0 {
+ panic("expected empty DupTextSyms2 slice for library")
+ }
}
- if !bytes.Contains(p[:e], emptyPkg) {
- return p, -1
+
+ // Used to record which dupok symbol we've assigned to a unit.
+ // Can't use the onlist attribute here because it will need to
+ // clear for the later assignment of the sym.Symbol to a unit.
+ // NB: we can convert to using onList once we no longer have to
+ // call the regular addToTextp.
+ assignedToUnit := MakeBitmap(l.NSym() + 1)
+
+ // Start off textp2 with reachable external syms.
+ textp2 := []Sym{}
+ for _, sym := range extsyms {
+ if !l.attrReachable.Has(sym) {
+ continue
+ }
+ textp2 = append(textp2, sym)
}
- 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
+ // Walk through all text symbols from Go object files and append
+ // them to their corresponding library's textp2 list.
+ for _, o := range l.objs[1:] {
+ r := o.r
+ lib := r.unit.Lib
+ for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
+ gi := l.toGlobal(r, i)
+ if !l.attrReachable.Has(gi) {
+ continue
+ }
+ osym := r.Sym2(i)
+ st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+ if st != sym.STEXT {
+ continue
+ }
+ dupok := osym.Dupok()
+ if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+ // A dupok text symbol is resolved to another package.
+ // We still need to record its presence in the current
+ // package, as the trampoline pass expects packages
+ // are laid out in dependency order.
+ lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi))
+ continue // symbol in different object
+ }
+ if dupok {
+ lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi))
+ }
+
+ lib.Textp2 = append(lib.Textp2, sym.LoaderSym(gi))
+ }
}
- 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)
+
+ // Now redo the assignment of text symbols to libs/units.
+ for _, doInternal := range [2]bool{true, false} {
+ for idx, lib := range libs {
+ if intlibs[idx] != doInternal {
+ continue
+ }
+ libtextp2 := []sym.LoaderSym{}
+ lists := [2][]sym.LoaderSym{lib.Textp2, lib.DupTextSyms2}
+ for _, list := range lists {
+ for _, s := range list {
+ sym := Sym(s)
+ if l.attrReachable.Has(sym) && !assignedToUnit.Has(sym) {
+ libtextp2 = append(libtextp2, s)
+ textp2 = append(textp2, sym)
+ unit := l.SymUnit(sym)
+ if unit != nil {
+ unit.Textp2 = append(unit.Textp2, s)
+ assignedToUnit.Set(sym)
+ }
+ }
+ }
+ }
+ lib.Textp2 = libtextp2
}
}
+
+ return textp2
}
// For debugging.
@@ -1328,18 +2687,24 @@ func (l *Loader) Dump() {
fmt.Println(obj.i, obj.r.unit.Lib)
}
}
+ fmt.Println("extStart:", l.extStart)
+ fmt.Println("Nsyms:", len(l.objSyms))
fmt.Println("syms")
- for i, s := range l.Syms {
- if i == 0 {
- continue
+ for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
+ pi := interface{}("")
+ if l.IsExternal(i) {
+ pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
+ }
+ var s *sym.Symbol
+ if int(i) < len(l.Syms) {
+ s = l.Syms[i]
}
if s != nil {
- fmt.Println(i, s, s.Type)
+ fmt.Println(i, s, s.Type, pi)
} else {
- fmt.Println(i, l.SymName(Sym(i)), "<not loaded>")
+ fmt.Println(i, l.SymName(i), "<not loaded>", pi)
}
}
- fmt.Println("overwrite:", l.overwrite)
fmt.Println("symsByName")
for name, i := range l.symsByName[0] {
fmt.Println(i, name, 0)
@@ -1347,4 +2712,9 @@ func (l *Loader) Dump() {
for name, i := range l.symsByName[1] {
fmt.Println(i, name, 1)
}
+ fmt.Println("payloads:")
+ for i := range l.payloads {
+ pp := l.payloads[i]
+ fmt.Println(i, pp.name, pp.ver, pp.kind)
+ }
}
diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go
new file mode 100644
index 0000000000..b2f823d17e
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader_test.go
@@ -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 loader
+
+import (
+ "bytes"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "fmt"
+ "testing"
+)
+
+// dummyAddSym adds the named symbol to the loader as if it had been
+// read from a Go object file. Note that it allocates a global
+// index without creating an associated object reader, so one can't
+// do anything interesting with this symbol (such as look at its
+// data or relocations).
+func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym {
+ idx := len(ldr.objSyms)
+ s, ok := ldr.AddSym(name, 0, or, idx, nonPkgDef, false, sym.SRODATA)
+ if !ok {
+ t.Errorf("AddrSym failed for '" + name + "'")
+ }
+ return s
+}
+
+func TestAddMaterializedSymbol(t *testing.T) {
+ edummy := func(s *sym.Symbol, str string, off int) {}
+ ldr := NewLoader(0, edummy)
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Create some syms from a dummy object file symbol to get things going.
+ ts1 := addDummyObjSym(t, ldr, or, "type.uint8")
+ ts2 := addDummyObjSym(t, ldr, or, "mumble")
+ ts3 := addDummyObjSym(t, ldr, or, "type.string")
+
+ // Create some external symbols.
+ es1 := ldr.LookupOrCreateSym("extnew1", 0)
+ if es1 == 0 {
+ t.Fatalf("LookupOrCreateSym failed for extnew1")
+ }
+ es1x := ldr.LookupOrCreateSym("extnew1", 0)
+ if es1x != es1 {
+ t.Fatalf("LookupOrCreateSym lookup: expected %d got %d for second lookup", es1, es1x)
+ }
+ es2 := ldr.LookupOrCreateSym("go.info.type.uint8", 0)
+ if es2 == 0 {
+ t.Fatalf("LookupOrCreateSym failed for go.info.type.uint8")
+ }
+ // Create a nameless symbol
+ es3 := ldr.CreateStaticSym("")
+ if es3 == 0 {
+ t.Fatalf("CreateStaticSym failed for nameless sym")
+ }
+
+ // Grab symbol builder pointers
+ sb1 := ldr.MakeSymbolUpdater(es1)
+ sb2 := ldr.MakeSymbolUpdater(es2)
+ sb3 := ldr.MakeSymbolUpdater(es3)
+
+ // Suppose we create some more symbols, which triggers a grow.
+ // Make sure the symbol builder's payload pointer is valid,
+ // even across a grow.
+ ldr.growSyms(9999)
+
+ // Check get/set symbol type
+ es3typ := sb3.Type()
+ if es3typ != sym.Sxxx {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ)
+ }
+ sb3.SetType(sym.SRODATA)
+ es3typ = sb3.Type()
+ if es3typ != sym.SRODATA {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+ }
+ es3typ = ldr.SymType(es3)
+ if es3typ != sym.SRODATA {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+ }
+
+ // New symbols should not initially be reachable.
+ if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) {
+ t.Errorf("newly materialized symbols should not be reachable")
+ }
+
+ // ... however it should be possible to set/unset their reachability.
+ ldr.SetAttrReachable(es3, true)
+ if !ldr.AttrReachable(es3) {
+ t.Errorf("expected reachable symbol after update")
+ }
+ ldr.SetAttrReachable(es3, false)
+ if ldr.AttrReachable(es3) {
+ t.Errorf("expected unreachable symbol after update")
+ }
+
+ // Test expansion of attr bitmaps
+ for idx := 0; idx < 36; idx++ {
+ es := ldr.LookupOrCreateSym(fmt.Sprintf("zext%d", idx), 0)
+ if ldr.AttrOnList(es) {
+ t.Errorf("expected OnList after creation")
+ }
+ ldr.SetAttrOnList(es, true)
+ if !ldr.AttrOnList(es) {
+ t.Errorf("expected !OnList after update")
+ }
+ if ldr.AttrDuplicateOK(es) {
+ t.Errorf("expected DupOK after creation")
+ }
+ ldr.SetAttrDuplicateOK(es, true)
+ if !ldr.AttrDuplicateOK(es) {
+ t.Errorf("expected !DupOK after update")
+ }
+ }
+
+ sb1 = ldr.MakeSymbolUpdater(es1)
+ sb2 = ldr.MakeSymbolUpdater(es2)
+
+ // Get/set a few other attributes
+ if ldr.AttrVisibilityHidden(es3) {
+ t.Errorf("expected initially not hidden")
+ }
+ ldr.SetAttrVisibilityHidden(es3, true)
+ if !ldr.AttrVisibilityHidden(es3) {
+ t.Errorf("expected hidden after update")
+ }
+
+ // Test get/set symbol value.
+ toTest := []Sym{ts2, es3}
+ for i, s := range toTest {
+ if v := ldr.SymValue(s); v != 0 {
+ t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v)
+ }
+ nv := int64(i + 101)
+ ldr.SetSymValue(s, nv)
+ if v := ldr.SymValue(s); v != nv {
+ t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v)
+ }
+ }
+
+ // Check/set alignment
+ es3al := ldr.SymAlign(es3)
+ if es3al != 0 {
+ t.Errorf("SymAlign(es3): expected 0, got %d", es3al)
+ }
+ ldr.SetSymAlign(es3, 128)
+ es3al = ldr.SymAlign(es3)
+ if es3al != 128 {
+ t.Errorf("SymAlign(es3): expected 128, got %d", es3al)
+ }
+
+ // Add some relocations to the new symbols.
+ r1 := Reloc{0, 1, objabi.R_ADDR, 0, ts1}
+ r2 := Reloc{3, 8, objabi.R_CALL, 0, ts2}
+ r3 := Reloc{7, 1, objabi.R_USETYPE, 0, ts3}
+ sb1.AddReloc(r1)
+ sb1.AddReloc(r2)
+ sb2.AddReloc(r3)
+
+ // Add some data to the symbols.
+ d1 := []byte{1, 2, 3}
+ d2 := []byte{4, 5, 6, 7}
+ sb1.AddBytes(d1)
+ sb2.AddBytes(d2)
+
+ // Now invoke the usual loader interfaces to make sure
+ // we're getting the right things back for these symbols.
+ // First relocations...
+ expRel := [][]Reloc{[]Reloc{r1, r2}, []Reloc{r3}}
+ for k, sb := range []*SymbolBuilder{sb1, sb2} {
+ rsl := sb.Relocs()
+ exp := expRel[k]
+ if !sameRelocSlice(&rsl, exp) {
+ t.Errorf("expected relocs %v, got %v", exp, rsl)
+ }
+ }
+
+ // ... then data.
+ dat := sb2.Data()
+ if bytes.Compare(dat, d2) != 0 {
+ t.Errorf("expected es2 data %v, got %v", d2, dat)
+ }
+
+ // Nameless symbol should still be nameless.
+ es3name := ldr.RawSymName(es3)
+ if "" != es3name {
+ t.Errorf("expected es3 name of '', got '%s'", es3name)
+ }
+
+ // Read value of materialized symbol.
+ es1val := sb1.Value()
+ if 0 != es1val {
+ t.Errorf("expected es1 value of 0, got %v", es1val)
+ }
+
+ // Test other misc methods
+ irm := ldr.IsReflectMethod(es1)
+ if 0 != es1val {
+ t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm)
+ }
+
+ // Writing data to a materialized symbol should mark it reachable.
+ if !sb1.Reachable() || !sb2.Reachable() {
+ t.Fatalf("written-to materialized symbols should be reachable")
+ }
+}
+
+func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool {
+ if s1.Count() != len(s2) {
+ return false
+ }
+ for i := 0; i < s1.Count(); i++ {
+ r1 := s1.At2(i)
+ r2 := &s2[i]
+ if r1.Sym() != r2.Sym ||
+ r1.Type() != r2.Type ||
+ r1.Off() != r2.Off ||
+ r1.Add() != r2.Add ||
+ r1.Siz() != r2.Size {
+ return false
+ }
+ }
+ return true
+}
+
+type addFunc func(l *Loader, s Sym, s2 Sym) Sym
+
+func TestAddDataMethods(t *testing.T) {
+ edummy := func(s *sym.Symbol, str string, off int) {}
+ ldr := NewLoader(0, edummy)
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Populate loader with some symbols.
+ addDummyObjSym(t, ldr, or, "type.uint8")
+ ldr.LookupOrCreateSym("hello", 0)
+
+ arch := sys.ArchAMD64
+ var testpoints = []struct {
+ which string
+ addDataFunc addFunc
+ expData []byte
+ expKind sym.SymKind
+ expRel []Reloc
+ }{
+ {
+ which: "AddUint8",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUint8('a')
+ return s
+ },
+ expData: []byte{'a'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "AddUintXX",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUintXX(arch, 25185, 2)
+ return s
+ },
+ expData: []byte{'a', 'b'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "SetUint8",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUint8('a')
+ sb.AddUint8('b')
+ sb.SetUint8(arch, 1, 'c')
+ return s
+ },
+ expData: []byte{'a', 'c'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "AddString",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.Addstring("hello")
+ return s
+ },
+ expData: []byte{'h', 'e', 'l', 'l', 'o', 0},
+ expKind: sym.SNOPTRDATA,
+ },
+ {
+ which: "AddAddrPlus",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddAddrPlus(arch, s2, 3)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{Reloc{Type: objabi.R_ADDR, Size: 8, Add: 3, Sym: 6}},
+ },
+ {
+ which: "AddAddrPlus4",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddAddrPlus4(arch, s2, 3)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{Reloc{Type: objabi.R_ADDR, Size: 4, Add: 3, Sym: 7}},
+ },
+ {
+ which: "AddCURelativeAddrPlus",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddCURelativeAddrPlus(arch, s2, 7)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{Reloc{Type: objabi.R_ADDRCUOFF, Size: 8, Add: 7, Sym: 8}},
+ },
+ }
+
+ var pmi Sym
+ for k, tp := range testpoints {
+ name := fmt.Sprintf("new%d", k+1)
+ mi := ldr.LookupOrCreateSym(name, 0)
+ if mi == 0 {
+ t.Fatalf("LookupOrCreateSym failed for '" + name + "'")
+ }
+ mi = tp.addDataFunc(ldr, mi, pmi)
+ if ldr.SymType(mi) != tp.expKind {
+ t.Errorf("testing Loader.%s: expected kind %s got %s",
+ tp.which, tp.expKind, ldr.SymType(mi))
+ }
+ if bytes.Compare(ldr.Data(mi), tp.expData) != 0 {
+ t.Errorf("testing Loader.%s: expected data %v got %v",
+ tp.which, tp.expData, ldr.Data(mi))
+ }
+ if !ldr.AttrReachable(mi) {
+ t.Fatalf("testing Loader.%s: sym updated should be reachable", tp.which)
+ }
+ relocs := ldr.Relocs(mi)
+ if !sameRelocSlice(&relocs, tp.expRel) {
+ t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v",
+ tp.which, relocs, tp.expRel)
+ }
+ pmi = mi
+ }
+}
+
+func TestOuterSub(t *testing.T) {
+ edummy := func(s *sym.Symbol, str string, off int) {}
+ ldr := NewLoader(0, edummy)
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Populate loader with some symbols.
+ addDummyObjSym(t, ldr, or, "type.uint8")
+ es1 := ldr.LookupOrCreateSym("outer", 0)
+ es2 := ldr.LookupOrCreateSym("sub1", 0)
+ es3 := ldr.LookupOrCreateSym("sub2", 0)
+ es4 := ldr.LookupOrCreateSym("sub3", 0)
+ es5 := ldr.LookupOrCreateSym("sub4", 0)
+ es6 := ldr.LookupOrCreateSym("sub5", 0)
+
+ // Should not have an outer sym initially
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("es1 outer sym set ")
+ }
+ if ldr.SubSym(es2) != 0 {
+ t.Errorf("es2 outer sym set ")
+ }
+
+ // Establish first outer/sub relationship
+ ldr.PrependSub(es1, es2)
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+ }
+ if ldr.OuterSym(es2) != es1 {
+ t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+ }
+ if ldr.SubSym(es1) != es2 {
+ t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2)
+ }
+ if ldr.SubSym(es2) != 0 {
+ t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0)
+ }
+
+ // Establish second outer/sub relationship
+ ldr.PrependSub(es1, es3)
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+ }
+ if ldr.OuterSym(es2) != es1 {
+ t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+ }
+ if ldr.OuterSym(es3) != es1 {
+ t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1)
+ }
+ if ldr.SubSym(es1) != es3 {
+ t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3)
+ }
+ if ldr.SubSym(es3) != es2 {
+ t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2)
+ }
+
+ // Some more
+ ldr.PrependSub(es1, es4)
+ ldr.PrependSub(es1, es5)
+ ldr.PrependSub(es1, es6)
+
+ // Set values.
+ ldr.SetSymValue(es2, 7)
+ ldr.SetSymValue(es3, 1)
+ ldr.SetSymValue(es4, 13)
+ ldr.SetSymValue(es5, 101)
+ ldr.SetSymValue(es6, 3)
+
+ // Sort
+ news := ldr.SortSub(es1)
+ if news != es3 {
+ t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3)
+ }
+ pv := int64(-1)
+ count := 0
+ for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) {
+ v := ldr.SymValue(ss)
+ if v <= pv {
+ t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d",
+ ss, v, pv)
+ }
+ pv = v
+ count++
+ }
+ if count != 5 {
+ t.Errorf("expected %d in sub list got %d", 5, count)
+ }
+}
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
new file mode 100644
index 0000000000..0ce5c6bde8
--- /dev/null
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -0,0 +1,349 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package loader
+
+import (
+ "cmd/internal/goobj2"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "fmt"
+)
+
+// SymbolBuilder is a helper designed to help with the construction
+// of new symbol contents.
+type SymbolBuilder struct {
+ *extSymPayload // points to payload being updated
+ symIdx Sym // index of symbol being updated/constructed
+ l *Loader // loader
+}
+
+// MakeSymbolBuilder creates a symbol builder for use in constructing
+// an entirely new symbol.
+func (l *Loader) MakeSymbolBuilder(name string) *SymbolBuilder {
+ // for now assume that any new sym is intended to be static
+ symIdx := l.CreateStaticSym(name)
+ if l.Syms[symIdx] != nil {
+ panic("can't build if sym.Symbol already present")
+ }
+ sb := &SymbolBuilder{l: l, symIdx: symIdx}
+ sb.extSymPayload = l.getPayload(symIdx)
+ return sb
+}
+
+// MakeSymbolUpdater creates a symbol builder helper for an existing
+// symbol 'symIdx'. If 'symIdx' is not an external symbol, then create
+// a clone of it (copy name, properties, etc) fix things up so that
+// the lookup tables and caches point to the new version, not the old
+// version.
+func (l *Loader) MakeSymbolUpdater(symIdx Sym) *SymbolBuilder {
+ if symIdx == 0 {
+ panic("can't update the null symbol")
+ }
+ if !l.IsExternal(symIdx) {
+ // Create a clone with the same name/version/kind etc.
+ l.cloneToExternal(symIdx)
+ }
+ // Now that we're doing phase 2 DWARF generation using the loader
+ // but before the wavefront has reached dodata(), we can't have this
+ // assertion here. Commented out for now.
+ if false {
+ if l.Syms[symIdx] != nil {
+ panic(fmt.Sprintf("can't build if sym.Symbol %q already present", l.RawSymName(symIdx)))
+ }
+ }
+
+ // Construct updater and return.
+ sb := &SymbolBuilder{l: l, symIdx: symIdx}
+ sb.extSymPayload = l.getPayload(symIdx)
+ return sb
+}
+
+// CreateSymForUpdate creates a symbol with given name and version,
+// returns a CreateSymForUpdate for update. If the symbol already
+// exists, it will update in-place.
+func (l *Loader) CreateSymForUpdate(name string, version int) *SymbolBuilder {
+ return l.MakeSymbolUpdater(l.LookupOrCreateSym(name, version))
+}
+
+// Getters for properties of the symbol we're working on.
+
+func (sb *SymbolBuilder) Sym() Sym { return sb.symIdx }
+func (sb *SymbolBuilder) Name() string { return sb.name }
+func (sb *SymbolBuilder) Version() int { return sb.ver }
+func (sb *SymbolBuilder) Type() sym.SymKind { return sb.kind }
+func (sb *SymbolBuilder) Size() int64 { return sb.size }
+func (sb *SymbolBuilder) Data() []byte { return sb.data }
+func (sb *SymbolBuilder) Value() int64 { return sb.l.SymValue(sb.symIdx) }
+func (sb *SymbolBuilder) Align() int32 { return sb.l.SymAlign(sb.symIdx) }
+func (sb *SymbolBuilder) Localentry() uint8 { return sb.l.SymLocalentry(sb.symIdx) }
+func (sb *SymbolBuilder) OnList() bool { return sb.l.AttrOnList(sb.symIdx) }
+func (sb *SymbolBuilder) External() bool { return sb.l.AttrExternal(sb.symIdx) }
+func (sb *SymbolBuilder) Extname() string { return sb.l.SymExtname(sb.symIdx) }
+func (sb *SymbolBuilder) CgoExportDynamic() bool { return sb.l.AttrCgoExportDynamic(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimplib() string { return sb.l.SymDynimplib(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimpvers() string { return sb.l.SymDynimpvers(sb.symIdx) }
+func (sb *SymbolBuilder) SubSym() Sym { return sb.l.SubSym(sb.symIdx) }
+func (sb *SymbolBuilder) GoType() Sym { return sb.l.SymGoType(sb.symIdx) }
+func (sb *SymbolBuilder) VisibilityHidden() bool { return sb.l.AttrVisibilityHidden(sb.symIdx) }
+
+// Setters for symbol properties.
+
+func (sb *SymbolBuilder) SetType(kind sym.SymKind) { sb.kind = kind }
+func (sb *SymbolBuilder) SetSize(size int64) { sb.size = size }
+func (sb *SymbolBuilder) SetData(data []byte) { sb.data = data }
+func (sb *SymbolBuilder) SetOnList(v bool) { sb.l.SetAttrOnList(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetExternal(v bool) { sb.l.SetAttrExternal(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetValue(v int64) { sb.l.SetSymValue(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetAlign(align int32) { sb.l.SetSymAlign(sb.symIdx, align) }
+func (sb *SymbolBuilder) SetLocalentry(value uint8) { sb.l.SetSymLocalentry(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetExtname(value string) { sb.l.SetSymExtname(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimplib(value string) { sb.l.SetSymDynimplib(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimpvers(value string) { sb.l.SetSymDynimpvers(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetPlt(value int32) { sb.l.SetPlt(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetGot(value int32) { sb.l.SetGot(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetSpecial(value bool) { sb.l.SetAttrSpecial(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetVisibilityHidden(value bool) {
+ sb.l.SetAttrVisibilityHidden(sb.symIdx, value)
+}
+
+func (sb *SymbolBuilder) SetNotInSymbolTable(value bool) {
+ sb.l.SetAttrNotInSymbolTable(sb.symIdx, value)
+}
+
+func (sb *SymbolBuilder) AddBytes(data []byte) {
+ sb.setReachable()
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ sb.data = append(sb.data, data...)
+ sb.size = int64(len(sb.data))
+}
+
+func (sb *SymbolBuilder) Relocs() Relocs {
+ return sb.l.Relocs(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetRelocs(rslice []Reloc) {
+ n := len(rslice)
+ if cap(sb.relocs) < n {
+ sb.relocs = make([]goobj2.Reloc2, n)
+ sb.reltypes = make([]objabi.RelocType, n)
+ } else {
+ sb.relocs = sb.relocs[:n]
+ sb.reltypes = sb.reltypes[:n]
+ }
+ for i := range rslice {
+ sb.SetReloc(i, rslice[i])
+ }
+}
+
+func (sb *SymbolBuilder) AddReloc(r Reloc) {
+ // Populate a goobj2.Reloc from external reloc record.
+ var b goobj2.Reloc2
+ b.Set(r.Off, r.Size, 0, r.Add, goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(r.Sym)})
+ sb.relocs = append(sb.relocs, b)
+ sb.reltypes = append(sb.reltypes, r.Type)
+}
+
+// Update the j-th relocation in place.
+func (sb *SymbolBuilder) SetReloc(j int, r Reloc) {
+ // Populate a goobj2.Reloc from external reloc record.
+ sb.relocs[j].Set(r.Off, r.Size, 0, r.Add, goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(r.Sym)})
+ sb.reltypes[j] = r.Type
+}
+
+func (sb *SymbolBuilder) Reachable() bool {
+ return sb.l.AttrReachable(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReachable(v bool) {
+ sb.l.SetAttrReachable(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) setReachable() {
+ sb.SetReachable(true)
+}
+
+func (sb *SymbolBuilder) ReadOnly() bool {
+ return sb.l.AttrReadOnly(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReadOnly(v bool) {
+ sb.l.SetAttrReadOnly(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) DuplicateOK() bool {
+ return sb.l.AttrDuplicateOK(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetDuplicateOK(v bool) {
+ sb.l.SetAttrDuplicateOK(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) Outer() Sym {
+ return sb.l.OuterSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) Sub() Sym {
+ return sb.l.SubSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SortSub() {
+ sb.l.SortSub(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) PrependSub(sub Sym) {
+ sb.l.PrependSub(sb.symIdx, sub)
+}
+
+func (sb *SymbolBuilder) AddUint8(v uint8) int64 {
+ off := sb.size
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ sb.setReachable()
+ sb.size++
+ sb.data = append(sb.data, v)
+ return off
+}
+
+func (sb *SymbolBuilder) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 {
+ off := sb.size
+ sb.setReachable()
+ sb.setUintXX(arch, off, v, int64(wid))
+ return off
+}
+
+func (sb *SymbolBuilder) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ if sb.size < off+wid {
+ sb.size = off + wid
+ sb.Grow(sb.size)
+ }
+
+ switch wid {
+ case 1:
+ sb.data[off] = uint8(v)
+ case 2:
+ arch.ByteOrder.PutUint16(sb.data[off:], uint16(v))
+ case 4:
+ arch.ByteOrder.PutUint32(sb.data[off:], uint32(v))
+ case 8:
+ arch.ByteOrder.PutUint64(sb.data[off:], v)
+ }
+
+ return off + wid
+}
+
+func (sb *SymbolBuilder) AddUint16(arch *sys.Arch, v uint16) int64 {
+ return sb.AddUintXX(arch, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) AddUint32(arch *sys.Arch, v uint32) int64 {
+ return sb.AddUintXX(arch, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) AddUint64(arch *sys.Arch, v uint64) int64 {
+ return sb.AddUintXX(arch, v, 8)
+}
+
+func (sb *SymbolBuilder) AddUint(arch *sys.Arch, v uint64) int64 {
+ return sb.AddUintXX(arch, v, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) SetUint8(arch *sys.Arch, r int64, v uint8) int64 {
+ sb.setReachable()
+ return sb.setUintXX(arch, r, uint64(v), 1)
+}
+
+func (sb *SymbolBuilder) SetUint16(arch *sys.Arch, r int64, v uint16) int64 {
+ sb.setReachable()
+ return sb.setUintXX(arch, r, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) SetUint32(arch *sys.Arch, r int64, v uint32) int64 {
+ sb.setReachable()
+ return sb.setUintXX(arch, r, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) SetUint(arch *sys.Arch, r int64, v uint64) int64 {
+ sb.setReachable()
+ return sb.setUintXX(arch, r, v, int64(arch.PtrSize))
+}
+
+func (sb *SymbolBuilder) Addstring(str string) int64 {
+ sb.setReachable()
+ if sb.kind == 0 {
+ sb.kind = sym.SNOPTRDATA
+ }
+ r := sb.size
+ if sb.name == ".shstrtab" {
+ // FIXME: find a better mechanism for this
+ sb.l.elfsetstring(nil, str, int(r))
+ }
+ sb.data = append(sb.data, str...)
+ sb.data = append(sb.data, 0)
+ sb.size = int64(len(sb.data))
+ return r
+}
+
+func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ i := sb.size
+
+ sb.size += int64(rsize)
+ sb.Grow(sb.size)
+
+ var r Reloc
+ r.Sym = tgt
+ r.Off = int32(i)
+ r.Size = uint8(rsize)
+ r.Type = typ
+ r.Add = add
+ sb.AddReloc(r)
+
+ return i + int64(r.Size)
+}
+
+// Add a symbol reference (relocation) with given type, addend, and size
+// (the most generic form).
+func (sb *SymbolBuilder) AddSymRef(arch *sys.Arch, tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+ sb.setReachable()
+ return sb.addSymRef(tgt, add, typ, rsize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ sb.setReachable()
+ return sb.addSymRef(tgt, add, objabi.R_ADDR, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus4(arch *sys.Arch, tgt Sym, add int64) int64 {
+ sb.setReachable()
+ return sb.addSymRef(tgt, add, objabi.R_ADDR, 4)
+}
+
+func (sb *SymbolBuilder) AddAddr(arch *sys.Arch, tgt Sym) int64 {
+ return sb.AddAddrPlus(arch, tgt, 0)
+}
+
+func (sb *SymbolBuilder) AddPCRelPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ sb.setReachable()
+ return sb.addSymRef(tgt, add, objabi.R_PCREL, 4)
+}
+
+func (sb *SymbolBuilder) AddCURelativeAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ sb.setReachable()
+ return sb.addSymRef(tgt, add, objabi.R_ADDRCUOFF, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddSize(arch *sys.Arch, tgt Sym) int64 {
+ sb.setReachable()
+ return sb.addSymRef(tgt, 0, objabi.R_SIZE, arch.PtrSize)
+}
diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go
index 85a1ebc631..d1ff82f9eb 100644
--- a/src/cmd/link/internal/loadmacho/ldmacho.go
+++ b/src/cmd/link/internal/loadmacho/ldmacho.go
@@ -14,7 +14,6 @@ import (
"cmd/link/internal/sym"
"encoding/binary"
"fmt"
- "io"
"sort"
)
@@ -101,7 +100,7 @@ type ldMachoSect struct {
flags uint32
res1 uint32
res2 uint32
- sym *sym.Symbol
+ sym loader.Sym
rel []ldMachoRel
}
@@ -132,7 +131,7 @@ type ldMachoSym struct {
desc uint16
kind int8
value uint64
- sym *sym.Symbol
+ sym loader.Sym
}
type ldMachoDysymtab struct {
@@ -320,10 +319,9 @@ func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int {
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 {
+ buf, _, err := m.f.Slice(uint64(sect.nreloc * 8))
+ if err != nil {
return -1
}
for i := uint32(0); i < sect.nreloc; i++ {
@@ -364,10 +362,9 @@ func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int {
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 {
+ p, _, err := m.f.Slice(uint64(n * 4))
+ if err != nil {
return -1
}
@@ -383,9 +380,9 @@ func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
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 {
+ strbuf, _, err := m.f.Slice(uint64(symtab.strsize))
+ if err != nil {
return -1
}
@@ -394,9 +391,9 @@ func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
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 {
+ symbuf, _, err := m.f.Slice(uint64(n))
+ if err != nil {
return -1
}
sym := make([]ldMachoSym, symtab.nsym)
@@ -424,28 +421,17 @@ func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
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.
+// 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) {
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
}
base := f.Offset()
- var hdr [7 * 4]uint8
- if _, err := io.ReadFull(f, hdr[:]); err != nil {
+ hdr, _, err := f.Slice(7 * 4)
+ if err != nil {
return errorf("reading hdr: %v", err)
}
@@ -499,8 +485,8 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
}
m.cmd = make([]ldMachoCmd, ncmd)
- cmdp := make([]byte, cmdsz)
- if _, err := io.ReadFull(f, cmdp); err != nil {
+ cmdp, _, err := f.Slice(uint64(cmdsz))
+ if err != nil {
return errorf("reading cmds: %v", err)
}
@@ -559,8 +545,8 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
}
f.MustSeek(m.base+int64(c.seg.fileoff), 0)
- dat := make([]byte, c.seg.filesz)
- if _, err := io.ReadFull(f, dat); err != nil {
+ dat, readOnly, err := f.Slice(uint64(c.seg.filesz))
+ if err != nil {
return errorf("cannot load object data: %v", err)
}
@@ -573,30 +559,32 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
continue
}
name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
- s := lookup(name, localSymVersion)
- if s.Type != 0 {
+ s := l.LookupOrCreateSym(name, localSymVersion)
+ bld := l.MakeSymbolUpdater(s)
+ if bld.Type() != 0 {
return errorf("duplicate %s/%s", sect.segname, sect.name)
}
if sect.flags&0xff == 1 { // S_ZEROFILL
- s.P = make([]byte, sect.size)
+ bld.SetData(make([]byte, sect.size))
} else {
- s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size]
+ bld.SetReadOnly(readOnly)
+ bld.SetData(dat[sect.addr-c.seg.vmaddr:][:sect.size])
}
- s.Size = int64(len(s.P))
+ bld.SetSize(int64(len(bld.Data())))
if sect.segname == "__TEXT" {
if sect.name == "__text" {
- s.Type = sym.STEXT
+ bld.SetType(sym.STEXT)
} else {
- s.Type = sym.SRODATA
+ bld.SetType(sym.SRODATA)
}
} else {
if sect.name == "__bss" {
- s.Type = sym.SNOPTRBSS
- s.P = s.P[:0]
+ bld.SetType(sym.SNOPTRBSS)
+ bld.SetData(nil)
} else {
- s.Type = sym.SNOPTRDATA
+ bld.SetType(sym.SNOPTRDATA)
}
}
@@ -621,12 +609,12 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
if machsym.type_&N_EXT == 0 {
v = localSymVersion
}
- s := lookup(name, v)
+ s := l.LookupOrCreateSym(name, v)
if machsym.type_&N_EXT == 0 {
- s.Attr |= sym.AttrDuplicateOK
+ l.SetAttrDuplicateOK(s, true)
}
if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
- s.Attr |= sym.AttrDuplicateOK
+ l.SetAttrDuplicateOK(s, true)
}
machsym.sym = s
if machsym.sectnum == 0 { // undefined
@@ -637,35 +625,32 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
}
sect := &c.seg.sect[machsym.sectnum-1]
+ bld := l.MakeSymbolUpdater(s)
outer := sect.sym
- if outer == nil {
+ if outer == 0 {
continue // ignore reference to invalid section
}
- if s.Outer != nil {
- if s.Attr.DuplicateOK() {
+ if osym := l.OuterSym(s); osym != 0 {
+ if l.AttrDuplicateOK(s) {
continue
}
- return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name)
+ return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym))
}
- 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
+ bld.SetType(l.SymType(outer))
+ l.PrependSub(outer, s)
+
+ bld.SetValue(int64(machsym.value - sect.addr))
+ if !l.AttrCgoExportDynamic(s) {
+ bld.SetDynimplib("") // satisfy dynimport
}
- if outer.Type == sym.STEXT {
- if s.Attr.External() && !s.Attr.DuplicateOK() {
+ if l.SymType(outer) == sym.STEXT {
+ if bld.External() && !bld.DuplicateOK() {
return errorf("%v: duplicate symbol definition", s)
}
- s.Attr |= sym.AttrExternal
+ bld.SetExternal(true)
}
-
- machsym.sym = s
}
// Sort outer lists by address, adding to textp.
@@ -673,33 +658,37 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
for i := 0; uint32(i) < c.seg.nsect; i++ {
sect := &c.seg.sect[i]
s := sect.sym
- if s == nil {
+ if s == 0 {
continue
}
- if s.Sub != nil {
- s.Sub = sym.SortSub(s.Sub)
+ bld := l.MakeSymbolUpdater(s)
+ if bld.SubSym() != 0 {
+
+ bld.SortSub()
// 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
+ for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+ s1Bld := l.MakeSymbolUpdater(s1)
+ if sub := l.SubSym(s1); sub != 0 {
+ s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1))
} else {
- s1.Size = s.Value + s.Size - s1.Value
+ dlen := int64(len(l.Data(s)))
+ s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1))
}
}
}
- if s.Type == sym.STEXT {
- if s.Attr.OnList() {
- return errorf("symbol %s listed multiple times", s.Name)
+ if bld.Type() == sym.STEXT {
+ if bld.OnList() {
+ return errorf("symbol %s listed multiple times", bld.Name())
}
- s.Attr |= sym.AttrOnList
+ bld.SetOnList(true)
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)
+ for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+ if l.AttrOnList(s1) {
+ return errorf("symbol %s listed multiple times", l.RawSymName(s1))
}
- s1.Attr |= sym.AttrOnList
+ l.SetAttrOnList(s1, true)
textp = append(textp, s1)
}
}
@@ -709,14 +698,14 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
for i := 0; uint32(i) < c.seg.nsect; i++ {
sect := &c.seg.sect[i]
s := sect.sym
- if s == nil {
+ if s == 0 {
continue
}
macholoadrel(m, sect)
if sect.rel == nil {
continue
}
- r := make([]sym.Reloc, sect.nreloc)
+ r := make([]loader.Reloc, sect.nreloc)
rpi := 0
Reloc:
for j := uint32(0); j < sect.nreloc; j++ {
@@ -741,7 +730,7 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
return errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_))
}
- rp.Siz = rel.length
+ rp.Size = rel.length
rp.Off = int32(rel.addr)
// NOTE(rsc): I haven't worked out why (really when)
@@ -765,7 +754,7 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
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 {
+ if ks.sym != 0 {
rp.Sym = ks.sym
rp.Add += int64(uint64(rel.value) - ks.addr)
} else if ks.segname == "__IMPORT" && ks.name == "__pointers" {
@@ -805,11 +794,12 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
return errorf("unsupported scattered relocation: invalid address %#x", rel.addr)
}
- rp.Siz = rel.length
+ rp.Size = 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).
+ p := l.Data(s)
if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED {
// Calculate the addend as the offset into the section.
//
@@ -828,9 +818,9 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
// [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)
+ rp.Add = int64(uint64(int64(int32(e.Uint32(p[rp.Off:])))+int64(rp.Off)+4) - secaddr)
} else {
- rp.Add = int64(int32(e.Uint32(s.P[rp.Off:])))
+ rp.Add = int64(int32(e.Uint32(p[rp.Off:])))
}
// An unsigned internal relocation has a value offset
@@ -844,7 +834,7 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
// 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)
+ rp.Add += int64(rp.Off) + int64(rp.Size)
}
if rel.extrn == 0 {
if rel.symnum < 1 || rel.symnum > c.seg.nsect {
@@ -852,7 +842,7 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
}
rp.Sym = c.seg.sect[rel.symnum-1].sym
- if rp.Sym == nil {
+ if rp.Sym == 0 {
return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
}
@@ -874,9 +864,9 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym
rpi++
}
- sort.Sort(sym.RelocByOff(r[:rpi]))
- s.R = r
- s.R = s.R[:rpi]
+ sort.Sort(loader.RelocByOff(r[:rpi]))
+ sb := l.MakeSymbolUpdater(sect.sym)
+ sb.SetRelocs(r[:rpi])
}
return textp, nil
diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go
index 8b6aac338c..88819f3488 100644
--- a/src/cmd/link/internal/loadpe/ldpe.go
+++ b/src/cmd/link/internal/loadpe/ldpe.go
@@ -145,22 +145,26 @@ func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) {
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)
+// makeUpdater creates a loader.SymbolBuilder if one hasn't been created previously.
+// We use this to lazily make SymbolBuilders as we don't always need a builder, and creating them for all symbols might be an error.
+func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loader.SymbolBuilder {
+ if bld != nil {
+ return bld
}
- return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
+ bld = l.MakeSymbolUpdater(s)
+ return bld
}
-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.
+// 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)
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc loader.Sym, err error) {
+ lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) {
+ s := l.LookupOrCreateSym(name, version)
+ sb := l.MakeSymbolUpdater(s)
+ return sb, s
+ }
+ sectsyms := make(map[*pe.Section]loader.Sym)
sectdata := make(map[*pe.Section][]byte)
// Some input files are archives containing multiple of
@@ -172,7 +176,7 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
// 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
+ return nil, 0, err
}
defer f.Close()
@@ -191,34 +195,34 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
}
name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
- s := lookup(name, localSymVersion)
+ bld, s := lookup(name, localSymVersion)
switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
- s.Type = sym.SRODATA
+ bld.SetType(sym.SRODATA)
case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss
- s.Type = sym.SNOPTRBSS
+ bld.SetType(sym.SNOPTRBSS)
case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data
- s.Type = sym.SNOPTRDATA
+ bld.SetType(sym.SNOPTRDATA)
case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text
- s.Type = sym.STEXT
+ bld.SetType(sym.STEXT)
default:
- return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
+ return nil, 0, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
}
- if s.Type != sym.SNOPTRBSS {
+ if bld.Type() != sym.SNOPTRBSS {
data, err := sect.Data()
if err != nil {
- return nil, nil, err
+ return nil, 0, err
}
sectdata[sect] = data
- s.P = data
+ bld.SetData(data)
}
- s.Size = int64(sect.Size)
+ bld.SetSize(int64(sect.Size))
sectsyms[sect] = s
if sect.Name == ".rsrc" {
rsrc = s
@@ -242,35 +246,35 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
continue
}
- rs := make([]sym.Reloc, rsect.NumberOfRelocations)
+ rs := make([]loader.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))
+ return nil, 0, 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)
+ _, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
if err != nil {
- return nil, nil, err
+ return nil, 0, err
}
- if gosym == nil {
+ if gosym == 0 {
name, err := pesym.FullName(f.StringTable)
if err != nil {
name = string(pesym.Name[:])
}
- return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
+ return nil, 0, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
}
rp.Sym = gosym
- rp.Siz = 4
+ rp.Size = 4
rp.Off = int32(r.VirtualAddress)
switch arch.Family {
default:
- return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
+ return nil, 0, 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)
+ return nil, 0, 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
@@ -286,7 +290,7 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
- rp.Siz = 8
+ rp.Size = 8
rp.Type = objabi.R_ADDR
@@ -297,7 +301,7 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
case sys.ARM:
switch r.Type {
default:
- return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
+ return nil, 0, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
case IMAGE_REL_ARM_SECREL:
rp.Type = objabi.R_PCREL
@@ -324,11 +328,10 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
}
}
- sort.Sort(sym.RelocByOff(rs[:rsect.NumberOfRelocations]))
+ sort.Sort(loader.RelocByOff(rs[:rsect.NumberOfRelocations]))
- s := sectsyms[rsect]
- s.R = rs
- s.R = s.R[:rsect.NumberOfRelocations]
+ bld := l.MakeSymbolUpdater(sectsyms[rsect])
+ bld.SetRelocs(rs[:rsect.NumberOfRelocations])
}
// enter sub-symbols into symbol table.
@@ -339,7 +342,7 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
name, err := pesym.FullName(f.StringTable)
if err != nil {
- return nil, nil, err
+ return nil, 0, err
}
if name == "" {
continue
@@ -361,54 +364,56 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
}
}
- s, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
+ bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
if err != nil {
- return nil, nil, err
+ return nil, 0, err
}
if pesym.SectionNumber == 0 { // extern
- if s.Type == sym.SDYNIMPORT {
- s.SetPlt(-2) // flag for dynimport in PE object files.
+ if l.SymType(s) == sym.SDYNIMPORT {
+ bld = makeUpdater(l, bld, s)
+ bld.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)
+ if l.SymType(s) == sym.SXREF && pesym.Value > 0 { // global data
+ bld = makeUpdater(l, bld, s)
+ bld.SetType(sym.SNOPTRDATA)
+ bld.SetSize(int64(pesym.Value))
}
continue
} else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
sect = f.Sections[pesym.SectionNumber-1]
if _, found := sectsyms[sect]; !found {
- return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
+ return nil, 0, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
}
} else {
- return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
+ return nil, 0, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
}
if sect == nil {
- return nil, rsrc, nil
+ return nil, 0, nil
}
- if s.Outer != nil {
- if s.Attr.DuplicateOK() {
+ if l.OuterSym(s) != 0 {
+ if l.AttrDuplicateOK(s) {
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)
+ outerName := l.SymName(l.OuterSym(s))
+ sectName := l.SymName(sectsyms[sect])
+ return nil, 0, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
}
+ bld = makeUpdater(l, bld, s)
sectsym := sectsyms[sect]
- 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)
+ bld.SetType(l.SymType(sectsym))
+ l.PrependSub(sectsym, s)
+ bld.SetValue(int64(pesym.Value))
+ bld.SetSize(4)
+ if l.SymType(sectsym) == sym.STEXT {
+ if bld.External() && !bld.DuplicateOK() {
+ return nil, 0, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
}
- s.Attr |= sym.AttrExternal
+ bld.SetExternal(true)
}
}
@@ -416,23 +421,16 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
// This keeps textp in increasing address order.
for _, sect := range f.Sections {
s := sectsyms[sect]
- if s == nil {
+ if s == 0 {
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)
+ l.SortSub(s)
+ if l.SymType(s) == sym.STEXT {
+ for ; s != 0; s = l.SubSym(s) {
+ if l.AttrOnList(s) {
+ return nil, 0, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
}
- s.Attr |= sym.AttrOnList
+ l.SetAttrOnList(s, true)
textp = append(textp, s)
}
}
@@ -445,14 +443,14 @@ 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) {
+func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader.Sym, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]loader.Sym, localSymVersion int) (*loader.SymbolBuilder, loader.Sym, error) {
symname, err := pesym.FullName(f.StringTable)
if err != nil {
- return nil, err
+ return nil, 0, err
}
var name string
if issect(pesym) {
- name = sectsyms[f.Sections[pesym.SectionNumber-1]].Name
+ name = l.SymName(sectsyms[f.Sections[pesym.SectionNumber-1]])
} else {
name = symname
switch arch.Family {
@@ -483,10 +481,11 @@ func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File,
name = name[:i]
}
- var s *sym.Symbol
+ var s loader.Sym
+ var bld *loader.SymbolBuilder
switch pesym.Type {
default:
- return nil, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type)
+ return nil, 0, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type)
case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
switch pesym.StorageClass {
@@ -495,19 +494,22 @@ func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File,
case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
s = lookup(name, localSymVersion)
- s.Attr |= sym.AttrDuplicateOK
+ bld = makeUpdater(l, bld, s)
+ bld.SetDuplicateOK(true)
default:
- return nil, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass)
+ return nil, 0, 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 s != 0 && l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) {
+ bld = makeUpdater(l, bld, s)
+ bld.SetType(sym.SXREF)
}
if strings.HasPrefix(symname, "__imp_") {
- s.SetGot(-2) // flag for __imp_
+ bld = makeUpdater(l, bld, s)
+ bld.SetGot(-2) // flag for __imp_
}
- return s, nil
+ return bld, s, nil
}
diff --git a/src/cmd/link/internal/loadxcoff/ldxcoff.go b/src/cmd/link/internal/loadxcoff/ldxcoff.go
index 759b1769dd..906e871b09 100644
--- a/src/cmd/link/internal/loadxcoff/ldxcoff.go
+++ b/src/cmd/link/internal/loadxcoff/ldxcoff.go
@@ -19,7 +19,7 @@ import (
// ldSection is an XCOFF section with its symbols.
type ldSection struct {
xcoff.Section
- sym *sym.Symbol
+ sym loader.Sym
}
// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf
@@ -39,23 +39,10 @@ func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) {
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) {
+// Symbols are written into loader, and a slice of the text symbols is returned.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
}
@@ -75,34 +62,35 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
lds := new(ldSection)
lds.Section = *sect
name := fmt.Sprintf("%s(%s)", pkg, lds.Name)
- s := lookup(name, localSymVersion)
+ symbol := l.LookupOrCreateSym(name, localSymVersion)
+ s := l.MakeSymbolUpdater(symbol)
switch lds.Type {
default:
return errorf("unrecognized section type 0x%x", lds.Type)
case xcoff.STYP_TEXT:
- s.Type = sym.STEXT
+ s.SetType(sym.STEXT)
case xcoff.STYP_DATA:
- s.Type = sym.SNOPTRDATA
+ s.SetType(sym.SNOPTRDATA)
case xcoff.STYP_BSS:
- s.Type = sym.SNOPTRBSS
+ s.SetType(sym.SNOPTRBSS)
}
- s.Size = int64(lds.Size)
- if s.Type != sym.SNOPTRBSS {
+ s.SetSize(int64(lds.Size))
+ if s.Type() != sym.SNOPTRBSS {
data, err := lds.Section.Data()
if err != nil {
return nil, err
}
- s.P = data
+ s.SetData(data)
}
- lds.sym = s
+ lds.sym = symbol
ldSections = append(ldSections, lds)
}
// sx = symbol from file
- // s = symbol for syms
+ // s = symbol for loader
for _, sx := range f.Symbols {
// get symbol type
stype, errmsg := getSymbolType(f, sx)
@@ -113,14 +101,14 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
continue
}
- s := lookup(sx.Name, 0)
+ s := l.LookupOrCreateSym(sx.Name, 0)
// Text symbol
- if s.Type == sym.STEXT {
- if s.Attr.OnList() {
- return errorf("symbol %s listed multiple times", s.Name)
+ if l.SymType(s) == sym.STEXT {
+ if l.AttrOnList(s) {
+ return errorf("symbol %s listed multiple times", l.SymName(s))
}
- s.Attr |= sym.AttrOnList
+ l.SetAttrOnList(s, true)
textp = append(textp, s)
}
}
@@ -131,11 +119,11 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA {
continue
}
- rs := make([]sym.Reloc, sect.Nreloc)
+ rs := make([]loader.Reloc, sect.Nreloc)
for i, rx := range sect.Relocs {
r := &rs[i]
- r.Sym = lookup(rx.Symbol.Name, 0)
+ r.Sym = l.LookupOrCreateSym(rx.Symbol.Name, 0)
if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
}
@@ -149,27 +137,26 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion
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.Size = 8
r.Type = objabi.R_CONST
r.Add = int64(rx.Symbol.Value)
case xcoff.R_RBR:
- r.Siz = 4
+ r.Size = 4
r.Type = objabi.R_CALLPOWER
r.Add = 0 //
}
}
- s := sect.sym
- s.R = rs
- s.R = s.R[:sect.Nreloc]
+ bld := l.MakeSymbolUpdater(sect.sym)
+ bld.SetRelocs(rs[: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 )
+// Returns nil if this shouldn't be added into loader (like .file or .dw symbols )
func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) {
// .file symbol
if s.SectionNumber == -2 {
diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go
index 16c94c147a..f8386d8ac0 100644
--- a/src/cmd/link/internal/mips/asm.go
+++ b/src/cmd/link/internal/mips/asm.go
@@ -34,17 +34,19 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"fmt"
"log"
+ "sync"
)
func gentext(ctxt *ld.Link) {
return
}
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
log.Fatalf("adddynrel not implemented")
return false
}
@@ -74,7 +76,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
return true
}
-func elfsetupplt(ctxt *ld.Link) {
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
return
}
@@ -96,8 +98,8 @@ func applyrel(arch *sys.Arch, r *sym.Reloc, s *sym.Symbol, val int64, t int64) i
}
}
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
- if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+ if target.IsExternal() {
switch r.Type {
default:
return val, false
@@ -116,12 +118,12 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
ld.Errorf(s, "missing section for %s", rs.Name)
}
r.Xsym = rs
- return applyrel(ctxt.Arch, r, s, val, r.Xadd), true
+ return applyrel(target.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
+ return applyrel(target.Arch, r, s, val, r.Add), true
}
}
@@ -129,10 +131,10 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
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 ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
t := ld.Symaddr(r.Sym) + r.Add
- return applyrel(ctxt.Arch, r, s, val, t), true
+ return applyrel(target.Arch, r, s, val, t), true
case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
t := ld.Symaddr(r.Sym) + r.Add
@@ -145,20 +147,20 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t)
}
- return applyrel(ctxt.Arch, r, s, val, t), true
+ return applyrel(target.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 applyrel(target.Arch, r, s, val, t), true
}
return val, false
}
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
return -1
}
@@ -167,24 +169,24 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, 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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go
index 5c6fef9c5b..3323616a37 100644
--- a/src/cmd/link/internal/mips64/asm.go
+++ b/src/cmd/link/internal/mips64/asm.go
@@ -34,15 +34,17 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"fmt"
"log"
+ "sync"
)
func gentext(ctxt *ld.Link) {}
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
log.Fatalf("adddynrel not implemented")
return false
}
@@ -91,7 +93,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
return true
}
-func elfsetupplt(ctxt *ld.Link) {
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
return
}
@@ -99,8 +101,8 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se
return false
}
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
- if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+ if target.IsExternal() {
switch r.Type {
default:
return val, false
@@ -136,11 +138,11 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
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 ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
case objabi.R_ADDRMIPS,
objabi.R_ADDRMIPSU:
t := ld.Symaddr(r.Sym) + r.Add
- o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:])
+ o1 := target.Arch.ByteOrder.Uint32(s.P[r.Off:])
if r.Type == objabi.R_ADDRMIPS {
return int64(o1&0xffff0000 | uint32(t)&0xffff), true
}
@@ -151,20 +153,20 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
if t < -32768 || t >= 32678 {
ld.Errorf(s, "TLS offset out of range %d", t)
}
- o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:])
+ o1 := target.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:])
+ o1 := target.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 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
return -1
}
@@ -173,28 +175,28 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length)
+
+ for _, sect := range ld.Segtext.Sections[1:] {
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index 9fbcff551a..f4ba47b8b6 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -34,12 +34,14 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"encoding/binary"
"fmt"
"log"
"strings"
+ "sync"
)
func genplt(ctxt *ld.Link) {
@@ -262,22 +264,23 @@ func gencallstub(ctxt *ld.Link, abicase int, stub *sym.Symbol, targ *sym.Symbol)
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)
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
+ if target.IsElf() {
+ return addelfdynrel(target, syms, s, r)
+ } else if target.IsAIX() {
+ return ld.Xcoffadddynrel(target, ldr, s, r)
}
return false
}
-func addelfdynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+
+func addelfdynrel(target *ld.Target, syms *ld.ArchSyms, 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))
+ ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type))
return false
}
@@ -313,12 +316,12 @@ func addelfdynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
r.Type = objabi.R_ADDR
if targ.Type == sym.SDYNIMPORT {
// These happen in .toc sections
- ld.Adddynsym(ctxt, targ)
+ ld.Adddynsym(target, syms, 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))
+ rela := syms.Rela
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off))
+ rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(targ.Dynid), uint32(elf.R_PPC64_ADDR64)))
+ rela.AddUint64(target.Arch, uint64(r.Add))
r.Type = objabi.ElfRelocOffset // ignore during relocsym
}
@@ -498,14 +501,13 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
return true
}
-func elfsetupplt(ctxt *ld.Link) {
- plt := ctxt.Syms.Lookup(".plt", 0)
- if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
// The dynamic linker stores the address of the
// dynamic resolver and the DSO identifier in the two
// doublewords at the beginning of the .plt section
// before the PLT array. Reserve space for these.
- plt.Size = 16
+ plt.SetSize(16)
}
}
@@ -514,15 +516,13 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se
}
// Return the value of .TOC. for symbol s
-func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 {
- var toc *sym.Symbol
-
+func symtoc(syms *ld.ArchSyms, s *sym.Symbol) int64 {
+ v := s.Version
if s.Outer != nil {
- toc = ctxt.Syms.ROLookup(".TOC.", int(s.Outer.Version))
- } else {
- toc = ctxt.Syms.ROLookup(".TOC.", int(s.Version))
+ v = s.Outer.Version
}
+ toc := syms.DotTOC[v]
if toc == nil {
ld.Errorf(s, "TOC-relative relocation in object without .TOC.")
return 0
@@ -536,8 +536,8 @@ func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 {
// 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 {
+func archreloctoc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
+ if target.IsLinux() {
ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name)
}
var o1, o2 uint32
@@ -555,13 +555,13 @@ func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
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
+ if target.IsInternal() && tarSym != nil && tarSym.Attr.Reachable() && (tarSym.Sect.Seg == &ld.Segdata) {
+ t = ld.Symaddr(tarSym) + r.Add - syms.TOC.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
+ t = ld.Symaddr(r.Sym) + r.Add - syms.TOC.Value
}
if t != int64(int32(t)) {
@@ -593,12 +593,12 @@ func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
// 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 {
+func archrelocaddr(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
+ if target.IsAIX() {
ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name)
}
var o1, o2 uint32
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
o1 = uint32(val >> 32)
o2 = uint32(val)
} else {
@@ -635,7 +635,7 @@ func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64
return -1
}
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
return int64(o1)<<32 | int64(o2)
}
return int64(o2)<<32 | int64(o1)
@@ -770,13 +770,13 @@ func gentramp(ctxt *ld.Link, tramp, target *sym.Symbol, offset int64) {
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 {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+ if target.IsExternal() {
// 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 {
+ if target.IsAIX() {
return val, false
}
case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE:
@@ -806,14 +806,14 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
}
r.Xsym = rs
- if ctxt.HeadType != objabi.Haix {
+ if !target.IsAIX() {
return val, true
}
case objabi.R_CALLPOWER:
r.Done = false
r.Xsym = r.Sym
r.Xadd = r.Add
- if ctxt.HeadType != objabi.Haix {
+ if !target.IsAIX() {
return val, true
}
}
@@ -823,11 +823,11 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
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 ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS:
- return archreloctoc(ctxt, r, s, val), true
+ return archreloctoc(target, syms, r, s, val), true
case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS:
- return archrelocaddr(ctxt, r, s, val), true
+ return archrelocaddr(target, syms, r, s, val), true
case objabi.R_CALLPOWER:
// Bits 6 through 29 = (S + A - P) >> 2
@@ -843,7 +843,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
}
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
+ return ld.Symaddr(r.Sym) + r.Add - symtoc(syms, s), true
case objabi.R_POWER_TLS_LE:
// The thread pointer points 0x7000 bytes after the start of the
@@ -851,7 +851,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
// Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI
// Specification".
v := r.Sym.Value - 0x7000
- if ctxt.HeadType == objabi.Haix {
+ if target.IsAIX() {
// On AIX, the thread pointer points 0x7800 bytes after
// the TLS.
v -= 0x800
@@ -865,7 +865,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
return val, false
}
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, 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)
@@ -879,7 +879,7 @@ func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64
// Whether to check for signed or unsigned
// overflow depends on the instruction
var o1 uint32
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
o1 = binary.BigEndian.Uint32(s.P[r.Off-2:])
} else {
o1 = binary.LittleEndian.Uint32(s.P[r.Off:])
@@ -913,7 +913,7 @@ func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64
// Whether to check for signed or unsigned
// overflow depends on the instruction
var o1 uint32
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
o1 = binary.BigEndian.Uint32(s.P[r.Off-2:])
} else {
o1 = binary.LittleEndian.Uint32(s.P[r.Off:])
@@ -937,7 +937,7 @@ func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64
case sym.RV_POWER_DS:
var o1 uint32
- if ctxt.Arch.ByteOrder == binary.BigEndian {
+ if target.IsBigEndian() {
o1 = uint32(binary.BigEndian.Uint16(s.P[r.Off:]))
} else {
o1 = uint32(binary.LittleEndian.Uint16(s.P[r.Off:]))
@@ -961,13 +961,13 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
return
}
- ld.Adddynsym(ctxt, s)
+ ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s)
if ctxt.IsELF {
plt := ctxt.Syms.Lookup(".plt", 0)
rela := ctxt.Syms.Lookup(".rela.plt", 0)
if plt.Size == 0 {
- elfsetupplt(ctxt)
+ panic("plt is not set up")
}
// Create the glink resolver if necessary
@@ -1056,7 +1056,7 @@ func ensureglinkresolver(ctxt *ld.Link) *sym.Symbol {
// before the first symbol resolver stub.
s := ctxt.Syms.Lookup(".dynamic", 0)
- ld.Elfwritedynentsymplus(ctxt, s, ld.DT_PPC64_GLINK, glink, glink.Size-32)
+ ld.Elfwritedynentsymplus(ctxt.Arch, s, ld.DT_PPC64_GLINK, glink, glink.Size-32)
return glink
}
@@ -1066,30 +1066,34 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
for _, sect := range ld.Segtext.Sections {
- ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+ offset := 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))
+ ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length)
} else {
- ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length)
}
}
+ for _, sect := range ld.Segtext.Sections[1:] {
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go
index b0897288fc..cc83b96a96 100644
--- a/src/cmd/link/internal/riscv64/asm.go
+++ b/src/cmd/link/internal/riscv64/asm.go
@@ -9,19 +9,21 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"fmt"
"log"
+ "sync"
)
func gentext(ctxt *ld.Link) {
}
-func adddynrela(ctxt *ld.Link, rel *sym.Symbol, s *sym.Symbol, r *sym.Reloc) {
+func adddynrela(target *ld.Target, syms *ld.ArchSyms, 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 {
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
log.Fatalf("adddynrel not implemented")
return false
}
@@ -31,7 +33,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
return false
}
-func elfsetupplt(ctxt *ld.Link) {
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
log.Fatalf("elfsetuplt")
}
@@ -40,7 +42,7 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se
return false
}
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
switch r.Type {
case objabi.R_CALLRISCV:
// Nothing to do.
@@ -91,7 +93,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
return val, false
}
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
log.Fatalf("archrelocvariant")
return -1
}
@@ -101,28 +103,28 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length)
+
+ for _, sect := range ld.Segtext.Sections[1:] {
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go
index 94a5a2f86c..7750da21cd 100644
--- a/src/cmd/link/internal/s390x/asm.go
+++ b/src/cmd/link/internal/s390x/asm.go
@@ -34,9 +34,11 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"fmt"
+ "sync"
)
// gentext generates assembly to append the local moduledata to the global
@@ -104,7 +106,7 @@ func gentext(ctxt *ld.Link) {
initarray_entry.AddAddr(ctxt.Arch, initfunc)
}
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool {
targ := r.Sym
r.InitExt()
@@ -158,8 +160,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add += int64(targ.Plt())
}
return true
@@ -169,8 +171,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add += int64(targ.Plt())
}
return true
@@ -200,7 +202,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPC):
r.Type = objabi.R_PCREL
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += int64(r.Siz)
return true
@@ -217,16 +219,16 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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.Sym = syms.GOT
r.Add += int64(r.Siz)
return true
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT):
- addgotsym(ctxt, targ)
+ addgotsym(target, syms, targ)
r.Type = objabi.R_PCREL
r.Variant = sym.RV_390_DBL
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += int64(targ.Got())
r.Add += int64(r.Siz)
return true
@@ -333,10 +335,8 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
return true
}
-func elfsetupplt(ctxt *ld.Link) {
- plt := ctxt.Syms.Lookup(".plt", 0)
- got := ctxt.Syms.Lookup(".got", 0)
- if plt.Size == 0 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
// stg %r1,56(%r15)
plt.AddUint8(0xe3)
plt.AddUint8(0x10)
@@ -347,7 +347,7 @@ func elfsetupplt(ctxt *ld.Link) {
// larl %r1,_GLOBAL_OFFSET_TABLE_
plt.AddUint8(0xc0)
plt.AddUint8(0x10)
- plt.AddPCRelPlus(ctxt.Arch, got, 6)
+ plt.AddSymRef(ctxt.Arch, got.Sym(), 6, objabi.R_PCRELDBL, 4)
// mvc 48(8,%r15),8(%r1)
plt.AddUint8(0xd2)
plt.AddUint8(0x07)
@@ -376,7 +376,7 @@ func elfsetupplt(ctxt *ld.Link) {
plt.AddUint8(0x00)
// assume got->size == 0 too
- got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+ got.AddAddrPlus(ctxt.Arch, dynamic, 0)
got.AddUint64(ctxt.Arch, 0)
got.AddUint64(ctxt.Arch, 0)
@@ -387,8 +387,8 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se
return false
}
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
- if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+ if target.IsExternal() {
return val, false
}
@@ -396,13 +396,13 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo
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 ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
}
return val, false
}
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, 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)
@@ -419,28 +419,28 @@ func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64
}
}
-func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Plt() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
+ ld.Adddynsym(target, syms, s)
- if ctxt.IsELF {
- plt := ctxt.Syms.Lookup(".plt", 0)
- got := ctxt.Syms.Lookup(".got", 0)
- rela := ctxt.Syms.Lookup(".rela.plt", 0)
+ if target.IsElf() {
+ plt := syms.PLT
+ got := syms.GOT
+ rela := syms.RelaPLT
if plt.Size == 0 {
- elfsetupplt(ctxt)
+ panic("plt is not set up")
}
// larl %r1,_GLOBAL_OFFSET_TABLE_+index
plt.AddUint8(0xc0)
plt.AddUint8(0x10)
- plt.AddPCRelPlus(ctxt.Arch, got, got.Size+6) // need variant?
+ plt.AddPCRelPlus(target.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
+ got.AddAddrPlus(target.Arch, plt, plt.Size+8) // weird but correct
// lg %r1,0(%r1)
plt.AddUint8(0xe3)
plt.AddUint8(0x10)
@@ -465,15 +465,15 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
plt.AddUint8(0xc0)
plt.AddUint8(0xf4)
- plt.AddUint32(ctxt.Arch, uint32(-((plt.Size - 2) >> 1))) // roll-your-own relocation
+ plt.AddUint32(target.Arch, uint32(-((plt.Size - 2) >> 1))) // roll-your-own relocation
//.plt index
- plt.AddUint32(ctxt.Arch, uint32(rela.Size)) // rela size before current entry
+ plt.AddUint32(target.Arch, uint32(rela.Size)) // rela size before current entry
// rela
- rela.AddAddrPlus(ctxt.Arch, got, got.Size-8)
+ rela.AddAddrPlus(target.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)
+ rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_JMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
s.SetPlt(int32(plt.Size - 32))
@@ -482,21 +482,21 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
}
}
-func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Got() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
- got := ctxt.Syms.Lookup(".got", 0)
+ ld.Adddynsym(target, syms, s)
+ got := syms.GOT
s.SetGot(int32(got.Size))
- got.AddUint64(ctxt.Arch, 0)
+ got.AddUint64(target.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)
+ if target.IsElf() {
+ rela := syms.Rela
+ rela.AddAddrPlus(target.Arch, got, int64(s.Got()))
+ rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_GLOB_DAT)))
+ rela.AddUint64(target.Arch, 0)
} else {
ld.Errorf(s, "addgotsym: unsupported binary format")
}
@@ -507,28 +507,28 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length)
+
+ for _, sect := range ld.Segtext.Sections[1:] {
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/internal/sym/attribute.go b/src/cmd/link/internal/sym/attribute.go
index 4b69bf32d0..eda3fe60e4 100644
--- a/src/cmd/link/internal/sym/attribute.go
+++ b/src/cmd/link/internal/sym/attribute.go
@@ -4,6 +4,8 @@
package sym
+import "sync/atomic"
+
// Attribute is a set of common symbol attributes.
type Attribute int32
@@ -84,34 +86,44 @@ const (
// 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) load() Attribute { return Attribute(atomic.LoadInt32((*int32)(a))) }
+
+func (a *Attribute) DuplicateOK() bool { return a.load()&AttrDuplicateOK != 0 }
+func (a *Attribute) External() bool { return a.load()&AttrExternal != 0 }
+func (a *Attribute) NoSplit() bool { return a.load()&AttrNoSplit != 0 }
+func (a *Attribute) Reachable() bool { return a.load()&AttrReachable != 0 }
+func (a *Attribute) CgoExportDynamic() bool { return a.load()&AttrCgoExportDynamic != 0 }
+func (a *Attribute) CgoExportStatic() bool { return a.load()&AttrCgoExportStatic != 0 }
+func (a *Attribute) Special() bool { return a.load()&AttrSpecial != 0 }
+func (a *Attribute) StackCheck() bool { return a.load()&AttrStackCheck != 0 }
+func (a *Attribute) NotInSymbolTable() bool { return a.load()&AttrNotInSymbolTable != 0 }
+func (a *Attribute) OnList() bool { return a.load()&AttrOnList != 0 }
+func (a *Attribute) Local() bool { return a.load()&AttrLocal != 0 }
+func (a *Attribute) ReflectMethod() bool { return a.load()&AttrReflectMethod != 0 }
+func (a *Attribute) MakeTypelink() bool { return a.load()&AttrMakeTypelink != 0 }
+func (a *Attribute) Shared() bool { return a.load()&AttrShared != 0 }
+func (a *Attribute) VisibilityHidden() bool { return a.load()&AttrVisibilityHidden != 0 }
+func (a *Attribute) SubSymbol() bool { return a.load()&AttrSubSymbol != 0 }
+func (a *Attribute) Container() bool { return a.load()&AttrContainer != 0 }
+func (a *Attribute) TopFrame() bool { return a.load()&AttrTopFrame != 0 }
+func (a *Attribute) ReadOnly() bool { return a.load()&AttrReadOnly != 0 }
-func (a Attribute) CgoExport() bool {
+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
+ // XXX it would be nice if we have atomic And, Or.
+ for {
+ a0 := a.load()
+ var anew Attribute
+ if value {
+ anew = a0 | flag
+ } else {
+ anew = a0 &^ flag
+ }
+ if atomic.CompareAndSwapInt32((*int32)(a), int32(a0), int32(anew)) {
+ return
+ }
}
}
diff --git a/src/cmd/link/internal/sym/compilation_unit.go b/src/cmd/link/internal/sym/compilation_unit.go
index 02fb0cfab8..b8b6845e6e 100644
--- a/src/cmd/link/internal/sym/compilation_unit.go
+++ b/src/cmd/link/internal/sym/compilation_unit.go
@@ -6,6 +6,10 @@ package sym
import "cmd/internal/dwarf"
+// LoaderSym holds a loader.Sym value. We can't refer to this
+// type from the sym package since loader imports sym.
+type LoaderSym int
+
// 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.).
@@ -20,4 +24,10 @@ type CompilationUnit struct {
RangeSyms []*Symbol // Symbols for debug_range
Textp []*Symbol // Text symbols in this CU
DWARFFileTable []string // The file table used to generate the .debug_lines
+
+ Consts2 LoaderSym // Package constants DIEs (loader)
+ FuncDIEs2 []LoaderSym // Function DIE subtrees (loader)
+ AbsFnDIEs2 []LoaderSym // Abstract function DIE subtrees (loader)
+ RangeSyms2 []LoaderSym // Symbols for debug_range (loader)
+ Textp2 []LoaderSym // Text symbols in this CU (loader)
}
diff --git a/src/cmd/link/internal/sym/library.go b/src/cmd/link/internal/sym/library.go
index 4f2023b8f7..21568dfbe2 100644
--- a/src/cmd/link/internal/sym/library.go
+++ b/src/cmd/link/internal/sym/library.go
@@ -18,6 +18,9 @@ type Library struct {
Main bool
Safe bool
Units []*CompilationUnit
+
+ Textp2 []LoaderSym // text syms defined in this library
+ DupTextSyms2 []LoaderSym // dupok text syms defined in this library
}
func (l Library) String() string {
diff --git a/src/cmd/link/internal/sym/segment.go b/src/cmd/link/internal/sym/segment.go
index d5255bf142..979241be61 100644
--- a/src/cmd/link/internal/sym/segment.go
+++ b/src/cmd/link/internal/sym/segment.go
@@ -55,4 +55,5 @@ type Section struct {
Elfsect interface{} // an *ld.ElfShdr
Reloff uint64
Rellen uint64
+ Sym *Symbol // symbol for the section, if any
}
diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go
index e9819a064f..1fee966c12 100644
--- a/src/cmd/link/internal/sym/symbol.go
+++ b/src/cmd/link/internal/sym/symbol.go
@@ -103,6 +103,10 @@ func (s *Symbol) Len() int64 {
return s.Size
}
+func (s *Symbol) Length(dwarfContext interface{}) int64 {
+ return s.Size
+}
+
func (s *Symbol) Grow(siz int64) {
if int64(int(siz)) != siz {
log.Fatalf("symgrow size %d too long", siz)
diff --git a/src/cmd/link/internal/sym/symbols.go b/src/cmd/link/internal/sym/symbols.go
index e772496534..d36be11ee8 100644
--- a/src/cmd/link/internal/sym/symbols.go
+++ b/src/cmd/link/internal/sym/symbols.go
@@ -31,105 +31,41 @@
package sym
type Symbols struct {
- symbolBatch []Symbol
-
// Symbol lookup based on name and indexed by version.
- hash []map[string]*Symbol
+ versions int
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),
- }
-}
+ // Provided by the loader
-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:]
+ // Look up the symbol with the given name and version, creating the
+ // symbol if it is not found.
+ Lookup func(name string, v int) *Symbol
- s.Dynid = -1
- s.Name = name
- s.Version = int16(v)
- syms.Allsym = append(syms.Allsym, s)
+ // Look up the symbol with the given name and version, returning nil
+ // if it is not found.
+ ROLookup func(name string, v int) *Symbol
- return s
+ // Create a symbol with the given name and version. The new symbol
+ // is not added to the lookup table and is not dedup'd with existing
+ // symbols (if any).
+ Newsym func(name string, v int) *Symbol
}
-// 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")
+func NewSymbols() *Symbols {
+ return &Symbols{
+ versions: SymVerStatic,
+ Allsym: make([]*Symbol, 0, 100000),
}
- 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
+ syms.versions++
+ return syms.versions - 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
- }
- }
+// returns the maximum version number
+func (syms *Symbols) MaxVersion() int {
+ return syms.versions
}
diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go
index 3fe36db64d..bb7365410e 100644
--- a/src/cmd/link/internal/x86/asm.go
+++ b/src/cmd/link/internal/x86/asm.go
@@ -34,9 +34,11 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"log"
+ "sync"
)
// Append 4 bytes to s and create a R_CALL relocation targeting t to fill them in.
@@ -167,13 +169,13 @@ func gentext(ctxt *ld.Link) {
initarray_entry.AddAddr(ctxt.Arch, initfunc)
}
-func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, 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))
+ ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type))
return false
}
@@ -195,8 +197,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
r.Type = objabi.R_PCREL
r.Add += 4
if targ.Type == sym.SDYNIMPORT {
- addpltsym(ctxt, targ)
- r.Sym = ctxt.Syms.Lookup(".plt", 0)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add += int64(targ.Plt())
}
@@ -228,7 +230,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
return false
}
- addgotsym(ctxt, targ)
+ addgotsym(target, syms, targ)
r.Type = objabi.R_CONST // write r->add during relocsym
r.Sym = nil
r.Add += int64(targ.Got())
@@ -240,7 +242,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC):
r.Type = objabi.R_PCREL
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ r.Sym = syms.GOT
r.Add += 4
return true
@@ -260,8 +262,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add = int64(targ.Plt())
r.Type = objabi.R_PCREL
return true
@@ -284,8 +286,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
return true
}
- addgotsym(ctxt, targ)
- r.Sym = ctxt.Syms.Lookup(".got", 0)
+ addgotsym(target, syms, targ)
+ r.Sym = syms.GOT
r.Add += int64(targ.Got())
r.Type = objabi.R_PCREL
return true
@@ -298,12 +300,12 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
switch r.Type {
case objabi.R_CALL,
objabi.R_PCREL:
- if ctxt.LinkMode == ld.LinkExternal {
+ if target.IsExternal() {
// External linker will do this relocation.
return true
}
- addpltsym(ctxt, targ)
- r.Sym = ctxt.Syms.Lookup(".plt", 0)
+ addpltsym(target, syms, targ)
+ r.Sym = syms.PLT
r.Add = int64(targ.Plt())
return true
@@ -311,17 +313,17 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
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)))
+ if target.IsElf() {
+ ld.Adddynsym(target, syms, targ)
+ rel := syms.Rel
+ rel.AddAddrPlus(target.Arch, s, int64(r.Off))
+ rel.AddUint32(target.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 {
+ if target.IsDarwin() && s.Size == int64(target.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.
@@ -332,17 +334,17 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool {
// 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)
+ ld.Adddynsym(target, syms, targ)
- got := ctxt.Syms.Lookup(".got", 0)
+ got := syms.GOT
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))
+ got.AddUint32(target.Arch, 0)
+ syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid))
r.Type = objabi.ElfRelocOffset // ignore during relocsym
return true
}
@@ -492,128 +494,126 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, secto
return true
}
-func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
- if ctxt.LinkMode == ld.LinkExternal {
+func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
+ if target.IsExternal() {
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 ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true
}
return val, false
}
-func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 {
+func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, 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 {
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
// pushl got+4
plt.AddUint8(0xff)
plt.AddUint8(0x35)
- plt.AddAddrPlus(ctxt.Arch, got, 4)
+ plt.AddAddrPlus(ctxt.Arch, got.Sym(), 4)
// jmp *got+8
plt.AddUint8(0xff)
plt.AddUint8(0x25)
- plt.AddAddrPlus(ctxt.Arch, got, 8)
+ plt.AddAddrPlus(ctxt.Arch, got.Sym(), 8)
// zero pad
plt.AddUint32(ctxt.Arch, 0)
// assume got->size == 0 too
- got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0)
+ got.AddAddrPlus(ctxt.Arch, dynamic, 0)
got.AddUint32(ctxt.Arch, 0)
got.AddUint32(ctxt.Arch, 0)
}
}
-func addpltsym(ctxt *ld.Link, s *sym.Symbol) {
+func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Plt() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
+ ld.Adddynsym(target, syms, 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 target.IsElf() {
+ plt := syms.PLT
+ got := syms.GOTPLT
+ rel := syms.RelPLT
if plt.Size == 0 {
- elfsetupplt(ctxt)
+ panic("plt is not set up")
}
// jmpq *got+size
plt.AddUint8(0xff)
plt.AddUint8(0x25)
- plt.AddAddrPlus(ctxt.Arch, got, got.Size)
+ plt.AddAddrPlus(target.Arch, got, got.Size)
// add to got: pointer to current pos in plt
- got.AddAddrPlus(ctxt.Arch, plt, plt.Size)
+ got.AddAddrPlus(target.Arch, plt, plt.Size)
// pushl $x
plt.AddUint8(0x68)
- plt.AddUint32(ctxt.Arch, uint32(rel.Size))
+ plt.AddUint32(target.Arch, uint32(rel.Size))
// jmp .plt
plt.AddUint8(0xe9)
- plt.AddUint32(ctxt.Arch, uint32(-(plt.Size + 4)))
+ plt.AddUint32(target.Arch, uint32(-(plt.Size + 4)))
// rel
- rel.AddAddrPlus(ctxt.Arch, got, got.Size-4)
+ rel.AddAddrPlus(target.Arch, got, got.Size-4)
- rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_JMP_SLOT)))
+ rel.AddUint32(target.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 {
+ } else if target.IsDarwin() {
// Same laziness as in 6l.
- plt := ctxt.Syms.Lookup(".plt", 0)
+ plt := syms.PLT
- addgotsym(ctxt, s)
+ addgotsym(target, syms, s)
- ctxt.Syms.Lookup(".linkedit.plt", 0).AddUint32(ctxt.Arch, uint32(s.Dynid))
+ syms.LinkEditPLT.AddUint32(target.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()))
+ plt.AddAddrPlus(target.Arch, syms.GOT, int64(s.Got()))
} else {
ld.Errorf(s, "addpltsym: unsupported binary format")
}
}
-func addgotsym(ctxt *ld.Link, s *sym.Symbol) {
+func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) {
if s.Got() >= 0 {
return
}
- ld.Adddynsym(ctxt, s)
- got := ctxt.Syms.Lookup(".got", 0)
+ ld.Adddynsym(target, syms, s)
+ got := syms.GOT
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))
+ got.AddUint32(target.Arch, 0)
+
+ if target.IsElf() {
+ rel := syms.Rel
+ rel.AddAddrPlus(target.Arch, got, int64(s.Got()))
+ rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_GLOB_DAT)))
+ } else if target.IsDarwin() {
+ syms.LinkEditGOT.AddUint32(target.Arch, uint32(s.Dynid))
} else {
ld.Errorf(s, "addgotsym: unsupported binary format")
}
@@ -624,29 +624,31 @@ func asmb(ctxt *ld.Link) {
ld.Asmbelfsetup()
}
+ var wg sync.WaitGroup
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))
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ f := func(ctxt *ld.Link, out *ld.OutBuf, start, length int64) {
+ ld.CodeblkPad(ctxt, out, start, length, []byte{0xCC})
+ }
+ ld.WriteParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length)
+
+ for _, sect := range ld.Segtext.Sections[1:] {
+ offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, 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))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen)
}
- ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff))
- ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+ ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen)
- ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
- ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+ ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen)
+ wg.Wait()
}
func asmb2(ctxt *ld.Link) {
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index b1f0e8882c..e665737cc3 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -535,6 +535,30 @@ func TestStrictDup(t *testing.T) {
}
}
+func TestOldLink(t *testing.T) {
+ // Test that old object file format still works.
+ // TODO(go115newobj): delete.
+
+ testenv.MustHaveGoBuild(t)
+
+ tmpdir, err := ioutil.TempDir("", "TestOldLink")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "main.go")
+ err = ioutil.WriteFile(src, []byte("package main; func main(){}\n"), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command(testenv.GoToolPath(t), "run", "-gcflags=all=-go115newobj=false", "-asmflags=all=-go115newobj=false", "-ldflags=-go115newobj=false", src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
+ }
+}
+
const testFuncAlignSrc = `
package main
import (
diff --git a/src/cmd/oldlink/doc.go b/src/cmd/oldlink/doc.go
new file mode 100644
index 0000000000..219499be0a
--- /dev/null
+++ b/src/cmd/oldlink/doc.go
@@ -0,0 +1,129 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Link, typically invoked as ``go tool link,'' reads the Go archive or object
+for a package main, along with its dependencies, and combines them
+into an executable binary.
+
+Command Line
+
+Usage:
+
+ go tool link [flags] main.a
+
+Flags:
+
+ -B note
+ Add an ELF_NT_GNU_BUILD_ID note when using ELF.
+ The value should start with 0x and be an even number of hex digits.
+ -D address
+ Set data segment address.
+ -E entry
+ Set entry symbol name.
+ -H type
+ Set executable format type.
+ The default format is inferred from GOOS and GOARCH.
+ On Windows, -H windowsgui writes a "GUI binary" instead of a "console binary."
+ -I interpreter
+ Set the ELF dynamic linker to use.
+ -L dir1 -L dir2
+ Search for imported packages in dir1, dir2, etc,
+ after consulting $GOROOT/pkg/$GOOS_$GOARCH.
+ -R quantum
+ Set address rounding quantum.
+ -T address
+ Set text segment address.
+ -V
+ Print linker version and exit.
+ -X importpath.name=value
+ Set the value of the string variable in importpath named name to value.
+ This is only effective if the variable is declared in the source code either uninitialized
+ or initialized to a constant string expression. -X will not work if the initializer makes
+ a function call or refers to other variables.
+ Note that before Go 1.5 this option took two separate arguments.
+ -a
+ Disassemble output.
+ -buildid id
+ Record id as Go toolchain build id.
+ -buildmode mode
+ Set build mode (default exe).
+ -c
+ Dump call graphs.
+ -compressdwarf
+ Compress DWARF if possible (default true).
+ -cpuprofile file
+ Write CPU profile to file.
+ -d
+ Disable generation of dynamic executables.
+ The emitted code is the same in either case; the option
+ controls only whether a dynamic header is included.
+ The dynamic header is on by default, even without any
+ references to dynamic libraries, because many common
+ system tools now assume the presence of the header.
+ -debugtramp int
+ Debug trampolines.
+ -dumpdep
+ Dump symbol dependency graph.
+ -extar ar
+ Set the external archive program (default "ar").
+ Used only for -buildmode=c-archive.
+ -extld linker
+ Set the external linker (default "clang" or "gcc").
+ -extldflags flags
+ Set space-separated flags to pass to the external linker.
+ -f
+ Ignore version mismatch in the linked archives.
+ -g
+ Disable Go package data checks.
+ -importcfg file
+ Read import configuration from file.
+ In the file, set packagefile, packageshlib to specify import resolution.
+ -installsuffix suffix
+ Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix
+ instead of $GOROOT/pkg/$GOOS_$GOARCH.
+ -k symbol
+ Set field tracking symbol. Use this flag when GOEXPERIMENT=fieldtrack is set.
+ -libgcc file
+ Set name of compiler support library.
+ This is only used in internal link mode.
+ If not set, default value comes from running the compiler,
+ which may be set by the -extld option.
+ Set to "none" to use no support library.
+ -linkmode mode
+ Set link mode (internal, external, auto).
+ This sets the linking mode as described in cmd/cgo/doc.go.
+ -linkshared
+ Link against installed Go shared libraries (experimental).
+ -memprofile file
+ Write memory profile to file.
+ -memprofilerate rate
+ Set runtime.MemProfileRate to rate.
+ -msan
+ Link with C/C++ memory sanitizer support.
+ -n
+ Dump symbol table.
+ -o file
+ Write output to file (default a.out, or a.out.exe on Windows).
+ -pluginpath path
+ The path name used to prefix exported plugin symbols.
+ -r dir1:dir2:...
+ Set the ELF dynamic linker search path.
+ -race
+ Link with race detection libraries.
+ -s
+ Omit the symbol table and debug information.
+ -shared
+ Generated shared object (implies -linkmode external; experimental).
+ -tmpdir dir
+ Write temporary files to dir.
+ Temporary files are only used in external linking mode.
+ -u
+ Reject unsafe packages.
+ -v
+ Print trace of linker operations.
+ -w
+ Omit the DWARF symbol table.
+*/
+package main
diff --git a/src/cmd/oldlink/internal/amd64/asm.go b/src/cmd/oldlink/internal/amd64/asm.go
new file mode 100644
index 0000000000..c120e4ea81
--- /dev/null
+++ b/src/cmd/oldlink/internal/amd64/asm.go
@@ -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
index 0000000000..393da6bf28
--- /dev/null
+++ b/src/cmd/oldlink/internal/amd64/l.go
@@ -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
index 0000000000..655d9f1e03
--- /dev/null
+++ b/src/cmd/oldlink/internal/amd64/obj.go
@@ -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
index 0000000000..8db0bc3c9a
--- /dev/null
+++ b/src/cmd/oldlink/internal/arm/asm.go
@@ -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
index 0000000000..a83d26bf06
--- /dev/null
+++ b/src/cmd/oldlink/internal/arm/l.go
@@ -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
index 0000000000..1fe4b9c2c8
--- /dev/null
+++ b/src/cmd/oldlink/internal/arm/obj.go
@@ -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
index 0000000000..a97671222d
--- /dev/null
+++ b/src/cmd/oldlink/internal/arm64/asm.go
@@ -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
index 0000000000..5f3530335e
--- /dev/null
+++ b/src/cmd/oldlink/internal/arm64/l.go
@@ -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 = 16
+)
+
+/* 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
index 0000000000..2dcc999dd1
--- /dev/null
+++ b/src/cmd/oldlink/internal/arm64/obj.go
@@ -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
index 0000000000..8df859f11c
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/ar.go
@@ -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
index 0000000000..2373b500e3
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/config.go
@@ -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
index 0000000000..13f412ccd8
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/data.go
@@ -0,0 +1,2509 @@
+// 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)
+ }
+
+ if sect.Align < s.Align {
+ sect.Align = s.Align
+ }
+
+ // 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
index 0000000000..1bdac31f12
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/deadcode.go
@@ -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
index 0000000000..82bfd60a47
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/deadcode2.go
@@ -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
index 0000000000..0676e94e2c
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/decodesym.go
@@ -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
index 0000000000..3d5220cbfb
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/dwarf.go
@@ -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/elf.go b/src/cmd/oldlink/internal/ld/elf.go
new file mode 100644
index 0000000000..610972118a
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/elf.go
@@ -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/execarchive.go b/src/cmd/oldlink/internal/ld/execarchive.go
new file mode 100644
index 0000000000..fe5cc40865
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/execarchive.go
@@ -0,0 +1,37 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !wasm,!windows
+
+package ld
+
+import (
+ "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
index 0000000000..a70dea9fda
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/execarchive_noexec.go
@@ -0,0 +1,13 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build wasm windows
+
+package ld
+
+const syscallExecSupported = false
+
+func (ctxt *Link) execArchive(argv []string) {
+ panic("should never arrive here")
+}
diff --git a/src/cmd/oldlink/internal/ld/go.go b/src/cmd/oldlink/internal/ld/go.go
new file mode 100644
index 0000000000..b05265580b
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/go.go
@@ -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/ld.go b/src/cmd/oldlink/internal/ld/ld.go
new file mode 100644
index 0000000000..7420dce9de
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/ld.go
@@ -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/lib.go b/src/cmd/oldlink/internal/ld/lib.go
new file mode 100644
index 0000000000..a6c86af6cf
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/lib.go
@@ -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
index 0000000000..15878f3267
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/link.go
@@ -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 && rs.Type != sym.SXREF {
+ 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
index 0000000000..960ed29067
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/macho.go
@@ -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
index 0000000000..9d9f916b8e
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/macho_combine_dwarf.go
@@ -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
index 0000000000..f7f3700398
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/main.go
@@ -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/outbuf.go b/src/cmd/oldlink/internal/ld/outbuf.go
new file mode 100644
index 0000000000..596d2395c8
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/outbuf.go
@@ -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
index 0000000000..4075141171
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/outbuf_mmap.go
@@ -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
index 0000000000..fba8cd8bc4
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/outbuf_nommap.go
@@ -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
index 0000000000..1cb05c301f
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/outbuf_windows.go
@@ -0,0 +1,49 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "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
index 0000000000..7d53ab8ad4
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/pcln.go
@@ -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
index 0000000000..b40557f485
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/pe.go
@@ -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
index 0000000000..c0f725c125
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/sym.go
@@ -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
index 0000000000..a324fdf600
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/symtab.go
@@ -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
index 0000000000..1bce30119a
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/httptest/main/main.go
@@ -0,0 +1,22 @@
+// A small test program that uses the net/http package. There is
+// nothing special about net/http here, this is just a convenient way
+// to pull in a lot of code.
+
+package main
+
+import (
+ "net/http"
+ "net/http/httptest"
+)
+
+type statusHandler int
+
+func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(int(*h))
+}
+
+func main() {
+ status := statusHandler(http.StatusNotFound)
+ s := httptest.NewServer(&status)
+ defer s.Close()
+}
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue10978/main.go b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.go
new file mode 100644
index 0000000000..5e8c09749f
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.go
@@ -0,0 +1,27 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func undefined()
+
+func defined1() int {
+ // To check multiple errors for a single symbol,
+ // reference undefined more than once.
+ undefined()
+ undefined()
+ return 0
+}
+
+func defined2() {
+ undefined()
+ undefined()
+}
+
+func init() {
+ _ = defined1()
+ defined2()
+}
+
+// The "main" function remains undeclared.
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue10978/main.s b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.s
new file mode 100644
index 0000000000..1d00e76c1d
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.s
@@ -0,0 +1 @@
+// This file is needed to make "go build" work for package with external functions.
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go b/src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go
new file mode 100644
index 0000000000..6032d76f49
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go
@@ -0,0 +1,27 @@
+package a
+
+const Always = true
+
+var Count int
+
+type FuncReturningInt func() int
+
+var PointerToConstIf FuncReturningInt
+
+func ConstIf() int {
+ if Always {
+ return 1
+ }
+ var imdead [4]int
+ imdead[Count] = 1
+ return imdead[0]
+}
+
+func CallConstIf() int {
+ Count += 3
+ return ConstIf()
+}
+
+func Another() {
+ defer func() { PointerToConstIf = ConstIf; Count += 1 }()
+}
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go
new file mode 100644
index 0000000000..3f5f365169
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go
@@ -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
index 0000000000..ca577490bc
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue26237/b.dir/b.go
@@ -0,0 +1,16 @@
+package b
+
+var q int
+
+func Top(x int) int {
+ q += 1
+ if q != x {
+ return 3
+ }
+ return 4
+}
+
+func OOO(x int) int {
+ defer func() { q += x & 7 }()
+ return Top(x + 1)
+}
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go
new file mode 100644
index 0000000000..88b54f1678
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go
@@ -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
index 0000000000..946278803e
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/ObjC.m
@@ -0,0 +1,16 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+#import <AppKit/NSAppearance.h>
+
+BOOL function(void) {
+#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED > 101300)
+ NSAppearance *darkAppearance;
+ if (@available(macOS 10.14, *)) {
+ darkAppearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
+ }
+#endif
+ return NO;
+}
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go
new file mode 100644
index 0000000000..514b9b9a4a
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go
@@ -0,0 +1,19 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package lib
+
+/*
+#cgo darwin CFLAGS: -D__MAC_OS_X_VERSION_MAX_ALLOWED=101450
+#cgo darwin LDFLAGS: -framework Foundation -framework AppKit
+#include "stdlib.h"
+int function(void);
+*/
+import "C"
+import "fmt"
+
+func DoC() {
+ C.function()
+ fmt.Println("called c function")
+}
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go
new file mode 100644
index 0000000000..9eec6cc5b5
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go
@@ -0,0 +1,11 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "cmd/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
index 0000000000..07d04bb13d
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/typelink.go
@@ -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
index 0000000000..97c9a44c1e
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/util.go
@@ -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
index 0000000000..4d66d6d75e
--- /dev/null
+++ b/src/cmd/oldlink/internal/ld/xcoff.go
@@ -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
index 0000000000..db37db9121
--- /dev/null
+++ b/src/cmd/oldlink/internal/loadelf/ldelf.go
@@ -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
index 0000000000..8c618bfe4d
--- /dev/null
+++ b/src/cmd/oldlink/internal/loader/loader.go
@@ -0,0 +1,629 @@
+// 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/objabi"
+ "cmd/internal/sys"
+ "cmd/oldlink/internal/sym"
+ "fmt"
+ "log"
+ "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 {
+ log.Fatal("-newobj in oldlink should not be used")
+ panic("unreachable")
+}
+
+// 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 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 {
+ panic("unreachable")
+}
+
+// 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)
+}
+
+// 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 {
+ panic("unreachable")
+}
+
+// Check that duplicate symbols have same contents.
+func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) {
+ panic("unreachable")
+}
+
+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 {
+ panic("unreachable")
+}
+
+// Returns the (patched) name of the i-th symbol.
+func (l *Loader) SymName(i Sym) string {
+ panic("unreachable")
+}
+
+// Returns the type of the i-th symbol.
+func (l *Loader) SymType(i Sym) sym.SymKind {
+ panic("unreachable")
+}
+
+// Returns the attributes of the i-th symbol.
+func (l *Loader) SymAttr(i Sym) uint8 {
+ panic("unreachable")
+}
+
+// Returns whether the i-th symbol has ReflectMethod attribute set.
+func (l *Loader) IsReflectMethod(i Sym) bool {
+ panic("unreachable")
+}
+
+// Returns whether this is a Go type symbol.
+func (l *Loader) IsGoType(i Sym) bool {
+ panic("unreachable")
+}
+
+// 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 {
+ panic("unreachable")
+}
+
+// Returns the number of aux symbols given a global index.
+func (l *Loader) NAux(i Sym) int {
+ panic("unreachable")
+}
+
+// Returns the referred symbol of the j-th aux symbol of the i-th
+// symbol.
+func (l *Loader) AuxSym(i Sym, j int) Sym {
+ panic("unreachable")
+}
+
+// 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 {
+ panic("unreachable")
+}
+
+// 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 {
+ panic("unreachable")
+}
+
+// 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 {
+ panic("unreachable")
+}
+
+// Relocs returns a Relocs object for the given global sym.
+func (l *Loader) Relocs(i Sym) Relocs {
+ panic("unreachable")
+}
+
+// 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) {
+ panic("unreachable")
+}
+
+// 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) {
+ panic("unreachable")
+}
+
+func abiToVer(abi uint16, localSymVersion int) int {
+ panic("unreachable")
+}
+
+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 {
+ panic("unreachable")
+}
+
+// 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 {
+ panic("unreachable")
+}
+
+// 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) {
+ panic("unreachable")
+}
+
+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
index 0000000000..8982180b9c
--- /dev/null
+++ b/src/cmd/oldlink/internal/loadmacho/ldmacho.go
@@ -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
index 0000000000..f7df774661
--- /dev/null
+++ b/src/cmd/oldlink/internal/loadpe/ldpe.go
@@ -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
index 0000000000..832b1681c7
--- /dev/null
+++ b/src/cmd/oldlink/internal/loadxcoff/ldxcoff.go
@@ -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
index 0000000000..48a2324151
--- /dev/null
+++ b/src/cmd/oldlink/internal/mips/asm.go
@@ -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
index 0000000000..adbde40f7c
--- /dev/null
+++ b/src/cmd/oldlink/internal/mips/l.go
@@ -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
index 0000000000..c80824ff14
--- /dev/null
+++ b/src/cmd/oldlink/internal/mips/obj.go
@@ -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
index 0000000000..775b74e1fd
--- /dev/null
+++ b/src/cmd/oldlink/internal/mips64/asm.go
@@ -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
index 0000000000..d794122f0b
--- /dev/null
+++ b/src/cmd/oldlink/internal/mips64/l.go
@@ -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
index 0000000000..1ddce45c06
--- /dev/null
+++ b/src/cmd/oldlink/internal/mips64/obj.go
@@ -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/link/internal/objfile/objfile.go b/src/cmd/oldlink/internal/objfile/objfile.go
index 295acb2d29..3a59f6a624 100644
--- a/src/cmd/link/internal/objfile/objfile.go
+++ b/src/cmd/oldlink/internal/objfile/objfile.go
@@ -13,10 +13,11 @@ import (
"bytes"
"cmd/internal/bio"
"cmd/internal/dwarf"
+ "cmd/internal/goobj2"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
- "cmd/link/internal/sym"
+ "cmd/oldlink/internal/sym"
"fmt"
"io"
"log"
@@ -117,6 +118,9 @@ func (r *objReader) loadObjFile() {
var buf [8]uint8
r.readFull(buf[:])
if string(buf[:]) != startmagic {
+ if string(buf[:]) == goobj2.Magic {
+ log.Fatalf("found object file %s in new format, but -go115newobj is false\nset -go115newobj consistently in all -gcflags, -asmflags, and -ldflags", r.pn)
+ }
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])
}
diff --git a/src/cmd/oldlink/internal/ppc64/asm.go b/src/cmd/oldlink/internal/ppc64/asm.go
new file mode 100644
index 0000000000..608bdecca6
--- /dev/null
+++ b/src/cmd/oldlink/internal/ppc64/asm.go
@@ -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
index 0000000000..c78535be58
--- /dev/null
+++ b/src/cmd/oldlink/internal/ppc64/l.go
@@ -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
index 0000000000..e9da5a36d5
--- /dev/null
+++ b/src/cmd/oldlink/internal/ppc64/obj.go
@@ -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
index 0000000000..f4db32df8a
--- /dev/null
+++ b/src/cmd/oldlink/internal/riscv64/asm.go
@@ -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
index 0000000000..a302657726
--- /dev/null
+++ b/src/cmd/oldlink/internal/riscv64/l.go
@@ -0,0 +1,14 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package riscv64
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1
+ funcAlign = 8
+
+ dwarfRegLR = 1
+ dwarfRegSP = 2
+)
diff --git a/src/cmd/oldlink/internal/riscv64/obj.go b/src/cmd/oldlink/internal/riscv64/obj.go
new file mode 100644
index 0000000000..a6a5adb86c
--- /dev/null
+++ b/src/cmd/oldlink/internal/riscv64/obj.go
@@ -0,0 +1,60 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package riscv64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/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
index 0000000000..6c5744dc95
--- /dev/null
+++ b/src/cmd/oldlink/internal/s390x/asm.go
@@ -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
index 0000000000..87d10ee782
--- /dev/null
+++ b/src/cmd/oldlink/internal/s390x/l.go
@@ -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
index 0000000000..b4af86bd75
--- /dev/null
+++ b/src/cmd/oldlink/internal/s390x/obj.go
@@ -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
index 0000000000..4b69bf32d0
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/attribute.go
@@ -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
index 0000000000..02fb0cfab8
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/compilation_unit.go
@@ -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
index 0000000000..4f2023b8f7
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/library.go
@@ -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
index 0000000000..4809db8c80
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/reloc.go
@@ -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
index 0000000000..d5255bf142
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/segment.go
@@ -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
index 0000000000..e6e3916dad
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/sizeof_test.go
@@ -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
index 0000000000..2756acd211
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/symbol.go
@@ -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
index 0000000000..e772496534
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/symbols.go
@@ -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
index 0000000000..1933dd7b21
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/symkind.go
@@ -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
index 0000000000..97af9925d5
--- /dev/null
+++ b/src/cmd/oldlink/internal/sym/symkind_string.go
@@ -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
index 0000000000..35bc7b1c95
--- /dev/null
+++ b/src/cmd/oldlink/internal/wasm/asm.go
@@ -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
index 0000000000..fdc9fb796a
--- /dev/null
+++ b/src/cmd/oldlink/internal/wasm/obj.go
@@ -0,0 +1,35 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package wasm
+
+import (
+ "cmd/internal/sys"
+ "cmd/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
index 0000000000..34668063da
--- /dev/null
+++ b/src/cmd/oldlink/internal/x86/asm.go
@@ -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
index 0000000000..0f104eab57
--- /dev/null
+++ b/src/cmd/oldlink/internal/x86/l.go
@@ -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
index 0000000000..b78ccbaf99
--- /dev/null
+++ b/src/cmd/oldlink/internal/x86/obj.go
@@ -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/main.go b/src/cmd/oldlink/main.go
new file mode 100644
index 0000000000..be1d0fde3b
--- /dev/null
+++ b/src/cmd/oldlink/main.go
@@ -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)
+}