mirror of
https://github.com/Binject/debug
synced 2026-06-08 10:28:33 +00:00
414 lines
9.0 KiB
Go
414 lines
9.0 KiB
Go
// 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.
|
|
|
|
// This file is a modified version of cmd/internal/obj/objfile2.go
|
|
|
|
package goobj2
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/Binject/debug/goobj2/internal/bio"
|
|
"github.com/Binject/debug/goobj2/internal/goobj2"
|
|
)
|
|
|
|
// Write writes the contents of the parsed archive to disk.
|
|
func (pkg *Package) Write(path string) (err error) {
|
|
b, err := bio.Create(path)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating object file: %v", err)
|
|
}
|
|
defer func() {
|
|
closeErr := b.Close()
|
|
if closeErr != nil && err == nil {
|
|
err = closeErr
|
|
}
|
|
}()
|
|
|
|
// Archive headers
|
|
b.Write(archiveHeader)
|
|
var arhdr [archiveHeaderLen]byte
|
|
var curArHdrOff, curObjStartOff int64
|
|
for i := range pkg.ArchiveMembers {
|
|
ctxt := &pkg.ArchiveMembers[i]
|
|
ar := ctxt.ArchiveHeader
|
|
curArHdrOff = b.Offset()
|
|
|
|
copy(arhdr[:], fmt.Sprintf("%-16s%-12s%-6s%-6s%-8s%-10d`\n", ar.Name, ar.Date, ar.UID, ar.GID, ar.Mode, ar.Size))
|
|
b.Write(arhdr[:])
|
|
curObjStartOff = b.Offset()
|
|
b.Write(ar.Data)
|
|
if ctxt.IsDataObj {
|
|
continue
|
|
}
|
|
|
|
genFuncInfoSyms(ctxt)
|
|
|
|
w := writer{
|
|
Writer: goobj2.NewWriter(b),
|
|
ctxt: ctxt,
|
|
}
|
|
|
|
start := b.Offset()
|
|
|
|
// Header
|
|
// We just reserve the space. We'll fill in the offsets later.
|
|
ctxt.ObjHeader.Write(w.Writer)
|
|
|
|
// String table
|
|
w.StringTable()
|
|
|
|
// Autolib
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkAutolib] = w.Offset()
|
|
for i := range ctxt.Imports {
|
|
ctxt.Imports[i].Write(w.Writer)
|
|
}
|
|
|
|
// Package references
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkPkgIdx] = w.Offset()
|
|
w.StringRef("")
|
|
for _, pkg := range ctxt.Packages {
|
|
w.StringRef(pkg)
|
|
}
|
|
|
|
// DWARF file table
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkDwarfFile] = w.Offset()
|
|
for _, f := range ctxt.DWARFFileList {
|
|
w.StringRef(filepath.ToSlash(f))
|
|
}
|
|
|
|
// Symbol definitions
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkSymdef] = w.Offset()
|
|
for _, s := range ctxt.SymDefs {
|
|
w.Sym(s)
|
|
}
|
|
|
|
// Non-pkg symbol definitions
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkNonpkgdef] = w.Offset()
|
|
for _, s := range ctxt.NonPkgSymDefs {
|
|
w.Sym(s)
|
|
}
|
|
|
|
// Non-pkg symbol references
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkNonpkgref] = w.Offset()
|
|
for _, s := range ctxt.NonPkgSymRefs {
|
|
w.Sym(s)
|
|
}
|
|
|
|
// Reloc indexes
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkRelocIdx] = w.Offset()
|
|
nreloc := uint32(0)
|
|
lists := [][]*Sym{ctxt.SymDefs, ctxt.NonPkgSymDefs}
|
|
for _, list := range lists {
|
|
for _, s := range list {
|
|
w.Uint32(nreloc)
|
|
nreloc += uint32(len(s.Reloc))
|
|
}
|
|
}
|
|
w.Uint32(nreloc)
|
|
|
|
// Symbol Info indexes
|
|
ctxt.ObjHeader.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
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkDataIdx] = w.Offset()
|
|
dataOff := uint32(0)
|
|
for _, list := range lists {
|
|
for _, s := range list {
|
|
w.Uint32(dataOff)
|
|
dataOff += uint32(len(s.Data))
|
|
}
|
|
}
|
|
w.Uint32(dataOff)
|
|
|
|
// Relocs
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkReloc] = w.Offset()
|
|
for _, list := range lists {
|
|
for _, s := range list {
|
|
for i := range s.Reloc {
|
|
w.Reloc(&s.Reloc[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
// Aux symbol info
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkAux] = w.Offset()
|
|
for _, list := range lists {
|
|
for _, s := range list {
|
|
w.Aux(s)
|
|
}
|
|
}
|
|
|
|
// Data
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkData] = w.Offset()
|
|
for _, list := range lists {
|
|
for _, s := range list {
|
|
w.Bytes(s.Data)
|
|
}
|
|
}
|
|
|
|
// Pcdata
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkPcdata] = w.Offset()
|
|
for _, ts := range ctxt.textSyms {
|
|
w.Bytes(ts.Func.PCSP)
|
|
w.Bytes(ts.Func.PCFile)
|
|
w.Bytes(ts.Func.PCLine)
|
|
w.Bytes(ts.Func.PCInline)
|
|
for i := range ts.Func.PCData {
|
|
w.Bytes(ts.Func.PCData[i])
|
|
}
|
|
}
|
|
|
|
// Referenced symbol names from other packages
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkRefName] = w.Offset()
|
|
for _, ref := range ctxt.SymRefs {
|
|
var o goobj2.RefName
|
|
o.SetSym(ref.SymRef)
|
|
o.SetName(ref.Name, w.Writer)
|
|
o.Write(w.Writer)
|
|
}
|
|
|
|
objEnd := w.Offset()
|
|
ctxt.ObjHeader.Offsets[goobj2.BlkEnd] = objEnd
|
|
|
|
// If the object size is odd, make it even by adding an
|
|
// extra null byte as padding
|
|
size := int64(objEnd) + (start - curObjStartOff)
|
|
end := start + int64(w.Offset())
|
|
if size%2 != 0 {
|
|
b.WriteByte(0x00)
|
|
end++
|
|
}
|
|
|
|
// Fix size field of the last archive header
|
|
b.MustSeek(curArHdrOff+48, 0)
|
|
b.WriteString(fmt.Sprintf("%-10d", size))
|
|
|
|
// Fix up block offsets in the object header
|
|
b.MustSeek(start, 0)
|
|
ctxt.ObjHeader.Write(w.Writer)
|
|
b.MustSeek(end, 0)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type writer struct {
|
|
*goobj2.Writer
|
|
ctxt *ArchiveMember
|
|
}
|
|
|
|
func (w *writer) StringTable() {
|
|
w.AddString("")
|
|
for _, p := range w.ctxt.Imports {
|
|
w.AddString(p.Pkg)
|
|
}
|
|
for _, pkg := range w.ctxt.Packages {
|
|
w.AddString(pkg)
|
|
}
|
|
|
|
writeSymStrings := func(s *Sym) {
|
|
w.AddString(s.Name)
|
|
|
|
for _, r := range s.Reloc {
|
|
w.AddString(r.Name)
|
|
}
|
|
if s.Type != nil {
|
|
w.AddString(s.Name)
|
|
}
|
|
|
|
if s.Kind == STEXT && s.Func != nil {
|
|
for _, d := range s.Func.FuncData {
|
|
w.AddString(d.Sym.Name)
|
|
}
|
|
for _, f := range s.Func.File {
|
|
w.AddString(filepath.ToSlash(f.Name))
|
|
}
|
|
for _, call := range s.Func.InlTree {
|
|
w.AddString(call.File.Name)
|
|
w.AddString(call.Func.Name)
|
|
}
|
|
|
|
dwsyms := []*SymRef{s.Func.DwarfRanges, s.Func.DwarfLoc, s.Func.DwarfDebugLines, s.Func.FuncInfo}
|
|
for _, dws := range dwsyms {
|
|
if dws != nil {
|
|
w.AddString(dws.Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Symbols of type STEXT (that have functions) are written first
|
|
for _, ts := range w.ctxt.textSyms {
|
|
writeSymStrings(ts)
|
|
}
|
|
|
|
syms := [][]*Sym{w.ctxt.NonPkgSymDefs, w.ctxt.SymDefs, w.ctxt.NonPkgSymRefs}
|
|
for _, list := range syms {
|
|
for _, s := range list {
|
|
if s.Kind == STEXT {
|
|
continue
|
|
}
|
|
|
|
writeSymStrings(s)
|
|
}
|
|
}
|
|
for _, r := range w.ctxt.SymRefs {
|
|
w.AddString(r.Name)
|
|
}
|
|
|
|
for _, f := range w.ctxt.DWARFFileList {
|
|
w.AddString(filepath.ToSlash(f))
|
|
}
|
|
}
|
|
|
|
func (w *writer) Sym(s *Sym) {
|
|
name := s.Name
|
|
if strings.HasPrefix(name, "gofile..") {
|
|
name = filepath.ToSlash(name)
|
|
}
|
|
|
|
var o goobj2.Sym
|
|
o.SetName(name, w.Writer)
|
|
o.SetABI(s.ABI)
|
|
o.SetType(uint8(s.Kind))
|
|
o.SetFlag(s.Flag)
|
|
o.SetSiz(s.Size)
|
|
o.SetAlign(s.Align)
|
|
o.Write(w.Writer)
|
|
}
|
|
|
|
func (w *writer) Reloc(r *Reloc) {
|
|
var o goobj2.Reloc
|
|
o.SetOff(int32(r.Offset))
|
|
o.SetSiz(uint8(r.Size))
|
|
o.SetType(uint8(r.Type))
|
|
o.SetAdd(r.Add)
|
|
o.SetSym(r.Sym)
|
|
o.Write(w.Writer)
|
|
}
|
|
|
|
func (w *writer) aux1(typ uint8, rs goobj2.SymRef) {
|
|
var o goobj2.Aux
|
|
o.SetType(typ)
|
|
o.SetSym(rs)
|
|
o.Write(w.Writer)
|
|
}
|
|
|
|
func (w *writer) Aux(s *Sym) {
|
|
if s.Type != nil {
|
|
w.aux1(goobj2.AuxGotype, s.Type.SymRef)
|
|
}
|
|
if s.Func != nil {
|
|
w.aux1(goobj2.AuxFuncInfo, s.Func.FuncInfo.SymRef)
|
|
|
|
for _, d := range s.Func.FuncData {
|
|
w.aux1(goobj2.AuxFuncdata, d.Sym.SymRef)
|
|
}
|
|
|
|
if s.Func.DwarfInfo != nil {
|
|
w.aux1(goobj2.AuxDwarfInfo, s.Func.DwarfInfo.SymRef)
|
|
}
|
|
if s.Func.DwarfLoc != nil {
|
|
w.aux1(goobj2.AuxDwarfLoc, s.Func.DwarfLoc.SymRef)
|
|
}
|
|
if s.Func.DwarfRanges != nil {
|
|
w.aux1(goobj2.AuxDwarfRanges, s.Func.DwarfRanges.SymRef)
|
|
}
|
|
if s.Func.DwarfDebugLines != nil {
|
|
w.aux1(goobj2.AuxDwarfLines, s.Func.DwarfDebugLines.SymRef)
|
|
}
|
|
}
|
|
}
|
|
|
|
// return the number of aux symbols s have.
|
|
func nAuxSym(s *Sym) int {
|
|
n := 0
|
|
if s.Type != nil {
|
|
n++
|
|
}
|
|
if s.Func != nil {
|
|
// FuncInfo is an aux symbol, each Funcdata is an aux symbol
|
|
n += 1 + len(s.Func.FuncData)
|
|
if s.Func.DwarfInfo != nil {
|
|
n++
|
|
}
|
|
if s.Func.DwarfLoc != nil {
|
|
n++
|
|
}
|
|
if s.Func.DwarfRanges != nil {
|
|
n++
|
|
}
|
|
if s.Func.DwarfDebugLines != nil {
|
|
n++
|
|
}
|
|
}
|
|
return n
|
|
}
|
|
|
|
// generate symbols for FuncInfo.
|
|
func genFuncInfoSyms(ctxt *ArchiveMember) {
|
|
var pcdataoff uint32
|
|
var b bytes.Buffer
|
|
for _, s := range ctxt.textSyms {
|
|
if s.Func == nil {
|
|
continue
|
|
}
|
|
|
|
o := goobj2.FuncInfo{
|
|
Args: uint32(s.Func.Args),
|
|
Locals: uint32(s.Func.Frame),
|
|
}
|
|
o.Pcsp = pcdataoff
|
|
pcdataoff += uint32(len(s.Func.PCSP))
|
|
o.Pcfile = pcdataoff
|
|
pcdataoff += uint32(len(s.Func.PCFile))
|
|
o.Pcline = pcdataoff
|
|
pcdataoff += uint32(len(s.Func.PCLine))
|
|
o.Pcinline = pcdataoff
|
|
pcdataoff += uint32(len(s.Func.PCInline))
|
|
o.Pcdata = make([]uint32, len(s.Func.PCData))
|
|
for i, pcd := range s.Func.PCData {
|
|
o.Pcdata[i] = pcdataoff
|
|
pcdataoff += uint32(len(pcd))
|
|
}
|
|
o.PcdataEnd = pcdataoff
|
|
o.Funcdataoff = make([]uint32, len(s.Func.FuncData))
|
|
for i, x := range s.Func.FuncData {
|
|
o.Funcdataoff[i] = x.Offset
|
|
}
|
|
o.File = make([]goobj2.SymRef, len(s.Func.File))
|
|
for i, f := range s.Func.File {
|
|
o.File[i] = f.SymRef
|
|
}
|
|
o.InlTree = make([]goobj2.InlTreeNode, len(s.Func.InlTree))
|
|
for i, inl := range s.Func.InlTree {
|
|
o.InlTree[i] = goobj2.InlTreeNode{
|
|
Parent: inl.Parent,
|
|
File: inl.File.SymRef,
|
|
Line: inl.Line,
|
|
Func: inl.Func.SymRef,
|
|
ParentPC: inl.ParentPC,
|
|
}
|
|
}
|
|
|
|
o.Write(&b)
|
|
ctxt.symMap[s.Func.dataSymIdx].Data = append([]byte(nil), b.Bytes()...)
|
|
b.Reset()
|
|
}
|
|
}
|