2026-08-21 00:14:02 +02:00
|
|
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
package asm
|
|
|
|
|
|
2026-09-19 23:49:13 +02:00
|
|
|
import "encoding/binary"
|
|
|
|
|
|
|
|
|
|
// Absolute 64-bit relocation types for the DWARF address fixups, one per
|
|
|
|
|
// supported architecture (the numbers debug/elf carries).
|
|
|
|
|
const (
|
|
|
|
|
rX8664Abs64 = 1 // R_X86_64_64
|
|
|
|
|
rAARCH64Abs64 = 257 // R_AARCH64_ABS64
|
|
|
|
|
rRISCVAbs64 = 2 // R_RISCV_64
|
|
|
|
|
rLarchAbs64 = 2 // R_LARCH_64
|
|
|
|
|
)
|
|
|
|
|
|
2026-08-21 00:14:02 +02:00
|
|
|
// dwarfELFSections holds the laid-out DWARF sections ready for inclusion
|
|
|
|
|
// in an ELF file.
|
|
|
|
|
type dwarfELFSections struct {
|
|
|
|
|
abbrevOff, abbrevSize int
|
|
|
|
|
infoOff, infoSize int
|
|
|
|
|
lineOff, lineSize int
|
|
|
|
|
lineStrOff, lineStrSize int
|
2026-08-21 01:06:21 +02:00
|
|
|
frameOff, frameSize int
|
2026-09-19 23:49:13 +02:00
|
|
|
// .rela.debug_info and .rela.debug_line contents: file offsets and
|
|
|
|
|
// entry counts (zero count: the section is absent).
|
|
|
|
|
infoRelaOff, infoRelaCount int
|
|
|
|
|
lineRelaOff, lineRelaCount int
|
|
|
|
|
frameRelaOff, frameRelaCount int
|
|
|
|
|
// Relocations for .debug_info address references, offsets relative to
|
|
|
|
|
// the section start (what an r_offset in .rela.debug_info means).
|
2026-08-21 00:14:02 +02:00
|
|
|
infoRelocs []elfDwarfReloc
|
2026-09-19 23:49:13 +02:00
|
|
|
// Relocations for .debug_line address references, section-relative.
|
2026-08-21 00:14:02 +02:00
|
|
|
lineRelocs []elfDwarfReloc
|
2026-09-19 23:49:13 +02:00
|
|
|
// Relocations for .debug_frame FDE initial locations, section-relative.
|
|
|
|
|
frameRelocs []elfDwarfReloc
|
2026-08-21 00:14:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type elfDwarfReloc struct {
|
2026-09-19 23:49:13 +02:00
|
|
|
off uint64 // offset within the target section
|
|
|
|
|
sym int // symbol index in .symtab
|
2026-08-21 00:14:02 +02:00
|
|
|
addend int64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// appendDWARFSections generates and appends DWARF5 debug sections to the ELF
|
|
|
|
|
// output. It returns the section offsets/sizes and relocations for the caller
|
|
|
|
|
// to emit section headers and relocation records.
|
|
|
|
|
//
|
|
|
|
|
// symIdx maps function names to their .symtab indices (needed for relocations
|
|
|
|
|
// against .text symbols). The map uses objectName format (pkg.name); the
|
2026-09-19 23:49:13 +02:00
|
|
|
// DWARF code uses bare function names, so we build a reverse lookup. cfi
|
|
|
|
|
// carries the architecture's .debug_frame register conventions.
|
|
|
|
|
func appendDWARFSections(out *[]byte, img *Image, srcFile string, symIdx map[string]int, align func(int), cfi cfiArch) *dwarfELFSections {
|
2026-08-21 00:14:02 +02:00
|
|
|
// Build a lookup from bare function name to symbol index.
|
|
|
|
|
nameToIdx := make(map[string]int, len(symIdx))
|
|
|
|
|
for name, idx := range symIdx {
|
|
|
|
|
// Strip package prefix: "pkg.name" → "name".
|
|
|
|
|
if i := len(name) - 1; i >= 0 {
|
|
|
|
|
for j := len(name) - 1; j >= 0; j-- {
|
|
|
|
|
if name[j] == '.' {
|
|
|
|
|
nameToIdx[name[j+1:]] = idx
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nameToIdx[name] = idx
|
|
|
|
|
}
|
2026-09-19 23:49:13 +02:00
|
|
|
ds := emitDWARF(img, srcFile, cfi)
|
2026-08-21 00:14:02 +02:00
|
|
|
if ds == nil || len(ds.debugAbbrev) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := &dwarfELFSections{}
|
|
|
|
|
|
|
|
|
|
// .debug_abbrev
|
|
|
|
|
align(1)
|
|
|
|
|
result.abbrevOff = len(*out)
|
|
|
|
|
result.abbrevSize = len(ds.debugAbbrev)
|
|
|
|
|
*out = append(*out, ds.debugAbbrev...)
|
|
|
|
|
|
|
|
|
|
// .debug_line_str
|
|
|
|
|
align(1)
|
|
|
|
|
result.lineStrOff = len(*out)
|
|
|
|
|
result.lineStrSize = len(ds.debugLineStr)
|
|
|
|
|
*out = append(*out, ds.debugLineStr...)
|
|
|
|
|
|
|
|
|
|
// .debug_line
|
|
|
|
|
align(1)
|
|
|
|
|
result.lineOff = len(*out)
|
|
|
|
|
result.lineSize = len(ds.debugLine)
|
|
|
|
|
*out = append(*out, ds.debugLine...)
|
|
|
|
|
for _, dr := range ds.lineRelocs {
|
|
|
|
|
if idx, ok := nameToIdx[dr.name]; ok {
|
|
|
|
|
result.lineRelocs = append(result.lineRelocs, elfDwarfReloc{
|
2026-09-19 23:49:13 +02:00
|
|
|
off: dr.off,
|
2026-08-21 00:14:02 +02:00
|
|
|
sym: idx,
|
|
|
|
|
addend: dr.addend,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// .debug_info
|
|
|
|
|
align(1)
|
|
|
|
|
result.infoOff = len(*out)
|
|
|
|
|
result.infoSize = len(ds.debugInfo)
|
|
|
|
|
*out = append(*out, ds.debugInfo...)
|
|
|
|
|
for _, dr := range ds.infoRelocs {
|
|
|
|
|
if idx, ok := nameToIdx[dr.name]; ok {
|
|
|
|
|
result.infoRelocs = append(result.infoRelocs, elfDwarfReloc{
|
2026-09-19 23:49:13 +02:00
|
|
|
off: dr.off,
|
2026-08-21 00:14:02 +02:00
|
|
|
sym: idx,
|
|
|
|
|
addend: dr.addend,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-19 23:49:13 +02:00
|
|
|
// .debug_frame: the section header declares alignment 8, so the data is
|
|
|
|
|
// padded to 8, matching it.
|
|
|
|
|
if len(ds.debugFrame) > 0 {
|
|
|
|
|
align(8)
|
|
|
|
|
result.frameOff = len(*out)
|
|
|
|
|
result.frameSize = len(ds.debugFrame)
|
|
|
|
|
*out = append(*out, ds.debugFrame...)
|
|
|
|
|
for _, dr := range ds.frameRelocs {
|
|
|
|
|
if idx, ok := nameToIdx[dr.name]; ok {
|
|
|
|
|
result.frameRelocs = append(result.frameRelocs, elfDwarfReloc{
|
|
|
|
|
off: dr.off,
|
|
|
|
|
sym: idx,
|
|
|
|
|
addend: dr.addend,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-21 00:14:02 +02:00
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-19 23:49:13 +02:00
|
|
|
// appendDWARFRelas writes the .rela.debug_info and .rela.debug_line section
|
|
|
|
|
// bodies from the relocations appendDWARFSections recorded, with the
|
|
|
|
|
// architecture's absolute 64-bit relocation type, and records their file
|
|
|
|
|
// offsets and entry counts on dw. Called after the DWARF sections
|
|
|
|
|
// themselves so the r_offsets (section-relative) need no adjustment.
|
|
|
|
|
func appendDWARFRelas(out *[]byte, dw *dwarfELFSections, abs64 uint32, align func(int)) {
|
|
|
|
|
le := binary.LittleEndian
|
|
|
|
|
write := func(relas []elfDwarfReloc) (off, count int) {
|
|
|
|
|
if len(relas) == 0 {
|
|
|
|
|
return 0, 0
|
|
|
|
|
}
|
|
|
|
|
align(8)
|
|
|
|
|
off = len(*out)
|
|
|
|
|
for _, r := range relas {
|
|
|
|
|
var b [24]byte
|
|
|
|
|
le.PutUint64(b[0:], r.off)
|
|
|
|
|
le.PutUint64(b[8:], uint64(r.sym)<<32|uint64(abs64))
|
|
|
|
|
le.PutUint64(b[16:], uint64(r.addend))
|
|
|
|
|
*out = append(*out, b[:]...)
|
|
|
|
|
}
|
|
|
|
|
return off, len(relas)
|
|
|
|
|
}
|
|
|
|
|
dw.infoRelaOff, dw.infoRelaCount = write(dw.infoRelocs)
|
|
|
|
|
dw.lineRelaOff, dw.lineRelaCount = write(dw.lineRelocs)
|
|
|
|
|
dw.frameRelaOff, dw.frameRelaCount = write(dw.frameRelocs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dwarfSourceName returns the source name the DWARF sections record: the
|
|
|
|
|
// image's source path when the assembler captured one, "gasm.s" otherwise.
|
|
|
|
|
func dwarfSourceName(img *Image) string {
|
|
|
|
|
if img.SourcePath != "" {
|
|
|
|
|
return img.SourcePath
|
|
|
|
|
}
|
|
|
|
|
return "gasm.s"
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-21 00:14:02 +02:00
|
|
|
// dwarfSectionNames returns the DWARF section names for the string table.
|
|
|
|
|
var dwarfSectionNames = []string{
|
|
|
|
|
".debug_abbrev", ".debug_info", ".debug_line", ".debug_line_str",
|
2026-08-21 01:06:21 +02:00
|
|
|
".debug_frame", ".rela.debug_info", ".rela.debug_line",
|
2026-09-19 23:49:13 +02:00
|
|
|
".rela.debug_frame",
|
2026-08-21 00:14:02 +02:00
|
|
|
}
|