// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package asm // 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 frameOff, frameSize int // Relocations for .debug_info address references. infoRelocs []elfDwarfReloc // Relocations for .debug_line address references. lineRelocs []elfDwarfReloc } type elfDwarfReloc struct { off uint64 sym int // symbol index in .symtab 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 // DWARF code uses bare function names, so we build a reverse lookup. func appendDWARFSections(out *[]byte, img *Image, srcFile string, symIdx map[string]int, align func(int)) *dwarfELFSections { // 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 } ds := emitDWARF(img, srcFile) 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) lineBase := len(*out) *out = append(*out, ds.debugLine...) // Patch .debug_line relocations: replace placeholder addresses with // actual .text offsets via symbol lookup. for _, dr := range ds.lineRelocs { if idx, ok := nameToIdx[dr.name]; ok { result.lineRelocs = append(result.lineRelocs, elfDwarfReloc{ off: uint64(lineBase) + dr.off, sym: idx, addend: dr.addend, }) } } // .debug_info align(1) result.infoOff = len(*out) result.infoSize = len(ds.debugInfo) infoBase := len(*out) *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. for _, dr := range ds.infoRelocs { if idx, ok := nameToIdx[dr.name]; ok { result.infoRelocs = append(result.infoRelocs, elfDwarfReloc{ off: uint64(infoBase) + dr.off, sym: idx, addend: dr.addend, }) } } return result } // dwarfSectionNames returns the DWARF section names for the string table. var dwarfSectionNames = []string{ ".debug_abbrev", ".debug_info", ".debug_line", ".debug_line_str", ".debug_frame", ".rela.debug_info", ".rela.debug_line", }