feat(asm): add LoongArch encoder with ELF and GOOBJ emission
Assisted-by: DeepSeek V4 Pro
This commit is contained in:
+143
-68
@@ -22,9 +22,15 @@ import (
|
||||
//
|
||||
// The object carries what the linker requires of an assembly object: the
|
||||
// functions (non-package symbols, as cmd/asm emits them), the GLOBL data,
|
||||
// one FuncInfo per function, and the pc-value tables (pcsp, pcfile,
|
||||
// pcline, pcinline). DWARF and the implicit funcdata symbols are omitted;
|
||||
// the linker fills their defaults.
|
||||
// one FuncInfo per function, the per-function DWARF symbols (the
|
||||
// .debug_line program and the subprogram DIE, which the linker's DWARF
|
||||
// pass reads verbatim), and the pc-value tables (pcsp, pcfile, pcline,
|
||||
// pcinline). The implicit funcdata symbols are omitted; the linker fills
|
||||
// their defaults.
|
||||
//
|
||||
// emitGOObject is architecture-agnostic; the per-architecture GOObject*
|
||||
// methods supply the toolchain preamble, the MinLC (pc-value delta unit)
|
||||
// and the relocation-type mapping for code relocations.
|
||||
|
||||
// GOOBJ block indices (cmd/internal/goobj).
|
||||
const (
|
||||
@@ -51,9 +57,11 @@ const (
|
||||
|
||||
// Symbol kinds used by assembly objects (cmd/internal/objabi).
|
||||
const (
|
||||
kindSTEXT = 1
|
||||
kindSRODATA = 3
|
||||
kindSDATA = 7
|
||||
kindSTEXT = 1
|
||||
kindSRODATA = 3
|
||||
kindSDATA = 7
|
||||
kindSDWARFFCN = 14
|
||||
kindSDWARFLINES = 20
|
||||
)
|
||||
|
||||
// Symbol flags (cmd/internal/goobj).
|
||||
@@ -66,11 +74,13 @@ const (
|
||||
|
||||
// Aux entry types (cmd/internal/goobj).
|
||||
const (
|
||||
auxFuncInfo = 1
|
||||
auxPcsp = 7
|
||||
auxPcfile = 8
|
||||
auxPcline = 9
|
||||
auxPcinline = 10
|
||||
auxFuncInfo = 1
|
||||
auxDwarfInfo = 3
|
||||
auxDwarfLines = 6
|
||||
auxPcsp = 7
|
||||
auxPcfile = 8
|
||||
auxPcline = 9
|
||||
auxPcinline = 10
|
||||
)
|
||||
|
||||
// FuncInfo flags (internal/abi).
|
||||
@@ -80,7 +90,11 @@ const (
|
||||
)
|
||||
|
||||
// Relocation types (cmd/internal/objabi).
|
||||
const relocPCRel = 14
|
||||
const (
|
||||
relocPCRel = 14 // R_PCREL
|
||||
relocAddr = 1 // R_ADDR
|
||||
relocDWTXTADDRU4 = 106 // R_DWTXTADDR_U4
|
||||
)
|
||||
|
||||
// Special package indices for symbol references.
|
||||
const (
|
||||
@@ -110,6 +124,13 @@ func (s goSym) append(b []byte, strOff map[string]uint32) []byte {
|
||||
return binary.LittleEndian.AppendUint32(b, s.align)
|
||||
}
|
||||
|
||||
// dwarfRelocSet attaches emitter-generated relocations (the DWARF
|
||||
// lines/info symbols' address references) to a definition index.
|
||||
type dwarfRelocSet struct {
|
||||
si int
|
||||
relocs []goobjReloc
|
||||
}
|
||||
|
||||
// GOObject returns the image as a GOOBJ object file for the given package
|
||||
// path (the linker qualifies the exported symbols with it, the way cmd/asm
|
||||
// does with its -p flag). srcPath names the source file recorded in the
|
||||
@@ -117,51 +138,26 @@ func (s goSym) append(b []byte, strOff map[string]uint32) []byte {
|
||||
// captured from the installed go tool asm, so the output links with the
|
||||
// toolchain it was produced on — exactly like a real assembly object.
|
||||
func (img *Image) GOObject(pkgPath, srcPath string) ([]byte, error) {
|
||||
if pkgPath == "" {
|
||||
return nil, fmt.Errorf("GOOBJ emission requires a package path (-p)")
|
||||
}
|
||||
pre, err := toolchainObjectPreamble()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// amd64: MinLC 1, R_PCREL for the code relocations.
|
||||
return img.emitGOObject(pkgPath, srcPath, pre, 1, func(Reloc) uint16 { return relocPCRel })
|
||||
}
|
||||
|
||||
// The symbol tables. Package definitions: the GLOBL symbols, then one
|
||||
// anonymous FuncInfo symbol per function. Non-package definitions: the
|
||||
// pc-value tables and the functions themselves, as cmd/asm lays them
|
||||
// out. defIdx maps a GLOBL's bare name to its definition index for the
|
||||
// relocations; fnNpIdx maps a function to its non-package index.
|
||||
var defs []goSym
|
||||
var defData [][]byte
|
||||
defIdx := map[string]int{}
|
||||
for _, d := range img.DataSyms {
|
||||
name := d.Name
|
||||
if !d.Static {
|
||||
name = pkgPath + "." + name
|
||||
}
|
||||
typ := uint8(kindSDATA)
|
||||
if d.Rodata {
|
||||
typ = kindSRODATA
|
||||
}
|
||||
flag := uint8(0)
|
||||
if d.Dupok {
|
||||
flag = symFlagDupok
|
||||
}
|
||||
abi := uint16(0)
|
||||
if d.Static {
|
||||
abi = symABIStatic
|
||||
}
|
||||
defIdx[d.Name] = len(defs)
|
||||
defs = append(defs, goSym{name: name, abi: abi, typ: typ, flag: flag, flag2: symFlag2Link, size: uint32(d.Size)})
|
||||
defData = append(defData, img.Data[d.Offset:d.Offset+d.Size])
|
||||
}
|
||||
fnFiIdx := make([]int, len(img.Funcs))
|
||||
for i := range img.Funcs {
|
||||
data := marshalFuncInfo(img.Funcs[i])
|
||||
fnFiIdx[i] = len(defs)
|
||||
defs = append(defs, goSym{typ: kindSDATA, size: uint32(len(data))})
|
||||
defData = append(defData, data)
|
||||
// emitGOObject assembles the GOOBJ payload for any architecture. pre is
|
||||
// the toolchain's object preamble; minLC is the architecture's minimum
|
||||
// instruction length, the unit of the pc-value table deltas; relocType
|
||||
// maps a code relocation to its objabi relocation type.
|
||||
func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, relocType func(Reloc) uint16) ([]byte, error) {
|
||||
if pkgPath == "" {
|
||||
return nil, fmt.Errorf("GOOBJ emission requires a package path (-p)")
|
||||
}
|
||||
|
||||
// The non-package definitions first — the DWARF symbols reference the
|
||||
// functions by these indices: per function the four pc-value tables
|
||||
// and the function itself, as cmd/asm lays them out.
|
||||
type npSym struct {
|
||||
sym goSym
|
||||
data []byte
|
||||
@@ -175,10 +171,10 @@ func (img *Image) GOObject(pkgPath, srcPath string) ([]byte, error) {
|
||||
data []byte
|
||||
dst *int
|
||||
}{
|
||||
{pcspTable(fn), &pcIdx[i].sp},
|
||||
{pcValueFlat(0, fn.Size), &pcIdx[i].file},
|
||||
{pcValueFlat(int32(fn.Line), fn.Size), &pcIdx[i].line},
|
||||
{pcValueFlat(-1, fn.Size), &pcIdx[i].inl},
|
||||
{pcspTable(fn, minLC), &pcIdx[i].sp},
|
||||
{pcValueFlat(0, fn.Size, minLC), &pcIdx[i].file},
|
||||
{pcValueFlat(int32(fn.Line), fn.Size, minLC), &pcIdx[i].line},
|
||||
{pcValueFlat(-1, fn.Size, minLC), &pcIdx[i].inl},
|
||||
}
|
||||
for _, t := range tables {
|
||||
*t.dst = len(nps)
|
||||
@@ -213,6 +209,66 @@ func (img *Image) GOObject(pkgPath, srcPath string) ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// The package definitions: the GLOBL symbols, then, per function, the
|
||||
// FuncInfo and the two DWARF symbols (the .debug_line program and the
|
||||
// subprogram DIE). defIdx maps a GLOBL's bare name to its definition
|
||||
// index for the code relocations.
|
||||
var defs []goSym
|
||||
var defData [][]byte
|
||||
defIdx := map[string]int{}
|
||||
for _, d := range img.DataSyms {
|
||||
name := d.Name
|
||||
if !d.Static {
|
||||
name = pkgPath + "." + name
|
||||
}
|
||||
typ := uint8(kindSDATA)
|
||||
if d.Rodata {
|
||||
typ = kindSRODATA
|
||||
}
|
||||
flag := uint8(0)
|
||||
if d.Dupok {
|
||||
flag = symFlagDupok
|
||||
}
|
||||
abi := uint16(0)
|
||||
if d.Static {
|
||||
abi = symABIStatic
|
||||
}
|
||||
defIdx[d.Name] = len(defs)
|
||||
defs = append(defs, goSym{name: name, abi: abi, typ: typ, flag: flag, flag2: symFlag2Link, size: uint32(d.Size)})
|
||||
defData = append(defData, img.Data[d.Offset:d.Offset+d.Size])
|
||||
}
|
||||
fnFiIdx := make([]int, len(img.Funcs))
|
||||
fnLinesIdx := make([]int, len(img.Funcs))
|
||||
fnDIEIdx := make([]int, len(img.Funcs))
|
||||
var dwarfRelocs []dwarfRelocSet
|
||||
for i, fn := range img.Funcs {
|
||||
data := marshalFuncInfo(fn)
|
||||
fnFiIdx[i] = len(defs)
|
||||
defs = append(defs, goSym{typ: kindSDATA, size: uint32(len(data))})
|
||||
defData = append(defData, data)
|
||||
|
||||
name := fn.Name
|
||||
if !fn.Static {
|
||||
name = pkgPath + "." + name
|
||||
}
|
||||
|
||||
// The DWARF symbols: the .debug_line state-machine program and the
|
||||
// subprogram DIE, both referencing the function by its non-package
|
||||
// index (package definitions, like cmd/asm's).
|
||||
lines, lrel := goobjDwarfLines(fn, fnNpIdx[i])
|
||||
fnLinesIdx[i] = len(defs)
|
||||
defs = append(defs, goSym{typ: kindSDWARFLINES, size: uint32(len(lines))})
|
||||
defData = append(defData, lines)
|
||||
die, drel := goobjDwarfInfo(fn, name, fnNpIdx[i])
|
||||
fnDIEIdx[i] = len(defs)
|
||||
defs = append(defs, goSym{typ: kindSDWARFFCN, size: uint32(len(die))})
|
||||
defData = append(defData, die)
|
||||
dwarfRelocs = append(dwarfRelocs,
|
||||
dwarfRelocSet{si: fnLinesIdx[i], relocs: lrel},
|
||||
dwarfRelocSet{si: fnDIEIdx[i], relocs: drel},
|
||||
)
|
||||
}
|
||||
|
||||
// Resolve external symbol references (cross-package). Build the
|
||||
// package index table and determine each external symbol's SymIdx
|
||||
// by reading the target package's export data.
|
||||
@@ -251,7 +307,7 @@ func (img *Image) GOObject(pkgPath, srcPath string) ([]byte, error) {
|
||||
var rec [23]byte
|
||||
binary.LittleEndian.PutUint32(rec[0:], uint32(int32(r.Off)))
|
||||
rec[4] = 4 // field width
|
||||
binary.LittleEndian.PutUint16(rec[5:], relocPCRel)
|
||||
binary.LittleEndian.PutUint16(rec[5:], relocType(r))
|
||||
binary.LittleEndian.PutUint64(rec[7:], uint64(r.Addend))
|
||||
binary.LittleEndian.PutUint32(rec[15:], uint32(pIdx))
|
||||
binary.LittleEndian.PutUint32(rec[19:], uint32(sIdx))
|
||||
@@ -265,16 +321,29 @@ func (img *Image) GOObject(pkgPath, srcPath string) ([]byte, error) {
|
||||
var rec [23]byte
|
||||
binary.LittleEndian.PutUint32(rec[0:], uint32(int32(r.Off)))
|
||||
rec[4] = 4 // field width
|
||||
binary.LittleEndian.PutUint16(rec[5:], relocPCRel)
|
||||
binary.LittleEndian.PutUint16(rec[5:], relocType(r))
|
||||
binary.LittleEndian.PutUint64(rec[7:], uint64(r.Addend))
|
||||
binary.LittleEndian.PutUint32(rec[15:], pkgIdxSelf)
|
||||
binary.LittleEndian.PutUint32(rec[19:], uint32(di))
|
||||
symRelocs[si] = append(symRelocs[si], rec[:]...)
|
||||
}
|
||||
}
|
||||
// The DWARF symbols' own relocations (the function address references).
|
||||
for _, ds := range dwarfRelocs {
|
||||
for _, r := range ds.relocs {
|
||||
var rec [23]byte
|
||||
binary.LittleEndian.PutUint32(rec[0:], uint32(r.off))
|
||||
rec[4] = r.siz
|
||||
binary.LittleEndian.PutUint16(rec[5:], r.typ)
|
||||
binary.LittleEndian.PutUint64(rec[7:], uint64(r.add))
|
||||
binary.LittleEndian.PutUint32(rec[15:], r.pkg)
|
||||
binary.LittleEndian.PutUint32(rec[19:], r.sym)
|
||||
symRelocs[ds.si] = append(symRelocs[ds.si], rec[:]...)
|
||||
}
|
||||
}
|
||||
|
||||
// Aux entries per function: FuncInfo, then the four pc tables.
|
||||
// References into the non-package table use pkgIdxNone.
|
||||
// Aux entries per function: FuncInfo, the DWARF symbols, then the four
|
||||
// pc tables. References into the non-package table use pkgIdxNone.
|
||||
symAux := make([][]byte, nsyms)
|
||||
for i := range img.Funcs {
|
||||
si := len(defs) + fnNpIdx[i]
|
||||
@@ -286,10 +355,14 @@ func (img *Image) GOObject(pkgPath, srcPath string) ([]byte, error) {
|
||||
symAux[si] = append(symAux[si], rec[:]...)
|
||||
}
|
||||
aux(auxFuncInfo, pkgIdxSelf, uint32(fnFiIdx[i]))
|
||||
aux(auxPcsp, pkgIdxNone, uint32(len(defs)+pcIdx[i].sp))
|
||||
aux(auxPcfile, pkgIdxNone, uint32(len(defs)+pcIdx[i].file))
|
||||
aux(auxPcline, pkgIdxNone, uint32(len(defs)+pcIdx[i].line))
|
||||
aux(auxPcinline, pkgIdxNone, uint32(len(defs)+pcIdx[i].inl))
|
||||
aux(auxDwarfInfo, pkgIdxSelf, uint32(fnDIEIdx[i]))
|
||||
aux(auxDwarfLines, pkgIdxSelf, uint32(fnLinesIdx[i]))
|
||||
// The pc-table references are 0-based within the non-package
|
||||
// definitions; the loader adds the package-definition count itself.
|
||||
aux(auxPcsp, pkgIdxNone, uint32(pcIdx[i].sp))
|
||||
aux(auxPcfile, pkgIdxNone, uint32(pcIdx[i].file))
|
||||
aux(auxPcline, pkgIdxNone, uint32(pcIdx[i].line))
|
||||
aux(auxPcinline, pkgIdxNone, uint32(pcIdx[i].inl))
|
||||
}
|
||||
|
||||
// The string table. Absolute offsets: it starts right after the
|
||||
@@ -417,19 +490,21 @@ func marshalFuncInfo(fn FuncLayout) []byte {
|
||||
}
|
||||
|
||||
// pcValueFlat encodes a pc-value table holding v over the whole function.
|
||||
func pcValueFlat(v int32, size int) []byte {
|
||||
// The pc deltas are in MinLC units (the runtime scales them by the
|
||||
// architecture's minimum instruction length).
|
||||
func pcValueFlat(v int32, size, minLC int) []byte {
|
||||
// The table is delta-encoded from an implicit value of -1: a varint
|
||||
// value delta, an unsigned pc delta to the end, and a zero terminator.
|
||||
out := binary.AppendVarint(nil, int64(v)+1)
|
||||
out = binary.AppendUvarint(out, uint64(size))
|
||||
out = binary.AppendUvarint(out, uint64(size/minLC))
|
||||
return append(out, 0)
|
||||
}
|
||||
|
||||
// pcspTable encodes the stack-adjustment table: the SP delta in effect at
|
||||
// every pc, from the function's prologue and epilogue boundaries.
|
||||
func pcspTable(fn FuncLayout) []byte {
|
||||
func pcspTable(fn FuncLayout, minLC int) []byte {
|
||||
if len(fn.Spadj) == 0 {
|
||||
return pcValueFlat(0, fn.Size)
|
||||
return pcValueFlat(0, fn.Size, minLC)
|
||||
}
|
||||
pts := make([]SpadjStep, 0, len(fn.Spadj)+1)
|
||||
pts = append(pts, SpadjStep{PC: 0, Value: 0})
|
||||
@@ -437,11 +512,11 @@ func pcspTable(fn FuncLayout) []byte {
|
||||
out := binary.AppendVarint(nil, int64(pts[0].Value)+1)
|
||||
cur, old := pts[0].PC, pts[0].Value
|
||||
for _, p := range pts[1:] {
|
||||
out = binary.AppendUvarint(out, uint64(p.PC-cur))
|
||||
out = binary.AppendUvarint(out, uint64((p.PC-cur)/minLC))
|
||||
out = binary.AppendVarint(out, int64(p.Value-old))
|
||||
cur, old = p.PC, p.Value
|
||||
}
|
||||
out = binary.AppendUvarint(out, uint64(fn.Size-cur))
|
||||
out = binary.AppendUvarint(out, uint64((fn.Size-cur)/minLC))
|
||||
return append(out, 0)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user