cmd/compile,cmd/link: move to DWARF5-style location lists

This patch updates the compiler to generate DWARF5-style location
lists (e.g. entries that feed into .debug_loclists) as opposed to
DWARF4-style location lists (which wind up in .debug_loc). The DWARF5
format is much more compact, and can make indirect references to text
addresses via the .debug_addr section for further space savings.

Updates #26379.

Change-Id: If2e6fce1136d9cba5125ea51a71419596d1d1691
Reviewed-on: https://go-review.googlesource.com/c/go/+/635836
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
Than McIntosh 2024-12-12 20:47:30 -05:00
parent 4c0a47a8ff
commit bef2bb80a9
3 changed files with 114 additions and 27 deletions

View File

@ -1486,8 +1486,67 @@ func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
state.lists[varID] = list
}
// PutLocationList adds list (a location list in its intermediate representation) to listSym.
// PutLocationList adds list (a location list in its intermediate
// representation) to listSym.
func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
if buildcfg.Experiment.Dwarf5 {
debugInfo.PutLocationListDwarf5(list, ctxt, listSym, startPC)
} else {
debugInfo.PutLocationListDwarf4(list, ctxt, listSym, startPC)
}
}
// PutLocationListDwarf5 adds list (a location list in its intermediate
// representation) to listSym in DWARF 5 format. NB: this is a somewhat
// hacky implementation in that it actually reads a DWARF4 encoded
// info from list (with all its DWARF4-specific quirks) then re-encodes
// it in DWARF5. It would probably be better at some point to have
// ssa/debug encode the list in a version-independent form and then
// have this func (and PutLocationListDwarf4) intoduce the quirks.
func (debugInfo *FuncDebug) PutLocationListDwarf5(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
getPC := debugInfo.GetPC
// base address entry
listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_base_addressx)
listSym.WriteDwTxtAddrx(ctxt, listSym.Size, startPC, ctxt.DwTextCount*2)
var stbuf, enbuf [10]byte
stb, enb := stbuf[:], enbuf[:]
// Re-read list, translating its address from block/value ID to PC.
for i := 0; i < len(list); {
begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:])))
end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:])))
// Write LLE_offset_pair tag followed by payload (ULEB for start
// and then end).
listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_offset_pair)
stb, enb = stb[:0], enb[:0]
stb = dwarf.AppendUleb128(stb, uint64(begin))
enb = dwarf.AppendUleb128(enb, uint64(end))
listSym.WriteBytes(ctxt, listSym.Size, stb)
listSym.WriteBytes(ctxt, listSym.Size, enb)
// The encoded data in "list" is in DWARF4 format, which uses
// a 2-byte length; DWARF5 uses an LEB-encoded value for this
// length. Read the length and then re-encode it.
i += 2 * ctxt.Arch.PtrSize
datalen := int(ctxt.Arch.ByteOrder.Uint16(list[i:]))
i += 2
stb = stb[:0]
stb = dwarf.AppendUleb128(stb, uint64(datalen))
listSym.WriteBytes(ctxt, listSym.Size, stb) // copy length
listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen]) // loc desc
i += datalen
}
// Terminator
listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_end_of_list)
}
// PutLocationListDwarf4 adds list (a location list in its intermediate
// representation) to listSym in DWARF 4 format.
func (debugInfo *FuncDebug) PutLocationListDwarf4(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
getPC := debugInfo.GetPC
if ctxt.UseBASEntries {

View File

@ -465,6 +465,20 @@ const (
DW_RLE_start_length = 0x7
)
// Table 7.10 (DWARF version 5), containing the encodings for the
// .debug_loclists entry formats.
const (
DW_LLE_end_of_list = 0x0
DW_LLE_base_addressx = 0x1
DW_LLE_startx_endx = 0x2
DW_LLE_startx_length = 0x3
DW_LLE_offset_pair = 0x4
DW_LLE_default_location = 0x5
DW_LLE_base_address = 0x6
DW_LLE_start_end = 0x7
DW_LLE_start_length = 0x8
)
// Table 7.27 (DWARF version 5), containing the encodings for the
// line number header entry formats.
const (

View File

@ -2261,7 +2261,7 @@ func (d *dwctxt) writedebugaddr(unit *sym.CompilationUnit, debugaddr loader.Sym)
fnSym := loader.Sym(s)
// NB: this looks at SDWARFFCN; it will need to also look
// at range and loc when they get there.
infosym, _, rangessym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym)
infosym, locsym, rangessym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym)
// Walk the relocations of the various DWARF symbols to
// collect relocations corresponding to indirect function
@ -2271,6 +2271,9 @@ func (d *dwctxt) writedebugaddr(unit *sym.CompilationUnit, debugaddr loader.Sym)
if rangessym != 0 {
dsyms = append(dsyms, rangessym)
}
if locsym != 0 {
dsyms = append(dsyms, locsym)
}
for _, dsym := range dsyms {
drelocs := d.ldr.Relocs(dsym)
for ri := 0; ri < drelocs.Count(); ri++ {
@ -2327,13 +2330,14 @@ func (d *dwctxt) dwarfGenerateDebugSyms() {
// Create the section symbols.
frameSym := mkSecSym(".debug_frame")
locSym := mkSecSym(".debug_loc")
lineSym := mkSecSym(".debug_line")
var rangesSym loader.Sym
var rangesSym, locSym loader.Sym
if buildcfg.Experiment.Dwarf5 {
rangesSym = mkSecSym(".debug_rnglists")
locSym = mkSecSym(".debug_loclists")
} else {
rangesSym = mkSecSym(".debug_ranges")
locSym = mkSecSym(".debug_loc")
}
infoSym := mkSecSym(".debug_info")
var addrSym loader.Sym
@ -2343,17 +2347,19 @@ func (d *dwctxt) dwarfGenerateDebugSyms() {
// Create the section objects
lineSec := dwarfSecInfo{syms: []loader.Sym{lineSym}}
locSec := dwarfSecInfo{syms: []loader.Sym{locSym}}
frameSec := dwarfSecInfo{syms: []loader.Sym{frameSym}}
infoSec := dwarfSecInfo{syms: []loader.Sym{infoSym}}
var addrSec, rangesSec dwarfSecInfo
var addrSec, rangesSec, locSec dwarfSecInfo
if buildcfg.Experiment.Dwarf5 {
addrHdr := d.writeDebugAddrHdr()
addrSec.syms = []loader.Sym{addrSym, addrHdr}
rnglistsHdr := d.writeDebugRngListsHdr()
rangesSec.syms = []loader.Sym{rangesSym, rnglistsHdr}
loclistsHdr := d.writeDebugLocListsHdr()
locSec.syms = []loader.Sym{locSym, loclistsHdr}
} else {
rangesSec = dwarfSecInfo{syms: []loader.Sym{rangesSym}}
locSec = dwarfSecInfo{syms: []loader.Sym{locSym}}
}
// Create any new symbols that will be needed during the
@ -2423,17 +2429,22 @@ func (d *dwctxt) dwarfGenerateDebugSyms() {
if buildcfg.Experiment.Dwarf5 {
// Compute total size of the DWARF5-specific .debug_* syms in
// each compilation unit.
var rltot, addrtot uint64
var rltot, addrtot, loctot uint64
for i := 0; i < ncu; i++ {
addrtot += uint64(d.ldr.SymSize(unitSyms[i].addrsym))
rs := unitSyms[i].rangessyms
for _, s := range rs {
rltot += uint64(d.ldr.SymSize(s))
}
loc := unitSyms[i].locsyms
for _, s := range loc {
loctot += uint64(d.ldr.SymSize(s))
}
}
// Call a helper to patch the length field in the headers.
patchHdr(&addrSec, addrtot)
patchHdr(&rangesSec, rltot)
patchHdr(&locSec, loctot)
}
// Stitch together the results.
@ -2505,9 +2516,9 @@ func dwarfaddshstrings(ctxt *Link, add func(string)) {
secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts"}
if buildcfg.Experiment.Dwarf5 {
secs = append(secs, "addr", "rnglists")
secs = append(secs, "addr", "rnglists", "loclists")
} else {
secs = append(secs, "ranges")
secs = append(secs, "ranges", "loc")
}
for _, sec := range secs {
@ -2667,30 +2678,33 @@ func addDwsectCUSize(sname string, pkgname string, size uint64) {
dwsectCUSize[sname+"."+pkgname] += size
}
// writeDebugAddrHdr creates a new symbol and writes the content
// for the .debug_rnglists header payload to it, then returns the new sym.
// Format of the header is described in DWARF5 spec section 7.28.
func (d *dwctxt) writeDebugRngListsHdr() loader.Sym {
// writeDebugMiscSecHdr writes a header section for the new new DWARF5
// sections ".debug_addr", ".debug_loclists", and ".debug_rnglists".
// A description of the format/layout of these headers can be found in
// the DWARF5 spec in sections 7.27 (.debug_addr), 7.28
// (.debug_rnglists) and 7.29 (.debug_loclists).
func (d *dwctxt) writeDebugMiscSecHdr(st sym.SymKind, addOffsetEntryCount bool) loader.Sym {
su := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0))
su.SetType(sym.SDWARFRANGE)
su.SetType(st)
su.SetReachable(true)
d.createUnitLength(su, 0) // will be filled in later.
su.AddUint16(d.arch, 5) // dwarf version (appendix F)
su.AddUint8(uint8(d.arch.PtrSize)) // address_size
su.AddUint8(0)
su.AddUint8(0) // segment selector
if addOffsetEntryCount {
su.AddUint32(d.arch, 0) // offset entry count (required but unused)
}
return su.Sym()
}
// writeDebugAddrHdr creates a new symbol and writes the content
// for the .debug_addr header payload to it, then returns the new sym.
// Format of the header is described in DWARF5 spec section 7.27.
func (d *dwctxt) writeDebugAddrHdr() loader.Sym {
su := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0))
su.SetType(sym.SDWARFADDR)
su.SetReachable(true)
d.createUnitLength(su, 0) // will be filled in later.
su.AddUint16(d.arch, 5) // dwarf version (appendix F)
su.AddUint8(uint8(d.arch.PtrSize)) // address_size
su.AddUint8(0)
return su.Sym()
func (d *dwctxt) writeDebugRngListsHdr() loader.Sym {
return d.writeDebugMiscSecHdr(sym.SDWARFRANGE, true)
}
func (d *dwctxt) writeDebugLocListsHdr() loader.Sym {
return d.writeDebugMiscSecHdr(sym.SDWARFLOC, true)
}
func (d *dwctxt) writeDebugAddrHdr() loader.Sym {
return d.writeDebugMiscSecHdr(sym.SDWARFADDR, false)
}