Skip to content

Commit e7cd497

Browse files
committed
cmd: initial compiler+linker support for DWARF5 .debug_addr
This patch rolls the main .debug_info DWARF section from version 4 to version 5, and also introduces machinery in the Go compiler and linker for taking advantage of the DWARF5 ".debug_addr" section for subprogram DIE "high" and "low" PC attributes. All functionality is gated by GOEXPERIMENT=dwarf5. For the compiler portion of this patch, we add a new DIE attribute form "DW_FORM_addrx", which accepts as an argument a function (text) symbol. The dwarf "putattr" function is enhanced to handle this format by invoking a new dwarf context method "AddIndirectTextRef". Under the hood, this method invokes the Lsym method WriteDwTxtAddrx, which emits a new objabi.R_DWTXTADDR_* relocation. The size of the relocation is dependent on the number of functions in the package; we pick a size that is just big enough for the largest func index. In the linker portion of this patch, we now switch over to writing out a version number of 5 (instead of 4) in the compile unit header (this is required if we want to use addrx attributes). In the parallel portion of DWARF gen, within each compilation unit we scan subprogram DIEs to look for R_DWTXTADDR_* relocations, and when we find such a reloc, we assign a slot in the .debug_addr section for the func targeted. After the parallel portion is complete, we then walk through all of the compilation units to assign a value to their DW_AT_addr_base attribute, which points to the portion of the single .debug_addr section containing the text addrs for that compilation unit. Note that once this patch is in, programs built with GOEXPERIMENT=dwarf5 will have broken/damaged DWARF info; in particular, since we've changed only the CU and subprogram DIEs and haven't incorported the other changes mandated by DWARF5 (ex: .debug_ranges => .debug_rnglists) a lot of the variable location info will be missing/incorrect. This will obviously change in subsequent patches. Note also that R_DWTXTADDR_* can't be used effectively for lexical scope DIE hi/lo PC attrs, since there isn't a viable way to encode "addrx + constant" in the attribute value (you would need a new entry for each attr endpoint in .debug_addr, which would defeat the point). Updates #26379. Change-Id: I2dfc45c9a8333e7b2a58f8e3b88fc8701fefd006 Reviewed-on: https://go-review.googlesource.com/c/go/+/635337 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>
1 parent 282a14e commit e7cd497

File tree

12 files changed

+447
-59
lines changed

12 files changed

+447
-59
lines changed

src/cmd/compile/internal/gc/compile.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ func prepareFunc(fn *ir.Func) {
114114
ir.CurFunc = fn
115115
walk.Walk(fn)
116116
ir.CurFunc = nil // enforce no further uses of CurFunc
117+
118+
base.Ctxt.DwTextCount++
117119
}
118120

119121
// compileFunctions compiles all functions in compilequeue.

src/cmd/internal/dwarf/dwarf.go

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ type Context interface {
198198
AddCURelativeAddress(s Sym, t interface{}, ofs int64)
199199
AddSectionOffset(s Sym, size int, t interface{}, ofs int64)
200200
AddDWARFAddrSectionOffset(s Sym, t interface{}, ofs int64)
201+
AddIndirectTextRef(s Sym, t interface{})
201202
CurrentOffset(s Sym) int64
202203
RecordDclReference(from Sym, to Sym, dclIdx int, inlIndex int)
203204
RecordChildDieOffsets(s Sym, vars []*Var, offsets []int32)
@@ -368,21 +369,35 @@ type dwAbbrev struct {
368369
var abbrevsFinalized bool
369370

370371
// expandPseudoForm takes an input DW_FORM_xxx value and translates it
371-
// into a platform-appropriate concrete form. Existing concrete/real
372-
// DW_FORM values are left untouched. For the moment the only
373-
// pseudo-form is DW_FORM_udata_pseudo, which gets expanded to
374-
// DW_FORM_data4 on Darwin and DW_FORM_udata everywhere else. See
375-
// issue #31459 for more context.
372+
// into a version- and platform-appropriate concrete form. Existing
373+
// concrete/real DW_FORM values are left untouched. For the moment the
374+
// only platform-specific pseudo-form is DW_FORM_udata_pseudo, which
375+
// gets expanded to DW_FORM_data4 on Darwin and DW_FORM_udata
376+
// everywhere else. See issue #31459 for more context. Then we have a
377+
// pair of pseudo-forms for lo and hi PC attributes, which are
378+
// expanded differently depending on whether we're generating DWARF
379+
// version 4 or 5.
376380
func expandPseudoForm(form uint8) uint8 {
377-
// Is this a pseudo-form?
378-
if form != DW_FORM_udata_pseudo {
381+
switch form {
382+
case DW_FORM_udata_pseudo:
383+
expandedForm := DW_FORM_udata
384+
if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" {
385+
expandedForm = DW_FORM_data4
386+
}
387+
return uint8(expandedForm)
388+
case DW_FORM_lo_pc_pseudo:
389+
if buildcfg.Experiment.Dwarf5 {
390+
return DW_FORM_addrx
391+
}
392+
return DW_FORM_addr
393+
case DW_FORM_hi_pc_pseudo:
394+
if buildcfg.Experiment.Dwarf5 {
395+
return DW_FORM_udata
396+
}
397+
return DW_FORM_addr
398+
default:
379399
return form
380400
}
381-
expandedForm := DW_FORM_udata
382-
if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" {
383-
expandedForm = DW_FORM_data4
384-
}
385-
return uint8(expandedForm)
386401
}
387402

388403
// Abbrevs returns the finalized abbrev array for the platform,
@@ -397,6 +412,25 @@ func Abbrevs() []dwAbbrev {
397412
abbrevs[i].attr[j].form = expandPseudoForm(abbrevs[i].attr[j].form)
398413
}
399414
}
415+
if buildcfg.Experiment.Dwarf5 {
416+
// Tack on a new DW_AT_addr_base attribute to the compunit DIE,
417+
// which will point to the offset in the .debug_addr section
418+
// containing entries for this comp unit (this attr gets
419+
// fixed up in the linker).
420+
for i := 1; i < len(abbrevs); i++ {
421+
haveLo := false
422+
for j := 0; j < len(abbrevs[i].attr); j++ {
423+
if abbrevs[i].attr[j].attr == DW_AT_low_pc {
424+
haveLo = true
425+
}
426+
}
427+
if abbrevs[i].tag == DW_TAG_compile_unit && haveLo {
428+
abbrevs[i].attr = append(abbrevs[i].attr,
429+
dwAttrForm{DW_AT_addr_base, DW_FORM_sec_offset})
430+
}
431+
}
432+
}
433+
400434
abbrevsFinalized = true
401435
return abbrevs
402436
}
@@ -422,6 +456,7 @@ var abbrevs = []dwAbbrev{
422456
{DW_AT_comp_dir, DW_FORM_string},
423457
{DW_AT_producer, DW_FORM_string},
424458
{DW_AT_go_package_name, DW_FORM_string},
459+
// NB: DWARF5 adds DW_AT_addr_base here.
425460
},
426461
},
427462

@@ -444,8 +479,8 @@ var abbrevs = []dwAbbrev{
444479
DW_CHILDREN_yes,
445480
[]dwAttrForm{
446481
{DW_AT_name, DW_FORM_string},
447-
{DW_AT_low_pc, DW_FORM_addr},
448-
{DW_AT_high_pc, DW_FORM_addr},
482+
{DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
483+
{DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
449484
{DW_AT_frame_base, DW_FORM_block1},
450485
{DW_AT_decl_file, DW_FORM_data4},
451486
{DW_AT_decl_line, DW_FORM_udata},
@@ -459,8 +494,8 @@ var abbrevs = []dwAbbrev{
459494
DW_CHILDREN_yes,
460495
[]dwAttrForm{
461496
{DW_AT_name, DW_FORM_string},
462-
{DW_AT_low_pc, DW_FORM_addr},
463-
{DW_AT_high_pc, DW_FORM_addr},
497+
{DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
498+
{DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
464499
{DW_AT_frame_base, DW_FORM_block1},
465500
{DW_AT_trampoline, DW_FORM_flag},
466501
},
@@ -484,8 +519,8 @@ var abbrevs = []dwAbbrev{
484519
DW_CHILDREN_yes,
485520
[]dwAttrForm{
486521
{DW_AT_abstract_origin, DW_FORM_ref_addr},
487-
{DW_AT_low_pc, DW_FORM_addr},
488-
{DW_AT_high_pc, DW_FORM_addr},
522+
{DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
523+
{DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
489524
{DW_AT_frame_base, DW_FORM_block1},
490525
},
491526
},
@@ -496,8 +531,8 @@ var abbrevs = []dwAbbrev{
496531
DW_CHILDREN_yes,
497532
[]dwAttrForm{
498533
{DW_AT_abstract_origin, DW_FORM_ref_addr},
499-
{DW_AT_low_pc, DW_FORM_addr},
500-
{DW_AT_high_pc, DW_FORM_addr},
534+
{DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
535+
{DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
501536
{DW_AT_frame_base, DW_FORM_block1},
502537
{DW_AT_trampoline, DW_FORM_flag},
503538
},
@@ -509,8 +544,8 @@ var abbrevs = []dwAbbrev{
509544
DW_CHILDREN_yes,
510545
[]dwAttrForm{
511546
{DW_AT_abstract_origin, DW_FORM_ref_addr},
512-
{DW_AT_low_pc, DW_FORM_addr},
513-
{DW_AT_high_pc, DW_FORM_addr},
547+
{DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
548+
{DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
514549
{DW_AT_call_file, DW_FORM_data4},
515550
{DW_AT_call_line, DW_FORM_udata_pseudo}, // pseudo-form
516551
},
@@ -565,6 +600,12 @@ var abbrevs = []dwAbbrev{
565600
DW_TAG_lexical_block,
566601
DW_CHILDREN_yes,
567602
[]dwAttrForm{
603+
// Note: we can't take advantage of DW_FORM_addrx here,
604+
// since there is no way (at least at the moment) to
605+
// have an encoding for low_pc of the form "addrx + constant"
606+
// in DWARF5. If we wanted to use addrx, we'd need to create
607+
// a whole new entry in .debug_addr for the block start,
608+
// which would kind of defeat the point.
568609
{DW_AT_low_pc, DW_FORM_addr},
569610
{DW_AT_high_pc, DW_FORM_addr},
570611
},
@@ -943,6 +984,9 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da
943984
}
944985
ctxt.AddDWARFAddrSectionOffset(s, data, value)
945986

987+
case DW_FORM_addrx: // index into .debug_addr section
988+
ctxt.AddIndirectTextRef(s, data)
989+
946990
case DW_FORM_ref1, // reference within the compilation unit
947991
DW_FORM_ref2, // reference
948992
DW_FORM_ref4, // reference
@@ -1241,8 +1285,7 @@ func putInlinedFunc(ctxt Context, s *FnState, callIdx int) error {
12411285
} else {
12421286
st := ic.Ranges[0].Start
12431287
en := ic.Ranges[0].End
1244-
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, st, s.StartPC)
1245-
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, en, s.StartPC)
1288+
emitHiLoPc(ctxt, abbrev, s, st, en)
12461289
}
12471290

12481291
// Emit call file, line attrs.
@@ -1274,14 +1317,24 @@ func putInlinedFunc(ctxt Context, s *FnState, callIdx int) error {
12741317
return nil
12751318
}
12761319

1320+
func emitHiLoPc(ctxt Context, abbrev int, fns *FnState, st int64, en int64) {
1321+
if buildcfg.Experiment.Dwarf5 {
1322+
putattr(ctxt, fns.Info, abbrev, DW_FORM_addrx, DW_CLS_CONSTANT, st, fns.StartPC)
1323+
putattr(ctxt, fns.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, en, 0)
1324+
} else {
1325+
putattr(ctxt, fns.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, st, fns.StartPC)
1326+
putattr(ctxt, fns.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, en, fns.StartPC)
1327+
}
1328+
}
1329+
12771330
// Emit DWARF attributes and child DIEs for a 'concrete' subprogram,
12781331
// meaning the out-of-line copy of a function that was inlined at some
12791332
// point during the compilation of its containing package. The first
12801333
// attribute for a concrete DIE is a reference to the 'abstract' DIE
12811334
// for the function (which holds location-independent attributes such
12821335
// as name, type), then the remainder of the attributes are specific
12831336
// to this instance (location, frame base, etc).
1284-
func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error {
1337+
func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool, fncount int) error {
12851338
if logDwarf {
12861339
ctxt.Logf("PutConcreteFunc(%v)\n", s.Info)
12871340
}
@@ -1295,8 +1348,7 @@ func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error {
12951348
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, s.Absfn)
12961349

12971350
// Start/end PC.
1298-
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
1299-
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
1351+
emitHiLoPc(ctxt, abbrev, s, 0, s.Size)
13001352

13011353
// cfa / frame base
13021354
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
@@ -1343,8 +1395,7 @@ func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error {
13431395
}
13441396

13451397
putattr(ctxt, s.Info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
1346-
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
1347-
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
1398+
emitHiLoPc(ctxt, abbrev, s, 0, s.Size)
13481399
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
13491400
if isWrapper {
13501401
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)

src/cmd/internal/dwarf/dwarf_defs.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ const (
187187
DW_AT_elemental = 0x66 // flag
188188
DW_AT_pure = 0x67 // flag
189189
DW_AT_recursive = 0x68 // flag
190+
DW_AT_addr_base = 0x73 // addrptr
190191

191192
DW_AT_lo_user = 0x2000 // ---
192193
DW_AT_hi_user = 0x3fff // ---
@@ -219,9 +220,14 @@ const (
219220
DW_FORM_sec_offset = 0x17 // lineptr, loclistptr, macptr, rangelistptr
220221
DW_FORM_exprloc = 0x18 // exprloc
221222
DW_FORM_flag_present = 0x19 // flag
222-
DW_FORM_ref_sig8 = 0x20 // reference
223+
// Dwarf5
224+
DW_FORM_addrx = 0x1b
223225
// Pseudo-form: expanded to data4 on IOS, udata elsewhere.
224226
DW_FORM_udata_pseudo = 0x99
227+
// Pseudo-form: expands to DW_FORM_addrx in DWARF5, DW_FORM_addr in DWARF4
228+
DW_FORM_lo_pc_pseudo = 0x9a
229+
// Pseudo-form: expands to DW_FORM_udata in DWARF5, DW_FORM_addr in DWARF4
230+
DW_FORM_hi_pc_pseudo = 0x9b
225231
)
226232

227233
// Table 24 (#operands, notes)

src/cmd/internal/obj/data.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,22 @@ func (s *LSym) AddRel(ctxt *Link, rel Reloc) {
210210
}
211211
s.R = append(s.R, rel)
212212
}
213+
214+
// WriteDwTxtAddrx appends a zero blob of the proper size to s at off
215+
// and attaches one of the various R_DWTXTADDR_U* relocations to the
216+
// symbol. Here size is dependent on the total number of functions in
217+
// the package (for more on why this is needed, consult the
218+
// .debug_addr generation code in the linker).
219+
func (s *LSym) WriteDwTxtAddrx(ctxt *Link, off int64, rsym *LSym, maxFuncs int) {
220+
rtype, sz := objabi.FuncCountToDwTxtAddrFlavor(maxFuncs)
221+
s.prepwrite(ctxt, off, sz)
222+
if int64(int32(off)) != off {
223+
ctxt.Diag("WriteDwTxtAddrx: off overflow %d in %s", off, s.Name)
224+
}
225+
s.AddRel(ctxt, Reloc{
226+
Type: rtype,
227+
Off: int32(off),
228+
Siz: uint8(sz),
229+
Sym: rsym,
230+
})
231+
}

src/cmd/internal/obj/dwarf.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,16 @@ func (c dwCtxt) Logf(format string, args ...interface{}) {
288288
c.Link.Logf(format, args...)
289289
}
290290

291+
func (c dwCtxt) AddIndirectTextRef(s dwarf.Sym, t interface{}) {
292+
ls := s.(*LSym)
293+
tsym := t.(*LSym)
294+
// Note the doubling below -- DwTextCount is an estimate and
295+
// usually a little short due to additional wrapper functions and
296+
// such; by using c.DwTextCount*2 as the limit we'll ensure that
297+
// we don't run out of space.
298+
ls.WriteDwTxtAddrx(c.Link, ls.Size, tsym, c.DwTextCount*2)
299+
}
300+
291301
func isDwarf64(ctxt *Link) bool {
292302
return ctxt.Headtype == objabi.Haix
293303
}
@@ -371,7 +381,8 @@ func (ctxt *Link) populateDWARF(curfn Func, s *LSym) {
371381
if err != nil {
372382
ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
373383
}
374-
err = dwarf.PutConcreteFunc(dwctxt, fnstate, s.Wrapper())
384+
err = dwarf.PutConcreteFunc(dwctxt, fnstate, s.Wrapper(),
385+
ctxt.DwTextCount)
375386
} else {
376387
err = dwarf.PutDefaultFunc(dwctxt, fnstate, s.Wrapper())
377388
}

src/cmd/internal/obj/link.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,7 @@ type Link struct {
11581158
PosTable src.PosTable
11591159
InlTree InlTree // global inlining tree used by gc/inl.go
11601160
DwFixups *DwarfFixupTable
1161+
DwTextCount int
11611162
Imports []goobj.ImportedPkg
11621163
DiagFunc func(string, ...interface{})
11631164
DiagFlush func()

src/cmd/link/internal/dwtest/dwtest.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,58 @@ func (ex *Examiner) Named(name string) []*dwarf.Entry {
195195
}
196196
return ret
197197
}
198+
199+
// SubprogLoAndHighPc returns the values of the lo_pc and high_pc
200+
// attrs of the DWARF DIE subprogdie. For DWARF versions 2-3, both of
201+
// these attributes had to be of class address; with DWARF 4 the rules
202+
// were changed, allowing compilers to emit a high PC attr of class
203+
// constant, where the high PC could be computed by starting with the
204+
// low PC address and then adding in the high_pc attr offset. This
205+
// function accepts both styles of specifying a hi/lo pair, returning
206+
// the values or an error if the attributes are malformed in some way.
207+
func SubprogLoAndHighPc(subprogdie *dwarf.Entry) (lo uint64, hi uint64, err error) {
208+
// The low_pc attr for a subprogram DIE has to be of class address.
209+
lofield := subprogdie.AttrField(dwarf.AttrLowpc)
210+
if lofield == nil {
211+
err = fmt.Errorf("subprogram DIE has no low_pc attr")
212+
return
213+
}
214+
if lofield.Class != dwarf.ClassAddress {
215+
err = fmt.Errorf("subprogram DIE low_pc attr is not of class address")
216+
return
217+
}
218+
if lopc, ok := lofield.Val.(uint64); ok {
219+
lo = lopc
220+
} else {
221+
err = fmt.Errorf("subprogram DIE low_pc not convertible to uint64")
222+
return
223+
}
224+
225+
// For the high_pc value, we'll accept either an address or a constant
226+
// offset from lo pc.
227+
hifield := subprogdie.AttrField(dwarf.AttrHighpc)
228+
if hifield == nil {
229+
err = fmt.Errorf("subprogram DIE has no high_pc attr")
230+
return
231+
}
232+
switch hifield.Class {
233+
case dwarf.ClassAddress:
234+
if hipc, ok := hifield.Val.(uint64); ok {
235+
hi = hipc
236+
} else {
237+
err = fmt.Errorf("subprogram DIE high not convertible to uint64")
238+
return
239+
}
240+
case dwarf.ClassConstant:
241+
if hioff, ok := hifield.Val.(int64); ok {
242+
hi = lo + uint64(hioff)
243+
} else {
244+
err = fmt.Errorf("subprogram DIE high_pc not convertible to uint64")
245+
return
246+
}
247+
default:
248+
err = fmt.Errorf("subprogram DIE high_pc unknown value class %s",
249+
hifield.Class)
250+
}
251+
return
252+
}

0 commit comments

Comments
 (0)