// 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 WriteObjFile(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 } if pkgpath == "" { flags |= goobj2.ObjFlagNeedNameExpansion } 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.SDWARFFCN && aux.Type != objabi.SDWARFABSFCN && 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) } } }