From adea6a90e361629d20a68400c0c5cdcdfcdf087e Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Thu, 23 Apr 2020 08:43:29 -0400 Subject: [PATCH 01/42] [dev.link] cmd/link/internal/loader: fix buglet in section handling Allow for the possibility that a client could call newExtSym(), then ask for the section of the new sym before SetSectSym is called on it (check in SymSect for this case). Change-Id: I7bd78e7b3b7618943705b616f62ea78c4a1b68d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/229603 Reviewed-by: Jeremy Faller --- src/cmd/link/internal/loader/loader.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 987feeb284..ba0cff3ff6 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1075,6 +1075,12 @@ func (l *Loader) SetSymAlign(i Sym, align int32) { // SymValue returns the section of the i-th symbol. i is global index. func (l *Loader) SymSect(i Sym) *sym.Section { + if int(i) >= len(l.symSects) { + // symSects is extended lazily -- it the sym in question is + // outside the range of the existing slice, then we assume its + // section has not yet been set. + return nil + } return l.sects[l.symSects[i]] } From 442fd182fba66a11f9cbe3321217e8b4618fcfc7 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Thu, 23 Apr 2020 09:18:44 -0400 Subject: [PATCH 02/42] [dev.link] cmd/link/internal/loader: add SetRelocType symbolbuilder method Clients may need to invoke the loader.Reloc2.SetType method to reset the type of a relocation from external flavor to internal flavor, meaning that the external type add-in needs to be zeroed (this is needed when adding dynsym entries). Add a new SymbolBuider method to support mutating the type of a reloc for an external symbol, so that the external type can be changed as well (Reloc2 doesn't have access to that). Also add similar methods for updating target symbol and addend, so as to have a consistent interface for ext reloc mutation. Change-Id: I8e26cdae0a0f353019acba5f9c8a0506e3970266 Reviewed-on: https://go-review.googlesource.com/c/go/+/229604 Reviewed-by: Cherry Zhang Reviewed-by: Jeremy Faller --- src/cmd/link/internal/loader/loader.go | 6 +++++- src/cmd/link/internal/loader/symbolbuilder.go | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index ba0cff3ff6..c0893daece 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -58,7 +58,7 @@ type Reloc2 struct { // 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. + // For Go symbols this will always be zero. // goobj2.Reloc2.Type() + typ is always the right type, for both Go and external // symbols. typ objabi.RelocType @@ -73,6 +73,10 @@ func (rel Reloc2) SetType(t objabi.RelocType) { panic("SetType: type doesn't fit into Reloc2") } rel.Reloc.SetType(uint8(t)) + if rel.typ != 0 { + // should use SymbolBuilder.SetRelocType + panic("wrong method to set reloc type") + } } // Aux2 holds a "handle" to access an aux symbol record from an diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go index f5db69856b..21ed621f80 100644 --- a/src/cmd/link/internal/loader/symbolbuilder.go +++ b/src/cmd/link/internal/loader/symbolbuilder.go @@ -143,6 +143,22 @@ func (sb *SymbolBuilder) SetRelocs(rslice []Reloc) { } } +// SetRelocType sets the type of the 'i'-th relocation on this sym to 't' +func (sb *SymbolBuilder) SetRelocType(i int, t objabi.RelocType) { + sb.relocs[i].SetType(0) + sb.reltypes[i] = t +} + +// SetRelocSym sets the target sym of the 'i'-th relocation on this sym to 's' +func (sb *SymbolBuilder) SetRelocSym(i int, tgt Sym) { + sb.relocs[i].SetSym(goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(tgt)}) +} + +// SetRelocAdd sets the addend of the 'i'-th relocation on this sym to 'a' +func (sb *SymbolBuilder) SetRelocAdd(i int, a int64) { + sb.relocs[i].SetAdd(a) +} + // Add n relocations, return a handle to the relocations. func (sb *SymbolBuilder) AddRelocs(n int) Relocs { sb.relocs = append(sb.relocs, make([]goobj2.Reloc, n)...) From 3d1007d28e92316b08974dc65cde44a6aa31bf21 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Wed, 22 Apr 2020 15:16:06 -0400 Subject: [PATCH 03/42] [dev.link] cmd/link: move more error handling into loader Move the guts of ctxt.Errorf into loader.Loader, so that we can make calls to it from functions that have a "*loader.Loader" available but not a "ctxt *Link". This is needed to start converting hooks like "adddynrel" in the arch-specific portions of the linker to use loader APIs. Change-Id: Ieedd4583b66504be0e77d7f3fbadafe0d2307a69 Reviewed-on: https://go-review.googlesource.com/c/go/+/229497 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/errors.go | 23 +---------- src/cmd/link/internal/ld/lib.go | 2 +- src/cmd/link/internal/ld/sym.go | 15 ++++--- src/cmd/link/internal/ld/util.go | 10 ++--- src/cmd/link/internal/loader/loader.go | 45 ++++++++++++++++++++- src/cmd/link/internal/loader/loader_test.go | 17 +++++--- 6 files changed, 70 insertions(+), 42 deletions(-) diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go index e66c518b85..61289b4798 100644 --- a/src/cmd/link/internal/ld/errors.go +++ b/src/cmd/link/internal/ld/errors.go @@ -7,8 +7,6 @@ import ( "cmd/internal/obj" "cmd/link/internal/loader" "cmd/link/internal/sym" - "fmt" - "os" "sync" ) @@ -22,6 +20,7 @@ type symNameFn func(s loader.Sym) string // ErrorReporter is used to make error reporting thread safe. type ErrorReporter struct { + loader.ErrorReporter unresOnce sync.Once unresSyms map[unresolvedSymKey]bool unresMutex sync.Mutex @@ -65,23 +64,3 @@ func (reporter *ErrorReporter) errorUnresolved(s *sym.Symbol, r *sym.Reloc) { } } } - -// 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/lib.go b/src/cmd/link/internal/ld/lib.go index 429c2641fb..675103ee45 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -503,7 +503,7 @@ func (ctxt *Link) loadlib() { default: log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) } - ctxt.loader = loader.NewLoader(flags, elfsetstring) + ctxt.loader = loader.NewLoader(flags, elfsetstring, &ctxt.ErrorReporter.ErrorReporter) ctxt.ErrorReporter.SymName = func(s loader.Sym) string { return ctxt.loader.SymName(s) } diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go index 6a8b3dbed1..7a6c4e43e9 100644 --- a/src/cmd/link/internal/ld/sym.go +++ b/src/cmd/link/internal/ld/sym.go @@ -34,19 +34,22 @@ package ld import ( "cmd/internal/objabi" "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "log" "runtime" ) func linknew(arch *sys.Arch) *Link { + ler := loader.ErrorReporter{AfterErrorAction: afterErrorAction} ctxt := &Link{ - Target: Target{Arch: arch}, - Syms: sym.NewSymbols(), - outSem: make(chan int, 2*runtime.GOMAXPROCS(0)), - Out: NewOutBuf(arch), - LibraryByPkg: make(map[string]*sym.Library), - numelfsym: 1, + Target: Target{Arch: arch}, + Syms: sym.NewSymbols(), + outSem: make(chan int, 2*runtime.GOMAXPROCS(0)), + Out: NewOutBuf(arch), + LibraryByPkg: make(map[string]*sym.Library), + numelfsym: 1, + ErrorReporter: ErrorReporter{ErrorReporter: ler}, } if objabi.GOARCH != arch.Name { diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go index 9f257b8fc0..b8c9ce28b0 100644 --- a/src/cmd/link/internal/ld/util.go +++ b/src/cmd/link/internal/ld/util.go @@ -73,12 +73,12 @@ func Errorf(s *sym.Symbol, format string, args ...interface{}) { // 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) + if ctxt.loader != nil { + ctxt.loader.Errorf(s, format, args) + return } + // Note: this is not expected to happen very often. + format = fmt.Sprintf("sym %d: %s", s, format) format += "\n" fmt.Fprintf(os.Stderr, format, args...) afterErrorAction() diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index c0893daece..458c87a6b6 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -270,6 +270,8 @@ type Loader struct { elfsetstring elfsetstringFunc + errorReporter *ErrorReporter + SymLookup func(name string, ver int) *sym.Symbol } @@ -301,9 +303,9 @@ const ( FlagStrictDups = 1 << iota ) -func NewLoader(flags uint32, elfsetstring elfsetstringFunc) *Loader { +func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorReporter) *Loader { nbuiltin := goobj2.NBuiltin() - return &Loader{ + ldr := &Loader{ start: make(map[*oReader]Sym), objs: []objIdx{{}}, // reserve index 0 for nil symbol objSyms: []objSym{{}}, // reserve index 0 for nil symbol @@ -332,8 +334,11 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc) *Loader { builtinSyms: make([]Sym, nbuiltin), flags: flags, elfsetstring: elfsetstring, + errorReporter: reporter, sects: []*sym.Section{nil}, // reserve index 0 for nil section } + reporter.ldr = ldr + return ldr } // Add object file r, return the start index. @@ -2754,6 +2759,42 @@ func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, exts return textp2 } +// ErrorReporter is a helper class for reporting errors. +type ErrorReporter struct { + ldr *Loader + AfterErrorAction func() +} + +// Errorf method logs an error message. +// +// After each error, the error actions function will be invoked; this +// will either terminate the link immediately (if -h option given) +// or it will keep a count and exit if more than 20 errors have been printed. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +// +func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) { + if s != 0 && reporter.ldr.SymName(s) != "" { + format = reporter.ldr.SymName(s) + ": " + format + } else { + format = fmt.Sprintf("sym %d: %s", s, format) + } + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + reporter.AfterErrorAction() +} + +// GetErrorReporter returns the loader's associated error reporter. +func (l *Loader) GetErrorReporter() *ErrorReporter { + return l.errorReporter +} + +// Errorf method logs an error message. See ErrorReporter.Errorf for details. +func (l *Loader) Errorf(s Sym, format string, args ...interface{}) { + l.errorReporter.Errorf(s, format, args) +} + // For debugging. func (l *Loader) Dump() { fmt.Println("objs") diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go index b2f823d17e..60ef69afb9 100644 --- a/src/cmd/link/internal/loader/loader_test.go +++ b/src/cmd/link/internal/loader/loader_test.go @@ -27,9 +27,16 @@ func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym { return s } -func TestAddMaterializedSymbol(t *testing.T) { +func mkLoader() *Loader { edummy := func(s *sym.Symbol, str string, off int) {} - ldr := NewLoader(0, edummy) + er := ErrorReporter{} + ldr := NewLoader(0, edummy, &er) + er.ldr = ldr + return ldr +} + +func TestAddMaterializedSymbol(t *testing.T) { + ldr := mkLoader() dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} or := &dummyOreader @@ -229,8 +236,7 @@ func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool { 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) + ldr := mkLoader() dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} or := &dummyOreader @@ -352,8 +358,7 @@ func TestAddDataMethods(t *testing.T) { } func TestOuterSub(t *testing.T) { - edummy := func(s *sym.Symbol, str string, off int) {} - ldr := NewLoader(0, edummy) + ldr := mkLoader() dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} or := &dummyOreader From 941de9760bcf35ad1ef4eee23d2851d3f2fa087b Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Tue, 21 Apr 2020 18:37:43 -0400 Subject: [PATCH 04/42] [dev.link] cmd/link: begin converting dodata() to loader APIs This patch begins the work of converting the linker's dodata phase to work with loader APIs. Passes all.bash on linux/amd64, but hasn't been tested on anything else (more arch-specific code needs to be written). Use of the new dodata() phase is currently gated by a temporary command line flag ("-newdodata"), and there is code in the linker's main routine to insure that we only use the new version for the right GOOS/GOARCH (currently restricted to ELF + AMD64). Change-Id: Ied3966677d2a450bc3e0990e0f519b3fceaab806 Reviewed-on: https://go-review.googlesource.com/c/go/+/229706 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/link/internal/amd64/asm.go | 258 ++--- src/cmd/link/internal/amd64/asm2.go | 402 ++++++++ src/cmd/link/internal/amd64/obj.go | 1 + src/cmd/link/internal/ld/data.go | 723 +++++++------- src/cmd/link/internal/ld/data2.go | 938 ++++++++++++++++++ src/cmd/link/internal/ld/elf.go | 81 +- src/cmd/link/internal/ld/elf2.go | 166 ++++ src/cmd/link/internal/ld/go.go | 8 +- src/cmd/link/internal/ld/lib.go | 34 +- src/cmd/link/internal/ld/link.go | 1 + src/cmd/link/internal/ld/main.go | 22 +- src/cmd/link/internal/ld/symtab.go | 7 +- src/cmd/link/internal/ld/xcoff.go | 10 +- src/cmd/link/internal/ld/xcoff2.go | 52 + src/cmd/link/internal/loader/loader.go | 50 +- src/cmd/link/internal/loader/symbolbuilder.go | 7 + src/cmd/link/internal/ppc64/asm.go | 2 +- src/cmd/link/internal/sym/segment.go | 5 +- 18 files changed, 2207 insertions(+), 560 deletions(-) create mode 100644 src/cmd/link/internal/amd64/asm2.go create mode 100644 src/cmd/link/internal/ld/data2.go create mode 100644 src/cmd/link/internal/ld/elf2.go create mode 100644 src/cmd/link/internal/ld/xcoff2.go diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index d26a9a234c..fa0f6ab9b5 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -78,48 +78,53 @@ func makeWritable(s *sym.Symbol) { } } -func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { - targ := r.Sym +func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r *loader.Reloc2, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } - switch r.Type { + su := ldr.MakeSymbolUpdater(s) + switch r.Type() { default: - if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) return false } // Handle relocations found in ELF object files. case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC32): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_X86_64_PC32 relocation for dynamic symbol %s", ldr.SymName(targ)) } // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make // sense and should be removed when someone has thought about it properly. - if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { - ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) } - r.Type = objabi.R_PCREL - r.Add += 4 + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC64): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_X86_64_PC64 relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_X86_64_PC64 relocation for dynamic symbol %s", ldr.SymName(targ)) } - if targ.Type == 0 || targ.Type == sym.SXREF { - ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + if targType == 0 || targType == sym.SXREF { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) } - r.Type = objabi.R_PCREL - r.Add += 8 + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, 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(target, syms, targ) - r.Sym = syms.PLT - r.Add += int64(targ.Plt()) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) } return true @@ -127,34 +132,35 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. 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 { + if targType != sym.SDYNIMPORT { // have symbol - if r.Off >= 2 && s.P[r.Off-2] == 0x8b { - makeWritable(s) + sData := ldr.Data(s) + if r.Off() >= 2 && sData[r.Off()-2] == 0x8b { + su := ldr.MakeSymbolUpdater(s) + su.MakeWritable() // turn MOVQ of GOT entry into LEAQ of symbol itself - s.P[r.Off-2] = 0x8d - - r.Type = objabi.R_PCREL - r.Add += 4 + writeableData := su.Data() + writeableData[r.Off()-2] = 0x8d + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) return true } } // fall back to using GOT and hope for the best (CMOV*) // TODO: just needs relocation, no need to put in .dynsym - addgotsym(target, syms, targ) + addgotsym2(target, ldr, syms, targ) - r.Type = objabi.R_PCREL - r.Sym = syms.GOT - r.Add += 4 - r.Add += int64(targ.Got()) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ))) 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) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ADDR + su.SetRelocType(rIdx, objabi.R_ADDR) if target.IsPIE() && target.IsInternal() { // For internal linking PIE, this R_ADDR relocation cannot // be resolved statically. We need to generate a dynamic @@ -168,19 +174,19 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. 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 + su.SetRelocType(rIdx, objabi.R_ADDR) - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected reloc for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ)) } return true case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: - if targ.Type == sym.SDYNIMPORT { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(targ.Plt()) - r.Type = objabi.R_PCREL + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) return true } fallthrough @@ -190,44 +196,51 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. 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 + su.SetRelocType(rIdx, objabi.R_PCREL) - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected pc-relative reloc for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected pc-relative reloc for dynamic symbol %s", ldr.SymName(targ)) } return true case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: - if targ.Type != sym.SDYNIMPORT { + if targType != 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) + sdata := ldr.Data(s) + if r.Off() < 2 || sdata[r.Off()-2] != 0x8b { + ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ)) return false } - makeWritable(s) - s.P[r.Off-2] = 0x8d - r.Type = objabi.R_PCREL + su := ldr.MakeSymbolUpdater(s) + su.MakeWritable() + sdata = su.Data() + sdata[r.Off()-2] = 0x8d + su.SetRelocType(rIdx, objabi.R_PCREL) return true } fallthrough case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT*2 + 1: - if targ.Type != sym.SDYNIMPORT { - ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + if targType != sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) } - addgotsym(target, syms, targ) - r.Type = objabi.R_PCREL - r.Sym = syms.GOT - r.Add += int64(targ.Got()) + addgotsym2(target, ldr, syms, targ) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) return true } - switch r.Type { + // Reread the reloc to incorporate any changes in type above. + relocs := ldr.Relocs(s) + *r = relocs.At2(rIdx) + + switch r.Type() { case objabi.R_CALL, objabi.R_PCREL: - if targ.Type != sym.SDYNIMPORT { + if targType != sym.SDYNIMPORT { // nothing to do, the relocation will be laid out in reloc return true } @@ -237,26 +250,26 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. } // Internal linking, for both ELF and Mach-O. // Build a PLT entry and change the relocation target to that entry. - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(targ.Plt()) + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) return true case objabi.R_ADDR: - if s.Type == sym.STEXT && target.IsElf() { + if ldr.SymType(s) == sym.STEXT && target.IsElf() { if target.IsSolaris() { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add += int64(targ.Plt()) + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) return true } // The code is asking for the address of an external // function. We provide it with the address of the // correspondent GOT symbol. - addgotsym(target, syms, targ) + addgotsym2(target, ldr, syms, targ) - r.Sym = syms.GOT - r.Add += int64(targ.Got()) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) return true } @@ -293,7 +306,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. // symbol offset as determined by reloc(), not the // final dynamically linked address as a dynamic // relocation would provide. - switch s.Name { + switch ldr.SymName(s) { case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic": return false } @@ -304,7 +317,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. // 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 { + if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA { break } } @@ -327,14 +340,14 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. // 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 := syms.Rela - rela.AddAddrPlus(target.Arch, s, int64(r.Off)) - if r.Siz == 8 { + rela := ldr.MakeSymbolUpdater(syms.Rela2) + rela.AddAddrPlus(target.Arch, s, int64(r.Off())) + if r.Siz() == 8 { 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) + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) } - rela.AddAddrPlus(target.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 @@ -342,7 +355,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. return true } - if target.IsDarwin() && s.Size == int64(target.Arch.PtrSize) && r.Off == 0 { + if target.IsDarwin() && ldr.SymSize(s) == 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. @@ -353,18 +366,17 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. // 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(target, syms, targ) + ld.Adddynsym2(ldr, target, syms, targ) - 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 := ldr.MakeSymbolUpdater(syms.GOT2) + su := ldr.MakeSymbolUpdater(s) + su.SetType(got.Type()) + got.PrependSub(s) + su.SetValue(got.Size()) got.AddUint64(target.Arch, 0) - syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid)) - r.Type = objabi.ElfRelocOffset // ignore during relocsym + leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT2) + leg.AddUint32(target.Arch, uint32(ldr.SymDynid(targ))) + su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym return true } } @@ -569,18 +581,18 @@ func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.S } } -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { +func addpltsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { return } - ld.Adddynsym(target, syms, s) + ld.Adddynsym2(ldr, target, syms, s) if target.IsElf() { - plt := syms.PLT - got := syms.GOTPLT - rela := syms.RelaPLT - if plt.Size == 0 { + plt := ldr.MakeSymbolUpdater(syms.PLT2) + got := ldr.MakeSymbolUpdater(syms.GOTPLT2) + rela := ldr.MakeSymbolUpdater(syms.RelaPLT2) + if plt.Size() == 0 { panic("plt is not set up") } @@ -588,28 +600,29 @@ func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddPCRelPlus(target.Arch, got, got.Size) + plt.AddPCRelPlus(target.Arch, got.Sym(), got.Size()) // add to got: pointer to current pos in plt - got.AddAddrPlus(target.Arch, plt, plt.Size) + got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()) // pushq $x plt.AddUint8(0x68) - plt.AddUint32(target.Arch, uint32((got.Size-24-8)/8)) + plt.AddUint32(target.Arch, uint32((got.Size()-24-8)/8)) // jmpq .plt plt.AddUint8(0xe9) - plt.AddUint32(target.Arch, uint32(-(plt.Size + 4))) + plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4))) // rela - rela.AddAddrPlus(target.Arch, got, got.Size-8) + rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8) - rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_JMP_SLOT))) + sDynid := ldr.SymDynid(s) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(sDynid), uint32(elf.R_X86_64_JMP_SLOT))) rela.AddUint64(target.Arch, 0) - s.SetPlt(int32(plt.Size - 16)) + ldr.SetPlt(s, int32(plt.Size()-16)) } else if target.IsDarwin() { // To do lazy symbol lookup right, we're supposed // to tell the dynamic loader which library each @@ -621,41 +634,44 @@ func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { // https://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html // has details about what we're avoiding. - addgotsym(target, syms, s) - plt := syms.PLT + addgotsym2(target, ldr, syms, s) + plt := ldr.MakeSymbolUpdater(syms.PLT2) - syms.LinkEditPLT.AddUint32(target.Arch, uint32(s.Dynid)) + sDynid := ldr.SymDynid(s) + lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT2) + lep.AddUint32(target.Arch, uint32(sDynid)) // jmpq *got+size(IP) - s.SetPlt(int32(plt.Size)) + ldr.SetPlt(s, int32(plt.Size())) plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddPCRelPlus(target.Arch, syms.GOT, int64(s.Got())) + plt.AddPCRelPlus(target.Arch, syms.GOT2, int64(ldr.SymGot(s))) } else { - ld.Errorf(s, "addpltsym: unsupported binary format") + ldr.Errorf(s, "addpltsym: unsupported binary format") } } -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { +func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymGot(s) >= 0 { return } - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) + ld.Adddynsym2(ldr, target, syms, s) + got := ldr.MakeSymbolUpdater(syms.GOT2) + ldr.SetGot(s, int32(got.Size())) 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 := ldr.MakeSymbolUpdater(syms.Rela2) + rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_X86_64_GLOB_DAT))) rela.AddUint64(target.Arch, 0) } else if target.IsDarwin() { - syms.LinkEditGOT.AddUint32(target.Arch, uint32(s.Dynid)) + leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT2) + leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s))) } else { - ld.Errorf(s, "addgotsym: unsupported binary format") + ldr.Errorf(s, "addgotsym: unsupported binary format") } } diff --git a/src/cmd/link/internal/amd64/asm2.go b/src/cmd/link/internal/amd64/asm2.go new file mode 100644 index 0000000000..ae12784105 --- /dev/null +++ b/src/cmd/link/internal/amd64/asm2.go @@ -0,0 +1,402 @@ +// 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 amd64 + +import ( + "cmd/internal/objabi" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +// Temporary dumping around for sym.Symbol version of helper +// functions in asm.go, still being used for some oses. +// FIXME: get rid of this file when dodata() is completely +// converted. + +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(target.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(target, syms, targ) + r.Sym = syms.PLT + 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 { + makeWritable(s) + // 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(target, syms, targ) + + r.Type = objabi.R_PCREL + r.Sym = syms.GOT + 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 target.IsPIE() && target.IsInternal() { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + break + } + return true + + // Handle relocations found in Mach-O object files. + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0: + // 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(target, syms, targ) + r.Sym = syms.PLT + 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 + } + + makeWritable(s) + 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(target, syms, targ) + r.Type = objabi.R_PCREL + r.Sym = syms.GOT + 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 target.IsExternal() { + // External linker will do this relocation. + return true + } + // Internal linking, for both ELF and Mach-O. + // Build a PLT entry and change the relocation target to that entry. + addpltsym(target, syms, targ) + r.Sym = syms.PLT + r.Add = int64(targ.Plt()) + return true + + case objabi.R_ADDR: + 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(target, syms, targ) + + r.Sym = syms.GOT + r.Add += int64(targ.Got()) + return true + } + + // Process dynamic relocations for the data sections. + if target.IsPIE() && target.IsInternal() { + // When internally linking, generate dynamic relocations + // for all typical R_ADDR relocations. The exception + // are those R_ADDR that are created as part of generating + // the dynamic relocations and must be resolved statically. + // + // There are three phases relevant to understanding this: + // + // dodata() // we are here + // address() // symbol address assignment + // reloc() // resolution of static R_ADDR relocs + // + // At this point symbol addresses have not been + // assigned yet (as the final size of the .rela section + // will affect the addresses), and so we cannot write + // the Elf64_Rela.r_offset now. Instead we delay it + // until after the 'address' phase of the linker is + // complete. We do this via Addaddrplus, which creates + // a new R_ADDR relocation which will be resolved in + // the 'reloc' phase. + // + // These synthetic static R_ADDR relocs must be skipped + // now, or else we will be caught in an infinite loop + // of generating synthetic relocs for our synthetic + // relocs. + // + // Furthermore, the rela sections contain dynamic + // relocations with R_ADDR relocations on + // Elf64_Rela.r_offset. This field should contain the + // symbol offset as determined by reloc(), not the + // final dynamically linked address as a dynamic + // relocation would provide. + switch 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 target.IsElf() { + // Generate R_X86_64_RELATIVE relocations for best + // efficiency in the dynamic linker. + // + // As noted above, symbol addresses have not been + // assigned yet, so we can't generate the final reloc + // entry yet. We ultimately want: + // + // r_offset = s + r.Off + // r_info = R_X86_64_RELATIVE + // r_addend = targ + r.Add + // + // The dynamic linker will set *offset = base address + + // addend. + // + // AddAddrPlus is used for r_offset and r_addend to + // generate new R_ADDR relocations that will update + // these fields in the 'reloc' phase. + rela := syms.Rela + rela.AddAddrPlus(target.Arch, s, int64(r.Off)) + if r.Siz == 8 { + 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(target.Arch, targ, int64(r.Add)) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). + return true + } + + if target.IsDarwin() && 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. + // 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(target, syms, targ) + + 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(target.Arch, 0) + syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid)) + r.Type = objabi.ElfRelocOffset // ignore during relocsym + return true + } + } + + return false +} + +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + + if target.IsElf() { + plt := syms.PLT + got := syms.GOTPLT + rela := syms.RelaPLT + if plt.Size == 0 { + panic("plt is not set up") + } + + // jmpq *got+size(IP) + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddPCRelPlus(target.Arch, got, got.Size) + + // add to got: pointer to current pos in plt + got.AddAddrPlus(target.Arch, plt, plt.Size) + + // pushq $x + plt.AddUint8(0x68) + + plt.AddUint32(target.Arch, uint32((got.Size-24-8)/8)) + + // jmpq .plt + plt.AddUint8(0xe9) + + plt.AddUint32(target.Arch, uint32(-(plt.Size + 4))) + + // rela + rela.AddAddrPlus(target.Arch, got, got.Size-8) + + 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 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 + // 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(target, syms, s) + plt := syms.PLT + + 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(target.Arch, syms.GOT, int64(s.Got())) + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + got := syms.GOT + s.SetGot(int32(got.Size)) + 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") + } +} diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go index fec775f142..b9d0bf4b83 100644 --- a/src/cmd/link/internal/amd64/obj.go +++ b/src/cmd/link/internal/amd64/obj.go @@ -47,6 +47,7 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfreglr: dwarfRegLR, Adddynrel: adddynrel, + Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, Archrelocvariant: archrelocvariant, diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 5cd7727cd0..505f22d595 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -677,32 +677,38 @@ func (ctxt *Link) windynrelocsyms() { ctxt.Textp2 = append(ctxt.Textp2, rel) } -func dynrelocsym(ctxt *Link, s *sym.Symbol) { +func dynrelocsym2(ctxt *Link, s loader.Sym) { target := &ctxt.Target ldr := ctxt.loader syms := &ctxt.ArchSyms - for ri := range s.R { - r := &s.R[ri] + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At2(ri) + // FIXME: the call to Adddynrel2 below is going to wind up + // eagerly promoting the symbol to external, which is not great-- + // it would improve things for internal/PIE if we could + // create the symbol updater lazily. if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal { // It's expected that some relocations will be done // later by relocsym (R_TLS_LE, R_ADDROFF), so // don't worry if Adddynrel returns false. - thearch.Adddynrel(target, ldr, syms, s, r) + thearch.Adddynrel2(target, ldr, syms, s, &r, ri) 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) + rSym := r.Sym() + if rSym != 0 && ldr.SymType(rSym) == sym.SDYNIMPORT || r.Type() >= objabi.ElfRelocOffset { + if rSym != 0 && !ldr.AttrReachable(rSym) { + ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s", ldr.SymName(rSym)) } - if !thearch.Adddynrel(target, ldr, syms, s, r) { - 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) + if !thearch.Adddynrel2(target, ldr, syms, s, &r, ri) { + ctxt.Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", ldr.SymName(rSym), r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymType(rSym), ldr.SymType(rSym)) } } } } -func (state *dodataState) dynreloc(ctxt *Link) { +func (state *dodataState) dynreloc2(ctxt *Link) { if ctxt.HeadType == objabi.Hwindows { return } @@ -712,16 +718,16 @@ func (state *dodataState) dynreloc(ctxt *Link) { return } - for _, s := range ctxt.Textp { - dynrelocsym(ctxt, s) + for _, s := range ctxt.Textp2 { + dynrelocsym2(ctxt, s) } - for _, syms := range state.data { + for _, syms := range state.data2 { for _, s := range syms { - dynrelocsym(ctxt, s) + dynrelocsym2(ctxt, s) } } if ctxt.IsELF { - elfdynhash(ctxt) + elfdynhash2(ctxt) } } @@ -1120,21 +1126,6 @@ func (ctxt *Link) dostrdata() { } } -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, ldr *loader.Loader, s *loader.SymbolBuilder, symname, str string) { @@ -1161,102 +1152,37 @@ func addinitarrdata(ctxt *Link, ldr *loader.Loader, s loader.Sym) { } // symalign returns the required alignment for the given symbol s. -func symalign(s *sym.Symbol) int32 { +func (state *dodataState) symalign2(s loader.Sym) int32 { min := int32(thearch.Minalign) - if s.Align >= min { - return s.Align - } else if s.Align != 0 { + ldr := state.ctxt.loader + align := ldr.SymAlign(s) + if align >= min { + return align + } else if align != 0 { return min } - if strings.HasPrefix(s.Name, "go.string.") || strings.HasPrefix(s.Name, "type..namedata.") { + // FIXME: figure out a way to avoid checking by name here. + sname := ldr.SymName(s) + if strings.HasPrefix(sname, "go.string.") || strings.HasPrefix(sname, "type..namedata.") { // String data is just bytes. // If we align it, we waste a lot of space to padding. return min } - align := int32(thearch.Maxalign) - for int64(align) > s.Size && align > min { + align = int32(thearch.Maxalign) + ssz := ldr.SymSize(s) + for int64(align) > ssz && align > min { align >>= 1 } - s.Align = align + ldr.SetSymAlign(s, align) return align } -func aligndatsize(datsize int64, s *sym.Symbol) int64 { - return Rnd(datsize, int64(symalign(s))) +func aligndatsize2(state *dodataState, datsize int64, s loader.Sym) int64 { + return Rnd(datsize, int64(state.symalign2(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) -} - type GCProg2 struct { ctxt *Link sym *loader.SymbolBuilder @@ -1332,26 +1258,6 @@ func (p *GCProg2) AddSym(s loader.Sym) { 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) @@ -1363,7 +1269,7 @@ func (state *dodataState) checkdatsize(symn sym.SymKind) { } // fixZeroSizedSymbols gives a few special symbols with zero size some space. -func fixZeroSizedSymbols(ctxt *Link) { +func fixZeroSizedSymbols2(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 @@ -1391,44 +1297,48 @@ func fixZeroSizedSymbols(ctxt *Link) { return } - bss := ctxt.Syms.Lookup("runtime.bss", 0) - bss.Size = 8 - bss.Attr.Set(sym.AttrSpecial, false) + ldr := ctxt.loader + bss := ldr.CreateSymForUpdate("runtime.bss", 0) + bss.SetSize(8) + ldr.SetAttrSpecial(bss.Sym(), false) - ctxt.Syms.Lookup("runtime.ebss", 0).Attr.Set(sym.AttrSpecial, false) + ebss := ldr.CreateSymForUpdate("runtime.ebss", 0) + ldr.SetAttrSpecial(ebss.Sym(), false) - data := ctxt.Syms.Lookup("runtime.data", 0) - data.Size = 8 - data.Attr.Set(sym.AttrSpecial, false) + data := ldr.CreateSymForUpdate("runtime.data", 0) + data.SetSize(8) + ldr.SetAttrSpecial(data.Sym(), false) + + edata := ldr.CreateSymForUpdate("runtime.edata", 0) + ldr.SetAttrSpecial(edata.Sym(), false) - 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 + edata.SetType(sym.SXCOFFTOC) } - types := ctxt.Syms.Lookup("runtime.types", 0) - types.Type = sym.STYPE - types.Size = 8 - types.Attr.Set(sym.AttrSpecial, false) + types := ldr.CreateSymForUpdate("runtime.types", 0) + types.SetType(sym.STYPE) + types.SetSize(8) + ldr.SetAttrSpecial(types.Sym(), false) - etypes := ctxt.Syms.Lookup("runtime.etypes", 0) - etypes.Type = sym.SFUNCTAB - etypes.Attr.Set(sym.AttrSpecial, false) + etypes := ldr.CreateSymForUpdate("runtime.etypes", 0) + etypes.SetType(sym.SFUNCTAB) + ldr.SetAttrSpecial(etypes.Sym(), 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) + rodata := ldr.CreateSymForUpdate("runtime.rodata", 0) + rodata.SetType(sym.SSTRING) + rodata.SetSize(8) + ldr.SetAttrSpecial(rodata.Sym(), false) - ctxt.Syms.Lookup("runtime.erodata", 0).Attr.Set(sym.AttrSpecial, false) + erodata := ldr.CreateSymForUpdate("runtime.erodata", 0) + ldr.SetAttrSpecial(erodata.Sym(), false) } } // makeRelroForSharedLib creates a section of readonly data if necessary. -func (state *dodataState) makeRelroForSharedLib(target *Link) { +func (state *dodataState) makeRelroForSharedLib2(target *Link) { if !target.UseRelro() { return } @@ -1436,31 +1346,33 @@ func (state *dodataState) makeRelroForSharedLib(target *Link) { // "read only" data with relocations needs to go in its own section // when building a shared library. We do this by boosting objects of // type SXXX with relocations to type SXXXRELRO. + ldr := target.loader for _, symnro := range sym.ReadOnly { symnrelro := sym.RelROMap[symnro] - ro := []*sym.Symbol{} - relro := state.data[symnrelro] + ro := []loader.Sym{} + relro := state.data2[symnrelro] - for _, s := range state.data[symnro] { - isRelro := len(s.R) > 0 - switch s.Type { + for _, s := range state.data2[symnro] { + relocs := ldr.Relocs(s) + isRelro := relocs.Count() > 0 + switch state.symType(s) { case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO: // Symbols are not sorted yet, so it is possible // that an Outer symbol has been changed to a // relro Type before it reaches here. isRelro = true case sym.SFUNCTAB: - if target.IsAIX() && s.Name == "runtime.etypes" { + if target.IsAIX() && ldr.SymName(s) == "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 + state.setSymType(s, symnrelro) + if outer := ldr.OuterSym(s); outer != 0 { + state.setSymType(outer, symnrelro) } relro = append(relro, s) } else { @@ -1473,14 +1385,18 @@ func (state *dodataState) makeRelroForSharedLib(target *Link) { // 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) + if outer := ldr.OuterSym(s); outer != 0 { + st := state.symType(s) + ost := state.symType(outer) + if st != ost { + state.ctxt.Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)", + ldr.SymName(outer), st, ost) + } } } - state.data[symnro] = ro - state.data[symnrelro] = relro + state.data2[symnro] = ro + state.data2[symnrelro] = relro } } @@ -1492,26 +1408,81 @@ type dodataState struct { ctxt *Link // Data symbols bucketed by type. data [sym.SXREF][]*sym.Symbol + // Data symbols bucketed by type. + data2 [sym.SXREF][]loader.Sym // Max alignment for each flavor of data symbol. dataMaxAlign [sym.SXREF]int32 + // Overridden sym type + symGroupType []sym.SymKind // Current data size so far. datsize int64 } -func (ctxt *Link) dodata() { +// A note on symType/setSymType below: +// +// In the legacy linker, the types of symbols (notably data symbols) are +// changed during the symtab() phase so as to insure that similar symbols +// are bucketed together, then their types are changed back again during +// dodata. Symbol to section assignment also plays tricks along these lines +// in the case where a relro segment is needed. +// +// The value returned from setType() below reflects the effects of +// any overrides made by symtab and/or dodata. + +// symType returns the (possibly overridden) type of 's'. +func (state *dodataState) symType(s loader.Sym) sym.SymKind { + if int(s) < len(state.symGroupType) { + if override := state.symGroupType[s]; override != 0 { + return override + } + } + return state.ctxt.loader.SymType(s) +} + +// setSymType sets a new override type for 's'. +func (state *dodataState) setSymType(s loader.Sym, kind sym.SymKind) { + if s == 0 { + panic("bad") + } + if int(s) < len(state.symGroupType) { + state.symGroupType[s] = kind + } else { + su := state.ctxt.loader.MakeSymbolUpdater(s) + su.SetType(kind) + } +} + +func (ctxt *Link) dodata2(symGroupType []sym.SymKind) { + // Give zeros sized symbols space if necessary. - fixZeroSizedSymbols(ctxt) + fixZeroSizedSymbols2(ctxt) // Collect data symbols by type into data. - state := dodataState{ctxt: ctxt} - for _, s := range ctxt.Syms.Allsym { - if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() { + state := dodataState{ctxt: ctxt, symGroupType: symGroupType} + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || ldr.AttrSubSymbol(s) || + !ldr.TopLevelSym(s) { continue } - if s.Type <= sym.STEXT || s.Type >= sym.SXREF { + + st := state.symType(s) + + if st <= sym.STEXT || st >= sym.SXREF { continue } - state.data[s.Type] = append(state.data[s.Type], s) + state.data2[st] = append(state.data2[st], s) + + // Set explicit alignment here, so as to avoid having to update + // symbol alignment in doDataSect2, which would cause a concurrent + // map read/write violation. + state.symalign2(s) + + // Similarly with checking the onlist attr. + if ldr.AttrOnList(s) { + log.Fatalf("symbol %s listed multiple times", ldr.SymName(s)) + } + ldr.SetAttrOnList(s, true) } // Now that we have the data symbols, but before we start @@ -1521,39 +1492,75 @@ func (ctxt *Link) dodata() { // // On darwin, we need the symbol table numbers for dynreloc. if ctxt.HeadType == objabi.Hdarwin { - machosymorder(ctxt) + panic("not yet implemented for darwin") + // machosymorder(ctxt) } - state.dynreloc(ctxt) + state.dynreloc2(ctxt) // Move any RO data with relocations to a separate section. - state.makeRelroForSharedLib(ctxt) + state.makeRelroForSharedLib2(ctxt) // Sort symbols. var wg sync.WaitGroup - for symn := range state.data { + for symn := range state.data2 { symn := sym.SymKind(symn) wg.Add(1) go func() { - state.data[symn], state.dataMaxAlign[symn] = dodataSect(ctxt, symn, state.data[symn]) + state.data2[symn], state.dataMaxAlign[symn] = state.dodataSect2(ctxt, symn, state.data2[symn]) wg.Done() }() } wg.Wait() + if ctxt.IsELF { + // Make .rela and .rela.plt contiguous, the ELF ABI requires this + // and Solaris actually cares. + syms := state.data2[sym.SELFROSECT] + reli, plti := -1, -1 + for i, s := range syms { + switch ldr.SymName(s) { + case ".rel.plt", ".rela.plt": + plti = i + case ".rel", ".rela": + reli = i + } + } + if reli >= 0 && plti >= 0 && plti != reli+1 { + var first, second int + if plti > reli { + first, second = reli, plti + } else { + first, second = plti, reli + } + rel, plt := syms[reli], syms[plti] + copy(syms[first+2:], syms[first+1:second]) + syms[first+0] = rel + syms[first+1] = plt + + // Make sure alignment doesn't introduce a gap. + // Setting the alignment explicitly prevents + // symalign from basing it on the size and + // getting it wrong. + ldr.SetSymAlign(rel, int32(ctxt.Arch.RegSize)) + ldr.SetSymAlign(plt, int32(ctxt.Arch.RegSize)) + } + state.data2[sym.SELFROSECT] = syms + } + if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal { // These symbols must have the same alignment as their section. // Otherwize, ld might change the layout of Go sections. - ctxt.Syms.ROLookup("runtime.data", 0).Align = state.dataMaxAlign[sym.SDATA] - ctxt.Syms.ROLookup("runtime.bss", 0).Align = state.dataMaxAlign[sym.SBSS] + ldr.SetSymAlign(ldr.Lookup("runtime.data", 0), state.dataMaxAlign[sym.SDATA]) + ldr.SetSymAlign(ldr.Lookup("runtime.bss", 0), state.dataMaxAlign[sym.SBSS]) } // Create *sym.Section objects and assign symbols to sections for // data/rodata (and related) symbols. - state.allocateDataSections(ctxt) + state.allocateDataSections2(ctxt) // Create *sym.Section objects and assign symbols to sections for // DWARF symbols. - state.allocateDwarfSections(ctxt) + state.allocateDwarfSections2(ctxt) /* number the sections */ n := int16(1) @@ -1584,9 +1591,11 @@ func (ctxt *Link) dodata() { // single symbol will be placed. Here "seg" is the segment into which // the section will go, "s" is the symbol to be placed into the new // section, and "rwx" contains permissions for the section. -func (state *dodataState) allocateDataSectionForSym(seg *sym.Segment, s *sym.Symbol, rwx int) *sym.Section { - sect := addsection(state.ctxt.loader, state.ctxt.Arch, seg, s.Name, rwx) - sect.Align = symalign(s) +func (state *dodataState) allocateDataSectionForSym2(seg *sym.Segment, s loader.Sym, rwx int) *sym.Section { + ldr := state.ctxt.loader + sname := ldr.SymName(s) + sect := addsection(ldr, state.ctxt.Arch, seg, sname, rwx) + sect.Align = state.symalign2(s) state.datsize = Rnd(state.datsize, int64(sect.Align)) sect.Vaddr = uint64(state.datsize) return sect @@ -1622,21 +1631,22 @@ func (state *dodataState) allocateNamedDataSection(seg *sym.Segment, sName strin // "forceType" (if non-zero) contains a new sym type to apply to each // sym during the assignment, and "aligner" is a hook to call to // handle alignment during the assignment process. -func (state *dodataState) assignDsymsToSection(sect *sym.Section, syms []*sym.Symbol, forceType sym.SymKind, aligner func(datsize int64, s *sym.Symbol) int64) { +func (state *dodataState) assignDsymsToSection2(sect *sym.Section, syms []loader.Sym, forceType sym.SymKind, aligner func(state *dodataState, datsize int64, s loader.Sym) int64) { + ldr := state.ctxt.loader for _, s := range syms { - state.datsize = aligner(state.datsize, s) - s.Sect = sect + state.datsize = aligner(state, state.datsize, s) + ldr.SetSymSect(s, sect) if forceType != sym.Sxxx { - s.Type = forceType + state.setSymType(s, forceType) } - s.Value = int64(uint64(state.datsize) - sect.Vaddr) - state.datsize += s.Size + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) } sect.Length = uint64(state.datsize) - sect.Vaddr } -func (state *dodataState) assignToSection(sect *sym.Section, symn sym.SymKind, forceType sym.SymKind) { - state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) +func (state *dodataState) assignToSection2(sect *sym.Section, symn sym.SymKind, forceType sym.SymKind) { + state.assignDsymsToSection2(sect, state.data2[symn], forceType, aligndatsize2) state.checkdatsize(symn) } @@ -1646,13 +1656,14 @@ func (state *dodataState) assignToSection(sect *sym.Section, symn sym.SymKind, f // symbol name. "Seg" is the segment into which to place the new // section, "forceType" is the new sym.SymKind to assign to the symbol // within the section, and "rwx" holds section permissions. -func (state *dodataState) allocateSingleSymSections(seg *sym.Segment, symn sym.SymKind, forceType sym.SymKind, rwx int) { - for _, s := range state.data[symn] { - sect := state.allocateDataSectionForSym(seg, s, rwx) - s.Sect = sect - s.Type = forceType - s.Value = int64(uint64(state.datsize) - sect.Vaddr) - state.datsize += s.Size +func (state *dodataState) allocateSingleSymSections2(seg *sym.Segment, symn sym.SymKind, forceType sym.SymKind, rwx int) { + ldr := state.ctxt.loader + for _, s := range state.data2[symn] { + sect := state.allocateDataSectionForSym2(seg, s, rwx) + ldr.SetSymSect(s, sect) + state.setSymType(s, forceType) + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) sect.Length = uint64(state.datsize) - sect.Vaddr } state.checkdatsize(symn) @@ -1665,16 +1676,16 @@ func (state *dodataState) allocateSingleSymSections(seg *sym.Segment, symn sym.S // name to give to the new section, "forceType" (if non-zero) contains // a new sym type to apply to each sym during the assignment, and // "rwx" holds section permissions. -func (state *dodataState) allocateNamedSectionAndAssignSyms(seg *sym.Segment, secName string, symn sym.SymKind, forceType sym.SymKind, rwx int) *sym.Section { +func (state *dodataState) allocateNamedSectionAndAssignSyms2(seg *sym.Segment, secName string, symn sym.SymKind, forceType sym.SymKind, rwx int) *sym.Section { sect := state.allocateNamedDataSection(seg, secName, []sym.SymKind{symn}, rwx) - state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) + state.assignDsymsToSection2(sect, state.data2[symn], forceType, aligndatsize2) return sect } // allocateDataSections allocates sym.Section objects for data/rodata // (and related) symbols, and then assigns symbols to those sections. -func (state *dodataState) allocateDataSections(ctxt *Link) { +func (state *dodataState) allocateDataSections2(ctxt *Link) { // Allocate sections. // Data is processed before segtext, because we need // to see all symbols in the .data and .bss sections in order @@ -1689,32 +1700,31 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { sym.SWINDOWS, } for _, symn := range writable { - state.allocateSingleSymSections(&Segdata, symn, sym.SDATA, 06) + state.allocateSingleSymSections2(&Segdata, symn, sym.SDATA, 06) } + ldr := ctxt.loader // .got (and .toc on ppc64) - if len(state.data[sym.SELFGOT]) > 0 { - sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06) + if len(state.data2[sym.SELFGOT]) > 0 { + sect := state.allocateNamedSectionAndAssignSyms2(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06) if ctxt.IsPPC64() { - for _, s := range state.data[sym.SELFGOT] { + for _, s := range state.data2[sym.SELFGOT] { // 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 + toc := ldr.Lookup(".TOC.", int(ldr.SymVersion(s))) + if toc != 0 { + ldr.SetSymSect(toc, sect) + ldr.PrependSub(s, toc) + ldr.SetSymValue(toc, 0x8000) } } } } /* pointer-free data */ - sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrdata", sym.SNOPTRDATA, sym.SDATA, 06) - ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect = sect - ctxt.Syms.Lookup("runtime.enoptrdata", 0).Sect = sect + sect := state.allocateNamedSectionAndAssignSyms2(&Segdata, ".noptrdata", sym.SNOPTRDATA, sym.SDATA, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrdata", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrdata", 0), sect) hasinitarr := ctxt.linkShared @@ -1725,31 +1735,31 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { } if ctxt.HeadType == objabi.Haix { - if len(state.data[sym.SINITARR]) > 0 { + if len(state.data2[sym.SINITARR]) > 0 { Errorf(nil, "XCOFF format doesn't allow .init_array section") } } - if hasinitarr && len(state.data[sym.SINITARR]) > 0 { - state.allocateNamedSectionAndAssignSyms(&Segdata, ".init_array", sym.SINITARR, sym.Sxxx, 06) + if hasinitarr && len(state.data2[sym.SINITARR]) > 0 { + state.allocateNamedSectionAndAssignSyms2(&Segdata, ".init_array", sym.SINITARR, sym.Sxxx, 06) } /* data */ - sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".data", sym.SDATA, sym.SDATA, 06) - ctxt.Syms.Lookup("runtime.data", 0).Sect = sect - ctxt.Syms.Lookup("runtime.edata", 0).Sect = sect + sect = state.allocateNamedSectionAndAssignSyms2(&Segdata, ".data", sym.SDATA, sym.SDATA, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.data", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.edata", 0), sect) dataGcEnd := state.datsize - int64(sect.Vaddr) // On AIX, TOC entries must be the last of .data // These aren't part of gc as they won't change during the runtime. - state.assignToSection(sect, sym.SXCOFFTOC, sym.SDATA) + state.assignToSection2(sect, sym.SXCOFFTOC, sym.SDATA) state.checkdatsize(sym.SDATA) sect.Length = uint64(state.datsize) - sect.Vaddr /* bss */ - sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".bss", sym.SBSS, sym.Sxxx, 06) - ctxt.Syms.Lookup("runtime.bss", 0).Sect = sect - ctxt.Syms.Lookup("runtime.ebss", 0).Sect = sect + sect = state.allocateNamedSectionAndAssignSyms2(&Segdata, ".bss", sym.SBSS, sym.Sxxx, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.bss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ebss", 0), sect) bssGcEnd := state.datsize - int64(sect.Vaddr) // Emit gcdata for bcc symbols now that symbol values have been assigned. @@ -1762,41 +1772,43 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { {"runtime.gcbss", sym.SBSS, bssGcEnd}, } for _, g := range gcsToEmit { - var gc GCProg + var gc GCProg2 gc.Init(ctxt, g.symName) - for _, s := range state.data[g.symKind] { + for _, s := range state.data2[g.symKind] { gc.AddSym(s) } gc.End(g.gcEnd) } /* pointer-free bss */ - sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06) - ctxt.Syms.Lookup("runtime.noptrbss", 0).Sect = sect - ctxt.Syms.Lookup("runtime.enoptrbss", 0).Sect = sect - ctxt.Syms.Lookup("runtime.end", 0).Sect = sect + sect = state.allocateNamedSectionAndAssignSyms2(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrbss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrbss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.end", 0), sect) // Coverage instrumentation counters for libfuzzer. if len(state.data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 { - state.allocateNamedSectionAndAssignSyms(&Segdata, "__libfuzzer_extra_counters", sym.SLIBFUZZER_EXTRA_COUNTER, sym.Sxxx, 06) + state.allocateNamedSectionAndAssignSyms2(&Segdata, "__libfuzzer_extra_counters", sym.SLIBFUZZER_EXTRA_COUNTER, sym.Sxxx, 06) } - if len(state.data[sym.STLSBSS]) > 0 { + if len(state.data2[sym.STLSBSS]) > 0 { var sect *sym.Section // FIXME: not clear why it is sometimes necessary to suppress .tbss section creation. if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) { - sect = addsection(ctxt.loader, ctxt.Arch, &Segdata, ".tbss", 06) + sect = addsection(ldr, ctxt.Arch, &Segdata, ".tbss", 06) sect.Align = int32(ctxt.Arch.PtrSize) // FIXME: why does this need to be set to zero? sect.Vaddr = 0 } state.datsize = 0 - for _, s := range state.data[sym.STLSBSS] { - state.datsize = aligndatsize(state.datsize, s) - s.Sect = sect - s.Value = state.datsize - state.datsize += s.Size + for _, s := range state.data2[sym.STLSBSS] { + state.datsize = aligndatsize2(state, state.datsize, s) + if sect != nil { + ldr.SetSymSect(s, sect) + } + ldr.SetSymValue(s, state.datsize) + state.datsize += ldr.SymSize(s) } state.checkdatsize(sym.STLSBSS) @@ -1826,34 +1838,35 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { state.datsize = 0 /* read-only executable ELF, Mach-O sections */ - if len(state.data[sym.STEXT]) != 0 { - Errorf(nil, "dodata found an sym.STEXT symbol: %s", state.data[sym.STEXT][0].Name) + if len(state.data2[sym.STEXT]) != 0 { + culprit := ldr.SymName(state.data2[sym.STEXT][0]) + Errorf(nil, "dodata found an sym.STEXT symbol: %s", culprit) } - state.allocateSingleSymSections(&Segtext, sym.SELFRXSECT, sym.SRODATA, 04) + state.allocateSingleSymSections2(&Segtext, sym.SELFRXSECT, sym.SRODATA, 04) /* read-only data */ sect = state.allocateNamedDataSection(segro, ".rodata", sym.ReadOnly, 04) - ctxt.Syms.Lookup("runtime.rodata", 0).Sect = sect - ctxt.Syms.Lookup("runtime.erodata", 0).Sect = sect + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.rodata", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.erodata", 0), sect) if !ctxt.UseRelro() { - ctxt.Syms.Lookup("runtime.types", 0).Sect = sect - ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect) } for _, symn := range sym.ReadOnly { symnStartValue := state.datsize - state.assignToSection(sect, symn, sym.SRODATA) + state.assignToSection2(sect, symn, sym.SRODATA) if ctxt.HeadType == objabi.Haix { // Read-only symbols might be wrapped inside their outer // symbol. // XCOFF symbol table needs to know the size of // these outer symbols. - xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) + xcoffUpdateOuterSize2(ctxt, state.datsize-symnStartValue, symn) } } /* read-only ELF, Mach-O sections */ - state.allocateSingleSymSections(segro, sym.SELFROSECT, sym.SRODATA, 04) - state.allocateSingleSymSections(segro, sym.SMACHOPLT, sym.SRODATA, 04) + state.allocateSingleSymSections2(segro, sym.SELFROSECT, sym.SRODATA, 04) + state.allocateSingleSymSections2(segro, sym.SMACHOPLT, sym.SRODATA, 04) // There is some data that are conceptually read-only but are written to by // relocations. On GNU systems, we can arrange for the dynamic linker to @@ -1902,8 +1915,8 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { /* data only written by relocations */ sect = state.allocateNamedDataSection(segrelro, genrelrosecname(""), relroReadOnly, relroSecPerm) - ctxt.Syms.Lookup("runtime.types", 0).Sect = sect - ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect) for i, symnro := range sym.ReadOnly { if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix { @@ -1917,18 +1930,19 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { symn := sym.RelROMap[symnro] symnStartValue := state.datsize - for _, s := range state.data[symn] { - 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) + for _, s := range state.data2[symn] { + outer := ldr.OuterSym(s) + if s != 0 && ldr.SymSect(outer) != nil && ldr.SymSect(outer) != sect { + ctxt.Errorf(s, "s.Outer (%s) in different section from s, %s != %s", ldr.SymName(outer), ldr.SymSect(outer).Name, sect.Name) } } - state.assignToSection(sect, symn, sym.SRODATA) + state.assignToSection2(sect, symn, sym.SRODATA) if ctxt.HeadType == objabi.Haix { // Read-only symbols might be wrapped inside their outer // symbol. // XCOFF symbol table needs to know the size of // these outer symbols. - xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) + xcoffUpdateOuterSize2(ctxt, state.datsize-symnStartValue, symn) } } @@ -1937,32 +1951,33 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { /* typelink */ sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm) - typelink := ctxt.Syms.Lookup("runtime.typelink", 0) - typelink.Sect = sect - typelink.Type = sym.SRODATA - state.datsize += typelink.Size + + typelink := ldr.CreateSymForUpdate("runtime.typelink", 0) + ldr.SetSymSect(typelink.Sym(), sect) + typelink.SetType(sym.SRODATA) + state.datsize += typelink.Size() state.checkdatsize(sym.STYPELINK) sect.Length = uint64(state.datsize) - sect.Vaddr /* itablink */ - sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".itablink"), sym.SITABLINK, sym.Sxxx, relroSecPerm) - ctxt.Syms.Lookup("runtime.itablink", 0).Sect = sect - ctxt.Syms.Lookup("runtime.eitablink", 0).Sect = sect + sect = state.allocateNamedSectionAndAssignSyms2(seg, genrelrosecname(".itablink"), sym.SITABLINK, sym.Sxxx, relroSecPerm) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.itablink", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.eitablink", 0), sect) 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) + xcoffUpdateOuterSize2(ctxt, int64(sect.Length), sym.SITABLINK) } /* gosymtab */ - sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gosymtab"), sym.SSYMTAB, sym.SRODATA, relroSecPerm) - ctxt.Syms.Lookup("runtime.symtab", 0).Sect = sect - ctxt.Syms.Lookup("runtime.esymtab", 0).Sect = sect + sect = state.allocateNamedSectionAndAssignSyms2(seg, genrelrosecname(".gosymtab"), sym.SSYMTAB, sym.SRODATA, relroSecPerm) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.symtab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.esymtab", 0), sect) /* gopclntab */ - sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm) - ctxt.Syms.Lookup("runtime.pclntab", 0).Sect = sect - ctxt.Syms.Lookup("runtime.epclntab", 0).Sect = sect + sect = state.allocateNamedSectionAndAssignSyms2(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect) // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. if state.datsize != int64(uint32(state.datsize)) { @@ -1970,37 +1985,38 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { } for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { - ctxt.datap = append(ctxt.datap, state.data[symn]...) + ctxt.datap2 = append(ctxt.datap2, state.data2[symn]...) } } // allocateDwarfSections allocates sym.Section objects for DWARF // symbols, and assigns symbols to sections. -func (state *dodataState) allocateDwarfSections(ctxt *Link) { +func (state *dodataState) allocateDwarfSections2(ctxt *Link) { - alignOne := func(datsize int64, s *sym.Symbol) int64 { return datsize } + alignOne := func(state *dodataState, datsize int64, s loader.Sym) int64 { return datsize } - for i := 0; i < len(dwarfp); i++ { + ldr := ctxt.loader + for i := 0; i < len(dwarfp2); i++ { // First the section symbol. - s := dwarfp[i].secSym() - sect := state.allocateNamedDataSection(&Segdwarf, s.Name, []sym.SymKind{}, 04) - sect.Sym = s - s.Sect = sect - s.Type = sym.SRODATA - s.Value = int64(uint64(state.datsize) - sect.Vaddr) - state.datsize += s.Size - curType := s.Type + s := dwarfp2[i].secSym() + sect := state.allocateNamedDataSection(&Segdwarf, ldr.SymName(s), []sym.SymKind{}, 04) + ldr.SetSymSect(s, sect) + sect.Sym2 = sym.LoaderSym(s) + curType := ldr.SymType(s) + state.setSymType(s, sym.SRODATA) + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) // Then any sub-symbols for the section symbol. - subSyms := dwarfp[i].subSyms() - state.assignDsymsToSection(sect, subSyms, sym.SRODATA, alignOne) + subSyms := dwarfp2[i].subSyms() + state.assignDsymsToSection2(sect, subSyms, sym.SRODATA, alignOne) for j := 0; j < len(subSyms); j++ { s := subSyms[j] if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC { // Update the size of .debug_loc for this symbol's // package. - addDwsectCUSize(".debug_loc", s.File, uint64(s.Size)) + addDwsectCUSize(".debug_loc", ldr.SymPkg(s), uint64(ldr.SymSize(s))) } } sect.Length = uint64(state.datsize) - sect.Vaddr @@ -2008,40 +2024,38 @@ func (state *dodataState) allocateDwarfSections(ctxt *Link) { } } -func dodataSect(ctxt *Link, symn sym.SymKind, syms []*sym.Symbol) (result []*sym.Symbol, maxAlign int32) { +func (state *dodataState) dodataSect2(ctxt *Link, symn sym.SymKind, syms []loader.Sym) (result []loader.Sym, 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)) + newSyms := make([]loader.Sym, 0, len(syms)) for _, s := range syms { - if s.Type == symn { + if state.symType(s) == symn { newSyms = append(newSyms, s) } } syms = newSyms } - var head, tail *sym.Symbol - symsSort := make([]dataSortKey, 0, len(syms)) + var head, tail loader.Sym + ldr := ctxt.loader for _, s := range syms { - if s.Attr.OnList() { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Attr |= sym.AttrOnList + ss := ldr.SymSize(s) + ds := int64(len(ldr.Data(s))) 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) + case ss < ds: + ctxt.Errorf(s, "initialize bounds (%d < %d)", ss, ds) + case ss < 0: + ctxt.Errorf(s, "negative size (%d bytes)", ss) + case ss > cutoff: + ctxt.Errorf(s, "symbol too large (%d bytes)", ss) } // If the usually-special section-marker symbols are being laid // out as regular symbols, put them either at the beginning or // end of their section. if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { - switch s.Name { + switch ldr.SymName(s) { case "runtime.text", "runtime.bss", "runtime.data", "runtime.types", "runtime.rodata": head = s continue @@ -2050,73 +2064,46 @@ func dodataSect(ctxt *Link, symn sym.SymKind, syms []*sym.Symbol) (result []*sym continue } } + } - key := dataSortKey{ - size: s.Size, - name: s.Name, - sym: s, + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type sym.SELFGOT, so in that case + // we skip size comparison and fall through to the name + // comparison (conveniently, .got sorts before .toc). + checkSize := symn != sym.SELFGOT + + // Perform the sort. + sort.Slice(syms, func(i, j int) bool { + si, sj := syms[i], syms[j] + switch { + case si == head, sj == tail: + return true + case sj == head, si == tail: + return false } - - 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 checkSize { + isz := ldr.SymSize(si) + jsz := ldr.SymSize(sj) + if isz != jsz { + return isz < jsz } } - 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 + iname := ldr.SymName(si) + jname := ldr.SymName(sj) + if iname != jname { + return iname < jname + } + return si < sj + }) - // 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) + // Reap alignment. + for k := range syms { + s := syms[k] + if s != head && s != tail { + align := state.symalign2(s) + if maxAlign < align { + maxAlign = align + } } } diff --git a/src/cmd/link/internal/ld/data2.go b/src/cmd/link/internal/ld/data2.go new file mode 100644 index 0000000000..3eb45818d2 --- /dev/null +++ b/src/cmd/link/internal/ld/data2.go @@ -0,0 +1,938 @@ +// 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/gcprog" + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "log" + "os" + "sort" + "strings" + "sync" +) + +// Temporary dumping around for sym.Symbol version of helper +// functions in dodata(), still being used for some archs/oses. +// FIXME: get rid of this file when dodata() is completely +// converted. + +func (ctxt *Link) dodata() { + // Give zeros sized symbols space if necessary. + fixZeroSizedSymbols(ctxt) + + // Collect data symbols by type into data. + state := dodataState{ctxt: ctxt} + 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 + } + state.data[s.Type] = append(state.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) + } + state.dynreloc(ctxt) + + // Move any RO data with relocations to a separate section. + state.makeRelroForSharedLib(ctxt) + + // Temporary for debugging. + symToIdx := make(map[*sym.Symbol]loader.Sym) + for s := loader.Sym(1); s < loader.Sym(ctxt.loader.NSym()); s++ { + sp := ctxt.loader.Syms[s] + if sp != nil { + symToIdx[sp] = s + } + } + + // Sort symbols. + var wg sync.WaitGroup + for symn := range state.data { + symn := sym.SymKind(symn) + wg.Add(1) + go func() { + state.data[symn], state.dataMaxAlign[symn] = dodataSect(ctxt, symn, state.data[symn], symToIdx) + 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 = state.dataMaxAlign[sym.SDATA] + ctxt.Syms.ROLookup("runtime.bss", 0).Align = state.dataMaxAlign[sym.SBSS] + } + + // Create *sym.Section objects and assign symbols to sections for + // data/rodata (and related) symbols. + state.allocateDataSections(ctxt) + + // Create *sym.Section objects and assign symbols to sections for + // DWARF symbols. + state.allocateDwarfSections(ctxt) + + /* number the sections */ + n := int16(1) + + for _, sect := range Segtext.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segrodata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segrelrodata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segdata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segdwarf.Sections { + sect.Extnum = n + n++ + } +} + +// makeRelroForSharedLib creates a section of readonly data if necessary. +func (state *dodataState) makeRelroForSharedLib(target *Link) { + if !target.UseRelro() { + return + } + + // "read only" data with relocations needs to go in its own section + // when building a shared library. We do this by boosting objects of + // type SXXX with relocations to type SXXXRELRO. + for _, symnro := range sym.ReadOnly { + symnrelro := sym.RelROMap[symnro] + + ro := []*sym.Symbol{} + relro := state.data[symnrelro] + + for _, s := range state.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) + } + } + + state.data[symnro] = ro + state.data[symnrelro] = relro + } +} + +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(target, ldr, syms, 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(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) + } + } + } +} + +func (state *dodataState) dynreloc(ctxt *Link) { + if ctxt.HeadType == objabi.Hwindows { + return + } + // -d suppresses dynamic loader format, so we may as well not + // compute these sections or mark their symbols as reachable. + if *FlagD { + return + } + + for _, s := range ctxt.Textp { + dynrelocsym(ctxt, s) + } + for _, syms := range state.data { + for _, s := range syms { + dynrelocsym(ctxt, s) + } + } + if ctxt.IsELF { + elfdynhash(ctxt) + } +} + +func 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 +} + +// 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))) +} + +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 + symIdx loader.Sym +} + +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 + } + if s1.name != s2.name { + return s1.name < s2.name + } + return s1.symIdx < s2.symIdx +} + +// 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 + } + + 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) + } +} + +// allocateDataSectionForSym creates a new sym.Section into which a a +// single symbol will be placed. Here "seg" is the segment into which +// the section will go, "s" is the symbol to be placed into the new +// section, and "rwx" contains permissions for the section. +func (state *dodataState) allocateDataSectionForSym(seg *sym.Segment, s *sym.Symbol, rwx int) *sym.Section { + sect := addsection(state.ctxt.loader, state.ctxt.Arch, seg, s.Name, rwx) + sect.Align = symalign(s) + state.datsize = Rnd(state.datsize, int64(sect.Align)) + sect.Vaddr = uint64(state.datsize) + return sect +} + +// assignDsymsToSection assigns a collection of data symbols to a +// newly created section. "sect" is the section into which to place +// the symbols, "syms" holds the list of symbols to assign, +// "forceType" (if non-zero) contains a new sym type to apply to each +// sym during the assignment, and "aligner" is a hook to call to +// handle alignment during the assignment process. +func (state *dodataState) assignDsymsToSection(sect *sym.Section, syms []*sym.Symbol, forceType sym.SymKind, aligner func(datsize int64, s *sym.Symbol) int64) { + for _, s := range syms { + state.datsize = aligner(state.datsize, s) + s.Sect = sect + if forceType != sym.Sxxx { + s.Type = forceType + } + s.Value = int64(uint64(state.datsize) - sect.Vaddr) + state.datsize += s.Size + } + sect.Length = uint64(state.datsize) - sect.Vaddr +} + +func (state *dodataState) assignToSection(sect *sym.Section, symn sym.SymKind, forceType sym.SymKind) { + state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) + state.checkdatsize(symn) +} + +// allocateSingleSymSections walks through the bucketed data symbols +// with type 'symn', creates a new section for each sym, and assigns +// the sym to a newly created section. Section name is set from the +// symbol name. "Seg" is the segment into which to place the new +// section, "forceType" is the new sym.SymKind to assign to the symbol +// within the section, and "rwx" holds section permissions. +func (state *dodataState) allocateSingleSymSections(seg *sym.Segment, symn sym.SymKind, forceType sym.SymKind, rwx int) { + for _, s := range state.data[symn] { + sect := state.allocateDataSectionForSym(seg, s, rwx) + s.Sect = sect + s.Type = forceType + s.Value = int64(uint64(state.datsize) - sect.Vaddr) + state.datsize += s.Size + sect.Length = uint64(state.datsize) - sect.Vaddr + } + state.checkdatsize(symn) +} + +// allocateNamedSectionAndAssignSyms creates a new section with the +// specified name, then walks through the bucketed data symbols with +// type 'symn' and assigns each of them to this new section. "Seg" is +// the segment into which to place the new section, "secName" is the +// name to give to the new section, "forceType" (if non-zero) contains +// a new sym type to apply to each sym during the assignment, and +// "rwx" holds section permissions. +func (state *dodataState) allocateNamedSectionAndAssignSyms(seg *sym.Segment, secName string, symn sym.SymKind, forceType sym.SymKind, rwx int) *sym.Section { + + sect := state.allocateNamedDataSection(seg, secName, []sym.SymKind{symn}, rwx) + state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) + return sect +} + +// allocateDataSections allocates sym.Section objects for data/rodata +// (and related) symbols, and then assigns symbols to those sections. +func (state *dodataState) allocateDataSections(ctxt *Link) { + // Allocate sections. + // Data is processed before segtext, because we need + // to see all symbols in the .data and .bss sections in order + // to generate garbage collection information. + + // Writable data sections that do not need any specialized handling. + writable := []sym.SymKind{ + sym.SBUILDINFO, + sym.SELFSECT, + sym.SMACHO, + sym.SMACHOGOT, + sym.SWINDOWS, + } + for _, symn := range writable { + state.allocateSingleSymSections(&Segdata, symn, sym.SDATA, 06) + } + + // .got (and .toc on ppc64) + if len(state.data[sym.SELFGOT]) > 0 { + sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06) + if ctxt.IsPPC64() { + for _, s := range state.data[sym.SELFGOT] { + // Resolve .TOC. symbol for this object file (ppc64) + toc := 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 + } + } + } + } + + /* pointer-free data */ + sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrdata", sym.SNOPTRDATA, sym.SDATA, 06) + ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect = sect + ctxt.Syms.Lookup("runtime.enoptrdata", 0).Sect = sect + + hasinitarr := ctxt.linkShared + + /* shared library initializer */ + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin: + hasinitarr = true + } + + if ctxt.HeadType == objabi.Haix { + if len(state.data[sym.SINITARR]) > 0 { + Errorf(nil, "XCOFF format doesn't allow .init_array section") + } + } + + if hasinitarr && len(state.data[sym.SINITARR]) > 0 { + state.allocateNamedSectionAndAssignSyms(&Segdata, ".init_array", sym.SINITARR, sym.Sxxx, 06) + } + + /* data */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".data", sym.SDATA, sym.SDATA, 06) + ctxt.Syms.Lookup("runtime.data", 0).Sect = sect + ctxt.Syms.Lookup("runtime.edata", 0).Sect = sect + dataGcEnd := state.datsize - int64(sect.Vaddr) + + // On AIX, TOC entries must be the last of .data + // These aren't part of gc as they won't change during the runtime. + state.assignToSection(sect, sym.SXCOFFTOC, sym.SDATA) + state.checkdatsize(sym.SDATA) + sect.Length = uint64(state.datsize) - sect.Vaddr + + /* bss */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".bss", sym.SBSS, sym.Sxxx, 06) + ctxt.Syms.Lookup("runtime.bss", 0).Sect = sect + ctxt.Syms.Lookup("runtime.ebss", 0).Sect = sect + bssGcEnd := state.datsize - int64(sect.Vaddr) + + // Emit gcdata for bcc symbols now that symbol values have been assigned. + gcsToEmit := []struct { + symName string + symKind sym.SymKind + gcEnd int64 + }{ + {"runtime.gcdata", sym.SDATA, dataGcEnd}, + {"runtime.gcbss", sym.SBSS, bssGcEnd}, + } + for _, g := range gcsToEmit { + var gc GCProg + gc.Init(ctxt, g.symName) + for _, s := range state.data[g.symKind] { + gc.AddSym(s) + } + gc.End(g.gcEnd) + } + + /* pointer-free bss */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06) + ctxt.Syms.Lookup("runtime.noptrbss", 0).Sect = sect + ctxt.Syms.Lookup("runtime.enoptrbss", 0).Sect = sect + ctxt.Syms.Lookup("runtime.end", 0).Sect = sect + + // Coverage instrumentation counters for libfuzzer. + if len(state.data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 { + state.allocateNamedSectionAndAssignSyms(&Segdata, "__libfuzzer_extra_counters", sym.SLIBFUZZER_EXTRA_COUNTER, sym.Sxxx, 06) + } + + if len(state.data[sym.STLSBSS]) > 0 { + var sect *sym.Section + // FIXME: not clear why it is sometimes necessary to suppress .tbss section creation. + if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) { + sect = addsection(ctxt.loader, ctxt.Arch, &Segdata, ".tbss", 06) + sect.Align = int32(ctxt.Arch.PtrSize) + // FIXME: why does this need to be set to zero? + sect.Vaddr = 0 + } + state.datsize = 0 + + for _, s := range state.data[sym.STLSBSS] { + state.datsize = aligndatsize(state.datsize, s) + s.Sect = sect + s.Value = state.datsize + state.datsize += s.Size + } + state.checkdatsize(sym.STLSBSS) + + if sect != nil { + sect.Length = uint64(state.datsize) + } + } + + /* + * We finished data, begin read-only data. + * Not all systems support a separate read-only non-executable data section. + * ELF and Windows PE systems do. + * OS X and Plan 9 do not. + * And if we're using external linking mode, the point is moot, + * since it's not our decision; that code expects the sections in + * segtext. + */ + var segro *sym.Segment + if ctxt.IsELF && ctxt.LinkMode == LinkInternal { + segro = &Segrodata + } else if ctxt.HeadType == objabi.Hwindows { + segro = &Segrodata + } else { + segro = &Segtext + } + + state.datsize = 0 + + /* read-only executable ELF, Mach-O sections */ + if len(state.data[sym.STEXT]) != 0 { + Errorf(nil, "dodata found an sym.STEXT symbol: %s", state.data[sym.STEXT][0].Name) + } + state.allocateSingleSymSections(&Segtext, sym.SELFRXSECT, sym.SRODATA, 04) + + /* read-only data */ + sect = state.allocateNamedDataSection(segro, ".rodata", sym.ReadOnly, 04) + 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 { + symnStartValue := state.datsize + state.assignToSection(sect, symn, sym.SRODATA) + if ctxt.HeadType == objabi.Haix { + // Read-only symbols might be wrapped inside their outer + // symbol. + // XCOFF symbol table needs to know the size of + // these outer symbols. + xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) + } + } + + /* read-only ELF, Mach-O sections */ + state.allocateSingleSymSections(segro, sym.SELFROSECT, sym.SRODATA, 04) + state.allocateSingleSymSections(segro, sym.SMACHOPLT, sym.SRODATA, 04) + + // There is some data that are conceptually read-only but are written to by + // relocations. On GNU systems, we can arrange for the dynamic linker to + // mprotect sections after relocations are applied by giving them write + // permissions in the object file and calling them ".data.rel.ro.FOO". We + // divide the .rodata section between actual .rodata and .data.rel.ro.rodata, + // but for the other sections that this applies to, we just write a read-only + // .FOO section or a read-write .data.rel.ro.FOO section depending on the + // situation. + // TODO(mwhudson): It would make sense to do this more widely, but it makes + // the system linker segfault on darwin. + const relroPerm = 06 + const fallbackPerm = 04 + relroSecPerm := fallbackPerm + genrelrosecname := func(suffix string) string { + return suffix + } + seg := segro + + 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. + state.datsize = 0 + } + + genrelrosecname = func(suffix string) string { + return ".data.rel.ro" + suffix + } + relroReadOnly := []sym.SymKind{} + for _, symnro := range sym.ReadOnly { + symn := sym.RelROMap[symnro] + relroReadOnly = append(relroReadOnly, symn) + } + seg = segrelro + relroSecPerm = relroPerm + + /* data only written by relocations */ + sect = state.allocateNamedDataSection(segrelro, genrelrosecname(""), relroReadOnly, relroSecPerm) + + ctxt.Syms.Lookup("runtime.types", 0).Sect = sect + ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect + + for i, symnro := range sym.ReadOnly { + if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix { + // Skip forward so that no type + // reference uses a zero offset. + // This is unlikely but possible in small + // programs with no other read-only data. + state.datsize++ + } + + symn := sym.RelROMap[symnro] + symnStartValue := state.datsize + + for _, s := range state.data[symn] { + 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) + } + } + state.assignToSection(sect, symn, sym.SRODATA) + if ctxt.HeadType == objabi.Haix { + // Read-only symbols might be wrapped inside their outer + // symbol. + // XCOFF symbol table needs to know the size of + // these outer symbols. + xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) + } + } + + sect.Length = uint64(state.datsize) - sect.Vaddr + } + + /* typelink */ + sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm) + typelink := ctxt.Syms.Lookup("runtime.typelink", 0) + typelink.Sect = sect + typelink.Type = sym.SRODATA + state.datsize += typelink.Size + state.checkdatsize(sym.STYPELINK) + sect.Length = uint64(state.datsize) - sect.Vaddr + + /* itablink */ + sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".itablink"), sym.SITABLINK, sym.Sxxx, relroSecPerm) + ctxt.Syms.Lookup("runtime.itablink", 0).Sect = sect + ctxt.Syms.Lookup("runtime.eitablink", 0).Sect = sect + 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 = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gosymtab"), sym.SSYMTAB, sym.SRODATA, relroSecPerm) + ctxt.Syms.Lookup("runtime.symtab", 0).Sect = sect + ctxt.Syms.Lookup("runtime.esymtab", 0).Sect = sect + + /* gopclntab */ + sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm) + ctxt.Syms.Lookup("runtime.pclntab", 0).Sect = sect + ctxt.Syms.Lookup("runtime.epclntab", 0).Sect = sect + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if state.datsize != int64(uint32(state.datsize)) { + Errorf(nil, "read-only data segment too large: %d", state.datsize) + } + + for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { + ctxt.datap = append(ctxt.datap, state.data[symn]...) + } +} + +// allocateDwarfSections allocates sym.Section objects for DWARF +// symbols, and assigns symbols to sections. +func (state *dodataState) allocateDwarfSections(ctxt *Link) { + + alignOne := func(datsize int64, s *sym.Symbol) int64 { return datsize } + + for i := 0; i < len(dwarfp); i++ { + // First the section symbol. + s := dwarfp[i].secSym() + sect := state.allocateNamedDataSection(&Segdwarf, s.Name, []sym.SymKind{}, 04) + sect.Sym = s + s.Sect = sect + curType := s.Type + s.Type = sym.SRODATA + s.Value = int64(uint64(state.datsize) - sect.Vaddr) + state.datsize += s.Size + + // Then any sub-symbols for the section symbol. + subSyms := dwarfp[i].subSyms() + state.assignDsymsToSection(sect, subSyms, sym.SRODATA, alignOne) + + for j := 0; j < len(subSyms); j++ { + s := subSyms[j] + if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC { + // Update the size of .debug_loc for this symbol's + // package. + addDwsectCUSize(".debug_loc", s.File, uint64(s.Size)) + } + } + sect.Length = uint64(state.datsize) - sect.Vaddr + state.checkdatsize(curType) + } +} + +func dodataSect(ctxt *Link, symn sym.SymKind, syms []*sym.Symbol, symToIdx map[*sym.Symbol]loader.Sym) (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, + symIdx: symToIdx[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 +} diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index fa7221ffb1..d3270720f1 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -628,9 +628,9 @@ func elfwriteshdrs(out *OutBuf) uint32 { return uint32(ehdr.shnum) * ELF32SHDRSIZE } -func elfsetstring(s *sym.Symbol, str string, off int) { +func elfsetstring2(ctxt *Link, s loader.Sym, str string, off int) { if nelfstr >= len(elfstr) { - Errorf(s, "too many elf strings") + ctxt.Errorf(s, "too many elf strings") errorexit() } @@ -753,8 +753,8 @@ func elfWriteDynEnt(arch *sys.Arch, s *sym.Symbol, tag int, val uint64) { } } -func elfWriteDynEntSym(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) { - Elfwritedynentsymplus(arch, s, tag, t, 0) +func elfWriteDynEntSym2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) { + Elfwritedynentsymplus2(ctxt, s, tag, t, 0) } func Elfwritedynentsymplus(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol, add int64) { @@ -1057,15 +1057,16 @@ havelib: return aux } -func elfdynhash(ctxt *Link) { +func elfdynhash2(ctxt *Link) { if !ctxt.IsELF { return } nsym := Nelfsym - s := ctxt.Syms.Lookup(".hash", 0) - s.Type = sym.SELFROSECT - s.Attr |= sym.AttrReachable + ldr := ctxt.loader + s := ldr.CreateSymForUpdate(".hash", 0) + s.SetType(sym.SELFROSECT) + s.SetReachable(true) i := nsym nbucket := 1 @@ -1079,21 +1080,19 @@ func elfdynhash(ctxt *Link) { chain := make([]uint32, nsym) buckets := make([]uint32, nbucket) - for _, sy := range ctxt.Syms.Allsym { - if sy.Dynid <= 0 { - continue + for _, sy := range ldr.DynidSyms() { + + dynid := ldr.SymDynid(sy) + if ldr.SymDynimpvers(sy) != "" { + need[dynid] = addelflib(&needlib, ldr.SymDynimplib(sy), ldr.SymDynimpvers(sy)) } - if sy.Dynimpvers() != "" { - need[sy.Dynid] = addelflib(&needlib, sy.Dynimplib(), sy.Dynimpvers()) - } - - name := sy.Extname() + name := ldr.SymExtname(sy) hc := elfhash(name) b := hc % uint32(nbucket) - chain[sy.Dynid] = buckets[b] - buckets[b] = uint32(sy.Dynid) + chain[dynid] = buckets[b] + buckets[b] = uint32(dynid) } // s390x (ELF64) hash table entries are 8 bytes @@ -1117,10 +1116,11 @@ func elfdynhash(ctxt *Link) { } } - // version symbols - dynstr := ctxt.Syms.Lookup(".dynstr", 0) + dynstr := ldr.CreateSymForUpdate(".dynstr", 0) - s = ctxt.Syms.Lookup(".gnu.version_r", 0) + // version symbols + gnuVersionR := ldr.CreateSymForUpdate(".gnu.version_r", 0) + s = gnuVersionR i = 2 nfile := 0 for l := needlib; l != nil; l = l.next { @@ -1132,9 +1132,9 @@ func elfdynhash(ctxt *Link) { 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 + s.AddUint16(ctxt.Arch, uint16(j)) // aux count + s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(l.file))) // file string offset + s.AddUint32(ctxt.Arch, 16) // offset from header to first aux if l.next != nil { s.AddUint32(ctxt.Arch, 16+uint32(j)*16) // offset from this header to next } else { @@ -1146,10 +1146,10 @@ func elfdynhash(ctxt *Link) { 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 + s.AddUint32(ctxt.Arch, elfhash(x.vers)) // hash + s.AddUint16(ctxt.Arch, 0) // flags + s.AddUint16(ctxt.Arch, uint16(x.num)) // other - index we refer to this by + s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(x.vers))) // version string offset if x.next != nil { s.AddUint32(ctxt.Arch, 16) // offset from this aux to next } else { @@ -1159,7 +1159,8 @@ func elfdynhash(ctxt *Link) { } // version references - s = ctxt.Syms.Lookup(".gnu.version", 0) + gnuVersion := ldr.CreateSymForUpdate(".gnu.version", 0) + s = gnuVersion for i := 0; i < nsym; i++ { if i == 0 { @@ -1171,26 +1172,26 @@ func elfdynhash(ctxt *Link) { } } - s = ctxt.Syms.Lookup(".dynamic", 0) + s = ldr.CreateSymForUpdate(".dynamic", 0) elfverneed = nfile if elfverneed != 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)) + elfWriteDynEntSym2(ctxt, s, DT_VERNEED, gnuVersionR.Sym()) + Elfwritedynent2(ctxt.Arch, s, DT_VERNEEDNUM, uint64(nfile)) + elfWriteDynEntSym2(ctxt, s, DT_VERSYM, gnuVersion.Sym()) } - sy := ctxt.Syms.Lookup(elfRelType+".plt", 0) - if sy.Size > 0 { + sy := ldr.CreateSymForUpdate(elfRelType+".plt", 0) + if sy.Size() > 0 { if elfRelType == ".rela" { - elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_RELA) + Elfwritedynent2(ctxt.Arch, s, DT_PLTREL, DT_RELA) } else { - elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_REL) + Elfwritedynent2(ctxt.Arch, s, DT_PLTREL, DT_REL) } - elfWriteDynEntSymSize(ctxt.Arch, s, DT_PLTRELSZ, sy) - elfWriteDynEntSym(ctxt.Arch, s, DT_JMPREL, sy) + elfwritedynentsymsize2(ctxt, s, DT_PLTRELSZ, sy.Sym()) + elfWriteDynEntSym2(ctxt, s, DT_JMPREL, sy.Sym()) } - elfWriteDynEnt(ctxt.Arch, s, DT_NULL, 0) + Elfwritedynent2(ctxt.Arch, s, DT_NULL, 0) } func elfphload(seg *sym.Segment) *ElfPhdr { diff --git a/src/cmd/link/internal/ld/elf2.go b/src/cmd/link/internal/ld/elf2.go new file mode 100644 index 0000000000..e77510f4a6 --- /dev/null +++ b/src/cmd/link/internal/ld/elf2.go @@ -0,0 +1,166 @@ +// 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/sys" + "cmd/link/internal/sym" +) + +// Temporary dumping around for sym.Symbol version of helper +// functions in elf.go, still being used for some archs/oses. +// FIXME: get rid of this file when dodata() is completely +// converted and the sym.Symbol functions are not needed. + +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 elfWriteDynEntSym(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) { + Elfwritedynentsymplus(arch, s, tag, t, 0) +} + +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.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.Arch, s, DT_PLTREL, DT_RELA) + } else { + elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_REL) + } + elfWriteDynEntSymSize(ctxt.Arch, s, DT_PLTRELSZ, sy) + elfWriteDynEntSym(ctxt.Arch, s, DT_JMPREL, sy) + } + + elfWriteDynEnt(ctxt.Arch, s, DT_NULL, 0) +} diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 8474cefa39..3bcd56ef6d 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -331,7 +331,7 @@ func adddynlib(ctxt *Link, lib string) { } } -func Adddynsym2(ldr *loader.Loader, reporter *ErrorReporter, target *Target, syms *ArchSyms, s loader.Sym) { +func Adddynsym2(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) { if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal { return } @@ -339,11 +339,11 @@ func Adddynsym2(ldr *loader.Loader, reporter *ErrorReporter, target *Target, sym 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)) + ldr.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") + ldr.Errorf(s, "adddynsym: unsupported binary format") } } @@ -425,7 +425,7 @@ func (ctxt *Link) addexport() { } for _, exp := range ctxt.dynexp2 { - Adddynsym2(ctxt.loader, &ctxt.ErrorReporter, &ctxt.Target, &ctxt.ArchSyms, exp) + Adddynsym2(ctxt.loader, &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 675103ee45..ede7596770 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -232,6 +232,7 @@ type Arch struct { Dragonflydynld string Solarisdynld string Adddynrel func(*Target, *loader.Loader, *ArchSyms, *sym.Symbol, *sym.Reloc) bool + Adddynrel2 func(*Target, *loader.Loader, *ArchSyms, loader.Sym, *loader.Reloc2, int) bool Archinit func(*Link) // Archreloc is an arch-specific hook that assists in // relocation processing (invoked by 'relocsym'); it handles @@ -2800,7 +2801,7 @@ func addToTextp(ctxt *Link) { ctxt.Textp = textp } -func (ctxt *Link) loadlibfull() { +func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind) { // Load full symbol contents, resolve indexed references. ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms) @@ -2841,12 +2842,41 @@ func (ctxt *Link) loadlibfull() { dwarfp = append(dwarfp, dwarfSecInfo2{syms: syms}) } + // Populate datap from datap2 + ctxt.datap = make([]*sym.Symbol, len(ctxt.datap2)) + for i, symIdx := range ctxt.datap2 { + s := ctxt.loader.Syms[symIdx] + if s == nil { + panic(fmt.Sprintf("nil sym for datap2 element %d", symIdx)) + } + ctxt.datap[i] = s + } + + // Populate the sym.Section 'Sym' fields based on their 'Sym2' + // fields. + allSegments := []*sym.Segment{&Segtext, &Segrodata, &Segrelrodata, &Segdata, &Segdwarf} + for _, seg := range allSegments { + for _, sect := range seg.Sections { + if sect.Sym2 != 0 { + s := ctxt.loader.Syms[sect.Sym2] + if s == nil { + panic(fmt.Sprintf("nil sym for sect %s sym %d", sect.Name, sect.Sym2)) + } + sect.Sym = s + } + } + } + // For now, overwrite symbol type with its "group" type, as dodata // expected. Once we converted dodata, this will probably not be // needed. for i, t := range symGroupType { if t != sym.Sxxx { - ctxt.loader.Syms[i].Type = t + s := ctxt.loader.Syms[i] + if s == nil { + panic(fmt.Sprintf("nil sym for symGroupType t=%s entry %d", t.String(), i)) + } + s.Type = t } } symGroupType = nil diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index f6441a5b65..6597d84368 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -92,6 +92,7 @@ type Link struct { cgo_export_dynamic map[string]bool datap []*sym.Symbol + datap2 []loader.Sym dynexp2 []loader.Sym // Elf symtab variables. diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 91656170b8..fe65d944c1 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -95,6 +95,7 @@ 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`") + flagnewDoData = flag.Bool("newdodata", true, "New style dodata") 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") @@ -155,6 +156,13 @@ func Main(arch *sys.Arch, theArch Arch) { } } + if *flagnewDoData { + // New dodata() is currently only implemented for linux/amd64. + if !(ctxt.IsElf() && ctxt.IsAMD64()) { + *flagnewDoData = false + } + } + checkStrictDups = *FlagStrictDups startProfile() @@ -297,11 +305,17 @@ func Main(arch *sys.Arch, theArch Arch) { bench.Start("dwarfGenerateDebugSyms") dwarfGenerateDebugSyms(ctxt) bench.Start("symtab") - ctxt.symtab() + symGroupType := ctxt.symtab() + if *flagnewDoData { + bench.Start("dodata") + ctxt.dodata2(symGroupType) + } bench.Start("loadlibfull") - ctxt.loadlibfull() // XXX do it here for now - bench.Start("dodata") - ctxt.dodata() + ctxt.loadlibfull(symGroupType) // XXX do it here for now + if !*flagnewDoData { + bench.Start("dodata") + ctxt.dodata() + } bench.Start("address") order := ctxt.address() bench.Start("dwarfcompress") diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 9aca0ded3b..5954176b1c 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -327,9 +327,7 @@ func textsectionmap(ctxt *Link) (loader.Sym, uint32) { return t.Sym(), uint32(n) } -var symGroupType []sym.SymKind // temporarily assign a symbol's "group" type - -func (ctxt *Link) symtab() { +func (ctxt *Link) symtab() []sym.SymKind { ldr := ctxt.loader if !ctxt.IsAIX() { @@ -441,7 +439,7 @@ func (ctxt *Link) symtab() { // just defined above will be first. // hide the specific symbols. nsym := loader.Sym(ldr.NSym()) - symGroupType = make([]sym.SymKind, nsym) + symGroupType := make([]sym.SymKind, nsym) for s := loader.Sym(1); s < nsym; s++ { name := ldr.SymName(s) if !ctxt.IsExternal() && isStaticTemp(name) { @@ -709,6 +707,7 @@ func (ctxt *Link) symtab() { lastmoduledatap.SetData(nil) lastmoduledatap.AddAddr(ctxt.Arch, moduledata.Sym()) } + return symGroupType } func isStaticTemp(name string) bool { diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index 94920f4457..b0c0c5d65c 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -567,11 +567,12 @@ var ( // 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) { +func xcoffUpdateOuterSize2(ctxt *Link, size int64, stype sym.SymKind) { if size == 0 { return } + ldr := ctxt.loader switch stype { default: Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String()) @@ -580,14 +581,16 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) { 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 + tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0)) + outerSymSize["typerel.*"] = size - tsize return } fallthrough case sym.STYPE: if !ctxt.DynlinkingGo() { // runtime.types size must be removed, as it's a real symbol. - outerSymSize["type.*"] = size - ctxt.Syms.ROLookup("runtime.types", 0).Size + tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0)) + outerSymSize["type.*"] = size - tsize } case sym.SGOSTRING: outerSymSize["go.string.*"] = size @@ -603,7 +606,6 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) { outerSymSize["runtime.itablink"] = size } - } // addSymbol writes a symbol or an auxiliary symbol entry on ctxt.out. diff --git a/src/cmd/link/internal/ld/xcoff2.go b/src/cmd/link/internal/ld/xcoff2.go new file mode 100644 index 0000000000..f2c893feca --- /dev/null +++ b/src/cmd/link/internal/ld/xcoff2.go @@ -0,0 +1,52 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import "cmd/link/internal/sym" + +// Temporary dumping around for sym.Symbol version of helper +// functions in xcoff.go, still being used for some archs/oses. +// FIXME: get rid of this file when dodata() is completely +// converted. + +// 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 + + } +} diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 458c87a6b6..114bd43477 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1268,6 +1268,17 @@ func (l *Loader) SetSymDynid(i Sym, val int32) { } } +// DynIdSyms returns the set of symbols for which dynID is set to an +// interesting (non-default) value. This is expected to be a fairly +// small set. +func (l *Loader) DynidSyms() []Sym { + sl := make([]Sym, 0, len(l.dynid)) + for s := range l.dynid { + sl = append(sl, s) + } + return sl +} + // SymGoType returns the 'Gotype' property for a given symbol (set by // the Go compiler for variable symbols). This version relies on // reading aux symbols for the target sym -- it could be that a faster @@ -2255,6 +2266,30 @@ func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUni return s } +// TopLevelSym tests a symbol (by name and kind) to determine whether +// the symbol first class sym (participating in the link) or is an +// anonymous aux or sub-symbol containing some sub-part or payload of +// another symbol. +func (l *Loader) TopLevelSym(s Sym) bool { + return topLevelSym(l.RawSymName(s), l.SymType(s)) +} + +// topLevelSym tests a symbol name and kind to determine whether +// the symbol first class sym (participating in the link) or is an +// anonymous aux or sub-symbol containing some sub-part or payload of +// another symbol. +func topLevelSym(sname string, skind sym.SymKind) bool { + if sname != "" { + return true + } + switch skind { + case sym.SDWARFINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES, sym.SGOFUNC: + return true + default: + return false + } +} + // 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. @@ -2268,16 +2303,11 @@ func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int { osym := r.Sym(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 (eg: - // funcdata). DWARF symbols are an exception however -- we - // want to include all reachable but nameless DWARF symbols. - if name == "" { - switch t { - case sym.SDWARFINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES: - default: - continue - } + + // Skip non-dwarf anonymous symbols (e.g. funcdata), + // since they will never be turned into sym.Symbols. + if !topLevelSym(name, t) { + continue } ver := abiToVer(osym.ABI(), r.version) if t == sym.SXREF { diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go index 21ed621f80..70adb369a4 100644 --- a/src/cmd/link/internal/loader/symbolbuilder.go +++ b/src/cmd/link/internal/loader/symbolbuilder.go @@ -447,3 +447,10 @@ func GenAddAddrPlusFunc(internalExec bool) func(s *SymbolBuilder, arch *sys.Arch return (*SymbolBuilder).AddAddrPlus } } + +func (sb *SymbolBuilder) MakeWritable() { + if sb.ReadOnly() { + sb.data = append([]byte(nil), sb.data...) + sb.l.SetAttrReadOnly(sb.symIdx, false) + } +} diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 0e3a691432..8f7fe35220 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -972,7 +972,7 @@ func addpltsym2(ctxt *ld.Link, ldr *loader.Loader, s loader.Sym) { return } - ld.Adddynsym2(ldr, &ctxt.ErrorReporter, &ctxt.Target, &ctxt.ArchSyms, s) + ld.Adddynsym2(ldr, &ctxt.Target, &ctxt.ArchSyms, s) if ctxt.IsELF { plt := ldr.MakeSymbolUpdater(ctxt.PLT2) diff --git a/src/cmd/link/internal/sym/segment.go b/src/cmd/link/internal/sym/segment.go index 5ca0228163..662e8e0c8f 100644 --- a/src/cmd/link/internal/sym/segment.go +++ b/src/cmd/link/internal/sym/segment.go @@ -55,6 +55,7 @@ type Section struct { Elfsect interface{} // an *ld.ElfShdr Reloff uint64 Rellen uint64 - Sym *Symbol // symbol for the section, if any - Index uint16 // each section has a unique index, used internally + Sym *Symbol // symbol for the section, if any + Sym2 LoaderSym // symbol for the section, if any + Index uint16 // each section has a unique index, used internally } From 3b32a446991bd927aba7161dd8ca2dffc6557e46 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Thu, 23 Apr 2020 23:30:15 -0400 Subject: [PATCH 05/42] [dev.link] cmd/link: remove -a flag It doesn't do what it says. It has been like that since Go 1.4. The current ouput is pretty useless. Remove it. Change-Id: Id9b4ba04139aaf7ea59acbd51428b1c992115389 Reviewed-on: https://go-review.googlesource.com/c/go/+/229859 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Jeremy Faller Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/data.go | 133 ------------------------------ src/cmd/link/internal/ld/main.go | 2 +- src/cmd/link/internal/ld/xcoff.go | 39 --------- 3 files changed, 1 insertion(+), 173 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 505f22d595..63e05a2645 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -736,66 +736,7 @@ func Codeblk(ctxt *Link, out *OutBuf, addr int64, size int64) { } 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()) - } - writeBlocks(out, ctxt.outSem, 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) - } - } } const blockSize = 1 << 20 // 1MB chunks written at a time. @@ -964,84 +905,10 @@ func DatblkBytes(ctxt *Link, addr int64, size int64) []byte { } 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()) - } - writeBlocks(out, ctxt.outSem, ctxt.datap, addr, size, zeros[:]) - - /* again for printing */ - if !*flagA { - return - } - - syms := ctxt.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, out *OutBuf, addr int64, size int64) { - if *flagA { - ctxt.Logf("dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset()) - } - // Concatenate the section symbol lists into a single list to pass // to writeBlocks. // diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index fe65d944c1..07ed057bf6 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -74,7 +74,7 @@ var ( 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") + flagA = flag.Bool("a", false, "no-op (deprecated)") FlagC = flag.Bool("c", false, "dump call graph") FlagD = flag.Bool("d", false, "disable dynamic executable") flagF = flag.Bool("f", false, "ignore version mismatch") diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index b0c0c5d65c..e4f30ffb31 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -1391,45 +1391,6 @@ func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) { } f.loaderSize = off + uint64(stlen) - - /* 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 From f790533d9ffd6d50c8efccf8f8edac0ef76b3da4 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Thu, 23 Apr 2020 20:54:04 -0400 Subject: [PATCH 06/42] [dev.link] cmd/link: support new dodata for elf/386 Add elf/386 arch support for the new dodata() phase. Change-Id: I78341dfe70a90719d95c0044183980f348a3369f Reviewed-on: https://go-review.googlesource.com/c/go/+/229797 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/link/internal/ld/main.go | 5 +- src/cmd/link/internal/x86/asm.go | 251 ++++++++++++++------------ src/cmd/link/internal/x86/asm2.go | 283 ++++++++++++++++++++++++++++++ src/cmd/link/internal/x86/obj.go | 1 + 4 files changed, 428 insertions(+), 112 deletions(-) create mode 100644 src/cmd/link/internal/x86/asm2.go diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 07ed057bf6..8b6b9b84a6 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -157,8 +157,9 @@ func Main(arch *sys.Arch, theArch Arch) { } if *flagnewDoData { - // New dodata() is currently only implemented for linux/amd64. - if !(ctxt.IsElf() && ctxt.IsAMD64()) { + // New dodata() is currently only implemented for selected targets. + if !(ctxt.IsElf() && + (ctxt.IsAMD64() || ctxt.Is386())) { *flagnewDoData = false } } diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 650fe12f94..03c73671b8 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -129,161 +129,189 @@ func gentext2(ctxt *ld.Link, ldr *loader.Loader) { o(0xc3) } -func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { - targ := r.Sym +func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r *loader.Reloc2, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } - switch r.Type { + switch r.Type() { default: - if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) return false } // Handle relocations found in ELF object files. case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PC32): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", ldr.SymName(targ)) } // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make // sense and should be removed when someone has thought about it properly. - if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { - ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) } - r.Type = objabi.R_PCREL - r.Add += 4 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PLT32): - r.Type = objabi.R_PCREL - r.Add += 4 - if targ.Type == sym.SDYNIMPORT { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add += int64(targ.Plt()) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) } return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32), objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32X): - if targ.Type != sym.SDYNIMPORT { + su := ldr.MakeSymbolUpdater(s) + if targType != 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 + sData := ldr.Data(s) - r.Type = objabi.R_GOTOFF + if r.Off() >= 2 && sData[r.Off()-2] == 0x8b { + su.MakeWritable() + + // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT. + writeableData := su.Data() + writeableData[r.Off()-2] = 0x8d + su.SetRelocType(rIdx, objabi.R_GOTOFF) return true } - if r.Off >= 2 && s.P[r.Off-2] == 0xff && s.P[r.Off-1] == 0xb3 { + if r.Off() >= 2 && sData[r.Off()-2] == 0xff && sData[r.Off()-1] == 0xb3 { + su.MakeWritable() // turn PUSHL of GOT entry into PUSHL of symbol itself. // use unnecessary SS prefix to keep instruction same length. - s.P[r.Off-2] = 0x36 - - s.P[r.Off-1] = 0x68 - r.Type = objabi.R_ADDR + writeableData := su.Data() + writeableData[r.Off()-2] = 0x36 + writeableData[r.Off()-1] = 0x68 + su.SetRelocType(rIdx, objabi.R_ADDR) return true } - ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) return false } - addgotsym(target, syms, targ) - r.Type = objabi.R_CONST // write r->add during relocsym - r.Sym = nil - r.Add += int64(targ.Got()) + addgotsym2(target, ldr, syms, targ) + su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym + su.SetRelocSym(rIdx, 0) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTOFF): - r.Type = objabi.R_GOTOFF + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_GOTOFF) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC): - r.Type = objabi.R_PCREL - r.Sym = syms.GOT - r.Add += 4 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, 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) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ADDR + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, 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) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ)) } return true case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1: - if targ.Type == sym.SDYNIMPORT { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(targ.Plt()) - r.Type = objabi.R_PCREL + su := ldr.MakeSymbolUpdater(s) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + su.SetRelocType(rIdx, objabi.R_PCREL) return true } - r.Type = objabi.R_PCREL + su.SetRelocType(rIdx, objabi.R_PCREL) return true case objabi.MachoRelocOffset + ld.MACHO_FAKE_GOTPCREL: - if targ.Type != sym.SDYNIMPORT { + su := ldr.MakeSymbolUpdater(s) + if targType != 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) + sData := ldr.Data(s) + if r.Off() < 2 || sData[r.Off()-2] != 0x8b { + ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) return false } - s.P[r.Off-2] = 0x8d - r.Type = objabi.R_PCREL + su.MakeWritable() + writeableData := su.Data() + writeableData[r.Off()-2] = 0x8d + su.SetRelocType(rIdx, objabi.R_PCREL) return true } - addgotsym(target, syms, targ) - r.Sym = syms.GOT - r.Add += int64(targ.Got()) - r.Type = objabi.R_PCREL + addgotsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + su.SetRelocType(rIdx, objabi.R_PCREL) return true } // Handle references to ELF symbols from our own object files. - if targ.Type != sym.SDYNIMPORT { + if targType != sym.SDYNIMPORT { return true } - switch r.Type { + + // Reread the reloc to incorporate any changes in type above. + relocs := ldr.Relocs(s) + *r = relocs.At2(rIdx) + + switch r.Type() { case objabi.R_CALL, objabi.R_PCREL: if target.IsExternal() { // External linker will do this relocation. return true } - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(targ.Plt()) + addpltsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) return true case objabi.R_ADDR: - if s.Type != sym.SDATA { + if ldr.SymType(s) != sym.SDATA { break } 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 + ld.Adddynsym2(ldr, target, syms, targ) + rel := ldr.MakeSymbolUpdater(syms.Rel2) + rel.AddAddrPlus(target.Arch, s, int64(r.Off())) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_386_32))) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym + su.SetRelocSym(rIdx, 0) return true } - if target.IsDarwin() && s.Size == int64(target.Arch.PtrSize) && r.Off == 0 { + if target.IsDarwin() && ldr.SymSize(s) == 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. @@ -294,18 +322,17 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. // 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(target, syms, targ) + ld.Adddynsym2(ldr, target, syms, targ) - 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 := ldr.MakeSymbolUpdater(syms.GOT2) + su := ldr.MakeSymbolUpdater(s) + su.SetType(got.Type()) + got.PrependSub(s) + su.SetValue(got.Size()) got.AddUint32(target.Arch, 0) - syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid)) - r.Type = objabi.ElfRelocOffset // ignore during relocsym + leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT2) + leg.AddUint32(target.Arch, uint32(ldr.SymDynid(targ))) + su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym return true } } @@ -452,18 +479,18 @@ func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.S } } -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { +func addpltsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { return } - ld.Adddynsym(target, syms, s) + ld.Adddynsym2(ldr, target, syms, s) if target.IsElf() { - plt := syms.PLT - got := syms.GOTPLT - rel := syms.RelPLT - if plt.Size == 0 { + plt := ldr.MakeSymbolUpdater(syms.PLT2) + got := ldr.MakeSymbolUpdater(syms.GOTPLT2) + rel := ldr.MakeSymbolUpdater(syms.RelPLT2) + if plt.Size() == 0 { panic("plt is not set up") } @@ -471,65 +498,69 @@ func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddAddrPlus(target.Arch, got, got.Size) + plt.AddAddrPlus(target.Arch, got.Sym(), got.Size()) // add to got: pointer to current pos in plt - got.AddAddrPlus(target.Arch, plt, plt.Size) + got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()) // pushl $x plt.AddUint8(0x68) - plt.AddUint32(target.Arch, uint32(rel.Size)) + plt.AddUint32(target.Arch, uint32(rel.Size())) // jmp .plt plt.AddUint8(0xe9) - plt.AddUint32(target.Arch, uint32(-(plt.Size + 4))) + plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4))) // rel - rel.AddAddrPlus(target.Arch, got, got.Size-4) + rel.AddAddrPlus(target.Arch, got.Sym(), got.Size()-4) - rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_JMP_SLOT))) + sDynid := ldr.SymDynid(s) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(sDynid), uint32(elf.R_386_JMP_SLOT))) - s.SetPlt(int32(plt.Size - 16)) + ldr.SetPlt(s, int32(plt.Size()-16)) } else if target.IsDarwin() { // Same laziness as in 6l. - plt := syms.PLT + plt := ldr.MakeSymbolUpdater(syms.PLT2) - addgotsym(target, syms, s) + addgotsym2(target, ldr, syms, s) - syms.LinkEditPLT.AddUint32(target.Arch, uint32(s.Dynid)) + sDynid := ldr.SymDynid(s) + lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT2) + lep.AddUint32(target.Arch, uint32(sDynid)) // jmpq *got+size(IP) - s.SetPlt(int32(plt.Size)) + ldr.SetPlt(s, int32(plt.Size())) plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddAddrPlus(target.Arch, syms.GOT, int64(s.Got())) + plt.AddAddrPlus(target.Arch, syms.GOT2, int64(ldr.SymGot(s))) } else { - ld.Errorf(s, "addpltsym: unsupported binary format") + ldr.Errorf(s, "addpltsym: unsupported binary format") } } -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { +func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymGot(s) >= 0 { return } - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) + ld.Adddynsym2(ldr, target, syms, s) + got := ldr.MakeSymbolUpdater(syms.GOT2) + ldr.SetGot(s, int32(got.Size())) 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))) + rel := ldr.MakeSymbolUpdater(syms.Rel2) + rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_386_GLOB_DAT))) } else if target.IsDarwin() { - syms.LinkEditGOT.AddUint32(target.Arch, uint32(s.Dynid)) + leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT2) + leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s))) } else { - ld.Errorf(s, "addgotsym: unsupported binary format") + ldr.Errorf(s, "addgotsym: unsupported binary format") } } diff --git a/src/cmd/link/internal/x86/asm2.go b/src/cmd/link/internal/x86/asm2.go new file mode 100644 index 0000000000..16f1682dc1 --- /dev/null +++ b/src/cmd/link/internal/x86/asm2.go @@ -0,0 +1,283 @@ +// 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 x86 + +import ( + "cmd/internal/objabi" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +// Temporary dumping around for sym.Symbol version of helper +// functions in asm.go, still being used for some oses. +// FIXME: get rid of this file when dodata() is completely +// converted. + +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(target.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(target, syms, targ) + r.Sym = syms.PLT + 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(target, syms, 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 = syms.GOT + 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(target, syms, targ) + r.Sym = syms.PLT + 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(target, syms, targ) + r.Sym = syms.GOT + 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 target.IsExternal() { + // External linker will do this relocation. + return true + } + addpltsym(target, syms, targ) + r.Sym = syms.PLT + r.Add = int64(targ.Plt()) + return true + + case objabi.R_ADDR: + if s.Type != sym.SDATA { + break + } + 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 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. + // 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(target, syms, targ) + + 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(target.Arch, 0) + syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid)) + r.Type = objabi.ElfRelocOffset // ignore during relocsym + return true + } + } + + return false +} + +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + + if target.IsElf() { + plt := syms.PLT + got := syms.GOTPLT + rel := syms.RelPLT + if plt.Size == 0 { + panic("plt is not set up") + } + + // jmpq *got+size + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddAddrPlus(target.Arch, got, got.Size) + + // add to got: pointer to current pos in plt + got.AddAddrPlus(target.Arch, plt, plt.Size) + + // pushl $x + plt.AddUint8(0x68) + + plt.AddUint32(target.Arch, uint32(rel.Size)) + + // jmp .plt + plt.AddUint8(0xe9) + + plt.AddUint32(target.Arch, uint32(-(plt.Size + 4))) + + // rel + rel.AddAddrPlus(target.Arch, got, got.Size-4) + + 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 target.IsDarwin() { + // Same laziness as in 6l. + + plt := syms.PLT + + addgotsym(target, syms, s) + + 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(target.Arch, syms.GOT, int64(s.Got())) + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + got := syms.GOT + s.SetGot(int32(got.Size)) + 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") + } +} diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go index 952113fbff..34babe9a72 100644 --- a/src/cmd/link/internal/x86/obj.go +++ b/src/cmd/link/internal/x86/obj.go @@ -47,6 +47,7 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfreglr: dwarfRegLR, Adddynrel: adddynrel, + Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, Archrelocvariant: archrelocvariant, From f2d8da1a353b493548e211b06fdf47bd998ad4b6 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 12:13:11 -0400 Subject: [PATCH 07/42] [dev.link] cmd/link: set HeadType early So we can use it to set per-OS flags. Also set flagnewDoData after archinit, where IsELF is set. This should correct the logic of setting flagnewDoData. Change-Id: I18c7252f141aa35119005c252becc9d7cb74f2f7 Reviewed-on: https://go-review.googlesource.com/c/go/+/229867 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/main.go | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 8b6b9b84a6..d8b4c8a94d 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -155,13 +155,8 @@ func Main(arch *sys.Arch, theArch Arch) { usage() } } - - if *flagnewDoData { - // New dodata() is currently only implemented for selected targets. - if !(ctxt.IsElf() && - (ctxt.IsAMD64() || ctxt.Is386())) { - *flagnewDoData = false - } + if ctxt.HeadType == objabi.Hunknown { + ctxt.HeadType.Set(objabi.GOOS) } checkStrictDups = *FlagStrictDups @@ -199,16 +194,19 @@ func Main(arch *sys.Arch, theArch Arch) { 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 *flagnewDoData { + // New dodata() is currently only implemented for selected targets. + if !(ctxt.IsElf() && + (ctxt.IsAMD64() || ctxt.Is386())) { + *flagnewDoData = false + } + } + if ctxt.linkShared && !ctxt.IsELF { Exitf("-linkshared can only be used on elf systems") } From 880ef2da7b81fe2e4e9fb75f4677377eeba70d1e Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 13:02:37 -0400 Subject: [PATCH 08/42] [dev.link] cmd/link: panic if HeadType is not set In the code there are conditions like !ctxt.IsDarwin(). This will accidentally be true if HeadType is not yet set. Panic when HeadType is not set, to catch errors. Change-Id: Ic891123f27f0276fff5a4b5d29e5b1f7ebbb94ee Reviewed-on: https://go-review.googlesource.com/c/go/+/229869 Run-TryBot: Cherry Zhang Reviewed-by: Than McIntosh TryBot-Result: Gobot Gobot --- src/cmd/link/internal/ld/target.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go index 7aa2c1ccd0..8c07d77fd8 100644 --- a/src/cmd/link/internal/ld/target.go +++ b/src/cmd/link/internal/ld/target.go @@ -61,6 +61,7 @@ func (t *Target) CanUsePlugins() bool { } func (t *Target) IsElf() bool { + t.mustSetHeadType() return t.IsELF } @@ -112,37 +113,51 @@ func (t *Target) IsWasm() bool { // func (t *Target) IsLinux() bool { + t.mustSetHeadType() return t.HeadType == objabi.Hlinux } func (t *Target) IsDarwin() bool { + t.mustSetHeadType() return t.HeadType == objabi.Hdarwin } func (t *Target) IsWindows() bool { + t.mustSetHeadType() return t.HeadType == objabi.Hwindows } func (t *Target) IsPlan9() bool { + t.mustSetHeadType() return t.HeadType == objabi.Hplan9 } func (t *Target) IsAIX() bool { + t.mustSetHeadType() return t.HeadType == objabi.Haix } func (t *Target) IsSolaris() bool { + t.mustSetHeadType() return t.HeadType == objabi.Hsolaris } func (t *Target) IsNetbsd() bool { + t.mustSetHeadType() return t.HeadType == objabi.Hnetbsd } func (t *Target) IsOpenbsd() bool { + t.mustSetHeadType() return t.HeadType == objabi.Hopenbsd } +func (t *Target) mustSetHeadType() { + if t.HeadType == objabi.Hunknown { + panic("HeadType is not set") + } +} + // // MISC // From e08f10b8b5fbb82ff1e2c263ad57e19d2de1e323 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Wed, 22 Apr 2020 19:21:30 -0400 Subject: [PATCH 09/42] [dev.link] cmd/internal/goobj2: add index fingerprint to object file The new object files use indices for symbol references, instead of names. Fundamental to the design, it requires that the importing and imported packages have consistent view of symbol indices. The Go command should already ensure this, when using "go build". But in case it goes wrong, it could lead to obscure errors like run-time crashes. It would be better to check the index consistency at build time. To do that, we add a fingerprint to each object file, which is a hash of symbol indices. In the object file it records the fingerprints of all imported packages, as well as its own fingerprint. At link time, the linker checks that a package's fingerprint matches the fingerprint recorded in the importing packages, and issue an error if they don't match. This CL does the first part: introducing the fingerprint in the object file, and propagating fingerprints through importing/exporting by the compiler. It is not yet used by the linker. Next CL will do. Change-Id: I0aa372da652e4afb11f2867cb71689a3e3f9966e Reviewed-on: https://go-review.googlesource.com/c/go/+/229617 Reviewed-by: Austin Clements Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/compile/internal/gc/iexport.go | 6 +++ src/cmd/compile/internal/gc/iimport.go | 11 ++++- src/cmd/compile/internal/gc/main.go | 22 +++++----- src/cmd/internal/goobj/readnew.go | 6 ++- src/cmd/internal/goobj2/objfile.go | 58 +++++++++++++++++++------- src/cmd/internal/obj/line.go | 5 ++- src/cmd/internal/obj/link.go | 5 ++- src/cmd/internal/obj/objfile.go | 5 ++- src/cmd/internal/obj/objfile2.go | 14 ++++--- src/cmd/internal/obj/sym.go | 10 +++++ src/cmd/link/internal/loader/loader.go | 6 ++- 11 files changed, 111 insertions(+), 37 deletions(-) diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index 917bf2394a..35b8d985cb 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -35,6 +35,8 @@ // } // } // +// Fingerprint [8]byte +// // uvarint means a uint64 written out using uvarint encoding. // // []T means a uvarint followed by that many T objects. In other @@ -296,6 +298,10 @@ func iexport(out *bufio.Writer) { io.Copy(out, &hdr) io.Copy(out, &p.strings) io.Copy(out, &p.data0) + + // Add fingerprint (used by linker object file). + // Attach this to the end, so tools (e.g. gcimporter) don't care. + out.Write(Ctxt.Fingerprint[:]) } // writeIndex writes out an object index. mainIndex indicates whether diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index f881a33ea7..f2f49f002c 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -10,6 +10,7 @@ package gc import ( "cmd/compile/internal/types" "cmd/internal/bio" + "cmd/internal/goobj2" "cmd/internal/obj" "cmd/internal/src" "encoding/binary" @@ -95,7 +96,7 @@ func (r *intReader) uint64() uint64 { return i } -func iimport(pkg *types.Pkg, in *bio.Reader) { +func iimport(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj2.FingerprintType) { ir := &intReader{in, pkg} version := ir.uint64() @@ -188,6 +189,14 @@ func iimport(pkg *types.Pkg, in *bio.Reader) { inlineImporter[s] = iimporterAndOffset{p, off} } } + + // Fingerprint + n, err := in.Read(fingerprint[:]) + if err != nil || n != len(fingerprint) { + yyerror("import %s: error reading fingerprint", pkg.Path) + errorexit() + } + return fingerprint } type iimporter struct { diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 2152c619fa..756cdbd3c9 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -14,6 +14,7 @@ import ( "cmd/compile/internal/types" "cmd/internal/bio" "cmd/internal/dwarf" + "cmd/internal/goobj2" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" @@ -1254,15 +1255,6 @@ func importfile(f *Val) *types.Pkg { } } - // assume files move (get installed) so don't record the full path - if packageFile != nil { - // If using a packageFile map, assume path_ can be recorded directly. - Ctxt.AddImport(path_) - } else { - // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a". - Ctxt.AddImport(file[len(file)-len(path_)-len(".a"):]) - } - // In the importfile, if we find: // $$\n (textual format): not supported anymore // $$B\n (binary format) : import directly, then feed the lexer a dummy statement @@ -1287,6 +1279,7 @@ func importfile(f *Val) *types.Pkg { c, _ = imp.ReadByte() } + var fingerprint goobj2.FingerprintType switch c { case '\n': yyerror("cannot import %s: old export format no longer supported (recompile library)", path_) @@ -1310,13 +1303,22 @@ func importfile(f *Val) *types.Pkg { yyerror("import %s: unexpected package format byte: %v", file, c) errorexit() } - iimport(importpkg, imp) + fingerprint = iimport(importpkg, imp) default: yyerror("no import in %q", path_) errorexit() } + // assume files move (get installed) so don't record the full path + if packageFile != nil { + // If using a packageFile map, assume path_ can be recorded directly. + Ctxt.AddImport(path_, fingerprint) + } else { + // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a". + Ctxt.AddImport(file[len(file)-len(path_)-len(".a"):], fingerprint) + } + if importpkg.Height >= myheight { myheight = importpkg.Height + 1 } diff --git a/src/cmd/internal/goobj/readnew.go b/src/cmd/internal/goobj/readnew.go index 5654da44d6..3e710576b6 100644 --- a/src/cmd/internal/goobj/readnew.go +++ b/src/cmd/internal/goobj/readnew.go @@ -25,7 +25,11 @@ func (r *objReader) readNew() { } // Imports - r.p.Imports = rr.Autolib() + autolib := rr.Autolib() + for _, p := range autolib { + r.p.Imports = append(r.p.Imports, p.Pkg) + // Ignore fingerprint (for tools like objdump which only reads one object). + } pkglist := rr.Pkglist() diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go index bee29a0ad6..3d3bc20133 100644 --- a/src/cmd/internal/goobj2/objfile.go +++ b/src/cmd/internal/goobj2/objfile.go @@ -19,17 +19,21 @@ import ( // New object file format. // // Header struct { -// Magic [...]byte // "\x00go115ld" -// Flags uint32 -// // TODO: Fingerprint -// Offsets [...]uint32 // byte offset of each block below +// Magic [...]byte // "\x00go115ld" +// Fingerprint [8]byte +// Flags uint32 +// Offsets [...]uint32 // byte offset of each block below // } // // Strings [...]struct { // Data [...]byte // } // -// Autolib [...]string // imported packages (for file loading) // TODO: add fingerprints +// Autolib [...]struct { // imported packages (for file loading) +// Pkg string +// Fingerprint [8]byte +// } +// // PkgIndex [...]string // referenced packages by index // // DwarfFiles [...]string @@ -119,6 +123,8 @@ import ( const stringRefSize = 8 // two uint32s +type FingerprintType [8]byte + // Package Index. const ( PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols @@ -149,15 +155,17 @@ const ( // File header. // TODO: probably no need to export this. type Header struct { - Magic string - Flags uint32 - Offsets [NBlk]uint32 + Magic string + Fingerprint FingerprintType + Flags uint32 + Offsets [NBlk]uint32 } const Magic = "\x00go115ld" func (h *Header) Write(w *Writer) { w.RawString(h.Magic) + w.Bytes(h.Fingerprint[:]) w.Uint32(h.Flags) for _, x := range h.Offsets { w.Uint32(x) @@ -171,6 +179,8 @@ func (h *Header) Read(r *Reader) error { return errors.New("wrong magic, not a Go object file") } off := uint32(len(h.Magic)) + copy(h.Fingerprint[:], r.BytesAt(off, len(h.Fingerprint))) + off += 8 h.Flags = r.uint32At(off) off += 4 for i := range h.Offsets { @@ -184,6 +194,19 @@ func (h *Header) Size() int { return len(h.Magic) + 4 + 4*len(h.Offsets) } +// Autolib +type ImportedPkg struct { + Pkg string + Fingerprint FingerprintType +} + +const importedPkgSize = stringRefSize + 8 + +func (p *ImportedPkg) Write(w *Writer) { + w.StringRef(p.Pkg) + w.Bytes(p.Fingerprint[:]) +} + // Symbol definition. // // Serialized format: @@ -495,12 +518,18 @@ func (r *Reader) StringRef(off uint32) string { return r.StringAt(r.uint32At(off+4), l) } -func (r *Reader) Autolib() []string { - n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / stringRefSize - s := make([]string, n) +func (r *Reader) Fingerprint() FingerprintType { + return r.h.Fingerprint +} + +func (r *Reader) Autolib() []ImportedPkg { + n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / importedPkgSize + s := make([]ImportedPkg, n) + off := r.h.Offsets[BlkAutolib] for i := range s { - off := r.h.Offsets[BlkAutolib] + uint32(i)*stringRefSize - s[i] = r.StringRef(off) + s[i].Pkg = r.StringRef(off) + copy(s[i].Fingerprint[:], r.BytesAt(off+stringRefSize, len(s[i].Fingerprint))) + off += importedPkgSize } return s } @@ -508,9 +537,10 @@ func (r *Reader) Autolib() []string { func (r *Reader) Pkglist() []string { n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / stringRefSize s := make([]string, n) + off := r.h.Offsets[BlkPkgIdx] for i := range s { - off := r.h.Offsets[BlkPkgIdx] + uint32(i)*stringRefSize s[i] = r.StringRef(off) + off += stringRefSize } return s } diff --git a/src/cmd/internal/obj/line.go b/src/cmd/internal/obj/line.go index fecf90c491..79ecb0068f 100644 --- a/src/cmd/internal/obj/line.go +++ b/src/cmd/internal/obj/line.go @@ -5,12 +5,13 @@ package obj import ( + "cmd/internal/goobj2" "cmd/internal/src" ) // AddImport adds a package to the list of imported packages. -func (ctxt *Link) AddImport(pkg string) { - ctxt.Imports = append(ctxt.Imports, pkg) +func (ctxt *Link) AddImport(pkg string, fingerprint goobj2.FingerprintType) { + ctxt.Imports = append(ctxt.Imports, goobj2.ImportedPkg{Pkg: pkg, Fingerprint: fingerprint}) } func linkgetlineFromPos(ctxt *Link, xpos src.XPos) (f string, l int32) { diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 046ad53ac7..e6f917dedb 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -33,6 +33,7 @@ package obj import ( "bufio" "cmd/internal/dwarf" + "cmd/internal/goobj2" "cmd/internal/objabi" "cmd/internal/src" "cmd/internal/sys" @@ -666,7 +667,7 @@ type Link struct { PosTable src.PosTable InlTree InlTree // global inlining tree used by gc/inl.go DwFixups *DwarfFixupTable - Imports []string + Imports []goobj2.ImportedPkg DiagFunc func(string, ...interface{}) DiagFlush func() DebugInfo func(fn *LSym, info *LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) // if non-nil, curfn is a *gc.Node @@ -698,6 +699,8 @@ type Link struct { defs []*LSym // list of defined symbols in the current package nonpkgdefs []*LSym // list of defined non-package symbols nonpkgrefs []*LSym // list of referenced non-package symbols + + Fingerprint goobj2.FingerprintType // fingerprint of symbol indices, to catch index mismatch } func (ctxt *Link) Diag(format string, args ...interface{}) { diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 2b0c45d6b2..6d7f42ed0b 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -98,8 +98,9 @@ func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) { w.wr.WriteByte(1) // Autolib - for _, pkg := range ctxt.Imports { - w.writeString(pkg) + for _, p := range ctxt.Imports { + w.writeString(p.Pkg) + // This object format ignores p.Fingerprint. } w.writeString("") diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go index 9792ef0846..061e43c434 100644 --- a/src/cmd/internal/obj/objfile2.go +++ b/src/cmd/internal/obj/objfile2.go @@ -38,7 +38,11 @@ func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) { if ctxt.Flag_shared { flags |= goobj2.ObjFlagShared } - h := goobj2.Header{Magic: goobj2.Magic, Flags: flags} + h := goobj2.Header{ + Magic: goobj2.Magic, + Fingerprint: ctxt.Fingerprint, + Flags: flags, + } h.Write(w.Writer) // String table @@ -46,8 +50,8 @@ func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) { // Autolib h.Offsets[goobj2.BlkAutolib] = w.Offset() - for _, pkg := range ctxt.Imports { - w.StringRef(pkg) + for i := range ctxt.Imports { + ctxt.Imports[i].Write(w.Writer) } // Package references @@ -180,8 +184,8 @@ func (w *writer) init() { func (w *writer) StringTable() { w.AddString("") - for _, pkg := range w.ctxt.Imports { - w.AddString(pkg) + for _, p := range w.ctxt.Imports { + w.AddString(p.Pkg) } for _, pkg := range w.pkglist { w.AddString(pkg) diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index 03ce8ddc5a..4a8b0ebb6f 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -34,6 +34,7 @@ package obj import ( "cmd/internal/goobj2" "cmd/internal/objabi" + "crypto/md5" "fmt" "log" "math" @@ -241,6 +242,15 @@ func (ctxt *Link) NumberSyms(asm bool) { ctxt.pkgIdx[pkg] = ipkg ipkg++ }) + + // Compute a fingerprint of the indices, for exporting. + if !asm { + h := md5.New() + for _, s := range ctxt.defs { + h.Write([]byte(s.Name)) + } + copy(ctxt.Fingerprint[:], h.Sum(nil)[:]) + } } // Returns whether s is a non-package symbol, which needs to be referenced diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 114bd43477..7b59e680ee 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1779,7 +1779,11 @@ func (l *Loader) Preload(syms *sym.Symbols, f *bio.Reader, lib *sym.Library, uni 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()...) + autolib := r.Autolib() + for _, p := range autolib { + lib.ImportStrings = append(lib.ImportStrings, p.Pkg) + // TODO: fingerprint is ignored for now + } // DWARF file table nfile := r.NDwarfFile() From 027055240f55b5664b760c13ddcc938e023e2dfe Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Wed, 22 Apr 2020 22:20:44 -0400 Subject: [PATCH 10/42] [dev.link] cmd/link: check fingerprint for index consistency Previous CL introduced index fingerprint in the object files. This CL implements the second part: checking fingerprint consistency in the linker when packages are loaded. Change-Id: I05dd4c4045a65adfd95e77b625d6c75a7a70e4f1 Reviewed-on: https://go-review.googlesource.com/c/go/+/229618 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/internal/goobj2/objfile.go | 2 + src/cmd/link/internal/ld/ld.go | 15 +++-- src/cmd/link/internal/ld/lib.go | 30 ++++++++-- src/cmd/link/internal/ld/link.go | 6 +- src/cmd/link/internal/ld/main.go | 8 ++- src/cmd/link/internal/loader/loader.go | 11 ++-- src/cmd/link/internal/sym/library.go | 25 ++++---- src/cmd/link/link_test.go | 58 +++++++++++++++++++ src/cmd/link/testdata/testIndexMismatch/a.go | 8 +++ src/cmd/link/testdata/testIndexMismatch/b.go | 8 +++ .../link/testdata/testIndexMismatch/main.go | 9 +++ 11 files changed, 147 insertions(+), 33 deletions(-) create mode 100644 src/cmd/link/testdata/testIndexMismatch/a.go create mode 100644 src/cmd/link/testdata/testIndexMismatch/b.go create mode 100644 src/cmd/link/testdata/testIndexMismatch/main.go diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go index 3d3bc20133..28702ebf07 100644 --- a/src/cmd/internal/goobj2/objfile.go +++ b/src/cmd/internal/goobj2/objfile.go @@ -125,6 +125,8 @@ const stringRefSize = 8 // two uint32s type FingerprintType [8]byte +func (fp FingerprintType) IsZero() bool { return fp == FingerprintType{} } + // Package Index. const ( PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols diff --git a/src/cmd/link/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go index 85038f3ad2..c913a519a1 100644 --- a/src/cmd/link/internal/ld/ld.go +++ b/src/cmd/link/internal/ld/ld.go @@ -32,6 +32,7 @@ package ld import ( + "cmd/internal/goobj2" "cmd/link/internal/loader" "cmd/link/internal/sym" "io/ioutil" @@ -155,11 +156,12 @@ func findlib(ctxt *Link, lib string) (string, bool) { return pname, isshlib } -func addlib(ctxt *Link, src string, obj string, lib string) *sym.Library { +func addlib(ctxt *Link, src, obj, lib string, fingerprint goobj2.FingerprintType) *sym.Library { pkg := pkgname(ctxt, lib) // already loaded? if l := ctxt.LibraryByPkg[pkg]; l != nil { + checkFingerprint(l, l.Fingerprint, src, fingerprint) return l } @@ -170,9 +172,9 @@ func addlib(ctxt *Link, src string, obj string, lib string) *sym.Library { } if isshlib { - return addlibpath(ctxt, src, obj, "", pkg, pname) + return addlibpath(ctxt, src, obj, "", pkg, pname, fingerprint) } - return addlibpath(ctxt, src, obj, pname, pkg, "") + return addlibpath(ctxt, src, obj, pname, pkg, "", fingerprint) } /* @@ -182,14 +184,16 @@ func addlib(ctxt *Link, src string, obj string, lib string) *sym.Library { * file: object file, e.g., /home/rsc/go/pkg/container/vector.a * pkg: package import path, e.g. container/vector * shlib: path to shared library, or .shlibname file holding path + * fingerprint: if not 0, expected fingerprint for import from srcref + * fingerprint is 0 if the library is not imported (e.g. main) */ -func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlib string) *sym.Library { +func addlibpath(ctxt *Link, srcref, objref, file, pkg, shlib string, fingerprint goobj2.FingerprintType) *sym.Library { if l := ctxt.LibraryByPkg[pkg]; l != nil { return l } if ctxt.Debugvlog > 1 { - ctxt.Logf("addlibpath: srcref: %s objref: %s file: %s pkg: %s shlib: %s\n", srcref, objref, file, pkg, shlib) + ctxt.Logf("addlibpath: srcref: %s objref: %s file: %s pkg: %s shlib: %s fingerprint: %x\n", srcref, objref, file, pkg, shlib, fingerprint) } l := &sym.Library{} @@ -199,6 +203,7 @@ func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg strin l.Srcref = srcref l.File = file l.Pkg = pkg + l.Fingerprint = fingerprint if shlib != "" { if strings.HasSuffix(shlib, ".shlibname") { data, err := ioutil.ReadFile(shlib) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index ede7596770..5d01babd5f 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -33,6 +33,7 @@ package ld import ( "bytes" "cmd/internal/bio" + "cmd/internal/goobj2" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/sys" @@ -425,14 +426,15 @@ func errorexit() { } func loadinternal(ctxt *Link, name string) *sym.Library { + zerofp := goobj2.FingerprintType{} if ctxt.linkShared && ctxt.PackageShlib != nil { if shlib := ctxt.PackageShlib[name]; shlib != "" { - return addlibpath(ctxt, "internal", "internal", "", name, shlib) + return addlibpath(ctxt, "internal", "internal", "", name, shlib, zerofp) } } if ctxt.PackageFile != nil { if pname := ctxt.PackageFile[name]; pname != "" { - return addlibpath(ctxt, "internal", "internal", pname, name, "") + return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp) } ctxt.Logf("loadinternal: cannot find %s\n", name) return nil @@ -445,7 +447,7 @@ func loadinternal(ctxt *Link, name string) *sym.Library { 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) + return addlibpath(ctxt, "internal", "internal", "", name, shlibname, zerofp) } } pname := filepath.Join(libdir, name+".a") @@ -453,7 +455,7 @@ func loadinternal(ctxt *Link, name string) *sym.Library { 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, "") + return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp) } } @@ -1985,11 +1987,29 @@ 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) - ctxt.loader.Preload(ctxt.Syms, f, lib, unit, eof-f.Offset(), 0) + fingerprint := ctxt.loader.Preload(ctxt.Syms, f, lib, unit, eof-f.Offset()) + if !fingerprint.IsZero() { // Assembly objects don't have fingerprints. Ignore them. + // Check fingerprint, to ensure the importing and imported packages + // have consistent view of symbol indices. + // Normally the go command should ensure this. But in case something + // goes wrong, it could lead to obscure bugs like run-time crash. + // Check it here to be sure. + if lib.Fingerprint.IsZero() { // Not yet imported. Update its fingerprint. + lib.Fingerprint = fingerprint + } + checkFingerprint(lib, fingerprint, lib.Srcref, lib.Fingerprint) + } + addImports(ctxt, lib, pn) return nil } +func checkFingerprint(lib *sym.Library, libfp goobj2.FingerprintType, src string, srcfp goobj2.FingerprintType) { + if libfp != srcfp { + Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp) + } +} + func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte { data := make([]byte, sym.Size) sect := f.Sections[sym.Section] diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index 6597d84368..839ee0ca7e 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -130,11 +130,11 @@ func (ctxt *Link) Logf(format string, args ...interface{}) { 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) + for _, imp := range l.Autolib { + lib := addlib(ctxt, pkg, pn, imp.Pkg, imp.Fingerprint) if lib != nil { l.Imports = append(l.Imports, lib) } } - l.ImportStrings = nil + l.Autolib = nil } diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index d8b4c8a94d..c361773c3c 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -32,6 +32,7 @@ package ld import ( "bufio" + "cmd/internal/goobj2" "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/benchmark" @@ -215,6 +216,7 @@ func Main(arch *sys.Arch, theArch Arch) { ctxt.Logf("HEADER = -H%d -T0x%x -R0x%x\n", ctxt.HeadType, uint64(*FlagTextAddr), uint32(*FlagRound)) } + zerofp := goobj2.FingerprintType{} switch ctxt.BuildMode { case BuildModeShared: for i := 0; i < flag.NArg(); i++ { @@ -228,12 +230,12 @@ func Main(arch *sys.Arch, theArch Arch) { } pkglistfornote = append(pkglistfornote, pkgpath...) pkglistfornote = append(pkglistfornote, '\n') - addlibpath(ctxt, "command line", "command line", file, pkgpath, "") + addlibpath(ctxt, "command line", "command line", file, pkgpath, "", zerofp) } case BuildModePlugin: - addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "") + addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "", zerofp) default: - addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "") + addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "", zerofp) } bench.Start("loadlib") ctxt.loadlib() diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 7b59e680ee..375e5c32b6 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1760,7 +1760,8 @@ func (l *Loader) FuncInfo(i Sym) 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) { +// Returns the fingerprint of the object. +func (l *Loader) Preload(syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj2.FingerprintType { roObject, readonly, err := f.Slice(uint64(length)) if err != nil { log.Fatal("cannot read object file:", err) @@ -1779,11 +1780,7 @@ func (l *Loader) Preload(syms *sym.Symbols, f *bio.Reader, lib *sym.Library, uni or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, make([]Sym, ndef+nnonpkgdef+r.NNonpkgref()), ndef, uint32(len(l.objs))} // Autolib - autolib := r.Autolib() - for _, p := range autolib { - lib.ImportStrings = append(lib.ImportStrings, p.Pkg) - // TODO: fingerprint is ignored for now - } + lib.Autolib = append(lib.Autolib, r.Autolib()...) // DWARF file table nfile := r.NDwarfFile() @@ -1797,6 +1794,8 @@ func (l *Loader) Preload(syms *sym.Symbols, f *bio.Reader, lib *sym.Library, uni // The caller expects us consuming all the data f.MustSeek(length, os.SEEK_CUR) + + return r.Fingerprint() } // Preload symbols of given kind from an object. diff --git a/src/cmd/link/internal/sym/library.go b/src/cmd/link/internal/sym/library.go index bed16565ba..c9be3abb27 100644 --- a/src/cmd/link/internal/sym/library.go +++ b/src/cmd/link/internal/sym/library.go @@ -4,18 +4,21 @@ package sym +import "cmd/internal/goobj2" + type Library struct { - Objref string - Srcref string - File string - Pkg string - Shlib string - Hash string - ImportStrings []string - Imports []*Library - Main bool - Safe bool - Units []*CompilationUnit + Objref string + Srcref string + File string + Pkg string + Shlib string + Hash string + Fingerprint goobj2.FingerprintType + Autolib []goobj2.ImportedPkg + Imports []*Library + Main bool + Safe bool + Units []*CompilationUnit Textp2 []LoaderSym // text syms defined in this library DupTextSyms2 []LoaderSym // dupok text syms defined in this library diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index bf097532de..1c9e177911 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -675,3 +675,61 @@ func TestTrampoline(t *testing.T) { t.Errorf("unexpected output:\n%s", out) } } + +func TestIndexMismatch(t *testing.T) { + // Test that index mismatch will cause a link-time error (not run-time error). + // This shouldn't happen with "go build". We invoke the compiler and the linker + // manually, and try to "trick" the linker with an inconsistent object file. + testenv.MustHaveGoBuild(t) + + tmpdir, err := ioutil.TempDir("", "TestIndexMismatch") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go") + bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go") + mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go") + aObj := filepath.Join(tmpdir, "a.o") + mObj := filepath.Join(tmpdir, "main.o") + exe := filepath.Join(tmpdir, "main.exe") + + // Build a program with main package importing package a. + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", aObj, aSrc) + t.Log(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("compiling a.go failed: %v\n%s", err, out) + } + cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", tmpdir, "-o", mObj, mSrc) + t.Log(cmd) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("compiling main.go failed: %v\n%s", err, out) + } + cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj) + t.Log(cmd) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("linking failed: %v\n%s", err, out) + } + + // Now, overwrite a.o with the object of b.go. This should + // result in an index mismatch. + cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", aObj, bSrc) + t.Log(cmd) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("compiling a.go failed: %v\n%s", err, out) + } + cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj) + t.Log(cmd) + out, err = cmd.CombinedOutput() + if err == nil { + t.Fatalf("linking didn't fail") + } + if !bytes.Contains(out, []byte("fingerprint mismatch")) { + t.Errorf("did not see expected error message. out:\n%s", out) + } +} diff --git a/src/cmd/link/testdata/testIndexMismatch/a.go b/src/cmd/link/testdata/testIndexMismatch/a.go new file mode 100644 index 0000000000..1f3b2c52d2 --- /dev/null +++ b/src/cmd/link/testdata/testIndexMismatch/a.go @@ -0,0 +1,8 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +//go:noinline +func A() { println("A") } diff --git a/src/cmd/link/testdata/testIndexMismatch/b.go b/src/cmd/link/testdata/testIndexMismatch/b.go new file mode 100644 index 0000000000..9b55dbf771 --- /dev/null +++ b/src/cmd/link/testdata/testIndexMismatch/b.go @@ -0,0 +1,8 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +//go:noinline +func B() { println("B") } diff --git a/src/cmd/link/testdata/testIndexMismatch/main.go b/src/cmd/link/testdata/testIndexMismatch/main.go new file mode 100644 index 0000000000..bc15236f1e --- /dev/null +++ b/src/cmd/link/testdata/testIndexMismatch/main.go @@ -0,0 +1,9 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "a" + +func main() { a.A() } From 42cca1a7fee916bb4ba769cb2db259ef6ec1c179 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Fri, 24 Apr 2020 11:07:17 -0400 Subject: [PATCH 11/42] [dev.link] cmd/link: create symbol updated lazily in amd64 adddynrel Tweak the code in the amd64 version of adddynrel to avoid creating a symbol updated for the symbol being processed until it's clear we need to alter its relocations. This should help performance for the PIE+internal linking scenario. Reviewed-on: https://go-review.googlesource.com/c/go/+/229866 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang Change-Id: Id25adfd81a5bbd2dde0f80a83b976397ba6abfb5 Reviewed-on: https://go-review.googlesource.com/c/go/+/230026 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/link/internal/amd64/asm.go | 13 +++++++++++-- src/cmd/link/internal/ld/data.go | 4 ---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index fa0f6ab9b5..e2a66daf4f 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -85,7 +85,6 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load targType = ldr.SymType(targ) } - su := ldr.MakeSymbolUpdater(s) switch r.Type() { default: if r.Type() >= objabi.ElfRelocOffset { @@ -103,6 +102,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) } + su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_PCREL) su.SetRelocAdd(rIdx, r.Add()+4) return true @@ -114,11 +114,13 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load if targType == 0 || targType == sym.SXREF { ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) } + su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_PCREL) su.SetRelocAdd(rIdx, r.Add()+8) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PLT32): + su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_PCREL) su.SetRelocAdd(rIdx, r.Add()+4) if targType == sym.SDYNIMPORT { @@ -132,11 +134,11 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCREL), objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCRELX), objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_REX_GOTPCRELX): + su := ldr.MakeSymbolUpdater(s) if targType != sym.SDYNIMPORT { // have symbol sData := ldr.Data(s) if r.Off() >= 2 && sData[r.Off()-2] == 0x8b { - su := ldr.MakeSymbolUpdater(s) su.MakeWritable() // turn MOVQ of GOT entry into LEAQ of symbol itself writeableData := su.Data() @@ -160,6 +162,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load if targType == sym.SDYNIMPORT { ldr.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", ldr.SymName(targ)) } + su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_ADDR) if target.IsPIE() && target.IsInternal() { // For internal linking PIE, this R_ADDR relocation cannot @@ -174,6 +177,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load 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? + su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_ADDR) if targType == sym.SDYNIMPORT { @@ -184,6 +188,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: if targType == sym.SDYNIMPORT { addpltsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) su.SetRelocSym(rIdx, syms.PLT2) su.SetRelocType(rIdx, objabi.R_PCREL) su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) @@ -196,6 +201,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_1*2 + 1, objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_2*2 + 1, objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_4*2 + 1: + su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_PCREL) if targType == sym.SDYNIMPORT { @@ -227,6 +233,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) } addgotsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, objabi.R_PCREL) su.SetRelocSym(rIdx, syms.GOT2) su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) @@ -251,12 +258,14 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load // Internal linking, for both ELF and Mach-O. // Build a PLT entry and change the relocation target to that entry. addpltsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) su.SetRelocSym(rIdx, syms.PLT2) su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) return true case objabi.R_ADDR: if ldr.SymType(s) == sym.STEXT && target.IsElf() { + su := ldr.MakeSymbolUpdater(s) if target.IsSolaris() { addpltsym2(target, ldr, syms, targ) su.SetRelocSym(rIdx, syms.PLT2) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 63e05a2645..d5286b4289 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -684,10 +684,6 @@ func dynrelocsym2(ctxt *Link, s loader.Sym) { relocs := ldr.Relocs(s) for ri := 0; ri < relocs.Count(); ri++ { r := relocs.At2(ri) - // FIXME: the call to Adddynrel2 below is going to wind up - // eagerly promoting the symbol to external, which is not great-- - // it would improve things for internal/PIE if we could - // create the symbol updater lazily. 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 From 1d083eba5bf09427ac132b913091cb3d8c1e733a Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 15:18:54 -0400 Subject: [PATCH 12/42] [dev.link] cmd/link: fix minor error on error reporting Correctly propagate ... arguments. (Maybe vet should warn on it?) Reviewed-on: https://go-review.googlesource.com/c/go/+/230017 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Change-Id: Ife56dc2321847cdaf0caea3142c2c7dad8b5924d Reviewed-on: https://go-review.googlesource.com/c/go/+/230027 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/link/internal/ld/util.go | 2 +- src/cmd/link/internal/loader/loader.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go index b8c9ce28b0..2186503f0c 100644 --- a/src/cmd/link/internal/ld/util.go +++ b/src/cmd/link/internal/ld/util.go @@ -74,7 +74,7 @@ func Errorf(s *sym.Symbol, format string, args ...interface{}) { // output file and return a non-zero error code. func (ctxt *Link) Errorf(s loader.Sym, format string, args ...interface{}) { if ctxt.loader != nil { - ctxt.loader.Errorf(s, format, args) + ctxt.loader.Errorf(s, format, args...) return } // Note: this is not expected to happen very often. diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 375e5c32b6..5d62690f82 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -2825,7 +2825,7 @@ func (l *Loader) GetErrorReporter() *ErrorReporter { // Errorf method logs an error message. See ErrorReporter.Errorf for details. func (l *Loader) Errorf(s Sym, format string, args ...interface{}) { - l.errorReporter.Errorf(s, format, args) + l.errorReporter.Errorf(s, format, args...) } // For debugging. From c7a11099c977b6e09c82ceaed014a4a55da568eb Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 15:19:55 -0400 Subject: [PATCH 13/42] [dev.link] cmd/link: fix buglet in new GCProg generation code It should check the name of the symbol being added, not the GC data symbol we're generating. Change-Id: I123679778ee542b8d1f5c15bf090fa3578025c19 Reviewed-on: https://go-review.googlesource.com/c/go/+/230018 Run-TryBot: Cherry Zhang Reviewed-by: Than McIntosh TryBot-Result: Gobot Gobot --- src/cmd/link/internal/ld/data.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index d5286b4289..2022c43bff 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1084,14 +1084,14 @@ func (p *GCProg2) AddSym(s loader.Sym) { // Things without pointers should be in sym.SNOPTRDATA or sym.SNOPTRBSS; // everything we see should have pointers and should therefore have a type. if typ == 0 { - switch p.sym.Name() { + switch ldr.SymName(s) { case "runtime.data", "runtime.edata", "runtime.bss", "runtime.ebss": // Ignore special symbols that are sometimes laid out // as real symbols. See comment about dyld on darwin in // the address function. return } - p.ctxt.Errorf(p.sym.Sym(), "missing Go type information for global symbol: size %d", ldr.SymSize(s)) + p.ctxt.Errorf(p.sym.Sym(), "missing Go type information for global symbol %s: size %d", ldr.SymName(s), ldr.SymSize(s)) return } From b2fde1098a151f89c7a20d0fe322dfb052211786 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 14:57:20 -0400 Subject: [PATCH 14/42] [dev.link] cmd/link: use new dodata on darwin/amd64 This probably breaks darwin/arm64. Will fix. Change-Id: I8be168985124f971e9d8ab5bc95c303336dd705b Reviewed-on: https://go-review.googlesource.com/c/go/+/230019 Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/data.go | 3 +- src/cmd/link/internal/ld/data2.go | 3 +- src/cmd/link/internal/ld/macho.go | 146 +++++++++++++++++------------- src/cmd/link/internal/ld/main.go | 12 ++- 4 files changed, 96 insertions(+), 68 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 2022c43bff..48eab03314 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1355,8 +1355,7 @@ func (ctxt *Link) dodata2(symGroupType []sym.SymKind) { // // On darwin, we need the symbol table numbers for dynreloc. if ctxt.HeadType == objabi.Hdarwin { - panic("not yet implemented for darwin") - // machosymorder(ctxt) + machosymorder(ctxt) } state.dynreloc2(ctxt) diff --git a/src/cmd/link/internal/ld/data2.go b/src/cmd/link/internal/ld/data2.go index 3eb45818d2..5c88fbaa57 100644 --- a/src/cmd/link/internal/ld/data2.go +++ b/src/cmd/link/internal/ld/data2.go @@ -45,7 +45,8 @@ func (ctxt *Link) dodata() { // // On darwin, we need the symbol table numbers for dynreloc. if ctxt.HeadType == objabi.Hdarwin { - machosymorder(ctxt) + panic("not supported") + //machosymorder(ctxt) } state.dynreloc(ctxt) diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index baa1f4094a..4dc7f819eb 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -8,6 +8,7 @@ import ( "bytes" "cmd/internal/objabi" "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/macho" "encoding/binary" @@ -216,7 +217,7 @@ const ( var nkind [NumSymKind]int -var sortsym []*sym.Symbol +var sortsym []loader.Sym var nsortsym int @@ -743,106 +744,125 @@ func Asmbmacho(ctxt *Link) { } } -func symkind(s *sym.Symbol) int { - if s.Type == sym.SDYNIMPORT { +func symkind(ldr *loader.Loader, s loader.Sym) int { + if ldr.SymType(s) == sym.SDYNIMPORT { return SymKindUndef } - if s.Attr.CgoExport() { + if ldr.AttrCgoExport(s) { return SymKindExtdef } return SymKindLocal } -func addsym(ctxt *Link, s *sym.Symbol, name string, type_ SymbolType, addr int64, gotype *sym.Symbol) { - if s == nil { - return +func collectmachosyms(ctxt *Link) { + ldr := ctxt.loader + + addsym := func(s loader.Sym) { + sortsym = append(sortsym, s) + nkind[symkind(ldr, s)]++ } - switch type_ { - default: - return - - case DataSym, BSSSym, TextSym: - break + // Add special runtime.text and runtime.etext symbols. + // We've already included this symbol in Textp on darwin if ctxt.DynlinkingGo(). + // See data.go:/textaddress + if !ctxt.DynlinkingGo() { + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } } - if sortsym != nil { - sortsym[nsortsym] = s - nkind[symkind(s)]++ + // Add text symbols. + for _, s := range ctxt.Textp2 { + addsym(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 + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + name := ldr.RawSymName(s) // TODO: try not to read the name + if name == "" || name[0] == '.' { + return false + } + return true } - return s1.Extname() < s2.Extname() -} + // Add data symbols and external references. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata + if t == sym.STLSBSS { + // TLSBSS is not used on darwin. See data.go:allocateDataSections + continue + } + if !shouldBeInSymbolTable(s) { + continue + } + addsym(s) + } + + switch t { + case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT, sym.SCONST: + addsym(s) + } -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" { + if t == sym.SDYNIMPORT && ldr.SymDynimplib(s) == "/usr/lib/libSystem.B.dylib" { // But only on macOS. if machoPlatform == PLATFORM_MACOS { - switch n := s.Extname(); n { + switch n := ldr.SymExtname(s); n { case "fdopendir": switch objabi.GOARCH { case "amd64": - s.SetExtname(n + "$INODE64") + ldr.SetSymExtname(s, n+"$INODE64") case "386": - s.SetExtname(n + "$INODE64$UNIX2003") + ldr.SetSymExtname(s, n+"$INODE64$UNIX2003") } case "readdir_r", "getfsstat": switch objabi.GOARCH { case "amd64", "386": - s.SetExtname(n + "$INODE64") + ldr.SetSymExtname(s, 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) - } - } } + + nsortsym = len(sortsym) } func machosymorder(ctxt *Link) { + ldr := ctxt.loader + // On Mac OS X Mountain Lion, we must sort exported symbols // So we sort them here and pre-allocate dynid for them // See https://golang.org/issue/4029 - for i := range dynexp { - dynexp[i].Attr |= sym.AttrReachable + for _, s := range ctxt.dynexp2 { + if !ldr.AttrReachable(s) { + panic("dynexp symbol is not reachable") + } } - 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) + collectmachosyms(ctxt) + sort.Slice(sortsym[:nsortsym], func(i, j int) bool { + s1 := sortsym[i] + s2 := sortsym[j] + k1 := symkind(ldr, s1) + k2 := symkind(ldr, s2) + if k1 != k2 { + return k1 < k2 + } + return ldr.SymExtname(s1) < ldr.SymExtname(s2) // Note: unnamed symbols are not added in collectmachosyms + }) + for i, s := range sortsym { + ldr.SetSymDynid(s, int32(i)) } } @@ -877,7 +897,7 @@ func machosymtab(ctxt *Link) { symstr := ctxt.Syms.Lookup(".machosymstr", 0) for i := 0; i < nsortsym; i++ { - s := sortsym[i] + s := ctxt.loader.Syms[sortsym[i]] symtab.AddUint32(ctxt.Arch, uint32(symstr.Size)) export := machoShouldExport(ctxt, s) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index c361773c3c..35363aa4ee 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -202,8 +202,16 @@ func Main(arch *sys.Arch, theArch Arch) { if *flagnewDoData { // New dodata() is currently only implemented for selected targets. - if !(ctxt.IsElf() && - (ctxt.IsAMD64() || ctxt.Is386())) { + switch { + case ctxt.IsElf(): + if !(ctxt.IsAMD64() || ctxt.Is386()) { + *flagnewDoData = false + } + case ctxt.IsDarwin(): + if !ctxt.IsAMD64() { + *flagnewDoData = false + } + default: *flagnewDoData = false } } From f9ed846a46974823e5136e08dc850853e5172a2f Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 17:15:40 -0400 Subject: [PATCH 15/42] [dev.link] cmd/link: use new dodata on Plan 9 and Wasm They don't have fancy Adddynrel stuff, so we can just enable it. Change-Id: I84082c3187d8a9ffa3a9c5458959794df0e3c2b6 Reviewed-on: https://go-review.googlesource.com/c/go/+/230030 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 35363aa4ee..96860ce4d8 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -211,6 +211,8 @@ func Main(arch *sys.Arch, theArch Arch) { if !ctxt.IsAMD64() { *flagnewDoData = false } + case ctxt.IsPlan9(), ctxt.IsWasm(): + // supported default: *flagnewDoData = false } From d5c932762865de1d12cf522d5d7aad5280943bde Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Fri, 24 Apr 2020 11:01:10 -0400 Subject: [PATCH 16/42] [dev.link] cmd/link: support new dodata for elf/{arm,arm64} Add elf/ARM arch support for the new dodata() phase. Change-Id: Iadd772b01036c6c5be95bcc6017f6c05d45a24c0 Reviewed-on: https://go-review.googlesource.com/c/go/+/229868 Reviewed-by: Cherry Zhang --- src/cmd/link/internal/arm/asm.go | 223 ++++++++++---------- src/cmd/link/internal/arm/asm2.go | 246 ++++++++++++++++++++++ src/cmd/link/internal/arm/obj.go | 1 + src/cmd/link/internal/arm64/asm.go | 213 ++++++++++--------- src/cmd/link/internal/arm64/asm2.go | 312 ++++++++++++++++++++++++++++ src/cmd/link/internal/arm64/obj.go | 1 + src/cmd/link/internal/ld/main.go | 3 +- src/cmd/link/internal/ld/target.go | 4 + 8 files changed, 798 insertions(+), 205 deletions(-) create mode 100644 src/cmd/link/internal/arm/asm2.go create mode 100644 src/cmd/link/internal/arm64/asm2.go diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index 446691f318..e1edc9e45d 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -103,24 +103,30 @@ func braddoff(a int32, b int32) int32 { return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) } -func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { - targ := r.Sym +func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r *loader.Reloc2, rIdx int) bool { - switch r.Type { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { default: - if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) return false } // Handle relocations found in ELF object files. case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PLT32): - r.Type = objabi.R_CALLARM + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM) - if targ.Type == sym.SDYNIMPORT { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) } return true @@ -130,113 +136,112 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. return false case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT32): // R_ARM_GOT_BREL - if targ.Type != sym.SDYNIMPORT { - addgotsyminternal(target, syms, targ) + if targType != sym.SDYNIMPORT { + addgotsyminternal2(target, ldr, syms, targ) } else { - addgotsym(target, syms, targ) + addgotsym2(target, ldr, syms, targ) } - r.Type = objabi.R_CONST // write r->add during relocsym - r.Sym = nil - r.Add += int64(targ.Got()) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym + su.SetRelocSym(rIdx, 0) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil - if targ.Type != sym.SDYNIMPORT { - addgotsyminternal(target, syms, targ) + if targType != sym.SDYNIMPORT { + addgotsyminternal2(target, ldr, syms, targ) } else { - addgotsym(target, syms, targ) + addgotsym2(target, ldr, syms, targ) } - - r.Type = objabi.R_PCREL - r.Sym = syms.GOT - r.Add += int64(targ.Got()) + 4 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ))) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTOFF): // R_ARM_GOTOFF32 - r.Type = objabi.R_GOTOFF - + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_GOTOFF) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTPC): // R_ARM_BASE_PREL - r.Type = objabi.R_PCREL - - r.Sym = syms.GOT - r.Add += 4 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, 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(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) } - return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_REL32): // R_ARM_REL32 - r.Type = objabi.R_PCREL - - r.Add += 4 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_ABS32): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", ldr.SymName(targ)) } - 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 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24), objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24): - r.Type = objabi.R_CALLARM - if targ.Type == sym.SDYNIMPORT { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) } return true } // Handle references to ELF symbols from our own object files. - if targ.Type != sym.SDYNIMPORT { + if targType != sym.SDYNIMPORT { return true } - switch r.Type { + // Reread the reloc to incorporate any changes in type above. + relocs := ldr.Relocs(s) + *r = relocs.At2(rIdx) + + switch r.Type() { case objabi.R_CALLARM: if target.IsExternal() { // External linker will do this relocation. return true } - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(targ.Plt()) + addpltsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) return true case objabi.R_ADDR: - if s.Type != sym.SDATA { + if ldr.SymType(s) != sym.SDATA { break } 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 + ld.Adddynsym2(ldr, target, syms, targ) + rel := ldr.MakeSymbolUpdater(syms.Rel2) + rel.AddAddrPlus(target.Arch, s, int64(r.Off())) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym + su.SetRelocSym(rIdx, 0) return true } } @@ -592,90 +597,88 @@ func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym return t } -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) - r.Siz = 4 - r.Type = typ - r.Add = int64(s.Got()) - 8 +func addpltreloc2(ldr *loader.Loader, plt *loader.SymbolBuilder, got *loader.SymbolBuilder, s loader.Sym, typ objabi.RelocType) { + r, _ := plt.AddRel(typ) + r.SetSym(got.Sym()) + r.SetOff(int32(plt.Size())) + r.SetSiz(4) + r.SetAdd(int64(ldr.SymGot(s)) - 8) - plt.Attr |= sym.AttrReachable - plt.Size += 4 - plt.Grow(plt.Size) + plt.SetReachable(true) + plt.SetSize(plt.Size() + 4) + plt.Grow(plt.Size()) } -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { +func addpltsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { return } - ld.Adddynsym(target, syms, s) + ld.Adddynsym2(ldr, target, syms, s) if target.IsElf() { - plt := syms.PLT - got := syms.GOTPLT - rel := syms.RelPLT - if plt.Size == 0 { + plt := ldr.MakeSymbolUpdater(syms.PLT2) + got := ldr.MakeSymbolUpdater(syms.GOTPLT2) + rel := ldr.MakeSymbolUpdater(syms.RelPLT2) + if plt.Size() == 0 { panic("plt is not set up") } // .got entry - s.SetGot(int32(got.Size)) + ldr.SetGot(s, int32(got.Size())) // In theory, all GOT should point to the first PLT entry, // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's // dynamic linker won't, so we'd better do it ourselves. - got.AddAddrPlus(target.Arch, plt, 0) + got.AddAddrPlus(target.Arch, plt.Sym(), 0) // .plt entry, this depends on the .got entry - s.SetPlt(int32(plt.Size)) + ldr.SetPlt(s, int32(plt.Size())) - 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]! + addpltreloc2(ldr, plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000 + addpltreloc2(ldr, plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000 + addpltreloc2(ldr, plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]! // rel - rel.AddAddrPlus(target.Arch, got, int64(s.Got())) + rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) - rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_JUMP_SLOT))) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_ARM_JUMP_SLOT))) } else { - ld.Errorf(s, "addpltsym: unsupported binary format") + ldr.Errorf(s, "addpltsym: unsupported binary format") } } -func addgotsyminternal(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { +func addgotsyminternal2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymGot(s) >= 0 { return } - got := syms.GOT - s.SetGot(int32(got.Size)) - + got := ldr.MakeSymbolUpdater(syms.GOT2) + ldr.SetGot(s, int32(got.Size())) got.AddAddrPlus(target.Arch, s, 0) if target.IsElf() { } else { - ld.Errorf(s, "addgotsyminternal: unsupported binary format") + ldr.Errorf(s, "addgotsyminternal: unsupported binary format") } } -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { +func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymGot(s) >= 0 { return } - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) - got.AddUint32(target.Arch, 0) + ld.Adddynsym2(ldr, target, syms, s) + got := ldr.MakeSymbolUpdater(syms.GOT2) + ldr.SetGot(s, int32(got.Size())) + got.AddUint64(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_ARM_GLOB_DAT))) + rel := ldr.MakeSymbolUpdater(syms.Rel2) + rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_ARM_GLOB_DAT))) } else { - ld.Errorf(s, "addgotsym: unsupported binary format") + ldr.Errorf(s, "addgotsym: unsupported binary format") } } diff --git a/src/cmd/link/internal/arm/asm2.go b/src/cmd/link/internal/arm/asm2.go new file mode 100644 index 0000000000..a8c26c67b4 --- /dev/null +++ b/src/cmd/link/internal/arm/asm2.go @@ -0,0 +1,246 @@ +// 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 arm + +import ( + "cmd/internal/objabi" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +// Temporary dumping ground for sym.Symbol version of helper +// functions in asm.go, still being used for some oses. +// FIXME: get rid of this file when dodata() is completely +// converted. + +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(target.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(target, syms, targ) + r.Sym = syms.PLT + 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(target, syms, targ) + } else { + addgotsym(target, syms, 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(target, syms, targ) + } else { + addgotsym(target, syms, targ) + } + + r.Type = objabi.R_PCREL + r.Sym = syms.GOT + 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 = 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(target, syms, targ) + r.Sym = syms.PLT + 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(target, syms, targ) + r.Sym = syms.PLT + 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 target.IsExternal() { + // External linker will do this relocation. + return true + } + addpltsym(target, syms, targ) + r.Sym = syms.PLT + r.Add = int64(targ.Plt()) + return true + + case objabi.R_ADDR: + if s.Type != sym.SDATA { + break + } + 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 + } + } + + return false +} + +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + + if target.IsElf() { + plt := syms.PLT + got := syms.GOTPLT + rel := syms.RelPLT + if plt.Size == 0 { + panic("plt is not set up") + } + + // .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(target.Arch, plt, 0) + + // .plt entry, this depends on the .got entry + s.SetPlt(int32(plt.Size)) + + 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(target.Arch, got, int64(s.Got())) + + 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 addpltreloc(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 addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + got := syms.GOT + s.SetGot(int32(got.Size)) + 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_ARM_GLOB_DAT))) + } else { + ld.Errorf(s, "addgotsym: unsupported binary format") + } +} + +func addgotsyminternal(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + got := syms.GOT + s.SetGot(int32(got.Size)) + + got.AddAddrPlus(target.Arch, s, 0) + + if target.IsElf() { + } else { + ld.Errorf(s, "addgotsyminternal: unsupported binary format") + } +} diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go index b277fb2a43..d6fa64de66 100644 --- a/src/cmd/link/internal/arm/obj.go +++ b/src/cmd/link/internal/arm/obj.go @@ -47,6 +47,7 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfreglr: dwarfRegLR, Adddynrel: adddynrel, + Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, Archrelocvariant: archrelocvariant, diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 46bda74c4c..a0f06b6aa6 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -78,86 +78,97 @@ func gentext2(ctxt *ld.Link, ldr *loader.Loader) { initfunc.AddReloc(rel2) } -func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { - targ := r.Sym +func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r *loader.Reloc2, rIdx int) bool { - switch r.Type { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { default: - if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) return false } // Handle relocations found in ELF object files. case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL32): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_AARCH64_PREL32 relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_AARCH64_PREL32 relocation for dynamic symbol %s", ldr.SymName(targ)) } // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make // sense and should be removed when someone has thought about it properly. - if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { - ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) } - r.Type = objabi.R_PCREL - r.Add += 4 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL64): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_AARCH64_PREL64 relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_AARCH64_PREL64 relocation for dynamic symbol %s", ldr.SymName(targ)) } - if targ.Type == 0 || targ.Type == sym.SXREF { - ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + if targType == 0 || targType == sym.SXREF { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) } - r.Type = objabi.R_PCREL - r.Add += 8 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+8) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26), objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26): - if targ.Type == sym.SDYNIMPORT { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add += int64(targ.Plt()) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) } - if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { - ld.Errorf(s, "unknown symbol %s in callarm64", targ.Name) + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in callarm64", ldr.SymName(targ)) } - r.Type = objabi.R_CALLARM64 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM64) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_GOT_PAGE), objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LD64_GOT_LO12_NC): - if targ.Type != sym.SDYNIMPORT { + if targType != sym.SDYNIMPORT { // have symbol // TODO: turn LDR of GOT entry into ADR of symbol itself } // fall back to using GOT // TODO: just needs relocation, no need to put in .dynsym - addgotsym(target, syms, targ) - - r.Type = objabi.R_ARM64_GOT - r.Sym = syms.GOT - r.Add += int64(targ.Got()) + addgotsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_GOT) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_PREL_PG_HI21), objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADD_ABS_LO12_NC): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) } - if targ.Type == 0 || targ.Type == sym.SXREF { - ld.Errorf(s, "unknown symbol %s", targ.Name) + if targType == 0 || targType == sym.SXREF { + ldr.Errorf(s, "unknown symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ARM64_PCREL + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, 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) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_AARCH64_ABS64 relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ADDR + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) if target.IsPIE() && target.IsInternal() { // For internal linking PIE, this R_ADDR relocation cannot // be resolved statically. We need to generate a dynamic @@ -167,39 +178,48 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. 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) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ARM64_LDST8 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, 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) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ARM64_LDST32 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_LDST32) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST64_ABS_LO12_NC): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ARM64_LDST64 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_LDST64) + return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST128_ABS_LO12_NC): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ARM64_LDST128 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_LDST128) return true } - switch r.Type { + // Reread the reloc to incorporate any changes in type above. + relocs := ldr.Relocs(s) + *r = relocs.At2(rIdx) + + switch r.Type() { case objabi.R_CALL, objabi.R_PCREL, objabi.R_CALLARM64: - if targ.Type != sym.SDYNIMPORT { + if targType != sym.SDYNIMPORT { // nothing to do, the relocation will be laid out in reloc return true } @@ -209,14 +229,14 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. } case objabi.R_ADDR: - if s.Type == sym.STEXT && target.IsElf() { + if ldr.SymType(s) == sym.STEXT && target.IsElf() { // The code is asking for the address of an external // function. We provide it with the address of the // correspondent GOT symbol. - addgotsym(target, syms, targ) - - r.Sym = syms.GOT - r.Add += int64(targ.Got()) + addgotsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) return true } @@ -253,7 +273,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. // symbol offset as determined by reloc(), not the // final dynamically linked address as a dynamic // relocation would provide. - switch s.Name { + switch ldr.SymName(s) { case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic": return false } @@ -264,7 +284,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. // 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 { + if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA { break } } @@ -287,14 +307,14 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. // 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 := syms.Rela - rela.AddAddrPlus(target.Arch, s, int64(r.Off)) - if r.Siz == 8 { + rela := ldr.MakeSymbolUpdater(syms.Rela2) + rela.AddAddrPlus(target.Arch, s, int64(r.Off())) + if r.Siz() == 8 { 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) + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) } - rela.AddAddrPlus(target.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 @@ -737,71 +757,76 @@ func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loade } } -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { +func addpltsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { return } - ld.Adddynsym(target, syms, s) + ld.Adddynsym2(ldr, target, syms, s) if target.IsElf() { - plt := syms.PLT - gotplt := syms.GOTPLT - rela := syms.RelaPLT - if plt.Size == 0 { + plt := ldr.MakeSymbolUpdater(syms.PLT2) + gotplt := ldr.MakeSymbolUpdater(syms.GOTPLT2) + rela := ldr.MakeSymbolUpdater(syms.RelaPLT2) + if plt.Size() == 0 { panic("plt is not set up") } // adrp x16, &got.plt[0] - plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(target.Arch, plt.Size-4, 0x90000010) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size()) + plt.SetUint32(target.Arch, plt.Size()-4, 0x90000010) + relocs := plt.Relocs() + plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_GOT) // is the offset value of &got.plt[n] to &got.plt[0] // ldr x17, [x16, ] - plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(target.Arch, plt.Size-4, 0xf9400211) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size()) + plt.SetUint32(target.Arch, plt.Size()-4, 0xf9400211) + relocs = plt.Relocs() + plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_GOT) // add x16, x16, - plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(target.Arch, plt.Size-4, 0x91000210) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL + plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size()) + plt.SetUint32(target.Arch, plt.Size()-4, 0x91000210) + relocs = plt.Relocs() + plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_PCREL) // br x17 plt.AddUint32(target.Arch, 0xd61f0220) // add to got.plt: pointer to plt[0] - gotplt.AddAddrPlus(target.Arch, plt, 0) + gotplt.AddAddrPlus(target.Arch, plt.Sym(), 0) // rela - 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.AddAddrPlus(target.Arch, gotplt.Sym(), gotplt.Size()-8) + sDynid := ldr.SymDynid(s) + + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(sDynid), uint32(elf.R_AARCH64_JUMP_SLOT))) rela.AddUint64(target.Arch, 0) - s.SetPlt(int32(plt.Size - 16)) + ldr.SetPlt(s, int32(plt.Size()-16)) } else { - ld.Errorf(s, "addpltsym: unsupported binary format") + ldr.Errorf(s, "addpltsym: unsupported binary format") } } -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { +func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymGot(s) >= 0 { return } - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) + ld.Adddynsym2(ldr, target, syms, s) + got := ldr.MakeSymbolUpdater(syms.GOT2) + ldr.SetGot(s, int32(got.Size())) 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_AARCH64_GLOB_DAT))) + rela := ldr.MakeSymbolUpdater(syms.Rela2) + rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_AARCH64_GLOB_DAT))) rela.AddUint64(target.Arch, 0) } else { - ld.Errorf(s, "addgotsym: unsupported binary format") + ldr.Errorf(s, "addgotsym: unsupported binary format") } } diff --git a/src/cmd/link/internal/arm64/asm2.go b/src/cmd/link/internal/arm64/asm2.go new file mode 100644 index 0000000000..b093958cb8 --- /dev/null +++ b/src/cmd/link/internal/arm64/asm2.go @@ -0,0 +1,312 @@ +// 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 arm64 + +import ( + "cmd/internal/objabi" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +// Temporary dumping ground for sym.Symbol version of helper +// functions in asm.go, still being used for some oses. +// FIXME: get rid of this file when dodata() is completely +// converted. + +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(target.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(target, syms, targ) + r.Sym = syms.PLT + 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(target, syms, targ) + + r.Type = objabi.R_ARM64_GOT + r.Sym = syms.GOT + 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 target.IsPIE() && target.IsInternal() { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + break + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST8_ABS_LO12_NC): + if 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 target.IsExternal() { + // External linker will do this relocation. + return true + } + + case objabi.R_ADDR: + 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(target, syms, targ) + + r.Sym = syms.GOT + r.Add += int64(targ.Got()) + return true + } + + // Process dynamic relocations for the data sections. + if target.IsPIE() && target.IsInternal() { + // When internally linking, generate dynamic relocations + // for all typical R_ADDR relocations. The exception + // are those R_ADDR that are created as part of generating + // the dynamic relocations and must be resolved statically. + // + // There are three phases relevant to understanding this: + // + // dodata() // we are here + // address() // symbol address assignment + // reloc() // resolution of static R_ADDR relocs + // + // At this point symbol addresses have not been + // assigned yet (as the final size of the .rela section + // will affect the addresses), and so we cannot write + // the Elf64_Rela.r_offset now. Instead we delay it + // until after the 'address' phase of the linker is + // complete. We do this via Addaddrplus, which creates + // a new R_ADDR relocation which will be resolved in + // the 'reloc' phase. + // + // These synthetic static R_ADDR relocs must be skipped + // now, or else we will be caught in an infinite loop + // of generating synthetic relocs for our synthetic + // relocs. + // + // Furthermore, the rela sections contain dynamic + // relocations with R_ADDR relocations on + // Elf64_Rela.r_offset. This field should contain the + // symbol offset as determined by reloc(), not the + // final dynamically linked address as a dynamic + // relocation would provide. + switch 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 target.IsElf() { + // Generate R_AARCH64_RELATIVE relocations for best + // efficiency in the dynamic linker. + // + // As noted above, symbol addresses have not been + // assigned yet, so we can't generate the final reloc + // entry yet. We ultimately want: + // + // r_offset = s + r.Off + // r_info = R_AARCH64_RELATIVE + // r_addend = targ + r.Add + // + // The dynamic linker will set *offset = base address + + // addend. + // + // AddAddrPlus is used for r_offset and r_addend to + // generate new R_ADDR relocations that will update + // these fields in the 'reloc' phase. + rela := syms.Rela + rela.AddAddrPlus(target.Arch, s, int64(r.Off)) + if r.Siz == 8 { + 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(target.Arch, targ, int64(r.Add)) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). + return true + } + } + return false +} +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + + if target.IsElf() { + plt := syms.PLT + gotplt := syms.GOTPLT + rela := syms.RelaPLT + if plt.Size == 0 { + panic("plt is not set up") + } + + // adrp x16, &got.plt[0] + plt.AddAddrPlus4(gotplt, gotplt.Size) + plt.SetUint32(target.Arch, plt.Size-4, 0x90000010) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + + // is the offset value of &got.plt[n] to &got.plt[0] + // ldr x17, [x16, ] + plt.AddAddrPlus4(gotplt, gotplt.Size) + plt.SetUint32(target.Arch, plt.Size-4, 0xf9400211) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + + // add x16, x16, + plt.AddAddrPlus4(gotplt, gotplt.Size) + plt.SetUint32(target.Arch, plt.Size-4, 0x91000210) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL + + // br x17 + plt.AddUint32(target.Arch, 0xd61f0220) + + // add to got.plt: pointer to plt[0] + gotplt.AddAddrPlus(target.Arch, plt, 0) + + // rela + 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 { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + got := syms.GOT + s.SetGot(int32(got.Size)) + 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_AARCH64_GLOB_DAT))) + rela.AddUint64(target.Arch, 0) + } else { + ld.Errorf(s, "addgotsym: unsupported binary format") + } +} diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go index c2ac6e7ad1..1895fbe0c2 100644 --- a/src/cmd/link/internal/arm64/obj.go +++ b/src/cmd/link/internal/arm64/obj.go @@ -47,6 +47,7 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfreglr: dwarfRegLR, Adddynrel: adddynrel, + Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, Archrelocvariant: archrelocvariant, diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 96860ce4d8..8744291d71 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -204,7 +204,8 @@ func Main(arch *sys.Arch, theArch Arch) { // New dodata() is currently only implemented for selected targets. switch { case ctxt.IsElf(): - if !(ctxt.IsAMD64() || ctxt.Is386()) { + if !(ctxt.IsAMD64() || ctxt.Is386() || + ctxt.IsARM() || ctxt.IsARM64()) { *flagnewDoData = false } case ctxt.IsDarwin(): diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go index 8c07d77fd8..78e41f09be 100644 --- a/src/cmd/link/internal/ld/target.go +++ b/src/cmd/link/internal/ld/target.go @@ -92,6 +92,10 @@ func (t *Target) IsARM() bool { return t.Arch.Family == sys.ARM } +func (t *Target) IsARM64() bool { + return t.Arch.Family == sys.ARM64 +} + func (t *Target) IsAMD64() bool { return t.Arch.Family == sys.AMD64 } From e77639f3a4ab27e2b6520ca54ee192953be00186 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sun, 26 Apr 2020 13:19:32 -0400 Subject: [PATCH 17/42] [dev.link] cmd/link: sort DynidSyms Sort DynidSyms to ensure a deterministic build. Fix Solaris build. Change-Id: I6c01cb3dec5e46b3d881e720e3c2079643b5c7c7 Reviewed-on: https://go-review.googlesource.com/c/go/+/230277 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/link/internal/loader/loader.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 5d62690f82..30121d4cba 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1276,6 +1276,7 @@ func (l *Loader) DynidSyms() []Sym { for s := range l.dynid { sl = append(sl, s) } + sort.Slice(sl, func(i, j int) bool { return sl[i] < sl[j] }) return sl } From f8b74eafd5d2019a7a0fcfc21e037ba5f2f84732 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sun, 26 Apr 2020 14:56:19 -0400 Subject: [PATCH 18/42] [dev.link] cmd/link: set symbol alignments after dynreloc2 The symbol alignment is set based on its size. In dynreloc2 symbol size may change (e.g. elfdynhash2). So the alignment must be set after dynreloc2. Noticed this while debugging nondeterministic build on Solaris. Idx Name Size VMA LMA File off Algn 8 .hash 000000c8 000000000048add2 000000000048add2 0008add2 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA This doesn't look right, as the section address is not a multiple of its alignment. Change-Id: I23534cbc59695b7bc241838173fcc71dde95b195 Reviewed-on: https://go-review.googlesource.com/c/go/+/230278 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/data.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 48eab03314..4b3bc2ce02 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1336,11 +1336,6 @@ func (ctxt *Link) dodata2(symGroupType []sym.SymKind) { } state.data2[st] = append(state.data2[st], s) - // Set explicit alignment here, so as to avoid having to update - // symbol alignment in doDataSect2, which would cause a concurrent - // map read/write violation. - state.symalign2(s) - // Similarly with checking the onlist attr. if ldr.AttrOnList(s) { log.Fatalf("symbol %s listed multiple times", ldr.SymName(s)) @@ -1362,6 +1357,17 @@ func (ctxt *Link) dodata2(symGroupType []sym.SymKind) { // Move any RO data with relocations to a separate section. state.makeRelroForSharedLib2(ctxt) + // Set explicit alignment here, so as to avoid having to update + // symbol alignment in doDataSect2, which would cause a concurrent + // map read/write violation. + // NOTE: this needs to be done after dynreloc2, where symbol size + // may change. + for _, list := range state.data2 { + for _, s := range list { + state.symalign2(s) + } + } + // Sort symbols. var wg sync.WaitGroup for symn := range state.data2 { From 26d6d077859c8d778ec51036fb8e9b4676e181b6 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sat, 25 Apr 2020 14:25:18 -0400 Subject: [PATCH 19/42] [dev.link] cmd/link: remove symbol movement workaround in dodata It is supposed to work around symbol movement in machosymorder. But machosymorder doesn't actually move symbols around. Change-Id: Ibdc2ad41aaa8cd49e865088aa1ddb7ab399736cd Reviewed-on: https://go-review.googlesource.com/c/go/+/230279 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/data.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 4b3bc2ce02..5ab7661d60 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1893,18 +1893,6 @@ func (state *dodataState) allocateDwarfSections2(ctxt *Link) { } func (state *dodataState) dodataSect2(ctxt *Link, symn sym.SymKind, syms []loader.Sym) (result []loader.Sym, maxAlign int32) { - if ctxt.HeadType == objabi.Hdarwin { - // Some symbols may no longer belong in syms - // due to movement in machosymorder. - newSyms := make([]loader.Sym, 0, len(syms)) - for _, s := range syms { - if state.symType(s) == symn { - newSyms = append(newSyms, s) - } - } - syms = newSyms - } - var head, tail loader.Sym ldr := ctxt.loader for _, s := range syms { From 5ea431fb63863d9db6272b54decba4a330124d36 Mon Sep 17 00:00:00 2001 From: Jeremy Faller Date: Fri, 24 Apr 2020 17:56:24 -0400 Subject: [PATCH 20/42] [dev.link] cmd/link: support Loader in s390x dodata Recreation of CL 229863 that was removed from the repo because it included the linker binary. Change-Id: I5e96afa079b1217df6e7cba63a107546bd96ef76 Reviewed-on: https://go-review.googlesource.com/c/go/+/230028 Reviewed-by: Than McIntosh Reviewed-by: Cherry Zhang --- src/cmd/link/internal/ld/main.go | 2 +- src/cmd/link/internal/s390x/asm.go | 176 +++++++++++---------- src/cmd/link/internal/s390x/asm2.go | 231 ++++++++++++++++++++++++++++ src/cmd/link/internal/s390x/obj.go | 1 + 4 files changed, 328 insertions(+), 82 deletions(-) create mode 100644 src/cmd/link/internal/s390x/asm2.go diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 8744291d71..8c86fd7236 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -205,7 +205,7 @@ func Main(arch *sys.Arch, theArch Arch) { switch { case ctxt.IsElf(): if !(ctxt.IsAMD64() || ctxt.Is386() || - ctxt.IsARM() || ctxt.IsARM64()) { + ctxt.IsARM() || ctxt.IsARM64() || ctxt.IsS390X()) { *flagnewDoData = false } case ctxt.IsDarwin(): diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index 9b6be28421..59c49b4537 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -75,135 +75,146 @@ func gentext2(ctxt *ld.Link, ldr *loader.Loader) { initfunc.AddUint32(ctxt.Arch, 0) } -func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { - targ := r.Sym - r.InitExt() +func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r *loader.Reloc2, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } - switch r.Type { + switch r.Type() { default: - if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d", r.Type) + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d", r.Type()) return false } // Handle relocations found in ELF object files. case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_12), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT12): - ld.Errorf(s, "s390x 12-bit relocations have not been implemented (relocation type %d)", r.Type-objabi.ElfRelocOffset) + ldr.Errorf(s, "s390x 12-bit relocations have not been implemented (relocation type %d)", r.Type()-objabi.ElfRelocOffset) return false case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_8), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_16), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_32), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_64): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_390_nn relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_390_nn relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_ADDR + + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC64): - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_390_PCnn relocation for dynamic symbol %s", targ.Name) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_390_PCnn relocation for dynamic symbol %s", ldr.SymName(targ)) } // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make // sense and should be removed when someone has thought about it properly. - if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { - ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) } - r.Type = objabi.R_PCREL - r.Add += int64(r.Siz) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT16), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT32), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT64): - ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset) + ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT16DBL), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32DBL): - r.Type = objabi.R_PCREL - r.Variant = sym.RV_390_DBL - r.Add += int64(r.Siz) - if targ.Type == sym.SDYNIMPORT { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add += int64(targ.Plt()) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + r.SetSym(syms.PLT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) } return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT64): - r.Type = objabi.R_PCREL - r.Add += int64(r.Siz) - if targ.Type == sym.SDYNIMPORT { - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add += int64(targ.Plt()) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + if targType == sym.SDYNIMPORT { + addpltsym2(target, ldr, syms, targ) + r.SetSym(syms.PLT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) } return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_COPY): - ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset) + ldr.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) + ldr.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) + ldr.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) + ldr.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) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_390_GOTOFF relocation for dynamic symbol %s", ldr.SymName(targ)) } - r.Type = objabi.R_GOTOFF + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_GOTOFF) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPC): - r.Type = objabi.R_PCREL - r.Sym = syms.GOT - r.Add += int64(r.Siz) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + r.SetSym(syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16DBL), objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32DBL): - 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) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_390_PCnnDBL relocation for dynamic symbol %s", ldr.SymName(targ)) } return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPCDBL): - r.Type = objabi.R_PCREL - r.Variant = sym.RV_390_DBL - r.Sym = syms.GOT - r.Add += int64(r.Siz) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL) + r.SetSym(syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT): - addgotsym(target, syms, targ) - - r.Type = objabi.R_PCREL - r.Variant = sym.RV_390_DBL - r.Sym = syms.GOT - r.Add += int64(targ.Got()) - r.Add += int64(r.Siz) + addgotsym2(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL) + r.SetSym(syms.GOT2) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))+int64(r.Siz())) return true } // Handle references to ELF symbols from our own object files. - if targ.Type != sym.SDYNIMPORT { + if targType != sym.SDYNIMPORT { return true } @@ -388,28 +399,30 @@ func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym } } -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { +func addpltsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { return } - ld.Adddynsym(target, syms, s) + ld.Adddynsym2(ldr, target, syms, s) if target.IsElf() { - plt := syms.PLT - got := syms.GOT - rela := syms.RelaPLT - if plt.Size == 0 { + plt := ldr.MakeSymbolUpdater(syms.PLT2) + got := ldr.MakeSymbolUpdater(syms.GOT2) + rela := ldr.MakeSymbolUpdater(syms.RelaPLT2) + if plt.Size() == 0 { panic("plt is not set up") } // larl %r1,_GLOBAL_OFFSET_TABLE_+index plt.AddUint8(0xc0) plt.AddUint8(0x10) - plt.AddPCRelPlus(target.Arch, got, got.Size+6) // need variant? + plt.AddPCRelPlus(target.Arch, got.Sym(), got.Size()+6) + pltrelocs := plt.Relocs() + ldr.SetRelocVariant(plt.Sym(), pltrelocs.Count()-1, sym.RV_390_DBL) // add to got: pointer to current pos in plt - got.AddAddrPlus(target.Arch, plt, plt.Size+8) // weird but correct + got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()+8) // weird but correct // lg %r1,0(%r1) plt.AddUint8(0xe3) plt.AddUint8(0x10) @@ -434,40 +447,41 @@ func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { plt.AddUint8(0xc0) plt.AddUint8(0xf4) - plt.AddUint32(target.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(target.Arch, uint32(rela.Size)) // rela size before current entry + plt.AddUint32(target.Arch, uint32(rela.Size())) // rela size before current entry // rela - rela.AddAddrPlus(target.Arch, got, got.Size-8) + rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8) - rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_JMP_SLOT))) + sDynid := ldr.SymDynid(s) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(sDynid), uint32(elf.R_390_JMP_SLOT))) rela.AddUint64(target.Arch, 0) - s.SetPlt(int32(plt.Size - 32)) + ldr.SetPlt(s, int32(plt.Size()-32)) } else { - ld.Errorf(s, "addpltsym: unsupported binary format") + ldr.Errorf(s, "addpltsym: unsupported binary format") } } -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { +func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymGot(s) >= 0 { return } - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) + ld.Adddynsym2(ldr, target, syms, s) + got := ldr.MakeSymbolUpdater(syms.GOT2) + ldr.SetGot(s, int32(got.Size())) 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_390_GLOB_DAT))) + rela := ldr.MakeSymbolUpdater(syms.Rela2) + rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_390_GLOB_DAT))) rela.AddUint64(target.Arch, 0) } else { - ld.Errorf(s, "addgotsym: unsupported binary format") + ldr.Errorf(s, "addgotsym: unsupported binary format") } } diff --git a/src/cmd/link/internal/s390x/asm2.go b/src/cmd/link/internal/s390x/asm2.go new file mode 100644 index 0000000000..1487f11db7 --- /dev/null +++ b/src/cmd/link/internal/s390x/asm2.go @@ -0,0 +1,231 @@ +// 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 s390x + +import ( + "cmd/internal/objabi" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +func adddynrel(target *ld.Target, ldr *loader.Loader, 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", 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(target, syms, targ) + r.Sym = syms.PLT + 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(target, syms, targ) + r.Sym = syms.PLT + 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 = syms.GOT + 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 = syms.GOT + r.Add += int64(r.Siz) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT): + addgotsym(target, syms, targ) + + r.Type = objabi.R_PCREL + r.Variant = sym.RV_390_DBL + r.Sym = syms.GOT + 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 addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + + if target.IsElf() { + plt := syms.PLT + got := syms.GOT + rela := syms.RelaPLT + if plt.Size == 0 { + panic("plt is not set up") + } + // larl %r1,_GLOBAL_OFFSET_TABLE_+index + + plt.AddUint8(0xc0) + plt.AddUint8(0x10) + plt.AddPCRelPlus(target.Arch, got, got.Size+6) // need variant? + + // add to got: pointer to current pos in plt + got.AddAddrPlus(target.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(target.Arch, uint32(-((plt.Size - 2) >> 1))) // roll-your-own relocation + //.plt index + plt.AddUint32(target.Arch, uint32(rela.Size)) // rela size before current entry + + // rela + rela.AddAddrPlus(target.Arch, got, got.Size-8) + + 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)) + + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(target, syms, s) + got := syms.GOT + s.SetGot(int32(got.Size)) + 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_390_GLOB_DAT))) + rela.AddUint64(target.Arch, 0) + } else { + ld.Errorf(s, "addgotsym: unsupported binary format") + } +} diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go index e463c5e727..f675be8a54 100644 --- a/src/cmd/link/internal/s390x/obj.go +++ b/src/cmd/link/internal/s390x/obj.go @@ -47,6 +47,7 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfreglr: dwarfRegLR, Adddynrel: adddynrel, + Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, Archrelocvariant: archrelocvariant, From aa74fce005b3ac35de79e799efb712f48c177256 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 21:55:24 -0400 Subject: [PATCH 21/42] [dev.link] cmd/link: use new dodata on MIPS(64) and RISCV64 They also don't need to do anything for Adddynrel. So we can just enable it. Change-Id: If85fceca63a7b3cb5a09e5db224c3018060e86de Reviewed-on: https://go-review.googlesource.com/c/go/+/229993 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/main.go | 7 +++++-- src/cmd/link/internal/ld/target.go | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 8c86fd7236..6893fa8784 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -204,8 +204,11 @@ func Main(arch *sys.Arch, theArch Arch) { // New dodata() is currently only implemented for selected targets. switch { case ctxt.IsElf(): - if !(ctxt.IsAMD64() || ctxt.Is386() || - ctxt.IsARM() || ctxt.IsARM64() || ctxt.IsS390X()) { + switch { + case ctxt.Is386(), ctxt.IsAMD64(), ctxt.IsARM(), ctxt.IsARM64(), + ctxt.IsMIPS(), ctxt.IsMIPS64(), ctxt.IsRISCV64(), ctxt.IsS390X(): + // supported + default: *flagnewDoData = false } case ctxt.IsDarwin(): diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go index 78e41f09be..102b6c5436 100644 --- a/src/cmd/link/internal/ld/target.go +++ b/src/cmd/link/internal/ld/target.go @@ -100,10 +100,22 @@ func (t *Target) IsAMD64() bool { return t.Arch.Family == sys.AMD64 } +func (t *Target) IsMIPS() bool { + return t.Arch.Family == sys.MIPS +} + +func (t *Target) IsMIPS64() bool { + return t.Arch.Family == sys.MIPS64 +} + func (t *Target) IsPPC64() bool { return t.Arch.Family == sys.PPC64 } +func (t *Target) IsRISCV64() bool { + return t.Arch.Family == sys.RISCV64 +} + func (t *Target) IsS390X() bool { return t.Arch.Family == sys.S390X } From 512a0219ef7f3d899ef4262c50e3b7daf08374bc Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Mon, 27 Apr 2020 11:10:14 -0400 Subject: [PATCH 22/42] [dev.link] cmd/link: enable new dodata on darwin/arm64 Change-Id: I6234e7288212e399f766d19fbca675f45c38e12d Reviewed-on: https://go-review.googlesource.com/c/go/+/230298 Run-TryBot: Cherry Zhang Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/main.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 6893fa8784..fcd391da90 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -211,11 +211,7 @@ func Main(arch *sys.Arch, theArch Arch) { default: *flagnewDoData = false } - case ctxt.IsDarwin(): - if !ctxt.IsAMD64() { - *flagnewDoData = false - } - case ctxt.IsPlan9(), ctxt.IsWasm(): + case ctxt.IsDarwin(), ctxt.IsPlan9(), ctxt.IsWasm(): // supported default: *flagnewDoData = false From 51ac260e5a92c75fc8fbdaa0e13958b26361b1a5 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 20:49:35 -0400 Subject: [PATCH 23/42] [dev.link] cmd/link: always run Asmb before reloc Currently, we run Asmb before reloc, except on Wasm, where the order is reversed. However, Asmb is no-op on Wasm. So we can always run Asmb first. Change-Id: Ifb8989d8150ebdd5777deb05cbccec16f8e36d82 Reviewed-on: https://go-review.googlesource.com/c/go/+/229990 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/main.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index fcd391da90..a5f2092f0f 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -346,20 +346,13 @@ func Main(arch *sys.Arch, theArch Arch) { if err := ctxt.Out.Mmap(filesize); err != nil { panic(err) } - // Asmb will redirect symbols to the output file mmap, and relocations - // will be applied directly there. - bench.Start("Asmb") - thearch.Asmb(ctxt) - bench.Start("reloc") - ctxt.reloc() - } 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) } + // 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("Asmb2") thearch.Asmb2(ctxt) From 2a874562bf7e1831bca5bb2c89dd1a9d0beac6d2 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 20:22:39 -0400 Subject: [PATCH 24/42] [dev.link] cmd/link: stop setting ReadOnly attribute in late stage The ReadOnly attribute was used to do copy on write when applying relocations to symbols with read-only backing stores. Now that we always apply relocations in the output buffer (mmap or heap), it is always writeable. No need to tamper with the ReadOnly attribute anymore. Wasm is an exception, where we don't copy symbol contents to the output buffer first. Do copy-on-write there. This is in preparation of converting reloc to using the loader. Change-Id: I15e53b7c162b9124e6689dfd8eb45cbe2ffd7153 Reviewed-on: https://go-review.googlesource.com/c/go/+/229991 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/data.go | 16 +++++++++------- src/cmd/link/internal/ld/outbuf.go | 1 - 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 5ab7661d60..0937540433 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -140,11 +140,14 @@ func relocsym(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arch if len(s.R) == 0 { return } - if s.Attr.ReadOnly() { + if target.IsWasm() && s.Attr.ReadOnly() { // The symbol's content is backed by read-only memory. // Copy it to writable memory to apply relocations. + // Only need to do this on Wasm. On other platforms we + // apply relocations to the output buffer, which is + // always writeable. s.P = append([]byte(nil), s.P...) - s.Attr.Set(sym.AttrReadOnly, false) + // No need to unset AttrReadOnly because it will not be used. } for ri := int32(0); ri < int32(len(s.R)); ri++ { r := &s.R[ri] @@ -579,8 +582,10 @@ func (ctxt *Link) reloc() { syms := &ctxt.ArchSyms wg.Add(3) go func() { - for _, s := range ctxt.Textp { - relocsym(target, ldr, reporter, syms, s) + if !ctxt.IsWasm() { // On Wasm, text relocations are applied in Asmb2. + for _, s := range ctxt.Textp { + relocsym(target, ldr, reporter, syms, s) + } } wg.Done() }() @@ -2503,8 +2508,6 @@ func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte { if len(s.R) != 0 && wasReadOnly { 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(target, ldr, reporter, archSyms, s) if _, err := z.Write(s.P); err != nil { @@ -2526,7 +2529,6 @@ func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte { // 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 } diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go index cee589fc05..40e02cb7cf 100644 --- a/src/cmd/link/internal/ld/outbuf.go +++ b/src/cmd/link/internal/ld/outbuf.go @@ -291,5 +291,4 @@ func (out *OutBuf) WriteSym(s *sym.Symbol) { copy(buf[pos:], s.P) out.off += n s.P = buf[pos : pos+n] - s.Attr.Set(sym.AttrReadOnly, false) } From a742d0ed5fb06fe2b7fe6170b1223c267abe5462 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Apr 2020 22:45:05 -0400 Subject: [PATCH 25/42] [dev.link] cmd/link: remove ctxt.Syms.Allsym Replace remaining uses with loader.Syms. Reduces some memory usage. Change-Id: I6f295b42b8cd734c6c18f08c61a5473506675075 Reviewed-on: https://go-review.googlesource.com/c/go/+/229992 Reviewed-by: Jeremy Faller Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/data2.go | 5 ++++- src/cmd/link/internal/ld/elf2.go | 5 ++++- src/cmd/link/internal/ld/lib.go | 10 ++++++++-- src/cmd/link/internal/ld/main.go | 2 +- src/cmd/link/internal/ld/xcoff.go | 5 ++++- src/cmd/link/internal/loader/loader.go | 18 +++++++++++++++--- src/cmd/link/internal/sym/symbols.go | 3 --- 7 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/cmd/link/internal/ld/data2.go b/src/cmd/link/internal/ld/data2.go index 5c88fbaa57..e11988fbbd 100644 --- a/src/cmd/link/internal/ld/data2.go +++ b/src/cmd/link/internal/ld/data2.go @@ -28,7 +28,10 @@ func (ctxt *Link) dodata() { // Collect data symbols by type into data. state := dodataState{ctxt: ctxt} - for _, s := range ctxt.Syms.Allsym { + for _, s := range ctxt.loader.Syms { + if s == nil { + continue + } if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() { continue } diff --git a/src/cmd/link/internal/ld/elf2.go b/src/cmd/link/internal/ld/elf2.go index e77510f4a6..c6e11d87bf 100644 --- a/src/cmd/link/internal/ld/elf2.go +++ b/src/cmd/link/internal/ld/elf2.go @@ -51,7 +51,10 @@ func elfdynhash(ctxt *Link) { chain := make([]uint32, nsym) buckets := make([]uint32, nbucket) - for _, sy := range ctxt.Syms.Allsym { + for _, sy := range ctxt.loader.Syms { + if sy == nil { + continue + } if sy.Dynid <= 0 { continue } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 5d01babd5f..a43aff22ee 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2555,7 +2555,10 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6 return true } - for _, s := range ctxt.Syms.Allsym { + for _, s := range ctxt.loader.Syms { + if s == nil { + continue + } if !shouldBeInSymbolTable(s) { continue } @@ -2909,7 +2912,10 @@ func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind) { } func (ctxt *Link) dumpsyms() { - for _, s := range ctxt.Syms.Allsym { + for _, s := range ctxt.loader.Syms { + if s == nil { + continue + } fmt.Printf("%s %s reachable=%v onlist=%v outer=%v sub=%v\n", s, s.Type, s.Attr.Reachable(), s.Attr.OnList(), s.Outer, s.Sub) for i := range s.R { fmt.Println("\t", s.R[i].Type, s.R[i].Sym) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index a5f2092f0f..837cfe59ca 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -364,7 +364,7 @@ func Main(arch *sys.Arch, theArch Arch) { bench.Start("hostlink") ctxt.hostlink() if ctxt.Debugvlog != 0 { - ctxt.Logf("%d symbols\n", len(ctxt.Syms.Allsym)) + ctxt.Logf("%d symbols, %d reachable\n", len(ctxt.loader.Syms), ctxt.loader.NReachableSym()) ctxt.Logf("%d liveness data\n", liveness) } bench.Start("Flush") diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index e4f30ffb31..379ecec6e7 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -1646,7 +1646,10 @@ func xcoffCreateExportFile(ctxt *Link) (fname string) { fname = filepath.Join(*flagTmpdir, "export_file.exp") var buf bytes.Buffer - for _, s := range ctxt.Syms.Allsym { + for _, s := range ctxt.loader.Syms { + if s == nil { + continue + } if !s.Attr.CgoExport() { continue } diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 30121d4cba..1eebb0f2d1 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -147,6 +147,16 @@ func (bm Bitmap) Has(i Sym) bool { func (bm Bitmap) Len() int { return len(bm) * 32 } + +// return the number of bits set. +func (bm Bitmap) Count() int { + s := 0 + for _, x := range bm { + s += bits.OnesCount32(x) + } + return s +} + func MakeBitmap(n int) Bitmap { return make(Bitmap, (n+31)/32) } @@ -625,6 +635,11 @@ func (l *Loader) NDef() int { return int(l.extStart) } +// Number of reachable symbols. +func (l *Loader) NReachableSym() int { + return l.attrReachable.Count() +} + // Returns the raw (unpatched) name of the i-th symbol. func (l *Loader) RawSymName(i Sym) string { if l.IsExternal(i) { @@ -2195,7 +2210,6 @@ func (l *Loader) ExtractSymbols(syms *sym.Symbols) { if s == nil { continue } - 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) } @@ -2209,7 +2223,6 @@ func (l *Loader) ExtractSymbols(syms *sym.Symbols) { } s := l.allocSym(name, ver) l.installSym(i, s) - syms.Allsym = append(syms.Allsym, s) // XXX see above return s } syms.Lookup = l.SymLookup @@ -2221,7 +2234,6 @@ func (l *Loader) ExtractSymbols(syms *sym.Symbols) { 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 } } diff --git a/src/cmd/link/internal/sym/symbols.go b/src/cmd/link/internal/sym/symbols.go index d36be11ee8..0d7b7e6a46 100644 --- a/src/cmd/link/internal/sym/symbols.go +++ b/src/cmd/link/internal/sym/symbols.go @@ -34,8 +34,6 @@ type Symbols struct { // Symbol lookup based on name and indexed by version. versions int - Allsym []*Symbol - // Provided by the loader // Look up the symbol with the given name and version, creating the @@ -55,7 +53,6 @@ type Symbols struct { func NewSymbols() *Symbols { return &Symbols{ versions: SymVerStatic, - Allsym: make([]*Symbol, 0, 100000), } } From 8bd7c014174cef595e058b8f4b1bffb6c108d414 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Fri, 24 Apr 2020 15:32:46 -0400 Subject: [PATCH 26/42] [dev.link] cmd/link: support new dodata for PPC64 Add linux/{ppc64,ppc64le} and aix/ppc64 arch support for the new dodata() phase. This completes the picture in terms of architecture support for the new dodata(), but to be safe this patch leaves the command line flag in place there are problems on the builders (especially given that we have a dead aix-ppc64 builder). Change-Id: I78da615c3b540d8925ed7b3226e199280eb7451d Reviewed-on: https://go-review.googlesource.com/c/go/+/229983 Reviewed-by: Cherry Zhang --- src/cmd/link/internal/ld/main.go | 18 ---- src/cmd/link/internal/ld/xcoff.go | 47 ++++++---- src/cmd/link/internal/ld/xcoff2.go | 63 ++++++++++++- src/cmd/link/internal/ppc64/asm.go | 110 ++++++++++++---------- src/cmd/link/internal/ppc64/asm2.go | 140 ++++++++++++++++++++++++++++ src/cmd/link/internal/ppc64/obj.go | 1 + 6 files changed, 293 insertions(+), 86 deletions(-) create mode 100644 src/cmd/link/internal/ppc64/asm2.go diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 837cfe59ca..b97ccf0ec9 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -200,24 +200,6 @@ func Main(arch *sys.Arch, theArch Arch) { bench.Start("Archinit") thearch.Archinit(ctxt) - if *flagnewDoData { - // New dodata() is currently only implemented for selected targets. - switch { - case ctxt.IsElf(): - switch { - case ctxt.Is386(), ctxt.IsAMD64(), ctxt.IsARM(), ctxt.IsARM64(), - ctxt.IsMIPS(), ctxt.IsMIPS64(), ctxt.IsRISCV64(), ctxt.IsS390X(): - // supported - default: - *flagnewDoData = false - } - case ctxt.IsDarwin(), ctxt.IsPlan9(), ctxt.IsWasm(): - // supported - default: - *flagnewDoData = false - } - } - if ctxt.linkShared && !ctxt.IsELF { Exitf("-linkshared can only be used on elf systems") } diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index 379ecec6e7..f156cbf439 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -360,7 +360,8 @@ type XcoffLdRel64 struct { // xcoffLoaderReloc holds information about a relocation made by the loader. type xcoffLoaderReloc struct { sym *sym.Symbol - rel *sym.Reloc + sym2 loader.Sym + roff int32 rtype uint16 symndx int32 } @@ -1108,51 +1109,55 @@ func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) { // Xcoffadddynrel adds a dynamic relocation in a XCOFF file. // This relocation will be made by the loader. -func Xcoffadddynrel(target *Target, ldr *loader.Loader, s *sym.Symbol, r *sym.Reloc) bool { +func Xcoffadddynrel2(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, r *loader.Reloc2, rIdx int) bool { if target.IsExternal() { return true } - if s.Type <= sym.SPCLNTAB { - Errorf(s, "cannot have a relocation to %s in a text section symbol", r.Sym.Name) + if ldr.SymType(s) <= sym.SPCLNTAB { + ldr.Errorf(s, "cannot have a relocation to %s in a text section symbol", ldr.SymName(r.Sym())) return false } xldr := &xcoffLoaderReloc{ - sym: s, - rel: r, + sym2: s, + roff: r.Off(), + } + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) } - switch r.Type { + switch r.Type() { default: - Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", r.Sym.Name, r.Type.String()) + ldr.Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", ldr.SymName(targ), r.Type().String()) return false case objabi.R_ADDR: - if s.Type == sym.SXCOFFTOC && r.Sym.Type == sym.SDYNIMPORT { + if ldr.SymType(s) == sym.SXCOFFTOC && targType == sym.SDYNIMPORT { // Imported symbol relocation for i, dynsym := range xfile.loaderSymbols { - if ldr.Syms[dynsym.sym].Name == r.Sym.Name { + if ldr.SymName(dynsym.sym) == ldr.SymName(targ) { xldr.symndx = int32(i + 3) // +3 because of 3 section symbols break } } - } else if s.Type == sym.SDATA { - switch r.Sym.Sect.Seg { + } else if ldr.SymType(s) == sym.SDATA { + switch ldr.SymSect(targ).Seg { default: - Errorf(s, "unknown segment for .loader relocation with symbol %s", r.Sym.Name) + ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ)) case &Segtext: case &Segrodata: xldr.symndx = 0 // .text case &Segdata: - if r.Sym.Type == sym.SBSS || r.Sym.Type == sym.SNOPTRBSS { + if targType == sym.SBSS || targType == sym.SNOPTRBSS { xldr.symndx = 2 // .bss } else { xldr.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) + ldr.Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", ldr.SymName(targ), ldr.SymType(s), ldr.SymType(targ)) return false } @@ -1303,14 +1308,18 @@ func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) { off += uint64(16 * len(f.loaderReloc)) for _, r := range f.loaderReloc { + symp := r.sym + if symp == nil { + symp = ctxt.loader.Syms[r.sym2] + } xldr = &XcoffLdRel64{ - Lvaddr: uint64(r.sym.Value + int64(r.rel.Off)), + Lvaddr: uint64(symp.Value + int64(r.roff)), Lrtype: r.rtype, Lsymndx: r.symndx, } - if r.sym.Sect != nil { - xldr.Lrsecnm = f.getXCOFFscnum(r.sym.Sect) + if symp.Sect != nil { + xldr.Lrsecnm = f.getXCOFFscnum(symp.Sect) } reloctab = append(reloctab, xldr) diff --git a/src/cmd/link/internal/ld/xcoff2.go b/src/cmd/link/internal/ld/xcoff2.go index f2c893feca..a8c2268fb7 100644 --- a/src/cmd/link/internal/ld/xcoff2.go +++ b/src/cmd/link/internal/ld/xcoff2.go @@ -4,7 +4,11 @@ package ld -import "cmd/link/internal/sym" +import ( + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" +) // Temporary dumping around for sym.Symbol version of helper // functions in xcoff.go, still being used for some archs/oses. @@ -50,3 +54,60 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) { } } + +// Xcoffadddynrel adds a dynamic relocation in a XCOFF file. +// This relocation will be made by the loader. +func Xcoffadddynrel(target *Target, ldr *loader.Loader, s *sym.Symbol, r *sym.Reloc) bool { + if target.IsExternal() { + 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 + } + + xldr := &xcoffLoaderReloc{ + sym: s, + roff: r.Off, + } + + 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 ldr.Syms[dynsym.sym].Name == r.Sym.Name { + xldr.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: + xldr.symndx = 0 // .text + case &Segdata: + if r.Sym.Type == sym.SBSS || r.Sym.Type == sym.SNOPTRBSS { + xldr.symndx = 2 // .bss + } else { + xldr.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 + } + + xldr.rtype = 0x3F<<8 + XCOFF_R_POS + } + + xfile.loaderReloc = append(xfile.loaderReloc, xldr) + return true +} diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 8f7fe35220..be124acaf8 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -267,120 +267,134 @@ func gencallstub2(ctxt *ld.Link, ldr *loader.Loader, abicase int, stub *loader.S stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr } -func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { +func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r *loader.Reloc2, rIdx int) bool { if target.IsElf() { - return addelfdynrel(target, syms, s, r) + return addelfdynrel2(target, ldr, syms, s, r, rIdx) } else if target.IsAIX() { - return ld.Xcoffadddynrel(target, ldr, s, r) + return ld.Xcoffadddynrel2(target, ldr, syms, s, r, rIdx) } return false } -func addelfdynrel(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { - targ := r.Sym - r.InitExt() +func addelfdynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r *loader.Reloc2, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } - switch r.Type { + switch r.Type() { default: - if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) return false } // Handle relocations found in ELF object files. case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24): - r.Type = objabi.R_CALLPOWER + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLPOWER) // This is a local call, so the caller isn't setting // up r12 and r2 is the same for the caller and // callee. Hence, we need to go to the local entry // point. (If we don't do this, the callee will try // to use r12 to compute r2.) - r.Add += int64(r.Sym.Localentry()) * 4 + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymLocalentry(targ))*4) - if targ.Type == sym.SDYNIMPORT { + if targType == sym.SDYNIMPORT { // Should have been handled in elfsetupplt - ld.Errorf(s, "unexpected R_PPC64_REL24 for dyn import") + ldr.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 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) - if targ.Type == sym.SDYNIMPORT { - ld.Errorf(s, "unexpected R_PPC_REL32 for dyn import") + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_PPC_REL32 for dyn import") } return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_ADDR64): - r.Type = objabi.R_ADDR - if targ.Type == sym.SDYNIMPORT { + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + if targType == sym.SDYNIMPORT { // These happen in .toc sections - ld.Adddynsym(target, syms, targ) + ld.Adddynsym2(ldr, target, syms, targ) - 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 + rela := ldr.MakeSymbolUpdater(syms.Rela2) + rela.AddAddrPlus(target.Arch, s, int64(r.Off())) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_PPC64_ADDR64))) + rela.AddUint64(target.Arch, uint64(r.Add())) + su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym } - return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16): - r.Type = objabi.R_POWER_TOC - r.Variant = sym.RV_POWER_LO | sym.RV_CHECK_OVERFLOW + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO|sym.RV_CHECK_OVERFLOW) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO): - r.Type = objabi.R_POWER_TOC - r.Variant = sym.RV_POWER_LO + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HA): - r.Type = objabi.R_POWER_TOC - r.Variant = sym.RV_POWER_HA | sym.RV_CHECK_OVERFLOW + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HI): - r.Type = objabi.R_POWER_TOC - r.Variant = sym.RV_POWER_HI | sym.RV_CHECK_OVERFLOW + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HI|sym.RV_CHECK_OVERFLOW) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_DS): - r.Type = objabi.R_POWER_TOC - r.Variant = sym.RV_POWER_DS | sym.RV_CHECK_OVERFLOW + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS|sym.RV_CHECK_OVERFLOW) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO_DS): - r.Type = objabi.R_POWER_TOC - r.Variant = sym.RV_POWER_DS + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_LO): - r.Type = objabi.R_PCREL - r.Variant = sym.RV_POWER_LO - r.Add += 2 // Compensate for relocation size of 2 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO) + su.SetRelocAdd(rIdx, r.Add()+2) // Compensate for relocation size of 2 return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HI): - r.Type = objabi.R_PCREL - r.Variant = sym.RV_POWER_HI | sym.RV_CHECK_OVERFLOW - r.Add += 2 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HI|sym.RV_CHECK_OVERFLOW) + su.SetRelocAdd(rIdx, r.Add()+2) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HA): - r.Type = objabi.R_PCREL - r.Variant = sym.RV_POWER_HA | sym.RV_CHECK_OVERFLOW - r.Add += 2 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW) + su.SetRelocAdd(rIdx, r.Add()+2) return true } // Handle references to ELF symbols from our own object files. - if targ.Type != sym.SDYNIMPORT { + if targType != sym.SDYNIMPORT { return true } diff --git a/src/cmd/link/internal/ppc64/asm2.go b/src/cmd/link/internal/ppc64/asm2.go new file mode 100644 index 0000000000..0940df92f5 --- /dev/null +++ b/src/cmd/link/internal/ppc64/asm2.go @@ -0,0 +1,140 @@ +// 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 ppc64 + +import ( + "cmd/internal/objabi" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +// Temporary dumping ground for sym.Symbol version of helper +// functions in asm.go, still being used for some oses. +// FIXME: get rid of this file when dodata() is completely +// converted. + +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(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(target.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(target, syms, targ) + + 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 + } + + 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 +} diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go index cff1e9cc73..51620b87a0 100644 --- a/src/cmd/link/internal/ppc64/obj.go +++ b/src/cmd/link/internal/ppc64/obj.go @@ -50,6 +50,7 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfreglr: dwarfRegLR, Adddynrel: adddynrel, + Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, Archrelocvariant: archrelocvariant, From 82f633a8e9a5d1b45293b54bc5224c3c846d3dc8 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Mon, 27 Apr 2020 16:01:24 -0400 Subject: [PATCH 27/42] [dev.link] cmd/link: accept more types in Xcoffadddynrel In dodata we overwrite symbol types to SDATA. Now we'll stop doing that, so accept more symbol types here. This is basically a list of all writeable types handled in dodata that could appear in XCOFF. Change-Id: Iee35369162f5acd59806a3f0e6c8d3682620067e Reviewed-on: https://go-review.googlesource.com/c/go/+/230310 Run-TryBot: Cherry Zhang Reviewed-by: Than McIntosh TryBot-Result: Gobot Gobot --- src/cmd/link/internal/ld/xcoff.go | 2 +- src/cmd/link/internal/ld/xcoff2.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index f156cbf439..c9c6507f1c 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -1141,7 +1141,7 @@ func Xcoffadddynrel2(target *Target, ldr *loader.Loader, syms *ArchSyms, s loade break } } - } else if ldr.SymType(s) == sym.SDATA { + } else if t := ldr.SymType(s); t == sym.SDATA || t == sym.SNOPTRDATA || t == sym.SBUILDINFO || t == sym.SXCOFFTOC { switch ldr.SymSect(targ).Seg { default: ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ)) diff --git a/src/cmd/link/internal/ld/xcoff2.go b/src/cmd/link/internal/ld/xcoff2.go index a8c2268fb7..27edbcb22d 100644 --- a/src/cmd/link/internal/ld/xcoff2.go +++ b/src/cmd/link/internal/ld/xcoff2.go @@ -84,7 +84,7 @@ func Xcoffadddynrel(target *Target, ldr *loader.Loader, s *sym.Symbol, r *sym.Re break } } - } else if s.Type == sym.SDATA { + } else if s.Type == sym.SDATA || s.Type == sym.SNOPTRDATA || s.Type == sym.SBUILDINFO || s.Type == sym.SXCOFFTOC { switch r.Sym.Sect.Seg { default: Errorf(s, "unknown segment for .loader relocation with symbol %s", r.Sym.Name) From 98e3fdab3eca022aa85eda84fb074d75a745b5cc Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Mon, 27 Apr 2020 16:46:23 -0400 Subject: [PATCH 28/42] [dev.link] cmd/link: delete old dodata Change-Id: I569bbee235630baad3c35ca0c6598b8bd059307a Reviewed-on: https://go-review.googlesource.com/c/go/+/230311 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/link/internal/amd64/asm2.go | 402 ------------ src/cmd/link/internal/amd64/obj.go | 1 - src/cmd/link/internal/arm/asm2.go | 246 ------- src/cmd/link/internal/arm/obj.go | 1 - src/cmd/link/internal/arm64/asm2.go | 312 --------- src/cmd/link/internal/arm64/obj.go | 1 - src/cmd/link/internal/ld/data2.go | 891 -------------------------- src/cmd/link/internal/ld/decodesym.go | 242 ------- src/cmd/link/internal/ld/elf.go | 87 --- src/cmd/link/internal/ld/elf2.go | 144 ----- src/cmd/link/internal/ld/go.go | 16 - src/cmd/link/internal/ld/main.go | 11 +- src/cmd/link/internal/ppc64/asm2.go | 140 ---- src/cmd/link/internal/ppc64/obj.go | 1 - src/cmd/link/internal/s390x/asm2.go | 231 ------- src/cmd/link/internal/s390x/obj.go | 1 - src/cmd/link/internal/x86/asm2.go | 283 -------- src/cmd/link/internal/x86/obj.go | 1 - 18 files changed, 2 insertions(+), 3009 deletions(-) delete mode 100644 src/cmd/link/internal/amd64/asm2.go delete mode 100644 src/cmd/link/internal/arm/asm2.go delete mode 100644 src/cmd/link/internal/arm64/asm2.go delete mode 100644 src/cmd/link/internal/ppc64/asm2.go delete mode 100644 src/cmd/link/internal/s390x/asm2.go delete mode 100644 src/cmd/link/internal/x86/asm2.go diff --git a/src/cmd/link/internal/amd64/asm2.go b/src/cmd/link/internal/amd64/asm2.go deleted file mode 100644 index ae12784105..0000000000 --- a/src/cmd/link/internal/amd64/asm2.go +++ /dev/null @@ -1,402 +0,0 @@ -// 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 amd64 - -import ( - "cmd/internal/objabi" - "cmd/link/internal/ld" - "cmd/link/internal/loader" - "cmd/link/internal/sym" - "debug/elf" -) - -// Temporary dumping around for sym.Symbol version of helper -// functions in asm.go, still being used for some oses. -// FIXME: get rid of this file when dodata() is completely -// converted. - -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(target.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(target, syms, targ) - r.Sym = syms.PLT - 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 { - makeWritable(s) - // 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(target, syms, targ) - - r.Type = objabi.R_PCREL - r.Sym = syms.GOT - 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 target.IsPIE() && target.IsInternal() { - // For internal linking PIE, this R_ADDR relocation cannot - // be resolved statically. We need to generate a dynamic - // relocation. Let the code below handle it. - break - } - return true - - // Handle relocations found in Mach-O object files. - case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0, - objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0, - objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0: - // 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(target, syms, targ) - r.Sym = syms.PLT - 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 - } - - makeWritable(s) - 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(target, syms, targ) - r.Type = objabi.R_PCREL - r.Sym = syms.GOT - 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 target.IsExternal() { - // External linker will do this relocation. - return true - } - // Internal linking, for both ELF and Mach-O. - // Build a PLT entry and change the relocation target to that entry. - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(targ.Plt()) - return true - - case objabi.R_ADDR: - 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(target, syms, targ) - - r.Sym = syms.GOT - r.Add += int64(targ.Got()) - return true - } - - // Process dynamic relocations for the data sections. - if target.IsPIE() && target.IsInternal() { - // When internally linking, generate dynamic relocations - // for all typical R_ADDR relocations. The exception - // are those R_ADDR that are created as part of generating - // the dynamic relocations and must be resolved statically. - // - // There are three phases relevant to understanding this: - // - // dodata() // we are here - // address() // symbol address assignment - // reloc() // resolution of static R_ADDR relocs - // - // At this point symbol addresses have not been - // assigned yet (as the final size of the .rela section - // will affect the addresses), and so we cannot write - // the Elf64_Rela.r_offset now. Instead we delay it - // until after the 'address' phase of the linker is - // complete. We do this via Addaddrplus, which creates - // a new R_ADDR relocation which will be resolved in - // the 'reloc' phase. - // - // These synthetic static R_ADDR relocs must be skipped - // now, or else we will be caught in an infinite loop - // of generating synthetic relocs for our synthetic - // relocs. - // - // Furthermore, the rela sections contain dynamic - // relocations with R_ADDR relocations on - // Elf64_Rela.r_offset. This field should contain the - // symbol offset as determined by reloc(), not the - // final dynamically linked address as a dynamic - // relocation would provide. - switch 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 target.IsElf() { - // Generate R_X86_64_RELATIVE relocations for best - // efficiency in the dynamic linker. - // - // As noted above, symbol addresses have not been - // assigned yet, so we can't generate the final reloc - // entry yet. We ultimately want: - // - // r_offset = s + r.Off - // r_info = R_X86_64_RELATIVE - // r_addend = targ + r.Add - // - // The dynamic linker will set *offset = base address + - // addend. - // - // AddAddrPlus is used for r_offset and r_addend to - // generate new R_ADDR relocations that will update - // these fields in the 'reloc' phase. - rela := syms.Rela - rela.AddAddrPlus(target.Arch, s, int64(r.Off)) - if r.Siz == 8 { - 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(target.Arch, targ, int64(r.Add)) - // Not mark r done here. So we still apply it statically, - // so in the file content we'll also have the right offset - // to the relocation target. So it can be examined statically - // (e.g. go version). - return true - } - - if target.IsDarwin() && 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. - // 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(target, syms, targ) - - 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(target.Arch, 0) - syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid)) - r.Type = objabi.ElfRelocOffset // ignore during relocsym - return true - } - } - - return false -} - -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - - if target.IsElf() { - plt := syms.PLT - got := syms.GOTPLT - rela := syms.RelaPLT - if plt.Size == 0 { - panic("plt is not set up") - } - - // jmpq *got+size(IP) - plt.AddUint8(0xff) - - plt.AddUint8(0x25) - plt.AddPCRelPlus(target.Arch, got, got.Size) - - // add to got: pointer to current pos in plt - got.AddAddrPlus(target.Arch, plt, plt.Size) - - // pushq $x - plt.AddUint8(0x68) - - plt.AddUint32(target.Arch, uint32((got.Size-24-8)/8)) - - // jmpq .plt - plt.AddUint8(0xe9) - - plt.AddUint32(target.Arch, uint32(-(plt.Size + 4))) - - // rela - rela.AddAddrPlus(target.Arch, got, got.Size-8) - - 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 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 - // 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(target, syms, s) - plt := syms.PLT - - 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(target.Arch, syms.GOT, int64(s.Got())) - } else { - ld.Errorf(s, "addpltsym: unsupported binary format") - } -} - -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) - 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") - } -} diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go index b9d0bf4b83..1fbbf60366 100644 --- a/src/cmd/link/internal/amd64/obj.go +++ b/src/cmd/link/internal/amd64/obj.go @@ -46,7 +46,6 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfregsp: dwarfRegSP, Dwarfreglr: dwarfRegLR, - Adddynrel: adddynrel, Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, diff --git a/src/cmd/link/internal/arm/asm2.go b/src/cmd/link/internal/arm/asm2.go deleted file mode 100644 index a8c26c67b4..0000000000 --- a/src/cmd/link/internal/arm/asm2.go +++ /dev/null @@ -1,246 +0,0 @@ -// 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 arm - -import ( - "cmd/internal/objabi" - "cmd/link/internal/ld" - "cmd/link/internal/loader" - "cmd/link/internal/sym" - "debug/elf" -) - -// Temporary dumping ground for sym.Symbol version of helper -// functions in asm.go, still being used for some oses. -// FIXME: get rid of this file when dodata() is completely -// converted. - -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(target.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(target, syms, targ) - r.Sym = syms.PLT - 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(target, syms, targ) - } else { - addgotsym(target, syms, 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(target, syms, targ) - } else { - addgotsym(target, syms, targ) - } - - r.Type = objabi.R_PCREL - r.Sym = syms.GOT - 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 = 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(target, syms, targ) - r.Sym = syms.PLT - 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(target, syms, targ) - r.Sym = syms.PLT - 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 target.IsExternal() { - // External linker will do this relocation. - return true - } - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(targ.Plt()) - return true - - case objabi.R_ADDR: - if s.Type != sym.SDATA { - break - } - 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 - } - } - - return false -} - -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - - if target.IsElf() { - plt := syms.PLT - got := syms.GOTPLT - rel := syms.RelPLT - if plt.Size == 0 { - panic("plt is not set up") - } - - // .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(target.Arch, plt, 0) - - // .plt entry, this depends on the .got entry - s.SetPlt(int32(plt.Size)) - - 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(target.Arch, got, int64(s.Got())) - - 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 addpltreloc(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 addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) - 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_ARM_GLOB_DAT))) - } else { - ld.Errorf(s, "addgotsym: unsupported binary format") - } -} - -func addgotsyminternal(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { - return - } - - got := syms.GOT - s.SetGot(int32(got.Size)) - - got.AddAddrPlus(target.Arch, s, 0) - - if target.IsElf() { - } else { - ld.Errorf(s, "addgotsyminternal: unsupported binary format") - } -} diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go index d6fa64de66..653f16dba1 100644 --- a/src/cmd/link/internal/arm/obj.go +++ b/src/cmd/link/internal/arm/obj.go @@ -46,7 +46,6 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfregsp: dwarfRegSP, Dwarfreglr: dwarfRegLR, - Adddynrel: adddynrel, Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, diff --git a/src/cmd/link/internal/arm64/asm2.go b/src/cmd/link/internal/arm64/asm2.go deleted file mode 100644 index b093958cb8..0000000000 --- a/src/cmd/link/internal/arm64/asm2.go +++ /dev/null @@ -1,312 +0,0 @@ -// 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 arm64 - -import ( - "cmd/internal/objabi" - "cmd/link/internal/ld" - "cmd/link/internal/loader" - "cmd/link/internal/sym" - "debug/elf" -) - -// Temporary dumping ground for sym.Symbol version of helper -// functions in asm.go, still being used for some oses. -// FIXME: get rid of this file when dodata() is completely -// converted. - -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(target.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(target, syms, targ) - r.Sym = syms.PLT - 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(target, syms, targ) - - r.Type = objabi.R_ARM64_GOT - r.Sym = syms.GOT - 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 target.IsPIE() && target.IsInternal() { - // For internal linking PIE, this R_ADDR relocation cannot - // be resolved statically. We need to generate a dynamic - // relocation. Let the code below handle it. - break - } - return true - - case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST8_ABS_LO12_NC): - if 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 target.IsExternal() { - // External linker will do this relocation. - return true - } - - case objabi.R_ADDR: - 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(target, syms, targ) - - r.Sym = syms.GOT - r.Add += int64(targ.Got()) - return true - } - - // Process dynamic relocations for the data sections. - if target.IsPIE() && target.IsInternal() { - // When internally linking, generate dynamic relocations - // for all typical R_ADDR relocations. The exception - // are those R_ADDR that are created as part of generating - // the dynamic relocations and must be resolved statically. - // - // There are three phases relevant to understanding this: - // - // dodata() // we are here - // address() // symbol address assignment - // reloc() // resolution of static R_ADDR relocs - // - // At this point symbol addresses have not been - // assigned yet (as the final size of the .rela section - // will affect the addresses), and so we cannot write - // the Elf64_Rela.r_offset now. Instead we delay it - // until after the 'address' phase of the linker is - // complete. We do this via Addaddrplus, which creates - // a new R_ADDR relocation which will be resolved in - // the 'reloc' phase. - // - // These synthetic static R_ADDR relocs must be skipped - // now, or else we will be caught in an infinite loop - // of generating synthetic relocs for our synthetic - // relocs. - // - // Furthermore, the rela sections contain dynamic - // relocations with R_ADDR relocations on - // Elf64_Rela.r_offset. This field should contain the - // symbol offset as determined by reloc(), not the - // final dynamically linked address as a dynamic - // relocation would provide. - switch 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 target.IsElf() { - // Generate R_AARCH64_RELATIVE relocations for best - // efficiency in the dynamic linker. - // - // As noted above, symbol addresses have not been - // assigned yet, so we can't generate the final reloc - // entry yet. We ultimately want: - // - // r_offset = s + r.Off - // r_info = R_AARCH64_RELATIVE - // r_addend = targ + r.Add - // - // The dynamic linker will set *offset = base address + - // addend. - // - // AddAddrPlus is used for r_offset and r_addend to - // generate new R_ADDR relocations that will update - // these fields in the 'reloc' phase. - rela := syms.Rela - rela.AddAddrPlus(target.Arch, s, int64(r.Off)) - if r.Siz == 8 { - 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(target.Arch, targ, int64(r.Add)) - // Not mark r done here. So we still apply it statically, - // so in the file content we'll also have the right offset - // to the relocation target. So it can be examined statically - // (e.g. go version). - return true - } - } - return false -} -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - - if target.IsElf() { - plt := syms.PLT - gotplt := syms.GOTPLT - rela := syms.RelaPLT - if plt.Size == 0 { - panic("plt is not set up") - } - - // adrp x16, &got.plt[0] - plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(target.Arch, plt.Size-4, 0x90000010) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT - - // is the offset value of &got.plt[n] to &got.plt[0] - // ldr x17, [x16, ] - plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(target.Arch, plt.Size-4, 0xf9400211) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT - - // add x16, x16, - plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(target.Arch, plt.Size-4, 0x91000210) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL - - // br x17 - plt.AddUint32(target.Arch, 0xd61f0220) - - // add to got.plt: pointer to plt[0] - gotplt.AddAddrPlus(target.Arch, plt, 0) - - // rela - 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 { - ld.Errorf(s, "addpltsym: unsupported binary format") - } -} - -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) - 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_AARCH64_GLOB_DAT))) - rela.AddUint64(target.Arch, 0) - } else { - ld.Errorf(s, "addgotsym: unsupported binary format") - } -} diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go index 1895fbe0c2..ffce0cb17d 100644 --- a/src/cmd/link/internal/arm64/obj.go +++ b/src/cmd/link/internal/arm64/obj.go @@ -46,7 +46,6 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfregsp: dwarfRegSP, Dwarfreglr: dwarfRegLR, - Adddynrel: adddynrel, Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, diff --git a/src/cmd/link/internal/ld/data2.go b/src/cmd/link/internal/ld/data2.go index e11988fbbd..033fe75068 100644 --- a/src/cmd/link/internal/ld/data2.go +++ b/src/cmd/link/internal/ld/data2.go @@ -5,16 +5,8 @@ package ld import ( - "cmd/internal/gcprog" - "cmd/internal/objabi" - "cmd/link/internal/loader" "cmd/link/internal/sym" - "fmt" - "log" - "os" - "sort" "strings" - "sync" ) // Temporary dumping around for sym.Symbol version of helper @@ -22,206 +14,6 @@ import ( // FIXME: get rid of this file when dodata() is completely // converted. -func (ctxt *Link) dodata() { - // Give zeros sized symbols space if necessary. - fixZeroSizedSymbols(ctxt) - - // Collect data symbols by type into data. - state := dodataState{ctxt: ctxt} - for _, s := range ctxt.loader.Syms { - if s == nil { - continue - } - if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() { - continue - } - if s.Type <= sym.STEXT || s.Type >= sym.SXREF { - continue - } - state.data[s.Type] = append(state.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 { - panic("not supported") - //machosymorder(ctxt) - } - state.dynreloc(ctxt) - - // Move any RO data with relocations to a separate section. - state.makeRelroForSharedLib(ctxt) - - // Temporary for debugging. - symToIdx := make(map[*sym.Symbol]loader.Sym) - for s := loader.Sym(1); s < loader.Sym(ctxt.loader.NSym()); s++ { - sp := ctxt.loader.Syms[s] - if sp != nil { - symToIdx[sp] = s - } - } - - // Sort symbols. - var wg sync.WaitGroup - for symn := range state.data { - symn := sym.SymKind(symn) - wg.Add(1) - go func() { - state.data[symn], state.dataMaxAlign[symn] = dodataSect(ctxt, symn, state.data[symn], symToIdx) - 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 = state.dataMaxAlign[sym.SDATA] - ctxt.Syms.ROLookup("runtime.bss", 0).Align = state.dataMaxAlign[sym.SBSS] - } - - // Create *sym.Section objects and assign symbols to sections for - // data/rodata (and related) symbols. - state.allocateDataSections(ctxt) - - // Create *sym.Section objects and assign symbols to sections for - // DWARF symbols. - state.allocateDwarfSections(ctxt) - - /* number the sections */ - n := int16(1) - - for _, sect := range Segtext.Sections { - sect.Extnum = n - n++ - } - for _, sect := range Segrodata.Sections { - sect.Extnum = n - n++ - } - for _, sect := range Segrelrodata.Sections { - sect.Extnum = n - n++ - } - for _, sect := range Segdata.Sections { - sect.Extnum = n - n++ - } - for _, sect := range Segdwarf.Sections { - sect.Extnum = n - n++ - } -} - -// makeRelroForSharedLib creates a section of readonly data if necessary. -func (state *dodataState) makeRelroForSharedLib(target *Link) { - if !target.UseRelro() { - return - } - - // "read only" data with relocations needs to go in its own section - // when building a shared library. We do this by boosting objects of - // type SXXX with relocations to type SXXXRELRO. - for _, symnro := range sym.ReadOnly { - symnrelro := sym.RelROMap[symnro] - - ro := []*sym.Symbol{} - relro := state.data[symnrelro] - - for _, s := range state.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) - } - } - - state.data[symnro] = ro - state.data[symnrelro] = relro - } -} - -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(target, ldr, syms, 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(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) - } - } - } -} - -func (state *dodataState) dynreloc(ctxt *Link) { - if ctxt.HeadType == objabi.Hwindows { - return - } - // -d suppresses dynamic loader format, so we may as well not - // compute these sections or mark their symbols as reachable. - if *FlagD { - return - } - - for _, s := range ctxt.Textp { - dynrelocsym(ctxt, s) - } - for _, syms := range state.data { - for _, s := range syms { - dynrelocsym(ctxt, s) - } - } - if ctxt.IsELF { - elfdynhash(ctxt) - } -} - func Addstring(s *sym.Symbol, str string) int64 { if s.Type == 0 { s.Type = sym.SNOPTRDATA @@ -257,686 +49,3 @@ func symalign(s *sym.Symbol) int32 { s.Align = align return align } - -func aligndatsize(datsize int64, s *sym.Symbol) int64 { - return Rnd(datsize, int64(symalign(s))) -} - -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 - symIdx loader.Sym -} - -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 - } - if s1.name != s2.name { - return s1.name < s2.name - } - return s1.symIdx < s2.symIdx -} - -// 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 - } - - 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) - } -} - -// allocateDataSectionForSym creates a new sym.Section into which a a -// single symbol will be placed. Here "seg" is the segment into which -// the section will go, "s" is the symbol to be placed into the new -// section, and "rwx" contains permissions for the section. -func (state *dodataState) allocateDataSectionForSym(seg *sym.Segment, s *sym.Symbol, rwx int) *sym.Section { - sect := addsection(state.ctxt.loader, state.ctxt.Arch, seg, s.Name, rwx) - sect.Align = symalign(s) - state.datsize = Rnd(state.datsize, int64(sect.Align)) - sect.Vaddr = uint64(state.datsize) - return sect -} - -// assignDsymsToSection assigns a collection of data symbols to a -// newly created section. "sect" is the section into which to place -// the symbols, "syms" holds the list of symbols to assign, -// "forceType" (if non-zero) contains a new sym type to apply to each -// sym during the assignment, and "aligner" is a hook to call to -// handle alignment during the assignment process. -func (state *dodataState) assignDsymsToSection(sect *sym.Section, syms []*sym.Symbol, forceType sym.SymKind, aligner func(datsize int64, s *sym.Symbol) int64) { - for _, s := range syms { - state.datsize = aligner(state.datsize, s) - s.Sect = sect - if forceType != sym.Sxxx { - s.Type = forceType - } - s.Value = int64(uint64(state.datsize) - sect.Vaddr) - state.datsize += s.Size - } - sect.Length = uint64(state.datsize) - sect.Vaddr -} - -func (state *dodataState) assignToSection(sect *sym.Section, symn sym.SymKind, forceType sym.SymKind) { - state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) - state.checkdatsize(symn) -} - -// allocateSingleSymSections walks through the bucketed data symbols -// with type 'symn', creates a new section for each sym, and assigns -// the sym to a newly created section. Section name is set from the -// symbol name. "Seg" is the segment into which to place the new -// section, "forceType" is the new sym.SymKind to assign to the symbol -// within the section, and "rwx" holds section permissions. -func (state *dodataState) allocateSingleSymSections(seg *sym.Segment, symn sym.SymKind, forceType sym.SymKind, rwx int) { - for _, s := range state.data[symn] { - sect := state.allocateDataSectionForSym(seg, s, rwx) - s.Sect = sect - s.Type = forceType - s.Value = int64(uint64(state.datsize) - sect.Vaddr) - state.datsize += s.Size - sect.Length = uint64(state.datsize) - sect.Vaddr - } - state.checkdatsize(symn) -} - -// allocateNamedSectionAndAssignSyms creates a new section with the -// specified name, then walks through the bucketed data symbols with -// type 'symn' and assigns each of them to this new section. "Seg" is -// the segment into which to place the new section, "secName" is the -// name to give to the new section, "forceType" (if non-zero) contains -// a new sym type to apply to each sym during the assignment, and -// "rwx" holds section permissions. -func (state *dodataState) allocateNamedSectionAndAssignSyms(seg *sym.Segment, secName string, symn sym.SymKind, forceType sym.SymKind, rwx int) *sym.Section { - - sect := state.allocateNamedDataSection(seg, secName, []sym.SymKind{symn}, rwx) - state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) - return sect -} - -// allocateDataSections allocates sym.Section objects for data/rodata -// (and related) symbols, and then assigns symbols to those sections. -func (state *dodataState) allocateDataSections(ctxt *Link) { - // Allocate sections. - // Data is processed before segtext, because we need - // to see all symbols in the .data and .bss sections in order - // to generate garbage collection information. - - // Writable data sections that do not need any specialized handling. - writable := []sym.SymKind{ - sym.SBUILDINFO, - sym.SELFSECT, - sym.SMACHO, - sym.SMACHOGOT, - sym.SWINDOWS, - } - for _, symn := range writable { - state.allocateSingleSymSections(&Segdata, symn, sym.SDATA, 06) - } - - // .got (and .toc on ppc64) - if len(state.data[sym.SELFGOT]) > 0 { - sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06) - if ctxt.IsPPC64() { - for _, s := range state.data[sym.SELFGOT] { - // Resolve .TOC. symbol for this object file (ppc64) - toc := 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 - } - } - } - } - - /* pointer-free data */ - sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrdata", sym.SNOPTRDATA, sym.SDATA, 06) - ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect = sect - ctxt.Syms.Lookup("runtime.enoptrdata", 0).Sect = sect - - hasinitarr := ctxt.linkShared - - /* shared library initializer */ - switch ctxt.BuildMode { - case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin: - hasinitarr = true - } - - if ctxt.HeadType == objabi.Haix { - if len(state.data[sym.SINITARR]) > 0 { - Errorf(nil, "XCOFF format doesn't allow .init_array section") - } - } - - if hasinitarr && len(state.data[sym.SINITARR]) > 0 { - state.allocateNamedSectionAndAssignSyms(&Segdata, ".init_array", sym.SINITARR, sym.Sxxx, 06) - } - - /* data */ - sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".data", sym.SDATA, sym.SDATA, 06) - ctxt.Syms.Lookup("runtime.data", 0).Sect = sect - ctxt.Syms.Lookup("runtime.edata", 0).Sect = sect - dataGcEnd := state.datsize - int64(sect.Vaddr) - - // On AIX, TOC entries must be the last of .data - // These aren't part of gc as they won't change during the runtime. - state.assignToSection(sect, sym.SXCOFFTOC, sym.SDATA) - state.checkdatsize(sym.SDATA) - sect.Length = uint64(state.datsize) - sect.Vaddr - - /* bss */ - sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".bss", sym.SBSS, sym.Sxxx, 06) - ctxt.Syms.Lookup("runtime.bss", 0).Sect = sect - ctxt.Syms.Lookup("runtime.ebss", 0).Sect = sect - bssGcEnd := state.datsize - int64(sect.Vaddr) - - // Emit gcdata for bcc symbols now that symbol values have been assigned. - gcsToEmit := []struct { - symName string - symKind sym.SymKind - gcEnd int64 - }{ - {"runtime.gcdata", sym.SDATA, dataGcEnd}, - {"runtime.gcbss", sym.SBSS, bssGcEnd}, - } - for _, g := range gcsToEmit { - var gc GCProg - gc.Init(ctxt, g.symName) - for _, s := range state.data[g.symKind] { - gc.AddSym(s) - } - gc.End(g.gcEnd) - } - - /* pointer-free bss */ - sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06) - ctxt.Syms.Lookup("runtime.noptrbss", 0).Sect = sect - ctxt.Syms.Lookup("runtime.enoptrbss", 0).Sect = sect - ctxt.Syms.Lookup("runtime.end", 0).Sect = sect - - // Coverage instrumentation counters for libfuzzer. - if len(state.data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 { - state.allocateNamedSectionAndAssignSyms(&Segdata, "__libfuzzer_extra_counters", sym.SLIBFUZZER_EXTRA_COUNTER, sym.Sxxx, 06) - } - - if len(state.data[sym.STLSBSS]) > 0 { - var sect *sym.Section - // FIXME: not clear why it is sometimes necessary to suppress .tbss section creation. - if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) { - sect = addsection(ctxt.loader, ctxt.Arch, &Segdata, ".tbss", 06) - sect.Align = int32(ctxt.Arch.PtrSize) - // FIXME: why does this need to be set to zero? - sect.Vaddr = 0 - } - state.datsize = 0 - - for _, s := range state.data[sym.STLSBSS] { - state.datsize = aligndatsize(state.datsize, s) - s.Sect = sect - s.Value = state.datsize - state.datsize += s.Size - } - state.checkdatsize(sym.STLSBSS) - - if sect != nil { - sect.Length = uint64(state.datsize) - } - } - - /* - * We finished data, begin read-only data. - * Not all systems support a separate read-only non-executable data section. - * ELF and Windows PE systems do. - * OS X and Plan 9 do not. - * And if we're using external linking mode, the point is moot, - * since it's not our decision; that code expects the sections in - * segtext. - */ - var segro *sym.Segment - if ctxt.IsELF && ctxt.LinkMode == LinkInternal { - segro = &Segrodata - } else if ctxt.HeadType == objabi.Hwindows { - segro = &Segrodata - } else { - segro = &Segtext - } - - state.datsize = 0 - - /* read-only executable ELF, Mach-O sections */ - if len(state.data[sym.STEXT]) != 0 { - Errorf(nil, "dodata found an sym.STEXT symbol: %s", state.data[sym.STEXT][0].Name) - } - state.allocateSingleSymSections(&Segtext, sym.SELFRXSECT, sym.SRODATA, 04) - - /* read-only data */ - sect = state.allocateNamedDataSection(segro, ".rodata", sym.ReadOnly, 04) - 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 { - symnStartValue := state.datsize - state.assignToSection(sect, symn, sym.SRODATA) - if ctxt.HeadType == objabi.Haix { - // Read-only symbols might be wrapped inside their outer - // symbol. - // XCOFF symbol table needs to know the size of - // these outer symbols. - xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) - } - } - - /* read-only ELF, Mach-O sections */ - state.allocateSingleSymSections(segro, sym.SELFROSECT, sym.SRODATA, 04) - state.allocateSingleSymSections(segro, sym.SMACHOPLT, sym.SRODATA, 04) - - // There is some data that are conceptually read-only but are written to by - // relocations. On GNU systems, we can arrange for the dynamic linker to - // mprotect sections after relocations are applied by giving them write - // permissions in the object file and calling them ".data.rel.ro.FOO". We - // divide the .rodata section between actual .rodata and .data.rel.ro.rodata, - // but for the other sections that this applies to, we just write a read-only - // .FOO section or a read-write .data.rel.ro.FOO section depending on the - // situation. - // TODO(mwhudson): It would make sense to do this more widely, but it makes - // the system linker segfault on darwin. - const relroPerm = 06 - const fallbackPerm = 04 - relroSecPerm := fallbackPerm - genrelrosecname := func(suffix string) string { - return suffix - } - seg := segro - - 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. - state.datsize = 0 - } - - genrelrosecname = func(suffix string) string { - return ".data.rel.ro" + suffix - } - relroReadOnly := []sym.SymKind{} - for _, symnro := range sym.ReadOnly { - symn := sym.RelROMap[symnro] - relroReadOnly = append(relroReadOnly, symn) - } - seg = segrelro - relroSecPerm = relroPerm - - /* data only written by relocations */ - sect = state.allocateNamedDataSection(segrelro, genrelrosecname(""), relroReadOnly, relroSecPerm) - - ctxt.Syms.Lookup("runtime.types", 0).Sect = sect - ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect - - for i, symnro := range sym.ReadOnly { - if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix { - // Skip forward so that no type - // reference uses a zero offset. - // This is unlikely but possible in small - // programs with no other read-only data. - state.datsize++ - } - - symn := sym.RelROMap[symnro] - symnStartValue := state.datsize - - for _, s := range state.data[symn] { - 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) - } - } - state.assignToSection(sect, symn, sym.SRODATA) - if ctxt.HeadType == objabi.Haix { - // Read-only symbols might be wrapped inside their outer - // symbol. - // XCOFF symbol table needs to know the size of - // these outer symbols. - xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) - } - } - - sect.Length = uint64(state.datsize) - sect.Vaddr - } - - /* typelink */ - sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm) - typelink := ctxt.Syms.Lookup("runtime.typelink", 0) - typelink.Sect = sect - typelink.Type = sym.SRODATA - state.datsize += typelink.Size - state.checkdatsize(sym.STYPELINK) - sect.Length = uint64(state.datsize) - sect.Vaddr - - /* itablink */ - sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".itablink"), sym.SITABLINK, sym.Sxxx, relroSecPerm) - ctxt.Syms.Lookup("runtime.itablink", 0).Sect = sect - ctxt.Syms.Lookup("runtime.eitablink", 0).Sect = sect - 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 = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gosymtab"), sym.SSYMTAB, sym.SRODATA, relroSecPerm) - ctxt.Syms.Lookup("runtime.symtab", 0).Sect = sect - ctxt.Syms.Lookup("runtime.esymtab", 0).Sect = sect - - /* gopclntab */ - sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm) - ctxt.Syms.Lookup("runtime.pclntab", 0).Sect = sect - ctxt.Syms.Lookup("runtime.epclntab", 0).Sect = sect - - // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. - if state.datsize != int64(uint32(state.datsize)) { - Errorf(nil, "read-only data segment too large: %d", state.datsize) - } - - for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { - ctxt.datap = append(ctxt.datap, state.data[symn]...) - } -} - -// allocateDwarfSections allocates sym.Section objects for DWARF -// symbols, and assigns symbols to sections. -func (state *dodataState) allocateDwarfSections(ctxt *Link) { - - alignOne := func(datsize int64, s *sym.Symbol) int64 { return datsize } - - for i := 0; i < len(dwarfp); i++ { - // First the section symbol. - s := dwarfp[i].secSym() - sect := state.allocateNamedDataSection(&Segdwarf, s.Name, []sym.SymKind{}, 04) - sect.Sym = s - s.Sect = sect - curType := s.Type - s.Type = sym.SRODATA - s.Value = int64(uint64(state.datsize) - sect.Vaddr) - state.datsize += s.Size - - // Then any sub-symbols for the section symbol. - subSyms := dwarfp[i].subSyms() - state.assignDsymsToSection(sect, subSyms, sym.SRODATA, alignOne) - - for j := 0; j < len(subSyms); j++ { - s := subSyms[j] - if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC { - // Update the size of .debug_loc for this symbol's - // package. - addDwsectCUSize(".debug_loc", s.File, uint64(s.Size)) - } - } - sect.Length = uint64(state.datsize) - sect.Vaddr - state.checkdatsize(curType) - } -} - -func dodataSect(ctxt *Link, symn sym.SymKind, syms []*sym.Symbol, symToIdx map[*sym.Symbol]loader.Sym) (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, - symIdx: symToIdx[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 -} diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index 8e248fc982..9534464916 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -5,12 +5,9 @@ package ld import ( - "bytes" "cmd/internal/objabi" "cmd/internal/sys" - "cmd/link/internal/sym" "debug/elf" - "fmt" ) // Decoding the type.* symbols. This has to be in sync with @@ -29,23 +26,6 @@ const ( 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: @@ -103,26 +83,6 @@ func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section { return nil } -// Type.commonType.gc -func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte { - if s.Type == sym.SDYNIMPORT { - addr := decodetypeGcprogShlib(ctxt, s.P) - 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, data []byte) uint64 { if ctxt.Arch.Family == sys.ARM64 { return 0 @@ -130,51 +90,6 @@ func decodetypeGcprogShlib(ctxt *Link, data []byte) uint64 { return decodeInuxi(ctxt.Arch, data[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize) } -func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte { - if s.Type == sym.SDYNIMPORT { - addr := decodetypeGcprogShlib(ctxt, s.P) - 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 @@ -189,75 +104,6 @@ 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)) @@ -279,91 +125,3 @@ const ( 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/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index d3270720f1..3a0a305db9 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -2343,93 +2343,6 @@ elfobj: } } -func elfadddynsym(target *Target, syms *ArchSyms, s *sym.Symbol) { - if elf64 { - s.Dynid = int32(Nelfsym) - Nelfsym++ - - d := syms.DynSym - - name := s.Extname() - d.AddUint32(target.Arch, uint32(Addstring(syms.DynStr, 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(target.Arch, SHN_UNDEF) - } else { - d.AddUint16(target.Arch, 1) - } - - /* value */ - if s.Type == sym.SDYNIMPORT { - d.AddUint64(target.Arch, 0) - } else { - d.AddAddr(target.Arch, s) - } - - /* size of object */ - d.AddUint64(target.Arch, uint64(s.Size)) - - 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 := syms.DynSym - - /* name */ - name := s.Extname() - - d.AddUint32(target.Arch, uint32(Addstring(syms.DynStr, name))) - - /* value */ - if s.Type == sym.SDYNIMPORT { - d.AddUint32(target.Arch, 0) - } else { - d.AddAddr(target.Arch, s) - } - - /* size of object */ - 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 target.Arch.Family == sys.I386 && s.Attr.CgoExport() && s.Type == sym.STEXT { - t |= STT_FUNC - } else if target.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(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++ diff --git a/src/cmd/link/internal/ld/elf2.go b/src/cmd/link/internal/ld/elf2.go index c6e11d87bf..3f7d72b310 100644 --- a/src/cmd/link/internal/ld/elf2.go +++ b/src/cmd/link/internal/ld/elf2.go @@ -5,7 +5,6 @@ package ld import ( - "cmd/internal/sys" "cmd/link/internal/sym" ) @@ -24,146 +23,3 @@ func elfsetstring(s *sym.Symbol, str string, off int) { elfstr[nelfstr].off = off nelfstr++ } - -func elfWriteDynEntSym(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) { - Elfwritedynentsymplus(arch, s, tag, t, 0) -} - -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.loader.Syms { - if sy == nil { - continue - } - 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.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.Arch, s, DT_PLTREL, DT_RELA) - } else { - elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_REL) - } - elfWriteDynEntSymSize(ctxt.Arch, s, DT_PLTRELSZ, sy) - elfWriteDynEntSym(ctxt.Arch, s, DT_JMPREL, sy) - } - - elfWriteDynEnt(ctxt.Arch, s, DT_NULL, 0) -} diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 3bcd56ef6d..9a63a3a0bb 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -347,22 +347,6 @@ func Adddynsym2(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym } } -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 target.HeadType == objabi.Hwindows { - // already taken care of - } else { - Errorf(s, "adddynsym: unsupported binary format") - } -} - func fieldtrack(arch *sys.Arch, l *loader.Loader) { var buf bytes.Buffer for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ { diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index b97ccf0ec9..bbd6f1c663 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -96,7 +96,6 @@ 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`") - flagnewDoData = flag.Bool("newdodata", true, "New style dodata") 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") @@ -299,16 +298,10 @@ func Main(arch *sys.Arch, theArch Arch) { dwarfGenerateDebugSyms(ctxt) bench.Start("symtab") symGroupType := ctxt.symtab() - if *flagnewDoData { - bench.Start("dodata") - ctxt.dodata2(symGroupType) - } + bench.Start("dodata") + ctxt.dodata2(symGroupType) bench.Start("loadlibfull") ctxt.loadlibfull(symGroupType) // XXX do it here for now - if !*flagnewDoData { - bench.Start("dodata") - ctxt.dodata() - } bench.Start("address") order := ctxt.address() bench.Start("dwarfcompress") diff --git a/src/cmd/link/internal/ppc64/asm2.go b/src/cmd/link/internal/ppc64/asm2.go deleted file mode 100644 index 0940df92f5..0000000000 --- a/src/cmd/link/internal/ppc64/asm2.go +++ /dev/null @@ -1,140 +0,0 @@ -// 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 ppc64 - -import ( - "cmd/internal/objabi" - "cmd/link/internal/ld" - "cmd/link/internal/loader" - "cmd/link/internal/sym" - "debug/elf" -) - -// Temporary dumping ground for sym.Symbol version of helper -// functions in asm.go, still being used for some oses. -// FIXME: get rid of this file when dodata() is completely -// converted. - -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(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(target.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(target, syms, targ) - - 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 - } - - 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 -} diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go index 51620b87a0..67002bc719 100644 --- a/src/cmd/link/internal/ppc64/obj.go +++ b/src/cmd/link/internal/ppc64/obj.go @@ -49,7 +49,6 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfregsp: dwarfRegSP, Dwarfreglr: dwarfRegLR, - Adddynrel: adddynrel, Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, diff --git a/src/cmd/link/internal/s390x/asm2.go b/src/cmd/link/internal/s390x/asm2.go deleted file mode 100644 index 1487f11db7..0000000000 --- a/src/cmd/link/internal/s390x/asm2.go +++ /dev/null @@ -1,231 +0,0 @@ -// 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 s390x - -import ( - "cmd/internal/objabi" - "cmd/link/internal/ld" - "cmd/link/internal/loader" - "cmd/link/internal/sym" - "debug/elf" -) - -func adddynrel(target *ld.Target, ldr *loader.Loader, 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", 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(target, syms, targ) - r.Sym = syms.PLT - 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(target, syms, targ) - r.Sym = syms.PLT - 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 = syms.GOT - 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 = syms.GOT - r.Add += int64(r.Siz) - return true - - case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT): - addgotsym(target, syms, targ) - - r.Type = objabi.R_PCREL - r.Variant = sym.RV_390_DBL - r.Sym = syms.GOT - 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 addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - - if target.IsElf() { - plt := syms.PLT - got := syms.GOT - rela := syms.RelaPLT - if plt.Size == 0 { - panic("plt is not set up") - } - // larl %r1,_GLOBAL_OFFSET_TABLE_+index - - plt.AddUint8(0xc0) - plt.AddUint8(0x10) - plt.AddPCRelPlus(target.Arch, got, got.Size+6) // need variant? - - // add to got: pointer to current pos in plt - got.AddAddrPlus(target.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(target.Arch, uint32(-((plt.Size - 2) >> 1))) // roll-your-own relocation - //.plt index - plt.AddUint32(target.Arch, uint32(rela.Size)) // rela size before current entry - - // rela - rela.AddAddrPlus(target.Arch, got, got.Size-8) - - 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)) - - } else { - ld.Errorf(s, "addpltsym: unsupported binary format") - } -} - -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) - 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_390_GLOB_DAT))) - rela.AddUint64(target.Arch, 0) - } else { - ld.Errorf(s, "addgotsym: unsupported binary format") - } -} diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go index f675be8a54..129c6a7ee4 100644 --- a/src/cmd/link/internal/s390x/obj.go +++ b/src/cmd/link/internal/s390x/obj.go @@ -46,7 +46,6 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfregsp: dwarfRegSP, Dwarfreglr: dwarfRegLR, - Adddynrel: adddynrel, Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, diff --git a/src/cmd/link/internal/x86/asm2.go b/src/cmd/link/internal/x86/asm2.go deleted file mode 100644 index 16f1682dc1..0000000000 --- a/src/cmd/link/internal/x86/asm2.go +++ /dev/null @@ -1,283 +0,0 @@ -// 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 x86 - -import ( - "cmd/internal/objabi" - "cmd/link/internal/ld" - "cmd/link/internal/loader" - "cmd/link/internal/sym" - "debug/elf" -) - -// Temporary dumping around for sym.Symbol version of helper -// functions in asm.go, still being used for some oses. -// FIXME: get rid of this file when dodata() is completely -// converted. - -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(target.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(target, syms, targ) - r.Sym = syms.PLT - 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(target, syms, 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 = syms.GOT - 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(target, syms, targ) - r.Sym = syms.PLT - 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(target, syms, targ) - r.Sym = syms.GOT - 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 target.IsExternal() { - // External linker will do this relocation. - return true - } - addpltsym(target, syms, targ) - r.Sym = syms.PLT - r.Add = int64(targ.Plt()) - return true - - case objabi.R_ADDR: - if s.Type != sym.SDATA { - break - } - 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 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. - // 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(target, syms, targ) - - 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(target.Arch, 0) - syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid)) - r.Type = objabi.ElfRelocOffset // ignore during relocsym - return true - } - } - - return false -} - -func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Plt() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - - if target.IsElf() { - plt := syms.PLT - got := syms.GOTPLT - rel := syms.RelPLT - if plt.Size == 0 { - panic("plt is not set up") - } - - // jmpq *got+size - plt.AddUint8(0xff) - - plt.AddUint8(0x25) - plt.AddAddrPlus(target.Arch, got, got.Size) - - // add to got: pointer to current pos in plt - got.AddAddrPlus(target.Arch, plt, plt.Size) - - // pushl $x - plt.AddUint8(0x68) - - plt.AddUint32(target.Arch, uint32(rel.Size)) - - // jmp .plt - plt.AddUint8(0xe9) - - plt.AddUint32(target.Arch, uint32(-(plt.Size + 4))) - - // rel - rel.AddAddrPlus(target.Arch, got, got.Size-4) - - 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 target.IsDarwin() { - // Same laziness as in 6l. - - plt := syms.PLT - - addgotsym(target, syms, s) - - 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(target.Arch, syms.GOT, int64(s.Got())) - } else { - ld.Errorf(s, "addpltsym: unsupported binary format") - } -} - -func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { - if s.Got() >= 0 { - return - } - - ld.Adddynsym(target, syms, s) - got := syms.GOT - s.SetGot(int32(got.Size)) - 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") - } -} diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go index 34babe9a72..61e3077b5b 100644 --- a/src/cmd/link/internal/x86/obj.go +++ b/src/cmd/link/internal/x86/obj.go @@ -46,7 +46,6 @@ func Init() (*sys.Arch, ld.Arch) { Dwarfregsp: dwarfRegSP, Dwarfreglr: dwarfRegLR, - Adddynrel: adddynrel, Adddynrel2: adddynrel2, Archinit: archinit, Archreloc: archreloc, From b6df7e555ec1e1f813cda0d250cd109533e6d4e5 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sat, 25 Apr 2020 17:27:34 -0400 Subject: [PATCH 29/42] [dev.link] cmd/link: stop overwriting symbol types in loadlibfull symGroupType was needed for dodata. Now that we have converted dodata to using the loader, stop overwriting it. Change-Id: Ie94109c0b35dd2f71a19ebb38f8cf20b6a37c624 Reviewed-on: https://go-review.googlesource.com/c/go/+/229994 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/lib.go | 16 +--------------- src/cmd/link/internal/ld/main.go | 2 +- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index a43aff22ee..96e3e8870c 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2824,7 +2824,7 @@ func addToTextp(ctxt *Link) { ctxt.Textp = textp } -func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind) { +func (ctxt *Link) loadlibfull() { // Load full symbol contents, resolve indexed references. ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms) @@ -2890,20 +2890,6 @@ func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind) { } } - // For now, overwrite symbol type with its "group" type, as dodata - // expected. Once we converted dodata, this will probably not be - // needed. - for i, t := range symGroupType { - if t != sym.Sxxx { - s := ctxt.loader.Syms[i] - if s == nil { - panic(fmt.Sprintf("nil sym for symGroupType t=%s entry %d", t.String(), i)) - } - s.Type = t - } - } - symGroupType = nil - if ctxt.Debugvlog > 1 { // loadlibfull is likely a good place to dump. // Only dump under -v=2 and above. diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index bbd6f1c663..6bcf274215 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -301,7 +301,7 @@ func Main(arch *sys.Arch, theArch Arch) { bench.Start("dodata") ctxt.dodata2(symGroupType) bench.Start("loadlibfull") - ctxt.loadlibfull(symGroupType) // XXX do it here for now + ctxt.loadlibfull() // XXX do it here for now bench.Start("address") order := ctxt.address() bench.Start("dwarfcompress") From 8e7ddac52d436e6930b3d1f4b2106ee85e7adb30 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Mon, 27 Apr 2020 18:47:03 -0400 Subject: [PATCH 30/42] [dev.link] cmd/link: fix accidental shadowing ctxt.Tlsg2 is supposed to be the embedded ArchSyms.Tlsg2. Change-Id: I4f9711f83999d4a98bcf6d99c24fab756c580905 Reviewed-on: https://go-review.googlesource.com/c/go/+/230379 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/link.go | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index 839ee0ca7e..84b1f9121e 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -66,7 +66,6 @@ type Link struct { compressDWARF bool - Tlsg2 loader.Sym Libdir []string Library []*sym.Library LibraryByPkg map[string]*sym.Library From b85a03ccc20e446f89d7e32e98c71f5e66393b0c Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Tue, 28 Apr 2020 14:05:20 +0000 Subject: [PATCH 31/42] Revert "[dev.link] cmd/link: stop overwriting symbol types in loadlibfull" This reverts CL 229994. Reason for revert: break AIX build. This is nice to have but isn't critical. We can revisit later. Change-Id: Ifc56a0a4c0fb36859cf7666ab149e25e0e5d4cc0 Reviewed-on: https://go-review.googlesource.com/c/go/+/230459 Run-TryBot: Cherry Zhang Reviewed-by: Than McIntosh --- src/cmd/link/internal/ld/lib.go | 16 +++++++++++++++- src/cmd/link/internal/ld/main.go | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 96e3e8870c..a43aff22ee 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2824,7 +2824,7 @@ func addToTextp(ctxt *Link) { ctxt.Textp = textp } -func (ctxt *Link) loadlibfull() { +func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind) { // Load full symbol contents, resolve indexed references. ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms) @@ -2890,6 +2890,20 @@ func (ctxt *Link) loadlibfull() { } } + // For now, overwrite symbol type with its "group" type, as dodata + // expected. Once we converted dodata, this will probably not be + // needed. + for i, t := range symGroupType { + if t != sym.Sxxx { + s := ctxt.loader.Syms[i] + if s == nil { + panic(fmt.Sprintf("nil sym for symGroupType t=%s entry %d", t.String(), i)) + } + s.Type = t + } + } + symGroupType = nil + if ctxt.Debugvlog > 1 { // loadlibfull is likely a good place to dump. // Only dump under -v=2 and above. diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 6bcf274215..bbd6f1c663 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -301,7 +301,7 @@ func Main(arch *sys.Arch, theArch Arch) { bench.Start("dodata") ctxt.dodata2(symGroupType) bench.Start("loadlibfull") - ctxt.loadlibfull() // XXX do it here for now + ctxt.loadlibfull(symGroupType) // XXX do it here for now bench.Start("address") order := ctxt.address() bench.Start("dwarfcompress") From dc83274c95c882f16d944aaf68d8314c12a1e2ae Mon Sep 17 00:00:00 2001 From: Jeremy Faller Date: Thu, 23 Apr 2020 14:28:20 -0400 Subject: [PATCH 32/42] [dev.link] cmd/link: move wavefront past address This change is just the mechanical work of moving the wavefront past address. Change-Id: I519ec49fa8ba50760c7d23fc084fcd3bb0544546 Reviewed-on: https://go-review.googlesource.com/c/go/+/229700 Run-TryBot: Jeremy Faller Reviewed-by: Cherry Zhang TryBot-Result: Gobot Gobot --- src/cmd/link/internal/ld/data.go | 130 +++++++++++++------------ src/cmd/link/internal/ld/main.go | 4 +- src/cmd/link/internal/loader/loader.go | 13 +++ 3 files changed, 82 insertions(+), 65 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 0937540433..40d07199fa 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -2310,13 +2310,14 @@ func (ctxt *Link) address() []*sym.Segment { Segdwarf.Length = va - Segdwarf.Vaddr } + ldr := ctxt.loader 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 + rodata = ldr.SymSect(ldr.LookupOrCreateSym("runtime.rodata", 0)) + itablink = ldr.SymSect(ldr.LookupOrCreateSym("runtime.itablink", 0)) + symtab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.symtab", 0)) + pclntab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0)) + types = ldr.SymSect(ldr.LookupOrCreateSym("runtime.types", 0)) ) lasttext := text // Could be multiple .text sections @@ -2326,38 +2327,41 @@ func (ctxt *Link) address() []*sym.Segment { } } - for _, s := range ctxt.datap { - if s.Sect != nil { - s.Value += int64(s.Sect.Vaddr) + for _, s := range ctxt.datap2 { + if sect := ldr.SymSect(s); sect != nil { + ldr.AddToSymValue(s, int64(sect.Vaddr)) } - for sub := s.Sub; sub != nil; sub = sub.Sub { - sub.Value += s.Value + v := ldr.SymValue(s) + for sub := ldr.SubSym(s); sub != 0; sub = ldr.SubSym(sub) { + ldr.AddToSymValue(sub, v) } } - for _, si := range dwarfp { + for _, si := range dwarfp2 { for _, s := range si.syms { - if s.Sect != nil { - s.Value += int64(s.Sect.Vaddr) + if sect := ldr.SymSect(s); sect != nil { + ldr.AddToSymValue(s, int64(sect.Vaddr)) } - if s.Sub != nil { - panic(fmt.Sprintf("unexpected sub-sym for %s %s", s.Name, s.Type.String())) + sub := ldr.SubSym(s) + if sub != 0 { + panic(fmt.Sprintf("unexpected sub-sym for %s %s", ldr.SymName(s), ldr.SymType(s).String())) } - for sub := s.Sub; sub != nil; sub = sub.Sub { - sub.Value += s.Value + v := ldr.SymValue(s) + for ; sub != 0; sub = ldr.SubSym(sub) { + ldr.AddToSymValue(s, v) } } } 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) + s := ldr.LookupOrCreateSym("go.link.abihashbytes", 0) + sect := ldr.SymSect(ldr.LookupOrCreateSym(".note.go.abihash", 0)) + ldr.SetSymSect(s, sect) + ldr.SetSymValue(s, int64(sect.Vaddr+16)) } - ctxt.xdefine("runtime.text", sym.STEXT, int64(text.Vaddr)) - ctxt.xdefine("runtime.etext", sym.STEXT, int64(lasttext.Vaddr+lasttext.Length)) + ctxt.xdefine2("runtime.text", sym.STEXT, int64(text.Vaddr)) + ctxt.xdefine2("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 @@ -2370,58 +2374,58 @@ func (ctxt *Link) address() []*sym.Segment { 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)) + ctxt.xdefine2(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)) + ctxt.xdefine2("runtime.rodata", sym.SRODATA, int64(rodata.Vaddr)) + ctxt.xdefine2("runtime.erodata", sym.SRODATA, int64(rodata.Vaddr+rodata.Length)) + ctxt.xdefine2("runtime.types", sym.SRODATA, int64(types.Vaddr)) + ctxt.xdefine2("runtime.etypes", sym.SRODATA, int64(types.Vaddr+types.Length)) + ctxt.xdefine2("runtime.itablink", sym.SRODATA, int64(itablink.Vaddr)) + ctxt.xdefine2("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 := ldr.Lookup("runtime.gcdata", 0) + ldr.SetAttrLocal(s, true) + ctxt.xdefine2("runtime.egcdata", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s)) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcdata", 0), ldr.SymSect(s)) - 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 + s = ldr.LookupOrCreateSym("runtime.gcbss", 0) + ldr.SetAttrLocal(s, true) + ctxt.xdefine2("runtime.egcbss", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s)) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcbss", 0), ldr.SymSect(s)) - ctxt.xdefine("runtime.symtab", sym.SRODATA, int64(symtab.Vaddr)) - ctxt.xdefine("runtime.esymtab", sym.SRODATA, int64(symtab.Vaddr+symtab.Length)) - ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr)) - ctxt.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)) + ctxt.xdefine2("runtime.symtab", sym.SRODATA, int64(symtab.Vaddr)) + ctxt.xdefine2("runtime.esymtab", sym.SRODATA, int64(symtab.Vaddr+symtab.Length)) + ctxt.xdefine2("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr)) + ctxt.xdefine2("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length)) + ctxt.xdefine2("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr)) + ctxt.xdefine2("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length)) + ctxt.xdefine2("runtime.bss", sym.SBSS, int64(bss.Vaddr)) + ctxt.xdefine2("runtime.ebss", sym.SBSS, int64(bss.Vaddr+bss.Length)) + ctxt.xdefine2("runtime.data", sym.SDATA, int64(data.Vaddr)) + ctxt.xdefine2("runtime.edata", sym.SDATA, int64(data.Vaddr+data.Length)) + ctxt.xdefine2("runtime.noptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr)) + ctxt.xdefine2("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) + ctxt.xdefine2("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 + etext := ldr.Lookup("runtime.etext", 0) + edata := ldr.Lookup("runtime.edata", 0) + end := ldr.Lookup("runtime.end", 0) + ldr.SetSymExtname(etext, "runtime.etext") + ldr.SetSymExtname(edata, "runtime.edata") + ldr.SetSymExtname(end, "runtime.end") + ctxt.xdefine2("_etext", ldr.SymType(etext), ldr.SymValue(etext)) + ctxt.xdefine2("_edata", ldr.SymType(edata), ldr.SymValue(edata)) + ctxt.xdefine2("_end", ldr.SymType(end), ldr.SymValue(end)) + ldr.SetSymSect(ldr.Lookup("_etext", 0), ldr.SymSect(etext)) + ldr.SetSymSect(ldr.Lookup("_edata", 0), ldr.SymSect(edata)) + ldr.SetSymSect(ldr.Lookup("_end", 0), ldr.SymSect(end)) } return order diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index bbd6f1c663..14856c160f 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -300,10 +300,10 @@ func Main(arch *sys.Arch, theArch Arch) { symGroupType := ctxt.symtab() bench.Start("dodata") ctxt.dodata2(symGroupType) - bench.Start("loadlibfull") - ctxt.loadlibfull(symGroupType) // XXX do it here for now bench.Start("address") order := ctxt.address() + bench.Start("loadlibfull") + ctxt.loadlibfull(symGroupType) // XXX do it here for now bench.Start("dwarfcompress") dwarfcompress(ctxt) bench.Start("layout") diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 1eebb0f2d1..1af771e970 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -756,6 +756,14 @@ func (l *Loader) SetAttrLocal(i Sym, v bool) { } } +// SymAddr checks that a symbol is reachable, and returns its value. +func (l *Loader) SymAddr(i Sym) int64 { + if !l.AttrReachable(i) { + panic("unreachable symbol in symaddr") + } + return l.values[i] +} + // AttrNotInSymbolTable returns true for symbols that should not be // added to the symbol table of the final generated load module. func (l *Loader) AttrNotInSymbolTable(i Sym) bool { @@ -1050,6 +1058,11 @@ func (l *Loader) SetSymValue(i Sym, val int64) { l.values[i] = val } +// AddToSymValue adds to the value of the i-th symbol. i is the global index. +func (l *Loader) AddToSymValue(i Sym, val int64) { + l.values[i] += val +} + // Returns the symbol content of the i-th symbol. i is global index. func (l *Loader) Data(i Sym) []byte { if l.IsExternal(i) { From 3e8975172f0cf72be89d5bbeb46dd3084faa9b06 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sat, 25 Apr 2020 14:17:10 -0400 Subject: [PATCH 33/42] [dev.link] cmd/link: convert dwarfcompress to using the loader Change-Id: I34f806b54e8e0985a30ef38ea4324352aabfc845 Reviewed-on: https://go-review.googlesource.com/c/go/+/229995 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/data.go | 648 ++++++++++++++--------------- src/cmd/link/internal/ld/data2.go | 443 ++++++++++++++++++++ src/cmd/link/internal/ld/dwarf2.go | 59 +-- src/cmd/link/internal/ld/errors.go | 50 ++- src/cmd/link/internal/ld/lib.go | 2 +- src/cmd/link/internal/ld/main.go | 4 +- 6 files changed, 836 insertions(+), 370 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 40d07199fa..1fd048e10d 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -120,139 +120,139 @@ func trampoline(ctxt *Link, s loader.Sym) { } -// 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 +// relocsym resolve relocations in "s", updating the symbol's content +// in "P". +// The main loop walks through the list of relocations attached to "s" +// and resolves them where applicable. Relocations are often +// architecture-specific, requiring calls into the 'archreloc' and/or +// 'archrelocvariant' functions for the architecture. When external +// linking is in effect, it may not be possible to completely resolve +// the address/offset for a symbol, in which case the goal is to lay +// the groundwork for turning a given relocation into an external reloc +// (to be applied by the external linker). For more on how relocations +// work in general, see // // "Linkers and Loaders", by John R. Levine (Morgan Kaufmann, 1999), ch. 7 // // This is a performance-critical function for the linker; be careful // to avoid introducing unnecessary allocations in the main loop. -// 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 { +func relocsym(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *ArchSyms, s loader.Sym, P []byte) { + relocs := ldr.Relocs(s) + if relocs.Count() == 0 { return } - if target.IsWasm() && s.Attr.ReadOnly() { - // The symbol's content is backed by read-only memory. - // Copy it to writable memory to apply relocations. - // Only need to do this on Wasm. On other platforms we - // apply relocations to the output buffer, which is - // always writeable. - s.P = append([]byte(nil), s.P...) - // No need to unset AttrReadOnly because it will not be used. - } - 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 + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At2(ri) + off := r.Off() + siz := int32(r.Siz()) + rs := r.Sym() + if rs != 0 { + rs = ldr.ResolveABIAlias(rs) } - r.Done = true - off := r.Off - siz := int32(r.Siz) - if off < 0 || off+siz > int32(len(s.P)) { + rt := r.Type() + if off < 0 || off+siz > int32(len(P)) { rname := "" - if r.Sym != nil { - rname = r.Sym.Name + if rs != 0 { + rname = ldr.SymName(rs) } - Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(s.P)) + err.Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(P)) continue } - if r.Sym != nil && ((r.Sym.Type == sym.Sxxx && !r.Sym.Attr.VisibilityHidden()) || r.Sym.Type == sym.SXREF) { + var rst sym.SymKind + if rs != 0 { + rst = ldr.SymType(rs) + } + + if rs != 0 && ((rst == sym.Sxxx && !ldr.AttrVisibilityHidden(rs)) || rst == sym.SXREF) { // When putting the runtime but not main into a shared library // these symbols are undefined and that's OK. if target.IsShared() || target.IsPlugin() { - if 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.") { + if ldr.SymName(rs) == "main.main" || (!target.IsPlugin() && ldr.SymName(rs) == "main..inittask") { + sb := ldr.MakeSymbolUpdater(rs) + sb.SetType(sym.SDYNIMPORT) + } else if strings.HasPrefix(ldr.SymName(rs), "go.info.") { // Skip go.info symbols. They are only needed to communicate // DWARF info between the compiler and linker. continue } } else { - err.errorUnresolved(s, r) + err.errorUnresolved(ldr, s, rs) continue } } - if r.Type >= objabi.ElfRelocOffset { + if rt >= objabi.ElfRelocOffset { continue } - if r.Siz == 0 { // informational relocation - no work to do + if 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 !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 !target.IsSolaris() && !target.IsDarwin() && !target.IsAIX() && rs != 0 && rst == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !ldr.AttrSubSymbol(rs) { + if !(target.IsPPC64() && target.IsExternal() && ldr.SymName(rs) == ".TOC.") { + err.Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", ldr.SymName(rs), rst, rst, rt, sym.RelocName(target.Arch, rt)) } } - if 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 rs != 0 && rst != sym.STLSBSS && rt != objabi.R_WEAKADDROFF && rt != objabi.R_METHODOFF && !ldr.AttrReachable(rs) { + err.Errorf(s, "unreachable sym in relocation: %s", ldr.SymName(rs)) } if target.IsExternal() { - r.InitExt() + panic("external linking not implemented") + //r.InitExt() } // TODO(mundaym): remove this special case - see issue 14218. - if target.IsS390X() { - 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 - } - } + //if target.IsS390X() { + // 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 { + switch rt { 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(target.Arch.ByteOrder.Uint16(s.P[off:])) - case 4: - o = int64(target.Arch.ByteOrder.Uint32(s.P[off:])) - case 8: - o = int64(target.Arch.ByteOrder.Uint64(s.P[off:])) - } - 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(target.Arch, r.Type)) - } + panic("not implemented") + //switch siz { + //default: + // err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs)) + //case 1: + // o = int64(P[off]) + //case 2: + // o = int64(target.Arch.ByteOrder.Uint16(P[off:])) + //case 4: + // o = int64(target.Arch.ByteOrder.Uint32(P[off:])) + //case 8: + // o = int64(target.Arch.ByteOrder.Uint64(P[off:])) + //} + //if out, ok := thearch.Archreloc(ldr, target, syms, &r, s, o); ok { + // o = out + //} else { + // err.Errorf(s, "unknown reloc to %v: %d (%s)", ldr.SymName(rs), rt, sym.RelocName(target.Arch, rt)) + //} case objabi.R_TLS_LE: - if target.IsExternal() && target.IsElf() { - r.Done = false - if r.Sym == nil { - r.Sym = syms.Tlsg - } - r.Xsym = r.Sym - r.Xadd = r.Add - o = 0 - if !target.IsAMD64() { - o = r.Add - } - break - } + //if target.IsExternal() && target.IsElf() { + // r.Done = false + // if r.Sym == nil { + // r.Sym = syms.Tlsg + // } + // r.Xsym = r.Sym + // r.Xadd = r.Add + // o = 0 + // if !target.IsAMD64() { + // o = r.Add + // } + // break + //} if target.IsElf() && target.IsARM() { // On ELF ARM, the thread pointer is 8 bytes before @@ -262,96 +262,94 @@ func relocsym(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arch // 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 + o = 8 + ldr.SymValue(rs) } else if target.IsElf() || target.IsPlan9() || target.IsDarwin() { - o = int64(syms.Tlsoffset) + r.Add + o = int64(syms.Tlsoffset) + r.Add() } else if target.IsWindows() { - o = r.Add + o = r.Add() } else { log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType) } case objabi.R_TLS_IE: - if target.IsExternal() && target.IsElf() { - r.Done = false - if r.Sym == nil { - r.Sym = syms.Tlsg - } - r.Xsym = r.Sym - r.Xadd = r.Add - o = 0 - if !target.IsAMD64() { - o = r.Add - } - break - } - if target.IsPIE() && target.IsElf() { - // We are linking the final executable, so we - // can optimize any TLS IE relocation to LE. - if thearch.TLSIEtoLE == nil { - log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) - } - thearch.TLSIEtoLE(s, int(off), int(r.Siz)) - 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) - } + panic("not implemented") + //if target.IsExternal() && target.IsElf() { + // r.Done = false + // if r.Sym == nil { + // r.Sym = syms.Tlsg + // } + // r.Xsym = r.Sym + // r.Xadd = r.Add + // o = 0 + // if !target.IsAMD64() { + // o = r.Add + // } + // break + //} + //if target.IsPIE() && target.IsElf() { + // // We are linking the final executable, so we + // // can optimize any TLS IE relocation to LE. + // if thearch.TLSIEtoLE == nil { + // log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) + // } + // thearch.TLSIEtoLE(ldr, s, int(off), int(siz)) + // o = int64(syms.Tlsoffset) + //} else { + // log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s)) + //} case objabi.R_ADDR: - if target.IsExternal() && 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 target.IsElf() { - if target.IsAMD64() { - o = 0 - } - } else if target.IsDarwin() { - if rs.Type != sym.SHOSTOBJ { - o += Symaddr(rs) - } - } else if target.IsWindows() { - // nothing to do - } else if target.IsAIX() { - o = Symaddr(r.Sym) + r.Add - } else { - Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType) - } - - break - } + //if target.IsExternal() && 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 target.IsElf() { + // if target.IsAMD64() { + // o = 0 + // } + // } else if target.IsDarwin() { + // if rs.Type != sym.SHOSTOBJ { + // o += Symaddr(rs) + // } + // } else if target.IsWindows() { + // // nothing to do + // } else if target.IsAIX() { + // o = Symaddr(r.Sym) + r.Add + // } else { + // Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType) + // } + // + // break + //} // On AIX, a second relocation must be done by the loader, // as section addresses can change once loaded. // The "default" symbol address is still needed by the loader so // the current relocation can't be skipped. - if target.IsAIX() && r.Sym.Type != sym.SDYNIMPORT { + if target.IsAIX() && rst != sym.SDYNIMPORT { // It's not possible to make a loader relocation in a // symbol which is not inside .data section. // FIXME: It should be forbidden to have R_ADDR from a // symbol which isn't in .data. However, as .text has the // same address once loaded, this is possible. - if s.Sect.Seg == &Segdata { - Xcoffadddynrel(target, ldr, s, r) + if ldr.SymSect(s).Seg == &Segdata { + //Xcoffadddynrel(target, ldr, err, s, &r) // XXX } } - o = Symaddr(r.Sym) + r.Add + o = ldr.SymValue(rs) + r.Add() // On amd64, 4-byte offsets will be sign-extended, so it is impossible to // access more than 2GB of static data; fail at link time is better than @@ -359,217 +357,198 @@ func relocsym(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arch // Instead of special casing only amd64, we treat this as an error on all // 64-bit architectures so as to be future-proof. if int32(o) < 0 && target.Arch.PtrSize > 4 && siz == 4 { - 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) + err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", ldr.SymName(rs), uint64(o), ldr.SymValue(rs), r.Add()) errorexit() } case objabi.R_DWARFSECREF: - if r.Sym.Sect == nil { - Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name) + if ldr.SymSect(rs) == nil { + err.Errorf(s, "missing DWARF section for relocation target %s", ldr.SymName(rs)) } - if target.IsExternal() { - 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 target.IsDarwin() { - 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 !target.IsWindows() { - r.Type = objabi.R_ADDR - } - - r.Xsym = r.Sym.Sect.Sym - r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) - - o = r.Xadd - if target.IsElf() && target.IsAMD64() { - o = 0 - } - break - } - o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr) - case objabi.R_WEAKADDROFF: - if !r.Sym.Attr.Reachable() { + //if target.IsExternal() { + // 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 target.IsDarwin() { + // 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 !target.IsWindows() { + // r.Type = objabi.R_ADDR + // } + // + // r.Xsym = r.Sym.Sect.Sym + // r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + // + // o = r.Xadd + // if target.IsElf() && target.IsAMD64() { + // o = 0 + // } + // break + //} + o = ldr.SymValue(rs) + r.Add() - int64(ldr.SymSect(rs).Vaddr) + case objabi.R_WEAKADDROFF, objabi.R_METHODOFF: + if !ldr.AttrReachable(rs) { 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 + if ldr.SymSect(rs).Name == ".text" { + o = ldr.SymValue(rs) - int64(Segtext.Sections[0].Vaddr) + r.Add() } else { - o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add + o = ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + r.Add() } case objabi.R_ADDRCUOFF: // debug_range and debug_loc elements use this relocation type to get an // offset from the start of the compile unit. - o = Symaddr(r.Sym) + r.Add - Symaddr(ldr.Syms[r.Sym.Unit.Textp2[0]]) + o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(loader.Sym(ldr.SymUnit(rs).Textp2[0])) - // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. + // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call. case objabi.R_GOTPCREL: - if target.IsDynlinkingGo() && target.IsDarwin() && 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 - } + //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 + // r.Xsym = r.Sym + // + // o = r.Xadd + // o += int64(r.Siz) + // break + //} fallthrough case objabi.R_CALL, objabi.R_PCREL: - if target.IsExternal() && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT { - // pass through to the external linker. - r.Done = false - r.Xadd = 0 - if target.IsElf() { - r.Xadd -= int64(r.Siz) - } - r.Xsym = r.Sym - o = 0 - break - } - 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. - 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 target.IsElf() { - if target.IsAMD64() { - o = 0 - } - } else if target.IsDarwin() { - if r.Type == objabi.R_CALL { - if target.IsExternal() && rs.Type == sym.SDYNIMPORT { - if target.IsAMD64() { - // AMD64 dynamic relocations are relative to the end of the relocation. - o += int64(r.Siz) - } - } 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 { - o += int64(r.Siz) - } - } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL - // PE/COFF's PC32 relocation uses the address after the relocated - // bytes as the base. Compensate by skewing the addend. - o += int64(r.Siz) - } else { - Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType) - } - - break - } + //if target.IsExternal() && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT { + // // pass through to the external linker. + // r.Done = false + // r.Xadd = 0 + // if target.IsElf() { + // r.Xadd -= int64(r.Siz) + // } + // r.Xsym = r.Sym + // o = 0 + // break + //} + //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. + // 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 target.IsElf() { + // if target.IsAMD64() { + // o = 0 + // } + // } else if target.IsDarwin() { + // if r.Type == objabi.R_CALL { + // if target.IsExternal() && rs.Type == sym.SDYNIMPORT { + // if target.IsAMD64() { + // // AMD64 dynamic relocations are relative to the end of the relocation. + // o += int64(r.Siz) + // } + // } 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 { + // o += int64(r.Siz) + // } + // } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL + // // PE/COFF's PC32 relocation uses the address after the relocated + // // bytes as the base. Compensate by skewing the addend. + // o += int64(r.Siz) + // } else { + // Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType) + // } + // + // break + //} o = 0 - if r.Sym != nil { - o += Symaddr(r.Sym) + if rs != 0 { + o = ldr.SymValue(rs) } - o += r.Add - (s.Value + int64(r.Off) + int64(r.Siz)) + o += r.Add() - (ldr.SymValue(s) + int64(off) + int64(siz)) case objabi.R_SIZE: - o = r.Sym.Size + r.Add + o = ldr.SymSize(rs) + r.Add() case objabi.R_XCOFFREF: if !target.IsAIX() { - Errorf(s, "find XCOFF R_REF on non-XCOFF files") + err.Errorf(s, "find XCOFF R_REF on non-XCOFF files") } if !target.IsExternal() { - Errorf(s, "find XCOFF R_REF with internal linking") + err.Errorf(s, "find XCOFF R_REF with internal linking") } - r.Xsym = r.Sym - r.Xadd = r.Add - r.Done = false + //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 + // We don't renumber files in dwarf.go:writelines anymore. + continue } - if target.IsPPC64() || target.IsS390X() { - r.InitExt() - if r.Variant != sym.RV_NONE { - o = thearch.Archrelocvariant(target, syms, r, s, o) - } - } + //if target.IsPPC64() || target.IsS390X() { + // r.InitExt() + // if r.Variant != sym.RV_NONE { + // o = thearch.Archrelocvariant(ldr, target, syms, &r, s, o) + // } + //} - if false { - nam := "" - var addr int64 - if r.Sym != nil { - nam = r.Sym.Name - addr = Symaddr(r.Sym) - } - xnam := "" - 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(target.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. + err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs)) case 1: - s.P[off] = byte(int8(o)) + 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) + err.Errorf(s, "relocation address for %s is too big: %#x", ldr.SymName(rs), o) } - i16 := int16(o) - target.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16)) + target.Arch.ByteOrder.PutUint16(P[off:], uint16(o)) case 4: - if r.Type == objabi.R_PCREL || r.Type == objabi.R_CALL { + if rt == objabi.R_PCREL || rt == objabi.R_CALL { if o != int64(int32(o)) { - Errorf(s, "pc-relative relocation address for %s is too big: %#x", r.Sym.Name, o) + err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o) } } else { if o != int64(int32(o)) && o != int64(uint32(o)) { - Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", r.Sym.Name, uint64(o)) + err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o)) } } - - fl := int32(o) - target.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl)) + target.Arch.ByteOrder.PutUint32(P[off:], uint32(o)) case 8: - target.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o)) + target.Arch.ByteOrder.PutUint64(P[off:], uint64(o)) } } } @@ -584,21 +563,21 @@ func (ctxt *Link) reloc() { go func() { if !ctxt.IsWasm() { // On Wasm, text relocations are applied in Asmb2. for _, s := range ctxt.Textp { - relocsym(target, ldr, reporter, syms, s) + relocsym2(target, ldr, reporter, syms, s) } } wg.Done() }() go func() { for _, s := range ctxt.datap { - relocsym(target, ldr, reporter, syms, s) + relocsym2(target, ldr, reporter, syms, s) } wg.Done() }() go func() { for _, si := range dwarfp { for _, s := range si.syms { - relocsym(target, ldr, reporter, syms, s) + relocsym2(target, ldr, reporter, syms, s) } } wg.Done() @@ -2478,10 +2457,11 @@ func (ctxt *Link) AddTramp(s *loader.SymbolBuilder) { // 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 { +func compressSyms(ctxt *Link, syms []loader.Sym) []byte { + ldr := ctxt.loader var total int64 for _, sym := range syms { - total += sym.Size + total += ldr.SymSize(sym) } var buf bytes.Buffer @@ -2501,23 +2481,22 @@ func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte { 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 + // Symbol data 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 { - relocbuf = append(relocbuf[:0], s.P...) - s.P = relocbuf + P := ldr.Data(s) + relocs := ldr.Relocs(s) + if relocs.Count() != 0 { + relocbuf = append(relocbuf[:0], P...) + P = relocbuf } - relocsym(target, ldr, reporter, archSyms, s) - if _, err := z.Write(s.P); err != nil { + relocsym(target, ldr, reporter, archSyms, s, P) + if _, err := z.Write(P); err != nil { log.Fatalf("compression failed: %s", err) } - for i := s.Size - int64(len(s.P)); i > 0; { + for i := ldr.SymSize(s) - int64(len(P)); i > 0; { b := zeros[:] if i < int64(len(b)) { b = b[:i] @@ -2528,15 +2507,6 @@ func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte { } 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 - for i := range s.R { - s.R[i].Done = false - } - } } if err := z.Close(); err != nil { log.Fatalf("compression failed: %s", err) diff --git a/src/cmd/link/internal/ld/data2.go b/src/cmd/link/internal/ld/data2.go index 033fe75068..79badd5fdd 100644 --- a/src/cmd/link/internal/ld/data2.go +++ b/src/cmd/link/internal/ld/data2.go @@ -5,7 +5,11 @@ package ld import ( + "cmd/internal/objabi" + "cmd/link/internal/loader" "cmd/link/internal/sym" + "fmt" + "log" "strings" ) @@ -49,3 +53,442 @@ func symalign(s *sym.Symbol) int32 { s.Align = align return align } + +func relocsym2(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *ArchSyms, s *sym.Symbol) { + if len(s.R) == 0 { + return + } + if target.IsWasm() && s.Attr.ReadOnly() { + // The symbol's content is backed by read-only memory. + // Copy it to writable memory to apply relocations. + // Only need to do this on Wasm. On other platforms we + // apply relocations to the output buffer, which is + // always writeable. + s.P = append([]byte(nil), s.P...) + // No need to unset AttrReadOnly because it will not be used. + } + 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 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 + // DWARF info between the compiler and linker. + continue + } + } else { + err.errorUnresolved2(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 !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 target.IsExternal() { + r.InitExt() + } + + // TODO(mundaym): remove this special case - see issue 14218. + if target.IsS390X() { + 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(target.Arch.ByteOrder.Uint16(s.P[off:])) + case 4: + o = int64(target.Arch.ByteOrder.Uint32(s.P[off:])) + case 8: + o = int64(target.Arch.ByteOrder.Uint64(s.P[off:])) + } + 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(target.Arch, r.Type)) + } + case objabi.R_TLS_LE: + if target.IsExternal() && target.IsElf() { + r.Done = false + if r.Sym == nil { + r.Sym = syms.Tlsg + } + r.Xsym = r.Sym + r.Xadd = r.Add + o = 0 + if !target.IsAMD64() { + o = r.Add + } + break + } + + if target.IsElf() && target.IsARM() { + // On ELF ARM, the thread pointer is 8 bytes before + // the start of the thread-local data block, so add 8 + // to the actual TLS offset (r->sym->value). + // This 8 seems to be a fundamental constant of + // ELF on ARM (or maybe Glibc on ARM); it is not + // related to the fact that our own TLS storage happens + // to take up 8 bytes. + o = 8 + r.Sym.Value + } else if target.IsElf() || target.IsPlan9() || target.IsDarwin() { + o = int64(syms.Tlsoffset) + r.Add + } else if target.IsWindows() { + o = r.Add + } else { + log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType) + } + case objabi.R_TLS_IE: + if target.IsExternal() && target.IsElf() { + r.Done = false + if r.Sym == nil { + r.Sym = syms.Tlsg + } + r.Xsym = r.Sym + r.Xadd = r.Add + o = 0 + if !target.IsAMD64() { + o = r.Add + } + break + } + if target.IsPIE() && target.IsElf() { + // We are linking the final executable, so we + // can optimize any TLS IE relocation to LE. + if thearch.TLSIEtoLE == nil { + log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) + } + thearch.TLSIEtoLE(s, int(off), int(r.Siz)) + 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 target.IsExternal() && 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 target.IsElf() { + if target.IsAMD64() { + o = 0 + } + } else if target.IsDarwin() { + if rs.Type != sym.SHOSTOBJ { + o += Symaddr(rs) + } + } else if target.IsWindows() { + // nothing to do + } else if target.IsAIX() { + o = Symaddr(r.Sym) + r.Add + } else { + Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType) + } + + break + } + + // On AIX, a second relocation must be done by the loader, + // as section addresses can change once loaded. + // The "default" symbol address is still needed by the loader so + // the current relocation can't be skipped. + if target.IsAIX() && 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(target, ldr, 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 && 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() + } + case objabi.R_DWARFSECREF: + if r.Sym.Sect == nil { + Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name) + } + + if target.IsExternal() { + 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 target.IsDarwin() { + 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 !target.IsWindows() { + r.Type = objabi.R_ADDR + } + + r.Xsym = r.Sym.Sect.Sym + r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + + o = r.Xadd + if target.IsElf() && target.IsAMD64() { + 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(ldr.Syms[r.Sym.Unit.Textp2[0]]) + + // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. + case objabi.R_GOTPCREL: + 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 + r.Xsym = r.Sym + + o = r.Xadd + o += int64(r.Siz) + break + } + fallthrough + case objabi.R_CALL, objabi.R_PCREL: + if target.IsExternal() && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT { + // pass through to the external linker. + r.Done = false + r.Xadd = 0 + if target.IsElf() { + r.Xadd -= int64(r.Siz) + } + r.Xsym = r.Sym + o = 0 + break + } + 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. + 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 target.IsElf() { + if target.IsAMD64() { + o = 0 + } + } else if target.IsDarwin() { + if r.Type == objabi.R_CALL { + if target.IsExternal() && rs.Type == sym.SDYNIMPORT { + if target.IsAMD64() { + // AMD64 dynamic relocations are relative to the end of the relocation. + o += int64(r.Siz) + } + } 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 { + o += int64(r.Siz) + } + } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL + // PE/COFF's PC32 relocation uses the address after the relocated + // bytes as the base. Compensate by skewing the addend. + o += int64(r.Siz) + } else { + Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.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 !target.IsAIX() { + Errorf(s, "find XCOFF R_REF on non-XCOFF files") + } + if !target.IsExternal() { + 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 target.IsPPC64() || target.IsS390X() { + r.InitExt() + if r.Variant != sym.RV_NONE { + o = thearch.Archrelocvariant(target, syms, r, s, o) + } + } + + if false { + nam := "" + var addr int64 + if r.Sym != nil { + nam = r.Sym.Name + addr = Symaddr(r.Sym) + } + xnam := "" + 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(target.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) + 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)) { + 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) + target.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl)) + case 8: + target.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o)) + } + } +} + diff --git a/src/cmd/link/internal/ld/dwarf2.go b/src/cmd/link/internal/ld/dwarf2.go index 4bd52f5105..336122800b 100644 --- a/src/cmd/link/internal/ld/dwarf2.go +++ b/src/cmd/link/internal/ld/dwarf2.go @@ -101,20 +101,20 @@ func dwarfcompress(ctxt *Link) { type compressedSect struct { index int compressed []byte - syms []*sym.Symbol + syms []loader.Sym } - supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin - if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal { + supported := ctxt.IsELF || ctxt.IsWindows() || ctxt.IsDarwin() + if !ctxt.compressDWARF || !supported || ctxt.IsExternal() { return } var compressedCount int resChannel := make(chan compressedSect) - for i := range dwarfp { - go func(resIndex int, syms []*sym.Symbol) { + for i := range dwarfp2 { + go func(resIndex int, syms []loader.Sym) { resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms} - }(compressedCount, dwarfp[i].syms) + }(compressedCount, dwarfp2[i].syms) compressedCount++ } res := make([]compressedSect, compressedCount) @@ -123,46 +123,55 @@ func dwarfcompress(ctxt *Link) { res[r.index] = r } - var newDwarfp []dwarfSecInfo2 + ldr := ctxt.loader + var newDwarfp []dwarfSecInfo Segdwarf.Sections = Segdwarf.Sections[:0] for _, z := range res { s := z.syms[0] if z.compressed == nil { // Compression didn't help. - ds := dwarfSecInfo2{syms: z.syms} + ds := dwarfSecInfo{syms: z.syms} newDwarfp = append(newDwarfp, ds) - Segdwarf.Sections = append(Segdwarf.Sections, s.Sect) + Segdwarf.Sections = append(Segdwarf.Sections, ldr.SymSect(s)) } else { - compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):] + compressedSegName := ".zdebug_" + ldr.SymSect(s).Name[len(".debug_"):] sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04) + sect.Align = 1 sect.Length = uint64(len(z.compressed)) - newSym := ctxt.Syms.Lookup(compressedSegName, 0) - newSym.P = z.compressed - newSym.Size = int64(len(z.compressed)) - newSym.Sect = sect - ds := dwarfSecInfo2{syms: []*sym.Symbol{newSym}} + newSym := ldr.CreateSymForUpdate(compressedSegName, 0) + newSym.SetReachable(true) + newSym.SetData(z.compressed) + newSym.SetSize(int64(len(z.compressed))) + ldr.SetSymSect(newSym.Sym(), sect) + ds := dwarfSecInfo{syms: []loader.Sym{newSym.Sym()}} newDwarfp = append(newDwarfp, ds) + + // compressed symbols are no longer needed. + for _, s := range z.syms { + ldr.SetAttrReachable(s, false) + } } } - dwarfp = newDwarfp + dwarfp2 = newDwarfp // Re-compute the locations of the compressed DWARF symbols // and sections, since the layout of these within the file is // based on Section.Vaddr and Symbol.Value. pos := Segdwarf.Vaddr var prevSect *sym.Section - for _, si := range dwarfp { + for _, si := range dwarfp2 { for _, s := range si.syms { - s.Value = int64(pos) - if s.Sect != prevSect { - s.Sect.Vaddr = uint64(s.Value) - prevSect = s.Sect + ldr.SetSymValue(s, int64(pos)) + sect := ldr.SymSect(s) + if sect != prevSect { + sect.Vaddr = uint64(pos) + prevSect = sect } - if s.Sub != nil { - log.Fatalf("%s: unexpected sub-symbols", s) + if ldr.SubSym(s) != 0 { + log.Fatalf("%s: unexpected sub-symbols", ldr.SymName(s)) } - pos += uint64(s.Size) - if ctxt.HeadType == objabi.Hwindows { + pos += uint64(ldr.SymSize(s)) + if ctxt.IsWindows() { pos = uint64(Rnd(int64(pos), PEFILEALIGN)) } } diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go index 61289b4798..c2c191d058 100644 --- a/src/cmd/link/internal/ld/errors.go +++ b/src/cmd/link/internal/ld/errors.go @@ -11,6 +11,11 @@ import ( ) type unresolvedSymKey struct { + from loader.Sym // Symbol that referenced unresolved "to" + to loader.Sym // Unresolved symbol referenced by "from" +} + +type unresolvedSymKey2 struct { from *sym.Symbol // Symbol that referenced unresolved "to" to *sym.Symbol // Unresolved symbol referenced by "from" } @@ -23,20 +28,59 @@ type ErrorReporter struct { loader.ErrorReporter unresOnce sync.Once unresSyms map[unresolvedSymKey]bool + unresSyms2 map[unresolvedSymKey2]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) { +// errorUnresolved prints unresolved symbol error for rs that is referenced from s. +func (reporter *ErrorReporter) errorUnresolved(ldr *loader.Loader, s, rs loader.Sym) { reporter.unresOnce.Do(func() { reporter.unresSyms = make(map[unresolvedSymKey]bool) }) - k := unresolvedSymKey{from: s, to: r.Sym} + k := unresolvedSymKey{from: s, to: rs} reporter.unresMutex.Lock() defer reporter.unresMutex.Unlock() if !reporter.unresSyms[k] { reporter.unresSyms[k] = true + name := ldr.SymName(rs) + + // Try to find symbol under another ABI. + var reqABI, haveABI obj.ABI + haveABI = ^obj.ABI(0) + reqABI, ok := sym.VersionToABI(ldr.SymVersion(rs)) + if ok { + for abi := obj.ABI(0); abi < obj.ABICount; abi++ { + v := sym.ABIToVersion(abi) + if v == -1 { + continue + } + if rs1 := ldr.Lookup(name, v); rs1 != 0 && ldr.SymType(rs1) != sym.Sxxx && ldr.SymType(rs1) != sym.SXREF { + haveABI = abi + } + } + } + + // Give a special error message for main symbol (see #24809). + if name == "main.main" { + reporter.Errorf(s, "function main is undeclared in the main package") + } else if haveABI != ^obj.ABI(0) { + reporter.Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", name, reqABI, haveABI) + } else { + reporter.Errorf(s, "relocation target %s not defined", name) + } + } +} + +// errorUnresolved2 prints unresolved symbol error for r.Sym that is referenced from s. +func (reporter *ErrorReporter) errorUnresolved2(s *sym.Symbol, r *sym.Reloc) { + reporter.unresOnce.Do(func() { reporter.unresSyms2 = make(map[unresolvedSymKey2]bool) }) + + k := unresolvedSymKey2{from: s, to: r.Sym} + reporter.unresMutex.Lock() + defer reporter.unresMutex.Unlock() + if !reporter.unresSyms2[k] { + reporter.unresSyms2[k] = true // Try to find symbol under another ABI. var reqABI, haveABI obj.ABI diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index a43aff22ee..7264f9383e 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2897,7 +2897,7 @@ func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind) { if t != sym.Sxxx { s := ctxt.loader.Syms[i] if s == nil { - panic(fmt.Sprintf("nil sym for symGroupType t=%s entry %d", t.String(), i)) + continue // in dwarfcompress we drop compressed DWARF symbols } s.Type = t } diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 14856c160f..6c5a18c359 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -302,12 +302,12 @@ func Main(arch *sys.Arch, theArch Arch) { ctxt.dodata2(symGroupType) bench.Start("address") order := ctxt.address() - bench.Start("loadlibfull") - ctxt.loadlibfull(symGroupType) // XXX do it here for now bench.Start("dwarfcompress") dwarfcompress(ctxt) bench.Start("layout") filesize := ctxt.layout(order) + bench.Start("loadlibfull") + ctxt.loadlibfull(symGroupType) // XXX do it here for now // Write out the output file. // It is split into two parts (Asmb and Asmb2). The first From dd34841de701d08632db27dabd9fac4aa104cb66 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Tue, 28 Apr 2020 09:53:29 -0400 Subject: [PATCH 34/42] [dev.link] cmd/link: write data sections to heap in Asmb on Wasm Make Wasm more like other architectures, writing data sections to heap in Asmb instead of Asmb2. Then we can remove the copy-on-write logic in applying relocations. Change-Id: I26d5315ea9fba032fe4bdb9b5c7fe483611c4373 Reviewed-on: https://go-review.googlesource.com/c/go/+/230465 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/link/internal/amd64/asm.go | 2 +- src/cmd/link/internal/arm/asm.go | 2 +- src/cmd/link/internal/arm64/asm.go | 2 +- src/cmd/link/internal/ld/data2.go | 9 ------ src/cmd/link/internal/ld/lib.go | 2 +- src/cmd/link/internal/ld/main.go | 2 +- src/cmd/link/internal/mips/asm.go | 2 +- src/cmd/link/internal/mips64/asm.go | 2 +- src/cmd/link/internal/ppc64/asm.go | 2 +- src/cmd/link/internal/riscv64/asm.go | 2 +- src/cmd/link/internal/s390x/asm.go | 2 +- src/cmd/link/internal/wasm/asm.go | 43 ++++++++++++++++++---------- src/cmd/link/internal/x86/asm.go | 2 +- 13 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index e2a66daf4f..95831d6dd0 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -684,7 +684,7 @@ func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load } } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index e1edc9e45d..7665cc59f0 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -682,7 +682,7 @@ func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load } } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index a0f06b6aa6..fbcbc9c8c3 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -830,7 +830,7 @@ func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load } } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } diff --git a/src/cmd/link/internal/ld/data2.go b/src/cmd/link/internal/ld/data2.go index 79badd5fdd..2aba40c231 100644 --- a/src/cmd/link/internal/ld/data2.go +++ b/src/cmd/link/internal/ld/data2.go @@ -58,15 +58,6 @@ func relocsym2(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arc if len(s.R) == 0 { return } - if target.IsWasm() && s.Attr.ReadOnly() { - // The symbol's content is backed by read-only memory. - // Copy it to writable memory to apply relocations. - // Only need to do this on Wasm. On other platforms we - // apply relocations to the output buffer, which is - // always writeable. - s.P = append([]byte(nil), s.P...) - // No need to unset AttrReadOnly because it will not be used. - } for ri := int32(0); ri < int32(len(s.R)); ri++ { r := &s.R[ri] if r.Done { diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 7264f9383e..57b9e8cb24 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -265,7 +265,7 @@ type Arch struct { // 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) + Asmb func(*Link, *loader.Loader) Asmb2 func(*Link) Elfreloc1 func(*Link, *sym.Reloc, int64) bool diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 6c5a18c359..48fea57cc9 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -325,7 +325,7 @@ func Main(arch *sys.Arch, theArch Arch) { // Asmb will redirect symbols to the output file mmap, and relocations // will be applied directly there. bench.Start("Asmb") - thearch.Asmb(ctxt) + thearch.Asmb(ctxt, ctxt.loader) bench.Start("reloc") ctxt.reloc() bench.Start("Asmb2") diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go index 21a57ccbb0..9281678704 100644 --- a/src/cmd/link/internal/mips/asm.go +++ b/src/cmd/link/internal/mips/asm.go @@ -164,7 +164,7 @@ func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym return -1 } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 0a2a3c11f3..615851b564 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -170,7 +170,7 @@ func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym return -1 } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index be124acaf8..6b6e1294d3 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -1082,7 +1082,7 @@ func ensureglinkresolver2(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuild return glink } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go index 51cc5980c8..5183de8d6b 100644 --- a/src/cmd/link/internal/riscv64/asm.go +++ b/src/cmd/link/internal/riscv64/asm.go @@ -98,7 +98,7 @@ func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym return -1 } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index 59c49b4537..ac634f99e3 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -485,7 +485,7 @@ func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load } } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go index 7f8742d008..1eb3291db6 100644 --- a/src/cmd/link/internal/wasm/asm.go +++ b/src/cmd/link/internal/wasm/asm.go @@ -92,7 +92,30 @@ func assignAddress(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, v return sect, n, va } -func asmb(ctxt *ld.Link) {} // dummy +type wasmDataSect struct { + sect *sym.Section + data []byte +} + +var dataSects []wasmDataSect + +func asmb(ctxt *ld.Link, ldr *loader.Loader) { + sections := []*sym.Section{ + ldr.SymSect(ldr.Lookup("runtime.rodata", 0)), + ldr.SymSect(ldr.Lookup("runtime.typelink", 0)), + ldr.SymSect(ldr.Lookup("runtime.itablink", 0)), + ldr.SymSect(ldr.Lookup("runtime.symtab", 0)), + ldr.SymSect(ldr.Lookup("runtime.pclntab", 0)), + ldr.SymSect(ldr.Lookup("runtime.noptrdata", 0)), + ldr.SymSect(ldr.Lookup("runtime.data", 0)), + } + + dataSects = make([]wasmDataSect, len(sections)) + for i, sect := range sections { + data := ld.DatblkBytes(ctxt, int64(sect.Vaddr), int64(sect.Length)) + dataSects[i] = wasmDataSect{sect, data} + } +} // asmb writes the final WebAssembly module binary. // Spec: https://webassembly.github.io/spec/core/binary/modules.html @@ -396,16 +419,6 @@ func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) { 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 @@ -420,9 +433,9 @@ func writeDataSec(ctxt *ld.Link) { 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) + for secIndex, ds := range dataSects { + data := ds.data + offset := int32(ds.sect.Vaddr) // skip leading zeroes for len(data) > 0 && data[0] == 0 { @@ -433,7 +446,7 @@ func writeDataSec(ctxt *ld.Link) { for len(data) > 0 { dataLen := int32(len(data)) var segmentEnd, zeroEnd int32 - if len(segments)+(len(sections)-secIndex) == maxNumSegments { + if len(segments)+(len(dataSects)-secIndex) == maxNumSegments { segmentEnd = dataLen zeroEnd = dataLen } else { diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 03c73671b8..069d2bc31c 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -564,7 +564,7 @@ func addgotsym2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load } } -func asmb(ctxt *ld.Link) { +func asmb(ctxt *ld.Link, _ *loader.Loader) { if ctxt.IsELF { ld.Asmbelfsetup() } From 0612e78f0f9b5381b8690891a91fd05b450a0a91 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Tue, 28 Apr 2020 13:45:35 -0400 Subject: [PATCH 35/42] [dev.link] cmd/link: tweak genasmsym to eliminate Gotype ref None of the users of genasmsym are doing anything with the Gotype field of sym.Symbol, so remove that param from the callback function. Change-Id: Ie902c4cdbcc6b68d353daf5ce21a99012161a946 Reviewed-on: https://go-review.googlesource.com/c/go/+/230545 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/link/internal/ld/lib.go | 22 +++++++++++----------- src/cmd/link/internal/ld/pe.go | 2 +- src/cmd/link/internal/ld/symtab.go | 4 ++-- src/cmd/link/internal/ld/xcoff.go | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 57b9e8cb24..9d0ccae719 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2493,7 +2493,7 @@ const ( DeletedAutoSym = 'x' ) -func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int64, *sym.Symbol)) { +func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int64)) { // 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) @@ -2503,7 +2503,7 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6 // 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) + put(ctxt, s, s.Name, TextSym, s.Value) } } @@ -2524,7 +2524,7 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6 break } if s.Type == sym.STEXT { - put(ctxt, s, s.Name, TextSym, s.Value, nil) + put(ctxt, s, s.Name, TextSym, s.Value) } n++ } @@ -2536,7 +2536,7 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6 // 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) + put(ctxt, s, s.Name, TextSym, s.Value) } } @@ -2589,7 +2589,7 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6 if !s.Attr.Reachable() { continue } - put(ctxt, s, s.Name, DataSym, Symaddr(s), s.Gotype) + put(ctxt, s, s.Name, DataSym, Symaddr(s)) case sym.SBSS, sym.SNOPTRBSS, sym.SLIBFUZZER_EXTRA_COUNTER: if !s.Attr.Reachable() { @@ -2598,11 +2598,11 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6 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) + put(ctxt, s, s.Name, BSSSym, Symaddr(s)) case sym.SUNDEFEXT: if ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Haix || ctxt.IsELF { - put(ctxt, s, s.Name, UndefinedSym, s.Value, nil) + put(ctxt, s, s.Name, UndefinedSym, s.Value) } case sym.SHOSTOBJ: @@ -2610,24 +2610,24 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6 continue } if ctxt.HeadType == objabi.Hwindows || ctxt.IsELF { - put(ctxt, s, s.Name, UndefinedSym, s.Value, nil) + put(ctxt, s, s.Name, UndefinedSym, s.Value) } case sym.SDYNIMPORT: if !s.Attr.Reachable() { continue } - put(ctxt, s, s.Extname(), UndefinedSym, 0, nil) + put(ctxt, s, s.Extname(), UndefinedSym, 0) case sym.STLSBSS: if ctxt.LinkMode == LinkExternal { - put(ctxt, s, s.Name, TLSSym, Symaddr(s), s.Gotype) + put(ctxt, s, s.Name, TLSSym, Symaddr(s)) } } } for _, s := range ctxt.Textp { - put(ctxt, s, s.Name, TextSym, s.Value, s.Gotype) + put(ctxt, s, s.Name, TextSym, s.Value) } if ctxt.Debugvlog != 0 || *flagN { diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index fda5590700..547200fbee 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -646,7 +646,7 @@ func (f *peFile) mapToPESection(s *sym.Symbol, linkmode LinkMode) (pesectidx int // 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) { + put := func(ctxt *Link, s *sym.Symbol, name string, type_ SymbolType, addr int64) { if s == nil { return } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 5954176b1c..1d7ec895dc 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -74,7 +74,7 @@ func putelfsyment(out *OutBuf, off int, addr int64, size int64, info int, shndx } } -func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64, go_ *sym.Symbol) { +func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64) { var typ int switch t { @@ -224,7 +224,7 @@ func Asmelfsym(ctxt *Link) { genasmsym(ctxt, putelfsym) } -func putplan9sym(ctxt *Link, x *sym.Symbol, s string, typ SymbolType, addr int64, go_ *sym.Symbol) { +func putplan9sym(ctxt *Link, x *sym.Symbol, s string, typ SymbolType, addr int64) { t := int(typ) switch typ { case TextSym, DataSym, BSSSym: diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index c9c6507f1c..6fd08d2dde 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -855,7 +855,7 @@ func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x *sym.Symbol) []xcoffSym { } // 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) { +func putaixsym(ctxt *Link, x *sym.Symbol, str string, t SymbolType, addr int64) { // All XCOFF symbols generated by this GO symbols // Can be a symbol entry or a auxiliary entry From 43a85a39bf4204267c20e60c2e32383a0e506347 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Mon, 27 Apr 2020 12:19:57 -0400 Subject: [PATCH 36/42] [dev.link] cmd/link: remove Gotype and File fields from sym.Symbol Remove the 'Gotype' field from sym.Symbol, as it is now no longer used. Store the loader.Sym for a symbol as a field in sym.Symbol ("SymIdx"). Then remove sym.Symbol 'File' field, and replace the field accesses in question with calls into the loader instead. Change-Id: I01c5504425006b8d3fe77fac2b69a86e198c7a5a Reviewed-on: https://go-review.googlesource.com/c/go/+/230304 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/lib.go | 7 +++++ src/cmd/link/internal/ld/xcoff.go | 12 ++++---- src/cmd/link/internal/loader/loader.go | 39 ++---------------------- src/cmd/link/internal/sym/sizeof_test.go | 2 +- src/cmd/link/internal/sym/symbol.go | 3 +- 5 files changed, 18 insertions(+), 45 deletions(-) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 9d0ccae719..03d841dccb 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2911,6 +2911,13 @@ func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind) { } } +func symPkg(ctxt *Link, s *sym.Symbol) string { + if s == nil { + return "" + } + return ctxt.loader.SymPkg(loader.Sym(s.SymIdx)) +} + func (ctxt *Link) dumpsyms() { for _, s := range ctxt.loader.Syms { if s == nil { diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index 6fd08d2dde..bb039884af 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -783,19 +783,19 @@ func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x *sym.Symbol) []xcoffSym { // 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 + } else if symPkg(ctxt, x) == "" { // Undefined global symbol // If this happens, the algorithm must be redone. if currSymSrcFile.name != "" { Exitf("undefined global symbol found inside another file") } } else { // Current file has changed. New C_FILE, C_DWARF, etc must be generated. - if currSymSrcFile.name != x.File { + if currSymSrcFile.name != symPkg(ctxt, x) { 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)) + currSymSrcFile.name = symPkg(ctxt, x) + f.writeSymbolNewFile(ctxt, symPkg(ctxt, x), 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 @@ -805,7 +805,7 @@ func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x *sym.Symbol) []xcoffSym { // TODO(aix); remove once ld has been fixed or the triggering // relocation has been found and fixed. if currSymSrcFile.name == "" { - currSymSrcFile.name = x.File + currSymSrcFile.name = symPkg(ctxt, x) f.writeSymbolNewFile(ctxt, "go_functions", uint64(x.Value), xfile.getXCOFFscnum(x.Sect)) } } @@ -866,7 +866,7 @@ func putaixsym(ctxt *Link, x *sym.Symbol, str string, t SymbolType, addr int64) return case TextSym: - if x.File != "" || strings.Contains(x.Name, "-tramp") || strings.HasPrefix(x.Name, "runtime.text.") { + if symPkg(ctxt, x) != "" || strings.Contains(x.Name, "-tramp") || strings.HasPrefix(x.Name, "runtime.text.") { // Function within a file syms = xfile.writeSymbolFunc(ctxt, x) } else { diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 1af771e970..2e4f5de0ee 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1992,14 +1992,6 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { s.Version = int16(pp.ver) s.Type = pp.kind s.Size = pp.size - if pp.gotype != 0 { - s.Gotype = l.Syms[pp.gotype] - } - if f, ok := l.symPkg[i]; ok { - s.File = f - } else if pp.objidx != 0 { - s.File = l.objs[pp.objidx].r.unit.Lib.Pkg - } // Copy relocations batch := l.relocBatch @@ -2161,22 +2153,7 @@ func (l *Loader) PropagateLoaderChangesToSymbols(toconvert []Sym, anonVerReplace 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.symPkg[cand]; ok { - s.File = f - } else { - r, _ := l.toLocal(cand) - if r != nil && r != l.extReader { - s.File = l.SymPkg(cand) - } - } - } - + // For 'new' symbols, copy other content. if relfix { relocfixup = append(relocfixup, cand) } @@ -2276,6 +2253,7 @@ func (l *Loader) installSym(i Sym, s *sym.Symbol) { panic("sym already present in installSym") } l.Syms[i] = s + s.SymIdx = sym.LoaderSym(i) } // addNewSym adds a new sym.Symbol to the i-th index in the list of symbols. @@ -2554,11 +2532,6 @@ func (l *Loader) CreateStaticSym(name string) Sym { } func loadObjFull(l *Loader, r *oReader) { - resolveSymRef := func(s goobj2.SymRef) *sym.Symbol { - i := l.resolve(r, s) - return l.Syms[i] - } - for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ { // A symbol may be a dup or overwritten. In this case, its // content will actually be provided by a different object @@ -2595,12 +2568,7 @@ func loadObjFull(l *Loader, r *oReader) { for j := range auxs { a := &auxs[j] switch a.Type() { - case goobj2.AuxGotype: - typ := resolveSymRef(a.Sym()) - if typ != nil { - s.Gotype = typ - } - case goobj2.AuxFuncInfo, goobj2.AuxFuncdata: + case goobj2.AuxFuncInfo, goobj2.AuxFuncdata, goobj2.AuxGotype: // already handled case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines: // ignored for now @@ -2609,7 +2577,6 @@ func loadObjFull(l *Loader, r *oReader) { } } - s.File = r.pkgprefix[:len(r.pkgprefix)-1] if s.Size < int64(size) { s.Size = int64(size) } diff --git a/src/cmd/link/internal/sym/sizeof_test.go b/src/cmd/link/internal/sym/sizeof_test.go index 3e97a833df..e05e2a0032 100644 --- a/src/cmd/link/internal/sym/sizeof_test.go +++ b/src/cmd/link/internal/sym/sizeof_test.go @@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Symbol{}, 104, 168}, + {Symbol{}, 96, 152}, } for _, tt := range tests { diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go index 9a6791e16f..5bb1d438b0 100644 --- a/src/cmd/link/internal/sym/symbol.go +++ b/src/cmd/link/internal/sym/symbol.go @@ -27,8 +27,7 @@ type Symbol struct { Size int64 Sub *Symbol Outer *Symbol - Gotype *Symbol - File string // actually package! + SymIdx LoaderSym auxinfo *AuxSymbol Sect *Section Unit *CompilationUnit From 183d25d9963107e6e4e86c49018ddd8871109d61 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Mon, 27 Apr 2020 14:02:06 -0400 Subject: [PATCH 37/42] [dev.link] cmd/link/internal/sym: remove SortSub Remove SortSub; this function no longer referenced. Change-Id: Ie0c78f5cd048a8f465f84ceeab30db57be5c05a7 Reviewed-on: https://go-review.googlesource.com/c/go/+/230457 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Jeremy Faller Reviewed-by: Cherry Zhang --- src/cmd/link/internal/sym/symbol.go | 74 ----------------------------- 1 file changed, 74 deletions(-) diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go index 5bb1d438b0..90c6d4c5f3 100644 --- a/src/cmd/link/internal/sym/symbol.go +++ b/src/cmd/link/internal/sym/symbol.go @@ -443,80 +443,6 @@ func (s *Symbol) SetElfType(val elf.SymType) { 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 Pcdata struct { P []byte } From 12b11f3af9be5421ba330136b34efe1d8c80e1d6 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Tue, 28 Apr 2020 10:37:33 -0400 Subject: [PATCH 38/42] [dev.link] cmd/link/internal/loader: add elf symbol methods Add new get/set methods to the loader for recording the ELF symbol index for a given loader symbol. These are map-based, since it is expected that many/most symbols will not need an ELF symbol table entry. Change-Id: I1102c3637775515ccc6650118e8b059468a2c3ea Reviewed-on: https://go-review.googlesource.com/c/go/+/230461 Run-TryBot: Than McIntosh Reviewed-by: Cherry Zhang TryBot-Result: Gobot Gobot --- src/cmd/link/internal/loader/loader.go | 58 ++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 2e4f5de0ee..1c687ee3e2 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -255,15 +255,17 @@ type Loader struct { 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 - symPkg map[Sym]string // stores package for symbol, or library for shlib-derived syms - plt map[Sym]int32 // stores dynimport for pe objects - got map[Sym]int32 // stores got for pe objects - dynid map[Sym]int32 // stores Dynid for symbol + dynimplib map[Sym]string // stores Dynimplib symbol attribute + dynimpvers map[Sym]string // stores Dynimpvers symbol attribute + localentry map[Sym]uint8 // stores Localentry symbol attribute + extname map[Sym]string // stores Extname symbol attribute + elfType map[Sym]elf.SymType // stores elf type symbol property + elfSym map[Sym]int32 // stores elf sym symbol property + localElfSym map[Sym]int32 // stores "local" elf sym symbol property + symPkg map[Sym]string // stores package for symbol, or library for shlib-derived syms + plt map[Sym]int32 // stores dynimport for pe objects + got map[Sym]int32 // stores got for pe objects + dynid map[Sym]int32 // stores Dynid for symbol relocVariant map[relocId]sym.RelocVariant // stores variant relocs @@ -331,6 +333,8 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor extname: make(map[Sym]string), attrReadOnly: make(map[Sym]bool), elfType: make(map[Sym]elf.SymType), + elfSym: make(map[Sym]int32), + localElfSym: make(map[Sym]int32), symPkg: make(map[Sym]string), plt: make(map[Sym]int32), got: make(map[Sym]int32), @@ -1235,6 +1239,42 @@ func (l *Loader) SetSymElfType(i Sym, et elf.SymType) { } } +// SymElfSym returns the ELF symbol index for a given loader +// symbol, assigned during ELF symtab generation. +func (l *Loader) SymElfSym(i Sym) int32 { + return l.elfSym[i] +} + +// SetSymElfSym sets the elf symbol index for a symbol. +func (l *Loader) SetSymElfSym(i Sym, es int32) { + if i == 0 { + panic("bad sym index") + } + if es == 0 { + delete(l.elfSym, i) + } else { + l.elfSym[i] = es + } +} + +// SymLocalElfSym returns the "local" ELF symbol index for a given loader +// symbol, assigned during ELF symtab generation. +func (l *Loader) SymLocalElfSym(i Sym) int32 { + return l.localElfSym[i] +} + +// SetSymLocalElfSym sets the "local" elf symbol index for a symbol. +func (l *Loader) SetSymLocalElfSym(i Sym, es int32) { + if i == 0 { + panic("bad sym index") + } + if es == 0 { + delete(l.localElfSym, i) + } else { + l.localElfSym[i] = es + } +} + // SymPlt returns the plt value for pe symbols. func (l *Loader) SymPlt(s Sym) int32 { if v, ok := l.plt[s]; ok { From 3f408fc7fdc8156f04414b529b9194f868485c8a Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Mon, 27 Apr 2020 13:21:48 -0400 Subject: [PATCH 39/42] [dev.link] cmd/link: remove Unit field from sym.Symbol Remove sym.Symbol 'Unit' field, replacing accesses to the field with calls into the loader instead. Change-Id: Ia1abd4c3d93036705dd624a49cb3d9cbe6a5188b Reviewed-on: https://go-review.googlesource.com/c/go/+/230307 Reviewed-by: Cherry Zhang Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/data2.go | 4 ++-- src/cmd/link/internal/loader/loader.go | 1 - src/cmd/link/internal/sym/sizeof_test.go | 2 +- src/cmd/link/internal/sym/symbol.go | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cmd/link/internal/ld/data2.go b/src/cmd/link/internal/ld/data2.go index 2aba40c231..b9c8cd83de 100644 --- a/src/cmd/link/internal/ld/data2.go +++ b/src/cmd/link/internal/ld/data2.go @@ -324,7 +324,8 @@ func relocsym2(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arc 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(ldr.Syms[r.Sym.Unit.Textp2[0]]) + u := ldr.SymUnit(loader.Sym(r.Sym.SymIdx)) + o = Symaddr(r.Sym) + r.Add - Symaddr(ldr.Syms[u.Textp2[0]]) // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. case objabi.R_GOTPCREL: @@ -482,4 +483,3 @@ func relocsym2(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arc } } } - diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 1c687ee3e2..fb792ac059 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -2307,7 +2307,6 @@ func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUni t = s.Type } s.Type = t - s.Unit = unit l.growSyms(int(i)) l.installSym(i, s) return s diff --git a/src/cmd/link/internal/sym/sizeof_test.go b/src/cmd/link/internal/sym/sizeof_test.go index e05e2a0032..d7db7f1daa 100644 --- a/src/cmd/link/internal/sym/sizeof_test.go +++ b/src/cmd/link/internal/sym/sizeof_test.go @@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Symbol{}, 96, 152}, + {Symbol{}, 92, 144}, } for _, tt := range tests { diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go index 90c6d4c5f3..643c48d589 100644 --- a/src/cmd/link/internal/sym/symbol.go +++ b/src/cmd/link/internal/sym/symbol.go @@ -30,7 +30,6 @@ type Symbol struct { SymIdx LoaderSym auxinfo *AuxSymbol Sect *Section - Unit *CompilationUnit // P contains the raw symbol data. P []byte R []Reloc From 0722a49b3713fe41bb46e7f690a3f7f1314edbc7 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Tue, 28 Apr 2020 10:51:40 -0400 Subject: [PATCH 40/42] [dev.link] cmd/link: remove elf sym fields from sym.Symbol Remove the "ElfSym" and "LocalElfSym" fields from sym.Symbol, replacing uses with loader method calls as needed. Change-Id: I3828f13203ece2bdc03eeb09ab37a5c94e21a726 Reviewed-on: https://go-review.googlesource.com/c/go/+/230462 Reviewed-by: Cherry Zhang --- src/cmd/link/internal/amd64/asm.go | 2 +- src/cmd/link/internal/arm/asm.go | 2 +- src/cmd/link/internal/arm64/asm.go | 2 +- src/cmd/link/internal/ld/elf.go | 3 +- src/cmd/link/internal/ld/lib.go | 11 +++++++ src/cmd/link/internal/ld/symtab.go | 6 ++-- src/cmd/link/internal/mips/asm.go | 2 +- src/cmd/link/internal/mips64/asm.go | 2 +- src/cmd/link/internal/ppc64/asm.go | 2 +- src/cmd/link/internal/s390x/asm.go | 2 +- src/cmd/link/internal/sym/sizeof_test.go | 2 +- src/cmd/link/internal/sym/symbol.go | 38 ++++++++---------------- src/cmd/link/internal/x86/asm.go | 2 +- 13 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index 95831d6dd0..24134adc27 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -396,7 +396,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { ctxt.Out.Write64(uint64(sectoff)) - elfsym := r.Xsym.ElfsymForReloc() + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) switch r.Type { default: return false diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index 7665cc59f0..e42ea0f6e5 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -252,7 +252,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { ctxt.Out.Write32(uint32(sectoff)) - elfsym := r.Xsym.ElfsymForReloc() + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) switch r.Type { default: return false diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index fbcbc9c8c3..f49172ea23 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -328,7 +328,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { ctxt.Out.Write64(uint64(sectoff)) - elfsym := r.Xsym.ElfsymForReloc() + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) switch r.Type { default: return false diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 3a0a305db9..3be3f99171 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -1411,7 +1411,8 @@ func elfrelocsect(ctxt *Link, sect *sym.Section, syms []*sym.Symbol) { Errorf(s, "missing xsym in relocation %#v %#v", r.Sym.Name, s) continue } - if r.Xsym.ElfsymForReloc() == 0 { + esr := ElfSymForReloc(ctxt, r.Xsym) + if esr == 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() { diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 03d841dccb..e1f89858b6 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2918,6 +2918,17 @@ func symPkg(ctxt *Link, s *sym.Symbol) string { return ctxt.loader.SymPkg(loader.Sym(s.SymIdx)) } +func ElfSymForReloc(ctxt *Link, s *sym.Symbol) int32 { + // If putelfsym created a local version of this symbol, use that in all + // relocations. + les := ctxt.loader.SymLocalElfSym(loader.Sym(s.SymIdx)) + if les != 0 { + return les + } else { + return ctxt.loader.SymElfSym(loader.Sym(s.SymIdx)) + } +} + func (ctxt *Link) dumpsyms() { for _, s := range ctxt.loader.Syms { if s == nil { diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 1d7ec895dc..f9eb05146f 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -185,7 +185,7 @@ func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64) { // 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(ctxt.numelfsym) + ctxt.loader.SetSymLocalElfSym(loader.Sym(x.SymIdx), int32(ctxt.numelfsym)) ctxt.numelfsym++ return } else if bind != ctxt.elfbind { @@ -193,13 +193,13 @@ func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64) { } putelfsyment(ctxt.Out, putelfstr(s), addr, size, bind<<4|typ&0xf, elfshnum, other) - x.Elfsym = int32(ctxt.numelfsym) + ctxt.loader.SetSymElfSym(loader.Sym(x.SymIdx), int32(ctxt.numelfsym)) ctxt.numelfsym++ } func putelfsectionsym(ctxt *Link, out *OutBuf, s *sym.Symbol, shndx int) { putelfsyment(out, 0, 0, 0, STB_LOCAL<<4|STT_SECTION, shndx, 0) - s.Elfsym = int32(ctxt.numelfsym) + ctxt.loader.SetSymElfSym(loader.Sym(s.SymIdx), int32(ctxt.numelfsym)) ctxt.numelfsym++ } diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go index 9281678704..53032a7e33 100644 --- a/src/cmd/link/internal/mips/asm.go +++ b/src/cmd/link/internal/mips/asm.go @@ -54,7 +54,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym. func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { ctxt.Out.Write32(uint32(sectoff)) - elfsym := r.Xsym.ElfsymForReloc() + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) switch r.Type { default: return false diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 615851b564..33f8b33509 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -61,7 +61,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { ctxt.Out.Write64(uint64(sectoff)) - elfsym := r.Xsym.ElfsymForReloc() + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) ctxt.Out.Write32(uint32(elfsym)) ctxt.Out.Write8(0) ctxt.Out.Write8(0) diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 6b6e1294d3..dfc55a30fd 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -453,7 +453,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { } ctxt.Out.Write64(uint64(sectoff)) - elfsym := r.Xsym.ElfsymForReloc() + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) switch r.Type { default: return false diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index ac634f99e3..a9cb79a1cc 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -224,7 +224,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { ctxt.Out.Write64(uint64(sectoff)) - elfsym := r.Xsym.ElfsymForReloc() + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) switch r.Type { default: return false diff --git a/src/cmd/link/internal/sym/sizeof_test.go b/src/cmd/link/internal/sym/sizeof_test.go index d7db7f1daa..4cfca3b5a3 100644 --- a/src/cmd/link/internal/sym/sizeof_test.go +++ b/src/cmd/link/internal/sym/sizeof_test.go @@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Symbol{}, 92, 144}, + {Symbol{}, 84, 136}, } for _, tt := range tests { diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go index 643c48d589..546798fdf4 100644 --- a/src/cmd/link/internal/sym/symbol.go +++ b/src/cmd/link/internal/sym/symbol.go @@ -15,21 +15,19 @@ import ( // 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 - SymIdx LoaderSym - auxinfo *AuxSymbol - Sect *Section + Name string + Type SymKind + Version int16 + Attr Attribute + Dynid int32 + Align int32 + Value int64 + Size int64 + Sub *Symbol + Outer *Symbol + SymIdx LoaderSym + auxinfo *AuxSymbol + Sect *Section // P contains the raw symbol data. P []byte R []Reloc @@ -86,16 +84,6 @@ 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) Len() int64 { return s.Size } diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 069d2bc31c..157e13496c 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -343,7 +343,7 @@ func adddynrel2(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s load func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { ctxt.Out.Write32(uint32(sectoff)) - elfsym := r.Xsym.ElfsymForReloc() + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) switch r.Type { default: return false From 1adae7fe76d27cb9623cb269bb3ede2372f5eea6 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sat, 25 Apr 2020 17:50:48 -0400 Subject: [PATCH 41/42] [dev.link] cmd/link: convert asmb pass to new style Change-Id: I8675f56a7f7f18653754eb87b95f5a7aec31ad74 Reviewed-on: https://go-review.googlesource.com/c/go/+/229860 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/data.go | 75 +++++++++++++------------- src/cmd/link/internal/ld/main.go | 5 +- src/cmd/link/internal/ld/outbuf.go | 11 ++-- src/cmd/link/internal/loader/loader.go | 31 ++++++++++- 4 files changed, 77 insertions(+), 45 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 1fd048e10d..d44d590623 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -716,7 +716,7 @@ func Codeblk(ctxt *Link, out *OutBuf, addr int64, size int64) { } func CodeblkPad(ctxt *Link, out *OutBuf, addr int64, size int64, pad []byte) { - writeBlocks(out, ctxt.outSem, ctxt.Textp, addr, size, pad) + writeBlocks(out, ctxt.outSem, ctxt.loader, ctxt.Textp2, addr, size, pad) } const blockSize = 1 << 20 // 1MB chunks written at a time. @@ -726,9 +726,9 @@ const blockSize = 1 << 20 // 1MB chunks written at a time. // 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) { +func writeBlocks(out *OutBuf, sem chan int, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { for i, s := range syms { - if s.Value >= addr && !s.Attr.SubSymbol() { + if ldr.SymValue(s) >= addr && !ldr.AttrSubSymbol(s) { syms = syms[i:] break } @@ -740,13 +740,14 @@ func writeBlocks(out *OutBuf, sem chan int, syms []*sym.Symbol, addr, size int64 // Find the last symbol we'd write. idx := -1 for i, s := range syms { - if s.Attr.SubSymbol() { + if ldr.AttrSubSymbol(s) { continue } // If the next symbol's size would put us out of bounds on the total length, // stop looking. - if s.Value+s.Size > lastAddr { + end := ldr.SymValue(s) + ldr.SymSize(s) + if end > lastAddr { break } @@ -754,7 +755,7 @@ func writeBlocks(out *OutBuf, sem chan int, syms []*sym.Symbol, addr, size int64 idx = i // If we cross over the max size, we've got enough symbols. - if s.Value+s.Size > addr+max { + if end > addr+max { break } } @@ -775,11 +776,11 @@ func writeBlocks(out *OutBuf, sem chan int, syms []*sym.Symbol, addr, size int64 // Skip over sub symbols so we won't split a containter symbol // into two blocks. next := syms[idx+1] - for next.Attr.SubSymbol() { + for ldr.AttrSubSymbol(next) { idx++ next = syms[idx+1] } - length = next.Value - addr + length = ldr.SymValue(next) - addr } if length == 0 || length > lastAddr-addr { length = lastAddr - addr @@ -789,13 +790,13 @@ func writeBlocks(out *OutBuf, sem chan int, syms []*sym.Symbol, addr, size int64 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) + go func(o *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { + writeBlock(o, ldr, syms, addr, size, pad) wg.Done() <-sem - }(o, syms, addr, length, pad) + }(o, ldr, syms, addr, length, pad) } else { // output not mmaped, don't parallelize. - writeBlock(out, syms, addr, length, pad) + writeBlock(out, ldr, syms, addr, length, pad) } // Prepare for the next loop. @@ -808,9 +809,9 @@ func writeBlocks(out *OutBuf, sem chan int, syms []*sym.Symbol, addr, size int64 wg.Wait() } -func writeBlock(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) { +func writeBlock(out *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { for i, s := range syms { - if s.Value >= addr && !s.Attr.SubSymbol() { + if ldr.SymValue(s) >= addr && !ldr.AttrSubSymbol(s) { syms = syms[i:] break } @@ -822,31 +823,33 @@ func writeBlock(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) { // so dwarfcompress will fix this up later if necessary. eaddr := addr + size for _, s := range syms { - if s.Attr.SubSymbol() { + if ldr.AttrSubSymbol(s) { continue } - if s.Value >= eaddr { + val := ldr.SymValue(s) + if val >= eaddr { break } - if s.Value < addr { - Errorf(s, "phase error: addr=%#x but sym=%#x type=%d", addr, s.Value, s.Type) + if val < addr { + ldr.Errorf(s, "phase error: addr=%#x but sym=%#x type=%d", addr, val, ldr.SymType(s)) errorexit() } - if addr < s.Value { - out.WriteStringPad("", int(s.Value-addr), pad) - addr = s.Value + if addr < val { + out.WriteStringPad("", int(val-addr), pad) + addr = val } - 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 + out.WriteSym(ldr, s) + addr += int64(len(ldr.Data(s))) + siz := ldr.SymSize(s) + if addr < val+siz { + out.WriteStringPad("", int(val+siz-addr), pad) + addr = val + siz } - if addr != s.Value+s.Size { - Errorf(s, "phase error: addr=%#x value+size=%#x", addr, s.Value+s.Size) + if addr != val+siz { + ldr.Errorf(s, "phase error: addr=%#x value+size=%#x", addr, val+siz) errorexit() } - if s.Value+s.Size >= eaddr { + if val+siz >= eaddr { break } } @@ -885,7 +888,7 @@ func DatblkBytes(ctxt *Link, addr int64, size int64) []byte { } func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) { - writeBlocks(out, ctxt.outSem, ctxt.datap, addr, size, zeros[:]) + writeBlocks(out, ctxt.outSem, ctxt.loader, ctxt.datap2, addr, size, zeros[:]) } func Dwarfblk(ctxt *Link, out *OutBuf, addr int64, size int64) { @@ -896,14 +899,14 @@ func Dwarfblk(ctxt *Link, out *OutBuf, addr int64, size int64) { // section, but this would run the risk of undoing any file offset // adjustments made during layout. n := 0 - for i := range dwarfp { - n += len(dwarfp[i].syms) + for i := range dwarfp2 { + n += len(dwarfp2[i].syms) } - syms := make([]*sym.Symbol, 0, n) - for i := range dwarfp { - syms = append(syms, dwarfp[i].syms...) + syms := make([]loader.Sym, 0, n) + for i := range dwarfp2 { + syms = append(syms, dwarfp2[i].syms...) } - writeBlocks(out, ctxt.outSem, syms, addr, size, zeros[:]) + writeBlocks(out, ctxt.outSem, ctxt.loader, syms, addr, size, zeros[:]) } var zeros [512]byte diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 48fea57cc9..a4526230c9 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -306,8 +306,6 @@ func Main(arch *sys.Arch, theArch Arch) { dwarfcompress(ctxt) bench.Start("layout") filesize := ctxt.layout(order) - bench.Start("loadlibfull") - ctxt.loadlibfull(symGroupType) // XXX do it here for now // Write out the output file. // It is split into two parts (Asmb and Asmb2). The first @@ -325,7 +323,10 @@ func Main(arch *sys.Arch, theArch Arch) { // Asmb will redirect symbols to the output file mmap, and relocations // will be applied directly there. bench.Start("Asmb") + ctxt.loader.InitOutData() thearch.Asmb(ctxt, ctxt.loader) + bench.Start("loadlibfull") + ctxt.loadlibfull(symGroupType) // XXX do it here for now bench.Start("reloc") ctxt.reloc() bench.Start("Asmb2") diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go index 40e02cb7cf..4ce211172c 100644 --- a/src/cmd/link/internal/ld/outbuf.go +++ b/src/cmd/link/internal/ld/outbuf.go @@ -6,7 +6,7 @@ package ld import ( "cmd/internal/sys" - "cmd/link/internal/sym" + "cmd/link/internal/loader" "encoding/binary" "errors" "log" @@ -285,10 +285,11 @@ func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) { // 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) { - n := int64(len(s.P)) +func (out *OutBuf) WriteSym(ldr *loader.Loader, s loader.Sym) { + P := ldr.Data(s) + n := int64(len(P)) pos, buf := out.writeLoc(n) - copy(buf[pos:], s.P) + copy(buf[pos:], P) out.off += n - s.P = buf[pos : pos+n] + ldr.SetOutData(s, buf[pos:pos+n]) } diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index fb792ac059..775a2cb522 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -216,6 +216,8 @@ type Loader struct { sects []*sym.Section // sections symSects []uint16 // symbol's section, index to sects array + outdata [][]byte // symbol's data in the output buffer + itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.* objByPkg map[string]*oReader // map package path to its Go object reader @@ -1080,6 +1082,32 @@ func (l *Loader) Data(i Sym) []byte { return r.Data(li) } +// Returns the data of the i-th symbol in the output buffer. +func (l *Loader) OutData(i Sym) []byte { + if int(i) < len(l.outdata) && l.outdata[i] != nil { + return l.outdata[i] + } + return l.Data(i) +} + +// SetOutData sets the position of the data of the i-th symbol in the output buffer. +// i is global index. +func (l *Loader) SetOutData(i Sym, data []byte) { + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp != nil { + pp.data = data + return + } + } + l.outdata[i] = data +} + +// InitOutData initializes the slice used to store symbol output data. +func (l *Loader) InitOutData() { + l.outdata = make([][]byte, l.extStart) +} + // SymAlign returns the alignment for a symbol. func (l *Loader) SymAlign(i Sym) int32 { // If an alignment has been recorded, return that. @@ -2592,8 +2620,7 @@ func loadObjFull(l *Loader, r *oReader) { size := osym.Siz() // Symbol data - s.P = r.Data(i) - s.Attr.Set(sym.AttrReadOnly, r.ReadOnly()) + s.P = l.OutData(gi) // Relocs relocs := l.relocs(r, i) From 76c6cce1160996e730d87e620ddb674b1d54f96e Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Sat, 25 Apr 2020 22:08:50 -0400 Subject: [PATCH 42/42] [dev.link] cmd/link: convert reloc pass to using the loader when internal linking Only enabled for AMD64 when internal linking for now. Change-Id: I2aa9ee47c0f7413ea7bbcdd31b8317c14220bba3 Reviewed-on: https://go-review.googlesource.com/c/go/+/230302 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/link/internal/amd64/asm.go | 4 +-- src/cmd/link/internal/ld/data.go | 35 +++++++++++++------------- src/cmd/link/internal/ld/data2.go | 35 +++++++++++++++++++++++++- src/cmd/link/internal/ld/lib.go | 7 +++--- src/cmd/link/internal/ld/main.go | 20 ++++++++++++--- src/cmd/link/internal/loader/loader.go | 34 ++++++++++++++----------- 6 files changed, 92 insertions(+), 43 deletions(-) diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index 24134adc27..f3b3d703b5 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -841,7 +841,7 @@ func asmb2(ctxt *ld.Link) { } } -func tlsIEtoLE(s *sym.Symbol, off, size int) { +func tlsIEtoLE(P []byte, off, size int) { // Transform the PC-relative instruction into a constant load. // That is, // @@ -852,7 +852,7 @@ func tlsIEtoLE(s *sym.Symbol, off, size int) { if off < 3 { log.Fatal("R_X86_64_GOTTPOFF reloc not preceded by MOVQ or ADDQ instruction") } - op := s.P[off-3 : off] + op := P[off-3 : off] reg := op[2] >> 3 if op[1] == 0x8b || reg == 4 { diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index d44d590623..44e9b884ff 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -271,7 +271,6 @@ func relocsym(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arch log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType) } case objabi.R_TLS_IE: - panic("not implemented") //if target.IsExternal() && target.IsElf() { // r.Done = false // if r.Sym == nil { @@ -285,17 +284,17 @@ func relocsym(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arch // } // break //} - //if target.IsPIE() && target.IsElf() { - // // We are linking the final executable, so we - // // can optimize any TLS IE relocation to LE. - // if thearch.TLSIEtoLE == nil { - // log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) - // } - // thearch.TLSIEtoLE(ldr, s, int(off), int(siz)) - // o = int64(syms.Tlsoffset) - //} else { - // log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s)) - //} + if target.IsPIE() && target.IsElf() { + // We are linking the final executable, so we + // can optimize any TLS IE relocation to LE. + if thearch.TLSIEtoLE == nil { + log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) + } + thearch.TLSIEtoLE(P, int(off), int(siz)) + o = int64(syms.Tlsoffset) + } else { + log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s)) + } case objabi.R_ADDR: //if target.IsExternal() && r.Sym.Type != sym.SCONST { // r.Done = false @@ -562,22 +561,22 @@ func (ctxt *Link) reloc() { wg.Add(3) go func() { if !ctxt.IsWasm() { // On Wasm, text relocations are applied in Asmb2. - for _, s := range ctxt.Textp { - relocsym2(target, ldr, reporter, syms, s) + for _, s := range ctxt.Textp2 { + relocsym(target, ldr, reporter, syms, s, ldr.OutData(s)) } } wg.Done() }() go func() { - for _, s := range ctxt.datap { - relocsym2(target, ldr, reporter, syms, s) + for _, s := range ctxt.datap2 { + relocsym(target, ldr, reporter, syms, s, ldr.OutData(s)) } wg.Done() }() go func() { - for _, si := range dwarfp { + for _, si := range dwarfp2 { for _, s := range si.syms { - relocsym2(target, ldr, reporter, syms, s) + relocsym(target, ldr, reporter, syms, s, ldr.OutData(s)) } } wg.Done() diff --git a/src/cmd/link/internal/ld/data2.go b/src/cmd/link/internal/ld/data2.go index b9c8cd83de..d4503a4b0a 100644 --- a/src/cmd/link/internal/ld/data2.go +++ b/src/cmd/link/internal/ld/data2.go @@ -11,6 +11,7 @@ import ( "fmt" "log" "strings" + "sync" ) // Temporary dumping around for sym.Symbol version of helper @@ -199,7 +200,7 @@ func relocsym2(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arc if thearch.TLSIEtoLE == nil { log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) } - thearch.TLSIEtoLE(s, int(off), int(r.Siz)) + thearch.TLSIEtoLE(s.P, int(off), int(r.Siz)) o = int64(syms.Tlsoffset) // TODO: o += r.Add when !target.IsAmd64()? // Why do we treat r.Add differently on AMD64? @@ -483,3 +484,35 @@ func relocsym2(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *Arc } } } + +func (ctxt *Link) reloc2() { + var wg sync.WaitGroup + target := &ctxt.Target + ldr := ctxt.loader + reporter := &ctxt.ErrorReporter + syms := &ctxt.ArchSyms + wg.Add(3) + go func() { + if !ctxt.IsWasm() { // On Wasm, text relocations are applied in Asmb2. + for _, s := range ctxt.Textp { + relocsym2(target, ldr, reporter, syms, s) + } + } + wg.Done() + }() + go func() { + for _, s := range ctxt.datap { + relocsym2(target, ldr, reporter, syms, s) + } + wg.Done() + }() + go func() { + for _, si := range dwarfp { + for _, s := range si.syms { + relocsym2(target, ldr, reporter, syms, s) + } + } + wg.Done() + }() + wg.Wait() +} diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index e1f89858b6..618faf2233 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -282,7 +282,7 @@ type Arch struct { // 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) + TLSIEtoLE func(P []byte, off, size int) // optional override for assignAddress AssignAddress func(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) @@ -2824,10 +2824,9 @@ func addToTextp(ctxt *Link) { ctxt.Textp = textp } -func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind) { - +func (ctxt *Link) loadlibfull(symGroupType []sym.SymKind, needReloc bool) { // Load full symbol contents, resolve indexed references. - ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms) + ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms, needReloc) // Convert ctxt.Moduledata2 to ctxt.Moduledata, etc if ctxt.Moduledata2 != 0 { diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index a4526230c9..84f40d9b81 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -325,10 +325,22 @@ func Main(arch *sys.Arch, theArch Arch) { bench.Start("Asmb") ctxt.loader.InitOutData() thearch.Asmb(ctxt, ctxt.loader) - bench.Start("loadlibfull") - ctxt.loadlibfull(symGroupType) // XXX do it here for now - bench.Start("reloc") - ctxt.reloc() + + newreloc := ctxt.IsInternal() && ctxt.IsAMD64() + if newreloc { + bench.Start("reloc") + ctxt.reloc() + bench.Start("loadlibfull") + // We don't need relocations at this point. + // An exception is Windows, see pe.go:addPEBaseRelocSym + needReloc := ctxt.IsWindows() + ctxt.loadlibfull(symGroupType, needReloc) // XXX do it here for now + } else { + bench.Start("loadlibfull") + ctxt.loadlibfull(symGroupType, true) // XXX do it here for now + bench.Start("reloc") + ctxt.reloc2() + } bench.Start("Asmb2") thearch.Asmb2(ctxt) diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 775a2cb522..2abd0e60e1 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -2016,7 +2016,7 @@ func (l *Loader) preprocess(arch *sys.Arch, s Sym, name string) { } // Load full contents. -func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { +func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols, needReloc bool) { // create all Symbols first. l.growSyms(l.NSym()) l.growSects(l.NSym()) @@ -2049,7 +2049,9 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { } // allocate a single large slab of relocations for all live symbols - l.relocBatch = make([]sym.Reloc, nr) + if needReloc { + l.relocBatch = make([]sym.Reloc, nr) + } // convert payload-based external symbols into sym.Symbol-based for _, i := range toConvert { @@ -2062,11 +2064,13 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { s.Size = pp.size // 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(i, &relocs, s, false) + if needReloc { + batch := l.relocBatch + s.R = batch[:len(pp.relocs):len(pp.relocs)] + l.relocBatch = batch[len(pp.relocs):] + relocs := l.Relocs(i) + l.convertRelocations(i, &relocs, s, false) + } // Copy data s.P = pp.data @@ -2077,7 +2081,7 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { // load contents of defined symbols for _, o := range l.objs[1:] { - loadObjFull(l, o.r) + loadObjFull(l, o.r, needReloc) } // Note: resolution of ABI aliases is now also handled in @@ -2598,7 +2602,7 @@ func (l *Loader) CreateStaticSym(name string) Sym { return l.newExtSym(name, l.anonVersion) } -func loadObjFull(l *Loader, r *oReader) { +func loadObjFull(l *Loader, r *oReader, needReloc bool) { for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ { // A symbol may be a dup or overwritten. In this case, its // content will actually be provided by a different object @@ -2623,11 +2627,13 @@ func loadObjFull(l *Loader, r *oReader) { s.P = l.OutData(gi) // Relocs - relocs := l.relocs(r, i) - batch := l.relocBatch - s.R = batch[:relocs.Count():relocs.Count()] - l.relocBatch = batch[relocs.Count():] - l.convertRelocations(gi, &relocs, s, false) + if needReloc { + relocs := l.relocs(r, i) + batch := l.relocBatch + s.R = batch[:relocs.Count():relocs.Count()] + l.relocBatch = batch[relocs.Count():] + l.convertRelocations(gi, &relocs, s, false) + } // Aux symbol info auxs := r.Auxs(i)