diff options
153 files changed, 40239 insertions, 4318 deletions
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go index 618b08cc36..e8535ae9ac 100644 --- a/src/cmd/asm/internal/flags/flags.go +++ b/src/cmd/asm/internal/flags/flags.go @@ -23,9 +23,10 @@ var ( Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries") AllErrors = flag.Bool("e", false, "no limit on number of errors reported") SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble") - Newobj = flag.Bool("newobj", false, "use new object file format") + Importpath = flag.String("p", "", "set expected package import to path") + Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)") - Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)") + Go115Newobj = flag.Bool("go115newobj", true, "use new object file format") ) var ( diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index 21c8bd963a..a927de854b 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -40,7 +40,7 @@ func main() { } ctxt.Flag_dynlink = *flags.Dynlink ctxt.Flag_shared = *flags.Shared || *flags.Dynlink - ctxt.Flag_newobj = *flags.Newobj + ctxt.Flag_go115newobj = *flags.Go115Newobj switch *flags.Spectre { default: log.Printf("unknown setting -spectre=%s", *flags.Spectre) @@ -87,7 +87,7 @@ func main() { pList.Firstpc, ok = parser.Parse() // reports errors to parser.Errorf if ok { - obj.Flushplist(ctxt, pList, nil, "") + obj.Flushplist(ctxt, pList, nil, *flags.Importpath) } } if !ok { diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index 7ee0876e8b..917bf2394a 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -991,7 +991,7 @@ func (w *exportWriter) linkname(s *types.Sym) { } func (w *exportWriter) symIdx(s *types.Sym) { - if Ctxt.Flag_newobj { + if Ctxt.Flag_go115newobj { lsym := s.Linksym() if lsym.PkgIdx > goobj2.PkgIdxSelf || (lsym.PkgIdx == goobj2.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" { // Don't export index for non-package symbols, linkname'd symbols, diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index f99b70ae27..f881a33ea7 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -687,7 +687,7 @@ func (r *importReader) linkname(s *types.Sym) { } func (r *importReader) symIdx(s *types.Sym) { - if Ctxt.Flag_newobj { + if Ctxt.Flag_go115newobj { lsym := s.Linksym() idx := int32(r.int64()) if idx != -1 { diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 81d31c2007..d04c09c93e 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -280,7 +280,7 @@ func Main(archInit func(*Arch)) { flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`") flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects") flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF") - flag.BoolVar(&Ctxt.Flag_newobj, "newobj", false, "use new object file format") + flag.BoolVar(&Ctxt.Flag_go115newobj, "go115newobj", true, "use new object file format") flag.StringVar(&jsonLogOpt, "json", "", "version,destination for JSON compiler/optimizer logging") objabi.Flagparse(usage) @@ -314,7 +314,7 @@ func Main(archInit func(*Arch)) { // Record flags that affect the build result. (And don't // record flags that don't, since that would cause spurious // changes in the binary.) - recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre", "newobj") + recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre", "go115newobj") if smallFrames { maxStackVarSize = 128 * 1024 diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index a07e64b472..5ec2381589 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -73,6 +73,7 @@ var bootstrapDirs = []string{ "cmd/link/internal/amd64", "cmd/link/internal/arm", "cmd/link/internal/arm64", + "cmd/link/internal/benchmark", "cmd/link/internal/ld", "cmd/link/internal/loadelf", "cmd/link/internal/loader", @@ -81,7 +82,6 @@ var bootstrapDirs = []string{ "cmd/link/internal/loadxcoff", "cmd/link/internal/mips", "cmd/link/internal/mips64", - "cmd/link/internal/objfile", "cmd/link/internal/ppc64", "cmd/link/internal/riscv64", "cmd/link/internal/s390x", diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 7d17c0c01e..78db845ae7 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -37,6 +37,17 @@ func (gcToolchain) linker() string { return base.Tool("link") } +func pkgPath(a *Action) string { + p := a.Package + ppath := p.ImportPath + if cfg.BuildBuildmode == "plugin" { + ppath = pluginPath(a) + } else if p.Name == "main" && !p.Internal.ForceLibrary { + ppath = "main" + } + return ppath +} + func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) { p := a.Package objdir := a.Objdir @@ -47,12 +58,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, s ofile = objdir + out } - pkgpath := p.ImportPath - if cfg.BuildBuildmode == "plugin" { - pkgpath = pluginPath(a) - } else if p.Name == "main" && !p.Internal.ForceLibrary { - pkgpath = "main" - } + pkgpath := pkgPath(a) gcargs := []string{"-p", pkgpath} if p.Module != nil && p.Module.GoVersion != "" && allowedVersion(p.Module.GoVersion) { gcargs = append(gcargs, "-lang=go"+p.Module.GoVersion) @@ -240,7 +246,8 @@ func (a *Action) trimpath() string { func asmArgs(a *Action, p *load.Package) []interface{} { // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files. inc := filepath.Join(cfg.GOROOT, "pkg", "include") - args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags} + pkgpath := pkgPath(a) + args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags} if p.ImportPath == "runtime" && cfg.Goarch == "386" { for _, arg := range forcedAsmflags { if arg == "-dynlink" { diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index 56b44a1ab5..a17b574cdd 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -18,6 +18,9 @@ import ( "strings" ) +// TODO(go115newobj): clean up. Some constant prefixes here are no longer +// needed in the new object files. + // InfoPrefix is the prefix for all the symbols containing DWARF info entries. const InfoPrefix = "go.info." @@ -48,7 +51,7 @@ var logDwarf bool // Sym represents a symbol. type Sym interface { - Len() int64 + Length(dwarfContext interface{}) int64 } // A Var represents a local variable or a function parameter. @@ -1279,7 +1282,7 @@ func PutInlinedFunc(ctxt Context, s *FnState, callersym Sym, callIdx int) error putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, callee) if abbrev == DW_ABRV_INLINED_SUBROUTINE_RANGES { - putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Len(), s.Ranges) + putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges) s.PutRanges(ctxt, ic.Ranges) } else { st := ic.Ranges[0].Start @@ -1440,7 +1443,7 @@ func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, s.StartPC) } else { Uleb128put(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES) - putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Len(), s.Ranges) + putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges) s.PutRanges(ctxt, scope.Ranges) } @@ -1585,7 +1588,7 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, } if abbrevUsesLoclist(abbrev) { - putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Loc.Len(), s.Loc) + putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Loc.Length(ctxt), s.Loc) v.PutLocationList(s.Loc, s.StartPC) } else { loc := encbuf[:0] diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go index 48537d2b1c..44e619cabb 100644 --- a/src/cmd/internal/goobj/read.go +++ b/src/cmd/internal/goobj/read.go @@ -11,6 +11,7 @@ package goobj import ( "bufio" "bytes" + "cmd/internal/goobj2" "cmd/internal/objabi" "errors" "fmt" @@ -507,7 +508,7 @@ func (r *objReader) parseObject(prefix []byte) error { if err != nil { return err } - if bytes.Equal(p, []byte("\x00go114LD")) { + if bytes.Equal(p, []byte(goobj2.Magic)) { r.readNew() return nil } diff --git a/src/cmd/internal/goobj/readnew.go b/src/cmd/internal/goobj/readnew.go index 3f9d0d1db6..3a5a016514 100644 --- a/src/cmd/internal/goobj/readnew.go +++ b/src/cmd/internal/goobj/readnew.go @@ -57,9 +57,8 @@ func (r *objReader) readNew() { pkg := pkglist[p] return SymID{fmt.Sprintf("%s.<#%d>", pkg, s.SymIdx), 0} } - sym := goobj2.Sym{} - sym.Read(rr, rr.SymOff(i)) - return SymID{sym.Name, abiToVer(sym.ABI)} + sym := rr.Sym2(i) + return SymID{sym.Name(rr), abiToVer(sym.ABI())} } // Read things for the current goobj API for now. @@ -69,16 +68,15 @@ func (r *objReader) readNew() { n := rr.NSym() + rr.NNonpkgdef() + rr.NNonpkgref() ndef := rr.NSym() + rr.NNonpkgdef() for i := 0; i < n; i++ { - osym := goobj2.Sym{} - osym.Read(rr, rr.SymOff(i)) - if osym.Name == "" { + osym := rr.Sym2(i) + if osym.Name(rr) == "" { continue // not a real symbol } // In a symbol name in an object file, "". denotes the // prefix for the package in which the object file has been found. // Expand it. - name := strings.ReplaceAll(osym.Name, `"".`, r.pkgprefix) - symID := SymID{Name: name, Version: abiToVer(osym.ABI)} + name := strings.ReplaceAll(osym.Name(rr), `"".`, r.pkgprefix) + symID := SymID{Name: name, Version: abiToVer(osym.ABI())} r.p.SymRefs = append(r.p.SymRefs, symID) if i >= ndef { @@ -91,45 +89,43 @@ func (r *objReader) readNew() { sym := Sym{ SymID: symID, - Kind: objabi.SymKind(osym.Type), + Kind: objabi.SymKind(osym.Type()), DupOK: osym.Dupok(), - Size: int64(osym.Siz), + Size: int64(osym.Siz()), Data: Data{int64(start + dataOff), siz}, } r.p.Syms = append(r.p.Syms, &sym) // Reloc - nreloc := rr.NReloc(i) - sym.Reloc = make([]Reloc, nreloc) - for j := 0; j < nreloc; j++ { - rel := goobj2.Reloc{} - rel.Read(rr, rr.RelocOff(i, j)) + relocs := rr.Relocs2(i) + sym.Reloc = make([]Reloc, len(relocs)) + for j := range relocs { + rel := &relocs[j] sym.Reloc[j] = Reloc{ - Offset: int64(rel.Off), - Size: int64(rel.Siz), - Type: objabi.RelocType(rel.Type), - Add: rel.Add, - Sym: resolveSymRef(rel.Sym), + Offset: int64(rel.Off()), + Size: int64(rel.Siz()), + Type: objabi.RelocType(rel.Type()), + Add: rel.Add(), + Sym: resolveSymRef(rel.Sym()), } } // Aux symbol info isym := -1 funcdata := make([]goobj2.SymRef, 0, 4) - naux := rr.NAux(i) - for j := 0; j < naux; j++ { - a := goobj2.Aux{} - a.Read(rr, rr.AuxOff(i, j)) - switch a.Type { + auxs := rr.Auxs2(i) + for j := range auxs { + a := &auxs[j] + switch a.Type() { case goobj2.AuxGotype: - sym.Type = resolveSymRef(a.Sym) + sym.Type = resolveSymRef(a.Sym()) case goobj2.AuxFuncInfo: - if a.Sym.PkgIdx != goobj2.PkgIdxSelf { + if a.Sym().PkgIdx != goobj2.PkgIdxSelf { panic("funcinfo symbol not defined in current package") } - isym = int(a.Sym.SymIdx) + isym = int(a.Sym().SymIdx) case goobj2.AuxFuncdata: - funcdata = append(funcdata, a.Sym) + funcdata = append(funcdata, a.Sym()) case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines: // nothing to do default: @@ -149,7 +145,7 @@ func (r *objReader) readNew() { f := &Func{ Args: int64(info.Args), Frame: int64(info.Locals), - NoSplit: info.NoSplit != 0, + NoSplit: osym.NoSplit(), Leaf: osym.Leaf(), TopFrame: osym.TopFrame(), PCSP: Data{int64(pcdataBase + info.Pcsp), int64(info.Pcfile - info.Pcsp)}, diff --git a/src/cmd/internal/goobj2/funcinfo.go b/src/cmd/internal/goobj2/funcinfo.go index 8620931970..053d7adc15 100644 --- a/src/cmd/internal/goobj2/funcinfo.go +++ b/src/cmd/internal/goobj2/funcinfo.go @@ -14,8 +14,6 @@ import ( // // TODO: make each pcdata a separate symbol? type FuncInfo struct { - NoSplit uint8 - Args uint32 Locals uint32 @@ -32,8 +30,6 @@ type FuncInfo struct { } func (a *FuncInfo) Write(w *bytes.Buffer) { - w.WriteByte(a.NoSplit) - var b [4]byte writeUint32 := func(x uint32) { binary.LittleEndian.PutUint32(b[:], x) @@ -68,9 +64,6 @@ func (a *FuncInfo) Write(w *bytes.Buffer) { } func (a *FuncInfo) Read(b []byte) { - a.NoSplit = b[0] - b = b[1:] - readUint32 := func() uint32 { x := binary.LittleEndian.Uint32(b) b = b[4:] @@ -107,6 +100,16 @@ func (a *FuncInfo) Read(b []byte) { } } +// Accessors reading only some fields. +// TODO: more accessors. + +func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) } + +// return start and end offsets. +func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) { + return binary.LittleEndian.Uint32(b[8:]), binary.LittleEndian.Uint32(b[12:]) +} + // InlTreeNode is the serialized form of FileInfo.InlTree. type InlTreeNode struct { Parent int32 diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go index 4c364b0c54..52544bf773 100644 --- a/src/cmd/internal/goobj2/objfile.go +++ b/src/cmd/internal/goobj2/objfile.go @@ -19,24 +19,23 @@ import ( // New object file format. // // Header struct { -// Magic [...]byte // "\x00go114LD" +// Magic [...]byte // "\x00go115ld" // Flags uint32 // // TODO: Fingerprint // Offsets [...]uint32 // byte offset of each block below // } // // Strings [...]struct { -// Len uint32 // Data [...]byte // } // -// Autolib [...]stringOff // imported packages (for file loading) // TODO: add fingerprints -// PkgIndex [...]stringOff // referenced packages by index +// Autolib [...]string // imported packages (for file loading) // TODO: add fingerprints +// PkgIndex [...]string // referenced packages by index // -// DwarfFiles [...]stringOff +// DwarfFiles [...]string // // SymbolDefs [...]struct { -// Name stringOff +// Name string // ABI uint16 // Type uint8 // Flag uint8 @@ -69,8 +68,8 @@ import ( // Data [...]byte // Pcdata [...]byte // -// stringOff is a uint32 (?) offset that points to the corresponding -// string, which is a uint32 length followed by that number of bytes. +// string is encoded as is a uint32 length followed by a uint32 offset +// that points to the corresponding string bytes. // // symRef is struct { PkgIdx, SymIdx uint32 }. // @@ -118,6 +117,8 @@ import ( // Currently a symbol's Gotype and FuncInfo are auxiliary symbols. We // may make use of aux symbols in more cases, e.g. DWARF symbols. +const stringRefSize = 8 // two uint32s + // Package Index. const ( PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols @@ -153,7 +154,7 @@ type Header struct { Offsets [NBlk]uint32 } -const Magic = "\x00go114LD" +const Magic = "\x00go115ld" func (h *Header) Write(w *Writer) { w.RawString(h.Magic) @@ -185,11 +186,12 @@ func (h *Header) Size() int { // Symbol definition. type Sym struct { - Name string - ABI uint16 - Type uint8 - Flag uint8 - Siz uint32 + Name string + ABI uint16 + Type uint8 + Flag uint8 + Siz uint32 + Align uint32 } const SymABIstatic = ^uint16(0) @@ -203,7 +205,7 @@ const ( SymFlagLocal SymFlagTypelink SymFlagLeaf - SymFlagCFunc + SymFlagNoSplit SymFlagReflectMethod SymFlagGoType SymFlagTopFrame @@ -215,28 +217,33 @@ func (s *Sym) Write(w *Writer) { w.Uint8(s.Type) w.Uint8(s.Flag) w.Uint32(s.Siz) + w.Uint32(s.Align) } -func (s *Sym) Read(r *Reader, off uint32) { - s.Name = r.StringRef(off) - s.ABI = r.uint16At(off + 4) - s.Type = r.uint8At(off + 6) - s.Flag = r.uint8At(off + 7) - s.Siz = r.uint32At(off + 8) -} +const SymSize = stringRefSize + 2 + 1 + 1 + 4 + 4 + +type Sym2 [SymSize]byte -func (s *Sym) Size() int { - return 4 + 2 + 1 + 1 + 4 +func (s *Sym2) Name(r *Reader) string { + len := binary.LittleEndian.Uint32(s[:]) + off := binary.LittleEndian.Uint32(s[4:]) + return r.StringAt(off, len) } -func (s *Sym) Dupok() bool { return s.Flag&SymFlagDupok != 0 } -func (s *Sym) Local() bool { return s.Flag&SymFlagLocal != 0 } -func (s *Sym) Typelink() bool { return s.Flag&SymFlagTypelink != 0 } -func (s *Sym) Leaf() bool { return s.Flag&SymFlagLeaf != 0 } -func (s *Sym) CFunc() bool { return s.Flag&SymFlagCFunc != 0 } -func (s *Sym) ReflectMethod() bool { return s.Flag&SymFlagReflectMethod != 0 } -func (s *Sym) IsGoType() bool { return s.Flag&SymFlagGoType != 0 } -func (s *Sym) TopFrame() bool { return s.Flag&SymFlagTopFrame != 0 } +func (s *Sym2) ABI() uint16 { return binary.LittleEndian.Uint16(s[8:]) } +func (s *Sym2) Type() uint8 { return s[10] } +func (s *Sym2) Flag() uint8 { return s[11] } +func (s *Sym2) Siz() uint32 { return binary.LittleEndian.Uint32(s[12:]) } +func (s *Sym2) Align() uint32 { return binary.LittleEndian.Uint32(s[16:]) } + +func (s *Sym2) Dupok() bool { return s.Flag()&SymFlagDupok != 0 } +func (s *Sym2) Local() bool { return s.Flag()&SymFlagLocal != 0 } +func (s *Sym2) Typelink() bool { return s.Flag()&SymFlagTypelink != 0 } +func (s *Sym2) Leaf() bool { return s.Flag()&SymFlagLeaf != 0 } +func (s *Sym2) NoSplit() bool { return s.Flag()&SymFlagNoSplit != 0 } +func (s *Sym2) ReflectMethod() bool { return s.Flag()&SymFlagReflectMethod != 0 } +func (s *Sym2) IsGoType() bool { return s.Flag()&SymFlagGoType != 0 } +func (s *Sym2) TopFrame() bool { return s.Flag()&SymFlagTopFrame != 0 } // Symbol reference. type SymRef struct { @@ -249,15 +256,6 @@ func (s *SymRef) Write(w *Writer) { w.Uint32(s.SymIdx) } -func (s *SymRef) Read(r *Reader, off uint32) { - s.PkgIdx = r.uint32At(off) - s.SymIdx = r.uint32At(off + 4) -} - -func (s *SymRef) Size() int { - return 4 + 4 -} - // Relocation. type Reloc struct { Off int32 @@ -275,16 +273,33 @@ func (r *Reloc) Write(w *Writer) { r.Sym.Write(w) } -func (o *Reloc) Read(r *Reader, off uint32) { - o.Off = r.int32At(off) - o.Siz = r.uint8At(off + 4) - o.Type = r.uint8At(off + 5) - o.Add = r.int64At(off + 6) - o.Sym.Read(r, off+14) +const RelocSize = 4 + 1 + 1 + 8 + 8 + +type Reloc2 [RelocSize]byte + +func (r *Reloc2) Off() int32 { return int32(binary.LittleEndian.Uint32(r[:])) } +func (r *Reloc2) Siz() uint8 { return r[4] } +func (r *Reloc2) Type() uint8 { return r[5] } +func (r *Reloc2) Add() int64 { return int64(binary.LittleEndian.Uint64(r[6:])) } +func (r *Reloc2) Sym() SymRef { + return SymRef{binary.LittleEndian.Uint32(r[14:]), binary.LittleEndian.Uint32(r[18:])} +} + +func (r *Reloc2) SetOff(x int32) { binary.LittleEndian.PutUint32(r[:], uint32(x)) } +func (r *Reloc2) SetSiz(x uint8) { r[4] = x } +func (r *Reloc2) SetType(x uint8) { r[5] = x } +func (r *Reloc2) SetAdd(x int64) { binary.LittleEndian.PutUint64(r[6:], uint64(x)) } +func (r *Reloc2) SetSym(x SymRef) { + binary.LittleEndian.PutUint32(r[14:], x.PkgIdx) + binary.LittleEndian.PutUint32(r[18:], x.SymIdx) } -func (r *Reloc) Size() int { - return 4 + 1 + 1 + 8 + r.Sym.Size() +func (r *Reloc2) Set(off int32, size uint8, typ uint8, add int64, sym SymRef) { + r.SetOff(off) + r.SetSiz(size) + r.SetType(typ) + r.SetAdd(add) + r.SetSym(sym) } // Aux symbol info. @@ -311,13 +326,13 @@ func (a *Aux) Write(w *Writer) { a.Sym.Write(w) } -func (a *Aux) Read(r *Reader, off uint32) { - a.Type = r.uint8At(off) - a.Sym.Read(r, off+1) -} +const AuxSize = 1 + 8 -func (a *Aux) Size() int { - return 1 + a.Sym.Size() +type Aux2 [AuxSize]byte + +func (a *Aux2) Type() uint8 { return a[0] } +func (a *Aux2) Sym() SymRef { + return SymRef{binary.LittleEndian.Uint32(a[1:]), binary.LittleEndian.Uint32(a[5:])} } type Writer struct { @@ -335,7 +350,6 @@ func (w *Writer) AddString(s string) { return } w.stringMap[s] = w.off - w.Uint32(uint32(len(s))) w.RawString(s) } @@ -344,6 +358,7 @@ func (w *Writer) StringRef(s string) { if !ok { panic(fmt.Sprintf("writeStringRef: string not added: %q", s)) } + w.Uint32(uint32(len(s))) w.Uint32(off) } @@ -441,9 +456,8 @@ func (r *Reader) uint8At(off uint32) uint8 { return b[0] } -func (r *Reader) StringAt(off uint32) string { - l := r.uint32At(off) - b := r.b[off+4 : off+4+l] +func (r *Reader) StringAt(off uint32, len uint32) string { + b := r.b[off : off+len] if r.readonly { return toString(b) // backed by RO memory, ok to make unsafe string } @@ -465,66 +479,69 @@ func toString(b []byte) string { } func (r *Reader) StringRef(off uint32) string { - return r.StringAt(r.uint32At(off)) + l := r.uint32At(off) + return r.StringAt(r.uint32At(off+4), l) } func (r *Reader) Autolib() []string { - n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / 4 + n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / stringRefSize s := make([]string, n) for i := range s { - off := r.h.Offsets[BlkAutolib] + uint32(i)*4 + off := r.h.Offsets[BlkAutolib] + uint32(i)*stringRefSize s[i] = r.StringRef(off) } return s } func (r *Reader) Pkglist() []string { - n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / 4 + n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / stringRefSize s := make([]string, n) for i := range s { - off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4 + off := r.h.Offsets[BlkPkgIdx] + uint32(i)*stringRefSize s[i] = r.StringRef(off) } return s } func (r *Reader) NPkg() int { - return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / 4 + return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / stringRefSize } func (r *Reader) Pkg(i int) string { - off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4 + off := r.h.Offsets[BlkPkgIdx] + uint32(i)*stringRefSize return r.StringRef(off) } func (r *Reader) NDwarfFile() int { - return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / 4 + return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / stringRefSize } func (r *Reader) DwarfFile(i int) string { - off := r.h.Offsets[BlkDwarfFile] + uint32(i)*4 + off := r.h.Offsets[BlkDwarfFile] + uint32(i)*stringRefSize return r.StringRef(off) } func (r *Reader) NSym() int { - symsiz := (&Sym{}).Size() - return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / symsiz + return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / SymSize } func (r *Reader) NNonpkgdef() int { - symsiz := (&Sym{}).Size() - return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / symsiz + return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / SymSize } func (r *Reader) NNonpkgref() int { - symsiz := (&Sym{}).Size() - return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / symsiz + return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / SymSize } // SymOff returns the offset of the i-th symbol. func (r *Reader) SymOff(i int) uint32 { - symsiz := (&Sym{}).Size() - return r.h.Offsets[BlkSymdef] + uint32(i*symsiz) + return r.h.Offsets[BlkSymdef] + uint32(i*SymSize) +} + +// Sym2 returns a pointer to the i-th symbol. +func (r *Reader) Sym2(i int) *Sym2 { + off := r.SymOff(i) + return (*Sym2)(unsafe.Pointer(&r.b[off])) } // NReloc returns the number of relocations of the i-th symbol. @@ -537,8 +554,20 @@ func (r *Reader) NReloc(i int) int { func (r *Reader) RelocOff(i int, j int) uint32 { relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4) relocIdx := r.uint32At(relocIdxOff) - relocsiz := (&Reloc{}).Size() - return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(relocsiz) + return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(RelocSize) +} + +// Reloc2 returns a pointer to the j-th relocation of the i-th symbol. +func (r *Reader) Reloc2(i int, j int) *Reloc2 { + off := r.RelocOff(i, j) + return (*Reloc2)(unsafe.Pointer(&r.b[off])) +} + +// Relocs2 returns a pointer to the relocations of the i-th symbol. +func (r *Reader) Relocs2(i int) []Reloc2 { + off := r.RelocOff(i, 0) + n := r.NReloc(i) + return (*[1 << 20]Reloc2)(unsafe.Pointer(&r.b[off]))[:n:n] } // NAux returns the number of aux symbols of the i-th symbol. @@ -551,8 +580,20 @@ func (r *Reader) NAux(i int) int { func (r *Reader) AuxOff(i int, j int) uint32 { auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4) auxIdx := r.uint32At(auxIdxOff) - auxsiz := (&Aux{}).Size() - return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(auxsiz) + return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(AuxSize) +} + +// Aux2 returns a pointer to the j-th aux symbol of the i-th symbol. +func (r *Reader) Aux2(i int, j int) *Aux2 { + off := r.AuxOff(i, j) + return (*Aux2)(unsafe.Pointer(&r.b[off])) +} + +// Auxs2 returns the aux symbols of the i-th symbol. +func (r *Reader) Auxs2(i int) []Aux2 { + off := r.AuxOff(i, 0) + n := r.NAux(i) + return (*[1 << 20]Aux2)(unsafe.Pointer(&r.b[off]))[:n:n] } // DataOff returns the offset of the i-th symbol's data. @@ -563,12 +604,17 @@ func (r *Reader) DataOff(i int) uint32 { // DataSize returns the size of the i-th symbol's data. func (r *Reader) DataSize(i int) int { - return int(r.DataOff(i+1) - r.DataOff(i)) + dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4) + return int(r.uint32At(dataIdxOff+4) - r.uint32At(dataIdxOff)) } // Data returns the i-th symbol's data. func (r *Reader) Data(i int) []byte { - return r.BytesAt(r.DataOff(i), r.DataSize(i)) + dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4) + base := r.h.Offsets[BlkData] + off := r.uint32At(dataIdxOff) + end := r.uint32At(dataIdxOff + 4) + return r.BytesAt(base+off, int(end-off)) } // AuxDataBase returns the base offset of the aux data block. diff --git a/src/cmd/internal/goobj2/objfile_test.go b/src/cmd/internal/goobj2/objfile_test.go new file mode 100644 index 0000000000..ee15136cbe --- /dev/null +++ b/src/cmd/internal/goobj2/objfile_test.go @@ -0,0 +1,39 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goobj2 + +import ( + "bufio" + "bytes" + "cmd/internal/bio" + "testing" +) + +func dummyWriter() *Writer { + var buf bytes.Buffer + wr := &bio.Writer{Writer: bufio.NewWriter(&buf)} // hacky: no file, so cannot seek + return NewWriter(wr) +} + +func TestSize(t *testing.T) { + // This test checks that hard-coded sizes match the actual sizes + // in the object file format. + tests := []struct { + x interface{ Write(*Writer) } + want uint32 + }{ + {&Reloc{}, RelocSize}, + {&Aux{}, AuxSize}, + } + w := dummyWriter() + for _, test := range tests { + off0 := w.off + test.x.Write(w) + got := w.off - off0 + if got != test.want { + t.Errorf("size(%T) mismatch: %d bytes written, but size=%d", test.x, got, test.want) + } + } +} diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 0879c611ba..ac3621bf74 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -653,7 +653,7 @@ type Link struct { Flag_linkshared bool Flag_optimize bool Flag_locationlists bool - Flag_newobj bool // use new object file format + Flag_go115newobj bool // use new object file format Retpoline bool // emit use of retpoline stubs for indirect jmp/call Bso *bufio.Writer Pathname string diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 46e8a551ad..cb6b709066 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -82,7 +82,7 @@ func newObjWriter(ctxt *Link, b *bufio.Writer, pkgpath string) *objWriter { } func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) { - if ctxt.Flag_newobj { + if ctxt.Flag_go115newobj { WriteObjFile2(ctxt, bout, pkgpath) return } @@ -229,7 +229,11 @@ func (w *objWriter) writeRefs(s *LSym) { } func (ctxt *Link) writeSymDebug(s *LSym) { - fmt.Fprintf(ctxt.Bso, "%s ", s.Name) + ctxt.writeSymDebugNamed(s, s.Name) +} + +func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { + fmt.Fprintf(ctxt.Bso, "%s ", name) if s.Type != 0 { fmt.Fprintf(ctxt.Bso, "%v ", s.Type) } @@ -500,12 +504,21 @@ func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) r := &ls.R[len(ls.R)-1] r.Type = objabi.R_DWARFSECREF } + func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) { ls := s.(*LSym) rsym := f.(*LSym) - ls.WriteAddr(c.Link, ls.Size, 4, rsym, 0) - r := &ls.R[len(ls.R)-1] - r.Type = objabi.R_DWARFFILEREF + if c.Link.Flag_go115newobj { + fidx := c.Link.PosTable.FileIndex(rsym.Name) + // Note the +1 here -- the value we're writing is going to be an + // index into the DWARF line table file section, whose entries + // are numbered starting at 1, not 0. + ls.WriteInt(c.Link, ls.Size, 4, int64(fidx+1)) + } else { + ls.WriteAddr(c.Link, ls.Size, 4, rsym, 0) + r := &ls.R[len(ls.R)-1] + r.Type = objabi.R_DWARFFILEREF + } } func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 { @@ -543,20 +556,37 @@ func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, ctxt.Diag("dwarfSym of non-TEXT %v", s) } if s.Func.dwarfInfoSym == nil { - s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name) - if ctxt.Flag_locationlists { - s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name) + if ctxt.Flag_go115newobj { + s.Func.dwarfInfoSym = &LSym{ + Type: objabi.SDWARFINFO, + } + if ctxt.Flag_locationlists { + s.Func.dwarfLocSym = &LSym{ + Type: objabi.SDWARFLOC, + } + } + s.Func.dwarfRangesSym = &LSym{ + Type: objabi.SDWARFRANGE, + } + s.Func.dwarfDebugLinesSym = &LSym{ + Type: objabi.SDWARFLINES, + } + } else { + s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name) + if ctxt.Flag_locationlists { + s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name) + } + s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name) + s.Func.dwarfDebugLinesSym = ctxt.LookupDerived(s, dwarf.DebugLinesPrefix+s.Name) } - s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name) if s.WasInlined() { s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s) } - s.Func.dwarfDebugLinesSym = ctxt.LookupDerived(s, dwarf.DebugLinesPrefix+s.Name) } return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym } -func (s *LSym) Len() int64 { +func (s *LSym) Length(dwarfContext interface{}) int64 { return s.Size } diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go index 69019e033d..6261924d0d 100644 --- a/src/cmd/internal/obj/objfile2.go +++ b/src/cmd/internal/obj/objfile2.go @@ -18,9 +18,8 @@ import ( // Entry point of writing new object file. func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) { - if ctxt.Debugasm > 0 { - ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug) - } + + debugAsmEmit(ctxt) genFuncInfoSyms(ctxt) @@ -60,7 +59,7 @@ func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) { // DWARF file table h.Offsets[goobj2.BlkDwarfFile] = w.Offset() for _, f := range ctxt.PosTable.DebugLinesFileTable() { - w.StringRef(f) + w.StringRef(filepath.ToSlash(f)) } // Symbol definitions @@ -207,7 +206,7 @@ func (w *writer) StringTable() { } }) for _, f := range w.ctxt.PosTable.DebugLinesFileTable() { - w.AddString(f) + w.AddString(filepath.ToSlash(f)) } } @@ -229,8 +228,8 @@ func (w *writer) Sym(s *LSym) { if s.Leaf() { flag |= goobj2.SymFlagLeaf } - if s.CFunc() { - flag |= goobj2.SymFlagCFunc + if s.NoSplit() { + flag |= goobj2.SymFlagNoSplit } if s.ReflectMethod() { flag |= goobj2.SymFlagReflectMethod @@ -245,12 +244,17 @@ func (w *writer) Sym(s *LSym) { if strings.HasPrefix(name, "gofile..") { name = filepath.ToSlash(name) } + var align uint32 + if s.Func != nil { + align = uint32(s.Func.Align) + } o := goobj2.Sym{ - Name: name, - ABI: abi, - Type: uint8(s.Type), - Flag: flag, - Siz: uint32(s.Size), + Name: name, + ABI: abi, + Type: uint8(s.Type), + Flag: flag, + Siz: uint32(s.Size), + Align: align, } o.Write(w.Writer) } @@ -300,28 +304,28 @@ func (w *writer) Aux(s *LSym) { o.Write(w.Writer) } - if s.Func.dwarfInfoSym != nil { + if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { o := goobj2.Aux{ Type: goobj2.AuxDwarfInfo, Sym: makeSymRef(s.Func.dwarfInfoSym), } o.Write(w.Writer) } - if s.Func.dwarfLocSym != nil { + if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { o := goobj2.Aux{ Type: goobj2.AuxDwarfLoc, Sym: makeSymRef(s.Func.dwarfLocSym), } o.Write(w.Writer) } - if s.Func.dwarfRangesSym != nil { + if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { o := goobj2.Aux{ Type: goobj2.AuxDwarfRanges, Sym: makeSymRef(s.Func.dwarfRangesSym), } o.Write(w.Writer) } - if s.Func.dwarfDebugLinesSym != nil { + if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { o := goobj2.Aux{ Type: goobj2.AuxDwarfLines, Sym: makeSymRef(s.Func.dwarfDebugLinesSym), @@ -340,16 +344,16 @@ func nAuxSym(s *LSym) int { if s.Func != nil { // FuncInfo is an aux symbol, each Funcdata is an aux symbol n += 1 + len(s.Func.Pcln.Funcdata) - if s.Func.dwarfInfoSym != nil { + if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { n++ } - if s.Func.dwarfLocSym != nil { + if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { n++ } - if s.Func.dwarfRangesSym != nil { + if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { n++ } - if s.Func.dwarfDebugLinesSym != nil { + if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { n++ } } @@ -366,14 +370,9 @@ func genFuncInfoSyms(ctxt *Link) { if s.Func == nil { continue } - nosplit := uint8(0) - if s.NoSplit() { - nosplit = 1 - } o := goobj2.FuncInfo{ - NoSplit: nosplit, - Args: uint32(s.Func.Args), - Locals: uint32(s.Func.Locals), + Args: uint32(s.Func.Args), + Locals: uint32(s.Func.Locals), } pc := &s.Func.Pcln o.Pcsp = pcdataoff @@ -424,6 +423,43 @@ func genFuncInfoSyms(ctxt *Link) { infosyms = append(infosyms, isym) s.Func.FuncInfoSym = isym b.Reset() + + dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym, s.Func.dwarfInfoSym} + for _, s := range dwsyms { + if s == nil || s.Size == 0 { + continue + } + s.PkgIdx = goobj2.PkgIdxSelf + s.SymIdx = symidx + s.Set(AttrIndexed, true) + symidx++ + infosyms = append(infosyms, s) + } } ctxt.defs = append(ctxt.defs, infosyms...) } + +// debugDumpAux is a dumper for selected aux symbols. +func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) { + // Most aux symbols (ex: funcdata) are not interesting-- + // pick out just the DWARF ones for now. + if aux.Type != objabi.SDWARFLOC && + aux.Type != objabi.SDWARFINFO && + aux.Type != objabi.SDWARFLINES && + aux.Type != objabi.SDWARFRANGE { + return + } + ctxt.writeSymDebugNamed(aux, "aux for "+par.Name) +} + +func debugAsmEmit(ctxt *Link) { + if ctxt.Debugasm > 0 { + ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug) + if ctxt.Debugasm > 1 { + fn := func(par *LSym, aux *LSym) { + writeAuxSymDebug(ctxt, par, aux) + } + ctxt.traverseAuxSyms(traverseAux, fn) + } + } +} diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go index 7579dd0390..44ec4602de 100644 --- a/src/cmd/internal/obj/plist.go +++ b/src/cmd/internal/obj/plist.go @@ -109,7 +109,9 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string continue } linkpcln(ctxt, s) - ctxt.populateDWARF(plist.Curfn, s, myimportpath) + if myimportpath != "" { + ctxt.populateDWARF(plist.Curfn, s, myimportpath) + } } } @@ -136,21 +138,27 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) { s.Type = objabi.STEXT ctxt.Text = append(ctxt.Text, s) - // Set up DWARF entries for s. + // Set up DWARF entries for s info, loc, ranges, _, lines := ctxt.dwarfSym(s) - info.Type = objabi.SDWARFINFO - info.Set(AttrDuplicateOK, s.DuplicateOK()) - if loc != nil { - loc.Type = objabi.SDWARFLOC - loc.Set(AttrDuplicateOK, s.DuplicateOK()) - ctxt.Data = append(ctxt.Data, loc) + + // When using new object files, the DWARF symbols are unnamed aux + // symbols and don't need to be added to ctxt.Data. + // But the old object file still needs them. + if !ctxt.Flag_go115newobj { + info.Type = objabi.SDWARFINFO + info.Set(AttrDuplicateOK, s.DuplicateOK()) + if loc != nil { + loc.Type = objabi.SDWARFLOC + loc.Set(AttrDuplicateOK, s.DuplicateOK()) + ctxt.Data = append(ctxt.Data, loc) + } + ranges.Type = objabi.SDWARFRANGE + ranges.Set(AttrDuplicateOK, s.DuplicateOK()) + ctxt.Data = append(ctxt.Data, info, ranges) + lines.Type = objabi.SDWARFLINES + lines.Set(AttrDuplicateOK, s.DuplicateOK()) + ctxt.Data = append(ctxt.Data, lines) } - ranges.Type = objabi.SDWARFRANGE - ranges.Set(AttrDuplicateOK, s.DuplicateOK()) - ctxt.Data = append(ctxt.Data, info, ranges) - lines.Type = objabi.SDWARFLINES - lines.Set(AttrDuplicateOK, s.DuplicateOK()) - ctxt.Data = append(ctxt.Data, lines) } func (ctxt *Link) Globl(s *LSym, size int64, flag int) { diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index 3ef886651f..03ce8ddc5a 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -164,7 +164,7 @@ func (ctxt *Link) Int64Sym(i int64) *LSym { // asm is set to true if this is called by the assembler (i.e. not the compiler), // in which case all the symbols are non-package (for now). func (ctxt *Link) NumberSyms(asm bool) { - if !ctxt.Flag_newobj { + if !ctxt.Flag_go115newobj { return } @@ -298,28 +298,72 @@ func (ctxt *Link) traverseSyms(flag traverseFlag, fn func(*LSym)) { fn(s.Gotype) } if s.Type == objabi.STEXT { - pc := &s.Func.Pcln - for _, d := range pc.Funcdata { - if d != nil { - fn(d) - } - } - for _, f := range pc.File { - if fsym := ctxt.Lookup(f); fsym != nil { - fn(fsym) - } - } - for _, call := range pc.InlTree.nodes { - if call.Func != nil { - fn(call.Func) - } - f, _ := linkgetlineFromPos(ctxt, call.Pos) - if fsym := ctxt.Lookup(f); fsym != nil { - fn(fsym) - } + f := func(parent *LSym, aux *LSym) { + fn(aux) } + ctxt.traverseFuncAux(flag, s, f) } } } } } + +func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent *LSym, aux *LSym)) { + pc := &fsym.Func.Pcln + if flag&traverseAux == 0 { + // NB: should it become necessary to walk aux sym reloc references + // without walking the aux syms themselves, this can be changed. + panic("should not be here") + } + for _, d := range pc.Funcdata { + if d != nil { + fn(fsym, d) + } + } + for _, f := range pc.File { + if filesym := ctxt.Lookup(f); filesym != nil { + fn(fsym, filesym) + } + } + for _, call := range pc.InlTree.nodes { + if call.Func != nil { + fn(fsym, call.Func) + } + f, _ := linkgetlineFromPos(ctxt, call.Pos) + if filesym := ctxt.Lookup(f); filesym != nil { + fn(fsym, filesym) + } + } + dwsyms := []*LSym{fsym.Func.dwarfRangesSym, fsym.Func.dwarfLocSym, fsym.Func.dwarfDebugLinesSym, fsym.Func.dwarfInfoSym} + for _, dws := range dwsyms { + if dws == nil || dws.Size == 0 { + continue + } + fn(fsym, dws) + if flag&traverseRefs != 0 { + for _, r := range dws.R { + if r.Sym != nil { + fn(dws, r.Sym) + } + } + } + } +} + +// Traverse aux symbols, calling fn for each sym/aux pair. +func (ctxt *Link) traverseAuxSyms(flag traverseFlag, fn func(parent *LSym, aux *LSym)) { + lists := [][]*LSym{ctxt.Text, ctxt.Data, ctxt.ABIAliases} + for _, list := range lists { + for _, s := range list { + if s.Gotype != nil { + if flag&traverseDefs != 0 { + fn(s, s.Gotype) + } + } + if s.Type != objabi.STEXT { + continue + } + ctxt.traverseFuncAux(flag, s, fn) + } + } +} diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index 26208cc619..cb1210422d 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -34,9 +34,11 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/elf" "log" + "sync" ) func PADDR(x uint32) uint32 { @@ -98,13 +100,21 @@ func gentext(ctxt *ld.Link) { initarray_entry.AddAddr(ctxt.Arch, initfunc) } -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { +// makeWritable makes a readonly symbol writable if we do opcode rewriting. +func makeWritable(s *sym.Symbol) { + if s.Attr.ReadOnly() { + s.Attr.Set(sym.AttrReadOnly, false) + s.P = append([]byte(nil), s.P...) + } +} + +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { targ := r.Sym switch r.Type { default: if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) return false } @@ -137,8 +147,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { r.Type = objabi.R_PCREL r.Add += 4 if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add += int64(targ.Plt()) } @@ -150,6 +160,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { if targ.Type != sym.SDYNIMPORT { // have symbol if r.Off >= 2 && s.P[r.Off-2] == 0x8b { + makeWritable(s) // turn MOVQ of GOT entry into LEAQ of symbol itself s.P[r.Off-2] = 0x8d @@ -161,10 +172,10 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { // fall back to using GOT and hope for the best (CMOV*) // TODO: just needs relocation, no need to put in .dynsym - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) r.Type = objabi.R_PCREL - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += 4 r.Add += int64(targ.Got()) return true @@ -174,7 +185,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { ld.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", targ.Name) } r.Type = objabi.R_ADDR - if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal { + if target.IsPIE() && target.IsInternal() { // For internal linking PIE, this R_ADDR relocation cannot // be resolved statically. We need to generate a dynamic // relocation. Let the code below handle it. @@ -196,8 +207,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add = int64(targ.Plt()) r.Type = objabi.R_PCREL return true @@ -225,6 +236,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { return false } + makeWritable(s) s.P[r.Off-2] = 0x8d r.Type = objabi.R_PCREL return true @@ -235,9 +247,9 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { if targ.Type != sym.SDYNIMPORT { ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name) } - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) r.Type = objabi.R_PCREL - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += int64(targ.Got()) return true } @@ -249,37 +261,37 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { // nothing to do, the relocation will be laid out in reloc return true } - if ctxt.LinkMode == ld.LinkExternal { + if target.IsExternal() { // External linker will do this relocation. return true } // Internal linking, for both ELF and Mach-O. // Build a PLT entry and change the relocation target to that entry. - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add = int64(targ.Plt()) return true case objabi.R_ADDR: - if s.Type == sym.STEXT && ctxt.IsELF { - if ctxt.HeadType == objabi.Hsolaris { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + if s.Type == sym.STEXT && target.IsElf() { + if target.IsSolaris() { + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add += int64(targ.Plt()) return true } // The code is asking for the address of an external // function. We provide it with the address of the // correspondent GOT symbol. - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += int64(targ.Got()) return true } // Process dynamic relocations for the data sections. - if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal { + if target.IsPIE() && target.IsInternal() { // When internally linking, generate dynamic relocations // for all typical R_ADDR relocations. The exception // are those R_ADDR that are created as part of generating @@ -327,7 +339,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { } } - if ctxt.IsELF { + if target.IsElf() { // Generate R_X86_64_RELATIVE relocations for best // efficiency in the dynamic linker. // @@ -345,14 +357,14 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { // AddAddrPlus is used for r_offset and r_addend to // generate new R_ADDR relocations that will update // these fields in the 'reloc' phase. - rela := ctxt.Syms.Lookup(".rela", 0) - rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) + rela := syms.Rela + rela.AddAddrPlus(target.Arch, s, int64(r.Off)) if r.Siz == 8 { - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_X86_64_RELATIVE))) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_X86_64_RELATIVE))) } else { ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) } - rela.AddAddrPlus(ctxt.Arch, targ, int64(r.Add)) + rela.AddAddrPlus(target.Arch, targ, int64(r.Add)) // Not mark r done here. So we still apply it statically, // so in the file content we'll also have the right offset // to the relocation target. So it can be examined statically @@ -360,7 +372,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { return true } - if ctxt.HeadType == objabi.Hdarwin && s.Size == int64(ctxt.Arch.PtrSize) && r.Off == 0 { + if target.IsDarwin() && s.Size == int64(target.Arch.PtrSize) && r.Off == 0 { // Mach-O relocations are a royal pain to lay out. // They use a compact stateful bytecode representation // that is too much bother to deal with. @@ -371,17 +383,17 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { // just in case the C code assigns to the variable, // and of course it only works for single pointers, // but we only need to support cgo and that's all it needs. - ld.Adddynsym(ctxt, targ) + ld.Adddynsym(target, syms, targ) - got := ctxt.Syms.Lookup(".got", 0) + got := syms.GOT s.Type = got.Type s.Attr |= sym.AttrSubSymbol s.Outer = got s.Sub = got.Sub got.Sub = s s.Value = got.Size - got.AddUint64(ctxt.Arch, 0) - ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(targ.Dynid)) + got.AddUint64(target.Arch, 0) + syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid)) r.Type = objabi.ElfRelocOffset // ignore during relocsym return true } @@ -553,84 +565,82 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, secto return true } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { log.Fatalf("unexpected relocation variant") return t } -func elfsetupplt(ctxt *ld.Link) { - plt := ctxt.Syms.Lookup(".plt", 0) - got := ctxt.Syms.Lookup(".got.plt", 0) - if plt.Size == 0 { +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { // pushq got+8(IP) plt.AddUint8(0xff) plt.AddUint8(0x35) - plt.AddPCRelPlus(ctxt.Arch, got, 8) + plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 8) // jmpq got+16(IP) plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddPCRelPlus(ctxt.Arch, got, 16) + plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 16) // nopl 0(AX) plt.AddUint32(ctxt.Arch, 0x00401f0f) // assume got->size == 0 too - got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0) + got.AddAddrPlus(ctxt.Arch, dynamic, 0) got.AddUint64(ctxt.Arch, 0) got.AddUint64(ctxt.Arch, 0) } } -func addpltsym(ctxt *ld.Link, s *sym.Symbol) { +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Plt() >= 0 { return } - ld.Adddynsym(ctxt, s) + ld.Adddynsym(target, syms, s) - if ctxt.IsELF { - plt := ctxt.Syms.Lookup(".plt", 0) - got := ctxt.Syms.Lookup(".got.plt", 0) - rela := ctxt.Syms.Lookup(".rela.plt", 0) + if target.IsElf() { + plt := syms.PLT + got := syms.GOTPLT + rela := syms.RelaPLT if plt.Size == 0 { - elfsetupplt(ctxt) + panic("plt is not set up") } // jmpq *got+size(IP) plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddPCRelPlus(ctxt.Arch, got, got.Size) + plt.AddPCRelPlus(target.Arch, got, got.Size) // add to got: pointer to current pos in plt - got.AddAddrPlus(ctxt.Arch, plt, plt.Size) + got.AddAddrPlus(target.Arch, plt, plt.Size) // pushq $x plt.AddUint8(0x68) - plt.AddUint32(ctxt.Arch, uint32((got.Size-24-8)/8)) + plt.AddUint32(target.Arch, uint32((got.Size-24-8)/8)) // jmpq .plt plt.AddUint8(0xe9) - plt.AddUint32(ctxt.Arch, uint32(-(plt.Size + 4))) + plt.AddUint32(target.Arch, uint32(-(plt.Size + 4))) // rela - rela.AddAddrPlus(ctxt.Arch, got, got.Size-8) + rela.AddAddrPlus(target.Arch, got, got.Size-8) - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_JMP_SLOT))) - rela.AddUint64(ctxt.Arch, 0) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_JMP_SLOT))) + rela.AddUint64(target.Arch, 0) s.SetPlt(int32(plt.Size - 16)) - } else if ctxt.HeadType == objabi.Hdarwin { + } else if target.IsDarwin() { // To do lazy symbol lookup right, we're supposed // to tell the dynamic loader which library each // symbol comes from and format the link info @@ -641,39 +651,39 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) { // https://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html // has details about what we're avoiding. - addgotsym(ctxt, s) - plt := ctxt.Syms.Lookup(".plt", 0) + addgotsym(target, syms, s) + plt := syms.PLT - ctxt.Syms.Lookup(".linkedit.plt", 0).AddUint32(ctxt.Arch, uint32(s.Dynid)) + syms.LinkEditPLT.AddUint32(target.Arch, uint32(s.Dynid)) // jmpq *got+size(IP) s.SetPlt(int32(plt.Size)) plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddPCRelPlus(ctxt.Arch, ctxt.Syms.Lookup(".got", 0), int64(s.Got())) + plt.AddPCRelPlus(target.Arch, syms.GOT, int64(s.Got())) } else { ld.Errorf(s, "addpltsym: unsupported binary format") } } -func addgotsym(ctxt *ld.Link, s *sym.Symbol) { +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Got() >= 0 { return } - ld.Adddynsym(ctxt, s) - got := ctxt.Syms.Lookup(".got", 0) + ld.Adddynsym(target, syms, s) + got := syms.GOT s.SetGot(int32(got.Size)) - got.AddUint64(ctxt.Arch, 0) - - if ctxt.IsELF { - rela := ctxt.Syms.Lookup(".rela", 0) - rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_GLOB_DAT))) - rela.AddUint64(ctxt.Arch, 0) - } else if ctxt.HeadType == objabi.Hdarwin { - ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(s.Dynid)) + got.AddUint64(target.Arch, 0) + + if target.IsElf() { + rela := syms.Rela + rela.AddAddrPlus(target.Arch, got, int64(s.Got())) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_GLOB_DAT))) + rela.AddUint64(target.Arch, 0) + } else if target.IsDarwin() { + syms.LinkEditGOT.AddUint32(target.Arch, uint32(s.Dynid)) } else { ld.Errorf(s, "addgotsym: unsupported binary format") } @@ -684,29 +694,33 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup sect := ld.Segtext.Sections[0] - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - // 0xCC is INT $3 - breakpoint instruction - ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC}) - for _, sect = range ld.Segtext.Sections[1:] { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + f := func(ctxt *ld.Link, out *ld.OutBuf, start, length int64) { + // 0xCC is INT $3 - breakpoint instruction + ld.CodeblkPad(ctxt, out, start, length, []byte{0xCC}) + } + ld.WriteParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length) + + for _, sect := range ld.Segtext.Sections[1:] { + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } + if ld.Segrelrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) + + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index f2fb6543d0..43ee4bf200 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -34,10 +34,12 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/elf" "fmt" "log" + "sync" ) // This assembler: @@ -115,13 +117,13 @@ func braddoff(a int32, b int32) int32 { return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) } -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { targ := r.Sym switch r.Type { default: if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) return false } @@ -130,8 +132,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { r.Type = objabi.R_CALLARM if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) } @@ -143,9 +145,9 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT32): // R_ARM_GOT_BREL if targ.Type != sym.SDYNIMPORT { - addgotsyminternal(ctxt, targ) + addgotsyminternal(target, syms, targ) } else { - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) } r.Type = objabi.R_CONST // write r->add during relocsym @@ -155,13 +157,13 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil if targ.Type != sym.SDYNIMPORT { - addgotsyminternal(ctxt, targ) + addgotsyminternal(target, syms, targ) } else { - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) } r.Type = objabi.R_PCREL - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += int64(targ.Got()) + 4 return true @@ -173,15 +175,15 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTPC): // R_ARM_BASE_PREL r.Type = objabi.R_PCREL - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += 4 return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL): r.Type = objabi.R_CALLARM if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) } @@ -214,8 +216,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24): r.Type = objabi.R_CALLARM if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) } @@ -229,12 +231,12 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { switch r.Type { case objabi.R_CALLARM: - if ctxt.LinkMode == ld.LinkExternal { + if target.IsExternal() { // External linker will do this relocation. return true } - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add = int64(targ.Plt()) return true @@ -242,12 +244,12 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { if s.Type != sym.SDATA { break } - if ctxt.IsELF { - ld.Adddynsym(ctxt, targ) - rel := ctxt.Syms.Lookup(".rel", 0) - rel.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) - rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc - r.Type = objabi.R_CONST // write r->add during relocsym + if target.IsElf() { + ld.Adddynsym(target, syms, targ) + rel := syms.Rel + rel.AddAddrPlus(target.Arch, s, int64(r.Off)) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc + r.Type = objabi.R_CONST // write r->add during relocsym r.Sym = nil return true } @@ -300,10 +302,8 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { return true } -func elfsetupplt(ctxt *ld.Link) { - plt := ctxt.Syms.Lookup(".plt", 0) - got := ctxt.Syms.Lookup(".got.plt", 0) - if plt.Size == 0 { +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { // str lr, [sp, #-4]! plt.AddUint32(ctxt.Arch, 0xe52de004) @@ -317,7 +317,7 @@ func elfsetupplt(ctxt *ld.Link) { plt.AddUint32(ctxt.Arch, 0xe5bef008) // .word &GLOBAL_OFFSET_TABLE[0] - . - plt.AddPCRelPlus(ctxt.Arch, got, 4) + plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 4) // the first .plt entry requires 3 .plt.got entries got.AddUint32(ctxt.Arch, 0) @@ -597,8 +597,8 @@ func gentrampdyn(arch *sys.Arch, tramp, target *sym.Symbol, offset int64) { } } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { - if ctxt.LinkMode == ld.LinkExternal { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if target.IsExternal() { switch r.Type { case objabi.R_CALLARM: r.Done = false @@ -623,7 +623,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo // the section load address. // we need to compensate that by removing the instruction's address // from addend. - if ctxt.HeadType == objabi.Hdarwin { + if target.IsDarwin() { r.Xadd -= ld.Symaddr(s) + int64(r.Off) } @@ -641,19 +641,19 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo case objabi.R_CONST: return r.Add, true case objabi.R_GOTOFF: - return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true // The following three arch specific relocations are only for generation of // Linux/ARM ELF's PLT entry (3 assembler instruction) case objabi.R_PLT0: // add ip, pc, #0xXX00000 - if ld.Symaddr(ctxt.Syms.Lookup(".got.plt", 0)) < ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) { + if ld.Symaddr(syms.GOTPLT) < ld.Symaddr(syms.PLT) { ld.Errorf(s, ".got.plt should be placed after .plt section.") } - return 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add)) >> 20)), true + return 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+int64(r.Off))+r.Add)) >> 20)), true case objabi.R_PLT1: // add ip, ip, #0xYY000 - return 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+4)) >> 12)), true + return 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+int64(r.Off))+r.Add+4)) >> 12)), true case objabi.R_PLT2: // ldr pc, [ip, #0xZZZ]! - return 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+8))), true + return 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(syms.PLT)+int64(r.Off))+r.Add+8))), true case objabi.R_CALLARM: // bl XXXXXX or b YYYYYY // r.Add is the instruction // low 24-bit encodes the target address @@ -667,12 +667,12 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { log.Fatalf("unexpected relocation variant") return t } -func addpltreloc(ctxt *ld.Link, plt *sym.Symbol, got *sym.Symbol, s *sym.Symbol, typ objabi.RelocType) { +func addpltreloc(plt *sym.Symbol, got *sym.Symbol, s *sym.Symbol, typ objabi.RelocType) { r := plt.AddRel() r.Sym = got r.Off = int32(plt.Size) @@ -685,19 +685,19 @@ func addpltreloc(ctxt *ld.Link, plt *sym.Symbol, got *sym.Symbol, s *sym.Symbol, plt.Grow(plt.Size) } -func addpltsym(ctxt *ld.Link, s *sym.Symbol) { +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Plt() >= 0 { return } - ld.Adddynsym(ctxt, s) + ld.Adddynsym(target, syms, s) - if ctxt.IsELF { - plt := ctxt.Syms.Lookup(".plt", 0) - got := ctxt.Syms.Lookup(".got.plt", 0) - rel := ctxt.Syms.Lookup(".rel.plt", 0) + if target.IsElf() { + plt := syms.PLT + got := syms.GOTPLT + rel := syms.RelPLT if plt.Size == 0 { - elfsetupplt(ctxt) + panic("plt is not set up") } // .got entry @@ -706,54 +706,54 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) { // In theory, all GOT should point to the first PLT entry, // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's // dynamic linker won't, so we'd better do it ourselves. - got.AddAddrPlus(ctxt.Arch, plt, 0) + got.AddAddrPlus(target.Arch, plt, 0) // .plt entry, this depends on the .got entry s.SetPlt(int32(plt.Size)) - addpltreloc(ctxt, plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000 - addpltreloc(ctxt, plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000 - addpltreloc(ctxt, plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]! + addpltreloc(plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000 + addpltreloc(plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000 + addpltreloc(plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]! // rel - rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) + rel.AddAddrPlus(target.Arch, got, int64(s.Got())) - rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_JUMP_SLOT))) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_JUMP_SLOT))) } else { ld.Errorf(s, "addpltsym: unsupported binary format") } } -func addgotsyminternal(ctxt *ld.Link, s *sym.Symbol) { +func addgotsyminternal(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Got() >= 0 { return } - got := ctxt.Syms.Lookup(".got", 0) + got := syms.GOT s.SetGot(int32(got.Size)) - got.AddAddrPlus(ctxt.Arch, s, 0) + got.AddAddrPlus(target.Arch, s, 0) - if ctxt.IsELF { + if target.IsElf() { } else { ld.Errorf(s, "addgotsyminternal: unsupported binary format") } } -func addgotsym(ctxt *ld.Link, s *sym.Symbol) { +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Got() >= 0 { return } - ld.Adddynsym(ctxt, s) - got := ctxt.Syms.Lookup(".got", 0) + ld.Adddynsym(target, syms, s) + got := syms.GOT s.SetGot(int32(got.Size)) - got.AddUint32(ctxt.Arch, 0) + got.AddUint32(target.Arch, 0) - if ctxt.IsELF { - rel := ctxt.Syms.Lookup(".rel", 0) - rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) - rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_GLOB_DAT))) + if target.IsElf() { + rel := syms.Rel + rel.AddAddrPlus(target.Arch, got, int64(s.Got())) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_GLOB_DAT))) } else { ld.Errorf(s, "addgotsym: unsupported binary format") } @@ -764,28 +764,28 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup sect := ld.Segtext.Sections[0] - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) - for _, sect = range ld.Segtext.Sections[1:] { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length) + + for _, sect := range ld.Segtext.Sections[1:] { + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } + if ld.Segrelrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 9c3f442238..66fa1c3a56 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -34,11 +34,12 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/elf" - "encoding/binary" "fmt" "log" + "sync" ) func gentext(ctxt *ld.Link) { @@ -92,13 +93,13 @@ func gentext(ctxt *ld.Link) { initarray_entry.AddAddr(ctxt.Arch, initfunc) } -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { targ := r.Sym switch r.Type { default: if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) return false } @@ -130,8 +131,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26), objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26): if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add += int64(targ.Plt()) } if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { @@ -149,10 +150,10 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { // fall back to using GOT // TODO: just needs relocation, no need to put in .dynsym - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) r.Type = objabi.R_ARM64_GOT - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += int64(targ.Got()) return true @@ -172,7 +173,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { ld.Errorf(s, "unexpected R_AARCH64_ABS64 relocation for dynamic symbol %s", targ.Name) } r.Type = objabi.R_ADDR - if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal { + if target.IsPIE() && target.IsInternal() { // For internal linking PIE, this R_ADDR relocation cannot // be resolved statically. We need to generate a dynamic // relocation. Let the code below handle it. @@ -217,25 +218,25 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { // nothing to do, the relocation will be laid out in reloc return true } - if ctxt.LinkMode == ld.LinkExternal { + if target.IsExternal() { // External linker will do this relocation. return true } case objabi.R_ADDR: - if s.Type == sym.STEXT && ctxt.IsELF { + if s.Type == sym.STEXT && target.IsElf() { // The code is asking for the address of an external // function. We provide it with the address of the // correspondent GOT symbol. - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += int64(targ.Got()) return true } // Process dynamic relocations for the data sections. - if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal { + if target.IsPIE() && target.IsInternal() { // When internally linking, generate dynamic relocations // for all typical R_ADDR relocations. The exception // are those R_ADDR that are created as part of generating @@ -283,7 +284,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { } } - if ctxt.IsELF { + if target.IsElf() { // Generate R_AARCH64_RELATIVE relocations for best // efficiency in the dynamic linker. // @@ -301,14 +302,14 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { // AddAddrPlus is used for r_offset and r_addend to // generate new R_ADDR relocations that will update // these fields in the 'reloc' phase. - rela := ctxt.Syms.Lookup(".rela", 0) - rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) + rela := syms.Rela + rela.AddAddrPlus(target.Arch, s, int64(r.Off)) if r.Siz == 8 { - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_AARCH64_RELATIVE))) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_AARCH64_RELATIVE))) } else { ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) } - rela.AddAddrPlus(ctxt.Arch, targ, int64(r.Add)) + rela.AddAddrPlus(target.Arch, targ, int64(r.Add)) // Not mark r done here. So we still apply it statically, // so in the file content we'll also have the right offset // to the relocation target. So it can be examined statically @@ -434,14 +435,14 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se return true } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { - if ctxt.LinkMode == ld.LinkExternal { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if target.IsExternal() { switch r.Type { default: return val, false case objabi.R_ARM64_GOTPCREL: var o1, o2 uint32 - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { o1 = uint32(val >> 32) o2 = uint32(val) } else { @@ -456,14 +457,14 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo // (https://sourceware.org/bugzilla/show_bug.cgi?id=18270). So // we convert the adrp; ld64 + R_ARM64_GOTPCREL into adrp; // add + R_ADDRARM64. - if !(r.Sym.IsFileLocal() || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && ctxt.DynlinkingGo() { + if !(r.Sym.IsFileLocal() || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && target.IsDynlinkingGo() { if o2&0xffc00000 != 0xf9400000 { ld.Errorf(s, "R_ARM64_GOTPCREL against unexpected instruction %x", o2) } o2 = 0x91000000 | (o2 & 0x000003ff) r.Type = objabi.R_ADDRARM64 } - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { val = int64(o1)<<32 | int64(o2) } else { val = int64(o2)<<32 | int64(o1) @@ -490,10 +491,10 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo // the BR26 relocation should be fully resolved at link time. // That is the reason why the next if block is disabled. When the bug in ld64 // is fixed, we can enable this block and also enable duff's device in cmd/7g. - if false && ctxt.HeadType == objabi.Hdarwin { + if false && target.IsDarwin() { var o0, o1 uint32 - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { o0 = uint32(val >> 32) o1 = uint32(val) } else { @@ -510,7 +511,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo r.Xadd = 0 // when laid out, the instruction order must always be o1, o2. - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { val = int64(o0)<<32 | int64(o1) } else { val = int64(o1)<<32 | int64(o0) @@ -533,7 +534,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo return r.Add, true case objabi.R_GOTOFF: - return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true case objabi.R_ADDRARM64: t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) @@ -543,7 +544,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo var o0, o1 uint32 - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { o0 = uint32(val >> 32) o1 = uint32(val) } else { @@ -555,42 +556,42 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo o1 |= uint32(t&0xfff) << 10 // when laid out, the instruction order must always be o1, o2. - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { return int64(o0)<<32 | int64(o1), true } return int64(o1)<<32 | int64(o0), true case objabi.R_ARM64_TLS_LE: r.Done = false - if ctxt.HeadType == objabi.Hdarwin { - ld.Errorf(s, "TLS reloc on unsupported OS %v", ctxt.HeadType) + if target.IsDarwin() { + ld.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType) } // The TCB is two pointers. This is not documented anywhere, but is // de facto part of the ABI. - v := r.Sym.Value + int64(2*ctxt.Arch.PtrSize) + v := r.Sym.Value + int64(2*target.Arch.PtrSize) if v < 0 || v >= 32678 { ld.Errorf(s, "TLS offset out of range %d", v) } return val | (v << 5), true case objabi.R_ARM64_TLS_IE: - if ctxt.BuildMode == ld.BuildModePIE && ctxt.IsELF { + if target.IsPIE() && target.IsElf() { // We are linking the final executable, so we // can optimize any TLS IE relocation to LE. r.Done = false - if ctxt.HeadType != objabi.Hlinux { - ld.Errorf(s, "TLS reloc on unsupported OS %v", ctxt.HeadType) + if !target.IsLinux() { + ld.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType) } // The TCB is two pointers. This is not documented anywhere, but is // de facto part of the ABI. - v := ld.Symaddr(r.Sym) + int64(2*ctxt.Arch.PtrSize) + r.Add + v := ld.Symaddr(r.Sym) + int64(2*target.Arch.PtrSize) + r.Add if v < 0 || v >= 32678 { ld.Errorf(s, "TLS offset out of range %d", v) } var o0, o1 uint32 - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { o0 = uint32(val >> 32) o1 = uint32(val) } else { @@ -609,7 +610,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo o1 = 0xf2800000 | uint32(o1&0x1f) | (uint32(v&0xffff) << 5) // when laid out, the instruction order must always be o0, o1. - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { return int64(o0)<<32 | int64(o1), true } return int64(o1)<<32 | int64(o0), true @@ -620,7 +621,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo case objabi.R_CALLARM64: var t int64 if r.Sym.Type == sym.SDYNIMPORT { - t = (ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) + r.Add) - (s.Value + int64(r.Off)) + t = (ld.Symaddr(syms.PLT) + r.Add) - (s.Value + int64(r.Off)) } else { t = (ld.Symaddr(r.Sym) + r.Add) - (s.Value + int64(r.Off)) } @@ -707,35 +708,30 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { log.Fatalf("unexpected relocation variant") return -1 } -func elfsetupplt(ctxt *ld.Link) { - plt := ctxt.Syms.Lookup(".plt", 0) - gotplt := ctxt.Syms.Lookup(".got.plt", 0) - if plt.Size == 0 { +func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { // stp x16, x30, [sp, #-16]! // identifying information plt.AddUint32(ctxt.Arch, 0xa9bf7bf0) // the following two instructions (adrp + ldr) load *got[2] into x17 // adrp x16, &got[0] - plt.AddAddrPlus4(gotplt, 16) - plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x90000010) // <imm> is the offset value of &got[2] to &got[0], the same below // ldr x17, [x16, <imm>] - plt.AddAddrPlus4(gotplt, 16) - plt.SetUint32(ctxt.Arch, plt.Size-4, 0xf9400211) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0xf9400211) // add x16, x16, <imm> - plt.AddAddrPlus4(gotplt, 16) - plt.SetUint32(ctxt.Arch, plt.Size-4, 0x91000210) - plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_PCREL, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x91000210) // br x17 plt.AddUint32(ctxt.Arch, 0xd61f0220) @@ -746,57 +742,57 @@ func elfsetupplt(ctxt *ld.Link) { plt.AddUint32(ctxt.Arch, 0xd503201f) // check gotplt.size == 0 - if gotplt.Size != 0 { - ld.Errorf(gotplt, "got.plt is not empty at the very beginning") + if gotplt.Size() != 0 { + ctxt.Errorf(gotplt.Sym(), "got.plt is not empty at the very beginning") } - gotplt.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0) + gotplt.AddAddrPlus(ctxt.Arch, dynamic, 0) gotplt.AddUint64(ctxt.Arch, 0) gotplt.AddUint64(ctxt.Arch, 0) } } -func addpltsym(ctxt *ld.Link, s *sym.Symbol) { +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Plt() >= 0 { return } - ld.Adddynsym(ctxt, s) + ld.Adddynsym(target, syms, s) - if ctxt.IsELF { - plt := ctxt.Syms.Lookup(".plt", 0) - gotplt := ctxt.Syms.Lookup(".got.plt", 0) - rela := ctxt.Syms.Lookup(".rela.plt", 0) + if target.IsElf() { + plt := syms.PLT + gotplt := syms.GOTPLT + rela := syms.RelaPLT if plt.Size == 0 { - elfsetupplt(ctxt) + panic("plt is not set up") } // adrp x16, &got.plt[0] plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010) + plt.SetUint32(target.Arch, plt.Size-4, 0x90000010) plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT // <offset> is the offset value of &got.plt[n] to &got.plt[0] // ldr x17, [x16, <offset>] plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(ctxt.Arch, plt.Size-4, 0xf9400211) + plt.SetUint32(target.Arch, plt.Size-4, 0xf9400211) plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT // add x16, x16, <offset> plt.AddAddrPlus4(gotplt, gotplt.Size) - plt.SetUint32(ctxt.Arch, plt.Size-4, 0x91000210) + plt.SetUint32(target.Arch, plt.Size-4, 0x91000210) plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL // br x17 - plt.AddUint32(ctxt.Arch, 0xd61f0220) + plt.AddUint32(target.Arch, 0xd61f0220) // add to got.plt: pointer to plt[0] - gotplt.AddAddrPlus(ctxt.Arch, plt, 0) + gotplt.AddAddrPlus(target.Arch, plt, 0) // rela - rela.AddAddrPlus(ctxt.Arch, gotplt, gotplt.Size-8) - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_JUMP_SLOT))) - rela.AddUint64(ctxt.Arch, 0) + rela.AddAddrPlus(target.Arch, gotplt, gotplt.Size-8) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_JUMP_SLOT))) + rela.AddUint64(target.Arch, 0) s.SetPlt(int32(plt.Size - 16)) } else { @@ -804,21 +800,21 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) { } } -func addgotsym(ctxt *ld.Link, s *sym.Symbol) { +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Got() >= 0 { return } - ld.Adddynsym(ctxt, s) - got := ctxt.Syms.Lookup(".got", 0) + ld.Adddynsym(target, syms, s) + got := syms.GOT s.SetGot(int32(got.Size)) - got.AddUint64(ctxt.Arch, 0) + got.AddUint64(target.Arch, 0) - if ctxt.IsELF { - rela := ctxt.Syms.Lookup(".rela", 0) - rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_GLOB_DAT))) - rela.AddUint64(ctxt.Arch, 0) + if target.IsElf() { + rela := syms.Rela + rela.AddAddrPlus(target.Arch, got, int64(s.Got())) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_GLOB_DAT))) + rela.AddUint64(target.Arch, 0) } else { ld.Errorf(s, "addgotsym: unsupported binary format") } @@ -829,28 +825,28 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup sect := ld.Segtext.Sections[0] - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) - for _, sect = range ld.Segtext.Sections[1:] { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length) + + for _, sect := range ld.Segtext.Sections[1:] { + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } + if ld.Segrelrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go new file mode 100644 index 0000000000..6c163c801e --- /dev/null +++ b/src/cmd/link/internal/benchmark/bench.go @@ -0,0 +1,195 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package benchmark provides a Metrics object that enables memory and CPU +// profiling for the linker. The Metrics objects can be used to mark stages +// of the code, and name the measurements during that stage. There is also +// optional GCs that can be performed at the end of each stage, so you +// can get an accurate measurement of how each stage changes live memory. +package benchmark + +import ( + "fmt" + "io" + "os" + "runtime" + "runtime/pprof" + "time" + "unicode" +) + +type Flags int + +const ( + GC = 1 << iota + NoGC Flags = 0 +) + +type Metrics struct { + gc Flags + marks []*mark + curMark *mark + filebase string + pprofFile *os.File +} + +type mark struct { + name string + startM, endM, gcM runtime.MemStats + startT, endT time.Time +} + +// New creates a new Metrics object. +// +// Typical usage should look like: +// +// func main() { +// filename := "" // Set to enable per-phase pprof file output. +// bench := benchmark.New(benchmark.GC, filename) +// defer bench.Report(os.Stdout) +// // etc +// bench.Start("foo") +// foo() +// bench.Start("bar") +// bar() +// } +// +// Note that a nil Metrics object won't cause any errors, so one could write +// code like: +// +// func main() { +// enableBenchmarking := flag.Bool("enable", true, "enables benchmarking") +// flag.Parse() +// var bench *benchmark.Metrics +// if *enableBenchmarking { +// bench = benchmark.New(benchmark.GC) +// } +// bench.Start("foo") +// // etc. +// } +func New(gc Flags, filebase string) *Metrics { + if gc == GC { + runtime.GC() + } + return &Metrics{gc: gc, filebase: filebase} +} + +// Report reports the metrics. +// Closes the currently Start(ed) range, and writes the report to the given io.Writer. +func (m *Metrics) Report(w io.Writer) { + if m == nil { + return + } + + m.closeMark() + + gcString := "" + if m.gc == GC { + gcString = "_GC" + } + + var totTime time.Duration + for _, curMark := range m.marks { + dur := curMark.endT.Sub(curMark.startT) + totTime += dur + fmt.Fprintf(w, "%s 1 %d ns/op", makeBenchString(curMark.name+gcString), dur.Nanoseconds()) + fmt.Fprintf(w, "\t%d B/op", curMark.endM.TotalAlloc-curMark.startM.TotalAlloc) + fmt.Fprintf(w, "\t%d allocs/op", curMark.endM.Mallocs-curMark.startM.Mallocs) + if m.gc == GC { + fmt.Fprintf(w, "\t%d live-B", curMark.gcM.HeapAlloc) + } else { + fmt.Fprintf(w, "\t%d heap-B", curMark.endM.HeapAlloc) + } + fmt.Fprintf(w, "\n") + } + fmt.Fprintf(w, "%s 1 %d ns/op\n", makeBenchString("total time"+gcString), totTime.Nanoseconds()) +} + +// Starts marks the beginning of a new measurement phase. +// Once a metric is started, it continues until either a Report is issued, or another Start is called. +func (m *Metrics) Start(name string) { + if m == nil { + return + } + m.closeMark() + m.curMark = &mark{name: name} + // Unlikely we need to a GC here, as one was likely just done in closeMark. + if m.shouldPProf() { + f, err := os.Create(makePProfFilename(m.filebase, name, "cpuprof")) + if err != nil { + panic(err) + } + m.pprofFile = f + if err = pprof.StartCPUProfile(m.pprofFile); err != nil { + panic(err) + } + } + runtime.ReadMemStats(&m.curMark.startM) + m.curMark.startT = time.Now() +} + +func (m *Metrics) closeMark() { + if m == nil || m.curMark == nil { + return + } + m.curMark.endT = time.Now() + if m.shouldPProf() { + pprof.StopCPUProfile() + m.pprofFile.Close() + m.pprofFile = nil + } + runtime.ReadMemStats(&m.curMark.endM) + if m.gc == GC { + runtime.GC() + runtime.ReadMemStats(&m.curMark.gcM) + if m.shouldPProf() { + // Collect a profile of the live heap. Do a + // second GC to force sweep completion so we + // get a complete snapshot of the live heap at + // the end of this phase. + runtime.GC() + f, err := os.Create(makePProfFilename(m.filebase, m.curMark.name, "memprof")) + if err != nil { + panic(err) + } + err = pprof.WriteHeapProfile(f) + if err != nil { + panic(err) + } + err = f.Close() + if err != nil { + panic(err) + } + } + } + m.marks = append(m.marks, m.curMark) + m.curMark = nil +} + +// shouldPProf returns true if we should be doing pprof runs. +func (m *Metrics) shouldPProf() bool { + return m != nil && len(m.filebase) > 0 +} + +// makeBenchString makes a benchmark string consumable by Go's benchmarking tools. +func makeBenchString(name string) string { + needCap := true + ret := []rune("Benchmark") + for _, r := range name { + if unicode.IsSpace(r) { + needCap = true + continue + } + if needCap { + r = unicode.ToUpper(r) + needCap = false + } + ret = append(ret, r) + } + return string(ret) +} + +func makePProfFilename(filebase, name, typ string) string { + return fmt.Sprintf("%s_%s.%s", filebase, makeBenchString(name), typ) +} diff --git a/src/cmd/link/internal/benchmark/bench_test.go b/src/cmd/link/internal/benchmark/bench_test.go new file mode 100644 index 0000000000..d8ec717c7c --- /dev/null +++ b/src/cmd/link/internal/benchmark/bench_test.go @@ -0,0 +1,53 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package benchmark + +import ( + "testing" +) + +func TestMakeBenchString(t *testing.T) { + tests := []struct { + have, want string + }{ + {"foo", "BenchmarkFoo"}, + {" foo ", "BenchmarkFoo"}, + {"foo bar", "BenchmarkFooBar"}, + } + for i, test := range tests { + if v := makeBenchString(test.have); test.want != v { + t.Errorf("test[%d] makeBenchString(%q) == %q, want %q", i, test.have, v, test.want) + } + } +} + +func TestPProfFlag(t *testing.T) { + tests := []struct { + name string + want bool + }{ + {"", false}, + {"foo", true}, + } + for i, test := range tests { + b := New(GC, test.name) + if v := b.shouldPProf(); test.want != v { + t.Errorf("test[%d] shouldPProf() == %v, want %v", i, v, test.want) + } + } +} + +func TestPProfNames(t *testing.T) { + want := "foo_BenchmarkTest.cpuprof" + if v := makePProfFilename("foo", "test", "cpuprof"); v != want { + t.Errorf("makePProfFilename() == %q, want %q", v, want) + } +} + +// Ensure that public APIs work with a nil Metrics object. +func TestNilBenchmarkObject(t *testing.T) { + var b *Metrics + b.Start("TEST") + b.Report(nil) +} diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go index 4a20d96f96..268f40e92d 100644 --- a/src/cmd/link/internal/ld/ar.go +++ b/src/cmd/link/internal/ld/ar.go @@ -104,15 +104,13 @@ func hostArchive(ctxt *Link, name string) { any := true for any { var load []uint64 - for _, s := range ctxt.Syms.Allsym { - for i := range s.R { - r := &s.R[i] // Copying sym.Reloc has measurable impact on performance - if r.Sym != nil && r.Sym.Type == sym.SXREF { - if off := armap[r.Sym.Name]; off != 0 && !loaded[off] { - load = append(load, off) - loaded[off] = true - } - } + returnAllUndefs := -1 + undefs := ctxt.loader.UndefinedRelocTargets(returnAllUndefs) + for _, symIdx := range undefs { + name := ctxt.loader.SymName(symIdx) + if off := armap[name]; off != 0 && !loaded[off] { + load = append(load, off) + loaded[off] = true } } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 31613e5cef..7c4b08a805 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -37,6 +37,7 @@ import ( "cmd/internal/gcprog" "cmd/internal/objabi" "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "compress/zlib" "encoding/binary" @@ -99,7 +100,7 @@ func trampoline(ctxt *Link, s *sym.Symbol) { if Symaddr(r.Sym) == 0 && (r.Sym.Type != sym.SDYNIMPORT && r.Sym.Type != sym.SUNDEFEXT) { if r.Sym.File != s.File { if !isRuntimeDepPkg(s.File) || !isRuntimeDepPkg(r.Sym.File) { - ctxt.ErrorUnresolved(s, r) + ctxt.errorUnresolved(s, r) } // runtime and its dependent packages may call to each other. // they are fine, as they will be laid down together. @@ -126,7 +127,9 @@ func trampoline(ctxt *Link, s *sym.Symbol) { // // This is a performance-critical function for the linker; be careful // to avoid introducing unnecessary allocations in the main loop. -func relocsym(ctxt *Link, s *sym.Symbol) { +// TODO: This function is called in parallel. When the Loader wavefront +// reaches here, calls into the loader need to be parallel as well. +func relocsym(target *Target, ldr *loader.Loader, err *ErrorReporter, syms *ArchSyms, s *sym.Symbol) { if len(s.R) == 0 { return } @@ -157,8 +160,8 @@ func relocsym(ctxt *Link, s *sym.Symbol) { if r.Sym != nil && ((r.Sym.Type == sym.Sxxx && !r.Sym.Attr.VisibilityHidden()) || r.Sym.Type == sym.SXREF) { // When putting the runtime but not main into a shared library // these symbols are undefined and that's OK. - if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin { - if r.Sym.Name == "main.main" || (ctxt.BuildMode != BuildModePlugin && r.Sym.Name == "main..inittask") { + if target.IsShared() || target.IsPlugin() { + if r.Sym.Name == "main.main" || (!target.IsPlugin() && r.Sym.Name == "main..inittask") { r.Sym.Type = sym.SDYNIMPORT } else if strings.HasPrefix(r.Sym.Name, "go.info.") { // Skip go.info symbols. They are only needed to communicate @@ -166,7 +169,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { continue } } else { - ctxt.ErrorUnresolved(s, r) + err.errorUnresolved(s, r) continue } } @@ -180,21 +183,21 @@ func relocsym(ctxt *Link, s *sym.Symbol) { // We need to be able to reference dynimport symbols when linking against // shared libraries, and Solaris, Darwin and AIX need it always - if ctxt.HeadType != objabi.Hsolaris && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Haix && r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT && !ctxt.DynlinkingGo() && !r.Sym.Attr.SubSymbol() { - if !(ctxt.Arch.Family == sys.PPC64 && ctxt.LinkMode == LinkExternal && r.Sym.Name == ".TOC.") { - Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", r.Sym.Name, r.Sym.Type, r.Sym.Type, r.Type, sym.RelocName(ctxt.Arch, r.Type)) + if !target.IsSolaris() && !target.IsDarwin() && !target.IsAIX() && r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !r.Sym.Attr.SubSymbol() { + if !(target.IsPPC64() && target.IsExternal() && r.Sym.Name == ".TOC.") { + Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", r.Sym.Name, r.Sym.Type, r.Sym.Type, r.Type, sym.RelocName(target.Arch, r.Type)) } } if r.Sym != nil && r.Sym.Type != sym.STLSBSS && r.Type != objabi.R_WEAKADDROFF && !r.Sym.Attr.Reachable() { Errorf(s, "unreachable sym in relocation: %s", r.Sym.Name) } - if ctxt.LinkMode == LinkExternal { + if target.IsExternal() { r.InitExt() } // TODO(mundaym): remove this special case - see issue 14218. - if ctxt.Arch.Family == sys.S390X { + if target.IsS390X() { switch r.Type { case objabi.R_PCRELDBL: r.InitExt() @@ -215,33 +218,33 @@ func relocsym(ctxt *Link, s *sym.Symbol) { case 1: o = int64(s.P[off]) case 2: - o = int64(ctxt.Arch.ByteOrder.Uint16(s.P[off:])) + o = int64(target.Arch.ByteOrder.Uint16(s.P[off:])) case 4: - o = int64(ctxt.Arch.ByteOrder.Uint32(s.P[off:])) + o = int64(target.Arch.ByteOrder.Uint32(s.P[off:])) case 8: - o = int64(ctxt.Arch.ByteOrder.Uint64(s.P[off:])) + o = int64(target.Arch.ByteOrder.Uint64(s.P[off:])) } - if offset, ok := thearch.Archreloc(ctxt, r, s, o); ok { + if offset, ok := thearch.Archreloc(target, syms, r, s, o); ok { o = offset } else { - Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type)) + Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(target.Arch, r.Type)) } case objabi.R_TLS_LE: - if ctxt.LinkMode == LinkExternal && ctxt.IsELF { + if target.IsExternal() && target.IsElf() { r.Done = false if r.Sym == nil { - r.Sym = ctxt.Tlsg + r.Sym = syms.Tlsg } r.Xsym = r.Sym r.Xadd = r.Add o = 0 - if ctxt.Arch.Family != sys.AMD64 { + if !target.IsAMD64() { o = r.Add } break } - if ctxt.IsELF && ctxt.Arch.Family == sys.ARM { + if target.IsElf() && target.IsARM() { // On ELF ARM, the thread pointer is 8 bytes before // the start of the thread-local data block, so add 8 // to the actual TLS offset (r->sym->value). @@ -250,43 +253,43 @@ func relocsym(ctxt *Link, s *sym.Symbol) { // related to the fact that our own TLS storage happens // to take up 8 bytes. o = 8 + r.Sym.Value - } else if ctxt.IsELF || ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hdarwin { - o = int64(ctxt.Tlsoffset) + r.Add - } else if ctxt.HeadType == objabi.Hwindows { + } else if target.IsElf() || target.IsPlan9() || target.IsDarwin() { + o = int64(syms.Tlsoffset) + r.Add + } else if target.IsWindows() { o = r.Add } else { - log.Fatalf("unexpected R_TLS_LE relocation for %v", ctxt.HeadType) + log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType) } case objabi.R_TLS_IE: - if ctxt.LinkMode == LinkExternal && ctxt.IsELF { + if target.IsExternal() && target.IsElf() { r.Done = false if r.Sym == nil { - r.Sym = ctxt.Tlsg + r.Sym = syms.Tlsg } r.Xsym = r.Sym r.Xadd = r.Add o = 0 - if ctxt.Arch.Family != sys.AMD64 { + if !target.IsAMD64() { o = r.Add } break } - if ctxt.BuildMode == BuildModePIE && ctxt.IsELF { + if target.IsPIE() && target.IsElf() { // We are linking the final executable, so we // can optimize any TLS IE relocation to LE. if thearch.TLSIEtoLE == nil { - log.Fatalf("internal linking of TLS IE not supported on %v", ctxt.Arch.Family) + log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) } thearch.TLSIEtoLE(s, int(off), int(r.Siz)) - o = int64(ctxt.Tlsoffset) - // TODO: o += r.Add when ctxt.Arch.Family != sys.AMD64? + o = int64(syms.Tlsoffset) + // TODO: o += r.Add when !target.IsAmd64()? // Why do we treat r.Add differently on AMD64? // Is the external linker using Xadd at all? } else { log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", s.Name) } case objabi.R_ADDR: - if ctxt.LinkMode == LinkExternal && r.Sym.Type != sym.SCONST { + if target.IsExternal() && r.Sym.Type != sym.SCONST { r.Done = false // set up addend for eventual relocation via outer symbol. @@ -304,20 +307,20 @@ func relocsym(ctxt *Link, s *sym.Symbol) { r.Xsym = rs o = r.Xadd - if ctxt.IsELF { - if ctxt.Arch.Family == sys.AMD64 { + if target.IsElf() { + if target.IsAMD64() { o = 0 } - } else if ctxt.HeadType == objabi.Hdarwin { + } else if target.IsDarwin() { if rs.Type != sym.SHOSTOBJ { o += Symaddr(rs) } - } else if ctxt.HeadType == objabi.Hwindows { + } else if target.IsWindows() { // nothing to do - } else if ctxt.HeadType == objabi.Haix { + } else if target.IsAIX() { o = Symaddr(r.Sym) + r.Add } else { - Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType) + Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType) } break @@ -327,14 +330,14 @@ func relocsym(ctxt *Link, s *sym.Symbol) { // as section addresses can change once loaded. // The "default" symbol address is still needed by the loader so // the current relocation can't be skipped. - if ctxt.HeadType == objabi.Haix && r.Sym.Type != sym.SDYNIMPORT { + if target.IsAIX() && r.Sym.Type != sym.SDYNIMPORT { // It's not possible to make a loader relocation in a // symbol which is not inside .data section. // FIXME: It should be forbidden to have R_ADDR from a // symbol which isn't in .data. However, as .text has the // same address once loaded, this is possible. if s.Sect.Seg == &Segdata { - Xcoffadddynrel(ctxt, s, r) + Xcoffadddynrel(target, ldr, s, r) } } @@ -345,7 +348,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { // fail at runtime. See https://golang.org/issue/7980. // Instead of special casing only amd64, we treat this as an error on all // 64-bit architectures so as to be future-proof. - if int32(o) < 0 && ctxt.Arch.PtrSize > 4 && siz == 4 { + if int32(o) < 0 && target.Arch.PtrSize > 4 && siz == 4 { Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", r.Sym.Name, uint64(o), Symaddr(r.Sym), r.Add) errorexit() } @@ -354,7 +357,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name) } - if ctxt.LinkMode == LinkExternal { + if target.IsExternal() { r.Done = false // On most platforms, the external linker needs to adjust DWARF references @@ -362,7 +365,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { // DWARF linking, and it understands how to follow section offsets. // Leaving in the relocation records confuses it (see // https://golang.org/issue/22068) so drop them for Darwin. - if ctxt.HeadType == objabi.Hdarwin { + if target.IsDarwin() { r.Done = true } @@ -371,15 +374,15 @@ func relocsym(ctxt *Link, s *sym.Symbol) { // IMAGE_REL_I386_DIR32, IMAGE_REL_AMD64_ADDR64 and IMAGE_REL_AMD64_ADDR32. // Do not replace R_DWARFSECREF with R_ADDR for windows - // let PE code emit correct relocations. - if ctxt.HeadType != objabi.Hwindows { + if !target.IsWindows() { r.Type = objabi.R_ADDR } - r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0) + r.Xsym = r.Sym.Sect.Sym r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) o = r.Xadd - if ctxt.IsELF && ctxt.Arch.Family == sys.AMD64 { + if target.IsElf() && target.IsAMD64() { o = 0 } break @@ -406,7 +409,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. case objabi.R_GOTPCREL: - if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin && r.Sym != nil && r.Sym.Type != sym.SCONST { + if target.IsDynlinkingGo() && target.IsDarwin() && r.Sym != nil && r.Sym.Type != sym.SCONST { r.Done = false r.Xadd = r.Add r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk @@ -418,18 +421,18 @@ func relocsym(ctxt *Link, s *sym.Symbol) { } fallthrough case objabi.R_CALL, objabi.R_PCREL: - if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT { + if target.IsExternal() && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT { // pass through to the external linker. r.Done = false r.Xadd = 0 - if ctxt.IsELF { + if target.IsElf() { r.Xadd -= int64(r.Siz) } r.Xsym = r.Sym o = 0 break } - if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type != sym.SCONST && (r.Sym.Sect != s.Sect || r.Type == objabi.R_GOTPCREL) { + if target.IsExternal() && r.Sym != nil && r.Sym.Type != sym.SCONST && (r.Sym.Sect != s.Sect || r.Type == objabi.R_GOTPCREL) { r.Done = false // set up addend for eventual relocation via outer symbol. @@ -448,14 +451,14 @@ func relocsym(ctxt *Link, s *sym.Symbol) { r.Xsym = rs o = r.Xadd - if ctxt.IsELF { - if ctxt.Arch.Family == sys.AMD64 { + if target.IsElf() { + if target.IsAMD64() { o = 0 } - } else if ctxt.HeadType == objabi.Hdarwin { + } else if target.IsDarwin() { if r.Type == objabi.R_CALL { - if ctxt.LinkMode == LinkExternal && rs.Type == sym.SDYNIMPORT { - switch ctxt.Arch.Family { + if target.IsExternal() && rs.Type == sym.SDYNIMPORT { + switch target.Arch.Family { case sys.AMD64: // AMD64 dynamic relocations are relative to the end of the relocation. o += int64(r.Siz) @@ -470,18 +473,18 @@ func relocsym(ctxt *Link, s *sym.Symbol) { } o -= int64(r.Off) // relative to section offset, not symbol } - } else if ctxt.Arch.Family == sys.ARM { + } else if target.IsARM() { // see ../arm/asm.go:/machoreloc1 o += Symaddr(rs) - s.Value - int64(r.Off) } else { o += int64(r.Siz) } - } else if ctxt.HeadType == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 { // only amd64 needs PCREL + } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL // PE/COFF's PC32 relocation uses the address after the relocated // bytes as the base. Compensate by skewing the addend. o += int64(r.Siz) } else { - Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType) + Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, target.HeadType) } break @@ -497,10 +500,10 @@ func relocsym(ctxt *Link, s *sym.Symbol) { o = r.Sym.Size + r.Add case objabi.R_XCOFFREF: - if ctxt.HeadType != objabi.Haix { + if !target.IsAIX() { Errorf(s, "find XCOFF R_REF on non-XCOFF files") } - if ctxt.LinkMode != LinkExternal { + if !target.IsExternal() { Errorf(s, "find XCOFF R_REF with internal linking") } r.Xsym = r.Sym @@ -516,10 +519,10 @@ func relocsym(ctxt *Link, s *sym.Symbol) { o = r.Add } - if ctxt.Arch.Family == sys.PPC64 || ctxt.Arch.Family == sys.S390X { + if target.IsPPC64() || target.IsS390X() { r.InitExt() if r.Variant != sym.RV_NONE { - o = thearch.Archrelocvariant(ctxt, r, s, o) + o = thearch.Archrelocvariant(target, syms, r, s, o) } } @@ -534,7 +537,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { if r.Xsym != nil { xnam = r.Xsym.Name } - fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x (xsym: %s +%#x) [type %d (%s)/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, addr, r.Add, xnam, r.Xadd, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Variant, o) + fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x (xsym: %s +%#x) [type %d (%s)/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, addr, r.Add, xnam, r.Xadd, r.Type, sym.RelocName(target.Arch, r.Type), r.Variant, o) } switch siz { default: @@ -549,7 +552,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { Errorf(s, "relocation address for %s is too big: %#x", r.Sym.Name, o) } i16 := int16(o) - ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16)) + target.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16)) case 4: if r.Type == objabi.R_PCREL || r.Type == objabi.R_CALL { if o != int64(int32(o)) { @@ -562,64 +565,95 @@ func relocsym(ctxt *Link, s *sym.Symbol) { } fl := int32(o) - ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl)) + target.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl)) case 8: - ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o)) + target.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o)) } } } func (ctxt *Link) reloc() { - for _, s := range ctxt.Textp { - relocsym(ctxt, s) - } - for _, s := range datap { - relocsym(ctxt, s) - } - for _, s := range dwarfp { - relocsym(ctxt, s) - } + var wg sync.WaitGroup + target := &ctxt.Target + ldr := ctxt.loader + reporter := &ctxt.ErrorReporter + syms := &ctxt.ArchSyms + wg.Add(3) + go func() { + for _, s := range ctxt.Textp { + relocsym(target, ldr, reporter, syms, s) + } + wg.Done() + }() + go func() { + for _, s := range ctxt.datap { + relocsym(target, ldr, reporter, syms, s) + } + wg.Done() + }() + go func() { + for _, s := range dwarfp { + relocsym(target, ldr, reporter, syms, s) + } + wg.Done() + }() + wg.Wait() } -func windynrelocsym(ctxt *Link, rel, s *sym.Symbol) { - for ri := range s.R { - r := &s.R[ri] - targ := r.Sym - if targ == nil { +func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) { + var su *loader.SymbolBuilder + relocs := ctxt.loader.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At2(ri) + targ := r.Sym() + if targ == 0 { continue } - if !targ.Attr.Reachable() { - if r.Type == objabi.R_WEAKADDROFF { + rt := r.Type() + if !ctxt.loader.AttrReachable(targ) { + if rt == objabi.R_WEAKADDROFF { continue } - Errorf(s, "dynamic relocation to unreachable symbol %s", targ.Name) + ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s", + ctxt.loader.SymName(targ)) } - if r.Sym.Plt() == -2 && r.Sym.Got() != -2 { // make dynimport JMP table for PE object files. - targ.SetPlt(int32(rel.Size)) - r.Sym = rel - r.Add = int64(targ.Plt()) + + tplt := ctxt.loader.SymPlt(targ) + tgot := ctxt.loader.SymGot(targ) + if tplt == -2 && tgot != -2 { // make dynimport JMP table for PE object files. + tplt := int32(rel.Size()) + ctxt.loader.SetPlt(targ, tplt) + + if su == nil { + su = ctxt.loader.MakeSymbolUpdater(s) + } + r.SetSym(rel.Sym()) + r.SetAdd(int64(tplt)) // jmp *addr switch ctxt.Arch.Family { default: - Errorf(s, "unsupported arch %v", ctxt.Arch.Family) + ctxt.Errorf(s, "unsupported arch %v", ctxt.Arch.Family) return case sys.I386: rel.AddUint8(0xff) rel.AddUint8(0x25) - rel.AddAddr(ctxt.Arch, targ) + rel.AddAddrPlus(ctxt.Arch, targ, 0) rel.AddUint8(0x90) rel.AddUint8(0x90) case sys.AMD64: rel.AddUint8(0xff) rel.AddUint8(0x24) rel.AddUint8(0x25) - rel.AddAddrPlus4(targ, 0) + rel.AddAddrPlus4(ctxt.Arch, targ, 0) rel.AddUint8(0x90) } - } else if r.Sym.Plt() >= 0 { - r.Sym = rel - r.Add = int64(targ.Plt()) + } else if tplt >= 0 { + if su == nil { + su = ctxt.loader.MakeSymbolUpdater(s) + } + r.SetSym(rel.Sym()) + r.SetAdd(int64(tplt)) } } } @@ -627,32 +661,32 @@ func windynrelocsym(ctxt *Link, rel, s *sym.Symbol) { // windynrelocsyms generates jump table to C library functions that will be // added later. windynrelocsyms writes the table into .rel symbol. func (ctxt *Link) windynrelocsyms() { - if !(ctxt.HeadType == objabi.Hwindows && iscgo && ctxt.LinkMode == LinkInternal) { + if !(ctxt.IsWindows() && iscgo && ctxt.IsInternal()) { return } - /* relocation table */ - rel := ctxt.Syms.Lookup(".rel", 0) - rel.Attr |= sym.AttrReachable - rel.Type = sym.STEXT - ctxt.Textp = append(ctxt.Textp, rel) + rel := ctxt.loader.LookupOrCreateSym(".rel", 0) + relu := ctxt.loader.MakeSymbolUpdater(rel) + relu.SetType(sym.STEXT) - for _, s := range ctxt.Textp { - if s == rel { - continue - } - windynrelocsym(ctxt, rel, s) + for _, s := range ctxt.Textp2 { + windynrelocsym(ctxt, relu, s) } + + ctxt.Textp2 = append(ctxt.Textp2, rel) } func dynrelocsym(ctxt *Link, s *sym.Symbol) { + target := &ctxt.Target + ldr := ctxt.loader + syms := &ctxt.ArchSyms for ri := range s.R { r := &s.R[ri] if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal { // It's expected that some relocations will be done // later by relocsym (R_TLS_LE, R_ADDROFF), so // don't worry if Adddynrel returns false. - thearch.Adddynrel(ctxt, s, r) + thearch.Adddynrel(target, ldr, syms, s, r) continue } @@ -660,7 +694,7 @@ func dynrelocsym(ctxt *Link, s *sym.Symbol) { if r.Sym != nil && !r.Sym.Attr.Reachable() { Errorf(s, "dynamic relocation to unreachable symbol %s", r.Sym.Name) } - if !thearch.Adddynrel(ctxt, s, r) { + if !thearch.Adddynrel(target, ldr, syms, s, r) { Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Sym.Type, r.Sym.Type) } } @@ -690,15 +724,16 @@ func dynreloc(ctxt *Link, data *[sym.SXREF][]*sym.Symbol) { } } -func Codeblk(ctxt *Link, addr int64, size int64) { - CodeblkPad(ctxt, addr, size, zeros[:]) +func Codeblk(ctxt *Link, out *OutBuf, addr int64, size int64) { + CodeblkPad(ctxt, out, addr, size, zeros[:]) } -func CodeblkPad(ctxt *Link, addr int64, size int64, pad []byte) { + +func CodeblkPad(ctxt *Link, out *OutBuf, addr int64, size int64, pad []byte) { if *flagA { ctxt.Logf("codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset()) } - blk(ctxt.Out, ctxt.Textp, addr, size, pad) + writeBlocks(out, ctxt.outSem, ctxt.Textp, addr, size, pad) /* again for printing */ if !*flagA { @@ -756,9 +791,86 @@ func CodeblkPad(ctxt *Link, addr int64, size int64, pad []byte) { } } -func blk(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) { +const blockSize = 1 << 20 // 1MB chunks written at a time. + +// writeBlocks writes a specified chunk of symbols to the output buffer. It +// breaks the write up into ≥blockSize chunks to write them out, and schedules +// as many goroutines as necessary to accomplish this task. This call then +// blocks, waiting on the writes to complete. Note that we use the sem parameter +// to limit the number of concurrent writes taking place. +func writeBlocks(out *OutBuf, sem chan int, syms []*sym.Symbol, addr, size int64, pad []byte) { for i, s := range syms { - if !s.Attr.SubSymbol() && s.Value >= addr { + if s.Value >= addr && !s.Attr.SubSymbol() { + syms = syms[i:] + break + } + } + + var wg sync.WaitGroup + max, lastAddr, written := int64(blockSize), addr+size, int64(0) + for addr < lastAddr { + // Find the last symbol we'd write. + idx := -1 + for i, s := range syms { + // If the next symbol's size would put us out of bounds on the total length, + // stop looking. + if s.Value+s.Size > lastAddr { + break + } + + // We're gonna write this symbol. + idx = i + + // If we cross over the max size, we've got enough symbols. + if s.Value+s.Size > addr+max { + break + } + } + + // If we didn't find any symbols to write, we're done here. + if idx < 0 { + break + } + + // Compute the length to write, including padding. + // We need to write to the end address (lastAddr), or the next symbol's + // start address, whichever comes first. If there is no more symbols, + // just write to lastAddr. This ensures we don't leave holes between the + // blocks or at the end. + length := int64(0) + if idx+1 < len(syms) { + length = syms[idx+1].Value - addr + } + if length == 0 || length > lastAddr-addr { + length = lastAddr - addr + } + + // Start the block output operator. + if o, err := out.View(uint64(out.Offset() + written)); err == nil { + sem <- 1 + wg.Add(1) + go func(o *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) { + writeBlock(o, syms, addr, size, pad) + wg.Done() + <-sem + }(o, syms, addr, length, pad) + } else { // output not mmaped, don't parallelize. + writeBlock(out, syms, addr, length, pad) + } + + // Prepare for the next loop. + if idx != -1 { + syms = syms[idx+1:] + } + written += length + addr += length + } + wg.Wait() +} + +func writeBlock(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) { + for i, s := range syms { + if s.Value >= addr && !s.Attr.SubSymbol() { syms = syms[i:] break } @@ -802,11 +914,26 @@ func blk(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) { if addr < eaddr { out.WriteStringPad("", int(eaddr-addr), pad) } - out.Flush() } -func Datblk(ctxt *Link, addr int64, size int64) { - writeDatblkToOutBuf(ctxt, ctxt.Out, addr, size) +type writeFn func(*Link, *OutBuf, int64, int64) + +// WriteParallel handles scheduling parallel execution of data write functions. +func WriteParallel(wg *sync.WaitGroup, fn writeFn, ctxt *Link, seek, vaddr, length uint64) { + if out, err := ctxt.Out.View(seek); err != nil { + ctxt.Out.SeekSet(int64(seek)) + fn(ctxt, ctxt.Out, int64(vaddr), int64(length)) + } else { + wg.Add(1) + go func() { + defer wg.Done() + fn(ctxt, out, int64(vaddr), int64(length)) + }() + } +} + +func Datblk(ctxt *Link, out *OutBuf, addr, size int64) { + writeDatblkToOutBuf(ctxt, out, addr, size) } // Used only on Wasm for now. @@ -823,14 +950,14 @@ func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) { ctxt.Logf("datblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset()) } - blk(out, datap, addr, size, zeros[:]) + writeBlocks(out, ctxt.outSem, ctxt.datap, addr, size, zeros[:]) /* again for printing */ if !*flagA { return } - syms := datap + syms := ctxt.datap for i, sym := range syms { if sym.Value >= addr { syms = syms[i:] @@ -892,12 +1019,12 @@ func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) { ctxt.Logf("\t%.8x|\n", uint(eaddr)) } -func Dwarfblk(ctxt *Link, addr int64, size int64) { +func Dwarfblk(ctxt *Link, out *OutBuf, addr int64, size int64) { if *flagA { ctxt.Logf("dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset()) } - blk(ctxt.Out, dwarfp, addr, size, zeros[:]) + writeBlocks(out, ctxt.outSem, dwarfp, addr, size, zeros[:]) } var zeros [512]byte @@ -927,48 +1054,43 @@ func addstrdata1(ctxt *Link, arg string) { } // addstrdata sets the initial value of the string variable name to value. -func addstrdata(ctxt *Link, name, value string) { - s := ctxt.Syms.ROLookup(name, 0) - if s == nil || s.Gotype == nil { - // Not defined in the loaded packages. +func addstrdata(arch *sys.Arch, l *loader.Loader, name, value string) { + s := l.Lookup(name, 0) + if s == 0 { return } - if s.Gotype.Name != "type.string" { - Errorf(s, "cannot set with -X: not a var of type string (%s)", s.Gotype.Name) + if goType := l.SymGoType(s); goType == 0 { + return + } else if typeName := l.SymName(goType); typeName != "type.string" { + Errorf(nil, "%s: cannot set with -X: not a var of type string (%s)", name, typeName) return } - if s.Type == sym.SBSS { - s.Type = sym.SDATA + if !l.AttrReachable(s) { + return // don't bother setting unreachable variable } - - p := fmt.Sprintf("%s.str", s.Name) - sp := ctxt.Syms.Lookup(p, 0) - - Addstring(sp, value) - sp.Type = sym.SRODATA - - s.Size = 0 - s.P = s.P[:0] - if s.Attr.ReadOnly() { - s.P = make([]byte, 0, ctxt.Arch.PtrSize*2) - s.Attr.Set(sym.AttrReadOnly, false) + bld := l.MakeSymbolUpdater(s) + if bld.Type() == sym.SBSS { + bld.SetType(sym.SDATA) } - s.R = s.R[:0] - reachable := s.Attr.Reachable() - s.AddAddr(ctxt.Arch, sp) - s.AddUint(ctxt.Arch, uint64(len(value))) - // addstring, addaddr, etc., mark the symbols as reachable. - // In this case that is not necessarily true, so stick to what - // we know before entering this function. - s.Attr.Set(sym.AttrReachable, reachable) + p := fmt.Sprintf("%s.str", name) + sp := l.LookupOrCreateSym(p, 0) + sbld := l.MakeSymbolUpdater(sp) - sp.Attr.Set(sym.AttrReachable, reachable) + sbld.Addstring(value) + sbld.SetType(sym.SRODATA) + + bld.SetSize(0) + bld.SetData(make([]byte, 0, arch.PtrSize*2)) + bld.SetReadOnly(false) + bld.SetRelocs(nil) + bld.AddAddrPlus(arch, sp, 0) + bld.AddUint(arch, uint64(len(value))) } func (ctxt *Link) dostrdata() { for _, name := range strnames { - addstrdata(ctxt, name, strdata[name]) + addstrdata(ctxt.Arch, ctxt.loader, name, strdata[name]) } } @@ -1139,71 +1261,131 @@ func checkdatsize(ctxt *Link, datsize int64, symn sym.SymKind) { } } -// datap is a collection of reachable data symbols in address order. -// Generated by dodata. -var datap []*sym.Symbol +// fixZeroSizedSymbols gives a few special symbols with zero size some space. +func fixZeroSizedSymbols(ctxt *Link) { + // The values in moduledata are filled out by relocations + // pointing to the addresses of these special symbols. + // Typically these symbols have no size and are not laid + // out with their matching section. + // + // However on darwin, dyld will find the special symbol + // in the first loaded module, even though it is local. + // + // (An hypothesis, formed without looking in the dyld sources: + // these special symbols have no size, so their address + // matches a real symbol. The dynamic linker assumes we + // want the normal symbol with the same address and finds + // it in the other module.) + // + // To work around this we lay out the symbls whose + // addresses are vital for multi-module programs to work + // as normal symbols, and give them a little size. + // + // On AIX, as all DATA sections are merged together, ld might not put + // these symbols at the beginning of their respective section if there + // aren't real symbols, their alignment might not match the + // first symbol alignment. Therefore, there are explicitly put at the + // beginning of their section with the same alignment. + if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + return + } -func (ctxt *Link) dodata() { - if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { - // The values in moduledata are filled out by relocations - // pointing to the addresses of these special symbols. - // Typically these symbols have no size and are not laid - // out with their matching section. - // - // However on darwin, dyld will find the special symbol - // in the first loaded module, even though it is local. - // - // (An hypothesis, formed without looking in the dyld sources: - // these special symbols have no size, so their address - // matches a real symbol. The dynamic linker assumes we - // want the normal symbol with the same address and finds - // it in the other module.) - // - // To work around this we lay out the symbls whose - // addresses are vital for multi-module programs to work - // as normal symbols, and give them a little size. - // - // On AIX, as all DATA sections are merged together, ld might not put - // these symbols at the beginning of their respective section if there - // aren't real symbols, their alignment might not match the - // first symbol alignment. Therefore, there are explicitly put at the - // beginning of their section with the same alignment. - bss := ctxt.Syms.Lookup("runtime.bss", 0) - bss.Size = 8 - bss.Attr.Set(sym.AttrSpecial, false) - - ctxt.Syms.Lookup("runtime.ebss", 0).Attr.Set(sym.AttrSpecial, false) - - data := ctxt.Syms.Lookup("runtime.data", 0) - data.Size = 8 - data.Attr.Set(sym.AttrSpecial, false) - - edata := ctxt.Syms.Lookup("runtime.edata", 0) - edata.Attr.Set(sym.AttrSpecial, false) - if ctxt.HeadType == objabi.Haix { - // XCOFFTOC symbols are part of .data section. - edata.Type = sym.SXCOFFTOC - } + bss := ctxt.Syms.Lookup("runtime.bss", 0) + bss.Size = 8 + bss.Attr.Set(sym.AttrSpecial, false) - types := ctxt.Syms.Lookup("runtime.types", 0) - types.Type = sym.STYPE - types.Size = 8 - types.Attr.Set(sym.AttrSpecial, false) + ctxt.Syms.Lookup("runtime.ebss", 0).Attr.Set(sym.AttrSpecial, false) - etypes := ctxt.Syms.Lookup("runtime.etypes", 0) - etypes.Type = sym.SFUNCTAB - etypes.Attr.Set(sym.AttrSpecial, false) + data := ctxt.Syms.Lookup("runtime.data", 0) + data.Size = 8 + data.Attr.Set(sym.AttrSpecial, false) - if ctxt.HeadType == objabi.Haix { - rodata := ctxt.Syms.Lookup("runtime.rodata", 0) - rodata.Type = sym.SSTRING - rodata.Size = 8 - rodata.Attr.Set(sym.AttrSpecial, false) + edata := ctxt.Syms.Lookup("runtime.edata", 0) + edata.Attr.Set(sym.AttrSpecial, false) + if ctxt.HeadType == objabi.Haix { + // XCOFFTOC symbols are part of .data section. + edata.Type = sym.SXCOFFTOC + } + + types := ctxt.Syms.Lookup("runtime.types", 0) + types.Type = sym.STYPE + types.Size = 8 + types.Attr.Set(sym.AttrSpecial, false) + + etypes := ctxt.Syms.Lookup("runtime.etypes", 0) + etypes.Type = sym.SFUNCTAB + etypes.Attr.Set(sym.AttrSpecial, false) + + if ctxt.HeadType == objabi.Haix { + rodata := ctxt.Syms.Lookup("runtime.rodata", 0) + rodata.Type = sym.SSTRING + rodata.Size = 8 + rodata.Attr.Set(sym.AttrSpecial, false) - ctxt.Syms.Lookup("runtime.erodata", 0).Attr.Set(sym.AttrSpecial, false) + ctxt.Syms.Lookup("runtime.erodata", 0).Attr.Set(sym.AttrSpecial, false) + } +} + +// makeRelroForSharedLib creates a section of readonly data if necessary. +func makeRelroForSharedLib(target *Link, data *[sym.SXREF][]*sym.Symbol) { + if !target.UseRelro() { + return + } + + // "read only" data with relocations needs to go in its own section + // when building a shared library. We do this by boosting objects of + // type SXXX with relocations to type SXXXRELRO. + for _, symnro := range sym.ReadOnly { + symnrelro := sym.RelROMap[symnro] + + ro := []*sym.Symbol{} + relro := data[symnrelro] + + for _, s := range data[symnro] { + isRelro := len(s.R) > 0 + switch s.Type { + case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO: + // Symbols are not sorted yet, so it is possible + // that an Outer symbol has been changed to a + // relro Type before it reaches here. + isRelro = true + case sym.SFUNCTAB: + if target.IsAIX() && s.Name == "runtime.etypes" { + // runtime.etypes must be at the end of + // the relro datas. + isRelro = true + } + } + if isRelro { + s.Type = symnrelro + if s.Outer != nil { + s.Outer.Type = s.Type + } + relro = append(relro, s) + } else { + ro = append(ro, s) + } + } + // Check that we haven't made two symbols with the same .Outer into + // different types (because references two symbols with non-nil Outer + // become references to the outer symbol + offset it's vital that the + // symbol and the outer end up in the same section). + for _, s := range relro { + if s.Outer != nil && s.Outer.Type != s.Type { + Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)", + s.Outer.Name, s.Type, s.Outer.Type) + } } + + data[symnro] = ro + data[symnrelro] = relro } +} + +func (ctxt *Link) dodata() { + // Give zeros sized symbols space if necessary. + fixZeroSizedSymbols(ctxt) // Collect data symbols by type into data. var data [sym.SXREF][]*sym.Symbol @@ -1228,57 +1410,8 @@ func (ctxt *Link) dodata() { } dynreloc(ctxt, &data) - if ctxt.UseRelro() { - // "read only" data with relocations needs to go in its own section - // when building a shared library. We do this by boosting objects of - // type SXXX with relocations to type SXXXRELRO. - for _, symnro := range sym.ReadOnly { - symnrelro := sym.RelROMap[symnro] - - ro := []*sym.Symbol{} - relro := data[symnrelro] - - for _, s := range data[symnro] { - isRelro := len(s.R) > 0 - switch s.Type { - case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO: - // Symbols are not sorted yet, so it is possible - // that an Outer symbol has been changed to a - // relro Type before it reaches here. - isRelro = true - case sym.SFUNCTAB: - if ctxt.HeadType == objabi.Haix && s.Name == "runtime.etypes" { - // runtime.etypes must be at the end of - // the relro datas. - isRelro = true - } - } - if isRelro { - s.Type = symnrelro - if s.Outer != nil { - s.Outer.Type = s.Type - } - relro = append(relro, s) - } else { - ro = append(ro, s) - } - } - - // Check that we haven't made two symbols with the same .Outer into - // different types (because references two symbols with non-nil Outer - // become references to the outer symbol + offset it's vital that the - // symbol and the outer end up in the same section). - for _, s := range relro { - if s.Outer != nil && s.Outer.Type != s.Type { - Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)", - s.Outer.Name, s.Type, s.Outer.Type) - } - } - - data[symnro] = ro - data[symnrelro] = relro - } - } + // Move any RO data with relocations to a separate section. + makeRelroForSharedLib(ctxt, &data) // Sort symbols. var dataMaxAlign [sym.SXREF]int32 @@ -1767,7 +1900,7 @@ func (ctxt *Link) dodata() { } for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { - datap = append(datap, data[symn]...) + ctxt.datap = append(ctxt.datap, data[symn]...) } dwarfGenerateDebugSyms(ctxt) @@ -1780,6 +1913,7 @@ func (ctxt *Link) dodata() { } sect = addsection(ctxt.Arch, &Segdwarf, s.Name, 04) + sect.Sym = s sect.Align = 1 datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) @@ -1794,18 +1928,20 @@ func (ctxt *Link) dodata() { for i < len(dwarfp) { curType := dwarfp[i].Type var sect *sym.Section + var sectname string switch curType { case sym.SDWARFINFO: - sect = addsection(ctxt.Arch, &Segdwarf, ".debug_info", 04) + sectname = ".debug_info" case sym.SDWARFRANGE: - sect = addsection(ctxt.Arch, &Segdwarf, ".debug_ranges", 04) + sectname = ".debug_ranges" case sym.SDWARFLOC: - sect = addsection(ctxt.Arch, &Segdwarf, ".debug_loc", 04) + sectname = ".debug_loc" default: // Error is unrecoverable, so panic. panic(fmt.Sprintf("unknown DWARF section %v", curType)) } - + sect = addsection(ctxt.Arch, &Segdwarf, sectname, 04) + sect.Sym = ctxt.Syms.ROLookup(sectname, 0) sect.Align = 1 datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) @@ -1980,18 +2116,19 @@ func (ctxt *Link) textbuildid() { return } - s := ctxt.Syms.Lookup("go.buildid", 0) - s.Attr |= sym.AttrReachable + ldr := ctxt.loader + s := ldr.CreateSymForUpdate("go.buildid", 0) + s.SetReachable(true) // The \xff is invalid UTF-8, meant to make it less likely // to find one of these accidentally. data := "\xff Go build ID: " + strconv.Quote(*flagBuildid) + "\n \xff" - s.Type = sym.STEXT - s.P = []byte(data) - s.Size = int64(len(s.P)) + s.SetType(sym.STEXT) + s.SetData([]byte(data)) + s.SetSize(int64(len(data))) - ctxt.Textp = append(ctxt.Textp, nil) - copy(ctxt.Textp[1:], ctxt.Textp) - ctxt.Textp[0] = s + ctxt.Textp2 = append(ctxt.Textp2, 0) + copy(ctxt.Textp2[1:], ctxt.Textp2) + ctxt.Textp2[0] = s.Sym() } func (ctxt *Link) buildinfo() { @@ -2317,7 +2454,7 @@ func (ctxt *Link) address() []*sym.Segment { } } - for _, s := range datap { + for _, s := range ctxt.datap { if s.Sect != nil { s.Value += int64(s.Sect.Vaddr) } @@ -2392,6 +2529,24 @@ func (ctxt *Link) address() []*sym.Segment { ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length)) + if ctxt.IsSolaris() { + // On Solaris, in the runtime it sets the external names of the + // end symbols. Unset them and define separate symbols, so we + // keep both. + etext := ctxt.Syms.ROLookup("runtime.etext", 0) + edata := ctxt.Syms.ROLookup("runtime.edata", 0) + end := ctxt.Syms.ROLookup("runtime.end", 0) + etext.SetExtname("runtime.etext") + edata.SetExtname("runtime.edata") + end.SetExtname("runtime.end") + ctxt.xdefine("_etext", etext.Type, etext.Value) + ctxt.xdefine("_edata", edata.Type, edata.Value) + ctxt.xdefine("_end", end.Type, end.Value) + ctxt.Syms.ROLookup("_etext", 0).Sect = etext.Sect + ctxt.Syms.ROLookup("_edata", 0).Sect = edata.Sect + ctxt.Syms.ROLookup("_end", 0).Sect = end.Sect + } + return order } @@ -2454,6 +2609,8 @@ func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte { binary.BigEndian.PutUint64(sizeBytes[:], uint64(total)) buf.Write(sizeBytes[:]) + var relocbuf []byte // temporary buffer for applying relocations + // Using zlib.BestSpeed achieves very nearly the same // compression levels of zlib.DefaultCompression, but takes // substantially less time. This is important because DWARF @@ -2462,17 +2619,22 @@ func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte { if err != nil { log.Fatalf("NewWriterLevel failed: %s", err) } + target := &ctxt.Target + ldr := ctxt.loader + reporter := &ctxt.ErrorReporter + archSyms := &ctxt.ArchSyms for _, s := range syms { // s.P may be read-only. Apply relocations in a // temporary buffer, and immediately write it out. oldP := s.P wasReadOnly := s.Attr.ReadOnly() if len(s.R) != 0 && wasReadOnly { - ctxt.relocbuf = append(ctxt.relocbuf[:0], s.P...) - s.P = ctxt.relocbuf + relocbuf = append(relocbuf[:0], s.P...) + s.P = relocbuf + // TODO: This function call needs to be parallelized when the loader wavefront gets here. s.Attr.Set(sym.AttrReadOnly, false) } - relocsym(ctxt, s) + relocsym(target, ldr, reporter, archSyms, s) if _, err := z.Write(s.P); err != nil { log.Fatalf("compression failed: %s", err) } diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 6d96ae50d7..13ddcdac24 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -6,11 +6,7 @@ package ld import ( "cmd/internal/objabi" - "cmd/internal/sys" "cmd/link/internal/sym" - "fmt" - "strings" - "unicode" ) // deadcode marks all reachable symbols. @@ -46,363 +42,59 @@ import ( // // Any unreached text symbols are removed from ctxt.Textp. func deadcode(ctxt *Link) { - if ctxt.Debugvlog != 0 { - ctxt.Logf("deadcode\n") - } - - if *flagNewobj { - deadcode2(ctxt) - return - } - - d := &deadcodepass{ - ctxt: ctxt, - ifaceMethod: make(map[methodsig]bool), - } - - // First, flood fill any symbols directly reachable in the call - // graph from *flagEntrySymbol. Ignore all methods not directly called. - d.init() - d.flood() - - callSym := ctxt.Syms.ROLookup("reflect.Value.Call", sym.SymVerABIInternal) - methSym := ctxt.Syms.ROLookup("reflect.Value.Method", sym.SymVerABIInternal) - reflectSeen := false - - if ctxt.DynlinkingGo() { - // Exported methods may satisfy interfaces we don't know - // about yet when dynamically linking. - reflectSeen = true - } - - for { - if !reflectSeen { - if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) { - // Methods might be called via reflection. Give up on - // static analysis, mark all exported methods of - // all reachable types as reachable. - reflectSeen = true - } - } - - // Mark all methods that could satisfy a discovered - // interface as reachable. We recheck old marked interfaces - // as new types (with new methods) may have been discovered - // in the last pass. - var rem []methodref - for _, m := range d.markableMethods { - if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] { - d.markMethod(m) - } else { - rem = append(rem, m) - } - } - d.markableMethods = rem - - if len(d.markQueue) == 0 { - // No new work was discovered. Done. - break - } - d.flood() - } - - // Remove all remaining unreached R_METHODOFF relocations. - for _, m := range d.markableMethods { - for _, r := range m.r { - d.cleanupReloc(r) - } - } - - if ctxt.BuildMode != BuildModeShared { - // Keep a itablink if the symbol it points at is being kept. - // (When BuildModeShared, always keep itablinks.) - for _, s := range ctxt.Syms.Allsym { - if strings.HasPrefix(s.Name, "go.itablink.") { - s.Attr.Set(sym.AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable()) - } - } - } - - addToTextp(ctxt) + deadcode2(ctxt) } +// addToTextp populates the context Textp slice (needed in various places +// in the linker) and also the unit Textp slices (needed by the "old" +// phase 2 DWARF generation). func addToTextp(ctxt *Link) { - // Remove dead text but keep file information (z symbols). - textp := []*sym.Symbol{} - for _, s := range ctxt.Textp { - if s.Attr.Reachable() { - textp = append(textp, s) + + // First set up ctxt.Textp, based on ctxt.Textp2. + textp := make([]*sym.Symbol, 0, len(ctxt.Textp2)) + haveshlibs := len(ctxt.Shlibs) > 0 + for _, tsym := range ctxt.Textp2 { + sp := ctxt.loader.Syms[tsym] + if sp == nil || !ctxt.loader.AttrReachable(tsym) { + panic("should never happen") } + if haveshlibs && sp.Type == sym.SDYNIMPORT { + continue + } + textp = append(textp, sp) } + ctxt.Textp = textp - // Put reachable text symbols into Textp. - // do it in postorder so that packages are laid down in dependency order - // internal first, then everything else - ctxt.Library = postorder(ctxt.Library) + // Dupok symbols may be defined in multiple packages; the + // associated package for a dupok sym is chosen sort of + // arbitrarily (the first containing package that the linker + // loads). The loop below canonicalizes the File to the package + // with which it will be laid down in text. Assumes that + // ctxt.Library is already in postorder. for _, doInternal := range [2]bool{true, false} { for _, lib := range ctxt.Library { if isRuntimeDepPkg(lib.Pkg) != doInternal { continue } - libtextp := lib.Textp[:0] - for _, s := range lib.Textp { - if s.Attr.Reachable() { - textp = append(textp, s) - libtextp = append(libtextp, s) - if s.Unit != nil { - s.Unit.Textp = append(s.Unit.Textp, s) - } + for _, dsym := range lib.DupTextSyms2 { + tsp := ctxt.loader.Syms[dsym] + if !tsp.Attr.OnList() { + tsp.Attr |= sym.AttrOnList + tsp.File = objabi.PathToPrefix(lib.Pkg) } } - for _, s := range lib.DupTextSyms { - if s.Attr.Reachable() && !s.Attr.OnList() { - textp = append(textp, s) - libtextp = append(libtextp, s) - if s.Unit != nil { - s.Unit.Textp = append(s.Unit.Textp, s) - } - s.Attr |= sym.AttrOnList - // dupok symbols may be defined in multiple packages. its - // associated package is chosen sort of arbitrarily (the - // first containing package that the linker loads). canonicalize - // it here to the package with which it will be laid down - // in text. - s.File = objabi.PathToPrefix(lib.Pkg) - } - } - lib.Textp = libtextp } } - ctxt.Textp = textp - - if len(ctxt.Shlibs) > 0 { - // We might have overwritten some functions above (this tends to happen for the - // autogenerated type equality/hashing functions) and we don't want to generated - // pcln table entries for these any more so remove them from Textp. - textp := make([]*sym.Symbol, 0, len(ctxt.Textp)) - for _, s := range ctxt.Textp { - if s.Type != sym.SDYNIMPORT { - textp = append(textp, s) - } - } - ctxt.Textp = textp - } -} - -// methodref holds the relocations from a receiver type symbol to its -// method. There are three relocations, one for each of the fields in -// the reflect.method struct: mtyp, ifn, and tfn. -type methodref struct { - m methodsig - src *sym.Symbol // receiver type symbol - r [3]*sym.Reloc // R_METHODOFF relocations to fields of runtime.method -} - -func (m methodref) ifn() *sym.Symbol { return m.r[1].Sym } - -func (m methodref) isExported() bool { - for _, r := range m.m { - return unicode.IsUpper(r) - } - panic("methodref has no signature") -} - -// deadcodepass holds state for the deadcode flood fill. -type deadcodepass struct { - ctxt *Link - markQueue []*sym.Symbol // symbols to flood fill in next pass - ifaceMethod map[methodsig]bool // methods declared in reached interfaces - markableMethods []methodref // methods of reached types - reflectMethod bool -} - -func (d *deadcodepass) cleanupReloc(r *sym.Reloc) { - if r.Sym.Attr.Reachable() { - r.Type = objabi.R_ADDROFF - } else { - if d.ctxt.Debugvlog > 1 { - d.ctxt.Logf("removing method %s\n", r.Sym.Name) - } - r.Sym = nil - r.Siz = 0 - } -} - -// mark appends a symbol to the mark queue for flood filling. -func (d *deadcodepass) mark(s, parent *sym.Symbol) { - if s == nil || s.Attr.Reachable() { - return - } - if s.Attr.ReflectMethod() { - d.reflectMethod = true - } - if *flagDumpDep { - p := "_" - if parent != nil { - p = parent.Name - } - fmt.Printf("%s -> %s\n", p, s.Name) - } - s.Attr |= sym.AttrReachable - if d.ctxt.Reachparent != nil { - d.ctxt.Reachparent[s] = parent - } - d.markQueue = append(d.markQueue, s) -} - -// markMethod marks a method as reachable. -func (d *deadcodepass) markMethod(m methodref) { - for _, r := range m.r { - d.mark(r.Sym, m.src) - r.Type = objabi.R_ADDROFF - } -} - -// init marks all initial symbols as reachable. -// In a typical binary, this is *flagEntrySymbol. -func (d *deadcodepass) init() { - var names []string - - if d.ctxt.BuildMode == BuildModeShared { - // Mark all symbols defined in this library as reachable when - // building a shared library. - for _, s := range d.ctxt.Syms.Allsym { - if s.Type != 0 && s.Type != sym.SDYNIMPORT { - d.mark(s, nil) - } - } - } else { - // In a normal binary, start at main.main and the init - // functions and mark what is reachable from there. - - if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { - names = append(names, "main.main", "main..inittask") - } else { - // The external linker refers main symbol directly. - if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { - if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 { - *flagEntrySymbol = "_main" - } else { - *flagEntrySymbol = "main" - } - } - names = append(names, *flagEntrySymbol) - if d.ctxt.BuildMode == BuildModePlugin { - names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs") - - // We don't keep the go.plugin.exports symbol, - // but we do keep the symbols it refers to. - exports := d.ctxt.Syms.ROLookup("go.plugin.exports", 0) - if exports != nil { - for i := range exports.R { - d.mark(exports.R[i].Sym, nil) - } - } - } - } - for _, s := range dynexp { - d.mark(s, nil) - } - } - - for _, name := range names { - // Mark symbol as a data/ABI0 symbol. - d.mark(d.ctxt.Syms.ROLookup(name, 0), nil) - // Also mark any Go functions (internal ABI). - d.mark(d.ctxt.Syms.ROLookup(name, sym.SymVerABIInternal), nil) - } -} - -// flood fills symbols reachable from the markQueue symbols. -// As it goes, it collects methodref and interface method declarations. -func (d *deadcodepass) flood() { - for len(d.markQueue) > 0 { - s := d.markQueue[0] - d.markQueue = d.markQueue[1:] - if s.Type == sym.STEXT { - if d.ctxt.Debugvlog > 1 { - d.ctxt.Logf("marktext %s\n", s.Name) - } - } - - if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' { - if len(s.P) == 0 { - // Probably a bug. The undefined symbol check - // later will give a better error than deadcode. - continue - } - if decodetypeKind(d.ctxt.Arch, s.P)&kindMask == kindInterface { - for _, sig := range decodeIfaceMethods(d.ctxt.Arch, s) { - if d.ctxt.Debugvlog > 1 { - d.ctxt.Logf("reached iface method: %s\n", sig) - } - d.ifaceMethod[sig] = true - } - } - } - - mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype - var methods []methodref - for i := range s.R { - r := &s.R[i] - if r.Sym == nil { - continue - } - if r.Type == objabi.R_WEAKADDROFF { - // An R_WEAKADDROFF relocation is not reason - // enough to mark the pointed-to symbol as - // reachable. - continue - } - if r.Sym.Type == sym.SABIALIAS { - // Patch this relocation through the - // ABI alias before marking. - r.Sym = resolveABIAlias(r.Sym) - } - if r.Type != objabi.R_METHODOFF { - d.mark(r.Sym, s) - continue - } - // Collect rtype pointers to methods for - // later processing in deadcode. - if mpos == 0 { - m := methodref{src: s} - m.r[0] = r - methods = append(methods, m) - } else { - methods[len(methods)-1].r[mpos] = r - } - mpos++ - if mpos == len(methodref{}.r) { - mpos = 0 - } - } - if len(methods) > 0 { - // Decode runtime type information for type methods - // to help work out which methods can be called - // dynamically via interfaces. - methodsigs := decodetypeMethods(d.ctxt.Arch, s) - if len(methods) != len(methodsigs) { - panic(fmt.Sprintf("%q has %d method relocations for %d methods", s.Name, len(methods), len(methodsigs))) - } - for i, m := range methodsigs { - name := string(m) - name = name[:strings.Index(name, "(")] - if !strings.HasSuffix(methods[i].ifn().Name, name) { - panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, methods[i].ifn().Name, name)) - } - methods[i].m = m - } - d.markableMethods = append(d.markableMethods, methods...) - } - if s.FuncInfo != nil { - for i := range s.FuncInfo.Funcdata { - d.mark(s.FuncInfo.Funcdata[i], s) + // Finally, set up compilation unit Textp slices. Can be removed + // once loader-Sym DWARF-gen phase 2 is always enabled. + for _, lib := range ctxt.Library { + for _, unit := range lib.Units { + for _, usym := range unit.Textp2 { + usp := ctxt.loader.Syms[usym] + usp.Attr |= sym.AttrOnList + unit.Textp = append(unit.Textp, usp) } } - d.mark(s.Gotype, s) - d.mark(s.Sub, s) - d.mark(s.Outer, s) } } diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go index 9197c700f5..3342efe39f 100644 --- a/src/cmd/link/internal/ld/deadcode2.go +++ b/src/cmd/link/internal/ld/deadcode2.go @@ -6,7 +6,6 @@ package ld import ( "bytes" - "cmd/internal/dwarf" "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/loader" @@ -37,7 +36,6 @@ type deadcodePass2 struct { ctxt *Link ldr *loader.Loader wq workQueue - rtmp []loader.Reloc ifaceMethod map[methodsig]bool // methods declared in reached interfaces markableMethods []methodref2 // methods of reached types @@ -58,9 +56,7 @@ func (d *deadcodePass2) init() { n := d.ldr.NDef() for i := 1; i < n; i++ { s := loader.Sym(i) - if !d.ldr.IsDup(s) { - d.mark(s, 0) - } + d.mark(s, 0) } return } @@ -88,9 +84,9 @@ func (d *deadcodePass2) init() { // but we do keep the symbols it refers to. exportsIdx := d.ldr.Lookup("go.plugin.exports", 0) if exportsIdx != 0 { - d.ReadRelocs(exportsIdx) - for i := 0; i < len(d.rtmp); i++ { - d.mark(d.rtmp[i].Sym, 0) + relocs := d.ldr.Relocs(exportsIdx) + for i := 0; i < relocs.Count(); i++ { + d.mark(relocs.At2(i).Sym(), 0) } } } @@ -104,14 +100,6 @@ func (d *deadcodePass2) init() { names = append(names, exp) } - // DWARF constant DIE symbols are not referenced, but needed by - // the dwarf pass. - if !*FlagW { - for _, lib := range d.ctxt.Library { - names = append(names, dwarf.ConstInfoPrefix+lib.Pkg) - } - } - for _, name := range names { // Mark symbol as a data/ABI0 symbol. d.mark(d.ldr.Lookup(name, 0), 0) @@ -121,20 +109,18 @@ func (d *deadcodePass2) init() { } func (d *deadcodePass2) flood() { - symRelocs := []loader.Reloc{} - auxSyms := []loader.Sym{} for !d.wq.empty() { symIdx := d.wq.pop() d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx) + isgotype := d.ldr.IsGoType(symIdx) relocs := d.ldr.Relocs(symIdx) - symRelocs = relocs.ReadAll(symRelocs) - if d.ldr.IsGoType(symIdx) { + if isgotype { p := d.ldr.Data(symIdx) if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface { - for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) { + for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) { if d.ctxt.Debugvlog > 1 { d.ctxt.Logf("reached iface method: %s\n", sig) } @@ -144,30 +130,31 @@ func (d *deadcodePass2) flood() { } var methods []methodref2 - for i := 0; i < relocs.Count; i++ { - r := symRelocs[i] - if r.Type == objabi.R_WEAKADDROFF { + for i := 0; i < relocs.Count(); i++ { + r := relocs.At2(i) + t := r.Type() + if t == objabi.R_WEAKADDROFF { continue } - if r.Type == objabi.R_METHODOFF { - if i+2 >= relocs.Count { + if t == objabi.R_METHODOFF { + if i+2 >= relocs.Count() { panic("expect three consecutive R_METHODOFF relocs") } methods = append(methods, methodref2{src: symIdx, r: i}) i += 2 continue } - if r.Type == objabi.R_USETYPE { + if t == objabi.R_USETYPE { // type symbol used for DWARF. we need to load the symbol but it may not // be otherwise reachable in the program. // do nothing for now as we still load all type symbols. continue } - d.mark(r.Sym, symIdx) + d.mark(r.Sym(), symIdx) } - auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms) - for i := 0; i < len(auxSyms); i++ { - d.mark(auxSyms[i], symIdx) + naux := d.ldr.NAux(symIdx) + for i := 0; i < naux; i++ { + d.mark(d.ldr.Aux2(symIdx, i).Sym(), symIdx) } // Some host object symbols have an outer object, which acts like a // "carrier" symbol, or it holds all the symbols for a particular @@ -175,14 +162,19 @@ func (d *deadcodePass2) flood() { // so we make sure we're pulling in all outer symbols, and their sub // symbols. This is not ideal, and these carrier/section symbols could // be removed. - d.mark(d.ldr.OuterSym(symIdx), symIdx) - d.mark(d.ldr.SubSym(symIdx), symIdx) + if d.ldr.IsExternal(symIdx) { + d.mark(d.ldr.OuterSym(symIdx), symIdx) + d.mark(d.ldr.SubSym(symIdx), symIdx) + } if len(methods) != 0 { + if !isgotype { + panic("method found on non-type symbol") + } // Decode runtime type information for type methods // to help work out which methods can be called // dynamically via interfaces. - methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) + methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) if len(methods) != len(methodsigs) { panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs))) } @@ -195,9 +187,9 @@ func (d *deadcodePass2) flood() { } func (d *deadcodePass2) mark(symIdx, parent loader.Sym) { - if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) { + if symIdx != 0 && !d.ldr.AttrReachable(symIdx) { d.wq.push(symIdx) - d.ldr.Reachable.Set(symIdx) + d.ldr.SetAttrReachable(symIdx, true) if d.ctxt.Reachparent != nil { d.ldr.Reachparent[symIdx] = parent } @@ -215,10 +207,10 @@ func (d *deadcodePass2) mark(symIdx, parent loader.Sym) { } func (d *deadcodePass2) markMethod(m methodref2) { - d.ReadRelocs(m.src) - d.mark(d.rtmp[m.r].Sym, m.src) - d.mark(d.rtmp[m.r+1].Sym, m.src) - d.mark(d.rtmp[m.r+2].Sym, m.src) + relocs := d.ldr.Relocs(m.src) + d.mark(relocs.At2(m.r).Sym(), m.src) + d.mark(relocs.At2(m.r+1).Sym(), m.src) + d.mark(relocs.At2(m.r+2).Sym(), m.src) } func deadcode2(ctxt *Link) { @@ -239,7 +231,7 @@ func deadcode2(ctxt *Link) { // Methods might be called via reflection. Give up on // static analysis, mark all exported methods of // all reachable types as reachable. - d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.Reachable.Has(callSym)) || (methSym != 0 && ldr.Reachable.Has(methSym)) + d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.AttrReachable(callSym)) || (methSym != 0 && ldr.AttrReachable(methSym)) // Mark all methods that could satisfy a discovered // interface as reachable. We recheck old marked interfaces @@ -271,8 +263,8 @@ func deadcode2(ctxt *Link) { s := loader.Sym(i) if ldr.IsItabLink(s) { relocs := ldr.Relocs(s) - if relocs.Count > 0 && ldr.Reachable.Has(relocs.At(0).Sym) { - ldr.Reachable.Set(s) + if relocs.Count() > 0 && ldr.AttrReachable(relocs.At2(0).Sym()) { + ldr.SetAttrReachable(s, true) } } } @@ -301,15 +293,15 @@ func (m methodref2) isExported() bool { // the function type. // // Conveniently this is the layout of both runtime.method and runtime.imethod. -func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, off, size, count int) []methodsig { +func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig { var buf bytes.Buffer var methods []methodsig for i := 0; i < count; i++ { - buf.WriteString(decodetypeName2(ldr, symIdx, symRelocs, off)) - mtypSym := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off+4)) + buf.WriteString(decodetypeName2(ldr, symIdx, relocs, off)) + mtypSym := decodeRelocSym2(ldr, symIdx, relocs, int32(off+4)) // FIXME: add some sort of caching here, since we may see some of the // same symbols over time for param types. - d.ReadRelocs(mtypSym) + mrelocs := ldr.Relocs(mtypSym) mp := ldr.Data(mtypSym) buf.WriteRune('(') @@ -318,7 +310,7 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym if i > 0 { buf.WriteString(", ") } - a := d.decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i) + a := decodetypeFuncInType2(ldr, arch, mtypSym, &mrelocs, i) buf.WriteString(ldr.SymName(a)) } buf.WriteString(") (") @@ -327,7 +319,7 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym if i > 0 { buf.WriteString(", ") } - a := d.decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i) + a := decodetypeFuncOutType2(ldr, arch, mtypSym, &mrelocs, i) buf.WriteString(ldr.SymName(a)) } buf.WriteRune(')') @@ -339,25 +331,26 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym return methods } -func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig { +func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { p := ldr.Data(symIdx) if decodetypeKind(arch, p)&kindMask != kindInterface { panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx))) } - rel := decodeReloc2(ldr, symIdx, symRelocs, int32(commonsize(arch)+arch.PtrSize)) - if rel.Sym == 0 { + rel := decodeReloc2(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize)) + s := rel.Sym() + if s == 0 { return nil } - if rel.Sym != symIdx { + if s != symIdx { panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx))) } - off := int(rel.Add) // array of reflect.imethod values + off := int(rel.Add()) // array of reflect.imethod values numMethods := int(decodetypeIfaceMethodCount(arch, p)) sizeofIMethod := 4 + 4 - return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofIMethod, numMethods) + return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods) } -func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig { +func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { p := ldr.Data(symIdx) if !decodetypeHasUncommon(arch, p) { panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx))) @@ -388,54 +381,5 @@ func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, s moff := int(decodeInuxi(arch, p[off+4+2+2:], 4)) off += moff // offset to array of reflect.method values const sizeofMethod = 4 * 4 // sizeof reflect.method in program - return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofMethod, mcount) -} - -func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Reloc { - for j := 0; j < len(symRelocs); j++ { - rel := symRelocs[j] - if rel.Off == off { - return rel - } - } - return loader.Reloc{} -} - -func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym { - return decodeReloc2(ldr, symIdx, symRelocs, off).Sym -} - -// decodetypeName2 decodes the name from a reflect.name. -func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string { - r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off)) - if r == 0 { - return "" - } - - data := ldr.Data(r) - namelen := int(uint16(data[1])<<8 | uint16(data[2])) - return string(data[3 : 3+namelen]) -} - -func (d *deadcodePass2) decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym { - uadd := commonsize(arch) + 4 - if arch.PtrSize == 8 { - uadd += 4 - } - if decodetypeHasUncommon(arch, ldr.Data(symIdx)) { - uadd += uncommonSize() - } - return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize)) -} - -func (d *deadcodePass2) decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym { - return d.decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx))) -} - -// readRelocs reads the relocations for the specified symbol into the -// deadcode relocs work array. Use with care, since the work array -// is a singleton. -func (d *deadcodePass2) ReadRelocs(symIdx loader.Sym) { - relocs := d.ldr.Relocs(symIdx) - d.rtmp = relocs.ReadAll(d.rtmp) + return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount) } diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index 3271c85157..50586081d3 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -125,11 +125,6 @@ func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte { func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 { if ctxt.Arch.Family == sys.ARM64 { - for _, shlib := range ctxt.Shlibs { - if shlib.Path == s.File { - return shlib.gcdataAddresses[s] - } - } return 0 } return decodeInuxi(ctxt.Arch, s.P[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize) diff --git a/src/cmd/link/internal/ld/decodesym2.go b/src/cmd/link/internal/ld/decodesym2.go new file mode 100644 index 0000000000..318ce36594 --- /dev/null +++ b/src/cmd/link/internal/ld/decodesym2.go @@ -0,0 +1,120 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/sys" + "cmd/link/internal/loader" +) + +// This file contains utilities to decode type.* symbols, for +// loader.Sym symbols (uses new loader interfaces). + +// At some point we'll want to migrate the contents of this file +// to decodesym.go once the rouetines there have been decprecated + removed. + +func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc2 { + for j := 0; j < relocs.Count(); j++ { + rel := relocs.At2(j) + if rel.Off() == off { + return rel + } + } + return loader.Reloc2{} +} + +func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym { + return decodeReloc2(ldr, symIdx, relocs, off).Sym() +} + +// decodetypeName2 decodes the name from a reflect.name. +func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string { + r := decodeRelocSym2(ldr, symIdx, relocs, int32(off)) + if r == 0 { + return "" + } + + data := ldr.Data(r) + namelen := int(uint16(data[1])<<8 | uint16(data[2])) + return string(data[3 : 3+namelen]) +} + +func decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { + uadd := commonsize(arch) + 4 + if arch.PtrSize == 8 { + uadd += 4 + } + if decodetypeHasUncommon(arch, ldr.Data(symIdx)) { + uadd += uncommonSize() + } + return decodeRelocSym2(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize)) +} + +func decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { + return decodetypeFuncInType2(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx))) +} + +func decodetypeArrayElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeArrayLen2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int64 { + data := ldr.Data(symIdx) + return int64(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +func decodetypeChanElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeMapKey2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeMapValue2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38 +} + +func decodetypePtrElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym2(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeStructFieldCount2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int { + data := ldr.Data(symIdx) + return int(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +func decodetypeStructFieldArrayOff2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int { + data := ldr.Data(symIdx) + off := commonsize(arch) + 4*arch.PtrSize + if decodetypeHasUncommon(arch, data) { + off += uncommonSize() + } + off += i * structfieldSize(arch) + return off +} + +func decodetypeStructFieldName2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) string { + off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodetypeName2(ldr, symIdx, &relocs, off) +} + +func decodetypeStructFieldType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym { + off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodeRelocSym2(ldr, symIdx, &relocs, int32(off+arch.PtrSize)) +} + +func decodetypeStructFieldOffsAnon2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 { + off := decodetypeStructFieldArrayOff2(ldr, arch, symIdx, i) + data := ldr.Data(symIdx) + return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize)) +} diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 6eba39bcf7..65d3daeffb 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1,4 +1,4 @@ -// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -19,6 +19,7 @@ import ( "cmd/internal/objabi" "cmd/internal/src" "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "fmt" "log" @@ -26,105 +27,175 @@ import ( "strings" ) -type dwctxt struct { +// dwctxt2 is a wrapper intended to satisfy the method set of +// dwarf.Context, so that functions like dwarf.PutAttrs will work with +// DIEs that use loader.Sym as opposed to *sym.Symbol. It is also +// being used as a place to store tables/maps that are useful as part +// of type conversion (this is just a convenience; it would be easy to +// split these things out into another type if need be). +type dwctxt2 struct { linkctxt *Link + ldr *loader.Loader + arch *sys.Arch + + // This maps type name string (e.g. "uintptr") to loader symbol for + // the DWARF DIE for that type (e.g. "go.info.type.uintptr") + tmap map[string]loader.Sym + + // This maps loader symbol for the DWARF DIE symbol generated for + // a type (e.g. "go.info.uintptr") to the type symbol itself + // ("type.uintptr"). + // FIXME: try converting this map (and the next one) to a single + // array indexed by loader.Sym -- this may perform better. + rtmap map[loader.Sym]loader.Sym + + // This maps Go type symbol (e.g. "type.XXX") to loader symbol for + // the typedef DIE for that type (e.g. "go.info.XXX..def") + tdmap map[loader.Sym]loader.Sym + + // Cache these type symbols, so as to avoid repeatedly looking them up + typeRuntimeEface loader.Sym + typeRuntimeIface loader.Sym + uintptrInfoSym loader.Sym } -func (c dwctxt) PtrSize() int { - return c.linkctxt.Arch.PtrSize +func newdwctxt2(linkctxt *Link, forTypeGen bool) dwctxt2 { + d := dwctxt2{ + linkctxt: linkctxt, + ldr: linkctxt.loader, + arch: linkctxt.Arch, + tmap: make(map[string]loader.Sym), + tdmap: make(map[loader.Sym]loader.Sym), + rtmap: make(map[loader.Sym]loader.Sym), + } + d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface") + d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface") + return d } -func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) { - ls := s.(*sym.Symbol) - ls.AddUintXX(c.linkctxt.Arch, uint64(i), size) + +// dwSym wraps a loader.Sym; this type is meant to obey the interface +// rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and +// DwAttr objects contain references to symbols via this type. +type dwSym loader.Sym + +func (s dwSym) Length(dwarfContext interface{}) int64 { + l := dwarfContext.(dwctxt2).ldr + return int64(len(l.Data(loader.Sym(s)))) } -func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) { - ls := s.(*sym.Symbol) - ls.AddBytes(b) + +func (c dwctxt2) PtrSize() int { + return c.arch.PtrSize } -func (c dwctxt) AddString(s dwarf.Sym, v string) { - Addstring(s.(*sym.Symbol), v) + +func (c dwctxt2) AddInt(s dwarf.Sym, size int, i int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.AddUintXX(c.arch, uint64(i), size) +} + +func (c dwctxt2) AddBytes(s dwarf.Sym, b []byte) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.AddBytes(b) } -func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { +func (c dwctxt2) AddString(s dwarf.Sym, v string) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.Addstring(v) +} + +func (c dwctxt2) AddAddress(s dwarf.Sym, data interface{}, value int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) if value != 0 { - value -= (data.(*sym.Symbol)).Value + value -= dsu.Value() } - s.(*sym.Symbol).AddAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) + tgtds := loader.Sym(data.(dwSym)) + dsu.AddAddrPlus(c.arch, tgtds, value) } -func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { +func (c dwctxt2) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) if value != 0 { - value -= (data.(*sym.Symbol)).Value + value -= dsu.Value() } - s.(*sym.Symbol).AddCURelativeAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) + tgtds := loader.Sym(data.(dwSym)) + dsu.AddCURelativeAddrPlus(c.arch, tgtds, value) } -func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { - ls := s.(*sym.Symbol) +func (c dwctxt2) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + tds := loader.Sym(t.(dwSym)) switch size { default: - Errorf(ls, "invalid size %d in adddwarfref\n", size) - fallthrough - case c.linkctxt.Arch.PtrSize: - ls.AddAddr(c.linkctxt.Arch, t.(*sym.Symbol)) - case 4: - ls.AddAddrPlus4(t.(*sym.Symbol), 0) + c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size) + case c.arch.PtrSize, 4: } - r := &ls.R[len(ls.R)-1] - r.Type = objabi.R_ADDROFF - r.Add = ofs + dsu.AddSymRef(c.arch, tds, ofs, objabi.R_ADDROFF, size) } -func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { +func (c dwctxt2) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { size := 4 if isDwarf64(c.linkctxt) { size = 8 } - - c.AddSectionOffset(s, size, t, ofs) - ls := s.(*sym.Symbol) - ls.R[len(ls.R)-1].Type = objabi.R_DWARFSECREF + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + tds := loader.Sym(t.(dwSym)) + switch size { + default: + c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size) + case c.arch.PtrSize, 4: + } + dsu.AddSymRef(c.arch, tds, ofs, objabi.R_DWARFSECREF, size) } -func (c dwctxt) Logf(format string, args ...interface{}) { +func (c dwctxt2) Logf(format string, args ...interface{}) { c.linkctxt.Logf(format, args...) } // At the moment these interfaces are only used in the compiler. -func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) { +func (c dwctxt2) AddFileRef(s dwarf.Sym, f interface{}) { panic("should be used only in the compiler") } -func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 { +func (c dwctxt2) CurrentOffset(s dwarf.Sym) int64 { panic("should be used only in the compiler") } -func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) { +func (c dwctxt2) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) { panic("should be used only in the compiler") } -func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { +func (c dwctxt2) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { panic("should be used only in the compiler") } -func isDwarf64(ctxt *Link) bool { - return ctxt.HeadType == objabi.Haix -} - var gdbscript string -var dwarfp []*sym.Symbol +var dwarfp2 []loader.Sym -func writeabbrev(ctxt *Link) *sym.Symbol { - s := ctxt.Syms.Lookup(".debug_abbrev", 0) - s.Type = sym.SDWARFSECT - s.AddBytes(dwarf.GetAbbrev()) - return s +func (d *dwctxt2) writeabbrev() loader.Sym { + abrvs := d.ldr.LookupOrCreateSym(".debug_abbrev", 0) + u := d.ldr.MakeSymbolUpdater(abrvs) + u.SetType(sym.SDWARFSECT) + u.AddBytes(dwarf.GetAbbrev()) + return abrvs } var dwtypes dwarf.DWDie +// newattr attaches a new attribute to the specified DIE. +// +// FIXME: at the moment attributes are stored in a linked list in a +// fairly space-inefficient way -- it might be better to instead look +// up all attrs in a single large table, then store indices into the +// table in the DIE. This would allow us to common up storage for +// attributes that are shared by many DIEs (ex: byte size of N). func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr { a := new(dwarf.DWAttr) a.Link = die.Attr @@ -165,7 +236,8 @@ func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr { // attribute (but it will only be written out if it is listed in the abbrev). // The compiler does create nameless DWARF DIEs (ex: concrete subprogram // instance). -func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie { +// FIXME: it would be more efficient to bulk-allocate DIEs. +func (d *dwctxt2) newdie(parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie { die := new(dwarf.DWDie) die.Abbrev = abbrev die.Link = parent.Child @@ -174,15 +246,22 @@ func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version in newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name) if name != "" && (abbrev <= dwarf.DW_ABRV_VARIABLE || abbrev >= dwarf.DW_ABRV_NULLTYPE) { + // Q: do we need version here? My understanding is that all these + // symbols should be version 0. if abbrev != dwarf.DW_ABRV_VARIABLE || version == 0 { if abbrev == dwarf.DW_ABRV_COMPUNIT { // Avoid collisions with "real" symbol names. - name = fmt.Sprintf(".pkg.%s.%d", name, len(ctxt.compUnits)) + name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits)) + } + ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, version) + dsu := d.ldr.MakeSymbolUpdater(ds) + dsu.SetType(sym.SDWARFINFO) + d.ldr.SetAttrNotInSymbolTable(ds, true) + d.ldr.SetAttrReachable(ds, true) + die.Sym = dwSym(ds) + if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL { + d.tmap[name] = ds } - s := ctxt.Syms.Lookup(dwarf.InfoPrefix+name, version) - s.Attr |= sym.AttrNotInSymbolTable - s.Type = sym.SDWARFINFO - die.Sym = s } } @@ -205,11 +284,22 @@ func walktypedef(die *dwarf.DWDie) *dwarf.DWDie { return die } -func walksymtypedef(ctxt *Link, s *sym.Symbol) *sym.Symbol { - if t := ctxt.Syms.ROLookup(s.Name+"..def", int(s.Version)); t != nil { - return t +func (d *dwctxt2) walksymtypedef(symIdx loader.Sym) loader.Sym { + + // We're being given the loader symbol for the type DIE, e.g. + // "go.info.type.uintptr". Map that first to the type symbol (e.g. + // "type.uintptr") and then to the typedef DIE for the type. + // FIXME: this seems clunky, maybe there is a better way to do this. + + if ts, ok := d.rtmap[symIdx]; ok { + if def, ok := d.tdmap[ts]; ok { + return def + } + d.linkctxt.Errorf(ts, "internal error: no entry for sym %d in tdmap\n", ts) + return 0 } - return s + d.linkctxt.Errorf(symIdx, "internal error: no entry for sym %d in rtmap\n", symIdx) + return 0 } // Find child by AT_name using hashtable if available or linear scan @@ -230,73 +320,66 @@ func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie { // Used to avoid string allocation when looking up dwarf symbols var prefixBuf = []byte(dwarf.InfoPrefix) -func find(ctxt *Link, name string) *sym.Symbol { - n := append(prefixBuf, name...) - // The string allocation below is optimized away because it is only used in a map lookup. - s := ctxt.Syms.ROLookup(string(n), 0) - prefixBuf = n[:len(dwarf.InfoPrefix)] - if s != nil && s.Type == sym.SDWARFINFO { - return s - } - return nil +// find looks up the loader symbol for the DWARF DIE generated for the +// type with the specified name. +func (d *dwctxt2) find(name string) loader.Sym { + return d.tmap[name] } -func mustFind(ctxt *Link, name string) *sym.Symbol { - r := find(ctxt, name) - if r == nil { +func (d *dwctxt2) mustFind(name string) loader.Sym { + r := d.find(name) + if r == 0 { Exitf("dwarf find: cannot find %s", name) } return r } -func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 { +func (d *dwctxt2) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) int64 { var result int64 switch size { default: - Errorf(s, "invalid size %d in adddwarfref\n", size) - fallthrough - case ctxt.Arch.PtrSize: - result = s.AddAddr(ctxt.Arch, t) - case 4: - result = s.AddAddrPlus4(t, 0) - } - r := &s.R[len(s.R)-1] - r.Type = objabi.R_DWARFSECREF + d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size) + case d.arch.PtrSize, 4: + } + result = sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size) return result } -func newrefattr(die *dwarf.DWDie, attr uint16, ref *sym.Symbol) *dwarf.DWAttr { - if ref == nil { +func (d *dwctxt2) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) *dwarf.DWAttr { + if ref == 0 { return nil } - return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, ref) + return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref)) } -func dtolsym(s dwarf.Sym) *sym.Symbol { +func (d *dwctxt2) dtolsym(s dwarf.Sym) loader.Sym { if s == nil { - return nil + return 0 } - return s.(*sym.Symbol) + dws := loader.Sym(s.(dwSym)) + return dws } -func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*sym.Symbol, die *dwarf.DWDie) []*sym.Symbol { - s := dtolsym(die.Sym) - if s == nil { +func (d *dwctxt2) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym { + s := d.dtolsym(die.Sym) + if s == 0 { s = syms[len(syms)-1] } else { - if s.Attr.OnList() { - log.Fatalf("symbol %s listed multiple times", s.Name) + if d.ldr.AttrOnList(s) { + log.Fatalf("symbol %s listed multiple times", d.ldr.SymName(s)) } - s.Attr |= sym.AttrOnList + d.ldr.SetAttrOnList(s, true) syms = append(syms, s) } - dwarf.Uleb128put(ctxt, s, int64(die.Abbrev)) - dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr) + sDwsym := dwSym(s) + dwarf.Uleb128put(d, sDwsym, int64(die.Abbrev)) + dwarf.PutAttrs(d, sDwsym, die.Abbrev, die.Attr) if dwarf.HasChildren(die) { for die := die.Child; die != nil; die = die.Link { - syms = putdie(linkctxt, ctxt, syms, die) + syms = d.putdie(syms, die) } - syms[len(syms)-1].AddUint8(0) + dsu := d.ldr.MakeSymbolUpdater(syms[len(syms)-1]) + dsu.AddUint8(0) } return syms } @@ -329,42 +412,23 @@ func newmemberoffsetattr(die *dwarf.DWDie, offs int32) { // GDB doesn't like FORM_addr for AT_location, so emit a // location expression that evals to a const. -func newabslocexprattr(die *dwarf.DWDie, addr int64, sym *sym.Symbol) { - newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, sym) - // below +func (d *dwctxt2) newabslocexprattr(die *dwarf.DWDie, addr int64, symIdx loader.Sym) { + newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, dwSym(symIdx)) } -// Lookup predefined types -func lookupOrDiag(ctxt *Link, n string) *sym.Symbol { - s := ctxt.Syms.ROLookup(n, 0) - if s == nil || s.Size == 0 { +func (d *dwctxt2) lookupOrDiag(n string) loader.Sym { + symIdx := d.ldr.Lookup(n, 0) + if symIdx == 0 { Exitf("dwarf: missing type: %s", n) } - - return s -} - -// dwarfFuncSym looks up a DWARF metadata symbol for function symbol s. -// If the symbol does not exist, it creates it if create is true, -// or returns nil otherwise. -func dwarfFuncSym(ctxt *Link, s *sym.Symbol, meta string, create bool) *sym.Symbol { - // All function ABIs use symbol version 0 for the DWARF data. - // - // TODO(austin): It may be useful to have DWARF info for ABI - // wrappers, in which case we may want these versions to - // align. Better yet, replace these name lookups with a - // general way to attach metadata to a symbol. - ver := 0 - if s.IsFileLocal() { - ver = int(s.Version) - } - if create { - return ctxt.Syms.Lookup(meta+s.Name, ver) + if len(d.ldr.Data(symIdx)) == 0 { + Exitf("dwarf: missing type (no data): %s", n) } - return ctxt.Syms.ROLookup(meta+s.Name, ver) + + return symIdx } -func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie { +func (d *dwctxt2) dotypedef(parent *dwarf.DWDie, gotype loader.Sym, name string, def *dwarf.DWDie) *dwarf.DWDie { // Only emit typedefs for real names. if strings.HasPrefix(name, "map[") { return nil @@ -382,53 +446,66 @@ func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) * Errorf(nil, "dwarf: bad def in dotypedef") } - s := ctxt.Syms.Lookup(dtolsym(def.Sym).Name+"..def", 0) - s.Attr |= sym.AttrNotInSymbolTable - s.Type = sym.SDWARFINFO - def.Sym = s + // Create a new loader symbol for the typedef. We no longer + // do lookups of typedef symbols by name, so this is going + // to be an anonymous symbol (we want this for perf reasons). + tds := d.ldr.CreateExtSym("", 0) + tdsu := d.ldr.MakeSymbolUpdater(tds) + tdsu.SetType(sym.SDWARFINFO) + def.Sym = dwSym(tds) + d.ldr.SetAttrNotInSymbolTable(tds, true) + d.ldr.SetAttrReachable(tds, true) // The typedef entry must be created after the def, // so that future lookups will find the typedef instead // of the real definition. This hooks the typedef into any // circular definition loops, so that gdb can understand them. - die := newdie(ctxt, parent, dwarf.DW_ABRV_TYPEDECL, name, 0) + die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name, 0) - newrefattr(die, dwarf.DW_AT_type, s) + d.newrefattr(die, dwarf.DW_AT_type, tds) return die } // Define gotype, for composite ones recurse into constituents. -func defgotype(ctxt *Link, gotype *sym.Symbol) *sym.Symbol { - if gotype == nil { - return mustFind(ctxt, "<unspecified>") +func (d *dwctxt2) defgotype(gotype loader.Sym) loader.Sym { + if gotype == 0 { + return d.mustFind("<unspecified>") } - if !strings.HasPrefix(gotype.Name, "type.") { - Errorf(gotype, "dwarf: type name doesn't start with \"type.\"") - return mustFind(ctxt, "<unspecified>") + // If we already have a tdmap entry for the gotype, return it. + if ds, ok := d.tdmap[gotype]; ok { + return ds } - name := gotype.Name[5:] // could also decode from Type.string - - sdie := find(ctxt, name) + sn := d.ldr.SymName(gotype) + if !strings.HasPrefix(sn, "type.") { + d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type.\"") + return d.mustFind("<unspecified>") + } + name := sn[5:] // could also decode from Type.string - if sdie != nil { + sdie := d.find(name) + if sdie != 0 { return sdie } - return newtype(ctxt, gotype).Sym.(*sym.Symbol) + gtdwSym := d.newtype(gotype) + d.tdmap[gotype] = loader.Sym(gtdwSym.Sym.(dwSym)) + return loader.Sym(gtdwSym.Sym.(dwSym)) } -func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { - name := gotype.Name[5:] // could also decode from Type.string - kind := decodetypeKind(ctxt.Arch, gotype.P) - bytesize := decodetypeSize(ctxt.Arch, gotype.P) +func (d *dwctxt2) newtype(gotype loader.Sym) *dwarf.DWDie { + sn := d.ldr.SymName(gotype) + name := sn[5:] // could also decode from Type.string + tdata := d.ldr.Data(gotype) + kind := decodetypeKind(d.arch, tdata) + bytesize := decodetypeSize(d.arch, tdata) var die, typedefdie *dwarf.DWDie switch kind { case objabi.KindBool: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) @@ -437,7 +514,7 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { objabi.KindInt16, objabi.KindInt32, objabi.KindInt64: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) @@ -447,118 +524,125 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { objabi.KindUint32, objabi.KindUint64, objabi.KindUintptr: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindFloat32, objabi.KindFloat64: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindComplex64, objabi.KindComplex128: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindArray: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) - s := decodetypeArrayElem(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) - fld := newdie(ctxt, die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0) + s := decodetypeArrayElem2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) + fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0) // use actual length not upper bound; correct for 0-length arrays. - newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(ctxt.Arch, gotype), 0) + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen2(d.ldr, d.arch, gotype), 0) - newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) case objabi.KindChan: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0) - s := decodetypeChanElem(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s)) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0) + s := decodetypeChanElem2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) // Save elem type for synthesizechantypes. We could synthesize here // but that would change the order of DIEs we output. - newrefattr(die, dwarf.DW_AT_type, s) + d.newrefattr(die, dwarf.DW_AT_type, s) case objabi.KindFunc: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) - nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + data := d.ldr.Data(gotype) + // FIXME: add caching or reuse reloc slice. + relocs := d.ldr.Relocs(gotype) + nfields := decodetypeFuncInCount(d.arch, data) for i := 0; i < nfields; i++ { - s := decodetypeFuncInType(ctxt.Arch, gotype, i) - fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) - newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s)) + s := decodetypeFuncInType2(d.ldr, d.arch, gotype, &relocs, i) + sn := d.ldr.SymName(s) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) } - if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) { - newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0) + if decodetypeFuncDotdotdot(d.arch, data) { + d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0) } - nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P) + nfields = decodetypeFuncOutCount(d.arch, data) for i := 0; i < nfields; i++ { - s := decodetypeFuncOutType(ctxt.Arch, gotype, i) - fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) - newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, defgotype(ctxt, s))) + s := decodetypeFuncOutType2(d.ldr, d.arch, gotype, &relocs, i) + sn := d.ldr.SymName(s) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s))) } case objabi.KindInterface: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) - nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P)) - var s *sym.Symbol + die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + data := d.ldr.Data(gotype) + nfields := int(decodetypeIfaceMethodCount(d.arch, data)) + var s loader.Sym if nfields == 0 { - s = lookupOrDiag(ctxt, "type.runtime.eface") + s = d.typeRuntimeEface } else { - s = lookupOrDiag(ctxt, "type.runtime.iface") + s = d.typeRuntimeIface } - newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) case objabi.KindMap: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0) - s := decodetypeMapKey(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_go_key, defgotype(ctxt, s)) - s = decodetypeMapValue(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s)) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0) + s := decodetypeMapKey2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s)) + s = decodetypeMapValue2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) // Save gotype for use in synthesizemaptypes. We could synthesize here, // but that would change the order of the DIEs. - newrefattr(die, dwarf.DW_AT_type, gotype) + d.newrefattr(die, dwarf.DW_AT_type, gotype) case objabi.KindPtr: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) - s := decodetypePtrElem(ctxt.Arch, gotype) - newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + s := decodetypePtrElem2(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) case objabi.KindSlice: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) - s := decodetypeArrayElem(ctxt.Arch, gotype) - elem := defgotype(ctxt, s) - newrefattr(die, dwarf.DW_AT_go_elem, elem) + s := decodetypeArrayElem2(d.ldr, d.arch, gotype) + elem := d.defgotype(s) + d.newrefattr(die, dwarf.DW_AT_go_elem, elem) case objabi.KindString: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindStruct: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0) - typedefdie = dotypedef(ctxt, &dwtypes, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) - nfields := decodetypeStructFieldCount(ctxt.Arch, gotype) + nfields := decodetypeStructFieldCount2(d.ldr, d.arch, gotype) for i := 0; i < nfields; i++ { - f := decodetypeStructFieldName(ctxt.Arch, gotype, i) - s := decodetypeStructFieldType(ctxt.Arch, gotype, i) + f := decodetypeStructFieldName2(d.ldr, d.arch, gotype, i) + s := decodetypeStructFieldType2(d.ldr, d.arch, gotype, i) if f == "" { - f = s.Name[5:] // skip "type." + sn := d.ldr.SymName(s) + f = sn[5:] // skip "type." } - fld := newdie(ctxt, die, dwarf.DW_ABRV_STRUCTFIELD, f, 0) - newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s)) - offsetAnon := decodetypeStructFieldOffsAnon(ctxt.Arch, gotype, i) + fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f, 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) + offsetAnon := decodetypeStructFieldOffsAnon2(d.ldr, d.arch, gotype, i) newmemberoffsetattr(fld, int32(offsetAnon>>1)) if offsetAnon&1 != 0 { // is embedded field newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) @@ -566,21 +650,33 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { } case objabi.KindUnsafePointer: - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0) default: - Errorf(gotype, "dwarf: definition of unknown kind %d", kind) - die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0) - newrefattr(die, dwarf.DW_AT_type, mustFind(ctxt, "<unspecified>")) + d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0) + d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("<unspecified>")) } newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0) - if gotype.Attr.Reachable() { - newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype) + + if d.ldr.AttrReachable(gotype) { + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gotype)) } - if _, ok := prototypedies[gotype.Name]; ok { - prototypedies[gotype.Name] = die + // Sanity check. + if _, ok := d.rtmap[gotype]; ok { + log.Fatalf("internal error: rtmap entry already installed\n") + } + + ds := loader.Sym(die.Sym.(dwSym)) + if typedefdie != nil { + ds = loader.Sym(typedefdie.Sym.(dwSym)) + } + d.rtmap[ds] = gotype + + if _, ok := prototypedies[sn]; ok { + prototypedies[sn] = die } if typedefdie != nil { @@ -589,55 +685,67 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { return die } -func nameFromDIESym(dwtype *sym.Symbol) string { - return strings.TrimSuffix(dwtype.Name[len(dwarf.InfoPrefix):], "..def") +func (d *dwctxt2) nameFromDIESym(dwtypeDIESym loader.Sym) string { + sn := d.ldr.SymName(dwtypeDIESym) + return sn[len(dwarf.InfoPrefix):] } -// Find or construct *T given T. -func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol { - ptrname := "*" + nameFromDIESym(dwtype) - if die := find(ctxt, ptrname); die != nil { +func (d *dwctxt2) defptrto(dwtype loader.Sym) loader.Sym { + + // FIXME: it would be nice if the compiler attached an aux symbol + // ref from the element type to the pointer type -- it would be + // more efficient to do it this way as opposed to via name lookups. + + ptrname := "*" + d.nameFromDIESym(dwtype) + if die := d.find(ptrname); die != 0 { return die } - pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0) - newrefattr(pdie, dwarf.DW_AT_type, dwtype) + pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0) + d.newrefattr(pdie, dwarf.DW_AT_type, dwtype) // The DWARF info synthesizes pointer types that don't exist at the // language level, like *hash<...> and *bucket<...>, and the data // pointers of slices. Link to the ones we can find. - gotype := ctxt.Syms.ROLookup("type."+ptrname, 0) - if gotype != nil && gotype.Attr.Reachable() { - newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype) + gts := d.ldr.Lookup("type."+ptrname, 0) + if gts != 0 && d.ldr.AttrReachable(gts) { + newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts)) + } + + if gts != 0 { + ds := loader.Sym(pdie.Sym.(dwSym)) + d.rtmap[ds] = gts + d.tdmap[gts] = ds } - return dtolsym(pdie.Sym) + + return d.dtolsym(pdie.Sym) } // Copies src's children into dst. Copies attributes by value. // DWAttr.data is copied as pointer only. If except is one of // the top-level children, it will not be copied. -func copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) { +func (d *dwctxt2) copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) { for src = src.Child; src != nil; src = src.Link { if src == except { continue } - c := newdie(ctxt, dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0) + c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0) for a := src.Attr; a != nil; a = a.Link { newattr(c, a.Atr, int(a.Cls), a.Value, a.Data) } - copychildrenexcept(ctxt, c, src, nil) + d.copychildrenexcept(ctxt, c, src, nil) } reverselist(&dst.Child) } -func copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) { - copychildrenexcept(ctxt, dst, src, nil) +func (d *dwctxt2) copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) { + d.copychildrenexcept(ctxt, dst, src, nil) } // Search children (assumed to have TAG_member) for the one named // field and set its AT_type to dwtype -func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) { +func (d *dwctxt2) substitutetype(structdie *dwarf.DWDie, field string, dwtype loader.Sym) { child := findchild(structdie, field) if child == nil { Exitf("dwarf substitutetype: %s does not have member %s", @@ -647,23 +755,26 @@ func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) { a := getattr(child, dwarf.DW_AT_type) if a != nil { - a.Data = dwtype + a.Data = dwSym(dwtype) } else { - newrefattr(child, dwarf.DW_AT_type, dwtype) + d.newrefattr(child, dwarf.DW_AT_type, dwtype) } } -func findprotodie(ctxt *Link, name string) *dwarf.DWDie { +func (d *dwctxt2) findprotodie(ctxt *Link, name string) *dwarf.DWDie { die, ok := prototypedies[name] if ok && die == nil { - defgotype(ctxt, lookupOrDiag(ctxt, name)) + d.defgotype(d.lookupOrDiag(name)) die = prototypedies[name] } + if die == nil { + log.Fatalf("internal error: DIE generation failed for %s\n", name) + } return die } -func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { - prototype := walktypedef(findprotodie(ctxt, "type.runtime.stringStructDWARF")) +func (d *dwctxt2) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.stringStructDWARF")) if prototype == nil { return } @@ -672,12 +783,12 @@ func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE { continue } - copychildren(ctxt, die, prototype) + d.copychildren(ctxt, die, prototype) } } -func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { - prototype := walktypedef(findprotodie(ctxt, "type.runtime.slice")) +func (d *dwctxt2) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.slice")) if prototype == nil { return } @@ -686,9 +797,9 @@ func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { if die.Abbrev != dwarf.DW_ABRV_SLICETYPE { continue } - copychildren(ctxt, die, prototype) - elem := getattr(die, dwarf.DW_AT_go_elem).Data.(*sym.Symbol) - substitutetype(die, "array", defptrto(ctxt, elem)) + d.copychildren(ctxt, die, prototype) + elem := loader.Sym(getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym)) + d.substitutetype(die, "array", d.defptrto(elem)) } } @@ -706,21 +817,21 @@ const ( BucketSize = 8 ) -func mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) *sym.Symbol { +func (d *dwctxt2) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) loader.Sym { name := mkinternaltypename(typename, keyname, valname) symname := dwarf.InfoPrefix + name - s := ctxt.Syms.ROLookup(symname, 0) - if s != nil && s.Type == sym.SDWARFINFO { + s := d.ldr.Lookup(symname, 0) + if s != 0 && d.ldr.SymType(s) == sym.SDWARFINFO { return s } - die := newdie(ctxt, &dwtypes, abbrev, name, 0) + die := d.newdie(&dwtypes, abbrev, name, 0) f(die) - return dtolsym(die.Sym) + return d.dtolsym(die.Sym) } -func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { - hash := walktypedef(findprotodie(ctxt, "type.runtime.hmap")) - bucket := walktypedef(findprotodie(ctxt, "type.runtime.bmap")) +func (d *dwctxt2) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { + hash := walktypedef(d.findprotodie(ctxt, "type.runtime.hmap")) + bucket := walktypedef(d.findprotodie(ctxt, "type.runtime.bmap")) if hash == nil { return @@ -730,92 +841,94 @@ func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { if die.Abbrev != dwarf.DW_ABRV_MAPTYPE { continue } - gotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol) - keytype := decodetypeMapKey(ctxt.Arch, gotype) - valtype := decodetypeMapValue(ctxt.Arch, gotype) - keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P) - keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype)) + gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym)) + keytype := decodetypeMapKey2(d.ldr, d.arch, gotype) + valtype := decodetypeMapValue2(d.ldr, d.arch, gotype) + keydata := d.ldr.Data(keytype) + valdata := d.ldr.Data(valtype) + keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata) + keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype)) // compute size info like hashmap.c does. indirectKey, indirectVal := false, false if keysize > MaxKeySize { - keysize = int64(ctxt.Arch.PtrSize) + keysize = int64(d.arch.PtrSize) indirectKey = true } if valsize > MaxValSize { - valsize = int64(ctxt.Arch.PtrSize) + valsize = int64(d.arch.PtrSize) indirectVal = true } // Construct type to represent an array of BucketSize keys - keyname := nameFromDIESym(keytype) - dwhks := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) { + keyname := d.nameFromDIESym(keytype) + dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) { newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0) t := keytype if indirectKey { - t = defptrto(ctxt, keytype) + t = d.defptrto(keytype) } - newrefattr(dwhk, dwarf.DW_AT_type, t) - fld := newdie(ctxt, dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + d.newrefattr(dwhk, dwarf.DW_AT_type, t) + fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) - newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) }) // Construct type to represent an array of BucketSize values - valname := nameFromDIESym(valtype) - dwhvs := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) { + valname := d.nameFromDIESym(valtype) + dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) { newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0) t := valtype if indirectVal { - t = defptrto(ctxt, valtype) + t = d.defptrto(valtype) } - newrefattr(dwhv, dwarf.DW_AT_type, t) - fld := newdie(ctxt, dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + d.newrefattr(dwhv, dwarf.DW_AT_type, t) + fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) - newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) }) // Construct bucket<K,V> - dwhbs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) { + dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) { // Copy over all fields except the field "data" from the generic // bucket. "data" will be replaced with keys/values below. - copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) + d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) - fld := newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0) - newrefattr(fld, dwarf.DW_AT_type, dwhks) + fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0) + d.newrefattr(fld, dwarf.DW_AT_type, dwhks) newmemberoffsetattr(fld, BucketSize) - fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0) - newrefattr(fld, dwarf.DW_AT_type, dwhvs) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0) + d.newrefattr(fld, dwarf.DW_AT_type, dwhvs) newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) - fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0) - newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, dtolsym(dwhb.Sym))) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym))) newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) - if ctxt.Arch.RegSize > ctxt.Arch.PtrSize { - fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0) - newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) - newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(ctxt.Arch.PtrSize)) + if d.arch.RegSize > d.arch.PtrSize { + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize)) } - newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(ctxt.Arch.RegSize), 0) + newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(d.arch.RegSize), 0) }) // Construct hash<K,V> - dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) { - copychildren(ctxt, dwh, hash) - substitutetype(dwh, "buckets", defptrto(ctxt, dwhbs)) - substitutetype(dwh, "oldbuckets", defptrto(ctxt, dwhbs)) + dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) { + d.copychildren(ctxt, dwh, hash) + d.substitutetype(dwh, "buckets", d.defptrto(dwhbs)) + d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs)) newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil) }) // make map type a pointer to hash<K,V> - newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs)) + d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) } } -func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { - sudog := walktypedef(findprotodie(ctxt, "type.runtime.sudog")) - waitq := walktypedef(findprotodie(ctxt, "type.runtime.waitq")) - hchan := walktypedef(findprotodie(ctxt, "type.runtime.hchan")) +func (d *dwctxt2) synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { + sudog := walktypedef(d.findprotodie(ctxt, "type.runtime.sudog")) + waitq := walktypedef(d.findprotodie(ctxt, "type.runtime.waitq")) + hchan := walktypedef(d.findprotodie(ctxt, "type.runtime.hchan")) if sudog == nil || waitq == nil || hchan == nil { return } @@ -826,130 +939,103 @@ func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { if die.Abbrev != dwarf.DW_ABRV_CHANTYPE { continue } - elemgotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol) - elemname := elemgotype.Name[5:] - elemtype := walksymtypedef(ctxt, defgotype(ctxt, elemgotype)) + elemgotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym)) + tname := d.ldr.SymName(elemgotype) + elemname := tname[5:] + elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname))) // sudog<T> - dwss := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) { - copychildren(ctxt, dws, sudog) - substitutetype(dws, "elem", defptrto(ctxt, elemtype)) + dwss := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) { + d.copychildren(ctxt, dws, sudog) + d.substitutetype(dws, "elem", d.defptrto(elemtype)) newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil) }) // waitq<T> - dwws := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { + dwws := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { - copychildren(ctxt, dww, waitq) - substitutetype(dww, "first", defptrto(ctxt, dwss)) - substitutetype(dww, "last", defptrto(ctxt, dwss)) + d.copychildren(ctxt, dww, waitq) + d.substitutetype(dww, "first", d.defptrto(dwss)) + d.substitutetype(dww, "last", d.defptrto(dwss)) newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil) }) // hchan<T> - dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) { - copychildren(ctxt, dwh, hchan) - substitutetype(dwh, "recvq", dwws) - substitutetype(dwh, "sendq", dwws) + dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) { + d.copychildren(ctxt, dwh, hchan) + d.substitutetype(dwh, "recvq", dwws) + d.substitutetype(dwh, "sendq", dwws) newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil) }) - newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs)) + d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) } } -func dwarfDefineGlobal(ctxt *Link, s *sym.Symbol, str string, v int64, gotype *sym.Symbol) { +func (d *dwctxt2) dwarfDefineGlobal(ctxt *Link, symIdx loader.Sym, str string, v int64, gotype loader.Sym) { // Find a suitable CU DIE to include the global. // One would think it's as simple as just looking at the unit, but that might // not have any reachable code. So, we go to the runtime's CU if our unit // isn't otherwise reachable. - var unit *sym.CompilationUnit - if s.Unit != nil { - unit = s.Unit - } else { + unit := d.ldr.SymUnit(symIdx) + if unit == nil { unit = ctxt.runtimeCU } - dv := newdie(ctxt, unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(s.Version)) - newabslocexprattr(dv, v, s) - if !s.IsFileLocal() { + ver := d.ldr.SymVersion(symIdx) + dv := d.newdie(unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(ver)) + d.newabslocexprattr(dv, v, symIdx) + if d.ldr.SymVersion(symIdx) < sym.SymVerStatic { newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0) } - dt := defgotype(ctxt, gotype) - newrefattr(dv, dwarf.DW_AT_type, dt) -} - -// For use with pass.c::genasmsym -func defdwsymb(ctxt *Link, s *sym.Symbol, str string, t SymbolType, v int64, gotype *sym.Symbol) { - if strings.HasPrefix(str, "go.string.") { - return - } - if strings.HasPrefix(str, "runtime.gcbits.") { - return - } - - switch t { - case DataSym, BSSSym: - switch s.Type { - case sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS: - // ok - case sym.SRODATA: - if gotype != nil { - defgotype(ctxt, gotype) - } - return - default: - return - } - if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) { - return - } - dwarfDefineGlobal(ctxt, s, str, v, gotype) - - case AutoSym, ParamSym, DeletedAutoSym: - defgotype(ctxt, gotype) - } + dt := d.defgotype(gotype) + d.newrefattr(dv, dwarf.DW_AT_type, dt) } // createUnitLength creates the initial length field with value v and update // offset of unit_length if needed. -func createUnitLength(ctxt *Link, s *sym.Symbol, v uint64) { - if isDwarf64(ctxt) { - s.AddUint32(ctxt.Arch, 0xFFFFFFFF) +func (d *dwctxt2) createUnitLength(su *loader.SymbolBuilder, v uint64) { + if isDwarf64(d.linkctxt) { + su.AddUint32(d.arch, 0xFFFFFFFF) } - addDwarfAddrField(ctxt, s, v) + d.addDwarfAddrField(su, v) } // addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits. -func addDwarfAddrField(ctxt *Link, s *sym.Symbol, v uint64) { - if isDwarf64(ctxt) { - s.AddUint(ctxt.Arch, v) +func (d *dwctxt2) addDwarfAddrField(sb *loader.SymbolBuilder, v uint64) { + if isDwarf64(d.linkctxt) { + sb.AddUint(d.arch, v) } else { - s.AddUint32(ctxt.Arch, uint32(v)) + sb.AddUint32(d.arch, uint32(v)) } } // addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits. -func addDwarfAddrRef(ctxt *Link, s *sym.Symbol, t *sym.Symbol) { - if isDwarf64(ctxt) { - adddwarfref(ctxt, s, t, 8) +func (d *dwctxt2) addDwarfAddrRef(sb *loader.SymbolBuilder, t loader.Sym) { + if isDwarf64(d.linkctxt) { + d.adddwarfref(sb, t, 8) } else { - adddwarfref(ctxt, s, t, 4) + d.adddwarfref(sb, t, 4) } } // calcCompUnitRanges calculates the PC ranges of the compilation units. -func calcCompUnitRanges(ctxt *Link) { +func (d *dwctxt2) calcCompUnitRanges() { var prevUnit *sym.CompilationUnit - for _, s := range ctxt.Textp { - if s.FuncInfo == nil { + for _, s := range d.linkctxt.Textp2 { + sym := loader.Sym(s) + + fi := d.ldr.FuncInfo(sym) + if !fi.Valid() { continue } + // Skip linker-created functions (ex: runtime.addmoduledata), since they // don't have DWARF to begin with. - if s.Unit == nil { + unit := d.ldr.SymUnit(sym) + if unit == nil { continue } - unit := s.Unit + // Update PC ranges. // // We don't simply compare the end of the previous @@ -957,11 +1043,13 @@ func calcCompUnitRanges(ctxt *Link) { // often a little padding between them. Instead, we // only create boundaries between symbols from // different units. + sval := d.ldr.SymValue(sym) + u0val := d.ldr.SymValue(loader.Sym(unit.Textp2[0])) if prevUnit != unit { - unit.PCs = append(unit.PCs, dwarf.Range{Start: s.Value - unit.Textp[0].Value}) + unit.PCs = append(unit.PCs, dwarf.Range{Start: sval - u0val}) prevUnit = unit } - unit.PCs[len(unit.PCs)-1].End = s.Value - unit.Textp[0].Value + s.Size + unit.PCs[len(unit.PCs)-1].End = sval - u0val + int64(len(d.ldr.Data(sym))) } } @@ -977,24 +1065,6 @@ func movetomodule(ctxt *Link, parent *dwarf.DWDie) { die.Link = parent.Child } -// If the pcln table contains runtime/proc.go, use that to set gdbscript path. -func finddebugruntimepath(s *sym.Symbol) { - if gdbscript != "" { - return - } - - for i := range s.FuncInfo.File { - f := s.FuncInfo.File[i] - // We can't use something that may be dead-code - // eliminated from a binary here. proc.go contains - // main and the scheduler, so it's not going anywhere. - if i := strings.Index(f.Name, "runtime/proc.go"); i >= 0 { - gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" - break - } - } -} - /* * Generate a sequence of opcodes that is as short as possible. * See section 6.2.5 @@ -1020,115 +1090,130 @@ func getCompilationDir() string { return "." } -func importInfoSymbol(ctxt *Link, dsym *sym.Symbol) { - dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable - dsym.Type = sym.SDWARFINFO - for i := range dsym.R { - r := &dsym.R[i] // Copying sym.Reloc has measurable impact on performance - if r.Type == objabi.R_DWARFSECREF && r.Sym.Size == 0 { - n := nameFromDIESym(r.Sym) - defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0)) +func (d *dwctxt2) importInfoSymbol(ctxt *Link, dsym loader.Sym) { + d.ldr.SetAttrReachable(dsym, true) + d.ldr.SetAttrNotInSymbolTable(dsym, true) + if d.ldr.SymType(dsym) != sym.SDWARFINFO { + log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String()) + } + relocs := d.ldr.Relocs(dsym) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At2(i) + if r.Type() != objabi.R_DWARFSECREF { + continue + } + rsym := r.Sym() + // If there is an entry for the symbol in our rtmap, then it + // means we've processed the type already, and can skip this one. + if _, ok := d.rtmap[rsym]; ok { + // type already generated + continue } + // FIXME: is there a way we could avoid materializing the + // symbol name here? + sn := d.ldr.SymName(rsym) + tn := sn[len(dwarf.InfoPrefix):] + ts := d.ldr.Lookup("type."+tn, 0) + d.defgotype(ts) } } -func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) { +func expandFile(fname string) string { + if strings.HasPrefix(fname, src.FileSymPrefix) { + fname = fname[len(src.FileSymPrefix):] + } + return expandGoroot(fname) +} + +func expandFileSym(l *loader.Loader, fsym loader.Sym) string { + return expandFile(l.SymName(fsym)) +} + +func (d *dwctxt2) writelines(unit *sym.CompilationUnit, ls loader.Sym) { - var dwarfctxt dwarf.Context = dwctxt{ctxt} is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles. unitstart := int64(-1) headerstart := int64(-1) headerend := int64(-1) - newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, ls.Size, ls) + lsu := d.ldr.MakeSymbolUpdater(ls) + newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, lsu.Size(), dwSym(ls)) // Write .debug_line Line Number Program Header (sec 6.2.4) // Fields marked with (*) must be changed for 64-bit dwarf - unitLengthOffset := ls.Size - createUnitLength(ctxt, ls, 0) // unit_length (*), filled in at end - unitstart = ls.Size - ls.AddUint16(ctxt.Arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05 - headerLengthOffset := ls.Size - addDwarfAddrField(ctxt, ls, 0) // header_length (*), filled in at end - headerstart = ls.Size + unitLengthOffset := lsu.Size() + d.createUnitLength(lsu, 0) // unit_length (*), filled in at end + + unitstart = lsu.Size() + lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05 + headerLengthOffset := lsu.Size() + d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end + headerstart = lsu.Size() // cpos == unitstart + 4 + 2 + 4 - ls.AddUint8(1) // minimum_instruction_length - ls.AddUint8(is_stmt) // default_is_stmt - ls.AddUint8(LINE_BASE & 0xFF) // line_base - ls.AddUint8(LINE_RANGE) // line_range - ls.AddUint8(OPCODE_BASE) // opcode_base - ls.AddUint8(0) // standard_opcode_lengths[1] - ls.AddUint8(1) // standard_opcode_lengths[2] - ls.AddUint8(1) // standard_opcode_lengths[3] - ls.AddUint8(1) // standard_opcode_lengths[4] - ls.AddUint8(1) // standard_opcode_lengths[5] - ls.AddUint8(0) // standard_opcode_lengths[6] - ls.AddUint8(0) // standard_opcode_lengths[7] - ls.AddUint8(0) // standard_opcode_lengths[8] - ls.AddUint8(1) // standard_opcode_lengths[9] - ls.AddUint8(0) // standard_opcode_lengths[10] - ls.AddUint8(0) // include_directories (empty) + lsu.AddUint8(1) // minimum_instruction_length + lsu.AddUint8(is_stmt) // default_is_stmt + lsu.AddUint8(LINE_BASE & 0xFF) // line_base + lsu.AddUint8(LINE_RANGE) // line_range + lsu.AddUint8(OPCODE_BASE) // opcode_base + lsu.AddUint8(0) // standard_opcode_lengths[1] + lsu.AddUint8(1) // standard_opcode_lengths[2] + lsu.AddUint8(1) // standard_opcode_lengths[3] + lsu.AddUint8(1) // standard_opcode_lengths[4] + lsu.AddUint8(1) // standard_opcode_lengths[5] + lsu.AddUint8(0) // standard_opcode_lengths[6] + lsu.AddUint8(0) // standard_opcode_lengths[7] + lsu.AddUint8(0) // standard_opcode_lengths[8] + lsu.AddUint8(1) // standard_opcode_lengths[9] + lsu.AddUint8(0) // standard_opcode_lengths[10] + lsu.AddUint8(0) // include_directories (empty) // Copy over the file table. fileNums := make(map[string]int) + lsDwsym := dwSym(ls) for i, name := range unit.DWARFFileTable { - if len(name) != 0 { - if strings.HasPrefix(name, src.FileSymPrefix) { - name = name[len(src.FileSymPrefix):] - } - name = expandGoroot(name) - } else { - // Can't have empty filenames, and having a unique filename is quite useful - // for debugging. + name := expandFile(name) + if len(name) == 0 { + // Can't have empty filenames, and having a unique + // filename is quite useful for debugging. name = fmt.Sprintf("<missing>_%d", i) } fileNums[name] = i + 1 - dwarfctxt.AddString(ls, name) - ls.AddUint8(0) - ls.AddUint8(0) - ls.AddUint8(0) - } - // Grab files for inlined functions. - // TODO: With difficulty, this could be moved into the compiler. - for _, s := range unit.Textp { - dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true) - for ri := 0; ri < len(dsym.R); ri++ { - r := &dsym.R[ri] - if r.Type != objabi.R_DWARFFILEREF { - continue + d.AddString(lsDwsym, name) + lsu.AddUint8(0) + lsu.AddUint8(0) + lsu.AddUint8(0) + if gdbscript == "" { + // We can't use something that may be dead-code + // eliminated from a binary here. proc.go contains + // main and the scheduler, so it's not going anywhere. + if i := strings.Index(name, "runtime/proc.go"); i >= 0 { + k := strings.Index(name, "runtime/proc.go") + gdbscript = name[:k] + "runtime/runtime-gdb.py" } - name := r.Sym.Name - if _, ok := fileNums[name]; ok { - continue - } - fileNums[name] = len(fileNums) + 1 - dwarfctxt.AddString(ls, name) - ls.AddUint8(0) - ls.AddUint8(0) - ls.AddUint8(0) } } // 4 zeros: the string termination + 3 fields. - ls.AddUint8(0) + lsu.AddUint8(0) // terminate file_names. - headerend = ls.Size + headerend = lsu.Size() // Output the state machine for each function remaining. var lastAddr int64 - for _, s := range unit.Textp { - finddebugruntimepath(s) + for _, s := range unit.Textp2 { + fnSym := loader.Sym(s) // Set the PC. - ls.AddUint8(0) - dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize)) - ls.AddUint8(dwarf.DW_LNE_set_address) - addr := ls.AddAddr(ctxt.Arch, s) + lsu.AddUint8(0) + dwarf.Uleb128put(d, lsDwsym, 1+int64(d.arch.PtrSize)) + lsu.AddUint8(dwarf.DW_LNE_set_address) + addr := lsu.AddAddrPlus(d.arch, fnSym, 0) // Make sure the units are sorted. if addr < lastAddr { - Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr) + d.linkctxt.Errorf(fnSym, "address wasn't increasing %x < %x", + addr, lastAddr) } lastAddr = addr @@ -1138,84 +1223,44 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) { // together rather then the append() below. This would allow us to have // the compiler emit the DW_LNE_set_address and a rope data structure // to concat them all together in the output. - lines := dwarfFuncSym(ctxt, s, dwarf.DebugLinesPrefix, false) - if lines != nil { - ls.P = append(ls.P, lines.P...) + _, _, _, lines := d.ldr.GetFuncDwarfAuxSyms(fnSym) + if lines != 0 { + lsu.AddBytes(d.ldr.Data(lines)) } } - ls.AddUint8(0) // start extended opcode - dwarf.Uleb128put(dwarfctxt, ls, 1) - ls.AddUint8(dwarf.DW_LNE_end_sequence) + lsu.AddUint8(0) // start extended opcode + dwarf.Uleb128put(d, lsDwsym, 1) + lsu.AddUint8(dwarf.DW_LNE_end_sequence) - if ctxt.HeadType == objabi.Haix { - saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(ls.Size-unitLengthOffset)) + if d.linkctxt.HeadType == objabi.Haix { + saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(lsu.Size()-unitLengthOffset)) } - if isDwarf64(ctxt) { - ls.SetUint(ctxt.Arch, unitLengthOffset+4, uint64(ls.Size-unitstart)) // +4 because of 0xFFFFFFFF - ls.SetUint(ctxt.Arch, headerLengthOffset, uint64(headerend-headerstart)) + if isDwarf64(d.linkctxt) { + lsu.SetUint(d.arch, unitLengthOffset+4, uint64(lsu.Size()-unitstart)) // +4 because of 0xFFFFFFFF + lsu.SetUint(d.arch, headerLengthOffset, uint64(headerend-headerstart)) } else { - ls.SetUint32(ctxt.Arch, unitLengthOffset, uint32(ls.Size-unitstart)) - ls.SetUint32(ctxt.Arch, headerLengthOffset, uint32(headerend-headerstart)) - } - - // Process any R_DWARFFILEREF relocations, since we now know the - // line table file indices for this compilation unit. Note that - // this loop visits only subprogram DIEs: if the compiler is - // changed to generate DW_AT_decl_file attributes for other - // DIE flavors (ex: variables) then those DIEs would need to - // be included below. - missing := make(map[int]interface{}) - s := unit.Textp[0] - for _, f := range unit.FuncDIEs { - for ri := range f.R { - r := &f.R[ri] - if r.Type != objabi.R_DWARFFILEREF { - continue - } - idx, ok := fileNums[r.Sym.Name] - if ok { - if int(int32(idx)) != idx { - Errorf(f, "bad R_DWARFFILEREF relocation: file index overflow") - } - if r.Siz != 4 { - Errorf(f, "bad R_DWARFFILEREF relocation: has size %d, expected 4", r.Siz) - } - if r.Off < 0 || r.Off+4 > int32(len(f.P)) { - Errorf(f, "bad R_DWARFFILEREF relocation offset %d + 4 would write past length %d", r.Off, len(s.P)) - continue - } - if r.Add != 0 { - Errorf(f, "bad R_DWARFFILEREF relocation: addend not zero") - } - r.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable - r.Add = int64(idx) // record the index in r.Add, we'll apply it in the reloc phase. - } else { - _, found := missing[int(r.Sym.Value)] - if !found { - Errorf(f, "R_DWARFFILEREF relocation file missing: %v idx %d", r.Sym, r.Sym.Value) - missing[int(r.Sym.Value)] = nil - } - } - } + lsu.SetUint32(d.arch, unitLengthOffset, uint32(lsu.Size()-unitstart)) + lsu.SetUint32(d.arch, headerLengthOffset, uint32(headerend-headerstart)) } } // writepcranges generates the DW_AT_ranges table for compilation unit cu. -func writepcranges(ctxt *Link, unit *sym.CompilationUnit, base *sym.Symbol, pcs []dwarf.Range, ranges *sym.Symbol) { - var dwarfctxt dwarf.Context = dwctxt{ctxt} +func (d *dwctxt2) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, ranges loader.Sym) { + + rsu := d.ldr.MakeSymbolUpdater(ranges) + rDwSym := dwSym(ranges) - unitLengthOffset := ranges.Size + unitLengthOffset := rsu.Size() // Create PC ranges for this CU. - newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, ranges.Size, ranges) - newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, base.Value, base) - dwarf.PutBasedRanges(dwarfctxt, ranges, pcs) + newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym) + newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base)) + dwarf.PutBasedRanges(d, rDwSym, pcs) - if ctxt.HeadType == objabi.Haix { - addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(ranges.Size-unitLengthOffset)) + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rsu.Size()-unitLengthOffset)) } - } /* @@ -1246,84 +1291,91 @@ func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte { return b } -func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { - var dwarfctxt dwarf.Context = dwctxt{ctxt} - fs := ctxt.Syms.Lookup(".debug_frame", 0) - fs.Type = sym.SDWARFSECT +func (d *dwctxt2) writeframes(syms []loader.Sym) []loader.Sym { + fs := d.ldr.LookupOrCreateSym(".debug_frame", 0) + fsd := dwSym(fs) + fsu := d.ldr.MakeSymbolUpdater(fs) + fsu.SetType(sym.SDWARFSECT) syms = append(syms, fs) + isdw64 := isDwarf64(d.linkctxt) + haslr := haslinkregister(d.linkctxt) // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64 lengthFieldSize := int64(4) - if isDwarf64(ctxt) { + if isdw64 { lengthFieldSize += 8 } // Emit the CIE, Section 6.4.1 cieReserve := uint32(16) - if haslinkregister(ctxt) { + if haslr { cieReserve = 32 } - if isDwarf64(ctxt) { + if isdw64 { cieReserve += 4 // 4 bytes added for cid } - createUnitLength(ctxt, fs, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize - addDwarfAddrField(ctxt, fs, ^uint64(0)) // cid - fs.AddUint8(3) // dwarf version (appendix F) - fs.AddUint8(0) // augmentation "" - dwarf.Uleb128put(dwarfctxt, fs, 1) // code_alignment_factor - dwarf.Sleb128put(dwarfctxt, fs, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // return_address_register - - fs.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address.. - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... - if haslinkregister(ctxt) { - dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...plus a 0 offset. - - fs.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) - - fs.AddUint8(dwarf.DW_CFA_val_offset) // The previous value... - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...of the platform's SP register... - dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...is CFA+0. + d.createUnitLength(fsu, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize + d.addDwarfAddrField(fsu, ^uint64(0)) // cid + fsu.AddUint8(3) // dwarf version (appendix F) + fsu.AddUint8(0) // augmentation "" + dwarf.Uleb128put(d, fsd, 1) // code_alignment_factor + dwarf.Sleb128put(d, fsd, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // return_address_register + + fsu.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address.. + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... + if haslr { + dwarf.Uleb128put(d, fsd, int64(0)) // ...plus a 0 offset. + + fsu.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) + + fsu.AddUint8(dwarf.DW_CFA_val_offset) // The previous value... + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...of the platform's SP register... + dwarf.Uleb128put(d, fsd, int64(0)) // ...is CFA+0. } else { - dwarf.Uleb128put(dwarfctxt, fs, int64(ctxt.Arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). + dwarf.Uleb128put(d, fsd, int64(d.arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). - fs.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value... - dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // ...of the return address... - dwarf.Uleb128put(dwarfctxt, fs, int64(-ctxt.Arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. + fsu.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value... + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // ...of the return address... + dwarf.Uleb128put(d, fsd, int64(-d.arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. } - pad := int64(cieReserve) + lengthFieldSize - fs.Size + pad := int64(cieReserve) + lengthFieldSize - int64(len(d.ldr.Data(fs))) if pad < 0 { Exitf("dwarf: cieReserve too small by %d bytes.", -pad) } - fs.AddBytes(zeros[:pad]) + fsu.AddBytes(zeros[:pad]) var deltaBuf []byte - pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) - for _, s := range ctxt.Textp { - if s.FuncInfo == nil { + pcsp := obj.NewPCIter(uint32(d.arch.MinLC)) + for _, s := range d.linkctxt.Textp2 { + fn := loader.Sym(s) + fi := d.ldr.FuncInfo(fn) + if !fi.Valid() { continue } + fpcsp := fi.Pcsp() // Emit a FDE, Section 6.4.1. // First build the section contents into a byte buffer. deltaBuf = deltaBuf[:0] - if haslinkregister(ctxt) && s.Attr.TopFrame() { + if haslr && d.ldr.AttrTopFrame(fn) { // Mark the link register as having an undefined value. // This stops call stack unwinders progressing any further. // TODO: similar mark on non-LR architectures. deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined) deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) } - for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() { + + for pcsp.Init(fpcsp); !pcsp.Done; pcsp.Next() { nextpc := pcsp.NextPC // pciterinit goes up to the end of the function, // but DWARF expects us to stop just before the end. - if int64(nextpc) == s.Size { + if int64(nextpc) == int64(len(d.ldr.Data(fn))) { nextpc-- if nextpc < pcsp.PC { continue @@ -1331,12 +1383,12 @@ func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { } spdelta := int64(pcsp.Value) - if !haslinkregister(ctxt) { + if !haslr { // Return address has been pushed onto stack. - spdelta += int64(ctxt.Arch.PtrSize) + spdelta += int64(d.arch.PtrSize) } - if haslinkregister(ctxt) && !s.Attr.TopFrame() { + if haslr && !d.ldr.AttrTopFrame(fn) { // TODO(bryanpkc): This is imprecise. In general, the instruction // that stores the return address to the stack frame is not the // same one that allocates the frame. @@ -1354,9 +1406,9 @@ func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { } } - deltaBuf = appendPCDeltaCFA(ctxt.Arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta) + deltaBuf = appendPCDeltaCFA(d.arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta) } - pad := int(Rnd(int64(len(deltaBuf)), int64(ctxt.Arch.PtrSize))) - len(deltaBuf) + pad := int(Rnd(int64(len(deltaBuf)), int64(d.arch.PtrSize))) - len(deltaBuf) deltaBuf = append(deltaBuf, zeros[:pad]...) // Emit the FDE header, Section 6.4.1. @@ -1365,48 +1417,61 @@ func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { // ptrsize: initial location // ptrsize: address range - fdeLength := uint64(4 + 2*ctxt.Arch.PtrSize + len(deltaBuf)) - if isDwarf64(ctxt) { + fdeLength := uint64(4 + 2*d.arch.PtrSize + len(deltaBuf)) + if isdw64 { fdeLength += 4 // 4 bytes added for CIE pointer } - createUnitLength(ctxt, fs, fdeLength) + d.createUnitLength(fsu, fdeLength) - if ctxt.LinkMode == LinkExternal { - addDwarfAddrRef(ctxt, fs, fs) + if d.linkctxt.LinkMode == LinkExternal { + d.addDwarfAddrRef(fsu, fs) } else { - addDwarfAddrField(ctxt, fs, 0) // CIE offset + d.addDwarfAddrField(fsu, 0) // CIE offset } - fs.AddAddr(ctxt.Arch, s) - fs.AddUintXX(ctxt.Arch, uint64(s.Size), ctxt.Arch.PtrSize) // address range - fs.AddBytes(deltaBuf) + fsu.AddAddrPlus(d.arch, s, 0) + fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range + fsu.AddBytes(deltaBuf) - if ctxt.HeadType == objabi.Haix { - addDwsectCUSize(".debug_frame", s.File, fdeLength+uint64(lengthFieldSize)) + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_frame", d.ldr.SymFile(fn), fdeLength+uint64(lengthFieldSize)) } } + return syms } /* * Walk DWarfDebugInfoEntries, and emit .debug_info */ + const ( COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 ) -func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abbrevsym *sym.Symbol, pubNames, pubTypes *pubWriter) []*sym.Symbol { - infosec := ctxt.Syms.Lookup(".debug_info", 0) - infosec.Type = sym.SDWARFINFO - infosec.Attr |= sym.AttrReachable - syms = append(syms, infosec) +// appendSyms appends the syms from 'src' into 'syms' and returns the +// result. This can go away once we do away with sym.LoaderSym +// entirely. +func appendSyms(syms []loader.Sym, src []sym.LoaderSym) []loader.Sym { + for _, s := range src { + syms = append(syms, loader.Sym(s)) + } + return syms +} - var dwarfctxt dwarf.Context = dwctxt{ctxt} +func (d *dwctxt2) writeinfo(syms []loader.Sym, units []*sym.CompilationUnit, abbrevsym loader.Sym, pubNames, pubTypes *pubWriter2) []loader.Sym { + + infosec := d.ldr.LookupOrCreateSym(".debug_info", 0) + disu := d.ldr.MakeSymbolUpdater(infosec) + disu.SetType(sym.SDWARFINFO) + d.ldr.SetAttrReachable(infosec, true) + syms = append(syms, infosec) for _, u := range units { compunit := u.DWInfo - s := dtolsym(compunit.Sym) + s := d.dtolsym(compunit.Sym) + su := d.ldr.MakeSymbolUpdater(s) - if len(u.Textp) == 0 && u.DWInfo.Child == nil { + if len(u.Textp2) == 0 && u.DWInfo.Child == nil { continue } @@ -1416,59 +1481,62 @@ func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abb // Write .debug_info Compilation Unit Header (sec 7.5.1) // Fields marked with (*) must be changed for 64-bit dwarf // This must match COMPUNITHEADERSIZE above. - createUnitLength(ctxt, s, 0) // unit_length (*), will be filled in later. - s.AddUint16(ctxt.Arch, 4) // dwarf version (appendix F) + d.createUnitLength(su, 0) // unit_length (*), will be filled in later. + su.AddUint16(d.arch, 4) // dwarf version (appendix F) // debug_abbrev_offset (*) - addDwarfAddrRef(ctxt, s, abbrevsym) + d.addDwarfAddrRef(su, abbrevsym) - s.AddUint8(uint8(ctxt.Arch.PtrSize)) // address_size + su.AddUint8(uint8(d.arch.PtrSize)) // address_size - dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev)) - dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr) + ds := dwSym(s) + dwarf.Uleb128put(d, ds, int64(compunit.Abbrev)) + dwarf.PutAttrs(d, ds, compunit.Abbrev, compunit.Attr) - cu := []*sym.Symbol{s} - cu = append(cu, u.AbsFnDIEs...) - cu = append(cu, u.FuncDIEs...) - if u.Consts != nil { - cu = append(cu, u.Consts) + cu := []loader.Sym{s} + cu = appendSyms(cu, u.AbsFnDIEs2) + cu = appendSyms(cu, u.FuncDIEs2) + if u.Consts2 != 0 { + cu = append(cu, loader.Sym(u.Consts2)) } var cusize int64 for _, child := range cu { - cusize += child.Size + cusize += int64(len(d.ldr.Data(child))) } for die := compunit.Child; die != nil; die = die.Link { l := len(cu) - lastSymSz := cu[l-1].Size - cu = putdie(ctxt, dwarfctxt, cu, die) + lastSymSz := int64(len(d.ldr.Data(cu[l-1]))) + cu = d.putdie(cu, die) if ispubname(die) { pubNames.add(die, cusize) } if ispubtype(die) { pubTypes.add(die, cusize) } - if lastSymSz != cu[l-1].Size { + if lastSymSz != int64(len(d.ldr.Data(cu[l-1]))) { // putdie will sometimes append directly to the last symbol of the list - cusize = cusize - lastSymSz + cu[l-1].Size + cusize = cusize - lastSymSz + int64(len(d.ldr.Data(cu[l-1]))) } for _, child := range cu[l:] { - cusize += child.Size + cusize += int64(len(d.ldr.Data(child))) } } - cu[len(cu)-1].AddUint8(0) // closes compilation unit DIE + + culu := d.ldr.MakeSymbolUpdater(cu[len(cu)-1]) + culu.AddUint8(0) // closes compilation unit DIE cusize++ // Save size for AIX symbol table. - if ctxt.HeadType == objabi.Haix { - saveDwsectCUSize(".debug_info", getPkgFromCUSym(s), uint64(cusize)) + if d.linkctxt.HeadType == objabi.Haix { + saveDwsectCUSize(".debug_info", d.getPkgFromCUSym(s), uint64(cusize)) } - if isDwarf64(ctxt) { - cusize -= 12 // exclude the length field. - s.SetUint(ctxt.Arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF + if isDwarf64(d.linkctxt) { + cusize -= 12 // exclude the length field. + su.SetUint(d.arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF } else { cusize -= 4 // exclude the length field. - s.SetUint32(ctxt.Arch, 0, uint32(cusize)) + su.SetUint32(d.arch, 0, uint32(cusize)) } pubNames.endCompUnit(compunit, uint32(cusize)+4) pubTypes.endCompUnit(compunit, uint32(cusize)+4) @@ -1481,79 +1549,81 @@ func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abb * Emit .debug_pubnames/_types. _info must have been written before, * because we need die->offs and infoo/infosize; */ -func ispubname(die *dwarf.DWDie) bool { - switch die.Abbrev { - case dwarf.DW_ABRV_FUNCTION, dwarf.DW_ABRV_VARIABLE: - a := getattr(die, dwarf.DW_AT_external) - return a != nil && a.Value != 0 - } - - return false -} - -func ispubtype(die *dwarf.DWDie) bool { - return die.Abbrev >= dwarf.DW_ABRV_NULLTYPE -} -type pubWriter struct { - ctxt *Link - s *sym.Symbol +type pubWriter2 struct { + d *dwctxt2 + s loader.Sym + su *loader.SymbolBuilder sname string sectionstart int64 culengthOff int64 } -func newPubWriter(ctxt *Link, sname string) *pubWriter { - s := ctxt.Syms.Lookup(sname, 0) - s.Type = sym.SDWARFSECT - return &pubWriter{ctxt: ctxt, s: s, sname: sname} +func newPubWriter2(d *dwctxt2, sname string) *pubWriter2 { + s := d.ldr.LookupOrCreateSym(sname, 0) + u := d.ldr.MakeSymbolUpdater(s) + u.SetType(sym.SDWARFSECT) + return &pubWriter2{d: d, s: s, su: u, sname: sname} } -func (pw *pubWriter) beginCompUnit(compunit *dwarf.DWDie) { - pw.sectionstart = pw.s.Size +func (pw *pubWriter2) beginCompUnit(compunit *dwarf.DWDie) { + pw.sectionstart = pw.su.Size() // Write .debug_pubnames/types Header (sec 6.1.1) - createUnitLength(pw.ctxt, pw.s, 0) // unit_length (*), will be filled in later. - pw.s.AddUint16(pw.ctxt.Arch, 2) // dwarf version (appendix F) - addDwarfAddrRef(pw.ctxt, pw.s, dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header) - pw.culengthOff = pw.s.Size - addDwarfAddrField(pw.ctxt, pw.s, uint64(0)) // debug_info_length, will be filled in later. - + pw.d.createUnitLength(pw.su, 0) // unit_length (*), will be filled in later. + pw.su.AddUint16(pw.d.arch, 2) // dwarf version (appendix F) + pw.d.addDwarfAddrRef(pw.su, pw.d.dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header) + pw.culengthOff = pw.su.Size() + pw.d.addDwarfAddrField(pw.su, uint64(0)) // debug_info_length, will be filled in later. } -func (pw *pubWriter) add(die *dwarf.DWDie, offset int64) { +func (pw *pubWriter2) add(die *dwarf.DWDie, offset int64) { dwa := getattr(die, dwarf.DW_AT_name) name := dwa.Data.(string) - if die.Sym == nil { + if pw.d.dtolsym(die.Sym) == 0 { fmt.Println("Missing sym for ", name) } - addDwarfAddrField(pw.ctxt, pw.s, uint64(offset)) - Addstring(pw.s, name) + pw.d.addDwarfAddrField(pw.su, uint64(offset)) + pw.su.Addstring(name) } -func (pw *pubWriter) endCompUnit(compunit *dwarf.DWDie, culength uint32) { - addDwarfAddrField(pw.ctxt, pw.s, 0) // Null offset +func (pw *pubWriter2) endCompUnit(compunit *dwarf.DWDie, culength uint32) { + pw.d.addDwarfAddrField(pw.su, 0) // Null offset // On AIX, save the current size of this compilation unit. - if pw.ctxt.HeadType == objabi.Haix { - saveDwsectCUSize(pw.sname, getPkgFromCUSym(dtolsym(compunit.Sym)), uint64(pw.s.Size-pw.sectionstart)) + if pw.d.linkctxt.HeadType == objabi.Haix { + saveDwsectCUSize(pw.sname, pw.d.getPkgFromCUSym(pw.d.dtolsym(compunit.Sym)), uint64(pw.su.Size()-pw.sectionstart)) } - if isDwarf64(pw.ctxt) { - pw.s.SetUint(pw.ctxt.Arch, pw.sectionstart+4, uint64(pw.s.Size-pw.sectionstart)-12) // exclude the length field. - pw.s.SetUint(pw.ctxt.Arch, pw.culengthOff, uint64(culength)) + if isDwarf64(pw.d.linkctxt) { + pw.su.SetUint(pw.d.arch, pw.sectionstart+4, uint64(pw.su.Size()-pw.sectionstart)-12) // exclude the length field. + pw.su.SetUint(pw.d.arch, pw.culengthOff, uint64(culength)) } else { - pw.s.SetUint32(pw.ctxt.Arch, pw.sectionstart, uint32(pw.s.Size-pw.sectionstart)-4) // exclude the length field. - pw.s.SetUint32(pw.ctxt.Arch, pw.culengthOff, culength) + pw.su.SetUint32(pw.d.arch, pw.sectionstart, uint32(pw.su.Size()-pw.sectionstart)-4) // exclude the length field. + pw.su.SetUint32(pw.d.arch, pw.culengthOff, culength) + } +} + +func ispubname(die *dwarf.DWDie) bool { + switch die.Abbrev { + case dwarf.DW_ABRV_FUNCTION, dwarf.DW_ABRV_VARIABLE: + a := getattr(die, dwarf.DW_AT_external) + return a != nil && a.Value != 0 } + + return false +} + +func ispubtype(die *dwarf.DWDie) bool { + return die.Abbrev >= dwarf.DW_ABRV_NULLTYPE } -func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { +func (d *dwctxt2) writegdbscript(syms []loader.Sym) []loader.Sym { // TODO (aix): make it available - if ctxt.HeadType == objabi.Haix { + if d.linkctxt.HeadType == objabi.Haix { return syms } - if ctxt.LinkMode == LinkExternal && ctxt.HeadType == objabi.Hwindows && ctxt.BuildMode == BuildModeCArchive { + if d.linkctxt.LinkMode == LinkExternal && d.linkctxt.HeadType == objabi.Hwindows && d.linkctxt.BuildMode == BuildModeCArchive { // gcc on Windows places .debug_gdb_scripts in the wrong location, which // causes the program not to run. See https://golang.org/issue/20183 // Non c-archives can avoid this issue via a linker script @@ -1564,16 +1634,22 @@ func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { } if gdbscript != "" { - s := ctxt.Syms.Lookup(".debug_gdb_scripts", 0) - s.Type = sym.SDWARFSECT - syms = append(syms, s) - s.AddUint8(1) // magic 1 byte? - Addstring(s, gdbscript) + gs := d.ldr.LookupOrCreateSym(".debug_gdb_scripts", 0) + u := d.ldr.MakeSymbolUpdater(gs) + u.SetType(sym.SDWARFSECT) + + syms = append(syms, gs) + u.AddUint8(1) // magic 1 byte? + u.Addstring(gdbscript) } return syms + } +// FIXME: might be worth looking replacing this map with a function +// that switches based on symbol instead. + var prototypedies map[string]*dwarf.DWDie func dwarfEnabled(ctxt *Link) bool { @@ -1606,11 +1682,30 @@ func dwarfEnabled(ctxt *Link) bool { return true } +// mkBuiltinType populates the dwctxt2 sym lookup maps for the +// newly created builtin type DIE 'typeDie'. +func (d *dwctxt2) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie { + // create type DIE + die := d.newdie(&dwtypes, abrv, tname, 0) + + // Look up type symbol. + gotype := d.lookupOrDiag("type." + tname) + + // Map from die sym to type sym + ds := loader.Sym(die.Sym.(dwSym)) + d.rtmap[ds] = gotype + + // Map from type to def sym + d.tdmap[gotype] = ds + + return die +} + // dwarfGenerateDebugInfo generated debug info entries for all types, // variables and functions in the program. // Along with dwarfGenerateDebugSyms they are the two main entry points into // dwarf generation: dwarfGenerateDebugInfo does all the work that should be -// done before symbol names are mangled while dwarfgeneratedebugsyms does +// done before symbol names are mangled while dwarfGenerateDebugSyms does // all the work that can only be done after addresses have been assigned to // text symbols. func dwarfGenerateDebugInfo(ctxt *Link) { @@ -1618,25 +1713,29 @@ func dwarfGenerateDebugInfo(ctxt *Link) { return } + d := newdwctxt2(ctxt, true) + if ctxt.HeadType == objabi.Haix { // Initial map used to store package size for each DWARF section. dwsectCUSize = make(map[string]uint64) } - // Forctxt.Diagnostic messages. + // For ctxt.Diagnostic messages. newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") - // Some types that must exist to define other ones. - newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0) + // Unspecified type. There are no references to this in the symbol table. + d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0) - newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "void", 0) - newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0) - - die := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size + // Some types that must exist to define other ones (uintptr in particular + // is needed for array size) + d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") + die := d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BASETYPE, "uintptr") newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) - newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(ctxt.Arch.PtrSize), 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(d.arch.PtrSize), 0) newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0) - newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, lookupOrDiag(ctxt, "type.uintptr")) + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, dwSym(d.lookupOrDiag("type.uintptr"))) + + d.uintptrInfoSym = d.mustFind("uintptr") // Prototypes needed for type synthesis. prototypedies = map[string]*dwarf.DWDie{ @@ -1662,7 +1761,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) { "type.runtime.interfacetype", "type.runtime.itab", "type.runtime.imethod"} { - defgotype(ctxt, lookupOrDiag(ctxt, typ)) + d.defgotype(d.lookupOrDiag(typ)) } // fake root DIE for compile unit DIEs @@ -1670,15 +1769,15 @@ func dwarfGenerateDebugInfo(ctxt *Link) { flagVariants := make(map[string]bool) for _, lib := range ctxt.Library { - consts := ctxt.Syms.ROLookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) + + consts := d.ldr.Lookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) for _, unit := range lib.Units { // We drop the constants into the first CU. - if consts != nil { - importInfoSymbol(ctxt, consts) - unit.Consts = consts - consts = nil + if consts != 0 { + unit.Consts2 = sym.LoaderSym(consts) + d.importInfoSymbol(ctxt, consts) + consts = 0 } - ctxt.compUnits = append(ctxt.compUnits, unit) // We need at least one runtime unit. @@ -1686,7 +1785,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) { ctxt.runtimeCU = unit } - unit.DWInfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0) + unit.DWInfo = d.newdie(&dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0) newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0) // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. compDir := getCompilationDir() @@ -1694,17 +1793,21 @@ func dwarfGenerateDebugInfo(ctxt *Link) { // the linker directory. If we move CU construction into the // compiler, this should happen naturally. newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir) - producerExtra := ctxt.Syms.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0) + + var peData []byte + if producerExtra := d.ldr.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0); producerExtra != 0 { + peData = d.ldr.Data(producerExtra) + } producer := "Go cmd/compile " + objabi.Version - if len(producerExtra.P) > 0 { + if len(peData) > 0 { // We put a semicolon before the flags to clearly // separate them from the version, which can be long // and have lots of weird things in it in development // versions. We promise not to put a semicolon in the // version, so it should be safe for readers to scan // forward to the semicolon. - producer += "; " + string(producerExtra.P) - flagVariants[string(producerExtra.P)] = true + producer += "; " + string(peData) + flagVariants[string(peData)] = true } else { flagVariants[""] = true } @@ -1712,12 +1815,13 @@ func dwarfGenerateDebugInfo(ctxt *Link) { newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer) var pkgname string - if s := ctxt.Syms.ROLookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); s != nil { - pkgname = string(s.P) + if pnSymIdx := d.ldr.Lookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); pnSymIdx != 0 { + pnsData := d.ldr.Data(pnSymIdx) + pkgname = string(pnsData) } newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname) - if len(unit.Textp) == 0 { + if len(unit.Textp2) == 0 { unit.DWInfo.Abbrev = dwarf.DW_ABRV_COMPUNIT_TEXTLESS } @@ -1725,36 +1829,50 @@ func dwarfGenerateDebugInfo(ctxt *Link) { // referenced types, create the file table for debug_line, find all // referenced abstract functions. // Collect all debug_range symbols in unit.rangeSyms - for _, s := range unit.Textp { // textp has been dead-code-eliminated already. - dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, false) - dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable - dsym.Type = sym.SDWARFINFO - unit.FuncDIEs = append(unit.FuncDIEs, dsym) - - rangeSym := dwarfFuncSym(ctxt, s, dwarf.RangePrefix, false) - if rangeSym != nil && rangeSym.Size > 0 { - rangeSym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable - rangeSym.Type = sym.SDWARFRANGE + for _, s := range unit.Textp2 { // textp2 has been dead-code-eliminated already. + fnSym := loader.Sym(s) + infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym) + if infosym == 0 { + continue + } + d.ldr.SetAttrNotInSymbolTable(infosym, true) + d.ldr.SetAttrReachable(infosym, true) + + unit.FuncDIEs2 = append(unit.FuncDIEs2, sym.LoaderSym(infosym)) + if rangesym != 0 { + rs := len(d.ldr.Data(rangesym)) + d.ldr.SetAttrNotInSymbolTable(rangesym, true) + d.ldr.SetAttrReachable(rangesym, true) if ctxt.HeadType == objabi.Haix { - addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rangeSym.Size)) + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rs)) } - unit.RangeSyms = append(unit.RangeSyms, rangeSym) + unit.RangeSyms2 = append(unit.RangeSyms2, sym.LoaderSym(rangesym)) } - for ri := 0; ri < len(dsym.R); ri++ { - r := &dsym.R[ri] - if r.Type == objabi.R_DWARFSECREF { - rsym := r.Sym - if strings.HasPrefix(rsym.Name, dwarf.InfoPrefix) && strings.HasSuffix(rsym.Name, dwarf.AbstractFuncSuffix) && !rsym.Attr.OnList() { + drelocs := d.ldr.Relocs(infosym) + for ri := 0; ri < drelocs.Count(); ri++ { + r := drelocs.At2(ri) + if r.Type() == objabi.R_DWARFSECREF { + rsym := r.Sym() + rsn := d.ldr.SymName(rsym) + if len(rsn) == 0 { + continue + } + // NB: there should be a better way to do this that doesn't involve materializing the symbol name and doing string prefix+suffix checks. + if strings.HasPrefix(rsn, dwarf.InfoPrefix) && strings.HasSuffix(rsn, dwarf.AbstractFuncSuffix) && !d.ldr.AttrOnList(rsym) { // abstract function - rsym.Attr |= sym.AttrOnList - unit.AbsFnDIEs = append(unit.AbsFnDIEs, rsym) - importInfoSymbol(ctxt, rsym) - } else if rsym.Size == 0 { - // a type we do not have a DIE for - n := nameFromDIESym(rsym) - defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0)) + d.ldr.SetAttrOnList(rsym, true) + unit.AbsFnDIEs2 = append(unit.AbsFnDIEs2, sym.LoaderSym(rsym)) + d.importInfoSymbol(ctxt, rsym) + continue + } + if _, ok := d.rtmap[rsym]; ok { + // type already generated + continue } + tn := rsn[len(dwarf.InfoPrefix):] + ts := d.ldr.Lookup("type."+tn, 0) + d.defgotype(ts) } } } @@ -1769,19 +1887,55 @@ func dwarfGenerateDebugInfo(ctxt *Link) { } // Create DIEs for global variables and the types they use. - genasmsym(ctxt, defdwsymb) + // FIXME: ideally this should be done in the compiler, since + // for globals there isn't any abiguity about which package + // a global belongs to. + for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ { + if !d.ldr.AttrReachable(idx) || + d.ldr.AttrNotInSymbolTable(idx) || + d.ldr.SymVersion(idx) >= sym.SymVerStatic { + continue + } + t := d.ldr.SymType(idx) + switch t { + case sym.SRODATA, sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS: + // ok + default: + continue + } + // Skip things with no type + if d.ldr.SymGoType(idx) == 0 { + continue + } + + sn := d.ldr.SymName(idx) + if ctxt.LinkMode != LinkExternal && isStaticTemp(sn) { + continue + } + if sn == "" { + // skip aux symbols + continue + } + + // Create DIE for global. + sv := d.ldr.SymValue(idx) + gt := d.ldr.SymGoType(idx) + d.dwarfDefineGlobal(ctxt, idx, sn, sv, gt) + } // Create DIEs for variable types indirectly referenced by function // autos (which may not appear directly as param/var DIEs). for _, lib := range ctxt.Library { for _, unit := range lib.Units { - lists := [][]*sym.Symbol{unit.AbsFnDIEs, unit.FuncDIEs} + lists := [][]sym.LoaderSym{unit.AbsFnDIEs2, unit.FuncDIEs2} for _, list := range lists { for _, s := range list { - for i := 0; i < len(s.R); i++ { - r := &s.R[i] - if r.Type == objabi.R_USETYPE { - defgotype(ctxt, r.Sym) + symIdx := loader.Sym(s) + relocs := d.ldr.Relocs(symIdx) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At2(i) + if r.Type() == objabi.R_USETYPE { + d.defgotype(r.Sym()) } } } @@ -1789,77 +1943,118 @@ func dwarfGenerateDebugInfo(ctxt *Link) { } } - synthesizestringtypes(ctxt, dwtypes.Child) - synthesizeslicetypes(ctxt, dwtypes.Child) - synthesizemaptypes(ctxt, dwtypes.Child) - synthesizechantypes(ctxt, dwtypes.Child) + d.synthesizestringtypes(ctxt, dwtypes.Child) + d.synthesizeslicetypes(ctxt, dwtypes.Child) + d.synthesizemaptypes(ctxt, dwtypes.Child) + d.synthesizechantypes(ctxt, dwtypes.Child) + + // NB: at this stage we have all the DIE objects constructed, but + // they have loader.Sym attributes and not sym.Symbol attributes. + // At the point when loadlibfull runs we will need to visit + // every DIE constructed and convert the symbols. } // dwarfGenerateDebugSyms constructs debug_line, debug_frame, debug_loc, // debug_pubnames and debug_pubtypes. It also writes out the debug_info -// section using symbols generated in dwarfGenerateDebugInfo. +// section using symbols generated in dwarfGenerateDebugInfo2. func dwarfGenerateDebugSyms(ctxt *Link) { if !dwarfEnabled(ctxt) { return } + d := &dwctxt2{ + linkctxt: ctxt, + ldr: ctxt.loader, + arch: ctxt.Arch, + } + d.dwarfGenerateDebugSyms() +} - abbrev := writeabbrev(ctxt) - syms := []*sym.Symbol{abbrev} +func (d *dwctxt2) dwarfGenerateDebugSyms() { - calcCompUnitRanges(ctxt) - sort.Sort(compilationUnitByStartPC(ctxt.compUnits)) + // Hack: because the "wavefront" hasn't been pushed all the way + // up to dodata(), there will have been changes made to the sym.Symbol's + // that are not yet reflected in the loader. Call a temporary + // loader routine that copies any changes back. + // WARNING: changing a symbol's content will usually require + // calling the loader cloneToExternal method, meaning that there + // can be an increase in memory, so this is likely to mess up any + // benchmarking runs. + d.ldr.PropagateSymbolChangesBackToLoader() - // Write per-package line and range tables and start their CU DIEs. - debugLine := ctxt.Syms.Lookup(".debug_line", 0) - debugLine.Type = sym.SDWARFSECT - debugRanges := ctxt.Syms.Lookup(".debug_ranges", 0) - debugRanges.Type = sym.SDWARFRANGE - debugRanges.Attr |= sym.AttrReachable + abbrev := d.writeabbrev() + syms := []loader.Sym{abbrev} + + d.calcCompUnitRanges() + sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits)) + + // Create .debug_line and .debug_ranges section symbols + debugLine := d.ldr.LookupOrCreateSym(".debug_line", 0) + dlu := d.ldr.MakeSymbolUpdater(debugLine) + dlu.SetType(sym.SDWARFSECT) + d.ldr.SetAttrReachable(debugLine, true) syms = append(syms, debugLine) - for _, u := range ctxt.compUnits { + + debugRanges := d.ldr.LookupOrCreateSym(".debug_ranges", 0) + dru := d.ldr.MakeSymbolUpdater(debugRanges) + dru.SetType(sym.SDWARFRANGE) + d.ldr.SetAttrReachable(debugRanges, true) + + // Write per-package line and range tables and start their CU DIEs. + for _, u := range d.linkctxt.compUnits { reversetree(&u.DWInfo.Child) if u.DWInfo.Abbrev == dwarf.DW_ABRV_COMPUNIT_TEXTLESS { continue } - writelines(ctxt, u, debugLine) - writepcranges(ctxt, u, u.Textp[0], u.PCs, debugRanges) + d.writelines(u, debugLine) + base := loader.Sym(u.Textp2[0]) + d.writepcranges(u, base, u.PCs, debugRanges) } // newdie adds DIEs to the *beginning* of the parent's DIE list. // Now that we're done creating DIEs, reverse the trees so DIEs // appear in the order they were created. reversetree(&dwtypes.Child) - movetomodule(ctxt, &dwtypes) + movetomodule(d.linkctxt, &dwtypes) - pubNames := newPubWriter(ctxt, ".debug_pubnames") - pubTypes := newPubWriter(ctxt, ".debug_pubtypes") + pubNames := newPubWriter2(d, ".debug_pubnames") + pubTypes := newPubWriter2(d, ".debug_pubtypes") // Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT - infosyms := writeinfo(ctxt, nil, ctxt.compUnits, abbrev, pubNames, pubTypes) + infosyms := d.writeinfo(nil, d.linkctxt.compUnits, abbrev, pubNames, pubTypes) - syms = writeframes(ctxt, syms) + syms = d.writeframes(syms) syms = append(syms, pubNames.s, pubTypes.s) - syms = writegdbscript(ctxt, syms) - // Now we're done writing SDWARFSECT symbols, so we can write + syms = d.writegdbscript(syms) + // We are now done writing SDWARFSECT symbols, so we can write // other SDWARF* symbols. syms = append(syms, infosyms...) - syms = collectlocs(ctxt, syms, ctxt.compUnits) + syms = d.collectlocs(syms, d.linkctxt.compUnits) syms = append(syms, debugRanges) - for _, unit := range ctxt.compUnits { - syms = append(syms, unit.RangeSyms...) + for _, unit := range d.linkctxt.compUnits { + for _, s := range unit.RangeSyms2 { + syms = append(syms, loader.Sym(s)) + } } - dwarfp = syms + dwarfp2 = syms + anonVerReplacement := d.linkctxt.Syms.IncVersion() + dwarfp = d.ldr.PropagateLoaderChangesToSymbols(dwarfp2, anonVerReplacement) } -func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) []*sym.Symbol { +func (d *dwctxt2) collectlocs(syms []loader.Sym, units []*sym.CompilationUnit) []loader.Sym { empty := true for _, u := range units { - for _, fn := range u.FuncDIEs { - for i := range fn.R { - reloc := &fn.R[i] // Copying sym.Reloc has measurable impact on performance - if reloc.Type == objabi.R_DWARFSECREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) { - reloc.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable - syms = append(syms, reloc.Sym) + for _, fn := range u.FuncDIEs2 { + relocs := d.ldr.Relocs(loader.Sym(fn)) + for i := 0; i < relocs.Count(); i++ { + reloc := relocs.At2(i) + if reloc.Type() != objabi.R_DWARFSECREF { + continue + } + rsym := reloc.Sym() + if d.ldr.SymType(rsym) == sym.SDWARFLOC { + d.ldr.SetAttrReachable(rsym, true) + d.ldr.SetAttrNotInSymbolTable(rsym, true) + syms = append(syms, rsym) empty = false // One location list entry per function, but many relocations to it. Don't duplicate. break @@ -1867,152 +2062,44 @@ func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) [ } } } + // Don't emit .debug_loc if it's empty -- it makes the ARM linker mad. if !empty { - locsym := ctxt.Syms.Lookup(".debug_loc", 0) - locsym.Type = sym.SDWARFLOC - locsym.Attr |= sym.AttrReachable + locsym := d.ldr.LookupOrCreateSym(".debug_loc", 0) + u := d.ldr.MakeSymbolUpdater(locsym) + u.SetType(sym.SDWARFLOC) + d.ldr.SetAttrReachable(locsym, true) syms = append(syms, locsym) } return syms } -// Read a pointer-sized uint from the beginning of buf. -func readPtr(ctxt *Link, buf []byte) uint64 { - switch ctxt.Arch.PtrSize { - case 4: - return uint64(ctxt.Arch.ByteOrder.Uint32(buf)) - case 8: - return ctxt.Arch.ByteOrder.Uint64(buf) - default: - panic("unexpected pointer size") - } -} - /* * Elf. */ -func dwarfaddshstrings(ctxt *Link, shstrtab *sym.Symbol) { - if *FlagW { // disable dwarf - return - } - - secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"} - for _, sec := range secs { - Addstring(shstrtab, ".debug_"+sec) - if ctxt.LinkMode == LinkExternal { - Addstring(shstrtab, elfRelType+".debug_"+sec) - } else { - Addstring(shstrtab, ".zdebug_"+sec) - } - } +func (d *dwctxt2) dwarfaddshstrings(ctxt *Link, shstrtab loader.Sym) { + panic("not yet implemented") } // Add section symbols for DWARF debug info. This is called before // dwarfaddelfheaders. -func dwarfaddelfsectionsyms(ctxt *Link) { - if *FlagW { // disable dwarf - return - } - if ctxt.LinkMode != LinkExternal { - return - } - - s := ctxt.Syms.Lookup(".debug_info", 0) - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - s = ctxt.Syms.Lookup(".debug_abbrev", 0) - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - s = ctxt.Syms.Lookup(".debug_line", 0) - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - s = ctxt.Syms.Lookup(".debug_frame", 0) - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - s = ctxt.Syms.Lookup(".debug_loc", 0) - if s.Sect != nil { - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - } - s = ctxt.Syms.Lookup(".debug_ranges", 0) - if s.Sect != nil { - putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) - } +func (d *dwctxt2) dwarfaddelfsectionsyms(ctxt *Link) { + panic("not yet implemented") } // dwarfcompress compresses the DWARF sections. Relocations are applied // on the fly. After this, dwarfp will contain a different (new) set of // symbols, and sections may have been replaced. -func dwarfcompress(ctxt *Link) { - supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin - if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal { - return - } - - var start int - var newDwarfp []*sym.Symbol - Segdwarf.Sections = Segdwarf.Sections[:0] - for i, s := range dwarfp { - // Find the boundaries between sections and compress - // the whole section once we've found the last of its - // symbols. - if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect { - s1 := compressSyms(ctxt, dwarfp[start:i+1]) - if s1 == nil { - // Compression didn't help. - newDwarfp = append(newDwarfp, dwarfp[start:i+1]...) - Segdwarf.Sections = append(Segdwarf.Sections, s.Sect) - } else { - compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):] - sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04) - sect.Length = uint64(len(s1)) - newSym := ctxt.Syms.Lookup(compressedSegName, 0) - newSym.P = s1 - newSym.Size = int64(len(s1)) - newSym.Sect = sect - newDwarfp = append(newDwarfp, newSym) - } - start = i + 1 - } - } - dwarfp = newDwarfp - ctxt.relocbuf = nil // no longer needed, don't hold it live - - // Re-compute the locations of the compressed DWARF symbols - // and sections, since the layout of these within the file is - // based on Section.Vaddr and Symbol.Value. - pos := Segdwarf.Vaddr - var prevSect *sym.Section - for _, s := range dwarfp { - s.Value = int64(pos) - if s.Sect != prevSect { - s.Sect.Vaddr = uint64(s.Value) - prevSect = s.Sect - } - if s.Sub != nil { - log.Fatalf("%s: unexpected sub-symbols", s) - } - pos += uint64(s.Size) - if ctxt.HeadType == objabi.Hwindows { - pos = uint64(Rnd(int64(pos), PEFILEALIGN)) - } - - } - Segdwarf.Length = pos - Segdwarf.Vaddr +func (d *dwctxt2) dwarfcompress(ctxt *Link) { + panic("not yet implemented") } -type compilationUnitByStartPC []*sym.CompilationUnit - -func (v compilationUnitByStartPC) Len() int { return len(v) } -func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] } - -func (v compilationUnitByStartPC) Less(i, j int) bool { - switch { - case len(v[i].Textp) == 0 && len(v[j].Textp) == 0: - return v[i].Lib.Pkg < v[j].Lib.Pkg - case len(v[i].Textp) != 0 && len(v[j].Textp) == 0: - return true - case len(v[i].Textp) == 0 && len(v[j].Textp) != 0: - return false - default: - return v[i].Textp[0].Value < v[j].Textp[0].Value - } +// getPkgFromCUSym returns the package name for the compilation unit +// represented by s. +// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get +// the package name. +func (d *dwctxt2) getPkgFromCUSym(s loader.Sym) string { + return strings.TrimPrefix(d.ldr.SymName(s), dwarf.InfoPrefix+".pkg.") } // On AIX, the symbol table needs to know where are the compilation units parts @@ -2034,11 +2121,3 @@ func saveDwsectCUSize(sname string, pkgname string, size uint64) { func addDwsectCUSize(sname string, pkgname string, size uint64) { dwsectCUSize[sname+"."+pkgname] += size } - -// getPkgFromCUSym returns the package name for the compilation unit -// represented by s. -// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get -// the package name. -func getPkgFromCUSym(s *sym.Symbol) string { - return strings.TrimPrefix(s.Name, dwarf.InfoPrefix+".pkg.") -} diff --git a/src/cmd/link/internal/ld/dwarf2.go b/src/cmd/link/internal/ld/dwarf2.go new file mode 100644 index 0000000000..233cd6a784 --- /dev/null +++ b/src/cmd/link/internal/ld/dwarf2.go @@ -0,0 +1,172 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type + +package ld + +import ( + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "log" +) + +func isDwarf64(ctxt *Link) bool { + return ctxt.HeadType == objabi.Haix +} + +var dwarfp []*sym.Symbol + +/* + * Elf. + */ +func dwarfaddshstrings(ctxt *Link, shstrtab *loader.SymbolBuilder) { + if *FlagW { // disable dwarf + return + } + + secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"} + for _, sec := range secs { + shstrtab.Addstring(".debug_" + sec) + if ctxt.IsExternal() { + shstrtab.Addstring(elfRelType + ".debug_" + sec) + } else { + shstrtab.Addstring(".zdebug_" + sec) + } + } +} + +// Add section symbols for DWARF debug info. This is called before +// dwarfaddelfheaders. +func dwarfaddelfsectionsyms(ctxt *Link) { + if *FlagW { // disable dwarf + return + } + if ctxt.LinkMode != LinkExternal { + return + } + + s := ctxt.Syms.Lookup(".debug_info", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_abbrev", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_line", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_frame", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_loc", 0) + if s.Sect != nil { + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + } + s = ctxt.Syms.Lookup(".debug_ranges", 0) + if s.Sect != nil { + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + } +} + +// dwarfcompress compresses the DWARF sections. Relocations are applied +// on the fly. After this, dwarfp will contain a different (new) set of +// symbols, and sections may have been replaced. +func dwarfcompress(ctxt *Link) { + // compressedSect is a helper type for parallelizing compression. + type compressedSect struct { + index int + compressed []byte + syms []*sym.Symbol + } + + supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin + if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal { + return + } + + var start, compressedCount int + resChannel := make(chan compressedSect) + for i, s := range dwarfp { + // Find the boundaries between sections and compress + // the whole section once we've found the last of its + // symbols. + if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect { + go func(resIndex int, syms []*sym.Symbol) { + resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms} + }(compressedCount, dwarfp[start:i+1]) + compressedCount++ + start = i + 1 + } + } + res := make([]compressedSect, compressedCount) + for ; compressedCount > 0; compressedCount-- { + r := <-resChannel + res[r.index] = r + } + + var newDwarfp []*sym.Symbol + Segdwarf.Sections = Segdwarf.Sections[:0] + for _, z := range res { + s := z.syms[0] + if z.compressed == nil { + // Compression didn't help. + newDwarfp = append(newDwarfp, z.syms...) + Segdwarf.Sections = append(Segdwarf.Sections, s.Sect) + } else { + compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):] + sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04) + sect.Length = uint64(len(z.compressed)) + newSym := ctxt.Syms.Lookup(compressedSegName, 0) + newSym.P = z.compressed + newSym.Size = int64(len(z.compressed)) + newSym.Sect = sect + newDwarfp = append(newDwarfp, newSym) + } + } + dwarfp = newDwarfp + + // Re-compute the locations of the compressed DWARF symbols + // and sections, since the layout of these within the file is + // based on Section.Vaddr and Symbol.Value. + pos := Segdwarf.Vaddr + var prevSect *sym.Section + for _, s := range dwarfp { + s.Value = int64(pos) + if s.Sect != prevSect { + s.Sect.Vaddr = uint64(s.Value) + prevSect = s.Sect + } + if s.Sub != nil { + log.Fatalf("%s: unexpected sub-symbols", s) + } + pos += uint64(s.Size) + if ctxt.HeadType == objabi.Hwindows { + pos = uint64(Rnd(int64(pos), PEFILEALIGN)) + } + + } + Segdwarf.Length = pos - Segdwarf.Vaddr +} + +type compilationUnitByStartPC []*sym.CompilationUnit + +func (v compilationUnitByStartPC) Len() int { return len(v) } +func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] } + +func (v compilationUnitByStartPC) Less(i, j int) bool { + switch { + case len(v[i].Textp2) == 0 && len(v[j].Textp2) == 0: + return v[i].Lib.Pkg < v[j].Lib.Pkg + case len(v[i].Textp2) != 0 && len(v[j].Textp2) == 0: + return true + case len(v[i].Textp2) == 0 && len(v[j].Textp2) != 0: + return false + default: + return v[i].PCs[0].Start < v[j].PCs[0].Start + } +} diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index b7221f04b3..e15f94d5e0 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -7,6 +7,7 @@ package ld import ( "cmd/internal/objabi" "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "crypto/sha1" "encoding/binary" @@ -742,21 +743,54 @@ func elfhash(name string) uint32 { return h } -func Elfwritedynent(ctxt *Link, s *sym.Symbol, tag int, val uint64) { +func elfWriteDynEnt(arch *sys.Arch, s *sym.Symbol, tag int, val uint64) { if elf64 { - s.AddUint64(ctxt.Arch, uint64(tag)) - s.AddUint64(ctxt.Arch, val) + s.AddUint64(arch, uint64(tag)) + s.AddUint64(arch, val) } else { - s.AddUint32(ctxt.Arch, uint32(tag)) - s.AddUint32(ctxt.Arch, uint32(val)) + s.AddUint32(arch, uint32(tag)) + s.AddUint32(arch, uint32(val)) + } +} + +func elfWriteDynEntSym(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) { + Elfwritedynentsymplus(arch, s, tag, t, 0) +} + +func Elfwritedynentsymplus(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol, add int64) { + if elf64 { + s.AddUint64(arch, uint64(tag)) + } else { + s.AddUint32(arch, uint32(tag)) + } + s.AddAddrPlus(arch, t, add) +} + +func elfWriteDynEntSymSize(arch *sys.Arch, s *sym.Symbol, tag int, t *sym.Symbol) { + if elf64 { + s.AddUint64(arch, uint64(tag)) + } else { + s.AddUint32(arch, uint32(tag)) } + s.AddSize(arch, t) } -func elfwritedynentsym(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) { - Elfwritedynentsymplus(ctxt, s, tag, t, 0) +// temporary +func Elfwritedynent2(arch *sys.Arch, s *loader.SymbolBuilder, tag int, val uint64) { + if elf64 { + s.AddUint64(arch, uint64(tag)) + s.AddUint64(arch, val) + } else { + s.AddUint32(arch, uint32(tag)) + s.AddUint32(arch, uint32(val)) + } } -func Elfwritedynentsymplus(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol, add int64) { +func elfwritedynentsym2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) { + Elfwritedynentsymplus2(ctxt, s, tag, t, 0) +} + +func Elfwritedynentsymplus2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym, add int64) { if elf64 { s.AddUint64(ctxt.Arch, uint64(tag)) } else { @@ -765,7 +799,7 @@ func Elfwritedynentsymplus(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol, ad s.AddAddrPlus(ctxt.Arch, t, add) } -func elfwritedynentsymsize(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) { +func elfwritedynentsymsize2(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) { if elf64 { s.AddUint64(ctxt.Arch, uint64(tag)) } else { @@ -1121,23 +1155,23 @@ func elfdynhash(ctxt *Link) { s = ctxt.Syms.Lookup(".dynamic", 0) elfverneed = nfile if elfverneed != 0 { - elfwritedynentsym(ctxt, s, DT_VERNEED, ctxt.Syms.Lookup(".gnu.version_r", 0)) - Elfwritedynent(ctxt, s, DT_VERNEEDNUM, uint64(nfile)) - elfwritedynentsym(ctxt, s, DT_VERSYM, ctxt.Syms.Lookup(".gnu.version", 0)) + elfWriteDynEntSym(ctxt.Arch, s, DT_VERNEED, ctxt.Syms.Lookup(".gnu.version_r", 0)) + elfWriteDynEnt(ctxt.Arch, s, DT_VERNEEDNUM, uint64(nfile)) + elfWriteDynEntSym(ctxt.Arch, s, DT_VERSYM, ctxt.Syms.Lookup(".gnu.version", 0)) } sy := ctxt.Syms.Lookup(elfRelType+".plt", 0) if sy.Size > 0 { if elfRelType == ".rela" { - Elfwritedynent(ctxt, s, DT_PLTREL, DT_RELA) + elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_RELA) } else { - Elfwritedynent(ctxt, s, DT_PLTREL, DT_REL) + elfWriteDynEnt(ctxt.Arch, s, DT_PLTREL, DT_REL) } - elfwritedynentsymsize(ctxt, s, DT_PLTRELSZ, sy) - elfwritedynentsym(ctxt, s, DT_JMPREL, sy) + elfWriteDynEntSymSize(ctxt.Arch, s, DT_PLTRELSZ, sy) + elfWriteDynEntSym(ctxt.Arch, s, DT_JMPREL, sy) } - Elfwritedynent(ctxt, s, DT_NULL, 0) + elfWriteDynEnt(ctxt.Arch, s, DT_NULL, 0) } func elfphload(seg *sym.Segment) *ElfPhdr { @@ -1381,18 +1415,18 @@ func Elfemitreloc(ctxt *Link) { if sect.Name == ".text" { elfrelocsect(ctxt, sect, ctxt.Textp) } else { - elfrelocsect(ctxt, sect, datap) + elfrelocsect(ctxt, sect, ctxt.datap) } } for _, sect := range Segrodata.Sections { - elfrelocsect(ctxt, sect, datap) + elfrelocsect(ctxt, sect, ctxt.datap) } for _, sect := range Segrelrodata.Sections { - elfrelocsect(ctxt, sect, datap) + elfrelocsect(ctxt, sect, ctxt.datap) } for _, sect := range Segdata.Sections { - elfrelocsect(ctxt, sect, datap) + elfrelocsect(ctxt, sect, ctxt.datap) } for _, sect := range Segdwarf.Sections { elfrelocsect(ctxt, sect, dwarfp) @@ -1400,9 +1434,10 @@ func Elfemitreloc(ctxt *Link) { } func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) { - s := ctxt.Syms.Lookup(sectionName, 0) - s.Attr |= sym.AttrReachable - s.Type = sym.SELFROSECT + ldr := ctxt.loader + s := ldr.CreateSymForUpdate(sectionName, 0) + s.SetReachable(true) + s.SetType(sym.SELFROSECT) // namesz s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME))) // descsz @@ -1410,93 +1445,91 @@ func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) { // tag s.AddUint32(ctxt.Arch, tag) // name + padding - s.P = append(s.P, ELF_NOTE_GO_NAME...) - for len(s.P)%4 != 0 { - s.P = append(s.P, 0) + s.AddBytes(ELF_NOTE_GO_NAME) + for len(s.Data())%4 != 0 { + s.AddUint8(0) } // desc + padding - s.P = append(s.P, desc...) - for len(s.P)%4 != 0 { - s.P = append(s.P, 0) + s.AddBytes(desc) + for len(s.Data())%4 != 0 { + s.AddUint8(0) } - s.Size = int64(len(s.P)) - s.Align = 4 + s.SetSize(int64(len(s.Data()))) + s.SetAlign(4) } func (ctxt *Link) doelf() { - if !ctxt.IsELF { - return - } + ldr := ctxt.loader /* predefine strings we need for section headers */ - shstrtab := ctxt.Syms.Lookup(".shstrtab", 0) + shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0) - shstrtab.Type = sym.SELFROSECT - shstrtab.Attr |= sym.AttrReachable + shstrtab.SetType(sym.SELFROSECT) + shstrtab.SetReachable(true) - Addstring(shstrtab, "") - Addstring(shstrtab, ".text") - Addstring(shstrtab, ".noptrdata") - Addstring(shstrtab, ".data") - Addstring(shstrtab, ".bss") - Addstring(shstrtab, ".noptrbss") - Addstring(shstrtab, "__libfuzzer_extra_counters") - Addstring(shstrtab, ".go.buildinfo") + shstrtab.Addstring("") + shstrtab.Addstring(".text") + shstrtab.Addstring(".noptrdata") + shstrtab.Addstring(".data") + shstrtab.Addstring(".bss") + shstrtab.Addstring(".noptrbss") + shstrtab.Addstring("__libfuzzer_extra_counters") + shstrtab.Addstring(".go.buildinfo") // generate .tbss section for dynamic internal linker or external // linking, so that various binutils could correctly calculate // PT_TLS size. See https://golang.org/issue/5200. - if !*FlagD || ctxt.LinkMode == LinkExternal { - Addstring(shstrtab, ".tbss") + if !*FlagD || ctxt.IsExternal() { + shstrtab.Addstring(".tbss") } - if ctxt.HeadType == objabi.Hnetbsd { - Addstring(shstrtab, ".note.netbsd.ident") + if ctxt.IsNetbsd() { + shstrtab.Addstring(".note.netbsd.ident") } - if ctxt.HeadType == objabi.Hopenbsd { - Addstring(shstrtab, ".note.openbsd.ident") + if ctxt.IsOpenbsd() { + shstrtab.Addstring(".note.openbsd.ident") } if len(buildinfo) > 0 { - Addstring(shstrtab, ".note.gnu.build-id") + shstrtab.Addstring(".note.gnu.build-id") } if *flagBuildid != "" { - Addstring(shstrtab, ".note.go.buildid") + shstrtab.Addstring(".note.go.buildid") } - Addstring(shstrtab, ".elfdata") - Addstring(shstrtab, ".rodata") + shstrtab.Addstring(".elfdata") + shstrtab.Addstring(".rodata") // See the comment about data.rel.ro.FOO section names in data.go. relro_prefix := "" if ctxt.UseRelro() { - Addstring(shstrtab, ".data.rel.ro") + shstrtab.Addstring(".data.rel.ro") relro_prefix = ".data.rel.ro" } - Addstring(shstrtab, relro_prefix+".typelink") - Addstring(shstrtab, relro_prefix+".itablink") - Addstring(shstrtab, relro_prefix+".gosymtab") - Addstring(shstrtab, relro_prefix+".gopclntab") + shstrtab.Addstring(relro_prefix + ".typelink") + shstrtab.Addstring(relro_prefix + ".itablink") + shstrtab.Addstring(relro_prefix + ".gosymtab") + shstrtab.Addstring(relro_prefix + ".gopclntab") - if ctxt.LinkMode == LinkExternal { + if ctxt.IsExternal() { *FlagD = true - Addstring(shstrtab, elfRelType+".text") - Addstring(shstrtab, elfRelType+".rodata") - Addstring(shstrtab, elfRelType+relro_prefix+".typelink") - Addstring(shstrtab, elfRelType+relro_prefix+".itablink") - Addstring(shstrtab, elfRelType+relro_prefix+".gosymtab") - Addstring(shstrtab, elfRelType+relro_prefix+".gopclntab") - Addstring(shstrtab, elfRelType+".noptrdata") - Addstring(shstrtab, elfRelType+".data") + shstrtab.Addstring(elfRelType + ".text") + shstrtab.Addstring(elfRelType + ".rodata") + shstrtab.Addstring(elfRelType + relro_prefix + ".typelink") + shstrtab.Addstring(elfRelType + relro_prefix + ".itablink") + shstrtab.Addstring(elfRelType + relro_prefix + ".gosymtab") + shstrtab.Addstring(elfRelType + relro_prefix + ".gopclntab") + shstrtab.Addstring(elfRelType + ".noptrdata") + shstrtab.Addstring(elfRelType + ".data") if ctxt.UseRelro() { - Addstring(shstrtab, elfRelType+".data.rel.ro") + shstrtab.Addstring(elfRelType + ".data.rel.ro") } - Addstring(shstrtab, elfRelType+".go.buildinfo") + shstrtab.Addstring(elfRelType + ".go.buildinfo") // add a .note.GNU-stack section to mark the stack as non-executable - Addstring(shstrtab, ".note.GNU-stack") + shstrtab.Addstring(".note.GNU-stack") - if ctxt.BuildMode == BuildModeShared { - Addstring(shstrtab, ".note.go.abihash") - Addstring(shstrtab, ".note.go.pkg-list") - Addstring(shstrtab, ".note.go.deps") + if ctxt.IsShared() { + shstrtab.Addstring(".note.go.abihash") + shstrtab.Addstring(".note.go.pkg-list") + shstrtab.Addstring(".note.go.deps") } } @@ -1509,171 +1542,171 @@ func (ctxt *Link) doelf() { } if hasinitarr { - Addstring(shstrtab, ".init_array") - Addstring(shstrtab, elfRelType+".init_array") + shstrtab.Addstring(".init_array") + shstrtab.Addstring(elfRelType + ".init_array") } if !*FlagS { - Addstring(shstrtab, ".symtab") - Addstring(shstrtab, ".strtab") + shstrtab.Addstring(".symtab") + shstrtab.Addstring(".strtab") dwarfaddshstrings(ctxt, shstrtab) } - Addstring(shstrtab, ".shstrtab") + shstrtab.Addstring(".shstrtab") if !*FlagD { /* -d suppresses dynamic loader format */ - Addstring(shstrtab, ".interp") - Addstring(shstrtab, ".hash") - Addstring(shstrtab, ".got") - if ctxt.Arch.Family == sys.PPC64 { - Addstring(shstrtab, ".glink") - } - Addstring(shstrtab, ".got.plt") - Addstring(shstrtab, ".dynamic") - Addstring(shstrtab, ".dynsym") - Addstring(shstrtab, ".dynstr") - Addstring(shstrtab, elfRelType) - Addstring(shstrtab, elfRelType+".plt") - - Addstring(shstrtab, ".plt") - Addstring(shstrtab, ".gnu.version") - Addstring(shstrtab, ".gnu.version_r") + shstrtab.Addstring(".interp") + shstrtab.Addstring(".hash") + shstrtab.Addstring(".got") + if ctxt.IsPPC64() { + shstrtab.Addstring(".glink") + } + shstrtab.Addstring(".got.plt") + shstrtab.Addstring(".dynamic") + shstrtab.Addstring(".dynsym") + shstrtab.Addstring(".dynstr") + shstrtab.Addstring(elfRelType) + shstrtab.Addstring(elfRelType + ".plt") + + shstrtab.Addstring(".plt") + shstrtab.Addstring(".gnu.version") + shstrtab.Addstring(".gnu.version_r") /* dynamic symbol table - first entry all zeros */ - s := ctxt.Syms.Lookup(".dynsym", 0) + dynsym := ldr.CreateSymForUpdate(".dynsym", 0) - s.Type = sym.SELFROSECT - s.Attr |= sym.AttrReachable + dynsym.SetType(sym.SELFROSECT) + dynsym.SetReachable(true) if elf64 { - s.Size += ELF64SYMSIZE + dynsym.SetSize(dynsym.Size() + ELF64SYMSIZE) } else { - s.Size += ELF32SYMSIZE + dynsym.SetSize(dynsym.Size() + ELF32SYMSIZE) } /* dynamic string table */ - s = ctxt.Syms.Lookup(".dynstr", 0) + dynstr := ldr.CreateSymForUpdate(".dynstr", 0) - s.Type = sym.SELFROSECT - s.Attr |= sym.AttrReachable - if s.Size == 0 { - Addstring(s, "") + dynstr.SetType(sym.SELFROSECT) + dynstr.SetReachable(true) + if dynstr.Size() == 0 { + dynstr.Addstring("") } - dynstr := s /* relocation table */ - s = ctxt.Syms.Lookup(elfRelType, 0) - s.Attr |= sym.AttrReachable - s.Type = sym.SELFROSECT + s := ldr.CreateSymForUpdate(elfRelType, 0) + s.SetReachable(true) + s.SetType(sym.SELFROSECT) /* global offset table */ - s = ctxt.Syms.Lookup(".got", 0) - - s.Attr |= sym.AttrReachable - s.Type = sym.SELFGOT // writable + got := ldr.CreateSymForUpdate(".got", 0) + got.SetReachable(true) + got.SetType(sym.SELFGOT) // writable /* ppc64 glink resolver */ - if ctxt.Arch.Family == sys.PPC64 { - s := ctxt.Syms.Lookup(".glink", 0) - s.Attr |= sym.AttrReachable - s.Type = sym.SELFRXSECT + if ctxt.IsPPC64() { + s := ldr.CreateSymForUpdate(".glink", 0) + s.SetReachable(true) + s.SetType(sym.SELFRXSECT) } /* hash */ - s = ctxt.Syms.Lookup(".hash", 0) + hash := ldr.CreateSymForUpdate(".hash", 0) + hash.SetReachable(true) + hash.SetType(sym.SELFROSECT) - s.Attr |= sym.AttrReachable - s.Type = sym.SELFROSECT + gotplt := ldr.CreateSymForUpdate(".got.plt", 0) + gotplt.SetReachable(true) + gotplt.SetType(sym.SELFSECT) // writable - s = ctxt.Syms.Lookup(".got.plt", 0) - s.Attr |= sym.AttrReachable - s.Type = sym.SELFSECT // writable - - s = ctxt.Syms.Lookup(".plt", 0) - - s.Attr |= sym.AttrReachable - if ctxt.Arch.Family == sys.PPC64 { + plt := ldr.CreateSymForUpdate(".plt", 0) + plt.SetReachable(true) + if ctxt.IsPPC64() { // In the ppc64 ABI, .plt is a data section // written by the dynamic linker. - s.Type = sym.SELFSECT + plt.SetType(sym.SELFSECT) } else { - s.Type = sym.SELFRXSECT + plt.SetType(sym.SELFRXSECT) } - thearch.Elfsetupplt(ctxt) - - s = ctxt.Syms.Lookup(elfRelType+".plt", 0) - s.Attr |= sym.AttrReachable - s.Type = sym.SELFROSECT + s = ldr.CreateSymForUpdate(elfRelType+".plt", 0) + s.SetReachable(true) + s.SetType(sym.SELFROSECT) - s = ctxt.Syms.Lookup(".gnu.version", 0) - s.Attr |= sym.AttrReachable - s.Type = sym.SELFROSECT + s = ldr.CreateSymForUpdate(".gnu.version", 0) + s.SetReachable(true) + s.SetType(sym.SELFROSECT) - s = ctxt.Syms.Lookup(".gnu.version_r", 0) - s.Attr |= sym.AttrReachable - s.Type = sym.SELFROSECT + s = ldr.CreateSymForUpdate(".gnu.version_r", 0) + s.SetReachable(true) + s.SetType(sym.SELFROSECT) /* define dynamic elf table */ - s = ctxt.Syms.Lookup(".dynamic", 0) + dynamic := ldr.CreateSymForUpdate(".dynamic", 0) + dynamic.SetReachable(true) + dynamic.SetType(sym.SELFSECT) // writable - s.Attr |= sym.AttrReachable - s.Type = sym.SELFSECT // writable + if ctxt.IsS390X() { + // S390X uses .got instead of .got.plt + gotplt = got + } + thearch.Elfsetupplt(ctxt, plt, gotplt, dynamic.Sym()) /* * .dynamic table */ - elfwritedynentsym(ctxt, s, DT_HASH, ctxt.Syms.Lookup(".hash", 0)) + elfwritedynentsym2(ctxt, dynamic, DT_HASH, hash.Sym()) - elfwritedynentsym(ctxt, s, DT_SYMTAB, ctxt.Syms.Lookup(".dynsym", 0)) + elfwritedynentsym2(ctxt, dynamic, DT_SYMTAB, dynsym.Sym()) if elf64 { - Elfwritedynent(ctxt, s, DT_SYMENT, ELF64SYMSIZE) + Elfwritedynent2(ctxt.Arch, dynamic, DT_SYMENT, ELF64SYMSIZE) } else { - Elfwritedynent(ctxt, s, DT_SYMENT, ELF32SYMSIZE) + Elfwritedynent2(ctxt.Arch, dynamic, DT_SYMENT, ELF32SYMSIZE) } - elfwritedynentsym(ctxt, s, DT_STRTAB, ctxt.Syms.Lookup(".dynstr", 0)) - elfwritedynentsymsize(ctxt, s, DT_STRSZ, ctxt.Syms.Lookup(".dynstr", 0)) + elfwritedynentsym2(ctxt, dynamic, DT_STRTAB, dynstr.Sym()) + elfwritedynentsymsize2(ctxt, dynamic, DT_STRSZ, dynstr.Sym()) if elfRelType == ".rela" { - elfwritedynentsym(ctxt, s, DT_RELA, ctxt.Syms.Lookup(".rela", 0)) - elfwritedynentsymsize(ctxt, s, DT_RELASZ, ctxt.Syms.Lookup(".rela", 0)) - Elfwritedynent(ctxt, s, DT_RELAENT, ELF64RELASIZE) + rela := ldr.LookupOrCreateSym(".rela", 0) + elfwritedynentsym2(ctxt, dynamic, DT_RELA, rela) + elfwritedynentsymsize2(ctxt, dynamic, DT_RELASZ, rela) + Elfwritedynent2(ctxt.Arch, dynamic, DT_RELAENT, ELF64RELASIZE) } else { - elfwritedynentsym(ctxt, s, DT_REL, ctxt.Syms.Lookup(".rel", 0)) - elfwritedynentsymsize(ctxt, s, DT_RELSZ, ctxt.Syms.Lookup(".rel", 0)) - Elfwritedynent(ctxt, s, DT_RELENT, ELF32RELSIZE) + rel := ldr.LookupOrCreateSym(".rel", 0) + elfwritedynentsym2(ctxt, dynamic, DT_REL, rel) + elfwritedynentsymsize2(ctxt, dynamic, DT_RELSZ, rel) + Elfwritedynent2(ctxt.Arch, dynamic, DT_RELENT, ELF32RELSIZE) } if rpath.val != "" { - Elfwritedynent(ctxt, s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val))) + Elfwritedynent2(ctxt.Arch, dynamic, DT_RUNPATH, uint64(dynstr.Addstring(rpath.val))) } - if ctxt.Arch.Family == sys.PPC64 { - elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".plt", 0)) - } else if ctxt.Arch.Family == sys.S390X { - elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got", 0)) + if ctxt.IsPPC64() { + elfwritedynentsym2(ctxt, dynamic, DT_PLTGOT, plt.Sym()) } else { - elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got.plt", 0)) + elfwritedynentsym2(ctxt, dynamic, DT_PLTGOT, gotplt.Sym()) } - if ctxt.Arch.Family == sys.PPC64 { - Elfwritedynent(ctxt, s, DT_PPC64_OPT, 0) + if ctxt.IsPPC64() { + Elfwritedynent2(ctxt.Arch, dynamic, DT_PPC64_OPT, 0) } // Solaris dynamic linker can't handle an empty .rela.plt if // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the // size of .rel(a).plt section. - Elfwritedynent(ctxt, s, DT_DEBUG, 0) + Elfwritedynent2(ctxt.Arch, dynamic, DT_DEBUG, 0) } - if ctxt.BuildMode == BuildModeShared { + if ctxt.IsShared() { // The go.link.abihashbytes symbol will be pointed at the appropriate // part of the .note.go.abihash section in data.go:func address(). - s := ctxt.Syms.Lookup("go.link.abihashbytes", 0) - s.Attr |= sym.AttrLocal - s.Type = sym.SRODATA - s.Attr |= sym.AttrSpecial - s.Attr |= sym.AttrReachable - s.Size = int64(sha1.Size) + s := ldr.LookupOrCreateSym("go.link.abihashbytes", 0) + sb := ldr.MakeSymbolUpdater(s) + ldr.SetAttrLocal(s, true) + sb.SetType(sym.SRODATA) + ldr.SetAttrSpecial(s, true) + sb.SetReachable(true) + sb.SetSize(sha1.Size) sort.Sort(byPkg(ctxt.Library)) h := sha1.New() @@ -2271,15 +2304,15 @@ elfobj: } } -func elfadddynsym(ctxt *Link, s *sym.Symbol) { +func elfadddynsym(target *Target, syms *ArchSyms, s *sym.Symbol) { if elf64 { s.Dynid = int32(Nelfsym) Nelfsym++ - d := ctxt.Syms.Lookup(".dynsym", 0) + d := syms.DynSym name := s.Extname() - d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name))) + d.AddUint32(target.Arch, uint32(Addstring(syms.DynStr, name))) /* type */ t := STB_GLOBAL << 4 @@ -2296,52 +2329,52 @@ func elfadddynsym(ctxt *Link, s *sym.Symbol) { /* section where symbol is defined */ if s.Type == sym.SDYNIMPORT { - d.AddUint16(ctxt.Arch, SHN_UNDEF) + d.AddUint16(target.Arch, SHN_UNDEF) } else { - d.AddUint16(ctxt.Arch, 1) + d.AddUint16(target.Arch, 1) } /* value */ if s.Type == sym.SDYNIMPORT { - d.AddUint64(ctxt.Arch, 0) + d.AddUint64(target.Arch, 0) } else { - d.AddAddr(ctxt.Arch, s) + d.AddAddr(target.Arch, s) } /* size of object */ - d.AddUint64(ctxt.Arch, uint64(s.Size)) + d.AddUint64(target.Arch, uint64(s.Size)) - if ctxt.Arch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib() != "" && !seenlib[s.Dynimplib()] { - Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(ctxt.Syms.Lookup(".dynstr", 0), s.Dynimplib()))) + if target.Arch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib() != "" && !seenlib[s.Dynimplib()] { + elfWriteDynEnt(target.Arch, syms.Dynamic, DT_NEEDED, uint64(Addstring(syms.DynStr, s.Dynimplib()))) } } else { s.Dynid = int32(Nelfsym) Nelfsym++ - d := ctxt.Syms.Lookup(".dynsym", 0) + d := syms.DynSym /* name */ name := s.Extname() - d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name))) + d.AddUint32(target.Arch, uint32(Addstring(syms.DynStr, name))) /* value */ if s.Type == sym.SDYNIMPORT { - d.AddUint32(ctxt.Arch, 0) + d.AddUint32(target.Arch, 0) } else { - d.AddAddr(ctxt.Arch, s) + d.AddAddr(target.Arch, s) } /* size of object */ - d.AddUint32(ctxt.Arch, uint32(s.Size)) + d.AddUint32(target.Arch, uint32(s.Size)) /* type */ t := STB_GLOBAL << 4 // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386. - if ctxt.Arch.Family == sys.I386 && s.Attr.CgoExport() && s.Type == sym.STEXT { + if target.Arch.Family == sys.I386 && s.Attr.CgoExport() && s.Type == sym.STEXT { t |= STT_FUNC - } else if ctxt.Arch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type == sym.STEXT { + } else if target.Arch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type == sym.STEXT { t |= STT_FUNC } else { t |= STT_OBJECT @@ -2351,9 +2384,95 @@ func elfadddynsym(ctxt *Link, s *sym.Symbol) { /* shndx */ if s.Type == sym.SDYNIMPORT { - d.AddUint16(ctxt.Arch, SHN_UNDEF) + d.AddUint16(target.Arch, SHN_UNDEF) + } else { + d.AddUint16(target.Arch, 1) + } + } +} + +func elfadddynsym2(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) { + ldr.SetSymDynid(s, int32(Nelfsym)) + Nelfsym++ + d := ldr.MakeSymbolUpdater(syms.DynSym2) + name := ldr.SymExtname(s) + dstru := ldr.MakeSymbolUpdater(syms.DynStr2) + st := ldr.SymType(s) + cgoeStatic := ldr.AttrCgoExportStatic(s) + cgoeDynamic := ldr.AttrCgoExportDynamic(s) + cgoexp := (cgoeStatic || cgoeDynamic) + + d.AddUint32(target.Arch, uint32(dstru.Addstring(name))) + + if elf64 { + + /* type */ + t := STB_GLOBAL << 4 + + if cgoexp && st == sym.STEXT { + t |= STT_FUNC + } else { + t |= STT_OBJECT + } + d.AddUint8(uint8(t)) + + /* reserved */ + d.AddUint8(0) + + /* section where symbol is defined */ + if st == sym.SDYNIMPORT { + d.AddUint16(target.Arch, SHN_UNDEF) + } else { + d.AddUint16(target.Arch, 1) + } + + /* value */ + if st == sym.SDYNIMPORT { + d.AddUint64(target.Arch, 0) + } else { + d.AddAddrPlus(target.Arch, s, 0) + } + + /* size of object */ + d.AddUint64(target.Arch, uint64(len(ldr.Data(s)))) + + dil := ldr.SymDynimplib(s) + + if target.Arch.Family == sys.AMD64 && !cgoeDynamic && dil != "" && !seenlib[dil] { + du := ldr.MakeSymbolUpdater(syms.Dynamic2) + Elfwritedynent2(target.Arch, du, DT_NEEDED, uint64(dstru.Addstring(dil))) + } + } else { + + /* value */ + if st == sym.SDYNIMPORT { + d.AddUint32(target.Arch, 0) + } else { + d.AddAddrPlus(target.Arch, s, 0) + } + + /* size of object */ + d.AddUint32(target.Arch, uint32(len(ldr.Data(s)))) + + /* type */ + t := STB_GLOBAL << 4 + + // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386. + if target.Arch.Family == sys.I386 && cgoexp && st == sym.STEXT { + t |= STT_FUNC + } else if target.Arch.Family == sys.ARM && cgoeDynamic && st == sym.STEXT { + t |= STT_FUNC + } else { + t |= STT_OBJECT + } + d.AddUint8(uint8(t)) + d.AddUint8(0) + + /* shndx */ + if st == sym.SDYNIMPORT { + d.AddUint16(target.Arch, SHN_UNDEF) } else { - d.AddUint16(ctxt.Arch, 1) + d.AddUint16(target.Arch, 1) } } } diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go new file mode 100644 index 0000000000..e66c518b85 --- /dev/null +++ b/src/cmd/link/internal/ld/errors.go @@ -0,0 +1,87 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package ld + +import ( + "cmd/internal/obj" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "os" + "sync" +) + +type unresolvedSymKey struct { + from *sym.Symbol // Symbol that referenced unresolved "to" + to *sym.Symbol // Unresolved symbol referenced by "from" +} + +type lookupFn func(name string, version int) *sym.Symbol +type symNameFn func(s loader.Sym) string + +// ErrorReporter is used to make error reporting thread safe. +type ErrorReporter struct { + unresOnce sync.Once + unresSyms map[unresolvedSymKey]bool + unresMutex sync.Mutex + lookup lookupFn + SymName symNameFn +} + +// errorUnresolved prints unresolved symbol error for r.Sym that is referenced from s. +func (reporter *ErrorReporter) errorUnresolved(s *sym.Symbol, r *sym.Reloc) { + reporter.unresOnce.Do(func() { reporter.unresSyms = make(map[unresolvedSymKey]bool) }) + + k := unresolvedSymKey{from: s, to: r.Sym} + reporter.unresMutex.Lock() + defer reporter.unresMutex.Unlock() + if !reporter.unresSyms[k] { + reporter.unresSyms[k] = true + + // Try to find symbol under another ABI. + var reqABI, haveABI obj.ABI + haveABI = ^obj.ABI(0) + reqABI, ok := sym.VersionToABI(int(r.Sym.Version)) + if ok { + for abi := obj.ABI(0); abi < obj.ABICount; abi++ { + v := sym.ABIToVersion(abi) + if v == -1 { + continue + } + if rs := reporter.lookup(r.Sym.Name, v); rs != nil && rs.Type != sym.Sxxx && rs.Type != sym.SXREF { + haveABI = abi + } + } + } + + // Give a special error message for main symbol (see #24809). + if r.Sym.Name == "main.main" { + Errorf(s, "function main is undeclared in the main package") + } else if haveABI != ^obj.ABI(0) { + Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", r.Sym.Name, reqABI, haveABI) + } else { + Errorf(s, "relocation target %s not defined", r.Sym.Name) + } + } +} + +// Errorf method logs an error message. +// +// If more than 20 errors have been printed, exit with an error. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +// TODO: consolidate the various different versions of Errorf ( +// function, Link method, and ErrorReporter method). +func (reporter *ErrorReporter) Errorf(s loader.Sym, format string, args ...interface{}) { + if s != 0 && reporter.SymName != nil { + sn := reporter.SymName(s) + format = sn + ": " + format + } else { + format = fmt.Sprintf("sym %d: %s", s, format) + } + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + afterErrorAction() +} diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 21457fdfc8..3b9d4ba7cc 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -10,6 +10,8 @@ import ( "bytes" "cmd/internal/bio" "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "encoding/json" "fmt" @@ -145,16 +147,14 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) { } } - if *flagNewobj { - // Record the directives. We'll process them later after Symbols are created. - ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives}) - } else { - setCgoAttr(ctxt, ctxt.Syms.Lookup, file, pkg, directives) - } + // Record the directives. We'll process them later after Symbols are created. + ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives}) } // Set symbol attributes or flags based on cgo directives. -func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, pkg string, directives [][]string) { +// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'. +func setCgoAttr(ctxt *Link, lookup func(string, int) loader.Sym, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) { + l := ctxt.loader for _, f := range directives { switch f[0] { case "cgo_import_dynamic": @@ -197,12 +197,16 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p remote, q = remote[:i], remote[i+1:] } s := lookup(local, 0) - if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS || s.Type == sym.SHOSTOBJ { - s.SetDynimplib(lib) - s.SetExtname(remote) - s.SetDynimpvers(q) - if s.Type != sym.SHOSTOBJ { - s.Type = sym.SDYNIMPORT + st := l.SymType(s) + if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ { + l.SetSymDynimplib(s, lib) + l.SetSymExtname(s, remote) + l.SetSymDynimpvers(s, q) + if st != sym.SHOSTOBJ { + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SDYNIMPORT) + } else { + hostObjSyms[s] = struct{}{} } havedynamic = 1 } @@ -216,8 +220,10 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p local := f[1] s := lookup(local, 0) - s.Type = sym.SHOSTOBJ - s.Size = 0 + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SHOSTOBJ) + su.SetSize(0) + hostObjSyms[s] = struct{}{} continue case "cgo_export_static", "cgo_export_dynamic": @@ -238,6 +244,10 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p // yet know it's an alias). s := lookup(local, 0) + if l.SymType(s) == sym.SHOSTOBJ { + hostObjSyms[s] = struct{}{} + } + switch ctxt.BuildMode { case BuildModeCShared, BuildModeCArchive, BuildModePlugin: if s == lookup("main", 0) { @@ -247,24 +257,27 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p // export overrides import, for openbsd/cgo. // see issue 4878. - if s.Dynimplib() != "" { - s.ResetDyninfo() - s.SetExtname("") - s.Type = 0 + if l.SymDynimplib(s) != "" { + l.SetSymDynimplib(s, "") + l.SetSymDynimpvers(s, "") + l.SetSymExtname(s, "") + var su *loader.SymbolBuilder + su = l.MakeSymbolUpdater(s) + su.SetType(0) } - if !s.Attr.CgoExport() { - s.SetExtname(remote) - } else if s.Extname() != remote { - fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname(), remote) + if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) { + l.SetSymExtname(s, remote) + } else if l.SymExtname(s) != remote { + fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote) nerrors++ return } if f[0] == "cgo_export_static" { - s.Attr |= sym.AttrCgoExportStatic + l.SetAttrCgoExportStatic(s, true) } else { - s.Attr |= sym.AttrCgoExportDynamic + l.SetAttrCgoExportDynamic(s, true) } continue @@ -295,6 +308,7 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, p fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f) nerrors++ } + return } var seenlib = make(map[string]bool) @@ -306,78 +320,99 @@ func adddynlib(ctxt *Link, lib string) { seenlib[lib] = true if ctxt.IsELF { - s := ctxt.Syms.Lookup(".dynstr", 0) - if s.Size == 0 { - Addstring(s, "") + dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr2) + if dsu.Size() == 0 { + dsu.Addstring("") } - Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib))) + du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic2) + Elfwritedynent2(ctxt.Arch, du, DT_NEEDED, uint64(dsu.Addstring(lib))) } else { Errorf(nil, "adddynlib: unsupported binary format") } } -func Adddynsym(ctxt *Link, s *sym.Symbol) { - if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal { +func Adddynsym2(ldr *loader.Loader, reporter *ErrorReporter, target *Target, syms *ArchSyms, s loader.Sym) { + if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal { return } - if ctxt.IsELF { - elfadddynsym(ctxt, s) - } else if ctxt.HeadType == objabi.Hdarwin { + if target.IsELF { + elfadddynsym2(ldr, target, syms, s) + } else if target.HeadType == objabi.Hdarwin { + reporter.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s)) + } else if target.HeadType == objabi.Hwindows { + // already taken care of + } else { + reporter.Errorf(s, "adddynsym: unsupported binary format") + } +} + +func Adddynsym(target *Target, syms *ArchSyms, s *sym.Symbol) { + if s.Dynid >= 0 || target.LinkMode == LinkExternal { + return + } + + if target.IsELF { + elfadddynsym(target, syms, s) + } else if target.HeadType == objabi.Hdarwin { Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname()) - } else if ctxt.HeadType == objabi.Hwindows { + } else if target.HeadType == objabi.Hwindows { // already taken care of } else { Errorf(s, "adddynsym: unsupported binary format") } } -func fieldtrack(ctxt *Link) { - // record field tracking references +func fieldtrack(arch *sys.Arch, l *loader.Loader) { var buf bytes.Buffer - for _, s := range ctxt.Syms.Allsym { - if strings.HasPrefix(s.Name, "go.track.") { - s.Attr |= sym.AttrSpecial // do not lay out in data segment - s.Attr |= sym.AttrNotInSymbolTable - if s.Attr.Reachable() { - buf.WriteString(s.Name[9:]) - for p := ctxt.Reachparent[s]; p != nil; p = ctxt.Reachparent[p] { + for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ { + if name := l.SymName(i); strings.HasPrefix(name, "go.track.") { + bld := l.MakeSymbolUpdater(i) + bld.SetSpecial(true) + bld.SetNotInSymbolTable(true) + if bld.Reachable() { + buf.WriteString(name[9:]) + for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] { buf.WriteString("\t") - buf.WriteString(p.Name) + buf.WriteString(l.SymName(p)) } buf.WriteString("\n") - } - s.Type = sym.SCONST - s.Value = 0 + bld.SetType(sym.SCONST) + bld.SetValue(0) + } } } - if *flagFieldTrack == "" { return } - s := ctxt.Syms.ROLookup(*flagFieldTrack, 0) - if s == nil || !s.Attr.Reachable() { + s := l.Lookup(*flagFieldTrack, 0) + if s == 0 || !l.AttrReachable(s) { return } - s.Type = sym.SDATA - addstrdata(ctxt, *flagFieldTrack, buf.String()) + bld := l.MakeSymbolUpdater(s) + bld.SetType(sym.SDATA) + addstrdata(arch, l, *flagFieldTrack, buf.String()) } func (ctxt *Link) addexport() { // Track undefined external symbols during external link. if ctxt.LinkMode == LinkExternal { - for _, s := range ctxt.Syms.Allsym { - if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() { - continue - } - if s.Type != sym.STEXT { + for _, s := range ctxt.Textp2 { + if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) { continue } - for i := range s.R { - r := &s.R[i] - if r.Sym != nil && r.Sym.Type == sym.Sxxx { - r.Sym.Type = sym.SUNDEFEXT + relocs := ctxt.loader.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + if rs := relocs.At2(i).Sym(); rs != 0 { + if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) { + // sanity check + if len(ctxt.loader.Data(rs)) != 0 { + panic("expected no data on undef symbol") + } + su := ctxt.loader.MakeSymbolUpdater(rs) + su.SetType(sym.SUNDEFEXT) + } } } } @@ -388,8 +423,8 @@ func (ctxt *Link) addexport() { return } - for _, exp := range dynexp { - Adddynsym(ctxt, exp) + for _, exp := range ctxt.dynexp2 { + Adddynsym2(ctxt.loader, &ctxt.ErrorReporter, &ctxt.Target, &ctxt.ArchSyms, exp) } for _, lib := range dynlib { adddynlib(ctxt, lib) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index a4b4b60ca1..3f21fc54ef 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -31,7 +31,6 @@ package ld import ( - "bufio" "bytes" "cmd/internal/bio" "cmd/internal/obj" @@ -42,7 +41,6 @@ import ( "cmd/link/internal/loadmacho" "cmd/link/internal/loadpe" "cmd/link/internal/loadxcoff" - "cmd/link/internal/objfile" "cmd/link/internal/sym" "crypto/sha1" "debug/elf" @@ -95,6 +93,128 @@ import ( // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +type LookupFn func(name string, version int) *sym.Symbol + +// ArchSyms holds a number of architecture specific symbols used during +// relocation. Rather than allowing them universal access to all symbols, +// we keep a subset for relocation application. +type ArchSyms struct { + TOC *sym.Symbol + DotTOC []*sym.Symbol // for each version + + GOT *sym.Symbol + PLT *sym.Symbol + GOTPLT *sym.Symbol + + Tlsg *sym.Symbol + Tlsoffset int + + Dynamic *sym.Symbol + DynSym *sym.Symbol + DynStr *sym.Symbol + + // Elf specific + Rel *sym.Symbol + Rela *sym.Symbol + RelPLT *sym.Symbol + RelaPLT *sym.Symbol + + // Darwin symbols + LinkEditGOT *sym.Symbol + LinkEditPLT *sym.Symbol + + // ----- loader.Sym equivalents ----- + + Rel2 loader.Sym + Rela2 loader.Sym + RelPLT2 loader.Sym + RelaPLT2 loader.Sym + + LinkEditGOT2 loader.Sym + LinkEditPLT2 loader.Sym + + TOC2 loader.Sym + DotTOC2 []loader.Sym // for each version + + GOT2 loader.Sym + PLT2 loader.Sym + GOTPLT2 loader.Sym + + Tlsg2 loader.Sym + + Dynamic2 loader.Sym + DynSym2 loader.Sym + DynStr2 loader.Sym +} + +const BeforeLoadlibFull = 1 +const AfterLoadlibFull = 2 + +func (ctxt *Link) mkArchSym(which int, name string, ls *loader.Sym, ss **sym.Symbol) { + if which == BeforeLoadlibFull { + *ls = ctxt.loader.LookupOrCreateSym(name, 0) + } else { + *ss = ctxt.loader.Syms[*ls] + } +} + +func (ctxt *Link) mkArchSymVec(which int, name string, i int, ls []loader.Sym, ss []*sym.Symbol) { + if which == BeforeLoadlibFull { + ls[i] = ctxt.loader.LookupOrCreateSym(name, 0) + } else { + ss[i] = ctxt.loader.Syms[ls[i]] + } +} + +// setArchSyms sets up the ArchSyms structure, and must be called before +// relocations are applied. This function is invoked twice, once prior +// to loadlibfull(), and once after the work of loadlibfull is complete. +func (ctxt *Link) setArchSyms(which int) { + if which != BeforeLoadlibFull && which != AfterLoadlibFull { + panic("internal error") + } + ctxt.mkArchSym(which, ".got", &ctxt.GOT2, &ctxt.GOT) + ctxt.mkArchSym(which, ".plt", &ctxt.PLT2, &ctxt.PLT) + ctxt.mkArchSym(which, ".got.plt", &ctxt.GOTPLT2, &ctxt.GOTPLT) + ctxt.mkArchSym(which, ".dynamic", &ctxt.Dynamic2, &ctxt.Dynamic) + ctxt.mkArchSym(which, ".dynsym", &ctxt.DynSym2, &ctxt.DynSym) + ctxt.mkArchSym(which, ".dynstr", &ctxt.DynStr2, &ctxt.DynStr) + + if ctxt.IsAIX() { + ctxt.mkArchSym(which, "TOC", &ctxt.TOC2, &ctxt.TOC) + + // NB: note the +2 below for DotTOC2 compared to the +1 for + // DocTOC. This is because loadlibfull() creates an additional + // syms version during conversion of loader.Sym symbols to + // *sym.Symbol symbols. Symbols that are assigned this final + // version are not going to have TOC references, so it should + // be ok for them to inherit an invalid .TOC. symbol. + if which == BeforeLoadlibFull { + ctxt.DotTOC2 = make([]loader.Sym, ctxt.Syms.MaxVersion()+2) + } else { + ctxt.DotTOC = make([]*sym.Symbol, ctxt.Syms.MaxVersion()+1) + } + for i := 0; i <= ctxt.Syms.MaxVersion(); i++ { + if i >= 2 && i < sym.SymVerStatic { // these versions are not used currently + continue + } + if ctxt.DotTOC2[i] != 0 { + ctxt.mkArchSymVec(which, ".TOC.", i, ctxt.DotTOC2, ctxt.DotTOC) + } + } + } + if ctxt.IsElf() { + ctxt.mkArchSym(which, ".rel", &ctxt.Rel2, &ctxt.Rel) + ctxt.mkArchSym(which, ".rela", &ctxt.Rela2, &ctxt.Rela) + ctxt.mkArchSym(which, ".rel.plt", &ctxt.RelPLT2, &ctxt.RelPLT) + ctxt.mkArchSym(which, ".rela.plt", &ctxt.RelaPLT2, &ctxt.RelaPLT) + } + if ctxt.IsDarwin() { + ctxt.mkArchSym(which, ".linkedit.got", &ctxt.LinkEditGOT2, &ctxt.LinkEditGOT) + ctxt.mkArchSym(which, ".linkedit.plt", &ctxt.LinkEditPLT2, &ctxt.LinkEditPLT) + } +} + type Arch struct { Funcalign int Maxalign int @@ -108,7 +228,7 @@ type Arch struct { Openbsddynld string Dragonflydynld string Solarisdynld string - Adddynrel func(*Link, *sym.Symbol, *sym.Reloc) bool + Adddynrel func(*Target, *loader.Loader, *ArchSyms, *sym.Symbol, *sym.Reloc) bool Archinit func(*Link) // Archreloc is an arch-specific hook that assists in // relocation processing (invoked by 'relocsym'); it handles @@ -119,7 +239,7 @@ type Arch struct { // value is the appropriately relocated value (to be written back // to the same spot in sym.P) and a boolean indicating // success/failure (a failing value indicates a fatal error). - Archreloc func(link *Link, rel *sym.Reloc, sym *sym.Symbol, + Archreloc func(target *Target, syms *ArchSyms, rel *sym.Reloc, sym *sym.Symbol, offset int64) (relocatedOffset int64, success bool) // Archrelocvariant is a second arch-specific hook used for // relocation processing; it handles relocations where r.Type is @@ -129,7 +249,7 @@ type Arch struct { // relocation applies, and "off" is the contents of the // to-be-relocated data item (from sym.P). Return is an updated // offset value. - Archrelocvariant func(link *Link, rel *sym.Reloc, sym *sym.Symbol, + Archrelocvariant func(target *Target, syms *ArchSyms, rel *sym.Reloc, sym *sym.Symbol, offset int64) (relocatedOffset int64) Trampoline func(*Link, *sym.Reloc, *sym.Symbol) @@ -141,7 +261,7 @@ type Arch struct { Asmb2 func(*Link) Elfreloc1 func(*Link, *sym.Reloc, int64) bool - Elfsetupplt func(*Link) + Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) Gentext func(*Link) Machoreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool PEreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool @@ -188,17 +308,6 @@ func (ctxt *Link) CanUsePlugins() bool { return ctxt.canUsePlugins } -// UseRelro reports whether to make use of "read only relocations" aka -// relro. -func (ctxt *Link) UseRelro() bool { - switch ctxt.BuildMode { - case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin: - return ctxt.IsELF || ctxt.HeadType == objabi.Haix - default: - return ctxt.linkShared || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) - } -} - var ( dynexp []*sym.Symbol dynlib []string @@ -275,14 +384,11 @@ func libinit(ctxt *Link) { Lflag(ctxt, filepath.Join(objabi.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", objabi.GOOS, objabi.GOARCH, suffixsep, suffix))) mayberemoveoutfile() - f, err := os.OpenFile(*flagOutfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) - if err != nil { + + if err := ctxt.Out.Open(*flagOutfile); err != nil { Exitf("cannot create %s: %v", *flagOutfile, err) } - ctxt.Out.w = bufio.NewWriter(f) - ctxt.Out.f = f - if *flagEntrySymbol == "" { switch ctxt.BuildMode { case BuildModeCShared, BuildModeCArchive: @@ -381,17 +487,18 @@ func (ctxt *Link) findLibPath(libname string) string { } func (ctxt *Link) loadlib() { - if *flagNewobj { - var flags uint32 - switch *FlagStrictDups { - case 0: - // nothing to do - case 1, 2: - flags = loader.FlagStrictDups - default: - log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) - } - ctxt.loader = loader.NewLoader(flags) + var flags uint32 + switch *FlagStrictDups { + case 0: + // nothing to do + case 1, 2: + flags = loader.FlagStrictDups + default: + log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) + } + ctxt.loader = loader.NewLoader(flags, elfsetstring) + ctxt.ErrorReporter.SymName = func(s loader.Sym) string { + return ctxt.loader.SymName(s) } ctxt.cgo_export_static = make(map[string]bool) @@ -423,35 +530,33 @@ func (ctxt *Link) loadlib() { loadobjfile(ctxt, lib) } } + // At this point, the Go objects are "preloaded". Not all the symbols are + // added to the symbol table (only defined package symbols are). Looking + // up symbol by name may not get expected result. - if *flagNewobj { - iscgo = ctxt.loader.Lookup("x_cgo_init", 0) != 0 - ctxt.canUsePlugins = ctxt.loader.Lookup("plugin.Open", sym.SymVerABIInternal) != 0 - } else { - iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil - ctxt.canUsePlugins = ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil - } + iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil + ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != nil // We now have enough information to determine the link mode. determineLinkMode(ctxt) - if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) { + if ctxt.LinkMode == LinkExternal && !iscgo && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) { // This indicates a user requested -linkmode=external. // The startup code uses an import of runtime/cgo to decide // whether to initialize the TLS. So give it one. This could // be handled differently but it's an unusual case. - if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil { - if lib.Shlib != "" { - ldshlibsyms(ctxt, lib.Shlib) - } else { - if ctxt.BuildMode == BuildModeShared || ctxt.linkShared { - Exitf("cannot implicitly include runtime/cgo in a shared library") - } - loadobjfile(ctxt, lib) + if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" { + if ctxt.BuildMode == BuildModeShared || ctxt.linkShared { + Exitf("cannot implicitly include runtime/cgo in a shared library") } + loadobjfile(ctxt, lib) } } + // Add non-package symbols and references of externally defined symbols. + ctxt.loader.LoadNonpkgSyms(ctxt.Syms) + + // Load symbols from shared libraries, after all Go object symbols are loaded. for _, lib := range ctxt.Library { if lib.Shlib != "" { if ctxt.Debugvlog > 1 { @@ -461,63 +566,20 @@ func (ctxt *Link) loadlib() { } } - if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 { - if *flagNewobj { - // In newobj mode, we typically create sym.Symbols later therefore - // also set cgo attributes later. However, for internal cgo linking, - // the host object loaders still work with sym.Symbols (for now), - // and they need cgo attributes set to work properly. So process - // them now. - lookup := func(name string, ver int) *sym.Symbol { return ctxt.loader.LookupOrCreate(name, ver, ctxt.Syms) } - for _, d := range ctxt.cgodata { - setCgoAttr(ctxt, lookup, d.file, d.pkg, d.directives) - } - ctxt.cgodata = nil - } - - // Drop all the cgo_import_static declarations. - // Turns out we won't be needing them. - for _, s := range ctxt.Syms.Allsym { - if s.Type == sym.SHOSTOBJ { - // If a symbol was marked both - // cgo_import_static and cgo_import_dynamic, - // then we want to make it cgo_import_dynamic - // now. - if s.Extname() != "" && s.Dynimplib() != "" && !s.Attr.CgoExport() { - s.Type = sym.SDYNIMPORT - } else { - s.Type = 0 - } - } - } - } + // Process cgo directives (has to be done before host object loading). + ctxt.loadcgodirectives() // Conditionally load host objects, or setup for external linking. hostobjs(ctxt) hostlinksetup(ctxt) - if *flagNewobj { - // Add references of externally defined symbols. - ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms) - } - - // Now that we know the link mode, set the dynexp list. - if !*flagNewobj { // set this later in newobj mode - setupdynexp(ctxt) - } - if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 { // If we have any undefined symbols in external // objects, try to read them from the libgcc file. any := false - for _, s := range ctxt.Syms.Allsym { - for i := range s.R { - r := &s.R[i] // Copying sym.Reloc has measurable impact on performance - if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" { - any = true - break - } - } + undefs := ctxt.loader.UndefinedRelocTargets(1) + if len(undefs) > 0 { + any = true } if any { if *flagLibGCC == "" { @@ -561,54 +623,99 @@ func (ctxt *Link) loadlib() { importcycles() - if *flagNewobj { - strictDupMsgCount = ctxt.loader.NStrictDupMsgs() + strictDupMsgCount = ctxt.loader.NStrictDupMsgs() +} + +// genSymsForDynexp constructs a *sym.Symbol version of ctxt.dynexp, +// writing to the global variable 'dynexp'. +func genSymsForDynexp(ctxt *Link) { + dynexp = make([]*sym.Symbol, len(ctxt.dynexp2)) + for i, s := range ctxt.dynexp2 { + dynexp[i] = ctxt.loader.Syms[s] } } -// Set up dynexp list. +// setupdynexp constructs ctxt.dynexp, a list of loader.Sym. func setupdynexp(ctxt *Link) { dynexpMap := ctxt.cgo_export_dynamic if ctxt.LinkMode == LinkExternal { dynexpMap = ctxt.cgo_export_static } - dynexp = make([]*sym.Symbol, 0, len(dynexpMap)) + d := make([]loader.Sym, 0, len(dynexpMap)) for exp := range dynexpMap { - s := ctxt.Syms.Lookup(exp, 0) - dynexp = append(dynexp, s) + s := ctxt.loader.LookupOrCreateSym(exp, 0) + d = append(d, s) + // sanity check + if !ctxt.loader.AttrReachable(s) { + panic("dynexp entry not reachable") + } } - sort.Sort(byName(dynexp)) + sort.Slice(d, func(i, j int) bool { + return ctxt.loader.SymName(d[i]) < ctxt.loader.SymName(d[j]) + }) // Resolve ABI aliases in the list of cgo-exported functions. // This is necessary because we load the ABI0 symbol for all // cgo exports. - for i, s := range dynexp { - if s.Type != sym.SABIALIAS { + for i, s := range d { + if ctxt.loader.SymType(s) != sym.SABIALIAS { continue } - t := resolveABIAlias(s) - t.Attr |= s.Attr - t.SetExtname(s.Extname()) - dynexp[i] = t + t := ctxt.loader.ResolveABIAlias(s) + ctxt.loader.CopyAttributes(s, t) + ctxt.loader.SetSymExtname(t, ctxt.loader.SymExtname(s)) + d[i] = t } + ctxt.dynexp2 = d ctxt.cgo_export_static = nil ctxt.cgo_export_dynamic = nil } +// loadcgodirectives reads the previously discovered cgo directives, creating +// symbols in preparation for host object loading or use later in the link. +func (ctxt *Link) loadcgodirectives() { + l := ctxt.loader + hostObjSyms := make(map[loader.Sym]struct{}) + for _, d := range ctxt.cgodata { + setCgoAttr(ctxt, ctxt.loader.LookupOrCreateSym, d.file, d.pkg, d.directives, hostObjSyms) + } + ctxt.cgodata = nil + + if ctxt.LinkMode == LinkInternal { + // Drop all the cgo_import_static declarations. + // Turns out we won't be needing them. + for symIdx := range hostObjSyms { + if l.SymType(symIdx) == sym.SHOSTOBJ { + // If a symbol was marked both + // cgo_import_static and cgo_import_dynamic, + // then we want to make it cgo_import_dynamic + // now. + su := l.MakeSymbolUpdater(symIdx) + if l.SymExtname(symIdx) != "" && l.SymDynimplib(symIdx) != "" && !(l.AttrCgoExportStatic(symIdx) || l.AttrCgoExportDynamic(symIdx)) { + su.SetType(sym.SDYNIMPORT) + } else { + su.SetType(0) + } + } + } + } +} + // Set up flags and special symbols depending on the platform build mode. +// This version works with loader.Loader. func (ctxt *Link) linksetup() { switch ctxt.BuildMode { case BuildModeCShared, BuildModePlugin: - s := ctxt.Syms.Lookup("runtime.islibrary", 0) - s.Type = sym.SNOPTRDATA - s.Attr |= sym.AttrDuplicateOK - s.AddUint8(1) + symIdx := ctxt.loader.LookupOrCreateSym("runtime.islibrary", 0) + sb := ctxt.loader.MakeSymbolUpdater(symIdx) + sb.SetType(sym.SNOPTRDATA) + sb.AddUint8(1) case BuildModeCArchive: - s := ctxt.Syms.Lookup("runtime.isarchive", 0) - s.Type = sym.SNOPTRDATA - s.Attr |= sym.AttrDuplicateOK - s.AddUint8(1) + symIdx := ctxt.loader.LookupOrCreateSym("runtime.isarchive", 0) + sb := ctxt.loader.MakeSymbolUpdater(symIdx) + sb.SetType(sym.SNOPTRDATA) + sb.AddUint8(1) } // Recalculate pe parameters now that we have ctxt.LinkMode set. @@ -637,69 +744,77 @@ func (ctxt *Link) linksetup() { } if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" { - toc := ctxt.Syms.Lookup(".TOC.", 0) - toc.Type = sym.SDYNIMPORT + toc := ctxt.loader.LookupOrCreateSym(".TOC.", 0) + sb := ctxt.loader.MakeSymbolUpdater(toc) + sb.SetType(sym.SDYNIMPORT) } // The Android Q linker started to complain about underalignment of the our TLS - // section. We don't actually use the section on android, so dont't + // section. We don't actually use the section on android, so don't // generate it. if objabi.GOOS != "android" { - tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0) + tlsg := ctxt.loader.LookupOrCreateSym("runtime.tlsg", 0) + sb := ctxt.loader.MakeSymbolUpdater(tlsg) // runtime.tlsg is used for external linking on platforms that do not define // a variable to hold g in assembly (currently only intel). - if tlsg.Type == 0 { - tlsg.Type = sym.STLSBSS - tlsg.Size = int64(ctxt.Arch.PtrSize) - } else if tlsg.Type != sym.SDYNIMPORT { - Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type) + if sb.Type() == 0 { + sb.SetType(sym.STLSBSS) + sb.SetSize(int64(ctxt.Arch.PtrSize)) + } else if sb.Type() != sym.SDYNIMPORT { + Errorf(nil, "runtime declared tlsg variable %v", sb.Type()) } - tlsg.Attr |= sym.AttrReachable - ctxt.Tlsg = tlsg + ctxt.loader.SetAttrReachable(tlsg, true) + ctxt.Tlsg2 = tlsg } - var moduledata *sym.Symbol + var moduledata loader.Sym + var mdsb *loader.SymbolBuilder if ctxt.BuildMode == BuildModePlugin { - moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0) - moduledata.Attr |= sym.AttrLocal + moduledata = ctxt.loader.LookupOrCreateSym("local.pluginmoduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) + ctxt.loader.SetAttrLocal(moduledata, true) } else { - moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0) + moduledata = ctxt.loader.LookupOrCreateSym("runtime.firstmoduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) } - if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT { + if mdsb.Type() != 0 && mdsb.Type() != sym.SDYNIMPORT { // If the module (toolchain-speak for "executable or shared // library") we are linking contains the runtime package, it // will define the runtime.firstmoduledata symbol and we // truncate it back to 0 bytes so we can define its entire // contents in symtab.go:symtab(). - moduledata.Size = 0 + mdsb.SetSize(0) // In addition, on ARM, the runtime depends on the linker // recording the value of GOARM. if ctxt.Arch.Family == sys.ARM { - s := ctxt.Syms.Lookup("runtime.goarm", 0) - s.Type = sym.SDATA - s.Size = 0 - s.AddUint8(uint8(objabi.GOARM)) + goarm := ctxt.loader.LookupOrCreateSym("runtime.goarm", 0) + sb := ctxt.loader.MakeSymbolUpdater(goarm) + sb.SetType(sym.SDATA) + sb.SetSize(0) + sb.AddUint8(uint8(objabi.GOARM)) } if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { - s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0) - s.Type = sym.SDATA - s.Size = 0 - s.AddUint8(1) + fpe := ctxt.loader.LookupOrCreateSym("runtime.framepointer_enabled", 0) + sb := ctxt.loader.MakeSymbolUpdater(fpe) + sb.SetType(sym.SNOPTRDATA) + sb.SetSize(0) + sb.AddUint8(1) } } else { // If OTOH the module does not contain the runtime package, // create a local symbol for the moduledata. - moduledata = ctxt.Syms.Lookup("local.moduledata", 0) - moduledata.Attr |= sym.AttrLocal + moduledata = ctxt.loader.LookupOrCreateSym("local.moduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) + ctxt.loader.SetAttrLocal(moduledata, true) } // In all cases way we mark the moduledata as noptrdata to hide it from // the GC. - moduledata.Type = sym.SNOPTRDATA - moduledata.Attr |= sym.AttrReachable - ctxt.Moduledata = moduledata + mdsb.SetType(sym.SNOPTRDATA) + ctxt.loader.SetAttrReachable(moduledata, true) + ctxt.Moduledata2 = moduledata // If package versioning is required, generate a hash of the // packages used in the link. @@ -713,11 +828,25 @@ func (ctxt *Link) linksetup() { if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows { if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() { - got := ctxt.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0) - got.Type = sym.SDYNIMPORT - got.Attr |= sym.AttrReachable + got := ctxt.loader.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0) + sb := ctxt.loader.MakeSymbolUpdater(got) + sb.SetType(sym.SDYNIMPORT) + ctxt.loader.SetAttrReachable(got, true) } } + + // DWARF-gen and other phases require that the unit Textp2 slices + // be populated, so that it can walk the functions in each unit. + // Call into the loader to do this (requires that we collect the + // set of internal libraries first). NB: might be simpler if we + // moved isRuntimeDepPkg to cmd/internal and then did the test in + // loader.AssignTextSymbolOrder. + ctxt.Library = postorder(ctxt.Library) + intlibs := []bool{} + for _, lib := range ctxt.Library { + intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg)) + } + ctxt.Textp2 = ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs, ctxt.Textp2) } // mangleTypeSym shortens the names of symbols that represent Go types @@ -738,10 +867,29 @@ func (ctxt *Link) mangleTypeSym() { return } - for _, s := range ctxt.Syms.Allsym { - newName := typeSymbolMangle(s.Name) - if newName != s.Name { - ctxt.Syms.Rename(s.Name, newName, int(s.Version), ctxt.Reachparent) + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + name := ldr.SymName(s) + newName := typeSymbolMangle(name) + if newName != name { + ldr.SetSymExtname(s, newName) + + // When linking against a shared library, the Go object file may + // have reference to the original symbol name whereas the shared + // library provides a symbol with the mangled name. We need to + // copy the payload of mangled to original. + // XXX maybe there is a better way to do this. + dup := ldr.Lookup(newName, ldr.SymVersion(s)) + if dup != 0 { + st := ldr.SymType(s) + dt := ldr.SymType(dup) + if st == sym.Sxxx && dt != sym.Sxxx { + ldr.CopySym(dup, s) + } + } } } } @@ -1051,25 +1199,21 @@ func hostlinksetup(ctxt *Link) { *flagTmpdir = dir ownTmpDir = true AtExit(func() { - ctxt.Out.f.Close() + ctxt.Out.Close() os.RemoveAll(*flagTmpdir) }) } // change our output to temporary object file - ctxt.Out.f.Close() + if err := ctxt.Out.Close(); err != nil { + Exitf("error closing output file") + } mayberemoveoutfile() p := filepath.Join(*flagTmpdir, "go.o") - var err error - f, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) - if err != nil { + if err := ctxt.Out.Open(p); err != nil { Exitf("cannot create %s: %v", p, err) } - - ctxt.Out.w = bufio.NewWriter(f) - ctxt.Out.f = f - ctxt.Out.off = 0 } // hostobjCopy creates a copy of the object files in hostobj in a @@ -1153,11 +1297,9 @@ func (ctxt *Link) archive() { // Force the buffer to flush here so that external // tools will see a complete file. - ctxt.Out.Flush() - if err := ctxt.Out.f.Close(); err != nil { - Exitf("close: %v", err) + if err := ctxt.Out.Close(); err != nil { + Exitf("error closing %v", *flagOutfile) } - ctxt.Out.f = nil argv := []string{*flagExtar, "-q", "-c", "-s"} if ctxt.HeadType == objabi.Haix { @@ -1702,107 +1844,55 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) if magic == 0x7f454c46 { // \x7F E L F - if *flagNewobj { - ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { - textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags) - if err != nil { - Errorf(nil, "%v", err) - return - } - ehdr.flags = flags - ctxt.Textp = append(ctxt.Textp, textp...) - } - return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) - } else { - ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { - textp, flags, err := loadelf.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags) - if err != nil { - Errorf(nil, "%v", err) - return - } - ehdr.flags = flags - ctxt.Textp = append(ctxt.Textp, textp...) + ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn, ehdr.flags) + if err != nil { + Errorf(nil, "%v", err) + return } - return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) + ehdr.flags = flags + ctxt.Textp2 = append(ctxt.Textp2, textp...) } + return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) } if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { - if *flagNewobj { - ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { - textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn) - if err != nil { - Errorf(nil, "%v", err) - return - } - ctxt.Textp = append(ctxt.Textp, textp...) - } - return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) - } else { - ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { - textp, err := loadmacho.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) - if err != nil { - Errorf(nil, "%v", err) - return - } - ctxt.Textp = append(ctxt.Textp, textp...) + ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return } - return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) + ctxt.Textp2 = append(ctxt.Textp2, textp...) } + return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) } if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 { - if *flagNewobj { - ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { - textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn) - if err != nil { - Errorf(nil, "%v", err) - return - } - if rsrc != nil { - setpersrc(ctxt, rsrc) - } - ctxt.Textp = append(ctxt.Textp, textp...) + ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return } - return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) - } else { - ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { - textp, rsrc, err := loadpe.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) - if err != nil { - Errorf(nil, "%v", err) - return - } - if rsrc != nil { - setpersrc(ctxt, rsrc) - } - ctxt.Textp = append(ctxt.Textp, textp...) + if rsrc != 0 { + setpersrc(ctxt, rsrc) } - return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) + ctxt.Textp2 = append(ctxt.Textp2, textp...) } + return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) } if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) { - if *flagNewobj { - ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { - textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn) - if err != nil { - Errorf(nil, "%v", err) - return - } - ctxt.Textp = append(ctxt.Textp, textp...) - } - return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) - } else { - ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { - textp, err := loadxcoff.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) - if err != nil { - Errorf(nil, "%v", err) - return - } - ctxt.Textp = append(ctxt.Textp, textp...) + ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return } - return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) + ctxt.Textp2 = append(ctxt.Textp2, textp...) } + return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) } /* check the header */ @@ -1887,24 +1977,7 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n f.MustSeek(import1, 0) - flags := 0 - switch *FlagStrictDups { - case 0: - break - case 1: - flags = objfile.StrictDupsWarnFlag - case 2: - flags = objfile.StrictDupsErrFlag - default: - log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) - } - var c int - if *flagNewobj { - ctxt.loader.Preload(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags) - } else { - c = objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags) - } - strictDupMsgCount += c + ctxt.loader.Preload(ctxt.Syms, f, lib, unit, eof-f.Offset(), 0) addImports(ctxt, lib, pn) return nil } @@ -2045,7 +2118,7 @@ func ldshlibsyms(ctxt *Link, shlib string) { Errorf(nil, "cannot read symbols from shared library: %s", libpath) return } - gcdataLocations := make(map[uint64]*sym.Symbol) + gcdataLocations := make(map[uint64]loader.Sym) for _, elfsym := range syms { if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION { continue @@ -2058,37 +2131,44 @@ func ldshlibsyms(ctxt *Link, shlib string) { ver = sym.SymVerABIInternal } - var lsym *sym.Symbol - if *flagNewobj { - i := ctxt.loader.AddExtSym(elfsym.Name, ver) - if i == 0 { - continue - } - lsym = ctxt.Syms.Newsym(elfsym.Name, ver) - ctxt.loader.Syms[i] = lsym - } else { - lsym = ctxt.Syms.Lookup(elfsym.Name, ver) - } - // Because loadlib above loads all .a files before loading any shared - // libraries, any non-dynimport symbols we find that duplicate symbols - // already loaded should be ignored (the symbols from the .a files - // "win"). - if lsym.Type != 0 && lsym.Type != sym.SDYNIMPORT { + l := ctxt.loader + s := l.LookupOrCreateSym(elfsym.Name, ver) + + // Because loadlib above loads all .a files before loading + // any shared libraries, any non-dynimport symbols we find + // that duplicate symbols already loaded should be ignored + // (the symbols from the .a files "win"). + if l.SymType(s) != 0 && l.SymType(s) != sym.SDYNIMPORT { continue } - lsym.Type = sym.SDYNIMPORT - lsym.SetElfType(elf.ST_TYPE(elfsym.Info)) - lsym.Size = int64(elfsym.Size) + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SDYNIMPORT) + l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info)) + su.SetSize(int64(elfsym.Size)) if elfsym.Section != elf.SHN_UNDEF { + // If it's not undefined, mark the symbol as reachable + // so as to protect it from dead code elimination, + // even if there aren't any explicit references to it. + // Under the previous sym.Symbol based regime this + // wasn't necessary, but for the loader-based deadcode + // it is definitely needed. + // + // FIXME: have a more general/flexible mechanism for this? + // + l.SetAttrReachable(s, true) + // Set .File for the library that actually defines the symbol. - lsym.File = libpath + l.SetSymFile(s, libpath) + // The decodetype_* functions in decodetype.go need access to // the type data. - if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") { - lsym.P = readelfsymboldata(ctxt, f, &elfsym) - gcdataLocations[elfsym.Value+2*uint64(ctxt.Arch.PtrSize)+8+1*uint64(ctxt.Arch.PtrSize)] = lsym + sname := l.SymName(s) + if strings.HasPrefix(sname, "type.") && !strings.HasPrefix(sname, "type..") { + su.SetData(readelfsymboldata(ctxt, f, &elfsym)) + gcdataLocations[elfsym.Value+2*uint64(ctxt.Arch.PtrSize)+8+1*uint64(ctxt.Arch.PtrSize)] = s } } + // For function symbols, we don't know what ABI is // available, so alias it under both ABIs. // @@ -2097,25 +2177,15 @@ func ldshlibsyms(ctxt *Link, shlib string) { // mangle Go function names in the .so to include the // ABI. if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 { - var alias *sym.Symbol - if *flagNewobj { - i := ctxt.loader.AddExtSym(elfsym.Name, sym.SymVerABIInternal) - if i == 0 { - continue - } - alias = ctxt.Syms.Newsym(elfsym.Name, sym.SymVerABIInternal) - ctxt.loader.Syms[i] = alias - } else { - alias = ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal) - } - if alias.Type != 0 { + alias := ctxt.loader.LookupOrCreateSym(elfsym.Name, sym.SymVerABIInternal) + if l.SymType(alias) != 0 { continue } - alias.Type = sym.SABIALIAS - alias.R = []sym.Reloc{{Sym: lsym}} + su := l.MakeSymbolUpdater(alias) + su.SetType(sym.SABIALIAS) + su.AddReloc(loader.Reloc{Sym: s}) } } - gcdataAddresses := make(map[*sym.Symbol]uint64) if ctxt.Arch.Family == sys.ARM64 { for _, sect := range f.Sections { if sect.Type == elf.SHT_RELA { @@ -2133,15 +2203,12 @@ func ldshlibsyms(ctxt *Link, shlib string) { if t != elf.R_AARCH64_RELATIVE { continue } - if lsym, ok := gcdataLocations[rela.Off]; ok { - gcdataAddresses[lsym] = uint64(rela.Addend) - } } } } } - ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdataAddresses: gcdataAddresses}) + ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f}) } func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section { @@ -2155,16 +2222,11 @@ func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Sec } type chain struct { - sym *sym.Symbol + sym loader.Sym up *chain limit int // limit on entry to sym } -var morestack *sym.Symbol - -// TODO: Record enough information in new object files to -// allow stack checks here. - func haslinkregister(ctxt *Link) bool { return ctxt.FixedFrameSize() != 0 } @@ -2176,10 +2238,23 @@ func callsize(ctxt *Link) int { return ctxt.Arch.RegSize } -func (ctxt *Link) dostkcheck() { - var ch chain +type stkChk struct { + ldr *loader.Loader + ctxt *Link + morestack loader.Sym + done loader.Bitmap +} - morestack = ctxt.Syms.Lookup("runtime.morestack", 0) +// Walk the call tree and check that there is always enough stack space +// for the call frames, especially for a chain of nosplit functions. +func (ctxt *Link) dostkcheck() { + ldr := ctxt.loader + sc := stkChk{ + ldr: ldr, + ctxt: ctxt, + morestack: ldr.Lookup("runtime.morestack", 0), + done: loader.MakeBitmap(ldr.NSym()), + } // Every splitting function ensures that there are at least StackLimit // bytes available below SP when the splitting prologue finishes. @@ -2188,8 +2263,7 @@ func (ctxt *Link) dostkcheck() { // Check that every function behaves correctly with this amount // of stack, following direct calls in order to piece together chains // of non-splitting functions. - ch.up = nil - + var ch chain ch.limit = objabi.StackLimit - callsize(ctxt) if objabi.GOARCH == "arm64" { // need extra 8 bytes below SP to save FP @@ -2198,118 +2272,115 @@ func (ctxt *Link) dostkcheck() { // Check every function, but do the nosplit functions in a first pass, // to make the printed failure chains as short as possible. - for _, s := range ctxt.Textp { - // runtime.racesymbolizethunk is called from gcc-compiled C - // code running on the operating system thread stack. - // It uses more than the usual amount of stack but that's okay. - if s.Name == "runtime.racesymbolizethunk" { - continue - } - - if s.Attr.NoSplit() { + for _, s := range ctxt.Textp2 { + if ldr.IsNoSplit(s) { ch.sym = s - stkcheck(ctxt, &ch, 0) + sc.check(&ch, 0) } } - for _, s := range ctxt.Textp { - if !s.Attr.NoSplit() { + for _, s := range ctxt.Textp2 { + if !ldr.IsNoSplit(s) { ch.sym = s - stkcheck(ctxt, &ch, 0) + sc.check(&ch, 0) } } } -func stkcheck(ctxt *Link, up *chain, depth int) int { +func (sc *stkChk) check(up *chain, depth int) int { limit := up.limit s := up.sym + ldr := sc.ldr + ctxt := sc.ctxt // Don't duplicate work: only need to consider each // function at top of safe zone once. top := limit == objabi.StackLimit-callsize(ctxt) if top { - if s.Attr.StackCheck() { + if sc.done.Has(s) { return 0 } - s.Attr |= sym.AttrStackCheck + sc.done.Set(s) } if depth > 500 { - Errorf(s, "nosplit stack check too deep") - stkbroke(ctxt, up, 0) + sc.ctxt.Errorf(s, "nosplit stack check too deep") + sc.broke(up, 0) return -1 } - if s.Attr.External() || s.FuncInfo == nil { + if ldr.AttrExternal(s) { // external function. // should never be called directly. // onlyctxt.Diagnose the direct caller. // TODO(mwhudson): actually think about this. // TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack. // See the trampolines in src/runtime/sys_darwin_$ARCH.go. - if depth == 1 && s.Type != sym.SXREF && !ctxt.DynlinkingGo() && - ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin { - //Errorf(s, "call to external function") - } + //if depth == 1 && ldr.SymType(s) != sym.SXREF && !ctxt.DynlinkingGo() && + // ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin { + // Errorf(s, "call to external function") + //} + return -1 + } + info := ldr.FuncInfo(s) + if !info.Valid() { // external function. see above. return -1 } if limit < 0 { - stkbroke(ctxt, up, limit) + sc.broke(up, limit) return -1 } // morestack looks like it calls functions, // but it switches the stack pointer first. - if s == morestack { + if s == sc.morestack { return 0 } var ch chain ch.up = up - if !s.Attr.NoSplit() { + if !ldr.IsNoSplit(s) { // Ensure we have enough stack to call morestack. ch.limit = limit - callsize(ctxt) - ch.sym = morestack - if stkcheck(ctxt, &ch, depth+1) < 0 { + ch.sym = sc.morestack + if sc.check(&ch, depth+1) < 0 { return -1 } if !top { return 0 } // Raise limit to allow frame. - locals := int32(0) - if s.FuncInfo != nil { - locals = s.FuncInfo.Locals - } + locals := info.Locals() limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize()) } // Walk through sp adjustments in function, consuming relocs. - ri := 0 - - endr := len(s.R) + relocs := ldr.Relocs(s) var ch1 chain pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) - var r *sym.Reloc - for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() { + for pcsp.Init(info.Pcsp()); !pcsp.Done; pcsp.Next() { // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). // Check stack size in effect for this span. if int32(limit)-pcsp.Value < 0 { - stkbroke(ctxt, up, int(int32(limit)-pcsp.Value)) + sc.broke(up, int(int32(limit)-pcsp.Value)) return -1 } // Process calls in this span. - for ; ri < endr && uint32(s.R[ri].Off) < pcsp.NextPC; ri++ { - r = &s.R[ri] + for i := 0; i < relocs.Count(); i++ { + r := relocs.At2(i) + if uint32(r.Off()) >= pcsp.NextPC { + break + } + t := r.Type() switch { - case r.Type.IsDirectCall(): + case t.IsDirectCall(): ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt))) - ch.sym = r.Sym - if stkcheck(ctxt, &ch, depth+1) < 0 { + ch.sym = r.Sym() + if sc.check(&ch, depth+1) < 0 { return -1 } @@ -2317,13 +2388,13 @@ func stkcheck(ctxt *Link, up *chain, depth int) int { // so we have to make sure it can call morestack. // Arrange the data structures to report both calls, so that // if there is an error, stkprint shows all the steps involved. - case r.Type == objabi.R_CALLIND: + case t == objabi.R_CALLIND: ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt))) - ch.sym = nil + ch.sym = 0 ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue ch1.up = &ch - ch1.sym = morestack - if stkcheck(ctxt, &ch1, depth+2) < 0 { + ch1.sym = sc.morestack + if sc.check(&ch1, depth+2) < 0 { return -1 } } @@ -2333,17 +2404,18 @@ func stkcheck(ctxt *Link, up *chain, depth int) int { return 0 } -func stkbroke(ctxt *Link, ch *chain, limit int) { - Errorf(ch.sym, "nosplit stack overflow") - stkprint(ctxt, ch, limit) +func (sc *stkChk) broke(ch *chain, limit int) { + sc.ctxt.Errorf(ch.sym, "nosplit stack overflow") + sc.print(ch, limit) } -func stkprint(ctxt *Link, ch *chain, limit int) { +func (sc *stkChk) print(ch *chain, limit int) { + ldr := sc.ldr + ctxt := sc.ctxt var name string - - if ch.sym != nil { - name = ch.sym.Name - if ch.sym.Attr.NoSplit() { + if ch.sym != 0 { + name = ldr.SymName(ch.sym) + if ldr.IsNoSplit(ch.sym) { name += " (nosplit)" } } else { @@ -2351,14 +2423,14 @@ func stkprint(ctxt *Link, ch *chain, limit int) { } if ch.up == nil { - // top of chain. ch->sym != nil. - if ch.sym.Attr.NoSplit() { + // top of chain. ch.sym != 0. + if ldr.IsNoSplit(ch.sym) { fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name) } else { fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name) } } else { - stkprint(ctxt, ch.up, ch.limit+callsize(ctxt)) + sc.print(ch.up, ch.limit+callsize(ctxt)) if !haslinkregister(ctxt) { fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name) } @@ -2619,7 +2691,7 @@ func (ctxt *Link) undef() { for _, s := range ctxt.Textp { undefsym(ctxt, s) } - for _, s := range datap { + for _, s := range ctxt.datap { undefsym(ctxt, s) } if nerrors > 0 { @@ -2632,16 +2704,17 @@ func (ctxt *Link) callgraph() { return } - var i int - var r *sym.Reloc - for _, s := range ctxt.Textp { - for i = 0; i < len(s.R); i++ { - r = &s.R[i] - if r.Sym == nil { + ldr := ctxt.loader + for _, s := range ctxt.Textp2 { + relocs := ldr.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At2(i) + rs := r.Sym() + if rs == 0 { continue } - if r.Type.IsDirectCall() && r.Sym.Type == sym.STEXT { - ctxt.Logf("%s calls %s\n", s.Name, r.Sym.Name) + if r.Type().IsDirectCall() && (ldr.SymType(rs) == sym.STEXT || ldr.SymType(rs) == sym.SABIALIAS) { + ctxt.Logf("%s calls %s\n", ldr.SymName(s), ldr.SymName(rs)) } } } @@ -2703,40 +2776,30 @@ func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library } func (ctxt *Link) loadlibfull() { + // Load full symbol contents, resolve indexed references. ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms) - // Pull the symbols out. - ctxt.loader.ExtractSymbols(ctxt.Syms) - - // Load cgo directives. - for _, d := range ctxt.cgodata { - setCgoAttr(ctxt, ctxt.Syms.Lookup, d.file, d.pkg, d.directives) + // Convert ctxt.Moduledata2 to ctxt.Moduledata, etc + if ctxt.Moduledata2 != 0 { + ctxt.Moduledata = ctxt.loader.Syms[ctxt.Moduledata2] + ctxt.Tlsg = ctxt.loader.Syms[ctxt.Tlsg2] } - setupdynexp(ctxt) + // Pull the symbols out. + ctxt.loader.ExtractSymbols(ctxt.Syms, ctxt.Reachparent) + ctxt.lookup = ctxt.Syms.ROLookup - // Populate ctxt.Reachparent if appropriate. - if ctxt.Reachparent != nil { - for i := 0; i < len(ctxt.loader.Reachparent); i++ { - p := ctxt.loader.Reachparent[i] - if p == 0 { - continue - } - if p == loader.Sym(i) { - panic("self-cycle in reachparent") - } - sym := ctxt.loader.Syms[i] - psym := ctxt.loader.Syms[p] - ctxt.Reachparent[sym] = psym - } - } + // Recreate dynexp using *sym.Symbol instead of loader.Sym + genSymsForDynexp(ctxt) - // Drop the reference. - ctxt.loader = nil + // Drop the cgodata reference. ctxt.cgodata = nil addToTextp(ctxt) + + // Set special global symbols. + ctxt.setArchSyms(AfterLoadlibFull) } func (ctxt *Link) dumpsyms() { diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index df3845fac3..24866d8e8c 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -32,7 +32,6 @@ package ld import ( "bufio" - "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/loader" @@ -42,67 +41,61 @@ import ( ) type Shlib struct { - Path string - Hash []byte - Deps []string - File *elf.File - gcdataAddresses map[*sym.Symbol]uint64 + Path string + Hash []byte + Deps []string + File *elf.File } // Link holds the context for writing object code from a compiler // or for reading that input into the linker. type Link struct { - Out *OutBuf + Target + ErrorReporter + ArchSyms + + outSem chan int // limits the number of output writers + Out *OutBuf Syms *sym.Symbols - Arch *sys.Arch Debugvlog int Bso *bufio.Writer Loaded bool // set after all inputs have been loaded as symbols - IsELF bool - HeadType objabi.HeadType - - linkShared bool // link against installed Go shared libraries - LinkMode LinkMode - BuildMode BuildMode - canUsePlugins bool // initialized when Loaded is set to true compressDWARF bool - Tlsg *sym.Symbol + Tlsg2 loader.Sym Libdir []string Library []*sym.Library LibraryByPkg map[string]*sym.Library Shlibs []Shlib - Tlsoffset int Textp []*sym.Symbol + Textp2 []loader.Sym Filesyms []*sym.Symbol Moduledata *sym.Symbol + Moduledata2 loader.Sym PackageFile map[string]string PackageShlib map[string]string tramps []*sym.Symbol // trampolines - // unresolvedSymSet is a set of erroneous unresolved references. - // Used to avoid duplicated error messages. - unresolvedSymSet map[unresolvedSymKey]bool - // Used to implement field tracking. Reachparent map[*sym.Symbol]*sym.Symbol compUnits []*sym.CompilationUnit // DWARF compilation units runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen. - relocbuf []byte // temporary buffer for applying relocations - loader *loader.Loader cgodata []cgodata // cgo directives to load, three strings are args for loadcgo cgo_export_static map[string]bool cgo_export_dynamic map[string]bool + + datap []*sym.Symbol + dynexp2 []loader.Sym } type cgodata struct { @@ -111,48 +104,6 @@ type cgodata struct { directives [][]string } -type unresolvedSymKey struct { - from *sym.Symbol // Symbol that referenced unresolved "to" - to *sym.Symbol // Unresolved symbol referenced by "from" -} - -// ErrorUnresolved prints unresolved symbol error for r.Sym that is referenced from s. -func (ctxt *Link) ErrorUnresolved(s *sym.Symbol, r *sym.Reloc) { - if ctxt.unresolvedSymSet == nil { - ctxt.unresolvedSymSet = make(map[unresolvedSymKey]bool) - } - - k := unresolvedSymKey{from: s, to: r.Sym} - if !ctxt.unresolvedSymSet[k] { - ctxt.unresolvedSymSet[k] = true - - // Try to find symbol under another ABI. - var reqABI, haveABI obj.ABI - haveABI = ^obj.ABI(0) - reqABI, ok := sym.VersionToABI(int(r.Sym.Version)) - if ok { - for abi := obj.ABI(0); abi < obj.ABICount; abi++ { - v := sym.ABIToVersion(abi) - if v == -1 { - continue - } - if rs := ctxt.Syms.ROLookup(r.Sym.Name, v); rs != nil && rs.Type != sym.Sxxx && rs.Type != sym.SXREF { - haveABI = abi - } - } - } - - // Give a special error message for main symbol (see #24809). - if r.Sym.Name == "main.main" { - Errorf(s, "function main is undeclared in the main package") - } else if haveABI != ^obj.ABI(0) { - Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", r.Sym.Name, reqABI, haveABI) - } else { - Errorf(s, "relocation target %s not defined", r.Sym.Name) - } - } -} - // The smallest possible offset from the hardware stack pointer to a local // variable on the stack. Architectures that use a link register save its value // on the stack in the function prologue and so always have a pointer between diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index e50eddd96c..0f36cd0cbe 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -428,42 +428,49 @@ func (ctxt *Link) domacho() { } // empirically, string table must begin with " \x00". - s := ctxt.Syms.Lookup(".machosymstr", 0) - - s.Type = sym.SMACHOSYMSTR - s.Attr |= sym.AttrReachable - s.AddUint8(' ') - s.AddUint8('\x00') - - s = ctxt.Syms.Lookup(".machosymtab", 0) - s.Type = sym.SMACHOSYMTAB - s.Attr |= sym.AttrReachable - - if ctxt.LinkMode != LinkExternal { - s := ctxt.Syms.Lookup(".plt", 0) // will be __symbol_stub - s.Type = sym.SMACHOPLT - s.Attr |= sym.AttrReachable - - s = ctxt.Syms.Lookup(".got", 0) // will be __nl_symbol_ptr - s.Type = sym.SMACHOGOT - s.Attr |= sym.AttrReachable - s.Align = 4 - - s = ctxt.Syms.Lookup(".linkedit.plt", 0) // indirect table for .plt - s.Type = sym.SMACHOINDIRECTPLT - s.Attr |= sym.AttrReachable - - s = ctxt.Syms.Lookup(".linkedit.got", 0) // indirect table for .got - s.Type = sym.SMACHOINDIRECTGOT - s.Attr |= sym.AttrReachable + s := ctxt.loader.LookupOrCreateSym(".machosymstr", 0) + sb := ctxt.loader.MakeSymbolUpdater(s) + + sb.SetType(sym.SMACHOSYMSTR) + sb.SetReachable(true) + sb.AddUint8(' ') + sb.AddUint8('\x00') + + s = ctxt.loader.LookupOrCreateSym(".machosymtab", 0) + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOSYMTAB) + sb.SetReachable(true) + + if ctxt.IsInternal() { + s = ctxt.loader.LookupOrCreateSym(".plt", 0) // will be __symbol_stub + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOPLT) + sb.SetReachable(true) + + s = ctxt.loader.LookupOrCreateSym(".got", 0) // will be __nl_symbol_ptr + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOGOT) + sb.SetReachable(true) + sb.SetAlign(4) + + s = ctxt.loader.LookupOrCreateSym(".linkedit.plt", 0) // indirect table for .plt + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOINDIRECTPLT) + sb.SetReachable(true) + + s = ctxt.loader.LookupOrCreateSym(".linkedit.got", 0) // indirect table for .got + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOINDIRECTGOT) + sb.SetReachable(true) } // Add a dummy symbol that will become the __asm marker section. - if ctxt.LinkMode == LinkExternal { - s := ctxt.Syms.Lookup(".llvmasm", 0) - s.Type = sym.SMACHO - s.Attr |= sym.AttrReachable - s.AddUint8(0) + if ctxt.IsExternal() { + s = ctxt.loader.LookupOrCreateSym(".llvmasm", 0) + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHO) + sb.SetReachable(true) + sb.AddUint8(0) } } @@ -1053,10 +1060,10 @@ func Machoemitreloc(ctxt *Link) { machorelocsect(ctxt, Segtext.Sections[0], ctxt.Textp) for _, sect := range Segtext.Sections[1:] { - machorelocsect(ctxt, sect, datap) + machorelocsect(ctxt, sect, ctxt.datap) } for _, sect := range Segdata.Sections { - machorelocsect(ctxt, sect, datap) + machorelocsect(ctxt, sect, ctxt.datap) } for _, sect := range Segdwarf.Sections { machorelocsect(ctxt, sect, dwarfp) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 9f9395b757..3772c5090c 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -34,10 +34,12 @@ import ( "bufio" "cmd/internal/objabi" "cmd/internal/sys" + "cmd/link/internal/benchmark" "cmd/link/internal/sym" "flag" "log" "os" + "os/exec" "runtime" "runtime/pprof" "strings" @@ -87,8 +89,6 @@ var ( flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines") FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).") - flagNewobj = flag.Bool("newobj", false, "use new object file format") - FlagRound = flag.Int("R", -1, "set address rounding `quantum`") FlagTextAddr = flag.Int64("T", -1, "set text segment `address`") flagEntrySymbol = flag.String("E", "", "set `entry` symbol name") @@ -96,6 +96,11 @@ var ( cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") memprofile = flag.String("memprofile", "", "write memory profile to `file`") memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`") + + benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking") + benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof") + + flagGo115Newobj = flag.Bool("go115newobj", true, "use new object file format") ) // Main is the main entry point for the linker code. @@ -135,6 +140,10 @@ func Main(arch *sys.Arch, theArch Arch) { objabi.Flagparse(usage) + if !*flagGo115Newobj { + oldlink() + } + switch *flagHeadType { case "": case "windowsgui": @@ -170,13 +179,29 @@ func Main(arch *sys.Arch, theArch Arch) { interpreter = *flagInterpreter + // enable benchmarking + var bench *benchmark.Metrics + if len(*benchmarkFlag) != 0 { + if *benchmarkFlag == "mem" { + bench = benchmark.New(benchmark.GC, *benchmarkFileFlag) + } else if *benchmarkFlag == "cpu" { + bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag) + } else { + Errorf(nil, "unknown benchmark flag: %q", *benchmarkFlag) + usage() + } + } + + bench.Start("libinit") libinit(ctxt) // creates outfile if ctxt.HeadType == objabi.Hunknown { ctxt.HeadType.Set(objabi.GOOS) } + bench.Start("computeTLSOffset") ctxt.computeTLSOffset() + bench.Start("Archinit") thearch.Archinit(ctxt) if ctxt.linkShared && !ctxt.IsELF { @@ -207,47 +232,84 @@ func Main(arch *sys.Arch, theArch Arch) { default: addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "") } + bench.Start("loadlib") ctxt.loadlib() + bench.Start("deadcode") deadcode(ctxt) - if *flagNewobj { - ctxt.loadlibfull() // XXX do it here for now - } + + bench.Start("linksetup") ctxt.linksetup() - ctxt.dostrdata() - dwarfGenerateDebugInfo(ctxt) + bench.Start("dostrdata") + ctxt.dostrdata() if objabi.Fieldtrack_enabled != 0 { - fieldtrack(ctxt) + bench.Start("fieldtrack") + fieldtrack(ctxt.Arch, ctxt.loader) } - ctxt.mangleTypeSym() + + bench.Start("dwarfGenerateDebugInfo") + dwarfGenerateDebugInfo(ctxt) + + bench.Start("callgraph") ctxt.callgraph() - ctxt.doelf() - if ctxt.HeadType == objabi.Hdarwin { + bench.Start("dostkcheck") + ctxt.dostkcheck() + + bench.Start("mangleTypeSym") + ctxt.mangleTypeSym() + + if ctxt.IsELF { + bench.Start("doelf") + ctxt.doelf() + } + if ctxt.IsDarwin() { + bench.Start("domacho") ctxt.domacho() } - ctxt.dostkcheck() - if ctxt.HeadType == objabi.Hwindows { + if ctxt.IsWindows() { + bench.Start("dope") ctxt.dope() + bench.Start("windynrelocsyms") ctxt.windynrelocsyms() } - if ctxt.HeadType == objabi.Haix { + if ctxt.IsAIX() { + bench.Start("doxcoff") ctxt.doxcoff() } + bench.Start("textbuildid") + ctxt.textbuildid() + bench.Start("addexport") + setupdynexp(ctxt) + ctxt.setArchSyms(BeforeLoadlibFull) ctxt.addexport() + + bench.Start("loadlibfull") + ctxt.loadlibfull() // XXX do it here for now + + bench.Start("Gentext") thearch.Gentext(ctxt) // trampolines, call stubs, etc. - ctxt.textbuildid() + bench.Start("textaddress") ctxt.textaddress() + bench.Start("pclntab") ctxt.pclntab() + bench.Start("findfunctab") ctxt.findfunctab() + bench.Start("typelink") ctxt.typelink() + bench.Start("symtab") ctxt.symtab() + bench.Start("buildinfo") ctxt.buildinfo() + bench.Start("dodata") ctxt.dodata() + bench.Start("address") order := ctxt.address() + bench.Start("dwarfcompress") dwarfcompress(ctxt) + bench.Start("layout") filesize := ctxt.layout(order) // Write out the output file. @@ -266,25 +328,36 @@ func Main(arch *sys.Arch, theArch Arch) { if outputMmapped { // Asmb will redirect symbols to the output file mmap, and relocations // will be applied directly there. + bench.Start("Asmb") thearch.Asmb(ctxt) + bench.Start("reloc") ctxt.reloc() + bench.Start("Munmap") ctxt.Out.Munmap() } else { // If we don't mmap, we need to apply relocations before // writing out. + bench.Start("reloc") ctxt.reloc() + bench.Start("Asmb") thearch.Asmb(ctxt) } + bench.Start("Asmb2") thearch.Asmb2(ctxt) + bench.Start("undef") ctxt.undef() + bench.Start("hostlink") ctxt.hostlink() if ctxt.Debugvlog != 0 { ctxt.Logf("%d symbols\n", len(ctxt.Syms.Allsym)) ctxt.Logf("%d liveness data\n", liveness) } + bench.Start("Flush") ctxt.Bso.Flush() + bench.Start("archive") ctxt.archive() + bench.Report(os.Stdout) errorexit() } @@ -336,3 +409,48 @@ func startProfile() { }) } } + +// Invoke the old linker and exit. +func oldlink() { + linker := os.Args[0] + if strings.HasSuffix(linker, "link") { + linker = linker[:len(linker)-4] + "oldlink" + } else if strings.HasSuffix(linker, "link.exe") { + linker = linker[:len(linker)-8] + "oldlink.exe" + } else { + log.Fatal("cannot find oldlink. arg0=", linker) + } + + // Copy args, filter out -go115newobj flag + args := make([]string, 0, len(os.Args)-1) + skipNext := false + for i, a := range os.Args { + if i == 0 { + continue // skip arg0 + } + if skipNext { + skipNext = false + continue + } + if a == "-go115newobj" { + skipNext = true + continue + } + if strings.HasPrefix(a, "-go115newobj=") { + continue + } + args = append(args, a) + } + + cmd := exec.Command(linker, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err == nil { + os.Exit(0) + } + if _, ok := err.(*exec.ExitError); ok { + os.Exit(2) // would be nice to use ExitError.ExitCode(), but that is too new + } + log.Fatal("invoke oldlink failed:", err) +} diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go index f8e65ef8ae..8e083477ec 100644 --- a/src/cmd/link/internal/ld/outbuf.go +++ b/src/cmd/link/internal/ld/outbuf.go @@ -9,6 +9,7 @@ import ( "cmd/internal/sys" "cmd/link/internal/sym" "encoding/binary" + "errors" "log" "os" ) @@ -23,19 +24,100 @@ import ( // Second, it provides a very cheap offset counter that doesn't require // any system calls to read the value. // -// It also mmaps the output file (if available). The intended usage is: +// Third, it also mmaps the output file (if available). The intended usage is: // - Mmap the output file // - Write the content // - possibly apply any edits in the output buffer // - Munmap the output file // - possibly write more content to the file, which will not be edited later. +// +// And finally, it provides a mechanism by which you can multithread the +// writing of output files. This mechanism is accomplished by copying a OutBuf, +// and using it in the thread/goroutine. +// +// Parallel OutBuf is intended to be used like: +// +// func write(out *OutBuf) { +// var wg sync.WaitGroup +// for i := 0; i < 10; i++ { +// wg.Add(1) +// view, err := out.View(start[i]) +// if err != nil { +// // handle output +// continue +// } +// go func(out *OutBuf, i int) { +// // do output +// wg.Done() +// }(view, i) +// } +// wg.Wait() +// } type OutBuf struct { - arch *sys.Arch - off int64 - w *bufio.Writer - buf []byte // backing store of mmap'd output file - f *os.File - encbuf [8]byte // temp buffer used by WriteN methods + arch *sys.Arch + off int64 + w *bufio.Writer + buf []byte // backing store of mmap'd output file + name string + f *os.File + encbuf [8]byte // temp buffer used by WriteN methods + isView bool // true if created from View() + start, length uint64 // start and length mmaped data. +} + +func (out *OutBuf) Open(name string) error { + if out.f != nil { + return errors.New("cannont open more than one file") + } + f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) + if err != nil { + return err + } + out.off = 0 + out.name = name + out.w = bufio.NewWriter(f) + out.f = f + return nil +} + +func NewOutBuf(arch *sys.Arch) *OutBuf { + return &OutBuf{ + arch: arch, + } +} + +var viewError = errors.New("output not mmapped") + +func (out *OutBuf) View(start uint64) (*OutBuf, error) { + if out.buf == nil { + return nil, viewError + } + return &OutBuf{ + arch: out.arch, + name: out.name, + buf: out.buf, + off: int64(start), + start: start, + length: out.length, + isView: true, + }, nil +} + +var viewCloseError = errors.New("cannot Close OutBuf from View") + +func (out *OutBuf) Close() error { + if out.isView { + return viewCloseError + } + out.Flush() + if out.f == nil { + return nil + } + if err := out.f.Close(); err != nil { + return err + } + out.f = nil + return nil } func (out *OutBuf) SeekSet(p int64) { @@ -45,7 +127,7 @@ func (out *OutBuf) SeekSet(p int64) { if out.buf == nil { out.Flush() if _, err := out.f.Seek(p, 0); err != nil { - Exitf("seeking to %d in %s: %v", p, out.f.Name(), err) + Exitf("seeking to %d in %s: %v", p, out.name, err) } } out.off = p @@ -154,13 +236,16 @@ func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) { // edit to the symbol content. // If the output file is not Mmap'd, just writes the content. func (out *OutBuf) WriteSym(s *sym.Symbol) { + // NB: We inline the Write call for speediness. if out.buf != nil { start := out.off - out.Write(s.P) + n := copy(out.buf[out.off:], s.P) + out.off += int64(n) s.P = out.buf[start:out.off] s.Attr.Set(sym.AttrReadOnly, false) } else { - out.Write(s.P) + n, _ := out.w.Write(s.P) + out.off += int64(n) } } @@ -168,10 +253,11 @@ func (out *OutBuf) Flush() { var err error if out.buf != nil { err = out.Msync() - } else { + } + if out.w != nil { err = out.w.Flush() } if err != nil { - Exitf("flushing %s: %v", out.f.Name(), err) + Exitf("flushing %s: %v", out.name, err) } } diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go index 4075141171..c064e9686a 100644 --- a/src/cmd/link/internal/ld/outbuf_mmap.go +++ b/src/cmd/link/internal/ld/outbuf_mmap.go @@ -16,11 +16,15 @@ func (out *OutBuf) Mmap(filesize uint64) error { if err != nil { Exitf("resize output file failed: %v", err) } + out.length = filesize out.buf, err = syscall.Mmap(int(out.f.Fd()), 0, int(filesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FILE) return err } func (out *OutBuf) Munmap() { + if out.buf == nil { + return + } err := out.Msync() if err != nil { Exitf("msync output file failed: %v", err) @@ -34,9 +38,12 @@ func (out *OutBuf) Munmap() { } func (out *OutBuf) Msync() error { + if out.buf == nil || out.length <= 0 { + return nil + } // TODO: netbsd supports mmap and msync, but the syscall package doesn't define MSYNC. // It is excluded from the build tag for now. - _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(len(out.buf)), syscall.MS_SYNC) + _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(out.length), syscall.MS_SYNC) if errno != 0 { return errno } diff --git a/src/cmd/link/internal/ld/outbuf_test.go b/src/cmd/link/internal/ld/outbuf_test.go new file mode 100644 index 0000000000..512238f39a --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_test.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "os" + "runtime" + "testing" +) + +// TestMMap ensures that we can actually mmap on every supported platform. +func TestMMap(t *testing.T) { + switch runtime.GOOS { + default: + t.Skip("unsupported OS") + case "darwin", "dragonfly", "freebsd", "linux", "openbsd", "windows": + } + filename := "foo.out" + ob := NewOutBuf(nil) + if err := ob.Open(filename); err != nil { + t.Errorf("error opening file: %v", err) + } + defer os.RemoveAll(filename) + defer ob.Close() + if err := ob.Mmap(1 << 20); err != nil { + t.Errorf("error mmapping file %v", err) + } +} diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go index 1cb05c301f..f745a5cb22 100644 --- a/src/cmd/link/internal/ld/outbuf_windows.go +++ b/src/cmd/link/internal/ld/outbuf_windows.go @@ -17,7 +17,7 @@ func (out *OutBuf) Mmap(filesize uint64) error { } low, high := uint32(filesize), uint32(filesize>>32) - fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READONLY, high, low, nil) + fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READWRITE, high, low, nil) if err != nil { return err } @@ -36,6 +36,7 @@ func (out *OutBuf) Munmap() { return } err := syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&out.buf[0]))) + out.buf = nil if err != nil { Exitf("UnmapViewOfFile failed: %v", err) } diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index f775132393..364e757985 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -10,6 +10,7 @@ package ld import ( "cmd/internal/objabi" "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/pe" "encoding/binary" @@ -237,7 +238,7 @@ var dosstub = []uint8{ } type Imp struct { - s *sym.Symbol + s loader.Sym off uint64 next *Imp argsize int @@ -252,13 +253,13 @@ type Dll struct { } var ( - rsrcsym *sym.Symbol + rsrcsym loader.Sym PESECTHEADR int32 PEFILEHEADR int32 pe64 int dr *Dll - dexport [1024]*sym.Symbol - nexport int + + dexport = make([]loader.Sym, 0, 1024) ) // peStringTable is a COFF string table. @@ -542,8 +543,8 @@ func (f *peFile) emitRelocations(ctxt *Link) { syms []*sym.Symbol }{ {f.textSect, &Segtext, ctxt.Textp}, - {f.rdataSect, &Segrodata, datap}, - {f.dataSect, &Segdata, datap}, + {f.rdataSect, &Segrodata, ctxt.datap}, + {f.dataSect, &Segdata, ctxt.datap}, } for _, s := range sects { s.peSect.emitRelocations(ctxt.Out, func() int { @@ -957,8 +958,15 @@ func Peinit(ctxt *Link) { if ctxt.LinkMode == LinkInternal { // some mingw libs depend on this symbol, for example, FindPESectionByName - ctxt.xdefine("__image_base__", sym.SDATA, PEBASE) - ctxt.xdefine("_image_base__", sym.SDATA, PEBASE) + for _, name := range [2]string{"__image_base__", "_image_base__"} { + s := ctxt.loader.LookupOrCreateSym(name, 0) + sb := ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SDATA) + sb.SetValue(PEBASE) + ctxt.loader.SetAttrReachable(s, true) + ctxt.loader.SetAttrSpecial(s, true) + ctxt.loader.SetAttrLocal(s, true) + } } HEADR = PEFILEHEADR @@ -996,16 +1004,18 @@ func strput(out *OutBuf, s string) { } func initdynimport(ctxt *Link) *Dll { + ldr := ctxt.loader var d *Dll dr = nil var m *Imp - for _, s := range ctxt.Syms.Allsym { - if !s.Attr.Reachable() || s.Type != sym.SDYNIMPORT { + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT { continue } + dynlib := ldr.SymDynimplib(s) for d = dr; d != nil; d = d.next { - if d.name == s.Dynimplib() { + if d.name == dynlib { m = new(Imp) break } @@ -1013,7 +1023,7 @@ func initdynimport(ctxt *Link) *Dll { if d == nil { d = new(Dll) - d.name = s.Dynimplib() + d.name = dynlib d.next = dr dr = d m = new(Imp) @@ -1024,15 +1034,15 @@ func initdynimport(ctxt *Link) *Dll { // of uinptrs this function consumes. Store the argsize and discard // the %n suffix if any. m.argsize = -1 - extName := s.Extname() + extName := ldr.SymExtname(s) if i := strings.IndexByte(extName, '%'); i >= 0 { var err error m.argsize, err = strconv.Atoi(extName[i+1:]) if err != nil { - Errorf(s, "failed to parse stdcall decoration: %v", err) + ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err) } m.argsize *= ctxt.Arch.PtrSize - s.SetExtname(extName[:i]) + ldr.SetSymExtname(s, extName[:i]) } m.s = s @@ -1040,42 +1050,38 @@ func initdynimport(ctxt *Link) *Dll { d.ms = m } - if ctxt.LinkMode == LinkExternal { + if ctxt.IsExternal() { // Add real symbol name for d := dr; d != nil; d = d.next { for m = d.ms; m != nil; m = m.next { - m.s.Type = sym.SDATA - m.s.Grow(int64(ctxt.Arch.PtrSize)) - dynName := m.s.Extname() + sb := ldr.MakeSymbolUpdater(m.s) + sb.SetType(sym.SDATA) + sb.Grow(int64(ctxt.Arch.PtrSize)) + dynName := sb.Extname() // only windows/386 requires stdcall decoration - if ctxt.Arch.Family == sys.I386 && m.argsize >= 0 { + if ctxt.Is386() && m.argsize >= 0 { dynName += fmt.Sprintf("@%d", m.argsize) } - dynSym := ctxt.Syms.Lookup(dynName, 0) - dynSym.Attr |= sym.AttrReachable - dynSym.Type = sym.SHOSTOBJ - r := m.s.AddRel() - r.Sym = dynSym - r.Off = 0 - r.Siz = uint8(ctxt.Arch.PtrSize) - r.Type = objabi.R_ADDR + dynSym := ldr.CreateSymForUpdate(dynName, 0) + dynSym.SetReachable(true) + dynSym.SetType(sym.SHOSTOBJ) + sb.AddReloc(loader.Reloc{Sym: dynSym.Sym(), Type: objabi.R_ADDR, Off: 0, Size: uint8(ctxt.Arch.PtrSize)}) } } } else { - dynamic := ctxt.Syms.Lookup(".windynamic", 0) - dynamic.Attr |= sym.AttrReachable - dynamic.Type = sym.SWINDOWS + dynamic := ldr.CreateSymForUpdate(".windynamic", 0) + dynamic.SetReachable(true) + dynamic.SetType(sym.SWINDOWS) for d := dr; d != nil; d = d.next { for m = d.ms; m != nil; m = m.next { - m.s.Type = sym.SWINDOWS - m.s.Attr |= sym.AttrSubSymbol - m.s.Sub = dynamic.Sub - dynamic.Sub = m.s - m.s.Value = dynamic.Size - dynamic.Size += int64(ctxt.Arch.PtrSize) + sb := ldr.MakeSymbolUpdater(m.s) + sb.SetType(sym.SWINDOWS) + dynamic.PrependSub(m.s) + sb.SetValue(dynamic.Size()) + dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) } - dynamic.Size += int64(ctxt.Arch.PtrSize) + dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) } } @@ -1095,6 +1101,7 @@ func peimporteddlls() []string { } func addimports(ctxt *Link, datsect *peSection) { + ldr := ctxt.loader startoff := ctxt.Out.Offset() dynamic := ctxt.Syms.Lookup(".windynamic", 0) @@ -1117,7 +1124,7 @@ func addimports(ctxt *Link, datsect *peSection) { for m := d.ms; m != nil; m = m.next { m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff) ctxt.Out.Write16(0) // hint - strput(ctxt.Out, m.s.Extname()) + strput(ctxt.Out, ldr.SymExtname(m.s)) } } @@ -1198,36 +1205,31 @@ func addimports(ctxt *Link, datsect *peSection) { out.SeekSet(endoff) } -type byExtname []*sym.Symbol - -func (s byExtname) Len() int { return len(s) } -func (s byExtname) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s byExtname) Less(i, j int) bool { return s[i].Extname() < s[j].Extname() } - func initdynexport(ctxt *Link) { - nexport = 0 - for _, s := range ctxt.Syms.Allsym { - if !s.Attr.Reachable() || !s.Attr.CgoExportDynamic() { + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) { continue } - if nexport+1 > len(dexport) { - Errorf(s, "pe dynexport table is full") + if len(dexport)+1 > cap(dexport) { + ctxt.Errorf(s, "pe dynexport table is full") errorexit() } - dexport[nexport] = s - nexport++ + dexport = append(dexport, s) } - sort.Sort(byExtname(dexport[:nexport])) + sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) }) } func addexports(ctxt *Link) { + ldr := ctxt.loader var e IMAGE_EXPORT_DIRECTORY + nexport := len(dexport) size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1 - for i := 0; i < nexport; i++ { - size += len(dexport[i].Extname()) + 1 + for _, s := range dexport { + size += len(ldr.Syms[s].Extname()) + 1 } if nexport == 0 { @@ -1262,16 +1264,16 @@ func addexports(ctxt *Link) { binary.Write(out, binary.LittleEndian, &e) // put EXPORT Address Table - for i := 0; i < nexport; i++ { - out.Write32(uint32(dexport[i].Value - PEBASE)) + for _, s := range dexport { + out.Write32(uint32(ldr.Syms[s].Value - PEBASE)) } // put EXPORT Name Pointer Table v := int(e.Name + uint32(len(*flagOutfile)) + 1) - for i := 0; i < nexport; i++ { + for _, s := range dexport { out.Write32(uint32(v)) - v += len(dexport[i].Extname()) + 1 + v += len(ldr.Syms[s].Extname()) + 1 } // put EXPORT Ordinal Table @@ -1282,8 +1284,9 @@ func addexports(ctxt *Link) { // put Names out.WriteStringN(*flagOutfile, len(*flagOutfile)+1) - for i := 0; i < nexport; i++ { - out.WriteStringN(dexport[i].Extname(), len(dexport[i].Extname())+1) + for _, s := range dexport { + ss := ldr.Syms[s] + out.WriteStringN(ss.Extname(), len(ss.Extname())+1) } sect.pad(out, uint32(size)) } @@ -1431,7 +1434,7 @@ func addPEBaseReloc(ctxt *Link) { for _, s := range ctxt.Textp { addPEBaseRelocSym(ctxt, s, &rt) } - for _, s := range datap { + for _, s := range ctxt.datap { addPEBaseRelocSym(ctxt, s, &rt) } @@ -1455,28 +1458,31 @@ func (ctxt *Link) dope() { initdynexport(ctxt) } -func setpersrc(ctxt *Link, sym *sym.Symbol) { - if rsrcsym != nil { - Errorf(sym, "too many .rsrc sections") +func setpersrc(ctxt *Link, sym loader.Sym) { + if rsrcsym != 0 { + Errorf(nil, "too many .rsrc sections") } rsrcsym = sym } func addpersrc(ctxt *Link) { - if rsrcsym == nil { + if rsrcsym == 0 { return } - h := pefile.addSection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size)) + data := ctxt.loader.Data(rsrcsym) + size := len(data) + h := pefile.addSection(".rsrc", size, size) h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA h.checkOffset(ctxt.Out.Offset()) // relocation - for ri := range rsrcsym.R { - r := &rsrcsym.R[ri] - p := rsrcsym.P[r.Off:] - val := uint32(int64(h.virtualAddress) + r.Add) + relocs := ctxt.loader.Relocs(rsrcsym) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At2(i) + p := data[r.Off():] + val := uint32(int64(h.virtualAddress) + r.Add()) // 32-bit little-endian p[0] = byte(val) @@ -1486,8 +1492,8 @@ func addpersrc(ctxt *Link) { p[3] = byte(val >> 24) } - ctxt.Out.Write(rsrcsym.P) - h.pad(ctxt.Out, uint32(rsrcsym.Size)) + ctxt.Out.Write(data) + h.pad(ctxt.Out, uint32(size)) // update data directory pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go index eb48ac842a..97966ed7e4 100644 --- a/src/cmd/link/internal/ld/sym.go +++ b/src/cmd/link/internal/ld/sym.go @@ -36,13 +36,15 @@ import ( "cmd/internal/sys" "cmd/link/internal/sym" "log" + "runtime" ) func linknew(arch *sys.Arch) *Link { ctxt := &Link{ + Target: Target{Arch: arch}, Syms: sym.NewSymbols(), - Out: &OutBuf{arch: arch}, - Arch: arch, + outSem: make(chan int, 2*runtime.GOMAXPROCS(0)), + Out: NewOutBuf(arch), LibraryByPkg: make(map[string]*sym.Library), } @@ -51,8 +53,8 @@ func linknew(arch *sys.Arch) *Link { } AtExit(func() { - if nerrors > 0 && ctxt.Out.f != nil { - ctxt.Out.f.Close() + if nerrors > 0 { + ctxt.Out.Close() mayberemoveoutfile() } }) diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index bba623eb48..97cbb5616e 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -163,6 +163,13 @@ func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64, go other |= 3 << 5 } + if s == x.Name { + // We should use Extname for ELF symbol table. + // TODO: maybe genasmsym should have done this. That function is too + // overloaded and I would rather not change it for now. + s = x.Extname() + } + // When dynamically linking, we create Symbols by reading the names from // the symbol tables of the shared libraries and so the names need to // match exactly. Tools like DTrace will have to wait for now. @@ -326,12 +333,11 @@ func textsectionmap(ctxt *Link) uint32 { } func (ctxt *Link) symtab() { - switch ctxt.BuildMode { - case BuildModeCArchive, BuildModeCShared: - for _, s := range ctxt.Syms.Allsym { - // Create a new entry in the .init_array section that points to the - // library initializer function. - if s.Name == *flagEntrySymbol && ctxt.HeadType != objabi.Haix { + if ctxt.HeadType != objabi.Haix { + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared: + s := ctxt.Syms.ROLookup(*flagEntrySymbol, sym.SymVerABI0) + if s != nil { addinitarrdata(ctxt, s) } } diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go new file mode 100644 index 0000000000..95f6ca17ab --- /dev/null +++ b/src/cmd/link/internal/ld/target.go @@ -0,0 +1,144 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "encoding/binary" +) + +// Target holds the configuration we're building for. +type Target struct { + Arch *sys.Arch + + HeadType objabi.HeadType + + LinkMode LinkMode + BuildMode BuildMode + + linkShared bool + canUsePlugins bool + IsELF bool +} + +// +// Target type functions +// + +func (t *Target) IsShared() bool { + return t.BuildMode == BuildModeShared +} + +func (t *Target) IsPlugin() bool { + return t.BuildMode == BuildModePlugin +} + +func (t *Target) IsInternal() bool { + return t.LinkMode == LinkInternal +} + +func (t *Target) IsExternal() bool { + return t.LinkMode == LinkExternal +} + +func (t *Target) IsPIE() bool { + return t.BuildMode == BuildModePIE +} + +func (t *Target) IsSharedGoLink() bool { + return t.linkShared +} + +func (t *Target) CanUsePlugins() bool { + return t.canUsePlugins +} + +func (t *Target) IsElf() bool { + return t.IsELF +} + +func (t *Target) IsDynlinkingGo() bool { + return t.IsShared() || t.IsSharedGoLink() || t.IsPlugin() || t.CanUsePlugins() +} + +// UseRelro reports whether to make use of "read only relocations" aka +// relro. +func (t *Target) UseRelro() bool { + switch t.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin: + return t.IsELF || t.HeadType == objabi.Haix + default: + return t.linkShared || (t.HeadType == objabi.Haix && t.LinkMode == LinkExternal) + } +} + +// +// Processor functions +// + +func (t *Target) Is386() bool { + return t.Arch.Family == sys.I386 +} + +func (t *Target) IsARM() bool { + return t.Arch.Family == sys.ARM +} + +func (t *Target) IsAMD64() bool { + return t.Arch.Family == sys.AMD64 +} + +func (t *Target) IsPPC64() bool { + return t.Arch.Family == sys.PPC64 +} + +func (t *Target) IsS390X() bool { + return t.Arch.Family == sys.S390X +} + +// +// OS Functions +// + +func (t *Target) IsLinux() bool { + return t.HeadType == objabi.Hlinux +} + +func (t *Target) IsDarwin() bool { + return t.HeadType == objabi.Hdarwin +} + +func (t *Target) IsWindows() bool { + return t.HeadType == objabi.Hwindows +} + +func (t *Target) IsPlan9() bool { + return t.HeadType == objabi.Hplan9 +} + +func (t *Target) IsAIX() bool { + return t.HeadType == objabi.Haix +} + +func (t *Target) IsSolaris() bool { + return t.HeadType == objabi.Hsolaris +} + +func (t *Target) IsNetbsd() bool { + return t.HeadType == objabi.Hnetbsd +} + +func (t *Target) IsOpenbsd() bool { + return t.HeadType == objabi.Hopenbsd +} + +// +// MISC +// + +func (t *Target) IsBigEndian() bool { + return t.Arch.ByteOrder == binary.BigEndian +} diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go index 9d236db766..9f257b8fc0 100644 --- a/src/cmd/link/internal/ld/util.go +++ b/src/cmd/link/internal/ld/util.go @@ -5,6 +5,7 @@ package ld import ( + "cmd/link/internal/loader" "cmd/link/internal/sym" "encoding/binary" "fmt" @@ -38,6 +39,18 @@ func Exitf(format string, a ...interface{}) { Exit(2) } +// afterErrorAction updates 'nerrors' on error and invokes exit or +// panics in the proper circumstances. +func afterErrorAction() { + nerrors++ + if *flagH { + panic("error") + } + if nerrors > 20 { + Exitf("too many errors") + } +} + // Errorf logs an error message. // // If more than 20 errors have been printed, exit with an error. @@ -50,13 +63,25 @@ func Errorf(s *sym.Symbol, format string, args ...interface{}) { } format += "\n" fmt.Fprintf(os.Stderr, format, args...) - nerrors++ - if *flagH { - panic("error") - } - if nerrors > 20 { - Exitf("too many errors") + afterErrorAction() +} + +// Errorf method logs an error message. +// +// If more than 20 errors have been printed, exit with an error. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +func (ctxt *Link) Errorf(s loader.Sym, format string, args ...interface{}) { + if s != 0 && ctxt.loader != nil { + sn := ctxt.loader.SymName(s) + format = sn + ": " + format + } else { + format = fmt.Sprintf("sym %d: %s", s, format) } + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + afterErrorAction() } func artrim(x []byte) string { diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index 8814bad4ae..5bdf863f14 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -7,6 +7,7 @@ package ld import ( "bytes" "cmd/internal/objabi" + "cmd/link/internal/loader" "cmd/link/internal/sym" "encoding/binary" "io/ioutil" @@ -338,7 +339,7 @@ type XcoffLdSym64 struct { } type xcoffLoaderSymbol struct { - sym *sym.Symbol + sym loader.Sym smtype int8 smclas int8 } @@ -1031,9 +1032,13 @@ func (f *xcoffFile) asmaixsym(ctxt *Link) { } func (f *xcoffFile) genDynSym(ctxt *Link) { - var dynsyms []*sym.Symbol - for _, s := range ctxt.Syms.Allsym { - if s.Type != sym.SHOSTOBJ && s.Type != sym.SDYNIMPORT { + ldr := ctxt.loader + var dynsyms []loader.Sym + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT { continue } dynsyms = append(dynsyms, s) @@ -1042,12 +1047,10 @@ func (f *xcoffFile) genDynSym(ctxt *Link) { for _, s := range dynsyms { f.adddynimpsym(ctxt, s) - if _, ok := f.dynLibraries[s.Dynimplib()]; !ok { - f.dynLibraries[s.Dynimplib()] = len(f.dynLibraries) + if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok { + f.dynLibraries[ldr.SymDynimplib(s)] = len(f.dynLibraries) } - } - } // (*xcoffFile)adddynimpsym adds the dynamic symbol "s" to a XCOFF file. @@ -1057,30 +1060,32 @@ func (f *xcoffFile) genDynSym(ctxt *Link) { // However, there is no writing protection on those symbols and // it might need to be added. // TODO(aix): Handles dynamic symbols without library. -func (f *xcoffFile) adddynimpsym(ctxt *Link, s *sym.Symbol) { +func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) { // Check that library name is given. // Pattern is already checked when compiling. - if ctxt.LinkMode == LinkInternal && s.Dynimplib() == "" { - Errorf(s, "imported symbol must have a given library") + ldr := ctxt.loader + if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" { + ctxt.Errorf(s, "imported symbol must have a given library") } - s.Type = sym.SXCOFFTOC + sb := ldr.MakeSymbolUpdater(s) + sb.SetType(sym.SXCOFFTOC) // Create new dynamic symbol - extsym := ctxt.Syms.Lookup(s.Extname(), 0) - extsym.Type = sym.SDYNIMPORT - extsym.Attr |= sym.AttrReachable - extsym.SetDynimplib(s.Dynimplib()) - extsym.SetExtname(s.Extname()) - extsym.SetDynimpvers(s.Dynimpvers()) + extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0) + extsym.SetType(sym.SDYNIMPORT) + extsym.SetReachable(true) + extsym.SetDynimplib(ldr.SymDynimplib(s)) + extsym.SetExtname(ldr.SymExtname(s)) + extsym.SetDynimpvers(ldr.SymDynimpvers(s)) // Add loader symbol lds := &xcoffLoaderSymbol{ - sym: extsym, + sym: extsym.Sym(), smtype: XTY_IMP, smclas: XMC_DS, } - if s.Name == "__n_pthreads" { + if ldr.SymName(s) == "__n_pthreads" { // Currently, all imported symbols made by cgo_import_dynamic are // syscall functions, except __n_pthreads which is a variable. // TODO(aix): Find a way to detect variables imported by cgo. @@ -1089,15 +1094,20 @@ func (f *xcoffFile) adddynimpsym(ctxt *Link, s *sym.Symbol) { f.loaderSymbols = append(f.loaderSymbols, lds) // Relocation to retrieve the external address - s.AddBytes(make([]byte, 8)) - s.SetAddr(ctxt.Arch, 0, extsym) - + sb.AddBytes(make([]byte, 8)) + sb.AddReloc(loader.Reloc{Off: 0, Size: uint8(ctxt.Arch.PtrSize), Type: objabi.R_ADDR, Sym: extsym.Sym()}) + // TODO: maybe this could be + // sb.SetSize(0) + // sb.SetData(nil) + // sb.AddAddr(ctxt.Arch, extsym.Sym()) + // If the size is not 0 to begin with, I don't think the added 8 bytes + // of zeros are necessary. } // Xcoffadddynrel adds a dynamic relocation in a XCOFF file. // This relocation will be made by the loader. -func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool { - if ctxt.LinkMode == LinkExternal { +func Xcoffadddynrel(target *Target, ldr *loader.Loader, s *sym.Symbol, r *sym.Reloc) bool { + if target.IsExternal() { return true } if s.Type <= sym.SPCLNTAB { @@ -1105,7 +1115,7 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool { return false } - ldr := &xcoffLoaderReloc{ + xldr := &xcoffLoaderReloc{ sym: s, rel: r, } @@ -1118,8 +1128,8 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool { if s.Type == sym.SXCOFFTOC && r.Sym.Type == sym.SDYNIMPORT { // Imported symbol relocation for i, dynsym := range xfile.loaderSymbols { - if dynsym.sym.Name == r.Sym.Name { - ldr.symndx = int32(i + 3) // +3 because of 3 section symbols + if ldr.Syms[dynsym.sym].Name == r.Sym.Name { + xldr.symndx = int32(i + 3) // +3 because of 3 section symbols break } } @@ -1129,12 +1139,12 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool { Errorf(s, "unknown segment for .loader relocation with symbol %s", r.Sym.Name) case &Segtext: case &Segrodata: - ldr.symndx = 0 // .text + xldr.symndx = 0 // .text case &Segdata: if r.Sym.Type == sym.SBSS || r.Sym.Type == sym.SNOPTRBSS { - ldr.symndx = 2 // .bss + xldr.symndx = 2 // .bss } else { - ldr.symndx = 1 // .data + xldr.symndx = 1 // .data } } @@ -1144,10 +1154,10 @@ func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool { return false } - ldr.rtype = 0x3F<<8 + XCOFF_R_POS + xldr.rtype = 0x3F<<8 + XCOFF_R_POS } - xfile.loaderReloc = append(xfile.loaderReloc, ldr) + xfile.loaderReloc = append(xfile.loaderReloc, xldr) return true } @@ -1156,16 +1166,17 @@ func (ctxt *Link) doxcoff() { // All XCOFF files have dynamic symbols because of the syscalls. Exitf("-d is not available on AIX") } + ldr := ctxt.loader // TOC - toc := ctxt.Syms.Lookup("TOC", 0) - toc.Type = sym.SXCOFFTOC - toc.Attr |= sym.AttrReachable - toc.Attr |= sym.AttrVisibilityHidden + toc := ldr.CreateSymForUpdate("TOC", 0) + toc.SetType(sym.SXCOFFTOC) + toc.SetReachable(true) + toc.SetVisibilityHidden(true) // Add entry point to .loader symbols. - ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0) - if !ep.Attr.Reachable() { + ep := ldr.Lookup(*flagEntrySymbol, 0) + if ep == 0 || !ldr.AttrReachable(ep) { Exitf("wrong entry point") } @@ -1177,33 +1188,43 @@ func (ctxt *Link) doxcoff() { xfile.genDynSym(ctxt) - for _, s := range ctxt.Syms.Allsym { - if strings.HasPrefix(s.Name, "TOC.") { - s.Type = sym.SXCOFFTOC + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if strings.HasPrefix(ldr.SymName(s), "TOC.") { + sb := ldr.MakeSymbolUpdater(s) + sb.SetType(sym.SXCOFFTOC) } } - if ctxt.LinkMode == LinkExternal { + if ctxt.IsExternal() { // Change rt0_go name to match name in runtime/cgo:main(). - rt0 := ctxt.Syms.ROLookup("runtime.rt0_go", 0) - ctxt.Syms.Rename(rt0.Name, "runtime_rt0_go", 0, ctxt.Reachparent) + rt0 := ldr.Lookup("runtime.rt0_go", 0) + ldr.SetSymExtname(rt0, "runtime_rt0_go") - for _, s := range ctxt.Syms.Allsym { - if !s.Attr.CgoExport() { + nsym := loader.Sym(ldr.NSym()) + for s := loader.Sym(1); s < nsym; s++ { + if !ldr.AttrCgoExport(s) { continue } + if ldr.SymVersion(s) != 0 { // sanity check + panic("cgo_export on non-version 0 symbol") + } - name := s.Extname() - if s.Type == sym.STEXT { + if ldr.SymType(s) == sym.STEXT || ldr.SymType(s) == sym.SABIALIAS { // On AIX, a exported function must have two symbols: // - a .text symbol which must start with a ".". // - a .data symbol which is a function descriptor. - ctxt.Syms.Rename(s.Name, "."+name, 0, ctxt.Reachparent) - - desc := ctxt.Syms.Lookup(name, 0) - desc.Type = sym.SNOPTRDATA + // + // CgoExport attribute should only be set on a version 0 + // symbol, which can be TEXT or ABIALIAS. + // (before, setupdynexp copies the attribute from the + // alias to the aliased. Now we are before setupdynexp.) + name := ldr.SymExtname(s) + ldr.SetSymExtname(s, "."+name) + + desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0)) + desc.SetType(sym.SNOPTRDATA) desc.AddAddr(ctxt.Arch, s) - desc.AddAddr(ctxt.Arch, toc) + desc.AddAddr(ctxt.Arch, toc.Sym()) desc.AddUint64(ctxt.Arch, 0) } } @@ -1243,18 +1264,19 @@ func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) { Lsmtype: s.smtype, Lsmclas: s.smclas, } + sym := ctxt.loader.Syms[s.sym] switch s.smtype { default: - Errorf(s.sym, "unexpected loader symbol type: 0x%x", s.smtype) + Errorf(sym, "unexpected loader symbol type: 0x%x", s.smtype) case XTY_ENT | XTY_SD: - lds.Lvalue = uint64(s.sym.Value) - lds.Lscnum = f.getXCOFFscnum(s.sym.Sect) + lds.Lvalue = uint64(sym.Value) + lds.Lscnum = f.getXCOFFscnum(sym.Sect) case XTY_IMP: - lds.Lifile = int32(f.dynLibraries[s.sym.Dynimplib()] + 1) + lds.Lifile = int32(f.dynLibraries[sym.Dynimplib()] + 1) } ldstr := &XcoffLdStr64{ - size: uint16(len(s.sym.Name) + 1), // + null terminator - name: s.sym.Name, + size: uint16(len(sym.Name) + 1), // + null terminator + name: sym.Name, } stlen += uint32(2 + ldstr.size) // 2 = sizeof ldstr.size symtab = append(symtab, lds) @@ -1268,28 +1290,28 @@ func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) { /* Reloc */ ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0) - ldr := &XcoffLdRel64{ + xldr := &XcoffLdRel64{ Lvaddr: uint64(ep.Value), Lrtype: 0x3F00, Lrsecnm: f.getXCOFFscnum(ep.Sect), Lsymndx: 0, } off += 16 - reloctab = append(reloctab, ldr) + reloctab = append(reloctab, xldr) off += uint64(16 * len(f.loaderReloc)) for _, r := range f.loaderReloc { - ldr = &XcoffLdRel64{ + xldr = &XcoffLdRel64{ Lvaddr: uint64(r.sym.Value + int64(r.rel.Off)), Lrtype: r.rtype, Lsymndx: r.symndx, } if r.sym.Sect != nil { - ldr.Lrsecnm = f.getXCOFFscnum(r.sym.Sect) + xldr.Lrsecnm = f.getXCOFFscnum(r.sym.Sect) } - reloctab = append(reloctab, ldr) + reloctab = append(reloctab, xldr) } off += uint64(16 * len(dynimpreloc)) @@ -1630,7 +1652,7 @@ func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) { if sect.Name == ".text" { n += relocsect(sect, ctxt.Textp, 0) } else { - n += relocsect(sect, datap, 0) + n += relocsect(sect, ctxt.datap, 0) } } } @@ -1662,9 +1684,12 @@ func xcoffCreateExportFile(ctxt *Link) (fname string) { if !s.Attr.CgoExport() { continue } - if !strings.HasPrefix(s.String(), "_cgoexp_") { + if !strings.HasPrefix(s.Extname(), "._cgoexp_") { continue } + if s.Version != 0 { + continue // Only export version 0 symbols. See the comment in doxcoff. + } // Retrieve the name of the initial symbol // exported by cgo. diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go index 1962d76338..0d40940093 100644 --- a/src/cmd/link/internal/loadelf/ldelf.go +++ b/src/cmd/link/internal/loadelf/ldelf.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Go Authors. All rights reserved. +// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -270,19 +270,20 @@ type ElfSymBytes64 struct { } type ElfSect struct { - name string - nameoff uint32 - type_ uint32 - flags uint64 - addr uint64 - off uint64 - size uint64 - link uint32 - info uint32 - align uint64 - entsize uint64 - base []byte - sym *sym.Symbol + name string + nameoff uint32 + type_ uint32 + flags uint64 + addr uint64 + off uint64 + size uint64 + link uint32 + info uint32 + align uint64 + entsize uint64 + base []byte + readOnlyMem bool // Is this section in readonly memory? + sym loader.Sym } type ElfObj struct { @@ -320,7 +321,7 @@ type ElfSym struct { type_ uint8 other uint8 shndx uint16 - sym *sym.Symbol + sym loader.Sym } var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'} @@ -452,32 +453,22 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags return found, ehdrFlags, nil } -func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) { - newSym := func(name string, version int) *sym.Symbol { - return l.Create(name, syms) - } - lookup := func(name string, version int) *sym.Symbol { - return l.LookupOrCreate(name, version, syms) - } - return load(arch, syms.IncVersion(), newSym, lookup, f, pkg, length, pn, flags) -} - -func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) { - return load(arch, syms.IncVersion(), syms.Newsym, syms.Lookup, f, pkg, length, pn, flags) -} - -type lookupFunc func(string, int) *sym.Symbol - -// load loads the ELF file pn from f. -// Symbols are written into syms, and a slice of the text symbols is returned. +// Load loads the ELF file pn from f. +// Symbols are installed into the loader, and a slice of the text symbols is returned. // // On ARM systems, Load will attempt to determine what ELF header flags to // emit by scanning the attributes in the ELF file being loaded. The // parameter initEhdrFlags contains the current header flags for the output // object, and the returned ehdrFlags contains what this Load function computes. // TODO: find a better place for this logic. -func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []*sym.Symbol, ehdrFlags uint32, err error) { - errorf := func(str string, args ...interface{}) ([]*sym.Symbol, uint32, error) { +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []loader.Sym, ehdrFlags uint32, err error) { + newSym := func(name string, version int) loader.Sym { + return l.CreateStaticSym(name) + } + lookup := func(name string, version int) loader.Sym { + return l.LookupOrCreateSym(name, version) + } + errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) { return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...)) } @@ -610,7 +601,6 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio sect := &elfobj.sect[i] if is64 != 0 { var b ElfSectBytes64 - if err := binary.Read(f, e, &b); err != nil { return errorf("malformed elf file: %v", err) } @@ -731,46 +721,47 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio } sectsymNames[name] = true - s := lookup(name, localSymVersion) + sb := l.MakeSymbolUpdater(lookup(name, localSymVersion)) switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) { default: return errorf("%s: unexpected flags for ELF section %s", pn, sect.name) case ElfSectFlagAlloc: - s.Type = sym.SRODATA + sb.SetType(sym.SRODATA) case ElfSectFlagAlloc + ElfSectFlagWrite: if sect.type_ == ElfSectNobits { - s.Type = sym.SNOPTRBSS + sb.SetType(sym.SNOPTRBSS) } else { - s.Type = sym.SNOPTRDATA + sb.SetType(sym.SNOPTRDATA) } case ElfSectFlagAlloc + ElfSectFlagExec: - s.Type = sym.STEXT + sb.SetType(sym.STEXT) } if sect.name == ".got" || sect.name == ".toc" { - s.Type = sym.SELFGOT + sb.SetType(sym.SELFGOT) } if sect.type_ == ElfSectProgbits { - s.P = sect.base - s.P = s.P[:sect.size] + sb.SetData(sect.base[:sect.size]) } - s.Size = int64(sect.size) - s.Align = int32(sect.align) - sect.sym = s + sb.SetSize(int64(sect.size)) + sb.SetAlign(int32(sect.align)) + sb.SetReadOnly(sect.readOnlyMem) + + sect.sym = sb.Sym() } // enter sub-symbols into symbol table. // symbol 0 is the null symbol. - symbols := make([]*sym.Symbol, elfobj.nsymtab) + symbols := make([]loader.Sym, elfobj.nsymtab) for i := 1; i < elfobj.nsymtab; i++ { var elfsym ElfSym - if err := readelfsym(newSym, lookup, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil { + if err := readelfsym(newSym, lookup, l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil { return errorf("%s: malformed elf file: %v", pn, err) } symbols[i] = elfsym.sym @@ -778,12 +769,12 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio continue } if elfsym.shndx == ElfSymShnCommon || elfsym.type_ == ElfSymTypeCommon { - s := elfsym.sym - if uint64(s.Size) < elfsym.size { - s.Size = int64(elfsym.size) + sb := l.MakeSymbolUpdater(elfsym.sym) + if uint64(sb.Size()) < elfsym.size { + sb.SetSize(int64(elfsym.size)) } - if s.Type == 0 || s.Type == sym.SXREF { - s.Type = sym.SNOPTRBSS + if sb.Type() == 0 || sb.Type() == sym.SXREF { + sb.SetType(sym.SNOPTRBSS) } continue } @@ -793,11 +784,11 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio } // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols - if elfsym.sym == nil { + if elfsym.sym == 0 { continue } sect = &elfobj.sect[elfsym.shndx] - if sect.sym == nil { + if sect.sym == 0 { if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this continue } @@ -822,36 +813,37 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio } s := elfsym.sym - if s.Outer != nil { - if s.Attr.DuplicateOK() { + if l.OuterSym(s) != 0 { + if l.AttrDuplicateOK(s) { continue } - return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name) + return errorf("duplicate symbol reference: %s in both %s and %s", + l.SymName(s), l.SymName(l.OuterSym(s)), l.SymName(sect.sym)) } - s.Sub = sect.sym.Sub - sect.sym.Sub = s - s.Type = sect.sym.Type - s.Attr |= sym.AttrSubSymbol - if !s.Attr.CgoExportDynamic() { - s.SetDynimplib("") // satisfy dynimport + sectsb := l.MakeSymbolUpdater(sect.sym) + sb := l.MakeSymbolUpdater(s) + + sb.SetType(sectsb.Type()) + sectsb.PrependSub(s) + if !l.AttrCgoExportDynamic(s) { + sb.SetDynimplib("") // satisfy dynimport } - s.Value = int64(elfsym.value) - s.Size = int64(elfsym.size) - s.Outer = sect.sym - if sect.sym.Type == sym.STEXT { - if s.Attr.External() && !s.Attr.DuplicateOK() { - return errorf("%v: duplicate symbol definition", s) + sb.SetValue(int64(elfsym.value)) + sb.SetSize(int64(elfsym.size)) + if sectsb.Type() == sym.STEXT { + if l.AttrExternal(s) && !l.AttrDuplicateOK(s) { + return errorf("%s: duplicate symbol definition", sb.Name()) } - s.Attr |= sym.AttrExternal + l.SetAttrExternal(s, true) } if elfobj.machine == ElfMachPower64 { flag := int(elfsym.other) >> 5 if 2 <= flag && flag <= 6 { - s.SetLocalentry(1 << uint(flag-2)) + l.SetSymLocalentry(s, 1<<uint(flag-2)) } else if flag == 7 { - return errorf("%v: invalid sym.other 0x%x", s, elfsym.other) + return errorf("%s: invalid sym.other 0x%x", sb.Name(), elfsym.other) } } } @@ -860,24 +852,27 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio // This keeps textp in increasing address order. for i := uint(0); i < elfobj.nsect; i++ { s := elfobj.sect[i].sym - if s == nil { + if s == 0 { continue } - if s.Sub != nil { - s.Sub = sym.SortSub(s.Sub) + sb := l.MakeSymbolUpdater(s) + if l.SubSym(s) != 0 { + sb.SortSub() } - if s.Type == sym.STEXT { - if s.Attr.OnList() { - return errorf("symbol %s listed multiple times", s.Name) + if sb.Type() == sym.STEXT { + if l.AttrOnList(s) { + return errorf("symbol %s listed multiple times", + l.SymName(s)) } - s.Attr |= sym.AttrOnList + l.SetAttrOnList(s, true) textp = append(textp, s) - for s = s.Sub; s != nil; s = s.Sub { - if s.Attr.OnList() { - return errorf("symbol %s listed multiple times", s.Name) + for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) { + if l.AttrOnList(ss) { + return errorf("symbol %s listed multiple times", + l.SymName(ss)) } - s.Attr |= sym.AttrOnList - textp = append(textp, s) + l.SetAttrOnList(ss, true) + textp = append(textp, ss) } } } @@ -900,7 +895,7 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio rela = 1 } n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) - r := make([]sym.Reloc, n) + r := make([]loader.Reloc, n) p := rsect.base for j := 0; j < n; j++ { var add uint64 @@ -951,22 +946,22 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio } if symIdx == 0 { // absolute relocation, don't bother reading the null symbol - rp.Sym = nil + rp.Sym = 0 } else { var elfsym ElfSym - if err := readelfsym(newSym, lookup, arch, elfobj, symIdx, &elfsym, 0, 0); err != nil { + if err := readelfsym(newSym, lookup, l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil { return errorf("malformed elf file: %v", err) } elfsym.sym = symbols[symIdx] - if elfsym.sym == nil { - return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, symIdx, elfsym.name, elfsym.shndx, elfsym.type_) + if elfsym.sym == 0 { + return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", l.SymName(sect.sym), j, int(symIdx), elfsym.name, elfsym.shndx, elfsym.type_) } rp.Sym = elfsym.sym } rp.Type = objabi.ElfRelocOffset + objabi.RelocType(relocType) - rp.Siz, err = relSize(arch, pn, uint32(relocType)) + rp.Size, err = relSize(arch, pn, uint32(relocType)) if err != nil { return nil, 0, err } @@ -974,30 +969,30 @@ func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio rp.Add = int64(add) } else { // load addend from image - if rp.Siz == 4 { + if rp.Size == 4 { rp.Add = int64(e.Uint32(sect.base[rp.Off:])) - } else if rp.Siz == 8 { + } else if rp.Size == 8 { rp.Add = int64(e.Uint64(sect.base[rp.Off:])) } else { - return errorf("invalid rela size %d", rp.Siz) + return errorf("invalid rela size %d", rp.Size) } } - if rp.Siz == 2 { + if rp.Size == 2 { rp.Add = int64(int16(rp.Add)) } - if rp.Siz == 4 { + if rp.Size == 4 { rp.Add = int64(int32(rp.Add)) } } //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); - sort.Sort(sym.RelocByOff(r[:n])) + sort.Sort(loader.RelocByOff(r[:n])) // just in case - s := sect.sym - s.R = r - s.R = s.R[:n] + sb := l.MakeSymbolUpdater(sect.sym) + r = r[:n] + sb.SetRelocs(r) } return textp, ehdrFlags, nil @@ -1022,16 +1017,16 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { return err } - sect.base = make([]byte, sect.size) elfobj.f.MustSeek(int64(uint64(elfobj.base)+sect.off), 0) - if _, err := io.ReadFull(elfobj.f, sect.base); err != nil { + sect.base, sect.readOnlyMem, err = elfobj.f.Slice(uint64(sect.size)) + if err != nil { return fmt.Errorf("short read: %v", err) } return nil } -func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) { +func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) { if i >= elfobj.nsymtab || i < 0 { err = fmt.Errorf("invalid elf symbol index") return err @@ -1063,7 +1058,8 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int elfsym.other = b.Other } - var s *sym.Symbol + var s loader.Sym + if elfsym.name == "_GLOBAL_OFFSET_TABLE_" { elfsym.name = ".got" } @@ -1090,8 +1086,12 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int // TODO(minux): correctly handle __i686.get_pc_thunk.bx without // set dupok generally. See https://golang.org/cl/5823055 // comment #5 for details. - if s != nil && elfsym.other == 2 { - s.Attr |= sym.AttrDuplicateOK | sym.AttrVisibilityHidden + if s != 0 && elfsym.other == 2 { + if !l.IsExternal(s) { + l.MakeSymbolUpdater(s) + } + l.SetAttrDuplicateOK(s, true) + l.SetAttrVisibilityHidden(s, true) } } @@ -1107,9 +1107,8 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int // so put it in the hash table. if needSym != 0 { s = lookup(elfsym.name, localSymVersion) - s.Attr |= sym.AttrVisibilityHidden + l.SetAttrVisibilityHidden(s, true) } - break } @@ -1121,20 +1120,19 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int // reduce mem use, but also (possibly) make it harder // to debug problems. s = newSym(elfsym.name, localSymVersion) - - s.Attr |= sym.AttrVisibilityHidden + l.SetAttrVisibilityHidden(s, true) } case ElfSymBindWeak: if needSym != 0 { s = lookup(elfsym.name, 0) if elfsym.other == 2 { - s.Attr |= sym.AttrVisibilityHidden + l.SetAttrVisibilityHidden(s, true) } // Allow weak symbols to be duplicated when already defined. - if s.Outer != nil { - s.Attr |= sym.AttrDuplicateOK + if l.OuterSym(s) != 0 { + l.SetAttrDuplicateOK(s, true) } } @@ -1146,8 +1144,9 @@ func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make // sense and should be removed when someone has thought about it properly. - if s != nil && s.Type == 0 && !s.Attr.VisibilityHidden() && elfsym.type_ != ElfSymTypeSection { - s.Type = sym.SXREF + if s != 0 && l.SymType(s) == 0 && !l.AttrVisibilityHidden(s) && elfsym.type_ != ElfSymTypeSection { + sb := l.MakeSymbolUpdater(s) + sb.SetType(sym.SXREF) } elfsym.sym = s diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 0adc395fef..7cc846a19e 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -7,14 +7,15 @@ package loader import ( "bytes" "cmd/internal/bio" - "cmd/internal/dwarf" "cmd/internal/goobj2" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/sym" + "debug/elf" "fmt" "log" + "math/bits" "os" "sort" "strconv" @@ -30,13 +31,11 @@ type Sym int // Relocs encapsulates the set of relocations on a given symbol; an // instance of this type is returned by the Loader Relocs() method. type Relocs struct { - Count int // number of relocs + rs []goobj2.Reloc2 li int // local index of symbol whose relocs we're examining r *oReader // object reader for containing package l *Loader // loader - - ext *sym.Symbol // external symbol if not nil } // Reloc contains the payload for a specific relocation. @@ -50,6 +49,42 @@ type Reloc struct { Sym Sym // global index of symbol the reloc addresses } +// Reloc2 holds a "handle" to access a relocation record from an +// object file. +type Reloc2 struct { + *goobj2.Reloc2 + r *oReader + l *Loader + + // External reloc types may not fit into a uint8 which the Go object file uses. + // Store it here, instead of in the byte of goobj2.Reloc2. + // For Go symbols this will always be 0. + // goobj2.Reloc2.Type() + typ is always the right type, for both Go and external + // symbols. + typ objabi.RelocType +} + +func (rel Reloc2) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc2.Type()) + rel.typ } +func (rel Reloc2) Sym() Sym { return rel.l.resolve(rel.r, rel.Reloc2.Sym()) } +func (rel Reloc2) SetSym(s Sym) { rel.Reloc2.SetSym(goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) } + +func (rel Reloc2) SetType(t objabi.RelocType) { + if t != objabi.RelocType(uint8(t)) { + panic("SetType: type doesn't fit into Reloc2") + } + rel.Reloc2.SetType(uint8(t)) +} + +// Aux2 holds a "handle" to access an aux symbol record from an +// object file. +type Aux2 struct { + *goobj2.Aux2 + r *oReader + l *Loader +} + +func (a Aux2) Sym() Sym { return a.l.resolve(a.r, a.Aux2.Sym()) } + // oReader is a wrapper type of obj.Reader, along with some // extra information. // TODO: rename to objReader once the old one is gone? @@ -59,13 +94,24 @@ type oReader struct { version int // version of static symbol flags uint32 // read from object file pkgprefix string - rcache []Sym // cache mapping local PkgNone symbol to resolved Sym + syms []Sym // Sym's global index, indexed by local index + ndef int // cache goobj2.Reader.NSym() + objidx uint32 // index of this reader in the objs slice } type objIdx struct { r *oReader i Sym // start index - e Sym // end index +} + +// objSym represents a symbol in an object file. It is a tuple of +// the object and the symbol's local index. +// For external symbols, r is l.extReader, s is its index into the +// payload array. +// {nil, 0} represents the nil symbol. +type objSym struct { + r *oReader + s int // local index } type nameVer struct { @@ -73,47 +119,134 @@ type nameVer struct { v int } -type bitmap []uint32 +type Bitmap []uint32 // set the i-th bit. -func (bm bitmap) Set(i Sym) { +func (bm Bitmap) Set(i Sym) { n, r := uint(i)/32, uint(i)%32 bm[n] |= 1 << r } +// unset the i-th bit. +func (bm Bitmap) Unset(i Sym) { + n, r := uint(i)/32, uint(i)%32 + bm[n] &^= (1 << r) +} + // whether the i-th bit is set. -func (bm bitmap) Has(i Sym) bool { +func (bm Bitmap) Has(i Sym) bool { n, r := uint(i)/32, uint(i)%32 return bm[n]&(1<<r) != 0 } -func makeBitmap(n int) bitmap { - return make(bitmap, (n+31)/32) +// return current length of bitmap in bits. +func (bm Bitmap) Len() int { + return len(bm) * 32 +} +func MakeBitmap(n int) Bitmap { + return make(Bitmap, (n+31)/32) +} + +// growBitmap insures that the specified bitmap has enough capacity, +// reallocating (doubling the size) if needed. +func growBitmap(reqLen int, b Bitmap) Bitmap { + curLen := b.Len() + if reqLen > curLen { + b = append(b, MakeBitmap(reqLen+1-curLen)...) + } + return b } // A Loader loads new object files and resolves indexed symbol references. +// +// Notes on the layout of global symbol index space: +// +// - Go object files are read before host object files; each Go object +// read adds its defined package symbols to the global index space. +// Nonpackage symbols are not yet added. +// +// - In loader.LoadNonpkgSyms, add non-package defined symbols and +// references in all object files to the global index space. +// +// - Host object file loading happens; the host object loader does a +// name/version lookup for each symbol it finds; this can wind up +// extending the external symbol index space range. The host object +// loader stores symbol payloads in loader.payloads using SymbolBuilder. +// +// - For now, in loader.LoadFull we convert all symbols (Go + external) +// to sym.Symbols. +// +// - At some point (when the wayfront is pushed through all of the +// linker), all external symbols will be payload-based, and we can +// get rid of the loader.Syms array. +// +// - Each symbol gets a unique global index. For duplicated and +// overwriting/overwritten symbols, the second (or later) appearance +// of the symbol gets the same global index as the first appearance. type Loader struct { start map[*oReader]Sym // map from object file to its start index objs []objIdx // sorted by start index (i.e. objIdx.i) - max Sym // current max index extStart Sym // from this index on, the symbols are externally defined - extSyms []nameVer // externally defined symbols builtinSyms []Sym // global index of builtin symbols - ocache int // index (into 'objs') of most recent lookup + + objSyms []objSym // global index mapping to local index symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name - overwrite map[Sym]Sym // overwrite[i]=j if symbol j overwrites symbol i + + extReader *oReader // a dummy oReader, for external symbols + payloadBatch []extSymPayload + payloads []*extSymPayload // contents of linker-materialized external syms + values []int64 // symbol values, indexed by global sym index itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.* objByPkg map[string]*oReader // map package path to its Go object reader - Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now. + Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now. + symBatch []sym.Symbol // batch of symbols. anonVersion int // most recently assigned ext static sym pseudo-version - Reachable bitmap // bitmap of reachable symbols, indexed by global index + // Bitmaps and other side structures used to store data used to store + // symbol flags/attributes; these are to be accessed via the + // corresponding loader "AttrXXX" and "SetAttrXXX" methods. Please + // visit the comments on these methods for more details on the + // semantics / interpretation of the specific flags or attribute. + attrReachable Bitmap // reachable symbols, indexed by global index + attrOnList Bitmap // "on list" symbols, indexed by global index + attrLocal Bitmap // "local" symbols, indexed by global index + attrNotInSymbolTable Bitmap // "not in symtab" symbols, indexed by glob idx + attrVisibilityHidden Bitmap // hidden symbols, indexed by ext sym index + attrDuplicateOK Bitmap // dupOK symbols, indexed by ext sym index + attrShared Bitmap // shared symbols, indexed by ext sym index + attrExternal Bitmap // external symbols, indexed by ext sym index + + attrReadOnly map[Sym]bool // readonly data for this sym + attrTopFrame map[Sym]struct{} // top frame symbols + attrSpecial map[Sym]struct{} // "special" frame symbols + attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols + attrCgoExportStatic map[Sym]struct{} // "cgo_export_static" symbols + + // Outer and Sub relations for symbols. + // TODO: figure out whether it's more efficient to just have these + // as fields on extSymPayload (note that this won't be a viable + // strategy if somewhere in the linker we set sub/outer for a + // non-external sym). + outer map[Sym]Sym + sub map[Sym]Sym + + align map[Sym]int32 // stores alignment for symbols + + dynimplib map[Sym]string // stores Dynimplib symbol attribute + dynimpvers map[Sym]string // stores Dynimpvers symbol attribute + localentry map[Sym]uint8 // stores Localentry symbol attribute + extname map[Sym]string // stores Extname symbol attribute + elfType map[Sym]elf.SymType // stores elf type symbol property + symFile map[Sym]string // stores file for shlib-derived syms + plt map[Sym]int32 // stores dynimport for pe objects + got map[Sym]int32 // stores got for pe objects + dynid map[Sym]int32 // stores Dynid for symbol // Used to implement field tracking; created during deadcode if // field tracking is enabled. Reachparent[K] contains the index of @@ -125,6 +258,32 @@ type Loader struct { flags uint32 strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled + + elfsetstring elfsetstringFunc + + SymLookup func(name string, ver int) *sym.Symbol +} + +const ( + pkgDef = iota + nonPkgDef + nonPkgRef +) + +type elfsetstringFunc func(s *sym.Symbol, str string, off int) + +// extSymPayload holds the payload (data + relocations) for linker-synthesized +// external symbols (note that symbol value is stored in a separate slice). +type extSymPayload struct { + name string // TODO: would this be better as offset into str table? + size int64 + ver int + kind sym.SymKind + objidx uint32 // index of original object if sym made by cloneToExternal + gotype Sym // Gotype (0 if not present) + relocs []goobj2.Reloc2 + reltypes []objabi.RelocType // relocation types + data []byte } const ( @@ -132,26 +291,40 @@ const ( FlagStrictDups = 1 << iota ) -func NewLoader(flags uint32) *Loader { +func NewLoader(flags uint32, elfsetstring elfsetstringFunc) *Loader { nbuiltin := goobj2.NBuiltin() return &Loader{ - start: make(map[*oReader]Sym), - objs: []objIdx{{nil, 0, 0}}, - symsByName: [2]map[string]Sym{make(map[string]Sym), make(map[string]Sym)}, - objByPkg: make(map[string]*oReader), - overwrite: make(map[Sym]Sym), - itablink: make(map[Sym]struct{}), - extStaticSyms: make(map[nameVer]Sym), - builtinSyms: make([]Sym, nbuiltin), - flags: flags, + start: make(map[*oReader]Sym), + objs: []objIdx{{}}, // reserve index 0 for nil symbol + objSyms: []objSym{{}}, // reserve index 0 for nil symbol + extReader: &oReader{}, + symsByName: [2]map[string]Sym{make(map[string]Sym, 100000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols + objByPkg: make(map[string]*oReader), + outer: make(map[Sym]Sym), + sub: make(map[Sym]Sym), + align: make(map[Sym]int32), + dynimplib: make(map[Sym]string), + dynimpvers: make(map[Sym]string), + localentry: make(map[Sym]uint8), + extname: make(map[Sym]string), + attrReadOnly: make(map[Sym]bool), + elfType: make(map[Sym]elf.SymType), + symFile: make(map[Sym]string), + plt: make(map[Sym]int32), + got: make(map[Sym]int32), + dynid: make(map[Sym]int32), + attrTopFrame: make(map[Sym]struct{}), + attrSpecial: make(map[Sym]struct{}), + attrCgoExportDynamic: make(map[Sym]struct{}), + attrCgoExportStatic: make(map[Sym]struct{}), + itablink: make(map[Sym]struct{}), + extStaticSyms: make(map[nameVer]Sym), + builtinSyms: make([]Sym, nbuiltin), + flags: flags, + elfsetstring: elfsetstring, } } -// Return the start index in the global index space for a given object file. -func (l *Loader) startIndex(r *oReader) Sym { - return l.start[r] -} - // Add object file r, return the start index. func (l *Loader) addObj(pkg string, r *oReader) Sym { if _, ok := l.start[r]; ok { @@ -161,89 +334,176 @@ func (l *Loader) addObj(pkg string, r *oReader) Sym { if _, ok := l.objByPkg[pkg]; !ok { l.objByPkg[pkg] = r } - n := r.NSym() + r.NNonpkgdef() - i := l.max + 1 + i := Sym(len(l.objSyms)) l.start[r] = i - l.objs = append(l.objs, objIdx{r, i, i + Sym(n) - 1}) - l.max += Sym(n) + l.objs = append(l.objs, objIdx{r, i}) return i } -// Add a symbol with a given index, return if it is added. -func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ sym.SymKind) bool { +// Add a symbol from an object file, return the global index and whether it is added. +// If the symbol already exist, it returns the index of that symbol. +func (l *Loader) AddSym(name string, ver int, r *oReader, li int, kind int, dupok bool, typ sym.SymKind) (Sym, bool) { if l.extStart != 0 { - panic("AddSym called after AddExtSym is called") + panic("AddSym called after external symbol is created") + } + i := Sym(len(l.objSyms)) + addToGlobal := func() { + l.objSyms = append(l.objSyms, objSym{r, li}) + } + if name == "" { + addToGlobal() + return i, true // unnamed aux symbol } if ver == r.version { // Static symbol. Add its global index but don't // add to name lookup table, as it cannot be // referenced by name. - return true - } - if oldi, ok := l.symsByName[ver][name]; ok { - if dupok { - if l.flags&FlagStrictDups != 0 { - l.checkdup(name, i, r, oldi) - } - return false - } - oldr, li := l.toLocal(oldi) - oldsym := goobj2.Sym{} - oldsym.Read(oldr.Reader, oldr.SymOff(li)) - if oldsym.Dupok() { - return false - } - overwrite := r.DataSize(int(i-l.startIndex(r))) != 0 - if overwrite { - // new symbol overwrites old symbol. - oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type)] - if !oldtyp.IsData() && r.DataSize(li) == 0 { - log.Fatalf("duplicated definition of symbol " + name) - } - l.overwrite[oldi] = i - } else { - // old symbol overwrites new symbol. - if typ != sym.SDATA && typ != sym.SNOPTRDATA && typ != sym.SBSS && typ != sym.SNOPTRBSS { // only allow overwriting data symbol - log.Fatalf("duplicated definition of symbol " + name) - } - l.overwrite[i] = oldi - return false - } + addToGlobal() + return i, true + } + if kind == pkgDef { + // Defined package symbols cannot be dup to each other. + // We load all the package symbols first, so we don't need + // to check dup here. + // We still add it to the lookup table, as it may still be + // referenced by name (e.g. through linkname). + l.symsByName[ver][name] = i + addToGlobal() + return i, true } - l.symsByName[ver][name] = i - return true -} -// Add an external symbol (without index). Return the index of newly added -// symbol, or 0 if not added. -func (l *Loader) AddExtSym(name string, ver int) Sym { - static := ver >= sym.SymVerStatic - if static { - if _, ok := l.extStaticSyms[nameVer{name, ver}]; ok { - return 0 + // Non-package (named) symbol. Check if it already exists. + oldi, existed := l.symsByName[ver][name] + if !existed { + l.symsByName[ver][name] = i + addToGlobal() + return i, true + } + // symbol already exists + if dupok { + if l.flags&FlagStrictDups != 0 { + l.checkdup(name, r, li, oldi) } + return oldi, false + } + oldr, oldli := l.toLocal(oldi) + oldsym := oldr.Sym2(oldli) + if oldsym.Dupok() { + return oldi, false + } + overwrite := r.DataSize(li) != 0 + if overwrite { + // new symbol overwrites old symbol. + oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())] + if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) { + log.Fatalf("duplicated definition of symbol " + name) + } + l.objSyms[oldi] = objSym{r, li} } else { - if _, ok := l.symsByName[ver][name]; ok { - return 0 + // old symbol overwrites new symbol. + if !typ.IsData() { // only allow overwriting data symbol + log.Fatalf("duplicated definition of symbol " + name) } } - i := l.max + 1 + return oldi, true +} + +// newExtSym creates a new external sym with the specified +// name/version. +func (l *Loader) newExtSym(name string, ver int) Sym { + i := Sym(len(l.objSyms)) + if l.extStart == 0 { + l.extStart = i + } + l.growSyms(int(i)) + pi := l.newPayload(name, ver) + l.objSyms = append(l.objSyms, objSym{l.extReader, int(pi)}) + l.extReader.syms = append(l.extReader.syms, i) + return i +} + +// LookupOrCreateSym looks up the symbol with the specified name/version, +// returning its Sym index if found. If the lookup fails, a new external +// Sym will be created, entered into the lookup tables, and returned. +func (l *Loader) LookupOrCreateSym(name string, ver int) Sym { + i := l.Lookup(name, ver) + if i != 0 { + return i + } + i = l.newExtSym(name, ver) + static := ver >= sym.SymVerStatic || ver < 0 if static { l.extStaticSyms[nameVer{name, ver}] = i } else { l.symsByName[ver][name] = i } - l.max++ - if l.extStart == 0 { - l.extStart = i - } - l.extSyms = append(l.extSyms, nameVer{name, ver}) - l.growSyms(int(i)) return i } func (l *Loader) IsExternal(i Sym) bool { - return l.extStart != 0 && i >= l.extStart + r, _ := l.toLocal(i) + return l.isExtReader(r) +} + +func (l *Loader) isExtReader(r *oReader) bool { + return r == l.extReader +} + +// For external symbol, return its index in the payloads array. +// XXX result is actually not a global index. We (ab)use the Sym type +// so we don't need conversion for accessing bitmaps. +func (l *Loader) extIndex(i Sym) Sym { + _, li := l.toLocal(i) + return Sym(li) +} + +// Get a new payload for external symbol, return its index in +// the payloads array. +func (l *Loader) newPayload(name string, ver int) int { + pi := len(l.payloads) + pp := l.allocPayload() + pp.name = name + pp.ver = ver + l.payloads = append(l.payloads, pp) + l.growExtAttrBitmaps() + return pi +} + +// getPayload returns a pointer to the extSymPayload struct for an +// external symbol if the symbol has a payload. Will panic if the +// symbol in question is bogus (zero or not an external sym). +func (l *Loader) getPayload(i Sym) *extSymPayload { + if !l.IsExternal(i) { + panic(fmt.Sprintf("bogus symbol index %d in getPayload", i)) + } + pi := l.extIndex(i) + return l.payloads[pi] +} + +// allocPayload allocates a new payload. +func (l *Loader) allocPayload() *extSymPayload { + batch := l.payloadBatch + if len(batch) == 0 { + batch = make([]extSymPayload, 1000) + } + p := &batch[0] + l.payloadBatch = batch[1:] + return p +} + +func (ms *extSymPayload) Grow(siz int64) { + if int64(int(siz)) != siz { + log.Fatalf("symgrow size %d too long", siz) + } + if int64(len(ms.data)) >= siz { + return + } + if cap(ms.data) < int(siz) { + cl := len(ms.data) + ms.data = append(ms.data, make([]byte, int(siz)+1-cl)...) + ms.data = ms.data[0:cl] + } + ms.data = ms.data[:siz] } // Ensure Syms slice has enough space. @@ -253,57 +513,18 @@ func (l *Loader) growSyms(i int) { return } l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...) + l.growValues(int(i) + 1) + l.growAttrBitmaps(int(i) + 1) } // Convert a local index to a global index. func (l *Loader) toGlobal(r *oReader, i int) Sym { - g := l.startIndex(r) + Sym(i) - if ov, ok := l.overwrite[g]; ok { - return ov - } - return g + return r.syms[i] } // Convert a global index to a local index. func (l *Loader) toLocal(i Sym) (*oReader, int) { - if ov, ok := l.overwrite[i]; ok { - i = ov - } - if l.IsExternal(i) { - return nil, int(i - l.extStart) - } - oc := l.ocache - if oc != 0 && i >= l.objs[oc].i && i <= l.objs[oc].e { - return l.objs[oc].r, int(i - l.objs[oc].i) - } - // Search for the local object holding index i. - // Below k is the first one that has its start index > i, - // so k-1 is the one we want. - k := sort.Search(len(l.objs), func(k int) bool { - return l.objs[k].i > i - }) - l.ocache = k - 1 - return l.objs[k-1].r, int(i - l.objs[k-1].i) -} - -// rcacheGet checks for a valid entry for 's' in the readers cache, -// where 's' is a local PkgIdxNone ref or def, or zero if -// the cache is empty or doesn't contain a value for 's'. -func (or *oReader) rcacheGet(symIdx uint32) Sym { - if len(or.rcache) > 0 { - return or.rcache[symIdx] - } - return 0 -} - -// rcacheSet installs a new entry in the oReader's PkgNone -// resolver cache for the specified PkgIdxNone ref or def, -// allocating a new cache if needed. -func (or *oReader) rcacheSet(symIdx uint32, gsym Sym) { - if len(or.rcache) == 0 { - or.rcache = make([]Sym, or.NNonpkgdef()+or.NNonpkgref()) - } - or.rcache[symIdx] = gsym + return l.objSyms[i].r, int(l.objSyms[i].s) } // Resolve a local symbol reference. Return global index. @@ -311,25 +532,19 @@ func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym { var rr *oReader switch p := s.PkgIdx; p { case goobj2.PkgIdxInvalid: + // {0, X} with non-zero X is never a valid sym reference from a Go object. + // We steal this space for symbol references from external objects. + // In this case, X is just the global index. + if l.isExtReader(r) { + return Sym(s.SymIdx) + } if s.SymIdx != 0 { panic("bad sym ref") } return 0 case goobj2.PkgIdxNone: - // Check for cached version first - if cached := r.rcacheGet(s.SymIdx); cached != 0 { - return cached - } - // Resolve by name - i := int(s.SymIdx) + r.NSym() - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(i)) - name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1) - v := abiToVer(osym.ABI, r.version) - gsym := l.Lookup(name, v) - // Add to cache, then return. - r.rcacheSet(s.SymIdx, gsym) - return gsym + i := int(s.SymIdx) + r.ndef + return r.syms[i] case goobj2.PkgIdxBuiltin: return l.builtinSyms[s.SymIdx] case goobj2.PkgIdxSelf: @@ -355,44 +570,11 @@ func (l *Loader) Lookup(name string, ver int) Sym { return l.symsByName[ver][name] } -// Returns whether i is a dup of another symbol, and i is not -// "primary", i.e. Lookup i by name will not return i. -func (l *Loader) IsDup(i Sym) bool { - if _, ok := l.overwrite[i]; ok { - return true - } - if l.IsExternal(i) { - return false - } - r, li := l.toLocal(i) - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(li)) - if !osym.Dupok() { - return false - } - if osym.Name == "" { - return false // Unnamed aux symbol cannot be dup. - } - if osym.ABI == goobj2.SymABIstatic { - return false // Static symbol cannot be dup. - } - name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1) - ver := abiToVer(osym.ABI, r.version) - return l.symsByName[ver][name] != i -} - // Check that duplicate symbols have same contents. -func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) { - li := int(i - l.startIndex(r)) +func (l *Loader) checkdup(name string, r *oReader, li int, dup Sym) { p := r.Data(li) - if strings.HasPrefix(name, "go.info.") { - p, _ = patchDWARFName1(p, r) - } rdup, ldup := l.toLocal(dup) pdup := rdup.Data(ldup) - if strings.HasPrefix(name, "go.info.") { - pdup, _ = patchDWARFName1(pdup, rdup) - } if bytes.Equal(p, pdup) { return } @@ -419,7 +601,7 @@ func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs } // Number of total symbols. func (l *Loader) NSym() int { - return int(l.max + 1) + return len(l.objSyms) } // Number of defined Go symbols. @@ -430,43 +612,44 @@ func (l *Loader) NDef() int { // Returns the raw (unpatched) name of the i-th symbol. func (l *Loader) RawSymName(i Sym) string { if l.IsExternal(i) { - if s := l.Syms[i]; s != nil { - return s.Name - } - return "" + pp := l.getPayload(i) + return pp.name } r, li := l.toLocal(i) - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(li)) - return osym.Name + return r.Sym2(li).Name(r.Reader) } // Returns the (patched) name of the i-th symbol. func (l *Loader) SymName(i Sym) string { if l.IsExternal(i) { - if s := l.Syms[i]; s != nil { - return s.Name // external name should already be patched? - } - return "" + pp := l.getPayload(i) + return pp.name + } + r, li := l.toLocal(i) + return strings.Replace(r.Sym2(li).Name(r.Reader), "\"\".", r.pkgprefix, -1) +} + +// Returns the version of the i-th symbol. +func (l *Loader) SymVersion(i Sym) int { + if l.IsExternal(i) { + pp := l.getPayload(i) + return pp.ver } r, li := l.toLocal(i) - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(li)) - return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1) + return int(abiToVer(r.Sym2(li).ABI(), r.version)) } // Returns the type of the i-th symbol. func (l *Loader) SymType(i Sym) sym.SymKind { if l.IsExternal(i) { - if s := l.Syms[i]; s != nil { - return s.Type + pp := l.getPayload(i) + if pp != nil { + return pp.kind } return 0 } r, li := l.toLocal(i) - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(li)) - return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)] + return sym.AbiSymKindToSymKind[objabi.SymKind(r.Sym2(li).Type())] } // Returns the attributes of the i-th symbol. @@ -476,16 +659,310 @@ func (l *Loader) SymAttr(i Sym) uint8 { return 0 } r, li := l.toLocal(i) - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(li)) - return osym.Flag + return r.Sym2(li).Flag() +} + +// AttrReachable returns true for symbols that are transitively +// referenced from the entry points. Unreachable symbols are not +// written to the output. +func (l *Loader) AttrReachable(i Sym) bool { + return l.attrReachable.Has(i) } +// SetAttrReachable sets the reachability property for a symbol (see +// AttrReachable). +func (l *Loader) SetAttrReachable(i Sym, v bool) { + if v { + l.attrReachable.Set(i) + } else { + l.attrReachable.Unset(i) + } +} + +// AttrOnList returns true for symbols that are on some list (such as +// the list of all text symbols, or one of the lists of data symbols) +// and is consulted to avoid bugs where a symbol is put on a list +// twice. +func (l *Loader) AttrOnList(i Sym) bool { + return l.attrOnList.Has(i) +} + +// SetAttrOnList sets the "on list" property for a symbol (see +// AttrOnList). +func (l *Loader) SetAttrOnList(i Sym, v bool) { + if v { + l.attrOnList.Set(i) + } else { + l.attrOnList.Unset(i) + } +} + +// AttrLocal returns true for symbols that are only visible within the +// module (executable or shared library) being linked. This attribute +// is applied to thunks and certain other linker-generated symbols. +func (l *Loader) AttrLocal(i Sym) bool { + return l.attrLocal.Has(i) +} + +// SetAttrLocal the "local" property for a symbol (see AttrLocal above). +func (l *Loader) SetAttrLocal(i Sym, v bool) { + if v { + l.attrLocal.Set(i) + } else { + l.attrLocal.Unset(i) + } +} + +// AttrNotInSymbolTable returns true for symbols that should not be +// added to the symbol table of the final generated load module. +func (l *Loader) AttrNotInSymbolTable(i Sym) bool { + return l.attrNotInSymbolTable.Has(i) +} + +// SetAttrNotInSymbolTable the "not in symtab" property for a symbol +// (see AttrNotInSymbolTable above). +func (l *Loader) SetAttrNotInSymbolTable(i Sym, v bool) { + if v { + l.attrNotInSymbolTable.Set(i) + } else { + l.attrNotInSymbolTable.Unset(i) + } +} + +// AttrVisibilityHidden symbols returns true for ELF symbols with +// visibility set to STV_HIDDEN. They become local symbols in +// the final executable. Only relevant when internally linking +// on an ELF platform. +func (l *Loader) AttrVisibilityHidden(i Sym) bool { + if !l.IsExternal(i) { + return false + } + return l.attrVisibilityHidden.Has(l.extIndex(i)) +} + +// SetAttrVisibilityHidden sets the "hidden visibility" property for a +// symbol (see AttrVisibilityHidden). +func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) { + if !l.IsExternal(i) { + panic("tried to set visibility attr on non-external symbol") + } + if v { + l.attrVisibilityHidden.Set(l.extIndex(i)) + } else { + l.attrVisibilityHidden.Unset(l.extIndex(i)) + } +} + +// AttrDuplicateOK returns true for a symbol that can be present in +// multiple object files. +func (l *Loader) AttrDuplicateOK(i Sym) bool { + if !l.IsExternal(i) { + // TODO: if this path winds up being taken frequently, it + // might make more sense to copy the flag value out of the object + // into a larger bitmap during preload. + r, li := l.toLocal(i) + return r.Sym2(li).Dupok() + } + return l.attrDuplicateOK.Has(l.extIndex(i)) +} + +// SetAttrDuplicateOK sets the "duplicate OK" property for an external +// symbol (see AttrDuplicateOK). +func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) { + if !l.IsExternal(i) { + panic("tried to set dupok attr on non-external symbol") + } + if v { + l.attrDuplicateOK.Set(l.extIndex(i)) + } else { + l.attrDuplicateOK.Unset(l.extIndex(i)) + } +} + +// AttrShared returns true for symbols compiled with the -shared option. +func (l *Loader) AttrShared(i Sym) bool { + if !l.IsExternal(i) { + // TODO: if this path winds up being taken frequently, it + // might make more sense to copy the flag value out of the + // object into a larger bitmap during preload. + r, _ := l.toLocal(i) + return (r.Flags() & goobj2.ObjFlagShared) != 0 + } + return l.attrShared.Has(l.extIndex(i)) +} + +// SetAttrShared sets the "shared" property for an external +// symbol (see AttrShared). +func (l *Loader) SetAttrShared(i Sym, v bool) { + if !l.IsExternal(i) { + panic(fmt.Sprintf("tried to set shared attr on non-external symbol %d %s", i, l.SymName(i))) + } + if v { + l.attrShared.Set(l.extIndex(i)) + } else { + l.attrShared.Unset(l.extIndex(i)) + } +} + +// AttrExternal returns true for function symbols loaded from host +// object files. +func (l *Loader) AttrExternal(i Sym) bool { + if !l.IsExternal(i) { + return false + } + return l.attrExternal.Has(l.extIndex(i)) +} + +// SetAttrExternal sets the "external" property for an host object +// symbol (see AttrExternal). +func (l *Loader) SetAttrExternal(i Sym, v bool) { + if !l.IsExternal(i) { + panic(fmt.Sprintf("tried to set external attr on non-external symbol %q", l.RawSymName(i))) + } + if v { + l.attrExternal.Set(l.extIndex(i)) + } else { + l.attrExternal.Unset(l.extIndex(i)) + } +} + +// AttrTopFrame returns true for a function symbol that is an entry +// point, meaning that unwinders should stop when they hit this +// function. +func (l *Loader) AttrTopFrame(i Sym) bool { + _, ok := l.attrTopFrame[i] + return ok +} + +// SetAttrTopFrame sets the "top frame" property for a symbol (see +// AttrTopFrame). +func (l *Loader) SetAttrTopFrame(i Sym, v bool) { + if v { + l.attrTopFrame[i] = struct{}{} + } else { + delete(l.attrTopFrame, i) + } +} + +// AttrSpecial returns true for a symbols that do not have their +// address (i.e. Value) computed by the usual mechanism of +// data.go:dodata() & data.go:address(). +func (l *Loader) AttrSpecial(i Sym) bool { + _, ok := l.attrSpecial[i] + return ok +} + +// SetAttrSpecial sets the "special" property for a symbol (see +// AttrSpecial). +func (l *Loader) SetAttrSpecial(i Sym, v bool) { + if v { + l.attrSpecial[i] = struct{}{} + } else { + delete(l.attrSpecial, i) + } +} + +// AttrCgoExportDynamic returns true for a symbol that has been +// specially marked via the "cgo_export_dynamic" compiler directive +// written by cgo (in response to //export directives in the source). +func (l *Loader) AttrCgoExportDynamic(i Sym) bool { + _, ok := l.attrCgoExportDynamic[i] + return ok +} + +// SetAttrCgoExportDynamic sets the "cgo_export_dynamic" for a symbol +// (see AttrCgoExportDynamic). +func (l *Loader) SetAttrCgoExportDynamic(i Sym, v bool) { + if v { + l.attrCgoExportDynamic[i] = struct{}{} + } else { + delete(l.attrCgoExportDynamic, i) + } +} + +// AttrCgoExportStatic returns true for a symbol that has been +// specially marked via the "cgo_export_static" directive +// written by cgo. +func (l *Loader) AttrCgoExportStatic(i Sym) bool { + _, ok := l.attrCgoExportStatic[i] + return ok +} + +// SetAttrCgoExportStatic sets the "cgo_export_static" for a symbol +// (see AttrCgoExportStatic). +func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) { + if v { + l.attrCgoExportStatic[i] = struct{}{} + } else { + delete(l.attrCgoExportStatic, i) + } +} + +func (l *Loader) AttrCgoExport(i Sym) bool { + return l.AttrCgoExportDynamic(i) || l.AttrCgoExportStatic(i) +} + +// AttrReadOnly returns true for a symbol whose underlying data +// is stored via a read-only mmap. +func (l *Loader) AttrReadOnly(i Sym) bool { + if v, ok := l.attrReadOnly[i]; ok { + return v + } + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp.objidx != 0 { + return l.objs[pp.objidx].r.ReadOnly() + } + return false + } + r, _ := l.toLocal(i) + return r.ReadOnly() +} + +// SetAttrReadOnly sets the "data is read only" property for a symbol +// (see AttrReadOnly). +func (l *Loader) SetAttrReadOnly(i Sym, v bool) { + l.attrReadOnly[i] = v +} + +// AttrSubSymbol returns true for symbols that are listed as a +// sub-symbol of some other outer symbol. The sub/outer mechanism is +// used when loading host objects (sections from the host object +// become regular linker symbols and symbols go on the Sub list of +// their section) and for constructing the global offset table when +// internally linking a dynamic executable. +func (l *Loader) AttrSubSymbol(i Sym) bool { + // we don't explicitly store this attribute any more -- return + // a value based on the sub-symbol setting. + return l.OuterSym(i) != 0 +} + +// AttrContainer returns true for symbols that are listed as a +// sub-symbol of some other outer symbol. The sub/outer mechanism is +// used when loading host objects (sections from the host object +// become regular linker symbols and symbols go on the Sub list of +// their section) and for constructing the global offset table when +// internally linking a dynamic executable. +func (l *Loader) AttrContainer(i Sym) bool { + // we don't explicitly store this attribute any more -- return + // a value based on the sub-symbol setting. + return l.SubSym(i) != 0 +} + +// Note that we don't have SetAttrSubSymbol' or 'SetAttrContainer' methods +// in the loader; clients should just use methods like PrependSub +// to establish these relationships + // Returns whether the i-th symbol has ReflectMethod attribute set. func (l *Loader) IsReflectMethod(i Sym) bool { return l.SymAttr(i)&goobj2.SymFlagReflectMethod != 0 } +// Returns whether the i-th symbol is nosplit. +func (l *Loader) IsNoSplit(i Sym) bool { + return l.SymAttr(i)&goobj2.SymFlagNoSplit != 0 +} + // Returns whether this is a Go type symbol. func (l *Loader) IsGoType(i Sym) bool { return l.SymAttr(i)&goobj2.SymFlagGoType != 0 @@ -499,11 +976,30 @@ func (l *Loader) IsItabLink(i Sym) bool { return false } +// growValues grows the slice used to store symbol values. +func (l *Loader) growValues(reqLen int) { + curLen := len(l.values) + if reqLen > curLen { + l.values = append(l.values, make([]int64, reqLen+1-curLen)...) + } +} + +// SymValue returns the value of the i-th symbol. i is global index. +func (l *Loader) SymValue(i Sym) int64 { + return l.values[i] +} + +// SetSymValue sets the value of the i-th symbol. i is global index. +func (l *Loader) SetSymValue(i Sym, val int64) { + l.values[i] = val +} + // Returns the symbol content of the i-th symbol. i is global index. func (l *Loader) Data(i Sym) []byte { if l.IsExternal(i) { - if s := l.Syms[i]; s != nil { - return s.P + pp := l.getPayload(i) + if pp != nil { + return pp.data } return nil } @@ -511,184 +1007,550 @@ func (l *Loader) Data(i Sym) []byte { return r.Data(li) } -// Returns the number of aux symbols given a global index. -func (l *Loader) NAux(i Sym) int { +// SymAlign returns the alignment for a symbol. +func (l *Loader) SymAlign(i Sym) int32 { + // If an alignment has been recorded, return that. + if align, ok := l.align[i]; ok { + return align + } + // TODO: would it make sense to return an arch-specific + // alignment depending on section type? E.g. STEXT => 32, + // SDATA => 1, etc? + return 0 +} + +// SetSymAlign sets the alignment for a symbol. +func (l *Loader) SetSymAlign(i Sym, align int32) { + // reject bad synbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymAlign") + } + // Reject nonsense alignments. + // TODO: do we need this? + if align < 0 { + panic("bad alignment value") + } + if align == 0 { + delete(l.align, i) + } else { + // Alignment should be a power of 2. + if bits.OnesCount32(uint32(align)) != 1 { + panic("bad alignment value") + } + l.align[i] = align + } +} + +// SymDynImplib returns the "dynimplib" attribute for the specified +// symbol, making up a portion of the info for a symbol specified +// on a "cgo_import_dynamic" compiler directive. +func (l *Loader) SymDynimplib(i Sym) string { + return l.dynimplib[i] +} + +// SetSymDynimplib sets the "dynimplib" attribute for a symbol. +func (l *Loader) SetSymDynimplib(i Sym, value string) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetDynimplib") + } + if value == "" { + delete(l.dynimplib, i) + } else { + l.dynimplib[i] = value + } +} + +// SymDynimpvers returns the "dynimpvers" attribute for the specified +// symbol, making up a portion of the info for a symbol specified +// on a "cgo_import_dynamic" compiler directive. +func (l *Loader) SymDynimpvers(i Sym) string { + return l.dynimpvers[i] +} + +// SetSymDynimpvers sets the "dynimpvers" attribute for a symbol. +func (l *Loader) SetSymDynimpvers(i Sym, value string) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetDynimpvers") + } + if value == "" { + delete(l.dynimpvers, i) + } else { + l.dynimpvers[i] = value + } +} + +// SymExtname returns the "extname" value for the specified +// symbol. +func (l *Loader) SymExtname(i Sym) string { + if s, ok := l.extname[i]; ok { + return s + } + return l.SymName(i) +} + +// SetSymExtname sets the "extname" attribute for a symbol. +func (l *Loader) SetSymExtname(i Sym, value string) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetExtname") + } + if value == "" { + delete(l.extname, i) + } else { + l.extname[i] = value + } +} + +// SymElfType returns the previously recorded ELF type for a symbol +// (used only for symbols read from shared libraries by ldshlibsyms). +// It is not set for symbols defined by the packages being linked or +// by symbols read by ldelf (and so is left as elf.STT_NOTYPE). +func (l *Loader) SymElfType(i Sym) elf.SymType { + if et, ok := l.elfType[i]; ok { + return et + } + return elf.STT_NOTYPE +} + +// SetSymElfType sets the elf type attribute for a symbol. +func (l *Loader) SetSymElfType(i Sym, et elf.SymType) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymElfType") + } + if et == elf.STT_NOTYPE { + delete(l.elfType, i) + } else { + l.elfType[i] = et + } +} + +// SymPlt returns the plt value for pe symbols. +func (l *Loader) SymPlt(s Sym) int32 { + if v, ok := l.plt[s]; ok { + return v + } + return -1 +} + +// SetPlt sets the plt value for pe symbols. +func (l *Loader) SetPlt(i Sym, v int32) { + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol for SetPlt") + } + if v == -1 { + delete(l.plt, i) + } else { + l.plt[i] = v + } +} + +// SymGot returns the got value for pe symbols. +func (l *Loader) SymGot(s Sym) int32 { + if v, ok := l.got[s]; ok { + return v + } + return -1 +} + +// SetGot sets the got value for pe symbols. +func (l *Loader) SetGot(i Sym, v int32) { + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol for SetGot") + } + if v == -1 { + delete(l.got, i) + } else { + l.got[i] = v + } +} + +// SymDynid returns the "dynid" property for the specified symbol. +func (l *Loader) SymDynid(i Sym) int32 { + if s, ok := l.dynid[i]; ok { + return s + } + return -1 +} + +// SetSymDynid sets the "dynid" property for a symbol. +func (l *Loader) SetSymDynid(i Sym, val int32) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymDynid") + } + if val == -1 { + delete(l.dynid, i) + } else { + l.dynid[i] = val + } +} + +// SymGoType returns the 'Gotype' property for a given symbol (set by +// the Go compiler for variable symbols). This version relies on +// reading aux symbols for the target sym -- it could be that a faster +// approach would be to check for gotype during preload and copy the +// results in to a map (might want to try this at some point and see +// if it helps speed things up). +func (l *Loader) SymGoType(i Sym) Sym { if l.IsExternal(i) { - return 0 + pp := l.getPayload(i) + return pp.gotype } r, li := l.toLocal(i) - return r.NAux(li) + auxs := r.Auxs2(li) + for j := range auxs { + a := &auxs[j] + switch a.Type() { + case goobj2.AuxGotype: + return l.resolve(r, a.Sym()) + } + } + return 0 +} + +// SymUnit returns the compilation unit for a given symbol (which will +// typically be nil for external or linker-manufactured symbols). +func (l *Loader) SymUnit(i Sym) *sym.CompilationUnit { + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp.objidx != 0 { + r := l.objs[pp.objidx].r + return r.unit + } + return nil + } + r, _ := l.toLocal(i) + return r.unit +} + +// SymFile returns the file for a symbol, which is normally the +// package the symbol came from (for regular compiler-generated Go +// symbols), but in the case of building with "-linkshared" (when a +// symbol is read from a a shared library), will hold the library +// name. +func (l *Loader) SymFile(i Sym) string { + if l.IsExternal(i) { + if f, ok := l.symFile[i]; ok { + return f + } + pp := l.getPayload(i) + if pp.objidx != 0 { + r := l.objs[pp.objidx].r + return r.unit.Lib.File + } + return "" + } + r, _ := l.toLocal(i) + return r.unit.Lib.File +} + +// SetSymFile sets the file attribute for a symbol. This is +// needed mainly for external symbols, specifically those imported +// from shared libraries. +func (l *Loader) SetSymFile(i Sym, file string) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymFile") + } + if !l.IsExternal(i) { + panic("can't set file for non-external sym") + } + l.symFile[i] = file } -// Returns the referred symbol of the j-th aux symbol of the i-th +// SymLocalentry returns the "local entry" value for the specified // symbol. -func (l *Loader) AuxSym(i Sym, j int) Sym { +func (l *Loader) SymLocalentry(i Sym) uint8 { + return l.localentry[i] +} + +// SetSymLocalentry sets the "local entry" attribute for a symbol. +func (l *Loader) SetSymLocalentry(i Sym, value uint8) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymLocalentry") + } + if value == 0 { + delete(l.localentry, i) + } else { + l.localentry[i] = value + } +} + +// Returns the number of aux symbols given a global index. +func (l *Loader) NAux(i Sym) int { if l.IsExternal(i) { return 0 } r, li := l.toLocal(i) - a := goobj2.Aux{} - a.Read(r.Reader, r.AuxOff(li, j)) - return l.resolve(r, a.Sym) + return r.NAux(li) } -// ReadAuxSyms reads the aux symbol ids for the specified symbol into the -// slice passed as a parameter. If the slice capacity is not large enough, a new -// larger slice will be allocated. Final slice is returned. -func (l *Loader) ReadAuxSyms(symIdx Sym, dst []Sym) []Sym { - if l.IsExternal(symIdx) { - return dst[:0] +// Returns the "handle" to the j-th aux symbol of the i-th symbol. +func (l *Loader) Aux2(i Sym, j int) Aux2 { + if l.IsExternal(i) { + return Aux2{} } - naux := l.NAux(symIdx) - if naux == 0 { - return dst[:0] + r, li := l.toLocal(i) + if j >= r.NAux(li) { + return Aux2{} } + return Aux2{r.Aux2(li, j), r, l} +} - if cap(dst) < naux { - dst = make([]Sym, naux) +// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF +// symbols associated with a given function symbol. Prior to the +// introduction of the loader, this was done purely using name +// lookups, e.f. for function with name XYZ we would then look up +// go.info.XYZ, etc. +// FIXME: once all of dwarfgen is converted over to the loader, +// it would save some space to make these aux symbols nameless. +func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) { + if l.SymType(fnSymIdx) != sym.STEXT { + log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String()) + } + if l.IsExternal(fnSymIdx) { + // Current expectation is that any external function will + // not have auxsyms. + return } - dst = dst[:0] - - r, li := l.toLocal(symIdx) - for i := 0; i < naux; i++ { - a := goobj2.Aux{} - a.Read(r.Reader, r.AuxOff(li, i)) - dst = append(dst, l.resolve(r, a.Sym)) + r, li := l.toLocal(fnSymIdx) + auxs := r.Auxs2(li) + for i := range auxs { + a := &auxs[i] + switch a.Type() { + case goobj2.AuxDwarfInfo: + auxDwarfInfo = l.resolve(r, a.Sym()) + if l.SymType(auxDwarfInfo) != sym.SDWARFINFO { + panic("aux dwarf info sym with wrong type") + } + case goobj2.AuxDwarfLoc: + auxDwarfLoc = l.resolve(r, a.Sym()) + if l.SymType(auxDwarfLoc) != sym.SDWARFLOC { + panic("aux dwarf loc sym with wrong type") + } + case goobj2.AuxDwarfRanges: + auxDwarfRanges = l.resolve(r, a.Sym()) + if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE { + panic("aux dwarf ranges sym with wrong type") + } + case goobj2.AuxDwarfLines: + auxDwarfLines = l.resolve(r, a.Sym()) + if l.SymType(auxDwarfLines) != sym.SDWARFLINES { + panic("aux dwarf lines sym with wrong type") + } + } } + return +} - return dst +// PrependSub prepends 'sub' onto the sub list for outer symbol 'outer'. +// Will panic if 'sub' already has an outer sym or sub sym. +// FIXME: should this be instead a method on SymbolBuilder? +func (l *Loader) PrependSub(outer Sym, sub Sym) { + // NB: this presupposes that an outer sym can't be a sub symbol of + // some other outer-outer sym (I'm assuming this is true, but I + // haven't tested exhaustively). + if l.OuterSym(outer) != 0 { + panic("outer has outer itself") + } + if l.SubSym(sub) != 0 { + panic("sub set for subsym") + } + if l.OuterSym(sub) != 0 { + panic("outer already set for subsym") + } + l.sub[sub] = l.sub[outer] + l.sub[outer] = sub + l.outer[sub] = outer } // OuterSym gets the outer symbol for host object loaded symbols. func (l *Loader) OuterSym(i Sym) Sym { - sym := l.Syms[i] - if sym != nil && sym.Outer != nil { - outer := sym.Outer - return l.Lookup(outer.Name, int(outer.Version)) - } - return 0 + // FIXME: add check for isExternal? + return l.outer[i] } // SubSym gets the subsymbol for host object loaded symbols. func (l *Loader) SubSym(i Sym) Sym { - sym := l.Syms[i] - if sym != nil && sym.Sub != nil { - sub := sym.Sub - return l.Lookup(sub.Name, int(sub.Version)) - } - return 0 + // NB: note -- no check for l.isExternal(), since I am pretty sure + // that later phases in the linker set subsym for "type." syms + return l.sub[i] } -// Initialize Reachable bitmap for running deadcode pass. +// Initialize Reachable bitmap and its siblings for running deadcode pass. func (l *Loader) InitReachable() { - l.Reachable = makeBitmap(l.NSym()) -} - -// At method returns the j-th reloc for a global symbol. -func (relocs *Relocs) At(j int) Reloc { - if relocs.ext != nil { - rel := &relocs.ext.R[j] - return Reloc{ - Off: rel.Off, - Size: rel.Siz, - Type: rel.Type, - Add: rel.Add, - Sym: relocs.l.Lookup(rel.Sym.Name, int(rel.Sym.Version)), - } - } - rel := goobj2.Reloc{} - rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j)) - target := relocs.l.resolve(relocs.r, rel.Sym) - return Reloc{ - Off: rel.Off, - Size: rel.Siz, - Type: objabi.RelocType(rel.Type), - Add: rel.Add, - Sym: target, - } -} - -// ReadAll method reads all relocations for a symbol into the -// specified slice. If the slice capacity is not large enough, a new -// larger slice will be allocated. Final slice is returned. -func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc { - if relocs.Count == 0 { - return dst[:0] - } - - if cap(dst) < relocs.Count { - dst = make([]Reloc, relocs.Count) - } - dst = dst[:0] - - if relocs.ext != nil { - for i := 0; i < relocs.Count; i++ { - erel := &relocs.ext.R[i] - rel := Reloc{ - Off: erel.Off, - Size: erel.Siz, - Type: erel.Type, - Add: erel.Add, - Sym: relocs.l.Lookup(erel.Sym.Name, int(erel.Sym.Version)), - } - dst = append(dst, rel) - } - return dst + l.growAttrBitmaps(l.NSym() + 1) +} + +type symWithVal struct { + s Sym + v int64 +} +type bySymValue []symWithVal + +func (s bySymValue) Len() int { return len(s) } +func (s bySymValue) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s bySymValue) Less(i, j int) bool { return s[i].v < s[j].v } + +// SortSub walks through the sub-symbols for 's' and sorts them +// in place by increasing value. Return value is the new +// sub symbol for the specified outer symbol. +func (l *Loader) SortSub(s Sym) Sym { + + if s == 0 || l.sub[s] == 0 { + return s + } + + // Sort symbols using a slice first. Use a stable sort on the off + // chance that there's more than once symbol with the same value, + // so as to preserve reproducible builds. + sl := []symWithVal{} + for ss := l.sub[s]; ss != 0; ss = l.sub[ss] { + sl = append(sl, symWithVal{s: ss, v: l.SymValue(ss)}) } + sort.Stable(bySymValue(sl)) - off := relocs.r.RelocOff(relocs.li, 0) - for i := 0; i < relocs.Count; i++ { - rel := goobj2.Reloc{} - rel.Read(relocs.r.Reader, off) - off += uint32(rel.Size()) - target := relocs.l.resolve(relocs.r, rel.Sym) - dst = append(dst, Reloc{ - Off: rel.Off, - Size: rel.Siz, - Type: objabi.RelocType(rel.Type), - Add: rel.Add, - Sym: target, - }) + // Then apply any changes needed to the sub map. + ns := Sym(0) + for i := len(sl) - 1; i >= 0; i-- { + s := sl[i].s + l.sub[s] = ns + ns = s } - return dst + + // Update sub for outer symbol, then return + l.sub[s] = sl[0].s + return sl[0].s +} + +// Insure that reachable bitmap and its siblings have enough size. +func (l *Loader) growAttrBitmaps(reqLen int) { + if reqLen > l.attrReachable.Len() { + // These are indexed by global symbol + l.attrReachable = growBitmap(reqLen, l.attrReachable) + l.attrOnList = growBitmap(reqLen, l.attrOnList) + l.attrLocal = growBitmap(reqLen, l.attrLocal) + l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable) + } + l.growExtAttrBitmaps() +} + +func (l *Loader) growExtAttrBitmaps() { + // These are indexed by external symbol index (e.g. l.extIndex(i)) + extReqLen := len(l.payloads) + if extReqLen > l.attrVisibilityHidden.Len() { + l.attrVisibilityHidden = growBitmap(extReqLen, l.attrVisibilityHidden) + l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK) + l.attrShared = growBitmap(extReqLen, l.attrShared) + l.attrExternal = growBitmap(extReqLen, l.attrExternal) + } +} + +func (relocs *Relocs) Count() int { return len(relocs.rs) } + +// At2 returns the j-th reloc for a global symbol. +func (relocs *Relocs) At2(j int) Reloc2 { + if relocs.l.isExtReader(relocs.r) { + pp := relocs.l.payloads[relocs.li] + return Reloc2{&relocs.rs[j], relocs.r, relocs.l, pp.reltypes[j]} + } + return Reloc2{&relocs.rs[j], relocs.r, relocs.l, 0} } // Relocs returns a Relocs object for the given global sym. func (l *Loader) Relocs(i Sym) Relocs { - if l.IsExternal(i) { - if s := l.Syms[i]; s != nil { - return Relocs{Count: len(s.R), l: l, ext: s} - } - return Relocs{} - } r, li := l.toLocal(i) + if r == nil { + panic(fmt.Sprintf("trying to get oreader for invalid sym %d\n\n", i)) + } return l.relocs(r, li) } // Relocs returns a Relocs object given a local sym index and reader. func (l *Loader) relocs(r *oReader, li int) Relocs { + var rs []goobj2.Reloc2 + if l.isExtReader(r) { + pp := l.payloads[li] + rs = pp.relocs + } else { + rs = r.Relocs2(li) + } return Relocs{ - Count: r.NReloc(li), - li: li, - r: r, - l: l, + rs: rs, + li: li, + r: r, + l: l, } } -// Preload a package: add autolibs, add symbols to the symbol table. -// Does not read symbol data yet. -func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) { +// RelocByOff implements sort.Interface for sorting relocations by offset. + +type RelocByOff []Reloc + +func (x RelocByOff) Len() int { return len(x) } +func (x RelocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x RelocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off } + +// FuncInfo provides hooks to access goobj2.FuncInfo in the objects. +type FuncInfo struct { + l *Loader + r *oReader + data []byte +} + +func (fi *FuncInfo) Valid() bool { return fi.r != nil } + +func (fi *FuncInfo) Locals() int { + return int((*goobj2.FuncInfo)(nil).ReadLocals(fi.data)) +} + +func (fi *FuncInfo) Pcsp() []byte { + pcsp, end := (*goobj2.FuncInfo)(nil).ReadPcsp(fi.data) + return fi.r.BytesAt(fi.r.PcdataBase()+pcsp, int(end-pcsp)) +} + +// TODO: more accessors. + +func (l *Loader) FuncInfo(i Sym) FuncInfo { + if l.IsExternal(i) { + return FuncInfo{} + } + r, li := l.toLocal(i) + auxs := r.Auxs2(li) + for j := range auxs { + a := &auxs[j] + if a.Type() == goobj2.AuxFuncInfo { + b := r.Data(int(a.Sym().SymIdx)) + return FuncInfo{l, r, b} + } + } + return FuncInfo{} +} + +// Preload a package: add autolibs, add defined package symbols to the symbol table. +// Does not add non-package symbols yet, which will be done in LoadNonpkgSyms. +// Does not read symbol data. +func (l *Loader) Preload(syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, flags int) { roObject, readonly, err := f.Slice(uint64(length)) if err != nil { log.Fatal("cannot read object file:", err) } r := goobj2.NewReaderFromBytes(roObject, readonly) if r == nil { + if len(roObject) >= 8 && bytes.Equal(roObject[:8], []byte("\x00go114ld")) { + log.Fatalf("found object file %s in old format, but -go115newobj is true\nset -go115newobj consistently in all -gcflags, -asmflags, and -ldflags", f.File().Name()) + } panic("cannot read object file") } localSymVersion := syms.IncVersion() pkgprefix := objabi.PathToPrefix(lib.Pkg) + "." - or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, nil} + ndef := r.NSym() + nnonpkgdef := r.NNonpkgdef() + or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, make([]Sym, ndef+nnonpkgdef+r.NNonpkgref()), ndef, uint32(len(l.objs))} // Autolib lib.ImportStrings = append(lib.ImportStrings, r.Autolib()...) @@ -700,51 +1562,88 @@ func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib * unit.DWARFFileTable[i] = r.DwarfFile(i) } - istart := l.addObj(lib.Pkg, or) + l.addObj(lib.Pkg, or) + l.preloadSyms(or, pkgDef) + // The caller expects us consuming all the data + f.MustSeek(length, os.SEEK_CUR) +} + +// Preload symbols of given kind from an object. +func (l *Loader) preloadSyms(r *oReader, kind int) { ndef := r.NSym() nnonpkgdef := r.NNonpkgdef() - for i, n := 0, ndef+nnonpkgdef; i < n; i++ { - osym := goobj2.Sym{} - osym.Read(r, r.SymOff(i)) - name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1) - if name == "" { - continue // don't add unnamed aux symbol - } - v := abiToVer(osym.ABI, localSymVersion) + var start, end int + switch kind { + case pkgDef: + start = 0 + end = ndef + case nonPkgDef: + start = ndef + end = ndef + nnonpkgdef + default: + panic("preloadSyms: bad kind") + } + l.growSyms(len(l.objSyms) + end - start) + l.growAttrBitmaps(len(l.objSyms) + end - start) + for i := start; i < end; i++ { + osym := r.Sym2(i) + name := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1) + v := abiToVer(osym.ABI(), r.version) dupok := osym.Dupok() - added := l.AddSym(name, v, istart+Sym(i), or, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]) - if added && strings.HasPrefix(name, "go.itablink.") { - l.itablink[istart+Sym(i)] = struct{}{} + gi, added := l.AddSym(name, v, r, i, kind, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]) + r.syms[i] = gi + if !added { + continue + } + if osym.TopFrame() { + l.SetAttrTopFrame(gi, true) + } + if osym.Local() { + l.SetAttrLocal(gi, true) + } + if strings.HasPrefix(name, "go.itablink.") { + l.itablink[gi] = struct{}{} } - if added && strings.HasPrefix(name, "runtime.") { + if strings.HasPrefix(name, "runtime.") { if bi := goobj2.BuiltinIdx(name, v); bi != -1 { // This is a definition of a builtin symbol. Record where it is. - l.builtinSyms[bi] = istart + Sym(i) + l.builtinSyms[bi] = gi } } + if strings.HasPrefix(name, "go.string.") || + strings.HasPrefix(name, "gclocals·") || + strings.HasPrefix(name, "runtime.gcbits.") { + l.SetAttrNotInSymbolTable(gi, true) + } + if a := osym.Align(); a != 0 { + l.SetSymAlign(gi, int32(a)) + } } - - // The caller expects us consuming all the data - f.MustSeek(length, os.SEEK_CUR) } -// Make sure referenced symbols are added. Most of them should already be added. -// This should only be needed for referenced external symbols. -func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) { +// Add non-package symbols and references to external symbols (which are always +// named). +func (l *Loader) LoadNonpkgSyms(syms *sym.Symbols) { + for _, o := range l.objs[1:] { + l.preloadSyms(o.r, nonPkgDef) + } for _, o := range l.objs[1:] { - loadObjRefs(l, o.r, arch, syms) + loadObjRefs(l, o.r, syms) } } -func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) { +func loadObjRefs(l *Loader, r *oReader, syms *sym.Symbols) { ndef := r.NSym() + r.NNonpkgdef() for i, n := 0, r.NNonpkgref(); i < n; i++ { - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(ndef+i)) - name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1) - v := abiToVer(osym.ABI, r.version) - l.AddExtSym(name, v) + osym := r.Sym2(ndef + i) + name := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1) + v := abiToVer(osym.ABI(), r.version) + r.syms[ndef+i] = l.LookupOrCreateSym(name, v) + if osym.Local() { + gi := r.syms[ndef+i] + l.SetAttrLocal(gi, true) + } } } @@ -794,22 +1693,64 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { nr += loadObjSyms(l, syms, o.r) } + // Make a first pass through the external symbols, making + // sure that each external symbol has a non-nil entry in + // l.Syms (note that relocations and symbol content will + // be copied in a later loop). + toConvert := make([]Sym, 0, len(l.payloads)) + for _, i := range l.extReader.syms { + sname := l.RawSymName(i) + if !l.attrReachable.Has(i) && !strings.HasPrefix(sname, "gofile..") { // XXX file symbols are used but not marked + continue + } + pp := l.getPayload(i) + nr += len(pp.relocs) + // create and install the sym.Symbol here so that l.Syms will + // be fully populated when we do relocation processing and + // outer/sub processing below. Note that once we do this, + // we'll need to get at the payload for a symbol with direct + // reference to l.payloads[] as opposed to calling l.getPayload(). + s := l.allocSym(sname, 0) + l.installSym(i, s) + toConvert = append(toConvert, i) + } + // allocate a single large slab of relocations for all live symbols l.relocBatch = make([]sym.Reloc, nr) - // external symbols - for i := l.extStart; i <= l.max; i++ { - if s := l.Syms[i]; s != nil { - s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i)) - continue // already loaded from external object + // convert payload-based external symbols into sym.Symbol-based + for _, i := range toConvert { + + // Copy kind/size/value etc. + pp := l.payloads[l.extIndex(i)] + s := l.Syms[i] + s.Version = int16(pp.ver) + s.Type = pp.kind + s.Size = pp.size + if pp.gotype != 0 { + s.Gotype = l.Syms[pp.gotype] } - nv := l.extSyms[i-l.extStart] - if l.Reachable.Has(i) || strings.HasPrefix(nv.name, "gofile..") { // XXX file symbols are used but not marked - s := syms.Newsym(nv.name, nv.v) - preprocess(arch, s) - s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i)) - l.Syms[i] = s + if f, ok := l.symFile[i]; ok { + s.File = f + } else if pp.objidx != 0 { + s.File = l.objs[pp.objidx].r.unit.Lib.File } + + // Copy relocations + batch := l.relocBatch + s.R = batch[:len(pp.relocs):len(pp.relocs)] + l.relocBatch = batch[len(pp.relocs):] + relocs := l.Relocs(i) + l.convertRelocations(&relocs, s, false) + + // Copy data + s.P = pp.data + + // Transfer over attributes. + l.migrateAttributes(i, s) + + // Preprocess symbol. May set 'AttrLocal'. + preprocess(arch, s) } // load contents of defined symbols @@ -817,11 +1758,15 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { loadObjFull(l, o.r) } + // Note: resolution of ABI aliases is now also handled in + // loader.convertRelocations, so once the host object loaders move + // completely to loader.Sym, we can remove the code below. + // Resolve ABI aliases for external symbols. This is only // needed for internal cgo linking. // (The old code does this in deadcode, but deadcode2 doesn't // do this.) - for i := l.extStart; i <= l.max; i++ { + for _, i := range l.extReader.syms { if s := l.Syms[i]; s != nil && s.Attr.Reachable() { for ri := range s.R { r := &s.R[ri] @@ -833,40 +1778,259 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { } } +// ResolveABIAlias given a symbol returns the ABI alias target of that +// symbol. If the sym in question is not an alias, the sym itself is +// returned. +func (l *Loader) ResolveABIAlias(s Sym) Sym { + if l.SymType(s) != sym.SABIALIAS { + return s + } + relocs := l.Relocs(s) + target := relocs.At2(0).Sym() + if l.SymType(target) == sym.SABIALIAS { + panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", l.SymName(s), l.SymName(target))) + } + return target +} + +// PropagateSymbolChangesBackToLoader is a temporary shim function +// that copies over a given sym.Symbol into the equivalent representation +// in the loader world. The intent is to enable converting a given +// linker phase/pass from dealing with sym.Symbol's to a modernized +// pass that works with loader.Sym, in cases where the "loader.Sym +// wavefront" has not yet reached the pass in question. For such work +// the recipe is to first call PropagateSymbolChangesBackToLoader(), +// then exexute the pass working with the loader, then call +// PropagateLoaderChangesToSymbols to copy the changes made by the +// pass back to the sym.Symbol world. +func (l *Loader) PropagateSymbolChangesBackToLoader() { + + // For the moment we only copy symbol values, and we don't touch + // any new sym.Symbols created since loadlibfull() was run. This + // seems to be what's needed for DWARF gen. + for i := Sym(1); i < Sym(len(l.objSyms)); i++ { + s := l.Syms[i] + if s != nil { + if s.Value != l.SymValue(i) { + l.SetSymValue(i, s.Value) + } + } + } +} + +// PropagateLoaderChangesToSymbols is a temporary shim function that +// takes a list of loader.Sym symbols and works to copy their contents +// and attributes over to a corresponding sym.Symbol. The parameter +// anonVerReplacement specifies a version number for any new anonymous +// symbols encountered on the list, when creating sym.Symbols for them +// (or zero if we don't expect to encounter any new anon symbols). See +// the PropagateSymbolChangesBackToLoader header comment for more +// info. +// +// WARNING: this function is brittle and depends heavily on loader +// implementation. A key problem with doing this is that as things +// stand at the moment, some sym.Symbol contents/attributes are +// populated only when converting from loader.Sym to sym.Symbol in +// loadlibfull, meaning we may wipe out some information when copying +// back. + +func (l *Loader) PropagateLoaderChangesToSymbols(toconvert []Sym, anonVerReplacement int) []*sym.Symbol { + + result := []*sym.Symbol{} + relocfixup := []Sym{} + + // Note: this loop needs to allow for the possibility that we may + // see "new" symbols on the 'toconvert' list that come from object + // files (for example, DWARF location lists), as opposed to just + // newly manufactured symbols (ex: DWARF section symbols such as + // ".debug_info"). This means that we have to be careful not to + // stomp on sym.Symbol attributes/content that was set up in + // in loadlibfull(). + + // Also note that in order for the relocation fixup to work, we + // have to do this in two passes -- one pass to create the symbols, + // and then a second fix up the relocations once all necessary + // sym.Symbols are created. + + // First pass, symbol creation and symbol data fixup. + for _, cand := range toconvert { + + sn := l.SymName(cand) + sv := l.SymVersion(cand) + st := l.SymType(cand) + if sv < 0 { + if anonVerReplacement == 0 { + panic("expected valid anon version replacement") + } + sv = anonVerReplacement + } + + s := l.Syms[cand] + + isnew := false + if sn == "" { + // Don't install anonymous symbols in the lookup tab. + if s == nil { + s = l.allocSym(sn, sv) + l.installSym(cand, s) + } + isnew = true + } else { + if s != nil { + // Already have a symbol for this -- it must be + // something that was previously processed by + // loadObjFull. Note that the symbol in question may + // or may not be in the name lookup map. + } else { + isnew = true + s = l.SymLookup(sn, sv) + } + } + result = append(result, s) + + // Always copy these from new to old. + s.Value = l.SymValue(cand) + s.Type = st + + // If the data for a symbol has increased in size, make sure + // we bring the new content across. + relfix := isnew + if isnew || len(l.Data(cand)) > len(s.P) { + s.P = l.Data(cand) + s.Size = int64(len(s.P)) + relfix = true + } + + // For 'new' symbols, copy other content (such as Gotype, + // sym file, relocations, etc). + if isnew { + if gt := l.SymGoType(cand); gt != 0 { + s.Gotype = l.Syms[gt] + } + if f, ok := l.symFile[cand]; ok { + s.File = f + } else { + r, _ := l.toLocal(cand) + if r != nil && r != l.extReader { + s.File = l.SymFile(cand) + } + } + } + + if relfix { + relocfixup = append(relocfixup, cand) + } + + // If new symbol, call a helper to migrate attributes. + // Otherwise touch only not-in-symbol-table, since there are + // some attrs that are only set up at the point where we + // convert loader.Sym to sym.Symbol. + if isnew { + l.migrateAttributes(cand, s) + } else { + if l.AttrNotInSymbolTable(cand) { + s.Attr.Set(sym.AttrNotInSymbolTable, true) + } + } + } + + // Second pass to fix up relocations. + for _, cand := range relocfixup { + s := l.Syms[cand] + relocs := l.Relocs(cand) + if len(s.R) != relocs.Count() { + s.R = make([]sym.Reloc, relocs.Count()) + } + l.convertRelocations(&relocs, s, true) + } + + return result +} + // ExtractSymbols grabs the symbols out of the loader for work that hasn't been // ported to the new symbol type. -func (l *Loader) ExtractSymbols(syms *sym.Symbols) { - // Nil out overwritten symbols. - // Overwritten Go symbols aren't a problem (as they're lazy loaded), but - // symbols loaded from host object loaders are fully loaded, and we might - // have multiple symbols with the same name. This loop nils them out. - for oldI := range l.overwrite { - l.Syms[oldI] = nil - } - - // Add symbols to the ctxt.Syms lookup table. This explicitly - // skips things created via loader.Create (marked with versions - // less than zero), since if we tried to add these we'd wind up - // with collisions. Along the way, update the version from the - // negative anon version to something larger than sym.SymVerStatic - // (needed so that sym.symbol.IsFileLocal() works properly). +func (l *Loader) ExtractSymbols(syms *sym.Symbols, rp map[*sym.Symbol]*sym.Symbol) { + // Add symbols to the ctxt.Syms lookup table. This explicitly skips things + // created via loader.Create (marked with versions less than zero), since + // if we tried to add these we'd wind up with collisions. We do, however, + // add these symbols to the list of global symbols so that other future + // steps (like pclntab generation) can find these symbols if neceassary. + // Along the way, update the version from the negative anon version to + // something larger than sym.SymVerStatic (needed so that + // sym.symbol.IsFileLocal() works properly). anonVerReplacement := syms.IncVersion() for _, s := range l.Syms { if s == nil { continue } - if s.Name != "" && s.Version >= 0 { - syms.Add(s) - } + syms.Allsym = append(syms.Allsym, s) // XXX still add to Allsym for now, as there are code looping through Allsym if s.Version < 0 { s.Version = int16(anonVerReplacement) } } + + for i, s := range l.Reachparent { + if i == 0 { + continue + } + rp[l.Syms[i]] = l.Syms[s] + } + + // Provide lookup functions for sym.Symbols. + l.SymLookup = func(name string, ver int) *sym.Symbol { + i := l.LookupOrCreateSym(name, ver) + if s := l.Syms[i]; s != nil { + return s + } + s := l.allocSym(name, ver) + l.installSym(i, s) + syms.Allsym = append(syms.Allsym, s) // XXX see above + return s + } + syms.Lookup = l.SymLookup + syms.ROLookup = func(name string, ver int) *sym.Symbol { + i := l.Lookup(name, ver) + return l.Syms[i] + } + syms.Newsym = func(name string, ver int) *sym.Symbol { + i := l.newExtSym(name, ver) + s := l.allocSym(name, ver) + l.installSym(i, s) + syms.Allsym = append(syms.Allsym, s) // XXX see above + return s + } +} + +// allocSym allocates a new symbol backing. +func (l *Loader) allocSym(name string, version int) *sym.Symbol { + batch := l.symBatch + if len(batch) == 0 { + batch = make([]sym.Symbol, 1000) + } + s := &batch[0] + l.symBatch = batch[1:] + + s.Dynid = -1 + s.Name = name + s.Version = int16(version) + + return s +} + +// installSym sets the underlying sym.Symbol for the specified sym index. +func (l *Loader) installSym(i Sym, s *sym.Symbol) { + if s == nil { + panic("installSym nil symbol") + } + if l.Syms[i] != nil { + panic("sym already present in installSym") + } + l.Syms[i] = s } // addNewSym adds a new sym.Symbol to the i-th index in the list of symbols. -func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol { - s := syms.Newsym(name, ver) +func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol { + s := l.allocSym(name, ver) if s.Type != 0 && s.Type != sym.SXREF { fmt.Println("symbol already processed:", unit.Lib, i, s) panic("symbol already processed") @@ -877,7 +2041,7 @@ func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit s.Type = t s.Unit = unit l.growSyms(int(i)) - l.Syms[i] = s + l.installSym(i, s) return s } @@ -885,44 +2049,39 @@ func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit // object corresponding to object reader "r". Return value is the // number of sym.Reloc entries required for all the new symbols. func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int { - istart := l.startIndex(r) nr := 0 - for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ { - // If it's been previously loaded in host object loading, we don't need to do it again. - if s := l.Syms[istart+Sym(i)]; s != nil { - // Mark symbol as reachable as it wasn't marked as such before. - s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i))) - nr += r.NReloc(i) - continue - } - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(i)) - name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1) - if name == "" { - continue + gi := r.syms[i] + if r2, i2 := l.toLocal(gi); r2 != r || i2 != i { + continue // come from a different object } - ver := abiToVer(osym.ABI, r.version) - if osym.ABI != goobj2.SymABIstatic && l.symsByName[ver][name] != istart+Sym(i) { + osym := r.Sym2(i) + name := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1) + t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())] + // NB: for the test below, we can skip most anonymous symbols + // since they will never be turned into sym.Symbols (ex: + // funcdata), however DWARF subprogram DIE symbols (which are + // nameless) will eventually need to be turned into + // sym.Symbols (with relocations), so the simplest thing to do + // is include them as part of this loop. + if name == "" && t != sym.SDWARFINFO { continue } - - t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)] + ver := abiToVer(osym.ABI(), r.version) if t == sym.SXREF { log.Fatalf("bad sxref") } if t == 0 { log.Fatalf("missing type for %s in %s", name, r.unit.Lib) } - if !l.Reachable.Has(istart+Sym(i)) && !(t == sym.SRODATA && strings.HasPrefix(name, "type.")) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" { + if !l.attrReachable.Has(gi) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" { // No need to load unreachable symbols. - // XXX some type symbol's content may be needed in DWARF code, but they are not marked. // XXX reference to runtime.addmoduledata may be generated later by the linker in plugin mode. continue } - s := l.addNewSym(istart+Sym(i), syms, name, ver, r.unit, t) - s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i))) + s := l.addNewSym(gi, name, ver, r.unit, t) + l.migrateAttributes(gi, s) nr += r.NReloc(i) } return nr @@ -933,9 +2092,9 @@ func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int { // We use this to delay populating FuncInfo until we can batch-allocate // slices for their sub-objects. type funcInfoSym struct { - s *sym.Symbol // sym.Symbol for a live function - osym goobj2.Sym // object file symbol data for that function - isym int // global symbol index of FuncInfo aux sym for func + s *sym.Symbol // sym.Symbol for a live function + osym *goobj2.Sym2 // object file symbol data for that function + isym int // global symbol index of FuncInfo aux sym for func } // funcAllocInfo records totals/counts for all functions in an objfile; @@ -947,85 +2106,201 @@ type funcAllocInfo struct { fdOff uint32 // number of int64's needed in all Funcdataoff slices } -// LoadSymbol loads a single symbol by name. -// This function should only be used by the host object loaders. -// NB: This function does NOT set the symbol as reachable. -func (l *Loader) LoadSymbol(name string, version int, syms *sym.Symbols) *sym.Symbol { - global := l.Lookup(name, version) - - // If we're already loaded, bail. - if global != 0 && int(global) < len(l.Syms) && l.Syms[global] != nil { - return l.Syms[global] +// cloneToExternal takes the existing object file symbol (symIdx) +// and creates a new external symbol payload that is a clone with +// respect to name, version, type, relocations, etc. The idea here +// is that if the linker decides it wants to update the contents of +// a symbol originally discovered as part of an object file, it's +// easier to do this if we make the updates to an external symbol +// payload. +// XXX maybe rename? makeExtPayload? +func (l *Loader) cloneToExternal(symIdx Sym) { + if l.IsExternal(symIdx) { + panic("sym is already external, no need for clone") } + l.growSyms(int(symIdx)) - // Read the symbol. - r, i := l.toLocal(global) - istart := l.startIndex(r) + // Read the particulars from object. + r, li := l.toLocal(symIdx) + osym := r.Sym2(li) + sname := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1) + sver := abiToVer(osym.ABI(), r.version) + skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())] + + // Create new symbol, update version and kind. + pi := l.newPayload(sname, sver) + pp := l.payloads[pi] + pp.kind = skind + pp.ver = sver + pp.size = int64(osym.Siz()) + pp.objidx = r.objidx + + // If this is a def, then copy the guts. We expect this case + // to be very rare (one case it may come up is with -X). + if li < (r.NSym() + r.NNonpkgdef()) { + + // Copy relocations + relocs := l.Relocs(symIdx) + pp.relocs = make([]goobj2.Reloc2, relocs.Count()) + pp.reltypes = make([]objabi.RelocType, relocs.Count()) + for i := range pp.relocs { + // Copy the relocs slice. + // Convert local reference to global reference. + rel := relocs.At2(i) + pp.relocs[i].Set(rel.Off(), rel.Siz(), 0, rel.Add(), goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())}) + pp.reltypes[i] = rel.Type() + } - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(int(i))) - if l.symsByName[version][name] != istart+Sym(i) { - return nil + // Copy data + pp.data = r.Data(li) } - return l.addNewSym(istart+Sym(i), syms, name, version, r.unit, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]) -} - -// LookupOrCreate looks up a symbol by name, and creates one if not found. -// Either way, it will also create a sym.Symbol for it, if not already. -// This should only be called when interacting with parts of the linker -// that still works on sym.Symbols (i.e. internal cgo linking, for now). -func (l *Loader) LookupOrCreate(name string, version int, syms *sym.Symbols) *sym.Symbol { - i := l.Lookup(name, version) - if i != 0 { - // symbol exists - if int(i) < len(l.Syms) && l.Syms[i] != nil { - return l.Syms[i] // already loaded - } - if l.IsExternal(i) { - panic("Can't load an external symbol.") + // If we're overriding a data symbol, collect the associated + // Gotype, so as to propagate it to the new symbol. + auxs := r.Auxs2(li) + for j := range auxs { + a := &auxs[j] + switch a.Type() { + case goobj2.AuxGotype: + pp.gotype = l.resolve(r, a.Sym()) + default: + log.Fatalf("internal error: cloneToExternal applied to %s symbol %s with non-gotype aux data %d", skind.String(), sname, a.Type()) } - return l.LoadSymbol(name, version, syms) } - i = l.AddExtSym(name, version) - s := syms.Newsym(name, version) - l.Syms[i] = s - return s + + // Install new payload to global index space. + // (This needs to happen at the end, as the accessors above + // need to access the old symbol content.) + l.objSyms[symIdx] = objSym{l.extReader, pi} + l.extReader.syms = append(l.extReader.syms, symIdx) } -// Create creates a symbol with the specified name, returning a -// sym.Symbol object for it. This method is intended for static/hidden -// symbols discovered while loading host objects. We can see more than -// one instance of a given static symbol with the same name/version, -// so we can't add them to the lookup tables "as is". Instead assign -// them fictitious (unique) versions, starting at -1 and decreasing by -// one for each newly created symbol, and record them in the -// extStaticSyms hash. -func (l *Loader) Create(name string, syms *sym.Symbols) *sym.Symbol { - i := l.max + 1 - l.max++ - if l.extStart == 0 { - l.extStart = i +// Copy the payload of symbol src to dst. Both src and dst must be external +// symbols. +// The intended use case is that when building/linking against a shared library, +// where we do symbol name mangling, the Go object file may have reference to +// the original symbol name whereas the shared library provides a symbol with +// the mangled name. When we do mangling, we copy payload of mangled to original. +func (l *Loader) CopySym(src, dst Sym) { + if !l.IsExternal(dst) { + panic("dst is not external") //l.newExtSym(l.SymName(dst), l.SymVersion(dst)) + } + if !l.IsExternal(src) { + panic("src is not external") //l.cloneToExternal(src) + } + l.payloads[l.extIndex(dst)] = l.payloads[l.extIndex(src)] + l.SetSymFile(dst, l.SymFile(src)) + // TODO: other attributes? +} + +// CopyAttributes copies over all of the attributes of symbol 'src' to +// symbol 'dst'. +func (l *Loader) CopyAttributes(src Sym, dst Sym) { + l.SetAttrReachable(dst, l.AttrReachable(src)) + l.SetAttrOnList(dst, l.AttrOnList(src)) + l.SetAttrLocal(dst, l.AttrLocal(src)) + l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src)) + if l.IsExternal(dst) { + l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src)) + l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src)) + l.SetAttrShared(dst, l.AttrShared(src)) + l.SetAttrExternal(dst, l.AttrExternal(src)) + } else { + // Some attributes are modifiable only for external symbols. + // In such cases, don't try to transfer over the attribute + // from the source even if there is a clash. This comes up + // when copying attributes from a dupOK ABI wrapper symbol to + // the real target symbol (which may not be marked dupOK). + } + l.SetAttrTopFrame(dst, l.AttrTopFrame(src)) + l.SetAttrSpecial(dst, l.AttrSpecial(src)) + l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src)) + l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src)) + l.SetAttrReadOnly(dst, l.AttrReadOnly(src)) +} + +// migrateAttributes copies over all of the attributes of symbol 'src' to +// sym.Symbol 'dst'. +func (l *Loader) migrateAttributes(src Sym, dst *sym.Symbol) { + dst.Value = l.SymValue(src) + dst.Align = l.SymAlign(src) + + dst.Attr.Set(sym.AttrReachable, l.AttrReachable(src)) + dst.Attr.Set(sym.AttrOnList, l.AttrOnList(src)) + dst.Attr.Set(sym.AttrLocal, l.AttrLocal(src)) + dst.Attr.Set(sym.AttrNotInSymbolTable, l.AttrNotInSymbolTable(src)) + dst.Attr.Set(sym.AttrNoSplit, l.IsNoSplit(src)) + dst.Attr.Set(sym.AttrVisibilityHidden, l.AttrVisibilityHidden(src)) + dst.Attr.Set(sym.AttrDuplicateOK, l.AttrDuplicateOK(src)) + dst.Attr.Set(sym.AttrShared, l.AttrShared(src)) + dst.Attr.Set(sym.AttrExternal, l.AttrExternal(src)) + dst.Attr.Set(sym.AttrTopFrame, l.AttrTopFrame(src)) + dst.Attr.Set(sym.AttrSpecial, l.AttrSpecial(src)) + dst.Attr.Set(sym.AttrCgoExportDynamic, l.AttrCgoExportDynamic(src)) + dst.Attr.Set(sym.AttrCgoExportStatic, l.AttrCgoExportStatic(src)) + dst.Attr.Set(sym.AttrReadOnly, l.AttrReadOnly(src)) + + // Convert outer/sub relationships + if outer, ok := l.outer[src]; ok { + dst.Outer = l.Syms[outer] + } + if sub, ok := l.sub[src]; ok { + dst.Sub = l.Syms[sub] } + // Set sub-symbol attribute. FIXME: would be better to do away + // with this and just use l.OuterSymbol() != 0 elsewhere within + // the linker. + dst.Attr.Set(sym.AttrSubSymbol, dst.Outer != nil) + + // Copy over dynimplib, dynimpvers, extname. + if name, ok := l.extname[src]; ok { + dst.SetExtname(name) + } + if l.SymDynimplib(src) != "" { + dst.SetDynimplib(l.SymDynimplib(src)) + } + if l.SymDynimpvers(src) != "" { + dst.SetDynimpvers(l.SymDynimpvers(src)) + } + + // Copy ELF type if set. + if et, ok := l.elfType[src]; ok { + dst.SetElfType(et) + } + + // Copy pe objects values if set. + if plt, ok := l.plt[src]; ok { + dst.SetPlt(plt) + } + if got, ok := l.got[src]; ok { + dst.SetGot(got) + } + + // Copy dynid + if dynid, ok := l.dynid[src]; ok { + dst.Dynid = dynid + } +} + +// CreateExtSym creates a new external symbol with the specified name +// without adding it to any lookup tables, returning a Sym index for it. +func (l *Loader) CreateExtSym(name string, ver int) Sym { + return l.newExtSym(name, ver) +} + +// CreateStaticSym creates a new static symbol with the specified name +// without adding it to any lookup tables, returning a Sym index for it. +func (l *Loader) CreateStaticSym(name string) Sym { // Assign a new unique negative version -- this is to mark the // symbol so that it can be skipped when ExtractSymbols is adding // ext syms to the sym.Symbols hash. l.anonVersion-- - ver := l.anonVersion - l.extSyms = append(l.extSyms, nameVer{name, ver}) - l.growSyms(int(i)) - s := syms.Newsym(name, ver) - l.Syms[i] = s - l.extStaticSyms[nameVer{name, ver}] = i - - return s + return l.newExtSym(name, l.anonVersion) } func loadObjFull(l *Loader, r *oReader) { lib := r.unit.Lib - istart := l.startIndex(r) - resolveSymRef := func(s goobj2.SymRef) *sym.Symbol { i := l.resolve(r, s) return l.Syms[i] @@ -1035,43 +2310,42 @@ func loadObjFull(l *Loader, r *oReader) { fdsyms := []*sym.Symbol{} var funcAllocCounts funcAllocInfo pcdataBase := r.PcdataBase() - rslice := []Reloc{} for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ { - osym := goobj2.Sym{} - osym.Read(r.Reader, r.SymOff(i)) - name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1) - if name == "" { - continue + // A symbol may be a dup or overwritten. In this case, its + // content will actually be provided by a different object + // (to which its global index points). Skip those symbols. + gi := l.toGlobal(r, i) + var isdup bool + if r2, i2 := l.toLocal(gi); r2 != r || i2 != i { + isdup = true } - ver := abiToVer(osym.ABI, r.version) + + osym := r.Sym2(i) dupok := osym.Dupok() - if dupok { - if dupsym := l.symsByName[ver][name]; dupsym != istart+Sym(i) { - if l.Reachable.Has(dupsym) { - // A dupok symbol is resolved to another package. We still need - // to record its presence in the current package, as the trampoline - // pass expects packages are laid out in dependency order. - s := l.Syms[dupsym] - if s.Type == sym.STEXT { - lib.DupTextSyms = append(lib.DupTextSyms, s) - } + if dupok && isdup { + if l.attrReachable.Has(gi) { + // A dupok symbol is resolved to another package. We still need + // to record its presence in the current package, as the trampoline + // pass expects packages are laid out in dependency order. + s := l.Syms[gi] + if s.Type == sym.STEXT { + lib.DupTextSyms = append(lib.DupTextSyms, s) } - continue } + continue } - s := l.Syms[istart+Sym(i)] + if isdup { + continue // come from a different object + } + s := l.Syms[gi] if s == nil { continue } - if s.Name != name { // Sanity check. We can remove it in the final version. - fmt.Println("name mismatch:", lib, i, s.Name, name) - panic("name mismatch") - } local := osym.Local() makeTypelink := osym.Typelink() - size := osym.Siz + size := osym.Siz() // Symbol data s.P = r.Data(i) @@ -1079,59 +2353,29 @@ func loadObjFull(l *Loader, r *oReader) { // Relocs relocs := l.relocs(r, i) - rslice = relocs.ReadAll(rslice) batch := l.relocBatch - s.R = batch[:relocs.Count:relocs.Count] - l.relocBatch = batch[relocs.Count:] - for j := range s.R { - r := rslice[j] - rs := r.Sym - sz := r.Size - rt := r.Type - if rt == objabi.R_METHODOFF { - if l.Reachable.Has(rs) { - rt = objabi.R_ADDROFF - } else { - sz = 0 - rs = 0 - } - } - if rt == objabi.R_WEAKADDROFF && !l.Reachable.Has(rs) { - rs = 0 - sz = 0 - } - if rs != 0 && l.SymType(rs) == sym.SABIALIAS { - rsrelocs := l.Relocs(rs) - rs = rsrelocs.At(0).Sym - } - s.R[j] = sym.Reloc{ - Off: r.Off, - Siz: sz, - Type: rt, - Add: r.Add, - Sym: l.Syms[rs], - } - } + s.R = batch[:relocs.Count():relocs.Count()] + l.relocBatch = batch[relocs.Count():] + l.convertRelocations(&relocs, s, false) // Aux symbol info isym := -1 - naux := r.NAux(i) - for j := 0; j < naux; j++ { - a := goobj2.Aux{} - a.Read(r.Reader, r.AuxOff(i, j)) - switch a.Type { + auxs := r.Auxs2(i) + for j := range auxs { + a := &auxs[j] + switch a.Type() { case goobj2.AuxGotype: - typ := resolveSymRef(a.Sym) + typ := resolveSymRef(a.Sym()) if typ != nil { s.Gotype = typ } case goobj2.AuxFuncdata: - fdsyms = append(fdsyms, resolveSymRef(a.Sym)) + fdsyms = append(fdsyms, resolveSymRef(a.Sym())) case goobj2.AuxFuncInfo: - if a.Sym.PkgIdx != goobj2.PkgIdxSelf { + if a.Sym().PkgIdx != goobj2.PkgIdxSelf { panic("funcinfo symbol not defined in current package") } - isym = int(a.Sym.SymIdx) + isym = int(a.Sym().SymIdx) case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines: // ignored for now default: @@ -1149,14 +2393,6 @@ func loadObjFull(l *Loader, r *oReader) { s.Attr.Set(sym.AttrLocal, local) s.Attr.Set(sym.AttrMakeTypelink, makeTypelink) - if s.Type == sym.SDWARFINFO { - // For DWARF symbols, replace `"".` to actual package prefix - // in the symbol content. - // TODO: maybe we should do this in the compiler and get rid - // of this. - patchDWARFName(s, r) - } - if s.Type != sym.STEXT { continue } @@ -1203,7 +2439,7 @@ func loadObjFull(l *Loader, r *oReader) { info := goobj2.FuncInfo{} info.Read(b) - if info.NoSplit != 0 { + if osym.NoSplit() { s.Attr |= sym.AttrNoSplit } if osym.ReflectMethod() { @@ -1283,41 +2519,164 @@ func loadObjFull(l *Loader, r *oReader) { } } -var emptyPkg = []byte(`"".`) +// convertRelocations takes a vector of loader.Reloc relocations and +// translates them into an equivalent set of sym.Reloc relocations on +// the symbol "dst", performing fixups along the way for ABI aliases, +// etc. It is assumed that the caller has pre-allocated the dst symbol +// relocations slice. If 'strict' is set, then this method will +// panic if it finds a relocation targeting a nil symbol. +func (l *Loader) convertRelocations(src *Relocs, dst *sym.Symbol, strict bool) { + for j := range dst.R { + r := src.At2(j) + rs := r.Sym() + sz := r.Siz() + rt := r.Type() + if rt == objabi.R_METHODOFF { + if l.attrReachable.Has(rs) { + rt = objabi.R_ADDROFF + } else { + sz = 0 + rs = 0 + } + } + if rt == objabi.R_WEAKADDROFF && !l.attrReachable.Has(rs) { + rs = 0 + sz = 0 + } + if rs != 0 && l.Syms[rs] != nil && l.Syms[rs].Type == sym.SABIALIAS { + rsrelocs := l.Relocs(rs) + rs = rsrelocs.At2(0).Sym() + } + if strict && rs != 0 && l.Syms[rs] == nil && rt != objabi.R_USETYPE { + panic("nil reloc target in convertRelocations") + } + dst.R[j] = sym.Reloc{ + Off: r.Off(), + Siz: sz, + Type: rt, + Add: r.Add(), + Sym: l.Syms[rs], + } + } +} -func patchDWARFName1(p []byte, r *oReader) ([]byte, int) { - // This is kind of ugly. Really the package name should not - // even be included here. - if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION { - return p, -1 +// UndefinedRelocTargets iterates through the global symbol index +// space, looking for symbols with relocations targeting undefined +// references. The linker's loadlib method uses this to determine if +// there are unresolved references to functions in system libraries +// (for example, libgcc.a), presumably due to CGO code. Return +// value is a list of loader.Sym's corresponding to the undefined +// cross-refs. The "limit" param controls the maximum number of +// results returned; if "limit" is -1, then all undefs are returned. +func (l *Loader) UndefinedRelocTargets(limit int) []Sym { + result := []Sym{} + for si := Sym(1); si < Sym(len(l.objSyms)); si++ { + relocs := l.Relocs(si) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At2(ri) + rs := r.Sym() + if rs != 0 && l.SymType(rs) == sym.SXREF && l.RawSymName(rs) != ".got" { + result = append(result, rs) + if limit != -1 && len(result) >= limit { + break + } + } + } } - e := bytes.IndexByte(p, 0) - if e == -1 { - return p, -1 + return result +} + +// AssignTextSymbolOrder populates the Textp2 slices within each +// library and compilation unit, insuring that packages are laid down +// in dependency order (internal first, then everything else). Return value +// is a slice of all text syms. +func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym { + + // Library Textp2 lists should be empty at this point. + for _, lib := range libs { + if len(lib.Textp2) != 0 { + panic("expected empty Textp2 slice for library") + } + if len(lib.DupTextSyms2) != 0 { + panic("expected empty DupTextSyms2 slice for library") + } } - if !bytes.Contains(p[:e], emptyPkg) { - return p, -1 + + // Used to record which dupok symbol we've assigned to a unit. + // Can't use the onlist attribute here because it will need to + // clear for the later assignment of the sym.Symbol to a unit. + // NB: we can convert to using onList once we no longer have to + // call the regular addToTextp. + assignedToUnit := MakeBitmap(l.NSym() + 1) + + // Start off textp2 with reachable external syms. + textp2 := []Sym{} + for _, sym := range extsyms { + if !l.attrReachable.Has(sym) { + continue + } + textp2 = append(textp2, sym) } - pkgprefix := []byte(r.pkgprefix) - patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1) - return append(patched, p[e:]...), e -} -func patchDWARFName(s *sym.Symbol, r *oReader) { - patched, e := patchDWARFName1(s.P, r) - if e == -1 { - return + // Walk through all text symbols from Go object files and append + // them to their corresponding library's textp2 list. + for _, o := range l.objs[1:] { + r := o.r + lib := r.unit.Lib + for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ { + gi := l.toGlobal(r, i) + if !l.attrReachable.Has(gi) { + continue + } + osym := r.Sym2(i) + st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())] + if st != sym.STEXT { + continue + } + dupok := osym.Dupok() + if r2, i2 := l.toLocal(gi); r2 != r || i2 != i { + // A dupok text symbol is resolved to another package. + // We still need to record its presence in the current + // package, as the trampoline pass expects packages + // are laid out in dependency order. + lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi)) + continue // symbol in different object + } + if dupok { + lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi)) + } + + lib.Textp2 = append(lib.Textp2, sym.LoaderSym(gi)) + } } - s.P = patched - s.Attr.Set(sym.AttrReadOnly, false) - delta := int64(len(s.P)) - s.Size - s.Size = int64(len(s.P)) - for i := range s.R { - r := &s.R[i] - if r.Off > int32(e) { - r.Off += int32(delta) + + // Now redo the assignment of text symbols to libs/units. + for _, doInternal := range [2]bool{true, false} { + for idx, lib := range libs { + if intlibs[idx] != doInternal { + continue + } + libtextp2 := []sym.LoaderSym{} + lists := [2][]sym.LoaderSym{lib.Textp2, lib.DupTextSyms2} + for _, list := range lists { + for _, s := range list { + sym := Sym(s) + if l.attrReachable.Has(sym) && !assignedToUnit.Has(sym) { + libtextp2 = append(libtextp2, s) + textp2 = append(textp2, sym) + unit := l.SymUnit(sym) + if unit != nil { + unit.Textp2 = append(unit.Textp2, s) + assignedToUnit.Set(sym) + } + } + } + } + lib.Textp2 = libtextp2 } } + + return textp2 } // For debugging. @@ -1328,18 +2687,24 @@ func (l *Loader) Dump() { fmt.Println(obj.i, obj.r.unit.Lib) } } + fmt.Println("extStart:", l.extStart) + fmt.Println("Nsyms:", len(l.objSyms)) fmt.Println("syms") - for i, s := range l.Syms { - if i == 0 { - continue + for i := Sym(1); i < Sym(len(l.objSyms)); i++ { + pi := interface{}("") + if l.IsExternal(i) { + pi = fmt.Sprintf("<ext %d>", l.extIndex(i)) + } + var s *sym.Symbol + if int(i) < len(l.Syms) { + s = l.Syms[i] } if s != nil { - fmt.Println(i, s, s.Type) + fmt.Println(i, s, s.Type, pi) } else { - fmt.Println(i, l.SymName(Sym(i)), "<not loaded>") + fmt.Println(i, l.SymName(i), "<not loaded>", pi) } } - fmt.Println("overwrite:", l.overwrite) fmt.Println("symsByName") for name, i := range l.symsByName[0] { fmt.Println(i, name, 0) @@ -1347,4 +2712,9 @@ func (l *Loader) Dump() { for name, i := range l.symsByName[1] { fmt.Println(i, name, 1) } + fmt.Println("payloads:") + for i := range l.payloads { + pp := l.payloads[i] + fmt.Println(i, pp.name, pp.ver, pp.kind) + } } diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go new file mode 100644 index 0000000000..b2f823d17e --- /dev/null +++ b/src/cmd/link/internal/loader/loader_test.go @@ -0,0 +1,441 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loader + +import ( + "bytes" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/sym" + "fmt" + "testing" +) + +// dummyAddSym adds the named symbol to the loader as if it had been +// read from a Go object file. Note that it allocates a global +// index without creating an associated object reader, so one can't +// do anything interesting with this symbol (such as look at its +// data or relocations). +func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym { + idx := len(ldr.objSyms) + s, ok := ldr.AddSym(name, 0, or, idx, nonPkgDef, false, sym.SRODATA) + if !ok { + t.Errorf("AddrSym failed for '" + name + "'") + } + return s +} + +func TestAddMaterializedSymbol(t *testing.T) { + edummy := func(s *sym.Symbol, str string, off int) {} + ldr := NewLoader(0, edummy) + dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} + or := &dummyOreader + + // Create some syms from a dummy object file symbol to get things going. + ts1 := addDummyObjSym(t, ldr, or, "type.uint8") + ts2 := addDummyObjSym(t, ldr, or, "mumble") + ts3 := addDummyObjSym(t, ldr, or, "type.string") + + // Create some external symbols. + es1 := ldr.LookupOrCreateSym("extnew1", 0) + if es1 == 0 { + t.Fatalf("LookupOrCreateSym failed for extnew1") + } + es1x := ldr.LookupOrCreateSym("extnew1", 0) + if es1x != es1 { + t.Fatalf("LookupOrCreateSym lookup: expected %d got %d for second lookup", es1, es1x) + } + es2 := ldr.LookupOrCreateSym("go.info.type.uint8", 0) + if es2 == 0 { + t.Fatalf("LookupOrCreateSym failed for go.info.type.uint8") + } + // Create a nameless symbol + es3 := ldr.CreateStaticSym("") + if es3 == 0 { + t.Fatalf("CreateStaticSym failed for nameless sym") + } + + // Grab symbol builder pointers + sb1 := ldr.MakeSymbolUpdater(es1) + sb2 := ldr.MakeSymbolUpdater(es2) + sb3 := ldr.MakeSymbolUpdater(es3) + + // Suppose we create some more symbols, which triggers a grow. + // Make sure the symbol builder's payload pointer is valid, + // even across a grow. + ldr.growSyms(9999) + + // Check get/set symbol type + es3typ := sb3.Type() + if es3typ != sym.Sxxx { + t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ) + } + sb3.SetType(sym.SRODATA) + es3typ = sb3.Type() + if es3typ != sym.SRODATA { + t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ) + } + es3typ = ldr.SymType(es3) + if es3typ != sym.SRODATA { + t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ) + } + + // New symbols should not initially be reachable. + if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) { + t.Errorf("newly materialized symbols should not be reachable") + } + + // ... however it should be possible to set/unset their reachability. + ldr.SetAttrReachable(es3, true) + if !ldr.AttrReachable(es3) { + t.Errorf("expected reachable symbol after update") + } + ldr.SetAttrReachable(es3, false) + if ldr.AttrReachable(es3) { + t.Errorf("expected unreachable symbol after update") + } + + // Test expansion of attr bitmaps + for idx := 0; idx < 36; idx++ { + es := ldr.LookupOrCreateSym(fmt.Sprintf("zext%d", idx), 0) + if ldr.AttrOnList(es) { + t.Errorf("expected OnList after creation") + } + ldr.SetAttrOnList(es, true) + if !ldr.AttrOnList(es) { + t.Errorf("expected !OnList after update") + } + if ldr.AttrDuplicateOK(es) { + t.Errorf("expected DupOK after creation") + } + ldr.SetAttrDuplicateOK(es, true) + if !ldr.AttrDuplicateOK(es) { + t.Errorf("expected !DupOK after update") + } + } + + sb1 = ldr.MakeSymbolUpdater(es1) + sb2 = ldr.MakeSymbolUpdater(es2) + + // Get/set a few other attributes + if ldr.AttrVisibilityHidden(es3) { + t.Errorf("expected initially not hidden") + } + ldr.SetAttrVisibilityHidden(es3, true) + if !ldr.AttrVisibilityHidden(es3) { + t.Errorf("expected hidden after update") + } + + // Test get/set symbol value. + toTest := []Sym{ts2, es3} + for i, s := range toTest { + if v := ldr.SymValue(s); v != 0 { + t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v) + } + nv := int64(i + 101) + ldr.SetSymValue(s, nv) + if v := ldr.SymValue(s); v != nv { + t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v) + } + } + + // Check/set alignment + es3al := ldr.SymAlign(es3) + if es3al != 0 { + t.Errorf("SymAlign(es3): expected 0, got %d", es3al) + } + ldr.SetSymAlign(es3, 128) + es3al = ldr.SymAlign(es3) + if es3al != 128 { + t.Errorf("SymAlign(es3): expected 128, got %d", es3al) + } + + // Add some relocations to the new symbols. + r1 := Reloc{0, 1, objabi.R_ADDR, 0, ts1} + r2 := Reloc{3, 8, objabi.R_CALL, 0, ts2} + r3 := Reloc{7, 1, objabi.R_USETYPE, 0, ts3} + sb1.AddReloc(r1) + sb1.AddReloc(r2) + sb2.AddReloc(r3) + + // Add some data to the symbols. + d1 := []byte{1, 2, 3} + d2 := []byte{4, 5, 6, 7} + sb1.AddBytes(d1) + sb2.AddBytes(d2) + + // Now invoke the usual loader interfaces to make sure + // we're getting the right things back for these symbols. + // First relocations... + expRel := [][]Reloc{[]Reloc{r1, r2}, []Reloc{r3}} + for k, sb := range []*SymbolBuilder{sb1, sb2} { + rsl := sb.Relocs() + exp := expRel[k] + if !sameRelocSlice(&rsl, exp) { + t.Errorf("expected relocs %v, got %v", exp, rsl) + } + } + + // ... then data. + dat := sb2.Data() + if bytes.Compare(dat, d2) != 0 { + t.Errorf("expected es2 data %v, got %v", d2, dat) + } + + // Nameless symbol should still be nameless. + es3name := ldr.RawSymName(es3) + if "" != es3name { + t.Errorf("expected es3 name of '', got '%s'", es3name) + } + + // Read value of materialized symbol. + es1val := sb1.Value() + if 0 != es1val { + t.Errorf("expected es1 value of 0, got %v", es1val) + } + + // Test other misc methods + irm := ldr.IsReflectMethod(es1) + if 0 != es1val { + t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm) + } + + // Writing data to a materialized symbol should mark it reachable. + if !sb1.Reachable() || !sb2.Reachable() { + t.Fatalf("written-to materialized symbols should be reachable") + } +} + +func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool { + if s1.Count() != len(s2) { + return false + } + for i := 0; i < s1.Count(); i++ { + r1 := s1.At2(i) + r2 := &s2[i] + if r1.Sym() != r2.Sym || + r1.Type() != r2.Type || + r1.Off() != r2.Off || + r1.Add() != r2.Add || + r1.Siz() != r2.Size { + return false + } + } + return true +} + +type addFunc func(l *Loader, s Sym, s2 Sym) Sym + +func TestAddDataMethods(t *testing.T) { + edummy := func(s *sym.Symbol, str string, off int) {} + ldr := NewLoader(0, edummy) + dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} + or := &dummyOreader + + // Populate loader with some symbols. + addDummyObjSym(t, ldr, or, "type.uint8") + ldr.LookupOrCreateSym("hello", 0) + + arch := sys.ArchAMD64 + var testpoints = []struct { + which string + addDataFunc addFunc + expData []byte + expKind sym.SymKind + expRel []Reloc + }{ + { + which: "AddUint8", + addDataFunc: func(l *Loader, s Sym, _ Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddUint8('a') + return s + }, + expData: []byte{'a'}, + expKind: sym.SDATA, + }, + { + which: "AddUintXX", + addDataFunc: func(l *Loader, s Sym, _ Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddUintXX(arch, 25185, 2) + return s + }, + expData: []byte{'a', 'b'}, + expKind: sym.SDATA, + }, + { + which: "SetUint8", + addDataFunc: func(l *Loader, s Sym, _ Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddUint8('a') + sb.AddUint8('b') + sb.SetUint8(arch, 1, 'c') + return s + }, + expData: []byte{'a', 'c'}, + expKind: sym.SDATA, + }, + { + which: "AddString", + addDataFunc: func(l *Loader, s Sym, _ Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.Addstring("hello") + return s + }, + expData: []byte{'h', 'e', 'l', 'l', 'o', 0}, + expKind: sym.SNOPTRDATA, + }, + { + which: "AddAddrPlus", + addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddAddrPlus(arch, s2, 3) + return s + }, + expData: []byte{0, 0, 0, 0, 0, 0, 0, 0}, + expKind: sym.SDATA, + expRel: []Reloc{Reloc{Type: objabi.R_ADDR, Size: 8, Add: 3, Sym: 6}}, + }, + { + which: "AddAddrPlus4", + addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddAddrPlus4(arch, s2, 3) + return s + }, + expData: []byte{0, 0, 0, 0}, + expKind: sym.SDATA, + expRel: []Reloc{Reloc{Type: objabi.R_ADDR, Size: 4, Add: 3, Sym: 7}}, + }, + { + which: "AddCURelativeAddrPlus", + addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddCURelativeAddrPlus(arch, s2, 7) + return s + }, + expData: []byte{0, 0, 0, 0, 0, 0, 0, 0}, + expKind: sym.SDATA, + expRel: []Reloc{Reloc{Type: objabi.R_ADDRCUOFF, Size: 8, Add: 7, Sym: 8}}, + }, + } + + var pmi Sym + for k, tp := range testpoints { + name := fmt.Sprintf("new%d", k+1) + mi := ldr.LookupOrCreateSym(name, 0) + if mi == 0 { + t.Fatalf("LookupOrCreateSym failed for '" + name + "'") + } + mi = tp.addDataFunc(ldr, mi, pmi) + if ldr.SymType(mi) != tp.expKind { + t.Errorf("testing Loader.%s: expected kind %s got %s", + tp.which, tp.expKind, ldr.SymType(mi)) + } + if bytes.Compare(ldr.Data(mi), tp.expData) != 0 { + t.Errorf("testing Loader.%s: expected data %v got %v", + tp.which, tp.expData, ldr.Data(mi)) + } + if !ldr.AttrReachable(mi) { + t.Fatalf("testing Loader.%s: sym updated should be reachable", tp.which) + } + relocs := ldr.Relocs(mi) + if !sameRelocSlice(&relocs, tp.expRel) { + t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v", + tp.which, relocs, tp.expRel) + } + pmi = mi + } +} + +func TestOuterSub(t *testing.T) { + edummy := func(s *sym.Symbol, str string, off int) {} + ldr := NewLoader(0, edummy) + dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} + or := &dummyOreader + + // Populate loader with some symbols. + addDummyObjSym(t, ldr, or, "type.uint8") + es1 := ldr.LookupOrCreateSym("outer", 0) + es2 := ldr.LookupOrCreateSym("sub1", 0) + es3 := ldr.LookupOrCreateSym("sub2", 0) + es4 := ldr.LookupOrCreateSym("sub3", 0) + es5 := ldr.LookupOrCreateSym("sub4", 0) + es6 := ldr.LookupOrCreateSym("sub5", 0) + + // Should not have an outer sym initially + if ldr.OuterSym(es1) != 0 { + t.Errorf("es1 outer sym set ") + } + if ldr.SubSym(es2) != 0 { + t.Errorf("es2 outer sym set ") + } + + // Establish first outer/sub relationship + ldr.PrependSub(es1, es2) + if ldr.OuterSym(es1) != 0 { + t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0) + } + if ldr.OuterSym(es2) != es1 { + t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1) + } + if ldr.SubSym(es1) != es2 { + t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2) + } + if ldr.SubSym(es2) != 0 { + t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0) + } + + // Establish second outer/sub relationship + ldr.PrependSub(es1, es3) + if ldr.OuterSym(es1) != 0 { + t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0) + } + if ldr.OuterSym(es2) != es1 { + t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1) + } + if ldr.OuterSym(es3) != es1 { + t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1) + } + if ldr.SubSym(es1) != es3 { + t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3) + } + if ldr.SubSym(es3) != es2 { + t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2) + } + + // Some more + ldr.PrependSub(es1, es4) + ldr.PrependSub(es1, es5) + ldr.PrependSub(es1, es6) + + // Set values. + ldr.SetSymValue(es2, 7) + ldr.SetSymValue(es3, 1) + ldr.SetSymValue(es4, 13) + ldr.SetSymValue(es5, 101) + ldr.SetSymValue(es6, 3) + + // Sort + news := ldr.SortSub(es1) + if news != es3 { + t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3) + } + pv := int64(-1) + count := 0 + for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) { + v := ldr.SymValue(ss) + if v <= pv { + t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d", + ss, v, pv) + } + pv = v + count++ + } + if count != 5 { + t.Errorf("expected %d in sub list got %d", 5, count) + } +} diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go new file mode 100644 index 0000000000..0ce5c6bde8 --- /dev/null +++ b/src/cmd/link/internal/loader/symbolbuilder.go @@ -0,0 +1,349 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loader + +import ( + "cmd/internal/goobj2" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/sym" + "fmt" +) + +// SymbolBuilder is a helper designed to help with the construction +// of new symbol contents. +type SymbolBuilder struct { + *extSymPayload // points to payload being updated + symIdx Sym // index of symbol being updated/constructed + l *Loader // loader +} + +// MakeSymbolBuilder creates a symbol builder for use in constructing +// an entirely new symbol. +func (l *Loader) MakeSymbolBuilder(name string) *SymbolBuilder { + // for now assume that any new sym is intended to be static + symIdx := l.CreateStaticSym(name) + if l.Syms[symIdx] != nil { + panic("can't build if sym.Symbol already present") + } + sb := &SymbolBuilder{l: l, symIdx: symIdx} + sb.extSymPayload = l.getPayload(symIdx) + return sb +} + +// MakeSymbolUpdater creates a symbol builder helper for an existing +// symbol 'symIdx'. If 'symIdx' is not an external symbol, then create +// a clone of it (copy name, properties, etc) fix things up so that +// the lookup tables and caches point to the new version, not the old +// version. +func (l *Loader) MakeSymbolUpdater(symIdx Sym) *SymbolBuilder { + if symIdx == 0 { + panic("can't update the null symbol") + } + if !l.IsExternal(symIdx) { + // Create a clone with the same name/version/kind etc. + l.cloneToExternal(symIdx) + } + // Now that we're doing phase 2 DWARF generation using the loader + // but before the wavefront has reached dodata(), we can't have this + // assertion here. Commented out for now. + if false { + if l.Syms[symIdx] != nil { + panic(fmt.Sprintf("can't build if sym.Symbol %q already present", l.RawSymName(symIdx))) + } + } + + // Construct updater and return. + sb := &SymbolBuilder{l: l, symIdx: symIdx} + sb.extSymPayload = l.getPayload(symIdx) + return sb +} + +// CreateSymForUpdate creates a symbol with given name and version, +// returns a CreateSymForUpdate for update. If the symbol already +// exists, it will update in-place. +func (l *Loader) CreateSymForUpdate(name string, version int) *SymbolBuilder { + return l.MakeSymbolUpdater(l.LookupOrCreateSym(name, version)) +} + +// Getters for properties of the symbol we're working on. + +func (sb *SymbolBuilder) Sym() Sym { return sb.symIdx } +func (sb *SymbolBuilder) Name() string { return sb.name } +func (sb *SymbolBuilder) Version() int { return sb.ver } +func (sb *SymbolBuilder) Type() sym.SymKind { return sb.kind } +func (sb *SymbolBuilder) Size() int64 { return sb.size } +func (sb *SymbolBuilder) Data() []byte { return sb.data } +func (sb *SymbolBuilder) Value() int64 { return sb.l.SymValue(sb.symIdx) } +func (sb *SymbolBuilder) Align() int32 { return sb.l.SymAlign(sb.symIdx) } +func (sb *SymbolBuilder) Localentry() uint8 { return sb.l.SymLocalentry(sb.symIdx) } +func (sb *SymbolBuilder) OnList() bool { return sb.l.AttrOnList(sb.symIdx) } +func (sb *SymbolBuilder) External() bool { return sb.l.AttrExternal(sb.symIdx) } +func (sb *SymbolBuilder) Extname() string { return sb.l.SymExtname(sb.symIdx) } +func (sb *SymbolBuilder) CgoExportDynamic() bool { return sb.l.AttrCgoExportDynamic(sb.symIdx) } +func (sb *SymbolBuilder) Dynimplib() string { return sb.l.SymDynimplib(sb.symIdx) } +func (sb *SymbolBuilder) Dynimpvers() string { return sb.l.SymDynimpvers(sb.symIdx) } +func (sb *SymbolBuilder) SubSym() Sym { return sb.l.SubSym(sb.symIdx) } +func (sb *SymbolBuilder) GoType() Sym { return sb.l.SymGoType(sb.symIdx) } +func (sb *SymbolBuilder) VisibilityHidden() bool { return sb.l.AttrVisibilityHidden(sb.symIdx) } + +// Setters for symbol properties. + +func (sb *SymbolBuilder) SetType(kind sym.SymKind) { sb.kind = kind } +func (sb *SymbolBuilder) SetSize(size int64) { sb.size = size } +func (sb *SymbolBuilder) SetData(data []byte) { sb.data = data } +func (sb *SymbolBuilder) SetOnList(v bool) { sb.l.SetAttrOnList(sb.symIdx, v) } +func (sb *SymbolBuilder) SetExternal(v bool) { sb.l.SetAttrExternal(sb.symIdx, v) } +func (sb *SymbolBuilder) SetValue(v int64) { sb.l.SetSymValue(sb.symIdx, v) } +func (sb *SymbolBuilder) SetAlign(align int32) { sb.l.SetSymAlign(sb.symIdx, align) } +func (sb *SymbolBuilder) SetLocalentry(value uint8) { sb.l.SetSymLocalentry(sb.symIdx, value) } +func (sb *SymbolBuilder) SetExtname(value string) { sb.l.SetSymExtname(sb.symIdx, value) } +func (sb *SymbolBuilder) SetDynimplib(value string) { sb.l.SetSymDynimplib(sb.symIdx, value) } +func (sb *SymbolBuilder) SetDynimpvers(value string) { sb.l.SetSymDynimpvers(sb.symIdx, value) } +func (sb *SymbolBuilder) SetPlt(value int32) { sb.l.SetPlt(sb.symIdx, value) } +func (sb *SymbolBuilder) SetGot(value int32) { sb.l.SetGot(sb.symIdx, value) } +func (sb *SymbolBuilder) SetSpecial(value bool) { sb.l.SetAttrSpecial(sb.symIdx, value) } +func (sb *SymbolBuilder) SetVisibilityHidden(value bool) { + sb.l.SetAttrVisibilityHidden(sb.symIdx, value) +} + +func (sb *SymbolBuilder) SetNotInSymbolTable(value bool) { + sb.l.SetAttrNotInSymbolTable(sb.symIdx, value) +} + +func (sb *SymbolBuilder) AddBytes(data []byte) { + sb.setReachable() + if sb.kind == 0 { + sb.kind = sym.SDATA + } + sb.data = append(sb.data, data...) + sb.size = int64(len(sb.data)) +} + +func (sb *SymbolBuilder) Relocs() Relocs { + return sb.l.Relocs(sb.symIdx) +} + +func (sb *SymbolBuilder) SetRelocs(rslice []Reloc) { + n := len(rslice) + if cap(sb.relocs) < n { + sb.relocs = make([]goobj2.Reloc2, n) + sb.reltypes = make([]objabi.RelocType, n) + } else { + sb.relocs = sb.relocs[:n] + sb.reltypes = sb.reltypes[:n] + } + for i := range rslice { + sb.SetReloc(i, rslice[i]) + } +} + +func (sb *SymbolBuilder) AddReloc(r Reloc) { + // Populate a goobj2.Reloc from external reloc record. + var b goobj2.Reloc2 + b.Set(r.Off, r.Size, 0, r.Add, goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(r.Sym)}) + sb.relocs = append(sb.relocs, b) + sb.reltypes = append(sb.reltypes, r.Type) +} + +// Update the j-th relocation in place. +func (sb *SymbolBuilder) SetReloc(j int, r Reloc) { + // Populate a goobj2.Reloc from external reloc record. + sb.relocs[j].Set(r.Off, r.Size, 0, r.Add, goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(r.Sym)}) + sb.reltypes[j] = r.Type +} + +func (sb *SymbolBuilder) Reachable() bool { + return sb.l.AttrReachable(sb.symIdx) +} + +func (sb *SymbolBuilder) SetReachable(v bool) { + sb.l.SetAttrReachable(sb.symIdx, v) +} + +func (sb *SymbolBuilder) setReachable() { + sb.SetReachable(true) +} + +func (sb *SymbolBuilder) ReadOnly() bool { + return sb.l.AttrReadOnly(sb.symIdx) +} + +func (sb *SymbolBuilder) SetReadOnly(v bool) { + sb.l.SetAttrReadOnly(sb.symIdx, v) +} + +func (sb *SymbolBuilder) DuplicateOK() bool { + return sb.l.AttrDuplicateOK(sb.symIdx) +} + +func (sb *SymbolBuilder) SetDuplicateOK(v bool) { + sb.l.SetAttrDuplicateOK(sb.symIdx, v) +} + +func (sb *SymbolBuilder) Outer() Sym { + return sb.l.OuterSym(sb.symIdx) +} + +func (sb *SymbolBuilder) Sub() Sym { + return sb.l.SubSym(sb.symIdx) +} + +func (sb *SymbolBuilder) SortSub() { + sb.l.SortSub(sb.symIdx) +} + +func (sb *SymbolBuilder) PrependSub(sub Sym) { + sb.l.PrependSub(sb.symIdx, sub) +} + +func (sb *SymbolBuilder) AddUint8(v uint8) int64 { + off := sb.size + if sb.kind == 0 { + sb.kind = sym.SDATA + } + sb.setReachable() + sb.size++ + sb.data = append(sb.data, v) + return off +} + +func (sb *SymbolBuilder) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 { + off := sb.size + sb.setReachable() + sb.setUintXX(arch, off, v, int64(wid)) + return off +} + +func (sb *SymbolBuilder) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 { + if sb.kind == 0 { + sb.kind = sym.SDATA + } + if sb.size < off+wid { + sb.size = off + wid + sb.Grow(sb.size) + } + + switch wid { + case 1: + sb.data[off] = uint8(v) + case 2: + arch.ByteOrder.PutUint16(sb.data[off:], uint16(v)) + case 4: + arch.ByteOrder.PutUint32(sb.data[off:], uint32(v)) + case 8: + arch.ByteOrder.PutUint64(sb.data[off:], v) + } + + return off + wid +} + +func (sb *SymbolBuilder) AddUint16(arch *sys.Arch, v uint16) int64 { + return sb.AddUintXX(arch, uint64(v), 2) +} + +func (sb *SymbolBuilder) AddUint32(arch *sys.Arch, v uint32) int64 { + return sb.AddUintXX(arch, uint64(v), 4) +} + +func (sb *SymbolBuilder) AddUint64(arch *sys.Arch, v uint64) int64 { + return sb.AddUintXX(arch, v, 8) +} + +func (sb *SymbolBuilder) AddUint(arch *sys.Arch, v uint64) int64 { + return sb.AddUintXX(arch, v, arch.PtrSize) +} + +func (sb *SymbolBuilder) SetUint8(arch *sys.Arch, r int64, v uint8) int64 { + sb.setReachable() + return sb.setUintXX(arch, r, uint64(v), 1) +} + +func (sb *SymbolBuilder) SetUint16(arch *sys.Arch, r int64, v uint16) int64 { + sb.setReachable() + return sb.setUintXX(arch, r, uint64(v), 2) +} + +func (sb *SymbolBuilder) SetUint32(arch *sys.Arch, r int64, v uint32) int64 { + sb.setReachable() + return sb.setUintXX(arch, r, uint64(v), 4) +} + +func (sb *SymbolBuilder) SetUint(arch *sys.Arch, r int64, v uint64) int64 { + sb.setReachable() + return sb.setUintXX(arch, r, v, int64(arch.PtrSize)) +} + +func (sb *SymbolBuilder) Addstring(str string) int64 { + sb.setReachable() + if sb.kind == 0 { + sb.kind = sym.SNOPTRDATA + } + r := sb.size + if sb.name == ".shstrtab" { + // FIXME: find a better mechanism for this + sb.l.elfsetstring(nil, str, int(r)) + } + sb.data = append(sb.data, str...) + sb.data = append(sb.data, 0) + sb.size = int64(len(sb.data)) + return r +} + +func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 { + if sb.kind == 0 { + sb.kind = sym.SDATA + } + i := sb.size + + sb.size += int64(rsize) + sb.Grow(sb.size) + + var r Reloc + r.Sym = tgt + r.Off = int32(i) + r.Size = uint8(rsize) + r.Type = typ + r.Add = add + sb.AddReloc(r) + + return i + int64(r.Size) +} + +// Add a symbol reference (relocation) with given type, addend, and size +// (the most generic form). +func (sb *SymbolBuilder) AddSymRef(arch *sys.Arch, tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 { + sb.setReachable() + return sb.addSymRef(tgt, add, typ, rsize) +} + +func (sb *SymbolBuilder) AddAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 { + sb.setReachable() + return sb.addSymRef(tgt, add, objabi.R_ADDR, arch.PtrSize) +} + +func (sb *SymbolBuilder) AddAddrPlus4(arch *sys.Arch, tgt Sym, add int64) int64 { + sb.setReachable() + return sb.addSymRef(tgt, add, objabi.R_ADDR, 4) +} + +func (sb *SymbolBuilder) AddAddr(arch *sys.Arch, tgt Sym) int64 { + return sb.AddAddrPlus(arch, tgt, 0) +} + +func (sb *SymbolBuilder) AddPCRelPlus(arch *sys.Arch, tgt Sym, add int64) int64 { + sb.setReachable() + return sb.addSymRef(tgt, add, objabi.R_PCREL, 4) +} + +func (sb *SymbolBuilder) AddCURelativeAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 { + sb.setReachable() + return sb.addSymRef(tgt, add, objabi.R_ADDRCUOFF, arch.PtrSize) +} + +func (sb *SymbolBuilder) AddSize(arch *sys.Arch, tgt Sym) int64 { + sb.setReachable() + return sb.addSymRef(tgt, 0, objabi.R_SIZE, arch.PtrSize) +} diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go index 85a1ebc631..d1ff82f9eb 100644 --- a/src/cmd/link/internal/loadmacho/ldmacho.go +++ b/src/cmd/link/internal/loadmacho/ldmacho.go @@ -14,7 +14,6 @@ import ( "cmd/link/internal/sym" "encoding/binary" "fmt" - "io" "sort" ) @@ -101,7 +100,7 @@ type ldMachoSect struct { flags uint32 res1 uint32 res2 uint32 - sym *sym.Symbol + sym loader.Sym rel []ldMachoRel } @@ -132,7 +131,7 @@ type ldMachoSym struct { desc uint16 kind int8 value uint64 - sym *sym.Symbol + sym loader.Sym } type ldMachoDysymtab struct { @@ -320,10 +319,9 @@ func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int { return 0 } rel := make([]ldMachoRel, sect.nreloc) - n := int(sect.nreloc * 8) - buf := make([]byte, n) m.f.MustSeek(m.base+int64(sect.reloff), 0) - if _, err := io.ReadFull(m.f, buf); err != nil { + buf, _, err := m.f.Slice(uint64(sect.nreloc * 8)) + if err != nil { return -1 } for i := uint32(0); i < sect.nreloc; i++ { @@ -364,10 +362,9 @@ func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int { func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int { n := int(d.nindirectsyms) - - p := make([]byte, n*4) m.f.MustSeek(m.base+int64(d.indirectsymoff), 0) - if _, err := io.ReadFull(m.f, p); err != nil { + p, _, err := m.f.Slice(uint64(n * 4)) + if err != nil { return -1 } @@ -383,9 +380,9 @@ func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int { return 0 } - strbuf := make([]byte, symtab.strsize) m.f.MustSeek(m.base+int64(symtab.stroff), 0) - if _, err := io.ReadFull(m.f, strbuf); err != nil { + strbuf, _, err := m.f.Slice(uint64(symtab.strsize)) + if err != nil { return -1 } @@ -394,9 +391,9 @@ func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int { symsize = 16 } n := int(symtab.nsym * uint32(symsize)) - symbuf := make([]byte, n) m.f.MustSeek(m.base+int64(symtab.symoff), 0) - if _, err := io.ReadFull(m.f, symbuf); err != nil { + symbuf, _, err := m.f.Slice(uint64(n)) + if err != nil { return -1 } sym := make([]ldMachoSym, symtab.nsym) @@ -424,28 +421,17 @@ func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int { return 0 } -func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) ([]*sym.Symbol, error) { - newSym := func(name string, version int) *sym.Symbol { - return l.LookupOrCreate(name, version, syms) - } - return load(arch, syms.IncVersion(), newSym, f, pkg, length, pn) -} - -func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { - return load(arch, syms.IncVersion(), syms.Lookup, f, pkg, length, pn) -} - -// load the Mach-O file pn from f. +// Load the Mach-O file pn from f. // Symbols are written into syms, and a slice of the text symbols is returned. -func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Symbol, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { - errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) { +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) { + errorf := func(str string, args ...interface{}) ([]loader.Sym, error) { return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...)) } base := f.Offset() - var hdr [7 * 4]uint8 - if _, err := io.ReadFull(f, hdr[:]); err != nil { + hdr, _, err := f.Slice(7 * 4) + if err != nil { return errorf("reading hdr: %v", err) } @@ -499,8 +485,8 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym } m.cmd = make([]ldMachoCmd, ncmd) - cmdp := make([]byte, cmdsz) - if _, err := io.ReadFull(f, cmdp); err != nil { + cmdp, _, err := f.Slice(uint64(cmdsz)) + if err != nil { return errorf("reading cmds: %v", err) } @@ -559,8 +545,8 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym } f.MustSeek(m.base+int64(c.seg.fileoff), 0) - dat := make([]byte, c.seg.filesz) - if _, err := io.ReadFull(f, dat); err != nil { + dat, readOnly, err := f.Slice(uint64(c.seg.filesz)) + if err != nil { return errorf("cannot load object data: %v", err) } @@ -573,30 +559,32 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym continue } name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name) - s := lookup(name, localSymVersion) - if s.Type != 0 { + s := l.LookupOrCreateSym(name, localSymVersion) + bld := l.MakeSymbolUpdater(s) + if bld.Type() != 0 { return errorf("duplicate %s/%s", sect.segname, sect.name) } if sect.flags&0xff == 1 { // S_ZEROFILL - s.P = make([]byte, sect.size) + bld.SetData(make([]byte, sect.size)) } else { - s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size] + bld.SetReadOnly(readOnly) + bld.SetData(dat[sect.addr-c.seg.vmaddr:][:sect.size]) } - s.Size = int64(len(s.P)) + bld.SetSize(int64(len(bld.Data()))) if sect.segname == "__TEXT" { if sect.name == "__text" { - s.Type = sym.STEXT + bld.SetType(sym.STEXT) } else { - s.Type = sym.SRODATA + bld.SetType(sym.SRODATA) } } else { if sect.name == "__bss" { - s.Type = sym.SNOPTRBSS - s.P = s.P[:0] + bld.SetType(sym.SNOPTRBSS) + bld.SetData(nil) } else { - s.Type = sym.SNOPTRDATA + bld.SetType(sym.SNOPTRDATA) } } @@ -621,12 +609,12 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym if machsym.type_&N_EXT == 0 { v = localSymVersion } - s := lookup(name, v) + s := l.LookupOrCreateSym(name, v) if machsym.type_&N_EXT == 0 { - s.Attr |= sym.AttrDuplicateOK + l.SetAttrDuplicateOK(s, true) } if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 { - s.Attr |= sym.AttrDuplicateOK + l.SetAttrDuplicateOK(s, true) } machsym.sym = s if machsym.sectnum == 0 { // undefined @@ -637,35 +625,32 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym } sect := &c.seg.sect[machsym.sectnum-1] + bld := l.MakeSymbolUpdater(s) outer := sect.sym - if outer == nil { + if outer == 0 { continue // ignore reference to invalid section } - if s.Outer != nil { - if s.Attr.DuplicateOK() { + if osym := l.OuterSym(s); osym != 0 { + if l.AttrDuplicateOK(s) { continue } - return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name) + return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym)) } - s.Type = outer.Type - s.Attr |= sym.AttrSubSymbol - s.Sub = outer.Sub - outer.Sub = s - s.Outer = outer - s.Value = int64(machsym.value - sect.addr) - if !s.Attr.CgoExportDynamic() { - s.SetDynimplib("") // satisfy dynimport + bld.SetType(l.SymType(outer)) + l.PrependSub(outer, s) + + bld.SetValue(int64(machsym.value - sect.addr)) + if !l.AttrCgoExportDynamic(s) { + bld.SetDynimplib("") // satisfy dynimport } - if outer.Type == sym.STEXT { - if s.Attr.External() && !s.Attr.DuplicateOK() { + if l.SymType(outer) == sym.STEXT { + if bld.External() && !bld.DuplicateOK() { return errorf("%v: duplicate symbol definition", s) } - s.Attr |= sym.AttrExternal + bld.SetExternal(true) } - - machsym.sym = s } // Sort outer lists by address, adding to textp. @@ -673,33 +658,37 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym for i := 0; uint32(i) < c.seg.nsect; i++ { sect := &c.seg.sect[i] s := sect.sym - if s == nil { + if s == 0 { continue } - if s.Sub != nil { - s.Sub = sym.SortSub(s.Sub) + bld := l.MakeSymbolUpdater(s) + if bld.SubSym() != 0 { + + bld.SortSub() // assign sizes, now that we know symbols in sorted order. - for s1 := s.Sub; s1 != nil; s1 = s1.Sub { - if s1.Sub != nil { - s1.Size = s1.Sub.Value - s1.Value + for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) { + s1Bld := l.MakeSymbolUpdater(s1) + if sub := l.SubSym(s1); sub != 0 { + s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1)) } else { - s1.Size = s.Value + s.Size - s1.Value + dlen := int64(len(l.Data(s))) + s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1)) } } } - if s.Type == sym.STEXT { - if s.Attr.OnList() { - return errorf("symbol %s listed multiple times", s.Name) + if bld.Type() == sym.STEXT { + if bld.OnList() { + return errorf("symbol %s listed multiple times", bld.Name()) } - s.Attr |= sym.AttrOnList + bld.SetOnList(true) textp = append(textp, s) - for s1 := s.Sub; s1 != nil; s1 = s1.Sub { - if s1.Attr.OnList() { - return errorf("symbol %s listed multiple times", s1.Name) + for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) { + if l.AttrOnList(s1) { + return errorf("symbol %s listed multiple times", l.RawSymName(s1)) } - s1.Attr |= sym.AttrOnList + l.SetAttrOnList(s1, true) textp = append(textp, s1) } } @@ -709,14 +698,14 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym for i := 0; uint32(i) < c.seg.nsect; i++ { sect := &c.seg.sect[i] s := sect.sym - if s == nil { + if s == 0 { continue } macholoadrel(m, sect) if sect.rel == nil { continue } - r := make([]sym.Reloc, sect.nreloc) + r := make([]loader.Reloc, sect.nreloc) rpi := 0 Reloc: for j := uint32(0); j < sect.nreloc; j++ { @@ -741,7 +730,7 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym return errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_)) } - rp.Siz = rel.length + rp.Size = rel.length rp.Off = int32(rel.addr) // NOTE(rsc): I haven't worked out why (really when) @@ -765,7 +754,7 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym for k := 0; uint32(k) < c.seg.nsect; k++ { ks := &c.seg.sect[k] if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size { - if ks.sym != nil { + if ks.sym != 0 { rp.Sym = ks.sym rp.Add += int64(uint64(rel.value) - ks.addr) } else if ks.segname == "__IMPORT" && ks.name == "__pointers" { @@ -805,11 +794,12 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym return errorf("unsupported scattered relocation: invalid address %#x", rel.addr) } - rp.Siz = rel.length + rp.Size = rel.length rp.Type = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel) rp.Off = int32(rel.addr) // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). + p := l.Data(s) if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED { // Calculate the addend as the offset into the section. // @@ -828,9 +818,9 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] secaddr := c.seg.sect[rel.symnum-1].addr - rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr) + rp.Add = int64(uint64(int64(int32(e.Uint32(p[rp.Off:])))+int64(rp.Off)+4) - secaddr) } else { - rp.Add = int64(int32(e.Uint32(s.P[rp.Off:]))) + rp.Add = int64(int32(e.Uint32(p[rp.Off:]))) } // An unsigned internal relocation has a value offset @@ -844,7 +834,7 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym // it *is* the PC being subtracted. Use that to make // it match our version of PC-relative. if rel.pcrel != 0 && arch.Family == sys.I386 { - rp.Add += int64(rp.Off) + int64(rp.Siz) + rp.Add += int64(rp.Off) + int64(rp.Size) } if rel.extrn == 0 { if rel.symnum < 1 || rel.symnum > c.seg.nsect { @@ -852,7 +842,7 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym } rp.Sym = c.seg.sect[rel.symnum-1].sym - if rp.Sym == nil { + if rp.Sym == 0 { return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name) } @@ -874,9 +864,9 @@ func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Sym rpi++ } - sort.Sort(sym.RelocByOff(r[:rpi])) - s.R = r - s.R = s.R[:rpi] + sort.Sort(loader.RelocByOff(r[:rpi])) + sb := l.MakeSymbolUpdater(sect.sym) + sb.SetRelocs(r[:rpi]) } return textp, nil diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go index 8b6aac338c..88819f3488 100644 --- a/src/cmd/link/internal/loadpe/ldpe.go +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -145,22 +145,26 @@ func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) { return n, nil } -func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) { - lookup := func(name string, version int) *sym.Symbol { - return l.LookupOrCreate(name, version, syms) +// makeUpdater creates a loader.SymbolBuilder if one hasn't been created previously. +// We use this to lazily make SymbolBuilders as we don't always need a builder, and creating them for all symbols might be an error. +func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loader.SymbolBuilder { + if bld != nil { + return bld } - return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn) + bld = l.MakeSymbolUpdater(s) + return bld } -func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) { - return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn) -} - -// load loads the PE file pn from input. +// Load loads the PE file pn from input. // Symbols are written into syms, and a slice of the text symbols is returned. // If an .rsrc section is found, its symbol is returned as rsrc. -func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) { - sectsyms := make(map[*pe.Section]*sym.Symbol) +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc loader.Sym, err error) { + lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) { + s := l.LookupOrCreateSym(name, version) + sb := l.MakeSymbolUpdater(s) + return sb, s + } + sectsyms := make(map[*pe.Section]loader.Sym) sectdata := make(map[*pe.Section][]byte) // Some input files are archives containing multiple of @@ -172,7 +176,7 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details) f, err := pe.NewFile(sr) if err != nil { - return nil, nil, err + return nil, 0, err } defer f.Close() @@ -191,34 +195,34 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion } name := fmt.Sprintf("%s(%s)", pkg, sect.Name) - s := lookup(name, localSymVersion) + bld, s := lookup(name, localSymVersion) switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata - s.Type = sym.SRODATA + bld.SetType(sym.SRODATA) case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss - s.Type = sym.SNOPTRBSS + bld.SetType(sym.SNOPTRBSS) case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data - s.Type = sym.SNOPTRDATA + bld.SetType(sym.SNOPTRDATA) case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text - s.Type = sym.STEXT + bld.SetType(sym.STEXT) default: - return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name) + return nil, 0, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name) } - if s.Type != sym.SNOPTRBSS { + if bld.Type() != sym.SNOPTRBSS { data, err := sect.Data() if err != nil { - return nil, nil, err + return nil, 0, err } sectdata[sect] = data - s.P = data + bld.SetData(data) } - s.Size = int64(sect.Size) + bld.SetSize(int64(sect.Size)) sectsyms[sect] = s if sect.Name == ".rsrc" { rsrc = s @@ -242,35 +246,35 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion continue } - rs := make([]sym.Reloc, rsect.NumberOfRelocations) + rs := make([]loader.Reloc, rsect.NumberOfRelocations) for j, r := range rsect.Relocs { rp := &rs[j] if int(r.SymbolTableIndex) >= len(f.COFFSymbols) { - return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) + return nil, 0, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) } pesym := &f.COFFSymbols[r.SymbolTableIndex] - gosym, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion) + _, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion) if err != nil { - return nil, nil, err + return nil, 0, err } - if gosym == nil { + if gosym == 0 { name, err := pesym.FullName(f.StringTable) if err != nil { name = string(pesym.Name[:]) } - return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type) + return nil, 0, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type) } rp.Sym = gosym - rp.Siz = 4 + rp.Size = 4 rp.Off = int32(r.VirtualAddress) switch arch.Family { default: - return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family) + return nil, 0, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family) case sys.I386, sys.AMD64: switch r.Type { default: - return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type) + return nil, 0, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type) case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 @@ -286,7 +290,7 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:]))) case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 - rp.Siz = 8 + rp.Size = 8 rp.Type = objabi.R_ADDR @@ -297,7 +301,7 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion case sys.ARM: switch r.Type { default: - return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type) + return nil, 0, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type) case IMAGE_REL_ARM_SECREL: rp.Type = objabi.R_PCREL @@ -324,11 +328,10 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion } } - sort.Sort(sym.RelocByOff(rs[:rsect.NumberOfRelocations])) + sort.Sort(loader.RelocByOff(rs[:rsect.NumberOfRelocations])) - s := sectsyms[rsect] - s.R = rs - s.R = s.R[:rsect.NumberOfRelocations] + bld := l.MakeSymbolUpdater(sectsyms[rsect]) + bld.SetRelocs(rs[:rsect.NumberOfRelocations]) } // enter sub-symbols into symbol table. @@ -339,7 +342,7 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion name, err := pesym.FullName(f.StringTable) if err != nil { - return nil, nil, err + return nil, 0, err } if name == "" { continue @@ -361,54 +364,56 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion } } - s, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion) + bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion) if err != nil { - return nil, nil, err + return nil, 0, err } if pesym.SectionNumber == 0 { // extern - if s.Type == sym.SDYNIMPORT { - s.SetPlt(-2) // flag for dynimport in PE object files. + if l.SymType(s) == sym.SDYNIMPORT { + bld = makeUpdater(l, bld, s) + bld.SetPlt(-2) // flag for dynimport in PE object files. } - if s.Type == sym.SXREF && pesym.Value > 0 { // global data - s.Type = sym.SNOPTRDATA - s.Size = int64(pesym.Value) + if l.SymType(s) == sym.SXREF && pesym.Value > 0 { // global data + bld = makeUpdater(l, bld, s) + bld.SetType(sym.SNOPTRDATA) + bld.SetSize(int64(pesym.Value)) } continue } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) { sect = f.Sections[pesym.SectionNumber-1] if _, found := sectsyms[sect]; !found { - return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s) + return nil, 0, fmt.Errorf("%s: %v: missing sect.sym", pn, s) } } else { - return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s) + return nil, 0, fmt.Errorf("%s: %v: sectnum < 0!", pn, s) } if sect == nil { - return nil, rsrc, nil + return nil, 0, nil } - if s.Outer != nil { - if s.Attr.DuplicateOK() { + if l.OuterSym(s) != 0 { + if l.AttrDuplicateOK(s) { continue } - return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sectsyms[sect].Name) + outerName := l.SymName(l.OuterSym(s)) + sectName := l.SymName(sectsyms[sect]) + return nil, 0, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName) } + bld = makeUpdater(l, bld, s) sectsym := sectsyms[sect] - s.Sub = sectsym.Sub - sectsym.Sub = s - s.Type = sectsym.Type - s.Attr |= sym.AttrSubSymbol - s.Value = int64(pesym.Value) - s.Size = 4 - s.Outer = sectsym - if sectsym.Type == sym.STEXT { - if s.Attr.External() && !s.Attr.DuplicateOK() { - return nil, nil, fmt.Errorf("%s: duplicate symbol definition", s.Name) + bld.SetType(l.SymType(sectsym)) + l.PrependSub(sectsym, s) + bld.SetValue(int64(pesym.Value)) + bld.SetSize(4) + if l.SymType(sectsym) == sym.STEXT { + if bld.External() && !bld.DuplicateOK() { + return nil, 0, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s)) } - s.Attr |= sym.AttrExternal + bld.SetExternal(true) } } @@ -416,23 +421,16 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion // This keeps textp in increasing address order. for _, sect := range f.Sections { s := sectsyms[sect] - if s == nil { + if s == 0 { continue } - if s.Sub != nil { - s.Sub = sym.SortSub(s.Sub) - } - if s.Type == sym.STEXT { - if s.Attr.OnList() { - return nil, nil, fmt.Errorf("symbol %s listed multiple times", s.Name) - } - s.Attr |= sym.AttrOnList - textp = append(textp, s) - for s = s.Sub; s != nil; s = s.Sub { - if s.Attr.OnList() { - return nil, nil, fmt.Errorf("symbol %s listed multiple times", s.Name) + l.SortSub(s) + if l.SymType(s) == sym.STEXT { + for ; s != 0; s = l.SubSym(s) { + if l.AttrOnList(s) { + return nil, 0, fmt.Errorf("symbol %s listed multiple times", l.SymName(s)) } - s.Attr |= sym.AttrOnList + l.SetAttrOnList(s, true) textp = append(textp, s) } } @@ -445,14 +443,14 @@ func issect(s *pe.COFFSymbol) bool { return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.' } -func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]*sym.Symbol, localSymVersion int) (*sym.Symbol, error) { +func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader.Sym, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]loader.Sym, localSymVersion int) (*loader.SymbolBuilder, loader.Sym, error) { symname, err := pesym.FullName(f.StringTable) if err != nil { - return nil, err + return nil, 0, err } var name string if issect(pesym) { - name = sectsyms[f.Sections[pesym.SectionNumber-1]].Name + name = l.SymName(sectsyms[f.Sections[pesym.SectionNumber-1]]) } else { name = symname switch arch.Family { @@ -483,10 +481,11 @@ func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File, name = name[:i] } - var s *sym.Symbol + var s loader.Sym + var bld *loader.SymbolBuilder switch pesym.Type { default: - return nil, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type) + return nil, 0, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type) case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL: switch pesym.StorageClass { @@ -495,19 +494,22 @@ func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File, case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL: s = lookup(name, localSymVersion) - s.Attr |= sym.AttrDuplicateOK + bld = makeUpdater(l, bld, s) + bld.SetDuplicateOK(true) default: - return nil, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass) + return nil, 0, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass) } } - if s != nil && s.Type == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) { - s.Type = sym.SXREF + if s != 0 && l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) { + bld = makeUpdater(l, bld, s) + bld.SetType(sym.SXREF) } if strings.HasPrefix(symname, "__imp_") { - s.SetGot(-2) // flag for __imp_ + bld = makeUpdater(l, bld, s) + bld.SetGot(-2) // flag for __imp_ } - return s, nil + return bld, s, nil } diff --git a/src/cmd/link/internal/loadxcoff/ldxcoff.go b/src/cmd/link/internal/loadxcoff/ldxcoff.go index 759b1769dd..906e871b09 100644 --- a/src/cmd/link/internal/loadxcoff/ldxcoff.go +++ b/src/cmd/link/internal/loadxcoff/ldxcoff.go @@ -19,7 +19,7 @@ import ( // ldSection is an XCOFF section with its symbols. type ldSection struct { xcoff.Section - sym *sym.Symbol + sym loader.Sym } // TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf @@ -39,23 +39,10 @@ func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) { return n, nil } -// Load loads xcoff files with the indexed object files. -func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { - lookup := func(name string, version int) *sym.Symbol { - return l.LookupOrCreate(name, version, syms) - } - return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn) -} - -// LoadOld uses the old version of object loading. -func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { - return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn) -} - // loads the Xcoff file pn from f. -// Symbols are written into syms, and a slice of the text symbols is returned. -func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { - errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) { +// Symbols are written into loader, and a slice of the text symbols is returned. +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) { + errorf := func(str string, args ...interface{}) ([]loader.Sym, error) { return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...)) } @@ -75,34 +62,35 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion lds := new(ldSection) lds.Section = *sect name := fmt.Sprintf("%s(%s)", pkg, lds.Name) - s := lookup(name, localSymVersion) + symbol := l.LookupOrCreateSym(name, localSymVersion) + s := l.MakeSymbolUpdater(symbol) switch lds.Type { default: return errorf("unrecognized section type 0x%x", lds.Type) case xcoff.STYP_TEXT: - s.Type = sym.STEXT + s.SetType(sym.STEXT) case xcoff.STYP_DATA: - s.Type = sym.SNOPTRDATA + s.SetType(sym.SNOPTRDATA) case xcoff.STYP_BSS: - s.Type = sym.SNOPTRBSS + s.SetType(sym.SNOPTRBSS) } - s.Size = int64(lds.Size) - if s.Type != sym.SNOPTRBSS { + s.SetSize(int64(lds.Size)) + if s.Type() != sym.SNOPTRBSS { data, err := lds.Section.Data() if err != nil { return nil, err } - s.P = data + s.SetData(data) } - lds.sym = s + lds.sym = symbol ldSections = append(ldSections, lds) } // sx = symbol from file - // s = symbol for syms + // s = symbol for loader for _, sx := range f.Symbols { // get symbol type stype, errmsg := getSymbolType(f, sx) @@ -113,14 +101,14 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion continue } - s := lookup(sx.Name, 0) + s := l.LookupOrCreateSym(sx.Name, 0) // Text symbol - if s.Type == sym.STEXT { - if s.Attr.OnList() { - return errorf("symbol %s listed multiple times", s.Name) + if l.SymType(s) == sym.STEXT { + if l.AttrOnList(s) { + return errorf("symbol %s listed multiple times", l.SymName(s)) } - s.Attr |= sym.AttrOnList + l.SetAttrOnList(s, true) textp = append(textp, s) } } @@ -131,11 +119,11 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA { continue } - rs := make([]sym.Reloc, sect.Nreloc) + rs := make([]loader.Reloc, sect.Nreloc) for i, rx := range sect.Relocs { r := &rs[i] - r.Sym = lookup(rx.Symbol.Name, 0) + r.Sym = l.LookupOrCreateSym(rx.Symbol.Name, 0) if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress { return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress) } @@ -149,27 +137,26 @@ func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion if rx.Length != 64 { return errorf("section %s: relocation R_POS has length different from 64: %d", sect.Name, rx.Length) } - r.Siz = 8 + r.Size = 8 r.Type = objabi.R_CONST r.Add = int64(rx.Symbol.Value) case xcoff.R_RBR: - r.Siz = 4 + r.Size = 4 r.Type = objabi.R_CALLPOWER r.Add = 0 // } } - s := sect.sym - s.R = rs - s.R = s.R[:sect.Nreloc] + bld := l.MakeSymbolUpdater(sect.sym) + bld.SetRelocs(rs[:sect.Nreloc]) } return textp, nil } // Convert symbol xcoff type to sym.SymKind -// Returns nil if this shouldn't be added into syms (like .file or .dw symbols ) +// Returns nil if this shouldn't be added into loader (like .file or .dw symbols ) func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) { // .file symbol if s.SectionNumber == -2 { diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go index 16c94c147a..f8386d8ac0 100644 --- a/src/cmd/link/internal/mips/asm.go +++ b/src/cmd/link/internal/mips/asm.go @@ -34,17 +34,19 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/elf" "fmt" "log" + "sync" ) func gentext(ctxt *ld.Link) { return } -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { log.Fatalf("adddynrel not implemented") return false } @@ -74,7 +76,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { return true } -func elfsetupplt(ctxt *ld.Link) { +func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { return } @@ -96,8 +98,8 @@ func applyrel(arch *sys.Arch, r *sym.Reloc, s *sym.Symbol, val int64, t int64) i } } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { - if ctxt.LinkMode == ld.LinkExternal { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if target.IsExternal() { switch r.Type { default: return val, false @@ -116,12 +118,12 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo ld.Errorf(s, "missing section for %s", rs.Name) } r.Xsym = rs - return applyrel(ctxt.Arch, r, s, val, r.Xadd), true + return applyrel(target.Arch, r, s, val, r.Xadd), true case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS: r.Done = false r.Xsym = r.Sym r.Xadd = r.Add - return applyrel(ctxt.Arch, r, s, val, r.Add), true + return applyrel(target.Arch, r, s, val, r.Add), true } } @@ -129,10 +131,10 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo case objabi.R_CONST: return r.Add, true case objabi.R_GOTOFF: - return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU: t := ld.Symaddr(r.Sym) + r.Add - return applyrel(ctxt.Arch, r, s, val, t), true + return applyrel(target.Arch, r, s, val, t), true case objabi.R_CALLMIPS, objabi.R_JMPMIPS: t := ld.Symaddr(r.Sym) + r.Add @@ -145,20 +147,20 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t) } - return applyrel(ctxt.Arch, r, s, val, t), true + return applyrel(target.Arch, r, s, val, t), true case objabi.R_ADDRMIPSTLS: // thread pointer is at 0x7000 offset from the start of TLS data area t := ld.Symaddr(r.Sym) + r.Add - 0x7000 if t < -32768 || t >= 32678 { ld.Errorf(s, "TLS offset out of range %d", t) } - return applyrel(ctxt.Arch, r, s, val, t), true + return applyrel(target.Arch, r, s, val, t), true } return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { return -1 } @@ -167,24 +169,24 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup sect := ld.Segtext.Sections[0] - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length) + for _, sect = range ld.Segtext.Sections[1:] { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 5c6fef9c5b..3323616a37 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -34,15 +34,17 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/elf" "fmt" "log" + "sync" ) func gentext(ctxt *ld.Link) {} -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { log.Fatalf("adddynrel not implemented") return false } @@ -91,7 +93,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { return true } -func elfsetupplt(ctxt *ld.Link) { +func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { return } @@ -99,8 +101,8 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se return false } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { - if ctxt.LinkMode == ld.LinkExternal { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if target.IsExternal() { switch r.Type { default: return val, false @@ -136,11 +138,11 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo case objabi.R_CONST: return r.Add, true case objabi.R_GOTOFF: - return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU: t := ld.Symaddr(r.Sym) + r.Add - o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:]) + o1 := target.Arch.ByteOrder.Uint32(s.P[r.Off:]) if r.Type == objabi.R_ADDRMIPS { return int64(o1&0xffff0000 | uint32(t)&0xffff), true } @@ -151,20 +153,20 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo if t < -32768 || t >= 32678 { ld.Errorf(s, "TLS offset out of range %d", t) } - o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:]) + o1 := target.Arch.ByteOrder.Uint32(s.P[r.Off:]) return int64(o1&0xffff0000 | uint32(t)&0xffff), true case objabi.R_CALLMIPS, objabi.R_JMPMIPS: // Low 26 bits = (S + A) >> 2 t := ld.Symaddr(r.Sym) + r.Add - o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:]) + o1 := target.Arch.ByteOrder.Uint32(s.P[r.Off:]) return int64(o1&0xfc000000 | uint32(t>>2)&^0xfc000000), true } return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { return -1 } @@ -173,28 +175,28 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup sect := ld.Segtext.Sections[0] - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) - for _, sect = range ld.Segtext.Sections[1:] { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length) + + for _, sect := range ld.Segtext.Sections[1:] { + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } + if ld.Segrelrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 9fbcff551a..f4ba47b8b6 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -34,12 +34,14 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/elf" "encoding/binary" "fmt" "log" "strings" + "sync" ) func genplt(ctxt *ld.Link) { @@ -262,22 +264,23 @@ func gencallstub(ctxt *ld.Link, abicase int, stub *sym.Symbol, targ *sym.Symbol) stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr } -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { - if ctxt.IsELF { - return addelfdynrel(ctxt, s, r) - } else if ctxt.HeadType == objabi.Haix { - return ld.Xcoffadddynrel(ctxt, s, r) +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { + if target.IsElf() { + return addelfdynrel(target, syms, s, r) + } else if target.IsAIX() { + return ld.Xcoffadddynrel(target, ldr, s, r) } return false } -func addelfdynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + +func addelfdynrel(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { targ := r.Sym r.InitExt() switch r.Type { default: if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) return false } @@ -313,12 +316,12 @@ func addelfdynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { r.Type = objabi.R_ADDR if targ.Type == sym.SDYNIMPORT { // These happen in .toc sections - ld.Adddynsym(ctxt, targ) + ld.Adddynsym(target, syms, targ) - rela := ctxt.Syms.Lookup(".rela", 0) - rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(targ.Dynid), uint32(elf.R_PPC64_ADDR64))) - rela.AddUint64(ctxt.Arch, uint64(r.Add)) + rela := syms.Rela + rela.AddAddrPlus(target.Arch, s, int64(r.Off)) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(targ.Dynid), uint32(elf.R_PPC64_ADDR64))) + rela.AddUint64(target.Arch, uint64(r.Add)) r.Type = objabi.ElfRelocOffset // ignore during relocsym } @@ -498,14 +501,13 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { return true } -func elfsetupplt(ctxt *ld.Link) { - plt := ctxt.Syms.Lookup(".plt", 0) - if plt.Size == 0 { +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { // The dynamic linker stores the address of the // dynamic resolver and the DSO identifier in the two // doublewords at the beginning of the .plt section // before the PLT array. Reserve space for these. - plt.Size = 16 + plt.SetSize(16) } } @@ -514,15 +516,13 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se } // Return the value of .TOC. for symbol s -func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 { - var toc *sym.Symbol - +func symtoc(syms *ld.ArchSyms, s *sym.Symbol) int64 { + v := s.Version if s.Outer != nil { - toc = ctxt.Syms.ROLookup(".TOC.", int(s.Outer.Version)) - } else { - toc = ctxt.Syms.ROLookup(".TOC.", int(s.Version)) + v = s.Outer.Version } + toc := syms.DotTOC[v] if toc == nil { ld.Errorf(s, "TOC-relative relocation in object without .TOC.") return 0 @@ -536,8 +536,8 @@ func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 { // default load instruction can be changed to an addi instruction and the // symbol address can be used directly. // This code is for AIX only. -func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 { - if ctxt.HeadType == objabi.Hlinux { +func archreloctoc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) int64 { + if target.IsLinux() { ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name) } var o1, o2 uint32 @@ -555,13 +555,13 @@ func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 { ld.Errorf(s, "archreloctoc called for a symbol without TOC anchor") } - if ctxt.LinkMode == ld.LinkInternal && tarSym != nil && tarSym.Attr.Reachable() && (tarSym.Sect.Seg == &ld.Segdata) { - t = ld.Symaddr(tarSym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value + if target.IsInternal() && tarSym != nil && tarSym.Attr.Reachable() && (tarSym.Sect.Seg == &ld.Segdata) { + t = ld.Symaddr(tarSym) + r.Add - syms.TOC.Value // change ld to addi in the second instruction o2 = (o2 & 0x03FF0000) | 0xE<<26 useAddi = true } else { - t = ld.Symaddr(r.Sym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value + t = ld.Symaddr(r.Sym) + r.Add - syms.TOC.Value } if t != int64(int32(t)) { @@ -593,12 +593,12 @@ func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 { // archrelocaddr relocates a symbol address. // This code is for AIX only. -func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 { - if ctxt.HeadType == objabi.Haix { +func archrelocaddr(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) int64 { + if target.IsAIX() { ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name) } var o1, o2 uint32 - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { o1 = uint32(val >> 32) o2 = uint32(val) } else { @@ -635,7 +635,7 @@ func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 return -1 } - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { return int64(o1)<<32 | int64(o2) } return int64(o2)<<32 | int64(o1) @@ -770,13 +770,13 @@ func gentramp(ctxt *ld.Link, tramp, target *sym.Symbol, offset int64) { ctxt.Arch.ByteOrder.PutUint32(tramp.P[12:], o4) } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { - if ctxt.LinkMode == ld.LinkExternal { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if target.IsExternal() { // On AIX, relocations (except TLS ones) must be also done to the // value with the current addresses. switch r.Type { default: - if ctxt.HeadType != objabi.Haix { + if target.IsAIX() { return val, false } case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE: @@ -806,14 +806,14 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo } r.Xsym = rs - if ctxt.HeadType != objabi.Haix { + if !target.IsAIX() { return val, true } case objabi.R_CALLPOWER: r.Done = false r.Xsym = r.Sym r.Xadd = r.Add - if ctxt.HeadType != objabi.Haix { + if !target.IsAIX() { return val, true } } @@ -823,11 +823,11 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo case objabi.R_CONST: return r.Add, true case objabi.R_GOTOFF: - return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS: - return archreloctoc(ctxt, r, s, val), true + return archreloctoc(target, syms, r, s, val), true case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS: - return archrelocaddr(ctxt, r, s, val), true + return archrelocaddr(target, syms, r, s, val), true case objabi.R_CALLPOWER: // Bits 6 through 29 = (S + A - P) >> 2 @@ -843,7 +843,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo } return val | int64(uint32(t)&^0xfc000003), true case objabi.R_POWER_TOC: // S + A - .TOC. - return ld.Symaddr(r.Sym) + r.Add - symtoc(ctxt, s), true + return ld.Symaddr(r.Sym) + r.Add - symtoc(syms, s), true case objabi.R_POWER_TLS_LE: // The thread pointer points 0x7000 bytes after the start of the @@ -851,7 +851,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo // Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI // Specification". v := r.Sym.Value - 0x7000 - if ctxt.HeadType == objabi.Haix { + if target.IsAIX() { // On AIX, the thread pointer points 0x7800 bytes after // the TLS. v -= 0x800 @@ -865,7 +865,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { switch r.Variant & sym.RV_TYPE_MASK { default: ld.Errorf(s, "unexpected relocation variant %d", r.Variant) @@ -879,7 +879,7 @@ func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 // Whether to check for signed or unsigned // overflow depends on the instruction var o1 uint32 - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { o1 = binary.BigEndian.Uint32(s.P[r.Off-2:]) } else { o1 = binary.LittleEndian.Uint32(s.P[r.Off:]) @@ -913,7 +913,7 @@ func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 // Whether to check for signed or unsigned // overflow depends on the instruction var o1 uint32 - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { o1 = binary.BigEndian.Uint32(s.P[r.Off-2:]) } else { o1 = binary.LittleEndian.Uint32(s.P[r.Off:]) @@ -937,7 +937,7 @@ func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 case sym.RV_POWER_DS: var o1 uint32 - if ctxt.Arch.ByteOrder == binary.BigEndian { + if target.IsBigEndian() { o1 = uint32(binary.BigEndian.Uint16(s.P[r.Off:])) } else { o1 = uint32(binary.LittleEndian.Uint16(s.P[r.Off:])) @@ -961,13 +961,13 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) { return } - ld.Adddynsym(ctxt, s) + ld.Adddynsym(&ctxt.Target, &ctxt.ArchSyms, s) if ctxt.IsELF { plt := ctxt.Syms.Lookup(".plt", 0) rela := ctxt.Syms.Lookup(".rela.plt", 0) if plt.Size == 0 { - elfsetupplt(ctxt) + panic("plt is not set up") } // Create the glink resolver if necessary @@ -1056,7 +1056,7 @@ func ensureglinkresolver(ctxt *ld.Link) *sym.Symbol { // before the first symbol resolver stub. s := ctxt.Syms.Lookup(".dynamic", 0) - ld.Elfwritedynentsymplus(ctxt, s, ld.DT_PPC64_GLINK, glink, glink.Size-32) + ld.Elfwritedynentsymplus(ctxt.Arch, s, ld.DT_PPC64_GLINK, glink, glink.Size-32) return glink } @@ -1066,30 +1066,34 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup for _, sect := range ld.Segtext.Sections { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff // Handle additional text sections with Codeblk if sect.Name == ".text" { - ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length) } else { - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } } + for _, sect := range ld.Segtext.Sections[1:] { + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) + } + if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } + if ld.Segrelrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go index b0897288fc..cc83b96a96 100644 --- a/src/cmd/link/internal/riscv64/asm.go +++ b/src/cmd/link/internal/riscv64/asm.go @@ -9,19 +9,21 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "fmt" "log" + "sync" ) func gentext(ctxt *ld.Link) { } -func adddynrela(ctxt *ld.Link, rel *sym.Symbol, s *sym.Symbol, r *sym.Reloc) { +func adddynrela(target *ld.Target, syms *ld.ArchSyms, rel *sym.Symbol, s *sym.Symbol, r *sym.Reloc) { log.Fatalf("adddynrela not implemented") } -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { log.Fatalf("adddynrel not implemented") return false } @@ -31,7 +33,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { return false } -func elfsetupplt(ctxt *ld.Link) { +func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { log.Fatalf("elfsetuplt") } @@ -40,7 +42,7 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se return false } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { switch r.Type { case objabi.R_CALLRISCV: // Nothing to do. @@ -91,7 +93,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { log.Fatalf("archrelocvariant") return -1 } @@ -101,28 +103,28 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup sect := ld.Segtext.Sections[0] - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) - for _, sect = range ld.Segtext.Sections[1:] { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length) + + for _, sect := range ld.Segtext.Sections[1:] { + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } + if ld.Segrelrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index 94a5a2f86c..7750da21cd 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -34,9 +34,11 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/elf" "fmt" + "sync" ) // gentext generates assembly to append the local moduledata to the global @@ -104,7 +106,7 @@ func gentext(ctxt *ld.Link) { initarray_entry.AddAddr(ctxt.Arch, initfunc) } -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { targ := r.Sym r.InitExt() @@ -158,8 +160,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { r.Variant = sym.RV_390_DBL r.Add += int64(r.Siz) if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add += int64(targ.Plt()) } return true @@ -169,8 +171,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { r.Type = objabi.R_PCREL r.Add += int64(r.Siz) if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add += int64(targ.Plt()) } return true @@ -200,7 +202,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPC): r.Type = objabi.R_PCREL - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += int64(r.Siz) return true @@ -217,16 +219,16 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPCDBL): r.Type = objabi.R_PCREL r.Variant = sym.RV_390_DBL - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += int64(r.Siz) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT): - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) r.Type = objabi.R_PCREL r.Variant = sym.RV_390_DBL - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += int64(targ.Got()) r.Add += int64(r.Siz) return true @@ -333,10 +335,8 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { return true } -func elfsetupplt(ctxt *ld.Link) { - plt := ctxt.Syms.Lookup(".plt", 0) - got := ctxt.Syms.Lookup(".got", 0) - if plt.Size == 0 { +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { // stg %r1,56(%r15) plt.AddUint8(0xe3) plt.AddUint8(0x10) @@ -347,7 +347,7 @@ func elfsetupplt(ctxt *ld.Link) { // larl %r1,_GLOBAL_OFFSET_TABLE_ plt.AddUint8(0xc0) plt.AddUint8(0x10) - plt.AddPCRelPlus(ctxt.Arch, got, 6) + plt.AddSymRef(ctxt.Arch, got.Sym(), 6, objabi.R_PCRELDBL, 4) // mvc 48(8,%r15),8(%r1) plt.AddUint8(0xd2) plt.AddUint8(0x07) @@ -376,7 +376,7 @@ func elfsetupplt(ctxt *ld.Link) { plt.AddUint8(0x00) // assume got->size == 0 too - got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0) + got.AddAddrPlus(ctxt.Arch, dynamic, 0) got.AddUint64(ctxt.Arch, 0) got.AddUint64(ctxt.Arch, 0) @@ -387,8 +387,8 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se return false } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { - if ctxt.LinkMode == ld.LinkExternal { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if target.IsExternal() { return val, false } @@ -396,13 +396,13 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo case objabi.R_CONST: return r.Add, true case objabi.R_GOTOFF: - return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true } return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { switch r.Variant & sym.RV_TYPE_MASK { default: ld.Errorf(s, "unexpected relocation variant %d", r.Variant) @@ -419,28 +419,28 @@ func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 } } -func addpltsym(ctxt *ld.Link, s *sym.Symbol) { +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Plt() >= 0 { return } - ld.Adddynsym(ctxt, s) + ld.Adddynsym(target, syms, s) - if ctxt.IsELF { - plt := ctxt.Syms.Lookup(".plt", 0) - got := ctxt.Syms.Lookup(".got", 0) - rela := ctxt.Syms.Lookup(".rela.plt", 0) + if target.IsElf() { + plt := syms.PLT + got := syms.GOT + rela := syms.RelaPLT if plt.Size == 0 { - elfsetupplt(ctxt) + panic("plt is not set up") } // larl %r1,_GLOBAL_OFFSET_TABLE_+index plt.AddUint8(0xc0) plt.AddUint8(0x10) - plt.AddPCRelPlus(ctxt.Arch, got, got.Size+6) // need variant? + plt.AddPCRelPlus(target.Arch, got, got.Size+6) // need variant? // add to got: pointer to current pos in plt - got.AddAddrPlus(ctxt.Arch, plt, plt.Size+8) // weird but correct + got.AddAddrPlus(target.Arch, plt, plt.Size+8) // weird but correct // lg %r1,0(%r1) plt.AddUint8(0xe3) plt.AddUint8(0x10) @@ -465,15 +465,15 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) { plt.AddUint8(0xc0) plt.AddUint8(0xf4) - plt.AddUint32(ctxt.Arch, uint32(-((plt.Size - 2) >> 1))) // roll-your-own relocation + plt.AddUint32(target.Arch, uint32(-((plt.Size - 2) >> 1))) // roll-your-own relocation //.plt index - plt.AddUint32(ctxt.Arch, uint32(rela.Size)) // rela size before current entry + plt.AddUint32(target.Arch, uint32(rela.Size)) // rela size before current entry // rela - rela.AddAddrPlus(ctxt.Arch, got, got.Size-8) + rela.AddAddrPlus(target.Arch, got, got.Size-8) - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_JMP_SLOT))) - rela.AddUint64(ctxt.Arch, 0) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_JMP_SLOT))) + rela.AddUint64(target.Arch, 0) s.SetPlt(int32(plt.Size - 32)) @@ -482,21 +482,21 @@ func addpltsym(ctxt *ld.Link, s *sym.Symbol) { } } -func addgotsym(ctxt *ld.Link, s *sym.Symbol) { +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Got() >= 0 { return } - ld.Adddynsym(ctxt, s) - got := ctxt.Syms.Lookup(".got", 0) + ld.Adddynsym(target, syms, s) + got := syms.GOT s.SetGot(int32(got.Size)) - got.AddUint64(ctxt.Arch, 0) + got.AddUint64(target.Arch, 0) - if ctxt.IsELF { - rela := ctxt.Syms.Lookup(".rela", 0) - rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) - rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_GLOB_DAT))) - rela.AddUint64(ctxt.Arch, 0) + if target.IsElf() { + rela := syms.Rela + rela.AddAddrPlus(target.Arch, got, int64(s.Got())) + rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_GLOB_DAT))) + rela.AddUint64(target.Arch, 0) } else { ld.Errorf(s, "addgotsym: unsupported binary format") } @@ -507,28 +507,28 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup sect := ld.Segtext.Sections[0] - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) - for _, sect = range ld.Segtext.Sections[1:] { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Codeblk, ctxt, offset, sect.Vaddr, sect.Length) + + for _, sect := range ld.Segtext.Sections[1:] { + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } + if ld.Segrelrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/internal/sym/attribute.go b/src/cmd/link/internal/sym/attribute.go index 4b69bf32d0..eda3fe60e4 100644 --- a/src/cmd/link/internal/sym/attribute.go +++ b/src/cmd/link/internal/sym/attribute.go @@ -4,6 +4,8 @@ package sym +import "sync/atomic" + // Attribute is a set of common symbol attributes. type Attribute int32 @@ -84,34 +86,44 @@ const ( // 19 attributes defined so far. ) -func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 } -func (a Attribute) External() bool { return a&AttrExternal != 0 } -func (a Attribute) NoSplit() bool { return a&AttrNoSplit != 0 } -func (a Attribute) Reachable() bool { return a&AttrReachable != 0 } -func (a Attribute) CgoExportDynamic() bool { return a&AttrCgoExportDynamic != 0 } -func (a Attribute) CgoExportStatic() bool { return a&AttrCgoExportStatic != 0 } -func (a Attribute) Special() bool { return a&AttrSpecial != 0 } -func (a Attribute) StackCheck() bool { return a&AttrStackCheck != 0 } -func (a Attribute) NotInSymbolTable() bool { return a&AttrNotInSymbolTable != 0 } -func (a Attribute) OnList() bool { return a&AttrOnList != 0 } -func (a Attribute) Local() bool { return a&AttrLocal != 0 } -func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 } -func (a Attribute) MakeTypelink() bool { return a&AttrMakeTypelink != 0 } -func (a Attribute) Shared() bool { return a&AttrShared != 0 } -func (a Attribute) VisibilityHidden() bool { return a&AttrVisibilityHidden != 0 } -func (a Attribute) SubSymbol() bool { return a&AttrSubSymbol != 0 } -func (a Attribute) Container() bool { return a&AttrContainer != 0 } -func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 } -func (a Attribute) ReadOnly() bool { return a&AttrReadOnly != 0 } +func (a *Attribute) load() Attribute { return Attribute(atomic.LoadInt32((*int32)(a))) } + +func (a *Attribute) DuplicateOK() bool { return a.load()&AttrDuplicateOK != 0 } +func (a *Attribute) External() bool { return a.load()&AttrExternal != 0 } +func (a *Attribute) NoSplit() bool { return a.load()&AttrNoSplit != 0 } +func (a *Attribute) Reachable() bool { return a.load()&AttrReachable != 0 } +func (a *Attribute) CgoExportDynamic() bool { return a.load()&AttrCgoExportDynamic != 0 } +func (a *Attribute) CgoExportStatic() bool { return a.load()&AttrCgoExportStatic != 0 } +func (a *Attribute) Special() bool { return a.load()&AttrSpecial != 0 } +func (a *Attribute) StackCheck() bool { return a.load()&AttrStackCheck != 0 } +func (a *Attribute) NotInSymbolTable() bool { return a.load()&AttrNotInSymbolTable != 0 } +func (a *Attribute) OnList() bool { return a.load()&AttrOnList != 0 } +func (a *Attribute) Local() bool { return a.load()&AttrLocal != 0 } +func (a *Attribute) ReflectMethod() bool { return a.load()&AttrReflectMethod != 0 } +func (a *Attribute) MakeTypelink() bool { return a.load()&AttrMakeTypelink != 0 } +func (a *Attribute) Shared() bool { return a.load()&AttrShared != 0 } +func (a *Attribute) VisibilityHidden() bool { return a.load()&AttrVisibilityHidden != 0 } +func (a *Attribute) SubSymbol() bool { return a.load()&AttrSubSymbol != 0 } +func (a *Attribute) Container() bool { return a.load()&AttrContainer != 0 } +func (a *Attribute) TopFrame() bool { return a.load()&AttrTopFrame != 0 } +func (a *Attribute) ReadOnly() bool { return a.load()&AttrReadOnly != 0 } -func (a Attribute) CgoExport() bool { +func (a *Attribute) CgoExport() bool { return a.CgoExportDynamic() || a.CgoExportStatic() } func (a *Attribute) Set(flag Attribute, value bool) { - if value { - *a |= flag - } else { - *a &^= flag + // XXX it would be nice if we have atomic And, Or. + for { + a0 := a.load() + var anew Attribute + if value { + anew = a0 | flag + } else { + anew = a0 &^ flag + } + if atomic.CompareAndSwapInt32((*int32)(a), int32(a0), int32(anew)) { + return + } } } diff --git a/src/cmd/link/internal/sym/compilation_unit.go b/src/cmd/link/internal/sym/compilation_unit.go index 02fb0cfab8..b8b6845e6e 100644 --- a/src/cmd/link/internal/sym/compilation_unit.go +++ b/src/cmd/link/internal/sym/compilation_unit.go @@ -6,6 +6,10 @@ package sym import "cmd/internal/dwarf" +// LoaderSym holds a loader.Sym value. We can't refer to this +// type from the sym package since loader imports sym. +type LoaderSym int + // CompilationUnit is an abstraction used by DWARF to represent a chunk of // debug-related data. We create a CompilationUnit per Object file in a // library (so, one for all the Go code, one for each assembly file, etc.). @@ -20,4 +24,10 @@ type CompilationUnit struct { RangeSyms []*Symbol // Symbols for debug_range Textp []*Symbol // Text symbols in this CU DWARFFileTable []string // The file table used to generate the .debug_lines + + Consts2 LoaderSym // Package constants DIEs (loader) + FuncDIEs2 []LoaderSym // Function DIE subtrees (loader) + AbsFnDIEs2 []LoaderSym // Abstract function DIE subtrees (loader) + RangeSyms2 []LoaderSym // Symbols for debug_range (loader) + Textp2 []LoaderSym // Text symbols in this CU (loader) } diff --git a/src/cmd/link/internal/sym/library.go b/src/cmd/link/internal/sym/library.go index 4f2023b8f7..21568dfbe2 100644 --- a/src/cmd/link/internal/sym/library.go +++ b/src/cmd/link/internal/sym/library.go @@ -18,6 +18,9 @@ type Library struct { Main bool Safe bool Units []*CompilationUnit + + Textp2 []LoaderSym // text syms defined in this library + DupTextSyms2 []LoaderSym // dupok text syms defined in this library } func (l Library) String() string { diff --git a/src/cmd/link/internal/sym/segment.go b/src/cmd/link/internal/sym/segment.go index d5255bf142..979241be61 100644 --- a/src/cmd/link/internal/sym/segment.go +++ b/src/cmd/link/internal/sym/segment.go @@ -55,4 +55,5 @@ type Section struct { Elfsect interface{} // an *ld.ElfShdr Reloff uint64 Rellen uint64 + Sym *Symbol // symbol for the section, if any } diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go index e9819a064f..1fee966c12 100644 --- a/src/cmd/link/internal/sym/symbol.go +++ b/src/cmd/link/internal/sym/symbol.go @@ -103,6 +103,10 @@ func (s *Symbol) Len() int64 { return s.Size } +func (s *Symbol) Length(dwarfContext interface{}) int64 { + return s.Size +} + func (s *Symbol) Grow(siz int64) { if int64(int(siz)) != siz { log.Fatalf("symgrow size %d too long", siz) diff --git a/src/cmd/link/internal/sym/symbols.go b/src/cmd/link/internal/sym/symbols.go index e772496534..d36be11ee8 100644 --- a/src/cmd/link/internal/sym/symbols.go +++ b/src/cmd/link/internal/sym/symbols.go @@ -31,105 +31,41 @@ package sym type Symbols struct { - symbolBatch []Symbol - // Symbol lookup based on name and indexed by version. - hash []map[string]*Symbol + versions int Allsym []*Symbol -} -func NewSymbols() *Symbols { - hash := make([]map[string]*Symbol, SymVerStatic) - // Preallocate about 2mb for hash of non static symbols - hash[0] = make(map[string]*Symbol, 100000) - // And another 1mb for internal ABI text symbols. - hash[SymVerABIInternal] = make(map[string]*Symbol, 50000) - return &Symbols{ - hash: hash, - Allsym: make([]*Symbol, 0, 100000), - } -} + // Provided by the loader -func (syms *Symbols) Newsym(name string, v int) *Symbol { - batch := syms.symbolBatch - if len(batch) == 0 { - batch = make([]Symbol, 1000) - } - s := &batch[0] - syms.symbolBatch = batch[1:] + // Look up the symbol with the given name and version, creating the + // symbol if it is not found. + Lookup func(name string, v int) *Symbol - s.Dynid = -1 - s.Name = name - s.Version = int16(v) - syms.Allsym = append(syms.Allsym, s) + // Look up the symbol with the given name and version, returning nil + // if it is not found. + ROLookup func(name string, v int) *Symbol - return s + // Create a symbol with the given name and version. The new symbol + // is not added to the lookup table and is not dedup'd with existing + // symbols (if any). + Newsym func(name string, v int) *Symbol } -// Look up the symbol with the given name and version, creating the -// symbol if it is not found. -func (syms *Symbols) Lookup(name string, v int) *Symbol { - m := syms.hash[v] - s := m[name] - if s != nil { - return s - } - s = syms.Newsym(name, v) - m[name] = s - return s -} - -// Look up the symbol with the given name and version, returning nil -// if it is not found. -func (syms *Symbols) ROLookup(name string, v int) *Symbol { - return syms.hash[v][name] -} - -// Add an existing symbol to the symbol table. -func (syms *Symbols) Add(s *Symbol) { - name := s.Name - v := int(s.Version) - m := syms.hash[v] - if _, ok := m[name]; ok { - panic(name + " already added") +func NewSymbols() *Symbols { + return &Symbols{ + versions: SymVerStatic, + Allsym: make([]*Symbol, 0, 100000), } - m[name] = s } // Allocate a new version (i.e. symbol namespace). func (syms *Symbols) IncVersion() int { - syms.hash = append(syms.hash, make(map[string]*Symbol)) - return len(syms.hash) - 1 + syms.versions++ + return syms.versions - 1 } -// Rename renames a symbol. -func (syms *Symbols) Rename(old, new string, v int, reachparent map[*Symbol]*Symbol) { - s := syms.hash[v][old] - oldExtName := s.Extname() - s.Name = new - if oldExtName == old { - s.SetExtname(new) - } - delete(syms.hash[v], old) - - dup := syms.hash[v][new] - if dup == nil { - syms.hash[v][new] = s - } else { - if s.Type == 0 { - dup.Attr |= s.Attr - if s.Attr.Reachable() && reachparent != nil { - reachparent[dup] = reachparent[s] - } - *s = *dup - } else if dup.Type == 0 { - s.Attr |= dup.Attr - if dup.Attr.Reachable() && reachparent != nil { - reachparent[s] = reachparent[dup] - } - *dup = *s - syms.hash[v][new] = s - } - } +// returns the maximum version number +func (syms *Symbols) MaxVersion() int { + return syms.versions } diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 3fe36db64d..bb7365410e 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -34,9 +34,11 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" + "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/elf" "log" + "sync" ) // Append 4 bytes to s and create a R_CALL relocation targeting t to fill them in. @@ -167,13 +169,13 @@ func gentext(ctxt *ld.Link) { initarray_entry.AddAddr(ctxt.Arch, initfunc) } -func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s *sym.Symbol, r *sym.Reloc) bool { targ := r.Sym switch r.Type { default: if r.Type >= objabi.ElfRelocOffset { - ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(target.Arch, r.Type)) return false } @@ -195,8 +197,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { r.Type = objabi.R_PCREL r.Add += 4 if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add += int64(targ.Plt()) } @@ -228,7 +230,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { return false } - addgotsym(ctxt, targ) + addgotsym(target, syms, targ) r.Type = objabi.R_CONST // write r->add during relocsym r.Sym = nil r.Add += int64(targ.Got()) @@ -240,7 +242,7 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC): r.Type = objabi.R_PCREL - r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Sym = syms.GOT r.Add += 4 return true @@ -260,8 +262,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1: if targ.Type == sym.SDYNIMPORT { - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add = int64(targ.Plt()) r.Type = objabi.R_PCREL return true @@ -284,8 +286,8 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { return true } - addgotsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".got", 0) + addgotsym(target, syms, targ) + r.Sym = syms.GOT r.Add += int64(targ.Got()) r.Type = objabi.R_PCREL return true @@ -298,12 +300,12 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { switch r.Type { case objabi.R_CALL, objabi.R_PCREL: - if ctxt.LinkMode == ld.LinkExternal { + if target.IsExternal() { // External linker will do this relocation. return true } - addpltsym(ctxt, targ) - r.Sym = ctxt.Syms.Lookup(".plt", 0) + addpltsym(target, syms, targ) + r.Sym = syms.PLT r.Add = int64(targ.Plt()) return true @@ -311,17 +313,17 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { if s.Type != sym.SDATA { break } - if ctxt.IsELF { - ld.Adddynsym(ctxt, targ) - rel := ctxt.Syms.Lookup(".rel", 0) - rel.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) - rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_386_32))) + if target.IsElf() { + ld.Adddynsym(target, syms, targ) + rel := syms.Rel + rel.AddAddrPlus(target.Arch, s, int64(r.Off)) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_386_32))) r.Type = objabi.R_CONST // write r->add during relocsym r.Sym = nil return true } - if ctxt.HeadType == objabi.Hdarwin && s.Size == int64(ctxt.Arch.PtrSize) && r.Off == 0 { + if target.IsDarwin() && s.Size == int64(target.Arch.PtrSize) && r.Off == 0 { // Mach-O relocations are a royal pain to lay out. // They use a compact stateful bytecode representation // that is too much bother to deal with. @@ -332,17 +334,17 @@ func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { // just in case the C code assigns to the variable, // and of course it only works for single pointers, // but we only need to support cgo and that's all it needs. - ld.Adddynsym(ctxt, targ) + ld.Adddynsym(target, syms, targ) - got := ctxt.Syms.Lookup(".got", 0) + got := syms.GOT s.Type = got.Type s.Attr |= sym.AttrSubSymbol s.Outer = got s.Sub = got.Sub got.Sub = s s.Value = got.Size - got.AddUint32(ctxt.Arch, 0) - ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(targ.Dynid)) + got.AddUint32(target.Arch, 0) + syms.LinkEditGOT.AddUint32(target.Arch, uint32(targ.Dynid)) r.Type = objabi.ElfRelocOffset // ignore during relocsym return true } @@ -492,128 +494,126 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, secto return true } -func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { - if ctxt.LinkMode == ld.LinkExternal { +func archreloc(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if target.IsExternal() { return val, false } switch r.Type { case objabi.R_CONST: return r.Add, true case objabi.R_GOTOFF: - return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(syms.GOT), true } return val, false } -func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { +func archrelocvariant(target *ld.Target, syms *ld.ArchSyms, r *sym.Reloc, s *sym.Symbol, t int64) int64 { log.Fatalf("unexpected relocation variant") return t } -func elfsetupplt(ctxt *ld.Link) { - plt := ctxt.Syms.Lookup(".plt", 0) - got := ctxt.Syms.Lookup(".got.plt", 0) - if plt.Size == 0 { +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { // pushl got+4 plt.AddUint8(0xff) plt.AddUint8(0x35) - plt.AddAddrPlus(ctxt.Arch, got, 4) + plt.AddAddrPlus(ctxt.Arch, got.Sym(), 4) // jmp *got+8 plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddAddrPlus(ctxt.Arch, got, 8) + plt.AddAddrPlus(ctxt.Arch, got.Sym(), 8) // zero pad plt.AddUint32(ctxt.Arch, 0) // assume got->size == 0 too - got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0) + got.AddAddrPlus(ctxt.Arch, dynamic, 0) got.AddUint32(ctxt.Arch, 0) got.AddUint32(ctxt.Arch, 0) } } -func addpltsym(ctxt *ld.Link, s *sym.Symbol) { +func addpltsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Plt() >= 0 { return } - ld.Adddynsym(ctxt, s) + ld.Adddynsym(target, syms, s) - if ctxt.IsELF { - plt := ctxt.Syms.Lookup(".plt", 0) - got := ctxt.Syms.Lookup(".got.plt", 0) - rel := ctxt.Syms.Lookup(".rel.plt", 0) + if target.IsElf() { + plt := syms.PLT + got := syms.GOTPLT + rel := syms.RelPLT if plt.Size == 0 { - elfsetupplt(ctxt) + panic("plt is not set up") } // jmpq *got+size plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddAddrPlus(ctxt.Arch, got, got.Size) + plt.AddAddrPlus(target.Arch, got, got.Size) // add to got: pointer to current pos in plt - got.AddAddrPlus(ctxt.Arch, plt, plt.Size) + got.AddAddrPlus(target.Arch, plt, plt.Size) // pushl $x plt.AddUint8(0x68) - plt.AddUint32(ctxt.Arch, uint32(rel.Size)) + plt.AddUint32(target.Arch, uint32(rel.Size)) // jmp .plt plt.AddUint8(0xe9) - plt.AddUint32(ctxt.Arch, uint32(-(plt.Size + 4))) + plt.AddUint32(target.Arch, uint32(-(plt.Size + 4))) // rel - rel.AddAddrPlus(ctxt.Arch, got, got.Size-4) + rel.AddAddrPlus(target.Arch, got, got.Size-4) - rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_JMP_SLOT))) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_JMP_SLOT))) s.SetPlt(int32(plt.Size - 16)) - } else if ctxt.HeadType == objabi.Hdarwin { + } else if target.IsDarwin() { // Same laziness as in 6l. - plt := ctxt.Syms.Lookup(".plt", 0) + plt := syms.PLT - addgotsym(ctxt, s) + addgotsym(target, syms, s) - ctxt.Syms.Lookup(".linkedit.plt", 0).AddUint32(ctxt.Arch, uint32(s.Dynid)) + syms.LinkEditPLT.AddUint32(target.Arch, uint32(s.Dynid)) // jmpq *got+size(IP) s.SetPlt(int32(plt.Size)) plt.AddUint8(0xff) plt.AddUint8(0x25) - plt.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".got", 0), int64(s.Got())) + plt.AddAddrPlus(target.Arch, syms.GOT, int64(s.Got())) } else { ld.Errorf(s, "addpltsym: unsupported binary format") } } -func addgotsym(ctxt *ld.Link, s *sym.Symbol) { +func addgotsym(target *ld.Target, syms *ld.ArchSyms, s *sym.Symbol) { if s.Got() >= 0 { return } - ld.Adddynsym(ctxt, s) - got := ctxt.Syms.Lookup(".got", 0) + ld.Adddynsym(target, syms, s) + got := syms.GOT s.SetGot(int32(got.Size)) - got.AddUint32(ctxt.Arch, 0) - - if ctxt.IsELF { - rel := ctxt.Syms.Lookup(".rel", 0) - rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) - rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_GLOB_DAT))) - } else if ctxt.HeadType == objabi.Hdarwin { - ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(s.Dynid)) + got.AddUint32(target.Arch, 0) + + if target.IsElf() { + rel := syms.Rel + rel.AddAddrPlus(target.Arch, got, int64(s.Got())) + rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_GLOB_DAT))) + } else if target.IsDarwin() { + syms.LinkEditGOT.AddUint32(target.Arch, uint32(s.Dynid)) } else { ld.Errorf(s, "addgotsym: unsupported binary format") } @@ -624,29 +624,31 @@ func asmb(ctxt *ld.Link) { ld.Asmbelfsetup() } + var wg sync.WaitGroup sect := ld.Segtext.Sections[0] - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - // 0xCC is INT $3 - breakpoint instruction - ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC}) - for _, sect = range ld.Segtext.Sections[1:] { - ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + f := func(ctxt *ld.Link, out *ld.OutBuf, start, length int64) { + ld.CodeblkPad(ctxt, out, start, length, []byte{0xCC}) + } + ld.WriteParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length) + + for _, sect := range ld.Segtext.Sections[1:] { + offset := sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff + ld.WriteParallel(&wg, ld.Datblk, ctxt, offset, sect.Vaddr, sect.Length) } if ld.Segrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrodata.Fileoff, ld.Segrodata.Vaddr, ld.Segrodata.Filelen) } + if ld.Segrelrodata.Filelen > 0 { - ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segrelrodata.Fileoff, ld.Segrelrodata.Vaddr, ld.Segrelrodata.Filelen) } - ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) - ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.WriteParallel(&wg, ld.Datblk, ctxt, ld.Segdata.Fileoff, ld.Segdata.Vaddr, ld.Segdata.Filelen) - ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) - ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + ld.WriteParallel(&wg, ld.Dwarfblk, ctxt, ld.Segdwarf.Fileoff, ld.Segdwarf.Vaddr, ld.Segdwarf.Filelen) + wg.Wait() } func asmb2(ctxt *ld.Link) { diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index b1f0e8882c..e665737cc3 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -535,6 +535,30 @@ func TestStrictDup(t *testing.T) { } } +func TestOldLink(t *testing.T) { + // Test that old object file format still works. + // TODO(go115newobj): delete. + + testenv.MustHaveGoBuild(t) + + tmpdir, err := ioutil.TempDir("", "TestOldLink") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + src := filepath.Join(tmpdir, "main.go") + err = ioutil.WriteFile(src, []byte("package main; func main(){}\n"), 0666) + if err != nil { + t.Fatal(err) + } + + cmd := exec.Command(testenv.GoToolPath(t), "run", "-gcflags=all=-go115newobj=false", "-asmflags=all=-go115newobj=false", "-ldflags=-go115newobj=false", src) + if out, err := cmd.CombinedOutput(); err != nil { + t.Errorf("%v: %v:\n%s", cmd.Args, err, out) + } +} + const testFuncAlignSrc = ` package main import ( diff --git a/src/cmd/oldlink/doc.go b/src/cmd/oldlink/doc.go new file mode 100644 index 0000000000..219499be0a --- /dev/null +++ b/src/cmd/oldlink/doc.go @@ -0,0 +1,129 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Link, typically invoked as ``go tool link,'' reads the Go archive or object +for a package main, along with its dependencies, and combines them +into an executable binary. + +Command Line + +Usage: + + go tool link [flags] main.a + +Flags: + + -B note + Add an ELF_NT_GNU_BUILD_ID note when using ELF. + The value should start with 0x and be an even number of hex digits. + -D address + Set data segment address. + -E entry + Set entry symbol name. + -H type + Set executable format type. + The default format is inferred from GOOS and GOARCH. + On Windows, -H windowsgui writes a "GUI binary" instead of a "console binary." + -I interpreter + Set the ELF dynamic linker to use. + -L dir1 -L dir2 + Search for imported packages in dir1, dir2, etc, + after consulting $GOROOT/pkg/$GOOS_$GOARCH. + -R quantum + Set address rounding quantum. + -T address + Set text segment address. + -V + Print linker version and exit. + -X importpath.name=value + Set the value of the string variable in importpath named name to value. + This is only effective if the variable is declared in the source code either uninitialized + or initialized to a constant string expression. -X will not work if the initializer makes + a function call or refers to other variables. + Note that before Go 1.5 this option took two separate arguments. + -a + Disassemble output. + -buildid id + Record id as Go toolchain build id. + -buildmode mode + Set build mode (default exe). + -c + Dump call graphs. + -compressdwarf + Compress DWARF if possible (default true). + -cpuprofile file + Write CPU profile to file. + -d + Disable generation of dynamic executables. + The emitted code is the same in either case; the option + controls only whether a dynamic header is included. + The dynamic header is on by default, even without any + references to dynamic libraries, because many common + system tools now assume the presence of the header. + -debugtramp int + Debug trampolines. + -dumpdep + Dump symbol dependency graph. + -extar ar + Set the external archive program (default "ar"). + Used only for -buildmode=c-archive. + -extld linker + Set the external linker (default "clang" or "gcc"). + -extldflags flags + Set space-separated flags to pass to the external linker. + -f + Ignore version mismatch in the linked archives. + -g + Disable Go package data checks. + -importcfg file + Read import configuration from file. + In the file, set packagefile, packageshlib to specify import resolution. + -installsuffix suffix + Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix + instead of $GOROOT/pkg/$GOOS_$GOARCH. + -k symbol + Set field tracking symbol. Use this flag when GOEXPERIMENT=fieldtrack is set. + -libgcc file + Set name of compiler support library. + This is only used in internal link mode. + If not set, default value comes from running the compiler, + which may be set by the -extld option. + Set to "none" to use no support library. + -linkmode mode + Set link mode (internal, external, auto). + This sets the linking mode as described in cmd/cgo/doc.go. + -linkshared + Link against installed Go shared libraries (experimental). + -memprofile file + Write memory profile to file. + -memprofilerate rate + Set runtime.MemProfileRate to rate. + -msan + Link with C/C++ memory sanitizer support. + -n + Dump symbol table. + -o file + Write output to file (default a.out, or a.out.exe on Windows). + -pluginpath path + The path name used to prefix exported plugin symbols. + -r dir1:dir2:... + Set the ELF dynamic linker search path. + -race + Link with race detection libraries. + -s + Omit the symbol table and debug information. + -shared + Generated shared object (implies -linkmode external; experimental). + -tmpdir dir + Write temporary files to dir. + Temporary files are only used in external linking mode. + -u + Reject unsafe packages. + -v + Print trace of linker operations. + -w + Omit the DWARF symbol table. +*/ +package main diff --git a/src/cmd/oldlink/internal/amd64/asm.go b/src/cmd/oldlink/internal/amd64/asm.go new file mode 100644 index 0000000000..c120e4ea81 --- /dev/null +++ b/src/cmd/oldlink/internal/amd64/asm.go @@ -0,0 +1,874 @@ +// Inferno utils/6l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "debug/elf" + "log" +) + +func PADDR(x uint32) uint32 { + return x &^ 0x80000000 +} + +func Addcall(ctxt *ld.Link, s *sym.Symbol, t *sym.Symbol) int64 { + s.Attr |= sym.AttrReachable + i := s.Size + s.Size += 4 + s.Grow(s.Size) + r := s.AddRel() + r.Sym = t + r.Off = int32(i) + r.Type = objabi.R_CALL + r.Siz = 4 + return i + int64(r.Siz) +} + +func gentext(ctxt *ld.Link) { + if !ctxt.DynlinkingGo() { + return + } + addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0) + if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin { + // we're linking a module containing the runtime -> no need for + // an init function + return + } + addmoduledata.Attr |= sym.AttrReachable + initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0) + initfunc.Type = sym.STEXT + initfunc.Attr |= sym.AttrLocal + initfunc.Attr |= sym.AttrReachable + o := func(op ...uint8) { + for _, op1 := range op { + initfunc.AddUint8(op1) + } + } + // 0000000000000000 <local.dso_init>: + // 0: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 7 <local.dso_init+0x7> + // 3: R_X86_64_PC32 runtime.firstmoduledata-0x4 + o(0x48, 0x8d, 0x3d) + initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 0) + // 7: e8 00 00 00 00 callq c <local.dso_init+0xc> + // 8: R_X86_64_PLT32 runtime.addmoduledata-0x4 + o(0xe8) + Addcall(ctxt, initfunc, addmoduledata) + // c: c3 retq + o(0xc3) + if ctxt.BuildMode == ld.BuildModePlugin { + ctxt.Textp = append(ctxt.Textp, addmoduledata) + } + ctxt.Textp = append(ctxt.Textp, initfunc) + initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0) + initarray_entry.Attr |= sym.AttrReachable + initarray_entry.Attr |= sym.AttrLocal + initarray_entry.Type = sym.SINITARR + initarray_entry.AddAddr(ctxt.Arch, initfunc) +} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + targ := r.Sym + + switch r.Type { + default: + if r.Type >= objabi.ElfRelocOffset { + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC32): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name) + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { + ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + } + r.Type = objabi.R_PCREL + r.Add += 4 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC64): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_X86_64_PC64 relocation for dynamic symbol %s", targ.Name) + } + if targ.Type == 0 || targ.Type == sym.SXREF { + ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + } + r.Type = objabi.R_PCREL + r.Add += 8 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PLT32): + r.Type = objabi.R_PCREL + r.Add += 4 + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add += int64(targ.Plt()) + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCREL), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCRELX), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_REX_GOTPCRELX): + if targ.Type != sym.SDYNIMPORT { + // have symbol + if r.Off >= 2 && s.P[r.Off-2] == 0x8b { + // turn MOVQ of GOT entry into LEAQ of symbol itself + s.P[r.Off-2] = 0x8d + + r.Type = objabi.R_PCREL + r.Add += 4 + return true + } + } + + // fall back to using GOT and hope for the best (CMOV*) + // TODO: just needs relocation, no need to put in .dynsym + addgotsym(ctxt, targ) + + r.Type = objabi.R_PCREL + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += 4 + r.Add += int64(targ.Got()) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_64): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ADDR + if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + break + } + return true + + // Handle relocations found in Mach-O object files. + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0: + // TODO: What is the difference between all these? + r.Type = objabi.R_ADDR + + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected reloc for dynamic symbol %s", targ.Name) + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add = int64(targ.Plt()) + r.Type = objabi.R_PCREL + return true + } + fallthrough + + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 1, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 1, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_1*2 + 1, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_2*2 + 1, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_4*2 + 1: + r.Type = objabi.R_PCREL + + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected pc-relative reloc for dynamic symbol %s", targ.Name) + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: + if targ.Type != sym.SDYNIMPORT { + // have symbol + // turn MOVQ of GOT entry into LEAQ of symbol itself + if r.Off < 2 || s.P[r.Off-2] != 0x8b { + ld.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ.Name) + return false + } + + s.P[r.Off-2] = 0x8d + r.Type = objabi.R_PCREL + return true + } + fallthrough + + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT*2 + 1: + if targ.Type != sym.SDYNIMPORT { + ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + } + addgotsym(ctxt, targ) + r.Type = objabi.R_PCREL + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(targ.Got()) + return true + } + + switch r.Type { + case objabi.R_CALL, + objabi.R_PCREL: + if targ.Type != sym.SDYNIMPORT { + // nothing to do, the relocation will be laid out in reloc + return true + } + if ctxt.LinkMode == ld.LinkExternal { + // External linker will do this relocation. + return true + } + // Internal linking, for both ELF and Mach-O. + // Build a PLT entry and change the relocation target to that entry. + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add = int64(targ.Plt()) + return true + + case objabi.R_ADDR: + if s.Type == sym.STEXT && ctxt.IsELF { + if ctxt.HeadType == objabi.Hsolaris { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add += int64(targ.Plt()) + return true + } + // The code is asking for the address of an external + // function. We provide it with the address of the + // correspondent GOT symbol. + addgotsym(ctxt, targ) + + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(targ.Got()) + return true + } + + // Process dynamic relocations for the data sections. + if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal { + // When internally linking, generate dynamic relocations + // for all typical R_ADDR relocations. The exception + // are those R_ADDR that are created as part of generating + // the dynamic relocations and must be resolved statically. + // + // There are three phases relevant to understanding this: + // + // dodata() // we are here + // address() // symbol address assignment + // reloc() // resolution of static R_ADDR relocs + // + // At this point symbol addresses have not been + // assigned yet (as the final size of the .rela section + // will affect the addresses), and so we cannot write + // the Elf64_Rela.r_offset now. Instead we delay it + // until after the 'address' phase of the linker is + // complete. We do this via Addaddrplus, which creates + // a new R_ADDR relocation which will be resolved in + // the 'reloc' phase. + // + // These synthetic static R_ADDR relocs must be skipped + // now, or else we will be caught in an infinite loop + // of generating synthetic relocs for our synthetic + // relocs. + // + // Furthermore, the rela sections contain dynamic + // relocations with R_ADDR relocations on + // Elf64_Rela.r_offset. This field should contain the + // symbol offset as determined by reloc(), not the + // final dynamically linked address as a dynamic + // relocation would provide. + switch s.Name { + case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic": + return false + } + } else { + // Either internally linking a static executable, + // in which case we can resolve these relocations + // statically in the 'reloc' phase, or externally + // linking, in which case the relocation will be + // prepared in the 'reloc' phase and passed to the + // external linker in the 'asmb' phase. + if s.Type != sym.SDATA && s.Type != sym.SRODATA { + break + } + } + + if ctxt.IsELF { + // Generate R_X86_64_RELATIVE relocations for best + // efficiency in the dynamic linker. + // + // As noted above, symbol addresses have not been + // assigned yet, so we can't generate the final reloc + // entry yet. We ultimately want: + // + // r_offset = s + r.Off + // r_info = R_X86_64_RELATIVE + // r_addend = targ + r.Add + // + // The dynamic linker will set *offset = base address + + // addend. + // + // AddAddrPlus is used for r_offset and r_addend to + // generate new R_ADDR relocations that will update + // these fields in the 'reloc' phase. + rela := ctxt.Syms.Lookup(".rela", 0) + rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) + if r.Siz == 8 { + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_X86_64_RELATIVE))) + } else { + ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + } + rela.AddAddrPlus(ctxt.Arch, targ, int64(r.Add)) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). + return true + } + + if ctxt.HeadType == objabi.Hdarwin && s.Size == int64(ctxt.Arch.PtrSize) && r.Off == 0 { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // just in case the C code assigns to the variable, + // and of course it only works for single pointers, + // but we only need to support cgo and that's all it needs. + ld.Adddynsym(ctxt, targ) + + got := ctxt.Syms.Lookup(".got", 0) + s.Type = got.Type + s.Attr |= sym.AttrSubSymbol + s.Outer = got + s.Sub = got.Sub + got.Sub = s + s.Value = got.Size + got.AddUint64(ctxt.Arch, 0) + ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(targ.Dynid)) + r.Type = objabi.ElfRelocOffset // ignore during relocsym + return true + } + } + + return false +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + ctxt.Out.Write64(uint64(sectoff)) + + elfsym := r.Xsym.ElfsymForReloc() + switch r.Type { + default: + return false + case objabi.R_ADDR: + if r.Siz == 4 { + ctxt.Out.Write64(uint64(elf.R_X86_64_32) | uint64(elfsym)<<32) + } else if r.Siz == 8 { + ctxt.Out.Write64(uint64(elf.R_X86_64_64) | uint64(elfsym)<<32) + } else { + return false + } + case objabi.R_TLS_LE: + if r.Siz == 4 { + ctxt.Out.Write64(uint64(elf.R_X86_64_TPOFF32) | uint64(elfsym)<<32) + } else { + return false + } + case objabi.R_TLS_IE: + if r.Siz == 4 { + ctxt.Out.Write64(uint64(elf.R_X86_64_GOTTPOFF) | uint64(elfsym)<<32) + } else { + return false + } + case objabi.R_CALL: + if r.Siz == 4 { + if r.Xsym.Type == sym.SDYNIMPORT { + if ctxt.DynlinkingGo() { + ctxt.Out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32) + } else { + ctxt.Out.Write64(uint64(elf.R_X86_64_GOTPCREL) | uint64(elfsym)<<32) + } + } else { + ctxt.Out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32) + } + } else { + return false + } + case objabi.R_PCREL: + if r.Siz == 4 { + if r.Xsym.Type == sym.SDYNIMPORT && r.Xsym.ElfType() == elf.STT_FUNC { + ctxt.Out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32) + } else { + ctxt.Out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32) + } + } else { + return false + } + case objabi.R_GOTPCREL: + if r.Siz == 4 { + ctxt.Out.Write64(uint64(elf.R_X86_64_GOTPCREL) | uint64(elfsym)<<32) + } else { + return false + } + } + + ctxt.Out.Write64(uint64(r.Xadd)) + return true +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + + if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_PCREL || r.Type == objabi.R_GOTPCREL || r.Type == objabi.R_CALL { + if rs.Dynid < 0 { + ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type) + return false + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32(rs.Sect.Extnum) + if v == 0 { + ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type) + return false + } + } + + switch r.Type { + default: + return false + + case objabi.R_ADDR: + v |= ld.MACHO_X86_64_RELOC_UNSIGNED << 28 + + case objabi.R_CALL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_BRANCH << 28 + + // NOTE: Only works with 'external' relocation. Forced above. + case objabi.R_PCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_SIGNED << 28 + case objabi.R_GOTPCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_GOT_LOAD << 28 + } + + switch r.Siz { + default: + return false + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + out.Write32(uint32(sectoff)) + out.Write32(v) + return true +} + +func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + + if rs.Dynid < 0 { + ld.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type) + return false + } + + out.Write32(uint32(sectoff)) + out.Write32(uint32(rs.Dynid)) + + switch r.Type { + default: + return false + + case objabi.R_DWARFSECREF: + v = ld.IMAGE_REL_AMD64_SECREL + + case objabi.R_ADDR: + if r.Siz == 8 { + v = ld.IMAGE_REL_AMD64_ADDR64 + } else { + v = ld.IMAGE_REL_AMD64_ADDR32 + } + + case objabi.R_CALL, + objabi.R_PCREL: + v = ld.IMAGE_REL_AMD64_REL32 + } + + out.Write16(uint16(v)) + + return true +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func elfsetupplt(ctxt *ld.Link) { + plt := ctxt.Syms.Lookup(".plt", 0) + got := ctxt.Syms.Lookup(".got.plt", 0) + if plt.Size == 0 { + // pushq got+8(IP) + plt.AddUint8(0xff) + + plt.AddUint8(0x35) + plt.AddPCRelPlus(ctxt.Arch, got, 8) + + // jmpq got+16(IP) + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddPCRelPlus(ctxt.Arch, got, 16) + + // nopl 0(AX) + plt.AddUint32(ctxt.Arch, 0x00401f0f) + + // assume got->size == 0 too + got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0) + + got.AddUint64(ctxt.Arch, 0) + got.AddUint64(ctxt.Arch, 0) + } +} + +func addpltsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ctxt.IsELF { + plt := ctxt.Syms.Lookup(".plt", 0) + got := ctxt.Syms.Lookup(".got.plt", 0) + rela := ctxt.Syms.Lookup(".rela.plt", 0) + if plt.Size == 0 { + elfsetupplt(ctxt) + } + + // jmpq *got+size(IP) + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddPCRelPlus(ctxt.Arch, got, got.Size) + + // add to got: pointer to current pos in plt + got.AddAddrPlus(ctxt.Arch, plt, plt.Size) + + // pushq $x + plt.AddUint8(0x68) + + plt.AddUint32(ctxt.Arch, uint32((got.Size-24-8)/8)) + + // jmpq .plt + plt.AddUint8(0xe9) + + plt.AddUint32(ctxt.Arch, uint32(-(plt.Size + 4))) + + // rela + rela.AddAddrPlus(ctxt.Arch, got, got.Size-8) + + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_JMP_SLOT))) + rela.AddUint64(ctxt.Arch, 0) + + s.SetPlt(int32(plt.Size - 16)) + } else if ctxt.HeadType == objabi.Hdarwin { + // To do lazy symbol lookup right, we're supposed + // to tell the dynamic loader which library each + // symbol comes from and format the link info + // section just so. I'm too lazy (ha!) to do that + // so for now we'll just use non-lazy pointers, + // which don't need to be told which library to use. + // + // https://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html + // has details about what we're avoiding. + + addgotsym(ctxt, s) + plt := ctxt.Syms.Lookup(".plt", 0) + + ctxt.Syms.Lookup(".linkedit.plt", 0).AddUint32(ctxt.Arch, uint32(s.Dynid)) + + // jmpq *got+size(IP) + s.SetPlt(int32(plt.Size)) + + plt.AddUint8(0xff) + plt.AddUint8(0x25) + plt.AddPCRelPlus(ctxt.Arch, ctxt.Syms.Lookup(".got", 0), int64(s.Got())) + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + got := ctxt.Syms.Lookup(".got", 0) + s.SetGot(int32(got.Size)) + got.AddUint64(ctxt.Arch, 0) + + if ctxt.IsELF { + rela := ctxt.Syms.Lookup(".rela", 0) + rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_X86_64_GLOB_DAT))) + rela.AddUint64(ctxt.Arch, 0) + } else if ctxt.HeadType == objabi.Hdarwin { + ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(s.Dynid)) + } else { + ld.Errorf(s, "addgotsym: unsupported binary format") + } +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sections[0] + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + // 0xCC is INT $3 - breakpoint instruction + ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC}) + for _, sect = range ld.Segtext.Sections[1:] { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + if ld.Segrelrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + machlink := int64(0) + if ctxt.HeadType == objabi.Hdarwin { + machlink = ld.Domacholink(ctxt) + } + + switch ctxt.HeadType { + default: + ld.Errorf(nil, "unknown header type %v", ctxt.HeadType) + fallthrough + + case objabi.Hplan9: + break + + case objabi.Hdarwin: + ld.Flag8 = true /* 64-bit addresses */ + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd, + objabi.Hdragonfly, + objabi.Hsolaris: + ld.Flag8 = true /* 64-bit addresses */ + + case objabi.Hwindows: + break + } + + ld.Symsize = 0 + ld.Spsize = 0 + ld.Lcsize = 0 + symo := int64(0) + if !*ld.FlagS { + switch ctxt.HeadType { + default: + case objabi.Hplan9: + *ld.FlagS = true + symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) + + case objabi.Hdarwin: + symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink)) + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd, + objabi.Hdragonfly, + objabi.Hsolaris: + symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = ld.Rnd(symo, int64(*ld.FlagRound)) + + case objabi.Hwindows: + symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = ld.Rnd(symo, ld.PEFILEALIGN) + } + + ctxt.Out.SeekSet(symo) + switch ctxt.HeadType { + default: + if ctxt.IsELF { + ctxt.Out.SeekSet(symo) + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + case objabi.Hplan9: + ld.Asmplan9sym(ctxt) + ctxt.Out.Flush() + + sym := ctxt.Syms.Lookup("pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + ctxt.Out.Write(sym.P) + ctxt.Out.Flush() + } + + case objabi.Hwindows: + // Do nothing + + case objabi.Hdarwin: + if ctxt.LinkMode == ld.LinkExternal { + ld.Machoemitreloc(ctxt) + } + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + default: + case objabi.Hplan9: /* plan9 */ + magic := int32(4*26*26 + 7) + + magic |= 0x00008000 /* fat header */ + ctxt.Out.Write32b(uint32(magic)) /* magic */ + ctxt.Out.Write32b(uint32(ld.Segtext.Filelen)) /* sizes */ + ctxt.Out.Write32b(uint32(ld.Segdata.Filelen)) + ctxt.Out.Write32b(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ctxt.Out.Write32b(uint32(ld.Symsize)) /* nsyms */ + vl := ld.Entryvalue(ctxt) + ctxt.Out.Write32b(PADDR(uint32(vl))) /* va of entry */ + ctxt.Out.Write32b(uint32(ld.Spsize)) /* sp offsets */ + ctxt.Out.Write32b(uint32(ld.Lcsize)) /* line offsets */ + ctxt.Out.Write64b(uint64(vl)) /* va of entry */ + + case objabi.Hdarwin: + ld.Asmbmacho(ctxt) + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd, + objabi.Hdragonfly, + objabi.Hsolaris: + ld.Asmbelf(ctxt, symo) + + case objabi.Hwindows: + ld.Asmbpe(ctxt) + } + + ctxt.Out.Flush() +} + +func tlsIEtoLE(s *sym.Symbol, off, size int) { + // Transform the PC-relative instruction into a constant load. + // That is, + // + // MOVQ X(IP), REG -> MOVQ $Y, REG + // + // To determine the instruction and register, we study the op codes. + // Consult an AMD64 instruction encoding guide to decipher this. + if off < 3 { + log.Fatal("R_X86_64_GOTTPOFF reloc not preceded by MOVQ or ADDQ instruction") + } + op := s.P[off-3 : off] + reg := op[2] >> 3 + + if op[1] == 0x8b || reg == 4 { + // MOVQ + if op[0] == 0x4c { + op[0] = 0x49 + } else if size == 4 && op[0] == 0x44 { + op[0] = 0x41 + } + if op[1] == 0x8b { + op[1] = 0xc7 + } else { + op[1] = 0x81 // special case for SP + } + op[2] = 0xc0 | reg + } else { + // An alternate op is ADDQ. This is handled by GNU gold, + // but right now is not generated by the Go compiler: + // ADDQ X(IP), REG -> ADDQ $Y, REG + // Consider adding support for it here. + log.Fatalf("expected TLS IE op to be MOVQ, got %v", op) + } +} diff --git a/src/cmd/oldlink/internal/amd64/l.go b/src/cmd/oldlink/internal/amd64/l.go new file mode 100644 index 0000000000..393da6bf28 --- /dev/null +++ b/src/cmd/oldlink/internal/amd64/l.go @@ -0,0 +1,43 @@ +// Inferno utils/6l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 16 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 7 + dwarfRegLR = 16 +) diff --git a/src/cmd/oldlink/internal/amd64/obj.go b/src/cmd/oldlink/internal/amd64/obj.go new file mode 100644 index 0000000000..655d9f1e03 --- /dev/null +++ b/src/cmd/oldlink/internal/amd64/obj.go @@ -0,0 +1,117 @@ +// Inferno utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchAMD64 + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Asmb: asmb, + Asmb2: asmb2, + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + PEreloc1: pereloc1, + TLSIEtoLE: tlsIEtoLE, + + Linuxdynld: "/lib64/ld-linux-x86-64.so.2", + Freebsddynld: "/libexec/ld-elf.so.1", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "/libexec/ld.elf_so", + Dragonflydynld: "/usr/libexec/ld-elf.so.2", + Solarisdynld: "/lib/amd64/ld.so.1", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + 8 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x200000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x200000 + } + + case objabi.Hdarwin: /* apple MACH */ + ld.HEADR = ld.INITIAL_MACHO_HEADR + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x1000000 + int64(ld.HEADR) + } + + case objabi.Hlinux, /* elf64 executable */ + objabi.Hfreebsd, /* freebsd */ + objabi.Hnetbsd, /* netbsd */ + objabi.Hopenbsd, /* openbsd */ + objabi.Hdragonfly, /* dragonfly */ + objabi.Hsolaris: /* solaris */ + ld.Elfinit(ctxt) + + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = (1 << 22) + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hwindows: /* PE executable */ + // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit + return + } +} diff --git a/src/cmd/oldlink/internal/arm/asm.go b/src/cmd/oldlink/internal/arm/asm.go new file mode 100644 index 0000000000..8db0bc3c9a --- /dev/null +++ b/src/cmd/oldlink/internal/arm/asm.go @@ -0,0 +1,891 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "debug/elf" + "fmt" + "log" +) + +// This assembler: +// +// .align 2 +// local.dso_init: +// ldr r0, .Lmoduledata +// .Lloadfrom: +// ldr r0, [r0] +// b runtime.addmoduledata@plt +// .align 2 +// .Lmoduledata: +// .word local.moduledata(GOT_PREL) + (. - (.Lloadfrom + 4)) +// assembles to: +// +// 00000000 <local.dso_init>: +// 0: e59f0004 ldr r0, [pc, #4] ; c <local.dso_init+0xc> +// 4: e5900000 ldr r0, [r0] +// 8: eafffffe b 0 <runtime.addmoduledata> +// 8: R_ARM_JUMP24 runtime.addmoduledata +// c: 00000004 .word 0x00000004 +// c: R_ARM_GOT_PREL local.moduledata + +func gentext(ctxt *ld.Link) { + if !ctxt.DynlinkingGo() { + return + } + addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0) + if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin { + // we're linking a module containing the runtime -> no need for + // an init function + return + } + addmoduledata.Attr |= sym.AttrReachable + initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0) + initfunc.Type = sym.STEXT + initfunc.Attr |= sym.AttrLocal + initfunc.Attr |= sym.AttrReachable + o := func(op uint32) { + initfunc.AddUint32(ctxt.Arch, op) + } + o(0xe59f0004) + o(0xe08f0000) + + o(0xeafffffe) + rel := initfunc.AddRel() + rel.Off = 8 + rel.Siz = 4 + rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0) + rel.Type = objabi.R_CALLARM + rel.Add = 0xeafffffe // vomit + + o(0x00000000) + rel = initfunc.AddRel() + rel.Off = 12 + rel.Siz = 4 + rel.Sym = ctxt.Moduledata + rel.Type = objabi.R_PCREL + rel.Add = 4 + + if ctxt.BuildMode == ld.BuildModePlugin { + ctxt.Textp = append(ctxt.Textp, addmoduledata) + } + ctxt.Textp = append(ctxt.Textp, initfunc) + initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0) + initarray_entry.Attr |= sym.AttrReachable + initarray_entry.Attr |= sym.AttrLocal + initarray_entry.Type = sym.SINITARR + initarray_entry.AddAddr(ctxt.Arch, initfunc) +} + +// Preserve highest 8 bits of a, and do addition to lower 24-bit +// of a and b; used to adjust ARM branch instruction's target +func braddoff(a int32, b int32) int32 { + return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) +} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + targ := r.Sym + + switch r.Type { + default: + if r.Type >= objabi.ElfRelocOffset { + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PLT32): + r.Type = objabi.R_CALLARM + + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_THM_PC22): // R_ARM_THM_CALL + ld.Exitf("R_ARM_THM_CALL, are you using -marm?") + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT32): // R_ARM_GOT_BREL + if targ.Type != sym.SDYNIMPORT { + addgotsyminternal(ctxt, targ) + } else { + addgotsym(ctxt, targ) + } + + r.Type = objabi.R_CONST // write r->add during relocsym + r.Sym = nil + r.Add += int64(targ.Got()) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil + if targ.Type != sym.SDYNIMPORT { + addgotsyminternal(ctxt, targ) + } else { + addgotsym(ctxt, targ) + } + + r.Type = objabi.R_PCREL + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(targ.Got()) + 4 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTOFF): // R_ARM_GOTOFF32 + r.Type = objabi.R_GOTOFF + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTPC): // R_ARM_BASE_PREL + r.Type = objabi.R_PCREL + + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += 4 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL): + r.Type = objabi.R_CALLARM + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_REL32): // R_ARM_REL32 + r.Type = objabi.R_PCREL + + r.Add += 4 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_ABS32): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ADDR + return true + + // we can just ignore this, because we are targeting ARM V5+ anyway + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_V4BX): + if r.Sym != nil { + // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it + r.Sym.Type = 0 + } + + r.Sym = nil + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24): + r.Type = objabi.R_CALLARM + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt()/4)) + } + + return true + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != sym.SDYNIMPORT { + return true + } + + switch r.Type { + case objabi.R_CALLARM: + if ctxt.LinkMode == ld.LinkExternal { + // External linker will do this relocation. + return true + } + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add = int64(targ.Plt()) + return true + + case objabi.R_ADDR: + if s.Type != sym.SDATA { + break + } + if ctxt.IsELF { + ld.Adddynsym(ctxt, targ) + rel := ctxt.Syms.Lookup(".rel", 0) + rel.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) + rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc + r.Type = objabi.R_CONST // write r->add during relocsym + r.Sym = nil + return true + } + } + + return false +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + ctxt.Out.Write32(uint32(sectoff)) + + elfsym := r.Xsym.ElfsymForReloc() + switch r.Type { + default: + return false + case objabi.R_ADDR: + if r.Siz == 4 { + ctxt.Out.Write32(uint32(elf.R_ARM_ABS32) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_PCREL: + if r.Siz == 4 { + ctxt.Out.Write32(uint32(elf.R_ARM_REL32) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_CALLARM: + if r.Siz == 4 { + if r.Add&0xff000000 == 0xeb000000 { // BL + ctxt.Out.Write32(uint32(elf.R_ARM_CALL) | uint32(elfsym)<<8) + } else { + ctxt.Out.Write32(uint32(elf.R_ARM_JUMP24) | uint32(elfsym)<<8) + } + } else { + return false + } + case objabi.R_TLS_LE: + ctxt.Out.Write32(uint32(elf.R_ARM_TLS_LE32) | uint32(elfsym)<<8) + case objabi.R_TLS_IE: + ctxt.Out.Write32(uint32(elf.R_ARM_TLS_IE32) | uint32(elfsym)<<8) + case objabi.R_GOTPCREL: + if r.Siz == 4 { + ctxt.Out.Write32(uint32(elf.R_ARM_GOT_PREL) | uint32(elfsym)<<8) + } else { + return false + } + } + + return true +} + +func elfsetupplt(ctxt *ld.Link) { + plt := ctxt.Syms.Lookup(".plt", 0) + got := ctxt.Syms.Lookup(".got.plt", 0) + if plt.Size == 0 { + // str lr, [sp, #-4]! + plt.AddUint32(ctxt.Arch, 0xe52de004) + + // ldr lr, [pc, #4] + plt.AddUint32(ctxt.Arch, 0xe59fe004) + + // add lr, pc, lr + plt.AddUint32(ctxt.Arch, 0xe08fe00e) + + // ldr pc, [lr, #8]! + plt.AddUint32(ctxt.Arch, 0xe5bef008) + + // .word &GLOBAL_OFFSET_TABLE[0] - . + plt.AddPCRelPlus(ctxt.Arch, got, 4) + + // the first .plt entry requires 3 .plt.got entries + got.AddUint32(ctxt.Arch, 0) + + got.AddUint32(ctxt.Arch, 0) + got.AddUint32(ctxt.Arch, 0) + } +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + + if r.Type == objabi.R_PCREL { + if rs.Type == sym.SHOSTOBJ { + ld.Errorf(s, "pc-relative relocation of external symbol is not supported") + return false + } + if r.Siz != 4 { + return false + } + + // emit a pair of "scattered" relocations that + // resolve to the difference of section addresses of + // the symbol and the instruction + // this value is added to the field being relocated + o1 := uint32(sectoff) + o1 |= 1 << 31 // scattered bit + o1 |= ld.MACHO_ARM_RELOC_SECTDIFF << 24 + o1 |= 2 << 28 // size = 4 + + o2 := uint32(0) + o2 |= 1 << 31 // scattered bit + o2 |= ld.MACHO_ARM_RELOC_PAIR << 24 + o2 |= 2 << 28 // size = 4 + + out.Write32(o1) + out.Write32(uint32(ld.Symaddr(rs))) + out.Write32(o2) + out.Write32(uint32(s.Value + int64(r.Off))) + return true + } + + if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALLARM { + if rs.Dynid < 0 { + ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type) + return false + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32(rs.Sect.Extnum) + if v == 0 { + ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type) + return false + } + } + + switch r.Type { + default: + return false + + case objabi.R_ADDR: + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + + case objabi.R_CALLARM: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM_RELOC_BR24 << 28 + } + + switch r.Siz { + default: + return false + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + out.Write32(uint32(sectoff)) + out.Write32(v) + return true +} + +func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + rs := r.Xsym + + if rs.Dynid < 0 { + ld.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type) + return false + } + + out.Write32(uint32(sectoff)) + out.Write32(uint32(rs.Dynid)) + + var v uint32 + switch r.Type { + default: + // unsupported relocation type + return false + + case objabi.R_DWARFSECREF: + v = ld.IMAGE_REL_ARM_SECREL + + case objabi.R_ADDR: + v = ld.IMAGE_REL_ARM_ADDR32 + } + + out.Write16(uint16(v)) + + return true +} + +// sign extend a 24-bit integer +func signext24(x int64) int32 { + return (int32(x) << 8) >> 8 +} + +// encode an immediate in ARM's imm12 format. copied from ../../../internal/obj/arm/asm5.go +func immrot(v uint32) uint32 { + for i := 0; i < 16; i++ { + if v&^0xff == 0 { + return uint32(i<<8) | v | 1<<25 + } + v = v<<2 | v>>30 + } + return 0 +} + +// Convert the direct jump relocation r to refer to a trampoline if the target is too far +func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) { + switch r.Type { + case objabi.R_CALLARM: + // r.Add is the instruction + // low 24-bit encodes the target address + t := (ld.Symaddr(r.Sym) + int64(signext24(r.Add&0xffffff)*4) - (s.Value + int64(r.Off))) / 4 + if t > 0x7fffff || t < -0x800000 || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) { + // direct call too far, need to insert trampoline. + // look up existing trampolines first. if we found one within the range + // of direct call, we can reuse it. otherwise create a new one. + offset := (signext24(r.Add&0xffffff) + 2) * 4 + var tramp *sym.Symbol + for i := 0; ; i++ { + name := r.Sym.Name + fmt.Sprintf("%+d-tramp%d", offset, i) + tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version)) + if tramp.Type == sym.SDYNIMPORT { + // don't reuse trampoline defined in other module + continue + } + if tramp.Value == 0 { + // either the trampoline does not exist -- we need to create one, + // or found one the address which is not assigned -- this will be + // laid down immediately after the current function. use this one. + break + } + + t = (ld.Symaddr(tramp) - 8 - (s.Value + int64(r.Off))) / 4 + if t >= -0x800000 && t < 0x7fffff { + // found an existing trampoline that is not too far + // we can just use it + break + } + } + if tramp.Type == 0 { + // trampoline does not exist, create one + ctxt.AddTramp(tramp) + if ctxt.DynlinkingGo() { + if immrot(uint32(offset)) == 0 { + ld.Errorf(s, "odd offset in dynlink direct call: %v+%d", r.Sym, offset) + } + gentrampdyn(ctxt.Arch, tramp, r.Sym, int64(offset)) + } else if ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE { + gentramppic(ctxt.Arch, tramp, r.Sym, int64(offset)) + } else { + gentramp(ctxt.Arch, ctxt.LinkMode, tramp, r.Sym, int64(offset)) + } + } + // modify reloc to point to tramp, which will be resolved later + r.Sym = tramp + r.Add = r.Add&0xff000000 | 0xfffffe // clear the offset embedded in the instruction + r.Done = false + } + default: + ld.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + } +} + +// generate a trampoline to target+offset +func gentramp(arch *sys.Arch, linkmode ld.LinkMode, tramp, target *sym.Symbol, offset int64) { + tramp.Size = 12 // 3 instructions + tramp.P = make([]byte, tramp.Size) + t := ld.Symaddr(target) + offset + o1 := uint32(0xe5900000 | 11<<12 | 15<<16) // MOVW (R15), R11 // R15 is actual pc + 8 + o2 := uint32(0xe12fff10 | 11) // JMP (R11) + o3 := uint32(t) // WORD $target + arch.ByteOrder.PutUint32(tramp.P, o1) + arch.ByteOrder.PutUint32(tramp.P[4:], o2) + arch.ByteOrder.PutUint32(tramp.P[8:], o3) + + if linkmode == ld.LinkExternal { + r := tramp.AddRel() + r.Off = 8 + r.Type = objabi.R_ADDR + r.Siz = 4 + r.Sym = target + r.Add = offset + } +} + +// generate a trampoline to target+offset in position independent code +func gentramppic(arch *sys.Arch, tramp, target *sym.Symbol, offset int64) { + tramp.Size = 16 // 4 instructions + tramp.P = make([]byte, tramp.Size) + o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 4) // MOVW 4(R15), R11 // R15 is actual pc + 8 + o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11 + o3 := uint32(0xe12fff10 | 11) // JMP (R11) + o4 := uint32(0) // WORD $(target-pc) // filled in with relocation + arch.ByteOrder.PutUint32(tramp.P, o1) + arch.ByteOrder.PutUint32(tramp.P[4:], o2) + arch.ByteOrder.PutUint32(tramp.P[8:], o3) + arch.ByteOrder.PutUint32(tramp.P[12:], o4) + + r := tramp.AddRel() + r.Off = 12 + r.Type = objabi.R_PCREL + r.Siz = 4 + r.Sym = target + r.Add = offset + 4 +} + +// generate a trampoline to target+offset in dynlink mode (using GOT) +func gentrampdyn(arch *sys.Arch, tramp, target *sym.Symbol, offset int64) { + tramp.Size = 20 // 5 instructions + o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 8) // MOVW 8(R15), R11 // R15 is actual pc + 8 + o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11 + o3 := uint32(0xe5900000 | 11<<12 | 11<<16) // MOVW (R11), R11 + o4 := uint32(0xe12fff10 | 11) // JMP (R11) + o5 := uint32(0) // WORD $target@GOT // filled in with relocation + o6 := uint32(0) + if offset != 0 { + // insert an instruction to add offset + tramp.Size = 24 // 6 instructions + o6 = o5 + o5 = o4 + o4 = 0xe2800000 | 11<<12 | 11<<16 | immrot(uint32(offset)) // ADD $offset, R11, R11 + o1 = uint32(0xe5900000 | 11<<12 | 15<<16 | 12) // MOVW 12(R15), R11 + } + tramp.P = make([]byte, tramp.Size) + arch.ByteOrder.PutUint32(tramp.P, o1) + arch.ByteOrder.PutUint32(tramp.P[4:], o2) + arch.ByteOrder.PutUint32(tramp.P[8:], o3) + arch.ByteOrder.PutUint32(tramp.P[12:], o4) + arch.ByteOrder.PutUint32(tramp.P[16:], o5) + if offset != 0 { + arch.ByteOrder.PutUint32(tramp.P[20:], o6) + } + + r := tramp.AddRel() + r.Off = 16 + r.Type = objabi.R_GOTPCREL + r.Siz = 4 + r.Sym = target + r.Add = 8 + if offset != 0 { + // increase reloc offset by 4 as we inserted an ADD instruction + r.Off = 20 + r.Add = 12 + } +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if ctxt.LinkMode == ld.LinkExternal { + switch r.Type { + case objabi.R_CALLARM: + r.Done = false + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + + r.Xadd = int64(signext24(r.Add & 0xffffff)) + r.Xadd *= 4 + for rs.Outer != nil { + r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Type != sym.SUNDEFEXT && rs.Sect == nil { + ld.Errorf(s, "missing section for %s", rs.Name) + } + r.Xsym = rs + + // ld64 for arm seems to want the symbol table to contain offset + // into the section rather than pseudo virtual address that contains + // the section load address. + // we need to compensate that by removing the instruction's address + // from addend. + if ctxt.HeadType == objabi.Hdarwin { + r.Xadd -= ld.Symaddr(s) + int64(r.Off) + } + + if r.Xadd/4 > 0x7fffff || r.Xadd/4 < -0x800000 { + ld.Errorf(s, "direct call too far %d", r.Xadd/4) + } + + return int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32(r.Xadd/4)))), true + } + + return -1, false + } + + switch r.Type { + case objabi.R_CONST: + return r.Add, true + case objabi.R_GOTOFF: + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + + // The following three arch specific relocations are only for generation of + // Linux/ARM ELF's PLT entry (3 assembler instruction) + case objabi.R_PLT0: // add ip, pc, #0xXX00000 + if ld.Symaddr(ctxt.Syms.Lookup(".got.plt", 0)) < ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) { + ld.Errorf(s, ".got.plt should be placed after .plt section.") + } + return 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add)) >> 20)), true + case objabi.R_PLT1: // add ip, ip, #0xYY000 + return 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+4)) >> 12)), true + case objabi.R_PLT2: // ldr pc, [ip, #0xZZZ]! + return 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+8))), true + case objabi.R_CALLARM: // bl XXXXXX or b YYYYYY + // r.Add is the instruction + // low 24-bit encodes the target address + t := (ld.Symaddr(r.Sym) + int64(signext24(r.Add&0xffffff)*4) - (s.Value + int64(r.Off))) / 4 + if t > 0x7fffff || t < -0x800000 { + ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t) + } + return int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&t))), true + } + + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func addpltreloc(ctxt *ld.Link, plt *sym.Symbol, got *sym.Symbol, s *sym.Symbol, typ objabi.RelocType) { + r := plt.AddRel() + r.Sym = got + r.Off = int32(plt.Size) + r.Siz = 4 + r.Type = typ + r.Add = int64(s.Got()) - 8 + + plt.Attr |= sym.AttrReachable + plt.Size += 4 + plt.Grow(plt.Size) +} + +func addpltsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ctxt.IsELF { + plt := ctxt.Syms.Lookup(".plt", 0) + got := ctxt.Syms.Lookup(".got.plt", 0) + rel := ctxt.Syms.Lookup(".rel.plt", 0) + if plt.Size == 0 { + elfsetupplt(ctxt) + } + + // .got entry + s.SetGot(int32(got.Size)) + + // In theory, all GOT should point to the first PLT entry, + // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's + // dynamic linker won't, so we'd better do it ourselves. + got.AddAddrPlus(ctxt.Arch, plt, 0) + + // .plt entry, this depends on the .got entry + s.SetPlt(int32(plt.Size)) + + addpltreloc(ctxt, plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000 + addpltreloc(ctxt, plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000 + addpltreloc(ctxt, plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]! + + // rel + rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) + + rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_JUMP_SLOT))) + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsyminternal(ctxt *ld.Link, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + got := ctxt.Syms.Lookup(".got", 0) + s.SetGot(int32(got.Size)) + + got.AddAddrPlus(ctxt.Arch, s, 0) + + if ctxt.IsELF { + } else { + ld.Errorf(s, "addgotsyminternal: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + got := ctxt.Syms.Lookup(".got", 0) + s.SetGot(int32(got.Size)) + got.AddUint32(ctxt.Arch, 0) + + if ctxt.IsELF { + rel := ctxt.Syms.Lookup(".rel", 0) + rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) + rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_ARM_GLOB_DAT))) + } else { + ld.Errorf(s, "addgotsym: unsupported binary format") + } +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sections[0] + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + for _, sect = range ld.Segtext.Sections[1:] { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + if ld.Segrelrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + machlink := uint32(0) + if ctxt.HeadType == objabi.Hdarwin { + machlink = uint32(ld.Domacholink(ctxt)) + } + + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if !*ld.FlagS { + // TODO: rationalize + switch ctxt.HeadType { + default: + if ctxt.IsELF { + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) + } + + case objabi.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + + case objabi.Hdarwin: + symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink)) + + case objabi.Hwindows: + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN)) + } + + ctxt.Out.SeekSet(int64(symo)) + switch ctxt.HeadType { + default: + if ctxt.IsELF { + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + case objabi.Hplan9: + ld.Asmplan9sym(ctxt) + ctxt.Out.Flush() + + sym := ctxt.Syms.Lookup("pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + ctxt.Out.Write(sym.P) + ctxt.Out.Flush() + } + + case objabi.Hwindows: + // Do nothing + + case objabi.Hdarwin: + if ctxt.LinkMode == ld.LinkExternal { + ld.Machoemitreloc(ctxt) + } + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + default: + case objabi.Hplan9: /* plan 9 */ + ctxt.Out.Write32b(0x647) /* magic */ + ctxt.Out.Write32b(uint32(ld.Segtext.Filelen)) /* sizes */ + ctxt.Out.Write32b(uint32(ld.Segdata.Filelen)) + ctxt.Out.Write32b(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ctxt.Out.Write32b(uint32(ld.Symsize)) /* nsyms */ + ctxt.Out.Write32b(uint32(ld.Entryvalue(ctxt))) /* va of entry */ + ctxt.Out.Write32b(0) + ctxt.Out.Write32b(uint32(ld.Lcsize)) + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Asmbelf(ctxt, int64(symo)) + + case objabi.Hdarwin: + ld.Asmbmacho(ctxt) + + case objabi.Hwindows: + ld.Asmbpe(ctxt) + } + + ctxt.Out.Flush() + if *ld.FlagC { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/oldlink/internal/arm/l.go b/src/cmd/oldlink/internal/arm/l.go new file mode 100644 index 0000000000..a83d26bf06 --- /dev/null +++ b/src/cmd/oldlink/internal/arm/l.go @@ -0,0 +1,75 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +// Writing object files. + +// Inferno utils/5l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 8 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 4 // single-instruction alignment +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 13 + dwarfRegLR = 14 +) diff --git a/src/cmd/oldlink/internal/arm/obj.go b/src/cmd/oldlink/internal/arm/obj.go new file mode 100644 index 0000000000..1fe4b9c2c8 --- /dev/null +++ b/src/cmd/oldlink/internal/arm/obj.go @@ -0,0 +1,116 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchARM + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Trampoline: trampoline, + Asmb: asmb, + Asmb2: asmb2, + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + PEreloc1: pereloc1, + + Linuxdynld: "/lib/ld-linux.so.3", // 2 for OABI, 3 for EABI + Freebsddynld: "/usr/libexec/ld-elf.so.1", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "/libexec/ld.elf_so", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4128 + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hlinux, /* arm elf */ + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + *ld.FlagD = false + // with dynamic linking + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + + case objabi.Hdarwin: /* apple MACH */ + ld.HEADR = ld.INITIAL_MACHO_HEADR + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4096 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hwindows: /* PE executable */ + // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit + return + } +} diff --git a/src/cmd/oldlink/internal/arm64/asm.go b/src/cmd/oldlink/internal/arm64/asm.go new file mode 100644 index 0000000000..a97671222d --- /dev/null +++ b/src/cmd/oldlink/internal/arm64/asm.go @@ -0,0 +1,946 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "debug/elf" + "encoding/binary" + "fmt" + "log" +) + +func gentext(ctxt *ld.Link) { + if !ctxt.DynlinkingGo() { + return + } + addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0) + if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin { + // we're linking a module containing the runtime -> no need for + // an init function + return + } + addmoduledata.Attr |= sym.AttrReachable + initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0) + initfunc.Type = sym.STEXT + initfunc.Attr |= sym.AttrLocal + initfunc.Attr |= sym.AttrReachable + o := func(op uint32) { + initfunc.AddUint32(ctxt.Arch, op) + } + // 0000000000000000 <local.dso_init>: + // 0: 90000000 adrp x0, 0 <runtime.firstmoduledata> + // 0: R_AARCH64_ADR_PREL_PG_HI21 local.moduledata + // 4: 91000000 add x0, x0, #0x0 + // 4: R_AARCH64_ADD_ABS_LO12_NC local.moduledata + o(0x90000000) + o(0x91000000) + rel := initfunc.AddRel() + rel.Off = 0 + rel.Siz = 8 + rel.Sym = ctxt.Moduledata + rel.Type = objabi.R_ADDRARM64 + + // 8: 14000000 b 0 <runtime.addmoduledata> + // 8: R_AARCH64_CALL26 runtime.addmoduledata + o(0x14000000) + rel = initfunc.AddRel() + rel.Off = 8 + rel.Siz = 4 + rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0) + rel.Type = objabi.R_CALLARM64 // Really should be R_AARCH64_JUMP26 but doesn't seem to make any difference + + if ctxt.BuildMode == ld.BuildModePlugin { + ctxt.Textp = append(ctxt.Textp, addmoduledata) + } + ctxt.Textp = append(ctxt.Textp, initfunc) + initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0) + initarray_entry.Attr |= sym.AttrReachable + initarray_entry.Attr |= sym.AttrLocal + initarray_entry.Type = sym.SINITARR + initarray_entry.AddAddr(ctxt.Arch, initfunc) +} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + targ := r.Sym + + switch r.Type { + default: + if r.Type >= objabi.ElfRelocOffset { + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL32): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_AARCH64_PREL32 relocation for dynamic symbol %s", targ.Name) + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { + ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + } + r.Type = objabi.R_PCREL + r.Add += 4 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL64): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_AARCH64_PREL64 relocation for dynamic symbol %s", targ.Name) + } + if targ.Type == 0 || targ.Type == sym.SXREF { + ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + } + r.Type = objabi.R_PCREL + r.Add += 8 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26): + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add += int64(targ.Plt()) + } + if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { + ld.Errorf(s, "unknown symbol %s in callarm64", targ.Name) + } + r.Type = objabi.R_CALLARM64 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_GOT_PAGE), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LD64_GOT_LO12_NC): + if targ.Type != sym.SDYNIMPORT { + // have symbol + // TODO: turn LDR of GOT entry into ADR of symbol itself + } + + // fall back to using GOT + // TODO: just needs relocation, no need to put in .dynsym + addgotsym(ctxt, targ) + + r.Type = objabi.R_ARM64_GOT + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(targ.Got()) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_PREL_PG_HI21), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADD_ABS_LO12_NC): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + } + if targ.Type == 0 || targ.Type == sym.SXREF { + ld.Errorf(s, "unknown symbol %s", targ.Name) + } + r.Type = objabi.R_ARM64_PCREL + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ABS64): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_AARCH64_ABS64 relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ADDR + if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + break + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST8_ABS_LO12_NC): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ARM64_LDST8 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST32_ABS_LO12_NC): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ARM64_LDST32 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST64_ABS_LO12_NC): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ARM64_LDST64 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST128_ABS_LO12_NC): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ARM64_LDST128 + return true + } + + switch r.Type { + case objabi.R_CALL, + objabi.R_PCREL, + objabi.R_CALLARM64: + if targ.Type != sym.SDYNIMPORT { + // nothing to do, the relocation will be laid out in reloc + return true + } + if ctxt.LinkMode == ld.LinkExternal { + // External linker will do this relocation. + return true + } + + case objabi.R_ADDR: + if s.Type == sym.STEXT && ctxt.IsELF { + // The code is asking for the address of an external + // function. We provide it with the address of the + // correspondent GOT symbol. + addgotsym(ctxt, targ) + + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(targ.Got()) + return true + } + + // Process dynamic relocations for the data sections. + if ctxt.BuildMode == ld.BuildModePIE && ctxt.LinkMode == ld.LinkInternal { + // When internally linking, generate dynamic relocations + // for all typical R_ADDR relocations. The exception + // are those R_ADDR that are created as part of generating + // the dynamic relocations and must be resolved statically. + // + // There are three phases relevant to understanding this: + // + // dodata() // we are here + // address() // symbol address assignment + // reloc() // resolution of static R_ADDR relocs + // + // At this point symbol addresses have not been + // assigned yet (as the final size of the .rela section + // will affect the addresses), and so we cannot write + // the Elf64_Rela.r_offset now. Instead we delay it + // until after the 'address' phase of the linker is + // complete. We do this via Addaddrplus, which creates + // a new R_ADDR relocation which will be resolved in + // the 'reloc' phase. + // + // These synthetic static R_ADDR relocs must be skipped + // now, or else we will be caught in an infinite loop + // of generating synthetic relocs for our synthetic + // relocs. + // + // Furthermore, the rela sections contain dynamic + // relocations with R_ADDR relocations on + // Elf64_Rela.r_offset. This field should contain the + // symbol offset as determined by reloc(), not the + // final dynamically linked address as a dynamic + // relocation would provide. + switch s.Name { + case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic": + return false + } + } else { + // Either internally linking a static executable, + // in which case we can resolve these relocations + // statically in the 'reloc' phase, or externally + // linking, in which case the relocation will be + // prepared in the 'reloc' phase and passed to the + // external linker in the 'asmb' phase. + if s.Type != sym.SDATA && s.Type != sym.SRODATA { + break + } + } + + if ctxt.IsELF { + // Generate R_AARCH64_RELATIVE relocations for best + // efficiency in the dynamic linker. + // + // As noted above, symbol addresses have not been + // assigned yet, so we can't generate the final reloc + // entry yet. We ultimately want: + // + // r_offset = s + r.Off + // r_info = R_AARCH64_RELATIVE + // r_addend = targ + r.Add + // + // The dynamic linker will set *offset = base address + + // addend. + // + // AddAddrPlus is used for r_offset and r_addend to + // generate new R_ADDR relocations that will update + // these fields in the 'reloc' phase. + rela := ctxt.Syms.Lookup(".rela", 0) + rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) + if r.Siz == 8 { + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_AARCH64_RELATIVE))) + } else { + ld.Errorf(s, "unexpected relocation for dynamic symbol %s", targ.Name) + } + rela.AddAddrPlus(ctxt.Arch, targ, int64(r.Add)) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). + return true + } + } + return false +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + ctxt.Out.Write64(uint64(sectoff)) + + elfsym := r.Xsym.ElfsymForReloc() + switch r.Type { + default: + return false + case objabi.R_ADDR: + switch r.Siz { + case 4: + ctxt.Out.Write64(uint64(elf.R_AARCH64_ABS32) | uint64(elfsym)<<32) + case 8: + ctxt.Out.Write64(uint64(elf.R_AARCH64_ABS64) | uint64(elfsym)<<32) + default: + return false + } + case objabi.R_ADDRARM64: + // two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_ADD_ABS_LO12_NC + ctxt.Out.Write64(uint64(elf.R_AARCH64_ADR_PREL_PG_HI21) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_AARCH64_ADD_ABS_LO12_NC) | uint64(elfsym)<<32) + case objabi.R_ARM64_TLS_LE: + ctxt.Out.Write64(uint64(elf.R_AARCH64_TLSLE_MOVW_TPREL_G0) | uint64(elfsym)<<32) + case objabi.R_ARM64_TLS_IE: + ctxt.Out.Write64(uint64(elf.R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) | uint64(elfsym)<<32) + case objabi.R_ARM64_GOTPCREL: + ctxt.Out.Write64(uint64(elf.R_AARCH64_ADR_GOT_PAGE) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_AARCH64_LD64_GOT_LO12_NC) | uint64(elfsym)<<32) + case objabi.R_CALLARM64: + if r.Siz != 4 { + return false + } + ctxt.Out.Write64(uint64(elf.R_AARCH64_CALL26) | uint64(elfsym)<<32) + + } + ctxt.Out.Write64(uint64(r.Xadd)) + + return true +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + + if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALLARM64 || r.Type == objabi.R_ADDRARM64 { + if rs.Dynid < 0 { + ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type) + return false + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32(rs.Sect.Extnum) + if v == 0 { + ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type) + return false + } + } + + switch r.Type { + default: + return false + case objabi.R_ADDR: + v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28 + case objabi.R_CALLARM64: + if r.Xadd != 0 { + ld.Errorf(s, "ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", rs.Name, r.Xadd) + } + + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM64_RELOC_BRANCH26 << 28 + case objabi.R_ADDRARM64: + r.Siz = 4 + // Two relocation entries: MACHO_ARM64_RELOC_PAGEOFF12 MACHO_ARM64_RELOC_PAGE21 + // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND. + if r.Xadd != 0 { + out.Write32(uint32(sectoff + 4)) + out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff)) + } + out.Write32(uint32(sectoff + 4)) + out.Write32(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25)) + if r.Xadd != 0 { + out.Write32(uint32(sectoff)) + out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff)) + } + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28 + } + + switch r.Siz { + default: + return false + case 1: + v |= 0 << 25 + case 2: + v |= 1 << 25 + case 4: + v |= 2 << 25 + case 8: + v |= 3 << 25 + } + + out.Write32(uint32(sectoff)) + out.Write32(v) + return true +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if ctxt.LinkMode == ld.LinkExternal { + switch r.Type { + default: + return val, false + case objabi.R_ARM64_GOTPCREL: + var o1, o2 uint32 + if ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = uint32(val >> 32) + o2 = uint32(val) + } else { + o1 = uint32(val) + o2 = uint32(val >> 32) + } + // Any relocation against a function symbol is redirected to + // be against a local symbol instead (see putelfsym in + // symtab.go) but unfortunately the system linker was buggy + // when confronted with a R_AARCH64_ADR_GOT_PAGE relocation + // against a local symbol until May 2015 + // (https://sourceware.org/bugzilla/show_bug.cgi?id=18270). So + // we convert the adrp; ld64 + R_ARM64_GOTPCREL into adrp; + // add + R_ADDRARM64. + if !(r.Sym.IsFileLocal() || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && ctxt.DynlinkingGo() { + if o2&0xffc00000 != 0xf9400000 { + ld.Errorf(s, "R_ARM64_GOTPCREL against unexpected instruction %x", o2) + } + o2 = 0x91000000 | (o2 & 0x000003ff) + r.Type = objabi.R_ADDRARM64 + } + if ctxt.Arch.ByteOrder == binary.BigEndian { + val = int64(o1)<<32 | int64(o2) + } else { + val = int64(o2)<<32 | int64(o1) + } + fallthrough + case objabi.R_ADDRARM64: + r.Done = false + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil { + ld.Errorf(s, "missing section for %s", rs.Name) + } + r.Xsym = rs + + // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation + // will make the linking fail because it thinks the code is not PIC even though + // the BR26 relocation should be fully resolved at link time. + // That is the reason why the next if block is disabled. When the bug in ld64 + // is fixed, we can enable this block and also enable duff's device in cmd/7g. + if false && ctxt.HeadType == objabi.Hdarwin { + var o0, o1 uint32 + + if ctxt.Arch.ByteOrder == binary.BigEndian { + o0 = uint32(val >> 32) + o1 = uint32(val) + } else { + o0 = uint32(val) + o1 = uint32(val >> 32) + } + // Mach-O wants the addend to be encoded in the instruction + // Note that although Mach-O supports ARM64_RELOC_ADDEND, it + // can only encode 24-bit of signed addend, but the instructions + // supports 33-bit of signed addend, so we always encode the + // addend in place. + o0 |= (uint32((r.Xadd>>12)&3) << 29) | (uint32((r.Xadd>>12>>2)&0x7ffff) << 5) + o1 |= uint32(r.Xadd&0xfff) << 10 + r.Xadd = 0 + + // when laid out, the instruction order must always be o1, o2. + if ctxt.Arch.ByteOrder == binary.BigEndian { + val = int64(o0)<<32 | int64(o1) + } else { + val = int64(o1)<<32 | int64(o0) + } + } + + return val, true + case objabi.R_CALLARM64, + objabi.R_ARM64_TLS_LE, + objabi.R_ARM64_TLS_IE: + r.Done = false + r.Xsym = r.Sym + r.Xadd = r.Add + return val, true + } + } + + switch r.Type { + case objabi.R_CONST: + return r.Add, true + + case objabi.R_GOTOFF: + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + + case objabi.R_ADDRARM64: + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + if t >= 1<<32 || t < -1<<32 { + ld.Errorf(s, "program too large, address relocation distance = %d", t) + } + + var o0, o1 uint32 + + if ctxt.Arch.ByteOrder == binary.BigEndian { + o0 = uint32(val >> 32) + o1 = uint32(val) + } else { + o0 = uint32(val) + o1 = uint32(val >> 32) + } + + o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) + o1 |= uint32(t&0xfff) << 10 + + // when laid out, the instruction order must always be o1, o2. + if ctxt.Arch.ByteOrder == binary.BigEndian { + return int64(o0)<<32 | int64(o1), true + } + return int64(o1)<<32 | int64(o0), true + + case objabi.R_ARM64_TLS_LE: + r.Done = false + if ctxt.HeadType == objabi.Hdarwin { + ld.Errorf(s, "TLS reloc on unsupported OS %v", ctxt.HeadType) + } + // The TCB is two pointers. This is not documented anywhere, but is + // de facto part of the ABI. + v := r.Sym.Value + int64(2*ctxt.Arch.PtrSize) + if v < 0 || v >= 32678 { + ld.Errorf(s, "TLS offset out of range %d", v) + } + return val | (v << 5), true + + case objabi.R_ARM64_TLS_IE: + if ctxt.BuildMode == ld.BuildModePIE && ctxt.IsELF { + // We are linking the final executable, so we + // can optimize any TLS IE relocation to LE. + r.Done = false + if ctxt.HeadType != objabi.Hlinux { + ld.Errorf(s, "TLS reloc on unsupported OS %v", ctxt.HeadType) + } + + // The TCB is two pointers. This is not documented anywhere, but is + // de facto part of the ABI. + v := ld.Symaddr(r.Sym) + int64(2*ctxt.Arch.PtrSize) + r.Add + if v < 0 || v >= 32678 { + ld.Errorf(s, "TLS offset out of range %d", v) + } + + var o0, o1 uint32 + if ctxt.Arch.ByteOrder == binary.BigEndian { + o0 = uint32(val >> 32) + o1 = uint32(val) + } else { + o0 = uint32(val) + o1 = uint32(val >> 32) + } + + // R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 + // turn ADRP to MOVZ + o0 = 0xd2a00000 | uint32(o0&0x1f) | (uint32((v>>16)&0xffff) << 5) + // R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC + // turn LD64 to MOVK + if v&3 != 0 { + ld.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC", v) + } + o1 = 0xf2800000 | uint32(o1&0x1f) | (uint32(v&0xffff) << 5) + + // when laid out, the instruction order must always be o0, o1. + if ctxt.Arch.ByteOrder == binary.BigEndian { + return int64(o0)<<32 | int64(o1), true + } + return int64(o1)<<32 | int64(o0), true + } else { + log.Fatalf("cannot handle R_ARM64_TLS_IE (sym %s) when linking internally", s.Name) + } + + case objabi.R_CALLARM64: + var t int64 + if r.Sym.Type == sym.SDYNIMPORT { + t = (ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) + r.Add) - (s.Value + int64(r.Off)) + } else { + t = (ld.Symaddr(r.Sym) + r.Add) - (s.Value + int64(r.Off)) + } + if t >= 1<<27 || t < -1<<27 { + ld.Errorf(s, "program too large, call relocation distance = %d", t) + } + return val | ((t >> 2) & 0x03ffffff), true + + case objabi.R_ARM64_GOT: + if s.P[r.Off+3]&0x9f == 0x90 { + // R_AARCH64_ADR_GOT_PAGE + // patch instruction: adrp + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + if t >= 1<<32 || t < -1<<32 { + ld.Errorf(s, "program too large, address relocation distance = %d", t) + } + var o0 uint32 + o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) + return val | int64(o0), true + } else if s.P[r.Off+3] == 0xf9 { + // R_AARCH64_LD64_GOT_LO12_NC + // patch instruction: ldr + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + if t&7 != 0 { + ld.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LD64_GOT_LO12_NC", t) + } + var o1 uint32 + o1 |= uint32(t&0xfff) << (10 - 3) + return val | int64(uint64(o1)), true + } else { + ld.Errorf(s, "unsupported instruction for %v R_GOTARM64", s.P[r.Off:r.Off+4]) + } + + case objabi.R_ARM64_PCREL: + if s.P[r.Off+3]&0x9f == 0x90 { + // R_AARCH64_ADR_PREL_PG_HI21 + // patch instruction: adrp + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + if t >= 1<<32 || t < -1<<32 { + ld.Errorf(s, "program too large, address relocation distance = %d", t) + } + o0 := (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) + return val | int64(o0), true + } else if s.P[r.Off+3]&0x91 == 0x91 { + // R_AARCH64_ADD_ABS_LO12_NC + // patch instruction: add + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + o1 := uint32(t&0xfff) << 10 + return val | int64(o1), true + } else { + ld.Errorf(s, "unsupported instruction for %v R_PCRELARM64", s.P[r.Off:r.Off+4]) + } + + case objabi.R_ARM64_LDST8: + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + o0 := uint32(t&0xfff) << 10 + return val | int64(o0), true + + case objabi.R_ARM64_LDST32: + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + if t&3 != 0 { + ld.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST32_ABS_LO12_NC", t) + } + o0 := (uint32(t&0xfff) >> 2) << 10 + return val | int64(o0), true + + case objabi.R_ARM64_LDST64: + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + if t&7 != 0 { + ld.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST64_ABS_LO12_NC", t) + } + o0 := (uint32(t&0xfff) >> 3) << 10 + return val | int64(o0), true + + case objabi.R_ARM64_LDST128: + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + if t&15 != 0 { + ld.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST128_ABS_LO12_NC", t) + } + o0 := (uint32(t&0xfff) >> 4) << 10 + return val | int64(o0), true + } + + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return -1 +} + +func elfsetupplt(ctxt *ld.Link) { + plt := ctxt.Syms.Lookup(".plt", 0) + gotplt := ctxt.Syms.Lookup(".got.plt", 0) + if plt.Size == 0 { + // stp x16, x30, [sp, #-16]! + // identifying information + plt.AddUint32(ctxt.Arch, 0xa9bf7bf0) + + // the following two instructions (adrp + ldr) load *got[2] into x17 + // adrp x16, &got[0] + plt.AddAddrPlus4(gotplt, 16) + plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + + // <imm> is the offset value of &got[2] to &got[0], the same below + // ldr x17, [x16, <imm>] + plt.AddAddrPlus4(gotplt, 16) + plt.SetUint32(ctxt.Arch, plt.Size-4, 0xf9400211) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + + // add x16, x16, <imm> + plt.AddAddrPlus4(gotplt, 16) + plt.SetUint32(ctxt.Arch, plt.Size-4, 0x91000210) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL + + // br x17 + plt.AddUint32(ctxt.Arch, 0xd61f0220) + + // 3 nop for place holder + plt.AddUint32(ctxt.Arch, 0xd503201f) + plt.AddUint32(ctxt.Arch, 0xd503201f) + plt.AddUint32(ctxt.Arch, 0xd503201f) + + // check gotplt.size == 0 + if gotplt.Size != 0 { + ld.Errorf(gotplt, "got.plt is not empty at the very beginning") + } + gotplt.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0) + + gotplt.AddUint64(ctxt.Arch, 0) + gotplt.AddUint64(ctxt.Arch, 0) + } +} + +func addpltsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ctxt.IsELF { + plt := ctxt.Syms.Lookup(".plt", 0) + gotplt := ctxt.Syms.Lookup(".got.plt", 0) + rela := ctxt.Syms.Lookup(".rela.plt", 0) + if plt.Size == 0 { + elfsetupplt(ctxt) + } + + // adrp x16, &got.plt[0] + plt.AddAddrPlus4(gotplt, gotplt.Size) + plt.SetUint32(ctxt.Arch, plt.Size-4, 0x90000010) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + + // <offset> is the offset value of &got.plt[n] to &got.plt[0] + // ldr x17, [x16, <offset>] + plt.AddAddrPlus4(gotplt, gotplt.Size) + plt.SetUint32(ctxt.Arch, plt.Size-4, 0xf9400211) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_GOT + + // add x16, x16, <offset> + plt.AddAddrPlus4(gotplt, gotplt.Size) + plt.SetUint32(ctxt.Arch, plt.Size-4, 0x91000210) + plt.R[len(plt.R)-1].Type = objabi.R_ARM64_PCREL + + // br x17 + plt.AddUint32(ctxt.Arch, 0xd61f0220) + + // add to got.plt: pointer to plt[0] + gotplt.AddAddrPlus(ctxt.Arch, plt, 0) + + // rela + rela.AddAddrPlus(ctxt.Arch, gotplt, gotplt.Size-8) + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_JUMP_SLOT))) + rela.AddUint64(ctxt.Arch, 0) + + s.SetPlt(int32(plt.Size - 16)) + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + got := ctxt.Syms.Lookup(".got", 0) + s.SetGot(int32(got.Size)) + got.AddUint64(ctxt.Arch, 0) + + if ctxt.IsELF { + rela := ctxt.Syms.Lookup(".rela", 0) + rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_AARCH64_GLOB_DAT))) + rela.AddUint64(ctxt.Arch, 0) + } else { + ld.Errorf(s, "addgotsym: unsupported binary format") + } +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sections[0] + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + for _, sect = range ld.Segtext.Sections[1:] { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + if ld.Segrelrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + machlink := uint32(0) + if ctxt.HeadType == objabi.Hdarwin { + machlink = uint32(ld.Domacholink(ctxt)) + } + + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if !*ld.FlagS { + // TODO: rationalize + switch ctxt.HeadType { + default: + if ctxt.IsELF { + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) + } + + case objabi.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + + case objabi.Hdarwin: + symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink)) + } + + ctxt.Out.SeekSet(int64(symo)) + switch ctxt.HeadType { + default: + if ctxt.IsELF { + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + case objabi.Hplan9: + ld.Asmplan9sym(ctxt) + ctxt.Out.Flush() + + sym := ctxt.Syms.Lookup("pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + ctxt.Out.Write(sym.P) + ctxt.Out.Flush() + } + + case objabi.Hdarwin: + if ctxt.LinkMode == ld.LinkExternal { + ld.Machoemitreloc(ctxt) + } + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + default: + case objabi.Hplan9: /* plan 9 */ + ctxt.Out.Write32(0x647) /* magic */ + ctxt.Out.Write32(uint32(ld.Segtext.Filelen)) /* sizes */ + ctxt.Out.Write32(uint32(ld.Segdata.Filelen)) + ctxt.Out.Write32(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ctxt.Out.Write32(uint32(ld.Symsize)) /* nsyms */ + ctxt.Out.Write32(uint32(ld.Entryvalue(ctxt))) /* va of entry */ + ctxt.Out.Write32(0) + ctxt.Out.Write32(uint32(ld.Lcsize)) + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Asmbelf(ctxt, int64(symo)) + + case objabi.Hdarwin: + ld.Asmbmacho(ctxt) + } + + ctxt.Out.Flush() + if *ld.FlagC { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/oldlink/internal/arm64/l.go b/src/cmd/oldlink/internal/arm64/l.go new file mode 100644 index 0000000000..50b88e479f --- /dev/null +++ b/src/cmd/oldlink/internal/arm64/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 8 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 31 + dwarfRegLR = 30 +) diff --git a/src/cmd/oldlink/internal/arm64/obj.go b/src/cmd/oldlink/internal/arm64/obj.go new file mode 100644 index 0000000000..2dcc999dd1 --- /dev/null +++ b/src/cmd/oldlink/internal/arm64/obj.go @@ -0,0 +1,110 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchARM64 + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Asmb: asmb, + Asmb2: asmb2, + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + + Androiddynld: "/system/bin/linker64", + Linuxdynld: "/lib/ld-linux-aarch64.so.1", + + Freebsddynld: "/usr/libexec/ld-elf.so.1", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "/libexec/ld.elf_so", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4096 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hlinux, /* arm64 elf */ + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + + case objabi.Hdarwin: /* apple MACH */ + ld.HEADR = ld.INITIAL_MACHO_HEADR + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4096 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + } +} diff --git a/src/cmd/oldlink/internal/ld/ar.go b/src/cmd/oldlink/internal/ld/ar.go new file mode 100644 index 0000000000..8df859f11c --- /dev/null +++ b/src/cmd/oldlink/internal/ld/ar.go @@ -0,0 +1,193 @@ +// Inferno utils/include/ar.h +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/include/ar.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/oldlink/internal/sym" + "encoding/binary" + "fmt" + "io" + "os" +) + +const ( + SARMAG = 8 + SAR_HDR = 16 + 44 +) + +const ( + ARMAG = "!<arch>\n" +) + +type ArHdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string +} + +// hostArchive reads an archive file holding host objects and links in +// required objects. The general format is the same as a Go archive +// file, but it has an armap listing symbols and the objects that +// define them. This is used for the compiler support library +// libgcc.a. +func hostArchive(ctxt *Link, name string) { + f, err := bio.Open(name) + if err != nil { + if os.IsNotExist(err) { + // It's OK if we don't have a libgcc file at all. + if ctxt.Debugvlog != 0 { + ctxt.Logf("skipping libgcc file: %v\n", err) + } + return + } + Exitf("cannot open file %s: %v", name, err) + } + defer f.Close() + + var magbuf [len(ARMAG)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { + Exitf("file %s too short", name) + } + + if string(magbuf[:]) != ARMAG { + Exitf("%s is not an archive file", name) + } + + var arhdr ArHdr + l := nextar(f, f.Offset(), &arhdr) + if l <= 0 { + Exitf("%s missing armap", name) + } + + var armap archiveMap + if arhdr.name == "/" || arhdr.name == "/SYM64/" { + armap = readArmap(name, f, arhdr) + } else { + Exitf("%s missing armap", name) + } + + loaded := make(map[uint64]bool) + any := true + for any { + var load []uint64 + for _, s := range ctxt.Syms.Allsym { + for i := range s.R { + r := &s.R[i] // Copying sym.Reloc has measurable impact on performance + if r.Sym != nil && r.Sym.Type == sym.SXREF { + if off := armap[r.Sym.Name]; off != 0 && !loaded[off] { + load = append(load, off) + loaded[off] = true + } + } + } + } + + for _, off := range load { + l := nextar(f, int64(off), &arhdr) + if l <= 0 { + Exitf("%s missing archive entry at offset %d", name, off) + } + pname := fmt.Sprintf("%s(%s)", name, arhdr.name) + l = atolwhex(arhdr.size) + + libgcc := sym.Library{Pkg: "libgcc"} + h := ldobj(ctxt, f, &libgcc, l, pname, name) + f.MustSeek(h.off, 0) + h.ld(ctxt, f, h.pkg, h.length, h.pn) + } + + any = len(load) > 0 + } +} + +// archiveMap is an archive symbol map: a mapping from symbol name to +// offset within the archive file. +type archiveMap map[string]uint64 + +// readArmap reads the archive symbol map. +func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap { + is64 := arhdr.name == "/SYM64/" + wordSize := 4 + if is64 { + wordSize = 8 + } + + contents := make([]byte, atolwhex(arhdr.size)) + if _, err := io.ReadFull(f, contents); err != nil { + Exitf("short read from %s", filename) + } + + var c uint64 + if is64 { + c = binary.BigEndian.Uint64(contents) + } else { + c = uint64(binary.BigEndian.Uint32(contents)) + } + contents = contents[wordSize:] + + ret := make(archiveMap) + + names := contents[c*uint64(wordSize):] + for i := uint64(0); i < c; i++ { + n := 0 + for names[n] != 0 { + n++ + } + name := string(names[:n]) + names = names[n+1:] + + // For Mach-O and PE/386 files we strip a leading + // underscore from the symbol name. + if objabi.GOOS == "darwin" || (objabi.GOOS == "windows" && objabi.GOARCH == "386") { + if name[0] == '_' && len(name) > 1 { + name = name[1:] + } + } + + var off uint64 + if is64 { + off = binary.BigEndian.Uint64(contents) + } else { + off = uint64(binary.BigEndian.Uint32(contents)) + } + contents = contents[wordSize:] + + ret[name] = off + } + + return ret +} diff --git a/src/cmd/oldlink/internal/ld/config.go b/src/cmd/oldlink/internal/ld/config.go new file mode 100644 index 0000000000..2373b500e3 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/config.go @@ -0,0 +1,272 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "fmt" + "log" +) + +// A BuildMode indicates the sort of object we are building. +// +// Possible build modes are the same as those for the -buildmode flag +// in cmd/go, and are documented in 'go help buildmode'. +type BuildMode uint8 + +const ( + BuildModeUnset BuildMode = iota + BuildModeExe + BuildModePIE + BuildModeCArchive + BuildModeCShared + BuildModeShared + BuildModePlugin +) + +func (mode *BuildMode) Set(s string) error { + badmode := func() error { + return fmt.Errorf("buildmode %s not supported on %s/%s", s, objabi.GOOS, objabi.GOARCH) + } + switch s { + default: + return fmt.Errorf("invalid buildmode: %q", s) + case "exe": + *mode = BuildModeExe + case "pie": + switch objabi.GOOS { + case "aix", "android", "linux", "windows": + case "darwin", "freebsd": + switch objabi.GOARCH { + case "amd64": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModePIE + case "c-archive": + switch objabi.GOOS { + case "aix", "darwin", "linux": + case "freebsd": + switch objabi.GOARCH { + case "amd64": + default: + return badmode() + } + case "windows": + switch objabi.GOARCH { + case "amd64", "386", "arm": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModeCArchive + case "c-shared": + switch objabi.GOARCH { + case "386", "amd64", "arm", "arm64", "ppc64le", "s390x": + default: + return badmode() + } + *mode = BuildModeCShared + case "shared": + switch objabi.GOOS { + case "linux": + switch objabi.GOARCH { + case "386", "amd64", "arm", "arm64", "ppc64le", "s390x": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModeShared + case "plugin": + switch objabi.GOOS { + case "linux": + switch objabi.GOARCH { + case "386", "amd64", "arm", "arm64", "s390x", "ppc64le": + default: + return badmode() + } + case "darwin", "freebsd": + switch objabi.GOARCH { + case "amd64": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModePlugin + } + return nil +} + +func (mode *BuildMode) String() string { + switch *mode { + case BuildModeUnset: + return "" // avoid showing a default in usage message + case BuildModeExe: + return "exe" + case BuildModePIE: + return "pie" + case BuildModeCArchive: + return "c-archive" + case BuildModeCShared: + return "c-shared" + case BuildModeShared: + return "shared" + case BuildModePlugin: + return "plugin" + } + return fmt.Sprintf("BuildMode(%d)", uint8(*mode)) +} + +// LinkMode indicates whether an external linker is used for the final link. +type LinkMode uint8 + +const ( + LinkAuto LinkMode = iota + LinkInternal + LinkExternal +) + +func (mode *LinkMode) Set(s string) error { + switch s { + default: + return fmt.Errorf("invalid linkmode: %q", s) + case "auto": + *mode = LinkAuto + case "internal": + *mode = LinkInternal + case "external": + *mode = LinkExternal + } + return nil +} + +func (mode *LinkMode) String() string { + switch *mode { + case LinkAuto: + return "auto" + case LinkInternal: + return "internal" + case LinkExternal: + return "external" + } + return fmt.Sprintf("LinkMode(%d)", uint8(*mode)) +} + +// mustLinkExternal reports whether the program being linked requires +// the external linker be used to complete the link. +func mustLinkExternal(ctxt *Link) (res bool, reason string) { + if ctxt.Debugvlog > 1 { + defer func() { + if res { + log.Printf("external linking is forced by: %s\n", reason) + } + }() + } + + if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) { + return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH) + } + + if *flagMsan { + return true, "msan" + } + + // Internally linking cgo is incomplete on some architectures. + // https://golang.org/issue/14449 + // https://golang.org/issue/21961 + if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64) { + return true, objabi.GOARCH + " does not support internal cgo" + } + if iscgo && objabi.GOOS == "android" { + return true, objabi.GOOS + " does not support internal cgo" + } + + // When the race flag is set, the LLVM tsan relocatable file is linked + // into the final binary, which means external linking is required because + // internal linking does not support it. + if *flagRace && ctxt.Arch.InFamily(sys.PPC64) { + return true, "race on " + objabi.GOARCH + } + + // Some build modes require work the internal linker cannot do (yet). + switch ctxt.BuildMode { + case BuildModeCArchive: + return true, "buildmode=c-archive" + case BuildModeCShared: + return true, "buildmode=c-shared" + case BuildModePIE: + switch objabi.GOOS + "/" + objabi.GOARCH { + case "linux/amd64", "linux/arm64", "android/arm64": + case "windows/386", "windows/amd64", "windows/arm": + default: + // Internal linking does not support TLS_IE. + return true, "buildmode=pie" + } + case BuildModePlugin: + return true, "buildmode=plugin" + case BuildModeShared: + return true, "buildmode=shared" + } + if ctxt.linkShared { + return true, "dynamically linking with a shared library" + } + + return false, "" +} + +// determineLinkMode sets ctxt.LinkMode. +// +// It is called after flags are processed and inputs are processed, +// so the ctxt.LinkMode variable has an initial value from the -linkmode +// flag and the iscgo externalobj variables are set. +func determineLinkMode(ctxt *Link) { + extNeeded, extReason := mustLinkExternal(ctxt) + via := "" + + if ctxt.LinkMode == LinkAuto { + // The environment variable GO_EXTLINK_ENABLED controls the + // default value of -linkmode. If it is not set when the + // linker is called we take the value it was set to when + // cmd/link was compiled. (See make.bash.) + switch objabi.Getgoextlinkenabled() { + case "0": + ctxt.LinkMode = LinkInternal + via = "via GO_EXTLINK_ENABLED " + case "1": + ctxt.LinkMode = LinkExternal + via = "via GO_EXTLINK_ENABLED " + default: + if extNeeded || (iscgo && externalobj) { + ctxt.LinkMode = LinkExternal + } else { + ctxt.LinkMode = LinkInternal + } + } + } + + switch ctxt.LinkMode { + case LinkInternal: + if extNeeded { + Exitf("internal linking requested %sbut external linking required: %s", via, extReason) + } + case LinkExternal: + switch { + case objabi.GOARCH == "riscv64": + Exitf("external linking not supported for %s/riscv64", objabi.GOOS) + case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix": + Exitf("external linking not supported for %s/ppc64", objabi.GOOS) + } + } +} diff --git a/src/cmd/oldlink/internal/ld/data.go b/src/cmd/oldlink/internal/ld/data.go new file mode 100644 index 0000000000..13f412ccd8 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/data.go @@ -0,0 +1,2509 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "bytes" + "cmd/internal/gcprog" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "compress/zlib" + "encoding/binary" + "fmt" + "log" + "os" + "sort" + "strconv" + "strings" + "sync" +) + +// isRuntimeDepPkg reports whether pkg is the runtime package or its dependency +func isRuntimeDepPkg(pkg string) bool { + switch pkg { + case "runtime", + "sync/atomic", // runtime may call to sync/atomic, due to go:linkname + "internal/bytealg", // for IndexByte + "internal/cpu": // for cpu features + return true + } + return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test") +} + +// Estimate the max size needed to hold any new trampolines created for this function. This +// is used to determine when the section can be split if it becomes too large, to ensure that +// the trampolines are in the same section as the function that uses them. +func maxSizeTrampolinesPPC64(s *sym.Symbol, isTramp bool) uint64 { + // If thearch.Trampoline is nil, then trampoline support is not available on this arch. + // A trampoline does not need any dependent trampolines. + if thearch.Trampoline == nil || isTramp { + return 0 + } + + n := uint64(0) + for ri := range s.R { + r := &s.R[ri] + if r.Type.IsDirectCallOrJump() { + n++ + } + } + // Trampolines in ppc64 are 4 instructions. + return n * 16 +} + +// detect too-far jumps in function s, and add trampolines if necessary +// ARM, PPC64 & PPC64LE support trampoline insertion for internal and external linking +// On PPC64 & PPC64LE the text sections might be split but will still insert trampolines +// where necessary. +func trampoline(ctxt *Link, s *sym.Symbol) { + if thearch.Trampoline == nil { + return // no need or no support of trampolines on this arch + } + + for ri := range s.R { + r := &s.R[ri] + if !r.Type.IsDirectCallOrJump() { + continue + } + if Symaddr(r.Sym) == 0 && (r.Sym.Type != sym.SDYNIMPORT && r.Sym.Type != sym.SUNDEFEXT) { + if r.Sym.File != s.File { + if !isRuntimeDepPkg(s.File) || !isRuntimeDepPkg(r.Sym.File) { + ctxt.ErrorUnresolved(s, r) + } + // runtime and its dependent packages may call to each other. + // they are fine, as they will be laid down together. + } + continue + } + + thearch.Trampoline(ctxt, r, s) + } + +} + +// relocsym resolve relocations in "s". The main loop walks through +// the list of relocations attached to "s" and resolves them where +// applicable. Relocations are often architecture-specific, requiring +// calls into the 'archreloc' and/or 'archrelocvariant' functions for +// the architecture. When external linking is in effect, it may not be +// possible to completely resolve the address/offset for a symbol, in +// which case the goal is to lay the groundwork for turning a given +// relocation into an external reloc (to be applied by the external +// linker). For more on how relocations work in general, see +// +// "Linkers and Loaders", by John R. Levine (Morgan Kaufmann, 1999), ch. 7 +// +// This is a performance-critical function for the linker; be careful +// to avoid introducing unnecessary allocations in the main loop. +func relocsym(ctxt *Link, s *sym.Symbol) { + if len(s.R) == 0 { + return + } + if s.Attr.ReadOnly() { + // The symbol's content is backed by read-only memory. + // Copy it to writable memory to apply relocations. + s.P = append([]byte(nil), s.P...) + s.Attr.Set(sym.AttrReadOnly, false) + } + for ri := int32(0); ri < int32(len(s.R)); ri++ { + r := &s.R[ri] + if r.Done { + // Relocation already processed by an earlier phase. + continue + } + r.Done = true + off := r.Off + siz := int32(r.Siz) + if off < 0 || off+siz > int32(len(s.P)) { + rname := "" + if r.Sym != nil { + rname = r.Sym.Name + } + Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(s.P)) + continue + } + + if r.Sym != nil && ((r.Sym.Type == sym.Sxxx && !r.Sym.Attr.VisibilityHidden()) || r.Sym.Type == sym.SXREF) { + // When putting the runtime but not main into a shared library + // these symbols are undefined and that's OK. + if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin { + if r.Sym.Name == "main.main" || (ctxt.BuildMode != BuildModePlugin && r.Sym.Name == "main..inittask") { + r.Sym.Type = sym.SDYNIMPORT + } else if strings.HasPrefix(r.Sym.Name, "go.info.") { + // Skip go.info symbols. They are only needed to communicate + // DWARF info between the compiler and linker. + continue + } + } else { + ctxt.ErrorUnresolved(s, r) + continue + } + } + + if r.Type >= objabi.ElfRelocOffset { + continue + } + if r.Siz == 0 { // informational relocation - no work to do + continue + } + + // We need to be able to reference dynimport symbols when linking against + // shared libraries, and Solaris, Darwin and AIX need it always + if ctxt.HeadType != objabi.Hsolaris && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Haix && r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT && !ctxt.DynlinkingGo() && !r.Sym.Attr.SubSymbol() { + if !(ctxt.Arch.Family == sys.PPC64 && ctxt.LinkMode == LinkExternal && r.Sym.Name == ".TOC.") { + Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", r.Sym.Name, r.Sym.Type, r.Sym.Type, r.Type, sym.RelocName(ctxt.Arch, r.Type)) + } + } + if r.Sym != nil && r.Sym.Type != sym.STLSBSS && r.Type != objabi.R_WEAKADDROFF && !r.Sym.Attr.Reachable() { + Errorf(s, "unreachable sym in relocation: %s", r.Sym.Name) + } + + if ctxt.LinkMode == LinkExternal { + r.InitExt() + } + + // TODO(mundaym): remove this special case - see issue 14218. + if ctxt.Arch.Family == sys.S390X { + switch r.Type { + case objabi.R_PCRELDBL: + r.InitExt() + r.Type = objabi.R_PCREL + r.Variant = sym.RV_390_DBL + case objabi.R_CALL: + r.InitExt() + r.Variant = sym.RV_390_DBL + } + } + + var o int64 + switch r.Type { + default: + switch siz { + default: + Errorf(s, "bad reloc size %#x for %s", uint32(siz), r.Sym.Name) + case 1: + o = int64(s.P[off]) + case 2: + o = int64(ctxt.Arch.ByteOrder.Uint16(s.P[off:])) + case 4: + o = int64(ctxt.Arch.ByteOrder.Uint32(s.P[off:])) + case 8: + o = int64(ctxt.Arch.ByteOrder.Uint64(s.P[off:])) + } + if offset, ok := thearch.Archreloc(ctxt, r, s, o); ok { + o = offset + } else { + Errorf(s, "unknown reloc to %v: %d (%s)", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type)) + } + case objabi.R_TLS_LE: + if ctxt.LinkMode == LinkExternal && ctxt.IsELF { + r.Done = false + if r.Sym == nil { + r.Sym = ctxt.Tlsg + } + r.Xsym = r.Sym + r.Xadd = r.Add + o = 0 + if ctxt.Arch.Family != sys.AMD64 { + o = r.Add + } + break + } + + if ctxt.IsELF && ctxt.Arch.Family == sys.ARM { + // On ELF ARM, the thread pointer is 8 bytes before + // the start of the thread-local data block, so add 8 + // to the actual TLS offset (r->sym->value). + // This 8 seems to be a fundamental constant of + // ELF on ARM (or maybe Glibc on ARM); it is not + // related to the fact that our own TLS storage happens + // to take up 8 bytes. + o = 8 + r.Sym.Value + } else if ctxt.IsELF || ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hdarwin { + o = int64(ctxt.Tlsoffset) + r.Add + } else if ctxt.HeadType == objabi.Hwindows { + o = r.Add + } else { + log.Fatalf("unexpected R_TLS_LE relocation for %v", ctxt.HeadType) + } + case objabi.R_TLS_IE: + if ctxt.LinkMode == LinkExternal && ctxt.IsELF { + r.Done = false + if r.Sym == nil { + r.Sym = ctxt.Tlsg + } + r.Xsym = r.Sym + r.Xadd = r.Add + o = 0 + if ctxt.Arch.Family != sys.AMD64 { + o = r.Add + } + break + } + if ctxt.BuildMode == BuildModePIE && ctxt.IsELF { + // We are linking the final executable, so we + // can optimize any TLS IE relocation to LE. + if thearch.TLSIEtoLE == nil { + log.Fatalf("internal linking of TLS IE not supported on %v", ctxt.Arch.Family) + } + thearch.TLSIEtoLE(s, int(off), int(r.Siz)) + o = int64(ctxt.Tlsoffset) + // TODO: o += r.Add when ctxt.Arch.Family != sys.AMD64? + // Why do we treat r.Add differently on AMD64? + // Is the external linker using Xadd at all? + } else { + log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", s.Name) + } + case objabi.R_ADDR: + if ctxt.LinkMode == LinkExternal && r.Sym.Type != sym.SCONST { + r.Done = false + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += Symaddr(rs) - Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Type != sym.SUNDEFEXT && rs.Sect == nil { + Errorf(s, "missing section for relocation target %s", rs.Name) + } + r.Xsym = rs + + o = r.Xadd + if ctxt.IsELF { + if ctxt.Arch.Family == sys.AMD64 { + o = 0 + } + } else if ctxt.HeadType == objabi.Hdarwin { + if rs.Type != sym.SHOSTOBJ { + o += Symaddr(rs) + } + } else if ctxt.HeadType == objabi.Hwindows { + // nothing to do + } else if ctxt.HeadType == objabi.Haix { + o = Symaddr(r.Sym) + r.Add + } else { + Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType) + } + + break + } + + // On AIX, a second relocation must be done by the loader, + // as section addresses can change once loaded. + // The "default" symbol address is still needed by the loader so + // the current relocation can't be skipped. + if ctxt.HeadType == objabi.Haix && r.Sym.Type != sym.SDYNIMPORT { + // It's not possible to make a loader relocation in a + // symbol which is not inside .data section. + // FIXME: It should be forbidden to have R_ADDR from a + // symbol which isn't in .data. However, as .text has the + // same address once loaded, this is possible. + if s.Sect.Seg == &Segdata { + Xcoffadddynrel(ctxt, s, r) + } + } + + o = Symaddr(r.Sym) + r.Add + + // On amd64, 4-byte offsets will be sign-extended, so it is impossible to + // access more than 2GB of static data; fail at link time is better than + // fail at runtime. See https://golang.org/issue/7980. + // Instead of special casing only amd64, we treat this as an error on all + // 64-bit architectures so as to be future-proof. + if int32(o) < 0 && ctxt.Arch.PtrSize > 4 && siz == 4 { + Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", r.Sym.Name, uint64(o), Symaddr(r.Sym), r.Add) + errorexit() + } + case objabi.R_DWARFSECREF: + if r.Sym.Sect == nil { + Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name) + } + + if ctxt.LinkMode == LinkExternal { + r.Done = false + + // On most platforms, the external linker needs to adjust DWARF references + // as it combines DWARF sections. However, on Darwin, dsymutil does the + // DWARF linking, and it understands how to follow section offsets. + // Leaving in the relocation records confuses it (see + // https://golang.org/issue/22068) so drop them for Darwin. + if ctxt.HeadType == objabi.Hdarwin { + r.Done = true + } + + // PE code emits IMAGE_REL_I386_SECREL and IMAGE_REL_AMD64_SECREL + // for R_DWARFSECREF relocations, while R_ADDR is replaced with + // IMAGE_REL_I386_DIR32, IMAGE_REL_AMD64_ADDR64 and IMAGE_REL_AMD64_ADDR32. + // Do not replace R_DWARFSECREF with R_ADDR for windows - + // let PE code emit correct relocations. + if ctxt.HeadType != objabi.Hwindows { + r.Type = objabi.R_ADDR + } + + r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0) + r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + + o = r.Xadd + if ctxt.IsELF && ctxt.Arch.Family == sys.AMD64 { + o = 0 + } + break + } + o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr) + case objabi.R_WEAKADDROFF: + if !r.Sym.Attr.Reachable() { + continue + } + fallthrough + case objabi.R_ADDROFF: + // The method offset tables using this relocation expect the offset to be relative + // to the start of the first text section, even if there are multiple. + if r.Sym.Sect.Name == ".text" { + o = Symaddr(r.Sym) - int64(Segtext.Sections[0].Vaddr) + r.Add + } else { + o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add + } + + case objabi.R_ADDRCUOFF: + // debug_range and debug_loc elements use this relocation type to get an + // offset from the start of the compile unit. + o = Symaddr(r.Sym) + r.Add - Symaddr(r.Sym.Unit.Textp[0]) + + // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. + case objabi.R_GOTPCREL: + if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin && r.Sym != nil && r.Sym.Type != sym.SCONST { + r.Done = false + r.Xadd = r.Add + r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk + r.Xsym = r.Sym + + o = r.Xadd + o += int64(r.Siz) + break + } + fallthrough + case objabi.R_CALL, objabi.R_PCREL: + if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT { + // pass through to the external linker. + r.Done = false + r.Xadd = 0 + if ctxt.IsELF { + r.Xadd -= int64(r.Siz) + } + r.Xsym = r.Sym + o = 0 + break + } + if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type != sym.SCONST && (r.Sym.Sect != s.Sect || r.Type == objabi.R_GOTPCREL) { + r.Done = false + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += Symaddr(rs) - Symaddr(rs.Outer) + rs = rs.Outer + } + + r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk + if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil { + Errorf(s, "missing section for relocation target %s", rs.Name) + } + r.Xsym = rs + + o = r.Xadd + if ctxt.IsELF { + if ctxt.Arch.Family == sys.AMD64 { + o = 0 + } + } else if ctxt.HeadType == objabi.Hdarwin { + if r.Type == objabi.R_CALL { + if ctxt.LinkMode == LinkExternal && rs.Type == sym.SDYNIMPORT { + switch ctxt.Arch.Family { + case sys.AMD64: + // AMD64 dynamic relocations are relative to the end of the relocation. + o += int64(r.Siz) + case sys.I386: + // I386 dynamic relocations are relative to the start of the section. + o -= int64(r.Off) // offset in symbol + o -= int64(s.Value - int64(s.Sect.Vaddr)) // offset of symbol in section + } + } else { + if rs.Type != sym.SHOSTOBJ { + o += int64(uint64(Symaddr(rs)) - rs.Sect.Vaddr) + } + o -= int64(r.Off) // relative to section offset, not symbol + } + } else if ctxt.Arch.Family == sys.ARM { + // see ../arm/asm.go:/machoreloc1 + o += Symaddr(rs) - s.Value - int64(r.Off) + } else { + o += int64(r.Siz) + } + } else if ctxt.HeadType == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 { // only amd64 needs PCREL + // PE/COFF's PC32 relocation uses the address after the relocated + // bytes as the base. Compensate by skewing the addend. + o += int64(r.Siz) + } else { + Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, ctxt.HeadType) + } + + break + } + + o = 0 + if r.Sym != nil { + o += Symaddr(r.Sym) + } + + o += r.Add - (s.Value + int64(r.Off) + int64(r.Siz)) + case objabi.R_SIZE: + o = r.Sym.Size + r.Add + + case objabi.R_XCOFFREF: + if ctxt.HeadType != objabi.Haix { + Errorf(s, "find XCOFF R_REF on non-XCOFF files") + } + if ctxt.LinkMode != LinkExternal { + Errorf(s, "find XCOFF R_REF with internal linking") + } + r.Xsym = r.Sym + r.Xadd = r.Add + r.Done = false + + // This isn't a real relocation so it must not update + // its offset value. + continue + + case objabi.R_DWARFFILEREF: + // The final file index is saved in r.Add in dwarf.go:writelines. + o = r.Add + } + + if ctxt.Arch.Family == sys.PPC64 || ctxt.Arch.Family == sys.S390X { + r.InitExt() + if r.Variant != sym.RV_NONE { + o = thearch.Archrelocvariant(ctxt, r, s, o) + } + } + + if false { + nam := "<nil>" + var addr int64 + if r.Sym != nil { + nam = r.Sym.Name + addr = Symaddr(r.Sym) + } + xnam := "<nil>" + if r.Xsym != nil { + xnam = r.Xsym.Name + } + fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x (xsym: %s +%#x) [type %d (%s)/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, addr, r.Add, xnam, r.Xadd, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Variant, o) + } + switch siz { + default: + Errorf(s, "bad reloc size %#x for %s", uint32(siz), r.Sym.Name) + fallthrough + + // TODO(rsc): Remove. + case 1: + s.P[off] = byte(int8(o)) + case 2: + if o != int64(int16(o)) { + Errorf(s, "relocation address for %s is too big: %#x", r.Sym.Name, o) + } + i16 := int16(o) + ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16)) + case 4: + if r.Type == objabi.R_PCREL || r.Type == objabi.R_CALL { + if o != int64(int32(o)) { + Errorf(s, "pc-relative relocation address for %s is too big: %#x", r.Sym.Name, o) + } + } else { + if o != int64(int32(o)) && o != int64(uint32(o)) { + Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", r.Sym.Name, uint64(o)) + } + } + + fl := int32(o) + ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl)) + case 8: + ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o)) + } + } +} + +func (ctxt *Link) reloc() { + for _, s := range ctxt.Textp { + relocsym(ctxt, s) + } + for _, s := range datap { + relocsym(ctxt, s) + } + for _, s := range dwarfp { + relocsym(ctxt, s) + } +} + +func windynrelocsym(ctxt *Link, rel, s *sym.Symbol) { + for ri := range s.R { + r := &s.R[ri] + targ := r.Sym + if targ == nil { + continue + } + if !targ.Attr.Reachable() { + if r.Type == objabi.R_WEAKADDROFF { + continue + } + Errorf(s, "dynamic relocation to unreachable symbol %s", targ.Name) + } + if r.Sym.Plt() == -2 && r.Sym.Got() != -2 { // make dynimport JMP table for PE object files. + targ.SetPlt(int32(rel.Size)) + r.Sym = rel + r.Add = int64(targ.Plt()) + + // jmp *addr + switch ctxt.Arch.Family { + default: + Errorf(s, "unsupported arch %v", ctxt.Arch.Family) + return + case sys.I386: + rel.AddUint8(0xff) + rel.AddUint8(0x25) + rel.AddAddr(ctxt.Arch, targ) + rel.AddUint8(0x90) + rel.AddUint8(0x90) + case sys.AMD64: + rel.AddUint8(0xff) + rel.AddUint8(0x24) + rel.AddUint8(0x25) + rel.AddAddrPlus4(targ, 0) + rel.AddUint8(0x90) + } + } else if r.Sym.Plt() >= 0 { + r.Sym = rel + r.Add = int64(targ.Plt()) + } + } +} + +// windynrelocsyms generates jump table to C library functions that will be +// added later. windynrelocsyms writes the table into .rel symbol. +func (ctxt *Link) windynrelocsyms() { + if !(ctxt.HeadType == objabi.Hwindows && iscgo && ctxt.LinkMode == LinkInternal) { + return + } + + /* relocation table */ + rel := ctxt.Syms.Lookup(".rel", 0) + rel.Attr |= sym.AttrReachable + rel.Type = sym.STEXT + ctxt.Textp = append(ctxt.Textp, rel) + + for _, s := range ctxt.Textp { + if s == rel { + continue + } + windynrelocsym(ctxt, rel, s) + } +} + +func dynrelocsym(ctxt *Link, s *sym.Symbol) { + for ri := range s.R { + r := &s.R[ri] + if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal { + // It's expected that some relocations will be done + // later by relocsym (R_TLS_LE, R_ADDROFF), so + // don't worry if Adddynrel returns false. + thearch.Adddynrel(ctxt, s, r) + continue + } + + if r.Sym != nil && r.Sym.Type == sym.SDYNIMPORT || r.Type >= objabi.ElfRelocOffset { + if r.Sym != nil && !r.Sym.Attr.Reachable() { + Errorf(s, "dynamic relocation to unreachable symbol %s", r.Sym.Name) + } + if !thearch.Adddynrel(ctxt, s, r) { + Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", r.Sym.Name, r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Sym.Type, r.Sym.Type) + } + } + } +} + +func dynreloc(ctxt *Link, data *[sym.SXREF][]*sym.Symbol) { + if ctxt.HeadType == objabi.Hwindows { + return + } + // -d suppresses dynamic loader format, so we may as well not + // compute these sections or mark their symbols as reachable. + if *FlagD { + return + } + + for _, s := range ctxt.Textp { + dynrelocsym(ctxt, s) + } + for _, syms := range data { + for _, s := range syms { + dynrelocsym(ctxt, s) + } + } + if ctxt.IsELF { + elfdynhash(ctxt) + } +} + +func Codeblk(ctxt *Link, addr int64, size int64) { + CodeblkPad(ctxt, addr, size, zeros[:]) +} +func CodeblkPad(ctxt *Link, addr int64, size int64, pad []byte) { + if *flagA { + ctxt.Logf("codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset()) + } + + blk(ctxt.Out, ctxt.Textp, addr, size, pad) + + /* again for printing */ + if !*flagA { + return + } + + syms := ctxt.Textp + for i, s := range syms { + if !s.Attr.Reachable() { + continue + } + if s.Value >= addr { + syms = syms[i:] + break + } + } + + eaddr := addr + size + for _, s := range syms { + if !s.Attr.Reachable() { + continue + } + if s.Value >= eaddr { + break + } + + if addr < s.Value { + ctxt.Logf("%-20s %.8x|", "_", uint64(addr)) + for ; addr < s.Value; addr++ { + ctxt.Logf(" %.2x", 0) + } + ctxt.Logf("\n") + } + + ctxt.Logf("%.6x\t%-20s\n", uint64(addr), s.Name) + q := s.P + + for len(q) >= 16 { + ctxt.Logf("%.6x\t% x\n", uint64(addr), q[:16]) + addr += 16 + q = q[16:] + } + + if len(q) > 0 { + ctxt.Logf("%.6x\t% x\n", uint64(addr), q) + addr += int64(len(q)) + } + } + + if addr < eaddr { + ctxt.Logf("%-20s %.8x|", "_", uint64(addr)) + for ; addr < eaddr; addr++ { + ctxt.Logf(" %.2x", 0) + } + } +} + +func blk(out *OutBuf, syms []*sym.Symbol, addr, size int64, pad []byte) { + for i, s := range syms { + if !s.Attr.SubSymbol() && s.Value >= addr { + syms = syms[i:] + break + } + } + + // This doesn't distinguish the memory size from the file + // size, and it lays out the file based on Symbol.Value, which + // is the virtual address. DWARF compression changes file sizes, + // so dwarfcompress will fix this up later if necessary. + eaddr := addr + size + for _, s := range syms { + if s.Attr.SubSymbol() { + continue + } + if s.Value >= eaddr { + break + } + if s.Value < addr { + Errorf(s, "phase error: addr=%#x but sym=%#x type=%d", addr, s.Value, s.Type) + errorexit() + } + if addr < s.Value { + out.WriteStringPad("", int(s.Value-addr), pad) + addr = s.Value + } + out.WriteSym(s) + addr += int64(len(s.P)) + if addr < s.Value+s.Size { + out.WriteStringPad("", int(s.Value+s.Size-addr), pad) + addr = s.Value + s.Size + } + if addr != s.Value+s.Size { + Errorf(s, "phase error: addr=%#x value+size=%#x", addr, s.Value+s.Size) + errorexit() + } + if s.Value+s.Size >= eaddr { + break + } + } + + if addr < eaddr { + out.WriteStringPad("", int(eaddr-addr), pad) + } + out.Flush() +} + +func Datblk(ctxt *Link, addr int64, size int64) { + writeDatblkToOutBuf(ctxt, ctxt.Out, addr, size) +} + +// Used only on Wasm for now. +func DatblkBytes(ctxt *Link, addr int64, size int64) []byte { + buf := bytes.NewBuffer(make([]byte, 0, size)) + out := &OutBuf{w: bufio.NewWriter(buf)} + writeDatblkToOutBuf(ctxt, out, addr, size) + out.Flush() + return buf.Bytes() +} + +func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) { + if *flagA { + ctxt.Logf("datblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset()) + } + + blk(out, datap, addr, size, zeros[:]) + + /* again for printing */ + if !*flagA { + return + } + + syms := datap + for i, sym := range syms { + if sym.Value >= addr { + syms = syms[i:] + break + } + } + + eaddr := addr + size + for _, sym := range syms { + if sym.Value >= eaddr { + break + } + if addr < sym.Value { + ctxt.Logf("\t%.8x| 00 ...\n", uint64(addr)) + addr = sym.Value + } + + ctxt.Logf("%s\n\t%.8x|", sym.Name, uint64(addr)) + for i, b := range sym.P { + if i > 0 && i%16 == 0 { + ctxt.Logf("\n\t%.8x|", uint64(addr)+uint64(i)) + } + ctxt.Logf(" %.2x", b) + } + + addr += int64(len(sym.P)) + for ; addr < sym.Value+sym.Size; addr++ { + ctxt.Logf(" %.2x", 0) + } + ctxt.Logf("\n") + + if ctxt.LinkMode != LinkExternal { + continue + } + for i := range sym.R { + r := &sym.R[i] // Copying sym.Reloc has measurable impact on performance + rsname := "" + rsval := int64(0) + if r.Sym != nil { + rsname = r.Sym.Name + rsval = r.Sym.Value + } + typ := "?" + switch r.Type { + case objabi.R_ADDR: + typ = "addr" + case objabi.R_PCREL: + typ = "pcrel" + case objabi.R_CALL: + typ = "call" + } + ctxt.Logf("\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, r.Add, rsval+r.Add) + } + } + + if addr < eaddr { + ctxt.Logf("\t%.8x| 00 ...\n", uint(addr)) + } + ctxt.Logf("\t%.8x|\n", uint(eaddr)) +} + +func Dwarfblk(ctxt *Link, addr int64, size int64) { + if *flagA { + ctxt.Logf("dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, ctxt.Out.Offset()) + } + + blk(ctxt.Out, dwarfp, addr, size, zeros[:]) +} + +var zeros [512]byte + +var ( + strdata = make(map[string]string) + strnames []string +) + +func addstrdata1(ctxt *Link, arg string) { + eq := strings.Index(arg, "=") + dot := strings.LastIndex(arg[:eq+1], ".") + if eq < 0 || dot < 0 { + Exitf("-X flag requires argument of the form importpath.name=value") + } + pkg := arg[:dot] + if ctxt.BuildMode == BuildModePlugin && pkg == "main" { + pkg = *flagPluginPath + } + pkg = objabi.PathToPrefix(pkg) + name := pkg + arg[dot:eq] + value := arg[eq+1:] + if _, ok := strdata[name]; !ok { + strnames = append(strnames, name) + } + strdata[name] = value +} + +// addstrdata sets the initial value of the string variable name to value. +func addstrdata(ctxt *Link, name, value string) { + s := ctxt.Syms.ROLookup(name, 0) + if s == nil || s.Gotype == nil { + // Not defined in the loaded packages. + return + } + if s.Gotype.Name != "type.string" { + Errorf(s, "cannot set with -X: not a var of type string (%s)", s.Gotype.Name) + return + } + if s.Type == sym.SBSS { + s.Type = sym.SDATA + } + + p := fmt.Sprintf("%s.str", s.Name) + sp := ctxt.Syms.Lookup(p, 0) + + Addstring(sp, value) + sp.Type = sym.SRODATA + + s.Size = 0 + s.P = s.P[:0] + if s.Attr.ReadOnly() { + s.P = make([]byte, 0, ctxt.Arch.PtrSize*2) + s.Attr.Set(sym.AttrReadOnly, false) + } + s.R = s.R[:0] + reachable := s.Attr.Reachable() + s.AddAddr(ctxt.Arch, sp) + s.AddUint(ctxt.Arch, uint64(len(value))) + + // addstring, addaddr, etc., mark the symbols as reachable. + // In this case that is not necessarily true, so stick to what + // we know before entering this function. + s.Attr.Set(sym.AttrReachable, reachable) + + sp.Attr.Set(sym.AttrReachable, reachable) +} + +func (ctxt *Link) dostrdata() { + for _, name := range strnames { + addstrdata(ctxt, name, strdata[name]) + } +} + +func Addstring(s *sym.Symbol, str string) int64 { + if s.Type == 0 { + s.Type = sym.SNOPTRDATA + } + s.Attr |= sym.AttrReachable + r := s.Size + if s.Name == ".shstrtab" { + elfsetstring(s, str, int(r)) + } + s.P = append(s.P, str...) + s.P = append(s.P, 0) + s.Size = int64(len(s.P)) + return r +} + +// addgostring adds str, as a Go string value, to s. symname is the name of the +// symbol used to define the string data and must be unique per linked object. +func addgostring(ctxt *Link, s *sym.Symbol, symname, str string) { + sdata := ctxt.Syms.Lookup(symname, 0) + if sdata.Type != sym.Sxxx { + Errorf(s, "duplicate symname in addgostring: %s", symname) + } + sdata.Attr |= sym.AttrReachable + sdata.Attr |= sym.AttrLocal + sdata.Type = sym.SRODATA + sdata.Size = int64(len(str)) + sdata.P = []byte(str) + s.AddAddr(ctxt.Arch, sdata) + s.AddUint(ctxt.Arch, uint64(len(str))) +} + +func addinitarrdata(ctxt *Link, s *sym.Symbol) { + p := s.Name + ".ptr" + sp := ctxt.Syms.Lookup(p, 0) + sp.Type = sym.SINITARR + sp.Size = 0 + sp.Attr |= sym.AttrDuplicateOK + sp.AddAddr(ctxt.Arch, s) +} + +// symalign returns the required alignment for the given symbol s. +func symalign(s *sym.Symbol) int32 { + min := int32(thearch.Minalign) + if s.Align >= min { + return s.Align + } else if s.Align != 0 { + return min + } + if strings.HasPrefix(s.Name, "go.string.") || strings.HasPrefix(s.Name, "type..namedata.") { + // String data is just bytes. + // If we align it, we waste a lot of space to padding. + return min + } + align := int32(thearch.Maxalign) + for int64(align) > s.Size && align > min { + align >>= 1 + } + s.Align = align + return align +} + +func aligndatsize(datsize int64, s *sym.Symbol) int64 { + return Rnd(datsize, int64(symalign(s))) +} + +const debugGCProg = false + +type GCProg struct { + ctxt *Link + sym *sym.Symbol + w gcprog.Writer +} + +func (p *GCProg) Init(ctxt *Link, name string) { + p.ctxt = ctxt + p.sym = ctxt.Syms.Lookup(name, 0) + p.w.Init(p.writeByte(ctxt)) + if debugGCProg { + fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name) + p.w.Debug(os.Stderr) + } +} + +func (p *GCProg) writeByte(ctxt *Link) func(x byte) { + return func(x byte) { + p.sym.AddUint8(x) + } +} + +func (p *GCProg) End(size int64) { + p.w.ZeroUntil(size / int64(p.ctxt.Arch.PtrSize)) + p.w.End() + if debugGCProg { + fmt.Fprintf(os.Stderr, "ld: end GCProg\n") + } +} + +func (p *GCProg) AddSym(s *sym.Symbol) { + typ := s.Gotype + // Things without pointers should be in sym.SNOPTRDATA or sym.SNOPTRBSS; + // everything we see should have pointers and should therefore have a type. + if typ == nil { + switch s.Name { + case "runtime.data", "runtime.edata", "runtime.bss", "runtime.ebss": + // Ignore special symbols that are sometimes laid out + // as real symbols. See comment about dyld on darwin in + // the address function. + return + } + Errorf(s, "missing Go type information for global symbol: size %d", s.Size) + return + } + + ptrsize := int64(p.ctxt.Arch.PtrSize) + nptr := decodetypePtrdata(p.ctxt.Arch, typ.P) / ptrsize + + if debugGCProg { + fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr) + } + + if decodetypeUsegcprog(p.ctxt.Arch, typ.P) == 0 { + // Copy pointers from mask into program. + mask := decodetypeGcmask(p.ctxt, typ) + for i := int64(0); i < nptr; i++ { + if (mask[i/8]>>uint(i%8))&1 != 0 { + p.w.Ptr(s.Value/ptrsize + i) + } + } + return + } + + // Copy program. + prog := decodetypeGcprog(p.ctxt, typ) + p.w.ZeroUntil(s.Value / ptrsize) + p.w.Append(prog[4:], nptr) +} + +// dataSortKey is used to sort a slice of data symbol *sym.Symbol pointers. +// The sort keys are kept inline to improve cache behavior while sorting. +type dataSortKey struct { + size int64 + name string + sym *sym.Symbol +} + +type bySizeAndName []dataSortKey + +func (d bySizeAndName) Len() int { return len(d) } +func (d bySizeAndName) Swap(i, j int) { d[i], d[j] = d[j], d[i] } +func (d bySizeAndName) Less(i, j int) bool { + s1, s2 := d[i], d[j] + if s1.size != s2.size { + return s1.size < s2.size + } + return s1.name < s2.name +} + +// cutoff is the maximum data section size permitted by the linker +// (see issue #9862). +const cutoff = 2e9 // 2 GB (or so; looks better in errors than 2^31) + +func checkdatsize(ctxt *Link, datsize int64, symn sym.SymKind) { + if datsize > cutoff { + Errorf(nil, "too much data in section %v (over %v bytes)", symn, cutoff) + } +} + +// datap is a collection of reachable data symbols in address order. +// Generated by dodata. +var datap []*sym.Symbol + +func (ctxt *Link) dodata() { + if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + // The values in moduledata are filled out by relocations + // pointing to the addresses of these special symbols. + // Typically these symbols have no size and are not laid + // out with their matching section. + // + // However on darwin, dyld will find the special symbol + // in the first loaded module, even though it is local. + // + // (An hypothesis, formed without looking in the dyld sources: + // these special symbols have no size, so their address + // matches a real symbol. The dynamic linker assumes we + // want the normal symbol with the same address and finds + // it in the other module.) + // + // To work around this we lay out the symbls whose + // addresses are vital for multi-module programs to work + // as normal symbols, and give them a little size. + // + // On AIX, as all DATA sections are merged together, ld might not put + // these symbols at the beginning of their respective section if there + // aren't real symbols, their alignment might not match the + // first symbol alignment. Therefore, there are explicitly put at the + // beginning of their section with the same alignment. + bss := ctxt.Syms.Lookup("runtime.bss", 0) + bss.Size = 8 + bss.Attr.Set(sym.AttrSpecial, false) + + ctxt.Syms.Lookup("runtime.ebss", 0).Attr.Set(sym.AttrSpecial, false) + + data := ctxt.Syms.Lookup("runtime.data", 0) + data.Size = 8 + data.Attr.Set(sym.AttrSpecial, false) + + edata := ctxt.Syms.Lookup("runtime.edata", 0) + edata.Attr.Set(sym.AttrSpecial, false) + if ctxt.HeadType == objabi.Haix { + // XCOFFTOC symbols are part of .data section. + edata.Type = sym.SXCOFFTOC + } + + types := ctxt.Syms.Lookup("runtime.types", 0) + types.Type = sym.STYPE + types.Size = 8 + types.Attr.Set(sym.AttrSpecial, false) + + etypes := ctxt.Syms.Lookup("runtime.etypes", 0) + etypes.Type = sym.SFUNCTAB + etypes.Attr.Set(sym.AttrSpecial, false) + + if ctxt.HeadType == objabi.Haix { + rodata := ctxt.Syms.Lookup("runtime.rodata", 0) + rodata.Type = sym.SSTRING + rodata.Size = 8 + rodata.Attr.Set(sym.AttrSpecial, false) + + ctxt.Syms.Lookup("runtime.erodata", 0).Attr.Set(sym.AttrSpecial, false) + + } + } + + // Collect data symbols by type into data. + var data [sym.SXREF][]*sym.Symbol + for _, s := range ctxt.Syms.Allsym { + if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() { + continue + } + if s.Type <= sym.STEXT || s.Type >= sym.SXREF { + continue + } + data[s.Type] = append(data[s.Type], s) + } + + // Now that we have the data symbols, but before we start + // to assign addresses, record all the necessary + // dynamic relocations. These will grow the relocation + // symbol, which is itself data. + // + // On darwin, we need the symbol table numbers for dynreloc. + if ctxt.HeadType == objabi.Hdarwin { + machosymorder(ctxt) + } + dynreloc(ctxt, &data) + + if ctxt.UseRelro() { + // "read only" data with relocations needs to go in its own section + // when building a shared library. We do this by boosting objects of + // type SXXX with relocations to type SXXXRELRO. + for _, symnro := range sym.ReadOnly { + symnrelro := sym.RelROMap[symnro] + + ro := []*sym.Symbol{} + relro := data[symnrelro] + + for _, s := range data[symnro] { + isRelro := len(s.R) > 0 + switch s.Type { + case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO: + // Symbols are not sorted yet, so it is possible + // that an Outer symbol has been changed to a + // relro Type before it reaches here. + isRelro = true + case sym.SFUNCTAB: + if ctxt.HeadType == objabi.Haix && s.Name == "runtime.etypes" { + // runtime.etypes must be at the end of + // the relro datas. + isRelro = true + } + } + if isRelro { + s.Type = symnrelro + if s.Outer != nil { + s.Outer.Type = s.Type + } + relro = append(relro, s) + } else { + ro = append(ro, s) + } + } + + // Check that we haven't made two symbols with the same .Outer into + // different types (because references two symbols with non-nil Outer + // become references to the outer symbol + offset it's vital that the + // symbol and the outer end up in the same section). + for _, s := range relro { + if s.Outer != nil && s.Outer.Type != s.Type { + Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)", + s.Outer.Name, s.Type, s.Outer.Type) + } + } + + data[symnro] = ro + data[symnrelro] = relro + } + } + + // Sort symbols. + var dataMaxAlign [sym.SXREF]int32 + var wg sync.WaitGroup + for symn := range data { + symn := sym.SymKind(symn) + wg.Add(1) + go func() { + data[symn], dataMaxAlign[symn] = dodataSect(ctxt, symn, data[symn]) + wg.Done() + }() + } + wg.Wait() + + if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal { + // These symbols must have the same alignment as their section. + // Otherwize, ld might change the layout of Go sections. + ctxt.Syms.ROLookup("runtime.data", 0).Align = dataMaxAlign[sym.SDATA] + ctxt.Syms.ROLookup("runtime.bss", 0).Align = dataMaxAlign[sym.SBSS] + } + + // Allocate sections. + // Data is processed before segtext, because we need + // to see all symbols in the .data and .bss sections in order + // to generate garbage collection information. + datsize := int64(0) + + // Writable data sections that do not need any specialized handling. + writable := []sym.SymKind{ + sym.SBUILDINFO, + sym.SELFSECT, + sym.SMACHO, + sym.SMACHOGOT, + sym.SWINDOWS, + } + for _, symn := range writable { + for _, s := range data[symn] { + sect := addsection(ctxt.Arch, &Segdata, s.Name, 06) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = sym.SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + sect.Length = uint64(datsize) - sect.Vaddr + } + checkdatsize(ctxt, datsize, symn) + } + + // .got (and .toc on ppc64) + if len(data[sym.SELFGOT]) > 0 { + sect := addsection(ctxt.Arch, &Segdata, ".got", 06) + sect.Align = dataMaxAlign[sym.SELFGOT] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + for _, s := range data[sym.SELFGOT] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = sym.SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + + // Resolve .TOC. symbol for this object file (ppc64) + toc := ctxt.Syms.ROLookup(".TOC.", int(s.Version)) + if toc != nil { + toc.Sect = sect + toc.Outer = s + toc.Sub = s.Sub + s.Sub = toc + + toc.Value = 0x8000 + } + + datsize += s.Size + } + checkdatsize(ctxt, datsize, sym.SELFGOT) + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* pointer-free data */ + sect := addsection(ctxt.Arch, &Segdata, ".noptrdata", 06) + sect.Align = dataMaxAlign[sym.SNOPTRDATA] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect = sect + ctxt.Syms.Lookup("runtime.enoptrdata", 0).Sect = sect + for _, s := range data[sym.SNOPTRDATA] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = sym.SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + checkdatsize(ctxt, datsize, sym.SNOPTRDATA) + sect.Length = uint64(datsize) - sect.Vaddr + + hasinitarr := ctxt.linkShared + + /* shared library initializer */ + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin: + hasinitarr = true + } + + if ctxt.HeadType == objabi.Haix { + if len(data[sym.SINITARR]) > 0 { + Errorf(nil, "XCOFF format doesn't allow .init_array section") + } + } + + if hasinitarr && len(data[sym.SINITARR]) > 0 { + sect := addsection(ctxt.Arch, &Segdata, ".init_array", 06) + sect.Align = dataMaxAlign[sym.SINITARR] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + for _, s := range data[sym.SINITARR] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + sect.Length = uint64(datsize) - sect.Vaddr + checkdatsize(ctxt, datsize, sym.SINITARR) + } + + /* data */ + sect = addsection(ctxt.Arch, &Segdata, ".data", 06) + sect.Align = dataMaxAlign[sym.SDATA] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + ctxt.Syms.Lookup("runtime.data", 0).Sect = sect + ctxt.Syms.Lookup("runtime.edata", 0).Sect = sect + var gc GCProg + gc.Init(ctxt, "runtime.gcdata") + for _, s := range data[sym.SDATA] { + s.Sect = sect + s.Type = sym.SDATA + datsize = aligndatsize(datsize, s) + s.Value = int64(uint64(datsize) - sect.Vaddr) + gc.AddSym(s) + datsize += s.Size + } + gc.End(datsize - int64(sect.Vaddr)) + // On AIX, TOC entries must be the last of .data + // These aren't part of gc as they won't change during the runtime. + for _, s := range data[sym.SXCOFFTOC] { + s.Sect = sect + s.Type = sym.SDATA + datsize = aligndatsize(datsize, s) + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + checkdatsize(ctxt, datsize, sym.SDATA) + sect.Length = uint64(datsize) - sect.Vaddr + + /* bss */ + sect = addsection(ctxt.Arch, &Segdata, ".bss", 06) + sect.Align = dataMaxAlign[sym.SBSS] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + ctxt.Syms.Lookup("runtime.bss", 0).Sect = sect + ctxt.Syms.Lookup("runtime.ebss", 0).Sect = sect + gc = GCProg{} + gc.Init(ctxt, "runtime.gcbss") + for _, s := range data[sym.SBSS] { + s.Sect = sect + datsize = aligndatsize(datsize, s) + s.Value = int64(uint64(datsize) - sect.Vaddr) + gc.AddSym(s) + datsize += s.Size + } + checkdatsize(ctxt, datsize, sym.SBSS) + sect.Length = uint64(datsize) - sect.Vaddr + gc.End(int64(sect.Length)) + + /* pointer-free bss */ + sect = addsection(ctxt.Arch, &Segdata, ".noptrbss", 06) + sect.Align = dataMaxAlign[sym.SNOPTRBSS] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + ctxt.Syms.Lookup("runtime.noptrbss", 0).Sect = sect + ctxt.Syms.Lookup("runtime.enoptrbss", 0).Sect = sect + for _, s := range data[sym.SNOPTRBSS] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + sect.Length = uint64(datsize) - sect.Vaddr + ctxt.Syms.Lookup("runtime.end", 0).Sect = sect + checkdatsize(ctxt, datsize, sym.SNOPTRBSS) + + // Coverage instrumentation counters for libfuzzer. + if len(data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 { + sect := addsection(ctxt.Arch, &Segdata, "__libfuzzer_extra_counters", 06) + sect.Align = dataMaxAlign[sym.SLIBFUZZER_EXTRA_COUNTER] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + for _, s := range data[sym.SLIBFUZZER_EXTRA_COUNTER] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + sect.Length = uint64(datsize) - sect.Vaddr + checkdatsize(ctxt, datsize, sym.SLIBFUZZER_EXTRA_COUNTER) + } + + if len(data[sym.STLSBSS]) > 0 { + var sect *sym.Section + if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) { + sect = addsection(ctxt.Arch, &Segdata, ".tbss", 06) + sect.Align = int32(ctxt.Arch.PtrSize) + sect.Vaddr = 0 + } + datsize = 0 + + for _, s := range data[sym.STLSBSS] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = datsize + datsize += s.Size + } + checkdatsize(ctxt, datsize, sym.STLSBSS) + + if sect != nil { + sect.Length = uint64(datsize) + } + } + + /* + * We finished data, begin read-only data. + * Not all systems support a separate read-only non-executable data section. + * ELF and Windows PE systems do. + * OS X and Plan 9 do not. + * And if we're using external linking mode, the point is moot, + * since it's not our decision; that code expects the sections in + * segtext. + */ + var segro *sym.Segment + if ctxt.IsELF && ctxt.LinkMode == LinkInternal { + segro = &Segrodata + } else if ctxt.HeadType == objabi.Hwindows { + segro = &Segrodata + } else { + segro = &Segtext + } + + datsize = 0 + + /* read-only executable ELF, Mach-O sections */ + if len(data[sym.STEXT]) != 0 { + Errorf(nil, "dodata found an sym.STEXT symbol: %s", data[sym.STEXT][0].Name) + } + for _, s := range data[sym.SELFRXSECT] { + sect := addsection(ctxt.Arch, &Segtext, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + sect.Length = uint64(datsize) - sect.Vaddr + checkdatsize(ctxt, datsize, sym.SELFRXSECT) + } + + /* read-only data */ + sect = addsection(ctxt.Arch, segro, ".rodata", 04) + + sect.Vaddr = 0 + ctxt.Syms.Lookup("runtime.rodata", 0).Sect = sect + ctxt.Syms.Lookup("runtime.erodata", 0).Sect = sect + if !ctxt.UseRelro() { + ctxt.Syms.Lookup("runtime.types", 0).Sect = sect + ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect + } + for _, symn := range sym.ReadOnly { + align := dataMaxAlign[symn] + if sect.Align < align { + sect.Align = align + } + } + datsize = Rnd(datsize, int64(sect.Align)) + for _, symn := range sym.ReadOnly { + symnStartValue := datsize + for _, s := range data[symn] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + checkdatsize(ctxt, datsize, symn) + if ctxt.HeadType == objabi.Haix { + // Read-only symbols might be wrapped inside their outer + // symbol. + // XCOFF symbol table needs to know the size of + // these outer symbols. + xcoffUpdateOuterSize(ctxt, datsize-symnStartValue, symn) + } + } + sect.Length = uint64(datsize) - sect.Vaddr + + /* read-only ELF, Mach-O sections */ + for _, s := range data[sym.SELFROSECT] { + sect = addsection(ctxt.Arch, segro, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + sect.Length = uint64(datsize) - sect.Vaddr + } + checkdatsize(ctxt, datsize, sym.SELFROSECT) + + for _, s := range data[sym.SMACHOPLT] { + sect = addsection(ctxt.Arch, segro, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + sect.Length = uint64(datsize) - sect.Vaddr + } + checkdatsize(ctxt, datsize, sym.SMACHOPLT) + + // There is some data that are conceptually read-only but are written to by + // relocations. On GNU systems, we can arrange for the dynamic linker to + // mprotect sections after relocations are applied by giving them write + // permissions in the object file and calling them ".data.rel.ro.FOO". We + // divide the .rodata section between actual .rodata and .data.rel.ro.rodata, + // but for the other sections that this applies to, we just write a read-only + // .FOO section or a read-write .data.rel.ro.FOO section depending on the + // situation. + // TODO(mwhudson): It would make sense to do this more widely, but it makes + // the system linker segfault on darwin. + addrelrosection := func(suffix string) *sym.Section { + return addsection(ctxt.Arch, segro, suffix, 04) + } + + if ctxt.UseRelro() { + segrelro := &Segrelrodata + if ctxt.LinkMode == LinkExternal && ctxt.HeadType != objabi.Haix { + // Using a separate segment with an external + // linker results in some programs moving + // their data sections unexpectedly, which + // corrupts the moduledata. So we use the + // rodata segment and let the external linker + // sort out a rel.ro segment. + segrelro = segro + } else { + // Reset datsize for new segment. + datsize = 0 + } + + addrelrosection = func(suffix string) *sym.Section { + return addsection(ctxt.Arch, segrelro, ".data.rel.ro"+suffix, 06) + } + + /* data only written by relocations */ + sect = addrelrosection("") + + ctxt.Syms.Lookup("runtime.types", 0).Sect = sect + ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect + + for _, symnro := range sym.ReadOnly { + symn := sym.RelROMap[symnro] + align := dataMaxAlign[symn] + if sect.Align < align { + sect.Align = align + } + } + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + + for i, symnro := range sym.ReadOnly { + if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix { + // Skip forward so that no type + // reference uses a zero offset. + // This is unlikely but possible in small + // programs with no other read-only data. + datsize++ + } + + symn := sym.RelROMap[symnro] + symnStartValue := datsize + for _, s := range data[symn] { + datsize = aligndatsize(datsize, s) + if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect { + Errorf(s, "s.Outer (%s) in different section from s, %s != %s", s.Outer.Name, s.Outer.Sect.Name, sect.Name) + } + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + checkdatsize(ctxt, datsize, symn) + if ctxt.HeadType == objabi.Haix { + // Read-only symbols might be wrapped inside their outer + // symbol. + // XCOFF symbol table needs to know the size of + // these outer symbols. + xcoffUpdateOuterSize(ctxt, datsize-symnStartValue, symn) + } + } + + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* typelink */ + sect = addrelrosection(".typelink") + sect.Align = dataMaxAlign[sym.STYPELINK] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + typelink := ctxt.Syms.Lookup("runtime.typelink", 0) + typelink.Sect = sect + typelink.Type = sym.SRODATA + datsize += typelink.Size + checkdatsize(ctxt, datsize, sym.STYPELINK) + sect.Length = uint64(datsize) - sect.Vaddr + + /* itablink */ + sect = addrelrosection(".itablink") + sect.Align = dataMaxAlign[sym.SITABLINK] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + ctxt.Syms.Lookup("runtime.itablink", 0).Sect = sect + ctxt.Syms.Lookup("runtime.eitablink", 0).Sect = sect + for _, s := range data[sym.SITABLINK] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + checkdatsize(ctxt, datsize, sym.SITABLINK) + sect.Length = uint64(datsize) - sect.Vaddr + if ctxt.HeadType == objabi.Haix { + // Store .itablink size because its symbols are wrapped + // under an outer symbol: runtime.itablink. + xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SITABLINK) + } + + /* gosymtab */ + sect = addrelrosection(".gosymtab") + sect.Align = dataMaxAlign[sym.SSYMTAB] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + ctxt.Syms.Lookup("runtime.symtab", 0).Sect = sect + ctxt.Syms.Lookup("runtime.esymtab", 0).Sect = sect + for _, s := range data[sym.SSYMTAB] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + checkdatsize(ctxt, datsize, sym.SSYMTAB) + sect.Length = uint64(datsize) - sect.Vaddr + + /* gopclntab */ + sect = addrelrosection(".gopclntab") + sect.Align = dataMaxAlign[sym.SPCLNTAB] + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + ctxt.Syms.Lookup("runtime.pclntab", 0).Sect = sect + ctxt.Syms.Lookup("runtime.epclntab", 0).Sect = sect + for _, s := range data[sym.SPCLNTAB] { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + } + checkdatsize(ctxt, datsize, sym.SRODATA) + sect.Length = uint64(datsize) - sect.Vaddr + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if datsize != int64(uint32(datsize)) { + Errorf(nil, "read-only data segment too large: %d", datsize) + } + + for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { + datap = append(datap, data[symn]...) + } + + dwarfGenerateDebugSyms(ctxt) + + var i int + for ; i < len(dwarfp); i++ { + s := dwarfp[i] + if s.Type != sym.SDWARFSECT { + break + } + + sect = addsection(ctxt.Arch, &Segdwarf, s.Name, 04) + sect.Align = 1 + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + sect.Length = uint64(datsize) - sect.Vaddr + } + checkdatsize(ctxt, datsize, sym.SDWARFSECT) + + for i < len(dwarfp) { + curType := dwarfp[i].Type + var sect *sym.Section + switch curType { + case sym.SDWARFINFO: + sect = addsection(ctxt.Arch, &Segdwarf, ".debug_info", 04) + case sym.SDWARFRANGE: + sect = addsection(ctxt.Arch, &Segdwarf, ".debug_ranges", 04) + case sym.SDWARFLOC: + sect = addsection(ctxt.Arch, &Segdwarf, ".debug_loc", 04) + default: + // Error is unrecoverable, so panic. + panic(fmt.Sprintf("unknown DWARF section %v", curType)) + } + + sect.Align = 1 + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + for ; i < len(dwarfp); i++ { + s := dwarfp[i] + if s.Type != curType { + break + } + s.Sect = sect + s.Type = sym.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + s.Attr |= sym.AttrLocal + datsize += s.Size + + if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC { + // Update the size of .debug_loc for this symbol's + // package. + addDwsectCUSize(".debug_loc", s.File, uint64(s.Size)) + } + } + sect.Length = uint64(datsize) - sect.Vaddr + checkdatsize(ctxt, datsize, curType) + } + + /* number the sections */ + n := int32(1) + + for _, sect := range Segtext.Sections { + sect.Extnum = int16(n) + n++ + } + for _, sect := range Segrodata.Sections { + sect.Extnum = int16(n) + n++ + } + for _, sect := range Segrelrodata.Sections { + sect.Extnum = int16(n) + n++ + } + for _, sect := range Segdata.Sections { + sect.Extnum = int16(n) + n++ + } + for _, sect := range Segdwarf.Sections { + sect.Extnum = int16(n) + n++ + } +} + +func dodataSect(ctxt *Link, symn sym.SymKind, syms []*sym.Symbol) (result []*sym.Symbol, maxAlign int32) { + if ctxt.HeadType == objabi.Hdarwin { + // Some symbols may no longer belong in syms + // due to movement in machosymorder. + newSyms := make([]*sym.Symbol, 0, len(syms)) + for _, s := range syms { + if s.Type == symn { + newSyms = append(newSyms, s) + } + } + syms = newSyms + } + + var head, tail *sym.Symbol + symsSort := make([]dataSortKey, 0, len(syms)) + for _, s := range syms { + if s.Attr.OnList() { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + switch { + case s.Size < int64(len(s.P)): + Errorf(s, "initialize bounds (%d < %d)", s.Size, len(s.P)) + case s.Size < 0: + Errorf(s, "negative size (%d bytes)", s.Size) + case s.Size > cutoff: + Errorf(s, "symbol too large (%d bytes)", s.Size) + } + + // If the usually-special section-marker symbols are being laid + // out as regular symbols, put them either at the beginning or + // end of their section. + if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + switch s.Name { + case "runtime.text", "runtime.bss", "runtime.data", "runtime.types", "runtime.rodata": + head = s + continue + case "runtime.etext", "runtime.ebss", "runtime.edata", "runtime.etypes", "runtime.erodata": + tail = s + continue + } + } + + key := dataSortKey{ + size: s.Size, + name: s.Name, + sym: s, + } + + switch s.Type { + case sym.SELFGOT: + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type sym.SELFGOT, so in that case + // we skip size comparison and fall through to the name + // comparison (conveniently, .got sorts before .toc). + key.size = 0 + } + + symsSort = append(symsSort, key) + } + + sort.Sort(bySizeAndName(symsSort)) + + off := 0 + if head != nil { + syms[0] = head + off++ + } + for i, symSort := range symsSort { + syms[i+off] = symSort.sym + align := symalign(symSort.sym) + if maxAlign < align { + maxAlign = align + } + } + if tail != nil { + syms[len(syms)-1] = tail + } + + if ctxt.IsELF && symn == sym.SELFROSECT { + // Make .rela and .rela.plt contiguous, the ELF ABI requires this + // and Solaris actually cares. + reli, plti := -1, -1 + for i, s := range syms { + switch s.Name { + case ".rel.plt", ".rela.plt": + plti = i + case ".rel", ".rela": + reli = i + } + } + if reli >= 0 && plti >= 0 && plti != reli+1 { + var first, second int + if plti > reli { + first, second = reli, plti + } else { + first, second = plti, reli + } + rel, plt := syms[reli], syms[plti] + copy(syms[first+2:], syms[first+1:second]) + syms[first+0] = rel + syms[first+1] = plt + + // Make sure alignment doesn't introduce a gap. + // Setting the alignment explicitly prevents + // symalign from basing it on the size and + // getting it wrong. + rel.Align = int32(ctxt.Arch.RegSize) + plt.Align = int32(ctxt.Arch.RegSize) + } + } + + return syms, maxAlign +} + +// Add buildid to beginning of text segment, on non-ELF systems. +// Non-ELF binary formats are not always flexible enough to +// give us a place to put the Go build ID. On those systems, we put it +// at the very beginning of the text segment. +// This ``header'' is read by cmd/go. +func (ctxt *Link) textbuildid() { + if ctxt.IsELF || ctxt.BuildMode == BuildModePlugin || *flagBuildid == "" { + return + } + + s := ctxt.Syms.Lookup("go.buildid", 0) + s.Attr |= sym.AttrReachable + // The \xff is invalid UTF-8, meant to make it less likely + // to find one of these accidentally. + data := "\xff Go build ID: " + strconv.Quote(*flagBuildid) + "\n \xff" + s.Type = sym.STEXT + s.P = []byte(data) + s.Size = int64(len(s.P)) + + ctxt.Textp = append(ctxt.Textp, nil) + copy(ctxt.Textp[1:], ctxt.Textp) + ctxt.Textp[0] = s +} + +func (ctxt *Link) buildinfo() { + if ctxt.linkShared || ctxt.BuildMode == BuildModePlugin { + // -linkshared and -buildmode=plugin get confused + // about the relocations in go.buildinfo + // pointing at the other data sections. + // The version information is only available in executables. + return + } + + s := ctxt.Syms.Lookup(".go.buildinfo", 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SBUILDINFO + s.Align = 16 + // The \xff is invalid UTF-8, meant to make it less likely + // to find one of these accidentally. + const prefix = "\xff Go buildinf:" // 14 bytes, plus 2 data bytes filled in below + data := make([]byte, 32) + copy(data, prefix) + data[len(prefix)] = byte(ctxt.Arch.PtrSize) + data[len(prefix)+1] = 0 + if ctxt.Arch.ByteOrder == binary.BigEndian { + data[len(prefix)+1] = 1 + } + s.P = data + s.Size = int64(len(s.P)) + s1 := ctxt.Syms.Lookup("runtime.buildVersion", 0) + s2 := ctxt.Syms.Lookup("runtime.modinfo", 0) + s.R = []sym.Reloc{ + {Off: 16, Siz: uint8(ctxt.Arch.PtrSize), Type: objabi.R_ADDR, Sym: s1}, + {Off: 16 + int32(ctxt.Arch.PtrSize), Siz: uint8(ctxt.Arch.PtrSize), Type: objabi.R_ADDR, Sym: s2}, + } +} + +// assign addresses to text +func (ctxt *Link) textaddress() { + addsection(ctxt.Arch, &Segtext, ".text", 05) + + // Assign PCs in text segment. + // Could parallelize, by assigning to text + // and then letting threads copy down, but probably not worth it. + sect := Segtext.Sections[0] + + sect.Align = int32(Funcalign) + + text := ctxt.Syms.Lookup("runtime.text", 0) + text.Sect = sect + if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal { + // Setting runtime.text has a real symbol prevents ld to + // change its base address resulting in wrong offsets for + // reflect methods. + text.Align = sect.Align + text.Size = 0x8 + } + + if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + etext := ctxt.Syms.Lookup("runtime.etext", 0) + etext.Sect = sect + + ctxt.Textp = append(ctxt.Textp, etext, nil) + copy(ctxt.Textp[1:], ctxt.Textp) + ctxt.Textp[0] = text + } + + va := uint64(*FlagTextAddr) + n := 1 + sect.Vaddr = va + ntramps := 0 + for _, s := range ctxt.Textp { + sect, n, va = assignAddress(ctxt, sect, n, s, va, false) + + trampoline(ctxt, s) // resolve jumps, may add trampolines if jump too far + + // lay down trampolines after each function + for ; ntramps < len(ctxt.tramps); ntramps++ { + tramp := ctxt.tramps[ntramps] + if ctxt.HeadType == objabi.Haix && strings.HasPrefix(tramp.Name, "runtime.text.") { + // Already set in assignAddress + continue + } + sect, n, va = assignAddress(ctxt, sect, n, tramp, va, true) + } + } + + sect.Length = va - sect.Vaddr + ctxt.Syms.Lookup("runtime.etext", 0).Sect = sect + + // merge tramps into Textp, keeping Textp in address order + if ntramps != 0 { + newtextp := make([]*sym.Symbol, 0, len(ctxt.Textp)+ntramps) + i := 0 + for _, s := range ctxt.Textp { + for ; i < ntramps && ctxt.tramps[i].Value < s.Value; i++ { + newtextp = append(newtextp, ctxt.tramps[i]) + } + newtextp = append(newtextp, s) + } + newtextp = append(newtextp, ctxt.tramps[i:ntramps]...) + + ctxt.Textp = newtextp + } +} + +// assigns address for a text symbol, returns (possibly new) section, its number, and the address +// Note: once we have trampoline insertion support for external linking, this function +// will not need to create new text sections, and so no need to return sect and n. +func assignAddress(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) { + if thearch.AssignAddress != nil { + return thearch.AssignAddress(ctxt, sect, n, s, va, isTramp) + } + + s.Sect = sect + if s.Attr.SubSymbol() { + return sect, n, va + } + if s.Align != 0 { + va = uint64(Rnd(int64(va), int64(s.Align))) + } else { + va = uint64(Rnd(int64(va), int64(Funcalign))) + } + + funcsize := uint64(MINFUNC) // spacing required for findfunctab + if s.Size > MINFUNC { + funcsize = uint64(s.Size) + } + + if sect.Align < s.Align { + sect.Align = s.Align + } + + // On ppc64x a text section should not be larger than 2^26 bytes due to the size of + // call target offset field in the bl instruction. Splitting into smaller text + // sections smaller than this limit allows the GNU linker to modify the long calls + // appropriately. The limit allows for the space needed for tables inserted by the linker. + + // If this function doesn't fit in the current text section, then create a new one. + + // Only break at outermost syms. + + if ctxt.Arch.InFamily(sys.PPC64) && s.Outer == nil && ctxt.LinkMode == LinkExternal && va-sect.Vaddr+funcsize+maxSizeTrampolinesPPC64(s, isTramp) > 0x1c00000 { + // Set the length for the previous text section + sect.Length = va - sect.Vaddr + + // Create new section, set the starting Vaddr + sect = addsection(ctxt.Arch, &Segtext, ".text", 05) + sect.Vaddr = va + s.Sect = sect + + // Create a symbol for the start of the secondary text sections + ntext := ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0) + ntext.Sect = sect + if ctxt.HeadType == objabi.Haix { + // runtime.text.X must be a real symbol on AIX. + // Assign its address directly in order to be the + // first symbol of this new section. + ntext.Type = sym.STEXT + ntext.Size = int64(MINFUNC) + ntext.Attr |= sym.AttrReachable + ntext.Attr |= sym.AttrOnList + ctxt.tramps = append(ctxt.tramps, ntext) + + ntext.Value = int64(va) + va += uint64(ntext.Size) + + if s.Align != 0 { + va = uint64(Rnd(int64(va), int64(s.Align))) + } else { + va = uint64(Rnd(int64(va), int64(Funcalign))) + } + } + n++ + } + + s.Value = 0 + for sub := s; sub != nil; sub = sub.Sub { + sub.Value += int64(va) + } + + va += funcsize + + return sect, n, va +} + +// address assigns virtual addresses to all segments and sections and +// returns all segments in file order. +func (ctxt *Link) address() []*sym.Segment { + var order []*sym.Segment // Layout order + + va := uint64(*FlagTextAddr) + order = append(order, &Segtext) + Segtext.Rwx = 05 + Segtext.Vaddr = va + for _, s := range Segtext.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segtext.Length = va - uint64(*FlagTextAddr) + + if len(Segrodata.Sections) > 0 { + // align to page boundary so as not to mix + // rodata and executable text. + // + // Note: gold or GNU ld will reduce the size of the executable + // file by arranging for the relro segment to end at a page + // boundary, and overlap the end of the text segment with the + // start of the relro segment in the file. The PT_LOAD segments + // will be such that the last page of the text segment will be + // mapped twice, once r-x and once starting out rw- and, after + // relocation processing, changed to r--. + // + // Ideally the last page of the text segment would not be + // writable even for this short period. + va = uint64(Rnd(int64(va), int64(*FlagRound))) + + order = append(order, &Segrodata) + Segrodata.Rwx = 04 + Segrodata.Vaddr = va + for _, s := range Segrodata.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrodata.Length = va - Segrodata.Vaddr + } + if len(Segrelrodata.Sections) > 0 { + // align to page boundary so as not to mix + // rodata, rel-ro data, and executable text. + va = uint64(Rnd(int64(va), int64(*FlagRound))) + if ctxt.HeadType == objabi.Haix { + // Relro data are inside data segment on AIX. + va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE) + } + + order = append(order, &Segrelrodata) + Segrelrodata.Rwx = 06 + Segrelrodata.Vaddr = va + for _, s := range Segrelrodata.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrelrodata.Length = va - Segrelrodata.Vaddr + } + + va = uint64(Rnd(int64(va), int64(*FlagRound))) + if ctxt.HeadType == objabi.Haix && len(Segrelrodata.Sections) == 0 { + // Data sections are moved to an unreachable segment + // to ensure that they are position-independent. + // Already done if relro sections exist. + va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE) + } + order = append(order, &Segdata) + Segdata.Rwx = 06 + Segdata.Vaddr = va + var data *sym.Section + var noptr *sym.Section + var bss *sym.Section + var noptrbss *sym.Section + for i, s := range Segdata.Sections { + if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && s.Name == ".tbss" { + continue + } + vlen := int64(s.Length) + if i+1 < len(Segdata.Sections) && !((ctxt.IsELF || ctxt.HeadType == objabi.Haix) && Segdata.Sections[i+1].Name == ".tbss") { + vlen = int64(Segdata.Sections[i+1].Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + Segdata.Length = va - Segdata.Vaddr + if s.Name == ".data" { + data = s + } + if s.Name == ".noptrdata" { + noptr = s + } + if s.Name == ".bss" { + bss = s + } + if s.Name == ".noptrbss" { + noptrbss = s + } + } + + // Assign Segdata's Filelen omitting the BSS. We do this here + // simply because right now we know where the BSS starts. + Segdata.Filelen = bss.Vaddr - Segdata.Vaddr + + va = uint64(Rnd(int64(va), int64(*FlagRound))) + order = append(order, &Segdwarf) + Segdwarf.Rwx = 06 + Segdwarf.Vaddr = va + for i, s := range Segdwarf.Sections { + vlen := int64(s.Length) + if i+1 < len(Segdwarf.Sections) { + vlen = int64(Segdwarf.Sections[i+1].Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + if ctxt.HeadType == objabi.Hwindows { + va = uint64(Rnd(int64(va), PEFILEALIGN)) + } + Segdwarf.Length = va - Segdwarf.Vaddr + } + + var ( + text = Segtext.Sections[0] + rodata = ctxt.Syms.Lookup("runtime.rodata", 0).Sect + itablink = ctxt.Syms.Lookup("runtime.itablink", 0).Sect + symtab = ctxt.Syms.Lookup("runtime.symtab", 0).Sect + pclntab = ctxt.Syms.Lookup("runtime.pclntab", 0).Sect + types = ctxt.Syms.Lookup("runtime.types", 0).Sect + ) + lasttext := text + // Could be multiple .text sections + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + lasttext = sect + } + } + + for _, s := range datap { + if s.Sect != nil { + s.Value += int64(s.Sect.Vaddr) + } + for sub := s.Sub; sub != nil; sub = sub.Sub { + sub.Value += s.Value + } + } + + for _, s := range dwarfp { + if s.Sect != nil { + s.Value += int64(s.Sect.Vaddr) + } + for sub := s.Sub; sub != nil; sub = sub.Sub { + sub.Value += s.Value + } + } + + if ctxt.BuildMode == BuildModeShared { + s := ctxt.Syms.Lookup("go.link.abihashbytes", 0) + sectSym := ctxt.Syms.Lookup(".note.go.abihash", 0) + s.Sect = sectSym.Sect + s.Value = int64(sectSym.Sect.Vaddr + 16) + } + + ctxt.xdefine("runtime.text", sym.STEXT, int64(text.Vaddr)) + ctxt.xdefine("runtime.etext", sym.STEXT, int64(lasttext.Vaddr+lasttext.Length)) + + // If there are multiple text sections, create runtime.text.n for + // their section Vaddr, using n for index + n := 1 + for _, sect := range Segtext.Sections[1:] { + if sect.Name != ".text" { + break + } + symname := fmt.Sprintf("runtime.text.%d", n) + if ctxt.HeadType != objabi.Haix || ctxt.LinkMode != LinkExternal { + // Addresses are already set on AIX with external linker + // because these symbols are part of their sections. + ctxt.xdefine(symname, sym.STEXT, int64(sect.Vaddr)) + } + n++ + } + + ctxt.xdefine("runtime.rodata", sym.SRODATA, int64(rodata.Vaddr)) + ctxt.xdefine("runtime.erodata", sym.SRODATA, int64(rodata.Vaddr+rodata.Length)) + ctxt.xdefine("runtime.types", sym.SRODATA, int64(types.Vaddr)) + ctxt.xdefine("runtime.etypes", sym.SRODATA, int64(types.Vaddr+types.Length)) + ctxt.xdefine("runtime.itablink", sym.SRODATA, int64(itablink.Vaddr)) + ctxt.xdefine("runtime.eitablink", sym.SRODATA, int64(itablink.Vaddr+itablink.Length)) + + s := ctxt.Syms.Lookup("runtime.gcdata", 0) + s.Attr |= sym.AttrLocal + ctxt.xdefine("runtime.egcdata", sym.SRODATA, Symaddr(s)+s.Size) + ctxt.Syms.Lookup("runtime.egcdata", 0).Sect = s.Sect + + s = ctxt.Syms.Lookup("runtime.gcbss", 0) + s.Attr |= sym.AttrLocal + ctxt.xdefine("runtime.egcbss", sym.SRODATA, Symaddr(s)+s.Size) + ctxt.Syms.Lookup("runtime.egcbss", 0).Sect = s.Sect + + ctxt.xdefine("runtime.symtab", sym.SRODATA, int64(symtab.Vaddr)) + ctxt.xdefine("runtime.esymtab", sym.SRODATA, int64(symtab.Vaddr+symtab.Length)) + ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr)) + ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length)) + ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr)) + ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length)) + ctxt.xdefine("runtime.bss", sym.SBSS, int64(bss.Vaddr)) + ctxt.xdefine("runtime.ebss", sym.SBSS, int64(bss.Vaddr+bss.Length)) + ctxt.xdefine("runtime.data", sym.SDATA, int64(data.Vaddr)) + ctxt.xdefine("runtime.edata", sym.SDATA, int64(data.Vaddr+data.Length)) + ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr)) + ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) + ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length)) + + return order +} + +// layout assigns file offsets and lengths to the segments in order. +// Returns the file size containing all the segments. +func (ctxt *Link) layout(order []*sym.Segment) uint64 { + var prev *sym.Segment + for _, seg := range order { + if prev == nil { + seg.Fileoff = uint64(HEADR) + } else { + switch ctxt.HeadType { + default: + // Assuming the previous segment was + // aligned, the following rounding + // should ensure that this segment's + // VA ≡ Fileoff mod FlagRound. + seg.Fileoff = uint64(Rnd(int64(prev.Fileoff+prev.Filelen), int64(*FlagRound))) + if seg.Vaddr%uint64(*FlagRound) != seg.Fileoff%uint64(*FlagRound) { + Exitf("bad segment rounding (Vaddr=%#x Fileoff=%#x FlagRound=%#x)", seg.Vaddr, seg.Fileoff, *FlagRound) + } + case objabi.Hwindows: + seg.Fileoff = prev.Fileoff + uint64(Rnd(int64(prev.Filelen), PEFILEALIGN)) + case objabi.Hplan9: + seg.Fileoff = prev.Fileoff + prev.Filelen + } + } + if seg != &Segdata { + // Link.address already set Segdata.Filelen to + // account for BSS. + seg.Filelen = seg.Length + } + prev = seg + } + return prev.Fileoff + prev.Filelen +} + +// add a trampoline with symbol s (to be laid down after the current function) +func (ctxt *Link) AddTramp(s *sym.Symbol) { + s.Type = sym.STEXT + s.Attr |= sym.AttrReachable + s.Attr |= sym.AttrOnList + ctxt.tramps = append(ctxt.tramps, s) + if *FlagDebugTramp > 0 && ctxt.Debugvlog > 0 { + ctxt.Logf("trampoline %s inserted\n", s) + } +} + +// compressSyms compresses syms and returns the contents of the +// compressed section. If the section would get larger, it returns nil. +func compressSyms(ctxt *Link, syms []*sym.Symbol) []byte { + var total int64 + for _, sym := range syms { + total += sym.Size + } + + var buf bytes.Buffer + buf.Write([]byte("ZLIB")) + var sizeBytes [8]byte + binary.BigEndian.PutUint64(sizeBytes[:], uint64(total)) + buf.Write(sizeBytes[:]) + + // Using zlib.BestSpeed achieves very nearly the same + // compression levels of zlib.DefaultCompression, but takes + // substantially less time. This is important because DWARF + // compression can be a significant fraction of link time. + z, err := zlib.NewWriterLevel(&buf, zlib.BestSpeed) + if err != nil { + log.Fatalf("NewWriterLevel failed: %s", err) + } + for _, s := range syms { + // s.P may be read-only. Apply relocations in a + // temporary buffer, and immediately write it out. + oldP := s.P + wasReadOnly := s.Attr.ReadOnly() + if len(s.R) != 0 && wasReadOnly { + ctxt.relocbuf = append(ctxt.relocbuf[:0], s.P...) + s.P = ctxt.relocbuf + s.Attr.Set(sym.AttrReadOnly, false) + } + relocsym(ctxt, s) + if _, err := z.Write(s.P); err != nil { + log.Fatalf("compression failed: %s", err) + } + for i := s.Size - int64(len(s.P)); i > 0; { + b := zeros[:] + if i < int64(len(b)) { + b = b[:i] + } + n, err := z.Write(b) + if err != nil { + log.Fatalf("compression failed: %s", err) + } + i -= int64(n) + } + // Restore s.P if a temporary buffer was used. If compression + // is not beneficial, we'll go back to use the uncompressed + // contents, in which case we still need s.P. + if len(s.R) != 0 && wasReadOnly { + s.P = oldP + s.Attr.Set(sym.AttrReadOnly, wasReadOnly) + for i := range s.R { + s.R[i].Done = false + } + } + } + if err := z.Close(); err != nil { + log.Fatalf("compression failed: %s", err) + } + if int64(buf.Len()) >= total { + // Compression didn't save any space. + return nil + } + return buf.Bytes() +} diff --git a/src/cmd/oldlink/internal/ld/deadcode.go b/src/cmd/oldlink/internal/ld/deadcode.go new file mode 100644 index 0000000000..1bdac31f12 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/deadcode.go @@ -0,0 +1,408 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "fmt" + "strings" + "unicode" +) + +// deadcode marks all reachable symbols. +// +// The basis of the dead code elimination is a flood fill of symbols, +// following their relocations, beginning at *flagEntrySymbol. +// +// This flood fill is wrapped in logic for pruning unused methods. +// All methods are mentioned by relocations on their receiver's *rtype. +// These relocations are specially defined as R_METHODOFF by the compiler +// so we can detect and manipulated them here. +// +// There are three ways a method of a reachable type can be invoked: +// +// 1. direct call +// 2. through a reachable interface type +// 3. reflect.Value.Call, .Method, or reflect.Method.Func +// +// The first case is handled by the flood fill, a directly called method +// is marked as reachable. +// +// The second case is handled by decomposing all reachable interface +// types into method signatures. Each encountered method is compared +// against the interface method signatures, if it matches it is marked +// as reachable. This is extremely conservative, but easy and correct. +// +// The third case is handled by looking to see if any of: +// - reflect.Value.Call is reachable +// - reflect.Value.Method is reachable +// - reflect.Type.Method or MethodByName is called. +// If any of these happen, all bets are off and all exported methods +// of reachable types are marked reachable. +// +// Any unreached text symbols are removed from ctxt.Textp. +func deadcode(ctxt *Link) { + if ctxt.Debugvlog != 0 { + ctxt.Logf("deadcode\n") + } + + if *flagNewobj { + deadcode2(ctxt) + return + } + + d := &deadcodepass{ + ctxt: ctxt, + ifaceMethod: make(map[methodsig]bool), + } + + // First, flood fill any symbols directly reachable in the call + // graph from *flagEntrySymbol. Ignore all methods not directly called. + d.init() + d.flood() + + callSym := ctxt.Syms.ROLookup("reflect.Value.Call", sym.SymVerABIInternal) + methSym := ctxt.Syms.ROLookup("reflect.Value.Method", sym.SymVerABIInternal) + reflectSeen := false + + if ctxt.DynlinkingGo() { + // Exported methods may satisfy interfaces we don't know + // about yet when dynamically linking. + reflectSeen = true + } + + for { + if !reflectSeen { + if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) { + // Methods might be called via reflection. Give up on + // static analysis, mark all exported methods of + // all reachable types as reachable. + reflectSeen = true + } + } + + // Mark all methods that could satisfy a discovered + // interface as reachable. We recheck old marked interfaces + // as new types (with new methods) may have been discovered + // in the last pass. + var rem []methodref + for _, m := range d.markableMethods { + if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] { + d.markMethod(m) + } else { + rem = append(rem, m) + } + } + d.markableMethods = rem + + if len(d.markQueue) == 0 { + // No new work was discovered. Done. + break + } + d.flood() + } + + // Remove all remaining unreached R_METHODOFF relocations. + for _, m := range d.markableMethods { + for _, r := range m.r { + d.cleanupReloc(r) + } + } + + if ctxt.BuildMode != BuildModeShared { + // Keep a itablink if the symbol it points at is being kept. + // (When BuildModeShared, always keep itablinks.) + for _, s := range ctxt.Syms.Allsym { + if strings.HasPrefix(s.Name, "go.itablink.") { + s.Attr.Set(sym.AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable()) + } + } + } + + addToTextp(ctxt) +} + +func addToTextp(ctxt *Link) { + // Remove dead text but keep file information (z symbols). + textp := []*sym.Symbol{} + for _, s := range ctxt.Textp { + if s.Attr.Reachable() { + textp = append(textp, s) + } + } + + // Put reachable text symbols into Textp. + // do it in postorder so that packages are laid down in dependency order + // internal first, then everything else + ctxt.Library = postorder(ctxt.Library) + for _, doInternal := range [2]bool{true, false} { + for _, lib := range ctxt.Library { + if isRuntimeDepPkg(lib.Pkg) != doInternal { + continue + } + libtextp := lib.Textp[:0] + for _, s := range lib.Textp { + if s.Attr.Reachable() { + textp = append(textp, s) + libtextp = append(libtextp, s) + if s.Unit != nil { + s.Unit.Textp = append(s.Unit.Textp, s) + } + } + } + for _, s := range lib.DupTextSyms { + if s.Attr.Reachable() && !s.Attr.OnList() { + textp = append(textp, s) + libtextp = append(libtextp, s) + if s.Unit != nil { + s.Unit.Textp = append(s.Unit.Textp, s) + } + s.Attr |= sym.AttrOnList + // dupok symbols may be defined in multiple packages. its + // associated package is chosen sort of arbitrarily (the + // first containing package that the linker loads). canonicalize + // it here to the package with which it will be laid down + // in text. + s.File = objabi.PathToPrefix(lib.Pkg) + } + } + lib.Textp = libtextp + } + } + ctxt.Textp = textp + + if len(ctxt.Shlibs) > 0 { + // We might have overwritten some functions above (this tends to happen for the + // autogenerated type equality/hashing functions) and we don't want to generated + // pcln table entries for these any more so remove them from Textp. + textp := make([]*sym.Symbol, 0, len(ctxt.Textp)) + for _, s := range ctxt.Textp { + if s.Type != sym.SDYNIMPORT { + textp = append(textp, s) + } + } + ctxt.Textp = textp + } +} + +// methodref holds the relocations from a receiver type symbol to its +// method. There are three relocations, one for each of the fields in +// the reflect.method struct: mtyp, ifn, and tfn. +type methodref struct { + m methodsig + src *sym.Symbol // receiver type symbol + r [3]*sym.Reloc // R_METHODOFF relocations to fields of runtime.method +} + +func (m methodref) ifn() *sym.Symbol { return m.r[1].Sym } + +func (m methodref) isExported() bool { + for _, r := range m.m { + return unicode.IsUpper(r) + } + panic("methodref has no signature") +} + +// deadcodepass holds state for the deadcode flood fill. +type deadcodepass struct { + ctxt *Link + markQueue []*sym.Symbol // symbols to flood fill in next pass + ifaceMethod map[methodsig]bool // methods declared in reached interfaces + markableMethods []methodref // methods of reached types + reflectMethod bool +} + +func (d *deadcodepass) cleanupReloc(r *sym.Reloc) { + if r.Sym.Attr.Reachable() { + r.Type = objabi.R_ADDROFF + } else { + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("removing method %s\n", r.Sym.Name) + } + r.Sym = nil + r.Siz = 0 + } +} + +// mark appends a symbol to the mark queue for flood filling. +func (d *deadcodepass) mark(s, parent *sym.Symbol) { + if s == nil || s.Attr.Reachable() { + return + } + if s.Attr.ReflectMethod() { + d.reflectMethod = true + } + if *flagDumpDep { + p := "_" + if parent != nil { + p = parent.Name + } + fmt.Printf("%s -> %s\n", p, s.Name) + } + s.Attr |= sym.AttrReachable + if d.ctxt.Reachparent != nil { + d.ctxt.Reachparent[s] = parent + } + d.markQueue = append(d.markQueue, s) +} + +// markMethod marks a method as reachable. +func (d *deadcodepass) markMethod(m methodref) { + for _, r := range m.r { + d.mark(r.Sym, m.src) + r.Type = objabi.R_ADDROFF + } +} + +// init marks all initial symbols as reachable. +// In a typical binary, this is *flagEntrySymbol. +func (d *deadcodepass) init() { + var names []string + + if d.ctxt.BuildMode == BuildModeShared { + // Mark all symbols defined in this library as reachable when + // building a shared library. + for _, s := range d.ctxt.Syms.Allsym { + if s.Type != 0 && s.Type != sym.SDYNIMPORT { + d.mark(s, nil) + } + } + } else { + // In a normal binary, start at main.main and the init + // functions and mark what is reachable from there. + + if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + names = append(names, "main.main", "main..inittask") + } else { + // The external linker refers main symbol directly. + if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 { + *flagEntrySymbol = "_main" + } else { + *flagEntrySymbol = "main" + } + } + names = append(names, *flagEntrySymbol) + if d.ctxt.BuildMode == BuildModePlugin { + names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs") + + // We don't keep the go.plugin.exports symbol, + // but we do keep the symbols it refers to. + exports := d.ctxt.Syms.ROLookup("go.plugin.exports", 0) + if exports != nil { + for i := range exports.R { + d.mark(exports.R[i].Sym, nil) + } + } + } + } + for _, s := range dynexp { + d.mark(s, nil) + } + } + + for _, name := range names { + // Mark symbol as a data/ABI0 symbol. + d.mark(d.ctxt.Syms.ROLookup(name, 0), nil) + // Also mark any Go functions (internal ABI). + d.mark(d.ctxt.Syms.ROLookup(name, sym.SymVerABIInternal), nil) + } +} + +// flood fills symbols reachable from the markQueue symbols. +// As it goes, it collects methodref and interface method declarations. +func (d *deadcodepass) flood() { + for len(d.markQueue) > 0 { + s := d.markQueue[0] + d.markQueue = d.markQueue[1:] + if s.Type == sym.STEXT { + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("marktext %s\n", s.Name) + } + } + + if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' { + if len(s.P) == 0 { + // Probably a bug. The undefined symbol check + // later will give a better error than deadcode. + continue + } + if decodetypeKind(d.ctxt.Arch, s.P)&kindMask == kindInterface { + for _, sig := range decodeIfaceMethods(d.ctxt.Arch, s) { + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("reached iface method: %s\n", sig) + } + d.ifaceMethod[sig] = true + } + } + } + + mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype + var methods []methodref + for i := range s.R { + r := &s.R[i] + if r.Sym == nil { + continue + } + if r.Type == objabi.R_WEAKADDROFF { + // An R_WEAKADDROFF relocation is not reason + // enough to mark the pointed-to symbol as + // reachable. + continue + } + if r.Sym.Type == sym.SABIALIAS { + // Patch this relocation through the + // ABI alias before marking. + r.Sym = resolveABIAlias(r.Sym) + } + if r.Type != objabi.R_METHODOFF { + d.mark(r.Sym, s) + continue + } + // Collect rtype pointers to methods for + // later processing in deadcode. + if mpos == 0 { + m := methodref{src: s} + m.r[0] = r + methods = append(methods, m) + } else { + methods[len(methods)-1].r[mpos] = r + } + mpos++ + if mpos == len(methodref{}.r) { + mpos = 0 + } + } + if len(methods) > 0 { + // Decode runtime type information for type methods + // to help work out which methods can be called + // dynamically via interfaces. + methodsigs := decodetypeMethods(d.ctxt.Arch, s) + if len(methods) != len(methodsigs) { + panic(fmt.Sprintf("%q has %d method relocations for %d methods", s.Name, len(methods), len(methodsigs))) + } + for i, m := range methodsigs { + name := string(m) + name = name[:strings.Index(name, "(")] + if !strings.HasSuffix(methods[i].ifn().Name, name) { + panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, methods[i].ifn().Name, name)) + } + methods[i].m = m + } + d.markableMethods = append(d.markableMethods, methods...) + } + + if s.FuncInfo != nil { + for i := range s.FuncInfo.Funcdata { + d.mark(s.FuncInfo.Funcdata[i], s) + } + } + d.mark(s.Gotype, s) + d.mark(s.Sub, s) + d.mark(s.Outer, s) + } +} diff --git a/src/cmd/oldlink/internal/ld/deadcode2.go b/src/cmd/oldlink/internal/ld/deadcode2.go new file mode 100644 index 0000000000..82bfd60a47 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/deadcode2.go @@ -0,0 +1,441 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "cmd/internal/dwarf" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/loader" + "cmd/oldlink/internal/sym" + "container/heap" + "fmt" + "unicode" +) + +var _ = fmt.Print + +type workQueue []loader.Sym + +// Implement container/heap.Interface. +func (q *workQueue) Len() int { return len(*q) } +func (q *workQueue) Less(i, j int) bool { return (*q)[i] < (*q)[j] } +func (q *workQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] } +func (q *workQueue) Push(i interface{}) { *q = append(*q, i.(loader.Sym)) } +func (q *workQueue) Pop() interface{} { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i } + +// Functions for deadcode pass to use. +// Deadcode pass should call push/pop, not Push/Pop. +func (q *workQueue) push(i loader.Sym) { heap.Push(q, i) } +func (q *workQueue) pop() loader.Sym { return heap.Pop(q).(loader.Sym) } +func (q *workQueue) empty() bool { return len(*q) == 0 } + +type deadcodePass2 struct { + ctxt *Link + ldr *loader.Loader + wq workQueue + rtmp []loader.Reloc + + ifaceMethod map[methodsig]bool // methods declared in reached interfaces + markableMethods []methodref2 // methods of reached types + reflectSeen bool // whether we have seen a reflect method call +} + +func (d *deadcodePass2) init() { + d.ldr.InitReachable() + d.ifaceMethod = make(map[methodsig]bool) + if d.ctxt.Reachparent != nil { + d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym()) + } + heap.Init(&d.wq) + + if d.ctxt.BuildMode == BuildModeShared { + // Mark all symbols defined in this library as reachable when + // building a shared library. + n := d.ldr.NDef() + for i := 1; i < n; i++ { + s := loader.Sym(i) + if !d.ldr.IsDup(s) { + d.mark(s, 0) + } + } + return + } + + var names []string + + // In a normal binary, start at main.main and the init + // functions and mark what is reachable from there. + if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + names = append(names, "main.main", "main..inittask") + } else { + // The external linker refers main symbol directly. + if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 { + *flagEntrySymbol = "_main" + } else { + *flagEntrySymbol = "main" + } + } + names = append(names, *flagEntrySymbol) + if d.ctxt.BuildMode == BuildModePlugin { + names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs") + + // We don't keep the go.plugin.exports symbol, + // but we do keep the symbols it refers to. + exportsIdx := d.ldr.Lookup("go.plugin.exports", 0) + if exportsIdx != 0 { + d.ReadRelocs(exportsIdx) + for i := 0; i < len(d.rtmp); i++ { + d.mark(d.rtmp[i].Sym, 0) + } + } + } + } + + dynexpMap := d.ctxt.cgo_export_dynamic + if d.ctxt.LinkMode == LinkExternal { + dynexpMap = d.ctxt.cgo_export_static + } + for exp := range dynexpMap { + names = append(names, exp) + } + + // DWARF constant DIE symbols are not referenced, but needed by + // the dwarf pass. + if !*FlagW { + for _, lib := range d.ctxt.Library { + names = append(names, dwarf.ConstInfoPrefix+lib.Pkg) + } + } + + for _, name := range names { + // Mark symbol as a data/ABI0 symbol. + d.mark(d.ldr.Lookup(name, 0), 0) + // Also mark any Go functions (internal ABI). + d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal), 0) + } +} + +func (d *deadcodePass2) flood() { + symRelocs := []loader.Reloc{} + auxSyms := []loader.Sym{} + for !d.wq.empty() { + symIdx := d.wq.pop() + + d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx) + + relocs := d.ldr.Relocs(symIdx) + symRelocs = relocs.ReadAll(symRelocs) + + if d.ldr.IsGoType(symIdx) { + p := d.ldr.Data(symIdx) + if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface { + for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) { + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("reached iface method: %s\n", sig) + } + d.ifaceMethod[sig] = true + } + } + } + + var methods []methodref2 + for i := 0; i < relocs.Count; i++ { + r := symRelocs[i] + if r.Type == objabi.R_WEAKADDROFF { + continue + } + if r.Type == objabi.R_METHODOFF { + if i+2 >= relocs.Count { + panic("expect three consecutive R_METHODOFF relocs") + } + methods = append(methods, methodref2{src: symIdx, r: i}) + i += 2 + continue + } + if r.Type == objabi.R_USETYPE { + // type symbol used for DWARF. we need to load the symbol but it may not + // be otherwise reachable in the program. + // do nothing for now as we still load all type symbols. + continue + } + d.mark(r.Sym, symIdx) + } + auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms) + for i := 0; i < len(auxSyms); i++ { + d.mark(auxSyms[i], symIdx) + } + // Some host object symbols have an outer object, which acts like a + // "carrier" symbol, or it holds all the symbols for a particular + // section. We need to mark all "referenced" symbols from that carrier, + // so we make sure we're pulling in all outer symbols, and their sub + // symbols. This is not ideal, and these carrier/section symbols could + // be removed. + d.mark(d.ldr.OuterSym(symIdx), symIdx) + d.mark(d.ldr.SubSym(symIdx), symIdx) + + if len(methods) != 0 { + // Decode runtime type information for type methods + // to help work out which methods can be called + // dynamically via interfaces. + methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) + if len(methods) != len(methodsigs) { + panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs))) + } + for i, m := range methodsigs { + methods[i].m = m + } + d.markableMethods = append(d.markableMethods, methods...) + } + } +} + +func (d *deadcodePass2) mark(symIdx, parent loader.Sym) { + if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) { + d.wq.push(symIdx) + d.ldr.Reachable.Set(symIdx) + if d.ctxt.Reachparent != nil { + d.ldr.Reachparent[symIdx] = parent + } + if *flagDumpDep { + to := d.ldr.SymName(symIdx) + if to != "" { + from := "_" + if parent != 0 { + from = d.ldr.SymName(parent) + } + fmt.Printf("%s -> %s\n", from, to) + } + } + } +} + +func (d *deadcodePass2) markMethod(m methodref2) { + d.ReadRelocs(m.src) + d.mark(d.rtmp[m.r].Sym, m.src) + d.mark(d.rtmp[m.r+1].Sym, m.src) + d.mark(d.rtmp[m.r+2].Sym, m.src) +} + +func deadcode2(ctxt *Link) { + ldr := ctxt.loader + d := deadcodePass2{ctxt: ctxt, ldr: ldr} + d.init() + d.flood() + + callSym := ldr.Lookup("reflect.Value.Call", sym.SymVerABIInternal) + methSym := ldr.Lookup("reflect.Value.Method", sym.SymVerABIInternal) + if ctxt.DynlinkingGo() { + // Exported methods may satisfy interfaces we don't know + // about yet when dynamically linking. + d.reflectSeen = true + } + + for { + // Methods might be called via reflection. Give up on + // static analysis, mark all exported methods of + // all reachable types as reachable. + d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.Reachable.Has(callSym)) || (methSym != 0 && ldr.Reachable.Has(methSym)) + + // Mark all methods that could satisfy a discovered + // interface as reachable. We recheck old marked interfaces + // as new types (with new methods) may have been discovered + // in the last pass. + rem := d.markableMethods[:0] + for _, m := range d.markableMethods { + if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] { + d.markMethod(m) + } else { + rem = append(rem, m) + } + } + d.markableMethods = rem + + if d.wq.empty() { + // No new work was discovered. Done. + break + } + d.flood() + } + + n := ldr.NSym() + + if ctxt.BuildMode != BuildModeShared { + // Keep a itablink if the symbol it points at is being kept. + // (When BuildModeShared, always keep itablinks.) + for i := 1; i < n; i++ { + s := loader.Sym(i) + if ldr.IsItabLink(s) { + relocs := ldr.Relocs(s) + if relocs.Count > 0 && ldr.Reachable.Has(relocs.At(0).Sym) { + ldr.Reachable.Set(s) + } + } + } + } +} + +// methodref2 holds the relocations from a receiver type symbol to its +// method. There are three relocations, one for each of the fields in +// the reflect.method struct: mtyp, ifn, and tfn. +type methodref2 struct { + m methodsig + src loader.Sym // receiver type symbol + r int // the index of R_METHODOFF relocations +} + +func (m methodref2) isExported() bool { + for _, r := range m.m { + return unicode.IsUpper(r) + } + panic("methodref has no signature") +} + +// decodeMethodSig2 decodes an array of method signature information. +// Each element of the array is size bytes. The first 4 bytes is a +// nameOff for the method name, and the next 4 bytes is a typeOff for +// the function type. +// +// Conveniently this is the layout of both runtime.method and runtime.imethod. +func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, off, size, count int) []methodsig { + var buf bytes.Buffer + var methods []methodsig + for i := 0; i < count; i++ { + buf.WriteString(decodetypeName2(ldr, symIdx, symRelocs, off)) + mtypSym := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off+4)) + // FIXME: add some sort of caching here, since we may see some of the + // same symbols over time for param types. + d.ReadRelocs(mtypSym) + mp := ldr.Data(mtypSym) + + buf.WriteRune('(') + inCount := decodetypeFuncInCount(arch, mp) + for i := 0; i < inCount; i++ { + if i > 0 { + buf.WriteString(", ") + } + a := d.decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i) + buf.WriteString(ldr.SymName(a)) + } + buf.WriteString(") (") + outCount := decodetypeFuncOutCount(arch, mp) + for i := 0; i < outCount; i++ { + if i > 0 { + buf.WriteString(", ") + } + a := d.decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i) + buf.WriteString(ldr.SymName(a)) + } + buf.WriteRune(')') + + off += size + methods = append(methods, methodsig(buf.String())) + buf.Reset() + } + return methods +} + +func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig { + p := ldr.Data(symIdx) + if decodetypeKind(arch, p)&kindMask != kindInterface { + panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx))) + } + rel := decodeReloc2(ldr, symIdx, symRelocs, int32(commonsize(arch)+arch.PtrSize)) + if rel.Sym == 0 { + return nil + } + if rel.Sym != symIdx { + panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx))) + } + off := int(rel.Add) // array of reflect.imethod values + numMethods := int(decodetypeIfaceMethodCount(arch, p)) + sizeofIMethod := 4 + 4 + return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofIMethod, numMethods) +} + +func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig { + p := ldr.Data(symIdx) + if !decodetypeHasUncommon(arch, p) { + panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx))) + } + off := commonsize(arch) // reflect.rtype + switch decodetypeKind(arch, p) & kindMask { + case kindStruct: // reflect.structType + off += 4 * arch.PtrSize + case kindPtr: // reflect.ptrType + off += arch.PtrSize + case kindFunc: // reflect.funcType + off += arch.PtrSize // 4 bytes, pointer aligned + case kindSlice: // reflect.sliceType + off += arch.PtrSize + case kindArray: // reflect.arrayType + off += 3 * arch.PtrSize + case kindChan: // reflect.chanType + off += 2 * arch.PtrSize + case kindMap: // reflect.mapType + off += 4*arch.PtrSize + 8 + case kindInterface: // reflect.interfaceType + off += 3 * arch.PtrSize + default: + // just Sizeof(rtype) + } + + mcount := int(decodeInuxi(arch, p[off+4:], 2)) + moff := int(decodeInuxi(arch, p[off+4+2+2:], 4)) + off += moff // offset to array of reflect.method values + const sizeofMethod = 4 * 4 // sizeof reflect.method in program + return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofMethod, mcount) +} + +func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Reloc { + for j := 0; j < len(symRelocs); j++ { + rel := symRelocs[j] + if rel.Off == off { + return rel + } + } + return loader.Reloc{} +} + +func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym { + return decodeReloc2(ldr, symIdx, symRelocs, off).Sym +} + +// decodetypeName2 decodes the name from a reflect.name. +func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string { + r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off)) + if r == 0 { + return "" + } + + data := ldr.Data(r) + namelen := int(uint16(data[1])<<8 | uint16(data[2])) + return string(data[3 : 3+namelen]) +} + +func (d *deadcodePass2) decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym { + uadd := commonsize(arch) + 4 + if arch.PtrSize == 8 { + uadd += 4 + } + if decodetypeHasUncommon(arch, ldr.Data(symIdx)) { + uadd += uncommonSize() + } + return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize)) +} + +func (d *deadcodePass2) decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym { + return d.decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx))) +} + +// readRelocs reads the relocations for the specified symbol into the +// deadcode relocs work array. Use with care, since the work array +// is a singleton. +func (d *deadcodePass2) ReadRelocs(symIdx loader.Sym) { + relocs := d.ldr.Relocs(symIdx) + d.rtmp = relocs.ReadAll(d.rtmp) +} diff --git a/src/cmd/oldlink/internal/ld/decodesym.go b/src/cmd/oldlink/internal/ld/decodesym.go new file mode 100644 index 0000000000..0676e94e2c --- /dev/null +++ b/src/cmd/oldlink/internal/ld/decodesym.go @@ -0,0 +1,374 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "debug/elf" + "fmt" +) + +// Decoding the type.* symbols. This has to be in sync with +// ../../runtime/type.go, or more specifically, with what +// cmd/compile/internal/gc/reflect.go stuffs in these. + +// tflag is documented in reflect/type.go. +// +// tflag values must be kept in sync with copies in: +// cmd/compile/internal/gc/reflect.go +// cmd/oldlink/internal/ld/decodesym.go +// reflect/type.go +// runtime/type.go +const ( + tflagUncommon = 1 << 0 + tflagExtraStar = 1 << 1 +) + +func decodeReloc(s *sym.Symbol, off int32) *sym.Reloc { + for i := range s.R { + if s.R[i].Off == off { + return &s.R[i] + } + } + return nil +} + +func decodeRelocSym(s *sym.Symbol, off int32) *sym.Symbol { + r := decodeReloc(s, off) + if r == nil { + return nil + } + return r.Sym +} + +func decodeInuxi(arch *sys.Arch, p []byte, sz int) uint64 { + switch sz { + case 2: + return uint64(arch.ByteOrder.Uint16(p)) + case 4: + return uint64(arch.ByteOrder.Uint32(p)) + case 8: + return arch.ByteOrder.Uint64(p) + default: + Exitf("dwarf: decode inuxi %d", sz) + panic("unreachable") + } +} + +func commonsize(arch *sys.Arch) int { return 4*arch.PtrSize + 8 + 8 } // runtime._type +func structfieldSize(arch *sys.Arch) int { return 3 * arch.PtrSize } // runtime.structfield +func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype + +// Type.commonType.kind +func decodetypeKind(arch *sys.Arch, p []byte) uint8 { + return p[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f +} + +// Type.commonType.kind +func decodetypeUsegcprog(arch *sys.Arch, p []byte) uint8 { + return p[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f +} + +// Type.commonType.size +func decodetypeSize(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p, arch.PtrSize)) // 0x8 / 0x10 +} + +// Type.commonType.ptrdata +func decodetypePtrdata(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10 +} + +// Type.commonType.tflag +func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool { + return p[2*arch.PtrSize+4]&tflagUncommon != 0 +} + +// Find the elf.Section of a given shared library that contains a given address. +func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section { + for _, shlib := range ctxt.Shlibs { + if shlib.Path == path { + for _, sect := range shlib.File.Sections { + if sect.Addr <= addr && addr <= sect.Addr+sect.Size { + return sect + } + } + } + } + return nil +} + +// Type.commonType.gc +func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte { + if s.Type == sym.SDYNIMPORT { + addr := decodetypeGcprogShlib(ctxt, s) + sect := findShlibSection(ctxt, s.File, addr) + if sect != nil { + // A gcprog is a 4-byte uint32 indicating length, followed by + // the actual program. + progsize := make([]byte, 4) + sect.ReadAt(progsize, int64(addr-sect.Addr)) + progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize)) + sect.ReadAt(progbytes, int64(addr-sect.Addr+4)) + return append(progsize, progbytes...) + } + Exitf("cannot find gcprog for %s", s.Name) + return nil + } + return decodeRelocSym(s, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize)).P +} + +func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 { + if ctxt.Arch.Family == sys.ARM64 { + for _, shlib := range ctxt.Shlibs { + if shlib.Path == s.File { + return shlib.gcdataAddresses[s] + } + } + return 0 + } + return decodeInuxi(ctxt.Arch, s.P[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize) +} + +func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte { + if s.Type == sym.SDYNIMPORT { + addr := decodetypeGcprogShlib(ctxt, s) + ptrdata := decodetypePtrdata(ctxt.Arch, s.P) + sect := findShlibSection(ctxt, s.File, addr) + if sect != nil { + r := make([]byte, ptrdata/int64(ctxt.Arch.PtrSize)) + sect.ReadAt(r, int64(addr-sect.Addr)) + return r + } + Exitf("cannot find gcmask for %s", s.Name) + return nil + } + mask := decodeRelocSym(s, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize)) + return mask.P +} + +// Type.ArrayType.elem and Type.SliceType.Elem +func decodetypeArrayElem(arch *sys.Arch, s *sym.Symbol) *sym.Symbol { + return decodeRelocSym(s, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeArrayLen(arch *sys.Arch, s *sym.Symbol) int64 { + return int64(decodeInuxi(arch, s.P[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +// Type.PtrType.elem +func decodetypePtrElem(arch *sys.Arch, s *sym.Symbol) *sym.Symbol { + return decodeRelocSym(s, int32(commonsize(arch))) // 0x1c / 0x30 +} + +// Type.MapType.key, elem +func decodetypeMapKey(arch *sys.Arch, s *sym.Symbol) *sym.Symbol { + return decodeRelocSym(s, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeMapValue(arch *sys.Arch, s *sym.Symbol) *sym.Symbol { + return decodeRelocSym(s, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38 +} + +// Type.ChanType.elem +func decodetypeChanElem(arch *sys.Arch, s *sym.Symbol) *sym.Symbol { + return decodeRelocSym(s, int32(commonsize(arch))) // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +func decodetypeFuncDotdotdot(arch *sys.Arch, p []byte) bool { + return uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2))&(1<<15) != 0 +} + +// Type.FuncType.inCount +func decodetypeFuncInCount(arch *sys.Arch, p []byte) int { + return int(decodeInuxi(arch, p[commonsize(arch):], 2)) +} + +func decodetypeFuncOutCount(arch *sys.Arch, p []byte) int { + return int(uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2)) & (1<<15 - 1)) +} + +func decodetypeFuncInType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol { + uadd := commonsize(arch) + 4 + if arch.PtrSize == 8 { + uadd += 4 + } + if decodetypeHasUncommon(arch, s.P) { + uadd += uncommonSize() + } + return decodeRelocSym(s, int32(uadd+i*arch.PtrSize)) +} + +func decodetypeFuncOutType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol { + return decodetypeFuncInType(arch, s, i+decodetypeFuncInCount(arch, s.P)) +} + +// Type.StructType.fields.Slice::length +func decodetypeStructFieldCount(arch *sys.Arch, s *sym.Symbol) int { + return int(decodeInuxi(arch, s.P[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +func decodetypeStructFieldArrayOff(arch *sys.Arch, s *sym.Symbol, i int) int { + off := commonsize(arch) + 4*arch.PtrSize + if decodetypeHasUncommon(arch, s.P) { + off += uncommonSize() + } + off += i * structfieldSize(arch) + return off +} + +// decodetypeStr returns the contents of an rtype's str field (a nameOff). +func decodetypeStr(arch *sys.Arch, s *sym.Symbol) string { + str := decodetypeName(s, 4*arch.PtrSize+8) + if s.P[2*arch.PtrSize+4]&tflagExtraStar != 0 { + return str[1:] + } + return str +} + +// decodetypeName decodes the name from a reflect.name. +func decodetypeName(s *sym.Symbol, off int) string { + r := decodeReloc(s, int32(off)) + if r == nil { + return "" + } + + data := r.Sym.P + namelen := int(uint16(data[1])<<8 | uint16(data[2])) + return string(data[3 : 3+namelen]) +} + +func decodetypeStructFieldName(arch *sys.Arch, s *sym.Symbol, i int) string { + off := decodetypeStructFieldArrayOff(arch, s, i) + return decodetypeName(s, off) +} + +func decodetypeStructFieldType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol { + off := decodetypeStructFieldArrayOff(arch, s, i) + return decodeRelocSym(s, int32(off+arch.PtrSize)) +} + +func decodetypeStructFieldOffs(arch *sys.Arch, s *sym.Symbol, i int) int64 { + return decodetypeStructFieldOffsAnon(arch, s, i) >> 1 +} + +func decodetypeStructFieldOffsAnon(arch *sys.Arch, s *sym.Symbol, i int) int64 { + off := decodetypeStructFieldArrayOff(arch, s, i) + return int64(decodeInuxi(arch, s.P[off+2*arch.PtrSize:], arch.PtrSize)) +} + +// InterfaceType.methods.length +func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +// methodsig is a fully qualified typed method signature, like +// "Visit(type.go/ast.Node) (type.go/ast.Visitor)". +type methodsig string + +// Matches runtime/typekind.go and reflect.Kind. +const ( + kindArray = 17 + kindChan = 18 + kindFunc = 19 + kindInterface = 20 + kindMap = 21 + kindPtr = 22 + kindSlice = 23 + kindStruct = 25 + kindMask = (1 << 5) - 1 +) + +// decodeMethodSig decodes an array of method signature information. +// Each element of the array is size bytes. The first 4 bytes is a +// nameOff for the method name, and the next 4 bytes is a typeOff for +// the function type. +// +// Conveniently this is the layout of both runtime.method and runtime.imethod. +func decodeMethodSig(arch *sys.Arch, s *sym.Symbol, off, size, count int) []methodsig { + var buf bytes.Buffer + var methods []methodsig + for i := 0; i < count; i++ { + buf.WriteString(decodetypeName(s, off)) + mtypSym := decodeRelocSym(s, int32(off+4)) + + buf.WriteRune('(') + inCount := decodetypeFuncInCount(arch, mtypSym.P) + for i := 0; i < inCount; i++ { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(decodetypeFuncInType(arch, mtypSym, i).Name) + } + buf.WriteString(") (") + outCount := decodetypeFuncOutCount(arch, mtypSym.P) + for i := 0; i < outCount; i++ { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(decodetypeFuncOutType(arch, mtypSym, i).Name) + } + buf.WriteRune(')') + + off += size + methods = append(methods, methodsig(buf.String())) + buf.Reset() + } + return methods +} + +func decodeIfaceMethods(arch *sys.Arch, s *sym.Symbol) []methodsig { + if decodetypeKind(arch, s.P)&kindMask != kindInterface { + panic(fmt.Sprintf("symbol %q is not an interface", s.Name)) + } + r := decodeReloc(s, int32(commonsize(arch)+arch.PtrSize)) + if r == nil { + return nil + } + if r.Sym != s { + panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", s.Name)) + } + off := int(r.Add) // array of reflect.imethod values + numMethods := int(decodetypeIfaceMethodCount(arch, s.P)) + sizeofIMethod := 4 + 4 + return decodeMethodSig(arch, s, off, sizeofIMethod, numMethods) +} + +func decodetypeMethods(arch *sys.Arch, s *sym.Symbol) []methodsig { + if !decodetypeHasUncommon(arch, s.P) { + panic(fmt.Sprintf("no methods on %q", s.Name)) + } + off := commonsize(arch) // reflect.rtype + switch decodetypeKind(arch, s.P) & kindMask { + case kindStruct: // reflect.structType + off += 4 * arch.PtrSize + case kindPtr: // reflect.ptrType + off += arch.PtrSize + case kindFunc: // reflect.funcType + off += arch.PtrSize // 4 bytes, pointer aligned + case kindSlice: // reflect.sliceType + off += arch.PtrSize + case kindArray: // reflect.arrayType + off += 3 * arch.PtrSize + case kindChan: // reflect.chanType + off += 2 * arch.PtrSize + case kindMap: // reflect.mapType + off += 4*arch.PtrSize + 8 + case kindInterface: // reflect.interfaceType + off += 3 * arch.PtrSize + default: + // just Sizeof(rtype) + } + + mcount := int(decodeInuxi(arch, s.P[off+4:], 2)) + moff := int(decodeInuxi(arch, s.P[off+4+2+2:], 4)) + off += moff // offset to array of reflect.method values + const sizeofMethod = 4 * 4 // sizeof reflect.method in program + return decodeMethodSig(arch, s, off, sizeofMethod, mcount) +} diff --git a/src/cmd/oldlink/internal/ld/dwarf.go b/src/cmd/oldlink/internal/ld/dwarf.go new file mode 100644 index 0000000000..3d5220cbfb --- /dev/null +++ b/src/cmd/oldlink/internal/ld/dwarf.go @@ -0,0 +1,2044 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type + +package ld + +import ( + "cmd/internal/dwarf" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "fmt" + "log" + "sort" + "strings" +) + +type dwctxt struct { + linkctxt *Link +} + +func (c dwctxt) PtrSize() int { + return c.linkctxt.Arch.PtrSize +} +func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) { + ls := s.(*sym.Symbol) + ls.AddUintXX(c.linkctxt.Arch, uint64(i), size) +} +func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) { + ls := s.(*sym.Symbol) + ls.AddBytes(b) +} +func (c dwctxt) AddString(s dwarf.Sym, v string) { + Addstring(s.(*sym.Symbol), v) +} + +func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { + if value != 0 { + value -= (data.(*sym.Symbol)).Value + } + s.(*sym.Symbol).AddAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) +} + +func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { + if value != 0 { + value -= (data.(*sym.Symbol)).Value + } + s.(*sym.Symbol).AddCURelativeAddrPlus(c.linkctxt.Arch, data.(*sym.Symbol), value) +} + +func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { + ls := s.(*sym.Symbol) + switch size { + default: + Errorf(ls, "invalid size %d in adddwarfref\n", size) + fallthrough + case c.linkctxt.Arch.PtrSize: + ls.AddAddr(c.linkctxt.Arch, t.(*sym.Symbol)) + case 4: + ls.AddAddrPlus4(t.(*sym.Symbol), 0) + } + r := &ls.R[len(ls.R)-1] + r.Type = objabi.R_ADDROFF + r.Add = ofs +} + +func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { + size := 4 + if isDwarf64(c.linkctxt) { + size = 8 + } + + c.AddSectionOffset(s, size, t, ofs) + ls := s.(*sym.Symbol) + ls.R[len(ls.R)-1].Type = objabi.R_DWARFSECREF +} + +func (c dwctxt) Logf(format string, args ...interface{}) { + c.linkctxt.Logf(format, args...) +} + +// At the moment these interfaces are only used in the compiler. + +func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) { + panic("should be used only in the compiler") +} + +func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 { + panic("should be used only in the compiler") +} + +func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) { + panic("should be used only in the compiler") +} + +func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { + panic("should be used only in the compiler") +} + +func isDwarf64(ctxt *Link) bool { + return ctxt.HeadType == objabi.Haix +} + +var gdbscript string + +var dwarfp []*sym.Symbol + +func writeabbrev(ctxt *Link) *sym.Symbol { + s := ctxt.Syms.Lookup(".debug_abbrev", 0) + s.Type = sym.SDWARFSECT + s.AddBytes(dwarf.GetAbbrev()) + return s +} + +var dwtypes dwarf.DWDie + +func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr { + a := new(dwarf.DWAttr) + a.Link = die.Attr + die.Attr = a + a.Atr = attr + a.Cls = uint8(cls) + a.Value = value + a.Data = data + return a +} + +// Each DIE (except the root ones) has at least 1 attribute: its +// name. getattr moves the desired one to the front so +// frequently searched ones are found faster. +func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr { + if die.Attr.Atr == attr { + return die.Attr + } + + a := die.Attr + b := a.Link + for b != nil { + if b.Atr == attr { + a.Link = b.Link + b.Link = die.Attr + die.Attr = b + return b + } + + a = b + b = b.Link + } + + return nil +} + +// Every DIE manufactured by the linker has at least an AT_name +// attribute (but it will only be written out if it is listed in the abbrev). +// The compiler does create nameless DWARF DIEs (ex: concrete subprogram +// instance). +func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie { + die := new(dwarf.DWDie) + die.Abbrev = abbrev + die.Link = parent.Child + parent.Child = die + + newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name) + + if name != "" && (abbrev <= dwarf.DW_ABRV_VARIABLE || abbrev >= dwarf.DW_ABRV_NULLTYPE) { + if abbrev != dwarf.DW_ABRV_VARIABLE || version == 0 { + if abbrev == dwarf.DW_ABRV_COMPUNIT { + // Avoid collisions with "real" symbol names. + name = fmt.Sprintf(".pkg.%s.%d", name, len(ctxt.compUnits)) + } + s := ctxt.Syms.Lookup(dwarf.InfoPrefix+name, version) + s.Attr |= sym.AttrNotInSymbolTable + s.Type = sym.SDWARFINFO + die.Sym = s + } + } + + return die +} + +func walktypedef(die *dwarf.DWDie) *dwarf.DWDie { + if die == nil { + return nil + } + // Resolve typedef if present. + if die.Abbrev == dwarf.DW_ABRV_TYPEDECL { + for attr := die.Attr; attr != nil; attr = attr.Link { + if attr.Atr == dwarf.DW_AT_type && attr.Cls == dwarf.DW_CLS_REFERENCE && attr.Data != nil { + return attr.Data.(*dwarf.DWDie) + } + } + } + + return die +} + +func walksymtypedef(ctxt *Link, s *sym.Symbol) *sym.Symbol { + if t := ctxt.Syms.ROLookup(s.Name+"..def", int(s.Version)); t != nil { + return t + } + return s +} + +// Find child by AT_name using hashtable if available or linear scan +// if not. +func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie { + var prev *dwarf.DWDie + for ; die != prev; prev, die = die, walktypedef(die) { + for a := die.Child; a != nil; a = a.Link { + if name == getattr(a, dwarf.DW_AT_name).Data { + return a + } + } + continue + } + return nil +} + +// Used to avoid string allocation when looking up dwarf symbols +var prefixBuf = []byte(dwarf.InfoPrefix) + +func find(ctxt *Link, name string) *sym.Symbol { + n := append(prefixBuf, name...) + // The string allocation below is optimized away because it is only used in a map lookup. + s := ctxt.Syms.ROLookup(string(n), 0) + prefixBuf = n[:len(dwarf.InfoPrefix)] + if s != nil && s.Type == sym.SDWARFINFO { + return s + } + return nil +} + +func mustFind(ctxt *Link, name string) *sym.Symbol { + r := find(ctxt, name) + if r == nil { + Exitf("dwarf find: cannot find %s", name) + } + return r +} + +func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 { + var result int64 + switch size { + default: + Errorf(s, "invalid size %d in adddwarfref\n", size) + fallthrough + case ctxt.Arch.PtrSize: + result = s.AddAddr(ctxt.Arch, t) + case 4: + result = s.AddAddrPlus4(t, 0) + } + r := &s.R[len(s.R)-1] + r.Type = objabi.R_DWARFSECREF + return result +} + +func newrefattr(die *dwarf.DWDie, attr uint16, ref *sym.Symbol) *dwarf.DWAttr { + if ref == nil { + return nil + } + return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, ref) +} + +func dtolsym(s dwarf.Sym) *sym.Symbol { + if s == nil { + return nil + } + return s.(*sym.Symbol) +} + +func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*sym.Symbol, die *dwarf.DWDie) []*sym.Symbol { + s := dtolsym(die.Sym) + if s == nil { + s = syms[len(syms)-1] + } else { + if s.Attr.OnList() { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + syms = append(syms, s) + } + dwarf.Uleb128put(ctxt, s, int64(die.Abbrev)) + dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr) + if dwarf.HasChildren(die) { + for die := die.Child; die != nil; die = die.Link { + syms = putdie(linkctxt, ctxt, syms, die) + } + syms[len(syms)-1].AddUint8(0) + } + return syms +} + +func reverselist(list **dwarf.DWDie) { + curr := *list + var prev *dwarf.DWDie + for curr != nil { + next := curr.Link + curr.Link = prev + prev = curr + curr = next + } + + *list = prev +} + +func reversetree(list **dwarf.DWDie) { + reverselist(list) + for die := *list; die != nil; die = die.Link { + if dwarf.HasChildren(die) { + reversetree(&die.Child) + } + } +} + +func newmemberoffsetattr(die *dwarf.DWDie, offs int32) { + newattr(die, dwarf.DW_AT_data_member_location, dwarf.DW_CLS_CONSTANT, int64(offs), nil) +} + +// GDB doesn't like FORM_addr for AT_location, so emit a +// location expression that evals to a const. +func newabslocexprattr(die *dwarf.DWDie, addr int64, sym *sym.Symbol) { + newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, sym) + // below +} + +// Lookup predefined types +func lookupOrDiag(ctxt *Link, n string) *sym.Symbol { + s := ctxt.Syms.ROLookup(n, 0) + if s == nil || s.Size == 0 { + Exitf("dwarf: missing type: %s", n) + } + + return s +} + +// dwarfFuncSym looks up a DWARF metadata symbol for function symbol s. +// If the symbol does not exist, it creates it if create is true, +// or returns nil otherwise. +func dwarfFuncSym(ctxt *Link, s *sym.Symbol, meta string, create bool) *sym.Symbol { + // All function ABIs use symbol version 0 for the DWARF data. + // + // TODO(austin): It may be useful to have DWARF info for ABI + // wrappers, in which case we may want these versions to + // align. Better yet, replace these name lookups with a + // general way to attach metadata to a symbol. + ver := 0 + if s.IsFileLocal() { + ver = int(s.Version) + } + if create { + return ctxt.Syms.Lookup(meta+s.Name, ver) + } + return ctxt.Syms.ROLookup(meta+s.Name, ver) +} + +func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie { + // Only emit typedefs for real names. + if strings.HasPrefix(name, "map[") { + return nil + } + if strings.HasPrefix(name, "struct {") { + return nil + } + if strings.HasPrefix(name, "chan ") { + return nil + } + if name[0] == '[' || name[0] == '*' { + return nil + } + if def == nil { + Errorf(nil, "dwarf: bad def in dotypedef") + } + + s := ctxt.Syms.Lookup(dtolsym(def.Sym).Name+"..def", 0) + s.Attr |= sym.AttrNotInSymbolTable + s.Type = sym.SDWARFINFO + def.Sym = s + + // The typedef entry must be created after the def, + // so that future lookups will find the typedef instead + // of the real definition. This hooks the typedef into any + // circular definition loops, so that gdb can understand them. + die := newdie(ctxt, parent, dwarf.DW_ABRV_TYPEDECL, name, 0) + + newrefattr(die, dwarf.DW_AT_type, s) + + return die +} + +// Define gotype, for composite ones recurse into constituents. +func defgotype(ctxt *Link, gotype *sym.Symbol) *sym.Symbol { + if gotype == nil { + return mustFind(ctxt, "<unspecified>") + } + + if !strings.HasPrefix(gotype.Name, "type.") { + Errorf(gotype, "dwarf: type name doesn't start with \"type.\"") + return mustFind(ctxt, "<unspecified>") + } + + name := gotype.Name[5:] // could also decode from Type.string + + sdie := find(ctxt, name) + + if sdie != nil { + return sdie + } + + return newtype(ctxt, gotype).Sym.(*sym.Symbol) +} + +func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie { + name := gotype.Name[5:] // could also decode from Type.string + kind := decodetypeKind(ctxt.Arch, gotype.P) + bytesize := decodetypeSize(ctxt.Arch, gotype.P) + + var die, typedefdie *dwarf.DWDie + switch kind { + case objabi.KindBool: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindInt, + objabi.KindInt8, + objabi.KindInt16, + objabi.KindInt32, + objabi.KindInt64: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindUint, + objabi.KindUint8, + objabi.KindUint16, + objabi.KindUint32, + objabi.KindUint64, + objabi.KindUintptr: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindFloat32, + objabi.KindFloat64: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindComplex64, + objabi.KindComplex128: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindArray: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0) + typedefdie = dotypedef(ctxt, &dwtypes, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + s := decodetypeArrayElem(ctxt.Arch, gotype) + newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) + fld := newdie(ctxt, die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0) + + // use actual length not upper bound; correct for 0-length arrays. + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(ctxt.Arch, gotype), 0) + + newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + + case objabi.KindChan: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0) + s := decodetypeChanElem(ctxt.Arch, gotype) + newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s)) + // Save elem type for synthesizechantypes. We could synthesize here + // but that would change the order of DIEs we output. + newrefattr(die, dwarf.DW_AT_type, s) + + case objabi.KindFunc: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + typedefdie = dotypedef(ctxt, &dwtypes, name, die) + nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P) + for i := 0; i < nfields; i++ { + s := decodetypeFuncInType(ctxt.Arch, gotype, i) + fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) + newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s)) + } + + if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) { + newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0) + } + nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P) + for i := 0; i < nfields; i++ { + s := decodetypeFuncOutType(ctxt.Arch, gotype, i) + fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) + newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, defgotype(ctxt, s))) + } + + case objabi.KindInterface: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0) + typedefdie = dotypedef(ctxt, &dwtypes, name, die) + nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P)) + var s *sym.Symbol + if nfields == 0 { + s = lookupOrDiag(ctxt, "type.runtime.eface") + } else { + s = lookupOrDiag(ctxt, "type.runtime.iface") + } + newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) + + case objabi.KindMap: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0) + s := decodetypeMapKey(ctxt.Arch, gotype) + newrefattr(die, dwarf.DW_AT_go_key, defgotype(ctxt, s)) + s = decodetypeMapValue(ctxt.Arch, gotype) + newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s)) + // Save gotype for use in synthesizemaptypes. We could synthesize here, + // but that would change the order of the DIEs. + newrefattr(die, dwarf.DW_AT_type, gotype) + + case objabi.KindPtr: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0) + typedefdie = dotypedef(ctxt, &dwtypes, name, die) + s := decodetypePtrElem(ctxt.Arch, gotype) + newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s)) + + case objabi.KindSlice: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0) + typedefdie = dotypedef(ctxt, &dwtypes, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + s := decodetypeArrayElem(ctxt.Arch, gotype) + elem := defgotype(ctxt, s) + newrefattr(die, dwarf.DW_AT_go_elem, elem) + + case objabi.KindString: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindStruct: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0) + typedefdie = dotypedef(ctxt, &dwtypes, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + nfields := decodetypeStructFieldCount(ctxt.Arch, gotype) + for i := 0; i < nfields; i++ { + f := decodetypeStructFieldName(ctxt.Arch, gotype, i) + s := decodetypeStructFieldType(ctxt.Arch, gotype, i) + if f == "" { + f = s.Name[5:] // skip "type." + } + fld := newdie(ctxt, die, dwarf.DW_ABRV_STRUCTFIELD, f, 0) + newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s)) + offsetAnon := decodetypeStructFieldOffsAnon(ctxt.Arch, gotype, i) + newmemberoffsetattr(fld, int32(offsetAnon>>1)) + if offsetAnon&1 != 0 { // is embedded field + newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) + } + } + + case objabi.KindUnsafePointer: + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0) + + default: + Errorf(gotype, "dwarf: definition of unknown kind %d", kind) + die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0) + newrefattr(die, dwarf.DW_AT_type, mustFind(ctxt, "<unspecified>")) + } + + newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0) + if gotype.Attr.Reachable() { + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype) + } + + if _, ok := prototypedies[gotype.Name]; ok { + prototypedies[gotype.Name] = die + } + + if typedefdie != nil { + return typedefdie + } + return die +} + +func nameFromDIESym(dwtype *sym.Symbol) string { + return strings.TrimSuffix(dwtype.Name[len(dwarf.InfoPrefix):], "..def") +} + +// Find or construct *T given T. +func defptrto(ctxt *Link, dwtype *sym.Symbol) *sym.Symbol { + ptrname := "*" + nameFromDIESym(dwtype) + if die := find(ctxt, ptrname); die != nil { + return die + } + + pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0) + newrefattr(pdie, dwarf.DW_AT_type, dwtype) + + // The DWARF info synthesizes pointer types that don't exist at the + // language level, like *hash<...> and *bucket<...>, and the data + // pointers of slices. Link to the ones we can find. + gotype := ctxt.Syms.ROLookup("type."+ptrname, 0) + if gotype != nil && gotype.Attr.Reachable() { + newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, gotype) + } + return dtolsym(pdie.Sym) +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. If except is one of +// the top-level children, it will not be copied. +func copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) { + for src = src.Child; src != nil; src = src.Link { + if src == except { + continue + } + c := newdie(ctxt, dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0) + for a := src.Attr; a != nil; a = a.Link { + newattr(c, a.Atr, int(a.Cls), a.Value, a.Data) + } + copychildrenexcept(ctxt, c, src, nil) + } + + reverselist(&dst.Child) +} + +func copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) { + copychildrenexcept(ctxt, dst, src, nil) +} + +// Search children (assumed to have TAG_member) for the one named +// field and set its AT_type to dwtype +func substitutetype(structdie *dwarf.DWDie, field string, dwtype *sym.Symbol) { + child := findchild(structdie, field) + if child == nil { + Exitf("dwarf substitutetype: %s does not have member %s", + getattr(structdie, dwarf.DW_AT_name).Data, field) + return + } + + a := getattr(child, dwarf.DW_AT_type) + if a != nil { + a.Data = dwtype + } else { + newrefattr(child, dwarf.DW_AT_type, dwtype) + } +} + +func findprotodie(ctxt *Link, name string) *dwarf.DWDie { + die, ok := prototypedies[name] + if ok && die == nil { + defgotype(ctxt, lookupOrDiag(ctxt, name)) + die = prototypedies[name] + } + return die +} + +func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(findprotodie(ctxt, "type.runtime.stringStructDWARF")) + if prototype == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE { + continue + } + copychildren(ctxt, die, prototype) + } +} + +func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(findprotodie(ctxt, "type.runtime.slice")) + if prototype == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_SLICETYPE { + continue + } + copychildren(ctxt, die, prototype) + elem := getattr(die, dwarf.DW_AT_go_elem).Data.(*sym.Symbol) + substitutetype(die, "array", defptrto(ctxt, elem)) + } +} + +func mkinternaltypename(base string, arg1 string, arg2 string) string { + if arg2 == "" { + return fmt.Sprintf("%s<%s>", base, arg1) + } + return fmt.Sprintf("%s<%s,%s>", base, arg1, arg2) +} + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +const ( + MaxKeySize = 128 + MaxValSize = 128 + BucketSize = 8 +) + +func mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) *sym.Symbol { + name := mkinternaltypename(typename, keyname, valname) + symname := dwarf.InfoPrefix + name + s := ctxt.Syms.ROLookup(symname, 0) + if s != nil && s.Type == sym.SDWARFINFO { + return s + } + die := newdie(ctxt, &dwtypes, abbrev, name, 0) + f(die) + return dtolsym(die.Sym) +} + +func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { + hash := walktypedef(findprotodie(ctxt, "type.runtime.hmap")) + bucket := walktypedef(findprotodie(ctxt, "type.runtime.bmap")) + + if hash == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_MAPTYPE { + continue + } + gotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol) + keytype := decodetypeMapKey(ctxt.Arch, gotype) + valtype := decodetypeMapValue(ctxt.Arch, gotype) + keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P) + keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype)) + + // compute size info like hashmap.c does. + indirectKey, indirectVal := false, false + if keysize > MaxKeySize { + keysize = int64(ctxt.Arch.PtrSize) + indirectKey = true + } + if valsize > MaxValSize { + valsize = int64(ctxt.Arch.PtrSize) + indirectVal = true + } + + // Construct type to represent an array of BucketSize keys + keyname := nameFromDIESym(keytype) + dwhks := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) { + newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0) + t := keytype + if indirectKey { + t = defptrto(ctxt, keytype) + } + newrefattr(dwhk, dwarf.DW_AT_type, t) + fld := newdie(ctxt, dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + }) + + // Construct type to represent an array of BucketSize values + valname := nameFromDIESym(valtype) + dwhvs := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) { + newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0) + t := valtype + if indirectVal { + t = defptrto(ctxt, valtype) + } + newrefattr(dwhv, dwarf.DW_AT_type, t) + fld := newdie(ctxt, dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + }) + + // Construct bucket<K,V> + dwhbs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) { + // Copy over all fields except the field "data" from the generic + // bucket. "data" will be replaced with keys/values below. + copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) + + fld := newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0) + newrefattr(fld, dwarf.DW_AT_type, dwhks) + newmemberoffsetattr(fld, BucketSize) + fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0) + newrefattr(fld, dwarf.DW_AT_type, dwhvs) + newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) + fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0) + newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, dtolsym(dwhb.Sym))) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) + if ctxt.Arch.RegSize > ctxt.Arch.PtrSize { + fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0) + newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr")) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(ctxt.Arch.PtrSize)) + } + + newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(ctxt.Arch.RegSize), 0) + }) + + // Construct hash<K,V> + dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) { + copychildren(ctxt, dwh, hash) + substitutetype(dwh, "buckets", defptrto(ctxt, dwhbs)) + substitutetype(dwh, "oldbuckets", defptrto(ctxt, dwhbs)) + newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil) + }) + + // make map type a pointer to hash<K,V> + newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs)) + } +} + +func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { + sudog := walktypedef(findprotodie(ctxt, "type.runtime.sudog")) + waitq := walktypedef(findprotodie(ctxt, "type.runtime.waitq")) + hchan := walktypedef(findprotodie(ctxt, "type.runtime.hchan")) + if sudog == nil || waitq == nil || hchan == nil { + return + } + + sudogsize := int(getattr(sudog, dwarf.DW_AT_byte_size).Value) + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_CHANTYPE { + continue + } + elemgotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol) + elemname := elemgotype.Name[5:] + elemtype := walksymtypedef(ctxt, defgotype(ctxt, elemgotype)) + + // sudog<T> + dwss := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) { + copychildren(ctxt, dws, sudog) + substitutetype(dws, "elem", defptrto(ctxt, elemtype)) + newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil) + }) + + // waitq<T> + dwws := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { + + copychildren(ctxt, dww, waitq) + substitutetype(dww, "first", defptrto(ctxt, dwss)) + substitutetype(dww, "last", defptrto(ctxt, dwss)) + newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil) + }) + + // hchan<T> + dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) { + copychildren(ctxt, dwh, hchan) + substitutetype(dwh, "recvq", dwws) + substitutetype(dwh, "sendq", dwws) + newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil) + }) + + newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs)) + } +} + +func dwarfDefineGlobal(ctxt *Link, s *sym.Symbol, str string, v int64, gotype *sym.Symbol) { + // Find a suitable CU DIE to include the global. + // One would think it's as simple as just looking at the unit, but that might + // not have any reachable code. So, we go to the runtime's CU if our unit + // isn't otherwise reachable. + var unit *sym.CompilationUnit + if s.Unit != nil { + unit = s.Unit + } else { + unit = ctxt.runtimeCU + } + dv := newdie(ctxt, unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(s.Version)) + newabslocexprattr(dv, v, s) + if !s.IsFileLocal() { + newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0) + } + dt := defgotype(ctxt, gotype) + newrefattr(dv, dwarf.DW_AT_type, dt) +} + +// For use with pass.c::genasmsym +func defdwsymb(ctxt *Link, s *sym.Symbol, str string, t SymbolType, v int64, gotype *sym.Symbol) { + if strings.HasPrefix(str, "go.string.") { + return + } + if strings.HasPrefix(str, "runtime.gcbits.") { + return + } + + switch t { + case DataSym, BSSSym: + switch s.Type { + case sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS: + // ok + case sym.SRODATA: + if gotype != nil { + defgotype(ctxt, gotype) + } + return + default: + return + } + if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) { + return + } + dwarfDefineGlobal(ctxt, s, str, v, gotype) + + case AutoSym, ParamSym, DeletedAutoSym: + defgotype(ctxt, gotype) + } +} + +// createUnitLength creates the initial length field with value v and update +// offset of unit_length if needed. +func createUnitLength(ctxt *Link, s *sym.Symbol, v uint64) { + if isDwarf64(ctxt) { + s.AddUint32(ctxt.Arch, 0xFFFFFFFF) + } + addDwarfAddrField(ctxt, s, v) +} + +// addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits. +func addDwarfAddrField(ctxt *Link, s *sym.Symbol, v uint64) { + if isDwarf64(ctxt) { + s.AddUint(ctxt.Arch, v) + } else { + s.AddUint32(ctxt.Arch, uint32(v)) + } +} + +// addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits. +func addDwarfAddrRef(ctxt *Link, s *sym.Symbol, t *sym.Symbol) { + if isDwarf64(ctxt) { + adddwarfref(ctxt, s, t, 8) + } else { + adddwarfref(ctxt, s, t, 4) + } +} + +// calcCompUnitRanges calculates the PC ranges of the compilation units. +func calcCompUnitRanges(ctxt *Link) { + var prevUnit *sym.CompilationUnit + for _, s := range ctxt.Textp { + if s.FuncInfo == nil { + continue + } + // Skip linker-created functions (ex: runtime.addmoduledata), since they + // don't have DWARF to begin with. + if s.Unit == nil { + continue + } + unit := s.Unit + // Update PC ranges. + // + // We don't simply compare the end of the previous + // symbol with the start of the next because there's + // often a little padding between them. Instead, we + // only create boundaries between symbols from + // different units. + if prevUnit != unit { + unit.PCs = append(unit.PCs, dwarf.Range{Start: s.Value - unit.Textp[0].Value}) + prevUnit = unit + } + unit.PCs[len(unit.PCs)-1].End = s.Value - unit.Textp[0].Value + s.Size + } +} + +func movetomodule(ctxt *Link, parent *dwarf.DWDie) { + die := ctxt.runtimeCU.DWInfo.Child + if die == nil { + ctxt.runtimeCU.DWInfo.Child = parent.Child + return + } + for die.Link != nil { + die = die.Link + } + die.Link = parent.Child +} + +// If the pcln table contains runtime/proc.go, use that to set gdbscript path. +func finddebugruntimepath(s *sym.Symbol) { + if gdbscript != "" { + return + } + + for i := range s.FuncInfo.File { + f := s.FuncInfo.File[i] + // We can't use something that may be dead-code + // eliminated from a binary here. proc.go contains + // main and the scheduler, so it's not going anywhere. + if i := strings.Index(f.Name, "runtime/proc.go"); i >= 0 { + gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" + break + } + } +} + +/* + * Generate a sequence of opcodes that is as short as possible. + * See section 6.2.5 + */ +const ( + LINE_BASE = -4 + LINE_RANGE = 10 + PC_RANGE = (255 - OPCODE_BASE) / LINE_RANGE + OPCODE_BASE = 11 +) + +/* + * Walk prog table, emit line program and build DIE tree. + */ + +func getCompilationDir() string { + // OSX requires this be set to something, but it's not easy to choose + // a value. Linking takes place in a temporary directory, so there's + // no point including it here. Paths in the file table are usually + // absolute, in which case debuggers will ignore this value. -trimpath + // produces relative paths, but we don't know where they start, so + // all we can do here is try not to make things worse. + return "." +} + +func importInfoSymbol(ctxt *Link, dsym *sym.Symbol) { + dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable + dsym.Type = sym.SDWARFINFO + for i := range dsym.R { + r := &dsym.R[i] // Copying sym.Reloc has measurable impact on performance + if r.Type == objabi.R_DWARFSECREF && r.Sym.Size == 0 { + n := nameFromDIESym(r.Sym) + defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0)) + } + } +} + +func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) { + + var dwarfctxt dwarf.Context = dwctxt{ctxt} + is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles. + + unitstart := int64(-1) + headerstart := int64(-1) + headerend := int64(-1) + + newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, ls.Size, ls) + + // Write .debug_line Line Number Program Header (sec 6.2.4) + // Fields marked with (*) must be changed for 64-bit dwarf + unitLengthOffset := ls.Size + createUnitLength(ctxt, ls, 0) // unit_length (*), filled in at end + unitstart = ls.Size + ls.AddUint16(ctxt.Arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05 + headerLengthOffset := ls.Size + addDwarfAddrField(ctxt, ls, 0) // header_length (*), filled in at end + headerstart = ls.Size + + // cpos == unitstart + 4 + 2 + 4 + ls.AddUint8(1) // minimum_instruction_length + ls.AddUint8(is_stmt) // default_is_stmt + ls.AddUint8(LINE_BASE & 0xFF) // line_base + ls.AddUint8(LINE_RANGE) // line_range + ls.AddUint8(OPCODE_BASE) // opcode_base + ls.AddUint8(0) // standard_opcode_lengths[1] + ls.AddUint8(1) // standard_opcode_lengths[2] + ls.AddUint8(1) // standard_opcode_lengths[3] + ls.AddUint8(1) // standard_opcode_lengths[4] + ls.AddUint8(1) // standard_opcode_lengths[5] + ls.AddUint8(0) // standard_opcode_lengths[6] + ls.AddUint8(0) // standard_opcode_lengths[7] + ls.AddUint8(0) // standard_opcode_lengths[8] + ls.AddUint8(1) // standard_opcode_lengths[9] + ls.AddUint8(0) // standard_opcode_lengths[10] + ls.AddUint8(0) // include_directories (empty) + + // Copy over the file table. + fileNums := make(map[string]int) + for i, name := range unit.DWARFFileTable { + if len(name) != 0 { + if strings.HasPrefix(name, src.FileSymPrefix) { + name = name[len(src.FileSymPrefix):] + } + name = expandGoroot(name) + } else { + // Can't have empty filenames, and having a unique filename is quite useful + // for debugging. + name = fmt.Sprintf("<missing>_%d", i) + } + fileNums[name] = i + 1 + dwarfctxt.AddString(ls, name) + ls.AddUint8(0) + ls.AddUint8(0) + ls.AddUint8(0) + } + // Grab files for inlined functions. + // TODO: With difficulty, this could be moved into the compiler. + for _, s := range unit.Textp { + dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true) + for ri := 0; ri < len(dsym.R); ri++ { + r := &dsym.R[ri] + if r.Type != objabi.R_DWARFFILEREF { + continue + } + name := r.Sym.Name + if _, ok := fileNums[name]; ok { + continue + } + fileNums[name] = len(fileNums) + 1 + dwarfctxt.AddString(ls, name) + ls.AddUint8(0) + ls.AddUint8(0) + ls.AddUint8(0) + } + } + + // 4 zeros: the string termination + 3 fields. + ls.AddUint8(0) + // terminate file_names. + headerend = ls.Size + + // Output the state machine for each function remaining. + var lastAddr int64 + for _, s := range unit.Textp { + finddebugruntimepath(s) + + // Set the PC. + ls.AddUint8(0) + dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize)) + ls.AddUint8(dwarf.DW_LNE_set_address) + addr := ls.AddAddr(ctxt.Arch, s) + // Make sure the units are sorted. + if addr < lastAddr { + Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr) + } + lastAddr = addr + + // Output the line table. + // TODO: Now that we have all the debug information in separate + // symbols, it would make sense to use a rope, and concatenate them all + // together rather then the append() below. This would allow us to have + // the compiler emit the DW_LNE_set_address and a rope data structure + // to concat them all together in the output. + lines := dwarfFuncSym(ctxt, s, dwarf.DebugLinesPrefix, false) + if lines != nil { + ls.P = append(ls.P, lines.P...) + } + } + + ls.AddUint8(0) // start extended opcode + dwarf.Uleb128put(dwarfctxt, ls, 1) + ls.AddUint8(dwarf.DW_LNE_end_sequence) + + if ctxt.HeadType == objabi.Haix { + saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(ls.Size-unitLengthOffset)) + } + if isDwarf64(ctxt) { + ls.SetUint(ctxt.Arch, unitLengthOffset+4, uint64(ls.Size-unitstart)) // +4 because of 0xFFFFFFFF + ls.SetUint(ctxt.Arch, headerLengthOffset, uint64(headerend-headerstart)) + } else { + ls.SetUint32(ctxt.Arch, unitLengthOffset, uint32(ls.Size-unitstart)) + ls.SetUint32(ctxt.Arch, headerLengthOffset, uint32(headerend-headerstart)) + } + + // Process any R_DWARFFILEREF relocations, since we now know the + // line table file indices for this compilation unit. Note that + // this loop visits only subprogram DIEs: if the compiler is + // changed to generate DW_AT_decl_file attributes for other + // DIE flavors (ex: variables) then those DIEs would need to + // be included below. + missing := make(map[int]interface{}) + s := unit.Textp[0] + for _, f := range unit.FuncDIEs { + for ri := range f.R { + r := &f.R[ri] + if r.Type != objabi.R_DWARFFILEREF { + continue + } + idx, ok := fileNums[r.Sym.Name] + if ok { + if int(int32(idx)) != idx { + Errorf(f, "bad R_DWARFFILEREF relocation: file index overflow") + } + if r.Siz != 4 { + Errorf(f, "bad R_DWARFFILEREF relocation: has size %d, expected 4", r.Siz) + } + if r.Off < 0 || r.Off+4 > int32(len(f.P)) { + Errorf(f, "bad R_DWARFFILEREF relocation offset %d + 4 would write past length %d", r.Off, len(s.P)) + continue + } + if r.Add != 0 { + Errorf(f, "bad R_DWARFFILEREF relocation: addend not zero") + } + r.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable + r.Add = int64(idx) // record the index in r.Add, we'll apply it in the reloc phase. + } else { + _, found := missing[int(r.Sym.Value)] + if !found { + Errorf(f, "R_DWARFFILEREF relocation file missing: %v idx %d", r.Sym, r.Sym.Value) + missing[int(r.Sym.Value)] = nil + } + } + } + } +} + +// writepcranges generates the DW_AT_ranges table for compilation unit cu. +func writepcranges(ctxt *Link, unit *sym.CompilationUnit, base *sym.Symbol, pcs []dwarf.Range, ranges *sym.Symbol) { + var dwarfctxt dwarf.Context = dwctxt{ctxt} + + unitLengthOffset := ranges.Size + + // Create PC ranges for this CU. + newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, ranges.Size, ranges) + newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, base.Value, base) + dwarf.PutBasedRanges(dwarfctxt, ranges, pcs) + + if ctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(ranges.Size-unitLengthOffset)) + } + +} + +/* + * Emit .debug_frame + */ +const ( + dataAlignmentFactor = -4 +) + +// appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice. +func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte { + b = append(b, dwarf.DW_CFA_def_cfa_offset_sf) + b = dwarf.AppendSleb128(b, cfa/dataAlignmentFactor) + + switch { + case deltapc < 0x40: + b = append(b, uint8(dwarf.DW_CFA_advance_loc+deltapc)) + case deltapc < 0x100: + b = append(b, dwarf.DW_CFA_advance_loc1) + b = append(b, uint8(deltapc)) + case deltapc < 0x10000: + b = append(b, dwarf.DW_CFA_advance_loc2, 0, 0) + arch.ByteOrder.PutUint16(b[len(b)-2:], uint16(deltapc)) + default: + b = append(b, dwarf.DW_CFA_advance_loc4, 0, 0, 0, 0) + arch.ByteOrder.PutUint32(b[len(b)-4:], uint32(deltapc)) + } + return b +} + +func writeframes(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { + var dwarfctxt dwarf.Context = dwctxt{ctxt} + fs := ctxt.Syms.Lookup(".debug_frame", 0) + fs.Type = sym.SDWARFSECT + syms = append(syms, fs) + + // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64 + lengthFieldSize := int64(4) + if isDwarf64(ctxt) { + lengthFieldSize += 8 + } + + // Emit the CIE, Section 6.4.1 + cieReserve := uint32(16) + if haslinkregister(ctxt) { + cieReserve = 32 + } + if isDwarf64(ctxt) { + cieReserve += 4 // 4 bytes added for cid + } + createUnitLength(ctxt, fs, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize + addDwarfAddrField(ctxt, fs, ^uint64(0)) // cid + fs.AddUint8(3) // dwarf version (appendix F) + fs.AddUint8(0) // augmentation "" + dwarf.Uleb128put(dwarfctxt, fs, 1) // code_alignment_factor + dwarf.Sleb128put(dwarfctxt, fs, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // return_address_register + + fs.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address.. + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... + if haslinkregister(ctxt) { + dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...plus a 0 offset. + + fs.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) + + fs.AddUint8(dwarf.DW_CFA_val_offset) // The previous value... + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfregsp)) // ...of the platform's SP register... + dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...is CFA+0. + } else { + dwarf.Uleb128put(dwarfctxt, fs, int64(ctxt.Arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). + + fs.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value... + dwarf.Uleb128put(dwarfctxt, fs, int64(thearch.Dwarfreglr)) // ...of the return address... + dwarf.Uleb128put(dwarfctxt, fs, int64(-ctxt.Arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. + } + + pad := int64(cieReserve) + lengthFieldSize - fs.Size + + if pad < 0 { + Exitf("dwarf: cieReserve too small by %d bytes.", -pad) + } + + fs.AddBytes(zeros[:pad]) + + var deltaBuf []byte + pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) + for _, s := range ctxt.Textp { + if s.FuncInfo == nil { + continue + } + + // Emit a FDE, Section 6.4.1. + // First build the section contents into a byte buffer. + deltaBuf = deltaBuf[:0] + if haslinkregister(ctxt) && s.Attr.TopFrame() { + // Mark the link register as having an undefined value. + // This stops call stack unwinders progressing any further. + // TODO: similar mark on non-LR architectures. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + } + for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() { + nextpc := pcsp.NextPC + + // pciterinit goes up to the end of the function, + // but DWARF expects us to stop just before the end. + if int64(nextpc) == s.Size { + nextpc-- + if nextpc < pcsp.PC { + continue + } + } + + spdelta := int64(pcsp.Value) + if !haslinkregister(ctxt) { + // Return address has been pushed onto stack. + spdelta += int64(ctxt.Arch.PtrSize) + } + + if haslinkregister(ctxt) && !s.Attr.TopFrame() { + // TODO(bryanpkc): This is imprecise. In general, the instruction + // that stores the return address to the stack frame is not the + // same one that allocates the frame. + if pcsp.Value > 0 { + // The return address is preserved at (CFA-frame_size) + // after a stack frame has been allocated. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + deltaBuf = dwarf.AppendSleb128(deltaBuf, -spdelta/dataAlignmentFactor) + } else { + // The return address is restored into the link register + // when a stack frame has been de-allocated. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + } + } + + deltaBuf = appendPCDeltaCFA(ctxt.Arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta) + } + pad := int(Rnd(int64(len(deltaBuf)), int64(ctxt.Arch.PtrSize))) - len(deltaBuf) + deltaBuf = append(deltaBuf, zeros[:pad]...) + + // Emit the FDE header, Section 6.4.1. + // 4 bytes: length, must be multiple of thearch.ptrsize + // 4/8 bytes: Pointer to the CIE above, at offset 0 + // ptrsize: initial location + // ptrsize: address range + + fdeLength := uint64(4 + 2*ctxt.Arch.PtrSize + len(deltaBuf)) + if isDwarf64(ctxt) { + fdeLength += 4 // 4 bytes added for CIE pointer + } + createUnitLength(ctxt, fs, fdeLength) + + if ctxt.LinkMode == LinkExternal { + addDwarfAddrRef(ctxt, fs, fs) + } else { + addDwarfAddrField(ctxt, fs, 0) // CIE offset + } + fs.AddAddr(ctxt.Arch, s) + fs.AddUintXX(ctxt.Arch, uint64(s.Size), ctxt.Arch.PtrSize) // address range + fs.AddBytes(deltaBuf) + + if ctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_frame", s.File, fdeLength+uint64(lengthFieldSize)) + } + } + return syms +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ +const ( + COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 +) + +func writeinfo(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit, abbrevsym *sym.Symbol, pubNames, pubTypes *pubWriter) []*sym.Symbol { + infosec := ctxt.Syms.Lookup(".debug_info", 0) + infosec.Type = sym.SDWARFINFO + infosec.Attr |= sym.AttrReachable + syms = append(syms, infosec) + + var dwarfctxt dwarf.Context = dwctxt{ctxt} + + for _, u := range units { + compunit := u.DWInfo + s := dtolsym(compunit.Sym) + + if len(u.Textp) == 0 && u.DWInfo.Child == nil { + continue + } + + pubNames.beginCompUnit(compunit) + pubTypes.beginCompUnit(compunit) + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + createUnitLength(ctxt, s, 0) // unit_length (*), will be filled in later. + s.AddUint16(ctxt.Arch, 4) // dwarf version (appendix F) + + // debug_abbrev_offset (*) + addDwarfAddrRef(ctxt, s, abbrevsym) + + s.AddUint8(uint8(ctxt.Arch.PtrSize)) // address_size + + dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev)) + dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr) + + cu := []*sym.Symbol{s} + cu = append(cu, u.AbsFnDIEs...) + cu = append(cu, u.FuncDIEs...) + if u.Consts != nil { + cu = append(cu, u.Consts) + } + var cusize int64 + for _, child := range cu { + cusize += child.Size + } + + for die := compunit.Child; die != nil; die = die.Link { + l := len(cu) + lastSymSz := cu[l-1].Size + cu = putdie(ctxt, dwarfctxt, cu, die) + if ispubname(die) { + pubNames.add(die, cusize) + } + if ispubtype(die) { + pubTypes.add(die, cusize) + } + if lastSymSz != cu[l-1].Size { + // putdie will sometimes append directly to the last symbol of the list + cusize = cusize - lastSymSz + cu[l-1].Size + } + for _, child := range cu[l:] { + cusize += child.Size + } + } + cu[len(cu)-1].AddUint8(0) // closes compilation unit DIE + cusize++ + + // Save size for AIX symbol table. + if ctxt.HeadType == objabi.Haix { + saveDwsectCUSize(".debug_info", getPkgFromCUSym(s), uint64(cusize)) + } + if isDwarf64(ctxt) { + cusize -= 12 // exclude the length field. + s.SetUint(ctxt.Arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF + } else { + cusize -= 4 // exclude the length field. + s.SetUint32(ctxt.Arch, 0, uint32(cusize)) + } + pubNames.endCompUnit(compunit, uint32(cusize)+4) + pubTypes.endCompUnit(compunit, uint32(cusize)+4) + syms = append(syms, cu...) + } + return syms +} + +/* + * Emit .debug_pubnames/_types. _info must have been written before, + * because we need die->offs and infoo/infosize; + */ +func ispubname(die *dwarf.DWDie) bool { + switch die.Abbrev { + case dwarf.DW_ABRV_FUNCTION, dwarf.DW_ABRV_VARIABLE: + a := getattr(die, dwarf.DW_AT_external) + return a != nil && a.Value != 0 + } + + return false +} + +func ispubtype(die *dwarf.DWDie) bool { + return die.Abbrev >= dwarf.DW_ABRV_NULLTYPE +} + +type pubWriter struct { + ctxt *Link + s *sym.Symbol + sname string + + sectionstart int64 + culengthOff int64 +} + +func newPubWriter(ctxt *Link, sname string) *pubWriter { + s := ctxt.Syms.Lookup(sname, 0) + s.Type = sym.SDWARFSECT + return &pubWriter{ctxt: ctxt, s: s, sname: sname} +} + +func (pw *pubWriter) beginCompUnit(compunit *dwarf.DWDie) { + pw.sectionstart = pw.s.Size + + // Write .debug_pubnames/types Header (sec 6.1.1) + createUnitLength(pw.ctxt, pw.s, 0) // unit_length (*), will be filled in later. + pw.s.AddUint16(pw.ctxt.Arch, 2) // dwarf version (appendix F) + addDwarfAddrRef(pw.ctxt, pw.s, dtolsym(compunit.Sym)) // debug_info_offset (of the Comp unit Header) + pw.culengthOff = pw.s.Size + addDwarfAddrField(pw.ctxt, pw.s, uint64(0)) // debug_info_length, will be filled in later. + +} + +func (pw *pubWriter) add(die *dwarf.DWDie, offset int64) { + dwa := getattr(die, dwarf.DW_AT_name) + name := dwa.Data.(string) + if die.Sym == nil { + fmt.Println("Missing sym for ", name) + } + addDwarfAddrField(pw.ctxt, pw.s, uint64(offset)) + Addstring(pw.s, name) +} + +func (pw *pubWriter) endCompUnit(compunit *dwarf.DWDie, culength uint32) { + addDwarfAddrField(pw.ctxt, pw.s, 0) // Null offset + + // On AIX, save the current size of this compilation unit. + if pw.ctxt.HeadType == objabi.Haix { + saveDwsectCUSize(pw.sname, getPkgFromCUSym(dtolsym(compunit.Sym)), uint64(pw.s.Size-pw.sectionstart)) + } + if isDwarf64(pw.ctxt) { + pw.s.SetUint(pw.ctxt.Arch, pw.sectionstart+4, uint64(pw.s.Size-pw.sectionstart)-12) // exclude the length field. + pw.s.SetUint(pw.ctxt.Arch, pw.culengthOff, uint64(culength)) + } else { + pw.s.SetUint32(pw.ctxt.Arch, pw.sectionstart, uint32(pw.s.Size-pw.sectionstart)-4) // exclude the length field. + pw.s.SetUint32(pw.ctxt.Arch, pw.culengthOff, culength) + } +} + +func writegdbscript(ctxt *Link, syms []*sym.Symbol) []*sym.Symbol { + // TODO (aix): make it available + if ctxt.HeadType == objabi.Haix { + return syms + } + if ctxt.LinkMode == LinkExternal && ctxt.HeadType == objabi.Hwindows && ctxt.BuildMode == BuildModeCArchive { + // gcc on Windows places .debug_gdb_scripts in the wrong location, which + // causes the program not to run. See https://golang.org/issue/20183 + // Non c-archives can avoid this issue via a linker script + // (see fix near writeGDBLinkerScript). + // c-archive users would need to specify the linker script manually. + // For UX it's better not to deal with this. + return syms + } + + if gdbscript != "" { + s := ctxt.Syms.Lookup(".debug_gdb_scripts", 0) + s.Type = sym.SDWARFSECT + syms = append(syms, s) + s.AddUint8(1) // magic 1 byte? + Addstring(s, gdbscript) + } + + return syms +} + +var prototypedies map[string]*dwarf.DWDie + +func dwarfEnabled(ctxt *Link) bool { + if *FlagW { // disable dwarf + return false + } + if *FlagS && ctxt.HeadType != objabi.Hdarwin { + return false + } + if ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hjs { + return false + } + + if ctxt.LinkMode == LinkExternal { + switch { + case ctxt.IsELF: + case ctxt.HeadType == objabi.Hdarwin: + case ctxt.HeadType == objabi.Hwindows: + case ctxt.HeadType == objabi.Haix: + res, err := dwarf.IsDWARFEnabledOnAIXLd(ctxt.extld()) + if err != nil { + Exitf("%v", err) + } + return res + default: + return false + } + } + + return true +} + +// dwarfGenerateDebugInfo generated debug info entries for all types, +// variables and functions in the program. +// Along with dwarfGenerateDebugSyms they are the two main entry points into +// dwarf generation: dwarfGenerateDebugInfo does all the work that should be +// done before symbol names are mangled while dwarfgeneratedebugsyms does +// all the work that can only be done after addresses have been assigned to +// text symbols. +func dwarfGenerateDebugInfo(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return + } + + if ctxt.HeadType == objabi.Haix { + // Initial map used to store package size for each DWARF section. + dwsectCUSize = make(map[string]uint64) + } + + // Forctxt.Diagnostic messages. + newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") + + // Some types that must exist to define other ones. + newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0) + + newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "void", 0) + newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0) + + die := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(ctxt.Arch.PtrSize), 0) + newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0) + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, lookupOrDiag(ctxt, "type.uintptr")) + + // Prototypes needed for type synthesis. + prototypedies = map[string]*dwarf.DWDie{ + "type.runtime.stringStructDWARF": nil, + "type.runtime.slice": nil, + "type.runtime.hmap": nil, + "type.runtime.bmap": nil, + "type.runtime.sudog": nil, + "type.runtime.waitq": nil, + "type.runtime.hchan": nil, + } + + // Needed by the prettyprinter code for interface inspection. + for _, typ := range []string{ + "type.runtime._type", + "type.runtime.arraytype", + "type.runtime.chantype", + "type.runtime.functype", + "type.runtime.maptype", + "type.runtime.ptrtype", + "type.runtime.slicetype", + "type.runtime.structtype", + "type.runtime.interfacetype", + "type.runtime.itab", + "type.runtime.imethod"} { + defgotype(ctxt, lookupOrDiag(ctxt, typ)) + } + + // fake root DIE for compile unit DIEs + var dwroot dwarf.DWDie + flagVariants := make(map[string]bool) + + for _, lib := range ctxt.Library { + consts := ctxt.Syms.ROLookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) + for _, unit := range lib.Units { + // We drop the constants into the first CU. + if consts != nil { + importInfoSymbol(ctxt, consts) + unit.Consts = consts + consts = nil + } + + ctxt.compUnits = append(ctxt.compUnits, unit) + + // We need at least one runtime unit. + if unit.Lib.Pkg == "runtime" { + ctxt.runtimeCU = unit + } + + unit.DWInfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, unit.Lib.Pkg, 0) + newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0) + // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. + compDir := getCompilationDir() + // TODO: Make this be the actual compilation directory, not + // the linker directory. If we move CU construction into the + // compiler, this should happen naturally. + newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir) + producerExtra := ctxt.Syms.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0) + producer := "Go cmd/compile " + objabi.Version + if len(producerExtra.P) > 0 { + // We put a semicolon before the flags to clearly + // separate them from the version, which can be long + // and have lots of weird things in it in development + // versions. We promise not to put a semicolon in the + // version, so it should be safe for readers to scan + // forward to the semicolon. + producer += "; " + string(producerExtra.P) + flagVariants[string(producerExtra.P)] = true + } else { + flagVariants[""] = true + } + + newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer) + + var pkgname string + if s := ctxt.Syms.ROLookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); s != nil { + pkgname = string(s.P) + } + newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname) + + if len(unit.Textp) == 0 { + unit.DWInfo.Abbrev = dwarf.DW_ABRV_COMPUNIT_TEXTLESS + } + + // Scan all functions in this compilation unit, create DIEs for all + // referenced types, create the file table for debug_line, find all + // referenced abstract functions. + // Collect all debug_range symbols in unit.rangeSyms + for _, s := range unit.Textp { // textp has been dead-code-eliminated already. + dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, false) + dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable + dsym.Type = sym.SDWARFINFO + unit.FuncDIEs = append(unit.FuncDIEs, dsym) + + rangeSym := dwarfFuncSym(ctxt, s, dwarf.RangePrefix, false) + if rangeSym != nil && rangeSym.Size > 0 { + rangeSym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable + rangeSym.Type = sym.SDWARFRANGE + if ctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, uint64(rangeSym.Size)) + } + unit.RangeSyms = append(unit.RangeSyms, rangeSym) + } + + for ri := 0; ri < len(dsym.R); ri++ { + r := &dsym.R[ri] + if r.Type == objabi.R_DWARFSECREF { + rsym := r.Sym + if strings.HasPrefix(rsym.Name, dwarf.InfoPrefix) && strings.HasSuffix(rsym.Name, dwarf.AbstractFuncSuffix) && !rsym.Attr.OnList() { + // abstract function + rsym.Attr |= sym.AttrOnList + unit.AbsFnDIEs = append(unit.AbsFnDIEs, rsym) + importInfoSymbol(ctxt, rsym) + } else if rsym.Size == 0 { + // a type we do not have a DIE for + n := nameFromDIESym(rsym) + defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0)) + } + } + } + } + } + } + + // Fix for 31034: if the objects feeding into this link were compiled + // with different sets of flags, then don't issue an error if + // the -strictdups checks fail. + if checkStrictDups > 1 && len(flagVariants) > 1 { + checkStrictDups = 1 + } + + // Create DIEs for global variables and the types they use. + genasmsym(ctxt, defdwsymb) + + // Create DIEs for variable types indirectly referenced by function + // autos (which may not appear directly as param/var DIEs). + for _, lib := range ctxt.Library { + for _, unit := range lib.Units { + lists := [][]*sym.Symbol{unit.AbsFnDIEs, unit.FuncDIEs} + for _, list := range lists { + for _, s := range list { + for i := 0; i < len(s.R); i++ { + r := &s.R[i] + if r.Type == objabi.R_USETYPE { + defgotype(ctxt, r.Sym) + } + } + } + } + } + } + + synthesizestringtypes(ctxt, dwtypes.Child) + synthesizeslicetypes(ctxt, dwtypes.Child) + synthesizemaptypes(ctxt, dwtypes.Child) + synthesizechantypes(ctxt, dwtypes.Child) +} + +// dwarfGenerateDebugSyms constructs debug_line, debug_frame, debug_loc, +// debug_pubnames and debug_pubtypes. It also writes out the debug_info +// section using symbols generated in dwarfGenerateDebugInfo. +func dwarfGenerateDebugSyms(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return + } + + abbrev := writeabbrev(ctxt) + syms := []*sym.Symbol{abbrev} + + calcCompUnitRanges(ctxt) + sort.Sort(compilationUnitByStartPC(ctxt.compUnits)) + + // Write per-package line and range tables and start their CU DIEs. + debugLine := ctxt.Syms.Lookup(".debug_line", 0) + debugLine.Type = sym.SDWARFSECT + debugRanges := ctxt.Syms.Lookup(".debug_ranges", 0) + debugRanges.Type = sym.SDWARFRANGE + debugRanges.Attr |= sym.AttrReachable + syms = append(syms, debugLine) + for _, u := range ctxt.compUnits { + reversetree(&u.DWInfo.Child) + if u.DWInfo.Abbrev == dwarf.DW_ABRV_COMPUNIT_TEXTLESS { + continue + } + writelines(ctxt, u, debugLine) + writepcranges(ctxt, u, u.Textp[0], u.PCs, debugRanges) + } + + // newdie adds DIEs to the *beginning* of the parent's DIE list. + // Now that we're done creating DIEs, reverse the trees so DIEs + // appear in the order they were created. + reversetree(&dwtypes.Child) + movetomodule(ctxt, &dwtypes) + + pubNames := newPubWriter(ctxt, ".debug_pubnames") + pubTypes := newPubWriter(ctxt, ".debug_pubtypes") + + // Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT + infosyms := writeinfo(ctxt, nil, ctxt.compUnits, abbrev, pubNames, pubTypes) + + syms = writeframes(ctxt, syms) + syms = append(syms, pubNames.s, pubTypes.s) + syms = writegdbscript(ctxt, syms) + // Now we're done writing SDWARFSECT symbols, so we can write + // other SDWARF* symbols. + syms = append(syms, infosyms...) + syms = collectlocs(ctxt, syms, ctxt.compUnits) + syms = append(syms, debugRanges) + for _, unit := range ctxt.compUnits { + syms = append(syms, unit.RangeSyms...) + } + dwarfp = syms +} + +func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*sym.CompilationUnit) []*sym.Symbol { + empty := true + for _, u := range units { + for _, fn := range u.FuncDIEs { + for i := range fn.R { + reloc := &fn.R[i] // Copying sym.Reloc has measurable impact on performance + if reloc.Type == objabi.R_DWARFSECREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) { + reloc.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable + syms = append(syms, reloc.Sym) + empty = false + // One location list entry per function, but many relocations to it. Don't duplicate. + break + } + } + } + } + // Don't emit .debug_loc if it's empty -- it makes the ARM linker mad. + if !empty { + locsym := ctxt.Syms.Lookup(".debug_loc", 0) + locsym.Type = sym.SDWARFLOC + locsym.Attr |= sym.AttrReachable + syms = append(syms, locsym) + } + return syms +} + +// Read a pointer-sized uint from the beginning of buf. +func readPtr(ctxt *Link, buf []byte) uint64 { + switch ctxt.Arch.PtrSize { + case 4: + return uint64(ctxt.Arch.ByteOrder.Uint32(buf)) + case 8: + return ctxt.Arch.ByteOrder.Uint64(buf) + default: + panic("unexpected pointer size") + } +} + +/* + * Elf. + */ +func dwarfaddshstrings(ctxt *Link, shstrtab *sym.Symbol) { + if *FlagW { // disable dwarf + return + } + + secs := []string{"abbrev", "frame", "info", "loc", "line", "pubnames", "pubtypes", "gdb_scripts", "ranges"} + for _, sec := range secs { + Addstring(shstrtab, ".debug_"+sec) + if ctxt.LinkMode == LinkExternal { + Addstring(shstrtab, elfRelType+".debug_"+sec) + } else { + Addstring(shstrtab, ".zdebug_"+sec) + } + } +} + +// Add section symbols for DWARF debug info. This is called before +// dwarfaddelfheaders. +func dwarfaddelfsectionsyms(ctxt *Link) { + if *FlagW { // disable dwarf + return + } + if ctxt.LinkMode != LinkExternal { + return + } + + s := ctxt.Syms.Lookup(".debug_info", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_abbrev", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_line", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_frame", 0) + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + s = ctxt.Syms.Lookup(".debug_loc", 0) + if s.Sect != nil { + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + } + s = ctxt.Syms.Lookup(".debug_ranges", 0) + if s.Sect != nil { + putelfsectionsym(ctxt.Out, s, s.Sect.Elfsect.(*ElfShdr).shnum) + } +} + +// dwarfcompress compresses the DWARF sections. Relocations are applied +// on the fly. After this, dwarfp will contain a different (new) set of +// symbols, and sections may have been replaced. +func dwarfcompress(ctxt *Link) { + supported := ctxt.IsELF || ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Hdarwin + if !ctxt.compressDWARF || !supported || ctxt.LinkMode != LinkInternal { + return + } + + var start int + var newDwarfp []*sym.Symbol + Segdwarf.Sections = Segdwarf.Sections[:0] + for i, s := range dwarfp { + // Find the boundaries between sections and compress + // the whole section once we've found the last of its + // symbols. + if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect { + s1 := compressSyms(ctxt, dwarfp[start:i+1]) + if s1 == nil { + // Compression didn't help. + newDwarfp = append(newDwarfp, dwarfp[start:i+1]...) + Segdwarf.Sections = append(Segdwarf.Sections, s.Sect) + } else { + compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):] + sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04) + sect.Length = uint64(len(s1)) + newSym := ctxt.Syms.Lookup(compressedSegName, 0) + newSym.P = s1 + newSym.Size = int64(len(s1)) + newSym.Sect = sect + newDwarfp = append(newDwarfp, newSym) + } + start = i + 1 + } + } + dwarfp = newDwarfp + ctxt.relocbuf = nil // no longer needed, don't hold it live + + // Re-compute the locations of the compressed DWARF symbols + // and sections, since the layout of these within the file is + // based on Section.Vaddr and Symbol.Value. + pos := Segdwarf.Vaddr + var prevSect *sym.Section + for _, s := range dwarfp { + s.Value = int64(pos) + if s.Sect != prevSect { + s.Sect.Vaddr = uint64(s.Value) + prevSect = s.Sect + } + if s.Sub != nil { + log.Fatalf("%s: unexpected sub-symbols", s) + } + pos += uint64(s.Size) + if ctxt.HeadType == objabi.Hwindows { + pos = uint64(Rnd(int64(pos), PEFILEALIGN)) + } + + } + Segdwarf.Length = pos - Segdwarf.Vaddr +} + +type compilationUnitByStartPC []*sym.CompilationUnit + +func (v compilationUnitByStartPC) Len() int { return len(v) } +func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] } + +func (v compilationUnitByStartPC) Less(i, j int) bool { + switch { + case len(v[i].Textp) == 0 && len(v[j].Textp) == 0: + return v[i].Lib.Pkg < v[j].Lib.Pkg + case len(v[i].Textp) != 0 && len(v[j].Textp) == 0: + return true + case len(v[i].Textp) == 0 && len(v[j].Textp) != 0: + return false + default: + return v[i].Textp[0].Value < v[j].Textp[0].Value + } +} + +// On AIX, the symbol table needs to know where are the compilation units parts +// for a specific package in each .dw section. +// dwsectCUSize map will save the size of a compilation unit for +// the corresponding .dw section. +// This size can later be retrieved with the index "sectionName.pkgName". +var dwsectCUSize map[string]uint64 + +// getDwsectCUSize retrieves the corresponding package size inside the current section. +func getDwsectCUSize(sname string, pkgname string) uint64 { + return dwsectCUSize[sname+"."+pkgname] +} + +func saveDwsectCUSize(sname string, pkgname string, size uint64) { + dwsectCUSize[sname+"."+pkgname] = size +} + +func addDwsectCUSize(sname string, pkgname string, size uint64) { + dwsectCUSize[sname+"."+pkgname] += size +} + +// getPkgFromCUSym returns the package name for the compilation unit +// represented by s. +// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get +// the package name. +func getPkgFromCUSym(s *sym.Symbol) string { + return strings.TrimPrefix(s.Name, dwarf.InfoPrefix+".pkg.") +} diff --git a/src/cmd/oldlink/internal/ld/elf.go b/src/cmd/oldlink/internal/ld/elf.go new file mode 100644 index 0000000000..610972118a --- /dev/null +++ b/src/cmd/oldlink/internal/ld/elf.go @@ -0,0 +1,2415 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "crypto/sha1" + "encoding/binary" + "encoding/hex" + "io" + "path/filepath" + "sort" + "strings" +) + +/* + * Derived from: + * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ + * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ + * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ + * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ + * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ + * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * + * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. + * Copyright (c) 2001 David E. O'Brien + * Portions Copyright 2009 The Go Authors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * ELF definitions that are independent of architecture or word size. + */ + +/* + * Note header. The ".note" section contains an array of notes. Each + * begins with this header, aligned to a word boundary. Immediately + * following the note header is n_namesz bytes of name, padded to the + * next word boundary. Then comes n_descsz bytes of descriptor, again + * padded to a word boundary. The values of n_namesz and n_descsz do + * not include the padding. + */ +type elfNote struct { + nNamesz uint32 + nDescsz uint32 + nType uint32 +} + +const ( + EI_MAG0 = 0 + EI_MAG1 = 1 + EI_MAG2 = 2 + EI_MAG3 = 3 + EI_CLASS = 4 + EI_DATA = 5 + EI_VERSION = 6 + EI_OSABI = 7 + EI_ABIVERSION = 8 + OLD_EI_BRAND = 8 + EI_PAD = 9 + EI_NIDENT = 16 + ELFMAG0 = 0x7f + ELFMAG1 = 'E' + ELFMAG2 = 'L' + ELFMAG3 = 'F' + SELFMAG = 4 + EV_NONE = 0 + EV_CURRENT = 1 + ELFCLASSNONE = 0 + ELFCLASS32 = 1 + ELFCLASS64 = 2 + ELFDATANONE = 0 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + ELFOSABI_NONE = 0 + ELFOSABI_HPUX = 1 + ELFOSABI_NETBSD = 2 + ELFOSABI_LINUX = 3 + ELFOSABI_HURD = 4 + ELFOSABI_86OPEN = 5 + ELFOSABI_SOLARIS = 6 + ELFOSABI_AIX = 7 + ELFOSABI_IRIX = 8 + ELFOSABI_FREEBSD = 9 + ELFOSABI_TRU64 = 10 + ELFOSABI_MODESTO = 11 + ELFOSABI_OPENBSD = 12 + ELFOSABI_OPENVMS = 13 + ELFOSABI_NSK = 14 + ELFOSABI_ARM = 97 + ELFOSABI_STANDALONE = 255 + ELFOSABI_SYSV = ELFOSABI_NONE + ELFOSABI_MONTEREY = ELFOSABI_AIX + ET_NONE = 0 + ET_REL = 1 + ET_EXEC = 2 + ET_DYN = 3 + ET_CORE = 4 + ET_LOOS = 0xfe00 + ET_HIOS = 0xfeff + ET_LOPROC = 0xff00 + ET_HIPROC = 0xffff + EM_NONE = 0 + EM_M32 = 1 + EM_SPARC = 2 + EM_386 = 3 + EM_68K = 4 + EM_88K = 5 + EM_860 = 7 + EM_MIPS = 8 + EM_S370 = 9 + EM_MIPS_RS3_LE = 10 + EM_PARISC = 15 + EM_VPP500 = 17 + EM_SPARC32PLUS = 18 + EM_960 = 19 + EM_PPC = 20 + EM_PPC64 = 21 + EM_S390 = 22 + EM_V800 = 36 + EM_FR20 = 37 + EM_RH32 = 38 + EM_RCE = 39 + EM_ARM = 40 + EM_SH = 42 + EM_SPARCV9 = 43 + EM_TRICORE = 44 + EM_ARC = 45 + EM_H8_300 = 46 + EM_H8_300H = 47 + EM_H8S = 48 + EM_H8_500 = 49 + EM_IA_64 = 50 + EM_MIPS_X = 51 + EM_COLDFIRE = 52 + EM_68HC12 = 53 + EM_MMA = 54 + EM_PCP = 55 + EM_NCPU = 56 + EM_NDR1 = 57 + EM_STARCORE = 58 + EM_ME16 = 59 + EM_ST100 = 60 + EM_TINYJ = 61 + EM_X86_64 = 62 + EM_AARCH64 = 183 + EM_486 = 6 + EM_MIPS_RS4_BE = 10 + EM_ALPHA_STD = 41 + EM_ALPHA = 0x9026 + EM_RISCV = 243 + SHN_UNDEF = 0 + SHN_LORESERVE = 0xff00 + SHN_LOPROC = 0xff00 + SHN_HIPROC = 0xff1f + SHN_LOOS = 0xff20 + SHN_HIOS = 0xff3f + SHN_ABS = 0xfff1 + SHN_COMMON = 0xfff2 + SHN_XINDEX = 0xffff + SHN_HIRESERVE = 0xffff + SHT_NULL = 0 + SHT_PROGBITS = 1 + SHT_SYMTAB = 2 + SHT_STRTAB = 3 + SHT_RELA = 4 + SHT_HASH = 5 + SHT_DYNAMIC = 6 + SHT_NOTE = 7 + SHT_NOBITS = 8 + SHT_REL = 9 + SHT_SHLIB = 10 + SHT_DYNSYM = 11 + SHT_INIT_ARRAY = 14 + SHT_FINI_ARRAY = 15 + SHT_PREINIT_ARRAY = 16 + SHT_GROUP = 17 + SHT_SYMTAB_SHNDX = 18 + SHT_LOOS = 0x60000000 + SHT_HIOS = 0x6fffffff + SHT_GNU_VERDEF = 0x6ffffffd + SHT_GNU_VERNEED = 0x6ffffffe + SHT_GNU_VERSYM = 0x6fffffff + SHT_LOPROC = 0x70000000 + SHT_ARM_ATTRIBUTES = 0x70000003 + SHT_HIPROC = 0x7fffffff + SHT_LOUSER = 0x80000000 + SHT_HIUSER = 0xffffffff + SHF_WRITE = 0x1 + SHF_ALLOC = 0x2 + SHF_EXECINSTR = 0x4 + SHF_MERGE = 0x10 + SHF_STRINGS = 0x20 + SHF_INFO_LINK = 0x40 + SHF_LINK_ORDER = 0x80 + SHF_OS_NONCONFORMING = 0x100 + SHF_GROUP = 0x200 + SHF_TLS = 0x400 + SHF_MASKOS = 0x0ff00000 + SHF_MASKPROC = 0xf0000000 + PT_NULL = 0 + PT_LOAD = 1 + PT_DYNAMIC = 2 + PT_INTERP = 3 + PT_NOTE = 4 + PT_SHLIB = 5 + PT_PHDR = 6 + PT_TLS = 7 + PT_LOOS = 0x60000000 + PT_HIOS = 0x6fffffff + PT_LOPROC = 0x70000000 + PT_HIPROC = 0x7fffffff + PT_GNU_STACK = 0x6474e551 + PT_GNU_RELRO = 0x6474e552 + PT_PAX_FLAGS = 0x65041580 + PT_SUNWSTACK = 0x6ffffffb + PF_X = 0x1 + PF_W = 0x2 + PF_R = 0x4 + PF_MASKOS = 0x0ff00000 + PF_MASKPROC = 0xf0000000 + DT_NULL = 0 + DT_NEEDED = 1 + DT_PLTRELSZ = 2 + DT_PLTGOT = 3 + DT_HASH = 4 + DT_STRTAB = 5 + DT_SYMTAB = 6 + DT_RELA = 7 + DT_RELASZ = 8 + DT_RELAENT = 9 + DT_STRSZ = 10 + DT_SYMENT = 11 + DT_INIT = 12 + DT_FINI = 13 + DT_SONAME = 14 + DT_RPATH = 15 + DT_SYMBOLIC = 16 + DT_REL = 17 + DT_RELSZ = 18 + DT_RELENT = 19 + DT_PLTREL = 20 + DT_DEBUG = 21 + DT_TEXTREL = 22 + DT_JMPREL = 23 + DT_BIND_NOW = 24 + DT_INIT_ARRAY = 25 + DT_FINI_ARRAY = 26 + DT_INIT_ARRAYSZ = 27 + DT_FINI_ARRAYSZ = 28 + DT_RUNPATH = 29 + DT_FLAGS = 30 + DT_ENCODING = 32 + DT_PREINIT_ARRAY = 32 + DT_PREINIT_ARRAYSZ = 33 + DT_LOOS = 0x6000000d + DT_HIOS = 0x6ffff000 + DT_LOPROC = 0x70000000 + DT_HIPROC = 0x7fffffff + DT_VERNEED = 0x6ffffffe + DT_VERNEEDNUM = 0x6fffffff + DT_VERSYM = 0x6ffffff0 + DT_PPC64_GLINK = DT_LOPROC + 0 + DT_PPC64_OPT = DT_LOPROC + 3 + DF_ORIGIN = 0x0001 + DF_SYMBOLIC = 0x0002 + DF_TEXTREL = 0x0004 + DF_BIND_NOW = 0x0008 + DF_STATIC_TLS = 0x0010 + NT_PRSTATUS = 1 + NT_FPREGSET = 2 + NT_PRPSINFO = 3 + STB_LOCAL = 0 + STB_GLOBAL = 1 + STB_WEAK = 2 + STB_LOOS = 10 + STB_HIOS = 12 + STB_LOPROC = 13 + STB_HIPROC = 15 + STT_NOTYPE = 0 + STT_OBJECT = 1 + STT_FUNC = 2 + STT_SECTION = 3 + STT_FILE = 4 + STT_COMMON = 5 + STT_TLS = 6 + STT_LOOS = 10 + STT_HIOS = 12 + STT_LOPROC = 13 + STT_HIPROC = 15 + STV_DEFAULT = 0x0 + STV_INTERNAL = 0x1 + STV_HIDDEN = 0x2 + STV_PROTECTED = 0x3 + STN_UNDEF = 0 +) + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Relocation types. + */ +const ( + ARM_MAGIC_TRAMP_NUMBER = 0x5c000003 +) + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * ELF header. + */ +type ElfEhdr struct { + ident [EI_NIDENT]uint8 + type_ uint16 + machine uint16 + version uint32 + entry uint64 + phoff uint64 + shoff uint64 + flags uint32 + ehsize uint16 + phentsize uint16 + phnum uint16 + shentsize uint16 + shnum uint16 + shstrndx uint16 +} + +/* + * Section header. + */ +type ElfShdr struct { + name uint32 + type_ uint32 + flags uint64 + addr uint64 + off uint64 + size uint64 + link uint32 + info uint32 + addralign uint64 + entsize uint64 + shnum int +} + +/* + * Program header. + */ +type ElfPhdr struct { + type_ uint32 + flags uint32 + off uint64 + vaddr uint64 + paddr uint64 + filesz uint64 + memsz uint64 + align uint64 +} + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * Go linker interface + */ +const ( + ELF64HDRSIZE = 64 + ELF64PHDRSIZE = 56 + ELF64SHDRSIZE = 64 + ELF64RELSIZE = 16 + ELF64RELASIZE = 24 + ELF64SYMSIZE = 24 + ELF32HDRSIZE = 52 + ELF32PHDRSIZE = 32 + ELF32SHDRSIZE = 40 + ELF32SYMSIZE = 16 + ELF32RELSIZE = 8 +) + +/* + * The interface uses the 64-bit structures always, + * to avoid code duplication. The writers know how to + * marshal a 32-bit representation from the 64-bit structure. + */ + +var Elfstrdat []byte + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, SHeaders, and interp. + * May waste some. + * On FreeBSD, cannot be larger than a page. + */ +const ( + ELFRESERVE = 4096 +) + +/* + * We use the 64-bit data structures on both 32- and 64-bit machines + * in order to write the code just once. The 64-bit data structure is + * written in the 32-bit format on the 32-bit machines. + */ +const ( + NSECT = 400 +) + +var ( + Nelfsym = 1 + + elf64 bool + // Either ".rel" or ".rela" depending on which type of relocation the + // target platform uses. + elfRelType string + + ehdr ElfEhdr + phdr [NSECT]*ElfPhdr + shdr [NSECT]*ElfShdr + + interp string +) + +type Elfstring struct { + s string + off int +} + +var elfstr [100]Elfstring + +var nelfstr int + +var buildinfo []byte + +/* + Initialize the global variable that describes the ELF header. It will be updated as + we write section and prog headers. +*/ +func Elfinit(ctxt *Link) { + ctxt.IsELF = true + + if ctxt.Arch.InFamily(sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) { + elfRelType = ".rela" + } else { + elfRelType = ".rel" + } + + switch ctxt.Arch.Family { + // 64-bit architectures + case sys.PPC64, sys.S390X: + if ctxt.Arch.ByteOrder == binary.BigEndian { + ehdr.flags = 1 /* Version 1 ABI */ + } else { + ehdr.flags = 2 /* Version 2 ABI */ + } + fallthrough + case sys.AMD64, sys.ARM64, sys.MIPS64, sys.RISCV64: + if ctxt.Arch.Family == sys.MIPS64 { + ehdr.flags = 0x20000004 /* MIPS 3 CPIC */ + } + elf64 = true + + ehdr.phoff = ELF64HDRSIZE /* Must be ELF64HDRSIZE: first PHdr must follow ELF header */ + ehdr.shoff = ELF64HDRSIZE /* Will move as we add PHeaders */ + ehdr.ehsize = ELF64HDRSIZE /* Must be ELF64HDRSIZE */ + ehdr.phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */ + ehdr.shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */ + + // 32-bit architectures + case sys.ARM, sys.MIPS: + if ctxt.Arch.Family == sys.ARM { + // we use EABI on linux/arm, freebsd/arm, netbsd/arm. + if ctxt.HeadType == objabi.Hlinux || ctxt.HeadType == objabi.Hfreebsd || ctxt.HeadType == objabi.Hnetbsd { + // We set a value here that makes no indication of which + // float ABI the object uses, because this is information + // used by the dynamic linker to compare executables and + // shared libraries -- so it only matters for cgo calls, and + // the information properly comes from the object files + // produced by the host C compiler. parseArmAttributes in + // ldelf.go reads that information and updates this field as + // appropriate. + ehdr.flags = 0x5000002 // has entry point, Version5 EABI + } + } else if ctxt.Arch.Family == sys.MIPS { + ehdr.flags = 0x50001004 /* MIPS 32 CPIC O32*/ + } + fallthrough + default: + ehdr.phoff = ELF32HDRSIZE + /* Must be ELF32HDRSIZE: first PHdr must follow ELF header */ + ehdr.shoff = ELF32HDRSIZE /* Will move as we add PHeaders */ + ehdr.ehsize = ELF32HDRSIZE /* Must be ELF32HDRSIZE */ + ehdr.phentsize = ELF32PHDRSIZE /* Must be ELF32PHDRSIZE */ + ehdr.shentsize = ELF32SHDRSIZE /* Must be ELF32SHDRSIZE */ + } +} + +// Make sure PT_LOAD is aligned properly and +// that there is no gap, +// correct ELF loaders will do this implicitly, +// but buggy ELF loaders like the one in some +// versions of QEMU and UPX won't. +func fixElfPhdr(e *ElfPhdr) { + frag := int(e.vaddr & (e.align - 1)) + + e.off -= uint64(frag) + e.vaddr -= uint64(frag) + e.paddr -= uint64(frag) + e.filesz += uint64(frag) + e.memsz += uint64(frag) +} + +func elf64phdr(out *OutBuf, e *ElfPhdr) { + if e.type_ == PT_LOAD { + fixElfPhdr(e) + } + + out.Write32(e.type_) + out.Write32(e.flags) + out.Write64(e.off) + out.Write64(e.vaddr) + out.Write64(e.paddr) + out.Write64(e.filesz) + out.Write64(e.memsz) + out.Write64(e.align) +} + +func elf32phdr(out *OutBuf, e *ElfPhdr) { + if e.type_ == PT_LOAD { + fixElfPhdr(e) + } + + out.Write32(e.type_) + out.Write32(uint32(e.off)) + out.Write32(uint32(e.vaddr)) + out.Write32(uint32(e.paddr)) + out.Write32(uint32(e.filesz)) + out.Write32(uint32(e.memsz)) + out.Write32(e.flags) + out.Write32(uint32(e.align)) +} + +func elf64shdr(out *OutBuf, e *ElfShdr) { + out.Write32(e.name) + out.Write32(e.type_) + out.Write64(e.flags) + out.Write64(e.addr) + out.Write64(e.off) + out.Write64(e.size) + out.Write32(e.link) + out.Write32(e.info) + out.Write64(e.addralign) + out.Write64(e.entsize) +} + +func elf32shdr(out *OutBuf, e *ElfShdr) { + out.Write32(e.name) + out.Write32(e.type_) + out.Write32(uint32(e.flags)) + out.Write32(uint32(e.addr)) + out.Write32(uint32(e.off)) + out.Write32(uint32(e.size)) + out.Write32(e.link) + out.Write32(e.info) + out.Write32(uint32(e.addralign)) + out.Write32(uint32(e.entsize)) +} + +func elfwriteshdrs(out *OutBuf) uint32 { + if elf64 { + for i := 0; i < int(ehdr.shnum); i++ { + elf64shdr(out, shdr[i]) + } + return uint32(ehdr.shnum) * ELF64SHDRSIZE + } + + for i := 0; i < int(ehdr.shnum); i++ { + elf32shdr(out, shdr[i]) + } + return uint32(ehdr.shnum) * ELF32SHDRSIZE +} + +func elfsetstring(s *sym.Symbol, str string, off int) { + if nelfstr >= len(elfstr) { + Errorf(s, "too many elf strings") + errorexit() + } + + elfstr[nelfstr].s = str + elfstr[nelfstr].off = off + nelfstr++ +} + +func elfwritephdrs(out *OutBuf) uint32 { + if elf64 { + for i := 0; i < int(ehdr.phnum); i++ { + elf64phdr(out, phdr[i]) + } + return uint32(ehdr.phnum) * ELF64PHDRSIZE + } + + for i := 0; i < int(ehdr.phnum); i++ { + elf32phdr(out, phdr[i]) + } + return uint32(ehdr.phnum) * ELF32PHDRSIZE +} + +func newElfPhdr() *ElfPhdr { + e := new(ElfPhdr) + if ehdr.phnum >= NSECT { + Errorf(nil, "too many phdrs") + } else { + phdr[ehdr.phnum] = e + ehdr.phnum++ + } + if elf64 { + ehdr.shoff += ELF64PHDRSIZE + } else { + ehdr.shoff += ELF32PHDRSIZE + } + return e +} + +func newElfShdr(name int64) *ElfShdr { + e := new(ElfShdr) + e.name = uint32(name) + e.shnum = int(ehdr.shnum) + if ehdr.shnum >= NSECT { + Errorf(nil, "too many shdrs") + } else { + shdr[ehdr.shnum] = e + ehdr.shnum++ + } + + return e +} + +func getElfEhdr() *ElfEhdr { + return &ehdr +} + +func elf64writehdr(out *OutBuf) uint32 { + out.Write(ehdr.ident[:]) + out.Write16(ehdr.type_) + out.Write16(ehdr.machine) + out.Write32(ehdr.version) + out.Write64(ehdr.entry) + out.Write64(ehdr.phoff) + out.Write64(ehdr.shoff) + out.Write32(ehdr.flags) + out.Write16(ehdr.ehsize) + out.Write16(ehdr.phentsize) + out.Write16(ehdr.phnum) + out.Write16(ehdr.shentsize) + out.Write16(ehdr.shnum) + out.Write16(ehdr.shstrndx) + return ELF64HDRSIZE +} + +func elf32writehdr(out *OutBuf) uint32 { + out.Write(ehdr.ident[:]) + out.Write16(ehdr.type_) + out.Write16(ehdr.machine) + out.Write32(ehdr.version) + out.Write32(uint32(ehdr.entry)) + out.Write32(uint32(ehdr.phoff)) + out.Write32(uint32(ehdr.shoff)) + out.Write32(ehdr.flags) + out.Write16(ehdr.ehsize) + out.Write16(ehdr.phentsize) + out.Write16(ehdr.phnum) + out.Write16(ehdr.shentsize) + out.Write16(ehdr.shnum) + out.Write16(ehdr.shstrndx) + return ELF32HDRSIZE +} + +func elfwritehdr(out *OutBuf) uint32 { + if elf64 { + return elf64writehdr(out) + } + return elf32writehdr(out) +} + +/* Taken directly from the definition document for ELF64 */ +func elfhash(name string) uint32 { + var h uint32 + for i := 0; i < len(name); i++ { + h = (h << 4) + uint32(name[i]) + if g := h & 0xf0000000; g != 0 { + h ^= g >> 24 + } + h &= 0x0fffffff + } + return h +} + +func Elfwritedynent(ctxt *Link, s *sym.Symbol, tag int, val uint64) { + if elf64 { + s.AddUint64(ctxt.Arch, uint64(tag)) + s.AddUint64(ctxt.Arch, val) + } else { + s.AddUint32(ctxt.Arch, uint32(tag)) + s.AddUint32(ctxt.Arch, uint32(val)) + } +} + +func elfwritedynentsym(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) { + Elfwritedynentsymplus(ctxt, s, tag, t, 0) +} + +func Elfwritedynentsymplus(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol, add int64) { + if elf64 { + s.AddUint64(ctxt.Arch, uint64(tag)) + } else { + s.AddUint32(ctxt.Arch, uint32(tag)) + } + s.AddAddrPlus(ctxt.Arch, t, add) +} + +func elfwritedynentsymsize(ctxt *Link, s *sym.Symbol, tag int, t *sym.Symbol) { + if elf64 { + s.AddUint64(ctxt.Arch, uint64(tag)) + } else { + s.AddUint32(ctxt.Arch, uint32(tag)) + } + s.AddSize(ctxt.Arch, t) +} + +func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int { + interp = p + n := len(interp) + 1 + sh.addr = startva + resoff - uint64(n) + sh.off = resoff - uint64(n) + sh.size = uint64(n) + + return n +} + +func elfwriteinterp(out *OutBuf) int { + sh := elfshname(".interp") + out.SeekSet(int64(sh.off)) + out.WriteString(interp) + out.Write8(0) + return int(sh.size) +} + +func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int) int { + n := 3*4 + uint64(sz) + resoff%4 + + sh.type_ = SHT_NOTE + sh.flags = SHF_ALLOC + sh.addralign = 4 + sh.addr = startva + resoff - n + sh.off = resoff - n + sh.size = n - resoff%4 + + return int(n) +} + +func elfwritenotehdr(out *OutBuf, str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr { + sh := elfshname(str) + + // Write Elf_Note header. + out.SeekSet(int64(sh.off)) + + out.Write32(namesz) + out.Write32(descsz) + out.Write32(tag) + + return sh +} + +// NetBSD Signature (as per sys/exec_elf.h) +const ( + ELF_NOTE_NETBSD_NAMESZ = 7 + ELF_NOTE_NETBSD_DESCSZ = 4 + ELF_NOTE_NETBSD_TAG = 1 + ELF_NOTE_NETBSD_VERSION = 700000000 /* NetBSD 7.0 */ +) + +var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00") + +func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritenetbsdsig(out *OutBuf) int { + // Write Elf_Note header. + sh := elfwritenotehdr(out, ".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by NetBSD string and version. + out.Write(ELF_NOTE_NETBSD_NAME) + out.Write8(0) + out.Write32(ELF_NOTE_NETBSD_VERSION) + + return int(sh.size) +} + +// OpenBSD Signature +const ( + ELF_NOTE_OPENBSD_NAMESZ = 8 + ELF_NOTE_OPENBSD_DESCSZ = 4 + ELF_NOTE_OPENBSD_TAG = 1 + ELF_NOTE_OPENBSD_VERSION = 0 +) + +var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00") + +func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ + return elfnote(sh, startva, resoff, n) +} + +func elfwriteopenbsdsig(out *OutBuf) int { + // Write Elf_Note header. + sh := elfwritenotehdr(out, ".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by OpenBSD string and version. + out.Write(ELF_NOTE_OPENBSD_NAME) + + out.Write32(ELF_NOTE_OPENBSD_VERSION) + + return int(sh.size) +} + +func addbuildinfo(val string) { + if !strings.HasPrefix(val, "0x") { + Exitf("-B argument must start with 0x: %s", val) + } + + ov := val + val = val[2:] + + const maxLen = 32 + if hex.DecodedLen(len(val)) > maxLen { + Exitf("-B option too long (max %d digits): %s", maxLen, ov) + } + + b, err := hex.DecodeString(val) + if err != nil { + if err == hex.ErrLength { + Exitf("-B argument must have even number of digits: %s", ov) + } + if inv, ok := err.(hex.InvalidByteError); ok { + Exitf("-B argument contains invalid hex digit %c: %s", byte(inv), ov) + } + Exitf("-B argument contains invalid hex: %s", ov) + } + + buildinfo = b +} + +// Build info note +const ( + ELF_NOTE_BUILDINFO_NAMESZ = 4 + ELF_NOTE_BUILDINFO_TAG = 3 +) + +var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00") + +func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int { + n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(*flagBuildid)), 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritebuildinfo(out *OutBuf) int { + sh := elfwritenotehdr(out, ".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG) + if sh == nil { + return 0 + } + + out.Write(ELF_NOTE_BUILDINFO_NAME) + out.Write(buildinfo) + var zero = make([]byte, 4) + out.Write(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))]) + + return int(sh.size) +} + +func elfwritegobuildid(out *OutBuf) int { + sh := elfwritenotehdr(out, ".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(*flagBuildid)), ELF_NOTE_GOBUILDID_TAG) + if sh == nil { + return 0 + } + + out.Write(ELF_NOTE_GO_NAME) + out.Write([]byte(*flagBuildid)) + var zero = make([]byte, 4) + out.Write(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))]) + + return int(sh.size) +} + +// Go specific notes +const ( + ELF_NOTE_GOPKGLIST_TAG = 1 + ELF_NOTE_GOABIHASH_TAG = 2 + ELF_NOTE_GODEPS_TAG = 3 + ELF_NOTE_GOBUILDID_TAG = 4 +) + +var ELF_NOTE_GO_NAME = []byte("Go\x00\x00") + +var elfverneed int + +type Elfaux struct { + next *Elfaux + num int + vers string +} + +type Elflib struct { + next *Elflib + aux *Elfaux + file string +} + +func addelflib(list **Elflib, file string, vers string) *Elfaux { + var lib *Elflib + + for lib = *list; lib != nil; lib = lib.next { + if lib.file == file { + goto havelib + } + } + lib = new(Elflib) + lib.next = *list + lib.file = file + *list = lib + +havelib: + for aux := lib.aux; aux != nil; aux = aux.next { + if aux.vers == vers { + return aux + } + } + aux := new(Elfaux) + aux.next = lib.aux + aux.vers = vers + lib.aux = aux + + return aux +} + +func elfdynhash(ctxt *Link) { + if !ctxt.IsELF { + return + } + + nsym := Nelfsym + s := ctxt.Syms.Lookup(".hash", 0) + s.Type = sym.SELFROSECT + s.Attr |= sym.AttrReachable + + i := nsym + nbucket := 1 + for i > 0 { + nbucket++ + i >>= 1 + } + + var needlib *Elflib + need := make([]*Elfaux, nsym) + chain := make([]uint32, nsym) + buckets := make([]uint32, nbucket) + + for _, sy := range ctxt.Syms.Allsym { + if sy.Dynid <= 0 { + continue + } + + if sy.Dynimpvers() != "" { + need[sy.Dynid] = addelflib(&needlib, sy.Dynimplib(), sy.Dynimpvers()) + } + + name := sy.Extname() + hc := elfhash(name) + + b := hc % uint32(nbucket) + chain[sy.Dynid] = buckets[b] + buckets[b] = uint32(sy.Dynid) + } + + // s390x (ELF64) hash table entries are 8 bytes + if ctxt.Arch.Family == sys.S390X { + s.AddUint64(ctxt.Arch, uint64(nbucket)) + s.AddUint64(ctxt.Arch, uint64(nsym)) + for i := 0; i < nbucket; i++ { + s.AddUint64(ctxt.Arch, uint64(buckets[i])) + } + for i := 0; i < nsym; i++ { + s.AddUint64(ctxt.Arch, uint64(chain[i])) + } + } else { + s.AddUint32(ctxt.Arch, uint32(nbucket)) + s.AddUint32(ctxt.Arch, uint32(nsym)) + for i := 0; i < nbucket; i++ { + s.AddUint32(ctxt.Arch, buckets[i]) + } + for i := 0; i < nsym; i++ { + s.AddUint32(ctxt.Arch, chain[i]) + } + } + + // version symbols + dynstr := ctxt.Syms.Lookup(".dynstr", 0) + + s = ctxt.Syms.Lookup(".gnu.version_r", 0) + i = 2 + nfile := 0 + for l := needlib; l != nil; l = l.next { + nfile++ + + // header + s.AddUint16(ctxt.Arch, 1) // table version + j := 0 + for x := l.aux; x != nil; x = x.next { + j++ + } + s.AddUint16(ctxt.Arch, uint16(j)) // aux count + s.AddUint32(ctxt.Arch, uint32(Addstring(dynstr, l.file))) // file string offset + s.AddUint32(ctxt.Arch, 16) // offset from header to first aux + if l.next != nil { + s.AddUint32(ctxt.Arch, 16+uint32(j)*16) // offset from this header to next + } else { + s.AddUint32(ctxt.Arch, 0) + } + + for x := l.aux; x != nil; x = x.next { + x.num = i + i++ + + // aux struct + s.AddUint32(ctxt.Arch, elfhash(x.vers)) // hash + s.AddUint16(ctxt.Arch, 0) // flags + s.AddUint16(ctxt.Arch, uint16(x.num)) // other - index we refer to this by + s.AddUint32(ctxt.Arch, uint32(Addstring(dynstr, x.vers))) // version string offset + if x.next != nil { + s.AddUint32(ctxt.Arch, 16) // offset from this aux to next + } else { + s.AddUint32(ctxt.Arch, 0) + } + } + } + + // version references + s = ctxt.Syms.Lookup(".gnu.version", 0) + + for i := 0; i < nsym; i++ { + if i == 0 { + s.AddUint16(ctxt.Arch, 0) // first entry - no symbol + } else if need[i] == nil { + s.AddUint16(ctxt.Arch, 1) // global + } else { + s.AddUint16(ctxt.Arch, uint16(need[i].num)) + } + } + + s = ctxt.Syms.Lookup(".dynamic", 0) + elfverneed = nfile + if elfverneed != 0 { + elfwritedynentsym(ctxt, s, DT_VERNEED, ctxt.Syms.Lookup(".gnu.version_r", 0)) + Elfwritedynent(ctxt, s, DT_VERNEEDNUM, uint64(nfile)) + elfwritedynentsym(ctxt, s, DT_VERSYM, ctxt.Syms.Lookup(".gnu.version", 0)) + } + + sy := ctxt.Syms.Lookup(elfRelType+".plt", 0) + if sy.Size > 0 { + if elfRelType == ".rela" { + Elfwritedynent(ctxt, s, DT_PLTREL, DT_RELA) + } else { + Elfwritedynent(ctxt, s, DT_PLTREL, DT_REL) + } + elfwritedynentsymsize(ctxt, s, DT_PLTRELSZ, sy) + elfwritedynentsym(ctxt, s, DT_JMPREL, sy) + } + + Elfwritedynent(ctxt, s, DT_NULL, 0) +} + +func elfphload(seg *sym.Segment) *ElfPhdr { + ph := newElfPhdr() + ph.type_ = PT_LOAD + if seg.Rwx&4 != 0 { + ph.flags |= PF_R + } + if seg.Rwx&2 != 0 { + ph.flags |= PF_W + } + if seg.Rwx&1 != 0 { + ph.flags |= PF_X + } + ph.vaddr = seg.Vaddr + ph.paddr = seg.Vaddr + ph.memsz = seg.Length + ph.off = seg.Fileoff + ph.filesz = seg.Filelen + ph.align = uint64(*FlagRound) + + return ph +} + +func elfphrelro(seg *sym.Segment) { + ph := newElfPhdr() + ph.type_ = PT_GNU_RELRO + ph.vaddr = seg.Vaddr + ph.paddr = seg.Vaddr + ph.memsz = seg.Length + ph.off = seg.Fileoff + ph.filesz = seg.Filelen + ph.align = uint64(*FlagRound) +} + +func elfshname(name string) *ElfShdr { + for i := 0; i < nelfstr; i++ { + if name != elfstr[i].s { + continue + } + off := elfstr[i].off + for i = 0; i < int(ehdr.shnum); i++ { + sh := shdr[i] + if sh.name == uint32(off) { + return sh + } + } + return newElfShdr(int64(off)) + } + Exitf("cannot find elf name %s", name) + return nil +} + +// Create an ElfShdr for the section with name. +// Create a duplicate if one already exists with that name +func elfshnamedup(name string) *ElfShdr { + for i := 0; i < nelfstr; i++ { + if name == elfstr[i].s { + off := elfstr[i].off + return newElfShdr(int64(off)) + } + } + + Errorf(nil, "cannot find elf name %s", name) + errorexit() + return nil +} + +func elfshalloc(sect *sym.Section) *ElfShdr { + sh := elfshname(sect.Name) + sect.Elfsect = sh + return sh +} + +func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr { + var sh *ElfShdr + + if sect.Name == ".text" { + if sect.Elfsect == nil { + sect.Elfsect = elfshnamedup(sect.Name) + } + sh = sect.Elfsect.(*ElfShdr) + } else { + sh = elfshalloc(sect) + } + + // If this section has already been set up as a note, we assume type_ and + // flags are already correct, but the other fields still need filling in. + if sh.type_ == SHT_NOTE { + if linkmode != LinkExternal { + // TODO(mwhudson): the approach here will work OK when + // linking internally for notes that we want to be included + // in a loadable segment (e.g. the abihash note) but not for + // notes that we do not want to be mapped (e.g. the package + // list note). The real fix is probably to define new values + // for Symbol.Type corresponding to mapped and unmapped notes + // and handle them in dodata(). + Errorf(nil, "sh.type_ == SHT_NOTE in elfshbits when linking internally") + } + sh.addralign = uint64(sect.Align) + sh.size = sect.Length + sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + return sh + } + if sh.type_ > 0 { + return sh + } + + if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { + sh.type_ = SHT_PROGBITS + } else { + sh.type_ = SHT_NOBITS + } + sh.flags = SHF_ALLOC + if sect.Rwx&1 != 0 { + sh.flags |= SHF_EXECINSTR + } + if sect.Rwx&2 != 0 { + sh.flags |= SHF_WRITE + } + if sect.Name == ".tbss" { + sh.flags |= SHF_TLS + sh.type_ = SHT_NOBITS + } + if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") { + sh.flags = 0 + } + + if linkmode != LinkExternal { + sh.addr = sect.Vaddr + } + sh.addralign = uint64(sect.Align) + sh.size = sect.Length + if sect.Name != ".tbss" { + sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + } + + return sh +} + +func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr { + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab or notes. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return nil + } + if sect.Name == ".shstrtab" || sect.Name == ".tbss" { + return nil + } + if sect.Elfsect.(*ElfShdr).type_ == SHT_NOTE { + return nil + } + + typ := SHT_REL + if elfRelType == ".rela" { + typ = SHT_RELA + } + + sh := elfshname(elfRelType + sect.Name) + // There could be multiple text sections but each needs + // its own .rela.text. + + if sect.Name == ".text" { + if sh.info != 0 && sh.info != uint32(sect.Elfsect.(*ElfShdr).shnum) { + sh = elfshnamedup(elfRelType + sect.Name) + } + } + + sh.type_ = uint32(typ) + sh.entsize = uint64(arch.RegSize) * 2 + if typ == SHT_RELA { + sh.entsize += uint64(arch.RegSize) + } + sh.link = uint32(elfshname(".symtab").shnum) + sh.info = uint32(sect.Elfsect.(*ElfShdr).shnum) + sh.off = sect.Reloff + sh.size = sect.Rellen + sh.addralign = uint64(arch.RegSize) + return sh +} + +func elfrelocsect(ctxt *Link, sect *sym.Section, syms []*sym.Symbol) { + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + if sect.Name == ".shstrtab" { + return + } + + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !s.Attr.Reachable() { + continue + } + if uint64(s.Value) >= sect.Vaddr { + syms = syms[i:] + break + } + } + + eaddr := int32(sect.Vaddr + sect.Length) + for _, s := range syms { + if !s.Attr.Reachable() { + continue + } + if s.Value >= int64(eaddr) { + break + } + for ri := range s.R { + r := &s.R[ri] + if r.Done { + continue + } + if r.Xsym == nil { + Errorf(s, "missing xsym in relocation %#v %#v", r.Sym.Name, s) + continue + } + if r.Xsym.ElfsymForReloc() == 0 { + Errorf(s, "reloc %d (%s) to non-elf symbol %s (outer=%s) %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Sym.Name, r.Xsym.Name, r.Sym.Type, r.Sym.Type) + } + if !r.Xsym.Attr.Reachable() { + Errorf(s, "unreachable reloc %d (%s) target %v", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Xsym.Name) + } + if !thearch.Elfreloc1(ctxt, r, int64(uint64(s.Value+int64(r.Off))-sect.Vaddr)) { + Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Siz, r.Sym.Name) + } + } + } + + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff +} + +func Elfemitreloc(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + elfrelocsect(ctxt, sect, ctxt.Textp) + } else { + elfrelocsect(ctxt, sect, datap) + } + } + + for _, sect := range Segrodata.Sections { + elfrelocsect(ctxt, sect, datap) + } + for _, sect := range Segrelrodata.Sections { + elfrelocsect(ctxt, sect, datap) + } + for _, sect := range Segdata.Sections { + elfrelocsect(ctxt, sect, datap) + } + for _, sect := range Segdwarf.Sections { + elfrelocsect(ctxt, sect, dwarfp) + } +} + +func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) { + s := ctxt.Syms.Lookup(sectionName, 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SELFROSECT + // namesz + s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME))) + // descsz + s.AddUint32(ctxt.Arch, uint32(len(desc))) + // tag + s.AddUint32(ctxt.Arch, tag) + // name + padding + s.P = append(s.P, ELF_NOTE_GO_NAME...) + for len(s.P)%4 != 0 { + s.P = append(s.P, 0) + } + // desc + padding + s.P = append(s.P, desc...) + for len(s.P)%4 != 0 { + s.P = append(s.P, 0) + } + s.Size = int64(len(s.P)) + s.Align = 4 +} + +func (ctxt *Link) doelf() { + if !ctxt.IsELF { + return + } + + /* predefine strings we need for section headers */ + shstrtab := ctxt.Syms.Lookup(".shstrtab", 0) + + shstrtab.Type = sym.SELFROSECT + shstrtab.Attr |= sym.AttrReachable + + Addstring(shstrtab, "") + Addstring(shstrtab, ".text") + Addstring(shstrtab, ".noptrdata") + Addstring(shstrtab, ".data") + Addstring(shstrtab, ".bss") + Addstring(shstrtab, ".noptrbss") + Addstring(shstrtab, "__libfuzzer_extra_counters") + Addstring(shstrtab, ".go.buildinfo") + + // generate .tbss section for dynamic internal linker or external + // linking, so that various binutils could correctly calculate + // PT_TLS size. See https://golang.org/issue/5200. + if !*FlagD || ctxt.LinkMode == LinkExternal { + Addstring(shstrtab, ".tbss") + } + if ctxt.HeadType == objabi.Hnetbsd { + Addstring(shstrtab, ".note.netbsd.ident") + } + if ctxt.HeadType == objabi.Hopenbsd { + Addstring(shstrtab, ".note.openbsd.ident") + } + if len(buildinfo) > 0 { + Addstring(shstrtab, ".note.gnu.build-id") + } + if *flagBuildid != "" { + Addstring(shstrtab, ".note.go.buildid") + } + Addstring(shstrtab, ".elfdata") + Addstring(shstrtab, ".rodata") + // See the comment about data.rel.ro.FOO section names in data.go. + relro_prefix := "" + if ctxt.UseRelro() { + Addstring(shstrtab, ".data.rel.ro") + relro_prefix = ".data.rel.ro" + } + Addstring(shstrtab, relro_prefix+".typelink") + Addstring(shstrtab, relro_prefix+".itablink") + Addstring(shstrtab, relro_prefix+".gosymtab") + Addstring(shstrtab, relro_prefix+".gopclntab") + + if ctxt.LinkMode == LinkExternal { + *FlagD = true + + Addstring(shstrtab, elfRelType+".text") + Addstring(shstrtab, elfRelType+".rodata") + Addstring(shstrtab, elfRelType+relro_prefix+".typelink") + Addstring(shstrtab, elfRelType+relro_prefix+".itablink") + Addstring(shstrtab, elfRelType+relro_prefix+".gosymtab") + Addstring(shstrtab, elfRelType+relro_prefix+".gopclntab") + Addstring(shstrtab, elfRelType+".noptrdata") + Addstring(shstrtab, elfRelType+".data") + if ctxt.UseRelro() { + Addstring(shstrtab, elfRelType+".data.rel.ro") + } + Addstring(shstrtab, elfRelType+".go.buildinfo") + + // add a .note.GNU-stack section to mark the stack as non-executable + Addstring(shstrtab, ".note.GNU-stack") + + if ctxt.BuildMode == BuildModeShared { + Addstring(shstrtab, ".note.go.abihash") + Addstring(shstrtab, ".note.go.pkg-list") + Addstring(shstrtab, ".note.go.deps") + } + } + + hasinitarr := ctxt.linkShared + + /* shared library initializer */ + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin: + hasinitarr = true + } + + if hasinitarr { + Addstring(shstrtab, ".init_array") + Addstring(shstrtab, elfRelType+".init_array") + } + + if !*FlagS { + Addstring(shstrtab, ".symtab") + Addstring(shstrtab, ".strtab") + dwarfaddshstrings(ctxt, shstrtab) + } + + Addstring(shstrtab, ".shstrtab") + + if !*FlagD { /* -d suppresses dynamic loader format */ + Addstring(shstrtab, ".interp") + Addstring(shstrtab, ".hash") + Addstring(shstrtab, ".got") + if ctxt.Arch.Family == sys.PPC64 { + Addstring(shstrtab, ".glink") + } + Addstring(shstrtab, ".got.plt") + Addstring(shstrtab, ".dynamic") + Addstring(shstrtab, ".dynsym") + Addstring(shstrtab, ".dynstr") + Addstring(shstrtab, elfRelType) + Addstring(shstrtab, elfRelType+".plt") + + Addstring(shstrtab, ".plt") + Addstring(shstrtab, ".gnu.version") + Addstring(shstrtab, ".gnu.version_r") + + /* dynamic symbol table - first entry all zeros */ + s := ctxt.Syms.Lookup(".dynsym", 0) + + s.Type = sym.SELFROSECT + s.Attr |= sym.AttrReachable + if elf64 { + s.Size += ELF64SYMSIZE + } else { + s.Size += ELF32SYMSIZE + } + + /* dynamic string table */ + s = ctxt.Syms.Lookup(".dynstr", 0) + + s.Type = sym.SELFROSECT + s.Attr |= sym.AttrReachable + if s.Size == 0 { + Addstring(s, "") + } + dynstr := s + + /* relocation table */ + s = ctxt.Syms.Lookup(elfRelType, 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SELFROSECT + + /* global offset table */ + s = ctxt.Syms.Lookup(".got", 0) + + s.Attr |= sym.AttrReachable + s.Type = sym.SELFGOT // writable + + /* ppc64 glink resolver */ + if ctxt.Arch.Family == sys.PPC64 { + s := ctxt.Syms.Lookup(".glink", 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SELFRXSECT + } + + /* hash */ + s = ctxt.Syms.Lookup(".hash", 0) + + s.Attr |= sym.AttrReachable + s.Type = sym.SELFROSECT + + s = ctxt.Syms.Lookup(".got.plt", 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SELFSECT // writable + + s = ctxt.Syms.Lookup(".plt", 0) + + s.Attr |= sym.AttrReachable + if ctxt.Arch.Family == sys.PPC64 { + // In the ppc64 ABI, .plt is a data section + // written by the dynamic linker. + s.Type = sym.SELFSECT + } else { + s.Type = sym.SELFRXSECT + } + + thearch.Elfsetupplt(ctxt) + + s = ctxt.Syms.Lookup(elfRelType+".plt", 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SELFROSECT + + s = ctxt.Syms.Lookup(".gnu.version", 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SELFROSECT + + s = ctxt.Syms.Lookup(".gnu.version_r", 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SELFROSECT + + /* define dynamic elf table */ + s = ctxt.Syms.Lookup(".dynamic", 0) + + s.Attr |= sym.AttrReachable + s.Type = sym.SELFSECT // writable + + /* + * .dynamic table + */ + elfwritedynentsym(ctxt, s, DT_HASH, ctxt.Syms.Lookup(".hash", 0)) + + elfwritedynentsym(ctxt, s, DT_SYMTAB, ctxt.Syms.Lookup(".dynsym", 0)) + if elf64 { + Elfwritedynent(ctxt, s, DT_SYMENT, ELF64SYMSIZE) + } else { + Elfwritedynent(ctxt, s, DT_SYMENT, ELF32SYMSIZE) + } + elfwritedynentsym(ctxt, s, DT_STRTAB, ctxt.Syms.Lookup(".dynstr", 0)) + elfwritedynentsymsize(ctxt, s, DT_STRSZ, ctxt.Syms.Lookup(".dynstr", 0)) + if elfRelType == ".rela" { + elfwritedynentsym(ctxt, s, DT_RELA, ctxt.Syms.Lookup(".rela", 0)) + elfwritedynentsymsize(ctxt, s, DT_RELASZ, ctxt.Syms.Lookup(".rela", 0)) + Elfwritedynent(ctxt, s, DT_RELAENT, ELF64RELASIZE) + } else { + elfwritedynentsym(ctxt, s, DT_REL, ctxt.Syms.Lookup(".rel", 0)) + elfwritedynentsymsize(ctxt, s, DT_RELSZ, ctxt.Syms.Lookup(".rel", 0)) + Elfwritedynent(ctxt, s, DT_RELENT, ELF32RELSIZE) + } + + if rpath.val != "" { + Elfwritedynent(ctxt, s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val))) + } + + if ctxt.Arch.Family == sys.PPC64 { + elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".plt", 0)) + } else if ctxt.Arch.Family == sys.S390X { + elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got", 0)) + } else { + elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got.plt", 0)) + } + + if ctxt.Arch.Family == sys.PPC64 { + Elfwritedynent(ctxt, s, DT_PPC64_OPT, 0) + } + + // Solaris dynamic linker can't handle an empty .rela.plt if + // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, + // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the + // size of .rel(a).plt section. + Elfwritedynent(ctxt, s, DT_DEBUG, 0) + } + + if ctxt.BuildMode == BuildModeShared { + // The go.link.abihashbytes symbol will be pointed at the appropriate + // part of the .note.go.abihash section in data.go:func address(). + s := ctxt.Syms.Lookup("go.link.abihashbytes", 0) + s.Attr |= sym.AttrLocal + s.Type = sym.SRODATA + s.Attr |= sym.AttrSpecial + s.Attr |= sym.AttrReachable + s.Size = int64(sha1.Size) + + sort.Sort(byPkg(ctxt.Library)) + h := sha1.New() + for _, l := range ctxt.Library { + io.WriteString(h, l.Hash) + } + addgonote(ctxt, ".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{})) + addgonote(ctxt, ".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote) + var deplist []string + for _, shlib := range ctxt.Shlibs { + deplist = append(deplist, filepath.Base(shlib.Path)) + } + addgonote(ctxt, ".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n"))) + } + + if ctxt.LinkMode == LinkExternal && *flagBuildid != "" { + addgonote(ctxt, ".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(*flagBuildid)) + } +} + +// Do not write DT_NULL. elfdynhash will finish it. +func shsym(sh *ElfShdr, s *sym.Symbol) { + addr := Symaddr(s) + if sh.flags&SHF_ALLOC != 0 { + sh.addr = uint64(addr) + } + sh.off = uint64(datoff(s, addr)) + sh.size = uint64(s.Size) +} + +func phsh(ph *ElfPhdr, sh *ElfShdr) { + ph.vaddr = sh.addr + ph.paddr = ph.vaddr + ph.off = sh.off + ph.filesz = sh.size + ph.memsz = sh.size + ph.align = sh.addralign +} + +func Asmbelfsetup() { + /* This null SHdr must appear before all others */ + elfshname("") + + for _, sect := range Segtext.Sections { + // There could be multiple .text sections. Instead check the Elfsect + // field to determine if already has an ElfShdr and if not, create one. + if sect.Name == ".text" { + if sect.Elfsect == nil { + sect.Elfsect = elfshnamedup(sect.Name) + } + } else { + elfshalloc(sect) + } + } + for _, sect := range Segrodata.Sections { + elfshalloc(sect) + } + for _, sect := range Segrelrodata.Sections { + elfshalloc(sect) + } + for _, sect := range Segdata.Sections { + elfshalloc(sect) + } + for _, sect := range Segdwarf.Sections { + elfshalloc(sect) + } +} + +func Asmbelf(ctxt *Link, symo int64) { + eh := getElfEhdr() + switch ctxt.Arch.Family { + default: + Exitf("unknown architecture in asmbelf: %v", ctxt.Arch.Family) + case sys.MIPS, sys.MIPS64: + eh.machine = EM_MIPS + case sys.ARM: + eh.machine = EM_ARM + case sys.AMD64: + eh.machine = EM_X86_64 + case sys.ARM64: + eh.machine = EM_AARCH64 + case sys.I386: + eh.machine = EM_386 + case sys.PPC64: + eh.machine = EM_PPC64 + case sys.RISCV64: + eh.machine = EM_RISCV + case sys.S390X: + eh.machine = EM_S390 + } + + elfreserve := int64(ELFRESERVE) + + numtext := int64(0) + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + numtext++ + } + } + + // If there are multiple text sections, extra space is needed + // in the elfreserve for the additional .text and .rela.text + // section headers. It can handle 4 extra now. Headers are + // 64 bytes. + + if numtext > 4 { + elfreserve += elfreserve + numtext*64*2 + } + + startva := *FlagTextAddr - int64(HEADR) + resoff := elfreserve + + var pph *ElfPhdr + var pnote *ElfPhdr + if ctxt.LinkMode == LinkExternal { + /* skip program headers */ + eh.phoff = 0 + + eh.phentsize = 0 + + if ctxt.BuildMode == BuildModeShared { + sh := elfshname(".note.go.pkg-list") + sh.type_ = SHT_NOTE + sh = elfshname(".note.go.abihash") + sh.type_ = SHT_NOTE + sh.flags = SHF_ALLOC + sh = elfshname(".note.go.deps") + sh.type_ = SHT_NOTE + } + + if *flagBuildid != "" { + sh := elfshname(".note.go.buildid") + sh.type_ = SHT_NOTE + sh.flags = SHF_ALLOC + } + + goto elfobj + } + + /* program header info */ + pph = newElfPhdr() + + pph.type_ = PT_PHDR + pph.flags = PF_R + pph.off = uint64(eh.ehsize) + pph.vaddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.off + pph.paddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.off + pph.align = uint64(*FlagRound) + + /* + * PHDR must be in a loaded segment. Adjust the text + * segment boundaries downwards to include it. + */ + { + o := int64(Segtext.Vaddr - pph.vaddr) + Segtext.Vaddr -= uint64(o) + Segtext.Length += uint64(o) + o = int64(Segtext.Fileoff - pph.off) + Segtext.Fileoff -= uint64(o) + Segtext.Filelen += uint64(o) + } + + if !*FlagD { /* -d suppresses dynamic loader format */ + /* interpreter */ + sh := elfshname(".interp") + + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + sh.addralign = 1 + + if interpreter == "" && objabi.GO_LDSO != "" { + interpreter = objabi.GO_LDSO + } + + if interpreter == "" { + switch ctxt.HeadType { + case objabi.Hlinux: + if objabi.GOOS == "android" { + interpreter = thearch.Androiddynld + if interpreter == "" { + Exitf("ELF interpreter not set") + } + } else { + interpreter = thearch.Linuxdynld + } + + case objabi.Hfreebsd: + interpreter = thearch.Freebsddynld + + case objabi.Hnetbsd: + interpreter = thearch.Netbsddynld + + case objabi.Hopenbsd: + interpreter = thearch.Openbsddynld + + case objabi.Hdragonfly: + interpreter = thearch.Dragonflydynld + + case objabi.Hsolaris: + interpreter = thearch.Solarisdynld + } + } + + resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter)) + + ph := newElfPhdr() + ph.type_ = PT_INTERP + ph.flags = PF_R + phsh(ph, sh) + } + + pnote = nil + if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd { + var sh *ElfShdr + switch ctxt.HeadType { + case objabi.Hnetbsd: + sh = elfshname(".note.netbsd.ident") + resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff))) + + case objabi.Hopenbsd: + sh = elfshname(".note.openbsd.ident") + resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff))) + } + + pnote = newElfPhdr() + pnote.type_ = PT_NOTE + pnote.flags = PF_R + phsh(pnote, sh) + } + + if len(buildinfo) > 0 { + sh := elfshname(".note.gnu.build-id") + resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff))) + + if pnote == nil { + pnote = newElfPhdr() + pnote.type_ = PT_NOTE + pnote.flags = PF_R + } + + phsh(pnote, sh) + } + + if *flagBuildid != "" { + sh := elfshname(".note.go.buildid") + resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff))) + + pnote := newElfPhdr() + pnote.type_ = PT_NOTE + pnote.flags = PF_R + phsh(pnote, sh) + } + + // Additions to the reserved area must be above this line. + + elfphload(&Segtext) + if len(Segrodata.Sections) > 0 { + elfphload(&Segrodata) + } + if len(Segrelrodata.Sections) > 0 { + elfphload(&Segrelrodata) + elfphrelro(&Segrelrodata) + } + elfphload(&Segdata) + + /* Dynamic linking sections */ + if !*FlagD { + sh := elfshname(".dynsym") + sh.type_ = SHT_DYNSYM + sh.flags = SHF_ALLOC + if elf64 { + sh.entsize = ELF64SYMSIZE + } else { + sh.entsize = ELF32SYMSIZE + } + sh.addralign = uint64(ctxt.Arch.RegSize) + sh.link = uint32(elfshname(".dynstr").shnum) + + // sh.info is the index of first non-local symbol (number of local symbols) + s := ctxt.Syms.Lookup(".dynsym", 0) + i := uint32(0) + for sub := s; sub != nil; sub = sub.Sub { + i++ + if !sub.Attr.Local() { + break + } + } + sh.info = i + shsym(sh, s) + + sh = elfshname(".dynstr") + sh.type_ = SHT_STRTAB + sh.flags = SHF_ALLOC + sh.addralign = 1 + shsym(sh, ctxt.Syms.Lookup(".dynstr", 0)) + + if elfverneed != 0 { + sh := elfshname(".gnu.version") + sh.type_ = SHT_GNU_VERSYM + sh.flags = SHF_ALLOC + sh.addralign = 2 + sh.link = uint32(elfshname(".dynsym").shnum) + sh.entsize = 2 + shsym(sh, ctxt.Syms.Lookup(".gnu.version", 0)) + + sh = elfshname(".gnu.version_r") + sh.type_ = SHT_GNU_VERNEED + sh.flags = SHF_ALLOC + sh.addralign = uint64(ctxt.Arch.RegSize) + sh.info = uint32(elfverneed) + sh.link = uint32(elfshname(".dynstr").shnum) + shsym(sh, ctxt.Syms.Lookup(".gnu.version_r", 0)) + } + + if elfRelType == ".rela" { + sh := elfshname(".rela.plt") + sh.type_ = SHT_RELA + sh.flags = SHF_ALLOC + sh.entsize = ELF64RELASIZE + sh.addralign = uint64(ctxt.Arch.RegSize) + sh.link = uint32(elfshname(".dynsym").shnum) + sh.info = uint32(elfshname(".plt").shnum) + shsym(sh, ctxt.Syms.Lookup(".rela.plt", 0)) + + sh = elfshname(".rela") + sh.type_ = SHT_RELA + sh.flags = SHF_ALLOC + sh.entsize = ELF64RELASIZE + sh.addralign = 8 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ctxt.Syms.Lookup(".rela", 0)) + } else { + sh := elfshname(".rel.plt") + sh.type_ = SHT_REL + sh.flags = SHF_ALLOC + sh.entsize = ELF32RELSIZE + sh.addralign = 4 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ctxt.Syms.Lookup(".rel.plt", 0)) + + sh = elfshname(".rel") + sh.type_ = SHT_REL + sh.flags = SHF_ALLOC + sh.entsize = ELF32RELSIZE + sh.addralign = 4 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ctxt.Syms.Lookup(".rel", 0)) + } + + if eh.machine == EM_PPC64 { + sh := elfshname(".glink") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_EXECINSTR + sh.addralign = 4 + shsym(sh, ctxt.Syms.Lookup(".glink", 0)) + } + + sh = elfshname(".plt") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_EXECINSTR + if eh.machine == EM_X86_64 { + sh.entsize = 16 + } else if eh.machine == EM_S390 { + sh.entsize = 32 + } else if eh.machine == EM_PPC64 { + // On ppc64, this is just a table of addresses + // filled by the dynamic linker + sh.type_ = SHT_NOBITS + + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = 8 + } else { + sh.entsize = 4 + } + sh.addralign = sh.entsize + shsym(sh, ctxt.Syms.Lookup(".plt", 0)) + + // On ppc64, .got comes from the input files, so don't + // create it here, and .got.plt is not used. + if eh.machine != EM_PPC64 { + sh := elfshname(".got") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = uint64(ctxt.Arch.RegSize) + sh.addralign = uint64(ctxt.Arch.RegSize) + shsym(sh, ctxt.Syms.Lookup(".got", 0)) + + sh = elfshname(".got.plt") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = uint64(ctxt.Arch.RegSize) + sh.addralign = uint64(ctxt.Arch.RegSize) + shsym(sh, ctxt.Syms.Lookup(".got.plt", 0)) + } + + sh = elfshname(".hash") + sh.type_ = SHT_HASH + sh.flags = SHF_ALLOC + sh.entsize = 4 + sh.addralign = uint64(ctxt.Arch.RegSize) + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ctxt.Syms.Lookup(".hash", 0)) + + /* sh and PT_DYNAMIC for .dynamic section */ + sh = elfshname(".dynamic") + + sh.type_ = SHT_DYNAMIC + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = 2 * uint64(ctxt.Arch.RegSize) + sh.addralign = uint64(ctxt.Arch.RegSize) + sh.link = uint32(elfshname(".dynstr").shnum) + shsym(sh, ctxt.Syms.Lookup(".dynamic", 0)) + ph := newElfPhdr() + ph.type_ = PT_DYNAMIC + ph.flags = PF_R + PF_W + phsh(ph, sh) + + /* + * Thread-local storage segment (really just size). + */ + tlssize := uint64(0) + for _, sect := range Segdata.Sections { + if sect.Name == ".tbss" { + tlssize = sect.Length + } + } + if tlssize != 0 { + ph := newElfPhdr() + ph.type_ = PT_TLS + ph.flags = PF_R + ph.memsz = tlssize + ph.align = uint64(ctxt.Arch.RegSize) + } + } + + if ctxt.HeadType == objabi.Hlinux { + ph := newElfPhdr() + ph.type_ = PT_GNU_STACK + ph.flags = PF_W + PF_R + ph.align = uint64(ctxt.Arch.RegSize) + + ph = newElfPhdr() + ph.type_ = PT_PAX_FLAGS + ph.flags = 0x2a00 // mprotect, randexec, emutramp disabled + ph.align = uint64(ctxt.Arch.RegSize) + } else if ctxt.HeadType == objabi.Hsolaris { + ph := newElfPhdr() + ph.type_ = PT_SUNWSTACK + ph.flags = PF_W + PF_R + } + +elfobj: + sh := elfshname(".shstrtab") + sh.type_ = SHT_STRTAB + sh.addralign = 1 + shsym(sh, ctxt.Syms.Lookup(".shstrtab", 0)) + eh.shstrndx = uint16(sh.shnum) + + // put these sections early in the list + if !*FlagS { + elfshname(".symtab") + elfshname(".strtab") + } + + for _, sect := range Segtext.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segrodata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segrelrodata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segdata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segdwarf.Sections { + elfshbits(ctxt.LinkMode, sect) + } + + if ctxt.LinkMode == LinkExternal { + for _, sect := range Segtext.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segrodata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segrelrodata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segdata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, s := range dwarfp { + if len(s.R) > 0 || s.Type == sym.SDWARFINFO || s.Type == sym.SDWARFLOC { + elfshreloc(ctxt.Arch, s.Sect) + } + } + // add a .note.GNU-stack section to mark the stack as non-executable + sh := elfshname(".note.GNU-stack") + + sh.type_ = SHT_PROGBITS + sh.addralign = 1 + sh.flags = 0 + } + + if !*FlagS { + sh := elfshname(".symtab") + sh.type_ = SHT_SYMTAB + sh.off = uint64(symo) + sh.size = uint64(Symsize) + sh.addralign = uint64(ctxt.Arch.RegSize) + sh.entsize = 8 + 2*uint64(ctxt.Arch.RegSize) + sh.link = uint32(elfshname(".strtab").shnum) + sh.info = uint32(elfglobalsymndx) + + sh = elfshname(".strtab") + sh.type_ = SHT_STRTAB + sh.off = uint64(symo) + uint64(Symsize) + sh.size = uint64(len(Elfstrdat)) + sh.addralign = 1 + } + + /* Main header */ + eh.ident[EI_MAG0] = '\177' + + eh.ident[EI_MAG1] = 'E' + eh.ident[EI_MAG2] = 'L' + eh.ident[EI_MAG3] = 'F' + if ctxt.HeadType == objabi.Hfreebsd { + eh.ident[EI_OSABI] = ELFOSABI_FREEBSD + } else if ctxt.HeadType == objabi.Hnetbsd { + eh.ident[EI_OSABI] = ELFOSABI_NETBSD + } else if ctxt.HeadType == objabi.Hopenbsd { + eh.ident[EI_OSABI] = ELFOSABI_OPENBSD + } else if ctxt.HeadType == objabi.Hdragonfly { + eh.ident[EI_OSABI] = ELFOSABI_NONE + } + if elf64 { + eh.ident[EI_CLASS] = ELFCLASS64 + } else { + eh.ident[EI_CLASS] = ELFCLASS32 + } + if ctxt.Arch.ByteOrder == binary.BigEndian { + eh.ident[EI_DATA] = ELFDATA2MSB + } else { + eh.ident[EI_DATA] = ELFDATA2LSB + } + eh.ident[EI_VERSION] = EV_CURRENT + + if ctxt.LinkMode == LinkExternal { + eh.type_ = ET_REL + } else if ctxt.BuildMode == BuildModePIE { + eh.type_ = ET_DYN + } else { + eh.type_ = ET_EXEC + } + + if ctxt.LinkMode != LinkExternal { + eh.entry = uint64(Entryvalue(ctxt)) + } + + eh.version = EV_CURRENT + + if pph != nil { + pph.filesz = uint64(eh.phnum) * uint64(eh.phentsize) + pph.memsz = pph.filesz + } + + ctxt.Out.SeekSet(0) + a := int64(0) + a += int64(elfwritehdr(ctxt.Out)) + a += int64(elfwritephdrs(ctxt.Out)) + a += int64(elfwriteshdrs(ctxt.Out)) + if !*FlagD { + a += int64(elfwriteinterp(ctxt.Out)) + } + if ctxt.LinkMode != LinkExternal { + if ctxt.HeadType == objabi.Hnetbsd { + a += int64(elfwritenetbsdsig(ctxt.Out)) + } + if ctxt.HeadType == objabi.Hopenbsd { + a += int64(elfwriteopenbsdsig(ctxt.Out)) + } + if len(buildinfo) > 0 { + a += int64(elfwritebuildinfo(ctxt.Out)) + } + if *flagBuildid != "" { + a += int64(elfwritegobuildid(ctxt.Out)) + } + } + + if a > elfreserve { + Errorf(nil, "ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext) + } +} + +func elfadddynsym(ctxt *Link, s *sym.Symbol) { + if elf64 { + s.Dynid = int32(Nelfsym) + Nelfsym++ + + d := ctxt.Syms.Lookup(".dynsym", 0) + + name := s.Extname() + d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name))) + + /* type */ + t := STB_GLOBAL << 4 + + if s.Attr.CgoExport() && s.Type == sym.STEXT { + t |= STT_FUNC + } else { + t |= STT_OBJECT + } + d.AddUint8(uint8(t)) + + /* reserved */ + d.AddUint8(0) + + /* section where symbol is defined */ + if s.Type == sym.SDYNIMPORT { + d.AddUint16(ctxt.Arch, SHN_UNDEF) + } else { + d.AddUint16(ctxt.Arch, 1) + } + + /* value */ + if s.Type == sym.SDYNIMPORT { + d.AddUint64(ctxt.Arch, 0) + } else { + d.AddAddr(ctxt.Arch, s) + } + + /* size of object */ + d.AddUint64(ctxt.Arch, uint64(s.Size)) + + if ctxt.Arch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib() != "" && !seenlib[s.Dynimplib()] { + Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(ctxt.Syms.Lookup(".dynstr", 0), s.Dynimplib()))) + } + } else { + s.Dynid = int32(Nelfsym) + Nelfsym++ + + d := ctxt.Syms.Lookup(".dynsym", 0) + + /* name */ + name := s.Extname() + + d.AddUint32(ctxt.Arch, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name))) + + /* value */ + if s.Type == sym.SDYNIMPORT { + d.AddUint32(ctxt.Arch, 0) + } else { + d.AddAddr(ctxt.Arch, s) + } + + /* size of object */ + d.AddUint32(ctxt.Arch, uint32(s.Size)) + + /* type */ + t := STB_GLOBAL << 4 + + // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386. + if ctxt.Arch.Family == sys.I386 && s.Attr.CgoExport() && s.Type == sym.STEXT { + t |= STT_FUNC + } else if ctxt.Arch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type == sym.STEXT { + t |= STT_FUNC + } else { + t |= STT_OBJECT + } + d.AddUint8(uint8(t)) + d.AddUint8(0) + + /* shndx */ + if s.Type == sym.SDYNIMPORT { + d.AddUint16(ctxt.Arch, SHN_UNDEF) + } else { + d.AddUint16(ctxt.Arch, 1) + } + } +} + +func ELF32_R_SYM(info uint32) uint32 { + return info >> 8 +} + +func ELF32_R_TYPE(info uint32) uint32 { + return uint32(uint8(info)) +} + +func ELF32_R_INFO(sym uint32, type_ uint32) uint32 { + return sym<<8 | type_ +} + +func ELF32_ST_BIND(info uint8) uint8 { + return info >> 4 +} + +func ELF32_ST_TYPE(info uint8) uint8 { + return info & 0xf +} + +func ELF32_ST_INFO(bind uint8, type_ uint8) uint8 { + return bind<<4 | type_&0xf +} + +func ELF32_ST_VISIBILITY(oth uint8) uint8 { + return oth & 3 +} + +func ELF64_R_SYM(info uint64) uint32 { + return uint32(info >> 32) +} + +func ELF64_R_TYPE(info uint64) uint32 { + return uint32(info) +} + +func ELF64_R_INFO(sym uint32, type_ uint32) uint64 { + return uint64(sym)<<32 | uint64(type_) +} + +func ELF64_ST_BIND(info uint8) uint8 { + return info >> 4 +} + +func ELF64_ST_TYPE(info uint8) uint8 { + return info & 0xf +} + +func ELF64_ST_INFO(bind uint8, type_ uint8) uint8 { + return bind<<4 | type_&0xf +} + +func ELF64_ST_VISIBILITY(oth uint8) uint8 { + return oth & 3 +} diff --git a/src/cmd/oldlink/internal/ld/execarchive.go b/src/cmd/oldlink/internal/ld/execarchive.go new file mode 100644 index 0000000000..fe5cc40865 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/execarchive.go @@ -0,0 +1,37 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !wasm,!windows + +package ld + +import ( + "os" + "os/exec" + "path/filepath" + "syscall" +) + +const syscallExecSupported = true + +// execArchive invokes the archiver tool with syscall.Exec(), with +// the expectation that this is the last thing that takes place +// in the linking operation. +func (ctxt *Link) execArchive(argv []string) { + var err error + argv0 := argv[0] + if filepath.Base(argv0) == argv0 { + argv0, err = exec.LookPath(argv0) + if err != nil { + Exitf("cannot find %s: %v", argv[0], err) + } + } + if ctxt.Debugvlog != 0 { + ctxt.Logf("invoking archiver with syscall.Exec()\n") + } + err = syscall.Exec(argv0, argv, os.Environ()) + if err != nil { + Exitf("running %s failed: %v", argv[0], err) + } +} diff --git a/src/cmd/oldlink/internal/ld/execarchive_noexec.go b/src/cmd/oldlink/internal/ld/execarchive_noexec.go new file mode 100644 index 0000000000..a70dea9fda --- /dev/null +++ b/src/cmd/oldlink/internal/ld/execarchive_noexec.go @@ -0,0 +1,13 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build wasm windows + +package ld + +const syscallExecSupported = false + +func (ctxt *Link) execArchive(argv []string) { + panic("should never arrive here") +} diff --git a/src/cmd/oldlink/internal/ld/go.go b/src/cmd/oldlink/internal/ld/go.go new file mode 100644 index 0000000000..b05265580b --- /dev/null +++ b/src/cmd/oldlink/internal/ld/go.go @@ -0,0 +1,442 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go-specific code shared across loaders (5l, 6l, 8l). + +package ld + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/oldlink/internal/sym" + "encoding/json" + "fmt" + "io" + "os" + "strings" +) + +// go-specific code shared across loaders (5l, 6l, 8l). + +// replace all "". with pkg. +func expandpkg(t0 string, pkg string) string { + return strings.Replace(t0, `"".`, pkg+".", -1) +} + +func resolveABIAlias(s *sym.Symbol) *sym.Symbol { + if s.Type != sym.SABIALIAS { + return s + } + target := s.R[0].Sym + if target.Type == sym.SABIALIAS { + panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", s, target)) + } + return target +} + +// TODO: +// generate debugging section in binary. +// once the dust settles, try to move some code to +// libmach, so that other linkers and ar can share. + +func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) { + if *flagG { + return + } + + if int64(int(length)) != length { + fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) + if *flagU { + errorexit() + } + return + } + + bdata := make([]byte, length) + if _, err := io.ReadFull(f, bdata); err != nil { + fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) + if *flagU { + errorexit() + } + return + } + data := string(bdata) + + // process header lines + for data != "" { + var line string + if i := strings.Index(data, "\n"); i >= 0 { + line, data = data[:i], data[i+1:] + } else { + line, data = data, "" + } + if line == "safe" { + lib.Safe = true + } + if line == "main" { + lib.Main = true + } + if line == "" { + break + } + } + + // look for cgo section + p0 := strings.Index(data, "\n$$ // cgo") + var p1 int + if p0 >= 0 { + p0 += p1 + i := strings.IndexByte(data[p0+1:], '\n') + if i < 0 { + fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) + if *flagU { + errorexit() + } + return + } + p0 += 1 + i + + p1 = strings.Index(data[p0:], "\n$$") + if p1 < 0 { + p1 = strings.Index(data[p0:], "\n!\n") + } + if p1 < 0 { + fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) + if *flagU { + errorexit() + } + return + } + p1 += p0 + loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1]) + } +} + +func loadcgo(ctxt *Link, file string, pkg string, p string) { + var directives [][]string + if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil { + fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err) + nerrors++ + return + } + + // Find cgo_export symbols. They are roots in the deadcode pass. + for _, f := range directives { + switch f[0] { + case "cgo_export_static", "cgo_export_dynamic": + if len(f) < 2 || len(f) > 3 { + continue + } + local := f[1] + switch ctxt.BuildMode { + case BuildModeCShared, BuildModeCArchive, BuildModePlugin: + if local == "main" { + continue + } + } + local = expandpkg(local, pkg) + if f[0] == "cgo_export_static" { + ctxt.cgo_export_static[local] = true + } else { + ctxt.cgo_export_dynamic[local] = true + } + } + } + + if *flagNewobj { + // Record the directives. We'll process them later after Symbols are created. + ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives}) + } else { + setCgoAttr(ctxt, ctxt.Syms.Lookup, file, pkg, directives) + } +} + +// Set symbol attributes or flags based on cgo directives. +func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, pkg string, directives [][]string) { + for _, f := range directives { + switch f[0] { + case "cgo_import_dynamic": + if len(f) < 2 || len(f) > 4 { + break + } + + local := f[1] + remote := local + if len(f) > 2 { + remote = f[2] + } + lib := "" + if len(f) > 3 { + lib = f[3] + } + + if *FlagD { + fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file) + nerrors++ + return + } + + if local == "_" && remote == "_" { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + havedynamic = 1 + + if ctxt.HeadType == objabi.Hdarwin { + machoadddynlib(lib, ctxt.LinkMode) + } else { + dynlib = append(dynlib, lib) + } + continue + } + + local = expandpkg(local, pkg) + q := "" + if i := strings.Index(remote, "#"); i >= 0 { + remote, q = remote[:i], remote[i+1:] + } + s := lookup(local, 0) + if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS || s.Type == sym.SHOSTOBJ { + s.SetDynimplib(lib) + s.SetExtname(remote) + s.SetDynimpvers(q) + if s.Type != sym.SHOSTOBJ { + s.Type = sym.SDYNIMPORT + } + havedynamic = 1 + } + + continue + + case "cgo_import_static": + if len(f) != 2 { + break + } + local := f[1] + + s := lookup(local, 0) + s.Type = sym.SHOSTOBJ + s.Size = 0 + continue + + case "cgo_export_static", "cgo_export_dynamic": + if len(f) < 2 || len(f) > 3 { + break + } + local := f[1] + remote := local + if len(f) > 2 { + remote = f[2] + } + local = expandpkg(local, pkg) + + // The compiler arranges for an ABI0 wrapper + // to be available for all cgo-exported + // functions. Link.loadlib will resolve any + // ABI aliases we find here (since we may not + // yet know it's an alias). + s := lookup(local, 0) + + switch ctxt.BuildMode { + case BuildModeCShared, BuildModeCArchive, BuildModePlugin: + if s == lookup("main", 0) { + continue + } + } + + // export overrides import, for openbsd/cgo. + // see issue 4878. + if s.Dynimplib() != "" { + s.ResetDyninfo() + s.SetExtname("") + s.Type = 0 + } + + if !s.Attr.CgoExport() { + s.SetExtname(remote) + } else if s.Extname() != remote { + fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname(), remote) + nerrors++ + return + } + + if f[0] == "cgo_export_static" { + s.Attr |= sym.AttrCgoExportStatic + } else { + s.Attr |= sym.AttrCgoExportDynamic + } + continue + + case "cgo_dynamic_linker": + if len(f) != 2 { + break + } + + if *flagInterpreter == "" { + if interpreter != "" && interpreter != f[1] { + fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1]) + nerrors++ + return + } + + interpreter = f[1] + } + continue + + case "cgo_ldflag": + if len(f) != 2 { + break + } + ldflag = append(ldflag, f[1]) + continue + } + + fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f) + nerrors++ + } +} + +var seenlib = make(map[string]bool) + +func adddynlib(ctxt *Link, lib string) { + if seenlib[lib] || ctxt.LinkMode == LinkExternal { + return + } + seenlib[lib] = true + + if ctxt.IsELF { + s := ctxt.Syms.Lookup(".dynstr", 0) + if s.Size == 0 { + Addstring(s, "") + } + Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib))) + } else { + Errorf(nil, "adddynlib: unsupported binary format") + } +} + +func Adddynsym(ctxt *Link, s *sym.Symbol) { + if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal { + return + } + + if ctxt.IsELF { + elfadddynsym(ctxt, s) + } else if ctxt.HeadType == objabi.Hdarwin { + Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname()) + } else if ctxt.HeadType == objabi.Hwindows { + // already taken care of + } else { + Errorf(s, "adddynsym: unsupported binary format") + } +} + +func fieldtrack(ctxt *Link) { + // record field tracking references + var buf bytes.Buffer + for _, s := range ctxt.Syms.Allsym { + if strings.HasPrefix(s.Name, "go.track.") { + s.Attr |= sym.AttrSpecial // do not lay out in data segment + s.Attr |= sym.AttrNotInSymbolTable + if s.Attr.Reachable() { + buf.WriteString(s.Name[9:]) + for p := ctxt.Reachparent[s]; p != nil; p = ctxt.Reachparent[p] { + buf.WriteString("\t") + buf.WriteString(p.Name) + } + buf.WriteString("\n") + } + + s.Type = sym.SCONST + s.Value = 0 + } + } + + if *flagFieldTrack == "" { + return + } + s := ctxt.Syms.ROLookup(*flagFieldTrack, 0) + if s == nil || !s.Attr.Reachable() { + return + } + s.Type = sym.SDATA + addstrdata(ctxt, *flagFieldTrack, buf.String()) +} + +func (ctxt *Link) addexport() { + // Track undefined external symbols during external link. + if ctxt.LinkMode == LinkExternal { + for _, s := range ctxt.Syms.Allsym { + if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() { + continue + } + if s.Type != sym.STEXT { + continue + } + for i := range s.R { + r := &s.R[i] + if r.Sym != nil && r.Sym.Type == sym.Sxxx { + r.Sym.Type = sym.SUNDEFEXT + } + } + } + } + + // TODO(aix) + if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix { + return + } + + for _, exp := range dynexp { + Adddynsym(ctxt, exp) + } + for _, lib := range dynlib { + adddynlib(ctxt, lib) + } +} + +type Pkg struct { + mark bool + checked bool + path string + impby []*Pkg +} + +var pkgall []*Pkg + +func (p *Pkg) cycle() *Pkg { + if p.checked { + return nil + } + + if p.mark { + nerrors++ + fmt.Printf("import cycle:\n") + fmt.Printf("\t%s\n", p.path) + return p + } + + p.mark = true + for _, q := range p.impby { + if bad := q.cycle(); bad != nil { + p.mark = false + p.checked = true + fmt.Printf("\timports %s\n", p.path) + if bad == p { + return nil + } + return bad + } + } + + p.checked = true + p.mark = false + return nil +} + +func importcycles() { + for _, p := range pkgall { + p.cycle() + } +} diff --git a/src/cmd/oldlink/internal/ld/ld.go b/src/cmd/oldlink/internal/ld/ld.go new file mode 100644 index 0000000000..7420dce9de --- /dev/null +++ b/src/cmd/oldlink/internal/ld/ld.go @@ -0,0 +1,217 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/oldlink/internal/sym" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strconv" + "strings" +) + +func (ctxt *Link) readImportCfg(file string) { + ctxt.PackageFile = make(map[string]string) + ctxt.PackageShlib = make(map[string]string) + data, err := ioutil.ReadFile(file) + if err != nil { + log.Fatalf("-importcfg: %v", err) + } + + for lineNum, line := range strings.Split(string(data), "\n") { + lineNum++ // 1-based + line = strings.TrimSpace(line) + if line == "" { + continue + } + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + var verb, args string + if i := strings.Index(line, " "); i < 0 { + verb = line + } else { + verb, args = line[:i], strings.TrimSpace(line[i+1:]) + } + var before, after string + if i := strings.Index(args, "="); i >= 0 { + before, after = args[:i], args[i+1:] + } + switch verb { + default: + log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb) + case "packagefile": + if before == "" || after == "" { + log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum) + } + ctxt.PackageFile[before] = after + case "packageshlib": + if before == "" || after == "" { + log.Fatalf(`%s:%d: invalid packageshlib: syntax is "packageshlib path=filename"`, file, lineNum) + } + ctxt.PackageShlib[before] = after + } + } +} + +func pkgname(ctxt *Link, lib string) string { + name := path.Clean(lib) + + // When using importcfg, we have the final package name. + if ctxt.PackageFile != nil { + return name + } + + // runtime.a -> runtime, runtime.6 -> runtime + pkg := name + if len(pkg) >= 2 && pkg[len(pkg)-2] == '.' { + pkg = pkg[:len(pkg)-2] + } + return pkg +} + +func findlib(ctxt *Link, lib string) (string, bool) { + name := path.Clean(lib) + + var pname string + isshlib := false + + if ctxt.linkShared && ctxt.PackageShlib[name] != "" { + pname = ctxt.PackageShlib[name] + isshlib = true + } else if ctxt.PackageFile != nil { + pname = ctxt.PackageFile[name] + if pname == "" { + ctxt.Logf("cannot find package %s (using -importcfg)\n", name) + return "", false + } + } else { + if filepath.IsAbs(name) { + pname = name + } else { + pkg := pkgname(ctxt, lib) + // Add .a if needed; the new -importcfg modes + // do not put .a into the package name anymore. + // This only matters when people try to mix + // compiles using -importcfg with links not using -importcfg, + // such as when running quick things like + // 'go tool compile x.go && go tool link x.o' + // by hand against a standard library built using -importcfg. + if !strings.HasSuffix(name, ".a") && !strings.HasSuffix(name, ".o") { + name += ".a" + } + // try dot, -L "libdir", and then goroot. + for _, dir := range ctxt.Libdir { + if ctxt.linkShared { + pname = filepath.Join(dir, pkg+".shlibname") + if _, err := os.Stat(pname); err == nil { + isshlib = true + break + } + } + pname = filepath.Join(dir, name) + if _, err := os.Stat(pname); err == nil { + break + } + } + } + pname = filepath.Clean(pname) + } + + return pname, isshlib +} + +func addlib(ctxt *Link, src string, obj string, lib string) *sym.Library { + pkg := pkgname(ctxt, lib) + + // already loaded? + if l := ctxt.LibraryByPkg[pkg]; l != nil { + return l + } + + pname, isshlib := findlib(ctxt, lib) + + if ctxt.Debugvlog > 1 { + ctxt.Logf("addlib: %s %s pulls in %s isshlib %v\n", obj, src, pname, isshlib) + } + + if isshlib { + return addlibpath(ctxt, src, obj, "", pkg, pname) + } + return addlibpath(ctxt, src, obj, pname, pkg, "") +} + +/* + * add library to library list, return added library. + * srcref: src file referring to package + * objref: object file referring to package + * file: object file, e.g., /home/rsc/go/pkg/container/vector.a + * pkg: package import path, e.g. container/vector + * shlib: path to shared library, or .shlibname file holding path + */ +func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlib string) *sym.Library { + if l := ctxt.LibraryByPkg[pkg]; l != nil { + return l + } + + if ctxt.Debugvlog > 1 { + ctxt.Logf("addlibpath: srcref: %s objref: %s file: %s pkg: %s shlib: %s\n", srcref, objref, file, pkg, shlib) + } + + l := &sym.Library{} + ctxt.LibraryByPkg[pkg] = l + ctxt.Library = append(ctxt.Library, l) + l.Objref = objref + l.Srcref = srcref + l.File = file + l.Pkg = pkg + if shlib != "" { + if strings.HasSuffix(shlib, ".shlibname") { + data, err := ioutil.ReadFile(shlib) + if err != nil { + Errorf(nil, "cannot read %s: %v", shlib, err) + } + shlib = strings.TrimSpace(string(data)) + } + l.Shlib = shlib + } + return l +} + +func atolwhex(s string) int64 { + n, _ := strconv.ParseInt(s, 0, 64) + return n +} diff --git a/src/cmd/oldlink/internal/ld/lib.go b/src/cmd/oldlink/internal/ld/lib.go new file mode 100644 index 0000000000..a6c86af6cf --- /dev/null +++ b/src/cmd/oldlink/internal/ld/lib.go @@ -0,0 +1,2749 @@ +// Inferno utils/8l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "bytes" + "cmd/internal/bio" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/loadelf" + "cmd/oldlink/internal/loader" + "cmd/oldlink/internal/loadmacho" + "cmd/oldlink/internal/loadpe" + "cmd/oldlink/internal/loadxcoff" + "cmd/oldlink/internal/objfile" + "cmd/oldlink/internal/sym" + "crypto/sha1" + "debug/elf" + "debug/macho" + "encoding/base64" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strings" + "sync" +) + +// Data layout and relocation. + +// Derived from Inferno utils/6l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +type Arch struct { + Funcalign int + Maxalign int + Minalign int + Dwarfregsp int + Dwarfreglr int + Androiddynld string + Linuxdynld string + Freebsddynld string + Netbsddynld string + Openbsddynld string + Dragonflydynld string + Solarisdynld string + Adddynrel func(*Link, *sym.Symbol, *sym.Reloc) bool + Archinit func(*Link) + // Archreloc is an arch-specific hook that assists in + // relocation processing (invoked by 'relocsym'); it handles + // target-specific relocation tasks. Here "rel" is the current + // relocation being examined, "sym" is the symbol containing the + // chunk of data to which the relocation applies, and "off" is the + // contents of the to-be-relocated data item (from sym.P). Return + // value is the appropriately relocated value (to be written back + // to the same spot in sym.P) and a boolean indicating + // success/failure (a failing value indicates a fatal error). + Archreloc func(link *Link, rel *sym.Reloc, sym *sym.Symbol, + offset int64) (relocatedOffset int64, success bool) + // Archrelocvariant is a second arch-specific hook used for + // relocation processing; it handles relocations where r.Type is + // insufficient to describe the relocation (r.Variant != + // sym.RV_NONE). Here "rel" is the relocation being applied, "sym" + // is the symbol containing the chunk of data to which the + // relocation applies, and "off" is the contents of the + // to-be-relocated data item (from sym.P). Return is an updated + // offset value. + Archrelocvariant func(link *Link, rel *sym.Reloc, sym *sym.Symbol, + offset int64) (relocatedOffset int64) + Trampoline func(*Link, *sym.Reloc, *sym.Symbol) + + // Asmb and Asmb2 are arch-specific routines that write the output + // file. Typically, Asmb writes most of the content (sections and + // segments), for which we have computed the size and offset. Asmb2 + // writes the rest. + Asmb func(*Link) + Asmb2 func(*Link) + + Elfreloc1 func(*Link, *sym.Reloc, int64) bool + Elfsetupplt func(*Link) + Gentext func(*Link) + Machoreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool + PEreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool + Xcoffreloc1 func(*sys.Arch, *OutBuf, *sym.Symbol, *sym.Reloc, int64) bool + + // TLSIEtoLE converts a TLS Initial Executable relocation to + // a TLS Local Executable relocation. + // + // This is possible when a TLS IE relocation refers to a local + // symbol in an executable, which is typical when internally + // linking PIE binaries. + TLSIEtoLE func(s *sym.Symbol, off, size int) + + // optional override for assignAddress + AssignAddress func(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) +} + +var ( + thearch Arch + Lcsize int32 + rpath Rpath + Spsize int32 + Symsize int32 +) + +const ( + MINFUNC = 16 // minimum size for a function +) + +// DynlinkingGo reports whether we are producing Go code that can live +// in separate shared libraries linked together at runtime. +func (ctxt *Link) DynlinkingGo() bool { + if !ctxt.Loaded { + panic("DynlinkingGo called before all symbols loaded") + } + return ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin || ctxt.canUsePlugins +} + +// CanUsePlugins reports whether a plugins can be used +func (ctxt *Link) CanUsePlugins() bool { + if !ctxt.Loaded { + panic("CanUsePlugins called before all symbols loaded") + } + return ctxt.canUsePlugins +} + +// UseRelro reports whether to make use of "read only relocations" aka +// relro. +func (ctxt *Link) UseRelro() bool { + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin: + return ctxt.IsELF || ctxt.HeadType == objabi.Haix + default: + return ctxt.linkShared || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) + } +} + +var ( + dynexp []*sym.Symbol + dynlib []string + ldflag []string + havedynamic int + Funcalign int + iscgo bool + elfglobalsymndx int + interpreter string + + debug_s bool // backup old value of debug['s'] + HEADR int32 + + nerrors int + liveness int64 + + // See -strictdups command line flag. + checkStrictDups int // 0=off 1=warning 2=error + strictDupMsgCount int +) + +var ( + Segtext sym.Segment + Segrodata sym.Segment + Segrelrodata sym.Segment + Segdata sym.Segment + Segdwarf sym.Segment +) + +const pkgdef = "__.PKGDEF" + +var ( + // Set if we see an object compiled by the host compiler that is not + // from a package that is known to support internal linking mode. + externalobj = false + theline string +) + +func Lflag(ctxt *Link, arg string) { + ctxt.Libdir = append(ctxt.Libdir, arg) +} + +/* + * Unix doesn't like it when we write to a running (or, sometimes, + * recently run) binary, so remove the output file before writing it. + * On Windows 7, remove() can force a subsequent create() to fail. + * S_ISREG() does not exist on Plan 9. + */ +func mayberemoveoutfile() { + if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() { + return + } + os.Remove(*flagOutfile) +} + +func libinit(ctxt *Link) { + Funcalign = thearch.Funcalign + + // add goroot to the end of the libdir list. + suffix := "" + + suffixsep := "" + if *flagInstallSuffix != "" { + suffixsep = "_" + suffix = *flagInstallSuffix + } else if *flagRace { + suffixsep = "_" + suffix = "race" + } else if *flagMsan { + suffixsep = "_" + suffix = "msan" + } + + Lflag(ctxt, filepath.Join(objabi.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", objabi.GOOS, objabi.GOARCH, suffixsep, suffix))) + + mayberemoveoutfile() + f, err := os.OpenFile(*flagOutfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) + if err != nil { + Exitf("cannot create %s: %v", *flagOutfile, err) + } + + ctxt.Out.w = bufio.NewWriter(f) + ctxt.Out.f = f + + if *flagEntrySymbol == "" { + switch ctxt.BuildMode { + case BuildModeCShared, BuildModeCArchive: + *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", objabi.GOARCH, objabi.GOOS) + case BuildModeExe, BuildModePIE: + *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", objabi.GOARCH, objabi.GOOS) + case BuildModeShared, BuildModePlugin: + // No *flagEntrySymbol for -buildmode=shared and plugin + default: + Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode) + } + } +} + +func exitIfErrors() { + if nerrors != 0 || checkStrictDups > 1 && strictDupMsgCount > 0 { + mayberemoveoutfile() + Exit(2) + } + +} + +func errorexit() { + exitIfErrors() + Exit(0) +} + +func loadinternal(ctxt *Link, name string) *sym.Library { + if ctxt.linkShared && ctxt.PackageShlib != nil { + if shlib := ctxt.PackageShlib[name]; shlib != "" { + return addlibpath(ctxt, "internal", "internal", "", name, shlib) + } + } + if ctxt.PackageFile != nil { + if pname := ctxt.PackageFile[name]; pname != "" { + return addlibpath(ctxt, "internal", "internal", pname, name, "") + } + ctxt.Logf("loadinternal: cannot find %s\n", name) + return nil + } + + for _, libdir := range ctxt.Libdir { + if ctxt.linkShared { + shlibname := filepath.Join(libdir, name+".shlibname") + if ctxt.Debugvlog != 0 { + ctxt.Logf("searching for %s.a in %s\n", name, shlibname) + } + if _, err := os.Stat(shlibname); err == nil { + return addlibpath(ctxt, "internal", "internal", "", name, shlibname) + } + } + pname := filepath.Join(libdir, name+".a") + if ctxt.Debugvlog != 0 { + ctxt.Logf("searching for %s.a in %s\n", name, pname) + } + if _, err := os.Stat(pname); err == nil { + return addlibpath(ctxt, "internal", "internal", pname, name, "") + } + } + + ctxt.Logf("warning: unable to find %s.a\n", name) + return nil +} + +// extld returns the current external linker. +func (ctxt *Link) extld() string { + if *flagExtld == "" { + *flagExtld = "gcc" + } + return *flagExtld +} + +// findLibPathCmd uses cmd command to find gcc library libname. +// It returns library full path if found, or "none" if not found. +func (ctxt *Link) findLibPathCmd(cmd, libname string) string { + extld := ctxt.extld() + args := hostlinkArchArgs(ctxt.Arch) + args = append(args, cmd) + if ctxt.Debugvlog != 0 { + ctxt.Logf("%s %v\n", extld, args) + } + out, err := exec.Command(extld, args...).Output() + if err != nil { + if ctxt.Debugvlog != 0 { + ctxt.Logf("not using a %s file because compiler failed\n%v\n%s\n", libname, err, out) + } + return "none" + } + return strings.TrimSpace(string(out)) +} + +// findLibPath searches for library libname. +// It returns library full path if found, or "none" if not found. +func (ctxt *Link) findLibPath(libname string) string { + return ctxt.findLibPathCmd("--print-file-name="+libname, libname) +} + +func (ctxt *Link) loadlib() { + if *flagNewobj { + var flags uint32 + switch *FlagStrictDups { + case 0: + // nothing to do + case 1, 2: + flags = loader.FlagStrictDups + default: + log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) + } + ctxt.loader = loader.NewLoader(flags) + } + + ctxt.cgo_export_static = make(map[string]bool) + ctxt.cgo_export_dynamic = make(map[string]bool) + + // ctxt.Library grows during the loop, so not a range loop. + i := 0 + for ; i < len(ctxt.Library); i++ { + lib := ctxt.Library[i] + if lib.Shlib == "" { + if ctxt.Debugvlog > 1 { + ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref) + } + loadobjfile(ctxt, lib) + } + } + + // load internal packages, if not already + if *flagRace { + loadinternal(ctxt, "runtime/race") + } + if *flagMsan { + loadinternal(ctxt, "runtime/msan") + } + loadinternal(ctxt, "runtime") + for ; i < len(ctxt.Library); i++ { + lib := ctxt.Library[i] + if lib.Shlib == "" { + loadobjfile(ctxt, lib) + } + } + + if *flagNewobj { + iscgo = ctxt.loader.Lookup("x_cgo_init", 0) != 0 + ctxt.canUsePlugins = ctxt.loader.Lookup("plugin.Open", sym.SymVerABIInternal) != 0 + } else { + iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil + ctxt.canUsePlugins = ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil + } + + // We now have enough information to determine the link mode. + determineLinkMode(ctxt) + + if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) { + // This indicates a user requested -linkmode=external. + // The startup code uses an import of runtime/cgo to decide + // whether to initialize the TLS. So give it one. This could + // be handled differently but it's an unusual case. + if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil { + if lib.Shlib != "" { + ldshlibsyms(ctxt, lib.Shlib) + } else { + if ctxt.BuildMode == BuildModeShared || ctxt.linkShared { + Exitf("cannot implicitly include runtime/cgo in a shared library") + } + loadobjfile(ctxt, lib) + } + } + } + + for _, lib := range ctxt.Library { + if lib.Shlib != "" { + if ctxt.Debugvlog > 1 { + ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref) + } + ldshlibsyms(ctxt, lib.Shlib) + } + } + + if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 { + if *flagNewobj { + // In newobj mode, we typically create sym.Symbols later therefore + // also set cgo attributes later. However, for internal cgo linking, + // the host object loaders still work with sym.Symbols (for now), + // and they need cgo attributes set to work properly. So process + // them now. + lookup := func(name string, ver int) *sym.Symbol { return ctxt.loader.LookupOrCreate(name, ver, ctxt.Syms) } + for _, d := range ctxt.cgodata { + setCgoAttr(ctxt, lookup, d.file, d.pkg, d.directives) + } + ctxt.cgodata = nil + } + + // Drop all the cgo_import_static declarations. + // Turns out we won't be needing them. + for _, s := range ctxt.Syms.Allsym { + if s.Type == sym.SHOSTOBJ { + // If a symbol was marked both + // cgo_import_static and cgo_import_dynamic, + // then we want to make it cgo_import_dynamic + // now. + if s.Extname() != "" && s.Dynimplib() != "" && !s.Attr.CgoExport() { + s.Type = sym.SDYNIMPORT + } else { + s.Type = 0 + } + } + } + } + + // Conditionally load host objects, or setup for external linking. + hostobjs(ctxt) + hostlinksetup(ctxt) + + if *flagNewobj { + // Add references of externally defined symbols. + ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms) + } + + // Now that we know the link mode, set the dynexp list. + if !*flagNewobj { // set this later in newobj mode + setupdynexp(ctxt) + } + + if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 { + // If we have any undefined symbols in external + // objects, try to read them from the libgcc file. + any := false + for _, s := range ctxt.Syms.Allsym { + for i := range s.R { + r := &s.R[i] // Copying sym.Reloc has measurable impact on performance + if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" { + any = true + break + } + } + } + if any { + if *flagLibGCC == "" { + *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc") + } + if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" { + // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a". + // In this case we fail to load libgcc.a and can encounter link + // errors - see if we can find libcompiler_rt.a instead. + *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") + } + if *flagLibGCC != "none" { + hostArchive(ctxt, *flagLibGCC) + } + if ctxt.HeadType == objabi.Hwindows { + if p := ctxt.findLibPath("libmingwex.a"); p != "none" { + hostArchive(ctxt, p) + } + if p := ctxt.findLibPath("libmingw32.a"); p != "none" { + hostArchive(ctxt, p) + } + // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol + // (see https://golang.org/issue/23649 for details). + if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" { + hostArchive(ctxt, p) + } + // TODO: maybe do something similar to peimporteddlls to collect all lib names + // and try link them all to final exe just like libmingwex.a and libmingw32.a: + /* + for: + #cgo windows LDFLAGS: -lmsvcrt -lm + import: + libmsvcrt.a libm.a + */ + } + } + } + + // We've loaded all the code now. + ctxt.Loaded = true + + importcycles() + + if *flagNewobj { + strictDupMsgCount = ctxt.loader.NStrictDupMsgs() + } +} + +// Set up dynexp list. +func setupdynexp(ctxt *Link) { + dynexpMap := ctxt.cgo_export_dynamic + if ctxt.LinkMode == LinkExternal { + dynexpMap = ctxt.cgo_export_static + } + dynexp = make([]*sym.Symbol, 0, len(dynexpMap)) + for exp := range dynexpMap { + s := ctxt.Syms.Lookup(exp, 0) + dynexp = append(dynexp, s) + } + sort.Sort(byName(dynexp)) + + // Resolve ABI aliases in the list of cgo-exported functions. + // This is necessary because we load the ABI0 symbol for all + // cgo exports. + for i, s := range dynexp { + if s.Type != sym.SABIALIAS { + continue + } + t := resolveABIAlias(s) + t.Attr |= s.Attr + t.SetExtname(s.Extname()) + dynexp[i] = t + } + + ctxt.cgo_export_static = nil + ctxt.cgo_export_dynamic = nil +} + +// Set up flags and special symbols depending on the platform build mode. +func (ctxt *Link) linksetup() { + switch ctxt.BuildMode { + case BuildModeCShared, BuildModePlugin: + s := ctxt.Syms.Lookup("runtime.islibrary", 0) + s.Type = sym.SNOPTRDATA + s.Attr |= sym.AttrDuplicateOK + s.AddUint8(1) + case BuildModeCArchive: + s := ctxt.Syms.Lookup("runtime.isarchive", 0) + s.Type = sym.SNOPTRDATA + s.Attr |= sym.AttrDuplicateOK + s.AddUint8(1) + } + + // Recalculate pe parameters now that we have ctxt.LinkMode set. + if ctxt.HeadType == objabi.Hwindows { + Peinit(ctxt) + } + + if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal { + *FlagTextAddr = 0 + } + + // If there are no dynamic libraries needed, gcc disables dynamic linking. + // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) + // assumes that a dynamic binary always refers to at least one dynamic library. + // Rather than be a source of test cases for glibc, disable dynamic linking + // the same way that gcc would. + // + // Exception: on OS X, programs such as Shark only work with dynamic + // binaries, so leave it enabled on OS X (Mach-O) binaries. + // Also leave it enabled on Solaris which doesn't support + // statically linked binaries. + if ctxt.BuildMode == BuildModeExe { + if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris { + *FlagD = true + } + } + + if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" { + toc := ctxt.Syms.Lookup(".TOC.", 0) + toc.Type = sym.SDYNIMPORT + } + + // The Android Q linker started to complain about underalignment of the our TLS + // section. We don't actually use the section on android, so dont't + // generate it. + if objabi.GOOS != "android" { + tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0) + + // runtime.tlsg is used for external linking on platforms that do not define + // a variable to hold g in assembly (currently only intel). + if tlsg.Type == 0 { + tlsg.Type = sym.STLSBSS + tlsg.Size = int64(ctxt.Arch.PtrSize) + } else if tlsg.Type != sym.SDYNIMPORT { + Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type) + } + tlsg.Attr |= sym.AttrReachable + ctxt.Tlsg = tlsg + } + + var moduledata *sym.Symbol + if ctxt.BuildMode == BuildModePlugin { + moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0) + moduledata.Attr |= sym.AttrLocal + } else { + moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0) + } + if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT { + // If the module (toolchain-speak for "executable or shared + // library") we are linking contains the runtime package, it + // will define the runtime.firstmoduledata symbol and we + // truncate it back to 0 bytes so we can define its entire + // contents in symtab.go:symtab(). + moduledata.Size = 0 + + // In addition, on ARM, the runtime depends on the linker + // recording the value of GOARM. + if ctxt.Arch.Family == sys.ARM { + s := ctxt.Syms.Lookup("runtime.goarm", 0) + s.Type = sym.SDATA + s.Size = 0 + s.AddUint8(uint8(objabi.GOARM)) + } + + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0) + s.Type = sym.SDATA + s.Size = 0 + s.AddUint8(1) + } + } else { + // If OTOH the module does not contain the runtime package, + // create a local symbol for the moduledata. + moduledata = ctxt.Syms.Lookup("local.moduledata", 0) + moduledata.Attr |= sym.AttrLocal + } + // In all cases way we mark the moduledata as noptrdata to hide it from + // the GC. + moduledata.Type = sym.SNOPTRDATA + moduledata.Attr |= sym.AttrReachable + ctxt.Moduledata = moduledata + + // If package versioning is required, generate a hash of the + // packages used in the link. + if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() { + for _, lib := range ctxt.Library { + if lib.Shlib == "" { + genhash(ctxt, lib) + } + } + } + + if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows { + if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() { + got := ctxt.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0) + got.Type = sym.SDYNIMPORT + got.Attr |= sym.AttrReachable + } + } +} + +// mangleTypeSym shortens the names of symbols that represent Go types +// if they are visible in the symbol table. +// +// As the names of these symbols are derived from the string of +// the type, they can run to many kilobytes long. So we shorten +// them using a SHA-1 when the name appears in the final binary. +// This also removes characters that upset external linkers. +// +// These are the symbols that begin with the prefix 'type.' and +// contain run-time type information used by the runtime and reflect +// packages. All Go binaries contain these symbols, but only +// those programs loaded dynamically in multiple parts need these +// symbols to have entries in the symbol table. +func (ctxt *Link) mangleTypeSym() { + if ctxt.BuildMode != BuildModeShared && !ctxt.linkShared && ctxt.BuildMode != BuildModePlugin && !ctxt.CanUsePlugins() { + return + } + + for _, s := range ctxt.Syms.Allsym { + newName := typeSymbolMangle(s.Name) + if newName != s.Name { + ctxt.Syms.Rename(s.Name, newName, int(s.Version), ctxt.Reachparent) + } + } +} + +// typeSymbolMangle mangles the given symbol name into something shorter. +// +// Keep the type.. prefix, which parts of the linker (like the +// DWARF generator) know means the symbol is not decodable. +// Leave type.runtime. symbols alone, because other parts of +// the linker manipulates them. +func typeSymbolMangle(name string) string { + if !strings.HasPrefix(name, "type.") { + return name + } + if strings.HasPrefix(name, "type.runtime.") { + return name + } + if len(name) <= 14 && !strings.Contains(name, "@") { // Issue 19529 + return name + } + hash := sha1.Sum([]byte(name)) + prefix := "type." + if name[5] == '.' { + prefix = "type.." + } + return prefix + base64.StdEncoding.EncodeToString(hash[:6]) +} + +/* + * look for the next file in an archive. + * adapted from libmach. + */ +func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 { + if off&1 != 0 { + off++ + } + bp.MustSeek(off, 0) + var buf [SAR_HDR]byte + if n, err := io.ReadFull(bp, buf[:]); err != nil { + if n == 0 && err != io.EOF { + return -1 + } + return 0 + } + + a.name = artrim(buf[0:16]) + a.date = artrim(buf[16:28]) + a.uid = artrim(buf[28:34]) + a.gid = artrim(buf[34:40]) + a.mode = artrim(buf[40:48]) + a.size = artrim(buf[48:58]) + a.fmag = artrim(buf[58:60]) + + arsize := atolwhex(a.size) + if arsize&1 != 0 { + arsize++ + } + return arsize + SAR_HDR +} + +func genhash(ctxt *Link, lib *sym.Library) { + f, err := bio.Open(lib.File) + if err != nil { + Errorf(nil, "cannot open file %s for hash generation: %v", lib.File, err) + return + } + defer f.Close() + + var magbuf [len(ARMAG)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { + Exitf("file %s too short", lib.File) + } + + if string(magbuf[:]) != ARMAG { + Exitf("%s is not an archive file", lib.File) + } + + var arhdr ArHdr + l := nextar(f, f.Offset(), &arhdr) + if l <= 0 { + Errorf(nil, "%s: short read on archive file symbol header", lib.File) + return + } + if arhdr.name != pkgdef { + Errorf(nil, "%s: missing package data entry", lib.File) + return + } + + h := sha1.New() + + // To compute the hash of a package, we hash the first line of + // __.PKGDEF (which contains the toolchain version and any + // GOEXPERIMENT flags) and the export data (which is between + // the first two occurrences of "\n$$"). + + pkgDefBytes := make([]byte, atolwhex(arhdr.size)) + _, err = io.ReadFull(f, pkgDefBytes) + if err != nil { + Errorf(nil, "%s: error reading package data: %v", lib.File, err) + return + } + firstEOL := bytes.IndexByte(pkgDefBytes, '\n') + if firstEOL < 0 { + Errorf(nil, "cannot parse package data of %s for hash generation, no newline found", lib.File) + return + } + firstDoubleDollar := bytes.Index(pkgDefBytes, []byte("\n$$")) + if firstDoubleDollar < 0 { + Errorf(nil, "cannot parse package data of %s for hash generation, no \\n$$ found", lib.File) + return + } + secondDoubleDollar := bytes.Index(pkgDefBytes[firstDoubleDollar+1:], []byte("\n$$")) + if secondDoubleDollar < 0 { + Errorf(nil, "cannot parse package data of %s for hash generation, only one \\n$$ found", lib.File) + return + } + h.Write(pkgDefBytes[0:firstEOL]) + h.Write(pkgDefBytes[firstDoubleDollar : firstDoubleDollar+secondDoubleDollar]) + lib.Hash = hex.EncodeToString(h.Sum(nil)) +} + +func loadobjfile(ctxt *Link, lib *sym.Library) { + pkg := objabi.PathToPrefix(lib.Pkg) + + if ctxt.Debugvlog > 1 { + ctxt.Logf("ldobj: %s (%s)\n", lib.File, pkg) + } + f, err := bio.Open(lib.File) + if err != nil { + Exitf("cannot open file %s: %v", lib.File, err) + } + defer f.Close() + defer func() { + if pkg == "main" && !lib.Main { + Exitf("%s: not package main", lib.File) + } + + // Ideally, we'd check that *all* object files within + // the archive were marked safe, but here we settle + // for *any*. + // + // Historically, cmd/link only checked the __.PKGDEF + // file, which in turn came from the first object + // file, typically produced by cmd/compile. The + // remaining object files are normally produced by + // cmd/asm, which doesn't support marking files as + // safe anyway. So at least in practice, this matches + // how safe mode has always worked. + if *flagU && !lib.Safe { + Exitf("%s: load of unsafe package %s", lib.File, pkg) + } + }() + + for i := 0; i < len(ARMAG); i++ { + if c, err := f.ReadByte(); err == nil && c == ARMAG[i] { + continue + } + + /* load it as a regular file */ + l := f.MustSeek(0, 2) + f.MustSeek(0, 0) + ldobj(ctxt, f, lib, l, lib.File, lib.File) + return + } + + /* + * load all the object files from the archive now. + * this gives us sequential file access and keeps us + * from needing to come back later to pick up more + * objects. it breaks the usual C archive model, but + * this is Go, not C. the common case in Go is that + * we need to load all the objects, and then we throw away + * the individual symbols that are unused. + * + * loading every object will also make it possible to + * load foreign objects not referenced by __.PKGDEF. + */ + var arhdr ArHdr + off := f.Offset() + for { + l := nextar(f, off, &arhdr) + if l == 0 { + break + } + if l < 0 { + Exitf("%s: malformed archive", lib.File) + } + off += l + + // __.PKGDEF isn't a real Go object file, and it's + // absent in -linkobj builds anyway. Skipping it + // ensures consistency between -linkobj and normal + // build modes. + if arhdr.name == pkgdef { + continue + } + + // Skip other special (non-object-file) sections that + // build tools may have added. Such sections must have + // short names so that the suffix is not truncated. + if len(arhdr.name) < 16 { + if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" { + continue + } + } + + pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name) + l = atolwhex(arhdr.size) + ldobj(ctxt, f, lib, l, pname, lib.File) + } +} + +type Hostobj struct { + ld func(*Link, *bio.Reader, string, int64, string) + pkg string + pn string + file string + off int64 + length int64 +} + +var hostobj []Hostobj + +// These packages can use internal linking mode. +// Others trigger external mode. +var internalpkg = []string{ + "crypto/x509", + "net", + "os/user", + "runtime/cgo", + "runtime/race", + "runtime/msan", +} + +func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj { + isinternal := false + for _, intpkg := range internalpkg { + if pkg == intpkg { + isinternal = true + break + } + } + + // DragonFly declares errno with __thread, which results in a symbol + // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not + // currently know how to handle TLS relocations, hence we have to + // force external linking for any libraries that link in code that + // uses errno. This can be removed if the Go linker ever supports + // these relocation types. + if headType == objabi.Hdragonfly { + if pkg == "net" || pkg == "os/user" { + isinternal = false + } + } + + if !isinternal { + externalobj = true + } + + hostobj = append(hostobj, Hostobj{}) + h := &hostobj[len(hostobj)-1] + h.ld = ld + h.pkg = pkg + h.pn = pn + h.file = file + h.off = f.Offset() + h.length = length + return h +} + +func hostobjs(ctxt *Link) { + if ctxt.LinkMode != LinkInternal { + return + } + var h *Hostobj + + for i := 0; i < len(hostobj); i++ { + h = &hostobj[i] + f, err := bio.Open(h.file) + if err != nil { + Exitf("cannot reopen %s: %v", h.pn, err) + } + + f.MustSeek(h.off, 0) + h.ld(ctxt, f, h.pkg, h.length, h.pn) + f.Close() + } +} + +func hostlinksetup(ctxt *Link) { + if ctxt.LinkMode != LinkExternal { + return + } + + // For external link, record that we need to tell the external linker -s, + // and turn off -s internally: the external linker needs the symbol + // information for its final link. + debug_s = *FlagS + *FlagS = false + + // create temporary directory and arrange cleanup + if *flagTmpdir == "" { + dir, err := ioutil.TempDir("", "go-link-") + if err != nil { + log.Fatal(err) + } + *flagTmpdir = dir + ownTmpDir = true + AtExit(func() { + ctxt.Out.f.Close() + os.RemoveAll(*flagTmpdir) + }) + } + + // change our output to temporary object file + ctxt.Out.f.Close() + mayberemoveoutfile() + + p := filepath.Join(*flagTmpdir, "go.o") + var err error + f, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) + if err != nil { + Exitf("cannot create %s: %v", p, err) + } + + ctxt.Out.w = bufio.NewWriter(f) + ctxt.Out.f = f + ctxt.Out.off = 0 +} + +// hostobjCopy creates a copy of the object files in hostobj in a +// temporary directory. +func hostobjCopy() (paths []string) { + var wg sync.WaitGroup + sema := make(chan struct{}, runtime.NumCPU()) // limit open file descriptors + for i, h := range hostobj { + h := h + dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i)) + paths = append(paths, dst) + + wg.Add(1) + go func() { + sema <- struct{}{} + defer func() { + <-sema + wg.Done() + }() + f, err := os.Open(h.file) + if err != nil { + Exitf("cannot reopen %s: %v", h.pn, err) + } + defer f.Close() + if _, err := f.Seek(h.off, 0); err != nil { + Exitf("cannot seek %s: %v", h.pn, err) + } + + w, err := os.Create(dst) + if err != nil { + Exitf("cannot create %s: %v", dst, err) + } + if _, err := io.CopyN(w, f, h.length); err != nil { + Exitf("cannot write %s: %v", dst, err) + } + if err := w.Close(); err != nil { + Exitf("cannot close %s: %v", dst, err) + } + }() + } + wg.Wait() + return paths +} + +// writeGDBLinkerScript creates gcc linker script file in temp +// directory. writeGDBLinkerScript returns created file path. +// The script is used to work around gcc bug +// (see https://golang.org/issue/20183 for details). +func writeGDBLinkerScript() string { + name := "fix_debug_gdb_scripts.ld" + path := filepath.Join(*flagTmpdir, name) + src := `SECTIONS +{ + .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : + { + *(.debug_gdb_scripts) + } +} +INSERT AFTER .debug_types; +` + err := ioutil.WriteFile(path, []byte(src), 0666) + if err != nil { + Errorf(nil, "WriteFile %s failed: %v", name, err) + } + return path +} + +// archive builds a .a archive from the hostobj object files. +func (ctxt *Link) archive() { + if ctxt.BuildMode != BuildModeCArchive { + return + } + + exitIfErrors() + + if *flagExtar == "" { + *flagExtar = "ar" + } + + mayberemoveoutfile() + + // Force the buffer to flush here so that external + // tools will see a complete file. + ctxt.Out.Flush() + if err := ctxt.Out.f.Close(); err != nil { + Exitf("close: %v", err) + } + ctxt.Out.f = nil + + argv := []string{*flagExtar, "-q", "-c", "-s"} + if ctxt.HeadType == objabi.Haix { + argv = append(argv, "-X64") + } + argv = append(argv, *flagOutfile) + argv = append(argv, filepath.Join(*flagTmpdir, "go.o")) + argv = append(argv, hostobjCopy()...) + + if ctxt.Debugvlog != 0 { + ctxt.Logf("archive: %s\n", strings.Join(argv, " ")) + } + + // If supported, use syscall.Exec() to invoke the archive command, + // which should be the final remaining step needed for the link. + // This will reduce peak RSS for the link (and speed up linking of + // large applications), since when the archive command runs we + // won't be holding onto all of the linker's live memory. + if syscallExecSupported && !ownTmpDir { + runAtExitFuncs() + ctxt.execArchive(argv) + panic("should not get here") + } + + // Otherwise invoke 'ar' in the usual way (fork + exec). + if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { + Exitf("running %s failed: %v\n%s", argv[0], err, out) + } +} + +func (ctxt *Link) hostlink() { + if ctxt.LinkMode != LinkExternal || nerrors > 0 { + return + } + if ctxt.BuildMode == BuildModeCArchive { + return + } + + var argv []string + argv = append(argv, ctxt.extld()) + argv = append(argv, hostlinkArchArgs(ctxt.Arch)...) + + if *FlagS || debug_s { + if ctxt.HeadType == objabi.Hdarwin { + // Recent versions of macOS print + // ld: warning: option -s is obsolete and being ignored + // so do not pass any arguments. + } else { + argv = append(argv, "-s") + } + } + + switch ctxt.HeadType { + case objabi.Hdarwin: + if machoPlatform == PLATFORM_MACOS { + // -headerpad is incompatible with -fembed-bitcode. + argv = append(argv, "-Wl,-headerpad,1144") + } + if ctxt.DynlinkingGo() && !ctxt.Arch.InFamily(sys.ARM, sys.ARM64) { + argv = append(argv, "-Wl,-flat_namespace") + } + case objabi.Hopenbsd: + argv = append(argv, "-Wl,-nopie") + case objabi.Hwindows: + if windowsgui { + argv = append(argv, "-mwindows") + } else { + argv = append(argv, "-mconsole") + } + // Mark as having awareness of terminal services, to avoid + // ancient compatibility hacks. + argv = append(argv, "-Wl,--tsaware") + + // Enable DEP + argv = append(argv, "-Wl,--nxcompat") + + argv = append(argv, fmt.Sprintf("-Wl,--major-os-version=%d", PeMinimumTargetMajorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--minor-os-version=%d", PeMinimumTargetMinorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--major-subsystem-version=%d", PeMinimumTargetMajorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--minor-subsystem-version=%d", PeMinimumTargetMinorVersion)) + case objabi.Haix: + argv = append(argv, "-pthread") + // prevent ld to reorder .text functions to keep the same + // first/last functions for moduledata. + argv = append(argv, "-Wl,-bnoobjreorder") + // mcmodel=large is needed for every gcc generated files, but + // ld still need -bbigtoc in order to allow larger TOC. + argv = append(argv, "-mcmodel=large") + argv = append(argv, "-Wl,-bbigtoc") + } + + switch ctxt.BuildMode { + case BuildModeExe: + if ctxt.HeadType == objabi.Hdarwin { + if machoPlatform == PLATFORM_MACOS { + argv = append(argv, "-Wl,-no_pie") + argv = append(argv, "-Wl,-pagezero_size,4000000") + } + } + case BuildModePIE: + switch ctxt.HeadType { + case objabi.Hdarwin, objabi.Haix: + case objabi.Hwindows: + // Enable ASLR. + argv = append(argv, "-Wl,--dynamicbase") + // enable high-entropy ASLR on 64-bit. + if ctxt.Arch.PtrSize >= 8 { + argv = append(argv, "-Wl,--high-entropy-va") + } + // Work around binutils limitation that strips relocation table for dynamicbase. + // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011 + argv = append(argv, "-Wl,--export-all-symbols") + default: + // ELF. + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-pie") + } + case BuildModeCShared: + if ctxt.HeadType == objabi.Hdarwin { + argv = append(argv, "-dynamiclib") + if ctxt.Arch.Family != sys.AMD64 { + argv = append(argv, "-Wl,-read_only_relocs,suppress") + } + } else { + // ELF. + argv = append(argv, "-Wl,-Bsymbolic") + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + if ctxt.HeadType != objabi.Hwindows { + // Pass -z nodelete to mark the shared library as + // non-closeable: a dlclose will do nothing. + argv = append(argv, "-Wl,-z,nodelete") + } + } + case BuildModeShared: + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + case BuildModePlugin: + if ctxt.HeadType == objabi.Hdarwin { + argv = append(argv, "-dynamiclib") + } else { + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + } + } + + if ctxt.IsELF && ctxt.DynlinkingGo() { + // We force all symbol resolution to be done at program startup + // because lazy PLT resolution can use large amounts of stack at + // times we cannot allow it to do so. + argv = append(argv, "-Wl,-znow") + + // Do not let the host linker generate COPY relocations. These + // can move symbols out of sections that rely on stable offsets + // from the beginning of the section (like sym.STYPE). + argv = append(argv, "-Wl,-znocopyreloc") + + if ctxt.Arch.InFamily(sys.ARM, sys.ARM64) && objabi.GOOS == "linux" { + // On ARM, the GNU linker will generate COPY relocations + // even with -znocopyreloc set. + // https://sourceware.org/bugzilla/show_bug.cgi?id=19962 + // + // On ARM64, the GNU linker will fail instead of + // generating COPY relocations. + // + // In both cases, switch to gold. + argv = append(argv, "-fuse-ld=gold") + + // If gold is not installed, gcc will silently switch + // back to ld.bfd. So we parse the version information + // and provide a useful error if gold is missing. + cmd := exec.Command(*flagExtld, "-fuse-ld=gold", "-Wl,--version") + if out, err := cmd.CombinedOutput(); err == nil { + if !bytes.Contains(out, []byte("GNU gold")) { + log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out) + } + } + } + } + + if ctxt.Arch.Family == sys.ARM64 && objabi.GOOS == "freebsd" { + // Switch to ld.bfd on freebsd/arm64. + argv = append(argv, "-fuse-ld=bfd") + + // Provide a useful error if ld.bfd is missing. + cmd := exec.Command(*flagExtld, "-fuse-ld=bfd", "-Wl,--version") + if out, err := cmd.CombinedOutput(); err == nil { + if !bytes.Contains(out, []byte("GNU ld")) { + log.Fatalf("ARM64 external linker must be ld.bfd (issue #35197), please install devel/binutils") + } + } + } + + if ctxt.IsELF && len(buildinfo) > 0 { + argv = append(argv, fmt.Sprintf("-Wl,--build-id=0x%x", buildinfo)) + } + + // On Windows, given -o foo, GCC will append ".exe" to produce + // "foo.exe". We have decided that we want to honor the -o + // option. To make this work, we append a '.' so that GCC + // will decide that the file already has an extension. We + // only want to do this when producing a Windows output file + // on a Windows host. + outopt := *flagOutfile + if objabi.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" { + outopt += "." + } + argv = append(argv, "-o") + argv = append(argv, outopt) + + if rpath.val != "" { + argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val)) + } + + // Force global symbols to be exported for dlopen, etc. + if ctxt.IsELF { + argv = append(argv, "-rdynamic") + } + if ctxt.HeadType == objabi.Haix { + fileName := xcoffCreateExportFile(ctxt) + argv = append(argv, "-Wl,-bE:"+fileName) + } + + if strings.Contains(argv[0], "clang") { + argv = append(argv, "-Qunused-arguments") + } + + const compressDWARF = "-Wl,--compress-debug-sections=zlib-gnu" + if ctxt.compressDWARF && linkerFlagSupported(argv[0], compressDWARF) { + argv = append(argv, compressDWARF) + } + + argv = append(argv, filepath.Join(*flagTmpdir, "go.o")) + argv = append(argv, hostobjCopy()...) + if ctxt.HeadType == objabi.Haix { + // We want to have C files after Go files to remove + // trampolines csects made by ld. + argv = append(argv, "-nostartfiles") + argv = append(argv, "/lib/crt0_64.o") + + extld := ctxt.extld() + // Get starting files. + getPathFile := func(file string) string { + args := []string{"-maix64", "--print-file-name=" + file} + out, err := exec.Command(extld, args...).CombinedOutput() + if err != nil { + log.Fatalf("running %s failed: %v\n%s", extld, err, out) + } + return strings.Trim(string(out), "\n") + } + argv = append(argv, getPathFile("crtcxa.o")) + argv = append(argv, getPathFile("crtdbase.o")) + } + + if ctxt.linkShared { + seenDirs := make(map[string]bool) + seenLibs := make(map[string]bool) + addshlib := func(path string) { + dir, base := filepath.Split(path) + if !seenDirs[dir] { + argv = append(argv, "-L"+dir) + if !rpath.set { + argv = append(argv, "-Wl,-rpath="+dir) + } + seenDirs[dir] = true + } + base = strings.TrimSuffix(base, ".so") + base = strings.TrimPrefix(base, "lib") + if !seenLibs[base] { + argv = append(argv, "-l"+base) + seenLibs[base] = true + } + } + for _, shlib := range ctxt.Shlibs { + addshlib(shlib.Path) + for _, dep := range shlib.Deps { + if dep == "" { + continue + } + libpath := findshlib(ctxt, dep) + if libpath != "" { + addshlib(libpath) + } + } + } + } + + // clang, unlike GCC, passes -rdynamic to the linker + // even when linking with -static, causing a linker + // error when using GNU ld. So take out -rdynamic if + // we added it. We do it in this order, rather than + // only adding -rdynamic later, so that -*extldflags + // can override -rdynamic without using -static. + checkStatic := func(arg string) { + if ctxt.IsELF && arg == "-static" { + for i := range argv { + if argv[i] == "-rdynamic" { + argv[i] = "-static" + } + } + } + } + + for _, p := range ldflag { + argv = append(argv, p) + checkStatic(p) + } + + // When building a program with the default -buildmode=exe the + // gc compiler generates code requires DT_TEXTREL in a + // position independent executable (PIE). On systems where the + // toolchain creates PIEs by default, and where DT_TEXTREL + // does not work, the resulting programs will not run. See + // issue #17847. To avoid this problem pass -no-pie to the + // toolchain if it is supported. + if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared { + // GCC uses -no-pie, clang uses -nopie. + for _, nopie := range []string{"-no-pie", "-nopie"} { + if linkerFlagSupported(argv[0], nopie) { + argv = append(argv, nopie) + break + } + } + } + + for _, p := range strings.Fields(*flagExtldflags) { + argv = append(argv, p) + checkStatic(p) + } + if ctxt.HeadType == objabi.Hwindows { + // use gcc linker script to work around gcc bug + // (see https://golang.org/issue/20183 for details). + p := writeGDBLinkerScript() + argv = append(argv, "-Wl,-T,"+p) + // libmingw32 and libmingwex have some inter-dependencies, + // so must use linker groups. + argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group") + argv = append(argv, peimporteddlls()...) + } + + if ctxt.Debugvlog != 0 { + ctxt.Logf("host link:") + for _, v := range argv { + ctxt.Logf(" %q", v) + } + ctxt.Logf("\n") + } + + out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput() + if err != nil { + Exitf("running %s failed: %v\n%s", argv[0], err, out) + } + + // Filter out useless linker warnings caused by bugs outside Go. + // See also cmd/go/internal/work/exec.go's gccld method. + var save [][]byte + var skipLines int + for _, line := range bytes.SplitAfter(out, []byte("\n")) { + // golang.org/issue/26073 - Apple Xcode bug + if bytes.Contains(line, []byte("ld: warning: text-based stub file")) { + continue + } + + if skipLines > 0 { + skipLines-- + continue + } + + // Remove TOC overflow warning on AIX. + if bytes.Contains(line, []byte("ld: 0711-783")) { + skipLines = 2 + continue + } + + save = append(save, line) + } + out = bytes.Join(save, nil) + + if len(out) > 0 { + // always print external output even if the command is successful, so that we don't + // swallow linker warnings (see https://golang.org/issue/17935). + ctxt.Logf("%s", out) + } + + if !*FlagS && !*FlagW && !debug_s && ctxt.HeadType == objabi.Hdarwin { + dsym := filepath.Join(*flagTmpdir, "go.dwarf") + if out, err := exec.Command("dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil { + Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out) + } + // Skip combining if `dsymutil` didn't generate a file. See #11994. + if _, err := os.Stat(dsym); os.IsNotExist(err) { + return + } + // For os.Rename to work reliably, must be in same directory as outfile. + combinedOutput := *flagOutfile + "~" + exef, err := os.Open(*flagOutfile) + if err != nil { + Exitf("%s: combining dwarf failed: %v", os.Args[0], err) + } + defer exef.Close() + exem, err := macho.NewFile(exef) + if err != nil { + Exitf("%s: parsing Mach-O header failed: %v", os.Args[0], err) + } + // Only macOS supports unmapped segments such as our __DWARF segment. + if machoPlatform == PLATFORM_MACOS { + if err := machoCombineDwarf(ctxt, exef, exem, dsym, combinedOutput); err != nil { + Exitf("%s: combining dwarf failed: %v", os.Args[0], err) + } + os.Remove(*flagOutfile) + if err := os.Rename(combinedOutput, *flagOutfile); err != nil { + Exitf("%s: %v", os.Args[0], err) + } + } + } +} + +var createTrivialCOnce sync.Once + +func linkerFlagSupported(linker, flag string) bool { + createTrivialCOnce.Do(func() { + src := filepath.Join(*flagTmpdir, "trivial.c") + if err := ioutil.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil { + Errorf(nil, "WriteFile trivial.c failed: %v", err) + } + }) + + flagsWithNextArgSkip := []string{ + "-F", + "-l", + "-L", + "-framework", + "-Wl,-framework", + "-Wl,-rpath", + "-Wl,-undefined", + } + flagsWithNextArgKeep := []string{ + "-arch", + "-isysroot", + "--sysroot", + "-target", + } + prefixesToKeep := []string{ + "-f", + "-m", + "-p", + "-Wl,", + "-arch", + "-isysroot", + "--sysroot", + "-target", + } + + var flags []string + keep := false + skip := false + extldflags := strings.Fields(*flagExtldflags) + for _, f := range append(extldflags, ldflag...) { + if keep { + flags = append(flags, f) + keep = false + } else if skip { + skip = false + } else if f == "" || f[0] != '-' { + } else if contains(flagsWithNextArgSkip, f) { + skip = true + } else if contains(flagsWithNextArgKeep, f) { + flags = append(flags, f) + keep = true + } else { + for _, p := range prefixesToKeep { + if strings.HasPrefix(f, p) { + flags = append(flags, f) + break + } + } + } + } + + flags = append(flags, flag, "trivial.c") + + cmd := exec.Command(linker, flags...) + cmd.Dir = *flagTmpdir + cmd.Env = append([]string{"LC_ALL=C"}, os.Environ()...) + out, err := cmd.CombinedOutput() + // GCC says "unrecognized command line option ‘-no-pie’" + // clang says "unknown argument: '-no-pie'" + return err == nil && !bytes.Contains(out, []byte("unrecognized")) && !bytes.Contains(out, []byte("unknown")) +} + +// hostlinkArchArgs returns arguments to pass to the external linker +// based on the architecture. +func hostlinkArchArgs(arch *sys.Arch) []string { + switch arch.Family { + case sys.I386: + return []string{"-m32"} + case sys.AMD64, sys.S390X: + return []string{"-m64"} + case sys.ARM: + return []string{"-marm"} + case sys.ARM64: + // nothing needed + case sys.MIPS64: + return []string{"-mabi=64"} + case sys.MIPS: + return []string{"-mabi=32"} + case sys.PPC64: + if objabi.GOOS == "aix" { + return []string{"-maix64"} + } else { + return []string{"-m64"} + } + + } + return nil +} + +// ldobj loads an input object. If it is a host object (an object +// compiled by a non-Go compiler) it returns the Hostobj pointer. If +// it is a Go object, it returns nil. +func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj { + pkg := objabi.PathToPrefix(lib.Pkg) + + eof := f.Offset() + length + start := f.Offset() + c1 := bgetc(f) + c2 := bgetc(f) + c3 := bgetc(f) + c4 := bgetc(f) + f.MustSeek(start, 0) + + unit := &sym.CompilationUnit{Lib: lib} + lib.Units = append(lib.Units, unit) + + magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) + if magic == 0x7f454c46 { // \x7F E L F + if *flagNewobj { + ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags) + if err != nil { + Errorf(nil, "%v", err) + return + } + ehdr.flags = flags + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) + } else { + ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, flags, err := loadelf.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags) + if err != nil { + Errorf(nil, "%v", err) + return + } + ehdr.flags = flags + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) + } + } + + if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { + if *flagNewobj { + ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) + } else { + ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadmacho.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) + } + } + + if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 { + if *flagNewobj { + ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + if rsrc != nil { + setpersrc(ctxt, rsrc) + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) + } else { + ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, rsrc, err := loadpe.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + if rsrc != nil { + setpersrc(ctxt, rsrc) + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) + } + } + + if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) { + if *flagNewobj { + ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) + } else { + ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadxcoff.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) + } + } + + /* check the header */ + line, err := f.ReadString('\n') + if err != nil { + Errorf(nil, "truncated object file: %s: %v", pn, err) + return nil + } + + if !strings.HasPrefix(line, "go object ") { + if strings.HasSuffix(pn, ".go") { + Exitf("%s: uncompiled .go source file", pn) + return nil + } + + if line == ctxt.Arch.Name { + // old header format: just $GOOS + Errorf(nil, "%s: stale object file", pn) + return nil + } + + Errorf(nil, "%s: not an object file", pn) + return nil + } + + // First, check that the basic GOOS, GOARCH, and Version match. + t := fmt.Sprintf("%s %s %s ", objabi.GOOS, objabi.GOARCH, objabi.Version) + + line = strings.TrimRight(line, "\n") + if !strings.HasPrefix(line[10:]+" ", t) && !*flagF { + Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], t) + return nil + } + + // Second, check that longer lines match each other exactly, + // so that the Go compiler and write additional information + // that must be the same from run to run. + if len(line) >= len(t)+10 { + if theline == "" { + theline = line[10:] + } else if theline != line[10:] { + Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], theline) + return nil + } + } + + // Skip over exports and other info -- ends with \n!\n. + // + // Note: It's possible for "\n!\n" to appear within the binary + // package export data format. To avoid truncating the package + // definition prematurely (issue 21703), we keep track of + // how many "$$" delimiters we've seen. + + import0 := f.Offset() + + c1 = '\n' // the last line ended in \n + c2 = bgetc(f) + c3 = bgetc(f) + markers := 0 + for { + if c1 == '\n' { + if markers%2 == 0 && c2 == '!' && c3 == '\n' { + break + } + if c2 == '$' && c3 == '$' { + markers++ + } + } + + c1 = c2 + c2 = c3 + c3 = bgetc(f) + if c3 == -1 { + Errorf(nil, "truncated object file: %s", pn) + return nil + } + } + + import1 := f.Offset() + + f.MustSeek(import0, 0) + ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n + f.MustSeek(import1, 0) + + flags := 0 + switch *FlagStrictDups { + case 0: + break + case 1: + flags = objfile.StrictDupsWarnFlag + case 2: + flags = objfile.StrictDupsErrFlag + default: + log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) + } + var c int + if *flagNewobj { + ctxt.loader.Preload(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags) + } else { + c = objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags) + } + strictDupMsgCount += c + addImports(ctxt, lib, pn) + return nil +} + +func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte { + data := make([]byte, sym.Size) + sect := f.Sections[sym.Section] + if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE { + Errorf(nil, "reading %s from non-data section", sym.Name) + } + n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr)) + if uint64(n) != sym.Size { + Errorf(nil, "reading contents of %s: %v", sym.Name, err) + } + return data +} + +func readwithpad(r io.Reader, sz int32) ([]byte, error) { + data := make([]byte, Rnd(int64(sz), 4)) + _, err := io.ReadFull(r, data) + if err != nil { + return nil, err + } + data = data[:sz] + return data, nil +} + +func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) { + for _, sect := range f.Sections { + if sect.Type != elf.SHT_NOTE { + continue + } + r := sect.Open() + for { + var namesize, descsize, noteType int32 + err := binary.Read(r, f.ByteOrder, &namesize) + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("read namesize failed: %v", err) + } + err = binary.Read(r, f.ByteOrder, &descsize) + if err != nil { + return nil, fmt.Errorf("read descsize failed: %v", err) + } + err = binary.Read(r, f.ByteOrder, ¬eType) + if err != nil { + return nil, fmt.Errorf("read type failed: %v", err) + } + noteName, err := readwithpad(r, namesize) + if err != nil { + return nil, fmt.Errorf("read name failed: %v", err) + } + desc, err := readwithpad(r, descsize) + if err != nil { + return nil, fmt.Errorf("read desc failed: %v", err) + } + if string(name) == string(noteName) && typ == noteType { + return desc, nil + } + } + } + return nil, nil +} + +func findshlib(ctxt *Link, shlib string) string { + if filepath.IsAbs(shlib) { + return shlib + } + for _, libdir := range ctxt.Libdir { + libpath := filepath.Join(libdir, shlib) + if _, err := os.Stat(libpath); err == nil { + return libpath + } + } + Errorf(nil, "cannot find shared library: %s", shlib) + return "" +} + +func ldshlibsyms(ctxt *Link, shlib string) { + var libpath string + if filepath.IsAbs(shlib) { + libpath = shlib + shlib = filepath.Base(shlib) + } else { + libpath = findshlib(ctxt, shlib) + if libpath == "" { + return + } + } + for _, processedlib := range ctxt.Shlibs { + if processedlib.Path == libpath { + return + } + } + if ctxt.Debugvlog > 1 { + ctxt.Logf("ldshlibsyms: found library with name %s at %s\n", shlib, libpath) + } + + f, err := elf.Open(libpath) + if err != nil { + Errorf(nil, "cannot open shared library: %s", libpath) + return + } + defer f.Close() + + hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) + if err != nil { + Errorf(nil, "cannot read ABI hash from shared library %s: %v", libpath, err) + return + } + + depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG) + if err != nil { + Errorf(nil, "cannot read dep list from shared library %s: %v", libpath, err) + return + } + var deps []string + for _, dep := range strings.Split(string(depsbytes), "\n") { + if dep == "" { + continue + } + if !filepath.IsAbs(dep) { + // If the dep can be interpreted as a path relative to the shlib + // in which it was found, do that. Otherwise, we will leave it + // to be resolved by libdir lookup. + abs := filepath.Join(filepath.Dir(libpath), dep) + if _, err := os.Stat(abs); err == nil { + dep = abs + } + } + deps = append(deps, dep) + } + + syms, err := f.DynamicSymbols() + if err != nil { + Errorf(nil, "cannot read symbols from shared library: %s", libpath) + return + } + gcdataLocations := make(map[uint64]*sym.Symbol) + for _, elfsym := range syms { + if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION { + continue + } + + // Symbols whose names start with "type." are compiler + // generated, so make functions with that prefix internal. + ver := 0 + if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type.") { + ver = sym.SymVerABIInternal + } + + var lsym *sym.Symbol + if *flagNewobj { + i := ctxt.loader.AddExtSym(elfsym.Name, ver) + if i == 0 { + continue + } + lsym = ctxt.Syms.Newsym(elfsym.Name, ver) + ctxt.loader.Syms[i] = lsym + } else { + lsym = ctxt.Syms.Lookup(elfsym.Name, ver) + } + // Because loadlib above loads all .a files before loading any shared + // libraries, any non-dynimport symbols we find that duplicate symbols + // already loaded should be ignored (the symbols from the .a files + // "win"). + if lsym.Type != 0 && lsym.Type != sym.SDYNIMPORT { + continue + } + lsym.Type = sym.SDYNIMPORT + lsym.SetElfType(elf.ST_TYPE(elfsym.Info)) + lsym.Size = int64(elfsym.Size) + if elfsym.Section != elf.SHN_UNDEF { + // Set .File for the library that actually defines the symbol. + lsym.File = libpath + // The decodetype_* functions in decodetype.go need access to + // the type data. + if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") { + lsym.P = readelfsymboldata(ctxt, f, &elfsym) + gcdataLocations[elfsym.Value+2*uint64(ctxt.Arch.PtrSize)+8+1*uint64(ctxt.Arch.PtrSize)] = lsym + } + } + // For function symbols, we don't know what ABI is + // available, so alias it under both ABIs. + // + // TODO(austin): This is almost certainly wrong once + // the ABIs are actually different. We might have to + // mangle Go function names in the .so to include the + // ABI. + if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 { + var alias *sym.Symbol + if *flagNewobj { + i := ctxt.loader.AddExtSym(elfsym.Name, sym.SymVerABIInternal) + if i == 0 { + continue + } + alias = ctxt.Syms.Newsym(elfsym.Name, sym.SymVerABIInternal) + ctxt.loader.Syms[i] = alias + } else { + alias = ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal) + } + if alias.Type != 0 { + continue + } + alias.Type = sym.SABIALIAS + alias.R = []sym.Reloc{{Sym: lsym}} + } + } + gcdataAddresses := make(map[*sym.Symbol]uint64) + if ctxt.Arch.Family == sys.ARM64 { + for _, sect := range f.Sections { + if sect.Type == elf.SHT_RELA { + var rela elf.Rela64 + rdr := sect.Open() + for { + err := binary.Read(rdr, f.ByteOrder, &rela) + if err == io.EOF { + break + } else if err != nil { + Errorf(nil, "reading relocation failed %v", err) + return + } + t := elf.R_AARCH64(rela.Info & 0xffff) + if t != elf.R_AARCH64_RELATIVE { + continue + } + if lsym, ok := gcdataLocations[rela.Off]; ok { + gcdataAddresses[lsym] = uint64(rela.Addend) + } + } + } + } + } + + ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdataAddresses: gcdataAddresses}) +} + +func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section { + sect := new(sym.Section) + sect.Rwx = uint8(rwx) + sect.Name = name + sect.Seg = seg + sect.Align = int32(arch.PtrSize) // everything is at least pointer-aligned + seg.Sections = append(seg.Sections, sect) + return sect +} + +type chain struct { + sym *sym.Symbol + up *chain + limit int // limit on entry to sym +} + +var morestack *sym.Symbol + +// TODO: Record enough information in new object files to +// allow stack checks here. + +func haslinkregister(ctxt *Link) bool { + return ctxt.FixedFrameSize() != 0 +} + +func callsize(ctxt *Link) int { + if haslinkregister(ctxt) { + return 0 + } + return ctxt.Arch.RegSize +} + +func (ctxt *Link) dostkcheck() { + var ch chain + + morestack = ctxt.Syms.Lookup("runtime.morestack", 0) + + // Every splitting function ensures that there are at least StackLimit + // bytes available below SP when the splitting prologue finishes. + // If the splitting function calls F, then F begins execution with + // at least StackLimit - callsize() bytes available. + // Check that every function behaves correctly with this amount + // of stack, following direct calls in order to piece together chains + // of non-splitting functions. + ch.up = nil + + ch.limit = objabi.StackLimit - callsize(ctxt) + if objabi.GOARCH == "arm64" { + // need extra 8 bytes below SP to save FP + ch.limit -= 8 + } + + // Check every function, but do the nosplit functions in a first pass, + // to make the printed failure chains as short as possible. + for _, s := range ctxt.Textp { + // runtime.racesymbolizethunk is called from gcc-compiled C + // code running on the operating system thread stack. + // It uses more than the usual amount of stack but that's okay. + if s.Name == "runtime.racesymbolizethunk" { + continue + } + + if s.Attr.NoSplit() { + ch.sym = s + stkcheck(ctxt, &ch, 0) + } + } + + for _, s := range ctxt.Textp { + if !s.Attr.NoSplit() { + ch.sym = s + stkcheck(ctxt, &ch, 0) + } + } +} + +func stkcheck(ctxt *Link, up *chain, depth int) int { + limit := up.limit + s := up.sym + + // Don't duplicate work: only need to consider each + // function at top of safe zone once. + top := limit == objabi.StackLimit-callsize(ctxt) + if top { + if s.Attr.StackCheck() { + return 0 + } + s.Attr |= sym.AttrStackCheck + } + + if depth > 500 { + Errorf(s, "nosplit stack check too deep") + stkbroke(ctxt, up, 0) + return -1 + } + + if s.Attr.External() || s.FuncInfo == nil { + // external function. + // should never be called directly. + // onlyctxt.Diagnose the direct caller. + // TODO(mwhudson): actually think about this. + // TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack. + // See the trampolines in src/runtime/sys_darwin_$ARCH.go. + if depth == 1 && s.Type != sym.SXREF && !ctxt.DynlinkingGo() && + ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin { + //Errorf(s, "call to external function") + } + return -1 + } + + if limit < 0 { + stkbroke(ctxt, up, limit) + return -1 + } + + // morestack looks like it calls functions, + // but it switches the stack pointer first. + if s == morestack { + return 0 + } + + var ch chain + ch.up = up + + if !s.Attr.NoSplit() { + // Ensure we have enough stack to call morestack. + ch.limit = limit - callsize(ctxt) + ch.sym = morestack + if stkcheck(ctxt, &ch, depth+1) < 0 { + return -1 + } + if !top { + return 0 + } + // Raise limit to allow frame. + locals := int32(0) + if s.FuncInfo != nil { + locals = s.FuncInfo.Locals + } + limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize()) + } + + // Walk through sp adjustments in function, consuming relocs. + ri := 0 + + endr := len(s.R) + var ch1 chain + pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) + var r *sym.Reloc + for pcsp.Init(s.FuncInfo.Pcsp.P); !pcsp.Done; pcsp.Next() { + // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). + + // Check stack size in effect for this span. + if int32(limit)-pcsp.Value < 0 { + stkbroke(ctxt, up, int(int32(limit)-pcsp.Value)) + return -1 + } + + // Process calls in this span. + for ; ri < endr && uint32(s.R[ri].Off) < pcsp.NextPC; ri++ { + r = &s.R[ri] + switch { + case r.Type.IsDirectCall(): + ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt))) + ch.sym = r.Sym + if stkcheck(ctxt, &ch, depth+1) < 0 { + return -1 + } + + // Indirect call. Assume it is a call to a splitting function, + // so we have to make sure it can call morestack. + // Arrange the data structures to report both calls, so that + // if there is an error, stkprint shows all the steps involved. + case r.Type == objabi.R_CALLIND: + ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt))) + ch.sym = nil + ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue + ch1.up = &ch + ch1.sym = morestack + if stkcheck(ctxt, &ch1, depth+2) < 0 { + return -1 + } + } + } + } + + return 0 +} + +func stkbroke(ctxt *Link, ch *chain, limit int) { + Errorf(ch.sym, "nosplit stack overflow") + stkprint(ctxt, ch, limit) +} + +func stkprint(ctxt *Link, ch *chain, limit int) { + var name string + + if ch.sym != nil { + name = ch.sym.Name + if ch.sym.Attr.NoSplit() { + name += " (nosplit)" + } + } else { + name = "function pointer" + } + + if ch.up == nil { + // top of chain. ch->sym != nil. + if ch.sym.Attr.NoSplit() { + fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name) + } else { + fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name) + } + } else { + stkprint(ctxt, ch.up, ch.limit+callsize(ctxt)) + if !haslinkregister(ctxt) { + fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name) + } + } + + if ch.limit != limit { + fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n") + objabi.Flagprint(os.Stderr) + Exit(2) +} + +type SymbolType int8 + +const ( + // see also https://9p.io/magic/man2html/1/nm + TextSym SymbolType = 'T' + DataSym SymbolType = 'D' + BSSSym SymbolType = 'B' + UndefinedSym SymbolType = 'U' + TLSSym SymbolType = 't' + FrameSym SymbolType = 'm' + ParamSym SymbolType = 'p' + AutoSym SymbolType = 'a' + + // Deleted auto (not a real sym, just placeholder for type) + DeletedAutoSym = 'x' +) + +func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int64, *sym.Symbol)) { + // These symbols won't show up in the first loop below because we + // skip sym.STEXT symbols. Normal sym.STEXT symbols are emitted by walking textp. + s := ctxt.Syms.Lookup("runtime.text", 0) + if s.Type == sym.STEXT { + // We've already included this symbol in ctxt.Textp + // if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin or + // on AIX with external linker. + // See data.go:/textaddress + if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + put(ctxt, s, s.Name, TextSym, s.Value, nil) + } + } + + n := 0 + + // Generate base addresses for all text sections if there are multiple + for _, sect := range Segtext.Sections { + if n == 0 { + n++ + continue + } + if sect.Name != ".text" || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + // On AIX, runtime.text.X are symbols already in the symtab. + break + } + s = ctxt.Syms.ROLookup(fmt.Sprintf("runtime.text.%d", n), 0) + if s == nil { + break + } + if s.Type == sym.STEXT { + put(ctxt, s, s.Name, TextSym, s.Value, nil) + } + n++ + } + + s = ctxt.Syms.Lookup("runtime.etext", 0) + if s.Type == sym.STEXT { + // We've already included this symbol in ctxt.Textp + // if ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin or + // on AIX with external linker. + // See data.go:/textaddress + if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + put(ctxt, s, s.Name, TextSym, s.Value, nil) + } + } + + shouldBeInSymbolTable := func(s *sym.Symbol) bool { + if s.Attr.NotInSymbolTable() { + return false + } + if ctxt.HeadType == objabi.Haix && s.Name == ".go.buildinfo" { + // On AIX, .go.buildinfo must be in the symbol table as + // it has relocations. + return true + } + if (s.Name == "" || s.Name[0] == '.') && !s.IsFileLocal() && s.Name != ".rathole" && s.Name != ".TOC." { + return false + } + return true + } + + for _, s := range ctxt.Syms.Allsym { + if !shouldBeInSymbolTable(s) { + continue + } + switch s.Type { + case sym.SCONST, + sym.SRODATA, + sym.SSYMTAB, + sym.SPCLNTAB, + sym.SINITARR, + sym.SDATA, + sym.SNOPTRDATA, + sym.SELFROSECT, + sym.SMACHOGOT, + sym.STYPE, + sym.SSTRING, + sym.SGOSTRING, + sym.SGOFUNC, + sym.SGCBITS, + sym.STYPERELRO, + sym.SSTRINGRELRO, + sym.SGOSTRINGRELRO, + sym.SGOFUNCRELRO, + sym.SGCBITSRELRO, + sym.SRODATARELRO, + sym.STYPELINK, + sym.SITABLINK, + sym.SWINDOWS: + if !s.Attr.Reachable() { + continue + } + put(ctxt, s, s.Name, DataSym, Symaddr(s), s.Gotype) + + case sym.SBSS, sym.SNOPTRBSS, sym.SLIBFUZZER_EXTRA_COUNTER: + if !s.Attr.Reachable() { + continue + } + if len(s.P) > 0 { + Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(s.P), s.Type, s.Attr.Special()) + } + put(ctxt, s, s.Name, BSSSym, Symaddr(s), s.Gotype) + + case sym.SUNDEFEXT: + if ctxt.HeadType == objabi.Hwindows || ctxt.HeadType == objabi.Haix || ctxt.IsELF { + put(ctxt, s, s.Name, UndefinedSym, s.Value, nil) + } + + case sym.SHOSTOBJ: + if !s.Attr.Reachable() { + continue + } + if ctxt.HeadType == objabi.Hwindows || ctxt.IsELF { + put(ctxt, s, s.Name, UndefinedSym, s.Value, nil) + } + + case sym.SDYNIMPORT: + if !s.Attr.Reachable() { + continue + } + put(ctxt, s, s.Extname(), UndefinedSym, 0, nil) + + case sym.STLSBSS: + if ctxt.LinkMode == LinkExternal { + put(ctxt, s, s.Name, TLSSym, Symaddr(s), s.Gotype) + } + } + } + + for _, s := range ctxt.Textp { + put(ctxt, s, s.Name, TextSym, s.Value, s.Gotype) + + locals := int32(0) + if s.FuncInfo != nil { + locals = s.FuncInfo.Locals + } + // NOTE(ality): acid can't produce a stack trace without .frame symbols + put(ctxt, nil, ".frame", FrameSym, int64(locals)+int64(ctxt.Arch.PtrSize), nil) + + if s.FuncInfo == nil { + continue + } + } + + if ctxt.Debugvlog != 0 || *flagN { + ctxt.Logf("symsize = %d\n", uint32(Symsize)) + } +} + +func Symaddr(s *sym.Symbol) int64 { + if !s.Attr.Reachable() { + Errorf(s, "unreachable symbol in symaddr") + } + return s.Value +} + +func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) { + s := ctxt.Syms.Lookup(p, 0) + s.Type = t + s.Value = v + s.Attr |= sym.AttrReachable + s.Attr |= sym.AttrSpecial + s.Attr |= sym.AttrLocal +} + +func datoff(s *sym.Symbol, addr int64) int64 { + if uint64(addr) >= Segdata.Vaddr { + return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff) + } + if uint64(addr) >= Segtext.Vaddr { + return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff) + } + Errorf(s, "invalid datoff %#x", addr) + return 0 +} + +func Entryvalue(ctxt *Link) int64 { + a := *flagEntrySymbol + if a[0] >= '0' && a[0] <= '9' { + return atolwhex(a) + } + s := ctxt.Syms.Lookup(a, 0) + if s.Type == 0 { + return *FlagTextAddr + } + if ctxt.HeadType != objabi.Haix && s.Type != sym.STEXT { + Errorf(s, "entry not text") + } + return s.Value +} + +func undefsym(ctxt *Link, s *sym.Symbol) { + var r *sym.Reloc + + for i := 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { // happens for some external ARM relocs + continue + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (r.Sym.Type == sym.Sxxx || r.Sym.Type == sym.SXREF) && !r.Sym.Attr.VisibilityHidden() { + Errorf(s, "undefined: %q", r.Sym.Name) + } + if !r.Sym.Attr.Reachable() && r.Type != objabi.R_WEAKADDROFF { + Errorf(s, "relocation target %q", r.Sym.Name) + } + } +} + +func (ctxt *Link) undef() { + // undefsym performs checks (almost) identical to checks + // that report undefined relocations in relocsym. + // Both undefsym and relocsym can report same symbol as undefined, + // which results in error message duplication (see #10978). + // + // The undef is run after Arch.Asmb and could detect some + // programming errors there, but if object being linked is already + // failed with errors, it is better to avoid duplicated errors. + if nerrors > 0 { + return + } + + for _, s := range ctxt.Textp { + undefsym(ctxt, s) + } + for _, s := range datap { + undefsym(ctxt, s) + } + if nerrors > 0 { + errorexit() + } +} + +func (ctxt *Link) callgraph() { + if !*FlagC { + return + } + + var i int + var r *sym.Reloc + for _, s := range ctxt.Textp { + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { + continue + } + if r.Type.IsDirectCall() && r.Sym.Type == sym.STEXT { + ctxt.Logf("%s calls %s\n", s.Name, r.Sym.Name) + } + } + } +} + +func Rnd(v int64, r int64) int64 { + if r <= 0 { + return v + } + v += r - 1 + c := v % r + if c < 0 { + c += r + } + v -= c + return v +} + +func bgetc(r *bio.Reader) int { + c, err := r.ReadByte() + if err != nil { + if err != io.EOF { + log.Fatalf("reading input: %v", err) + } + return -1 + } + return int(c) +} + +type markKind uint8 // for postorder traversal +const ( + _ markKind = iota + visiting + visited +) + +func postorder(libs []*sym.Library) []*sym.Library { + order := make([]*sym.Library, 0, len(libs)) // hold the result + mark := make(map[*sym.Library]markKind, len(libs)) + for _, lib := range libs { + dfs(lib, mark, &order) + } + return order +} + +func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library) { + if mark[lib] == visited { + return + } + if mark[lib] == visiting { + panic("found import cycle while visiting " + lib.Pkg) + } + mark[lib] = visiting + for _, i := range lib.Imports { + dfs(i, mark, order) + } + mark[lib] = visited + *order = append(*order, lib) +} + +func (ctxt *Link) loadlibfull() { + // Load full symbol contents, resolve indexed references. + ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms) + + // Pull the symbols out. + ctxt.loader.ExtractSymbols(ctxt.Syms) + + // Load cgo directives. + for _, d := range ctxt.cgodata { + setCgoAttr(ctxt, ctxt.Syms.Lookup, d.file, d.pkg, d.directives) + } + + setupdynexp(ctxt) + + // Populate ctxt.Reachparent if appropriate. + if ctxt.Reachparent != nil { + for i := 0; i < len(ctxt.loader.Reachparent); i++ { + p := ctxt.loader.Reachparent[i] + if p == 0 { + continue + } + if p == loader.Sym(i) { + panic("self-cycle in reachparent") + } + sym := ctxt.loader.Syms[i] + psym := ctxt.loader.Syms[p] + ctxt.Reachparent[sym] = psym + } + } + + // Drop the reference. + ctxt.loader = nil + ctxt.cgodata = nil + + addToTextp(ctxt) +} + +func (ctxt *Link) dumpsyms() { + for _, s := range ctxt.Syms.Allsym { + fmt.Printf("%s %s %p %v %v\n", s, s.Type, s, s.Attr.Reachable(), s.Attr.OnList()) + for i := range s.R { + fmt.Println("\t", s.R[i].Type, s.R[i].Sym) + } + } +} diff --git a/src/cmd/oldlink/internal/ld/link.go b/src/cmd/oldlink/internal/ld/link.go new file mode 100644 index 0000000000..4020f8dbc4 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/link.go @@ -0,0 +1,187 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/loader" + "cmd/oldlink/internal/sym" + "debug/elf" + "fmt" +) + +type Shlib struct { + Path string + Hash []byte + Deps []string + File *elf.File + gcdataAddresses map[*sym.Symbol]uint64 +} + +// Link holds the context for writing object code from a compiler +// or for reading that input into the linker. +type Link struct { + Out *OutBuf + + Syms *sym.Symbols + + Arch *sys.Arch + Debugvlog int + Bso *bufio.Writer + + Loaded bool // set after all inputs have been loaded as symbols + + IsELF bool + HeadType objabi.HeadType + + linkShared bool // link against installed Go shared libraries + LinkMode LinkMode + BuildMode BuildMode + canUsePlugins bool // initialized when Loaded is set to true + compressDWARF bool + + Tlsg *sym.Symbol + Libdir []string + Library []*sym.Library + LibraryByPkg map[string]*sym.Library + Shlibs []Shlib + Tlsoffset int + Textp []*sym.Symbol + Filesyms []*sym.Symbol + Moduledata *sym.Symbol + + PackageFile map[string]string + PackageShlib map[string]string + + tramps []*sym.Symbol // trampolines + + // unresolvedSymSet is a set of erroneous unresolved references. + // Used to avoid duplicated error messages. + unresolvedSymSet map[unresolvedSymKey]bool + + // Used to implement field tracking. + Reachparent map[*sym.Symbol]*sym.Symbol + + compUnits []*sym.CompilationUnit // DWARF compilation units + runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen. + + relocbuf []byte // temporary buffer for applying relocations + + loader *loader.Loader + cgodata []cgodata // cgo directives to load, three strings are args for loadcgo + + cgo_export_static map[string]bool + cgo_export_dynamic map[string]bool +} + +type cgodata struct { + file string + pkg string + directives [][]string +} + +type unresolvedSymKey struct { + from *sym.Symbol // Symbol that referenced unresolved "to" + to *sym.Symbol // Unresolved symbol referenced by "from" +} + +// ErrorUnresolved prints unresolved symbol error for r.Sym that is referenced from s. +func (ctxt *Link) ErrorUnresolved(s *sym.Symbol, r *sym.Reloc) { + if ctxt.unresolvedSymSet == nil { + ctxt.unresolvedSymSet = make(map[unresolvedSymKey]bool) + } + + k := unresolvedSymKey{from: s, to: r.Sym} + if !ctxt.unresolvedSymSet[k] { + ctxt.unresolvedSymSet[k] = true + + // Try to find symbol under another ABI. + var reqABI, haveABI obj.ABI + haveABI = ^obj.ABI(0) + reqABI, ok := sym.VersionToABI(int(r.Sym.Version)) + if ok { + for abi := obj.ABI(0); abi < obj.ABICount; abi++ { + v := sym.ABIToVersion(abi) + if v == -1 { + continue + } + if rs := ctxt.Syms.ROLookup(r.Sym.Name, v); rs != nil && rs.Type != sym.Sxxx { + haveABI = abi + } + } + } + + // Give a special error message for main symbol (see #24809). + if r.Sym.Name == "main.main" { + Errorf(s, "function main is undeclared in the main package") + } else if haveABI != ^obj.ABI(0) { + Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", r.Sym.Name, reqABI, haveABI) + } else { + Errorf(s, "relocation target %s not defined", r.Sym.Name) + } + } +} + +// The smallest possible offset from the hardware stack pointer to a local +// variable on the stack. Architectures that use a link register save its value +// on the stack in the function prologue and so always have a pointer between +// the hardware stack pointer and the local variable area. +func (ctxt *Link) FixedFrameSize() int64 { + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: + return 0 + case sys.PPC64: + // PIC code on ppc64le requires 32 bytes of stack, and it's easier to + // just use that much stack always on ppc64x. + return int64(4 * ctxt.Arch.PtrSize) + default: + return int64(ctxt.Arch.PtrSize) + } +} + +func (ctxt *Link) Logf(format string, args ...interface{}) { + fmt.Fprintf(ctxt.Bso, format, args...) + ctxt.Bso.Flush() +} + +func addImports(ctxt *Link, l *sym.Library, pn string) { + pkg := objabi.PathToPrefix(l.Pkg) + for _, importStr := range l.ImportStrings { + lib := addlib(ctxt, pkg, pn, importStr) + if lib != nil { + l.Imports = append(l.Imports, lib) + } + } + l.ImportStrings = nil +} diff --git a/src/cmd/oldlink/internal/ld/macho.go b/src/cmd/oldlink/internal/ld/macho.go new file mode 100644 index 0000000000..960ed29067 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/macho.go @@ -0,0 +1,1119 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "debug/macho" + "encoding/binary" + "fmt" + "io" + "os" + "sort" + "strings" +) + +type MachoHdr struct { + cpu uint32 + subcpu uint32 +} + +type MachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloc uint32 + nreloc uint32 + flag uint32 + res1 uint32 + res2 uint32 +} + +type MachoSeg struct { + name string + vsize uint64 + vaddr uint64 + fileoffset uint64 + filesize uint64 + prot1 uint32 + prot2 uint32 + nsect uint32 + msect uint32 + sect []MachoSect + flag uint32 +} + +// MachoPlatformLoad represents a LC_VERSION_MIN_* or +// LC_BUILD_VERSION load command. +type MachoPlatformLoad struct { + platform MachoPlatform // One of PLATFORM_* constants. + cmd MachoLoad +} + +type MachoLoad struct { + type_ uint32 + data []uint32 +} + +type MachoPlatform int + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, and SHeaders. + * May waste some. + */ +const ( + INITIAL_MACHO_HEADR = 4 * 1024 +) + +const ( + MACHO_CPU_AMD64 = 1<<24 | 7 + MACHO_CPU_386 = 7 + MACHO_SUBCPU_X86 = 3 + MACHO_CPU_ARM = 12 + MACHO_SUBCPU_ARM = 0 + MACHO_SUBCPU_ARMV7 = 9 + MACHO_CPU_ARM64 = 1<<24 | 12 + MACHO_SUBCPU_ARM64_ALL = 0 + MACHO32SYMSIZE = 12 + MACHO64SYMSIZE = 16 + MACHO_X86_64_RELOC_UNSIGNED = 0 + MACHO_X86_64_RELOC_SIGNED = 1 + MACHO_X86_64_RELOC_BRANCH = 2 + MACHO_X86_64_RELOC_GOT_LOAD = 3 + MACHO_X86_64_RELOC_GOT = 4 + MACHO_X86_64_RELOC_SUBTRACTOR = 5 + MACHO_X86_64_RELOC_SIGNED_1 = 6 + MACHO_X86_64_RELOC_SIGNED_2 = 7 + MACHO_X86_64_RELOC_SIGNED_4 = 8 + MACHO_ARM_RELOC_VANILLA = 0 + MACHO_ARM_RELOC_PAIR = 1 + MACHO_ARM_RELOC_SECTDIFF = 2 + MACHO_ARM_RELOC_BR24 = 5 + MACHO_ARM64_RELOC_UNSIGNED = 0 + MACHO_ARM64_RELOC_BRANCH26 = 2 + MACHO_ARM64_RELOC_PAGE21 = 3 + MACHO_ARM64_RELOC_PAGEOFF12 = 4 + MACHO_ARM64_RELOC_ADDEND = 10 + MACHO_GENERIC_RELOC_VANILLA = 0 + MACHO_FAKE_GOTPCREL = 100 +) + +const ( + MH_MAGIC = 0xfeedface + MH_MAGIC_64 = 0xfeedfacf + + MH_OBJECT = 0x1 + MH_EXECUTE = 0x2 + + MH_NOUNDEFS = 0x1 +) + +const ( + LC_SEGMENT = 0x1 + LC_SYMTAB = 0x2 + LC_SYMSEG = 0x3 + LC_THREAD = 0x4 + LC_UNIXTHREAD = 0x5 + LC_LOADFVMLIB = 0x6 + LC_IDFVMLIB = 0x7 + LC_IDENT = 0x8 + LC_FVMFILE = 0x9 + LC_PREPAGE = 0xa + LC_DYSYMTAB = 0xb + LC_LOAD_DYLIB = 0xc + LC_ID_DYLIB = 0xd + LC_LOAD_DYLINKER = 0xe + LC_ID_DYLINKER = 0xf + LC_PREBOUND_DYLIB = 0x10 + LC_ROUTINES = 0x11 + LC_SUB_FRAMEWORK = 0x12 + LC_SUB_UMBRELLA = 0x13 + LC_SUB_CLIENT = 0x14 + LC_SUB_LIBRARY = 0x15 + LC_TWOLEVEL_HINTS = 0x16 + LC_PREBIND_CKSUM = 0x17 + LC_LOAD_WEAK_DYLIB = 0x80000018 + LC_SEGMENT_64 = 0x19 + LC_ROUTINES_64 = 0x1a + LC_UUID = 0x1b + LC_RPATH = 0x8000001c + LC_CODE_SIGNATURE = 0x1d + LC_SEGMENT_SPLIT_INFO = 0x1e + LC_REEXPORT_DYLIB = 0x8000001f + LC_LAZY_LOAD_DYLIB = 0x20 + LC_ENCRYPTION_INFO = 0x21 + LC_DYLD_INFO = 0x22 + LC_DYLD_INFO_ONLY = 0x80000022 + LC_LOAD_UPWARD_DYLIB = 0x80000023 + LC_VERSION_MIN_MACOSX = 0x24 + LC_VERSION_MIN_IPHONEOS = 0x25 + LC_FUNCTION_STARTS = 0x26 + LC_DYLD_ENVIRONMENT = 0x27 + LC_MAIN = 0x80000028 + LC_DATA_IN_CODE = 0x29 + LC_SOURCE_VERSION = 0x2A + LC_DYLIB_CODE_SIGN_DRS = 0x2B + LC_ENCRYPTION_INFO_64 = 0x2C + LC_LINKER_OPTION = 0x2D + LC_LINKER_OPTIMIZATION_HINT = 0x2E + LC_VERSION_MIN_TVOS = 0x2F + LC_VERSION_MIN_WATCHOS = 0x30 + LC_VERSION_NOTE = 0x31 + LC_BUILD_VERSION = 0x32 +) + +const ( + S_REGULAR = 0x0 + S_ZEROFILL = 0x1 + S_NON_LAZY_SYMBOL_POINTERS = 0x6 + S_SYMBOL_STUBS = 0x8 + S_MOD_INIT_FUNC_POINTERS = 0x9 + S_ATTR_PURE_INSTRUCTIONS = 0x80000000 + S_ATTR_DEBUG = 0x02000000 + S_ATTR_SOME_INSTRUCTIONS = 0x00000400 +) + +const ( + PLATFORM_MACOS MachoPlatform = 1 + PLATFORM_IOS MachoPlatform = 2 + PLATFORM_TVOS MachoPlatform = 3 + PLATFORM_WATCHOS MachoPlatform = 4 + PLATFORM_BRIDGEOS MachoPlatform = 5 +) + +// Mach-O file writing +// https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html + +var machohdr MachoHdr + +var load []MachoLoad + +var machoPlatform MachoPlatform + +var seg [16]MachoSeg + +var nseg int + +var ndebug int + +var nsect int + +const ( + SymKindLocal = 0 + iota + SymKindExtdef + SymKindUndef + NumSymKind +) + +var nkind [NumSymKind]int + +var sortsym []*sym.Symbol + +var nsortsym int + +// Amount of space left for adding load commands +// that refer to dynamic libraries. Because these have +// to go in the Mach-O header, we can't just pick a +// "big enough" header size. The initial header is +// one page, the non-dynamic library stuff takes +// up about 1300 bytes; we overestimate that as 2k. +var loadBudget = INITIAL_MACHO_HEADR - 2*1024 + +func getMachoHdr() *MachoHdr { + return &machohdr +} + +func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad { + if arch.PtrSize == 8 && (ndata&1 != 0) { + ndata++ + } + + load = append(load, MachoLoad{}) + l := &load[len(load)-1] + l.type_ = type_ + l.data = make([]uint32, ndata) + return l +} + +func newMachoSeg(name string, msect int) *MachoSeg { + if nseg >= len(seg) { + Exitf("too many segs") + } + + s := &seg[nseg] + nseg++ + s.name = name + s.msect = uint32(msect) + s.sect = make([]MachoSect, msect) + return s +} + +func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect { + if seg.nsect >= seg.msect { + Exitf("too many sects in segment %s", seg.name) + } + + s := &seg.sect[seg.nsect] + seg.nsect++ + s.name = name + s.segname = segname + nsect++ + return s +} + +// Generic linking code. + +var dylib []string + +var linkoff int64 + +func machowrite(arch *sys.Arch, out *OutBuf, linkmode LinkMode) int { + o1 := out.Offset() + + loadsize := 4 * 4 * ndebug + for i := range load { + loadsize += 4 * (len(load[i].data) + 2) + } + if arch.PtrSize == 8 { + loadsize += 18 * 4 * nseg + loadsize += 20 * 4 * nsect + } else { + loadsize += 14 * 4 * nseg + loadsize += 17 * 4 * nsect + } + + if arch.PtrSize == 8 { + out.Write32(MH_MAGIC_64) + } else { + out.Write32(MH_MAGIC) + } + out.Write32(machohdr.cpu) + out.Write32(machohdr.subcpu) + if linkmode == LinkExternal { + out.Write32(MH_OBJECT) /* file type - mach object */ + } else { + out.Write32(MH_EXECUTE) /* file type - mach executable */ + } + out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) + out.Write32(uint32(loadsize)) + if nkind[SymKindUndef] == 0 { + out.Write32(MH_NOUNDEFS) /* flags - no undefines */ + } else { + out.Write32(0) /* flags */ + } + if arch.PtrSize == 8 { + out.Write32(0) /* reserved */ + } + + for i := 0; i < nseg; i++ { + s := &seg[i] + if arch.PtrSize == 8 { + out.Write32(LC_SEGMENT_64) + out.Write32(72 + 80*s.nsect) + out.WriteStringN(s.name, 16) + out.Write64(s.vaddr) + out.Write64(s.vsize) + out.Write64(s.fileoffset) + out.Write64(s.filesize) + out.Write32(s.prot1) + out.Write32(s.prot2) + out.Write32(s.nsect) + out.Write32(s.flag) + } else { + out.Write32(LC_SEGMENT) + out.Write32(56 + 68*s.nsect) + out.WriteStringN(s.name, 16) + out.Write32(uint32(s.vaddr)) + out.Write32(uint32(s.vsize)) + out.Write32(uint32(s.fileoffset)) + out.Write32(uint32(s.filesize)) + out.Write32(s.prot1) + out.Write32(s.prot2) + out.Write32(s.nsect) + out.Write32(s.flag) + } + + for j := uint32(0); j < s.nsect; j++ { + t := &s.sect[j] + if arch.PtrSize == 8 { + out.WriteStringN(t.name, 16) + out.WriteStringN(t.segname, 16) + out.Write64(t.addr) + out.Write64(t.size) + out.Write32(t.off) + out.Write32(t.align) + out.Write32(t.reloc) + out.Write32(t.nreloc) + out.Write32(t.flag) + out.Write32(t.res1) /* reserved */ + out.Write32(t.res2) /* reserved */ + out.Write32(0) /* reserved */ + } else { + out.WriteStringN(t.name, 16) + out.WriteStringN(t.segname, 16) + out.Write32(uint32(t.addr)) + out.Write32(uint32(t.size)) + out.Write32(t.off) + out.Write32(t.align) + out.Write32(t.reloc) + out.Write32(t.nreloc) + out.Write32(t.flag) + out.Write32(t.res1) /* reserved */ + out.Write32(t.res2) /* reserved */ + } + } + } + + for i := range load { + l := &load[i] + out.Write32(l.type_) + out.Write32(4 * (uint32(len(l.data)) + 2)) + for j := 0; j < len(l.data); j++ { + out.Write32(l.data[j]) + } + } + + return int(out.Offset() - o1) +} + +func (ctxt *Link) domacho() { + if *FlagD { + return + } + + // Copy platform load command. + for _, h := range hostobj { + load, err := hostobjMachoPlatform(&h) + if err != nil { + Exitf("%v", err) + } + if load != nil { + machoPlatform = load.platform + ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data))) + copy(ml.data, load.cmd.data) + break + } + } + if machoPlatform == 0 { + switch ctxt.Arch.Family { + default: + machoPlatform = PLATFORM_MACOS + if ctxt.LinkMode == LinkInternal { + // For lldb, must say LC_VERSION_MIN_MACOSX or else + // it won't know that this Mach-O binary is from OS X + // (could be iOS or WatchOS instead). + // Go on iOS uses linkmode=external, and linkmode=external + // adds this itself. So we only need this code for linkmode=internal + // and we can assume OS X. + // + // See golang.org/issues/12941. + // + // The version must be at least 10.9; see golang.org/issues/30488. + ml := newMachoLoad(ctxt.Arch, LC_VERSION_MIN_MACOSX, 2) + ml.data[0] = 10<<16 | 9<<8 | 0<<0 // OS X version 10.9.0 + ml.data[1] = 10<<16 | 9<<8 | 0<<0 // SDK 10.9.0 + } + case sys.ARM, sys.ARM64: + machoPlatform = PLATFORM_IOS + } + } + + // empirically, string table must begin with " \x00". + s := ctxt.Syms.Lookup(".machosymstr", 0) + + s.Type = sym.SMACHOSYMSTR + s.Attr |= sym.AttrReachable + s.AddUint8(' ') + s.AddUint8('\x00') + + s = ctxt.Syms.Lookup(".machosymtab", 0) + s.Type = sym.SMACHOSYMTAB + s.Attr |= sym.AttrReachable + + if ctxt.LinkMode != LinkExternal { + s := ctxt.Syms.Lookup(".plt", 0) // will be __symbol_stub + s.Type = sym.SMACHOPLT + s.Attr |= sym.AttrReachable + + s = ctxt.Syms.Lookup(".got", 0) // will be __nl_symbol_ptr + s.Type = sym.SMACHOGOT + s.Attr |= sym.AttrReachable + s.Align = 4 + + s = ctxt.Syms.Lookup(".linkedit.plt", 0) // indirect table for .plt + s.Type = sym.SMACHOINDIRECTPLT + s.Attr |= sym.AttrReachable + + s = ctxt.Syms.Lookup(".linkedit.got", 0) // indirect table for .got + s.Type = sym.SMACHOINDIRECTGOT + s.Attr |= sym.AttrReachable + } + + // Add a dummy symbol that will become the __asm marker section. + if ctxt.LinkMode == LinkExternal { + s := ctxt.Syms.Lookup(".llvmasm", 0) + s.Type = sym.SMACHO + s.Attr |= sym.AttrReachable + s.AddUint8(0) + } +} + +func machoadddynlib(lib string, linkmode LinkMode) { + if seenlib[lib] || linkmode == LinkExternal { + return + } + seenlib[lib] = true + + // Will need to store the library name rounded up + // and 24 bytes of header metadata. If not enough + // space, grab another page of initial space at the + // beginning of the output file. + loadBudget -= (len(lib)+7)/8*8 + 24 + + if loadBudget < 0 { + HEADR += 4096 + *FlagTextAddr += 4096 + loadBudget += 4096 + } + + dylib = append(dylib, lib) +} + +func machoshbits(ctxt *Link, mseg *MachoSeg, sect *sym.Section, segname string) { + buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) + + var msect *MachoSect + if sect.Rwx&1 == 0 && segname != "__DWARF" && (ctxt.Arch.Family == sys.ARM64 || + ctxt.Arch.Family == sys.ARM || + (ctxt.Arch.Family == sys.AMD64 && ctxt.BuildMode != BuildModeExe)) { + // Darwin external linker on arm and arm64, and on amd64 in c-shared/c-archive buildmode + // complains about absolute relocs in __TEXT, so if the section is not + // executable, put it in __DATA segment. + msect = newMachoSect(mseg, buf, "__DATA") + } else { + msect = newMachoSect(mseg, buf, segname) + } + + if sect.Rellen > 0 { + msect.reloc = uint32(sect.Reloff) + msect.nreloc = uint32(sect.Rellen / 8) + } + + for 1<<msect.align < sect.Align { + msect.align++ + } + msect.addr = sect.Vaddr + msect.size = sect.Length + + if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { + // data in file + if sect.Length > sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr { + Errorf(nil, "macho cannot represent section %s crossing data and bss", sect.Name) + } + msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr) + } else { + msect.off = 0 + msect.flag |= S_ZEROFILL + } + + if sect.Rwx&1 != 0 { + msect.flag |= S_ATTR_SOME_INSTRUCTIONS + } + + if sect.Name == ".text" { + msect.flag |= S_ATTR_PURE_INSTRUCTIONS + } + + if sect.Name == ".plt" { + msect.name = "__symbol_stub1" + msect.flag = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS | S_SYMBOL_STUBS + msect.res1 = 0 //nkind[SymKindLocal]; + msect.res2 = 6 + } + + if sect.Name == ".got" { + msect.name = "__nl_symbol_ptr" + msect.flag = S_NON_LAZY_SYMBOL_POINTERS + msect.res1 = uint32(ctxt.Syms.Lookup(".linkedit.plt", 0).Size / 4) /* offset into indirect symbol table */ + } + + if sect.Name == ".init_array" { + msect.name = "__mod_init_func" + msect.flag = S_MOD_INIT_FUNC_POINTERS + } + + // Some platforms such as watchOS and tvOS require binaries with + // bitcode enabled. The Go toolchain can't output bitcode, so use + // a marker section in the __LLVM segment, "__asm", to tell the Apple + // toolchain that the Go text came from assembler and thus has no + // bitcode. This is not true, but Kotlin/Native, Rust and Flutter + // are also using this trick. + if sect.Name == ".llvmasm" { + msect.name = "__asm" + msect.segname = "__LLVM" + } + + if segname == "__DWARF" { + msect.flag |= S_ATTR_DEBUG + } +} + +func Asmbmacho(ctxt *Link) { + /* apple MACH */ + va := *FlagTextAddr - int64(HEADR) + + mh := getMachoHdr() + switch ctxt.Arch.Family { + default: + Exitf("unknown macho architecture: %v", ctxt.Arch.Family) + + case sys.ARM: + mh.cpu = MACHO_CPU_ARM + mh.subcpu = MACHO_SUBCPU_ARMV7 + + case sys.AMD64: + mh.cpu = MACHO_CPU_AMD64 + mh.subcpu = MACHO_SUBCPU_X86 + + case sys.ARM64: + mh.cpu = MACHO_CPU_ARM64 + mh.subcpu = MACHO_SUBCPU_ARM64_ALL + + case sys.I386: + mh.cpu = MACHO_CPU_386 + mh.subcpu = MACHO_SUBCPU_X86 + } + + var ms *MachoSeg + if ctxt.LinkMode == LinkExternal { + /* segment for entire file */ + ms = newMachoSeg("", 40) + + ms.fileoffset = Segtext.Fileoff + ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff + ms.vsize = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr + } + + /* segment for zero page */ + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__PAGEZERO", 0) + ms.vsize = uint64(va) + } + + /* text */ + v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__TEXT", 20) + ms.vaddr = uint64(va) + ms.vsize = uint64(v) + ms.fileoffset = 0 + ms.filesize = uint64(v) + ms.prot1 = 7 + ms.prot2 = 5 + } + + for _, sect := range Segtext.Sections { + machoshbits(ctxt, ms, sect, "__TEXT") + } + + /* data */ + if ctxt.LinkMode != LinkExternal { + w := int64(Segdata.Length) + ms = newMachoSeg("__DATA", 20) + ms.vaddr = uint64(va) + uint64(v) + ms.vsize = uint64(w) + ms.fileoffset = uint64(v) + ms.filesize = Segdata.Filelen + ms.prot1 = 3 + ms.prot2 = 3 + } + + for _, sect := range Segdata.Sections { + machoshbits(ctxt, ms, sect, "__DATA") + } + + /* dwarf */ + if !*FlagW { + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__DWARF", 20) + ms.vaddr = Segdwarf.Vaddr + ms.vsize = 0 + ms.fileoffset = Segdwarf.Fileoff + ms.filesize = Segdwarf.Filelen + } + for _, sect := range Segdwarf.Sections { + machoshbits(ctxt, ms, sect, "__DWARF") + } + } + + if ctxt.LinkMode != LinkExternal { + switch ctxt.Arch.Family { + default: + Exitf("unknown macho architecture: %v", ctxt.Arch.Family) + + case sys.ARM: + ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 17+2) + ml.data[0] = 1 /* thread type */ + ml.data[1] = 17 /* word count */ + ml.data[2+15] = uint32(Entryvalue(ctxt)) /* start pc */ + + case sys.AMD64: + ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 42+2) + ml.data[0] = 4 /* thread type */ + ml.data[1] = 42 /* word count */ + ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */ + ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32) + + case sys.ARM64: + ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 68+2) + ml.data[0] = 6 /* thread type */ + ml.data[1] = 68 /* word count */ + ml.data[2+64] = uint32(Entryvalue(ctxt)) /* start pc */ + ml.data[2+64+1] = uint32(Entryvalue(ctxt) >> 32) + + case sys.I386: + ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 16+2) + ml.data[0] = 1 /* thread type */ + ml.data[1] = 16 /* word count */ + ml.data[2+10] = uint32(Entryvalue(ctxt)) /* start pc */ + } + } + + if !*FlagD { + // must match domacholink below + s1 := ctxt.Syms.Lookup(".machosymtab", 0) + s2 := ctxt.Syms.Lookup(".linkedit.plt", 0) + s3 := ctxt.Syms.Lookup(".linkedit.got", 0) + s4 := ctxt.Syms.Lookup(".machosymstr", 0) + + if ctxt.LinkMode != LinkExternal { + ms := newMachoSeg("__LINKEDIT", 0) + ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(*FlagRound))) + ms.vsize = uint64(s1.Size) + uint64(s2.Size) + uint64(s3.Size) + uint64(s4.Size) + ms.fileoffset = uint64(linkoff) + ms.filesize = ms.vsize + ms.prot1 = 7 + ms.prot2 = 3 + } + + ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4) + ml.data[0] = uint32(linkoff) /* symoff */ + ml.data[1] = uint32(nsortsym) /* nsyms */ + ml.data[2] = uint32(linkoff + s1.Size + s2.Size + s3.Size) /* stroff */ + ml.data[3] = uint32(s4.Size) /* strsize */ + + machodysymtab(ctxt) + + if ctxt.LinkMode != LinkExternal { + ml := newMachoLoad(ctxt.Arch, LC_LOAD_DYLINKER, 6) + ml.data[0] = 12 /* offset to string */ + stringtouint32(ml.data[1:], "/usr/lib/dyld") + + for _, lib := range dylib { + ml = newMachoLoad(ctxt.Arch, LC_LOAD_DYLIB, 4+(uint32(len(lib))+1+7)/8*2) + ml.data[0] = 24 /* offset of string from beginning of load */ + ml.data[1] = 0 /* time stamp */ + ml.data[2] = 0 /* version */ + ml.data[3] = 0 /* compatibility version */ + stringtouint32(ml.data[4:], lib) + } + } + } + + a := machowrite(ctxt.Arch, ctxt.Out, ctxt.LinkMode) + if int32(a) > HEADR { + Exitf("HEADR too small: %d > %d", a, HEADR) + } +} + +func symkind(s *sym.Symbol) int { + if s.Type == sym.SDYNIMPORT { + return SymKindUndef + } + if s.Attr.CgoExport() { + return SymKindExtdef + } + return SymKindLocal +} + +func addsym(ctxt *Link, s *sym.Symbol, name string, type_ SymbolType, addr int64, gotype *sym.Symbol) { + if s == nil { + return + } + + switch type_ { + default: + return + + case DataSym, BSSSym, TextSym: + break + } + + if sortsym != nil { + sortsym[nsortsym] = s + nkind[symkind(s)]++ + } + + nsortsym++ +} + +type machoscmp []*sym.Symbol + +func (x machoscmp) Len() int { + return len(x) +} + +func (x machoscmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x machoscmp) Less(i, j int) bool { + s1 := x[i] + s2 := x[j] + + k1 := symkind(s1) + k2 := symkind(s2) + if k1 != k2 { + return k1 < k2 + } + + return s1.Extname() < s2.Extname() +} + +func machogenasmsym(ctxt *Link) { + genasmsym(ctxt, addsym) + for _, s := range ctxt.Syms.Allsym { + // Some 64-bit functions have a "$INODE64" or "$INODE64$UNIX2003" suffix. + if s.Type == sym.SDYNIMPORT && s.Dynimplib() == "/usr/lib/libSystem.B.dylib" { + // But only on macOS. + if machoPlatform == PLATFORM_MACOS { + switch n := s.Extname(); n { + case "fdopendir": + switch objabi.GOARCH { + case "amd64": + s.SetExtname(n + "$INODE64") + case "386": + s.SetExtname(n + "$INODE64$UNIX2003") + } + case "readdir_r", "getfsstat": + switch objabi.GOARCH { + case "amd64", "386": + s.SetExtname(n + "$INODE64") + } + } + } + } + + if s.Type == sym.SDYNIMPORT || s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT { + if s.Attr.Reachable() { + addsym(ctxt, s, "", DataSym, 0, nil) + } + } + } +} + +func machosymorder(ctxt *Link) { + // On Mac OS X Mountain Lion, we must sort exported symbols + // So we sort them here and pre-allocate dynid for them + // See https://golang.org/issue/4029 + for i := range dynexp { + dynexp[i].Attr |= sym.AttrReachable + } + machogenasmsym(ctxt) + sortsym = make([]*sym.Symbol, nsortsym) + nsortsym = 0 + machogenasmsym(ctxt) + sort.Sort(machoscmp(sortsym[:nsortsym])) + for i := 0; i < nsortsym; i++ { + sortsym[i].Dynid = int32(i) + } +} + +// machoShouldExport reports whether a symbol needs to be exported. +// +// When dynamically linking, all non-local variables and plugin-exported +// symbols need to be exported. +func machoShouldExport(ctxt *Link, s *sym.Symbol) bool { + if !ctxt.DynlinkingGo() || s.Attr.Local() { + return false + } + if ctxt.BuildMode == BuildModePlugin && strings.HasPrefix(s.Extname(), objabi.PathToPrefix(*flagPluginPath)) { + return true + } + if strings.HasPrefix(s.Name, "go.itab.") { + return true + } + if strings.HasPrefix(s.Name, "type.") && !strings.HasPrefix(s.Name, "type..") { + // reduce runtime typemap pressure, but do not + // export alg functions (type..*), as these + // appear in pclntable. + return true + } + if strings.HasPrefix(s.Name, "go.link.pkghash") { + return true + } + return s.Type >= sym.SFirstWritable // only writable sections +} + +func machosymtab(ctxt *Link) { + symtab := ctxt.Syms.Lookup(".machosymtab", 0) + symstr := ctxt.Syms.Lookup(".machosymstr", 0) + + for i := 0; i < nsortsym; i++ { + s := sortsym[i] + symtab.AddUint32(ctxt.Arch, uint32(symstr.Size)) + + export := machoShouldExport(ctxt, s) + isGoSymbol := strings.Contains(s.Extname(), ".") + + // In normal buildmodes, only add _ to C symbols, as + // Go symbols have dot in the name. + // + // Do not export C symbols in plugins, as runtime C + // symbols like crosscall2 are in pclntab and end up + // pointing at the host binary, breaking unwinding. + // See Issue #18190. + cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s)) + if cexport || export || isGoSymbol { + symstr.AddUint8('_') + } + + // replace "·" as ".", because DTrace cannot handle it. + Addstring(symstr, strings.Replace(s.Extname(), "·", ".", -1)) + + if s.Type == sym.SDYNIMPORT || s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT { + symtab.AddUint8(0x01) // type N_EXT, external symbol + symtab.AddUint8(0) // no section + symtab.AddUint16(ctxt.Arch, 0) // desc + symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value + } else { + if s.Attr.CgoExport() || export { + symtab.AddUint8(0x0f) + } else { + symtab.AddUint8(0x0e) + } + o := s + for o.Outer != nil { + o = o.Outer + } + if o.Sect == nil { + Errorf(s, "missing section for symbol") + symtab.AddUint8(0) + } else { + symtab.AddUint8(uint8(o.Sect.Extnum)) + } + symtab.AddUint16(ctxt.Arch, 0) // desc + symtab.AddUintXX(ctxt.Arch, uint64(Symaddr(s)), ctxt.Arch.PtrSize) + } + } +} + +func machodysymtab(ctxt *Link) { + ml := newMachoLoad(ctxt.Arch, LC_DYSYMTAB, 18) + + n := 0 + ml.data[0] = uint32(n) /* ilocalsym */ + ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */ + n += nkind[SymKindLocal] + + ml.data[2] = uint32(n) /* iextdefsym */ + ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */ + n += nkind[SymKindExtdef] + + ml.data[4] = uint32(n) /* iundefsym */ + ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */ + + ml.data[6] = 0 /* tocoffset */ + ml.data[7] = 0 /* ntoc */ + ml.data[8] = 0 /* modtaboff */ + ml.data[9] = 0 /* nmodtab */ + ml.data[10] = 0 /* extrefsymoff */ + ml.data[11] = 0 /* nextrefsyms */ + + // must match domacholink below + s1 := ctxt.Syms.Lookup(".machosymtab", 0) + + s2 := ctxt.Syms.Lookup(".linkedit.plt", 0) + s3 := ctxt.Syms.Lookup(".linkedit.got", 0) + ml.data[12] = uint32(linkoff + s1.Size) /* indirectsymoff */ + ml.data[13] = uint32((s2.Size + s3.Size) / 4) /* nindirectsyms */ + + ml.data[14] = 0 /* extreloff */ + ml.data[15] = 0 /* nextrel */ + ml.data[16] = 0 /* locreloff */ + ml.data[17] = 0 /* nlocrel */ +} + +func Domacholink(ctxt *Link) int64 { + machosymtab(ctxt) + + // write data that will be linkedit section + s1 := ctxt.Syms.Lookup(".machosymtab", 0) + + s2 := ctxt.Syms.Lookup(".linkedit.plt", 0) + s3 := ctxt.Syms.Lookup(".linkedit.got", 0) + s4 := ctxt.Syms.Lookup(".machosymstr", 0) + + // Force the linkedit section to end on a 16-byte + // boundary. This allows pure (non-cgo) Go binaries + // to be code signed correctly. + // + // Apple's codesign_allocate (a helper utility for + // the codesign utility) can do this fine itself if + // it is run on a dynamic Mach-O binary. However, + // when it is run on a pure (non-cgo) Go binary, where + // the linkedit section is mostly empty, it fails to + // account for the extra padding that it itself adds + // when adding the LC_CODE_SIGNATURE load command + // (which must be aligned on a 16-byte boundary). + // + // By forcing the linkedit section to end on a 16-byte + // boundary, codesign_allocate will not need to apply + // any alignment padding itself, working around the + // issue. + for s4.Size%16 != 0 { + s4.AddUint8(0) + } + + size := int(s1.Size + s2.Size + s3.Size + s4.Size) + + if size > 0 { + linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound)) + ctxt.Out.SeekSet(linkoff) + + ctxt.Out.Write(s1.P[:s1.Size]) + ctxt.Out.Write(s2.P[:s2.Size]) + ctxt.Out.Write(s3.P[:s3.Size]) + ctxt.Out.Write(s4.P[:s4.Size]) + } + + return Rnd(int64(size), int64(*FlagRound)) +} + +func machorelocsect(ctxt *Link, sect *sym.Section, syms []*sym.Symbol) { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !s.Attr.Reachable() { + continue + } + if uint64(s.Value) >= sect.Vaddr { + syms = syms[i:] + break + } + } + + eaddr := int32(sect.Vaddr + sect.Length) + for _, s := range syms { + if !s.Attr.Reachable() { + continue + } + if s.Value >= int64(eaddr) { + break + } + for ri := range s.R { + r := &s.R[ri] + if r.Done { + continue + } + if r.Xsym == nil { + Errorf(s, "missing xsym in relocation") + continue + } + if !r.Xsym.Attr.Reachable() { + Errorf(s, "unreachable reloc %d (%s) target %v", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Xsym.Name) + } + if !thearch.Machoreloc1(ctxt.Arch, ctxt.Out, s, r, int64(uint64(s.Value+int64(r.Off))-sect.Vaddr)) { + Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type, sym.RelocName(ctxt.Arch, r.Type), r.Siz, r.Sym.Name) + } + } + } + + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff +} + +func Machoemitreloc(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + machorelocsect(ctxt, Segtext.Sections[0], ctxt.Textp) + for _, sect := range Segtext.Sections[1:] { + machorelocsect(ctxt, sect, datap) + } + for _, sect := range Segdata.Sections { + machorelocsect(ctxt, sect, datap) + } + for _, sect := range Segdwarf.Sections { + machorelocsect(ctxt, sect, dwarfp) + } +} + +// hostobjMachoPlatform returns the first platform load command found +// in the host object, if any. +func hostobjMachoPlatform(h *Hostobj) (*MachoPlatformLoad, error) { + f, err := os.Open(h.file) + if err != nil { + return nil, fmt.Errorf("%s: failed to open host object: %v\n", h.file, err) + } + defer f.Close() + sr := io.NewSectionReader(f, h.off, h.length) + m, err := macho.NewFile(sr) + if err != nil { + // Not a valid Mach-O file. + return nil, nil + } + return peekMachoPlatform(m) +} + +// peekMachoPlatform returns the first LC_VERSION_MIN_* or LC_BUILD_VERSION +// load command found in the Mach-O file, if any. +func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) { + for _, cmd := range m.Loads { + raw := cmd.Raw() + ml := MachoLoad{ + type_: m.ByteOrder.Uint32(raw), + } + // Skip the type and command length. + data := raw[8:] + var p MachoPlatform + switch ml.type_ { + case LC_VERSION_MIN_IPHONEOS: + p = PLATFORM_IOS + case LC_VERSION_MIN_MACOSX: + p = PLATFORM_MACOS + case LC_VERSION_MIN_WATCHOS: + p = PLATFORM_WATCHOS + case LC_VERSION_MIN_TVOS: + p = PLATFORM_TVOS + case LC_BUILD_VERSION: + p = MachoPlatform(m.ByteOrder.Uint32(data)) + default: + continue + } + ml.data = make([]uint32, len(data)/4) + r := bytes.NewReader(data) + if err := binary.Read(r, m.ByteOrder, &ml.data); err != nil { + return nil, err + } + return &MachoPlatformLoad{ + platform: p, + cmd: ml, + }, nil + } + return nil, nil +} diff --git a/src/cmd/oldlink/internal/ld/macho_combine_dwarf.go b/src/cmd/oldlink/internal/ld/macho_combine_dwarf.go new file mode 100644 index 0000000000..9d9f916b8e --- /dev/null +++ b/src/cmd/oldlink/internal/ld/macho_combine_dwarf.go @@ -0,0 +1,462 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "compress/zlib" + "debug/macho" + "encoding/binary" + "fmt" + "io" + "os" + "reflect" + "unsafe" +) + +const ( + pageAlign = 12 // 4096 = 1 << 12 +) + +type loadCmd struct { + Cmd macho.LoadCmd + Len uint32 +} + +type dyldInfoCmd struct { + Cmd macho.LoadCmd + Len uint32 + RebaseOff, RebaseLen uint32 + BindOff, BindLen uint32 + WeakBindOff, WeakBindLen uint32 + LazyBindOff, LazyBindLen uint32 + ExportOff, ExportLen uint32 +} + +type linkEditDataCmd struct { + Cmd macho.LoadCmd + Len uint32 + DataOff, DataLen uint32 +} + +type encryptionInfoCmd struct { + Cmd macho.LoadCmd + Len uint32 + CryptOff, CryptLen uint32 + CryptId uint32 +} + +type loadCmdReader struct { + offset, next int64 + f *os.File + order binary.ByteOrder +} + +func (r *loadCmdReader) Next() (loadCmd, error) { + var cmd loadCmd + + r.offset = r.next + if _, err := r.f.Seek(r.offset, 0); err != nil { + return cmd, err + } + if err := binary.Read(r.f, r.order, &cmd); err != nil { + return cmd, err + } + r.next = r.offset + int64(cmd.Len) + return cmd, nil +} + +func (r loadCmdReader) ReadAt(offset int64, data interface{}) error { + if _, err := r.f.Seek(r.offset+offset, 0); err != nil { + return err + } + return binary.Read(r.f, r.order, data) +} + +func (r loadCmdReader) WriteAt(offset int64, data interface{}) error { + if _, err := r.f.Seek(r.offset+offset, 0); err != nil { + return err + } + return binary.Write(r.f, r.order, data) +} + +// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable. +// +// With internal linking, DWARF is embedded into the executable, this lets us do the +// same for external linking. +// exef is the file of the executable with no DWARF. It must have enough room in the macho +// header to add the DWARF sections. (Use ld's -headerpad option) +// exem is the macho representation of exef. +// dsym is the path to the macho file containing DWARF from dsymutil. +// outexe is the path where the combined executable should be saved. +func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe string) error { + dwarff, err := os.Open(dsym) + if err != nil { + return err + } + defer dwarff.Close() + outf, err := os.OpenFile(outexe, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return err + } + defer outf.Close() + dwarfm, err := macho.NewFile(dwarff) + if err != nil { + return err + } + defer dwarfm.Close() + + // The string table needs to be the last thing in the file + // for code signing to work. So we'll need to move the + // linkedit section, but all the others can be copied directly. + linkseg := exem.Segment("__LINKEDIT") + if linkseg == nil { + return fmt.Errorf("missing __LINKEDIT segment") + } + + if _, err := exef.Seek(0, 0); err != nil { + return err + } + if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil { + return err + } + + realdwarf := dwarfm.Segment("__DWARF") + if realdwarf == nil { + return fmt.Errorf("missing __DWARF segment") + } + + // Try to compress the DWARF sections. This includes some Apple + // proprietary sections like __apple_types. + compressedSects, compressedBytes, err := machoCompressSections(ctxt, dwarfm) + if err != nil { + return err + } + + // Now copy the dwarf data into the output. + // Kernel requires all loaded segments to be page-aligned in the file, + // even though we mark this one as being 0 bytes of virtual address space. + dwarfstart := machoCalcStart(realdwarf.Offset, linkseg.Offset, pageAlign) + if _, err := outf.Seek(dwarfstart, 0); err != nil { + return err + } + + if _, err := dwarff.Seek(int64(realdwarf.Offset), 0); err != nil { + return err + } + + // Write out the compressed sections, or the originals if we gave up + // on compressing them. + var dwarfsize uint64 + if compressedBytes != nil { + dwarfsize = uint64(len(compressedBytes)) + if _, err := outf.Write(compressedBytes); err != nil { + return err + } + } else { + if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil { + return err + } + dwarfsize = realdwarf.Filesz + } + + // And finally the linkedit section. + if _, err := exef.Seek(int64(linkseg.Offset), 0); err != nil { + return err + } + linkstart := machoCalcStart(linkseg.Offset, uint64(dwarfstart)+dwarfsize, pageAlign) + if _, err := outf.Seek(linkstart, 0); err != nil { + return err + } + if _, err := io.Copy(outf, exef); err != nil { + return err + } + + // Now we need to update the headers. + textsect := exem.Section("__text") + if textsect == nil { + return fmt.Errorf("missing __text section") + } + + cmdOffset := unsafe.Sizeof(exem.FileHeader) + if is64bit := exem.Magic == macho.Magic64; is64bit { + // mach_header_64 has one extra uint32. + cmdOffset += unsafe.Sizeof(exem.Magic) + } + dwarfCmdOffset := uint32(cmdOffset) + exem.FileHeader.Cmdsz + availablePadding := textsect.Offset - dwarfCmdOffset + if availablePadding < realdwarf.Len { + return fmt.Errorf("no room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding) + } + // First, copy the dwarf load command into the header. It will be + // updated later with new offsets and lengths as necessary. + if _, err := outf.Seek(int64(dwarfCmdOffset), 0); err != nil { + return err + } + if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil { + return err + } + if _, err := outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil { + return err + } + if err := binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil { + return err + } + if err := binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil { + return err + } + + reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder} + for i := uint32(0); i < exem.Ncmd; i++ { + cmd, err := reader.Next() + if err != nil { + return err + } + linkoffset := uint64(linkstart) - linkseg.Offset + switch cmd.Cmd { + case macho.LoadCmdSegment64: + err = machoUpdateSegment(reader, linkseg, linkoffset, &macho.Segment64{}, &macho.Section64{}) + case macho.LoadCmdSegment: + err = machoUpdateSegment(reader, linkseg, linkoffset, &macho.Segment32{}, &macho.Section32{}) + case LC_DYLD_INFO, LC_DYLD_INFO_ONLY: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff") + case macho.LoadCmdSymtab: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.SymtabCmd{}, "Symoff", "Stroff") + case macho.LoadCmdDysymtab: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff") + case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &linkEditDataCmd{}, "DataOff") + case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &encryptionInfoCmd{}, "CryptOff") + case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH, LC_ID_DYLIB, LC_SYMSEG, LC_LOADFVMLIB, LC_IDFVMLIB, LC_IDENT, LC_FVMFILE, LC_PREPAGE, LC_ID_DYLINKER, LC_ROUTINES, LC_SUB_FRAMEWORK, LC_SUB_UMBRELLA, LC_SUB_CLIENT, LC_SUB_LIBRARY, LC_TWOLEVEL_HINTS, LC_PREBIND_CKSUM, LC_ROUTINES_64, LC_LAZY_LOAD_DYLIB, LC_LOAD_UPWARD_DYLIB, LC_DYLD_ENVIRONMENT, LC_LINKER_OPTION, LC_LINKER_OPTIMIZATION_HINT, LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS, LC_VERSION_NOTE, LC_BUILD_VERSION: + // Nothing to update + default: + err = fmt.Errorf("unknown load command 0x%x (%s)", int(cmd.Cmd), cmd.Cmd) + } + if err != nil { + return err + } + } + // Do the final update of the DWARF segment's load command. + return machoUpdateDwarfHeader(&reader, compressedSects, dwarfsize, dwarfstart, realdwarf) +} + +// machoCompressSections tries to compress the DWARF segments in dwarfm, +// returning the updated sections and segment contents, nils if the sections +// weren't compressed, or an error if there was a problem reading dwarfm. +func machoCompressSections(ctxt *Link, dwarfm *macho.File) ([]*macho.Section, []byte, error) { + if !ctxt.compressDWARF { + return nil, nil, nil + } + + dwarfseg := dwarfm.Segment("__DWARF") + var sects []*macho.Section + var buf bytes.Buffer + + for _, sect := range dwarfm.Sections { + if sect.Seg != "__DWARF" { + continue + } + + // As of writing, there are no relocations in dsymutil's output + // so there's no point in worrying about them. Bail out if that + // changes. + if sect.Nreloc != 0 { + return nil, nil, nil + } + + data, err := sect.Data() + if err != nil { + return nil, nil, err + } + + compressed, contents, err := machoCompressSection(data) + if err != nil { + return nil, nil, err + } + + newSec := *sect + newSec.Offset = uint32(dwarfseg.Offset) + uint32(buf.Len()) + newSec.Addr = dwarfseg.Addr + uint64(buf.Len()) + if compressed { + newSec.Name = "__z" + sect.Name[2:] + newSec.Size = uint64(len(contents)) + } + sects = append(sects, &newSec) + buf.Write(contents) + } + return sects, buf.Bytes(), nil +} + +// machoCompressSection compresses secBytes if it results in less data. +func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, err error) { + var buf bytes.Buffer + buf.WriteString("ZLIB") + var sizeBytes [8]byte + binary.BigEndian.PutUint64(sizeBytes[:], uint64(len(sectBytes))) + buf.Write(sizeBytes[:]) + + z := zlib.NewWriter(&buf) + if _, err := z.Write(sectBytes); err != nil { + return false, nil, err + } + if err := z.Close(); err != nil { + return false, nil, err + } + if buf.Len() >= len(sectBytes) { + return false, sectBytes, nil + } + return true, buf.Bytes(), nil +} + +// machoUpdateSegment updates the load command for a moved segment. +// Only the linkedit segment should move, and it should have 0 sections. +// seg should be a macho.Segment32 or macho.Segment64 as appropriate. +// sect should be a macho.Section32 or macho.Section64 as appropriate. +func machoUpdateSegment(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64, seg, sect interface{}) error { + if err := r.ReadAt(0, seg); err != nil { + return err + } + segValue := reflect.ValueOf(seg) + offset := reflect.Indirect(segValue).FieldByName("Offset") + + // Only the linkedit segment moved, anything before that is fine. + if offset.Uint() < linkseg.Offset { + return nil + } + offset.SetUint(offset.Uint() + linkoffset) + if err := r.WriteAt(0, seg); err != nil { + return err + } + // There shouldn't be any sections, but just to make sure... + return machoUpdateSections(r, segValue, reflect.ValueOf(sect), linkoffset, nil) +} + +func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset uint64, compressedSects []*macho.Section) error { + iseg := reflect.Indirect(seg) + nsect := iseg.FieldByName("Nsect").Uint() + if nsect == 0 { + return nil + } + sectOffset := int64(iseg.Type().Size()) + + isect := reflect.Indirect(sect) + offsetField := isect.FieldByName("Offset") + reloffField := isect.FieldByName("Reloff") + addrField := isect.FieldByName("Addr") + nameField := isect.FieldByName("Name") + sizeField := isect.FieldByName("Size") + sectSize := int64(isect.Type().Size()) + for i := uint64(0); i < nsect; i++ { + if err := r.ReadAt(sectOffset, sect.Interface()); err != nil { + return err + } + if compressedSects != nil { + cSect := compressedSects[i] + var name [16]byte + copy(name[:], []byte(cSect.Name)) + nameField.Set(reflect.ValueOf(name)) + sizeField.SetUint(cSect.Size) + if cSect.Offset != 0 { + offsetField.SetUint(uint64(cSect.Offset) + deltaOffset) + } + if cSect.Addr != 0 { + addrField.SetUint(cSect.Addr) + } + } else { + if offsetField.Uint() != 0 { + offsetField.SetUint(offsetField.Uint() + deltaOffset) + } + if reloffField.Uint() != 0 { + reloffField.SetUint(reloffField.Uint() + deltaOffset) + } + if addrField.Uint() != 0 { + addrField.SetUint(addrField.Uint()) + } + } + if err := r.WriteAt(sectOffset, sect.Interface()); err != nil { + return err + } + sectOffset += sectSize + } + return nil +} + +// machoUpdateDwarfHeader updates the DWARF segment load command. +func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, dwarfsize uint64, dwarfstart int64, realdwarf *macho.Segment) error { + var seg, sect interface{} + cmd, err := r.Next() + if err != nil { + return err + } + if cmd.Cmd == macho.LoadCmdSegment64 { + seg = new(macho.Segment64) + sect = new(macho.Section64) + } else { + seg = new(macho.Segment32) + sect = new(macho.Section32) + } + if err := r.ReadAt(0, seg); err != nil { + return err + } + segv := reflect.ValueOf(seg).Elem() + segv.FieldByName("Offset").SetUint(uint64(dwarfstart)) + + if compressedSects != nil { + var segSize uint64 + for _, newSect := range compressedSects { + segSize += newSect.Size + } + segv.FieldByName("Filesz").SetUint(segSize) + } else { + segv.FieldByName("Filesz").SetUint(dwarfsize) + } + + // We want the DWARF segment to be considered non-loadable, so + // force vmaddr and vmsize to zero. In addition, set the initial + // protection to zero so as to make the dynamic loader happy, + // since otherwise it may complain that that the vm size and file + // size don't match for the segment. See issues 21647 and 32673 + // for more context. Also useful to refer to the Apple dynamic + // loader source, specifically ImageLoaderMachO::sniffLoadCommands + // in ImageLoaderMachO.cpp (various versions can be found online, see + // https://opensource.apple.com/source/dyld/dyld-519.2.2/src/ImageLoaderMachO.cpp.auto.html + // as one example). + segv.FieldByName("Addr").SetUint(0) + segv.FieldByName("Memsz").SetUint(0) + segv.FieldByName("Prot").SetUint(0) + + if err := r.WriteAt(0, seg); err != nil { + return err + } + return machoUpdateSections(*r, segv, reflect.ValueOf(sect), uint64(dwarfstart)-realdwarf.Offset, compressedSects) +} + +func machoUpdateLoadCommand(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64, cmd interface{}, fields ...string) error { + if err := r.ReadAt(0, cmd); err != nil { + return err + } + value := reflect.Indirect(reflect.ValueOf(cmd)) + + for _, name := range fields { + field := value.FieldByName(name) + if fieldval := field.Uint(); fieldval >= linkseg.Offset { + field.SetUint(fieldval + linkoffset) + } + } + if err := r.WriteAt(0, cmd); err != nil { + return err + } + return nil +} + +func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 { + align := uint64(1 << alignExp) + origMod, newMod := origAddr%align, newAddr%align + if origMod == newMod { + return int64(newAddr) + } + return int64(newAddr + align + origMod - newMod) +} diff --git a/src/cmd/oldlink/internal/ld/main.go b/src/cmd/oldlink/internal/ld/main.go new file mode 100644 index 0000000000..f7f3700398 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/main.go @@ -0,0 +1,338 @@ +// Inferno utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "flag" + "log" + "os" + "runtime" + "runtime/pprof" + "strings" +) + +var ( + pkglistfornote []byte + windowsgui bool // writes a "GUI binary" instead of a "console binary" + ownTmpDir bool // set to true if tmp dir created by linker (e.g. no -tmpdir) +) + +func init() { + flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...") +} + +// Flags used by the linker. The exported flags are used by the architecture-specific packages. +var ( + flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id") + + flagOutfile = flag.String("o", "", "write output to `file`") + flagPluginPath = flag.String("pluginpath", "", "full path name for plugin") + + flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`") + flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph") + flagRace = flag.Bool("race", false, "enable race detector") + flagMsan = flag.Bool("msan", false, "enable MSan interface") + + flagFieldTrack = flag.String("k", "", "set field tracking `symbol`") + flagLibGCC = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable") + flagTmpdir = flag.String("tmpdir", "", "use `directory` for temporary files") + + flagExtld = flag.String("extld", "", "use `linker` when linking in external mode") + flagExtldflags = flag.String("extldflags", "", "pass `flags` to external linker") + flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive") + + flagA = flag.Bool("a", false, "disassemble output") + FlagC = flag.Bool("c", false, "dump call graph") + FlagD = flag.Bool("d", false, "disable dynamic executable") + flagF = flag.Bool("f", false, "ignore version mismatch") + flagG = flag.Bool("g", false, "disable go package data checks") + flagH = flag.Bool("h", false, "halt on error") + flagN = flag.Bool("n", false, "dump symbol table") + FlagS = flag.Bool("s", false, "disable symbol table") + flagU = flag.Bool("u", false, "reject unsafe packages") + FlagW = flag.Bool("w", false, "disable DWARF generation") + Flag8 bool // use 64-bit addresses in symbol table + flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") + FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines") + FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).") + flagNewobj = flag.Bool("newobj", false, "use new object file format") + + FlagRound = flag.Int("R", -1, "set address rounding `quantum`") + FlagTextAddr = flag.Int64("T", -1, "set text segment `address`") + flagEntrySymbol = flag.String("E", "", "set `entry` symbol name") + + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") + memprofile = flag.String("memprofile", "", "write memory profile to `file`") + memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`") +) + +// Main is the main entry point for the linker code. +func Main(arch *sys.Arch, theArch Arch) { + thearch = theArch + ctxt := linknew(arch) + ctxt.Bso = bufio.NewWriter(os.Stdout) + + // For testing behavior of go command when tools crash silently. + // Undocumented, not in standard flag parser to avoid + // exposing in usage message. + for _, arg := range os.Args { + if arg == "-crash_for_testing" { + os.Exit(2) + } + } + + final := gorootFinal() + addstrdata1(ctxt, "runtime/internal/sys.DefaultGoroot="+final) + addstrdata1(ctxt, "cmd/internal/objabi.defaultGOROOT="+final) + + // TODO(matloob): define these above and then check flag values here + if ctxt.Arch.Family == sys.AMD64 && objabi.GOOS == "plan9" { + flag.BoolVar(&Flag8, "8", false, "use 64-bit addresses in symbol table") + } + flagHeadType := flag.String("H", "", "set header `type`") + flag.BoolVar(&ctxt.linkShared, "linkshared", false, "link against installed Go shared libraries") + flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`") + flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`") + flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible") + objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) + objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) }) + objabi.AddVersionFlag() // -V + objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) }) + objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog) + objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg) + + objabi.Flagparse(usage) + + switch *flagHeadType { + case "": + case "windowsgui": + ctxt.HeadType = objabi.Hwindows + windowsgui = true + default: + if err := ctxt.HeadType.Set(*flagHeadType); err != nil { + Errorf(nil, "%v", err) + usage() + } + } + + if objabi.Fieldtrack_enabled != 0 { + ctxt.Reachparent = make(map[*sym.Symbol]*sym.Symbol) + } + checkStrictDups = *FlagStrictDups + + startProfile() + if ctxt.BuildMode == BuildModeUnset { + ctxt.BuildMode = BuildModeExe + } + + if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 { + usage() + } + + if *flagOutfile == "" { + *flagOutfile = "a.out" + if ctxt.HeadType == objabi.Hwindows { + *flagOutfile += ".exe" + } + } + + interpreter = *flagInterpreter + + libinit(ctxt) // creates outfile + + if ctxt.HeadType == objabi.Hunknown { + ctxt.HeadType.Set(objabi.GOOS) + } + + ctxt.computeTLSOffset() + thearch.Archinit(ctxt) + + if ctxt.linkShared && !ctxt.IsELF { + Exitf("-linkshared can only be used on elf systems") + } + + if ctxt.Debugvlog != 0 { + ctxt.Logf("HEADER = -H%d -T0x%x -R0x%x\n", ctxt.HeadType, uint64(*FlagTextAddr), uint32(*FlagRound)) + } + + switch ctxt.BuildMode { + case BuildModeShared: + for i := 0; i < flag.NArg(); i++ { + arg := flag.Arg(i) + parts := strings.SplitN(arg, "=", 2) + var pkgpath, file string + if len(parts) == 1 { + pkgpath, file = "main", arg + } else { + pkgpath, file = parts[0], parts[1] + } + pkglistfornote = append(pkglistfornote, pkgpath...) + pkglistfornote = append(pkglistfornote, '\n') + addlibpath(ctxt, "command line", "command line", file, pkgpath, "") + } + case BuildModePlugin: + addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "") + default: + addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "") + } + ctxt.loadlib() + + deadcode(ctxt) + if *flagNewobj { + ctxt.loadlibfull() // XXX do it here for now + } + ctxt.linksetup() + ctxt.dostrdata() + + dwarfGenerateDebugInfo(ctxt) + if objabi.Fieldtrack_enabled != 0 { + fieldtrack(ctxt) + } + ctxt.mangleTypeSym() + ctxt.callgraph() + + ctxt.doelf() + if ctxt.HeadType == objabi.Hdarwin { + ctxt.domacho() + } + ctxt.dostkcheck() + if ctxt.HeadType == objabi.Hwindows { + ctxt.dope() + ctxt.windynrelocsyms() + } + if ctxt.HeadType == objabi.Haix { + ctxt.doxcoff() + } + + ctxt.addexport() + thearch.Gentext(ctxt) // trampolines, call stubs, etc. + ctxt.textbuildid() + ctxt.textaddress() + ctxt.pclntab() + ctxt.findfunctab() + ctxt.typelink() + ctxt.symtab() + ctxt.buildinfo() + ctxt.dodata() + order := ctxt.address() + dwarfcompress(ctxt) + filesize := ctxt.layout(order) + + // Write out the output file. + // It is split into two parts (Asmb and Asmb2). The first + // part writes most of the content (sections and segments), + // for which we have computed the size and offset, in a + // mmap'd region. The second part writes more content, for + // which we don't know the size. + var outputMmapped bool + if ctxt.Arch.Family != sys.Wasm { + // Don't mmap if we're building for Wasm. Wasm file + // layout is very different so filesize is meaningless. + err := ctxt.Out.Mmap(filesize) + outputMmapped = err == nil + } + if outputMmapped { + // Asmb will redirect symbols to the output file mmap, and relocations + // will be applied directly there. + thearch.Asmb(ctxt) + ctxt.reloc() + ctxt.Out.Munmap() + } else { + // If we don't mmap, we need to apply relocations before + // writing out. + ctxt.reloc() + thearch.Asmb(ctxt) + } + thearch.Asmb2(ctxt) + + ctxt.undef() + ctxt.hostlink() + if ctxt.Debugvlog != 0 { + ctxt.Logf("%d symbols\n", len(ctxt.Syms.Allsym)) + ctxt.Logf("%d liveness data\n", liveness) + } + ctxt.Bso.Flush() + ctxt.archive() + + errorexit() +} + +type Rpath struct { + set bool + val string +} + +func (r *Rpath) Set(val string) error { + r.set = true + r.val = val + return nil +} + +func (r *Rpath) String() string { + return r.val +} + +func startProfile() { + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatalf("%v", err) + } + if err := pprof.StartCPUProfile(f); err != nil { + log.Fatalf("%v", err) + } + AtExit(pprof.StopCPUProfile) + } + if *memprofile != "" { + if *memprofilerate != 0 { + runtime.MemProfileRate = int(*memprofilerate) + } + f, err := os.Create(*memprofile) + if err != nil { + log.Fatalf("%v", err) + } + AtExit(func() { + // Profile all outstanding allocations. + runtime.GC() + // compilebench parses the memory profile to extract memstats, + // which are only written in the legacy pprof format. + // See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap. + const writeLegacyFormat = 1 + if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil { + log.Fatalf("%v", err) + } + }) + } +} diff --git a/src/cmd/oldlink/internal/ld/outbuf.go b/src/cmd/oldlink/internal/ld/outbuf.go new file mode 100644 index 0000000000..596d2395c8 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/outbuf.go @@ -0,0 +1,177 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bufio" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "encoding/binary" + "log" + "os" +) + +// OutBuf is a buffered file writer. +// +// It is simlar to the Writer in cmd/internal/bio with a few small differences. +// +// First, it tracks the output architecture and uses it to provide +// endian helpers. +// +// Second, it provides a very cheap offset counter that doesn't require +// any system calls to read the value. +// +// It also mmaps the output file (if available). The intended usage is: +// - Mmap the output file +// - Write the content +// - possibly apply any edits in the output buffer +// - Munmap the output file +// - possibly write more content to the file, which will not be edited later. +type OutBuf struct { + arch *sys.Arch + off int64 + w *bufio.Writer + buf []byte // backing store of mmap'd output file + f *os.File + encbuf [8]byte // temp buffer used by WriteN methods +} + +func (out *OutBuf) SeekSet(p int64) { + if p == out.off { + return + } + if out.buf == nil { + out.Flush() + if _, err := out.f.Seek(p, 0); err != nil { + Exitf("seeking to %d in %s: %v", p, out.f.Name(), err) + } + } + out.off = p +} + +func (out *OutBuf) Offset() int64 { + return out.off +} + +// Write writes the contents of v to the buffer. +// +// As Write is backed by a bufio.Writer, callers do not have +// to explicitly handle the returned error as long as Flush is +// eventually called. +func (out *OutBuf) Write(v []byte) (int, error) { + if out.buf != nil { + n := copy(out.buf[out.off:], v) + out.off += int64(n) + return n, nil + } + n, err := out.w.Write(v) + out.off += int64(n) + return n, err +} + +func (out *OutBuf) Write8(v uint8) { + if out.buf != nil { + out.buf[out.off] = v + out.off++ + return + } + if err := out.w.WriteByte(v); err == nil { + out.off++ + } +} + +// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface. +func (out *OutBuf) WriteByte(v byte) error { + out.Write8(v) + return nil +} + +func (out *OutBuf) Write16(v uint16) { + out.arch.ByteOrder.PutUint16(out.encbuf[:], v) + out.Write(out.encbuf[:2]) +} + +func (out *OutBuf) Write32(v uint32) { + out.arch.ByteOrder.PutUint32(out.encbuf[:], v) + out.Write(out.encbuf[:4]) +} + +func (out *OutBuf) Write32b(v uint32) { + binary.BigEndian.PutUint32(out.encbuf[:], v) + out.Write(out.encbuf[:4]) +} + +func (out *OutBuf) Write64(v uint64) { + out.arch.ByteOrder.PutUint64(out.encbuf[:], v) + out.Write(out.encbuf[:8]) +} + +func (out *OutBuf) Write64b(v uint64) { + binary.BigEndian.PutUint64(out.encbuf[:], v) + out.Write(out.encbuf[:8]) +} + +func (out *OutBuf) WriteString(s string) { + if out.buf != nil { + n := copy(out.buf[out.off:], s) + if n != len(s) { + log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s)) + } + out.off += int64(n) + return + } + n, _ := out.w.WriteString(s) + out.off += int64(n) +} + +// WriteStringN writes the first n bytes of s. +// If n is larger than len(s) then it is padded with zero bytes. +func (out *OutBuf) WriteStringN(s string, n int) { + out.WriteStringPad(s, n, zeros[:]) +} + +// WriteStringPad writes the first n bytes of s. +// If n is larger than len(s) then it is padded with the bytes in pad (repeated as needed). +func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) { + if len(s) >= n { + out.WriteString(s[:n]) + } else { + out.WriteString(s) + n -= len(s) + for n > len(pad) { + out.Write(pad) + n -= len(pad) + + } + out.Write(pad[:n]) + } +} + +// WriteSym writes the content of a Symbol, then changes the Symbol's content +// to point to the output buffer that we just wrote, so we can apply further +// edit to the symbol content. +// If the output file is not Mmap'd, just writes the content. +func (out *OutBuf) WriteSym(s *sym.Symbol) { + if out.buf != nil { + start := out.off + out.Write(s.P) + s.P = out.buf[start:out.off] + s.Attr.Set(sym.AttrReadOnly, false) + } else { + out.Write(s.P) + } +} + +func (out *OutBuf) Flush() { + var err error + if out.buf != nil { + err = out.Msync() + } else { + err = out.w.Flush() + } + if err != nil { + Exitf("flushing %s: %v", out.f.Name(), err) + } +} diff --git a/src/cmd/oldlink/internal/ld/outbuf_mmap.go b/src/cmd/oldlink/internal/ld/outbuf_mmap.go new file mode 100644 index 0000000000..4075141171 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/outbuf_mmap.go @@ -0,0 +1,44 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux openbsd + +package ld + +import ( + "syscall" + "unsafe" +) + +func (out *OutBuf) Mmap(filesize uint64) error { + err := out.f.Truncate(int64(filesize)) + if err != nil { + Exitf("resize output file failed: %v", err) + } + out.buf, err = syscall.Mmap(int(out.f.Fd()), 0, int(filesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FILE) + return err +} + +func (out *OutBuf) Munmap() { + err := out.Msync() + if err != nil { + Exitf("msync output file failed: %v", err) + } + syscall.Munmap(out.buf) + out.buf = nil + _, err = out.f.Seek(out.off, 0) + if err != nil { + Exitf("seek output file failed: %v", err) + } +} + +func (out *OutBuf) Msync() error { + // TODO: netbsd supports mmap and msync, but the syscall package doesn't define MSYNC. + // It is excluded from the build tag for now. + _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(len(out.buf)), syscall.MS_SYNC) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/cmd/oldlink/internal/ld/outbuf_nommap.go b/src/cmd/oldlink/internal/ld/outbuf_nommap.go new file mode 100644 index 0000000000..fba8cd8bc4 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/outbuf_nommap.go @@ -0,0 +1,15 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!openbsd,!windows + +package ld + +import "errors" + +var errNotSupported = errors.New("mmap not supported") + +func (out *OutBuf) Mmap(filesize uint64) error { return errNotSupported } +func (out *OutBuf) Munmap() { panic("unreachable") } +func (out *OutBuf) Msync() error { panic("unreachable") } diff --git a/src/cmd/oldlink/internal/ld/outbuf_windows.go b/src/cmd/oldlink/internal/ld/outbuf_windows.go new file mode 100644 index 0000000000..1cb05c301f --- /dev/null +++ b/src/cmd/oldlink/internal/ld/outbuf_windows.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "reflect" + "syscall" + "unsafe" +) + +func (out *OutBuf) Mmap(filesize uint64) error { + err := out.f.Truncate(int64(filesize)) + if err != nil { + Exitf("resize output file failed: %v", err) + } + + low, high := uint32(filesize), uint32(filesize>>32) + fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READONLY, high, low, nil) + if err != nil { + return err + } + defer syscall.CloseHandle(fmap) + + ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ|syscall.FILE_MAP_WRITE, 0, 0, uintptr(filesize)) + if err != nil { + return err + } + *(*reflect.SliceHeader)(unsafe.Pointer(&out.buf)) = reflect.SliceHeader{Data: ptr, Len: int(filesize), Cap: int(filesize)} + return nil +} + +func (out *OutBuf) Munmap() { + if out.buf == nil { + return + } + err := syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&out.buf[0]))) + if err != nil { + Exitf("UnmapViewOfFile failed: %v", err) + } +} + +func (out *OutBuf) Msync() error { + if out.buf == nil { + return nil + } + return syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0) +} diff --git a/src/cmd/oldlink/internal/ld/pcln.go b/src/cmd/oldlink/internal/ld/pcln.go new file mode 100644 index 0000000000..7d53ab8ad4 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/pcln.go @@ -0,0 +1,530 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "encoding/binary" + "fmt" + "log" + "os" + "path/filepath" + "strings" +) + +func ftabaddstring(ftab *sym.Symbol, s string) int32 { + start := len(ftab.P) + ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL + copy(ftab.P[start:], s) + return int32(start) +} + +// numberfile assigns a file number to the file if it hasn't been assigned already. +func numberfile(ctxt *Link, file *sym.Symbol) { + if file.Type != sym.SFILEPATH { + ctxt.Filesyms = append(ctxt.Filesyms, file) + file.Value = int64(len(ctxt.Filesyms)) + file.Type = sym.SFILEPATH + path := file.Name[len(src.FileSymPrefix):] + file.Name = expandGoroot(path) + } +} + +func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) { + // Give files numbers. + for _, f := range files { + numberfile(ctxt, f) + } + + buf := make([]byte, binary.MaxVarintLen32) + newval := int32(-1) + var out sym.Pcdata + it := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) + for it.Init(d.P); !it.Done; it.Next() { + // value delta + oldval := it.Value + + var val int32 + if oldval == -1 { + val = -1 + } else { + if oldval < 0 || oldval >= int32(len(files)) { + log.Fatalf("bad pcdata %d", oldval) + } + val = int32(files[oldval].Value) + } + + dv := val - newval + newval = val + + // value + n := binary.PutVarint(buf, int64(dv)) + out.P = append(out.P, buf[:n]...) + + // pc delta + pc := (it.NextPC - it.PC) / it.PCScale + n = binary.PutUvarint(buf, uint64(pc)) + out.P = append(out.P, buf[:n]...) + } + + // terminating value delta + // we want to write varint-encoded 0, which is just 0 + out.P = append(out.P, 0) + + *d = out +} + +// onlycsymbol reports whether this is a symbol that is referenced by C code. +func onlycsymbol(s *sym.Symbol) bool { + switch s.Name { + case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2": + return true + } + if strings.HasPrefix(s.Name, "_cgoexp_") { + return true + } + return false +} + +func emitPcln(ctxt *Link, s *sym.Symbol) bool { + if s == nil { + return true + } + if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) { + return false + } + // We want to generate func table entries only for the "lowest level" symbols, + // not containers of subsymbols. + return !s.Attr.Container() +} + +// pclntab initializes the pclntab symbol with +// runtime function and file name information. + +var pclntabZpcln sym.FuncInfo + +// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. +var pclntabNfunc int32 +var pclntabFiletabOffset int32 +var pclntabPclntabOffset int32 +var pclntabFirstFunc *sym.Symbol +var pclntabLastFunc *sym.Symbol + +func (ctxt *Link) pclntab() { + funcdataBytes := int64(0) + ftab := ctxt.Syms.Lookup("runtime.pclntab", 0) + ftab.Type = sym.SPCLNTAB + ftab.Attr |= sym.AttrReachable + + // See golang.org/s/go12symtab for the format. Briefly: + // 8-byte header + // nfunc [thearch.ptrsize bytes] + // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] + // end PC [thearch.ptrsize bytes] + // offset to file table [4 bytes] + + // Find container symbols and mark them as such. + for _, s := range ctxt.Textp { + if s.Outer != nil { + s.Outer.Attr |= sym.AttrContainer + } + } + + // Gather some basic stats and info. + var nfunc int32 + prevSect := ctxt.Textp[0].Sect + for _, s := range ctxt.Textp { + if !emitPcln(ctxt, s) { + continue + } + nfunc++ + if pclntabFirstFunc == nil { + pclntabFirstFunc = s + } + if s.Sect != prevSect { + // With multiple text sections, the external linker may insert functions + // between the sections, which are not known by Go. This leaves holes in + // the PC range covered by the func table. We need to generate an entry + // to mark the hole. + nfunc++ + prevSect = s.Sect + } + } + + pclntabNfunc = nfunc + ftab.Grow(8 + int64(ctxt.Arch.PtrSize) + int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4) + ftab.SetUint32(ctxt.Arch, 0, 0xfffffffb) + ftab.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC)) + ftab.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize)) + ftab.SetUint(ctxt.Arch, 8, uint64(nfunc)) + pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize) + + funcnameoff := make(map[string]int32) + nameToOffset := func(name string) int32 { + nameoff, ok := funcnameoff[name] + if !ok { + nameoff = ftabaddstring(ftab, name) + funcnameoff[name] = nameoff + } + return nameoff + } + + pctaboff := make(map[string]uint32) + writepctab := func(off int32, p []byte) int32 { + start, ok := pctaboff[string(p)] + if !ok { + if len(p) > 0 { + start = uint32(len(ftab.P)) + ftab.AddBytes(p) + } + pctaboff[string(p)] = start + } + newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start)) + return newoff + } + + nfunc = 0 // repurpose nfunc as a running index + prevFunc := ctxt.Textp[0] + for _, s := range ctxt.Textp { + if !emitPcln(ctxt, s) { + continue + } + + if s.Sect != prevFunc.Sect { + // With multiple text sections, there may be a hole here in the address + // space (see the comment above). We use an invalid funcoff value to + // mark the hole. + // See also runtime/symtab.go:findfunc + ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFunc.Size) + ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0)) + nfunc++ + } + prevFunc = s + + pcln := s.FuncInfo + if pcln == nil { + pcln = &pclntabZpcln + } + + if len(pcln.InlTree) > 0 { + if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex { + // Create inlining pcdata table. + pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1) + copy(pcdata, pcln.Pcdata) + pcln.Pcdata = pcdata + } + + if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree { + // Create inline tree funcdata. + funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1) + funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1) + copy(funcdata, pcln.Funcdata) + copy(funcdataoff, pcln.Funcdataoff) + pcln.Funcdata = funcdata + pcln.Funcdataoff = funcdataoff + } + } + + funcstart := int32(len(ftab.P)) + funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize + + ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s) + ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart)) + + // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func + // and package debug/gosym. + + // fixed size of struct, checked below + off := funcstart + + end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize) + if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) { + end += 4 + } + ftab.Grow(int64(end)) + + // entry uintptr + off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s)) + + // name int32 + nameoff := nameToOffset(s.Name) + off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff))) + + // args int32 + // TODO: Move into funcinfo. + args := uint32(0) + if s.FuncInfo != nil { + args = uint32(s.FuncInfo.Args) + } + off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args)) + + // deferreturn + deferreturn := uint32(0) + lastWasmAddr := uint32(0) + for _, r := range s.R { + if ctxt.Arch.Family == sys.Wasm && r.Type == objabi.R_ADDR { + // Wasm does not have a live variable set at the deferreturn + // call itself. Instead it has one identified by the + // resumption point immediately preceding the deferreturn. + // The wasm code has a R_ADDR relocation which is used to + // set the resumption point to PC_B. + lastWasmAddr = uint32(r.Add) + } + if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" { + if ctxt.Arch.Family == sys.Wasm { + deferreturn = lastWasmAddr - 1 + } else { + // Note: the relocation target is in the call instruction, but + // is not necessarily the whole instruction (for instance, on + // x86 the relocation applies to bytes [1:5] of the 5 byte call + // instruction). + deferreturn = uint32(r.Off) + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: + deferreturn-- + case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64: + // no change + case sys.RISCV64: + // TODO(jsing): The JALR instruction is marked with + // R_CALLRISCV, whereas the actual reloc is currently + // one instruction earlier starting with the AUIPC. + deferreturn -= 4 + case sys.S390X: + deferreturn -= 2 + default: + panic(fmt.Sprint("Unhandled architecture:", ctxt.Arch.Family)) + } + } + break // only need one + } + } + off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn)) + + if pcln != &pclntabZpcln { + renumberfiles(ctxt, pcln.File, &pcln.Pcfile) + if false { + // Sanity check the new numbering + it := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) + for it.Init(pcln.Pcfile.P); !it.Done; it.Next() { + if it.Value < 1 || it.Value > int32(len(ctxt.Filesyms)) { + Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(ctxt.Filesyms)) + errorexit() + } + } + } + } + + if len(pcln.InlTree) > 0 { + inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0) + inlTreeSym.Type = sym.SRODATA + inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK + + for i, call := range pcln.InlTree { + // Usually, call.File is already numbered since the file + // shows up in the Pcfile table. However, two inlined calls + // might overlap exactly so that only the innermost file + // appears in the Pcfile table. In that case, this assigns + // the outer file a number. + numberfile(ctxt, call.File) + nameoff := nameToOffset(call.Func) + + inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent)) + inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func, ""))) + // byte 3 is unused + inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value)) + inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line)) + inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff)) + inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC)) + } + + pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym + pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline + } + + // pcdata + off = writepctab(off, pcln.Pcsp.P) + off = writepctab(off, pcln.Pcfile.P) + off = writepctab(off, pcln.Pcline.P) + off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata)))) + + // funcID uint8 + var file string + if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 { + file = s.FuncInfo.File[0].Name + } + funcID := objabi.GetFuncID(s.Name, file) + + off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID))) + + // unused + off += 2 + + // nfuncdata must be the final entry. + off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(pcln.Funcdata)))) + for i := range pcln.Pcdata { + off = writepctab(off, pcln.Pcdata[i].P) + } + + // funcdata, must be pointer-aligned and we're only int32-aligned. + // Missing funcdata will be 0 (nil pointer). + if len(pcln.Funcdata) > 0 { + if off&int32(ctxt.Arch.PtrSize-1) != 0 { + off += 4 + } + for i := range pcln.Funcdata { + dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i) + if pcln.Funcdata[i] == nil { + ftab.SetUint(ctxt.Arch, dataoff, uint64(pcln.Funcdataoff[i])) + continue + } + // TODO: Dedup. + funcdataBytes += pcln.Funcdata[i].Size + ftab.SetAddrPlus(ctxt.Arch, dataoff, pcln.Funcdata[i], pcln.Funcdataoff[i]) + } + off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize) + } + + if off != end { + Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), ctxt.Arch.PtrSize) + errorexit() + } + + nfunc++ + } + + last := ctxt.Textp[len(ctxt.Textp)-1] + pclntabLastFunc = last + // Final entry of table is just end pc. + ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size) + + // Start file table. + start := int32(len(ftab.P)) + + start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) + pclntabFiletabOffset = start + ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start)) + + ftab.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4) + ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1)) + for i := len(ctxt.Filesyms) - 1; i >= 0; i-- { + s := ctxt.Filesyms[i] + ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) + } + + ftab.Size = int64(len(ftab.P)) + + if ctxt.Debugvlog != 0 { + ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size, funcdataBytes) + } +} + +func gorootFinal() string { + root := objabi.GOROOT + if final := os.Getenv("GOROOT_FINAL"); final != "" { + root = final + } + return root +} + +func expandGoroot(s string) string { + const n = len("$GOROOT") + if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') { + return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:])) + } + return s +} + +const ( + BUCKETSIZE = 256 * MINFUNC + SUBBUCKETS = 16 + SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS + NOIDX = 0x7fffffff +) + +// findfunctab generates a lookup table to quickly find the containing +// function for a pc. See src/runtime/symtab.go:findfunc for details. +func (ctxt *Link) findfunctab() { + t := ctxt.Syms.Lookup("runtime.findfunctab", 0) + t.Type = sym.SRODATA + t.Attr |= sym.AttrReachable + t.Attr |= sym.AttrLocal + + // find min and max address + min := ctxt.Textp[0].Value + lastp := ctxt.Textp[len(ctxt.Textp)-1] + max := lastp.Value + lastp.Size + + // for each subbucket, compute the minimum of all symbol indexes + // that map to that subbucket. + n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) + + indexes := make([]int32, n) + for i := int32(0); i < n; i++ { + indexes[i] = NOIDX + } + idx := int32(0) + for i, s := range ctxt.Textp { + if !emitPcln(ctxt, s) { + continue + } + p := s.Value + var e *sym.Symbol + i++ + if i < len(ctxt.Textp) { + e = ctxt.Textp[i] + } + for !emitPcln(ctxt, e) && i < len(ctxt.Textp) { + e = ctxt.Textp[i] + i++ + } + q := max + if e != nil { + q = e.Value + } + + //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); + for ; p < q; p += SUBBUCKETSIZE { + i = int((p - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + } + + i = int((q - 1 - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + idx++ + } + + // allocate table + nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) + + t.Grow(4*int64(nbuckets) + int64(n)) + + // fill in table + for i := int32(0); i < nbuckets; i++ { + base := indexes[i*SUBBUCKETS] + if base == NOIDX { + Errorf(nil, "hole in findfunctab") + } + t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base)) + for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { + idx = indexes[i*SUBBUCKETS+j] + if idx == NOIDX { + Errorf(nil, "hole in findfunctab") + } + if idx-base >= 256 { + Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) + } + + t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) + } + } +} diff --git a/src/cmd/oldlink/internal/ld/pe.go b/src/cmd/oldlink/internal/ld/pe.go new file mode 100644 index 0000000000..b40557f485 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/pe.go @@ -0,0 +1,1562 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// PE (Portable Executable) file writing +// https://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "debug/pe" + "encoding/binary" + "fmt" + "sort" + "strconv" + "strings" +) + +type IMAGE_IMPORT_DESCRIPTOR struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 +} + +type IMAGE_EXPORT_DIRECTORY struct { + Characteristics uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + Name uint32 + Base uint32 + NumberOfFunctions uint32 + NumberOfNames uint32 + AddressOfFunctions uint32 + AddressOfNames uint32 + AddressOfNameOrdinals uint32 +} + +const ( + PEBASE = 0x00400000 +) + +var ( + // SectionAlignment must be greater than or equal to FileAlignment. + // The default is the page size for the architecture. + PESECTALIGN int64 = 0x1000 + + // FileAlignment should be a power of 2 between 512 and 64 K, inclusive. + // The default is 512. If the SectionAlignment is less than + // the architecture's page size, then FileAlignment must match SectionAlignment. + PEFILEALIGN int64 = 2 << 8 +) + +const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 + IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 + IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 + IMAGE_SCN_ALIGN_32BYTES = 0x600000 +) + +// TODO(crawshaw): add these constants to debug/pe. +const ( + // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2 + IMAGE_SYM_TYPE_NULL = 0 + IMAGE_SYM_TYPE_STRUCT = 8 + IMAGE_SYM_DTYPE_FUNCTION = 0x20 + IMAGE_SYM_DTYPE_ARRAY = 0x30 + IMAGE_SYM_CLASS_EXTERNAL = 2 + IMAGE_SYM_CLASS_STATIC = 3 + + IMAGE_REL_I386_DIR32 = 0x0006 + IMAGE_REL_I386_SECREL = 0x000B + IMAGE_REL_I386_REL32 = 0x0014 + + IMAGE_REL_AMD64_ADDR64 = 0x0001 + IMAGE_REL_AMD64_ADDR32 = 0x0002 + IMAGE_REL_AMD64_REL32 = 0x0004 + IMAGE_REL_AMD64_SECREL = 0x000B + + IMAGE_REL_ARM_ABSOLUTE = 0x0000 + IMAGE_REL_ARM_ADDR32 = 0x0001 + IMAGE_REL_ARM_ADDR32NB = 0x0002 + IMAGE_REL_ARM_BRANCH24 = 0x0003 + IMAGE_REL_ARM_BRANCH11 = 0x0004 + IMAGE_REL_ARM_SECREL = 0x000F + + IMAGE_REL_BASED_HIGHLOW = 3 + IMAGE_REL_BASED_DIR64 = 10 +) + +const ( + PeMinimumTargetMajorVersion = 6 + PeMinimumTargetMinorVersion = 1 +) + +// DOS stub that prints out +// "This program cannot be run in DOS mode." +var dosstub = []uint8{ + 0x4d, + 0x5a, + 0x90, + 0x00, + 0x03, + 0x00, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xff, + 0xff, + 0x00, + 0x00, + 0x8b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x0e, + 0x1f, + 0xba, + 0x0e, + 0x00, + 0xb4, + 0x09, + 0xcd, + 0x21, + 0xb8, + 0x01, + 0x4c, + 0xcd, + 0x21, + 0x54, + 0x68, + 0x69, + 0x73, + 0x20, + 0x70, + 0x72, + 0x6f, + 0x67, + 0x72, + 0x61, + 0x6d, + 0x20, + 0x63, + 0x61, + 0x6e, + 0x6e, + 0x6f, + 0x74, + 0x20, + 0x62, + 0x65, + 0x20, + 0x72, + 0x75, + 0x6e, + 0x20, + 0x69, + 0x6e, + 0x20, + 0x44, + 0x4f, + 0x53, + 0x20, + 0x6d, + 0x6f, + 0x64, + 0x65, + 0x2e, + 0x0d, + 0x0d, + 0x0a, + 0x24, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +} + +type Imp struct { + s *sym.Symbol + off uint64 + next *Imp + argsize int +} + +type Dll struct { + name string + nameoff uint64 + thunkoff uint64 + ms *Imp + next *Dll +} + +var ( + rsrcsym *sym.Symbol + PESECTHEADR int32 + PEFILEHEADR int32 + pe64 int + dr *Dll + dexport [1024]*sym.Symbol + nexport int +) + +// peStringTable is a COFF string table. +type peStringTable struct { + strings []string + stringsLen int +} + +// size returns size of string table t. +func (t *peStringTable) size() int { + // string table starts with 4-byte length at the beginning + return t.stringsLen + 4 +} + +// add adds string str to string table t. +func (t *peStringTable) add(str string) int { + off := t.size() + t.strings = append(t.strings, str) + t.stringsLen += len(str) + 1 // each string will have 0 appended to it + return off +} + +// write writes string table t into the output file. +func (t *peStringTable) write(out *OutBuf) { + out.Write32(uint32(t.size())) + for _, s := range t.strings { + out.WriteString(s) + out.Write8(0) + } +} + +// peSection represents section from COFF section table. +type peSection struct { + name string + shortName string + index int // one-based index into the Section Table + virtualSize uint32 + virtualAddress uint32 + sizeOfRawData uint32 + pointerToRawData uint32 + pointerToRelocations uint32 + numberOfRelocations uint16 + characteristics uint32 +} + +// checkOffset verifies COFF section sect offset in the file. +func (sect *peSection) checkOffset(off int64) { + if off != int64(sect.pointerToRawData) { + Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off)) + errorexit() + } +} + +// checkSegment verifies COFF section sect matches address +// and file offset provided in segment seg. +func (sect *peSection) checkSegment(seg *sym.Segment) { + if seg.Vaddr-PEBASE != uint64(sect.virtualAddress) { + Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-PEBASE))) + errorexit() + } + if seg.Fileoff != uint64(sect.pointerToRawData) { + Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff))) + errorexit() + } +} + +// pad adds zeros to the section sect. It writes as many bytes +// as necessary to make section sect.SizeOfRawData bytes long. +// It assumes that n bytes are already written to the file. +func (sect *peSection) pad(out *OutBuf, n uint32) { + out.WriteStringN("", int(sect.sizeOfRawData-n)) +} + +// write writes COFF section sect into the output file. +func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error { + h := pe.SectionHeader32{ + VirtualSize: sect.virtualSize, + SizeOfRawData: sect.sizeOfRawData, + PointerToRawData: sect.pointerToRawData, + PointerToRelocations: sect.pointerToRelocations, + NumberOfRelocations: sect.numberOfRelocations, + Characteristics: sect.characteristics, + } + if linkmode != LinkExternal { + h.VirtualAddress = sect.virtualAddress + } + copy(h.Name[:], sect.shortName) + return binary.Write(out, binary.LittleEndian, h) +} + +// emitRelocations emits the relocation entries for the sect. +// The actual relocations are emitted by relocfn. +// This updates the corresponding PE section table entry +// with the relocation offset and count. +func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) { + sect.pointerToRelocations = uint32(out.Offset()) + // first entry: extended relocs + out.Write32(0) // placeholder for number of relocation + 1 + out.Write32(0) + out.Write16(0) + + n := relocfn() + 1 + + cpos := out.Offset() + out.SeekSet(int64(sect.pointerToRelocations)) + out.Write32(uint32(n)) + out.SeekSet(cpos) + if n > 0x10000 { + n = 0x10000 + sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL + } else { + sect.pointerToRelocations += 10 // skip the extend reloc entry + } + sect.numberOfRelocations = uint16(n - 1) +} + +// peFile is used to build COFF file. +type peFile struct { + sections []*peSection + stringTable peStringTable + textSect *peSection + rdataSect *peSection + dataSect *peSection + bssSect *peSection + ctorsSect *peSection + nextSectOffset uint32 + nextFileOffset uint32 + symtabOffset int64 // offset to the start of symbol table + symbolCount int // number of symbol table records written + dataDirectory [16]pe.DataDirectory +} + +// addSection adds section to the COFF file f. +func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection { + sect := &peSection{ + name: name, + shortName: name, + index: len(f.sections) + 1, + virtualSize: uint32(sectsize), + virtualAddress: f.nextSectOffset, + pointerToRawData: f.nextFileOffset, + } + f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN)) + if filesize > 0 { + sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN)) + f.nextFileOffset += sect.sizeOfRawData + } + f.sections = append(f.sections, sect) + return sect +} + +// addDWARFSection adds DWARF section to the COFF file f. +// This function is similar to addSection, but DWARF section names are +// longer than 8 characters, so they need to be stored in the string table. +func (f *peFile) addDWARFSection(name string, size int) *peSection { + if size == 0 { + Exitf("DWARF section %q is empty", name) + } + // DWARF section names are longer than 8 characters. + // PE format requires such names to be stored in string table, + // and section names replaced with slash (/) followed by + // correspondent string table index. + // see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx + // for details + off := f.stringTable.add(name) + h := f.addSection(name, size, size) + h.shortName = fmt.Sprintf("/%d", off) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + return h +} + +// addDWARF adds DWARF information to the COFF file f. +func (f *peFile) addDWARF() { + if *FlagS { // disable symbol table + return + } + if *FlagW { // disable dwarf + return + } + for _, sect := range Segdwarf.Sections { + h := f.addDWARFSection(sect.Name, int(sect.Length)) + fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff + if uint64(h.pointerToRawData) != fileoff { + Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff) + } + } +} + +// addInitArray adds .ctors COFF section to the file f. +func (f *peFile) addInitArray(ctxt *Link) *peSection { + // The size below was determined by the specification for array relocations, + // and by observing what GCC writes here. If the initarray section grows to + // contain more than one constructor entry, the size will need to be 8 * constructor_count. + // However, the entire Go runtime is initialized from just one function, so it is unlikely + // that this will need to grow in the future. + var size int + switch objabi.GOARCH { + default: + Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", objabi.GOARCH) + case "386": + size = 4 + case "amd64": + size = 8 + case "arm": + size = 4 + } + sect := f.addSection(".ctors", size, size) + sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + sect.sizeOfRawData = uint32(size) + ctxt.Out.SeekSet(int64(sect.pointerToRawData)) + sect.checkOffset(ctxt.Out.Offset()) + + init_entry := ctxt.Syms.Lookup(*flagEntrySymbol, 0) + addr := uint64(init_entry.Value) - init_entry.Sect.Vaddr + switch objabi.GOARCH { + case "386", "arm": + ctxt.Out.Write32(uint32(addr)) + case "amd64": + ctxt.Out.Write64(addr) + } + return sect +} + +// emitRelocations emits relocation entries for go.o in external linking. +func (f *peFile) emitRelocations(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + // relocsect relocates symbols from first in section sect, and returns + // the total number of relocations emitted. + relocsect := func(sect *sym.Section, syms []*sym.Symbol, base uint64) int { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return 0 + } + relocs := 0 + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !s.Attr.Reachable() { + continue + } + if uint64(s.Value) >= sect.Vaddr { + syms = syms[i:] + break + } + } + eaddr := int32(sect.Vaddr + sect.Length) + for _, sym := range syms { + if !sym.Attr.Reachable() { + continue + } + if sym.Value >= int64(eaddr) { + break + } + for ri := range sym.R { + r := &sym.R[ri] + if r.Done { + continue + } + if r.Xsym == nil { + Errorf(sym, "missing xsym in relocation") + continue + } + if r.Xsym.Dynid < 0 { + Errorf(sym, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type) + } + if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, sym, r, int64(uint64(sym.Value+int64(r.Off))-base)) { + Errorf(sym, "unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) + } + relocs++ + } + } + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff + return relocs + } + + sects := []struct { + peSect *peSection + seg *sym.Segment + syms []*sym.Symbol + }{ + {f.textSect, &Segtext, ctxt.Textp}, + {f.rdataSect, &Segrodata, datap}, + {f.dataSect, &Segdata, datap}, + } + for _, s := range sects { + s.peSect.emitRelocations(ctxt.Out, func() int { + var n int + for _, sect := range s.seg.Sections { + n += relocsect(sect, s.syms, s.seg.Vaddr) + } + return n + }) + } + +dwarfLoop: + for _, sect := range Segdwarf.Sections { + for _, pesect := range f.sections { + if sect.Name == pesect.name { + pesect.emitRelocations(ctxt.Out, func() int { + return relocsect(sect, dwarfp, sect.Vaddr) + }) + continue dwarfLoop + } + } + Errorf(nil, "emitRelocations: could not find %q section", sect.Name) + } + + f.ctorsSect.emitRelocations(ctxt.Out, func() int { + dottext := ctxt.Syms.Lookup(".text", 0) + ctxt.Out.Write32(0) + ctxt.Out.Write32(uint32(dottext.Dynid)) + switch objabi.GOARCH { + default: + Errorf(dottext, "unknown architecture for PE: %q\n", objabi.GOARCH) + case "386": + ctxt.Out.Write16(IMAGE_REL_I386_DIR32) + case "amd64": + ctxt.Out.Write16(IMAGE_REL_AMD64_ADDR64) + case "arm": + ctxt.Out.Write16(IMAGE_REL_ARM_ADDR32) + } + return 1 + }) +} + +// writeSymbol appends symbol s to file f symbol table. +// It also sets s.Dynid to written symbol number. +func (f *peFile) writeSymbol(out *OutBuf, s *sym.Symbol, value int64, sectidx int, typ uint16, class uint8) { + if len(s.Name) > 8 { + out.Write32(0) + out.Write32(uint32(f.stringTable.add(s.Name))) + } else { + out.WriteStringN(s.Name, 8) + } + out.Write32(uint32(value)) + out.Write16(uint16(sectidx)) + out.Write16(typ) + out.Write8(class) + out.Write8(0) // no aux entries + + s.Dynid = int32(f.symbolCount) + + f.symbolCount++ +} + +// mapToPESection searches peFile f for s symbol's location. +// It returns PE section index, and offset within that section. +func (f *peFile) mapToPESection(s *sym.Symbol, linkmode LinkMode) (pesectidx int, offset int64, err error) { + if s.Sect == nil { + return 0, 0, fmt.Errorf("could not map %s symbol with no section", s.Name) + } + if s.Sect.Seg == &Segtext { + return f.textSect.index, int64(uint64(s.Value) - Segtext.Vaddr), nil + } + if s.Sect.Seg == &Segrodata { + return f.rdataSect.index, int64(uint64(s.Value) - Segrodata.Vaddr), nil + } + if s.Sect.Seg != &Segdata { + return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", s.Name) + } + v := uint64(s.Value) - Segdata.Vaddr + if linkmode != LinkExternal { + return f.dataSect.index, int64(v), nil + } + if s.Type == sym.SDATA { + return f.dataSect.index, int64(v), nil + } + // Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section + // it still belongs to the .data section, not the .bss section. + if v < Segdata.Filelen { + return f.dataSect.index, int64(v), nil + } + return f.bssSect.index, int64(v - Segdata.Filelen), nil +} + +// writeSymbols writes all COFF symbol table records. +func (f *peFile) writeSymbols(ctxt *Link) { + + put := func(ctxt *Link, s *sym.Symbol, name string, type_ SymbolType, addr int64, gotype *sym.Symbol) { + if s == nil { + return + } + if s.Sect == nil && type_ != UndefinedSym { + return + } + switch type_ { + default: + return + case DataSym, BSSSym, TextSym, UndefinedSym: + } + + // Only windows/386 requires underscore prefix on external symbols. + if ctxt.Arch.Family == sys.I386 && + ctxt.LinkMode == LinkExternal && + (s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT || s.Attr.CgoExport()) { + s.Name = "_" + s.Name + } + + var typ uint16 + if ctxt.LinkMode == LinkExternal { + typ = IMAGE_SYM_TYPE_NULL + } else { + // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308 + typ = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT + typ = 0x0308 // "array of structs" + } + sect, value, err := f.mapToPESection(s, ctxt.LinkMode) + if err != nil { + if type_ == UndefinedSym { + typ = IMAGE_SYM_DTYPE_FUNCTION + } else { + Errorf(s, "addpesym: %v", err) + } + } + class := IMAGE_SYM_CLASS_EXTERNAL + if s.IsFileLocal() || s.Attr.VisibilityHidden() || s.Attr.Local() { + class = IMAGE_SYM_CLASS_STATIC + } + f.writeSymbol(ctxt.Out, s, value, sect, typ, uint8(class)) + } + + if ctxt.LinkMode == LinkExternal { + // Include section symbols as external, because + // .ctors and .debug_* section relocations refer to it. + for _, pesect := range f.sections { + sym := ctxt.Syms.Lookup(pesect.name, 0) + f.writeSymbol(ctxt.Out, sym, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC) + } + } + + genasmsym(ctxt, put) +} + +// writeSymbolTableAndStringTable writes out symbol and string tables for peFile f. +func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) { + f.symtabOffset = ctxt.Out.Offset() + + // write COFF symbol table + if !*FlagS || ctxt.LinkMode == LinkExternal { + f.writeSymbols(ctxt) + } + + // update COFF file header and section table + size := f.stringTable.size() + 18*f.symbolCount + var h *peSection + if ctxt.LinkMode != LinkExternal { + // We do not really need .symtab for go.o, and if we have one, ld + // will also include it in the exe, and that will confuse windows. + h = f.addSection(".symtab", size, size) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + h.checkOffset(f.symtabOffset) + } + + // write COFF string table + f.stringTable.write(ctxt.Out) + if ctxt.LinkMode != LinkExternal { + h.pad(ctxt.Out, uint32(size)) + } +} + +// writeFileHeader writes COFF file header for peFile f. +func (f *peFile) writeFileHeader(ctxt *Link) { + var fh pe.FileHeader + + switch ctxt.Arch.Family { + default: + Exitf("unknown PE architecture: %v", ctxt.Arch.Family) + case sys.AMD64: + fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64 + case sys.I386: + fh.Machine = pe.IMAGE_FILE_MACHINE_I386 + case sys.ARM: + fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT + } + + fh.NumberOfSections = uint16(len(f.sections)) + + // Being able to produce identical output for identical input is + // much more beneficial than having build timestamp in the header. + fh.TimeDateStamp = 0 + + if ctxt.LinkMode == LinkExternal { + fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED + } else { + fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: + if ctxt.BuildMode != BuildModePIE { + fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED + } + } + } + if pe64 != 0 { + var oh64 pe.OptionalHeader64 + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) + fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE + } else { + var oh pe.OptionalHeader32 + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) + fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE + } + + fh.PointerToSymbolTable = uint32(f.symtabOffset) + fh.NumberOfSymbols = uint32(f.symbolCount) + + binary.Write(ctxt.Out, binary.LittleEndian, &fh) +} + +// writeOptionalHeader writes COFF optional header for peFile f. +func (f *peFile) writeOptionalHeader(ctxt *Link) { + var oh pe.OptionalHeader32 + var oh64 pe.OptionalHeader64 + + if pe64 != 0 { + oh64.Magic = 0x20b // PE32+ + } else { + oh.Magic = 0x10b // PE32 + oh.BaseOfData = f.dataSect.virtualAddress + } + + // Fill out both oh64 and oh. We only use one. Oh well. + oh64.MajorLinkerVersion = 3 + oh.MajorLinkerVersion = 3 + oh64.MinorLinkerVersion = 0 + oh.MinorLinkerVersion = 0 + oh64.SizeOfCode = f.textSect.sizeOfRawData + oh.SizeOfCode = f.textSect.sizeOfRawData + oh64.SizeOfInitializedData = f.dataSect.sizeOfRawData + oh.SizeOfInitializedData = f.dataSect.sizeOfRawData + oh64.SizeOfUninitializedData = 0 + oh.SizeOfUninitializedData = 0 + if ctxt.LinkMode != LinkExternal { + oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE) + oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE) + } + oh64.BaseOfCode = f.textSect.virtualAddress + oh.BaseOfCode = f.textSect.virtualAddress + oh64.ImageBase = PEBASE + oh.ImageBase = PEBASE + oh64.SectionAlignment = uint32(PESECTALIGN) + oh.SectionAlignment = uint32(PESECTALIGN) + oh64.FileAlignment = uint32(PEFILEALIGN) + oh.FileAlignment = uint32(PEFILEALIGN) + oh64.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion + oh.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion + oh64.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion + oh.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion + oh64.MajorImageVersion = 1 + oh.MajorImageVersion = 1 + oh64.MinorImageVersion = 0 + oh.MinorImageVersion = 0 + oh64.MajorSubsystemVersion = PeMinimumTargetMajorVersion + oh.MajorSubsystemVersion = PeMinimumTargetMajorVersion + oh64.MinorSubsystemVersion = PeMinimumTargetMinorVersion + oh.MinorSubsystemVersion = PeMinimumTargetMinorVersion + oh64.SizeOfImage = f.nextSectOffset + oh.SizeOfImage = f.nextSectOffset + oh64.SizeOfHeaders = uint32(PEFILEHEADR) + oh.SizeOfHeaders = uint32(PEFILEHEADR) + if windowsgui { + oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI + oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI + } else { + oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI + oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI + } + + // Mark as having awareness of terminal services, to avoid ancient compatibility hacks. + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE + + // Enable DEP + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT + + // The DLL can be relocated at load time. + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: + if ctxt.BuildMode == BuildModePIE { + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + } + case sys.ARM: + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + } + + // Image can handle a high entropy 64-bit virtual address space. + if ctxt.BuildMode == BuildModePIE { + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA + } + + // Disable stack growth as we don't want Windows to + // fiddle with the thread stack limits, which we set + // ourselves to circumvent the stack checks in the + // Windows exception dispatcher. + // Commit size must be strictly less than reserve + // size otherwise reserve will be rounded up to a + // larger size, as verified with VMMap. + + // On 64-bit, we always reserve 2MB stacks. "Pure" Go code is + // okay with much smaller stacks, but the syscall package + // makes it easy to call into arbitrary C code without cgo, + // and system calls even in "pure" Go code are actually C + // calls that may need more stack than we think. + // + // The default stack reserve size directly affects only the main + // thread, ctrlhandler thread, and profileloop thread. For + // these, it must be greater than the stack size assumed by + // externalthreadhandler. + // + // For other threads, the runtime explicitly asks the kernel + // to use the default stack size so that all stacks are + // consistent. + // + // At thread start, in minit, the runtime queries the OS for + // the actual stack bounds so that the stack size doesn't need + // to be hard-coded into the runtime. + oh64.SizeOfStackReserve = 0x00200000 + if !iscgo { + oh64.SizeOfStackCommit = 0x00001000 + } else { + // TODO(brainman): Maybe remove optional header writing altogether for cgo. + // For cgo it is the external linker that is building final executable. + // And it probably does not use any information stored in optional header. + oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages + } + + oh.SizeOfStackReserve = 0x00100000 + if !iscgo { + oh.SizeOfStackCommit = 0x00001000 + } else { + oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages + } + + oh64.SizeOfHeapReserve = 0x00100000 + oh.SizeOfHeapReserve = 0x00100000 + oh64.SizeOfHeapCommit = 0x00001000 + oh.SizeOfHeapCommit = 0x00001000 + oh64.NumberOfRvaAndSizes = 16 + oh.NumberOfRvaAndSizes = 16 + + if pe64 != 0 { + oh64.DataDirectory = f.dataDirectory + } else { + oh.DataDirectory = f.dataDirectory + } + + if pe64 != 0 { + binary.Write(ctxt.Out, binary.LittleEndian, &oh64) + } else { + binary.Write(ctxt.Out, binary.LittleEndian, &oh) + } +} + +var pefile peFile + +func Peinit(ctxt *Link) { + var l int + + switch ctxt.Arch.Family { + // 64-bit architectures + case sys.AMD64: + pe64 = 1 + var oh64 pe.OptionalHeader64 + l = binary.Size(&oh64) + + // 32-bit architectures + default: + var oh pe.OptionalHeader32 + l = binary.Size(&oh) + + } + + if ctxt.LinkMode == LinkExternal { + // .rdata section will contain "masks" and "shifts" symbols, and they + // need to be aligned to 16-bytes. So make all sections aligned + // to 32-byte and mark them all IMAGE_SCN_ALIGN_32BYTES so external + // linker will honour that requirement. + PESECTALIGN = 32 + PEFILEALIGN = 0 + } + + var sh [16]pe.SectionHeader32 + var fh pe.FileHeader + PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN)) + if ctxt.LinkMode != LinkExternal { + PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN)) + } else { + PESECTHEADR = 0 + } + pefile.nextSectOffset = uint32(PESECTHEADR) + pefile.nextFileOffset = uint32(PEFILEHEADR) + + if ctxt.LinkMode == LinkInternal { + // some mingw libs depend on this symbol, for example, FindPESectionByName + ctxt.xdefine("__image_base__", sym.SDATA, PEBASE) + ctxt.xdefine("_image_base__", sym.SDATA, PEBASE) + } + + HEADR = PEFILEHEADR + if *FlagTextAddr == -1 { + *FlagTextAddr = PEBASE + int64(PESECTHEADR) + } + if *FlagRound == -1 { + *FlagRound = int(PESECTALIGN) + } +} + +func pewrite(ctxt *Link) { + ctxt.Out.SeekSet(0) + if ctxt.LinkMode != LinkExternal { + ctxt.Out.Write(dosstub) + ctxt.Out.WriteStringN("PE", 4) + } + + pefile.writeFileHeader(ctxt) + + pefile.writeOptionalHeader(ctxt) + + for _, sect := range pefile.sections { + sect.write(ctxt.Out, ctxt.LinkMode) + } +} + +func strput(out *OutBuf, s string) { + out.WriteString(s) + out.Write8(0) + // string must be padded to even size + if (len(s)+1)%2 != 0 { + out.Write8(0) + } +} + +func initdynimport(ctxt *Link) *Dll { + var d *Dll + + dr = nil + var m *Imp + for _, s := range ctxt.Syms.Allsym { + if !s.Attr.Reachable() || s.Type != sym.SDYNIMPORT { + continue + } + for d = dr; d != nil; d = d.next { + if d.name == s.Dynimplib() { + m = new(Imp) + break + } + } + + if d == nil { + d = new(Dll) + d.name = s.Dynimplib() + d.next = dr + dr = d + m = new(Imp) + } + + // Because external link requires properly stdcall decorated name, + // all external symbols in runtime use %n to denote that the number + // of uinptrs this function consumes. Store the argsize and discard + // the %n suffix if any. + m.argsize = -1 + extName := s.Extname() + if i := strings.IndexByte(extName, '%'); i >= 0 { + var err error + m.argsize, err = strconv.Atoi(extName[i+1:]) + if err != nil { + Errorf(s, "failed to parse stdcall decoration: %v", err) + } + m.argsize *= ctxt.Arch.PtrSize + s.SetExtname(extName[:i]) + } + + m.s = s + m.next = d.ms + d.ms = m + } + + if ctxt.LinkMode == LinkExternal { + // Add real symbol name + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + m.s.Type = sym.SDATA + m.s.Grow(int64(ctxt.Arch.PtrSize)) + dynName := m.s.Extname() + // only windows/386 requires stdcall decoration + if ctxt.Arch.Family == sys.I386 && m.argsize >= 0 { + dynName += fmt.Sprintf("@%d", m.argsize) + } + dynSym := ctxt.Syms.Lookup(dynName, 0) + dynSym.Attr |= sym.AttrReachable + dynSym.Type = sym.SHOSTOBJ + r := m.s.AddRel() + r.Sym = dynSym + r.Off = 0 + r.Siz = uint8(ctxt.Arch.PtrSize) + r.Type = objabi.R_ADDR + } + } + } else { + dynamic := ctxt.Syms.Lookup(".windynamic", 0) + dynamic.Attr |= sym.AttrReachable + dynamic.Type = sym.SWINDOWS + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + m.s.Type = sym.SWINDOWS + m.s.Attr |= sym.AttrSubSymbol + m.s.Sub = dynamic.Sub + dynamic.Sub = m.s + m.s.Value = dynamic.Size + dynamic.Size += int64(ctxt.Arch.PtrSize) + } + + dynamic.Size += int64(ctxt.Arch.PtrSize) + } + } + + return dr +} + +// peimporteddlls returns the gcc command line argument to link all imported +// DLLs. +func peimporteddlls() []string { + var dlls []string + + for d := dr; d != nil; d = d.next { + dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll")) + } + + return dlls +} + +func addimports(ctxt *Link, datsect *peSection) { + startoff := ctxt.Out.Offset() + dynamic := ctxt.Syms.Lookup(".windynamic", 0) + + // skip import descriptor table (will write it later) + n := uint64(0) + + for d := dr; d != nil; d = d.next { + n++ + } + ctxt.Out.SeekSet(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1)) + + // write dll names + for d := dr; d != nil; d = d.next { + d.nameoff = uint64(ctxt.Out.Offset()) - uint64(startoff) + strput(ctxt.Out, d.name) + } + + // write function names + for d := dr; d != nil; d = d.next { + for m := d.ms; m != nil; m = m.next { + m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff) + ctxt.Out.Write16(0) // hint + strput(ctxt.Out, m.s.Extname()) + } + } + + // write OriginalFirstThunks + oftbase := uint64(ctxt.Out.Offset()) - uint64(startoff) + + n = uint64(ctxt.Out.Offset()) + for d := dr; d != nil; d = d.next { + d.thunkoff = uint64(ctxt.Out.Offset()) - n + for m := d.ms; m != nil; m = m.next { + if pe64 != 0 { + ctxt.Out.Write64(m.off) + } else { + ctxt.Out.Write32(uint32(m.off)) + } + } + + if pe64 != 0 { + ctxt.Out.Write64(0) + } else { + ctxt.Out.Write32(0) + } + } + + // add pe section and pad it at the end + n = uint64(ctxt.Out.Offset()) - uint64(startoff) + + isect := pefile.addSection(".idata", int(n), int(n)) + isect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + isect.checkOffset(startoff) + isect.pad(ctxt.Out, uint32(n)) + endoff := ctxt.Out.Offset() + + // write FirstThunks (allocated in .data section) + ftbase := uint64(dynamic.Value) - uint64(datsect.virtualAddress) - PEBASE + + ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase)) + for d := dr; d != nil; d = d.next { + for m := d.ms; m != nil; m = m.next { + if pe64 != 0 { + ctxt.Out.Write64(m.off) + } else { + ctxt.Out.Write32(uint32(m.off)) + } + } + + if pe64 != 0 { + ctxt.Out.Write64(0) + } else { + ctxt.Out.Write32(0) + } + } + + // finally write import descriptor table + out := ctxt.Out + out.SeekSet(startoff) + + for d := dr; d != nil; d = d.next { + out.Write32(uint32(uint64(isect.virtualAddress) + oftbase + d.thunkoff)) + out.Write32(0) + out.Write32(0) + out.Write32(uint32(uint64(isect.virtualAddress) + d.nameoff)) + out.Write32(uint32(uint64(datsect.virtualAddress) + ftbase + d.thunkoff)) + } + + out.Write32(0) //end + out.Write32(0) + out.Write32(0) + out.Write32(0) + out.Write32(0) + + // update data directory + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size) + + out.SeekSet(endoff) +} + +type byExtname []*sym.Symbol + +func (s byExtname) Len() int { return len(s) } +func (s byExtname) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s byExtname) Less(i, j int) bool { return s[i].Extname() < s[j].Extname() } + +func initdynexport(ctxt *Link) { + nexport = 0 + for _, s := range ctxt.Syms.Allsym { + if !s.Attr.Reachable() || !s.Attr.CgoExportDynamic() { + continue + } + if nexport+1 > len(dexport) { + Errorf(s, "pe dynexport table is full") + errorexit() + } + + dexport[nexport] = s + nexport++ + } + + sort.Sort(byExtname(dexport[:nexport])) +} + +func addexports(ctxt *Link) { + var e IMAGE_EXPORT_DIRECTORY + + size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1 + for i := 0; i < nexport; i++ { + size += len(dexport[i].Extname()) + 1 + } + + if nexport == 0 { + return + } + + sect := pefile.addSection(".edata", size, size) + sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + sect.checkOffset(ctxt.Out.Offset()) + va := int(sect.virtualAddress) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize + + vaName := va + binary.Size(&e) + nexport*4 + vaAddr := va + binary.Size(&e) + vaNa := va + binary.Size(&e) + nexport*8 + + e.Characteristics = 0 + e.MajorVersion = 0 + e.MinorVersion = 0 + e.NumberOfFunctions = uint32(nexport) + e.NumberOfNames = uint32(nexport) + e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names. + e.Base = 1 + e.AddressOfFunctions = uint32(vaAddr) + e.AddressOfNames = uint32(vaName) + e.AddressOfNameOrdinals = uint32(vaNa) + + out := ctxt.Out + + // put IMAGE_EXPORT_DIRECTORY + binary.Write(out, binary.LittleEndian, &e) + + // put EXPORT Address Table + for i := 0; i < nexport; i++ { + out.Write32(uint32(dexport[i].Value - PEBASE)) + } + + // put EXPORT Name Pointer Table + v := int(e.Name + uint32(len(*flagOutfile)) + 1) + + for i := 0; i < nexport; i++ { + out.Write32(uint32(v)) + v += len(dexport[i].Extname()) + 1 + } + + // put EXPORT Ordinal Table + for i := 0; i < nexport; i++ { + out.Write16(uint16(i)) + } + + // put Names + out.WriteStringN(*flagOutfile, len(*flagOutfile)+1) + + for i := 0; i < nexport; i++ { + out.WriteStringN(dexport[i].Extname(), len(dexport[i].Extname())+1) + } + sect.pad(out, uint32(size)) +} + +// peBaseRelocEntry represents a single relocation entry. +type peBaseRelocEntry struct { + typeOff uint16 + rel *sym.Reloc + sym *sym.Symbol // For debug +} + +// peBaseRelocBlock represents a Base Relocation Block. A block +// is a collection of relocation entries in a page, where each +// entry describes a single relocation. +// The block page RVA (Relative Virtual Address) is the index +// into peBaseRelocTable.blocks. +type peBaseRelocBlock struct { + entries []peBaseRelocEntry +} + +// pePages is a type used to store the list of pages for which there +// are base relocation blocks. This is defined as a type so that +// it can be sorted. +type pePages []uint32 + +func (p pePages) Len() int { return len(p) } +func (p pePages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p pePages) Less(i, j int) bool { return p[i] < p[j] } + +// A PE base relocation table is a list of blocks, where each block +// contains relocation information for a single page. The blocks +// must be emitted in order of page virtual address. +// See https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-reloc-section-image-only +type peBaseRelocTable struct { + blocks map[uint32]peBaseRelocBlock + + // pePages is a list of keys into blocks map. + // It is stored separately for ease of sorting. + pages pePages +} + +func (rt *peBaseRelocTable) init(ctxt *Link) { + rt.blocks = make(map[uint32]peBaseRelocBlock) +} + +func (rt *peBaseRelocTable) addentry(ctxt *Link, s *sym.Symbol, r *sym.Reloc) { + // pageSize is the size in bytes of a page + // described by a base relocation block. + const pageSize = 0x1000 + const pageMask = pageSize - 1 + + addr := s.Value + int64(r.Off) - int64(PEBASE) + page := uint32(addr &^ pageMask) + off := uint32(addr & pageMask) + + b, ok := rt.blocks[page] + if !ok { + rt.pages = append(rt.pages, page) + } + + e := peBaseRelocEntry{ + typeOff: uint16(off & 0xFFF), + rel: r, + sym: s, + } + + // Set entry type + switch r.Siz { + default: + Exitf("unsupported relocation size %d\n", r.Siz) + case 4: + e.typeOff |= uint16(IMAGE_REL_BASED_HIGHLOW << 12) + case 8: + e.typeOff |= uint16(IMAGE_REL_BASED_DIR64 << 12) + } + + b.entries = append(b.entries, e) + rt.blocks[page] = b +} + +func (rt *peBaseRelocTable) write(ctxt *Link) { + out := ctxt.Out + + // sort the pages array + sort.Sort(rt.pages) + + for _, p := range rt.pages { + b := rt.blocks[p] + const sizeOfPEbaseRelocBlock = 8 // 2 * sizeof(uint32) + blockSize := uint32(sizeOfPEbaseRelocBlock + len(b.entries)*2) + out.Write32(p) + out.Write32(blockSize) + + for _, e := range b.entries { + out.Write16(e.typeOff) + } + } +} + +func addPEBaseRelocSym(ctxt *Link, s *sym.Symbol, rt *peBaseRelocTable) { + for ri := 0; ri < len(s.R); ri++ { + r := &s.R[ri] + + if r.Sym == nil { + continue + } + if !r.Sym.Attr.Reachable() { + continue + } + if r.Type >= objabi.ElfRelocOffset { + continue + } + if r.Siz == 0 { // informational relocation + continue + } + if r.Type == objabi.R_DWARFFILEREF { + continue + } + + switch r.Type { + default: + case objabi.R_ADDR: + rt.addentry(ctxt, s, r) + } + } +} + +func addPEBaseReloc(ctxt *Link) { + // Arm does not work without base relocation table. + // 386 and amd64 will only require the table for BuildModePIE. + switch ctxt.Arch.Family { + default: + return + case sys.I386, sys.AMD64: + if ctxt.BuildMode != BuildModePIE { + return + } + case sys.ARM: + } + + var rt peBaseRelocTable + rt.init(ctxt) + + // Get relocation information + for _, s := range ctxt.Textp { + addPEBaseRelocSym(ctxt, s, &rt) + } + for _, s := range datap { + addPEBaseRelocSym(ctxt, s, &rt) + } + + // Write relocation information + startoff := ctxt.Out.Offset() + rt.write(ctxt) + size := ctxt.Out.Offset() - startoff + + // Add a PE section and pad it at the end + rsect := pefile.addSection(".reloc", int(size), int(size)) + rsect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + rsect.checkOffset(startoff) + rsect.pad(ctxt.Out, uint32(size)) + + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize +} + +func (ctxt *Link) dope() { + initdynimport(ctxt) + initdynexport(ctxt) +} + +func setpersrc(ctxt *Link, sym *sym.Symbol) { + if rsrcsym != nil { + Errorf(sym, "too many .rsrc sections") + } + + rsrcsym = sym +} + +func addpersrc(ctxt *Link) { + if rsrcsym == nil { + return + } + + h := pefile.addSection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size)) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA + h.checkOffset(ctxt.Out.Offset()) + + // relocation + for ri := range rsrcsym.R { + r := &rsrcsym.R[ri] + p := rsrcsym.P[r.Off:] + val := uint32(int64(h.virtualAddress) + r.Add) + + // 32-bit little-endian + p[0] = byte(val) + + p[1] = byte(val >> 8) + p[2] = byte(val >> 16) + p[3] = byte(val >> 24) + } + + ctxt.Out.Write(rsrcsym.P) + h.pad(ctxt.Out, uint32(rsrcsym.Size)) + + // update data directory + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress + + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize +} + +func Asmbpe(ctxt *Link) { + switch ctxt.Arch.Family { + default: + Exitf("unknown PE architecture: %v", ctxt.Arch.Family) + case sys.AMD64, sys.I386, sys.ARM: + } + + t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length)) + t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ + if ctxt.LinkMode == LinkExternal { + // some data symbols (e.g. masks) end up in the .text section, and they normally + // expect larger alignment requirement than the default text section alignment. + t.characteristics |= IMAGE_SCN_ALIGN_32BYTES + } + t.checkSegment(&Segtext) + pefile.textSect = t + + ro := pefile.addSection(".rdata", int(Segrodata.Length), int(Segrodata.Length)) + ro.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + if ctxt.LinkMode == LinkExternal { + // some data symbols (e.g. masks) end up in the .rdata section, and they normally + // expect larger alignment requirement than the default text section alignment. + ro.characteristics |= IMAGE_SCN_ALIGN_32BYTES + } + ro.checkSegment(&Segrodata) + pefile.rdataSect = ro + + var d *peSection + if ctxt.LinkMode != LinkExternal { + d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen)) + d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + d.checkSegment(&Segdata) + pefile.dataSect = d + } else { + d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen)) + d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES + d.checkSegment(&Segdata) + pefile.dataSect = d + + b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0) + b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES + b.pointerToRawData = 0 + pefile.bssSect = b + } + + pefile.addDWARF() + + if ctxt.LinkMode == LinkExternal { + pefile.ctorsSect = pefile.addInitArray(ctxt) + } + + ctxt.Out.SeekSet(int64(pefile.nextFileOffset)) + if ctxt.LinkMode != LinkExternal { + addimports(ctxt, d) + addexports(ctxt) + addPEBaseReloc(ctxt) + } + pefile.writeSymbolTableAndStringTable(ctxt) + addpersrc(ctxt) + if ctxt.LinkMode == LinkExternal { + pefile.emitRelocations(ctxt) + } + + pewrite(ctxt) +} diff --git a/src/cmd/oldlink/internal/ld/sym.go b/src/cmd/oldlink/internal/ld/sym.go new file mode 100644 index 0000000000..c0f725c125 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/sym.go @@ -0,0 +1,115 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "log" +) + +func linknew(arch *sys.Arch) *Link { + ctxt := &Link{ + Syms: sym.NewSymbols(), + Out: &OutBuf{arch: arch}, + Arch: arch, + LibraryByPkg: make(map[string]*sym.Library), + } + + if objabi.GOARCH != arch.Name { + log.Fatalf("invalid objabi.GOARCH %s (want %s)", objabi.GOARCH, arch.Name) + } + + AtExit(func() { + if nerrors > 0 && ctxt.Out.f != nil { + ctxt.Out.f.Close() + mayberemoveoutfile() + } + }) + + return ctxt +} + +// computeTLSOffset records the thread-local storage offset. +// Not used for Android where the TLS offset is determined at runtime. +func (ctxt *Link) computeTLSOffset() { + switch ctxt.HeadType { + default: + log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType) + + case objabi.Hplan9, objabi.Hwindows, objabi.Hjs, objabi.Haix: + break + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd, + objabi.Hdragonfly, + objabi.Hsolaris: + /* + * ELF uses TLS offset negative from FS. + * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). + * Known to low-level assembly in package runtime and runtime/cgo. + */ + ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize + + case objabi.Hdarwin: + /* + * OS X system constants - offset from 0(GS) to our TLS. + */ + switch ctxt.Arch.Family { + default: + log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) + + /* + * For x86, Apple has reserved a slot in the TLS for Go. See issue 23617. + * That slot is at offset 0x30 on amd64, and 0x18 on 386. + * The slot will hold the G pointer. + * These constants should match those in runtime/sys_darwin_{386,amd64}.s + * and runtime/cgo/gcc_darwin_{386,amd64}.c. + */ + case sys.I386: + ctxt.Tlsoffset = 0x18 + + case sys.AMD64: + ctxt.Tlsoffset = 0x30 + + case sys.ARM: + ctxt.Tlsoffset = 0 // dummy value, not needed + + case sys.ARM64: + ctxt.Tlsoffset = 0 // dummy value, not needed + } + } + +} diff --git a/src/cmd/oldlink/internal/ld/symtab.go b/src/cmd/oldlink/internal/ld/symtab.go new file mode 100644 index 0000000000..a324fdf600 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/symtab.go @@ -0,0 +1,713 @@ +// Inferno utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "fmt" + "path/filepath" + "strings" +) + +// Symbol table. + +func putelfstr(s string) int { + if len(Elfstrdat) == 0 && s != "" { + // first entry must be empty string + putelfstr("") + } + + off := len(Elfstrdat) + Elfstrdat = append(Elfstrdat, s...) + Elfstrdat = append(Elfstrdat, 0) + return off +} + +func putelfsyment(out *OutBuf, off int, addr int64, size int64, info int, shndx int, other int) { + if elf64 { + out.Write32(uint32(off)) + out.Write8(uint8(info)) + out.Write8(uint8(other)) + out.Write16(uint16(shndx)) + out.Write64(uint64(addr)) + out.Write64(uint64(size)) + Symsize += ELF64SYMSIZE + } else { + out.Write32(uint32(off)) + out.Write32(uint32(addr)) + out.Write32(uint32(size)) + out.Write8(uint8(info)) + out.Write8(uint8(other)) + out.Write16(uint16(shndx)) + Symsize += ELF32SYMSIZE + } +} + +var numelfsym = 1 // 0 is reserved + +var elfbind int + +func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64, go_ *sym.Symbol) { + var typ int + + switch t { + default: + return + + case TextSym: + typ = STT_FUNC + + case DataSym, BSSSym: + typ = STT_OBJECT + + case UndefinedSym: + // ElfType is only set for symbols read from Go shared libraries, but + // for other symbols it is left as STT_NOTYPE which is fine. + typ = int(x.ElfType()) + + case TLSSym: + typ = STT_TLS + } + + size := x.Size + if t == UndefinedSym { + size = 0 + } + + xo := x + for xo.Outer != nil { + xo = xo.Outer + } + + var elfshnum int + if xo.Type == sym.SDYNIMPORT || xo.Type == sym.SHOSTOBJ || xo.Type == sym.SUNDEFEXT { + elfshnum = SHN_UNDEF + } else { + if xo.Sect == nil { + Errorf(x, "missing section in putelfsym") + return + } + if xo.Sect.Elfsect == nil { + Errorf(x, "missing ELF section in putelfsym") + return + } + elfshnum = xo.Sect.Elfsect.(*ElfShdr).shnum + } + + // One pass for each binding: STB_LOCAL, STB_GLOBAL, + // maybe one day STB_WEAK. + bind := STB_GLOBAL + + if x.IsFileLocal() || x.Attr.VisibilityHidden() || x.Attr.Local() { + bind = STB_LOCAL + } + + // In external linking mode, we have to invoke gcc with -rdynamic + // to get the exported symbols put into the dynamic symbol table. + // To avoid filling the dynamic table with lots of unnecessary symbols, + // mark all Go symbols local (not global) in the final executable. + // But when we're dynamically linking, we need all those global symbols. + if !ctxt.DynlinkingGo() && ctxt.LinkMode == LinkExternal && !x.Attr.CgoExportStatic() && elfshnum != SHN_UNDEF { + bind = STB_LOCAL + } + + if ctxt.LinkMode == LinkExternal && elfshnum != SHN_UNDEF { + addr -= int64(xo.Sect.Vaddr) + } + other := STV_DEFAULT + if x.Attr.VisibilityHidden() { + // TODO(mwhudson): We only set AttrVisibilityHidden in ldelf, i.e. when + // internally linking. But STV_HIDDEN visibility only matters in object + // files and shared libraries, and as we are a long way from implementing + // internal linking for shared libraries and only create object files when + // externally linking, I don't think this makes a lot of sense. + other = STV_HIDDEN + } + if ctxt.Arch.Family == sys.PPC64 && typ == STT_FUNC && x.Attr.Shared() && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" { + // On ppc64 the top three bits of the st_other field indicate how + // many instructions separate the global and local entry points. In + // our case it is two instructions, indicated by the value 3. + // The conditions here match those in preprocess in + // cmd/internal/obj/ppc64/obj9.go, which is where the + // instructions are inserted. + other |= 3 << 5 + } + + // When dynamically linking, we create Symbols by reading the names from + // the symbol tables of the shared libraries and so the names need to + // match exactly. Tools like DTrace will have to wait for now. + if !ctxt.DynlinkingGo() { + // Rewrite · to . for ASCII-only tools like DTrace (sigh) + s = strings.Replace(s, "·", ".", -1) + } + + if ctxt.DynlinkingGo() && bind == STB_GLOBAL && elfbind == STB_LOCAL && x.Type == sym.STEXT { + // When dynamically linking, we want references to functions defined + // in this module to always be to the function object, not to the + // PLT. We force this by writing an additional local symbol for every + // global function symbol and making all relocations against the + // global symbol refer to this local symbol instead (see + // (*sym.Symbol).ElfsymForReloc). This is approximately equivalent to the + // ELF linker -Bsymbolic-functions option, but that is buggy on + // several platforms. + putelfsyment(ctxt.Out, putelfstr("local."+s), addr, size, STB_LOCAL<<4|typ&0xf, elfshnum, other) + x.LocalElfsym = int32(numelfsym) + numelfsym++ + return + } else if bind != elfbind { + return + } + + putelfsyment(ctxt.Out, putelfstr(s), addr, size, bind<<4|typ&0xf, elfshnum, other) + x.Elfsym = int32(numelfsym) + numelfsym++ +} + +func putelfsectionsym(out *OutBuf, s *sym.Symbol, shndx int) { + putelfsyment(out, 0, 0, 0, STB_LOCAL<<4|STT_SECTION, shndx, 0) + s.Elfsym = int32(numelfsym) + numelfsym++ +} + +func Asmelfsym(ctxt *Link) { + // the first symbol entry is reserved + putelfsyment(ctxt.Out, 0, 0, 0, STB_LOCAL<<4|STT_NOTYPE, 0, 0) + + dwarfaddelfsectionsyms(ctxt) + + // Some linkers will add a FILE sym if one is not present. + // Avoid having the working directory inserted into the symbol table. + // It is added with a name to avoid problems with external linking + // encountered on some versions of Solaris. See issue #14957. + putelfsyment(ctxt.Out, putelfstr("go.go"), 0, 0, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0) + numelfsym++ + + elfbind = STB_LOCAL + genasmsym(ctxt, putelfsym) + + elfbind = STB_GLOBAL + elfglobalsymndx = numelfsym + genasmsym(ctxt, putelfsym) +} + +func putplan9sym(ctxt *Link, x *sym.Symbol, s string, typ SymbolType, addr int64, go_ *sym.Symbol) { + t := int(typ) + switch typ { + case TextSym, DataSym, BSSSym: + if x.IsFileLocal() { + t += 'a' - 'A' + } + fallthrough + + case AutoSym, ParamSym, FrameSym: + l := 4 + if ctxt.HeadType == objabi.Hplan9 && ctxt.Arch.Family == sys.AMD64 && !Flag8 { + ctxt.Out.Write32b(uint32(addr >> 32)) + l = 8 + } + + ctxt.Out.Write32b(uint32(addr)) + ctxt.Out.Write8(uint8(t + 0x80)) /* 0x80 is variable length */ + + ctxt.Out.WriteString(s) + ctxt.Out.Write8(0) + + Symsize += int32(l) + 1 + int32(len(s)) + 1 + + default: + return + } +} + +func Asmplan9sym(ctxt *Link) { + genasmsym(ctxt, putplan9sym) +} + +var symt *sym.Symbol + +type byPkg []*sym.Library + +func (libs byPkg) Len() int { + return len(libs) +} + +func (libs byPkg) Less(a, b int) bool { + return libs[a].Pkg < libs[b].Pkg +} + +func (libs byPkg) Swap(a, b int) { + libs[a], libs[b] = libs[b], libs[a] +} + +// Create a table with information on the text sections. + +func textsectionmap(ctxt *Link) uint32 { + + t := ctxt.Syms.Lookup("runtime.textsectionmap", 0) + t.Type = sym.SRODATA + t.Attr |= sym.AttrReachable + nsections := int64(0) + + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + nsections++ + } else { + break + } + } + t.Grow(3 * nsections * int64(ctxt.Arch.PtrSize)) + + off := int64(0) + n := 0 + + // The vaddr for each text section is the difference between the section's + // Vaddr and the Vaddr for the first text section as determined at compile + // time. + + // The symbol for the first text section is named runtime.text as before. + // Additional text sections are named runtime.text.n where n is the + // order of creation starting with 1. These symbols provide the section's + // address after relocation by the linker. + + textbase := Segtext.Sections[0].Vaddr + for _, sect := range Segtext.Sections { + if sect.Name != ".text" { + break + } + off = t.SetUint(ctxt.Arch, off, sect.Vaddr-textbase) + off = t.SetUint(ctxt.Arch, off, sect.Length) + if n == 0 { + s := ctxt.Syms.ROLookup("runtime.text", 0) + if s == nil { + Errorf(nil, "Unable to find symbol runtime.text\n") + } + off = t.SetAddr(ctxt.Arch, off, s) + + } else { + s := ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0) + if s == nil { + Errorf(nil, "Unable to find symbol runtime.text.%d\n", n) + } + off = t.SetAddr(ctxt.Arch, off, s) + } + n++ + } + return uint32(n) +} + +func (ctxt *Link) symtab() { + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared: + for _, s := range ctxt.Syms.Allsym { + // Create a new entry in the .init_array section that points to the + // library initializer function. + if s.Name == *flagEntrySymbol && ctxt.HeadType != objabi.Haix { + addinitarrdata(ctxt, s) + } + } + } + + // Define these so that they'll get put into the symbol table. + // data.c:/^address will provide the actual values. + ctxt.xdefine("runtime.text", sym.STEXT, 0) + + ctxt.xdefine("runtime.etext", sym.STEXT, 0) + ctxt.xdefine("runtime.itablink", sym.SRODATA, 0) + ctxt.xdefine("runtime.eitablink", sym.SRODATA, 0) + ctxt.xdefine("runtime.rodata", sym.SRODATA, 0) + ctxt.xdefine("runtime.erodata", sym.SRODATA, 0) + ctxt.xdefine("runtime.types", sym.SRODATA, 0) + ctxt.xdefine("runtime.etypes", sym.SRODATA, 0) + ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, 0) + ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, 0) + ctxt.xdefine("runtime.data", sym.SDATA, 0) + ctxt.xdefine("runtime.edata", sym.SDATA, 0) + ctxt.xdefine("runtime.bss", sym.SBSS, 0) + ctxt.xdefine("runtime.ebss", sym.SBSS, 0) + ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, 0) + ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, 0) + ctxt.xdefine("runtime.end", sym.SBSS, 0) + ctxt.xdefine("runtime.epclntab", sym.SRODATA, 0) + ctxt.xdefine("runtime.esymtab", sym.SRODATA, 0) + + // garbage collection symbols + s := ctxt.Syms.Lookup("runtime.gcdata", 0) + + s.Type = sym.SRODATA + s.Size = 0 + s.Attr |= sym.AttrReachable + ctxt.xdefine("runtime.egcdata", sym.SRODATA, 0) + + s = ctxt.Syms.Lookup("runtime.gcbss", 0) + s.Type = sym.SRODATA + s.Size = 0 + s.Attr |= sym.AttrReachable + ctxt.xdefine("runtime.egcbss", sym.SRODATA, 0) + + // pseudo-symbols to mark locations of type, string, and go string data. + var symtype *sym.Symbol + var symtyperel *sym.Symbol + if !ctxt.DynlinkingGo() { + if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) { + s = ctxt.Syms.Lookup("type.*", 0) + + s.Type = sym.STYPE + s.Size = 0 + s.Attr |= sym.AttrReachable + symtype = s + + s = ctxt.Syms.Lookup("typerel.*", 0) + + s.Type = sym.STYPERELRO + s.Size = 0 + s.Attr |= sym.AttrReachable + symtyperel = s + } else { + s = ctxt.Syms.Lookup("type.*", 0) + + s.Type = sym.STYPE + s.Size = 0 + s.Attr |= sym.AttrReachable + symtype = s + symtyperel = s + } + } + + groupSym := func(name string, t sym.SymKind) *sym.Symbol { + s := ctxt.Syms.Lookup(name, 0) + s.Type = t + s.Size = 0 + s.Attr |= sym.AttrLocal | sym.AttrReachable + return s + } + var ( + symgostring = groupSym("go.string.*", sym.SGOSTRING) + symgofunc = groupSym("go.func.*", sym.SGOFUNC) + symgcbits = groupSym("runtime.gcbits.*", sym.SGCBITS) + ) + + var symgofuncrel *sym.Symbol + if !ctxt.DynlinkingGo() { + if ctxt.UseRelro() { + symgofuncrel = groupSym("go.funcrel.*", sym.SGOFUNCRELRO) + } else { + symgofuncrel = symgofunc + } + } + + symitablink := ctxt.Syms.Lookup("runtime.itablink", 0) + symitablink.Type = sym.SITABLINK + + symt = ctxt.Syms.Lookup("runtime.symtab", 0) + symt.Attr |= sym.AttrLocal + symt.Type = sym.SSYMTAB + symt.Size = 0 + symt.Attr |= sym.AttrReachable + + nitablinks := 0 + + // assign specific types so that they sort together. + // within a type they sort by size, so the .* symbols + // just defined above will be first. + // hide the specific symbols. + for _, s := range ctxt.Syms.Allsym { + if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) { + s.Attr |= sym.AttrNotInSymbolTable + } + + if !s.Attr.Reachable() || s.Attr.Special() || s.Type != sym.SRODATA { + continue + } + + switch { + case strings.HasPrefix(s.Name, "type."): + if !ctxt.DynlinkingGo() { + s.Attr |= sym.AttrNotInSymbolTable + } + if ctxt.UseRelro() { + s.Type = sym.STYPERELRO + s.Outer = symtyperel + } else { + s.Type = sym.STYPE + s.Outer = symtype + } + + case strings.HasPrefix(s.Name, "go.importpath.") && ctxt.UseRelro(): + // Keep go.importpath symbols in the same section as types and + // names, as they can be referred to by a section offset. + s.Type = sym.STYPERELRO + + case strings.HasPrefix(s.Name, "go.itablink."): + nitablinks++ + s.Type = sym.SITABLINK + s.Attr |= sym.AttrNotInSymbolTable + s.Outer = symitablink + + case strings.HasPrefix(s.Name, "go.string."): + s.Type = sym.SGOSTRING + s.Attr |= sym.AttrNotInSymbolTable + s.Outer = symgostring + + case strings.HasPrefix(s.Name, "runtime.gcbits."): + s.Type = sym.SGCBITS + s.Attr |= sym.AttrNotInSymbolTable + s.Outer = symgcbits + + case strings.HasSuffix(s.Name, "·f"): + if !ctxt.DynlinkingGo() { + s.Attr |= sym.AttrNotInSymbolTable + } + if ctxt.UseRelro() { + s.Type = sym.SGOFUNCRELRO + s.Outer = symgofuncrel + } else { + s.Type = sym.SGOFUNC + s.Outer = symgofunc + } + + case strings.HasPrefix(s.Name, "gcargs."), + strings.HasPrefix(s.Name, "gclocals."), + strings.HasPrefix(s.Name, "gclocals·"), + strings.HasPrefix(s.Name, "inltree."), + strings.HasSuffix(s.Name, ".opendefer"): + s.Type = sym.SGOFUNC + s.Attr |= sym.AttrNotInSymbolTable + s.Outer = symgofunc + s.Align = 4 + liveness += (s.Size + int64(s.Align) - 1) &^ (int64(s.Align) - 1) + } + } + + if ctxt.BuildMode == BuildModeShared { + abihashgostr := ctxt.Syms.Lookup("go.link.abihash."+filepath.Base(*flagOutfile), 0) + abihashgostr.Attr |= sym.AttrReachable + abihashgostr.Type = sym.SRODATA + hashsym := ctxt.Syms.Lookup("go.link.abihashbytes", 0) + abihashgostr.AddAddr(ctxt.Arch, hashsym) + abihashgostr.AddUint(ctxt.Arch, uint64(hashsym.Size)) + } + if ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() { + for _, l := range ctxt.Library { + s := ctxt.Syms.Lookup("go.link.pkghashbytes."+l.Pkg, 0) + s.Attr |= sym.AttrReachable + s.Type = sym.SRODATA + s.Size = int64(len(l.Hash)) + s.P = []byte(l.Hash) + str := ctxt.Syms.Lookup("go.link.pkghash."+l.Pkg, 0) + str.Attr |= sym.AttrReachable + str.Type = sym.SRODATA + str.AddAddr(ctxt.Arch, s) + str.AddUint(ctxt.Arch, uint64(len(l.Hash))) + } + } + + nsections := textsectionmap(ctxt) + + // Information about the layout of the executable image for the + // runtime to use. Any changes here must be matched by changes to + // the definition of moduledata in runtime/symtab.go. + // This code uses several global variables that are set by pcln.go:pclntab. + moduledata := ctxt.Moduledata + // The pclntab slice + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0)) + moduledata.AddUint(ctxt.Arch, uint64(ctxt.Syms.Lookup("runtime.pclntab", 0).Size)) + moduledata.AddUint(ctxt.Arch, uint64(ctxt.Syms.Lookup("runtime.pclntab", 0).Size)) + // The ftab slice + moduledata.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabPclntabOffset)) + moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1)) + moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1)) + // The filetab slice + moduledata.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabFiletabOffset)) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Filesyms))+1) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Filesyms))+1) + // findfunctab + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.findfunctab", 0)) + // minpc, maxpc + moduledata.AddAddr(ctxt.Arch, pclntabFirstFunc) + moduledata.AddAddrPlus(ctxt.Arch, pclntabLastFunc, pclntabLastFunc.Size) + // pointers to specific parts of the module + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.text", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.etext", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.noptrdata", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.enoptrdata", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.data", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.edata", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.bss", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.ebss", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.noptrbss", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.enoptrbss", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.end", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.gcdata", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.gcbss", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.types", 0)) + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.etypes", 0)) + + if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal { + // Add R_REF relocation to prevent ld's garbage collection of + // runtime.rodata, runtime.erodata and runtime.epclntab. + addRef := func(name string) { + r := moduledata.AddRel() + r.Sym = ctxt.Syms.Lookup(name, 0) + r.Type = objabi.R_XCOFFREF + r.Siz = uint8(ctxt.Arch.PtrSize) + } + addRef("runtime.rodata") + addRef("runtime.erodata") + addRef("runtime.epclntab") + } + + // text section information + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.textsectionmap", 0)) + moduledata.AddUint(ctxt.Arch, uint64(nsections)) + moduledata.AddUint(ctxt.Arch, uint64(nsections)) + + // The typelinks slice + typelinkSym := ctxt.Syms.Lookup("runtime.typelink", 0) + ntypelinks := uint64(typelinkSym.Size) / 4 + moduledata.AddAddr(ctxt.Arch, typelinkSym) + moduledata.AddUint(ctxt.Arch, ntypelinks) + moduledata.AddUint(ctxt.Arch, ntypelinks) + // The itablinks slice + moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.itablink", 0)) + moduledata.AddUint(ctxt.Arch, uint64(nitablinks)) + moduledata.AddUint(ctxt.Arch, uint64(nitablinks)) + // The ptab slice + if ptab := ctxt.Syms.ROLookup("go.plugin.tabs", 0); ptab != nil && ptab.Attr.Reachable() { + ptab.Attr |= sym.AttrLocal + ptab.Type = sym.SRODATA + + nentries := uint64(len(ptab.P) / 8) // sizeof(nameOff) + sizeof(typeOff) + moduledata.AddAddr(ctxt.Arch, ptab) + moduledata.AddUint(ctxt.Arch, nentries) + moduledata.AddUint(ctxt.Arch, nentries) + } else { + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + if ctxt.BuildMode == BuildModePlugin { + addgostring(ctxt, moduledata, "go.link.thispluginpath", objabi.PathToPrefix(*flagPluginPath)) + + pkghashes := ctxt.Syms.Lookup("go.link.pkghashes", 0) + pkghashes.Attr |= sym.AttrReachable + pkghashes.Attr |= sym.AttrLocal + pkghashes.Type = sym.SRODATA + + for i, l := range ctxt.Library { + // pkghashes[i].name + addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkgname.%d", i), l.Pkg) + // pkghashes[i].linktimehash + addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkglinkhash.%d", i), l.Hash) + // pkghashes[i].runtimehash + hash := ctxt.Syms.ROLookup("go.link.pkghash."+l.Pkg, 0) + pkghashes.AddAddr(ctxt.Arch, hash) + } + moduledata.AddAddr(ctxt.Arch, pkghashes) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library))) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library))) + } else { + moduledata.AddUint(ctxt.Arch, 0) // pluginpath + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) // pkghashes slice + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + if len(ctxt.Shlibs) > 0 { + thismodulename := filepath.Base(*flagOutfile) + switch ctxt.BuildMode { + case BuildModeExe, BuildModePIE: + // When linking an executable, outfile is just "a.out". Make + // it something slightly more comprehensible. + thismodulename = "the executable" + } + addgostring(ctxt, moduledata, "go.link.thismodulename", thismodulename) + + modulehashes := ctxt.Syms.Lookup("go.link.abihashes", 0) + modulehashes.Attr |= sym.AttrReachable + modulehashes.Attr |= sym.AttrLocal + modulehashes.Type = sym.SRODATA + + for i, shlib := range ctxt.Shlibs { + // modulehashes[i].modulename + modulename := filepath.Base(shlib.Path) + addgostring(ctxt, modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename) + + // modulehashes[i].linktimehash + addgostring(ctxt, modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash)) + + // modulehashes[i].runtimehash + abihash := ctxt.Syms.Lookup("go.link.abihash."+modulename, 0) + abihash.Attr |= sym.AttrReachable + modulehashes.AddAddr(ctxt.Arch, abihash) + } + + moduledata.AddAddr(ctxt.Arch, modulehashes) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs))) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs))) + } else { + moduledata.AddUint(ctxt.Arch, 0) // modulename + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) // moduleshashes slice + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + + hasmain := ctxt.BuildMode == BuildModeExe || ctxt.BuildMode == BuildModePIE + if hasmain { + moduledata.AddUint8(1) + } else { + moduledata.AddUint8(0) + } + + // The rest of moduledata is zero initialized. + // When linking an object that does not contain the runtime we are + // creating the moduledata from scratch and it does not have a + // compiler-provided size, so read it from the type data. + moduledatatype := ctxt.Syms.ROLookup("type.runtime.moduledata", 0) + moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype.P) + moduledata.Grow(moduledata.Size) + + lastmoduledatap := ctxt.Syms.Lookup("runtime.lastmoduledatap", 0) + if lastmoduledatap.Type != sym.SDYNIMPORT { + lastmoduledatap.Type = sym.SNOPTRDATA + lastmoduledatap.Size = 0 // overwrite existing value + lastmoduledatap.AddAddr(ctxt.Arch, moduledata) + } +} + +func isStaticTemp(name string) bool { + if i := strings.LastIndex(name, "/"); i >= 0 { + name = name[i:] + } + return strings.Contains(name, "..stmp_") +} diff --git a/src/cmd/oldlink/internal/ld/testdata/httptest/main/main.go b/src/cmd/oldlink/internal/ld/testdata/httptest/main/main.go new file mode 100644 index 0000000000..1bce30119a --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/httptest/main/main.go @@ -0,0 +1,22 @@ +// A small test program that uses the net/http package. There is +// nothing special about net/http here, this is just a convenient way +// to pull in a lot of code. + +package main + +import ( + "net/http" + "net/http/httptest" +) + +type statusHandler int + +func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(int(*h)) +} + +func main() { + status := statusHandler(http.StatusNotFound) + s := httptest.NewServer(&status) + defer s.Close() +} diff --git a/src/cmd/oldlink/internal/ld/testdata/issue10978/main.go b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.go new file mode 100644 index 0000000000..5e8c09749f --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func undefined() + +func defined1() int { + // To check multiple errors for a single symbol, + // reference undefined more than once. + undefined() + undefined() + return 0 +} + +func defined2() { + undefined() + undefined() +} + +func init() { + _ = defined1() + defined2() +} + +// The "main" function remains undeclared. diff --git a/src/cmd/oldlink/internal/ld/testdata/issue10978/main.s b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.s new file mode 100644 index 0000000000..1d00e76c1d --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue10978/main.s @@ -0,0 +1 @@ +// This file is needed to make "go build" work for package with external functions.
diff --git a/src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go b/src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go new file mode 100644 index 0000000000..6032d76f49 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue25459/a/a.go @@ -0,0 +1,27 @@ +package a + +const Always = true + +var Count int + +type FuncReturningInt func() int + +var PointerToConstIf FuncReturningInt + +func ConstIf() int { + if Always { + return 1 + } + var imdead [4]int + imdead[Count] = 1 + return imdead[0] +} + +func CallConstIf() int { + Count += 3 + return ConstIf() +} + +func Another() { + defer func() { PointerToConstIf = ConstIf; Count += 1 }() +} diff --git a/src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go new file mode 100644 index 0000000000..3f5f365169 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue25459/main/main.go @@ -0,0 +1,10 @@ +package main + +import "cmd/oldlink/internal/ld/testdata/issue25459/a" + +var Glob int + +func main() { + a.Another() + Glob += a.ConstIf() + a.CallConstIf() +} diff --git a/src/cmd/oldlink/internal/ld/testdata/issue26237/b.dir/b.go b/src/cmd/oldlink/internal/ld/testdata/issue26237/b.dir/b.go new file mode 100644 index 0000000000..ca577490bc --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue26237/b.dir/b.go @@ -0,0 +1,16 @@ +package b + +var q int + +func Top(x int) int { + q += 1 + if q != x { + return 3 + } + return 4 +} + +func OOO(x int) int { + defer func() { q += x & 7 }() + return Top(x + 1) +} diff --git a/src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go new file mode 100644 index 0000000000..88b54f1678 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue26237/main/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + + b "cmd/oldlink/internal/ld/testdata/issue26237/b.dir" +) + +var skyx int + +func main() { + skyx += b.OOO(skyx) + if b.Top(1) == 99 { + fmt.Printf("Beware the Jabberwock, my son!\n") + } +} diff --git a/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/ObjC.m b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/ObjC.m new file mode 100644 index 0000000000..946278803e --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/ObjC.m @@ -0,0 +1,16 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#import <Foundation/Foundation.h> +#import <AppKit/NSAppearance.h> + +BOOL function(void) { +#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED > 101300) + NSAppearance *darkAppearance; + if (@available(macOS 10.14, *)) { + darkAppearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; + } +#endif + return NO; +} diff --git a/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go new file mode 100644 index 0000000000..514b9b9a4a --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue32233/lib/lib.go @@ -0,0 +1,19 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lib + +/* +#cgo darwin CFLAGS: -D__MAC_OS_X_VERSION_MAX_ALLOWED=101450 +#cgo darwin LDFLAGS: -framework Foundation -framework AppKit +#include "stdlib.h" +int function(void); +*/ +import "C" +import "fmt" + +func DoC() { + C.function() + fmt.Println("called c function") +} diff --git a/src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go b/src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go new file mode 100644 index 0000000000..9eec6cc5b5 --- /dev/null +++ b/src/cmd/oldlink/internal/ld/testdata/issue32233/main/main.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "cmd/oldlink/internal/ld/testdata/issue32233/lib" + +func main() { + lib.DoC() +} diff --git a/src/cmd/oldlink/internal/ld/typelink.go b/src/cmd/oldlink/internal/ld/typelink.go new file mode 100644 index 0000000000..07d04bb13d --- /dev/null +++ b/src/cmd/oldlink/internal/ld/typelink.go @@ -0,0 +1,49 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/oldlink/internal/sym" + "sort" +) + +type byTypeStr []typelinkSortKey + +type typelinkSortKey struct { + TypeStr string + Type *sym.Symbol +} + +func (s byTypeStr) Less(i, j int) bool { return s[i].TypeStr < s[j].TypeStr } +func (s byTypeStr) Len() int { return len(s) } +func (s byTypeStr) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// typelink generates the typelink table which is used by reflect.typelinks(). +// Types that should be added to the typelinks table are marked with the +// MakeTypelink attribute by the compiler. +func (ctxt *Link) typelink() { + typelinks := byTypeStr{} + for _, s := range ctxt.Syms.Allsym { + if s.Attr.Reachable() && s.Attr.MakeTypelink() { + typelinks = append(typelinks, typelinkSortKey{decodetypeStr(ctxt.Arch, s), s}) + } + } + sort.Sort(typelinks) + + tl := ctxt.Syms.Lookup("runtime.typelink", 0) + tl.Type = sym.STYPELINK + tl.Attr |= sym.AttrReachable | sym.AttrLocal + tl.Size = int64(4 * len(typelinks)) + tl.P = make([]byte, tl.Size) + tl.R = make([]sym.Reloc, len(typelinks)) + for i, s := range typelinks { + r := &tl.R[i] + r.Sym = s.Type + r.Off = int32(i * 4) + r.Siz = 4 + r.Type = objabi.R_ADDROFF + } +} diff --git a/src/cmd/oldlink/internal/ld/util.go b/src/cmd/oldlink/internal/ld/util.go new file mode 100644 index 0000000000..97c9a44c1e --- /dev/null +++ b/src/cmd/oldlink/internal/ld/util.go @@ -0,0 +1,97 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/oldlink/internal/sym" + "encoding/binary" + "fmt" + "os" +) + +var atExitFuncs []func() + +func AtExit(f func()) { + atExitFuncs = append(atExitFuncs, f) +} + +// runAtExitFuncs runs the queued set of AtExit functions. +func runAtExitFuncs() { + for i := len(atExitFuncs) - 1; i >= 0; i-- { + atExitFuncs[i]() + } + atExitFuncs = nil +} + +// Exit exits with code after executing all atExitFuncs. +func Exit(code int) { + runAtExitFuncs() + os.Exit(code) +} + +// Exitf logs an error message then calls Exit(2). +func Exitf(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...) + nerrors++ + Exit(2) +} + +// Errorf logs an error message. +// +// If more than 20 errors have been printed, exit with an error. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +func Errorf(s *sym.Symbol, format string, args ...interface{}) { + if s != nil { + format = s.Name + ": " + format + } + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + nerrors++ + if *flagH { + panic("error") + } + if nerrors > 20 { + Exitf("too many errors") + } +} + +func artrim(x []byte) string { + i := 0 + j := len(x) + for i < len(x) && x[i] == ' ' { + i++ + } + for j > i && x[j-1] == ' ' { + j-- + } + return string(x[i:j]) +} + +func stringtouint32(x []uint32, s string) { + for i := 0; len(s) > 0; i++ { + var buf [4]byte + s = s[copy(buf[:], s):] + x[i] = binary.LittleEndian.Uint32(buf[:]) + } +} + +// contains reports whether v is in s. +func contains(s []string, v string) bool { + for _, x := range s { + if x == v { + return true + } + } + return false +} + +// implements sort.Interface, for sorting symbols by name. +type byName []*sym.Symbol + +func (s byName) Len() int { return len(s) } +func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name } diff --git a/src/cmd/oldlink/internal/ld/xcoff.go b/src/cmd/oldlink/internal/ld/xcoff.go new file mode 100644 index 0000000000..4d66d6d75e --- /dev/null +++ b/src/cmd/oldlink/internal/ld/xcoff.go @@ -0,0 +1,1685 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "cmd/internal/objabi" + "cmd/oldlink/internal/sym" + "encoding/binary" + "io/ioutil" + "math/bits" + "path/filepath" + "sort" + "strings" +) + +// This file handles all algorithms related to XCOFF files generation. +// Most of them are adaptations of the ones in cmd/oldlink/internal/pe.go +// as PE and XCOFF are based on COFF files. +// XCOFF files generated are 64 bits. + +const ( + // Total amount of space to reserve at the start of the file + // for File Header, Auxiliary Header, and Section Headers. + // May waste some. + XCOFFHDRRESERVE = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23 + XCOFFSECTALIGN int64 = 32 // base on dump -o + + // XCOFF binaries should normally have all its sections position-independent. + // However, this is not yet possible for .text because of some R_ADDR relocations + // inside RODATA symbols. + // .data and .bss are position-independent so their address start inside a unreachable + // segment during execution to force segfault if something is wrong. + XCOFFTEXTBASE = 0x100000000 // Start of text address + XCOFFDATABASE = 0x200000000 // Start of data address +) + +// File Header +type XcoffFileHdr64 struct { + Fmagic uint16 // Target machine + Fnscns uint16 // Number of sections + Ftimedat int32 // Time and date of file creation + Fsymptr uint64 // Byte offset to symbol table start + Fopthdr uint16 // Number of bytes in optional header + Fflags uint16 // Flags + Fnsyms int32 // Number of entries in symbol table +} + +const ( + U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF +) + +// Flags that describe the type of the object file. +const ( + F_RELFLG = 0x0001 + F_EXEC = 0x0002 + F_LNNO = 0x0004 + F_FDPR_PROF = 0x0010 + F_FDPR_OPTI = 0x0020 + F_DSA = 0x0040 + F_VARPG = 0x0100 + F_DYNLOAD = 0x1000 + F_SHROBJ = 0x2000 + F_LOADONLY = 0x4000 +) + +// Auxiliary Header +type XcoffAoutHdr64 struct { + Omagic int16 // Flags - Ignored If Vstamp Is 1 + Ovstamp int16 // Version + Odebugger uint32 // Reserved For Debugger + Otextstart uint64 // Virtual Address Of Text + Odatastart uint64 // Virtual Address Of Data + Otoc uint64 // Toc Address + Osnentry int16 // Section Number For Entry Point + Osntext int16 // Section Number For Text + Osndata int16 // Section Number For Data + Osntoc int16 // Section Number For Toc + Osnloader int16 // Section Number For Loader + Osnbss int16 // Section Number For Bss + Oalgntext int16 // Max Text Alignment + Oalgndata int16 // Max Data Alignment + Omodtype [2]byte // Module Type Field + Ocpuflag uint8 // Bit Flags - Cputypes Of Objects + Ocputype uint8 // Reserved for CPU type + Otextpsize uint8 // Requested text page size + Odatapsize uint8 // Requested data page size + Ostackpsize uint8 // Requested stack page size + Oflags uint8 // Flags And TLS Alignment + Otsize uint64 // Text Size In Bytes + Odsize uint64 // Data Size In Bytes + Obsize uint64 // Bss Size In Bytes + Oentry uint64 // Entry Point Address + Omaxstack uint64 // Max Stack Size Allowed + Omaxdata uint64 // Max Data Size Allowed + Osntdata int16 // Section Number For Tdata Section + Osntbss int16 // Section Number For Tbss Section + Ox64flags uint16 // Additional Flags For 64-Bit Objects + Oresv3a int16 // Reserved + Oresv3 [2]int32 // Reserved +} + +// Section Header +type XcoffScnHdr64 struct { + Sname [8]byte // Section Name + Spaddr uint64 // Physical Address + Svaddr uint64 // Virtual Address + Ssize uint64 // Section Size + Sscnptr uint64 // File Offset To Raw Data + Srelptr uint64 // File Offset To Relocation + Slnnoptr uint64 // File Offset To Line Numbers + Snreloc uint32 // Number Of Relocation Entries + Snlnno uint32 // Number Of Line Number Entries + Sflags uint32 // flags +} + +// Flags defining the section type. +const ( + STYP_DWARF = 0x0010 + STYP_TEXT = 0x0020 + STYP_DATA = 0x0040 + STYP_BSS = 0x0080 + STYP_EXCEPT = 0x0100 + STYP_INFO = 0x0200 + STYP_TDATA = 0x0400 + STYP_TBSS = 0x0800 + STYP_LOADER = 0x1000 + STYP_DEBUG = 0x2000 + STYP_TYPCHK = 0x4000 + STYP_OVRFLO = 0x8000 +) +const ( + SSUBTYP_DWINFO = 0x10000 // DWARF info section + SSUBTYP_DWLINE = 0x20000 // DWARF line-number section + SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section + SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section + SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section + SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section + SSUBTYP_DWSTR = 0x70000 // DWARF strings section + SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section + SSUBTYP_DWLOC = 0x90000 // DWARF location lists section + SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section + SSUBTYP_DWMAC = 0xB0000 // DWARF macros section +) + +// Headers size +const ( + FILHSZ_32 = 20 + FILHSZ_64 = 24 + AOUTHSZ_EXEC32 = 72 + AOUTHSZ_EXEC64 = 120 + SCNHSZ_32 = 40 + SCNHSZ_64 = 72 + LDHDRSZ_32 = 32 + LDHDRSZ_64 = 56 + LDSYMSZ_64 = 24 + RELSZ_64 = 14 +) + +// Type representing all XCOFF symbols. +type xcoffSym interface { +} + +// Symbol Table Entry +type XcoffSymEnt64 struct { + Nvalue uint64 // Symbol value + Noffset uint32 // Offset of the name in string table or .debug section + Nscnum int16 // Section number of symbol + Ntype uint16 // Basic and derived type specification + Nsclass uint8 // Storage class of symbol + Nnumaux int8 // Number of auxiliary entries +} + +const SYMESZ = 18 + +const ( + // Nscnum + N_DEBUG = -2 + N_ABS = -1 + N_UNDEF = 0 + + //Ntype + SYM_V_INTERNAL = 0x1000 + SYM_V_HIDDEN = 0x2000 + SYM_V_PROTECTED = 0x3000 + SYM_V_EXPORTED = 0x4000 + SYM_TYPE_FUNC = 0x0020 // is function +) + +// Storage Class. +const ( + C_NULL = 0 // Symbol table entry marked for deletion + C_EXT = 2 // External symbol + C_STAT = 3 // Static symbol + C_BLOCK = 100 // Beginning or end of inner block + C_FCN = 101 // Beginning or end of function + C_FILE = 103 // Source file name and compiler information + C_HIDEXT = 107 // Unnamed external symbol + C_BINCL = 108 // Beginning of include file + C_EINCL = 109 // End of include file + C_WEAKEXT = 111 // Weak external symbol + C_DWARF = 112 // DWARF symbol + C_GSYM = 128 // Global variable + C_LSYM = 129 // Automatic variable allocated on stack + C_PSYM = 130 // Argument to subroutine allocated on stack + C_RSYM = 131 // Register variable + C_RPSYM = 132 // Argument to function or procedure stored in register + C_STSYM = 133 // Statically allocated symbol + C_BCOMM = 135 // Beginning of common block + C_ECOML = 136 // Local member of common block + C_ECOMM = 137 // End of common block + C_DECL = 140 // Declaration of object + C_ENTRY = 141 // Alternate entry + C_FUN = 142 // Function or procedure + C_BSTAT = 143 // Beginning of static block + C_ESTAT = 144 // End of static block + C_GTLS = 145 // Global thread-local variable + C_STTLS = 146 // Static thread-local variable +) + +// File Auxiliary Entry +type XcoffAuxFile64 struct { + Xzeroes uint32 // The name is always in the string table + Xoffset uint32 // Offset in the string table + X_pad1 [6]byte + Xftype uint8 // Source file string type + X_pad2 [2]byte + Xauxtype uint8 // Type of auxiliary entry +} + +// Function Auxiliary Entry +type XcoffAuxFcn64 struct { + Xlnnoptr uint64 // File pointer to line number + Xfsize uint32 // Size of function in bytes + Xendndx uint32 // Symbol table index of next entry + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +// csect Auxiliary Entry. +type XcoffAuxCSect64 struct { + Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index + Xparmhash uint32 // Offset of parameter type-check string + Xsnhash uint16 // .typchk section number + Xsmtyp uint8 // Symbol alignment and type + Xsmclas uint8 // Storage-mapping class + Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +// DWARF Auxiliary Entry +type XcoffAuxDWARF64 struct { + Xscnlen uint64 // Length of this symbol section + X_pad [9]byte + Xauxtype uint8 // Type of auxiliary entry +} + +// Auxiliary type +const ( + _AUX_EXCEPT = 255 + _AUX_FCN = 254 + _AUX_SYM = 253 + _AUX_FILE = 252 + _AUX_CSECT = 251 + _AUX_SECT = 250 +) + +// Xftype field +const ( + XFT_FN = 0 // Source File Name + XFT_CT = 1 // Compile Time Stamp + XFT_CV = 2 // Compiler Version Number + XFT_CD = 128 // Compiler Defined Information/ + +) + +// Symbol type field. +const ( + XTY_ER = 0 // External reference + XTY_SD = 1 // Section definition + XTY_LD = 2 // Label definition + XTY_CM = 3 // Common csect definition + XTY_WK = 0x8 // Weak symbol + XTY_EXP = 0x10 // Exported symbol + XTY_ENT = 0x20 // Entry point symbol + XTY_IMP = 0x40 // Imported symbol +) + +// Storage-mapping class. +const ( + XMC_PR = 0 // Program code + XMC_RO = 1 // Read-only constant + XMC_DB = 2 // Debug dictionary table + XMC_TC = 3 // TOC entry + XMC_UA = 4 // Unclassified + XMC_RW = 5 // Read/Write data + XMC_GL = 6 // Global linkage + XMC_XO = 7 // Extended operation + XMC_SV = 8 // 32-bit supervisor call descriptor + XMC_BS = 9 // BSS class + XMC_DS = 10 // Function descriptor + XMC_UC = 11 // Unnamed FORTRAN common + XMC_TC0 = 15 // TOC anchor + XMC_TD = 16 // Scalar data entry in the TOC + XMC_SV64 = 17 // 64-bit supervisor call descriptor + XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit + XMC_TL = 20 // Read/Write thread-local data + XMC_UL = 21 // Read/Write thread-local data (.tbss) + XMC_TE = 22 // TOC entry +) + +// Loader Header +type XcoffLdHdr64 struct { + Lversion int32 // Loader section version number + Lnsyms int32 // Number of symbol table entries + Lnreloc int32 // Number of relocation table entries + Listlen uint32 // Length of import file ID string table + Lnimpid int32 // Number of import file IDs + Lstlen uint32 // Length of string table + Limpoff uint64 // Offset to start of import file IDs + Lstoff uint64 // Offset to start of string table + Lsymoff uint64 // Offset to start of symbol table + Lrldoff uint64 // Offset to start of relocation entries +} + +// Loader Symbol +type XcoffLdSym64 struct { + Lvalue uint64 // Address field + Loffset uint32 // Byte offset into string table of symbol name + Lscnum int16 // Section number containing symbol + Lsmtype int8 // Symbol type, export, import flags + Lsmclas int8 // Symbol storage class + Lifile int32 // Import file ID; ordinal of import file IDs + Lparm uint32 // Parameter type-check field +} + +type xcoffLoaderSymbol struct { + sym *sym.Symbol + smtype int8 + smclas int8 +} + +type XcoffLdImportFile64 struct { + Limpidpath string + Limpidbase string + Limpidmem string +} + +type XcoffLdRel64 struct { + Lvaddr uint64 // Address Field + Lrtype uint16 // Relocation Size and Type + Lrsecnm int16 // Section Number being relocated + Lsymndx int32 // Loader-Section symbol table index +} + +// xcoffLoaderReloc holds information about a relocation made by the loader. +type xcoffLoaderReloc struct { + sym *sym.Symbol + rel *sym.Reloc + rtype uint16 + symndx int32 +} + +const ( + XCOFF_R_POS = 0x00 // A(sym) Positive Relocation + XCOFF_R_NEG = 0x01 // -A(sym) Negative Relocation + XCOFF_R_REL = 0x02 // A(sym-*) Relative to self + XCOFF_R_TOC = 0x03 // A(sym-TOC) Relative to TOC + XCOFF_R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load. + + XCOFF_R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst + XCOFF_R_GL = 0x05 // A(external TOC of sym) Global Linkage + XCOFF_R_TCL = 0x06 // A(local TOC of sym) Local object TOC address + XCOFF_R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction + XCOFF_R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction + XCOFF_R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect + XCOFF_R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction + XCOFF_R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction + XCOFF_R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable + XCOFF_R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr + + XCOFF_R_TLS = 0x20 // General-dynamic reference to TLS symbol + XCOFF_R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol + XCOFF_R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol + XCOFF_R_TLS_LE = 0x23 // Local-exec reference to TLS symbol + XCOFF_R_TLSM = 0x24 // Module reference to TLS symbol + XCOFF_R_TLSML = 0x25 // Module reference to local (own) module + + XCOFF_R_TOCU = 0x30 // Relative to TOC - high order bits + XCOFF_R_TOCL = 0x31 // Relative to TOC - low order bits +) + +type XcoffLdStr64 struct { + size uint16 + name string +} + +// xcoffFile is used to build XCOFF file. +type xcoffFile struct { + xfhdr XcoffFileHdr64 + xahdr XcoffAoutHdr64 + sections []*XcoffScnHdr64 + sectText *XcoffScnHdr64 + sectData *XcoffScnHdr64 + sectBss *XcoffScnHdr64 + stringTable xcoffStringTable + sectNameToScnum map[string]int16 + loaderSize uint64 + symtabOffset int64 // offset to the start of symbol table + symbolCount uint32 // number of symbol table records written + symtabSym []xcoffSym // XCOFF symbols for the symbol table + dynLibraries map[string]int // Dynamic libraries in .loader section. The integer represents its import file number (- 1) + loaderSymbols []*xcoffLoaderSymbol // symbols inside .loader symbol table + loaderReloc []*xcoffLoaderReloc // Reloc that must be made inside loader +} + +// Var used by XCOFF Generation algorithms +var ( + xfile xcoffFile +) + +// xcoffStringTable is a XCOFF string table. +type xcoffStringTable struct { + strings []string + stringsLen int +} + +// size returns size of string table t. +func (t *xcoffStringTable) size() int { + // string table starts with 4-byte length at the beginning + return t.stringsLen + 4 +} + +// add adds string str to string table t. +func (t *xcoffStringTable) add(str string) int { + off := t.size() + t.strings = append(t.strings, str) + t.stringsLen += len(str) + 1 // each string will have 0 appended to it + return off +} + +// write writes string table t into the output file. +func (t *xcoffStringTable) write(out *OutBuf) { + out.Write32(uint32(t.size())) + for _, s := range t.strings { + out.WriteString(s) + out.Write8(0) + } +} + +// write writes XCOFF section sect into the output file. +func (sect *XcoffScnHdr64) write(ctxt *Link) { + binary.Write(ctxt.Out, binary.BigEndian, sect) + ctxt.Out.Write32(0) // Add 4 empty bytes at the end to match alignment +} + +// addSection adds section to the XCOFF file f. +func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 { + sect := &XcoffScnHdr64{ + Spaddr: addr, + Svaddr: addr, + Ssize: size, + Sscnptr: fileoff, + Sflags: flags, + } + copy(sect.Sname[:], name) // copy string to [8]byte + f.sections = append(f.sections, sect) + f.sectNameToScnum[name] = int16(len(f.sections)) + return sect +} + +// addDwarfSection adds a dwarf section to the XCOFF file f. +// This function is similar to addSection, but Dwarf section names +// must be modified to conventional names and they are various subtypes. +func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 { + newName, subtype := xcoffGetDwarfSubtype(s.Name) + return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype) +} + +// xcoffGetDwarfSubtype returns the XCOFF name of the DWARF section str +// and its subtype constant. +func xcoffGetDwarfSubtype(str string) (string, uint32) { + switch str { + default: + Exitf("unknown DWARF section name for XCOFF: %s", str) + case ".debug_abbrev": + return ".dwabrev", SSUBTYP_DWABREV + case ".debug_info": + return ".dwinfo", SSUBTYP_DWINFO + case ".debug_frame": + return ".dwframe", SSUBTYP_DWFRAME + case ".debug_line": + return ".dwline", SSUBTYP_DWLINE + case ".debug_loc": + return ".dwloc", SSUBTYP_DWLOC + case ".debug_pubnames": + return ".dwpbnms", SSUBTYP_DWPBNMS + case ".debug_pubtypes": + return ".dwpbtyp", SSUBTYP_DWPBTYP + case ".debug_ranges": + return ".dwrnges", SSUBTYP_DWRNGES + } + // never used + return "", 0 +} + +// getXCOFFscnum returns the XCOFF section number of a Go section. +func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 { + switch sect.Seg { + case &Segtext: + return f.sectNameToScnum[".text"] + case &Segdata: + if sect.Name == ".noptrbss" || sect.Name == ".bss" { + return f.sectNameToScnum[".bss"] + } + if sect.Name == ".tbss" { + return f.sectNameToScnum[".tbss"] + } + return f.sectNameToScnum[".data"] + case &Segdwarf: + name, _ := xcoffGetDwarfSubtype(sect.Name) + return f.sectNameToScnum[name] + case &Segrelrodata: + return f.sectNameToScnum[".data"] + } + Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name) + return -1 +} + +// Xcoffinit initialised some internal value and setups +// already known header information +func Xcoffinit(ctxt *Link) { + xfile.dynLibraries = make(map[string]int) + + HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN)) + if *FlagTextAddr != -1 { + Errorf(nil, "-T not available on AIX") + } + *FlagTextAddr = XCOFFTEXTBASE + int64(HEADR) + if *FlagRound != -1 { + Errorf(nil, "-R not available on AIX") + } + *FlagRound = int(XCOFFSECTALIGN) + +} + +// SYMBOL TABLE + +// type records C_FILE information needed for genasmsym in XCOFF. +type xcoffSymSrcFile struct { + name string + file *XcoffSymEnt64 // Symbol of this C_FILE + csectAux *XcoffAuxCSect64 // Symbol for the current .csect + csectSymNb uint64 // Symbol number for the current .csect + csectSize int64 +} + +var ( + currDwscnoff = make(map[string]uint64) // Needed to create C_DWARF symbols + currSymSrcFile xcoffSymSrcFile + outerSymSize = make(map[string]int64) +) + +// xcoffUpdateOuterSize stores the size of outer symbols in order to have it +// in the symbol table. +func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) { + if size == 0 { + return + } + + switch stype { + default: + Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String()) + case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING: + // Nothing to do + case sym.STYPERELRO: + if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) { + // runtime.types size must be removed, as it's a real symbol. + outerSymSize["typerel.*"] = size - ctxt.Syms.ROLookup("runtime.types", 0).Size + return + } + fallthrough + case sym.STYPE: + if !ctxt.DynlinkingGo() { + // runtime.types size must be removed, as it's a real symbol. + outerSymSize["type.*"] = size - ctxt.Syms.ROLookup("runtime.types", 0).Size + } + case sym.SGOSTRING: + outerSymSize["go.string.*"] = size + case sym.SGOFUNC: + if !ctxt.DynlinkingGo() { + outerSymSize["go.func.*"] = size + } + case sym.SGOFUNCRELRO: + outerSymSize["go.funcrel.*"] = size + case sym.SGCBITS: + outerSymSize["runtime.gcbits.*"] = size + case sym.SITABLINK: + outerSymSize["runtime.itablink"] = size + + } + +} + +// addSymbol writes a symbol or an auxiliary symbol entry on ctxt.out. +func (f *xcoffFile) addSymbol(sym xcoffSym) { + f.symtabSym = append(f.symtabSym, sym) + f.symbolCount++ +} + +// xcoffAlign returns the log base 2 of the symbol's alignment. +func xcoffAlign(x *sym.Symbol, t SymbolType) uint8 { + align := x.Align + if align == 0 { + if t == TextSym { + align = int32(Funcalign) + } else { + align = symalign(x) + } + } + return logBase2(int(align)) +} + +// logBase2 returns the log in base 2 of a. +func logBase2(a int) uint8 { + return uint8(bits.Len(uint(a)) - 1) +} + +// Write symbols needed when a new file appeared: +// - a C_FILE with one auxiliary entry for its name +// - C_DWARF symbols to provide debug information +// - a C_HIDEXT which will be a csect containing all of its functions +// It needs several parameters to create .csect symbols such as its entry point and its section number. +// +// Currently, a new file is in fact a new package. It seems to be OK, but it might change +// in the future. +func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint64, extnum int16) { + /* C_FILE */ + s := &XcoffSymEnt64{ + Noffset: uint32(f.stringTable.add(".file")), + Nsclass: C_FILE, + Nscnum: N_DEBUG, + Ntype: 0, // Go isn't inside predefined language. + Nnumaux: 1, + } + f.addSymbol(s) + currSymSrcFile.file = s + + // Auxiliary entry for file name. + auxf := &XcoffAuxFile64{ + Xoffset: uint32(f.stringTable.add(name)), + Xftype: XFT_FN, + Xauxtype: _AUX_FILE, + } + f.addSymbol(auxf) + + /* Dwarf */ + for _, sect := range Segdwarf.Sections { + var dwsize uint64 + if ctxt.LinkMode == LinkInternal { + // Find the size of this corresponding package DWARF compilation unit. + // This size is set during DWARF generation (see dwarf.go). + dwsize = getDwsectCUSize(sect.Name, name) + // .debug_abbrev is common to all packages and not found with the previous function + if sect.Name == ".debug_abbrev" { + s := ctxt.Syms.ROLookup(sect.Name, 0) + dwsize = uint64(s.Size) + + } + } else { + // There is only one .FILE with external linking. + dwsize = sect.Length + } + + // get XCOFF name + name, _ := xcoffGetDwarfSubtype(sect.Name) + s := &XcoffSymEnt64{ + Nvalue: currDwscnoff[sect.Name], + Noffset: uint32(f.stringTable.add(name)), + Nsclass: C_DWARF, + Nscnum: f.getXCOFFscnum(sect), + Nnumaux: 1, + } + + if currSymSrcFile.csectAux == nil { + // Dwarf relocations need the symbol number of .dw* symbols. + // It doesn't need to know it for each package, one is enough. + // currSymSrcFile.csectAux == nil means first package. + dws := ctxt.Syms.Lookup(sect.Name, 0) + dws.Dynid = int32(f.symbolCount) + + if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal { + // CIE size must be added to the first package. + dwsize += 48 + } + } + + f.addSymbol(s) + + // update the DWARF section offset in this file + if sect.Name != ".debug_abbrev" { + currDwscnoff[sect.Name] += dwsize + } + + // Auxiliary dwarf section + auxd := &XcoffAuxDWARF64{ + Xscnlen: dwsize, + Xauxtype: _AUX_SECT, + } + + f.addSymbol(auxd) + } + + /* .csect */ + // Check if extnum is in text. + // This is temporary and only here to check if this algorithm is correct. + if extnum != 1 { + Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text") + } + + currSymSrcFile.csectSymNb = uint64(f.symbolCount) + + // No offset because no name + s = &XcoffSymEnt64{ + Nvalue: firstEntry, + Nscnum: extnum, + Nsclass: C_HIDEXT, + Ntype: 0, // check visibility ? + Nnumaux: 1, + } + f.addSymbol(s) + + aux := &XcoffAuxCSect64{ + Xsmclas: XMC_PR, + Xsmtyp: XTY_SD | logBase2(Funcalign)<<3, + Xauxtype: _AUX_CSECT, + } + f.addSymbol(aux) + + currSymSrcFile.csectAux = aux + currSymSrcFile.csectSize = 0 +} + +// Update values for the previous package. +// - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1 +// - Xsclen of the csect symbol. +func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) { + // first file + if currSymSrcFile.file == nil { + return + } + + // Update C_FILE + cfile := currSymSrcFile.file + if last { + cfile.Nvalue = 0xFFFFFFFFFFFFFFFF + } else { + cfile.Nvalue = uint64(f.symbolCount) + } + + // update csect scnlen in this auxiliary entry + aux := currSymSrcFile.csectAux + aux.Xscnlenlo = uint32(currSymSrcFile.csectSize & 0xFFFFFFFF) + aux.Xscnlenhi = uint32(currSymSrcFile.csectSize >> 32) +} + +// Write symbol representing a .text function. +// The symbol table is split with C_FILE corresponding to each package +// and not to each source file as it should be. +func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x *sym.Symbol) []xcoffSym { + // New XCOFF symbols which will be written. + syms := []xcoffSym{} + + // Check if a new file is detected. + if strings.Contains(x.Name, "-tramp") || strings.HasPrefix(x.Name, "runtime.text.") { + // Trampoline don't have a FILE so there are considered + // in the current file. + // Same goes for runtime.text.X symbols. + } else if x.File == "" { // Undefined global symbol + // If this happens, the algorithm must be redone. + if currSymSrcFile.name != "" { + Exitf("undefined global symbol found inside another file") + } + } else { + // Current file has changed. New C_FILE, C_DWARF, etc must be generated. + if currSymSrcFile.name != x.File { + if ctxt.LinkMode == LinkInternal { + // update previous file values + xfile.updatePreviousFile(ctxt, false) + currSymSrcFile.name = x.File + f.writeSymbolNewFile(ctxt, x.File, uint64(x.Value), xfile.getXCOFFscnum(x.Sect)) + } else { + // With external linking, ld will crash if there is several + // .FILE and DWARF debugging enable, somewhere during + // the relocation phase. + // Therefore, all packages are merged under a fake .FILE + // "go_functions". + // TODO(aix); remove once ld has been fixed or the triggering + // relocation has been found and fixed. + if currSymSrcFile.name == "" { + currSymSrcFile.name = x.File + f.writeSymbolNewFile(ctxt, "go_functions", uint64(x.Value), xfile.getXCOFFscnum(x.Sect)) + } + } + + } + } + + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(x.Extname())), + Nvalue: uint64(x.Value), + Nscnum: f.getXCOFFscnum(x.Sect), + Ntype: SYM_TYPE_FUNC, + Nnumaux: 2, + } + + if x.Version != 0 || x.Attr.VisibilityHidden() || x.Attr.Local() { + s.Nsclass = C_HIDEXT + } + + x.Dynid = int32(xfile.symbolCount) + syms = append(syms, s) + + // Update current csect size + currSymSrcFile.csectSize += x.Size + + // create auxiliary entries + a2 := &XcoffAuxFcn64{ + Xfsize: uint32(x.Size), + Xlnnoptr: 0, // TODO + Xendndx: xfile.symbolCount + 3, // this symbol + 2 aux entries + Xauxtype: _AUX_FCN, + } + syms = append(syms, a2) + + a4 := &XcoffAuxCSect64{ + Xscnlenlo: uint32(currSymSrcFile.csectSymNb & 0xFFFFFFFF), + Xscnlenhi: uint32(currSymSrcFile.csectSymNb >> 32), + Xsmclas: XMC_PR, // Program Code + Xsmtyp: XTY_LD, // label definition (based on C) + Xauxtype: _AUX_CSECT, + } + a4.Xsmtyp |= uint8(xcoffAlign(x, TextSym) << 3) + + syms = append(syms, a4) + return syms +} + +// put function used by genasmsym to write symbol table +func putaixsym(ctxt *Link, x *sym.Symbol, str string, t SymbolType, addr int64, go_ *sym.Symbol) { + + // All XCOFF symbols generated by this GO symbols + // Can be a symbol entry or a auxiliary entry + syms := []xcoffSym{} + + switch t { + default: + return + + case TextSym: + if x.FuncInfo != nil || strings.Contains(x.Name, "-tramp") || strings.HasPrefix(x.Name, "runtime.text.") { + // Function within a file + syms = xfile.writeSymbolFunc(ctxt, x) + } else { + // Only runtime.text and runtime.etext come through this way + if x.Name != "runtime.text" && x.Name != "runtime.etext" && x.Name != "go.buildid" { + Exitf("putaixsym: unknown text symbol %s", x.Name) + } + s := &XcoffSymEnt64{ + Nsclass: C_HIDEXT, + Noffset: uint32(xfile.stringTable.add(str)), + Nvalue: uint64(x.Value), + Nscnum: xfile.getXCOFFscnum(x.Sect), + Ntype: SYM_TYPE_FUNC, + Nnumaux: 1, + } + x.Dynid = int32(xfile.symbolCount) + syms = append(syms, s) + + size := uint64(x.Size) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + Xsmclas: XMC_PR, + Xsmtyp: XTY_SD, + } + a4.Xsmtyp |= uint8(xcoffAlign(x, TextSym) << 3) + syms = append(syms, a4) + + } + + case DataSym, BSSSym: + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(str)), + Nvalue: uint64(x.Value), + Nscnum: xfile.getXCOFFscnum(x.Sect), + Nnumaux: 1, + } + + if x.Version != 0 || x.Attr.VisibilityHidden() || x.Attr.Local() { + // There is more symbols in the case of a global data + // which are related to the assembly generated + // to access such symbols. + // But as Golang as its own way to check if a symbol is + // global or local (the capital letter), we don't need to + // implement them yet. + s.Nsclass = C_HIDEXT + } + + x.Dynid = int32(xfile.symbolCount) + syms = append(syms, s) + + // Create auxiliary entry + + // Normally, size should be the size of csect containing all + // the data and bss symbols of one file/package. + // However, it's easier to just have a csect for each symbol. + // It might change + size := uint64(x.Size) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + } + + if x.Type >= sym.STYPE && x.Type <= sym.SPCLNTAB { + if ctxt.LinkMode == LinkExternal && strings.HasPrefix(x.Sect.Name, ".data.rel.ro") { + // During external linking, read-only datas with relocation + // must be in .data. + a4.Xsmclas = XMC_RW + } else { + // Read only data + a4.Xsmclas = XMC_RO + } + } else if x.Type == sym.SDATA && strings.HasPrefix(x.Name, "TOC.") && ctxt.LinkMode == LinkExternal { + a4.Xsmclas = XMC_TC + } else if x.Name == "TOC" { + a4.Xsmclas = XMC_TC0 + } else { + a4.Xsmclas = XMC_RW + } + if t == DataSym { + a4.Xsmtyp |= XTY_SD + } else { + a4.Xsmtyp |= XTY_CM + } + + a4.Xsmtyp |= uint8(xcoffAlign(x, t) << 3) + + syms = append(syms, a4) + + case UndefinedSym: + if x.Type != sym.SDYNIMPORT && x.Type != sym.SHOSTOBJ && x.Type != sym.SUNDEFEXT { + return + } + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(str)), + Nnumaux: 1, + } + x.Dynid = int32(xfile.symbolCount) + syms = append(syms, s) + + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xsmclas: XMC_DS, + Xsmtyp: XTY_ER | XTY_IMP, + } + + if x.Name == "__n_pthreads" { + // Currently, all imported symbols made by cgo_import_dynamic are + // syscall functions, except __n_pthreads which is a variable. + // TODO(aix): Find a way to detect variables imported by cgo. + a4.Xsmclas = XMC_RW + } + + syms = append(syms, a4) + + case TLSSym: + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(str)), + Nscnum: xfile.getXCOFFscnum(x.Sect), + Nvalue: uint64(x.Value), + Nnumaux: 1, + } + + x.Dynid = int32(xfile.symbolCount) + syms = append(syms, s) + + size := uint64(x.Size) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xsmclas: XMC_UL, + Xsmtyp: XTY_CM, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + } + + syms = append(syms, a4) + } + + for _, s := range syms { + xfile.addSymbol(s) + } +} + +// Generate XCOFF Symbol table. +// It will be written in out file in Asmbxcoff, because it must be +// at the very end, especially after relocation sections which needs symbols' index. +func (f *xcoffFile) asmaixsym(ctxt *Link) { + // Get correct size for symbols wrapping others symbols like go.string.* + // sym.Size can be used directly as the symbols have already been written. + for name, size := range outerSymSize { + sym := ctxt.Syms.ROLookup(name, 0) + if sym == nil { + Errorf(nil, "unknown outer symbol with name %s", name) + } else { + sym.Size = size + } + } + + genasmsym(ctxt, putaixsym) + xfile.updatePreviousFile(ctxt, true) +} + +func (f *xcoffFile) genDynSym(ctxt *Link) { + var dynsyms []*sym.Symbol + for _, s := range ctxt.Syms.Allsym { + if s.Type != sym.SHOSTOBJ && s.Type != sym.SDYNIMPORT { + continue + } + dynsyms = append(dynsyms, s) + } + + for _, s := range dynsyms { + f.adddynimpsym(ctxt, s) + + if _, ok := f.dynLibraries[s.Dynimplib()]; !ok { + f.dynLibraries[s.Dynimplib()] = len(f.dynLibraries) + } + + } + +} + +// (*xcoffFile)adddynimpsym adds the dynamic symbol "s" to a XCOFF file. +// A new symbol named s.Extname() is created to be the actual dynamic symbol +// in the .loader section and in the symbol table as an External Reference. +// The symbol "s" is transformed to SXCOFFTOC to end up in .data section. +// However, there is no writing protection on those symbols and +// it might need to be added. +// TODO(aix): Handles dynamic symbols without library. +func (f *xcoffFile) adddynimpsym(ctxt *Link, s *sym.Symbol) { + // Check that library name is given. + // Pattern is already checked when compiling. + if ctxt.LinkMode == LinkInternal && s.Dynimplib() == "" { + Errorf(s, "imported symbol must have a given library") + } + + s.Type = sym.SXCOFFTOC + + // Create new dynamic symbol + extsym := ctxt.Syms.Lookup(s.Extname(), 0) + extsym.Type = sym.SDYNIMPORT + extsym.Attr |= sym.AttrReachable + extsym.SetDynimplib(s.Dynimplib()) + extsym.SetExtname(s.Extname()) + extsym.SetDynimpvers(s.Dynimpvers()) + + // Add loader symbol + lds := &xcoffLoaderSymbol{ + sym: extsym, + smtype: XTY_IMP, + smclas: XMC_DS, + } + if s.Name == "__n_pthreads" { + // Currently, all imported symbols made by cgo_import_dynamic are + // syscall functions, except __n_pthreads which is a variable. + // TODO(aix): Find a way to detect variables imported by cgo. + lds.smclas = XMC_RW + } + f.loaderSymbols = append(f.loaderSymbols, lds) + + // Relocation to retrieve the external address + s.AddBytes(make([]byte, 8)) + s.SetAddr(ctxt.Arch, 0, extsym) + +} + +// Xcoffadddynrel adds a dynamic relocation in a XCOFF file. +// This relocation will be made by the loader. +func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool { + if ctxt.LinkMode == LinkExternal { + return true + } + if s.Type <= sym.SPCLNTAB { + Errorf(s, "cannot have a relocation to %s in a text section symbol", r.Sym.Name) + return false + } + + ldr := &xcoffLoaderReloc{ + sym: s, + rel: r, + } + + switch r.Type { + default: + Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", r.Sym.Name, r.Type.String()) + return false + case objabi.R_ADDR: + if s.Type == sym.SXCOFFTOC && r.Sym.Type == sym.SDYNIMPORT { + // Imported symbol relocation + for i, dynsym := range xfile.loaderSymbols { + if dynsym.sym.Name == r.Sym.Name { + ldr.symndx = int32(i + 3) // +3 because of 3 section symbols + break + } + } + } else if s.Type == sym.SDATA { + switch r.Sym.Sect.Seg { + default: + Errorf(s, "unknown segment for .loader relocation with symbol %s", r.Sym.Name) + case &Segtext: + case &Segrodata: + ldr.symndx = 0 // .text + case &Segdata: + if r.Sym.Type == sym.SBSS || r.Sym.Type == sym.SNOPTRBSS { + ldr.symndx = 2 // .bss + } else { + ldr.symndx = 1 // .data + } + + } + + } else { + Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", r.Sym.Name, s.Type, r.Sym.Type) + return false + } + + ldr.rtype = 0x3F<<8 + XCOFF_R_POS + } + + xfile.loaderReloc = append(xfile.loaderReloc, ldr) + return true +} + +func (ctxt *Link) doxcoff() { + if *FlagD { + // All XCOFF files have dynamic symbols because of the syscalls. + Exitf("-d is not available on AIX") + } + + // TOC + toc := ctxt.Syms.Lookup("TOC", 0) + toc.Type = sym.SXCOFFTOC + toc.Attr |= sym.AttrReachable + toc.Attr |= sym.AttrVisibilityHidden + + // Add entry point to .loader symbols. + ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0) + if !ep.Attr.Reachable() { + Exitf("wrong entry point") + } + + xfile.loaderSymbols = append(xfile.loaderSymbols, &xcoffLoaderSymbol{ + sym: ep, + smtype: XTY_ENT | XTY_SD, + smclas: XMC_DS, + }) + + xfile.genDynSym(ctxt) + + for _, s := range ctxt.Syms.Allsym { + if strings.HasPrefix(s.Name, "TOC.") { + s.Type = sym.SXCOFFTOC + } + } + + if ctxt.LinkMode == LinkExternal { + // Change rt0_go name to match name in runtime/cgo:main(). + rt0 := ctxt.Syms.ROLookup("runtime.rt0_go", 0) + ctxt.Syms.Rename(rt0.Name, "runtime_rt0_go", 0, ctxt.Reachparent) + + for _, s := range ctxt.Syms.Allsym { + if !s.Attr.CgoExport() { + continue + } + + name := s.Extname() + if s.Type == sym.STEXT { + // On AIX, a exported function must have two symbols: + // - a .text symbol which must start with a ".". + // - a .data symbol which is a function descriptor. + ctxt.Syms.Rename(s.Name, "."+name, 0, ctxt.Reachparent) + + desc := ctxt.Syms.Lookup(name, 0) + desc.Type = sym.SNOPTRDATA + desc.AddAddr(ctxt.Arch, s) + desc.AddAddr(ctxt.Arch, toc) + desc.AddUint64(ctxt.Arch, 0) + } + } + } +} + +// Loader section +// Currently, this section is created from scratch when assembling the XCOFF file +// according to information retrieved in xfile object. + +// Create loader section and returns its size +func Loaderblk(ctxt *Link, off uint64) { + xfile.writeLdrScn(ctxt, off) +} + +func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) { + var symtab []*XcoffLdSym64 + var strtab []*XcoffLdStr64 + var importtab []*XcoffLdImportFile64 + var reloctab []*XcoffLdRel64 + var dynimpreloc []*XcoffLdRel64 + + // As the string table is updated in any loader subsection, + // its length must be computed at the same time. + stlen := uint32(0) + + // Loader Header + hdr := &XcoffLdHdr64{ + Lversion: 2, + Lsymoff: LDHDRSZ_64, + } + + /* Symbol table */ + for _, s := range f.loaderSymbols { + lds := &XcoffLdSym64{ + Loffset: uint32(stlen + 2), + Lsmtype: s.smtype, + Lsmclas: s.smclas, + } + switch s.smtype { + default: + Errorf(s.sym, "unexpected loader symbol type: 0x%x", s.smtype) + case XTY_ENT | XTY_SD: + lds.Lvalue = uint64(s.sym.Value) + lds.Lscnum = f.getXCOFFscnum(s.sym.Sect) + case XTY_IMP: + lds.Lifile = int32(f.dynLibraries[s.sym.Dynimplib()] + 1) + } + ldstr := &XcoffLdStr64{ + size: uint16(len(s.sym.Name) + 1), // + null terminator + name: s.sym.Name, + } + stlen += uint32(2 + ldstr.size) // 2 = sizeof ldstr.size + symtab = append(symtab, lds) + strtab = append(strtab, ldstr) + + } + + hdr.Lnsyms = int32(len(symtab)) + hdr.Lrldoff = hdr.Lsymoff + uint64(24*hdr.Lnsyms) // 24 = sizeof one symbol + off := hdr.Lrldoff // current offset is the same of reloc offset + + /* Reloc */ + ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0) + ldr := &XcoffLdRel64{ + Lvaddr: uint64(ep.Value), + Lrtype: 0x3F00, + Lrsecnm: f.getXCOFFscnum(ep.Sect), + Lsymndx: 0, + } + off += 16 + reloctab = append(reloctab, ldr) + + off += uint64(16 * len(f.loaderReloc)) + for _, r := range f.loaderReloc { + ldr = &XcoffLdRel64{ + Lvaddr: uint64(r.sym.Value + int64(r.rel.Off)), + Lrtype: r.rtype, + Lsymndx: r.symndx, + } + + if r.sym.Sect != nil { + ldr.Lrsecnm = f.getXCOFFscnum(r.sym.Sect) + } + + reloctab = append(reloctab, ldr) + } + + off += uint64(16 * len(dynimpreloc)) + reloctab = append(reloctab, dynimpreloc...) + + hdr.Lnreloc = int32(len(reloctab)) + hdr.Limpoff = off + + /* Import */ + // Default import: /usr/lib:/lib + ldimpf := &XcoffLdImportFile64{ + Limpidpath: "/usr/lib:/lib", + } + off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter + importtab = append(importtab, ldimpf) + + // The map created by adddynimpsym associates the name to a number + // This number represents the librairie index (- 1) in this import files section + // Therefore, they must be sorted before being put inside the section + libsOrdered := make([]string, len(f.dynLibraries)) + for key, val := range f.dynLibraries { + if libsOrdered[val] != "" { + continue + } + libsOrdered[val] = key + } + + for _, lib := range libsOrdered { + // lib string is defined as base.a/mem.o or path/base.a/mem.o + n := strings.Split(lib, "/") + path := "" + base := n[len(n)-2] + mem := n[len(n)-1] + if len(n) > 2 { + path = lib[:len(lib)-len(base)-len(mem)-2] + + } + ldimpf = &XcoffLdImportFile64{ + Limpidpath: path, + Limpidbase: base, + Limpidmem: mem, + } + off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter + importtab = append(importtab, ldimpf) + } + + hdr.Lnimpid = int32(len(importtab)) + hdr.Listlen = uint32(off - hdr.Limpoff) + hdr.Lstoff = off + hdr.Lstlen = stlen + + /* Writing */ + ctxt.Out.SeekSet(int64(globalOff)) + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, hdr) + + for _, s := range symtab { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s) + + } + for _, r := range reloctab { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, r) + } + for _, f := range importtab { + ctxt.Out.WriteString(f.Limpidpath) + ctxt.Out.Write8(0) + ctxt.Out.WriteString(f.Limpidbase) + ctxt.Out.Write8(0) + ctxt.Out.WriteString(f.Limpidmem) + ctxt.Out.Write8(0) + } + for _, s := range strtab { + ctxt.Out.Write16(s.size) + ctxt.Out.WriteString(s.name) + ctxt.Out.Write8(0) // null terminator + } + + f.loaderSize = off + uint64(stlen) + ctxt.Out.Flush() + + /* again for printing */ + if !*flagA { + return + } + + ctxt.Logf("\n.loader section") + // write in buf + var buf bytes.Buffer + + binary.Write(&buf, ctxt.Arch.ByteOrder, hdr) + for _, s := range symtab { + binary.Write(&buf, ctxt.Arch.ByteOrder, s) + + } + for _, f := range importtab { + buf.WriteString(f.Limpidpath) + buf.WriteByte(0) + buf.WriteString(f.Limpidbase) + buf.WriteByte(0) + buf.WriteString(f.Limpidmem) + buf.WriteByte(0) + } + for _, s := range strtab { + binary.Write(&buf, ctxt.Arch.ByteOrder, s.size) + buf.WriteString(s.name) + buf.WriteByte(0) // null terminator + } + + // Log buffer + ctxt.Logf("\n\t%.8x|", globalOff) + for i, b := range buf.Bytes() { + if i > 0 && i%16 == 0 { + ctxt.Logf("\n\t%.8x|", uint64(globalOff)+uint64(i)) + } + ctxt.Logf(" %.2x", b) + } + ctxt.Logf("\n") + +} + +// XCOFF assembling and writing file + +func (f *xcoffFile) writeFileHeader(ctxt *Link) { + // File header + f.xfhdr.Fmagic = U64_TOCMAGIC + f.xfhdr.Fnscns = uint16(len(f.sections)) + f.xfhdr.Ftimedat = 0 + + if !*FlagS { + f.xfhdr.Fsymptr = uint64(f.symtabOffset) + f.xfhdr.Fnsyms = int32(f.symbolCount) + } + + if ctxt.BuildMode == BuildModeExe && ctxt.LinkMode == LinkInternal { + f.xfhdr.Fopthdr = AOUTHSZ_EXEC64 + f.xfhdr.Fflags = F_EXEC + + // auxiliary header + f.xahdr.Ovstamp = 1 // based on dump -o + f.xahdr.Omagic = 0x10b + copy(f.xahdr.Omodtype[:], "1L") + entry := ctxt.Syms.ROLookup(*flagEntrySymbol, 0) + f.xahdr.Oentry = uint64(entry.Value) + f.xahdr.Osnentry = f.getXCOFFscnum(entry.Sect) + toc := ctxt.Syms.ROLookup("TOC", 0) + f.xahdr.Otoc = uint64(toc.Value) + f.xahdr.Osntoc = f.getXCOFFscnum(toc.Sect) + + f.xahdr.Oalgntext = int16(logBase2(int(Funcalign))) + f.xahdr.Oalgndata = 0x5 + + binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr) + binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr) + } else { + f.xfhdr.Fopthdr = 0 + binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr) + } + +} + +func xcoffwrite(ctxt *Link) { + ctxt.Out.SeekSet(0) + + xfile.writeFileHeader(ctxt) + + for _, sect := range xfile.sections { + sect.write(ctxt) + } +} + +// Generate XCOFF assembly file +func Asmbxcoff(ctxt *Link, fileoff int64) { + xfile.sectNameToScnum = make(map[string]int16) + + // Add sections + s := xfile.addSection(".text", Segtext.Vaddr, Segtext.Length, Segtext.Fileoff, STYP_TEXT) + xfile.xahdr.Otextstart = s.Svaddr + xfile.xahdr.Osntext = xfile.sectNameToScnum[".text"] + xfile.xahdr.Otsize = s.Ssize + xfile.sectText = s + + segdataVaddr := Segdata.Vaddr + segdataFilelen := Segdata.Filelen + segdataFileoff := Segdata.Fileoff + segbssFilelen := Segdata.Length - Segdata.Filelen + if len(Segrelrodata.Sections) > 0 { + // Merge relro segment to data segment as + // relro data are inside data segment on AIX. + segdataVaddr = Segrelrodata.Vaddr + segdataFileoff = Segrelrodata.Fileoff + segdataFilelen = Segdata.Vaddr + Segdata.Filelen - Segrelrodata.Vaddr + } + + s = xfile.addSection(".data", segdataVaddr, segdataFilelen, segdataFileoff, STYP_DATA) + xfile.xahdr.Odatastart = s.Svaddr + xfile.xahdr.Osndata = xfile.sectNameToScnum[".data"] + xfile.xahdr.Odsize = s.Ssize + xfile.sectData = s + + s = xfile.addSection(".bss", segdataVaddr+segdataFilelen, segbssFilelen, 0, STYP_BSS) + xfile.xahdr.Osnbss = xfile.sectNameToScnum[".bss"] + xfile.xahdr.Obsize = s.Ssize + xfile.sectBss = s + + if ctxt.LinkMode == LinkExternal { + var tbss *sym.Section + for _, s := range Segdata.Sections { + if s.Name == ".tbss" { + tbss = s + break + } + } + s = xfile.addSection(".tbss", tbss.Vaddr, tbss.Length, 0, STYP_TBSS) + } + + // add dwarf sections + for _, sect := range Segdwarf.Sections { + xfile.addDwarfSection(sect) + } + + // add and write remaining sections + if ctxt.LinkMode == LinkInternal { + // Loader section + if ctxt.BuildMode == BuildModeExe { + Loaderblk(ctxt, uint64(fileoff)) + s = xfile.addSection(".loader", 0, xfile.loaderSize, uint64(fileoff), STYP_LOADER) + xfile.xahdr.Osnloader = xfile.sectNameToScnum[".loader"] + + // Update fileoff for symbol table + fileoff += int64(xfile.loaderSize) + } + } + + // Create Symbol table + xfile.asmaixsym(ctxt) + + if ctxt.LinkMode == LinkExternal { + xfile.emitRelocations(ctxt, fileoff) + } + + // Write Symbol table + xfile.symtabOffset = ctxt.Out.Offset() + for _, s := range xfile.symtabSym { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s) + } + // write string table + xfile.stringTable.write(ctxt.Out) + + ctxt.Out.Flush() + + // write headers + xcoffwrite(ctxt) +} + +// byOffset is used to sort relocations by offset +type byOffset []sym.Reloc + +func (x byOffset) Len() int { return len(x) } + +func (x byOffset) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x byOffset) Less(i, j int) bool { + return x[i].Off < x[j].Off +} + +// emitRelocations emits relocation entries for go.o in external linking. +func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) { + ctxt.Out.SeekSet(fileoff) + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + // relocsect relocates symbols from first in section sect, and returns + // the total number of relocations emitted. + relocsect := func(sect *sym.Section, syms []*sym.Symbol, base uint64) uint32 { + // ctxt.Logf("%s 0x%x\n", sect.Name, sect.Vaddr) + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return 0 + } + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !s.Attr.Reachable() { + continue + } + if uint64(s.Value) >= sect.Vaddr { + syms = syms[i:] + break + } + } + eaddr := int64(sect.Vaddr + sect.Length) + for _, s := range syms { + if !s.Attr.Reachable() { + continue + } + if s.Value >= int64(eaddr) { + break + } + + // Relocation must be ordered by address, so s.R is ordered by Off. + sort.Sort(byOffset(s.R)) + + for ri := range s.R { + + r := &s.R[ri] + + if r.Done { + continue + } + if r.Xsym == nil { + Errorf(s, "missing xsym in relocation") + continue + } + if r.Xsym.Dynid < 0 { + Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type.String(), r.Sym.Name, r.Xsym.Name, r.Sym.Type, r.Xsym.Dynid) + } + if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, s, r, int64(uint64(s.Value+int64(r.Off))-base)) { + Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type, r.Type.String(), r.Siz, r.Sym.Name) + } + } + } + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff + return uint32(sect.Rellen) / RELSZ_64 + } + sects := []struct { + xcoffSect *XcoffScnHdr64 + segs []*sym.Segment + }{ + {f.sectText, []*sym.Segment{&Segtext}}, + {f.sectData, []*sym.Segment{&Segrelrodata, &Segdata}}, + } + for _, s := range sects { + s.xcoffSect.Srelptr = uint64(ctxt.Out.Offset()) + n := uint32(0) + for _, seg := range s.segs { + for _, sect := range seg.Sections { + if sect.Name == ".text" { + n += relocsect(sect, ctxt.Textp, 0) + } else { + n += relocsect(sect, datap, 0) + } + } + } + s.xcoffSect.Snreloc += n + } + +dwarfLoop: + for _, sect := range Segdwarf.Sections { + for _, xcoffSect := range f.sections { + _, subtyp := xcoffGetDwarfSubtype(sect.Name) + if xcoffSect.Sflags&0xF0000 == subtyp { + xcoffSect.Srelptr = uint64(ctxt.Out.Offset()) + xcoffSect.Snreloc = relocsect(sect, dwarfp, sect.Vaddr) + continue dwarfLoop + } + } + Errorf(nil, "emitRelocations: could not find %q section", sect.Name) + } +} + +// xcoffCreateExportFile creates a file with exported symbols for +// -Wl,-bE option. +// ld won't export symbols unless they are listed in an export file. +func xcoffCreateExportFile(ctxt *Link) (fname string) { + fname = filepath.Join(*flagTmpdir, "export_file.exp") + var buf bytes.Buffer + + for _, s := range ctxt.Syms.Allsym { + if !s.Attr.CgoExport() { + continue + } + if !strings.HasPrefix(s.String(), "_cgoexp_") { + continue + } + + // Retrieve the name of the initial symbol + // exported by cgo. + // The corresponding Go symbol is: + // _cgoexp_hashcode_symname. + name := strings.SplitN(s.Extname(), "_", 4)[3] + + buf.Write([]byte(name + "\n")) + } + + err := ioutil.WriteFile(fname, buf.Bytes(), 0666) + if err != nil { + Errorf(nil, "WriteFile %s failed: %v", fname, err) + } + + return fname + +} diff --git a/src/cmd/oldlink/internal/loadelf/ldelf.go b/src/cmd/oldlink/internal/loadelf/ldelf.go new file mode 100644 index 0000000000..db37db9121 --- /dev/null +++ b/src/cmd/oldlink/internal/loadelf/ldelf.go @@ -0,0 +1,1282 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loadelf implements an ELF file reader. +package loadelf + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/loader" + "cmd/oldlink/internal/sym" + "debug/elf" + "encoding/binary" + "fmt" + "io" + "log" + "sort" + "strings" +) + +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +const ( + ElfClassNone = 0 + ElfClass32 = 1 + ElfClass64 = 2 +) + +const ( + ElfDataNone = 0 + ElfDataLsb = 1 + ElfDataMsb = 2 +) + +const ( + ElfTypeNone = 0 + ElfTypeRelocatable = 1 + ElfTypeExecutable = 2 + ElfTypeSharedObject = 3 + ElfTypeCore = 4 +) + +const ( + ElfMachNone = 0 + ElfMach32100 = 1 + ElfMachSparc = 2 + ElfMach386 = 3 + ElfMach68000 = 4 + ElfMach88000 = 5 + ElfMach486 = 6 + ElfMach860 = 7 + ElfMachMips = 8 + ElfMachS370 = 9 + ElfMachMipsLe = 10 + ElfMachParisc = 15 + ElfMachVpp500 = 17 + ElfMachSparc32Plus = 18 + ElfMach960 = 19 + ElfMachPower = 20 + ElfMachPower64 = 21 + ElfMachS390 = 22 + ElfMachV800 = 36 + ElfMachFr20 = 37 + ElfMachRh32 = 38 + ElfMachRce = 39 + ElfMachArm = 40 + ElfMachAlpha = 41 + ElfMachSH = 42 + ElfMachSparc9 = 43 + ElfMachAmd64 = 62 + ElfMachArm64 = 183 +) + +const ( + ElfAbiNone = 0 + ElfAbiSystemV = 0 + ElfAbiHPUX = 1 + ElfAbiNetBSD = 2 + ElfAbiLinux = 3 + ElfAbiSolaris = 6 + ElfAbiAix = 7 + ElfAbiIrix = 8 + ElfAbiFreeBSD = 9 + ElfAbiTru64 = 10 + ElfAbiModesto = 11 + ElfAbiOpenBSD = 12 + ElfAbiARM = 97 + ElfAbiEmbedded = 255 +) + +const ( + ElfSectNone = 0 + ElfSectProgbits = 1 + ElfSectSymtab = 2 + ElfSectStrtab = 3 + ElfSectRela = 4 + ElfSectHash = 5 + ElfSectDynamic = 6 + ElfSectNote = 7 + ElfSectNobits = 8 + ElfSectRel = 9 + ElfSectShlib = 10 + ElfSectDynsym = 11 + ElfSectFlagWrite = 0x1 + ElfSectFlagAlloc = 0x2 + ElfSectFlagExec = 0x4 +) + +const ( + ElfSymBindLocal = 0 + ElfSymBindGlobal = 1 + ElfSymBindWeak = 2 +) + +const ( + ElfSymTypeNone = 0 + ElfSymTypeObject = 1 + ElfSymTypeFunc = 2 + ElfSymTypeSection = 3 + ElfSymTypeFile = 4 + ElfSymTypeCommon = 5 + ElfSymTypeTLS = 6 +) + +const ( + ElfSymShnNone = 0 + ElfSymShnAbs = 0xFFF1 + ElfSymShnCommon = 0xFFF2 +) + +const ( + ElfProgNone = 0 + ElfProgLoad = 1 + ElfProgDynamic = 2 + ElfProgInterp = 3 + ElfProgNote = 4 + ElfProgShlib = 5 + ElfProgPhdr = 6 + ElfProgFlagExec = 0x1 + ElfProgFlagWrite = 0x2 + ElfProgFlagRead = 0x4 +) + +const ( + ElfNotePrStatus = 1 + ElfNotePrFpreg = 2 + ElfNotePrPsinfo = 3 + ElfNotePrTaskstruct = 4 + ElfNotePrAuxv = 6 + ElfNotePrXfpreg = 0x46e62b7f +) + +// TODO(crawshaw): de-duplicate with cmd/oldlink/internal/ld/elf.go. +const ( + ELF64SYMSIZE = 24 + ELF32SYMSIZE = 16 + + SHT_ARM_ATTRIBUTES = 0x70000003 +) + +type ElfHdrBytes struct { + Ident [16]uint8 + Type [2]uint8 + Machine [2]uint8 + Version [4]uint8 + Entry [4]uint8 + Phoff [4]uint8 + Shoff [4]uint8 + Flags [4]uint8 + Ehsize [2]uint8 + Phentsize [2]uint8 + Phnum [2]uint8 + Shentsize [2]uint8 + Shnum [2]uint8 + Shstrndx [2]uint8 +} + +type ElfSectBytes struct { + Name [4]uint8 + Type [4]uint8 + Flags [4]uint8 + Addr [4]uint8 + Off [4]uint8 + Size [4]uint8 + Link [4]uint8 + Info [4]uint8 + Align [4]uint8 + Entsize [4]uint8 +} + +type ElfProgBytes struct { +} + +type ElfSymBytes struct { + Name [4]uint8 + Value [4]uint8 + Size [4]uint8 + Info uint8 + Other uint8 + Shndx [2]uint8 +} + +type ElfHdrBytes64 struct { + Ident [16]uint8 + Type [2]uint8 + Machine [2]uint8 + Version [4]uint8 + Entry [8]uint8 + Phoff [8]uint8 + Shoff [8]uint8 + Flags [4]uint8 + Ehsize [2]uint8 + Phentsize [2]uint8 + Phnum [2]uint8 + Shentsize [2]uint8 + Shnum [2]uint8 + Shstrndx [2]uint8 +} + +type ElfSectBytes64 struct { + Name [4]uint8 + Type [4]uint8 + Flags [8]uint8 + Addr [8]uint8 + Off [8]uint8 + Size [8]uint8 + Link [4]uint8 + Info [4]uint8 + Align [8]uint8 + Entsize [8]uint8 +} + +type ElfProgBytes64 struct { +} + +type ElfSymBytes64 struct { + Name [4]uint8 + Info uint8 + Other uint8 + Shndx [2]uint8 + Value [8]uint8 + Size [8]uint8 +} + +type ElfSect struct { + name string + nameoff uint32 + type_ uint32 + flags uint64 + addr uint64 + off uint64 + size uint64 + link uint32 + info uint32 + align uint64 + entsize uint64 + base []byte + sym *sym.Symbol +} + +type ElfObj struct { + f *bio.Reader + base int64 // offset in f where ELF begins + length int64 // length of ELF + is64 int + name string + e binary.ByteOrder + sect []ElfSect + nsect uint + nsymtab int + symtab *ElfSect + symstr *ElfSect + type_ uint32 + machine uint32 + version uint32 + entry uint64 + phoff uint64 + shoff uint64 + flags uint32 + ehsize uint32 + phentsize uint32 + phnum uint32 + shentsize uint32 + shnum uint32 + shstrndx uint32 +} + +type ElfSym struct { + name string + value uint64 + size uint64 + bind uint8 + type_ uint8 + other uint8 + shndx uint16 + sym *sym.Symbol +} + +var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'} + +const ( + TagFile = 1 + TagCPUName = 4 + TagCPURawName = 5 + TagCompatibility = 32 + TagNoDefaults = 64 + TagAlsoCompatibleWith = 65 + TagABIVFPArgs = 28 +) + +type elfAttribute struct { + tag uint64 + sval string + ival uint64 +} + +type elfAttributeList struct { + data []byte + err error +} + +func (a *elfAttributeList) string() string { + if a.err != nil { + return "" + } + nul := bytes.IndexByte(a.data, 0) + if nul < 0 { + a.err = io.EOF + return "" + } + s := string(a.data[:nul]) + a.data = a.data[nul+1:] + return s +} + +func (a *elfAttributeList) uleb128() uint64 { + if a.err != nil { + return 0 + } + v, size := binary.Uvarint(a.data) + a.data = a.data[size:] + return v +} + +// Read an elfAttribute from the list following the rules used on ARM systems. +func (a *elfAttributeList) armAttr() elfAttribute { + attr := elfAttribute{tag: a.uleb128()} + switch { + case attr.tag == TagCompatibility: + attr.ival = a.uleb128() + attr.sval = a.string() + + case attr.tag == 64: // Tag_nodefaults has no argument + + case attr.tag == 65: // Tag_also_compatible_with + // Not really, but we don't actually care about this tag. + attr.sval = a.string() + + // Tag with string argument + case attr.tag == TagCPUName || attr.tag == TagCPURawName || (attr.tag >= 32 && attr.tag&1 != 0): + attr.sval = a.string() + + default: // Tag with integer argument + attr.ival = a.uleb128() + } + return attr +} + +func (a *elfAttributeList) done() bool { + if a.err != nil || len(a.data) == 0 { + return true + } + return false +} + +// Look for the attribute that indicates the object uses the hard-float ABI (a +// file-level attribute with tag Tag_VFP_arch and value 1). Unfortunately the +// format used means that we have to parse all of the file-level attributes to +// find the one we are looking for. This format is slightly documented in "ELF +// for the ARM Architecture" but mostly this is derived from reading the source +// to gold and readelf. +func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags uint32, err error) { + found = false + if data[0] != 'A' { + return false, 0, fmt.Errorf(".ARM.attributes has unexpected format %c\n", data[0]) + } + data = data[1:] + for len(data) != 0 { + sectionlength := e.Uint32(data) + sectiondata := data[4:sectionlength] + data = data[sectionlength:] + + nulIndex := bytes.IndexByte(sectiondata, 0) + if nulIndex < 0 { + return false, 0, fmt.Errorf("corrupt .ARM.attributes (section name not NUL-terminated)\n") + } + name := string(sectiondata[:nulIndex]) + sectiondata = sectiondata[nulIndex+1:] + + if name != "aeabi" { + continue + } + for len(sectiondata) != 0 { + subsectiontag, sz := binary.Uvarint(sectiondata) + subsectionsize := e.Uint32(sectiondata[sz:]) + subsectiondata := sectiondata[sz+4 : subsectionsize] + sectiondata = sectiondata[subsectionsize:] + + if subsectiontag != TagFile { + continue + } + attrList := elfAttributeList{data: subsectiondata} + for !attrList.done() { + attr := attrList.armAttr() + if attr.tag == TagABIVFPArgs && attr.ival == 1 { + found = true + ehdrFlags = 0x5000402 // has entry point, Version5 EABI, hard-float ABI + } + } + if attrList.err != nil { + return false, 0, fmt.Errorf("could not parse .ARM.attributes\n") + } + } + } + return found, ehdrFlags, nil +} + +func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) { + newSym := func(name string, version int) *sym.Symbol { + return l.Create(name, syms) + } + lookup := func(name string, version int) *sym.Symbol { + return l.LookupOrCreate(name, version, syms) + } + return load(arch, syms.IncVersion(), newSym, lookup, f, pkg, length, pn, flags) +} + +func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) { + return load(arch, syms.IncVersion(), syms.Newsym, syms.Lookup, f, pkg, length, pn, flags) +} + +type lookupFunc func(string, int) *sym.Symbol + +// load loads the ELF file pn from f. +// Symbols are written into syms, and a slice of the text symbols is returned. +// +// On ARM systems, Load will attempt to determine what ELF header flags to +// emit by scanning the attributes in the ELF file being loaded. The +// parameter initEhdrFlags contains the current header flags for the output +// object, and the returned ehdrFlags contains what this Load function computes. +// TODO: find a better place for this logic. +func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []*sym.Symbol, ehdrFlags uint32, err error) { + errorf := func(str string, args ...interface{}) ([]*sym.Symbol, uint32, error) { + return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...)) + } + + base := f.Offset() + + var hdrbuf [64]uint8 + if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { + return errorf("malformed elf file: %v", err) + } + hdr := new(ElfHdrBytes) + binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter + if string(hdr.Ident[:4]) != "\x7FELF" { + return errorf("malformed elf file, bad header") + } + var e binary.ByteOrder + switch hdr.Ident[5] { + case ElfDataLsb: + e = binary.LittleEndian + + case ElfDataMsb: + e = binary.BigEndian + + default: + return errorf("malformed elf file, unknown header") + } + + // read header + elfobj := new(ElfObj) + + elfobj.e = e + elfobj.f = f + elfobj.base = base + elfobj.length = length + elfobj.name = pn + + is64 := 0 + if hdr.Ident[4] == ElfClass64 { + is64 = 1 + hdr := new(ElfHdrBytes64) + binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter + elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) + elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) + elfobj.version = e.Uint32(hdr.Version[:]) + elfobj.phoff = e.Uint64(hdr.Phoff[:]) + elfobj.shoff = e.Uint64(hdr.Shoff[:]) + elfobj.flags = e.Uint32(hdr.Flags[:]) + elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) + elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) + elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) + elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) + elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) + elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) + } else { + elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) + elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) + elfobj.version = e.Uint32(hdr.Version[:]) + elfobj.entry = uint64(e.Uint32(hdr.Entry[:])) + elfobj.phoff = uint64(e.Uint32(hdr.Phoff[:])) + elfobj.shoff = uint64(e.Uint32(hdr.Shoff[:])) + elfobj.flags = e.Uint32(hdr.Flags[:]) + elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) + elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) + elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) + elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) + elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) + elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) + } + + elfobj.is64 = is64 + + if v := uint32(hdr.Ident[6]); v != elfobj.version { + return errorf("malformed elf version: got %d, want %d", v, elfobj.version) + } + + if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable { + return errorf("elf but not elf relocatable object") + } + + switch arch.Family { + default: + return errorf("elf %s unimplemented", arch.Name) + + case sys.MIPS: + if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass32 { + return errorf("elf object but not mips") + } + + case sys.MIPS64: + if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 { + return errorf("elf object but not mips64") + } + + case sys.ARM: + if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 { + return errorf("elf object but not arm") + } + + case sys.AMD64: + if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 { + return errorf("elf object but not amd64") + } + + case sys.ARM64: + if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 { + return errorf("elf object but not arm64") + } + + case sys.I386: + if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 { + return errorf("elf object but not 386") + } + + case sys.PPC64: + if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 { + return errorf("elf object but not ppc64") + } + + case sys.S390X: + if elfobj.machine != ElfMachS390 || hdr.Ident[4] != ElfClass64 { + return errorf("elf object but not s390x") + } + } + + // load section list into memory. + elfobj.sect = make([]ElfSect, elfobj.shnum) + + elfobj.nsect = uint(elfobj.shnum) + for i := 0; uint(i) < elfobj.nsect; i++ { + f.MustSeek(int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) + sect := &elfobj.sect[i] + if is64 != 0 { + var b ElfSectBytes64 + + if err := binary.Read(f, e, &b); err != nil { + return errorf("malformed elf file: %v", err) + } + + sect.nameoff = e.Uint32(b.Name[:]) + sect.type_ = e.Uint32(b.Type[:]) + sect.flags = e.Uint64(b.Flags[:]) + sect.addr = e.Uint64(b.Addr[:]) + sect.off = e.Uint64(b.Off[:]) + sect.size = e.Uint64(b.Size[:]) + sect.link = e.Uint32(b.Link[:]) + sect.info = e.Uint32(b.Info[:]) + sect.align = e.Uint64(b.Align[:]) + sect.entsize = e.Uint64(b.Entsize[:]) + } else { + var b ElfSectBytes + + if err := binary.Read(f, e, &b); err != nil { + return errorf("malformed elf file: %v", err) + } + + sect.nameoff = e.Uint32(b.Name[:]) + sect.type_ = e.Uint32(b.Type[:]) + sect.flags = uint64(e.Uint32(b.Flags[:])) + sect.addr = uint64(e.Uint32(b.Addr[:])) + sect.off = uint64(e.Uint32(b.Off[:])) + sect.size = uint64(e.Uint32(b.Size[:])) + sect.link = e.Uint32(b.Link[:]) + sect.info = e.Uint32(b.Info[:]) + sect.align = uint64(e.Uint32(b.Align[:])) + sect.entsize = uint64(e.Uint32(b.Entsize[:])) + } + } + + // read section string table and translate names + if elfobj.shstrndx >= uint32(elfobj.nsect) { + return errorf("malformed elf file: shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect) + } + + sect := &elfobj.sect[elfobj.shstrndx] + if err := elfmap(elfobj, sect); err != nil { + return errorf("malformed elf file: %v", err) + } + for i := 0; uint(i) < elfobj.nsect; i++ { + if elfobj.sect[i].nameoff != 0 { + elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:]) + } + } + + // load string table for symbols into memory. + elfobj.symtab = section(elfobj, ".symtab") + + if elfobj.symtab == nil { + // our work is done here - no symbols means nothing can refer to this file + return + } + + if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) { + return errorf("elf object has symbol table with invalid string table link") + } + + elfobj.symstr = &elfobj.sect[elfobj.symtab.link] + if is64 != 0 { + elfobj.nsymtab = int(elfobj.symtab.size / ELF64SYMSIZE) + } else { + elfobj.nsymtab = int(elfobj.symtab.size / ELF32SYMSIZE) + } + + if err := elfmap(elfobj, elfobj.symtab); err != nil { + return errorf("malformed elf file: %v", err) + } + if err := elfmap(elfobj, elfobj.symstr); err != nil { + return errorf("malformed elf file: %v", err) + } + + // load text and data segments into memory. + // they are not as small as the section lists, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + + // create symbols for elfmapped sections + sectsymNames := make(map[string]bool) + counter := 0 + for i := 0; uint(i) < elfobj.nsect; i++ { + sect = &elfobj.sect[i] + if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" { + if err := elfmap(elfobj, sect); err != nil { + return errorf("%s: malformed elf file: %v", pn, err) + } + // We assume the soft-float ABI unless we see a tag indicating otherwise. + if initEhdrFlags == 0x5000002 { + ehdrFlags = 0x5000202 + } else { + ehdrFlags = initEhdrFlags + } + found, newEhdrFlags, err := parseArmAttributes(e, sect.base[:sect.size]) + if err != nil { + // TODO(dfc) should this return an error? + log.Printf("%s: %v", pn, err) + } + if found { + ehdrFlags = newEhdrFlags + } + } + if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 { + continue + } + if sect.type_ != ElfSectNobits { + if err := elfmap(elfobj, sect); err != nil { + return errorf("%s: malformed elf file: %v", pn, err) + } + } + + name := fmt.Sprintf("%s(%s)", pkg, sect.name) + for sectsymNames[name] { + counter++ + name = fmt.Sprintf("%s(%s%d)", pkg, sect.name, counter) + } + sectsymNames[name] = true + + s := lookup(name, localSymVersion) + + switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) { + default: + return errorf("%s: unexpected flags for ELF section %s", pn, sect.name) + + case ElfSectFlagAlloc: + s.Type = sym.SRODATA + + case ElfSectFlagAlloc + ElfSectFlagWrite: + if sect.type_ == ElfSectNobits { + s.Type = sym.SNOPTRBSS + } else { + s.Type = sym.SNOPTRDATA + } + + case ElfSectFlagAlloc + ElfSectFlagExec: + s.Type = sym.STEXT + } + + if sect.name == ".got" || sect.name == ".toc" { + s.Type = sym.SELFGOT + } + if sect.type_ == ElfSectProgbits { + s.P = sect.base + s.P = s.P[:sect.size] + } + + s.Size = int64(sect.size) + s.Align = int32(sect.align) + sect.sym = s + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + symbols := make([]*sym.Symbol, elfobj.nsymtab) + + for i := 1; i < elfobj.nsymtab; i++ { + var elfsym ElfSym + if err := readelfsym(newSym, lookup, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil { + return errorf("%s: malformed elf file: %v", pn, err) + } + symbols[i] = elfsym.sym + if elfsym.type_ != ElfSymTypeFunc && elfsym.type_ != ElfSymTypeObject && elfsym.type_ != ElfSymTypeNone && elfsym.type_ != ElfSymTypeCommon { + continue + } + if elfsym.shndx == ElfSymShnCommon || elfsym.type_ == ElfSymTypeCommon { + s := elfsym.sym + if uint64(s.Size) < elfsym.size { + s.Size = int64(elfsym.size) + } + if s.Type == 0 || s.Type == sym.SXREF { + s.Type = sym.SNOPTRBSS + } + continue + } + + if uint(elfsym.shndx) >= elfobj.nsect || elfsym.shndx == 0 { + continue + } + + // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols + if elfsym.sym == nil { + continue + } + sect = &elfobj.sect[elfsym.shndx] + if sect.sym == nil { + if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this + continue + } + + if elfsym.name == "" && elfsym.type_ == 0 && sect.name == ".debug_str" { + // This reportedly happens with clang 3.7 on ARM. + // See issue 13139. + continue + } + + if strings.HasPrefix(elfsym.name, "$d") && elfsym.type_ == 0 && sect.name == ".debug_frame" { + // "$d" is a marker, not a real symbol. + // This happens with gcc on ARM64. + // See https://sourceware.org/bugzilla/show_bug.cgi?id=21809 + continue + } + + if strings.HasPrefix(elfsym.name, ".LASF") { // gcc on s390x does this + continue + } + return errorf("%v: sym#%d: ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.shndx, elfsym.type_) + } + + s := elfsym.sym + if s.Outer != nil { + if s.Attr.DuplicateOK() { + continue + } + return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name) + } + + s.Sub = sect.sym.Sub + sect.sym.Sub = s + s.Type = sect.sym.Type + s.Attr |= sym.AttrSubSymbol + if !s.Attr.CgoExportDynamic() { + s.SetDynimplib("") // satisfy dynimport + } + s.Value = int64(elfsym.value) + s.Size = int64(elfsym.size) + s.Outer = sect.sym + if sect.sym.Type == sym.STEXT { + if s.Attr.External() && !s.Attr.DuplicateOK() { + return errorf("%v: duplicate symbol definition", s) + } + s.Attr |= sym.AttrExternal + } + + if elfobj.machine == ElfMachPower64 { + flag := int(elfsym.other) >> 5 + if 2 <= flag && flag <= 6 { + s.SetLocalentry(1 << uint(flag-2)) + } else if flag == 7 { + return errorf("%v: invalid sym.other 0x%x", s, elfsym.other) + } + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i := uint(0); i < elfobj.nsect; i++ { + s := elfobj.sect[i].sym + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = sym.SortSub(s.Sub) + } + if s.Type == sym.STEXT { + if s.Attr.OnList() { + return errorf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + textp = append(textp, s) + for s = s.Sub; s != nil; s = s.Sub { + if s.Attr.OnList() { + return errorf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + textp = append(textp, s) + } + } + } + + // load relocations + for i := uint(0); i < elfobj.nsect; i++ { + rsect := &elfobj.sect[i] + if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel { + continue + } + if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil { + continue + } + sect = &elfobj.sect[rsect.info] + if err := elfmap(elfobj, rsect); err != nil { + return errorf("malformed elf file: %v", err) + } + rela := 0 + if rsect.type_ == ElfSectRela { + rela = 1 + } + n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) + r := make([]sym.Reloc, n) + p := rsect.base + for j := 0; j < n; j++ { + var add uint64 + var symIdx int + var relocType uint64 + + rp := &r[j] + if is64 != 0 { + // 64-bit rel/rela + rp.Off = int32(e.Uint64(p)) + + p = p[8:] + switch arch.Family { + case sys.MIPS64: + // https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf + // The doc shows it's different with general Linux ELF + symIdx = int(e.Uint32(p)) + relocType = uint64(p[7]) + default: + info := e.Uint64(p) + relocType = info & 0xffffffff + symIdx = int(info >> 32) + } + p = p[8:] + if rela != 0 { + add = e.Uint64(p) + p = p[8:] + } + } else { + // 32-bit rel/rela + rp.Off = int32(e.Uint32(p)) + + p = p[4:] + info := e.Uint32(p) + relocType = uint64(info & 0xff) + symIdx = int(info >> 8) + p = p[4:] + if rela != 0 { + add = uint64(e.Uint32(p)) + p = p[4:] + } + } + + if relocType == 0 { // skip R_*_NONE relocation + j-- + n-- + continue + } + + if symIdx == 0 { // absolute relocation, don't bother reading the null symbol + rp.Sym = nil + } else { + var elfsym ElfSym + if err := readelfsym(newSym, lookup, arch, elfobj, symIdx, &elfsym, 0, 0); err != nil { + return errorf("malformed elf file: %v", err) + } + elfsym.sym = symbols[symIdx] + if elfsym.sym == nil { + return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, symIdx, elfsym.name, elfsym.shndx, elfsym.type_) + } + + rp.Sym = elfsym.sym + } + + rp.Type = objabi.ElfRelocOffset + objabi.RelocType(relocType) + rp.Siz, err = relSize(arch, pn, uint32(relocType)) + if err != nil { + return nil, 0, err + } + if rela != 0 { + rp.Add = int64(add) + } else { + // load addend from image + if rp.Siz == 4 { + rp.Add = int64(e.Uint32(sect.base[rp.Off:])) + } else if rp.Siz == 8 { + rp.Add = int64(e.Uint64(sect.base[rp.Off:])) + } else { + return errorf("invalid rela size %d", rp.Siz) + } + } + + if rp.Siz == 2 { + rp.Add = int64(int16(rp.Add)) + } + if rp.Siz == 4 { + rp.Add = int64(int32(rp.Add)) + } + } + + //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); + sort.Sort(sym.RelocByOff(r[:n])) + // just in case + + s := sect.sym + s.R = r + s.R = s.R[:n] + } + + return textp, ehdrFlags, nil +} + +func section(elfobj *ElfObj, name string) *ElfSect { + for i := 0; uint(i) < elfobj.nsect; i++ { + if elfobj.sect[i].name != "" && name != "" && elfobj.sect[i].name == name { + return &elfobj.sect[i] + } + } + return nil +} + +func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { + if sect.base != nil { + return nil + } + + if sect.off+sect.size > uint64(elfobj.length) { + err = fmt.Errorf("elf section past end of file") + return err + } + + sect.base = make([]byte, sect.size) + elfobj.f.MustSeek(int64(uint64(elfobj.base)+sect.off), 0) + if _, err := io.ReadFull(elfobj.f, sect.base); err != nil { + return fmt.Errorf("short read: %v", err) + } + + return nil +} + +func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) { + if i >= elfobj.nsymtab || i < 0 { + err = fmt.Errorf("invalid elf symbol index") + return err + } + + if i == 0 { + return fmt.Errorf("readym: read null symbol!") + } + + if elfobj.is64 != 0 { + b := new(ElfSymBytes64) + binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF64SYMSIZE:(i+1)*ELF64SYMSIZE]), elfobj.e, b) + elfsym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):]) + elfsym.value = elfobj.e.Uint64(b.Value[:]) + elfsym.size = elfobj.e.Uint64(b.Size[:]) + elfsym.shndx = elfobj.e.Uint16(b.Shndx[:]) + elfsym.bind = b.Info >> 4 + elfsym.type_ = b.Info & 0xf + elfsym.other = b.Other + } else { + b := new(ElfSymBytes) + binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF32SYMSIZE:(i+1)*ELF32SYMSIZE]), elfobj.e, b) + elfsym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):]) + elfsym.value = uint64(elfobj.e.Uint32(b.Value[:])) + elfsym.size = uint64(elfobj.e.Uint32(b.Size[:])) + elfsym.shndx = elfobj.e.Uint16(b.Shndx[:]) + elfsym.bind = b.Info >> 4 + elfsym.type_ = b.Info & 0xf + elfsym.other = b.Other + } + + var s *sym.Symbol + if elfsym.name == "_GLOBAL_OFFSET_TABLE_" { + elfsym.name = ".got" + } + if elfsym.name == ".TOC." { + // Magic symbol on ppc64. Will be set to this object + // file's .got+0x8000. + elfsym.bind = ElfSymBindLocal + } + + switch elfsym.type_ { + case ElfSymTypeSection: + s = elfobj.sect[elfsym.shndx].sym + + case ElfSymTypeObject, ElfSymTypeFunc, ElfSymTypeNone, ElfSymTypeCommon: + switch elfsym.bind { + case ElfSymBindGlobal: + if needSym != 0 { + s = lookup(elfsym.name, 0) + + // for global scoped hidden symbols we should insert it into + // symbol hash table, but mark them as hidden. + // __i686.get_pc_thunk.bx is allowed to be duplicated, to + // workaround that we set dupok. + // TODO(minux): correctly handle __i686.get_pc_thunk.bx without + // set dupok generally. See https://golang.org/cl/5823055 + // comment #5 for details. + if s != nil && elfsym.other == 2 { + s.Attr |= sym.AttrDuplicateOK | sym.AttrVisibilityHidden + } + } + + case ElfSymBindLocal: + if (arch.Family == sys.ARM || arch.Family == sys.ARM64) && (strings.HasPrefix(elfsym.name, "$a") || strings.HasPrefix(elfsym.name, "$d") || strings.HasPrefix(elfsym.name, "$x")) { + // binutils for arm and arm64 generate these mapping + // symbols, ignore these + break + } + + if elfsym.name == ".TOC." { + // We need to be able to look this up, + // so put it in the hash table. + if needSym != 0 { + s = lookup(elfsym.name, localSymVersion) + s.Attr |= sym.AttrVisibilityHidden + } + + break + } + + if needSym != 0 { + // local names and hidden global names are unique + // and should only be referenced by their index, not name, so we + // don't bother to add them into the hash table + // FIXME: pass empty string here for name? This would + // reduce mem use, but also (possibly) make it harder + // to debug problems. + s = newSym(elfsym.name, localSymVersion) + + s.Attr |= sym.AttrVisibilityHidden + } + + case ElfSymBindWeak: + if needSym != 0 { + s = lookup(elfsym.name, 0) + if elfsym.other == 2 { + s.Attr |= sym.AttrVisibilityHidden + } + + // Allow weak symbols to be duplicated when already defined. + if s.Outer != nil { + s.Attr |= sym.AttrDuplicateOK + } + } + + default: + err = fmt.Errorf("%s: invalid symbol binding %d", elfsym.name, elfsym.bind) + return err + } + } + + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if s != nil && s.Type == 0 && !s.Attr.VisibilityHidden() && elfsym.type_ != ElfSymTypeSection { + s.Type = sym.SXREF + } + elfsym.sym = s + + return nil +} + +func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) { + // TODO(mdempsky): Replace this with a struct-valued switch statement + // once golang.org/issue/15164 is fixed or found to not impair cmd/link + // performance. + + const ( + AMD64 = uint32(sys.AMD64) + ARM = uint32(sys.ARM) + ARM64 = uint32(sys.ARM64) + I386 = uint32(sys.I386) + PPC64 = uint32(sys.PPC64) + S390X = uint32(sys.S390X) + MIPS = uint32(sys.MIPS) + MIPS64 = uint32(sys.MIPS64) + ) + + switch uint32(arch.Family) | elftype<<16 { + default: + return 0, fmt.Errorf("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype) + + case MIPS | uint32(elf.R_MIPS_HI16)<<16, + MIPS | uint32(elf.R_MIPS_LO16)<<16, + MIPS | uint32(elf.R_MIPS_GOT16)<<16, + MIPS | uint32(elf.R_MIPS_GPREL16)<<16, + MIPS | uint32(elf.R_MIPS_GOT_PAGE)<<16, + MIPS | uint32(elf.R_MIPS_JALR)<<16, + MIPS | uint32(elf.R_MIPS_GOT_OFST)<<16, + MIPS64 | uint32(elf.R_MIPS_HI16)<<16, + MIPS64 | uint32(elf.R_MIPS_LO16)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT16)<<16, + MIPS64 | uint32(elf.R_MIPS_GPREL16)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT_PAGE)<<16, + MIPS64 | uint32(elf.R_MIPS_JALR)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT_OFST)<<16: + return 4, nil + + case S390X | uint32(elf.R_390_8)<<16: + return 1, nil + + case PPC64 | uint32(elf.R_PPC64_TOC16)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_LO)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_HI)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_HA)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_DS)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_LO_DS)<<16, + PPC64 | uint32(elf.R_PPC64_REL16_LO)<<16, + PPC64 | uint32(elf.R_PPC64_REL16_HI)<<16, + PPC64 | uint32(elf.R_PPC64_REL16_HA)<<16, + S390X | uint32(elf.R_390_16)<<16, + S390X | uint32(elf.R_390_GOT16)<<16, + S390X | uint32(elf.R_390_PC16)<<16, + S390X | uint32(elf.R_390_PC16DBL)<<16, + S390X | uint32(elf.R_390_PLT16DBL)<<16: + return 2, nil + + case ARM | uint32(elf.R_ARM_ABS32)<<16, + ARM | uint32(elf.R_ARM_GOT32)<<16, + ARM | uint32(elf.R_ARM_PLT32)<<16, + ARM | uint32(elf.R_ARM_GOTOFF)<<16, + ARM | uint32(elf.R_ARM_GOTPC)<<16, + ARM | uint32(elf.R_ARM_THM_PC22)<<16, + ARM | uint32(elf.R_ARM_REL32)<<16, + ARM | uint32(elf.R_ARM_CALL)<<16, + ARM | uint32(elf.R_ARM_V4BX)<<16, + ARM | uint32(elf.R_ARM_GOT_PREL)<<16, + ARM | uint32(elf.R_ARM_PC24)<<16, + ARM | uint32(elf.R_ARM_JUMP24)<<16, + ARM64 | uint32(elf.R_AARCH64_CALL26)<<16, + ARM64 | uint32(elf.R_AARCH64_ADR_GOT_PAGE)<<16, + ARM64 | uint32(elf.R_AARCH64_LD64_GOT_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_ADR_PREL_PG_HI21)<<16, + ARM64 | uint32(elf.R_AARCH64_ADD_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST8_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST32_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST64_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST128_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_PREL32)<<16, + ARM64 | uint32(elf.R_AARCH64_JUMP26)<<16, + AMD64 | uint32(elf.R_X86_64_PC32)<<16, + AMD64 | uint32(elf.R_X86_64_PLT32)<<16, + AMD64 | uint32(elf.R_X86_64_GOTPCREL)<<16, + AMD64 | uint32(elf.R_X86_64_GOTPCRELX)<<16, + AMD64 | uint32(elf.R_X86_64_REX_GOTPCRELX)<<16, + I386 | uint32(elf.R_386_32)<<16, + I386 | uint32(elf.R_386_PC32)<<16, + I386 | uint32(elf.R_386_GOT32)<<16, + I386 | uint32(elf.R_386_PLT32)<<16, + I386 | uint32(elf.R_386_GOTOFF)<<16, + I386 | uint32(elf.R_386_GOTPC)<<16, + I386 | uint32(elf.R_386_GOT32X)<<16, + PPC64 | uint32(elf.R_PPC64_REL24)<<16, + PPC64 | uint32(elf.R_PPC_REL32)<<16, + S390X | uint32(elf.R_390_32)<<16, + S390X | uint32(elf.R_390_PC32)<<16, + S390X | uint32(elf.R_390_GOT32)<<16, + S390X | uint32(elf.R_390_PLT32)<<16, + S390X | uint32(elf.R_390_PC32DBL)<<16, + S390X | uint32(elf.R_390_PLT32DBL)<<16, + S390X | uint32(elf.R_390_GOTPCDBL)<<16, + S390X | uint32(elf.R_390_GOTENT)<<16: + return 4, nil + + case AMD64 | uint32(elf.R_X86_64_64)<<16, + AMD64 | uint32(elf.R_X86_64_PC64)<<16, + ARM64 | uint32(elf.R_AARCH64_ABS64)<<16, + ARM64 | uint32(elf.R_AARCH64_PREL64)<<16, + PPC64 | uint32(elf.R_PPC64_ADDR64)<<16, + S390X | uint32(elf.R_390_GLOB_DAT)<<16, + S390X | uint32(elf.R_390_RELATIVE)<<16, + S390X | uint32(elf.R_390_GOTOFF)<<16, + S390X | uint32(elf.R_390_GOTPC)<<16, + S390X | uint32(elf.R_390_64)<<16, + S390X | uint32(elf.R_390_PC64)<<16, + S390X | uint32(elf.R_390_GOT64)<<16, + S390X | uint32(elf.R_390_PLT64)<<16: + return 8, nil + } +} + +func cstring(x []byte) string { + i := bytes.IndexByte(x, '\x00') + if i >= 0 { + x = x[:i] + } + return string(x) +} diff --git a/src/cmd/oldlink/internal/loader/loader.go b/src/cmd/oldlink/internal/loader/loader.go new file mode 100644 index 0000000000..8c618bfe4d --- /dev/null +++ b/src/cmd/oldlink/internal/loader/loader.go @@ -0,0 +1,629 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loader + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/dwarf" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/sym" + "fmt" + "log" + "sort" + "strconv" + "strings" +) + +var _ = fmt.Print + +// Sym encapsulates a global symbol index, used to identify a specific +// Go symbol. The 0-valued Sym is corresponds to an invalid symbol. +type Sym int + +// Relocs encapsulates the set of relocations on a given symbol; an +// instance of this type is returned by the Loader Relocs() method. +type Relocs struct { + Count int // number of relocs + + li int // local index of symbol whose relocs we're examining + r *oReader // object reader for containing package + l *Loader // loader + + ext *sym.Symbol // external symbol if not nil +} + +// Reloc contains the payload for a specific relocation. +// TODO: replace this with sym.Reloc, once we change the +// relocation target from "*sym.Symbol" to "loader.Sym" in sym.Reloc. +type Reloc struct { + Off int32 // offset to rewrite + Size uint8 // number of bytes to rewrite: 0, 1, 2, or 4 + Type objabi.RelocType // the relocation type + Add int64 // addend + Sym Sym // global index of symbol the reloc addresses +} + +// oReader is a wrapper type of obj.Reader, along with some +// extra information. +// TODO: rename to objReader once the old one is gone? +type oReader struct { + //*goobj2.Reader + unit *sym.CompilationUnit + version int // version of static symbol + flags uint32 // read from object file + pkgprefix string + rcache []Sym // cache mapping local PkgNone symbol to resolved Sym +} + +type objIdx struct { + r *oReader + i Sym // start index + e Sym // end index +} + +type nameVer struct { + name string + v int +} + +type bitmap []uint32 + +// set the i-th bit. +func (bm bitmap) Set(i Sym) { + n, r := uint(i)/32, uint(i)%32 + bm[n] |= 1 << r +} + +// whether the i-th bit is set. +func (bm bitmap) Has(i Sym) bool { + n, r := uint(i)/32, uint(i)%32 + return bm[n]&(1<<r) != 0 +} + +func makeBitmap(n int) bitmap { + return make(bitmap, (n+31)/32) +} + +// A Loader loads new object files and resolves indexed symbol references. +type Loader struct { + start map[*oReader]Sym // map from object file to its start index + objs []objIdx // sorted by start index (i.e. objIdx.i) + max Sym // current max index + extStart Sym // from this index on, the symbols are externally defined + extSyms []nameVer // externally defined symbols + builtinSyms []Sym // global index of builtin symbols + ocache int // index (into 'objs') of most recent lookup + + symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal + extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name + overwrite map[Sym]Sym // overwrite[i]=j if symbol j overwrites symbol i + + itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.* + + objByPkg map[string]*oReader // map package path to its Go object reader + + Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now. + + anonVersion int // most recently assigned ext static sym pseudo-version + + Reachable bitmap // bitmap of reachable symbols, indexed by global index + + // Used to implement field tracking; created during deadcode if + // field tracking is enabled. Reachparent[K] contains the index of + // the symbol that triggered the marking of symbol K as live. + Reachparent []Sym + + relocBatch []sym.Reloc // for bulk allocation of relocations + + flags uint32 + + strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled +} + +const ( + // Loader.flags + FlagStrictDups = 1 << iota +) + +func NewLoader(flags uint32) *Loader { + log.Fatal("-newobj in oldlink should not be used") + panic("unreachable") +} + +// Return the start index in the global index space for a given object file. +func (l *Loader) startIndex(r *oReader) Sym { + return l.start[r] +} + +// Add a symbol with a given index, return if it is added. +func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ sym.SymKind) bool { + panic("unreachable") +} + +// Add an external symbol (without index). Return the index of newly added +// symbol, or 0 if not added. +func (l *Loader) AddExtSym(name string, ver int) Sym { + static := ver >= sym.SymVerStatic + if static { + if _, ok := l.extStaticSyms[nameVer{name, ver}]; ok { + return 0 + } + } else { + if _, ok := l.symsByName[ver][name]; ok { + return 0 + } + } + i := l.max + 1 + if static { + l.extStaticSyms[nameVer{name, ver}] = i + } else { + l.symsByName[ver][name] = i + } + l.max++ + if l.extStart == 0 { + l.extStart = i + } + l.extSyms = append(l.extSyms, nameVer{name, ver}) + l.growSyms(int(i)) + return i +} + +func (l *Loader) IsExternal(i Sym) bool { + return l.extStart != 0 && i >= l.extStart +} + +// Ensure Syms slice has enough space. +func (l *Loader) growSyms(i int) { + n := len(l.Syms) + if n > i { + return + } + l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...) +} + +// Convert a local index to a global index. +func (l *Loader) toGlobal(r *oReader, i int) Sym { + g := l.startIndex(r) + Sym(i) + if ov, ok := l.overwrite[g]; ok { + return ov + } + return g +} + +// Convert a global index to a local index. +func (l *Loader) toLocal(i Sym) (*oReader, int) { + if ov, ok := l.overwrite[i]; ok { + i = ov + } + if l.IsExternal(i) { + return nil, int(i - l.extStart) + } + oc := l.ocache + if oc != 0 && i >= l.objs[oc].i && i <= l.objs[oc].e { + return l.objs[oc].r, int(i - l.objs[oc].i) + } + // Search for the local object holding index i. + // Below k is the first one that has its start index > i, + // so k-1 is the one we want. + k := sort.Search(len(l.objs), func(k int) bool { + return l.objs[k].i > i + }) + l.ocache = k - 1 + return l.objs[k-1].r, int(i - l.objs[k-1].i) +} + +// Look up a symbol by name, return global index, or 0 if not found. +// This is more like Syms.ROLookup than Lookup -- it doesn't create +// new symbol. +func (l *Loader) Lookup(name string, ver int) Sym { + if ver >= sym.SymVerStatic || ver < 0 { + return l.extStaticSyms[nameVer{name, ver}] + } + return l.symsByName[ver][name] +} + +// Returns whether i is a dup of another symbol, and i is not +// "primary", i.e. Lookup i by name will not return i. +func (l *Loader) IsDup(i Sym) bool { + panic("unreachable") +} + +// Check that duplicate symbols have same contents. +func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) { + panic("unreachable") +} + +func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs } + +// Number of total symbols. +func (l *Loader) NSym() int { + return int(l.max + 1) +} + +// Number of defined Go symbols. +func (l *Loader) NDef() int { + return int(l.extStart) +} + +// Returns the raw (unpatched) name of the i-th symbol. +func (l *Loader) RawSymName(i Sym) string { + panic("unreachable") +} + +// Returns the (patched) name of the i-th symbol. +func (l *Loader) SymName(i Sym) string { + panic("unreachable") +} + +// Returns the type of the i-th symbol. +func (l *Loader) SymType(i Sym) sym.SymKind { + panic("unreachable") +} + +// Returns the attributes of the i-th symbol. +func (l *Loader) SymAttr(i Sym) uint8 { + panic("unreachable") +} + +// Returns whether the i-th symbol has ReflectMethod attribute set. +func (l *Loader) IsReflectMethod(i Sym) bool { + panic("unreachable") +} + +// Returns whether this is a Go type symbol. +func (l *Loader) IsGoType(i Sym) bool { + panic("unreachable") +} + +// Returns whether this is a "go.itablink.*" symbol. +func (l *Loader) IsItabLink(i Sym) bool { + if _, ok := l.itablink[i]; ok { + return true + } + return false +} + +// Returns the symbol content of the i-th symbol. i is global index. +func (l *Loader) Data(i Sym) []byte { + panic("unreachable") +} + +// Returns the number of aux symbols given a global index. +func (l *Loader) NAux(i Sym) int { + panic("unreachable") +} + +// Returns the referred symbol of the j-th aux symbol of the i-th +// symbol. +func (l *Loader) AuxSym(i Sym, j int) Sym { + panic("unreachable") +} + +// ReadAuxSyms reads the aux symbol ids for the specified symbol into the +// slice passed as a parameter. If the slice capacity is not large enough, a new +// larger slice will be allocated. Final slice is returned. +func (l *Loader) ReadAuxSyms(symIdx Sym, dst []Sym) []Sym { + panic("unreachable") +} + +// OuterSym gets the outer symbol for host object loaded symbols. +func (l *Loader) OuterSym(i Sym) Sym { + sym := l.Syms[i] + if sym != nil && sym.Outer != nil { + outer := sym.Outer + return l.Lookup(outer.Name, int(outer.Version)) + } + return 0 +} + +// SubSym gets the subsymbol for host object loaded symbols. +func (l *Loader) SubSym(i Sym) Sym { + sym := l.Syms[i] + if sym != nil && sym.Sub != nil { + sub := sym.Sub + return l.Lookup(sub.Name, int(sub.Version)) + } + return 0 +} + +// Initialize Reachable bitmap for running deadcode pass. +func (l *Loader) InitReachable() { + l.Reachable = makeBitmap(l.NSym()) +} + +// At method returns the j-th reloc for a global symbol. +func (relocs *Relocs) At(j int) Reloc { + panic("unreachable") +} + +// ReadAll method reads all relocations for a symbol into the +// specified slice. If the slice capacity is not large enough, a new +// larger slice will be allocated. Final slice is returned. +func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc { + panic("unreachable") +} + +// Relocs returns a Relocs object for the given global sym. +func (l *Loader) Relocs(i Sym) Relocs { + panic("unreachable") +} + +// Preload a package: add autolibs, add symbols to the symbol table. +// Does not read symbol data yet. +func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) { + panic("unreachable") +} + +// Make sure referenced symbols are added. Most of them should already be added. +// This should only be needed for referenced external symbols. +func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) { + for _, o := range l.objs[1:] { + loadObjRefs(l, o.r, arch, syms) + } +} + +func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) { + panic("unreachable") +} + +func abiToVer(abi uint16, localSymVersion int) int { + panic("unreachable") +} + +func preprocess(arch *sys.Arch, s *sym.Symbol) { + if s.Name != "" && s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 { + x, err := strconv.ParseUint(s.Name[5:], 16, 64) + if err != nil { + log.Panicf("failed to parse $-symbol %s: %v", s.Name, err) + } + s.Type = sym.SRODATA + s.Attr |= sym.AttrLocal + switch s.Name[:5] { + case "$f32.": + if uint64(uint32(x)) != x { + log.Panicf("$-symbol %s too large: %d", s.Name, x) + } + s.AddUint32(arch, uint32(x)) + case "$f64.", "$i64.": + s.AddUint64(arch, x) + default: + log.Panicf("unrecognized $-symbol: %s", s.Name) + } + } +} + +// Load full contents. +func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { + // create all Symbols first. + l.growSyms(l.NSym()) + + nr := 0 // total number of sym.Reloc's we'll need + for _, o := range l.objs[1:] { + nr += loadObjSyms(l, syms, o.r) + } + + // allocate a single large slab of relocations for all live symbols + l.relocBatch = make([]sym.Reloc, nr) + + // external symbols + for i := l.extStart; i <= l.max; i++ { + if s := l.Syms[i]; s != nil { + s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i)) + continue // already loaded from external object + } + nv := l.extSyms[i-l.extStart] + if l.Reachable.Has(i) || strings.HasPrefix(nv.name, "gofile..") { // XXX file symbols are used but not marked + s := syms.Newsym(nv.name, nv.v) + preprocess(arch, s) + s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i)) + l.Syms[i] = s + } + } + + // load contents of defined symbols + for _, o := range l.objs[1:] { + loadObjFull(l, o.r) + } + + // Resolve ABI aliases for external symbols. This is only + // needed for internal cgo linking. + // (The old code does this in deadcode, but deadcode2 doesn't + // do this.) + for i := l.extStart; i <= l.max; i++ { + if s := l.Syms[i]; s != nil && s.Attr.Reachable() { + for ri := range s.R { + r := &s.R[ri] + if r.Sym != nil && r.Sym.Type == sym.SABIALIAS { + r.Sym = r.Sym.R[0].Sym + } + } + } + } +} + +// ExtractSymbols grabs the symbols out of the loader for work that hasn't been +// ported to the new symbol type. +func (l *Loader) ExtractSymbols(syms *sym.Symbols) { + // Nil out overwritten symbols. + // Overwritten Go symbols aren't a problem (as they're lazy loaded), but + // symbols loaded from host object loaders are fully loaded, and we might + // have multiple symbols with the same name. This loop nils them out. + for oldI := range l.overwrite { + l.Syms[oldI] = nil + } + + // Add symbols to the ctxt.Syms lookup table. This explicitly + // skips things created via loader.Create (marked with versions + // less than zero), since if we tried to add these we'd wind up + // with collisions. Along the way, update the version from the + // negative anon version to something larger than sym.SymVerStatic + // (needed so that sym.symbol.IsFileLocal() works properly). + anonVerReplacement := syms.IncVersion() + for _, s := range l.Syms { + if s == nil { + continue + } + if s.Name != "" && s.Version >= 0 { + syms.Add(s) + } + if s.Version < 0 { + s.Version = int16(anonVerReplacement) + } + } +} + +// addNewSym adds a new sym.Symbol to the i-th index in the list of symbols. +func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol { + s := syms.Newsym(name, ver) + if s.Type != 0 && s.Type != sym.SXREF { + fmt.Println("symbol already processed:", unit.Lib, i, s) + panic("symbol already processed") + } + if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) { + t = s.Type + } + s.Type = t + s.Unit = unit + l.growSyms(int(i)) + l.Syms[i] = s + return s +} + +// loadObjSyms creates sym.Symbol objects for the live Syms in the +// object corresponding to object reader "r". Return value is the +// number of sym.Reloc entries required for all the new symbols. +func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int { + panic("unreachable") +} + +// LoadSymbol loads a single symbol by name. +// This function should only be used by the host object loaders. +// NB: This function does NOT set the symbol as reachable. +func (l *Loader) LoadSymbol(name string, version int, syms *sym.Symbols) *sym.Symbol { + panic("unreachable") +} + +// LookupOrCreate looks up a symbol by name, and creates one if not found. +// Either way, it will also create a sym.Symbol for it, if not already. +// This should only be called when interacting with parts of the linker +// that still works on sym.Symbols (i.e. internal cgo linking, for now). +func (l *Loader) LookupOrCreate(name string, version int, syms *sym.Symbols) *sym.Symbol { + i := l.Lookup(name, version) + if i != 0 { + // symbol exists + if int(i) < len(l.Syms) && l.Syms[i] != nil { + return l.Syms[i] // already loaded + } + if l.IsExternal(i) { + panic("Can't load an external symbol.") + } + return l.LoadSymbol(name, version, syms) + } + i = l.AddExtSym(name, version) + s := syms.Newsym(name, version) + l.Syms[i] = s + return s +} + +// Create creates a symbol with the specified name, returning a +// sym.Symbol object for it. This method is intended for static/hidden +// symbols discovered while loading host objects. We can see more than +// one instance of a given static symbol with the same name/version, +// so we can't add them to the lookup tables "as is". Instead assign +// them fictitious (unique) versions, starting at -1 and decreasing by +// one for each newly created symbol, and record them in the +// extStaticSyms hash. +func (l *Loader) Create(name string, syms *sym.Symbols) *sym.Symbol { + i := l.max + 1 + l.max++ + if l.extStart == 0 { + l.extStart = i + } + + // Assign a new unique negative version -- this is to mark the + // symbol so that it can be skipped when ExtractSymbols is adding + // ext syms to the sym.Symbols hash. + l.anonVersion-- + ver := l.anonVersion + l.extSyms = append(l.extSyms, nameVer{name, ver}) + l.growSyms(int(i)) + s := syms.Newsym(name, ver) + l.Syms[i] = s + l.extStaticSyms[nameVer{name, ver}] = i + + return s +} + +func loadObjFull(l *Loader, r *oReader) { + panic("unreachable") +} + +var emptyPkg = []byte(`"".`) + +func patchDWARFName1(p []byte, r *oReader) ([]byte, int) { + // This is kind of ugly. Really the package name should not + // even be included here. + if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION { + return p, -1 + } + e := bytes.IndexByte(p, 0) + if e == -1 { + return p, -1 + } + if !bytes.Contains(p[:e], emptyPkg) { + return p, -1 + } + pkgprefix := []byte(r.pkgprefix) + patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1) + return append(patched, p[e:]...), e +} + +func patchDWARFName(s *sym.Symbol, r *oReader) { + patched, e := patchDWARFName1(s.P, r) + if e == -1 { + return + } + s.P = patched + s.Attr.Set(sym.AttrReadOnly, false) + delta := int64(len(s.P)) - s.Size + s.Size = int64(len(s.P)) + for i := range s.R { + r := &s.R[i] + if r.Off > int32(e) { + r.Off += int32(delta) + } + } +} + +// For debugging. +func (l *Loader) Dump() { + fmt.Println("objs") + for _, obj := range l.objs { + if obj.r != nil { + fmt.Println(obj.i, obj.r.unit.Lib) + } + } + fmt.Println("syms") + for i, s := range l.Syms { + if i == 0 { + continue + } + if s != nil { + fmt.Println(i, s, s.Type) + } else { + fmt.Println(i, l.SymName(Sym(i)), "<not loaded>") + } + } + fmt.Println("overwrite:", l.overwrite) + fmt.Println("symsByName") + for name, i := range l.symsByName[0] { + fmt.Println(i, name, 0) + } + for name, i := range l.symsByName[1] { + fmt.Println(i, name, 1) + } +} diff --git a/src/cmd/oldlink/internal/loadmacho/ldmacho.go b/src/cmd/oldlink/internal/loadmacho/ldmacho.go new file mode 100644 index 0000000000..8982180b9c --- /dev/null +++ b/src/cmd/oldlink/internal/loadmacho/ldmacho.go @@ -0,0 +1,891 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loadmacho implements a Mach-O file reader. +package loadmacho + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/loader" + "cmd/oldlink/internal/sym" + "encoding/binary" + "fmt" + "io" + "sort" +) + +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld +const ( + MACHO_X86_64_RELOC_UNSIGNED = 0 + MACHO_X86_64_RELOC_SIGNED = 1 + MACHO_FAKE_GOTPCREL = 100 +) + +type ldMachoObj struct { + f *bio.Reader + base int64 // off in f where Mach-O begins + length int64 // length of Mach-O + is64 bool + name string + e binary.ByteOrder + cputype uint + subcputype uint + filetype uint32 + flags uint32 + cmd []ldMachoCmd + ncmd uint +} + +type ldMachoCmd struct { + type_ int + off uint32 + size uint32 + seg ldMachoSeg + sym ldMachoSymtab + dsym ldMachoDysymtab +} + +type ldMachoSeg struct { + name string + vmaddr uint64 + vmsize uint64 + fileoff uint32 + filesz uint32 + maxprot uint32 + initprot uint32 + nsect uint32 + flags uint32 + sect []ldMachoSect +} + +type ldMachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloff uint32 + nreloc uint32 + flags uint32 + res1 uint32 + res2 uint32 + sym *sym.Symbol + rel []ldMachoRel +} + +type ldMachoRel struct { + addr uint32 + symnum uint32 + pcrel uint8 + length uint8 + extrn uint8 + type_ uint8 + scattered uint8 + value uint32 +} + +type ldMachoSymtab struct { + symoff uint32 + nsym uint32 + stroff uint32 + strsize uint32 + str []byte + sym []ldMachoSym +} + +type ldMachoSym struct { + name string + type_ uint8 + sectnum uint8 + desc uint16 + kind int8 + value uint64 + sym *sym.Symbol +} + +type ldMachoDysymtab struct { + ilocalsym uint32 + nlocalsym uint32 + iextdefsym uint32 + nextdefsym uint32 + iundefsym uint32 + nundefsym uint32 + tocoff uint32 + ntoc uint32 + modtaboff uint32 + nmodtab uint32 + extrefsymoff uint32 + nextrefsyms uint32 + indirectsymoff uint32 + nindirectsyms uint32 + extreloff uint32 + nextrel uint32 + locreloff uint32 + nlocrel uint32 + indir []uint32 +} + +// ldMachoSym.type_ +const ( + N_EXT = 0x01 + N_TYPE = 0x1e + N_STAB = 0xe0 +) + +// ldMachoSym.desc +const ( + N_WEAK_REF = 0x40 + N_WEAK_DEF = 0x80 +) + +const ( + LdMachoCpuVax = 1 + LdMachoCpu68000 = 6 + LdMachoCpu386 = 7 + LdMachoCpuAmd64 = 0x1000007 + LdMachoCpuMips = 8 + LdMachoCpu98000 = 10 + LdMachoCpuHppa = 11 + LdMachoCpuArm = 12 + LdMachoCpu88000 = 13 + LdMachoCpuSparc = 14 + LdMachoCpu860 = 15 + LdMachoCpuAlpha = 16 + LdMachoCpuPower = 18 + LdMachoCmdSegment = 1 + LdMachoCmdSymtab = 2 + LdMachoCmdSymseg = 3 + LdMachoCmdThread = 4 + LdMachoCmdDysymtab = 11 + LdMachoCmdSegment64 = 25 + LdMachoFileObject = 1 + LdMachoFileExecutable = 2 + LdMachoFileFvmlib = 3 + LdMachoFileCore = 4 + LdMachoFilePreload = 5 +) + +func unpackcmd(p []byte, m *ldMachoObj, c *ldMachoCmd, type_ uint, sz uint) int { + e4 := m.e.Uint32 + e8 := m.e.Uint64 + + c.type_ = int(type_) + c.size = uint32(sz) + switch type_ { + default: + return -1 + + case LdMachoCmdSegment: + if sz < 56 { + return -1 + } + c.seg.name = cstring(p[8:24]) + c.seg.vmaddr = uint64(e4(p[24:])) + c.seg.vmsize = uint64(e4(p[28:])) + c.seg.fileoff = e4(p[32:]) + c.seg.filesz = e4(p[36:]) + c.seg.maxprot = e4(p[40:]) + c.seg.initprot = e4(p[44:]) + c.seg.nsect = e4(p[48:]) + c.seg.flags = e4(p[52:]) + c.seg.sect = make([]ldMachoSect, c.seg.nsect) + if uint32(sz) < 56+c.seg.nsect*68 { + return -1 + } + p = p[56:] + var s *ldMachoSect + for i := 0; uint32(i) < c.seg.nsect; i++ { + s = &c.seg.sect[i] + s.name = cstring(p[0:16]) + s.segname = cstring(p[16:32]) + s.addr = uint64(e4(p[32:])) + s.size = uint64(e4(p[36:])) + s.off = e4(p[40:]) + s.align = e4(p[44:]) + s.reloff = e4(p[48:]) + s.nreloc = e4(p[52:]) + s.flags = e4(p[56:]) + s.res1 = e4(p[60:]) + s.res2 = e4(p[64:]) + p = p[68:] + } + + case LdMachoCmdSegment64: + if sz < 72 { + return -1 + } + c.seg.name = cstring(p[8:24]) + c.seg.vmaddr = e8(p[24:]) + c.seg.vmsize = e8(p[32:]) + c.seg.fileoff = uint32(e8(p[40:])) + c.seg.filesz = uint32(e8(p[48:])) + c.seg.maxprot = e4(p[56:]) + c.seg.initprot = e4(p[60:]) + c.seg.nsect = e4(p[64:]) + c.seg.flags = e4(p[68:]) + c.seg.sect = make([]ldMachoSect, c.seg.nsect) + if uint32(sz) < 72+c.seg.nsect*80 { + return -1 + } + p = p[72:] + var s *ldMachoSect + for i := 0; uint32(i) < c.seg.nsect; i++ { + s = &c.seg.sect[i] + s.name = cstring(p[0:16]) + s.segname = cstring(p[16:32]) + s.addr = e8(p[32:]) + s.size = e8(p[40:]) + s.off = e4(p[48:]) + s.align = e4(p[52:]) + s.reloff = e4(p[56:]) + s.nreloc = e4(p[60:]) + s.flags = e4(p[64:]) + s.res1 = e4(p[68:]) + s.res2 = e4(p[72:]) + + // p+76 is reserved + p = p[80:] + } + + case LdMachoCmdSymtab: + if sz < 24 { + return -1 + } + c.sym.symoff = e4(p[8:]) + c.sym.nsym = e4(p[12:]) + c.sym.stroff = e4(p[16:]) + c.sym.strsize = e4(p[20:]) + + case LdMachoCmdDysymtab: + if sz < 80 { + return -1 + } + c.dsym.ilocalsym = e4(p[8:]) + c.dsym.nlocalsym = e4(p[12:]) + c.dsym.iextdefsym = e4(p[16:]) + c.dsym.nextdefsym = e4(p[20:]) + c.dsym.iundefsym = e4(p[24:]) + c.dsym.nundefsym = e4(p[28:]) + c.dsym.tocoff = e4(p[32:]) + c.dsym.ntoc = e4(p[36:]) + c.dsym.modtaboff = e4(p[40:]) + c.dsym.nmodtab = e4(p[44:]) + c.dsym.extrefsymoff = e4(p[48:]) + c.dsym.nextrefsyms = e4(p[52:]) + c.dsym.indirectsymoff = e4(p[56:]) + c.dsym.nindirectsyms = e4(p[60:]) + c.dsym.extreloff = e4(p[64:]) + c.dsym.nextrel = e4(p[68:]) + c.dsym.locreloff = e4(p[72:]) + c.dsym.nlocrel = e4(p[76:]) + } + + return 0 +} + +func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int { + if sect.rel != nil || sect.nreloc == 0 { + return 0 + } + rel := make([]ldMachoRel, sect.nreloc) + n := int(sect.nreloc * 8) + buf := make([]byte, n) + m.f.MustSeek(m.base+int64(sect.reloff), 0) + if _, err := io.ReadFull(m.f, buf); err != nil { + return -1 + } + for i := uint32(0); i < sect.nreloc; i++ { + r := &rel[i] + p := buf[i*8:] + r.addr = m.e.Uint32(p) + + // TODO(rsc): Wrong interpretation for big-endian bitfields? + if r.addr&0x80000000 != 0 { + // scatterbrained relocation + r.scattered = 1 + + v := r.addr >> 24 + r.addr &= 0xFFFFFF + r.type_ = uint8(v & 0xF) + v >>= 4 + r.length = 1 << (v & 3) + v >>= 2 + r.pcrel = uint8(v & 1) + r.value = m.e.Uint32(p[4:]) + } else { + v := m.e.Uint32(p[4:]) + r.symnum = v & 0xFFFFFF + v >>= 24 + r.pcrel = uint8(v & 1) + v >>= 1 + r.length = 1 << (v & 3) + v >>= 2 + r.extrn = uint8(v & 1) + v >>= 1 + r.type_ = uint8(v) + } + } + + sect.rel = rel + return 0 +} + +func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int { + n := int(d.nindirectsyms) + + p := make([]byte, n*4) + m.f.MustSeek(m.base+int64(d.indirectsymoff), 0) + if _, err := io.ReadFull(m.f, p); err != nil { + return -1 + } + + d.indir = make([]uint32, n) + for i := 0; i < n; i++ { + d.indir[i] = m.e.Uint32(p[4*i:]) + } + return 0 +} + +func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int { + if symtab.sym != nil { + return 0 + } + + strbuf := make([]byte, symtab.strsize) + m.f.MustSeek(m.base+int64(symtab.stroff), 0) + if _, err := io.ReadFull(m.f, strbuf); err != nil { + return -1 + } + + symsize := 12 + if m.is64 { + symsize = 16 + } + n := int(symtab.nsym * uint32(symsize)) + symbuf := make([]byte, n) + m.f.MustSeek(m.base+int64(symtab.symoff), 0) + if _, err := io.ReadFull(m.f, symbuf); err != nil { + return -1 + } + sym := make([]ldMachoSym, symtab.nsym) + p := symbuf + for i := uint32(0); i < symtab.nsym; i++ { + s := &sym[i] + v := m.e.Uint32(p) + if v >= symtab.strsize { + return -1 + } + s.name = cstring(strbuf[v:]) + s.type_ = p[4] + s.sectnum = p[5] + s.desc = m.e.Uint16(p[6:]) + if m.is64 { + s.value = m.e.Uint64(p[8:]) + } else { + s.value = uint64(m.e.Uint32(p[8:])) + } + p = p[symsize:] + } + + symtab.str = strbuf + symtab.sym = sym + return 0 +} + +func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) ([]*sym.Symbol, error) { + newSym := func(name string, version int) *sym.Symbol { + return l.LookupOrCreate(name, version, syms) + } + return load(arch, syms.IncVersion(), newSym, f, pkg, length, pn) +} + +func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { + return load(arch, syms.IncVersion(), syms.Lookup, f, pkg, length, pn) +} + +// load the Mach-O file pn from f. +// Symbols are written into syms, and a slice of the text symbols is returned. +func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Symbol, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { + errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) { + return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...)) + } + + base := f.Offset() + + var hdr [7 * 4]uint8 + if _, err := io.ReadFull(f, hdr[:]); err != nil { + return errorf("reading hdr: %v", err) + } + + var e binary.ByteOrder + if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { + e = binary.BigEndian + } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { + e = binary.LittleEndian + } else { + return errorf("bad magic - not mach-o file") + } + + is64 := e.Uint32(hdr[:]) == 0xFEEDFACF + ncmd := e.Uint32(hdr[4*4:]) + cmdsz := e.Uint32(hdr[5*4:]) + if ncmd > 0x10000 || cmdsz >= 0x01000000 { + return errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz) + } + + if is64 { + f.MustSeek(4, 1) // skip reserved word in header + } + + m := &ldMachoObj{ + f: f, + e: e, + cputype: uint(e.Uint32(hdr[1*4:])), + subcputype: uint(e.Uint32(hdr[2*4:])), + filetype: e.Uint32(hdr[3*4:]), + ncmd: uint(ncmd), + flags: e.Uint32(hdr[6*4:]), + is64: is64, + base: base, + length: length, + name: pn, + } + + switch arch.Family { + default: + return errorf("mach-o %s unimplemented", arch.Name) + + case sys.AMD64: + if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { + return errorf("mach-o object but not amd64") + } + + case sys.I386: + if e != binary.LittleEndian || m.cputype != LdMachoCpu386 { + return errorf("mach-o object but not 386") + } + } + + m.cmd = make([]ldMachoCmd, ncmd) + cmdp := make([]byte, cmdsz) + if _, err := io.ReadFull(f, cmdp); err != nil { + return errorf("reading cmds: %v", err) + } + + // read and parse load commands + var c *ldMachoCmd + + var symtab *ldMachoSymtab + var dsymtab *ldMachoDysymtab + + off := uint32(len(hdr)) + for i := uint32(0); i < ncmd; i++ { + ty := e.Uint32(cmdp) + sz := e.Uint32(cmdp[4:]) + m.cmd[i].off = off + unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz)) + cmdp = cmdp[sz:] + off += sz + if ty == LdMachoCmdSymtab { + if symtab != nil { + return errorf("multiple symbol tables") + } + + symtab = &m.cmd[i].sym + macholoadsym(m, symtab) + } + + if ty == LdMachoCmdDysymtab { + dsymtab = &m.cmd[i].dsym + macholoaddsym(m, dsymtab) + } + + if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) { + if c != nil { + return errorf("multiple load commands") + } + + c = &m.cmd[i] + } + } + + // load text and data segments into memory. + // they are not as small as the load commands, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + if c == nil { + return errorf("no load command") + } + + if symtab == nil { + // our work is done here - no symbols means nothing can refer to this file + return + } + + if int64(c.seg.fileoff+c.seg.filesz) >= length { + return errorf("load segment out of range") + } + + f.MustSeek(m.base+int64(c.seg.fileoff), 0) + dat := make([]byte, c.seg.filesz) + if _, err := io.ReadFull(f, dat); err != nil { + return errorf("cannot load object data: %v", err) + } + + for i := uint32(0); i < c.seg.nsect; i++ { + sect := &c.seg.sect[i] + if sect.segname != "__TEXT" && sect.segname != "__DATA" { + continue + } + if sect.name == "__eh_frame" { + continue + } + name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name) + s := lookup(name, localSymVersion) + if s.Type != 0 { + return errorf("duplicate %s/%s", sect.segname, sect.name) + } + + if sect.flags&0xff == 1 { // S_ZEROFILL + s.P = make([]byte, sect.size) + } else { + s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size] + } + s.Size = int64(len(s.P)) + + if sect.segname == "__TEXT" { + if sect.name == "__text" { + s.Type = sym.STEXT + } else { + s.Type = sym.SRODATA + } + } else { + if sect.name == "__bss" { + s.Type = sym.SNOPTRBSS + s.P = s.P[:0] + } else { + s.Type = sym.SNOPTRDATA + } + } + + sect.sym = s + } + + // enter sub-symbols into symbol table. + // have to guess sizes from next symbol. + for i := uint32(0); i < symtab.nsym; i++ { + machsym := &symtab.sym[i] + if machsym.type_&N_STAB != 0 { + continue + } + + // TODO: check sym->type against outer->type. + name := machsym.name + + if name[0] == '_' && name[1] != '\x00' { + name = name[1:] + } + v := 0 + if machsym.type_&N_EXT == 0 { + v = localSymVersion + } + s := lookup(name, v) + if machsym.type_&N_EXT == 0 { + s.Attr |= sym.AttrDuplicateOK + } + if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 { + s.Attr |= sym.AttrDuplicateOK + } + machsym.sym = s + if machsym.sectnum == 0 { // undefined + continue + } + if uint32(machsym.sectnum) > c.seg.nsect { + return errorf("reference to invalid section %d", machsym.sectnum) + } + + sect := &c.seg.sect[machsym.sectnum-1] + outer := sect.sym + if outer == nil { + continue // ignore reference to invalid section + } + + if s.Outer != nil { + if s.Attr.DuplicateOK() { + continue + } + return errorf("duplicate symbol reference: %s in both %s and %s", s.Name, s.Outer.Name, sect.sym.Name) + } + + s.Type = outer.Type + s.Attr |= sym.AttrSubSymbol + s.Sub = outer.Sub + outer.Sub = s + s.Outer = outer + s.Value = int64(machsym.value - sect.addr) + if !s.Attr.CgoExportDynamic() { + s.SetDynimplib("") // satisfy dynimport + } + if outer.Type == sym.STEXT { + if s.Attr.External() && !s.Attr.DuplicateOK() { + return errorf("%v: duplicate symbol definition", s) + } + s.Attr |= sym.AttrExternal + } + + machsym.sym = s + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i := 0; uint32(i) < c.seg.nsect; i++ { + sect := &c.seg.sect[i] + s := sect.sym + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = sym.SortSub(s.Sub) + + // assign sizes, now that we know symbols in sorted order. + for s1 := s.Sub; s1 != nil; s1 = s1.Sub { + if s1.Sub != nil { + s1.Size = s1.Sub.Value - s1.Value + } else { + s1.Size = s.Value + s.Size - s1.Value + } + } + } + + if s.Type == sym.STEXT { + if s.Attr.OnList() { + return errorf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + textp = append(textp, s) + for s1 := s.Sub; s1 != nil; s1 = s1.Sub { + if s1.Attr.OnList() { + return errorf("symbol %s listed multiple times", s1.Name) + } + s1.Attr |= sym.AttrOnList + textp = append(textp, s1) + } + } + } + + // load relocations + for i := 0; uint32(i) < c.seg.nsect; i++ { + sect := &c.seg.sect[i] + s := sect.sym + if s == nil { + continue + } + macholoadrel(m, sect) + if sect.rel == nil { + continue + } + r := make([]sym.Reloc, sect.nreloc) + rpi := 0 + Reloc: + for j := uint32(0); j < sect.nreloc; j++ { + rp := &r[rpi] + rel := §.rel[j] + if rel.scattered != 0 { + if arch.Family != sys.I386 { + // mach-o only uses scattered relocation on 32-bit platforms + return errorf("%v: unexpected scattered relocation", s) + } + + // on 386, rewrite scattered 4/1 relocation and some + // scattered 2/1 relocation into the pseudo-pc-relative + // reference that it is. + // assume that the second in the pair is in this section + // and use that as the pc-relative base. + if j+1 >= sect.nreloc { + return errorf("unsupported scattered relocation %d", int(rel.type_)) + } + + if sect.rel[j+1].scattered == 0 || sect.rel[j+1].type_ != 1 || (rel.type_ != 4 && rel.type_ != 2) || uint64(sect.rel[j+1].value) < sect.addr || uint64(sect.rel[j+1].value) >= sect.addr+sect.size { + return errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_)) + } + + rp.Siz = rel.length + rp.Off = int32(rel.addr) + + // NOTE(rsc): I haven't worked out why (really when) + // we should ignore the addend on a + // scattered relocation, but it seems that the + // common case is we ignore it. + // It's likely that this is not strictly correct + // and that the math should look something + // like the non-scattered case below. + rp.Add = 0 + + // want to make it pc-relative aka relative to rp->off+4 + // but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr. + // adjust rp->add accordingly. + rp.Type = objabi.R_PCREL + + rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr)) + + // now consider the desired symbol. + // find the section where it lives. + for k := 0; uint32(k) < c.seg.nsect; k++ { + ks := &c.seg.sect[k] + if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size { + if ks.sym != nil { + rp.Sym = ks.sym + rp.Add += int64(uint64(rel.value) - ks.addr) + } else if ks.segname == "__IMPORT" && ks.name == "__pointers" { + // handle reference to __IMPORT/__pointers. + // how much worse can this get? + // why are we supporting 386 on the mac anyway? + rp.Type = objabi.MachoRelocOffset + MACHO_FAKE_GOTPCREL + + // figure out which pointer this is a reference to. + k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4) + + // load indirect table for __pointers + // fetch symbol number + if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil { + return errorf("invalid scattered relocation: indirect symbol reference out of range") + } + + k = int(dsymtab.indir[k]) + if k < 0 || uint32(k) >= symtab.nsym { + return errorf("invalid scattered relocation: symbol reference out of range") + } + + rp.Sym = symtab.sym[k].sym + } else { + return errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name) + } + + rpi++ + + // skip #1 of 2 rel; continue skips #2 of 2. + j++ + + continue Reloc + } + } + + return errorf("unsupported scattered relocation: invalid address %#x", rel.addr) + } + + rp.Siz = rel.length + rp.Type = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel) + rp.Off = int32(rel.addr) + + // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). + if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED { + // Calculate the addend as the offset into the section. + // + // The rip-relative offset stored in the object file is encoded + // as follows: + // + // movsd 0x00000360(%rip),%xmm0 + // + // To get the absolute address of the value this rip-relative address is pointing + // to, we must add the address of the next instruction to it. This is done by + // taking the address of the relocation and adding 4 to it (since the rip-relative + // offset can at most be 32 bits long). To calculate the offset into the section the + // relocation is referencing, we subtract the vaddr of the start of the referenced + // section found in the original object file. + // + // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] + secaddr := c.seg.sect[rel.symnum-1].addr + + rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr) + } else { + rp.Add = int64(int32(e.Uint32(s.P[rp.Off:]))) + } + + // An unsigned internal relocation has a value offset + // by the section address. + if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_UNSIGNED { + secaddr := c.seg.sect[rel.symnum-1].addr + rp.Add -= int64(secaddr) + } + + // For i386 Mach-O PC-relative, the addend is written such that + // it *is* the PC being subtracted. Use that to make + // it match our version of PC-relative. + if rel.pcrel != 0 && arch.Family == sys.I386 { + rp.Add += int64(rp.Off) + int64(rp.Siz) + } + if rel.extrn == 0 { + if rel.symnum < 1 || rel.symnum > c.seg.nsect { + return errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect) + } + + rp.Sym = c.seg.sect[rel.symnum-1].sym + if rp.Sym == nil { + return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name) + } + + // References to symbols in other sections + // include that information in the addend. + // We only care about the delta from the + // section base. + if arch.Family == sys.I386 { + rp.Add -= int64(c.seg.sect[rel.symnum-1].addr) + } + } else { + if rel.symnum >= symtab.nsym { + return errorf("invalid relocation: symbol reference out of range") + } + + rp.Sym = symtab.sym[rel.symnum].sym + } + + rpi++ + } + + sort.Sort(sym.RelocByOff(r[:rpi])) + s.R = r + s.R = s.R[:rpi] + } + + return textp, nil +} + +func cstring(x []byte) string { + i := bytes.IndexByte(x, '\x00') + if i >= 0 { + x = x[:i] + } + return string(x) +} diff --git a/src/cmd/oldlink/internal/loadpe/ldpe.go b/src/cmd/oldlink/internal/loadpe/ldpe.go new file mode 100644 index 0000000000..f7df774661 --- /dev/null +++ b/src/cmd/oldlink/internal/loadpe/ldpe.go @@ -0,0 +1,513 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loadpe implements a PE/COFF file reader. +package loadpe + +import ( + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/loader" + "cmd/oldlink/internal/sym" + "debug/pe" + "encoding/binary" + "errors" + "fmt" + "io" + "sort" + "strings" +) + +const ( + // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 (same with IMAGE_SYM_DTYPE_POINTER and IMAGE_SYM_DTYPE_FUNCTION) + IMAGE_SYM_UNDEFINED = 0 + IMAGE_SYM_ABSOLUTE = -1 + IMAGE_SYM_DEBUG = -2 + IMAGE_SYM_TYPE_NULL = 0 + IMAGE_SYM_TYPE_VOID = 1 + IMAGE_SYM_TYPE_CHAR = 2 + IMAGE_SYM_TYPE_SHORT = 3 + IMAGE_SYM_TYPE_INT = 4 + IMAGE_SYM_TYPE_LONG = 5 + IMAGE_SYM_TYPE_FLOAT = 6 + IMAGE_SYM_TYPE_DOUBLE = 7 + IMAGE_SYM_TYPE_STRUCT = 8 + IMAGE_SYM_TYPE_UNION = 9 + IMAGE_SYM_TYPE_ENUM = 10 + IMAGE_SYM_TYPE_MOE = 11 + IMAGE_SYM_TYPE_BYTE = 12 + IMAGE_SYM_TYPE_WORD = 13 + IMAGE_SYM_TYPE_UINT = 14 + IMAGE_SYM_TYPE_DWORD = 15 + IMAGE_SYM_TYPE_PCODE = 32768 + IMAGE_SYM_DTYPE_NULL = 0 + IMAGE_SYM_DTYPE_POINTER = 0x10 + IMAGE_SYM_DTYPE_FUNCTION = 0x20 + IMAGE_SYM_DTYPE_ARRAY = 0x30 + IMAGE_SYM_CLASS_END_OF_FUNCTION = -1 + IMAGE_SYM_CLASS_NULL = 0 + IMAGE_SYM_CLASS_AUTOMATIC = 1 + IMAGE_SYM_CLASS_EXTERNAL = 2 + IMAGE_SYM_CLASS_STATIC = 3 + IMAGE_SYM_CLASS_REGISTER = 4 + IMAGE_SYM_CLASS_EXTERNAL_DEF = 5 + IMAGE_SYM_CLASS_LABEL = 6 + IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7 + IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8 + IMAGE_SYM_CLASS_ARGUMENT = 9 + IMAGE_SYM_CLASS_STRUCT_TAG = 10 + IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11 + IMAGE_SYM_CLASS_UNION_TAG = 12 + IMAGE_SYM_CLASS_TYPE_DEFINITION = 13 + IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14 + IMAGE_SYM_CLASS_ENUM_TAG = 15 + IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16 + IMAGE_SYM_CLASS_REGISTER_PARAM = 17 + IMAGE_SYM_CLASS_BIT_FIELD = 18 + IMAGE_SYM_CLASS_FAR_EXTERNAL = 68 /* Not in PECOFF v8 spec */ + IMAGE_SYM_CLASS_BLOCK = 100 + IMAGE_SYM_CLASS_FUNCTION = 101 + IMAGE_SYM_CLASS_END_OF_STRUCT = 102 + IMAGE_SYM_CLASS_FILE = 103 + IMAGE_SYM_CLASS_SECTION = 104 + IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105 + IMAGE_SYM_CLASS_CLR_TOKEN = 107 + IMAGE_REL_I386_ABSOLUTE = 0x0000 + IMAGE_REL_I386_DIR16 = 0x0001 + IMAGE_REL_I386_REL16 = 0x0002 + IMAGE_REL_I386_DIR32 = 0x0006 + IMAGE_REL_I386_DIR32NB = 0x0007 + IMAGE_REL_I386_SEG12 = 0x0009 + IMAGE_REL_I386_SECTION = 0x000A + IMAGE_REL_I386_SECREL = 0x000B + IMAGE_REL_I386_TOKEN = 0x000C + IMAGE_REL_I386_SECREL7 = 0x000D + IMAGE_REL_I386_REL32 = 0x0014 + IMAGE_REL_AMD64_ABSOLUTE = 0x0000 + IMAGE_REL_AMD64_ADDR64 = 0x0001 + IMAGE_REL_AMD64_ADDR32 = 0x0002 + IMAGE_REL_AMD64_ADDR32NB = 0x0003 + IMAGE_REL_AMD64_REL32 = 0x0004 + IMAGE_REL_AMD64_REL32_1 = 0x0005 + IMAGE_REL_AMD64_REL32_2 = 0x0006 + IMAGE_REL_AMD64_REL32_3 = 0x0007 + IMAGE_REL_AMD64_REL32_4 = 0x0008 + IMAGE_REL_AMD64_REL32_5 = 0x0009 + IMAGE_REL_AMD64_SECTION = 0x000A + IMAGE_REL_AMD64_SECREL = 0x000B + IMAGE_REL_AMD64_SECREL7 = 0x000C + IMAGE_REL_AMD64_TOKEN = 0x000D + IMAGE_REL_AMD64_SREL32 = 0x000E + IMAGE_REL_AMD64_PAIR = 0x000F + IMAGE_REL_AMD64_SSPAN32 = 0x0010 + IMAGE_REL_ARM_ABSOLUTE = 0x0000 + IMAGE_REL_ARM_ADDR32 = 0x0001 + IMAGE_REL_ARM_ADDR32NB = 0x0002 + IMAGE_REL_ARM_BRANCH24 = 0x0003 + IMAGE_REL_ARM_BRANCH11 = 0x0004 + IMAGE_REL_ARM_SECTION = 0x000E + IMAGE_REL_ARM_SECREL = 0x000F + IMAGE_REL_ARM_MOV32 = 0x0010 + IMAGE_REL_THUMB_MOV32 = 0x0011 + IMAGE_REL_THUMB_BRANCH20 = 0x0012 + IMAGE_REL_THUMB_BRANCH24 = 0x0014 + IMAGE_REL_THUMB_BLX23 = 0x0015 + IMAGE_REL_ARM_PAIR = 0x0016 +) + +// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld, ideally in debug/pe. +const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 +) + +// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf + +// peBiobuf makes bio.Reader look like io.ReaderAt. +type peBiobuf bio.Reader + +func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) { + ret := ((*bio.Reader)(f)).MustSeek(off, 0) + if ret < 0 { + return 0, errors.New("fail to seek") + } + n, err := f.Read(p) + if err != nil { + return 0, err + } + return n, nil +} + +func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) { + lookup := func(name string, version int) *sym.Symbol { + return l.LookupOrCreate(name, version, syms) + } + return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn) +} + +func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) { + return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn) +} + +// load loads the PE file pn from input. +// Symbols are written into syms, and a slice of the text symbols is returned. +// If an .rsrc section is found, its symbol is returned as rsrc. +func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) { + sectsyms := make(map[*pe.Section]*sym.Symbol) + sectdata := make(map[*pe.Section][]byte) + + // Some input files are archives containing multiple of + // object files, and pe.NewFile seeks to the start of + // input file and get confused. Create section reader + // to stop pe.NewFile looking before current position. + sr := io.NewSectionReader((*peBiobuf)(input), input.Offset(), 1<<63-1) + + // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details) + f, err := pe.NewFile(sr) + if err != nil { + return nil, nil, err + } + defer f.Close() + + // TODO return error if found .cormeta + + // create symbols for mapped sections + for _, sect := range f.Sections { + if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + + if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue + } + + name := fmt.Sprintf("%s(%s)", pkg, sect.Name) + s := lookup(name, localSymVersion) + + switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { + case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata + s.Type = sym.SRODATA + + case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss + s.Type = sym.SNOPTRBSS + + case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data + s.Type = sym.SNOPTRDATA + + case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text + s.Type = sym.STEXT + + default: + return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name) + } + + if s.Type != sym.SNOPTRBSS { + data, err := sect.Data() + if err != nil { + return nil, nil, err + } + sectdata[sect] = data + s.P = data + } + s.Size = int64(sect.Size) + sectsyms[sect] = s + if sect.Name == ".rsrc" { + rsrc = s + } + } + + // load relocations + for _, rsect := range f.Sections { + if _, found := sectsyms[rsect]; !found { + continue + } + if rsect.NumberOfRelocations == 0 { + continue + } + if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue + } + + rs := make([]sym.Reloc, rsect.NumberOfRelocations) + for j, r := range rsect.Relocs { + rp := &rs[j] + if int(r.SymbolTableIndex) >= len(f.COFFSymbols) { + return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) + } + pesym := &f.COFFSymbols[r.SymbolTableIndex] + gosym, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion) + if err != nil { + return nil, nil, err + } + if gosym == nil { + name, err := pesym.FullName(f.StringTable) + if err != nil { + name = string(pesym.Name[:]) + } + return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type) + } + + rp.Sym = gosym + rp.Siz = 4 + rp.Off = int32(r.VirtualAddress) + switch arch.Family { + default: + return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family) + case sys.I386, sys.AMD64: + switch r.Type { + default: + return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type) + + case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, + IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 + IMAGE_REL_AMD64_ADDR32NB: + rp.Type = objabi.R_PCREL + + rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:]))) + + case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: + rp.Type = objabi.R_ADDR + + // load addend from image + rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:]))) + + case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 + rp.Siz = 8 + + rp.Type = objabi.R_ADDR + + // load addend from image + rp.Add = int64(binary.LittleEndian.Uint64(sectdata[rsect][rp.Off:])) + } + + case sys.ARM: + switch r.Type { + default: + return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type) + + case IMAGE_REL_ARM_SECREL: + rp.Type = objabi.R_PCREL + + rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:]))) + + case IMAGE_REL_ARM_ADDR32: + rp.Type = objabi.R_ADDR + + rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:]))) + + case IMAGE_REL_ARM_BRANCH24: + rp.Type = objabi.R_CALLARM + + rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:]))) + } + } + + // ld -r could generate multiple section symbols for the + // same section but with different values, we have to take + // that into account + if issect(pesym) { + rp.Add += int64(pesym.Value) + } + } + + sort.Sort(sym.RelocByOff(rs[:rsect.NumberOfRelocations])) + + s := sectsyms[rsect] + s.R = rs + s.R = s.R[:rsect.NumberOfRelocations] + } + + // enter sub-symbols into symbol table. + for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 { + pesym := &f.COFFSymbols[i] + + numaux = int(pesym.NumberOfAuxSymbols) + + name, err := pesym.FullName(f.StringTable) + if err != nil { + return nil, nil, err + } + if name == "" { + continue + } + if issect(pesym) { + continue + } + if int(pesym.SectionNumber) > len(f.Sections) { + continue + } + if pesym.SectionNumber == IMAGE_SYM_DEBUG { + continue + } + var sect *pe.Section + if pesym.SectionNumber > 0 { + sect = f.Sections[pesym.SectionNumber-1] + if _, found := sectsyms[sect]; !found { + continue + } + } + + s, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion) + if err != nil { + return nil, nil, err + } + + if pesym.SectionNumber == 0 { // extern + if s.Type == sym.SDYNIMPORT { + s.SetPlt(-2) // flag for dynimport in PE object files. + } + if s.Type == sym.SXREF && pesym.Value > 0 { // global data + s.Type = sym.SNOPTRDATA + s.Size = int64(pesym.Value) + } + + continue + } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) { + sect = f.Sections[pesym.SectionNumber-1] + if _, found := sectsyms[sect]; !found { + return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s) + } + } else { + return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s) + } + + if sect == nil { + return nil, rsrc, nil + } + + if s.Outer != nil { + if s.Attr.DuplicateOK() { + continue + } + return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sectsyms[sect].Name) + } + + sectsym := sectsyms[sect] + s.Sub = sectsym.Sub + sectsym.Sub = s + s.Type = sectsym.Type + s.Attr |= sym.AttrSubSymbol + s.Value = int64(pesym.Value) + s.Size = 4 + s.Outer = sectsym + if sectsym.Type == sym.STEXT { + if s.Attr.External() && !s.Attr.DuplicateOK() { + return nil, nil, fmt.Errorf("%s: duplicate symbol definition", s.Name) + } + s.Attr |= sym.AttrExternal + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for _, sect := range f.Sections { + s := sectsyms[sect] + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = sym.SortSub(s.Sub) + } + if s.Type == sym.STEXT { + if s.Attr.OnList() { + return nil, nil, fmt.Errorf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + textp = append(textp, s) + for s = s.Sub; s != nil; s = s.Sub { + if s.Attr.OnList() { + return nil, nil, fmt.Errorf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + textp = append(textp, s) + } + } + } + + return textp, rsrc, nil +} + +func issect(s *pe.COFFSymbol) bool { + return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.' +} + +func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]*sym.Symbol, localSymVersion int) (*sym.Symbol, error) { + symname, err := pesym.FullName(f.StringTable) + if err != nil { + return nil, err + } + var name string + if issect(pesym) { + name = sectsyms[f.Sections[pesym.SectionNumber-1]].Name + } else { + name = symname + switch arch.Family { + case sys.AMD64: + if name == "__imp___acrt_iob_func" { + // Do not rename __imp___acrt_iob_func into __acrt_iob_func, + // because __imp___acrt_iob_func symbol is real + // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details). + } else { + name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name + } + case sys.I386: + if name == "__imp____acrt_iob_func" { + // Do not rename __imp____acrt_iob_func into ___acrt_iob_func, + // because __imp____acrt_iob_func symbol is real + // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details). + } else { + name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name + } + if name[0] == '_' { + name = name[1:] // _Name => Name + } + } + } + + // remove last @XXX + if i := strings.LastIndex(name, "@"); i >= 0 { + name = name[:i] + } + + var s *sym.Symbol + switch pesym.Type { + default: + return nil, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type) + + case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL: + switch pesym.StorageClass { + case IMAGE_SYM_CLASS_EXTERNAL: //global + s = lookup(name, 0) + + case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL: + s = lookup(name, localSymVersion) + s.Attr |= sym.AttrDuplicateOK + + default: + return nil, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass) + } + } + + if s != nil && s.Type == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) { + s.Type = sym.SXREF + } + if strings.HasPrefix(symname, "__imp_") { + s.SetGot(-2) // flag for __imp_ + } + + return s, nil +} diff --git a/src/cmd/oldlink/internal/loadxcoff/ldxcoff.go b/src/cmd/oldlink/internal/loadxcoff/ldxcoff.go new file mode 100644 index 0000000000..832b1681c7 --- /dev/null +++ b/src/cmd/oldlink/internal/loadxcoff/ldxcoff.go @@ -0,0 +1,238 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loadxcoff implements a XCOFF file reader. +package loadxcoff + +import ( + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/loader" + "cmd/oldlink/internal/sym" + "errors" + "fmt" + "internal/xcoff" +) + +// ldSection is an XCOFF section with its symbols. +type ldSection struct { + xcoff.Section + sym *sym.Symbol +} + +// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf + +// xcoffBiobuf makes bio.Reader look like io.ReaderAt. +type xcoffBiobuf bio.Reader + +func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) { + ret := ((*bio.Reader)(f)).MustSeek(off, 0) + if ret < 0 { + return 0, errors.New("fail to seek") + } + n, err := f.Read(p) + if err != nil { + return 0, err + } + return n, nil +} + +// Load loads xcoff files with the indexed object files. +func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { + lookup := func(name string, version int) *sym.Symbol { + return l.LookupOrCreate(name, version, syms) + } + return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn) +} + +// LoadOld uses the old version of object loading. +func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { + return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn) +} + +// loads the Xcoff file pn from f. +// Symbols are written into syms, and a slice of the text symbols is returned. +func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) { + errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) { + return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...)) + } + + var ldSections []*ldSection + + f, err := xcoff.NewFile((*xcoffBiobuf)(input)) + if err != nil { + return nil, err + } + defer f.Close() + + for _, sect := range f.Sections { + //only text, data and bss section + if sect.Type < xcoff.STYP_TEXT || sect.Type > xcoff.STYP_BSS { + continue + } + lds := new(ldSection) + lds.Section = *sect + name := fmt.Sprintf("%s(%s)", pkg, lds.Name) + s := lookup(name, localSymVersion) + + switch lds.Type { + default: + return errorf("unrecognized section type 0x%x", lds.Type) + case xcoff.STYP_TEXT: + s.Type = sym.STEXT + case xcoff.STYP_DATA: + s.Type = sym.SNOPTRDATA + case xcoff.STYP_BSS: + s.Type = sym.SNOPTRBSS + } + + s.Size = int64(lds.Size) + if s.Type != sym.SNOPTRBSS { + data, err := lds.Section.Data() + if err != nil { + return nil, err + } + s.P = data + } + + lds.sym = s + ldSections = append(ldSections, lds) + } + + // sx = symbol from file + // s = symbol for syms + for _, sx := range f.Symbols { + // get symbol type + stype, errmsg := getSymbolType(f, sx) + if errmsg != "" { + return errorf("error reading symbol %s: %s", sx.Name, errmsg) + } + if stype == sym.Sxxx { + continue + } + + s := lookup(sx.Name, 0) + + // Text symbol + if s.Type == sym.STEXT { + if s.Attr.OnList() { + return errorf("symbol %s listed multiple times", s.Name) + } + s.Attr |= sym.AttrOnList + textp = append(textp, s) + } + } + + // Read relocations + for _, sect := range ldSections { + // TODO(aix): Dwarf section relocation if needed + if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA { + continue + } + rs := make([]sym.Reloc, sect.Nreloc) + for i, rx := range sect.Relocs { + r := &rs[i] + + r.Sym = lookup(rx.Symbol.Name, 0) + if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress { + return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress) + } + r.Off = int32(rx.VirtualAddress) + switch rx.Type { + default: + return errorf("section %s: unknown relocation of type 0x%x", sect.Name, rx.Type) + case xcoff.R_POS: + // Reloc the address of r.Sym + // Length should be 64 + if rx.Length != 64 { + return errorf("section %s: relocation R_POS has length different from 64: %d", sect.Name, rx.Length) + } + r.Siz = 8 + r.Type = objabi.R_CONST + r.Add = int64(rx.Symbol.Value) + + case xcoff.R_RBR: + r.Siz = 4 + r.Type = objabi.R_CALLPOWER + r.Add = 0 // + + } + } + s := sect.sym + s.R = rs + s.R = s.R[:sect.Nreloc] + } + return textp, nil + +} + +// Convert symbol xcoff type to sym.SymKind +// Returns nil if this shouldn't be added into syms (like .file or .dw symbols ) +func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) { + // .file symbol + if s.SectionNumber == -2 { + if s.StorageClass == xcoff.C_FILE { + return sym.Sxxx, "" + } + return sym.Sxxx, "unrecognised StorageClass for sectionNumber = -2" + } + + // extern symbols + // TODO(aix) + if s.SectionNumber == 0 { + return sym.Sxxx, "" + } + + sectType := f.Sections[s.SectionNumber-1].SectionHeader.Type + switch sectType { + default: + return sym.Sxxx, fmt.Sprintf("getSymbolType for Section type 0x%x not implemented", sectType) + case xcoff.STYP_DWARF, xcoff.STYP_DEBUG: + return sym.Sxxx, "" + case xcoff.STYP_DATA, xcoff.STYP_BSS, xcoff.STYP_TEXT: + } + + switch s.StorageClass { + default: + return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x not implemented", s.StorageClass) + case xcoff.C_HIDEXT, xcoff.C_EXT, xcoff.C_WEAKEXT: + switch s.AuxCSect.StorageMappingClass { + default: + return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x and Storage Map 0x%x not implemented", s.StorageClass, s.AuxCSect.StorageMappingClass) + + // Program Code + case xcoff.XMC_PR: + if sectType == xcoff.STYP_TEXT { + return sym.STEXT, "" + } + return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_PR", sectType, s.StorageClass) + + // Read/Write Data + case xcoff.XMC_RW: + if sectType == xcoff.STYP_DATA { + return sym.SDATA, "" + } + if sectType == xcoff.STYP_BSS { + return sym.SBSS, "" + } + return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_RW", sectType, s.StorageClass) + + // Function descriptor + case xcoff.XMC_DS: + if sectType == xcoff.STYP_DATA { + return sym.SDATA, "" + } + return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass) + + // TOC anchor and TOC entry + case xcoff.XMC_TC0, xcoff.XMC_TE: + if sectType == xcoff.STYP_DATA { + return sym.SXCOFFTOC, "" + } + return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass) + + } + } +} diff --git a/src/cmd/oldlink/internal/mips/asm.go b/src/cmd/oldlink/internal/mips/asm.go new file mode 100644 index 0000000000..48a2324151 --- /dev/null +++ b/src/cmd/oldlink/internal/mips/asm.go @@ -0,0 +1,230 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2016 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "debug/elf" + "fmt" + "log" +) + +func gentext(ctxt *ld.Link) { + return +} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + log.Fatalf("adddynrel not implemented") + return false +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + ctxt.Out.Write32(uint32(sectoff)) + + elfsym := r.Xsym.ElfsymForReloc() + switch r.Type { + default: + return false + case objabi.R_ADDR: + if r.Siz != 4 { + return false + } + ctxt.Out.Write32(uint32(elf.R_MIPS_32) | uint32(elfsym)<<8) + case objabi.R_ADDRMIPS: + ctxt.Out.Write32(uint32(elf.R_MIPS_LO16) | uint32(elfsym)<<8) + case objabi.R_ADDRMIPSU: + ctxt.Out.Write32(uint32(elf.R_MIPS_HI16) | uint32(elfsym)<<8) + case objabi.R_ADDRMIPSTLS: + ctxt.Out.Write32(uint32(elf.R_MIPS_TLS_TPREL_LO16) | uint32(elfsym)<<8) + case objabi.R_CALLMIPS, objabi.R_JMPMIPS: + ctxt.Out.Write32(uint32(elf.R_MIPS_26) | uint32(elfsym)<<8) + } + + return true +} + +func elfsetupplt(ctxt *ld.Link) { + return +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + return false +} + +func applyrel(arch *sys.Arch, r *sym.Reloc, s *sym.Symbol, val int64, t int64) int64 { + o := arch.ByteOrder.Uint32(s.P[r.Off:]) + switch r.Type { + case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSTLS: + return int64(o&0xffff0000 | uint32(t)&0xffff) + case objabi.R_ADDRMIPSU: + return int64(o&0xffff0000 | uint32((t+(1<<15))>>16)&0xffff) + case objabi.R_CALLMIPS, objabi.R_JMPMIPS: + return int64(o&0xfc000000 | uint32(t>>2)&^0xfc000000) + default: + return val + } +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if ctxt.LinkMode == ld.LinkExternal { + switch r.Type { + default: + return val, false + case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU: + r.Done = false + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil { + ld.Errorf(s, "missing section for %s", rs.Name) + } + r.Xsym = rs + return applyrel(ctxt.Arch, r, s, val, r.Xadd), true + case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS: + r.Done = false + r.Xsym = r.Sym + r.Xadd = r.Add + return applyrel(ctxt.Arch, r, s, val, r.Add), true + } + } + + switch r.Type { + case objabi.R_CONST: + return r.Add, true + case objabi.R_GOTOFF: + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU: + t := ld.Symaddr(r.Sym) + r.Add + return applyrel(ctxt.Arch, r, s, val, t), true + case objabi.R_CALLMIPS, objabi.R_JMPMIPS: + t := ld.Symaddr(r.Sym) + r.Add + + if t&3 != 0 { + ld.Errorf(s, "direct call is not aligned: %s %x", r.Sym.Name, t) + } + + // check if target address is in the same 256 MB region as the next instruction + if (s.Value+int64(r.Off)+4)&0xf0000000 != (t & 0xf0000000) { + ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t) + } + + return applyrel(ctxt.Arch, r, s, val, t), true + case objabi.R_ADDRMIPSTLS: + // thread pointer is at 0x7000 offset from the start of TLS data area + t := ld.Symaddr(r.Sym) + r.Add - 0x7000 + if t < -32768 || t >= 32678 { + ld.Errorf(s, "TLS offset out of range %d", t) + } + return applyrel(ctxt.Arch, r, s, val, t), true + } + + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + return -1 +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sections[0] + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + for _, sect = range ld.Segtext.Sections[1:] { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if !*ld.FlagS { + if !ctxt.IsELF { + ld.Errorf(nil, "unsupported executable format") + } + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) + + ctxt.Out.SeekSet(int64(symo)) + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + default: + ld.Errorf(nil, "unsupported operating system") + case objabi.Hlinux: + ld.Asmbelf(ctxt, int64(symo)) + } + + ctxt.Out.Flush() + if *ld.FlagC { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/oldlink/internal/mips/l.go b/src/cmd/oldlink/internal/mips/l.go new file mode 100644 index 0000000000..adbde40f7c --- /dev/null +++ b/src/cmd/oldlink/internal/mips/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2016 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2016 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + MaxAlign = 32 // max data alignment + MinAlign = 1 // min data alignment + FuncAlign = 4 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + DWARFREGSP = 29 + DWARFREGLR = 31 +) diff --git a/src/cmd/oldlink/internal/mips/obj.go b/src/cmd/oldlink/internal/mips/obj.go new file mode 100644 index 0000000000..c80824ff14 --- /dev/null +++ b/src/cmd/oldlink/internal/mips/obj.go @@ -0,0 +1,89 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2016 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchMIPS + if objabi.GOARCH == "mipsle" { + arch = sys.ArchMIPSLE + } + + theArch := ld.Arch{ + Funcalign: FuncAlign, + Maxalign: MaxAlign, + Minalign: MinAlign, + Dwarfregsp: DWARFREGSP, + Dwarfreglr: DWARFREGLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Asmb: asmb, + Asmb2: asmb2, + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib/ld.so.1", + + Freebsddynld: "XXX", + Openbsddynld: "XXX", + Netbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + case objabi.Hlinux: /* mips elf */ + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + } +} diff --git a/src/cmd/oldlink/internal/mips64/asm.go b/src/cmd/oldlink/internal/mips64/asm.go new file mode 100644 index 0000000000..775b74e1fd --- /dev/null +++ b/src/cmd/oldlink/internal/mips64/asm.go @@ -0,0 +1,278 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "debug/elf" + "fmt" + "log" +) + +func gentext(ctxt *ld.Link) {} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + log.Fatalf("adddynrel not implemented") + return false +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + // mips64 ELF relocation (endian neutral) + // offset uint64 + // sym uint32 + // ssym uint8 + // type3 uint8 + // type2 uint8 + // type uint8 + // addend int64 + + ctxt.Out.Write64(uint64(sectoff)) + + elfsym := r.Xsym.ElfsymForReloc() + ctxt.Out.Write32(uint32(elfsym)) + ctxt.Out.Write8(0) + ctxt.Out.Write8(0) + ctxt.Out.Write8(0) + switch r.Type { + default: + return false + case objabi.R_ADDR: + switch r.Siz { + case 4: + ctxt.Out.Write8(uint8(elf.R_MIPS_32)) + case 8: + ctxt.Out.Write8(uint8(elf.R_MIPS_64)) + default: + return false + } + case objabi.R_ADDRMIPS: + ctxt.Out.Write8(uint8(elf.R_MIPS_LO16)) + case objabi.R_ADDRMIPSU: + ctxt.Out.Write8(uint8(elf.R_MIPS_HI16)) + case objabi.R_ADDRMIPSTLS: + ctxt.Out.Write8(uint8(elf.R_MIPS_TLS_TPREL_LO16)) + case objabi.R_CALLMIPS, + objabi.R_JMPMIPS: + ctxt.Out.Write8(uint8(elf.R_MIPS_26)) + } + ctxt.Out.Write64(uint64(r.Xadd)) + + return true +} + +func elfsetupplt(ctxt *ld.Link) { + return +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + return false +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if ctxt.LinkMode == ld.LinkExternal { + switch r.Type { + default: + return val, false + case objabi.R_ADDRMIPS, + objabi.R_ADDRMIPSU: + r.Done = false + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil { + ld.Errorf(s, "missing section for %s", rs.Name) + } + r.Xsym = rs + + return val, true + case objabi.R_ADDRMIPSTLS, + objabi.R_CALLMIPS, + objabi.R_JMPMIPS: + r.Done = false + r.Xsym = r.Sym + r.Xadd = r.Add + return val, true + } + } + + switch r.Type { + case objabi.R_CONST: + return r.Add, true + case objabi.R_GOTOFF: + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + case objabi.R_ADDRMIPS, + objabi.R_ADDRMIPSU: + t := ld.Symaddr(r.Sym) + r.Add + o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:]) + if r.Type == objabi.R_ADDRMIPS { + return int64(o1&0xffff0000 | uint32(t)&0xffff), true + } + return int64(o1&0xffff0000 | uint32((t+1<<15)>>16)&0xffff), true + case objabi.R_ADDRMIPSTLS: + // thread pointer is at 0x7000 offset from the start of TLS data area + t := ld.Symaddr(r.Sym) + r.Add - 0x7000 + if t < -32768 || t >= 32678 { + ld.Errorf(s, "TLS offset out of range %d", t) + } + o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:]) + return int64(o1&0xffff0000 | uint32(t)&0xffff), true + case objabi.R_CALLMIPS, + objabi.R_JMPMIPS: + // Low 26 bits = (S + A) >> 2 + t := ld.Symaddr(r.Sym) + r.Add + o1 := ctxt.Arch.ByteOrder.Uint32(s.P[r.Off:]) + return int64(o1&0xfc000000 | uint32(t>>2)&^0xfc000000), true + } + + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + return -1 +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sections[0] + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + for _, sect = range ld.Segtext.Sections[1:] { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + if ld.Segrelrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if !*ld.FlagS { + // TODO: rationalize + switch ctxt.HeadType { + default: + if ctxt.IsELF { + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) + } + + case objabi.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + } + + ctxt.Out.SeekSet(int64(symo)) + switch ctxt.HeadType { + default: + if ctxt.IsELF { + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + case objabi.Hplan9: + ld.Asmplan9sym(ctxt) + ctxt.Out.Flush() + + sym := ctxt.Syms.Lookup("pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + ctxt.Out.Write(sym.P) + ctxt.Out.Flush() + } + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + default: + case objabi.Hplan9: /* plan 9 */ + magic := uint32(4*18*18 + 7) + if ctxt.Arch == sys.ArchMIPS64LE { + magic = uint32(4*26*26 + 7) + } + ctxt.Out.Write32(magic) /* magic */ + ctxt.Out.Write32(uint32(ld.Segtext.Filelen)) /* sizes */ + ctxt.Out.Write32(uint32(ld.Segdata.Filelen)) + ctxt.Out.Write32(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ctxt.Out.Write32(uint32(ld.Symsize)) /* nsyms */ + ctxt.Out.Write32(uint32(ld.Entryvalue(ctxt))) /* va of entry */ + ctxt.Out.Write32(0) + ctxt.Out.Write32(uint32(ld.Lcsize)) + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Asmbelf(ctxt, int64(symo)) + } + + ctxt.Out.Flush() + if *ld.FlagC { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/oldlink/internal/mips64/l.go b/src/cmd/oldlink/internal/mips64/l.go new file mode 100644 index 0000000000..d794122f0b --- /dev/null +++ b/src/cmd/oldlink/internal/mips64/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips64 + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 8 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 29 + dwarfRegLR = 31 +) diff --git a/src/cmd/oldlink/internal/mips64/obj.go b/src/cmd/oldlink/internal/mips64/obj.go new file mode 100644 index 0000000000..1ddce45c06 --- /dev/null +++ b/src/cmd/oldlink/internal/mips64/obj.go @@ -0,0 +1,98 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchMIPS64 + if objabi.GOARCH == "mips64le" { + arch = sys.ArchMIPS64LE + } + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Asmb: asmb, + Asmb2: asmb2, + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib64/ld64.so.1", + Freebsddynld: "XXX", + Openbsddynld: "XXX", + Netbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 16*1024 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 16 * 1024 + } + + case objabi.Hlinux: /* mips64 elf */ + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + } +} diff --git a/src/cmd/link/internal/objfile/objfile.go b/src/cmd/oldlink/internal/objfile/objfile.go index 295acb2d29..3a59f6a624 100644 --- a/src/cmd/link/internal/objfile/objfile.go +++ b/src/cmd/oldlink/internal/objfile/objfile.go @@ -13,10 +13,11 @@ import ( "bytes" "cmd/internal/bio" "cmd/internal/dwarf" + "cmd/internal/goobj2" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/sys" - "cmd/link/internal/sym" + "cmd/oldlink/internal/sym" "fmt" "io" "log" @@ -117,6 +118,9 @@ func (r *objReader) loadObjFile() { var buf [8]uint8 r.readFull(buf[:]) if string(buf[:]) != startmagic { + if string(buf[:]) == goobj2.Magic { + log.Fatalf("found object file %s in new format, but -go115newobj is false\nset -go115newobj consistently in all -gcflags, -asmflags, and -ldflags", r.pn) + } log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", r.pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]) } diff --git a/src/cmd/oldlink/internal/ppc64/asm.go b/src/cmd/oldlink/internal/ppc64/asm.go new file mode 100644 index 0000000000..608bdecca6 --- /dev/null +++ b/src/cmd/oldlink/internal/ppc64/asm.go @@ -0,0 +1,1181 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "debug/elf" + "encoding/binary" + "fmt" + "log" + "strings" +) + +func genplt(ctxt *ld.Link) { + // The ppc64 ABI PLT has similar concepts to other + // architectures, but is laid out quite differently. When we + // see an R_PPC64_REL24 relocation to a dynamic symbol + // (indicating that the call needs to go through the PLT), we + // generate up to three stubs and reserve a PLT slot. + // + // 1) The call site will be bl x; nop (where the relocation + // applies to the bl). We rewrite this to bl x_stub; ld + // r2,24(r1). The ld is necessary because x_stub will save + // r2 (the TOC pointer) at 24(r1) (the "TOC save slot"). + // + // 2) We reserve space for a pointer in the .plt section (once + // per referenced dynamic function). .plt is a data + // section filled solely by the dynamic linker (more like + // .plt.got on other architectures). Initially, the + // dynamic linker will fill each slot with a pointer to the + // corresponding x@plt entry point. + // + // 3) We generate the "call stub" x_stub (once per dynamic + // function/object file pair). This saves the TOC in the + // TOC save slot, reads the function pointer from x's .plt + // slot and calls it like any other global entry point + // (including setting r12 to the function address). + // + // 4) We generate the "symbol resolver stub" x@plt (once per + // dynamic function). This is solely a branch to the glink + // resolver stub. + // + // 5) We generate the glink resolver stub (only once). This + // computes which symbol resolver stub we came through and + // invokes the dynamic resolver via a pointer provided by + // the dynamic linker. This will patch up the .plt slot to + // point directly at the function so future calls go + // straight from the call stub to the real function, and + // then call the function. + + // NOTE: It's possible we could make ppc64 closer to other + // architectures: ppc64's .plt is like .plt.got on other + // platforms and ppc64's .glink is like .plt on other + // platforms. + + // Find all R_PPC64_REL24 relocations that reference dynamic + // imports. Reserve PLT entries for these symbols and + // generate call stubs. The call stubs need to live in .text, + // which is why we need to do this pass this early. + // + // This assumes "case 1" from the ABI, where the caller needs + // us to save and restore the TOC pointer. + var stubs []*sym.Symbol + for _, s := range ctxt.Textp { + for i := range s.R { + r := &s.R[i] + if r.Type != objabi.ElfRelocOffset+objabi.RelocType(elf.R_PPC64_REL24) || r.Sym.Type != sym.SDYNIMPORT { + continue + } + + // Reserve PLT entry and generate symbol + // resolver + addpltsym(ctxt, r.Sym) + + // Generate call stub + n := fmt.Sprintf("%s.%s", s.Name, r.Sym.Name) + + stub := ctxt.Syms.Lookup(n, 0) + if s.Attr.Reachable() { + stub.Attr |= sym.AttrReachable + } + if stub.Size == 0 { + // Need outer to resolve .TOC. + stub.Outer = s + stubs = append(stubs, stub) + gencallstub(ctxt, 1, stub, r.Sym) + } + + // Update the relocation to use the call stub + r.Sym = stub + + // Restore TOC after bl. The compiler put a + // nop here for us to overwrite. + const o1 = 0xe8410018 // ld r2,24(r1) + ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1) + } + } + // Put call stubs at the beginning (instead of the end). + // So when resolving the relocations to calls to the stubs, + // the addresses are known and trampolines can be inserted + // when necessary. + ctxt.Textp = append(stubs, ctxt.Textp...) +} + +func genaddmoduledata(ctxt *ld.Link) { + addmoduledata := ctxt.Syms.ROLookup("runtime.addmoduledata", sym.SymVerABI0) + if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin { + return + } + addmoduledata.Attr |= sym.AttrReachable + initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0) + initfunc.Type = sym.STEXT + initfunc.Attr |= sym.AttrLocal + initfunc.Attr |= sym.AttrReachable + o := func(op uint32) { + initfunc.AddUint32(ctxt.Arch, op) + } + // addis r2, r12, .TOC.-func@ha + rel := initfunc.AddRel() + rel.Off = int32(initfunc.Size) + rel.Siz = 8 + rel.Sym = ctxt.Syms.Lookup(".TOC.", 0) + rel.Sym.Attr |= sym.AttrReachable + rel.Type = objabi.R_ADDRPOWER_PCREL + o(0x3c4c0000) + // addi r2, r2, .TOC.-func@l + o(0x38420000) + // mflr r31 + o(0x7c0802a6) + // stdu r31, -32(r1) + o(0xf801ffe1) + // addis r3, r2, local.moduledata@got@ha + rel = initfunc.AddRel() + rel.Off = int32(initfunc.Size) + rel.Siz = 8 + if s := ctxt.Syms.ROLookup("local.moduledata", 0); s != nil { + rel.Sym = s + } else if s := ctxt.Syms.ROLookup("local.pluginmoduledata", 0); s != nil { + rel.Sym = s + } else { + rel.Sym = ctxt.Syms.Lookup("runtime.firstmoduledata", 0) + } + rel.Sym.Attr |= sym.AttrReachable + rel.Sym.Attr |= sym.AttrLocal + rel.Type = objabi.R_ADDRPOWER_GOT + o(0x3c620000) + // ld r3, local.moduledata@got@l(r3) + o(0xe8630000) + // bl runtime.addmoduledata + rel = initfunc.AddRel() + rel.Off = int32(initfunc.Size) + rel.Siz = 4 + rel.Sym = addmoduledata + rel.Type = objabi.R_CALLPOWER + o(0x48000001) + // nop + o(0x60000000) + // ld r31, 0(r1) + o(0xe8010000) + // mtlr r31 + o(0x7c0803a6) + // addi r1,r1,32 + o(0x38210020) + // blr + o(0x4e800020) + + if ctxt.BuildMode == ld.BuildModePlugin { + ctxt.Textp = append(ctxt.Textp, addmoduledata) + } + initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0) + ctxt.Textp = append(ctxt.Textp, initfunc) + initarray_entry.Attr |= sym.AttrReachable + initarray_entry.Attr |= sym.AttrLocal + initarray_entry.Type = sym.SINITARR + initarray_entry.AddAddr(ctxt.Arch, initfunc) +} + +func gentext(ctxt *ld.Link) { + if ctxt.DynlinkingGo() { + genaddmoduledata(ctxt) + } + + if ctxt.LinkMode == ld.LinkInternal { + genplt(ctxt) + } +} + +// Construct a call stub in stub that calls symbol targ via its PLT +// entry. +func gencallstub(ctxt *ld.Link, abicase int, stub *sym.Symbol, targ *sym.Symbol) { + if abicase != 1 { + // If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC + // relocations, we'll need to implement cases 2 and 3. + log.Fatalf("gencallstub only implements case 1 calls") + } + + plt := ctxt.Syms.Lookup(".plt", 0) + + stub.Type = sym.STEXT + + // Save TOC pointer in TOC save slot + stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1) + + // Load the function pointer from the PLT. + r := stub.AddRel() + + r.Off = int32(stub.Size) + r.Sym = plt + r.Add = int64(targ.Plt()) + r.Siz = 2 + if ctxt.Arch.ByteOrder == binary.BigEndian { + r.Off += int32(r.Siz) + } + r.Type = objabi.R_POWER_TOC + r.Variant = sym.RV_POWER_HA + stub.AddUint32(ctxt.Arch, 0x3d820000) // addis r12,r2,targ@plt@toc@ha + r = stub.AddRel() + r.Off = int32(stub.Size) + r.Sym = plt + r.Add = int64(targ.Plt()) + r.Siz = 2 + if ctxt.Arch.ByteOrder == binary.BigEndian { + r.Off += int32(r.Siz) + } + r.Type = objabi.R_POWER_TOC + r.Variant = sym.RV_POWER_LO + stub.AddUint32(ctxt.Arch, 0xe98c0000) // ld r12,targ@plt@toc@l(r12) + + // Jump to the loaded pointer + stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12 + stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr +} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + if ctxt.IsELF { + return addelfdynrel(ctxt, s, r) + } else if ctxt.HeadType == objabi.Haix { + return ld.Xcoffadddynrel(ctxt, s, r) + } + return false +} +func addelfdynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + targ := r.Sym + r.InitExt() + + switch r.Type { + default: + if r.Type >= objabi.ElfRelocOffset { + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24): + r.Type = objabi.R_CALLPOWER + + // This is a local call, so the caller isn't setting + // up r12 and r2 is the same for the caller and + // callee. Hence, we need to go to the local entry + // point. (If we don't do this, the callee will try + // to use r12 to compute r2.) + r.Add += int64(r.Sym.Localentry()) * 4 + + if targ.Type == sym.SDYNIMPORT { + // Should have been handled in elfsetupplt + ld.Errorf(s, "unexpected R_PPC64_REL24 for dyn import") + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC_REL32): + r.Type = objabi.R_PCREL + r.Add += 4 + + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_PPC_REL32 for dyn import") + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_ADDR64): + r.Type = objabi.R_ADDR + if targ.Type == sym.SDYNIMPORT { + // These happen in .toc sections + ld.Adddynsym(ctxt, targ) + + rela := ctxt.Syms.Lookup(".rela", 0) + rela.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(targ.Dynid), uint32(elf.R_PPC64_ADDR64))) + rela.AddUint64(ctxt.Arch, uint64(r.Add)) + r.Type = objabi.ElfRelocOffset // ignore during relocsym + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16): + r.Type = objabi.R_POWER_TOC + r.Variant = sym.RV_POWER_LO | sym.RV_CHECK_OVERFLOW + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO): + r.Type = objabi.R_POWER_TOC + r.Variant = sym.RV_POWER_LO + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HA): + r.Type = objabi.R_POWER_TOC + r.Variant = sym.RV_POWER_HA | sym.RV_CHECK_OVERFLOW + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HI): + r.Type = objabi.R_POWER_TOC + r.Variant = sym.RV_POWER_HI | sym.RV_CHECK_OVERFLOW + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_DS): + r.Type = objabi.R_POWER_TOC + r.Variant = sym.RV_POWER_DS | sym.RV_CHECK_OVERFLOW + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO_DS): + r.Type = objabi.R_POWER_TOC + r.Variant = sym.RV_POWER_DS + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_LO): + r.Type = objabi.R_PCREL + r.Variant = sym.RV_POWER_LO + r.Add += 2 // Compensate for relocation size of 2 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HI): + r.Type = objabi.R_PCREL + r.Variant = sym.RV_POWER_HI | sym.RV_CHECK_OVERFLOW + r.Add += 2 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HA): + r.Type = objabi.R_PCREL + r.Variant = sym.RV_POWER_HA | sym.RV_CHECK_OVERFLOW + r.Add += 2 + return true + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != sym.SDYNIMPORT { + return true + } + + // TODO(austin): Translate our relocations to ELF + + return false +} + +func xcoffreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + rs := r.Xsym + + emitReloc := func(v uint16, off uint64) { + out.Write64(uint64(sectoff) + off) + out.Write32(uint32(rs.Dynid)) + out.Write16(v) + } + + var v uint16 + switch r.Type { + default: + return false + case objabi.R_ADDR: + v = ld.XCOFF_R_POS + if r.Siz == 4 { + v |= 0x1F << 8 + } else { + v |= 0x3F << 8 + } + emitReloc(v, 0) + case objabi.R_ADDRPOWER_TOCREL: + case objabi.R_ADDRPOWER_TOCREL_DS: + emitReloc(ld.XCOFF_R_TOCU|(0x0F<<8), 2) + emitReloc(ld.XCOFF_R_TOCL|(0x0F<<8), 6) + case objabi.R_POWER_TLS_LE: + emitReloc(ld.XCOFF_R_TLS_LE|0x0F<<8, 2) + case objabi.R_CALLPOWER: + if r.Siz != 4 { + return false + } + emitReloc(ld.XCOFF_R_RBR|0x19<<8, 0) + case objabi.R_XCOFFREF: + emitReloc(ld.XCOFF_R_REF|0x3F<<8, 0) + + } + return true + +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + // Beware that bit0~bit15 start from the third byte of a instruction in Big-Endian machines. + if r.Type == objabi.R_ADDR || r.Type == objabi.R_POWER_TLS || r.Type == objabi.R_CALLPOWER { + } else { + if ctxt.Arch.ByteOrder == binary.BigEndian { + sectoff += 2 + } + } + ctxt.Out.Write64(uint64(sectoff)) + + elfsym := r.Xsym.ElfsymForReloc() + switch r.Type { + default: + return false + case objabi.R_ADDR: + switch r.Siz { + case 4: + ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR32) | uint64(elfsym)<<32) + case 8: + ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR64) | uint64(elfsym)<<32) + default: + return false + } + case objabi.R_POWER_TLS: + ctxt.Out.Write64(uint64(elf.R_PPC64_TLS) | uint64(elfsym)<<32) + case objabi.R_POWER_TLS_LE: + ctxt.Out.Write64(uint64(elf.R_PPC64_TPREL16) | uint64(elfsym)<<32) + case objabi.R_POWER_TLS_IE: + ctxt.Out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_HA) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_LO_DS) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER: + ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR16_LO) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_DS: + ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_PPC64_ADDR16_LO_DS) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_GOT: + ctxt.Out.Write64(uint64(elf.R_PPC64_GOT16_HA) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_PPC64_GOT16_LO_DS) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_PCREL: + ctxt.Out.Write64(uint64(elf.R_PPC64_REL16_HA) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_PPC64_REL16_LO) | uint64(elfsym)<<32) + r.Xadd += 4 + case objabi.R_ADDRPOWER_TOCREL: + ctxt.Out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_PPC64_TOC16_LO) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_TOCREL_DS: + ctxt.Out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32) + ctxt.Out.Write64(uint64(r.Xadd)) + ctxt.Out.Write64(uint64(sectoff + 4)) + ctxt.Out.Write64(uint64(elf.R_PPC64_TOC16_LO_DS) | uint64(elfsym)<<32) + case objabi.R_CALLPOWER: + if r.Siz != 4 { + return false + } + ctxt.Out.Write64(uint64(elf.R_PPC64_REL24) | uint64(elfsym)<<32) + + } + ctxt.Out.Write64(uint64(r.Xadd)) + + return true +} + +func elfsetupplt(ctxt *ld.Link) { + plt := ctxt.Syms.Lookup(".plt", 0) + if plt.Size == 0 { + // The dynamic linker stores the address of the + // dynamic resolver and the DSO identifier in the two + // doublewords at the beginning of the .plt section + // before the PLT array. Reserve space for these. + plt.Size = 16 + } +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + return false +} + +// Return the value of .TOC. for symbol s +func symtoc(ctxt *ld.Link, s *sym.Symbol) int64 { + var toc *sym.Symbol + + if s.Outer != nil { + toc = ctxt.Syms.ROLookup(".TOC.", int(s.Outer.Version)) + } else { + toc = ctxt.Syms.ROLookup(".TOC.", int(s.Version)) + } + + if toc == nil { + ld.Errorf(s, "TOC-relative relocation in object without .TOC.") + return 0 + } + + return toc.Value +} + +// archreloctoc relocates a TOC relative symbol. +// If the symbol pointed by this TOC relative symbol is in .data or .bss, the +// default load instruction can be changed to an addi instruction and the +// symbol address can be used directly. +// This code is for AIX only. +func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 { + if ctxt.HeadType == objabi.Hlinux { + ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name) + } + var o1, o2 uint32 + + o1 = uint32(val >> 32) + o2 = uint32(val) + + var t int64 + useAddi := false + const prefix = "TOC." + var tarSym *sym.Symbol + if strings.HasPrefix(r.Sym.Name, prefix) { + tarSym = r.Sym.R[0].Sym + } else { + ld.Errorf(s, "archreloctoc called for a symbol without TOC anchor") + } + + if ctxt.LinkMode == ld.LinkInternal && tarSym != nil && tarSym.Attr.Reachable() && (tarSym.Sect.Seg == &ld.Segdata) { + t = ld.Symaddr(tarSym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value + // change ld to addi in the second instruction + o2 = (o2 & 0x03FF0000) | 0xE<<26 + useAddi = true + } else { + t = ld.Symaddr(r.Sym) + r.Add - ctxt.Syms.ROLookup("TOC", 0).Value + } + + if t != int64(int32(t)) { + ld.Errorf(s, "TOC relocation for %s is too big to relocate %s: 0x%x", s.Name, r.Sym, t) + } + + if t&0x8000 != 0 { + t += 0x10000 + } + + o1 |= uint32((t >> 16) & 0xFFFF) + + switch r.Type { + case objabi.R_ADDRPOWER_TOCREL_DS: + if useAddi { + o2 |= uint32(t) & 0xFFFF + } else { + if t&3 != 0 { + ld.Errorf(s, "bad DS reloc for %s: %d", s.Name, ld.Symaddr(r.Sym)) + } + o2 |= uint32(t) & 0xFFFC + } + default: + return -1 + } + + return int64(o1)<<32 | int64(o2) +} + +// archrelocaddr relocates a symbol address. +// This code is for AIX only. +func archrelocaddr(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 { + if ctxt.HeadType == objabi.Haix { + ld.Errorf(s, "archrelocaddr called for %s relocation\n", r.Sym.Name) + } + var o1, o2 uint32 + if ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = uint32(val >> 32) + o2 = uint32(val) + } else { + o1 = uint32(val) + o2 = uint32(val >> 32) + } + + // We are spreading a 31-bit address across two instructions, putting the + // high (adjusted) part in the low 16 bits of the first instruction and the + // low part in the low 16 bits of the second instruction, or, in the DS case, + // bits 15-2 (inclusive) of the address into bits 15-2 of the second + // instruction (it is an error in this case if the low 2 bits of the address + // are non-zero). + + t := ld.Symaddr(r.Sym) + r.Add + if t < 0 || t >= 1<<31 { + ld.Errorf(s, "relocation for %s is too big (>=2G): 0x%x", s.Name, ld.Symaddr(r.Sym)) + } + if t&0x8000 != 0 { + t += 0x10000 + } + + switch r.Type { + case objabi.R_ADDRPOWER: + o1 |= (uint32(t) >> 16) & 0xffff + o2 |= uint32(t) & 0xffff + case objabi.R_ADDRPOWER_DS: + o1 |= (uint32(t) >> 16) & 0xffff + if t&3 != 0 { + ld.Errorf(s, "bad DS reloc for %s: %d", s.Name, ld.Symaddr(r.Sym)) + } + o2 |= uint32(t) & 0xfffc + default: + return -1 + } + + if ctxt.Arch.ByteOrder == binary.BigEndian { + return int64(o1)<<32 | int64(o2) + } + return int64(o2)<<32 | int64(o1) +} + +// resolve direct jump relocation r in s, and add trampoline if necessary +func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) { + + // Trampolines are created if the branch offset is too large and the linker cannot insert a call stub to handle it. + // For internal linking, trampolines are always created for long calls. + // For external linking, the linker can insert a call stub to handle a long call, but depends on having the TOC address in + // r2. For those build modes with external linking where the TOC address is not maintained in r2, trampolines must be created. + if ctxt.LinkMode == ld.LinkExternal && (ctxt.DynlinkingGo() || ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE) { + // No trampolines needed since r2 contains the TOC + return + } + + t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off)) + switch r.Type { + case objabi.R_CALLPOWER: + + // If branch offset is too far then create a trampoline. + + if (ctxt.LinkMode == ld.LinkExternal && s.Sect != r.Sym.Sect) || (ctxt.LinkMode == ld.LinkInternal && int64(int32(t<<6)>>6) != t) || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) { + var tramp *sym.Symbol + for i := 0; ; i++ { + + // Using r.Add as part of the name is significant in functions like duffzero where the call + // target is at some offset within the function. Calls to duff+8 and duff+256 must appear as + // distinct trampolines. + + name := r.Sym.Name + if r.Add == 0 { + name = name + fmt.Sprintf("-tramp%d", i) + } else { + name = name + fmt.Sprintf("%+x-tramp%d", r.Add, i) + } + + // Look up the trampoline in case it already exists + + tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version)) + if tramp.Value == 0 { + break + } + + t = ld.Symaddr(tramp) + r.Add - (s.Value + int64(r.Off)) + + // With internal linking, the trampoline can be used if it is not too far. + // With external linking, the trampoline must be in this section for it to be reused. + if (ctxt.LinkMode == ld.LinkInternal && int64(int32(t<<6)>>6) == t) || (ctxt.LinkMode == ld.LinkExternal && s.Sect == tramp.Sect) { + break + } + } + if tramp.Type == 0 { + if ctxt.DynlinkingGo() || ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE { + // Should have returned for above cases + ld.Errorf(s, "unexpected trampoline for shared or dynamic linking\n") + } else { + ctxt.AddTramp(tramp) + gentramp(ctxt, tramp, r.Sym, r.Add) + } + } + r.Sym = tramp + r.Add = 0 // This was folded into the trampoline target address + r.Done = false + } + default: + ld.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + } +} + +func gentramp(ctxt *ld.Link, tramp, target *sym.Symbol, offset int64) { + tramp.Size = 16 // 4 instructions + tramp.P = make([]byte, tramp.Size) + t := ld.Symaddr(target) + offset + var o1, o2 uint32 + + if ctxt.HeadType == objabi.Haix { + // On AIX, the address is retrieved with a TOC symbol. + // For internal linking, the "Linux" way might still be used. + // However, all text symbols are accessed with a TOC symbol as + // text relocations aren't supposed to be possible. + // So, keep using the external linking way to be more AIX friendly. + o1 = uint32(0x3fe20000) // lis r2, toctargetaddr hi + o2 = uint32(0xebff0000) // ld r31, toctargetaddr lo + + toctramp := ctxt.Syms.Lookup("TOC."+tramp.Name, 0) + toctramp.Type = sym.SXCOFFTOC + toctramp.Attr |= sym.AttrReachable + toctramp.AddAddr(ctxt.Arch, target) + + tr := tramp.AddRel() + tr.Off = 0 + tr.Type = objabi.R_ADDRPOWER_TOCREL_DS + tr.Siz = 8 // generates 2 relocations: HA + LO + tr.Sym = toctramp + tr.Add = offset + } else { + // Used for default build mode for an executable + // Address of the call target is generated using + // relocation and doesn't depend on r2 (TOC). + o1 = uint32(0x3fe00000) // lis r31,targetaddr hi + o2 = uint32(0x3bff0000) // addi r31,targetaddr lo + + // With external linking, the target address must be + // relocated using LO and HA + if ctxt.LinkMode == ld.LinkExternal { + tr := tramp.AddRel() + tr.Off = 0 + tr.Type = objabi.R_ADDRPOWER + tr.Siz = 8 // generates 2 relocations: HA + LO + tr.Sym = target + tr.Add = offset + + } else { + // adjustment needed if lo has sign bit set + // when using addi to compute address + val := uint32((t & 0xffff0000) >> 16) + if t&0x8000 != 0 { + val += 1 + } + o1 |= val // hi part of addr + o2 |= uint32(t & 0xffff) // lo part of addr + } + } + + o3 := uint32(0x7fe903a6) // mtctr r31 + o4 := uint32(0x4e800420) // bctr + ctxt.Arch.ByteOrder.PutUint32(tramp.P, o1) + ctxt.Arch.ByteOrder.PutUint32(tramp.P[4:], o2) + ctxt.Arch.ByteOrder.PutUint32(tramp.P[8:], o3) + ctxt.Arch.ByteOrder.PutUint32(tramp.P[12:], o4) +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if ctxt.LinkMode == ld.LinkExternal { + // On AIX, relocations (except TLS ones) must be also done to the + // value with the current addresses. + switch r.Type { + default: + if ctxt.HeadType != objabi.Haix { + return val, false + } + case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE: + r.Done = false + // check Outer is nil, Type is TLSBSS? + r.Xadd = r.Add + r.Xsym = r.Sym + return val, true + case objabi.R_ADDRPOWER, + objabi.R_ADDRPOWER_DS, + objabi.R_ADDRPOWER_TOCREL, + objabi.R_ADDRPOWER_TOCREL_DS, + objabi.R_ADDRPOWER_GOT, + objabi.R_ADDRPOWER_PCREL: + r.Done = false + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Type != sym.SUNDEFEXT && rs.Sect == nil { + ld.Errorf(s, "missing section for %s", rs.Name) + } + r.Xsym = rs + + if ctxt.HeadType != objabi.Haix { + return val, true + } + case objabi.R_CALLPOWER: + r.Done = false + r.Xsym = r.Sym + r.Xadd = r.Add + if ctxt.HeadType != objabi.Haix { + return val, true + } + } + } + + switch r.Type { + case objabi.R_CONST: + return r.Add, true + case objabi.R_GOTOFF: + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS: + return archreloctoc(ctxt, r, s, val), true + case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS: + return archrelocaddr(ctxt, r, s, val), true + case objabi.R_CALLPOWER: + // Bits 6 through 29 = (S + A - P) >> 2 + + t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off)) + + if t&3 != 0 { + ld.Errorf(s, "relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t) + } + // If branch offset is too far then create a trampoline. + + if int64(int32(t<<6)>>6) != t { + ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t) + } + return val | int64(uint32(t)&^0xfc000003), true + case objabi.R_POWER_TOC: // S + A - .TOC. + return ld.Symaddr(r.Sym) + r.Add - symtoc(ctxt, s), true + + case objabi.R_POWER_TLS_LE: + // The thread pointer points 0x7000 bytes after the start of the + // thread local storage area as documented in section "3.7.2 TLS + // Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI + // Specification". + v := r.Sym.Value - 0x7000 + if ctxt.HeadType == objabi.Haix { + // On AIX, the thread pointer points 0x7800 bytes after + // the TLS. + v -= 0x800 + } + if int64(int16(v)) != v { + ld.Errorf(s, "TLS offset out of range %d", v) + } + return (val &^ 0xffff) | (v & 0xffff), true + } + + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + switch r.Variant & sym.RV_TYPE_MASK { + default: + ld.Errorf(s, "unexpected relocation variant %d", r.Variant) + fallthrough + + case sym.RV_NONE: + return t + + case sym.RV_POWER_LO: + if r.Variant&sym.RV_CHECK_OVERFLOW != 0 { + // Whether to check for signed or unsigned + // overflow depends on the instruction + var o1 uint32 + if ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = binary.BigEndian.Uint32(s.P[r.Off-2:]) + } else { + o1 = binary.LittleEndian.Uint32(s.P[r.Off:]) + } + switch o1 >> 26 { + case 24, // ori + 26, // xori + 28: // andi + if t>>16 != 0 { + goto overflow + } + + default: + if int64(int16(t)) != t { + goto overflow + } + } + } + + return int64(int16(t)) + + case sym.RV_POWER_HA: + t += 0x8000 + fallthrough + + // Fallthrough + case sym.RV_POWER_HI: + t >>= 16 + + if r.Variant&sym.RV_CHECK_OVERFLOW != 0 { + // Whether to check for signed or unsigned + // overflow depends on the instruction + var o1 uint32 + if ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = binary.BigEndian.Uint32(s.P[r.Off-2:]) + } else { + o1 = binary.LittleEndian.Uint32(s.P[r.Off:]) + } + switch o1 >> 26 { + case 25, // oris + 27, // xoris + 29: // andis + if t>>16 != 0 { + goto overflow + } + + default: + if int64(int16(t)) != t { + goto overflow + } + } + } + + return int64(int16(t)) + + case sym.RV_POWER_DS: + var o1 uint32 + if ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = uint32(binary.BigEndian.Uint16(s.P[r.Off:])) + } else { + o1 = uint32(binary.LittleEndian.Uint16(s.P[r.Off:])) + } + if t&3 != 0 { + ld.Errorf(s, "relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t) + } + if (r.Variant&sym.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t { + goto overflow + } + return int64(o1)&0x3 | int64(int16(t)) + } + +overflow: + ld.Errorf(s, "relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t) + return t +} + +func addpltsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ctxt.IsELF { + plt := ctxt.Syms.Lookup(".plt", 0) + rela := ctxt.Syms.Lookup(".rela.plt", 0) + if plt.Size == 0 { + elfsetupplt(ctxt) + } + + // Create the glink resolver if necessary + glink := ensureglinkresolver(ctxt) + + // Write symbol resolver stub (just a branch to the + // glink resolver stub) + r := glink.AddRel() + + r.Sym = glink + r.Off = int32(glink.Size) + r.Siz = 4 + r.Type = objabi.R_CALLPOWER + glink.AddUint32(ctxt.Arch, 0x48000000) // b .glink + + // In the ppc64 ABI, the dynamic linker is responsible + // for writing the entire PLT. We just need to + // reserve 8 bytes for each PLT entry and generate a + // JMP_SLOT dynamic relocation for it. + // + // TODO(austin): ABI v1 is different + s.SetPlt(int32(plt.Size)) + + plt.Size += 8 + + rela.AddAddrPlus(ctxt.Arch, plt, int64(s.Plt())) + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_PPC64_JMP_SLOT))) + rela.AddUint64(ctxt.Arch, 0) + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +// Generate the glink resolver stub if necessary and return the .glink section +func ensureglinkresolver(ctxt *ld.Link) *sym.Symbol { + glink := ctxt.Syms.Lookup(".glink", 0) + if glink.Size != 0 { + return glink + } + + // This is essentially the resolver from the ppc64 ELF ABI. + // At entry, r12 holds the address of the symbol resolver stub + // for the target routine and the argument registers hold the + // arguments for the target routine. + // + // This stub is PIC, so first get the PC of label 1 into r11. + // Other things will be relative to this. + glink.AddUint32(ctxt.Arch, 0x7c0802a6) // mflr r0 + glink.AddUint32(ctxt.Arch, 0x429f0005) // bcl 20,31,1f + glink.AddUint32(ctxt.Arch, 0x7d6802a6) // 1: mflr r11 + glink.AddUint32(ctxt.Arch, 0x7c0803a6) // mtlf r0 + + // Compute the .plt array index from the entry point address. + // Because this is PIC, everything is relative to label 1b (in + // r11): + // r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4 + glink.AddUint32(ctxt.Arch, 0x3800ffd0) // li r0,-(res_0-1b)=-48 + glink.AddUint32(ctxt.Arch, 0x7c006214) // add r0,r0,r12 + glink.AddUint32(ctxt.Arch, 0x7c0b0050) // sub r0,r0,r11 + glink.AddUint32(ctxt.Arch, 0x7800f082) // srdi r0,r0,2 + + // r11 = address of the first byte of the PLT + r := glink.AddRel() + + r.Off = int32(glink.Size) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Siz = 8 + r.Type = objabi.R_ADDRPOWER + + glink.AddUint32(ctxt.Arch, 0x3d600000) // addis r11,0,.plt@ha + glink.AddUint32(ctxt.Arch, 0x396b0000) // addi r11,r11,.plt@l + + // Load r12 = dynamic resolver address and r11 = DSO + // identifier from the first two doublewords of the PLT. + glink.AddUint32(ctxt.Arch, 0xe98b0000) // ld r12,0(r11) + glink.AddUint32(ctxt.Arch, 0xe96b0008) // ld r11,8(r11) + + // Jump to the dynamic resolver + glink.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12 + glink.AddUint32(ctxt.Arch, 0x4e800420) // bctr + + // The symbol resolvers must immediately follow. + // res_0: + + // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes + // before the first symbol resolver stub. + s := ctxt.Syms.Lookup(".dynamic", 0) + + ld.Elfwritedynentsymplus(ctxt, s, ld.DT_PPC64_GLINK, glink, glink.Size-32) + + return glink +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + for _, sect := range ld.Segtext.Sections { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + // Handle additional text sections with Codeblk + if sect.Name == ".text" { + ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } else { + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + if ld.Segrelrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if !*ld.FlagS { + // TODO: rationalize + switch ctxt.HeadType { + default: + if ctxt.IsELF { + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) + } + + case objabi.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + + case objabi.Haix: + // Nothing to do + } + + ctxt.Out.SeekSet(int64(symo)) + switch ctxt.HeadType { + default: + if ctxt.IsELF { + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + case objabi.Hplan9: + ld.Asmplan9sym(ctxt) + ctxt.Out.Flush() + + sym := ctxt.Syms.Lookup("pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + ctxt.Out.Write(sym.P) + ctxt.Out.Flush() + } + + case objabi.Haix: + // symtab must be added once sections have been created in ld.Asmbxcoff + ctxt.Out.Flush() + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + default: + case objabi.Hplan9: /* plan 9 */ + ctxt.Out.Write32(0x647) /* magic */ + ctxt.Out.Write32(uint32(ld.Segtext.Filelen)) /* sizes */ + ctxt.Out.Write32(uint32(ld.Segdata.Filelen)) + ctxt.Out.Write32(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ctxt.Out.Write32(uint32(ld.Symsize)) /* nsyms */ + ctxt.Out.Write32(uint32(ld.Entryvalue(ctxt))) /* va of entry */ + ctxt.Out.Write32(0) + ctxt.Out.Write32(uint32(ld.Lcsize)) + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Asmbelf(ctxt, int64(symo)) + + case objabi.Haix: + fileoff := uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + fileoff = uint32(ld.Rnd(int64(fileoff), int64(*ld.FlagRound))) + ld.Asmbxcoff(ctxt, int64(fileoff)) + } + + ctxt.Out.Flush() + if *ld.FlagC { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/oldlink/internal/ppc64/l.go b/src/cmd/oldlink/internal/ppc64/l.go new file mode 100644 index 0000000000..c78535be58 --- /dev/null +++ b/src/cmd/oldlink/internal/ppc64/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 16 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 1 + dwarfRegLR = 65 +) diff --git a/src/cmd/oldlink/internal/ppc64/obj.go b/src/cmd/oldlink/internal/ppc64/obj.go new file mode 100644 index 0000000000..e9da5a36d5 --- /dev/null +++ b/src/cmd/oldlink/internal/ppc64/obj.go @@ -0,0 +1,106 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchPPC64 + if objabi.GOARCH == "ppc64le" { + arch = sys.ArchPPC64LE + } + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Asmb: asmb, + Asmb2: asmb2, + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Trampoline: trampoline, + Machoreloc1: machoreloc1, + Xcoffreloc1: xcoffreloc1, + + // TODO(austin): ABI v1 uses /usr/lib/ld.so.1, + Linuxdynld: "/lib64/ld64.so.1", + + Freebsddynld: "XXX", + Openbsddynld: "XXX", + Netbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4128 + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hlinux: /* ppc64 elf */ + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + + case objabi.Haix: + ld.Xcoffinit(ctxt) + } +} diff --git a/src/cmd/oldlink/internal/riscv64/asm.go b/src/cmd/oldlink/internal/riscv64/asm.go new file mode 100644 index 0000000000..f4db32df8a --- /dev/null +++ b/src/cmd/oldlink/internal/riscv64/asm.go @@ -0,0 +1,168 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package riscv64 + +import ( + "cmd/internal/obj/riscv" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "fmt" + "log" +) + +func gentext(ctxt *ld.Link) { +} + +func adddynrela(ctxt *ld.Link, rel *sym.Symbol, s *sym.Symbol, r *sym.Reloc) { + log.Fatalf("adddynrela not implemented") +} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + log.Fatalf("adddynrel not implemented") + return false +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + log.Fatalf("elfreloc1") + return false +} + +func elfsetupplt(ctxt *ld.Link) { + log.Fatalf("elfsetuplt") +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + log.Fatalf("machoreloc1 not implemented") + return false +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + switch r.Type { + case objabi.R_CALLRISCV: + // Nothing to do. + return val, true + + case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE: + pc := s.Value + int64(r.Off) + off := ld.Symaddr(r.Sym) + r.Add - pc + + // Generate AUIPC and second instruction immediates. + low, high, err := riscv.Split32BitImmediate(off) + if err != nil { + ld.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32-bits: %d", off) + } + + auipcImm, err := riscv.EncodeUImmediate(high) + if err != nil { + ld.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", r.Sym.Name, err) + } + + var secondImm, secondImmMask int64 + switch r.Type { + case objabi.R_RISCV_PCREL_ITYPE: + secondImmMask = riscv.ITypeImmMask + secondImm, err = riscv.EncodeIImmediate(low) + if err != nil { + ld.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", r.Sym.Name, err) + } + case objabi.R_RISCV_PCREL_STYPE: + secondImmMask = riscv.STypeImmMask + secondImm, err = riscv.EncodeSImmediate(low) + if err != nil { + ld.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", r.Sym.Name, err) + } + default: + panic(fmt.Sprintf("Unknown relocation type: %v", r.Type)) + } + + auipc := int64(uint32(val)) + second := int64(uint32(val >> 32)) + + auipc = (auipc &^ riscv.UTypeImmMask) | int64(uint32(auipcImm)) + second = (second &^ secondImmMask) | int64(uint32(secondImm)) + + return second<<32 | auipc, true + } + + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + log.Fatalf("archrelocvariant") + return -1 +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sections[0] + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + for _, sect = range ld.Segtext.Sections[1:] { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + if ld.Segrelrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + ld.Symsize = 0 + ld.Lcsize = 0 + symo := uint32(0) + + if !*ld.FlagS { + if !ctxt.IsELF { + ld.Errorf(nil, "unsupported executable format") + } + + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) + ctxt.Out.SeekSet(int64(symo)) + + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + case objabi.Hlinux: + ld.Asmbelf(ctxt, int64(symo)) + default: + ld.Errorf(nil, "unsupported operating system") + } + ctxt.Out.Flush() + + if *ld.FlagC { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/oldlink/internal/riscv64/l.go b/src/cmd/oldlink/internal/riscv64/l.go new file mode 100644 index 0000000000..a302657726 --- /dev/null +++ b/src/cmd/oldlink/internal/riscv64/l.go @@ -0,0 +1,14 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package riscv64 + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 + funcAlign = 8 + + dwarfRegLR = 1 + dwarfRegSP = 2 +) diff --git a/src/cmd/oldlink/internal/riscv64/obj.go b/src/cmd/oldlink/internal/riscv64/obj.go new file mode 100644 index 0000000000..a6a5adb86c --- /dev/null +++ b/src/cmd/oldlink/internal/riscv64/obj.go @@ -0,0 +1,60 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package riscv64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchRISCV64 + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Asmb: asmb, + Asmb2: asmb2, + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib/ld.so.1", + + Freebsddynld: "XXX", + Netbsddynld: "XXX", + Openbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + case objabi.Hlinux: + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + } +} diff --git a/src/cmd/oldlink/internal/s390x/asm.go b/src/cmd/oldlink/internal/s390x/asm.go new file mode 100644 index 0000000000..6c5744dc95 --- /dev/null +++ b/src/cmd/oldlink/internal/s390x/asm.go @@ -0,0 +1,574 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "debug/elf" + "fmt" +) + +// gentext generates assembly to append the local moduledata to the global +// moduledata linked list at initialization time. This is only done if the runtime +// is in a different module. +// +// <go.link.addmoduledata>: +// larl %r2, <local.moduledata> +// jg <runtime.addmoduledata@plt> +// undef +// +// The job of appending the moduledata is delegated to runtime.addmoduledata. +func gentext(ctxt *ld.Link) { + if !ctxt.DynlinkingGo() { + return + } + addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0) + if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin { + // we're linking a module containing the runtime -> no need for + // an init function + return + } + addmoduledata.Attr |= sym.AttrReachable + initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0) + initfunc.Type = sym.STEXT + initfunc.Attr |= sym.AttrLocal + initfunc.Attr |= sym.AttrReachable + + // larl %r2, <local.moduledata> + initfunc.AddUint8(0xc0) + initfunc.AddUint8(0x20) + lmd := initfunc.AddRel() + lmd.InitExt() + lmd.Off = int32(initfunc.Size) + lmd.Siz = 4 + lmd.Sym = ctxt.Moduledata + lmd.Type = objabi.R_PCREL + lmd.Variant = sym.RV_390_DBL + lmd.Add = 2 + int64(lmd.Siz) + initfunc.AddUint32(ctxt.Arch, 0) + + // jg <runtime.addmoduledata[@plt]> + initfunc.AddUint8(0xc0) + initfunc.AddUint8(0xf4) + rel := initfunc.AddRel() + rel.InitExt() + rel.Off = int32(initfunc.Size) + rel.Siz = 4 + rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0) + rel.Type = objabi.R_CALL + rel.Variant = sym.RV_390_DBL + rel.Add = 2 + int64(rel.Siz) + initfunc.AddUint32(ctxt.Arch, 0) + + // undef (for debugging) + initfunc.AddUint32(ctxt.Arch, 0) + if ctxt.BuildMode == ld.BuildModePlugin { + ctxt.Textp = append(ctxt.Textp, addmoduledata) + } + ctxt.Textp = append(ctxt.Textp, initfunc) + initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0) + initarray_entry.Attr |= sym.AttrLocal + initarray_entry.Attr |= sym.AttrReachable + initarray_entry.Type = sym.SINITARR + initarray_entry.AddAddr(ctxt.Arch, initfunc) +} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + targ := r.Sym + r.InitExt() + + switch r.Type { + default: + if r.Type >= objabi.ElfRelocOffset { + ld.Errorf(s, "unexpected relocation type %d", r.Type) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_12), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT12): + ld.Errorf(s, "s390x 12-bit relocations have not been implemented (relocation type %d)", r.Type-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_8), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_16), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_64): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_390_nn relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ADDR + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC64): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_390_PCnn relocation for dynamic symbol %s", targ.Name) + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { + ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + } + r.Type = objabi.R_PCREL + r.Add += int64(r.Siz) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT16), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT64): + ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT16DBL), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32DBL): + r.Type = objabi.R_PCREL + r.Variant = sym.RV_390_DBL + r.Add += int64(r.Siz) + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add += int64(targ.Plt()) + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT64): + r.Type = objabi.R_PCREL + r.Add += int64(r.Siz) + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add += int64(targ.Plt()) + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_COPY): + ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GLOB_DAT): + ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_JMP_SLOT): + ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_RELATIVE): + ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTOFF): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_390_GOTOFF relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_GOTOFF + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPC): + r.Type = objabi.R_PCREL + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(r.Siz) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16DBL), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32DBL): + r.Type = objabi.R_PCREL + r.Variant = sym.RV_390_DBL + r.Add += int64(r.Siz) + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_390_PCnnDBL relocation for dynamic symbol %s", targ.Name) + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPCDBL): + r.Type = objabi.R_PCREL + r.Variant = sym.RV_390_DBL + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(r.Siz) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT): + addgotsym(ctxt, targ) + + r.Type = objabi.R_PCREL + r.Variant = sym.RV_390_DBL + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(targ.Got()) + r.Add += int64(r.Siz) + return true + } + // Handle references to ELF symbols from our own object files. + if targ.Type != sym.SDYNIMPORT { + return true + } + + return false +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + ctxt.Out.Write64(uint64(sectoff)) + + elfsym := r.Xsym.ElfsymForReloc() + switch r.Type { + default: + return false + case objabi.R_TLS_LE: + switch r.Siz { + default: + return false + case 4: + // WARNING - silently ignored by linker in ELF64 + ctxt.Out.Write64(uint64(elf.R_390_TLS_LE32) | uint64(elfsym)<<32) + case 8: + // WARNING - silently ignored by linker in ELF32 + ctxt.Out.Write64(uint64(elf.R_390_TLS_LE64) | uint64(elfsym)<<32) + } + case objabi.R_TLS_IE: + switch r.Siz { + default: + return false + case 4: + ctxt.Out.Write64(uint64(elf.R_390_TLS_IEENT) | uint64(elfsym)<<32) + } + case objabi.R_ADDR: + switch r.Siz { + default: + return false + case 4: + ctxt.Out.Write64(uint64(elf.R_390_32) | uint64(elfsym)<<32) + case 8: + ctxt.Out.Write64(uint64(elf.R_390_64) | uint64(elfsym)<<32) + } + case objabi.R_GOTPCREL: + if r.Siz == 4 { + ctxt.Out.Write64(uint64(elf.R_390_GOTENT) | uint64(elfsym)<<32) + } else { + return false + } + case objabi.R_PCREL, objabi.R_PCRELDBL, objabi.R_CALL: + elfrel := elf.R_390_NONE + isdbl := r.Variant&sym.RV_TYPE_MASK == sym.RV_390_DBL + // TODO(mundaym): all DBL style relocations should be + // signalled using the variant - see issue 14218. + switch r.Type { + case objabi.R_PCRELDBL, objabi.R_CALL: + isdbl = true + } + if r.Xsym.Type == sym.SDYNIMPORT && (r.Xsym.ElfType() == elf.STT_FUNC || r.Type == objabi.R_CALL) { + if isdbl { + switch r.Siz { + case 2: + elfrel = elf.R_390_PLT16DBL + case 4: + elfrel = elf.R_390_PLT32DBL + } + } else { + switch r.Siz { + case 4: + elfrel = elf.R_390_PLT32 + case 8: + elfrel = elf.R_390_PLT64 + } + } + } else { + if isdbl { + switch r.Siz { + case 2: + elfrel = elf.R_390_PC16DBL + case 4: + elfrel = elf.R_390_PC32DBL + } + } else { + switch r.Siz { + case 2: + elfrel = elf.R_390_PC16 + case 4: + elfrel = elf.R_390_PC32 + case 8: + elfrel = elf.R_390_PC64 + } + } + } + if elfrel == elf.R_390_NONE { + return false // unsupported size/dbl combination + } + ctxt.Out.Write64(uint64(elfrel) | uint64(elfsym)<<32) + } + + ctxt.Out.Write64(uint64(r.Xadd)) + return true +} + +func elfsetupplt(ctxt *ld.Link) { + plt := ctxt.Syms.Lookup(".plt", 0) + got := ctxt.Syms.Lookup(".got", 0) + if plt.Size == 0 { + // stg %r1,56(%r15) + plt.AddUint8(0xe3) + plt.AddUint8(0x10) + plt.AddUint8(0xf0) + plt.AddUint8(0x38) + plt.AddUint8(0x00) + plt.AddUint8(0x24) + // larl %r1,_GLOBAL_OFFSET_TABLE_ + plt.AddUint8(0xc0) + plt.AddUint8(0x10) + plt.AddPCRelPlus(ctxt.Arch, got, 6) + // mvc 48(8,%r15),8(%r1) + plt.AddUint8(0xd2) + plt.AddUint8(0x07) + plt.AddUint8(0xf0) + plt.AddUint8(0x30) + plt.AddUint8(0x10) + plt.AddUint8(0x08) + // lg %r1,16(%r1) + plt.AddUint8(0xe3) + plt.AddUint8(0x10) + plt.AddUint8(0x10) + plt.AddUint8(0x10) + plt.AddUint8(0x00) + plt.AddUint8(0x04) + // br %r1 + plt.AddUint8(0x07) + plt.AddUint8(0xf1) + // nopr %r0 + plt.AddUint8(0x07) + plt.AddUint8(0x00) + // nopr %r0 + plt.AddUint8(0x07) + plt.AddUint8(0x00) + // nopr %r0 + plt.AddUint8(0x07) + plt.AddUint8(0x00) + + // assume got->size == 0 too + got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0) + + got.AddUint64(ctxt.Arch, 0) + got.AddUint64(ctxt.Arch, 0) + } +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + return false +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if ctxt.LinkMode == ld.LinkExternal { + return val, false + } + + switch r.Type { + case objabi.R_CONST: + return r.Add, true + case objabi.R_GOTOFF: + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + } + + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + switch r.Variant & sym.RV_TYPE_MASK { + default: + ld.Errorf(s, "unexpected relocation variant %d", r.Variant) + return t + + case sym.RV_NONE: + return t + + case sym.RV_390_DBL: + if (t & 1) != 0 { + ld.Errorf(s, "%s+%v is not 2-byte aligned", r.Sym.Name, r.Sym.Value) + } + return t >> 1 + } +} + +func addpltsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ctxt.IsELF { + plt := ctxt.Syms.Lookup(".plt", 0) + got := ctxt.Syms.Lookup(".got", 0) + rela := ctxt.Syms.Lookup(".rela.plt", 0) + if plt.Size == 0 { + elfsetupplt(ctxt) + } + // larl %r1,_GLOBAL_OFFSET_TABLE_+index + + plt.AddUint8(0xc0) + plt.AddUint8(0x10) + plt.AddPCRelPlus(ctxt.Arch, got, got.Size+6) // need variant? + + // add to got: pointer to current pos in plt + got.AddAddrPlus(ctxt.Arch, plt, plt.Size+8) // weird but correct + // lg %r1,0(%r1) + plt.AddUint8(0xe3) + plt.AddUint8(0x10) + plt.AddUint8(0x10) + plt.AddUint8(0x00) + plt.AddUint8(0x00) + plt.AddUint8(0x04) + // br %r1 + plt.AddUint8(0x07) + plt.AddUint8(0xf1) + // basr %r1,%r0 + plt.AddUint8(0x0d) + plt.AddUint8(0x10) + // lgf %r1,12(%r1) + plt.AddUint8(0xe3) + plt.AddUint8(0x10) + plt.AddUint8(0x10) + plt.AddUint8(0x0c) + plt.AddUint8(0x00) + plt.AddUint8(0x14) + // jg .plt + plt.AddUint8(0xc0) + plt.AddUint8(0xf4) + + plt.AddUint32(ctxt.Arch, uint32(-((plt.Size - 2) >> 1))) // roll-your-own relocation + //.plt index + plt.AddUint32(ctxt.Arch, uint32(rela.Size)) // rela size before current entry + + // rela + rela.AddAddrPlus(ctxt.Arch, got, got.Size-8) + + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_JMP_SLOT))) + rela.AddUint64(ctxt.Arch, 0) + + s.SetPlt(int32(plt.Size - 32)) + + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + got := ctxt.Syms.Lookup(".got", 0) + s.SetGot(int32(got.Size)) + got.AddUint64(ctxt.Arch, 0) + + if ctxt.IsELF { + rela := ctxt.Syms.Lookup(".rela", 0) + rela.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) + rela.AddUint64(ctxt.Arch, ld.ELF64_R_INFO(uint32(s.Dynid), uint32(elf.R_390_GLOB_DAT))) + rela.AddUint64(ctxt.Arch, 0) + } else { + ld.Errorf(s, "addgotsym: unsupported binary format") + } +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sections[0] + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + for _, sect = range ld.Segtext.Sections[1:] { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + if ld.Segrelrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if !*ld.FlagS { + if !ctxt.IsELF { + ld.Errorf(nil, "unsupported executable format") + } + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) + + ctxt.Out.SeekSet(int64(symo)) + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + default: + ld.Errorf(nil, "unsupported operating system") + case objabi.Hlinux: + ld.Asmbelf(ctxt, int64(symo)) + } + + ctxt.Out.Flush() + if *ld.FlagC { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/oldlink/internal/s390x/l.go b/src/cmd/oldlink/internal/s390x/l.go new file mode 100644 index 0000000000..87d10ee782 --- /dev/null +++ b/src/cmd/oldlink/internal/s390x/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 32 // max data alignment + minAlign = 2 // min data alignment + funcAlign = 16 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 15 + dwarfRegLR = 14 +) diff --git a/src/cmd/oldlink/internal/s390x/obj.go b/src/cmd/oldlink/internal/s390x/obj.go new file mode 100644 index 0000000000..b4af86bd75 --- /dev/null +++ b/src/cmd/oldlink/internal/s390x/obj.go @@ -0,0 +1,88 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchS390X + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Asmb: asmb, // in asm.go + Asmb2: asmb2, // in asm.go + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib64/ld64.so.1", + + // not relevant for s390x + Freebsddynld: "XXX", + Openbsddynld: "XXX", + Netbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hlinux: // s390x ELF + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + } +} diff --git a/src/cmd/oldlink/internal/sym/attribute.go b/src/cmd/oldlink/internal/sym/attribute.go new file mode 100644 index 0000000000..4b69bf32d0 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/attribute.go @@ -0,0 +1,117 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +// Attribute is a set of common symbol attributes. +type Attribute int32 + +const ( + // AttrDuplicateOK marks a symbol that can be present in multiple object + // files. + AttrDuplicateOK Attribute = 1 << iota + // AttrExternal marks function symbols loaded from host object files. + AttrExternal + // AttrNoSplit marks functions that cannot split the stack; the linker + // cares because it checks that there are no call chains of nosplit + // functions that require more than StackLimit bytes (see + // lib.go:dostkcheck) + AttrNoSplit + // AttrReachable marks symbols that are transitively referenced from the + // entry points. Unreachable symbols are not written to the output. + AttrReachable + // AttrCgoExportDynamic and AttrCgoExportStatic mark symbols referenced + // by directives written by cgo (in response to //export directives in + // the source). + AttrCgoExportDynamic + AttrCgoExportStatic + // AttrSpecial marks symbols that do not have their address (i.e. Value) + // computed by the usual mechanism of data.go:dodata() & + // data.go:address(). + AttrSpecial + // AttrStackCheck is used by dostkcheck to only check each NoSplit + // function's stack usage once. + AttrStackCheck + // AttrNotInSymbolTable marks symbols that are not written to the symbol table. + AttrNotInSymbolTable + // AttrOnList marks symbols that are on some list (such as the list of + // all text symbols, or one of the lists of data symbols) and is + // consulted to avoid bugs where a symbol is put on a list twice. + AttrOnList + // AttrLocal marks symbols that are only visible within the module + // (executable or shared library) being linked. Only relevant when + // dynamically linking Go code. + AttrLocal + // AttrReflectMethod marks certain methods from the reflect package that + // can be used to call arbitrary methods. If no symbol with this bit set + // is marked as reachable, more dead code elimination can be done. + AttrReflectMethod + // AttrMakeTypelink Amarks types that should be added to the typelink + // table. See typelinks.go:typelinks(). + AttrMakeTypelink + // AttrShared marks symbols compiled with the -shared option. + AttrShared + // AttrVisibilityHidden symbols are ELF symbols with + // visibility set to STV_HIDDEN. They become local symbols in + // the final executable. Only relevant when internally linking + // on an ELF platform. + AttrVisibilityHidden + // AttrSubSymbol mostly means that the symbol appears on the Sub list of some + // other symbol. Unfortunately, it's not 100% reliable; at least, it's not set + // correctly for the .TOC. symbol in Link.dodata. Usually the Outer field of the + // symbol points to the symbol whose list it is on, but that it is not set for the + // symbols added to .windynamic in initdynimport in pe.go. + // + // TODO(mwhudson): fix the inconsistencies noticed above. + // + // Sub lists are used when loading host objects (sections from the host object + // become regular linker symbols and symbols go on the Sub list of their section) + // and for constructing the global offset table when internally linking a dynamic + // executable. + // + // TODO(mwhudson): perhaps a better name for this is AttrNonGoSymbol. + AttrSubSymbol + // AttrContainer is set on text symbols that are present as the .Outer for some + // other symbol. + AttrContainer + // AttrTopFrame means that the function is an entry point and unwinders + // should stop when they hit this function. + AttrTopFrame + // AttrReadOnly indicates whether the symbol's content (Symbol.P) is backed by + // read-only memory. + AttrReadOnly + // 19 attributes defined so far. +) + +func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 } +func (a Attribute) External() bool { return a&AttrExternal != 0 } +func (a Attribute) NoSplit() bool { return a&AttrNoSplit != 0 } +func (a Attribute) Reachable() bool { return a&AttrReachable != 0 } +func (a Attribute) CgoExportDynamic() bool { return a&AttrCgoExportDynamic != 0 } +func (a Attribute) CgoExportStatic() bool { return a&AttrCgoExportStatic != 0 } +func (a Attribute) Special() bool { return a&AttrSpecial != 0 } +func (a Attribute) StackCheck() bool { return a&AttrStackCheck != 0 } +func (a Attribute) NotInSymbolTable() bool { return a&AttrNotInSymbolTable != 0 } +func (a Attribute) OnList() bool { return a&AttrOnList != 0 } +func (a Attribute) Local() bool { return a&AttrLocal != 0 } +func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 } +func (a Attribute) MakeTypelink() bool { return a&AttrMakeTypelink != 0 } +func (a Attribute) Shared() bool { return a&AttrShared != 0 } +func (a Attribute) VisibilityHidden() bool { return a&AttrVisibilityHidden != 0 } +func (a Attribute) SubSymbol() bool { return a&AttrSubSymbol != 0 } +func (a Attribute) Container() bool { return a&AttrContainer != 0 } +func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 } +func (a Attribute) ReadOnly() bool { return a&AttrReadOnly != 0 } + +func (a Attribute) CgoExport() bool { + return a.CgoExportDynamic() || a.CgoExportStatic() +} + +func (a *Attribute) Set(flag Attribute, value bool) { + if value { + *a |= flag + } else { + *a &^= flag + } +} diff --git a/src/cmd/oldlink/internal/sym/compilation_unit.go b/src/cmd/oldlink/internal/sym/compilation_unit.go new file mode 100644 index 0000000000..02fb0cfab8 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/compilation_unit.go @@ -0,0 +1,23 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +import "cmd/internal/dwarf" + +// CompilationUnit is an abstraction used by DWARF to represent a chunk of +// debug-related data. We create a CompilationUnit per Object file in a +// library (so, one for all the Go code, one for each assembly file, etc.). +type CompilationUnit struct { + Pkg string // The package name, eg ("fmt", or "runtime") + Lib *Library // Our library + Consts *Symbol // Package constants DIEs + PCs []dwarf.Range // PC ranges, relative to Textp[0] + DWInfo *dwarf.DWDie // CU root DIE + FuncDIEs []*Symbol // Function DIE subtrees + AbsFnDIEs []*Symbol // Abstract function DIE subtrees + RangeSyms []*Symbol // Symbols for debug_range + Textp []*Symbol // Text symbols in this CU + DWARFFileTable []string // The file table used to generate the .debug_lines +} diff --git a/src/cmd/oldlink/internal/sym/library.go b/src/cmd/oldlink/internal/sym/library.go new file mode 100644 index 0000000000..4f2023b8f7 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/library.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +type Library struct { + Objref string + Srcref string + File string + Pkg string + Shlib string + Hash string + ImportStrings []string + Imports []*Library + Textp []*Symbol // text symbols defined in this library + DupTextSyms []*Symbol // dupok text symbols defined in this library + Main bool + Safe bool + Units []*CompilationUnit +} + +func (l Library) String() string { + return l.Pkg +} diff --git a/src/cmd/oldlink/internal/sym/reloc.go b/src/cmd/oldlink/internal/sym/reloc.go new file mode 100644 index 0000000000..4809db8c80 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/reloc.go @@ -0,0 +1,128 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "debug/elf" +) + +// Reloc is a relocation. +// +// The typical Reloc rewrites part of a symbol at offset Off to address Sym. +// A Reloc is stored in a slice on the Symbol it rewrites. +// +// Relocations are generated by the compiler as the type +// cmd/internal/obj.Reloc, which is encoded into the object file wire +// format and decoded by the linker into this type. A separate type is +// used to hold linker-specific state about the relocation. +// +// Some relocations are created by cmd/link. +type Reloc struct { + Off int32 // offset to rewrite + Siz uint8 // number of bytes to rewrite, 1, 2, or 4 + Done bool // set to true when relocation is complete + Type objabi.RelocType // the relocation type + Add int64 // addend + Sym *Symbol // symbol the relocation addresses + *relocExt // extra fields (see below), may be nil, call InitExt before use +} + +// relocExt contains extra fields in Reloc that are used only in +// certain cases. +type relocExt struct { + Xadd int64 // addend passed to external linker + Xsym *Symbol // symbol passed to external linker + Variant RelocVariant // variation on Type, currently used only on PPC64 and S390X +} + +func (r *Reloc) InitExt() { + if r.relocExt == nil { + r.relocExt = new(relocExt) + } +} + +// RelocVariant is a linker-internal variation on a relocation. +type RelocVariant uint8 + +const ( + RV_NONE RelocVariant = iota + RV_POWER_LO + RV_POWER_HI + RV_POWER_HA + RV_POWER_DS + + // RV_390_DBL is a s390x-specific relocation variant that indicates that + // the value to be placed into the relocatable field should first be + // divided by 2. + RV_390_DBL + + RV_CHECK_OVERFLOW RelocVariant = 1 << 7 + RV_TYPE_MASK RelocVariant = RV_CHECK_OVERFLOW - 1 +) + +func RelocName(arch *sys.Arch, r objabi.RelocType) string { + // We didn't have some relocation types at Go1.4. + // Uncomment code when we include those in bootstrap code. + + switch { + case r >= objabi.MachoRelocOffset: // Mach-O + // nr := (r - objabi.MachoRelocOffset)>>1 + // switch ctxt.Arch.Family { + // case sys.AMD64: + // return macho.RelocTypeX86_64(nr).String() + // case sys.ARM: + // return macho.RelocTypeARM(nr).String() + // case sys.ARM64: + // return macho.RelocTypeARM64(nr).String() + // case sys.I386: + // return macho.RelocTypeGeneric(nr).String() + // default: + // panic("unreachable") + // } + case r >= objabi.ElfRelocOffset: // ELF + nr := r - objabi.ElfRelocOffset + switch arch.Family { + case sys.AMD64: + return elf.R_X86_64(nr).String() + case sys.ARM: + return elf.R_ARM(nr).String() + case sys.ARM64: + return elf.R_AARCH64(nr).String() + case sys.I386: + return elf.R_386(nr).String() + case sys.MIPS, sys.MIPS64: + return elf.R_MIPS(nr).String() + case sys.PPC64: + return elf.R_PPC64(nr).String() + case sys.S390X: + return elf.R_390(nr).String() + default: + panic("unreachable") + } + } + + return r.String() +} + +// RelocByOff implements sort.Interface for sorting relocations by offset. +type RelocByOff []Reloc + +func (x RelocByOff) Len() int { return len(x) } + +func (x RelocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x RelocByOff) Less(i, j int) bool { + a := &x[i] + b := &x[j] + if a.Off < b.Off { + return true + } + if a.Off > b.Off { + return false + } + return false +} diff --git a/src/cmd/oldlink/internal/sym/segment.go b/src/cmd/oldlink/internal/sym/segment.go new file mode 100644 index 0000000000..d5255bf142 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/segment.go @@ -0,0 +1,58 @@ +// Inferno utils/8l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package sym + +// Terrible but standard terminology. +// A segment describes a block of file to load into memory. +// A section further describes the pieces of that block for +// use in debuggers and such. + +type Segment struct { + Rwx uint8 // permission as usual unix bits (5 = r-x etc) + Vaddr uint64 // virtual address + Length uint64 // length in memory + Fileoff uint64 // file offset + Filelen uint64 // length on disk + Sections []*Section +} + +type Section struct { + Rwx uint8 + Extnum int16 + Align int32 + Name string + Vaddr uint64 + Length uint64 + Seg *Segment + Elfsect interface{} // an *ld.ElfShdr + Reloff uint64 + Rellen uint64 +} diff --git a/src/cmd/oldlink/internal/sym/sizeof_test.go b/src/cmd/oldlink/internal/sym/sizeof_test.go new file mode 100644 index 0000000000..e6e3916dad --- /dev/null +++ b/src/cmd/oldlink/internal/sym/sizeof_test.go @@ -0,0 +1,37 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +import ( + "reflect" + "testing" + "unsafe" +) + +// Assert that the size of important structures do not change unexpectedly. + +func TestSizeof(t *testing.T) { + const nbit = unsafe.Sizeof(uintptr(0)) * 8 + const _64bit = nbit == 64 + + var tests = []struct { + val interface{} // type as a value + _32bit uintptr // size on 32bit platforms + _64bit uintptr // size on 64bit platforms + }{ + {Symbol{}, 108, 176}, + } + + for _, tt := range tests { + want := tt._32bit + if _64bit { + want = tt._64bit + } + got := reflect.TypeOf(tt.val).Size() + if want != got { + t.Errorf("%d bit unsafe.Sizeof(%T) = %d, want %d", nbit, tt.val, got, want) + } + } +} diff --git a/src/cmd/oldlink/internal/sym/symbol.go b/src/cmd/oldlink/internal/sym/symbol.go new file mode 100644 index 0000000000..2756acd211 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/symbol.go @@ -0,0 +1,543 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +import ( + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/sys" + "debug/elf" + "fmt" + "log" +) + +// Symbol is an entry in the symbol table. +type Symbol struct { + Name string + Type SymKind + Version int16 + Attr Attribute + Dynid int32 + Align int32 + Elfsym int32 + LocalElfsym int32 + Value int64 + Size int64 + Sub *Symbol + Outer *Symbol + Gotype *Symbol + File string // actually package! + auxinfo *AuxSymbol + Sect *Section + FuncInfo *FuncInfo + Unit *CompilationUnit + // P contains the raw symbol data. + P []byte + R []Reloc +} + +// AuxSymbol contains less-frequently used sym.Symbol fields. +type AuxSymbol struct { + extname string + dynimplib string + dynimpvers string + localentry uint8 + plt int32 + got int32 + // ElfType is set for symbols read from shared libraries by ldshlibsyms. It + // is not set for symbols defined by the packages being linked or by symbols + // read by ldelf (and so is left as elf.STT_NOTYPE). + elftype elf.SymType +} + +const ( + SymVerABI0 = 0 + SymVerABIInternal = 1 + SymVerStatic = 10 // Minimum version used by static (file-local) syms +) + +func ABIToVersion(abi obj.ABI) int { + switch abi { + case obj.ABI0: + return SymVerABI0 + case obj.ABIInternal: + return SymVerABIInternal + } + return -1 +} + +func VersionToABI(v int) (obj.ABI, bool) { + switch v { + case SymVerABI0: + return obj.ABI0, true + case SymVerABIInternal: + return obj.ABIInternal, true + } + return ^obj.ABI(0), false +} + +func (s *Symbol) String() string { + if s.Version == 0 { + return s.Name + } + return fmt.Sprintf("%s<%d>", s.Name, s.Version) +} + +func (s *Symbol) IsFileLocal() bool { + return s.Version >= SymVerStatic +} + +func (s *Symbol) ElfsymForReloc() int32 { + // If putelfsym created a local version of this symbol, use that in all + // relocations. + if s.LocalElfsym != 0 { + return s.LocalElfsym + } else { + return s.Elfsym + } +} + +func (s *Symbol) Length(_ interface{}) int64 { + return s.Size +} + +func (s *Symbol) Grow(siz int64) { + if int64(int(siz)) != siz { + log.Fatalf("symgrow size %d too long", siz) + } + if int64(len(s.P)) >= siz { + return + } + if cap(s.P) < int(siz) { + p := make([]byte, 2*(siz+1)) + s.P = append(p[:0], s.P...) + } + s.P = s.P[:siz] +} + +func (s *Symbol) AddBytes(bytes []byte) int64 { + if s.Type == 0 { + s.Type = SDATA + } + s.Attr |= AttrReachable + s.P = append(s.P, bytes...) + s.Size = int64(len(s.P)) + + return s.Size +} + +func (s *Symbol) AddUint8(v uint8) int64 { + off := s.Size + if s.Type == 0 { + s.Type = SDATA + } + s.Attr |= AttrReachable + s.Size++ + s.P = append(s.P, v) + + return off +} + +func (s *Symbol) AddUint16(arch *sys.Arch, v uint16) int64 { + return s.AddUintXX(arch, uint64(v), 2) +} + +func (s *Symbol) AddUint32(arch *sys.Arch, v uint32) int64 { + return s.AddUintXX(arch, uint64(v), 4) +} + +func (s *Symbol) AddUint64(arch *sys.Arch, v uint64) int64 { + return s.AddUintXX(arch, v, 8) +} + +func (s *Symbol) AddUint(arch *sys.Arch, v uint64) int64 { + return s.AddUintXX(arch, v, arch.PtrSize) +} + +func (s *Symbol) SetUint8(arch *sys.Arch, r int64, v uint8) int64 { + return s.setUintXX(arch, r, uint64(v), 1) +} + +func (s *Symbol) SetUint16(arch *sys.Arch, r int64, v uint16) int64 { + return s.setUintXX(arch, r, uint64(v), 2) +} + +func (s *Symbol) SetUint32(arch *sys.Arch, r int64, v uint32) int64 { + return s.setUintXX(arch, r, uint64(v), 4) +} + +func (s *Symbol) SetUint(arch *sys.Arch, r int64, v uint64) int64 { + return s.setUintXX(arch, r, v, int64(arch.PtrSize)) +} + +func (s *Symbol) addAddrPlus(arch *sys.Arch, t *Symbol, add int64, typ objabi.RelocType) int64 { + if s.Type == 0 { + s.Type = SDATA + } + s.Attr |= AttrReachable + i := s.Size + s.Size += int64(arch.PtrSize) + s.Grow(s.Size) + r := s.AddRel() + r.Sym = t + r.Off = int32(i) + r.Siz = uint8(arch.PtrSize) + r.Type = typ + r.Add = add + return i + int64(r.Siz) +} + +func (s *Symbol) AddAddrPlus(arch *sys.Arch, t *Symbol, add int64) int64 { + return s.addAddrPlus(arch, t, add, objabi.R_ADDR) +} + +func (s *Symbol) AddCURelativeAddrPlus(arch *sys.Arch, t *Symbol, add int64) int64 { + return s.addAddrPlus(arch, t, add, objabi.R_ADDRCUOFF) +} + +func (s *Symbol) AddPCRelPlus(arch *sys.Arch, t *Symbol, add int64) int64 { + if s.Type == 0 { + s.Type = SDATA + } + s.Attr |= AttrReachable + i := s.Size + s.Size += 4 + s.Grow(s.Size) + r := s.AddRel() + r.Sym = t + r.Off = int32(i) + r.Add = add + r.Type = objabi.R_PCREL + r.Siz = 4 + if arch.Family == sys.S390X || arch.Family == sys.PPC64 { + r.InitExt() + } + if arch.Family == sys.S390X { + r.Variant = RV_390_DBL + } + return i + int64(r.Siz) +} + +func (s *Symbol) AddAddr(arch *sys.Arch, t *Symbol) int64 { + return s.AddAddrPlus(arch, t, 0) +} + +func (s *Symbol) SetAddrPlus(arch *sys.Arch, off int64, t *Symbol, add int64) int64 { + if s.Type == 0 { + s.Type = SDATA + } + s.Attr |= AttrReachable + if off+int64(arch.PtrSize) > s.Size { + s.Size = off + int64(arch.PtrSize) + s.Grow(s.Size) + } + + r := s.AddRel() + r.Sym = t + r.Off = int32(off) + r.Siz = uint8(arch.PtrSize) + r.Type = objabi.R_ADDR + r.Add = add + return off + int64(r.Siz) +} + +func (s *Symbol) SetAddr(arch *sys.Arch, off int64, t *Symbol) int64 { + return s.SetAddrPlus(arch, off, t, 0) +} + +func (s *Symbol) AddSize(arch *sys.Arch, t *Symbol) int64 { + if s.Type == 0 { + s.Type = SDATA + } + s.Attr |= AttrReachable + i := s.Size + s.Size += int64(arch.PtrSize) + s.Grow(s.Size) + r := s.AddRel() + r.Sym = t + r.Off = int32(i) + r.Siz = uint8(arch.PtrSize) + r.Type = objabi.R_SIZE + return i + int64(r.Siz) +} + +func (s *Symbol) AddAddrPlus4(t *Symbol, add int64) int64 { + if s.Type == 0 { + s.Type = SDATA + } + s.Attr |= AttrReachable + i := s.Size + s.Size += 4 + s.Grow(s.Size) + r := s.AddRel() + r.Sym = t + r.Off = int32(i) + r.Siz = 4 + r.Type = objabi.R_ADDR + r.Add = add + return i + int64(r.Siz) +} + +func (s *Symbol) AddRel() *Reloc { + s.R = append(s.R, Reloc{}) + return &s.R[len(s.R)-1] +} + +func (s *Symbol) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 { + off := s.Size + s.setUintXX(arch, off, v, int64(wid)) + return off +} + +func (s *Symbol) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 { + if s.Type == 0 { + s.Type = SDATA + } + s.Attr |= AttrReachable + if s.Size < off+wid { + s.Size = off + wid + s.Grow(s.Size) + } + + switch wid { + case 1: + s.P[off] = uint8(v) + case 2: + arch.ByteOrder.PutUint16(s.P[off:], uint16(v)) + case 4: + arch.ByteOrder.PutUint32(s.P[off:], uint32(v)) + case 8: + arch.ByteOrder.PutUint64(s.P[off:], v) + } + + return off + wid +} + +func (s *Symbol) makeAuxInfo() { + if s.auxinfo == nil { + s.auxinfo = &AuxSymbol{extname: s.Name, plt: -1, got: -1} + } +} + +func (s *Symbol) Extname() string { + if s.auxinfo == nil { + return s.Name + } + return s.auxinfo.extname +} + +func (s *Symbol) SetExtname(n string) { + if s.auxinfo == nil { + if s.Name == n { + return + } + s.makeAuxInfo() + } + s.auxinfo.extname = n +} + +func (s *Symbol) Dynimplib() string { + if s.auxinfo == nil { + return "" + } + return s.auxinfo.dynimplib +} + +func (s *Symbol) Dynimpvers() string { + if s.auxinfo == nil { + return "" + } + return s.auxinfo.dynimpvers +} + +func (s *Symbol) SetDynimplib(lib string) { + if s.auxinfo == nil { + s.makeAuxInfo() + } + s.auxinfo.dynimplib = lib +} + +func (s *Symbol) SetDynimpvers(vers string) { + if s.auxinfo == nil { + s.makeAuxInfo() + } + s.auxinfo.dynimpvers = vers +} + +func (s *Symbol) ResetDyninfo() { + if s.auxinfo != nil { + s.auxinfo.dynimplib = "" + s.auxinfo.dynimpvers = "" + } +} + +func (s *Symbol) Localentry() uint8 { + if s.auxinfo == nil { + return 0 + } + return s.auxinfo.localentry +} + +func (s *Symbol) SetLocalentry(val uint8) { + if s.auxinfo == nil { + if val != 0 { + return + } + s.makeAuxInfo() + } + s.auxinfo.localentry = val +} + +func (s *Symbol) Plt() int32 { + if s.auxinfo == nil { + return -1 + } + return s.auxinfo.plt +} + +func (s *Symbol) SetPlt(val int32) { + if s.auxinfo == nil { + if val == -1 { + return + } + s.makeAuxInfo() + } + s.auxinfo.plt = val +} + +func (s *Symbol) Got() int32 { + if s.auxinfo == nil { + return -1 + } + return s.auxinfo.got +} + +func (s *Symbol) SetGot(val int32) { + if s.auxinfo == nil { + if val == -1 { + return + } + s.makeAuxInfo() + } + s.auxinfo.got = val +} + +func (s *Symbol) ElfType() elf.SymType { + if s.auxinfo == nil { + return elf.STT_NOTYPE + } + return s.auxinfo.elftype +} + +func (s *Symbol) SetElfType(val elf.SymType) { + if s.auxinfo == nil { + if val == elf.STT_NOTYPE { + return + } + s.makeAuxInfo() + } + s.auxinfo.elftype = val +} + +// SortSub sorts a linked-list (by Sub) of *Symbol by Value. +// Used for sub-symbols when loading host objects (see e.g. ldelf.go). +func SortSub(l *Symbol) *Symbol { + if l == nil || l.Sub == nil { + return l + } + + l1 := l + l2 := l + for { + l2 = l2.Sub + if l2 == nil { + break + } + l2 = l2.Sub + if l2 == nil { + break + } + l1 = l1.Sub + } + + l2 = l1.Sub + l1.Sub = nil + l1 = SortSub(l) + l2 = SortSub(l2) + + /* set up lead element */ + if l1.Value < l2.Value { + l = l1 + l1 = l1.Sub + } else { + l = l2 + l2 = l2.Sub + } + + le := l + + for { + if l1 == nil { + for l2 != nil { + le.Sub = l2 + le = l2 + l2 = l2.Sub + } + + le.Sub = nil + break + } + + if l2 == nil { + for l1 != nil { + le.Sub = l1 + le = l1 + l1 = l1.Sub + } + + break + } + + if l1.Value < l2.Value { + le.Sub = l1 + le = l1 + l1 = l1.Sub + } else { + le.Sub = l2 + le = l2 + l2 = l2.Sub + } + } + + le.Sub = nil + return l +} + +type FuncInfo struct { + Args int32 + Locals int32 + Pcsp Pcdata + Pcfile Pcdata + Pcline Pcdata + Pcinline Pcdata + Pcdata []Pcdata + Funcdata []*Symbol + Funcdataoff []int64 + File []*Symbol + InlTree []InlinedCall +} + +// InlinedCall is a node in a local inlining tree (FuncInfo.InlTree). +type InlinedCall struct { + Parent int32 // index of parent in InlTree + File *Symbol // file of the inlined call + Line int32 // line number of the inlined call + Func string // name of the function that was inlined + ParentPC int32 // PC of the instruction just before the inlined body (offset from function start) +} + +type Pcdata struct { + P []byte +} diff --git a/src/cmd/oldlink/internal/sym/symbols.go b/src/cmd/oldlink/internal/sym/symbols.go new file mode 100644 index 0000000000..e772496534 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/symbols.go @@ -0,0 +1,135 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package sym + +type Symbols struct { + symbolBatch []Symbol + + // Symbol lookup based on name and indexed by version. + hash []map[string]*Symbol + + Allsym []*Symbol +} + +func NewSymbols() *Symbols { + hash := make([]map[string]*Symbol, SymVerStatic) + // Preallocate about 2mb for hash of non static symbols + hash[0] = make(map[string]*Symbol, 100000) + // And another 1mb for internal ABI text symbols. + hash[SymVerABIInternal] = make(map[string]*Symbol, 50000) + return &Symbols{ + hash: hash, + Allsym: make([]*Symbol, 0, 100000), + } +} + +func (syms *Symbols) Newsym(name string, v int) *Symbol { + batch := syms.symbolBatch + if len(batch) == 0 { + batch = make([]Symbol, 1000) + } + s := &batch[0] + syms.symbolBatch = batch[1:] + + s.Dynid = -1 + s.Name = name + s.Version = int16(v) + syms.Allsym = append(syms.Allsym, s) + + return s +} + +// Look up the symbol with the given name and version, creating the +// symbol if it is not found. +func (syms *Symbols) Lookup(name string, v int) *Symbol { + m := syms.hash[v] + s := m[name] + if s != nil { + return s + } + s = syms.Newsym(name, v) + m[name] = s + return s +} + +// Look up the symbol with the given name and version, returning nil +// if it is not found. +func (syms *Symbols) ROLookup(name string, v int) *Symbol { + return syms.hash[v][name] +} + +// Add an existing symbol to the symbol table. +func (syms *Symbols) Add(s *Symbol) { + name := s.Name + v := int(s.Version) + m := syms.hash[v] + if _, ok := m[name]; ok { + panic(name + " already added") + } + m[name] = s +} + +// Allocate a new version (i.e. symbol namespace). +func (syms *Symbols) IncVersion() int { + syms.hash = append(syms.hash, make(map[string]*Symbol)) + return len(syms.hash) - 1 +} + +// Rename renames a symbol. +func (syms *Symbols) Rename(old, new string, v int, reachparent map[*Symbol]*Symbol) { + s := syms.hash[v][old] + oldExtName := s.Extname() + s.Name = new + if oldExtName == old { + s.SetExtname(new) + } + delete(syms.hash[v], old) + + dup := syms.hash[v][new] + if dup == nil { + syms.hash[v][new] = s + } else { + if s.Type == 0 { + dup.Attr |= s.Attr + if s.Attr.Reachable() && reachparent != nil { + reachparent[dup] = reachparent[s] + } + *s = *dup + } else if dup.Type == 0 { + s.Attr |= dup.Attr + if dup.Attr.Reachable() && reachparent != nil { + reachparent[s] = reachparent[dup] + } + *dup = *s + syms.hash[v][new] = s + } + } +} diff --git a/src/cmd/oldlink/internal/sym/symkind.go b/src/cmd/oldlink/internal/sym/symkind.go new file mode 100644 index 0000000000..1933dd7b21 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/symkind.go @@ -0,0 +1,168 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package sym + +// A SymKind describes the kind of memory represented by a symbol. +type SymKind uint8 + +// Defined SymKind values. +// +// TODO(rsc): Give idiomatic Go names. +//go:generate stringer -type=SymKind +const ( + Sxxx SymKind = iota + STEXT + SELFRXSECT + + // Read-only sections. + STYPE + SSTRING + SGOSTRING + SGOFUNC + SGCBITS + SRODATA + SFUNCTAB + + SELFROSECT + SMACHOPLT + + // Read-only sections with relocations. + // + // Types STYPE-SFUNCTAB above are written to the .rodata section by default. + // When linking a shared object, some conceptually "read only" types need to + // be written to by relocations and putting them in a section called + // ".rodata" interacts poorly with the system linkers. The GNU linkers + // support this situation by arranging for sections of the name + // ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after + // relocations have applied, so when the Go linker is creating a shared + // object it checks all objects of the above types and bumps any object that + // has a relocation to it to the corresponding type below, which are then + // written to sections with appropriate magic names. + STYPERELRO + SSTRINGRELRO + SGOSTRINGRELRO + SGOFUNCRELRO + SGCBITSRELRO + SRODATARELRO + SFUNCTABRELRO + + // Part of .data.rel.ro if it exists, otherwise part of .rodata. + STYPELINK + SITABLINK + SSYMTAB + SPCLNTAB + + // Writable sections. + SFirstWritable + SBUILDINFO + SELFSECT + SMACHO + SMACHOGOT + SWINDOWS + SELFGOT + SNOPTRDATA + SINITARR + SDATA + SXCOFFTOC + SBSS + SNOPTRBSS + SLIBFUZZER_EXTRA_COUNTER + STLSBSS + SXREF + SMACHOSYMSTR + SMACHOSYMTAB + SMACHOINDIRECTPLT + SMACHOINDIRECTGOT + SFILEPATH + SCONST + SDYNIMPORT + SHOSTOBJ + SUNDEFEXT // Undefined symbol for resolution by external linker + + // Sections for debugging information + SDWARFSECT + SDWARFINFO + SDWARFRANGE + SDWARFLOC + SDWARFLINES + + // ABI aliases (these never appear in the output) + SABIALIAS +) + +// AbiSymKindToSymKind maps values read from object files (which are +// of type cmd/internal/objabi.SymKind) to values of type SymKind. +var AbiSymKindToSymKind = [...]SymKind{ + Sxxx, + STEXT, + SRODATA, + SNOPTRDATA, + SDATA, + SBSS, + SNOPTRBSS, + STLSBSS, + SDWARFINFO, + SDWARFRANGE, + SDWARFLOC, + SDWARFLINES, + SABIALIAS, + SLIBFUZZER_EXTRA_COUNTER, +} + +// ReadOnly are the symbol kinds that form read-only sections. In some +// cases, if they will require relocations, they are transformed into +// rel-ro sections using relROMap. +var ReadOnly = []SymKind{ + STYPE, + SSTRING, + SGOSTRING, + SGOFUNC, + SGCBITS, + SRODATA, + SFUNCTAB, +} + +// RelROMap describes the transformation of read-only symbols to rel-ro +// symbols. +var RelROMap = map[SymKind]SymKind{ + STYPE: STYPERELRO, + SSTRING: SSTRINGRELRO, + SGOSTRING: SGOSTRINGRELRO, + SGOFUNC: SGOFUNCRELRO, + SGCBITS: SGCBITSRELRO, + SRODATA: SRODATARELRO, + SFUNCTAB: SFUNCTABRELRO, +} + +// IsData returns true if the type is a data type. +func (t SymKind) IsData() bool { + return t == SDATA || t == SNOPTRDATA || t == SBSS || t == SNOPTRBSS +} diff --git a/src/cmd/oldlink/internal/sym/symkind_string.go b/src/cmd/oldlink/internal/sym/symkind_string.go new file mode 100644 index 0000000000..97af9925d5 --- /dev/null +++ b/src/cmd/oldlink/internal/sym/symkind_string.go @@ -0,0 +1,76 @@ +// Code generated by "stringer -type=SymKind symkind.go"; DO NOT EDIT. + +package sym + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Sxxx-0] + _ = x[STEXT-1] + _ = x[SELFRXSECT-2] + _ = x[STYPE-3] + _ = x[SSTRING-4] + _ = x[SGOSTRING-5] + _ = x[SGOFUNC-6] + _ = x[SGCBITS-7] + _ = x[SRODATA-8] + _ = x[SFUNCTAB-9] + _ = x[SELFROSECT-10] + _ = x[SMACHOPLT-11] + _ = x[STYPERELRO-12] + _ = x[SSTRINGRELRO-13] + _ = x[SGOSTRINGRELRO-14] + _ = x[SGOFUNCRELRO-15] + _ = x[SGCBITSRELRO-16] + _ = x[SRODATARELRO-17] + _ = x[SFUNCTABRELRO-18] + _ = x[STYPELINK-19] + _ = x[SITABLINK-20] + _ = x[SSYMTAB-21] + _ = x[SPCLNTAB-22] + _ = x[SFirstWritable-23] + _ = x[SBUILDINFO-24] + _ = x[SELFSECT-25] + _ = x[SMACHO-26] + _ = x[SMACHOGOT-27] + _ = x[SWINDOWS-28] + _ = x[SELFGOT-29] + _ = x[SNOPTRDATA-30] + _ = x[SINITARR-31] + _ = x[SDATA-32] + _ = x[SXCOFFTOC-33] + _ = x[SBSS-34] + _ = x[SNOPTRBSS-35] + _ = x[SLIBFUZZER_EXTRA_COUNTER-36] + _ = x[STLSBSS-37] + _ = x[SXREF-38] + _ = x[SMACHOSYMSTR-39] + _ = x[SMACHOSYMTAB-40] + _ = x[SMACHOINDIRECTPLT-41] + _ = x[SMACHOINDIRECTGOT-42] + _ = x[SFILEPATH-43] + _ = x[SCONST-44] + _ = x[SDYNIMPORT-45] + _ = x[SHOSTOBJ-46] + _ = x[SUNDEFEXT-47] + _ = x[SDWARFSECT-48] + _ = x[SDWARFINFO-49] + _ = x[SDWARFRANGE-50] + _ = x[SDWARFLOC-51] + _ = x[SDWARFLINES-52] + _ = x[SABIALIAS-53] +} + +const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_EXTRA_COUNTERSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSCONSTSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOCSDWARFLINESSABIALIAS" + +var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 337, 344, 349, 361, 373, 390, 407, 416, 422, 432, 440, 449, 459, 469, 480, 489, 500, 509} + +func (i SymKind) String() string { + if i >= SymKind(len(_SymKind_index)-1) { + return "SymKind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]] +} diff --git a/src/cmd/oldlink/internal/wasm/asm.go b/src/cmd/oldlink/internal/wasm/asm.go new file mode 100644 index 0000000000..35bc7b1c95 --- /dev/null +++ b/src/cmd/oldlink/internal/wasm/asm.go @@ -0,0 +1,583 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package wasm + +import ( + "bytes" + "cmd/internal/objabi" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "io" + "regexp" +) + +const ( + I32 = 0x7F + I64 = 0x7E + F32 = 0x7D + F64 = 0x7C +) + +const ( + sectionCustom = 0 + sectionType = 1 + sectionImport = 2 + sectionFunction = 3 + sectionTable = 4 + sectionMemory = 5 + sectionGlobal = 6 + sectionExport = 7 + sectionStart = 8 + sectionElement = 9 + sectionCode = 10 + sectionData = 11 +) + +// funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly +const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses + +func gentext(ctxt *ld.Link) { +} + +type wasmFunc struct { + Name string + Type uint32 + Code []byte +} + +type wasmFuncType struct { + Params []byte + Results []byte +} + +var wasmFuncTypes = map[string]*wasmFuncType{ + "_rt0_wasm_js": {Params: []byte{}}, // + "wasm_export_run": {Params: []byte{I32, I32}}, // argc, argv + "wasm_export_resume": {Params: []byte{}}, // + "wasm_export_getsp": {Results: []byte{I32}}, // sp + "wasm_pc_f_loop": {Params: []byte{}}, // + "runtime.wasmMove": {Params: []byte{I32, I32, I32}}, // dst, src, len + "runtime.wasmZero": {Params: []byte{I32, I32}}, // ptr, len + "runtime.wasmDiv": {Params: []byte{I64, I64}, Results: []byte{I64}}, // x, y -> x/y + "runtime.wasmTruncS": {Params: []byte{F64}, Results: []byte{I64}}, // x -> int(x) + "runtime.wasmTruncU": {Params: []byte{F64}, Results: []byte{I64}}, // x -> uint(x) + "runtime.gcWriteBarrier": {Params: []byte{I64, I64}}, // ptr, val + "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1 + "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1 + "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0 + "memchr": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // s, c, len -> index +} + +func assignAddress(ctxt *ld.Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) { + // WebAssembly functions do not live in the same address space as the linear memory. + // Instead, WebAssembly automatically assigns indices. Imported functions (section "import") + // have indices 0 to n. They are followed by native functions (sections "function" and "code") + // with indices n+1 and following. + // + // The following rules describe how wasm handles function indices and addresses: + // PC_F = funcValueOffset + WebAssembly function index (not including the imports) + // s.Value = PC = PC_F<<16 + PC_B + // + // The funcValueOffset is necessary to avoid conflicts with expectations + // that the Go runtime has about function addresses. + // The field "s.Value" corresponds to the concept of PC at runtime. + // However, there is no PC register, only PC_F and PC_B. PC_F denotes the function, + // PC_B the resume point inside of that function. The entry of the function has PC_B = 0. + s.Sect = sect + s.Value = int64(funcValueOffset+va/ld.MINFUNC) << 16 // va starts at zero + va += uint64(ld.MINFUNC) + return sect, n, va +} + +func asmb(ctxt *ld.Link) {} // dummy + +// asmb writes the final WebAssembly module binary. +// Spec: https://webassembly.github.io/spec/core/binary/modules.html +func asmb2(ctxt *ld.Link) { + types := []*wasmFuncType{ + // For normal Go functions, the single parameter is PC_B, + // the return value is + // 0 if the function returned normally or + // 1 if the stack needs to be unwound. + {Params: []byte{I32}, Results: []byte{I32}}, + } + + // collect host imports (functions that get imported from the WebAssembly host, usually JavaScript) + hostImports := []*wasmFunc{ + { + Name: "debug", + Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), + }, + } + hostImportMap := make(map[*sym.Symbol]int64) + for _, fn := range ctxt.Textp { + for _, r := range fn.R { + if r.Type == objabi.R_WASMIMPORT { + hostImportMap[r.Sym] = int64(len(hostImports)) + hostImports = append(hostImports, &wasmFunc{ + Name: r.Sym.Name, + Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), + }) + } + } + } + + // collect functions with WebAssembly body + var buildid []byte + fns := make([]*wasmFunc, len(ctxt.Textp)) + for i, fn := range ctxt.Textp { + wfn := new(bytes.Buffer) + if fn.Name == "go.buildid" { + writeUleb128(wfn, 0) // number of sets of locals + writeI32Const(wfn, 0) + wfn.WriteByte(0x0b) // end + buildid = fn.P + } else { + // Relocations have variable length, handle them here. + off := int32(0) + for _, r := range fn.R { + wfn.Write(fn.P[off:r.Off]) + off = r.Off + switch r.Type { + case objabi.R_ADDR: + writeSleb128(wfn, r.Sym.Value+r.Add) + case objabi.R_CALL: + writeSleb128(wfn, int64(len(hostImports))+r.Sym.Value>>16-funcValueOffset) + case objabi.R_WASMIMPORT: + writeSleb128(wfn, hostImportMap[r.Sym]) + default: + ld.Errorf(fn, "bad reloc type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + continue + } + } + wfn.Write(fn.P[off:]) + } + + typ := uint32(0) + if sig, ok := wasmFuncTypes[fn.Name]; ok { + typ = lookupType(sig, &types) + } + + name := nameRegexp.ReplaceAllString(fn.Name, "_") + fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()} + } + + ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic + ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version + + // Add any buildid early in the binary: + if len(buildid) != 0 { + writeBuildID(ctxt, buildid) + } + + writeTypeSec(ctxt, types) + writeImportSec(ctxt, hostImports) + writeFunctionSec(ctxt, fns) + writeTableSec(ctxt, fns) + writeMemorySec(ctxt) + writeGlobalSec(ctxt) + writeExportSec(ctxt, len(hostImports)) + writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns))) + writeCodeSec(ctxt, fns) + writeDataSec(ctxt) + writeProducerSec(ctxt) + if !*ld.FlagS { + writeNameSec(ctxt, len(hostImports), fns) + } + + ctxt.Out.Flush() +} + +func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 { + for i, t := range *types { + if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) { + return uint32(i) + } + } + *types = append(*types, sig) + return uint32(len(*types) - 1) +} + +func writeSecHeader(ctxt *ld.Link, id uint8) int64 { + ctxt.Out.WriteByte(id) + sizeOffset := ctxt.Out.Offset() + ctxt.Out.Write(make([]byte, 5)) // placeholder for length + return sizeOffset +} + +func writeSecSize(ctxt *ld.Link, sizeOffset int64) { + endOffset := ctxt.Out.Offset() + ctxt.Out.SeekSet(sizeOffset) + writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5) + ctxt.Out.SeekSet(endOffset) +} + +func writeBuildID(ctxt *ld.Link, buildid []byte) { + sizeOffset := writeSecHeader(ctxt, sectionCustom) + writeName(ctxt.Out, "go.buildid") + ctxt.Out.Write(buildid) + writeSecSize(ctxt, sizeOffset) +} + +// writeTypeSec writes the section that declares all function types +// so they can be referenced by index. +func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) { + sizeOffset := writeSecHeader(ctxt, sectionType) + + writeUleb128(ctxt.Out, uint64(len(types))) + + for _, t := range types { + ctxt.Out.WriteByte(0x60) // functype + writeUleb128(ctxt.Out, uint64(len(t.Params))) + for _, v := range t.Params { + ctxt.Out.WriteByte(byte(v)) + } + writeUleb128(ctxt.Out, uint64(len(t.Results))) + for _, v := range t.Results { + ctxt.Out.WriteByte(byte(v)) + } + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeImportSec writes the section that lists the functions that get +// imported from the WebAssembly host, usually JavaScript. +func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionImport) + + writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports + for _, fn := range hostImports { + writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js + writeName(ctxt.Out, fn.Name) + ctxt.Out.WriteByte(0x00) // func import + writeUleb128(ctxt.Out, uint64(fn.Type)) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeFunctionSec writes the section that declares the types of functions. +// The bodies of these functions will later be provided in the "code" section. +func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionFunction) + + writeUleb128(ctxt.Out, uint64(len(fns))) + for _, fn := range fns { + writeUleb128(ctxt.Out, uint64(fn.Type)) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeTableSec writes the section that declares tables. Currently there is only a single table +// that is used by the CallIndirect operation to dynamically call any function. +// The contents of the table get initialized by the "element" section. +func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionTable) + + numElements := uint64(funcValueOffset + len(fns)) + writeUleb128(ctxt.Out, 1) // number of tables + ctxt.Out.WriteByte(0x70) // type: anyfunc + ctxt.Out.WriteByte(0x00) // no max + writeUleb128(ctxt.Out, numElements) // min + + writeSecSize(ctxt, sizeOffset) +} + +// writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used. +// Linear memory always starts at address zero. More memory can be requested with the GrowMemory instruction. +func writeMemorySec(ctxt *ld.Link) { + sizeOffset := writeSecHeader(ctxt, sectionMemory) + + dataSection := ctxt.Syms.Lookup("runtime.data", 0).Sect + dataEnd := dataSection.Vaddr + dataSection.Length + var initialSize = dataEnd + 16<<20 // 16MB, enough for runtime init without growing + + const wasmPageSize = 64 << 10 // 64KB + + writeUleb128(ctxt.Out, 1) // number of memories + ctxt.Out.WriteByte(0x00) // no maximum memory size + writeUleb128(ctxt.Out, initialSize/wasmPageSize) // minimum (initial) memory size + + writeSecSize(ctxt, sizeOffset) +} + +// writeGlobalSec writes the section that declares global variables. +func writeGlobalSec(ctxt *ld.Link) { + sizeOffset := writeSecHeader(ctxt, sectionGlobal) + + globalRegs := []byte{ + I32, // 0: SP + I64, // 1: CTXT + I64, // 2: g + I64, // 3: RET0 + I64, // 4: RET1 + I64, // 5: RET2 + I64, // 6: RET3 + I32, // 7: PAUSE + } + + writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals + + for _, typ := range globalRegs { + ctxt.Out.WriteByte(typ) + ctxt.Out.WriteByte(0x01) // var + switch typ { + case I32: + writeI32Const(ctxt.Out, 0) + case I64: + writeI64Const(ctxt.Out, 0) + } + ctxt.Out.WriteByte(0x0b) // end + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeExportSec writes the section that declares exports. +// Exports can be accessed by the WebAssembly host, usually JavaScript. +// The wasm_export_* functions and the linear memory get exported. +func writeExportSec(ctxt *ld.Link, lenHostImports int) { + sizeOffset := writeSecHeader(ctxt, sectionExport) + + writeUleb128(ctxt.Out, 4) // number of exports + + for _, name := range []string{"run", "resume", "getsp"} { + idx := uint32(lenHostImports) + uint32(ctxt.Syms.ROLookup("wasm_export_"+name, 0).Value>>16) - funcValueOffset + writeName(ctxt.Out, name) // inst.exports.run/resume/getsp in wasm_exec.js + ctxt.Out.WriteByte(0x00) // func export + writeUleb128(ctxt.Out, uint64(idx)) // funcidx + } + + writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js + ctxt.Out.WriteByte(0x02) // mem export + writeUleb128(ctxt.Out, 0) // memidx + + writeSecSize(ctxt, sizeOffset) +} + +// writeElementSec writes the section that initializes the tables declared by the "table" section. +// The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value) +// maps linearly to the function index (numImports + PC_F). +func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) { + sizeOffset := writeSecHeader(ctxt, sectionElement) + + writeUleb128(ctxt.Out, 1) // number of element segments + + writeUleb128(ctxt.Out, 0) // tableidx + writeI32Const(ctxt.Out, funcValueOffset) + ctxt.Out.WriteByte(0x0b) // end + + writeUleb128(ctxt.Out, numFns) // number of entries + for i := uint64(0); i < numFns; i++ { + writeUleb128(ctxt.Out, numImports+i) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeElementSec writes the section that provides the function bodies for the functions +// declared by the "func" section. +func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionCode) + + writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries + for _, fn := range fns { + writeUleb128(ctxt.Out, uint64(len(fn.Code))) + ctxt.Out.Write(fn.Code) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeDataSec writes the section that provides data that will be used to initialize the linear memory. +func writeDataSec(ctxt *ld.Link) { + sizeOffset := writeSecHeader(ctxt, sectionData) + + sections := []*sym.Section{ + ctxt.Syms.Lookup("runtime.rodata", 0).Sect, + ctxt.Syms.Lookup("runtime.typelink", 0).Sect, + ctxt.Syms.Lookup("runtime.itablink", 0).Sect, + ctxt.Syms.Lookup("runtime.symtab", 0).Sect, + ctxt.Syms.Lookup("runtime.pclntab", 0).Sect, + ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect, + ctxt.Syms.Lookup("runtime.data", 0).Sect, + } + + type dataSegment struct { + offset int32 + data []byte + } + + // Omit blocks of zeroes and instead emit data segments with offsets skipping the zeroes. + // This reduces the size of the WebAssembly binary. We use 8 bytes as an estimate for the + // overhead of adding a new segment (same as wasm-opt's memory-packing optimization uses). + const segmentOverhead = 8 + + // Generate at most this many segments. A higher number of segments gets rejected by some WebAssembly runtimes. + const maxNumSegments = 100000 + + var segments []*dataSegment + for secIndex, sec := range sections { + data := ld.DatblkBytes(ctxt, int64(sec.Vaddr), int64(sec.Length)) + offset := int32(sec.Vaddr) + + // skip leading zeroes + for len(data) > 0 && data[0] == 0 { + data = data[1:] + offset++ + } + + for len(data) > 0 { + dataLen := int32(len(data)) + var segmentEnd, zeroEnd int32 + if len(segments)+(len(sections)-secIndex) == maxNumSegments { + segmentEnd = dataLen + zeroEnd = dataLen + } else { + for { + // look for beginning of zeroes + for segmentEnd < dataLen && data[segmentEnd] != 0 { + segmentEnd++ + } + // look for end of zeroes + zeroEnd = segmentEnd + for zeroEnd < dataLen && data[zeroEnd] == 0 { + zeroEnd++ + } + // emit segment if omitting zeroes reduces the output size + if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen { + break + } + segmentEnd = zeroEnd + } + } + + segments = append(segments, &dataSegment{ + offset: offset, + data: data[:segmentEnd], + }) + data = data[zeroEnd:] + offset += zeroEnd + } + } + + writeUleb128(ctxt.Out, uint64(len(segments))) // number of data entries + for _, seg := range segments { + writeUleb128(ctxt.Out, 0) // memidx + writeI32Const(ctxt.Out, seg.offset) + ctxt.Out.WriteByte(0x0b) // end + writeUleb128(ctxt.Out, uint64(len(seg.data))) + ctxt.Out.Write(seg.data) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeProducerSec writes an optional section that reports the source language and compiler version. +func writeProducerSec(ctxt *ld.Link) { + sizeOffset := writeSecHeader(ctxt, sectionCustom) + writeName(ctxt.Out, "producers") + + writeUleb128(ctxt.Out, 2) // number of fields + + writeName(ctxt.Out, "language") // field name + writeUleb128(ctxt.Out, 1) // number of values + writeName(ctxt.Out, "Go") // value: name + writeName(ctxt.Out, objabi.Version) // value: version + + writeName(ctxt.Out, "processed-by") // field name + writeUleb128(ctxt.Out, 1) // number of values + writeName(ctxt.Out, "Go cmd/compile") // value: name + writeName(ctxt.Out, objabi.Version) // value: version + + writeSecSize(ctxt, sizeOffset) +} + +var nameRegexp = regexp.MustCompile(`[^\w\.]`) + +// writeNameSec writes an optional section that assigns names to the functions declared by the "func" section. +// The names are only used by WebAssembly stack traces, debuggers and decompilers. +// TODO(neelance): add symbol table of DATA symbols +func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionCustom) + writeName(ctxt.Out, "name") + + sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names + writeUleb128(ctxt.Out, uint64(len(fns))) + for i, fn := range fns { + writeUleb128(ctxt.Out, uint64(firstFnIndex+i)) + writeName(ctxt.Out, fn.Name) + } + writeSecSize(ctxt, sizeOffset2) + + writeSecSize(ctxt, sizeOffset) +} + +type nameWriter interface { + io.ByteWriter + io.Writer +} + +func writeI32Const(w io.ByteWriter, v int32) { + w.WriteByte(0x41) // i32.const + writeSleb128(w, int64(v)) +} + +func writeI64Const(w io.ByteWriter, v int64) { + w.WriteByte(0x42) // i64.const + writeSleb128(w, v) +} + +func writeName(w nameWriter, name string) { + writeUleb128(w, uint64(len(name))) + w.Write([]byte(name)) +} + +func writeUleb128(w io.ByteWriter, v uint64) { + if v < 128 { + w.WriteByte(uint8(v)) + return + } + more := true + for more { + c := uint8(v & 0x7f) + v >>= 7 + more = v != 0 + if more { + c |= 0x80 + } + w.WriteByte(c) + } +} + +func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) { + for i := 0; i < length; i++ { + c := uint8(v & 0x7f) + v >>= 7 + if i < length-1 { + c |= 0x80 + } + w.WriteByte(c) + } + if v != 0 { + panic("writeUleb128FixedLength: length too small") + } +} + +func writeSleb128(w io.ByteWriter, v int64) { + more := true + for more { + c := uint8(v & 0x7f) + s := uint8(v & 0x40) + v >>= 7 + more = !((v == 0 && s == 0) || (v == -1 && s != 0)) + if more { + c |= 0x80 + } + w.WriteByte(c) + } +} diff --git a/src/cmd/oldlink/internal/wasm/obj.go b/src/cmd/oldlink/internal/wasm/obj.go new file mode 100644 index 0000000000..fdc9fb796a --- /dev/null +++ b/src/cmd/oldlink/internal/wasm/obj.go @@ -0,0 +1,35 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package wasm + +import ( + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + theArch := ld.Arch{ + Funcalign: 16, + Maxalign: 32, + Minalign: 1, + + Archinit: archinit, + AssignAddress: assignAddress, + Asmb: asmb, + Asmb2: asmb2, + Gentext: gentext, + } + + return sys.ArchWasm, theArch +} + +func archinit(ctxt *ld.Link) { + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0 + } +} diff --git a/src/cmd/oldlink/internal/x86/asm.go b/src/cmd/oldlink/internal/x86/asm.go new file mode 100644 index 0000000000..34668063da --- /dev/null +++ b/src/cmd/oldlink/internal/x86/asm.go @@ -0,0 +1,745 @@ +// Inferno utils/8l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/sym" + "debug/elf" + "log" +) + +// Append 4 bytes to s and create a R_CALL relocation targeting t to fill them in. +func addcall(ctxt *ld.Link, s *sym.Symbol, t *sym.Symbol) { + s.Attr |= sym.AttrReachable + i := s.Size + s.Size += 4 + s.Grow(s.Size) + r := s.AddRel() + r.Sym = t + r.Off = int32(i) + r.Type = objabi.R_CALL + r.Siz = 4 +} + +func gentext(ctxt *ld.Link) { + if ctxt.DynlinkingGo() { + // We need get_pc_thunk. + } else { + switch ctxt.BuildMode { + case ld.BuildModeCArchive: + if !ctxt.IsELF { + return + } + case ld.BuildModePIE, ld.BuildModeCShared, ld.BuildModePlugin: + // We need get_pc_thunk. + default: + return + } + } + + // Generate little thunks that load the PC of the next instruction into a register. + thunks := make([]*sym.Symbol, 0, 7+len(ctxt.Textp)) + for _, r := range [...]struct { + name string + num uint8 + }{ + {"ax", 0}, + {"cx", 1}, + {"dx", 2}, + {"bx", 3}, + // sp + {"bp", 5}, + {"si", 6}, + {"di", 7}, + } { + thunkfunc := ctxt.Syms.Lookup("__x86.get_pc_thunk."+r.name, 0) + thunkfunc.Type = sym.STEXT + thunkfunc.Attr |= sym.AttrLocal + thunkfunc.Attr |= sym.AttrReachable //TODO: remove? + o := func(op ...uint8) { + for _, op1 := range op { + thunkfunc.AddUint8(op1) + } + } + // 8b 04 24 mov (%esp),%eax + // Destination register is in bits 3-5 of the middle byte, so add that in. + o(0x8b, 0x04+r.num<<3, 0x24) + // c3 ret + o(0xc3) + + thunks = append(thunks, thunkfunc) + } + ctxt.Textp = append(thunks, ctxt.Textp...) // keep Textp in dependency order + + addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0) + if addmoduledata.Type == sym.STEXT && ctxt.BuildMode != ld.BuildModePlugin { + // we're linking a module containing the runtime -> no need for + // an init function + return + } + + addmoduledata.Attr |= sym.AttrReachable + + initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0) + initfunc.Type = sym.STEXT + initfunc.Attr |= sym.AttrLocal + initfunc.Attr |= sym.AttrReachable + o := func(op ...uint8) { + for _, op1 := range op { + initfunc.AddUint8(op1) + } + } + + // go.link.addmoduledata: + // 53 push %ebx + // e8 00 00 00 00 call __x86.get_pc_thunk.cx + R_CALL __x86.get_pc_thunk.cx + // 8d 81 00 00 00 00 lea 0x0(%ecx), %eax + R_PCREL ctxt.Moduledata + // 8d 99 00 00 00 00 lea 0x0(%ecx), %ebx + R_GOTPC _GLOBAL_OFFSET_TABLE_ + // e8 00 00 00 00 call runtime.addmoduledata@plt + R_CALL runtime.addmoduledata + // 5b pop %ebx + // c3 ret + + o(0x53) + + o(0xe8) + addcall(ctxt, initfunc, ctxt.Syms.Lookup("__x86.get_pc_thunk.cx", 0)) + + o(0x8d, 0x81) + initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 6) + + o(0x8d, 0x99) + i := initfunc.Size + initfunc.Size += 4 + initfunc.Grow(initfunc.Size) + r := initfunc.AddRel() + r.Sym = ctxt.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0) + r.Off = int32(i) + r.Type = objabi.R_PCREL + r.Add = 12 + r.Siz = 4 + + o(0xe8) + addcall(ctxt, initfunc, addmoduledata) + + o(0x5b) + + o(0xc3) + + if ctxt.BuildMode == ld.BuildModePlugin { + ctxt.Textp = append(ctxt.Textp, addmoduledata) + } + ctxt.Textp = append(ctxt.Textp, initfunc) + initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0) + initarray_entry.Attr |= sym.AttrReachable + initarray_entry.Attr |= sym.AttrLocal + initarray_entry.Type = sym.SINITARR + initarray_entry.AddAddr(ctxt.Arch, initfunc) +} + +func adddynrel(ctxt *ld.Link, s *sym.Symbol, r *sym.Reloc) bool { + targ := r.Sym + + switch r.Type { + default: + if r.Type >= objabi.ElfRelocOffset { + ld.Errorf(s, "unexpected relocation type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PC32): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", targ.Name) + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (targ.Type == 0 || targ.Type == sym.SXREF) && !targ.Attr.VisibilityHidden() { + ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name) + } + r.Type = objabi.R_PCREL + r.Add += 4 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PLT32): + r.Type = objabi.R_PCREL + r.Add += 4 + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add += int64(targ.Plt()) + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32X): + if targ.Type != sym.SDYNIMPORT { + // have symbol + if r.Off >= 2 && s.P[r.Off-2] == 0x8b { + // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT. + s.P[r.Off-2] = 0x8d + + r.Type = objabi.R_GOTOFF + return true + } + + if r.Off >= 2 && s.P[r.Off-2] == 0xff && s.P[r.Off-1] == 0xb3 { + // turn PUSHL of GOT entry into PUSHL of symbol itself. + // use unnecessary SS prefix to keep instruction same length. + s.P[r.Off-2] = 0x36 + + s.P[r.Off-1] = 0x68 + r.Type = objabi.R_ADDR + return true + } + + ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + return false + } + + addgotsym(ctxt, targ) + r.Type = objabi.R_CONST // write r->add during relocsym + r.Sym = nil + r.Add += int64(targ.Got()) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTOFF): + r.Type = objabi.R_GOTOFF + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC): + r.Type = objabi.R_PCREL + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += 4 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_32): + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", targ.Name) + } + r.Type = objabi.R_ADDR + return true + + case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0: + r.Type = objabi.R_ADDR + if targ.Type == sym.SDYNIMPORT { + ld.Errorf(s, "unexpected reloc for dynamic symbol %s", targ.Name) + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1: + if targ.Type == sym.SDYNIMPORT { + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add = int64(targ.Plt()) + r.Type = objabi.R_PCREL + return true + } + + r.Type = objabi.R_PCREL + return true + + case objabi.MachoRelocOffset + ld.MACHO_FAKE_GOTPCREL: + if targ.Type != sym.SDYNIMPORT { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if r.Off < 2 || s.P[r.Off-2] != 0x8b { + ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + return false + } + + s.P[r.Off-2] = 0x8d + r.Type = objabi.R_PCREL + return true + } + + addgotsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".got", 0) + r.Add += int64(targ.Got()) + r.Type = objabi.R_PCREL + return true + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != sym.SDYNIMPORT { + return true + } + switch r.Type { + case objabi.R_CALL, + objabi.R_PCREL: + if ctxt.LinkMode == ld.LinkExternal { + // External linker will do this relocation. + return true + } + addpltsym(ctxt, targ) + r.Sym = ctxt.Syms.Lookup(".plt", 0) + r.Add = int64(targ.Plt()) + return true + + case objabi.R_ADDR: + if s.Type != sym.SDATA { + break + } + if ctxt.IsELF { + ld.Adddynsym(ctxt, targ) + rel := ctxt.Syms.Lookup(".rel", 0) + rel.AddAddrPlus(ctxt.Arch, s, int64(r.Off)) + rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(targ.Dynid), uint32(elf.R_386_32))) + r.Type = objabi.R_CONST // write r->add during relocsym + r.Sym = nil + return true + } + + if ctxt.HeadType == objabi.Hdarwin && s.Size == int64(ctxt.Arch.PtrSize) && r.Off == 0 { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // just in case the C code assigns to the variable, + // and of course it only works for single pointers, + // but we only need to support cgo and that's all it needs. + ld.Adddynsym(ctxt, targ) + + got := ctxt.Syms.Lookup(".got", 0) + s.Type = got.Type + s.Attr |= sym.AttrSubSymbol + s.Outer = got + s.Sub = got.Sub + got.Sub = s + s.Value = got.Size + got.AddUint32(ctxt.Arch, 0) + ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(targ.Dynid)) + r.Type = objabi.ElfRelocOffset // ignore during relocsym + return true + } + } + + return false +} + +func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool { + ctxt.Out.Write32(uint32(sectoff)) + + elfsym := r.Xsym.ElfsymForReloc() + switch r.Type { + default: + return false + case objabi.R_ADDR: + if r.Siz == 4 { + ctxt.Out.Write32(uint32(elf.R_386_32) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_GOTPCREL: + if r.Siz == 4 { + ctxt.Out.Write32(uint32(elf.R_386_GOTPC)) + if r.Xsym.Name != "_GLOBAL_OFFSET_TABLE_" { + ctxt.Out.Write32(uint32(sectoff)) + ctxt.Out.Write32(uint32(elf.R_386_GOT32) | uint32(elfsym)<<8) + } + } else { + return false + } + case objabi.R_CALL: + if r.Siz == 4 { + if r.Xsym.Type == sym.SDYNIMPORT { + ctxt.Out.Write32(uint32(elf.R_386_PLT32) | uint32(elfsym)<<8) + } else { + ctxt.Out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8) + } + } else { + return false + } + case objabi.R_PCREL: + if r.Siz == 4 { + ctxt.Out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_TLS_LE: + if r.Siz == 4 { + ctxt.Out.Write32(uint32(elf.R_386_TLS_LE) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_TLS_IE: + if r.Siz == 4 { + ctxt.Out.Write32(uint32(elf.R_386_GOTPC)) + ctxt.Out.Write32(uint32(sectoff)) + ctxt.Out.Write32(uint32(elf.R_386_TLS_GOTIE) | uint32(elfsym)<<8) + } else { + return false + } + } + + return true +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + + if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALL { + if rs.Dynid < 0 { + ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type) + return false + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32(rs.Sect.Extnum) + if v == 0 { + ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type) + return false + } + } + + switch r.Type { + default: + return false + case objabi.R_ADDR: + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + case objabi.R_CALL, + objabi.R_PCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + } + + switch r.Siz { + default: + return false + case 1: + v |= 0 << 25 + case 2: + v |= 1 << 25 + case 4: + v |= 2 << 25 + case 8: + v |= 3 << 25 + } + + out.Write32(uint32(sectoff)) + out.Write32(v) + return true +} + +func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + + if rs.Dynid < 0 { + ld.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type) + return false + } + + out.Write32(uint32(sectoff)) + out.Write32(uint32(rs.Dynid)) + + switch r.Type { + default: + return false + + case objabi.R_DWARFSECREF: + v = ld.IMAGE_REL_I386_SECREL + + case objabi.R_ADDR: + v = ld.IMAGE_REL_I386_DIR32 + + case objabi.R_CALL, + objabi.R_PCREL: + v = ld.IMAGE_REL_I386_REL32 + } + + out.Write16(uint16(v)) + + return true +} + +func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) { + if ctxt.LinkMode == ld.LinkExternal { + return val, false + } + switch r.Type { + case objabi.R_CONST: + return r.Add, true + case objabi.R_GOTOFF: + return ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0)), true + } + + return val, false +} + +func archrelocvariant(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func elfsetupplt(ctxt *ld.Link) { + plt := ctxt.Syms.Lookup(".plt", 0) + got := ctxt.Syms.Lookup(".got.plt", 0) + if plt.Size == 0 { + // pushl got+4 + plt.AddUint8(0xff) + + plt.AddUint8(0x35) + plt.AddAddrPlus(ctxt.Arch, got, 4) + + // jmp *got+8 + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddAddrPlus(ctxt.Arch, got, 8) + + // zero pad + plt.AddUint32(ctxt.Arch, 0) + + // assume got->size == 0 too + got.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".dynamic", 0), 0) + + got.AddUint32(ctxt.Arch, 0) + got.AddUint32(ctxt.Arch, 0) + } +} + +func addpltsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Plt() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ctxt.IsELF { + plt := ctxt.Syms.Lookup(".plt", 0) + got := ctxt.Syms.Lookup(".got.plt", 0) + rel := ctxt.Syms.Lookup(".rel.plt", 0) + if plt.Size == 0 { + elfsetupplt(ctxt) + } + + // jmpq *got+size + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddAddrPlus(ctxt.Arch, got, got.Size) + + // add to got: pointer to current pos in plt + got.AddAddrPlus(ctxt.Arch, plt, plt.Size) + + // pushl $x + plt.AddUint8(0x68) + + plt.AddUint32(ctxt.Arch, uint32(rel.Size)) + + // jmp .plt + plt.AddUint8(0xe9) + + plt.AddUint32(ctxt.Arch, uint32(-(plt.Size + 4))) + + // rel + rel.AddAddrPlus(ctxt.Arch, got, got.Size-4) + + rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_JMP_SLOT))) + + s.SetPlt(int32(plt.Size - 16)) + } else if ctxt.HeadType == objabi.Hdarwin { + // Same laziness as in 6l. + + plt := ctxt.Syms.Lookup(".plt", 0) + + addgotsym(ctxt, s) + + ctxt.Syms.Lookup(".linkedit.plt", 0).AddUint32(ctxt.Arch, uint32(s.Dynid)) + + // jmpq *got+size(IP) + s.SetPlt(int32(plt.Size)) + + plt.AddUint8(0xff) + plt.AddUint8(0x25) + plt.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup(".got", 0), int64(s.Got())) + } else { + ld.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *sym.Symbol) { + if s.Got() >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + got := ctxt.Syms.Lookup(".got", 0) + s.SetGot(int32(got.Size)) + got.AddUint32(ctxt.Arch, 0) + + if ctxt.IsELF { + rel := ctxt.Syms.Lookup(".rel", 0) + rel.AddAddrPlus(ctxt.Arch, got, int64(s.Got())) + rel.AddUint32(ctxt.Arch, ld.ELF32_R_INFO(uint32(s.Dynid), uint32(elf.R_386_GLOB_DAT))) + } else if ctxt.HeadType == objabi.Hdarwin { + ctxt.Syms.Lookup(".linkedit.got", 0).AddUint32(ctxt.Arch, uint32(s.Dynid)) + } else { + ld.Errorf(s, "addgotsym: unsupported binary format") + } +} + +func asmb(ctxt *ld.Link) { + if ctxt.IsELF { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sections[0] + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + // 0xCC is INT $3 - breakpoint instruction + ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC}) + for _, sect = range ld.Segtext.Sections[1:] { + ctxt.Out.SeekSet(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + if ld.Segrelrodata.Filelen > 0 { + ctxt.Out.SeekSet(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } + + ctxt.Out.SeekSet(int64(ld.Segdata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) +} + +func asmb2(ctxt *ld.Link) { + machlink := uint32(0) + if ctxt.HeadType == objabi.Hdarwin { + machlink = uint32(ld.Domacholink(ctxt)) + } + + ld.Symsize = 0 + ld.Spsize = 0 + ld.Lcsize = 0 + symo := uint32(0) + if !*ld.FlagS { + // TODO: rationalize + switch ctxt.HeadType { + default: + if ctxt.IsELF { + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) + } + + case objabi.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + + case objabi.Hdarwin: + symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink)) + + case objabi.Hwindows: + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) + symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN)) + } + + ctxt.Out.SeekSet(int64(symo)) + switch ctxt.HeadType { + default: + if ctxt.IsELF { + ld.Asmelfsym(ctxt) + ctxt.Out.Flush() + ctxt.Out.Write(ld.Elfstrdat) + + if ctxt.LinkMode == ld.LinkExternal { + ld.Elfemitreloc(ctxt) + } + } + + case objabi.Hplan9: + ld.Asmplan9sym(ctxt) + ctxt.Out.Flush() + + sym := ctxt.Syms.Lookup("pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + ctxt.Out.Write(sym.P) + ctxt.Out.Flush() + } + + case objabi.Hwindows: + // Do nothing + + case objabi.Hdarwin: + if ctxt.LinkMode == ld.LinkExternal { + ld.Machoemitreloc(ctxt) + } + } + } + + ctxt.Out.SeekSet(0) + switch ctxt.HeadType { + default: + case objabi.Hplan9: /* plan9 */ + magic := int32(4*11*11 + 7) + + ctxt.Out.Write32b(uint32(magic)) /* magic */ + ctxt.Out.Write32b(uint32(ld.Segtext.Filelen)) /* sizes */ + ctxt.Out.Write32b(uint32(ld.Segdata.Filelen)) + ctxt.Out.Write32b(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ctxt.Out.Write32b(uint32(ld.Symsize)) /* nsyms */ + ctxt.Out.Write32b(uint32(ld.Entryvalue(ctxt))) /* va of entry */ + ctxt.Out.Write32b(uint32(ld.Spsize)) /* sp offsets */ + ctxt.Out.Write32b(uint32(ld.Lcsize)) /* line offsets */ + + case objabi.Hdarwin: + ld.Asmbmacho(ctxt) + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Asmbelf(ctxt, int64(symo)) + + case objabi.Hwindows: + ld.Asmbpe(ctxt) + } + + ctxt.Out.Flush() +} diff --git a/src/cmd/oldlink/internal/x86/l.go b/src/cmd/oldlink/internal/x86/l.go new file mode 100644 index 0000000000..0f104eab57 --- /dev/null +++ b/src/cmd/oldlink/internal/x86/l.go @@ -0,0 +1,43 @@ +// Inferno utils/8l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 16 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 4 + dwarfRegLR = 8 +) diff --git a/src/cmd/oldlink/internal/x86/obj.go b/src/cmd/oldlink/internal/x86/obj.go new file mode 100644 index 0000000000..b78ccbaf99 --- /dev/null +++ b/src/cmd/oldlink/internal/x86/obj.go @@ -0,0 +1,113 @@ +// Inferno utils/8l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.Arch386 + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Asmb: asmb, + Asmb2: asmb2, + Elfreloc1: elfreloc1, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + PEreloc1: pereloc1, + + Linuxdynld: "/lib/ld-linux.so.2", + Freebsddynld: "/usr/libexec/ld-elf.so.1", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "/usr/libexec/ld.elf_so", + Solarisdynld: "/lib/ld.so.1", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4096 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hdarwin: /* apple MACH */ + ld.HEADR = ld.INITIAL_MACHO_HEADR + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4096 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hlinux, /* elf32 executable */ + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Elfinit(ctxt) + + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x08048000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hwindows: /* PE executable */ + // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit + return + } +} diff --git a/src/cmd/oldlink/main.go b/src/cmd/oldlink/main.go new file mode 100644 index 0000000000..be1d0fde3b --- /dev/null +++ b/src/cmd/oldlink/main.go @@ -0,0 +1,71 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO(go115newobj): delete. + +package main + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/oldlink/internal/amd64" + "cmd/oldlink/internal/arm" + "cmd/oldlink/internal/arm64" + "cmd/oldlink/internal/ld" + "cmd/oldlink/internal/mips" + "cmd/oldlink/internal/mips64" + "cmd/oldlink/internal/ppc64" + "cmd/oldlink/internal/riscv64" + "cmd/oldlink/internal/s390x" + "cmd/oldlink/internal/wasm" + "cmd/oldlink/internal/x86" + "fmt" + "os" +) + +// The bulk of the linker implementation lives in cmd/oldlink/internal/ld. +// Architecture-specific code lives in cmd/oldlink/internal/GOARCH. +// +// Program initialization: +// +// Before any argument parsing is done, the Init function of relevant +// architecture package is called. The only job done in Init is +// configuration of the architecture-specific variables. +// +// Then control flow passes to ld.Main, which parses flags, makes +// some configuration decisions, and then gives the architecture +// packages a second chance to modify the linker's configuration +// via the ld.Arch.Archinit function. + +func main() { + var arch *sys.Arch + var theArch ld.Arch + + switch objabi.GOARCH { + default: + fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", objabi.GOARCH) + os.Exit(2) + case "386": + arch, theArch = x86.Init() + case "amd64": + arch, theArch = amd64.Init() + case "arm": + arch, theArch = arm.Init() + case "arm64": + arch, theArch = arm64.Init() + case "mips", "mipsle": + arch, theArch = mips.Init() + case "mips64", "mips64le": + arch, theArch = mips64.Init() + case "ppc64", "ppc64le": + arch, theArch = ppc64.Init() + case "riscv64": + arch, theArch = riscv64.Init() + case "s390x": + arch, theArch = s390x.Init() + case "wasm": + arch, theArch = wasm.Init() + } + ld.Main(arch, theArch) +} |