| // 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. | |
| // Writing Go object files. | |
| package obj | |
| import ( | |
| "bytes" | |
| "cmd/internal/bio" | |
| "cmd/internal/goobj2" | |
| "cmd/internal/objabi" | |
| "fmt" | |
| "path/filepath" | |
| "strings" | |
| ) | |
| // Entry point of writing new object file. | |
| func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) { | |
| debugAsmEmit(ctxt) | |
| genFuncInfoSyms(ctxt) | |
| w := writer{ | |
| Writer: goobj2.NewWriter(b), | |
| ctxt: ctxt, | |
| pkgpath: objabi.PathToPrefix(pkgpath), | |
| } | |
| start := b.Offset() | |
| w.init() | |
| // Header | |
| // We just reserve the space. We'll fill in the offsets later. | |
| flags := uint32(0) | |
| if ctxt.Flag_shared { | |
| flags |= goobj2.ObjFlagShared | |
| } | |
| h := goobj2.Header{ | |
| Magic: goobj2.Magic, | |
| Fingerprint: ctxt.Fingerprint, | |
| Flags: flags, | |
| } | |
| h.Write(w.Writer) | |
| // String table | |
| w.StringTable() | |
| // Autolib | |
| h.Offsets[goobj2.BlkAutolib] = w.Offset() | |
| for i := range ctxt.Imports { | |
| ctxt.Imports[i].Write(w.Writer) | |
| } | |
| // Package references | |
| h.Offsets[goobj2.BlkPkgIdx] = w.Offset() | |
| for _, pkg := range w.pkglist { | |
| w.StringRef(pkg) | |
| } | |
| // DWARF file table | |
| h.Offsets[goobj2.BlkDwarfFile] = w.Offset() | |
| for _, f := range ctxt.PosTable.DebugLinesFileTable() { | |
| w.StringRef(filepath.ToSlash(f)) | |
| } | |
| // Symbol definitions | |
| h.Offsets[goobj2.BlkSymdef] = w.Offset() | |
| for _, s := range ctxt.defs { | |
| w.Sym(s) | |
| } | |
| // Non-pkg symbol definitions | |
| h.Offsets[goobj2.BlkNonpkgdef] = w.Offset() | |
| for _, s := range ctxt.nonpkgdefs { | |
| w.Sym(s) | |
| } | |
| // Non-pkg symbol references | |
| h.Offsets[goobj2.BlkNonpkgref] = w.Offset() | |
| for _, s := range ctxt.nonpkgrefs { | |
| w.Sym(s) | |
| } | |
| // Reloc indexes | |
| h.Offsets[goobj2.BlkRelocIdx] = w.Offset() | |
| nreloc := uint32(0) | |
| lists := [][]*LSym{ctxt.defs, ctxt.nonpkgdefs} | |
| for _, list := range lists { | |
| for _, s := range list { | |
| w.Uint32(nreloc) | |
| nreloc += uint32(len(s.R)) | |
| } | |
| } | |
| w.Uint32(nreloc) | |
| // Symbol Info indexes | |
| h.Offsets[goobj2.BlkAuxIdx] = w.Offset() | |
| naux := uint32(0) | |
| for _, list := range lists { | |
| for _, s := range list { | |
| w.Uint32(naux) | |
| naux += uint32(nAuxSym(s)) | |
| } | |
| } | |
| w.Uint32(naux) | |
| // Data indexes | |
| h.Offsets[goobj2.BlkDataIdx] = w.Offset() | |
| dataOff := uint32(0) | |
| for _, list := range lists { | |
| for _, s := range list { | |
| w.Uint32(dataOff) | |
| dataOff += uint32(len(s.P)) | |
| } | |
| } | |
| w.Uint32(dataOff) | |
| // Relocs | |
| h.Offsets[goobj2.BlkReloc] = w.Offset() | |
| for _, list := range lists { | |
| for _, s := range list { | |
| for i := range s.R { | |
| w.Reloc(&s.R[i]) | |
| } | |
| } | |
| } | |
| // Aux symbol info | |
| h.Offsets[goobj2.BlkAux] = w.Offset() | |
| for _, list := range lists { | |
| for _, s := range list { | |
| w.Aux(s) | |
| } | |
| } | |
| // Data | |
| h.Offsets[goobj2.BlkData] = w.Offset() | |
| for _, list := range lists { | |
| for _, s := range list { | |
| w.Bytes(s.P) | |
| } | |
| } | |
| // Pcdata | |
| h.Offsets[goobj2.BlkPcdata] = w.Offset() | |
| for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms | |
| if s.Func != nil { | |
| pc := &s.Func.Pcln | |
| w.Bytes(pc.Pcsp.P) | |
| w.Bytes(pc.Pcfile.P) | |
| w.Bytes(pc.Pcline.P) | |
| w.Bytes(pc.Pcinline.P) | |
| for i := range pc.Pcdata { | |
| w.Bytes(pc.Pcdata[i].P) | |
| } | |
| } | |
| } | |
| // Fix up block offsets in the header | |
| end := start + int64(w.Offset()) | |
| b.MustSeek(start, 0) | |
| h.Write(w.Writer) | |
| b.MustSeek(end, 0) | |
| } | |
| type writer struct { | |
| *goobj2.Writer | |
| ctxt *Link | |
| pkgpath string // the package import path (escaped), "" if unknown | |
| pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx | |
| } | |
| // prepare package index list | |
| func (w *writer) init() { | |
| w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1) | |
| w.pkglist[0] = "" // dummy invalid package for index 0 | |
| for pkg, i := range w.ctxt.pkgIdx { | |
| w.pkglist[i] = pkg | |
| } | |
| } | |
| func (w *writer) StringTable() { | |
| w.AddString("") | |
| for _, p := range w.ctxt.Imports { | |
| w.AddString(p.Pkg) | |
| } | |
| for _, pkg := range w.pkglist { | |
| w.AddString(pkg) | |
| } | |
| w.ctxt.traverseSyms(traverseAll, func(s *LSym) { | |
| if w.pkgpath != "" { | |
| s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1) | |
| } | |
| w.AddString(s.Name) | |
| }) | |
| w.ctxt.traverseSyms(traverseDefs, func(s *LSym) { | |
| if s.Type != objabi.STEXT { | |
| return | |
| } | |
| pc := &s.Func.Pcln | |
| for _, f := range pc.File { | |
| w.AddString(filepath.ToSlash(f)) | |
| } | |
| for _, call := range pc.InlTree.nodes { | |
| f, _ := linkgetlineFromPos(w.ctxt, call.Pos) | |
| w.AddString(filepath.ToSlash(f)) | |
| } | |
| }) | |
| for _, f := range w.ctxt.PosTable.DebugLinesFileTable() { | |
| w.AddString(filepath.ToSlash(f)) | |
| } | |
| } | |
| func (w *writer) Sym(s *LSym) { | |
| abi := uint16(s.ABI()) | |
| if s.Static() { | |
| abi = goobj2.SymABIstatic | |
| } | |
| flag := uint8(0) | |
| if s.DuplicateOK() { | |
| flag |= goobj2.SymFlagDupok | |
| } | |
| if s.Local() { | |
| flag |= goobj2.SymFlagLocal | |
| } | |
| if s.MakeTypelink() { | |
| flag |= goobj2.SymFlagTypelink | |
| } | |
| if s.Leaf() { | |
| flag |= goobj2.SymFlagLeaf | |
| } | |
| if s.NoSplit() { | |
| flag |= goobj2.SymFlagNoSplit | |
| } | |
| if s.ReflectMethod() { | |
| flag |= goobj2.SymFlagReflectMethod | |
| } | |
| if s.TopFrame() { | |
| flag |= goobj2.SymFlagTopFrame | |
| } | |
| if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA { | |
| flag |= goobj2.SymFlagGoType | |
| } | |
| name := s.Name | |
| if strings.HasPrefix(name, "gofile..") { | |
| name = filepath.ToSlash(name) | |
| } | |
| var align uint32 | |
| if s.Func != nil { | |
| align = uint32(s.Func.Align) | |
| } | |
| var o goobj2.Sym | |
| o.SetName(name, w.Writer) | |
| o.SetABI(abi) | |
| o.SetType(uint8(s.Type)) | |
| o.SetFlag(flag) | |
| o.SetSiz(uint32(s.Size)) | |
| o.SetAlign(align) | |
| o.Write(w.Writer) | |
| } | |
| func makeSymRef(s *LSym) goobj2.SymRef { | |
| if s == nil { | |
| return goobj2.SymRef{} | |
| } | |
| if s.PkgIdx == 0 || !s.Indexed() { | |
| fmt.Printf("unindexed symbol reference: %v\n", s) | |
| panic("unindexed symbol reference") | |
| } | |
| return goobj2.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)} | |
| } | |
| func (w *writer) Reloc(r *Reloc) { | |
| var o goobj2.Reloc | |
| o.SetOff(r.Off) | |
| o.SetSiz(r.Siz) | |
| o.SetType(uint8(r.Type)) | |
| o.SetAdd(r.Add) | |
| o.SetSym(makeSymRef(r.Sym)) | |
| o.Write(w.Writer) | |
| } | |
| func (w *writer) aux1(typ uint8, rs *LSym) { | |
| var o goobj2.Aux | |
| o.SetType(typ) | |
| o.SetSym(makeSymRef(rs)) | |
| o.Write(w.Writer) | |
| } | |
| func (w *writer) Aux(s *LSym) { | |
| if s.Gotype != nil { | |
| w.aux1(goobj2.AuxGotype, s.Gotype) | |
| } | |
| if s.Func != nil { | |
| w.aux1(goobj2.AuxFuncInfo, s.Func.FuncInfoSym) | |
| for _, d := range s.Func.Pcln.Funcdata { | |
| w.aux1(goobj2.AuxFuncdata, d) | |
| } | |
| if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { | |
| w.aux1(goobj2.AuxDwarfInfo, s.Func.dwarfInfoSym) | |
| } | |
| if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { | |
| w.aux1(goobj2.AuxDwarfLoc, s.Func.dwarfLocSym) | |
| } | |
| if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { | |
| w.aux1(goobj2.AuxDwarfRanges, s.Func.dwarfRangesSym) | |
| } | |
| if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { | |
| w.aux1(goobj2.AuxDwarfLines, s.Func.dwarfDebugLinesSym) | |
| } | |
| } | |
| } | |
| // return the number of aux symbols s have. | |
| func nAuxSym(s *LSym) int { | |
| n := 0 | |
| if s.Gotype != nil { | |
| n++ | |
| } | |
| 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 && s.Func.dwarfInfoSym.Size != 0 { | |
| n++ | |
| } | |
| if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { | |
| n++ | |
| } | |
| if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { | |
| n++ | |
| } | |
| if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { | |
| n++ | |
| } | |
| } | |
| return n | |
| } | |
| // generate symbols for FuncInfo. | |
| func genFuncInfoSyms(ctxt *Link) { | |
| infosyms := make([]*LSym, 0, len(ctxt.Text)) | |
| var pcdataoff uint32 | |
| var b bytes.Buffer | |
| symidx := int32(len(ctxt.defs)) | |
| for _, s := range ctxt.Text { | |
| if s.Func == nil { | |
| continue | |
| } | |
| o := goobj2.FuncInfo{ | |
| Args: uint32(s.Func.Args), | |
| Locals: uint32(s.Func.Locals), | |
| } | |
| pc := &s.Func.Pcln | |
| o.Pcsp = pcdataoff | |
| pcdataoff += uint32(len(pc.Pcsp.P)) | |
| o.Pcfile = pcdataoff | |
| pcdataoff += uint32(len(pc.Pcfile.P)) | |
| o.Pcline = pcdataoff | |
| pcdataoff += uint32(len(pc.Pcline.P)) | |
| o.Pcinline = pcdataoff | |
| pcdataoff += uint32(len(pc.Pcinline.P)) | |
| o.Pcdata = make([]uint32, len(pc.Pcdata)) | |
| for i, pcd := range pc.Pcdata { | |
| o.Pcdata[i] = pcdataoff | |
| pcdataoff += uint32(len(pcd.P)) | |
| } | |
| o.PcdataEnd = pcdataoff | |
| o.Funcdataoff = make([]uint32, len(pc.Funcdataoff)) | |
| for i, x := range pc.Funcdataoff { | |
| o.Funcdataoff[i] = uint32(x) | |
| } | |
| o.File = make([]goobj2.SymRef, len(pc.File)) | |
| for i, f := range pc.File { | |
| fsym := ctxt.Lookup(f) | |
| o.File[i] = makeSymRef(fsym) | |
| } | |
| o.InlTree = make([]goobj2.InlTreeNode, len(pc.InlTree.nodes)) | |
| for i, inl := range pc.InlTree.nodes { | |
| f, l := linkgetlineFromPos(ctxt, inl.Pos) | |
| fsym := ctxt.Lookup(f) | |
| o.InlTree[i] = goobj2.InlTreeNode{ | |
| Parent: int32(inl.Parent), | |
| File: makeSymRef(fsym), | |
| Line: l, | |
| Func: makeSymRef(inl.Func), | |
| ParentPC: inl.ParentPC, | |
| } | |
| } | |
| o.Write(&b) | |
| isym := &LSym{ | |
| Type: objabi.SDATA, // for now, I don't think it matters | |
| PkgIdx: goobj2.PkgIdxSelf, | |
| SymIdx: symidx, | |
| P: append([]byte(nil), b.Bytes()...), | |
| } | |
| isym.Set(AttrIndexed, true) | |
| symidx++ | |
| 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) | |
| } | |
| } | |
| } |