aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCherry Zhang <cherryyz@google.com>2020-04-02 15:04:24 -0400
committerCherry Zhang <cherryyz@google.com>2020-04-02 15:51:19 -0400
commit48a90d639d578d2b33fdc1903f03e028b4d40fa9 (patch)
tree882f5003a645cb33647331aa2fd26ede618965f7
parent9baafabac9a84813a336f068862207d2bb06d255 (diff)
parent6435590182bb06f12c5caae749855390a981a37f (diff)
downloadgo-48a90d639d578d2b33fdc1903f03e028b4d40fa9.tar.gz
go-48a90d639d578d2b33fdc1903f03e028b4d40fa9.zip
cmd: merge branch 'dev.link' into master
In the dev.link branch we continued developing the new object file format support and the linker improvements described in https://golang.org/s/better-linker . The new object file is index-based and provides random access. The linker maps the object files into read-only memory, and accesses symbols on-demand using indices, as opposed to reading all object files sequentially into the heap with the old format. This work is not done yet. Currently we still convert back to the old in-memory representation half way through the link process, but only for symbols that are needed. At this point, we think it is ready to enable the new object files and new linker for early testing. Using the new object files and the new linker, it reduces the linker's memory usage by ~10% and wall-clock run time by ~5%, and more to come. Currently, both the old and new object file formats are supported. The new format and new linker are used by default. For feature gating, as a fallback, the old format and old linker can be used by setting the compiler/assembler/linker's -go115newobj flag to false. Note that the flag needs to be specified consistently to all compilations, i.e. -gcflags=all=-go115newobj=false -asmflags=all=-go115newobj=false -ldflags=all=-go115newobj=false In case we need to revert, we can set the flags default to false. CL 224626 is an example. cmd/oldlink is a full copy of the old linker. It is invoked if the old format is requested. This is a clean merge, as we already merged master branch to dev.link first. Change-Id: I8f081eef8c4621362f03ecbcb850d6262b5d3dc6
-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)
+}