feat(asm): add .debug_frame CFI section for stack unwinding
Assisted-by: MiMo V2.5 Pro
This commit is contained in:
@@ -269,6 +269,9 @@ func (img *Image) ELFObject() ([]byte, error) {
|
|||||||
putSh(".debug_info", shtProgbits, 0, dw.infoOff, dw.infoSize, 0, 0, 1, 0)
|
putSh(".debug_info", shtProgbits, 0, dw.infoOff, dw.infoSize, 0, 0, 1, 0)
|
||||||
putSh(".debug_line", shtProgbits, 0, dw.lineOff, dw.lineSize, 0, 0, 1, 0)
|
putSh(".debug_line", shtProgbits, 0, dw.lineOff, dw.lineSize, 0, 0, 1, 0)
|
||||||
putSh(".debug_line_str", shtProgbits, 0, dw.lineStrOff, dw.lineStrSize, 0, 0, 1, 0)
|
putSh(".debug_line_str", shtProgbits, 0, dw.lineStrOff, dw.lineStrSize, 0, 0, 1, 0)
|
||||||
|
if dw.frameSize > 0 {
|
||||||
|
putSh(".debug_frame", shtProgbits, 0, dw.frameOff, dw.frameSize, 0, 0, 8, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The ELF header.
|
// The ELF header.
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ type dwarfSections struct {
|
|||||||
debugInfo []byte
|
debugInfo []byte
|
||||||
debugLine []byte
|
debugLine []byte
|
||||||
debugLineStr []byte
|
debugLineStr []byte
|
||||||
|
debugFrame []byte
|
||||||
// Relocations for .debug_info: (offset, symbol name, addend).
|
// Relocations for .debug_info: (offset, symbol name, addend).
|
||||||
infoRelocs []dwarfReloc
|
infoRelocs []dwarfReloc
|
||||||
// Relocations for .debug_line: (offset, symbol name, addend).
|
// Relocations for .debug_line: (offset, symbol name, addend).
|
||||||
@@ -90,6 +91,9 @@ func emitDWARF(img *Image, srcFile string) *dwarfSections {
|
|||||||
|
|
||||||
// Build .debug_info.
|
// Build .debug_info.
|
||||||
ds.debugInfo = dwarfBuildInfoSection(img, srcFile, ds)
|
ds.debugInfo = dwarfBuildInfoSection(img, srcFile, ds)
|
||||||
|
|
||||||
|
// Build .debug_frame.
|
||||||
|
ds.debugFrame = dwarfBuildFrameSection(img)
|
||||||
return ds
|
return ds
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,3 +252,68 @@ func dwarfBuildInfoSection(img *Image, srcFile string, ds *dwarfSections) []byte
|
|||||||
func appendUleb(b []byte, v uint64) []byte {
|
func appendUleb(b []byte, v uint64) []byte {
|
||||||
return binary.AppendUvarint(b, v)
|
return binary.AppendUvarint(b, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendSleb(b []byte, v int64) []byte {
|
||||||
|
return binary.AppendVarint(b, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dwarfBuildFrameSection builds a .debug_frame section with CFI for stack
|
||||||
|
// unwinding. It emits one CIE and one FDE per function, encoding the
|
||||||
|
// CFA (Canonical Frame Address) rule changes at each stack-adjustment
|
||||||
|
// boundary recorded in FuncLayout.Spadj.
|
||||||
|
func dwarfBuildFrameSection(img *Image) []byte {
|
||||||
|
var b []byte
|
||||||
|
le := binary.LittleEndian
|
||||||
|
|
||||||
|
// CIE (Common Information Entry).
|
||||||
|
cieStart := len(b)
|
||||||
|
b = append(b, 0, 0, 0, 0) // length (placeholder)
|
||||||
|
b = le.AppendUint32(b, 0xFFFFFFFF) // CIE marker
|
||||||
|
b = append(b, 3) // version (DWARF3, widely supported)
|
||||||
|
b = append(b, 0) // augmentation (empty)
|
||||||
|
b = appendUleb(b, 1) // code alignment
|
||||||
|
b = appendSleb(b, -8) // data alignment (-8 for 64-bit)
|
||||||
|
b = appendUleb(b, 16) // return address register (LR on arm64, RIP on amd64)
|
||||||
|
// Initial CFA rule: DW_CFA_def_cfa (SP, 0)
|
||||||
|
b = append(b, 0x0c) // DW_CFA_def_cfa
|
||||||
|
b = appendUleb(b, 31) // register: SP (RSP=7 on amd64, SP=31 on arm64)
|
||||||
|
b = appendUleb(b, 0) // offset: 0
|
||||||
|
b = append(b, 0) // DW_CFA_nop (padding)
|
||||||
|
// Patch CIE length.
|
||||||
|
le.PutUint32(b[cieStart:], uint32(len(b)-cieStart-4))
|
||||||
|
|
||||||
|
// FDEs (Frame Description Entries) — one per function.
|
||||||
|
for _, fn := range img.Funcs {
|
||||||
|
fdeStart := len(b)
|
||||||
|
b = append(b, 0, 0, 0, 0) // length (placeholder)
|
||||||
|
b = le.AppendUint32(b, uint32(cieStart)) // CIE pointer (offset from start)
|
||||||
|
// Initial location: function offset in .text (relocated by linker).
|
||||||
|
b = le.AppendUint64(b, uint64(fn.Offset))
|
||||||
|
// Address range: function size.
|
||||||
|
b = le.AppendUint64(b, uint64(fn.Size))
|
||||||
|
|
||||||
|
// Emit CFA rule changes at each Spadj boundary.
|
||||||
|
for _, step := range fn.Spadj {
|
||||||
|
if step.Value == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// DW_CFA_def_cfa_offset: set CFA = SP + |delta|.
|
||||||
|
// The delta is negative (stack grows down), so CFA offset = -delta.
|
||||||
|
offset := -step.Value
|
||||||
|
if offset > 0 {
|
||||||
|
b = append(b, 0x0e) // DW_CFA_def_cfa_offset
|
||||||
|
b = appendUleb(b, uint64(offset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad to alignment.
|
||||||
|
for len(b)%4 != 0 {
|
||||||
|
b = append(b, 0) // DW_CFA_nop
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch FDE length.
|
||||||
|
le.PutUint32(b[fdeStart:], uint32(len(b)-fdeStart-4))
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type dwarfELFSections struct {
|
|||||||
infoOff, infoSize int
|
infoOff, infoSize int
|
||||||
lineOff, lineSize int
|
lineOff, lineSize int
|
||||||
lineStrOff, lineStrSize int
|
lineStrOff, lineStrSize int
|
||||||
|
frameOff, frameSize int
|
||||||
// Relocations for .debug_info address references.
|
// Relocations for .debug_info address references.
|
||||||
infoRelocs []elfDwarfReloc
|
infoRelocs []elfDwarfReloc
|
||||||
// Relocations for .debug_line address references.
|
// Relocations for .debug_line address references.
|
||||||
@@ -91,6 +92,14 @@ func appendDWARFSections(out *[]byte, img *Image, srcFile string, symIdx map[str
|
|||||||
infoBase := len(*out)
|
infoBase := len(*out)
|
||||||
*out = append(*out, ds.debugInfo...)
|
*out = append(*out, ds.debugInfo...)
|
||||||
|
|
||||||
|
// .debug_frame
|
||||||
|
if len(ds.debugFrame) > 0 {
|
||||||
|
align(1)
|
||||||
|
result.frameOff = len(*out)
|
||||||
|
result.frameSize = len(ds.debugFrame)
|
||||||
|
*out = append(*out, ds.debugFrame...)
|
||||||
|
}
|
||||||
|
|
||||||
// Patch .debug_info relocations.
|
// Patch .debug_info relocations.
|
||||||
for _, dr := range ds.infoRelocs {
|
for _, dr := range ds.infoRelocs {
|
||||||
if idx, ok := nameToIdx[dr.name]; ok {
|
if idx, ok := nameToIdx[dr.name]; ok {
|
||||||
@@ -130,5 +139,5 @@ func dwarfRelaRecords(dw *dwarfELFSections, relType uint32) []byte {
|
|||||||
// dwarfSectionNames returns the DWARF section names for the string table.
|
// dwarfSectionNames returns the DWARF section names for the string table.
|
||||||
var dwarfSectionNames = []string{
|
var dwarfSectionNames = []string{
|
||||||
".debug_abbrev", ".debug_info", ".debug_line", ".debug_line_str",
|
".debug_abbrev", ".debug_info", ".debug_line", ".debug_line_str",
|
||||||
".rela.debug_info", ".rela.debug_line",
|
".debug_frame", ".rela.debug_info", ".rela.debug_line",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,9 @@ func (img *Image) ELFAARCH64Object() ([]byte, error) {
|
|||||||
putSh(".debug_info", shtProgbits, 0, dw.infoOff, dw.infoSize, 0, 0, 1, 0)
|
putSh(".debug_info", shtProgbits, 0, dw.infoOff, dw.infoSize, 0, 0, 1, 0)
|
||||||
putSh(".debug_line", shtProgbits, 0, dw.lineOff, dw.lineSize, 0, 0, 1, 0)
|
putSh(".debug_line", shtProgbits, 0, dw.lineOff, dw.lineSize, 0, 0, 1, 0)
|
||||||
putSh(".debug_line_str", shtProgbits, 0, dw.lineStrOff, dw.lineStrSize, 0, 0, 1, 0)
|
putSh(".debug_line_str", shtProgbits, 0, dw.lineStrOff, dw.lineStrSize, 0, 0, 1, 0)
|
||||||
|
if dw.frameSize > 0 {
|
||||||
|
putSh(".debug_frame", shtProgbits, 0, dw.frameOff, dw.frameSize, 0, 0, 8, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ELF header.
|
// ELF header.
|
||||||
|
|||||||
@@ -219,6 +219,9 @@ func (img *Image) ELFLOONG64Object() ([]byte, error) {
|
|||||||
putSh(".debug_info", shtProgbits, 0, dw.infoOff, dw.infoSize, 0, 0, 1, 0)
|
putSh(".debug_info", shtProgbits, 0, dw.infoOff, dw.infoSize, 0, 0, 1, 0)
|
||||||
putSh(".debug_line", shtProgbits, 0, dw.lineOff, dw.lineSize, 0, 0, 1, 0)
|
putSh(".debug_line", shtProgbits, 0, dw.lineOff, dw.lineSize, 0, 0, 1, 0)
|
||||||
putSh(".debug_line_str", shtProgbits, 0, dw.lineStrOff, dw.lineStrSize, 0, 0, 1, 0)
|
putSh(".debug_line_str", shtProgbits, 0, dw.lineStrOff, dw.lineStrSize, 0, 0, 1, 0)
|
||||||
|
if dw.frameSize > 0 {
|
||||||
|
putSh(".debug_frame", shtProgbits, 0, dw.frameOff, dw.frameSize, 0, 0, 8, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ELF header.
|
// ELF header.
|
||||||
|
|||||||
@@ -232,6 +232,9 @@ func (img *Image) ELFRISCVObject() ([]byte, error) {
|
|||||||
putSh(".debug_info", shtProgbits, 0, dw.infoOff, dw.infoSize, 0, 0, 1, 0)
|
putSh(".debug_info", shtProgbits, 0, dw.infoOff, dw.infoSize, 0, 0, 1, 0)
|
||||||
putSh(".debug_line", shtProgbits, 0, dw.lineOff, dw.lineSize, 0, 0, 1, 0)
|
putSh(".debug_line", shtProgbits, 0, dw.lineOff, dw.lineSize, 0, 0, 1, 0)
|
||||||
putSh(".debug_line_str", shtProgbits, 0, dw.lineStrOff, dw.lineStrSize, 0, 0, 1, 0)
|
putSh(".debug_line_str", shtProgbits, 0, dw.lineStrOff, dw.lineStrSize, 0, 0, 1, 0)
|
||||||
|
if dw.frameSize > 0 {
|
||||||
|
putSh(".debug_frame", shtProgbits, 0, dw.frameOff, dw.frameSize, 0, 0, 8, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ELF header.
|
// ELF header.
|
||||||
|
|||||||
Reference in New Issue
Block a user