]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile,cmd/link: move to DWARF5-style range lists
authorThan McIntosh <thanm@golang.org>
Thu, 12 Dec 2024 00:20:58 +0000 (19:20 -0500)
committerThan McIntosh <thanm@golang.org>
Tue, 4 Mar 2025 12:48:46 +0000 (04:48 -0800)
This patch updates the compiler to generate DWARF5-style range lists
(e.g. entries that feed into .debug_rnglists) as opposed to
DWARF4-style range lists (which wind up in .debug_ranges). The DWARF5
format is much more compact, and can make indirect references to text
address via the .debug_addr section for further space savings.

Updates #26379.

Change-Id: I273a6283484b7fe33d79d5412e31c5155b22a7c0
Reviewed-on: https://go-review.googlesource.com/c/go/+/635345
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/internal/dwarf/dwarf.go
src/cmd/internal/dwarf/dwarf_defs.go
src/cmd/link/internal/ld/dwarf.go

index 44b0de7d7c2394751a76d171487207513e3f55a1..88dac10c1b74eaa525d85832bd8677fa64b3ca4a 100644 (file)
@@ -1059,12 +1059,41 @@ func PutBasedRanges(ctxt Context, sym Sym, ranges []Range) {
        ctxt.AddInt(sym, ps, 0)
 }
 
+// PutRngListRanges writes a DWARF5-style set of rangelist entries to sym,
+// using base as a starting/base address.
+func PutRngListRanges(ctxt Context, sym Sym, base Sym, ranges []Range) {
+       addULEB128 := func(v int64) {
+               b := sevenBitU(v)
+               if b == nil {
+                       var encbuf [20]byte
+                       b = AppendUleb128(encbuf[:0], uint64(v))
+               }
+               ctxt.AddBytes(sym, b)
+       }
+       // First entry is base address.
+       ctxt.AddInt(sym, 1, DW_RLE_base_addressx)
+       ctxt.AddIndirectTextRef(sym, base)
+       // Remaining entries are .debug_rnglist offset pairs
+       for _, r := range ranges {
+               ctxt.AddInt(sym, 1, DW_RLE_offset_pair)
+               addULEB128(r.Start)
+               addULEB128(r.End)
+       }
+       // Terminator to mark end of list
+       ctxt.AddInt(sym, 1, DW_RLE_end_of_list)
+}
+
 // PutRanges writes a range table to s.Ranges.
 // All addresses in ranges are relative to s.base.
 func (s *FnState) PutRanges(ctxt Context, ranges []Range) {
        ps := ctxt.PtrSize()
        sym, base := s.Ranges, s.StartPC
 
+       if buildcfg.Experiment.Dwarf5 {
+               PutRngListRanges(ctxt, sym, base, ranges)
+               return
+       }
+
        if s.UseBASEntries {
                // Using a Base Address Selection Entry reduces the number of relocations, but
                // this is not done on macOS because it is not supported by dsymutil/dwarfdump/lldb
index 549a809bfb8ac1df21c363bb89bcbf2fd16b27a3..db4245e95d91b91c03f14646aca084b16685d5f6 100644 (file)
@@ -452,6 +452,19 @@ const (
        DW_LNE_hi_user      = 0xff
 )
 
+// Table 7.25 (DWARF version 5), containing the encodings for the
+// .debug_rnglists entry formats.
+const (
+       DW_RLE_end_of_list   = 0x0
+       DW_RLE_base_addressx = 0x1
+       DW_RLE_startx_endx   = 0x2
+       DW_RLE_startx_length = 0x3
+       DW_RLE_offset_pair   = 0x4
+       DW_RLE_base_address  = 0x5
+       DW_RLE_start_end     = 0x6
+       DW_RLE_start_length  = 0x7
+)
+
 // Table 7.27 (DWARF version 5), containing the encodings for the
 // line number header entry formats.
 const (
index e6de8b59146a984a7fe844805391cb5a69b9fae5..cabdedecf1a41eb3aa851e641bed8ca5c8762fd1 100644 (file)
@@ -148,6 +148,13 @@ func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64)
        dsu.AddSymRef(c.arch, tds, ofs, objabi.R_DWARFSECREF, size)
 }
 
+func (c dwctxt) AddIndirectTextRef(s dwarf.Sym, t interface{}) {
+       ds := loader.Sym(s.(dwSym))
+       dsu := c.ldr.MakeSymbolUpdater(ds)
+       tds := loader.Sym(t.(dwSym))
+       dsu.AddSymRef(c.arch, tds, 0, objabi.R_DWTXTADDR_U4, 4)
+}
+
 func (c dwctxt) Logf(format string, args ...interface{}) {
        c.linkctxt.Logf(format, args...)
 }
@@ -166,12 +173,6 @@ func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []
        panic("should be used only in the compiler")
 }
 
-func (c dwctxt) AddIndirectTextRef(s dwarf.Sym, t interface{}) {
-       // NB: at the moment unused in the linker; will be needed
-       // later on in a subsequent patch.
-       panic("should be used only in the compiler")
-}
-
 func isDwarf64(ctxt *Link) bool {
        return ctxt.HeadType == objabi.Haix
 }
@@ -1470,7 +1471,7 @@ func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []
 // writepcranges generates the DW_AT_ranges table for compilation unit
 // "unit", and returns a collection of ranges symbols (one for the
 // compilation unit DIE itself and the remainder from functions in the unit).
-func (d *dwctxt) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, rangeProlog loader.Sym) []loader.Sym {
+func (d *dwctxt) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, rangeProlog loader.Sym, debugaddrsym loader.Sym) []loader.Sym {
 
        syms := make([]loader.Sym, 0, len(unit.RangeSyms)+1)
        syms = append(syms, rangeProlog)
@@ -1480,7 +1481,24 @@ func (d *dwctxt) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs [
        // Create PC ranges for the compilation unit DIE.
        newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym)
        newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base))
-       dwarf.PutBasedRanges(d, rDwSym, pcs)
+       if buildcfg.Experiment.Dwarf5 && debugaddrsym != 0 {
+               debugaddr := d.ldr.MakeSymbolUpdater(debugaddrsym)
+               // Write DWARF5-based ranges for the unit. This will introduce
+               // a series of new R_DWTXTADDR_* relocs, which we'll process
+               // in the loop immediately following this call.
+               dwarf.PutRngListRanges(d, rDwSym, dwSym(base), pcs)
+               drelocs := d.ldr.Relocs(rangeProlog)
+               for ri := 0; ri < drelocs.Count(); ri++ {
+                       r := drelocs.At(ri)
+                       if !r.Type().IsDwTxtAddr() {
+                               continue
+                       }
+                       cusym := d.dtolsym(unit.DWInfo.Sym)
+                       d.assignDebugAddrSlot(unit, cusym, r, debugaddr)
+               }
+       } else {
+               dwarf.PutBasedRanges(d, rDwSym, pcs)
+       }
 
        // Collect up the ranges for functions in the unit.
        rsize := uint64(rsu.Size())
@@ -2221,10 +2239,10 @@ func (d *dwctxt) dwUnitPortion(u *sym.CompilationUnit, abbrevsym loader.Sym, us
        if u.DWInfo.Abbrev != dwarf.DW_ABRV_COMPUNIT_TEXTLESS {
                us.linesyms = d.writelines(u, us.lineProlog)
                base := loader.Sym(u.Textp[0])
-               us.rangessyms = d.writepcranges(u, base, u.PCs, us.rangeProlog)
                if buildcfg.Experiment.Dwarf5 {
                        d.writedebugaddr(u, us.addrsym)
                }
+               us.rangessyms = d.writepcranges(u, base, u.PCs, us.rangeProlog, us.addrsym)
                us.locsyms = d.collectUnitLocs(u)
        }
        us.infosyms = d.writeUnitInfo(u, abbrevsym, us.addrsym, us.infoEpilog)
@@ -2238,33 +2256,42 @@ func (d *dwctxt) dwUnitPortion(u *sym.CompilationUnit, abbrevsym loader.Sym, us
 func (d *dwctxt) writedebugaddr(unit *sym.CompilationUnit, debugaddr loader.Sym) {
        dasu := d.ldr.MakeSymbolUpdater(debugaddr)
 
+       var dsyms []loader.Sym
        for _, s := range unit.Textp {
                fnSym := loader.Sym(s)
                // NB: this looks at SDWARFFCN; it will need to also look
                // at range and loc when they get there.
-               infosym, _, _, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym)
-
-               // Walk the relocations of the subprogram DIE symbol to collect
-               // relocations corresponding to indirect function references
-               // via .debug_addr.
-               drelocs := d.ldr.Relocs(infosym)
-               for ri := 0; ri < drelocs.Count(); ri++ {
-                       r := drelocs.At(ri)
-                       if !r.Type().IsDwTxtAddr() {
-                               continue
-                       }
-                       rsym := r.Sym()
-                       rst := d.ldr.SymType(rsym)
-                       // Do some consistency checks.
-                       if !rst.IsText() {
-                               // R_DWTXTADDR_* relocation should only refer to text
-                               // symbols, so something apparently went wrong here.
-                               log.Fatalf("internal error: R_DWTXTADDR_* relocation on dwinfosym for %s against non-function %s type:%s", d.ldr.SymName(fnSym), d.ldr.SymName(rsym), rst.String())
-                       }
-                       if runit := d.ldr.SymUnit(rsym); runit != unit {
-                               log.Fatalf("internal error: R_DWTXTADDR_* relocation target text sym unit mismatch (want %q got %q)", unit.Lib.Pkg, runit.Lib.Pkg)
+               infosym, _, rangessym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+
+               // Walk the relocations of the various DWARF symbols to
+               // collect relocations corresponding to indirect function
+               // references via .debug_addr.
+               dsyms = dsyms[:0]
+               dsyms = append(dsyms, infosym)
+               if rangessym != 0 {
+                       dsyms = append(dsyms, rangessym)
+               }
+               for _, dsym := range dsyms {
+                       drelocs := d.ldr.Relocs(dsym)
+                       for ri := 0; ri < drelocs.Count(); ri++ {
+                               r := drelocs.At(ri)
+                               if !r.Type().IsDwTxtAddr() {
+                                       continue
+                               }
+                               rsym := r.Sym()
+                               rst := d.ldr.SymType(rsym)
+                               // Do some consistency checks.
+                               if !rst.IsText() {
+                                       // R_DWTXTADDR_* relocation should only refer to
+                                       // text symbols, so something apparently went
+                                       // wrong here.
+                                       log.Fatalf("internal error: R_DWTXTADDR_* relocation on dwinfosym for %s against non-function %s type:%s", d.ldr.SymName(fnSym), d.ldr.SymName(rsym), rst.String())
+                               }
+                               if runit := d.ldr.SymUnit(rsym); runit != unit {
+                                       log.Fatalf("internal error: R_DWTXTADDR_* relocation target text sym unit mismatch (want %q got %q)", unit.Lib.Pkg, runit.Lib.Pkg)
+                               }
+                               d.assignDebugAddrSlot(unit, fnSym, r, dasu)
                        }
-                       d.assignDebugAddrSlot(unit, fnSym, r, dasu)
                }
        }
 }
@@ -2302,7 +2329,12 @@ func (d *dwctxt) dwarfGenerateDebugSyms() {
        frameSym := mkSecSym(".debug_frame")
        locSym := mkSecSym(".debug_loc")
        lineSym := mkSecSym(".debug_line")
-       rangesSym := mkSecSym(".debug_ranges")
+       var rangesSym loader.Sym
+       if buildcfg.Experiment.Dwarf5 {
+               rangesSym = mkSecSym(".debug_rnglists")
+       } else {
+               rangesSym = mkSecSym(".debug_ranges")
+       }
        infoSym := mkSecSym(".debug_info")
        var addrSym loader.Sym
        if buildcfg.Experiment.Dwarf5 {
@@ -2312,13 +2344,16 @@ func (d *dwctxt) dwarfGenerateDebugSyms() {
        // Create the section objects
        lineSec := dwarfSecInfo{syms: []loader.Sym{lineSym}}
        locSec := dwarfSecInfo{syms: []loader.Sym{locSym}}
-       rangesSec := dwarfSecInfo{syms: []loader.Sym{rangesSym}}
        frameSec := dwarfSecInfo{syms: []loader.Sym{frameSym}}
        infoSec := dwarfSecInfo{syms: []loader.Sym{infoSym}}
-       var addrSec dwarfSecInfo
+       var addrSec, rangesSec dwarfSecInfo
        if buildcfg.Experiment.Dwarf5 {
                addrHdr := d.writeDebugAddrHdr()
                addrSec.syms = []loader.Sym{addrSym, addrHdr}
+               rnglistsHdr := d.writeDebugRngListsHdr()
+               rangesSec.syms = []loader.Sym{rangesSym, rnglistsHdr}
+       } else {
+               rangesSec = dwarfSecInfo{syms: []loader.Sym{rangesSym}}
        }
 
        // Create any new symbols that will be needed during the
@@ -2386,13 +2421,19 @@ func (d *dwctxt) dwarfGenerateDebugSyms() {
        }
 
        if buildcfg.Experiment.Dwarf5 {
-               // Compute total size of the .debug_addr unit syms.
-               var addrtot uint64
+               // Compute total size of the DWARF5-specific .debug_* syms in
+               // each compilation unit.
+               var rltot, addrtot 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))
+                       }
                }
-               // Call a helper to patch the length field in the header.
+               // Call a helper to patch the length field in the headers.
                patchHdr(&addrSec, addrtot)
+               patchHdr(&rangesSec, rltot)
        }
 
        // Stitch together the results.
@@ -2462,10 +2503,13 @@ func dwarfaddshstrings(ctxt *Link, add func(string)) {
                return
        }
 
-       secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts", "ranges"}
+       secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts"}
        if buildcfg.Experiment.Dwarf5 {
-               secs = append(secs, "addr")
+               secs = append(secs, "addr", "rnglists")
+       } else {
+               secs = append(secs, "ranges")
        }
+
        for _, sec := range secs {
                add(".debug_" + sec)
                if ctxt.IsExternal() {
@@ -2623,6 +2667,20 @@ 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 {
+       su := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0))
+       su.SetType(sym.SDWARFRANGE)
+       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()
+}
+
 // 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.