ir.CurFunc = fn
walk.Walk(fn)
ir.CurFunc = nil // enforce no further uses of CurFunc
+
+ base.Ctxt.DwTextCount++
}
// compileFunctions compiles all functions in compilequeue.
AddCURelativeAddress(s Sym, t interface{}, ofs int64)
AddSectionOffset(s Sym, size int, t interface{}, ofs int64)
AddDWARFAddrSectionOffset(s Sym, t interface{}, ofs int64)
+ AddIndirectTextRef(s Sym, t interface{})
CurrentOffset(s Sym) int64
RecordDclReference(from Sym, to Sym, dclIdx int, inlIndex int)
RecordChildDieOffsets(s Sym, vars []*Var, offsets []int32)
var abbrevsFinalized bool
// expandPseudoForm takes an input DW_FORM_xxx value and translates it
-// into a platform-appropriate concrete form. Existing concrete/real
-// DW_FORM values are left untouched. For the moment the only
-// pseudo-form is DW_FORM_udata_pseudo, which gets expanded to
-// DW_FORM_data4 on Darwin and DW_FORM_udata everywhere else. See
-// issue #31459 for more context.
+// into a version- and platform-appropriate concrete form. Existing
+// concrete/real DW_FORM values are left untouched. For the moment the
+// only platform-specific pseudo-form is DW_FORM_udata_pseudo, which
+// gets expanded to DW_FORM_data4 on Darwin and DW_FORM_udata
+// everywhere else. See issue #31459 for more context. Then we have a
+// pair of pseudo-forms for lo and hi PC attributes, which are
+// expanded differently depending on whether we're generating DWARF
+// version 4 or 5.
func expandPseudoForm(form uint8) uint8 {
- // Is this a pseudo-form?
- if form != DW_FORM_udata_pseudo {
+ switch form {
+ case DW_FORM_udata_pseudo:
+ expandedForm := DW_FORM_udata
+ if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" {
+ expandedForm = DW_FORM_data4
+ }
+ return uint8(expandedForm)
+ case DW_FORM_lo_pc_pseudo:
+ if buildcfg.Experiment.Dwarf5 {
+ return DW_FORM_addrx
+ }
+ return DW_FORM_addr
+ case DW_FORM_hi_pc_pseudo:
+ if buildcfg.Experiment.Dwarf5 {
+ return DW_FORM_udata
+ }
+ return DW_FORM_addr
+ default:
return form
}
- expandedForm := DW_FORM_udata
- if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" {
- expandedForm = DW_FORM_data4
- }
- return uint8(expandedForm)
}
// Abbrevs returns the finalized abbrev array for the platform,
abbrevs[i].attr[j].form = expandPseudoForm(abbrevs[i].attr[j].form)
}
}
+ if buildcfg.Experiment.Dwarf5 {
+ // Tack on a new DW_AT_addr_base attribute to the compunit DIE,
+ // which will point to the offset in the .debug_addr section
+ // containing entries for this comp unit (this attr gets
+ // fixed up in the linker).
+ for i := 1; i < len(abbrevs); i++ {
+ haveLo := false
+ for j := 0; j < len(abbrevs[i].attr); j++ {
+ if abbrevs[i].attr[j].attr == DW_AT_low_pc {
+ haveLo = true
+ }
+ }
+ if abbrevs[i].tag == DW_TAG_compile_unit && haveLo {
+ abbrevs[i].attr = append(abbrevs[i].attr,
+ dwAttrForm{DW_AT_addr_base, DW_FORM_sec_offset})
+ }
+ }
+ }
+
abbrevsFinalized = true
return abbrevs
}
{DW_AT_comp_dir, DW_FORM_string},
{DW_AT_producer, DW_FORM_string},
{DW_AT_go_package_name, DW_FORM_string},
+ // NB: DWARF5 adds DW_AT_addr_base here.
},
},
DW_CHILDREN_yes,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
- {DW_AT_low_pc, DW_FORM_addr},
- {DW_AT_high_pc, DW_FORM_addr},
+ {DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
+ {DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
{DW_AT_frame_base, DW_FORM_block1},
{DW_AT_decl_file, DW_FORM_data4},
{DW_AT_decl_line, DW_FORM_udata},
DW_CHILDREN_yes,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
- {DW_AT_low_pc, DW_FORM_addr},
- {DW_AT_high_pc, DW_FORM_addr},
+ {DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
+ {DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
{DW_AT_frame_base, DW_FORM_block1},
{DW_AT_trampoline, DW_FORM_flag},
},
DW_CHILDREN_yes,
[]dwAttrForm{
{DW_AT_abstract_origin, DW_FORM_ref_addr},
- {DW_AT_low_pc, DW_FORM_addr},
- {DW_AT_high_pc, DW_FORM_addr},
+ {DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
+ {DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
{DW_AT_frame_base, DW_FORM_block1},
},
},
DW_CHILDREN_yes,
[]dwAttrForm{
{DW_AT_abstract_origin, DW_FORM_ref_addr},
- {DW_AT_low_pc, DW_FORM_addr},
- {DW_AT_high_pc, DW_FORM_addr},
+ {DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
+ {DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
{DW_AT_frame_base, DW_FORM_block1},
{DW_AT_trampoline, DW_FORM_flag},
},
DW_CHILDREN_yes,
[]dwAttrForm{
{DW_AT_abstract_origin, DW_FORM_ref_addr},
- {DW_AT_low_pc, DW_FORM_addr},
- {DW_AT_high_pc, DW_FORM_addr},
+ {DW_AT_low_pc, DW_FORM_lo_pc_pseudo},
+ {DW_AT_high_pc, DW_FORM_hi_pc_pseudo},
{DW_AT_call_file, DW_FORM_data4},
{DW_AT_call_line, DW_FORM_udata_pseudo}, // pseudo-form
},
DW_TAG_lexical_block,
DW_CHILDREN_yes,
[]dwAttrForm{
+ // Note: we can't take advantage of DW_FORM_addrx here,
+ // since there is no way (at least at the moment) to
+ // have an encoding for low_pc of the form "addrx + constant"
+ // in DWARF5. If we wanted to use addrx, we'd need to create
+ // a whole new entry in .debug_addr for the block start,
+ // which would kind of defeat the point.
{DW_AT_low_pc, DW_FORM_addr},
{DW_AT_high_pc, DW_FORM_addr},
},
}
ctxt.AddDWARFAddrSectionOffset(s, data, value)
+ case DW_FORM_addrx: // index into .debug_addr section
+ ctxt.AddIndirectTextRef(s, data)
+
case DW_FORM_ref1, // reference within the compilation unit
DW_FORM_ref2, // reference
DW_FORM_ref4, // reference
} else {
st := ic.Ranges[0].Start
en := ic.Ranges[0].End
- putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, st, s.StartPC)
- putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, en, s.StartPC)
+ emitHiLoPc(ctxt, abbrev, s, st, en)
}
// Emit call file, line attrs.
return nil
}
+func emitHiLoPc(ctxt Context, abbrev int, fns *FnState, st int64, en int64) {
+ if buildcfg.Experiment.Dwarf5 {
+ putattr(ctxt, fns.Info, abbrev, DW_FORM_addrx, DW_CLS_CONSTANT, st, fns.StartPC)
+ putattr(ctxt, fns.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, en, 0)
+ } else {
+ putattr(ctxt, fns.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, st, fns.StartPC)
+ putattr(ctxt, fns.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, en, fns.StartPC)
+ }
+}
+
// Emit DWARF attributes and child DIEs for a 'concrete' subprogram,
// meaning the out-of-line copy of a function that was inlined at some
// point during the compilation of its containing package. The first
// for the function (which holds location-independent attributes such
// as name, type), then the remainder of the attributes are specific
// to this instance (location, frame base, etc).
-func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error {
+func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool, fncount int) error {
if logDwarf {
ctxt.Logf("PutConcreteFunc(%v)\n", s.Info)
}
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, s.Absfn)
// Start/end PC.
- putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
- putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
+ emitHiLoPc(ctxt, abbrev, s, 0, s.Size)
// cfa / frame base
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
}
putattr(ctxt, s.Info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
- putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
- putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
+ emitHiLoPc(ctxt, abbrev, s, 0, s.Size)
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
if isWrapper {
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)
DW_AT_elemental = 0x66 // flag
DW_AT_pure = 0x67 // flag
DW_AT_recursive = 0x68 // flag
+ DW_AT_addr_base = 0x73 // addrptr
DW_AT_lo_user = 0x2000 // ---
DW_AT_hi_user = 0x3fff // ---
DW_FORM_sec_offset = 0x17 // lineptr, loclistptr, macptr, rangelistptr
DW_FORM_exprloc = 0x18 // exprloc
DW_FORM_flag_present = 0x19 // flag
- DW_FORM_ref_sig8 = 0x20 // reference
+ // Dwarf5
+ DW_FORM_addrx = 0x1b
// Pseudo-form: expanded to data4 on IOS, udata elsewhere.
DW_FORM_udata_pseudo = 0x99
+ // Pseudo-form: expands to DW_FORM_addrx in DWARF5, DW_FORM_addr in DWARF4
+ DW_FORM_lo_pc_pseudo = 0x9a
+ // Pseudo-form: expands to DW_FORM_udata in DWARF5, DW_FORM_addr in DWARF4
+ DW_FORM_hi_pc_pseudo = 0x9b
)
// Table 24 (#operands, notes)
}
s.R = append(s.R, rel)
}
+
+// WriteDwTxtAddrx appends a zero blob of the proper size to s at off
+// and attaches one of the various R_DWTXTADDR_U* relocations to the
+// symbol. Here size is dependent on the total number of functions in
+// the package (for more on why this is needed, consult the
+// .debug_addr generation code in the linker).
+func (s *LSym) WriteDwTxtAddrx(ctxt *Link, off int64, rsym *LSym, maxFuncs int) {
+ rtype, sz := objabi.FuncCountToDwTxtAddrFlavor(maxFuncs)
+ s.prepwrite(ctxt, off, sz)
+ if int64(int32(off)) != off {
+ ctxt.Diag("WriteDwTxtAddrx: off overflow %d in %s", off, s.Name)
+ }
+ s.AddRel(ctxt, Reloc{
+ Type: rtype,
+ Off: int32(off),
+ Siz: uint8(sz),
+ Sym: rsym,
+ })
+}
c.Link.Logf(format, args...)
}
+func (c dwCtxt) AddIndirectTextRef(s dwarf.Sym, t interface{}) {
+ ls := s.(*LSym)
+ tsym := t.(*LSym)
+ // Note the doubling below -- DwTextCount is an estimate and
+ // usually a little short due to additional wrapper functions and
+ // such; by using c.DwTextCount*2 as the limit we'll ensure that
+ // we don't run out of space.
+ ls.WriteDwTxtAddrx(c.Link, ls.Size, tsym, c.DwTextCount*2)
+}
+
func isDwarf64(ctxt *Link) bool {
return ctxt.Headtype == objabi.Haix
}
if err != nil {
ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
}
- err = dwarf.PutConcreteFunc(dwctxt, fnstate, s.Wrapper())
+ err = dwarf.PutConcreteFunc(dwctxt, fnstate, s.Wrapper(),
+ ctxt.DwTextCount)
} else {
err = dwarf.PutDefaultFunc(dwctxt, fnstate, s.Wrapper())
}
PosTable src.PosTable
InlTree InlTree // global inlining tree used by gc/inl.go
DwFixups *DwarfFixupTable
+ DwTextCount int
Imports []goobj.ImportedPkg
DiagFunc func(string, ...interface{})
DiagFlush func()
}
return ret
}
+
+// SubprogLoAndHighPc returns the values of the lo_pc and high_pc
+// attrs of the DWARF DIE subprogdie. For DWARF versions 2-3, both of
+// these attributes had to be of class address; with DWARF 4 the rules
+// were changed, allowing compilers to emit a high PC attr of class
+// constant, where the high PC could be computed by starting with the
+// low PC address and then adding in the high_pc attr offset. This
+// function accepts both styles of specifying a hi/lo pair, returning
+// the values or an error if the attributes are malformed in some way.
+func SubprogLoAndHighPc(subprogdie *dwarf.Entry) (lo uint64, hi uint64, err error) {
+ // The low_pc attr for a subprogram DIE has to be of class address.
+ lofield := subprogdie.AttrField(dwarf.AttrLowpc)
+ if lofield == nil {
+ err = fmt.Errorf("subprogram DIE has no low_pc attr")
+ return
+ }
+ if lofield.Class != dwarf.ClassAddress {
+ err = fmt.Errorf("subprogram DIE low_pc attr is not of class address")
+ return
+ }
+ if lopc, ok := lofield.Val.(uint64); ok {
+ lo = lopc
+ } else {
+ err = fmt.Errorf("subprogram DIE low_pc not convertible to uint64")
+ return
+ }
+
+ // For the high_pc value, we'll accept either an address or a constant
+ // offset from lo pc.
+ hifield := subprogdie.AttrField(dwarf.AttrHighpc)
+ if hifield == nil {
+ err = fmt.Errorf("subprogram DIE has no high_pc attr")
+ return
+ }
+ switch hifield.Class {
+ case dwarf.ClassAddress:
+ if hipc, ok := hifield.Val.(uint64); ok {
+ hi = hipc
+ } else {
+ err = fmt.Errorf("subprogram DIE high not convertible to uint64")
+ return
+ }
+ case dwarf.ClassConstant:
+ if hioff, ok := hifield.Val.(int64); ok {
+ hi = lo + uint64(hioff)
+ } else {
+ err = fmt.Errorf("subprogram DIE high_pc not convertible to uint64")
+ return
+ }
+ default:
+ err = fmt.Errorf("subprogram DIE high_pc unknown value class %s",
+ hifield.Class)
+ }
+ return
+}
st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", ldr.SymName(rs), uint64(o), ldr.SymValue(rs), r.Add())
errorexit()
}
+ case objabi.R_DWTXTADDR_U1, objabi.R_DWTXTADDR_U2, objabi.R_DWTXTADDR_U3, objabi.R_DWTXTADDR_U4:
+ unit := ldr.SymUnit(rs)
+ if idx, ok := unit.Addrs[sym.LoaderSym(rs)]; ok {
+ o = int64(idx)
+ } else {
+ st.err.Errorf(s, "missing .debug_addr index relocation target %s", ldr.SymName(rs))
+ }
+
+ // For these relocations we write a ULEB128, but using a
+ // cooked/hacked recipe that ensures the result has a
+ // fixed length. That is, if we're writing a value of 1
+ // with length requirement 3, we'll actually emit three
+ // bytes, 0x81 0x80 0x0.
+ _, leb128len := rt.DwTxtAddrRelocParams()
+ if err := writeUleb128FixedLength(P[off:], uint64(o), leb128len); err != nil {
+ st.err.Errorf(s, "internal error: %v applying %s to DWARF sym with reloc target %s", err, rt.String(), ldr.SymName(rs))
+ }
+ continue
+
case objabi.R_DWARFSECREF:
if ldr.SymSect(rs) == nil {
st.err.Errorf(s, "missing DWARF section for relocation target %s", ldr.SymName(rs))
// These reloc types don't need external relocations.
case objabi.R_ADDROFF, objabi.R_METHODOFF, objabi.R_ADDRCUOFF,
- objabi.R_SIZE, objabi.R_CONST, objabi.R_GOTOFF:
+ objabi.R_SIZE, objabi.R_CONST, objabi.R_GOTOFF,
+ objabi.R_DWTXTADDR_U1, objabi.R_DWTXTADDR_U2,
+ objabi.R_DWTXTADDR_U3, objabi.R_DWTXTADDR_U4:
return rr, false
}
return rr, true
}
return buf.Bytes()
}
+
+// writeUleb128FixedLength writes out value v in LEB128 encoded
+// format, ensuring that the space written takes up length bytes. When
+// extra space is needed, we write initial bytes with just the
+// continuation bit set. For example, if val is 1 and length is 3,
+// we'll write 0x80 0x80 0x1 (first two bytes with zero val but
+// continuation bit set). NB: this function adapted from a similar
+// function in cmd/link/internal/wasm, they could be commoned up if
+// needed.
+func writeUleb128FixedLength(b []byte, v uint64, length int) error {
+ for i := 0; i < length; i++ {
+ c := uint8(v & 0x7f)
+ v >>= 7
+ if i < length-1 {
+ c |= 0x80
+ }
+ b[i] = c
+ }
+ if v != 0 {
+ return fmt.Errorf("writeUleb128FixedLength: length too small")
+ }
+ return nil
+}
}
}
}
+
+func TestWriteULebFixedLength(t *testing.T) {
+ flavs := []objabi.RelocType{
+ objabi.R_DWTXTADDR_U1,
+ objabi.R_DWTXTADDR_U2,
+ objabi.R_DWTXTADDR_U3,
+ objabi.R_DWTXTADDR_U4,
+ }
+ var clear, scratch [7]byte
+ tmp := scratch[:]
+ for i := range 5 {
+ for _, rt := range flavs {
+ scratch = clear
+ _, leb128len := rt.DwTxtAddrRelocParams()
+ _, n := objabi.FuncCountToDwTxtAddrFlavor(i)
+ if n > leb128len {
+ continue
+ }
+ err := writeUleb128FixedLength(tmp, uint64(i), leb128len)
+ if err != nil {
+ t.Errorf("unexpected err %v on val %d flav %s leb128len %d",
+ err, i, rt.String(), leb128len)
+ continue
+ }
+ }
+ }
+}
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
}
* Walk DWarfDebugInfoEntries, and emit .debug_info
*/
-const (
- COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
-)
-
-func (d *dwctxt) writeUnitInfo(u *sym.CompilationUnit, abbrevsym loader.Sym, infoEpilog loader.Sym) []loader.Sym {
+func (d *dwctxt) writeUnitInfo(u *sym.CompilationUnit, abbrevsym loader.Sym, addrsym loader.Sym, infoEpilog loader.Sym) []loader.Sym {
syms := []loader.Sym{}
if len(u.Textp) == 0 && u.DWInfo.Child == nil && len(u.VarDIEs) == 0 {
return syms
// Write .debug_info Compilation Unit Header (sec 7.5.1)
// Fields marked with (*) must be changed for 64-bit dwarf
- // This must match COMPUNITHEADERSIZE above.
d.createUnitLength(su, 0) // unit_length (*), will be filled in later.
- su.AddUint16(d.arch, 4) // dwarf version (appendix F)
-
- // debug_abbrev_offset (*)
- d.addDwarfAddrRef(su, abbrevsym)
+ dwver := 4
+ if buildcfg.Experiment.Dwarf5 {
+ dwver = 5
+ }
+ su.AddUint16(d.arch, uint16(dwver)) // dwarf version (appendix F)
- su.AddUint8(uint8(d.arch.PtrSize)) // address_size
+ if buildcfg.Experiment.Dwarf5 {
+ // DWARF5 at this point requires
+ // 1. unit type
+ // 2. address size
+ // 3. abbrev offset
+ su.AddUint8(uint8(dwarf.DW_UT_compile))
+ su.AddUint8(uint8(d.arch.PtrSize))
+ d.addDwarfAddrRef(su, abbrevsym)
+ } else {
+ // DWARF4 requires
+ // 1. abbrev offset
+ // 2. address size
+ d.addDwarfAddrRef(su, abbrevsym)
+ su.AddUint8(uint8(d.arch.PtrSize))
+ }
ds := dwSym(s)
dwarf.Uleb128put(d, ds, int64(compunit.Abbrev))
+ if buildcfg.Experiment.Dwarf5 {
+ // If this CU has functions, update the DW_AT_addr_base
+ // attribute to point to the correct section symbol.
+ abattr := getattr(compunit, dwarf.DW_AT_addr_base)
+ if abattr != nil {
+ abattr.Data = dwSym(addrsym)
+ }
+ }
dwarf.PutAttrs(d, ds, compunit.Abbrev, compunit.Attr)
// This is an under-estimate; more will be needed for type DIEs.
return die
}
+// assignDebugAddrSlot assigns a slot (if needed) in the .debug_addr
+// section fragment of the specified unit for the function pointed to
+// by R_DWTXTADDR_* relocation r. The slot index selected will be
+// filled in when the relocation is actually applied/resolved.
+func (d *dwctxt) assignDebugAddrSlot(unit *sym.CompilationUnit, fnsym loader.Sym, r loader.Reloc, sb *loader.SymbolBuilder) {
+ rsym := r.Sym()
+ if unit.Addrs == nil {
+ unit.Addrs = make(map[sym.LoaderSym]uint32)
+ }
+ if _, ok := unit.Addrs[sym.LoaderSym(rsym)]; ok {
+ // already present, no work needed
+ } else {
+ sl := len(unit.Addrs)
+ rt := r.Type()
+ lim, _ := rt.DwTxtAddrRelocParams()
+ if sl > lim {
+ log.Fatalf("internal error: %s relocation overflow on infosym for %s", rt.String(), d.ldr.SymName(fnsym))
+ }
+ unit.Addrs[sym.LoaderSym(rsym)] = uint32(sl)
+ sb.AddAddrPlus(d.arch, rsym, 0)
+ data := sb.Data()
+ if d.arch.PtrSize == 4 {
+ d.arch.ByteOrder.PutUint32(data[len(data)-4:], uint32(d.ldr.SymValue(rsym)))
+ } else {
+ d.arch.ByteOrder.PutUint64(data[len(data)-8:], uint64(d.ldr.SymValue(rsym)))
+ }
+ }
+}
+
// dwarfVisitFunction takes a function (text) symbol and processes the
// subprogram DIE for the function and picks up any other DIEs
// (absfns, types) that it references.
}
newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname)
+ if buildcfg.Experiment.Dwarf5 && cuabrv == dwarf.DW_ABRV_COMPUNIT {
+ // For DWARF5, the CU die will have an attribute that
+ // points to the offset into the .debug_addr section
+ // that contains the compilation unit's
+ // contributions. Add this attribute now. Note that
+ // we'll later on update the Data field in this attr
+ // once we know the .debug_addr sym for the unit.
+ newattr(unit.DWInfo, dwarf.DW_AT_addr_base, dwarf.DW_CLS_REFERENCE, 0, 0)
+ }
+
// Scan all functions in this compilation unit, create
// DIEs for all referenced types, find all referenced
// abstract functions, visit range symbols. Note that
// dwarfGenerateDebugSyms constructs debug_line, debug_frame, and
// debug_loc. It also writes out the debug_info section using symbols
-// generated in dwarfGenerateDebugInfo2.
+// generated in dwarfGenerateDebugInfo.
func dwarfGenerateDebugSyms(ctxt *Link) {
if !dwarfEnabled(ctxt) {
return
infosyms []loader.Sym
locsyms []loader.Sym
rangessyms []loader.Sym
+ addrsym loader.Sym
}
-// dwUnitPortion assembles the DWARF content for a given compilation
-// unit: debug_info, debug_lines, debug_ranges, debug_loc (debug_frame
-// is handled elsewhere). Order is important; the calls to writelines
-// and writepcranges below make updates to the compilation unit DIE,
-// hence they have to happen before the call to writeUnitInfo.
+// dwUnitPortion assembles the DWARF content for a given comp unit:
+// debug_info, debug_lines, debug_ranges(V4) or debug_rnglists (V5),
+// debug_loc (V4) or debug_loclists (V5) and debug_addr (V5);
+// debug_frame is handled elsewhere. Order is important; the calls to
+// writelines and writepcranges below make updates to the compilation
+// unit DIE, hence they have to happen before the call to
+// writeUnitInfo.
func (d *dwctxt) dwUnitPortion(u *sym.CompilationUnit, abbrevsym loader.Sym, us *dwUnitSyms) {
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.locsyms = d.collectUnitLocs(u)
}
- us.infosyms = d.writeUnitInfo(u, abbrevsym, us.infoEpilog)
+ us.infosyms = d.writeUnitInfo(u, abbrevsym, us.addrsym, us.infoEpilog)
+}
+
+// writedebugaddr scans the symbols of interest in unit for
+// R_DWTXTADDR_R* relocations and converts these into the material
+// we'll need to generate the .debug_addr section for the unit. This
+// will create a map within the unit (as a side effect), mapping func
+// symbol to debug_addr slot.
+func (d *dwctxt) writedebugaddr(unit *sym.CompilationUnit, debugaddr loader.Sym) {
+ dasu := d.ldr.MakeSymbolUpdater(debugaddr)
+
+ 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)
+ }
+ d.assignDebugAddrSlot(unit, fnSym, r, dasu)
+ }
+ }
}
func (d *dwctxt) dwarfGenerateDebugSyms() {
s.SetReachable(true)
return s.Sym()
}
+
mkAnonSym := func(kind sym.SymKind) loader.Sym {
s := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0))
s.SetType(kind)
lineSym := mkSecSym(".debug_line")
rangesSym := mkSecSym(".debug_ranges")
infoSym := mkSecSym(".debug_info")
+ var addrSym loader.Sym
+ if buildcfg.Experiment.Dwarf5 {
+ addrSym = mkSecSym(".debug_addr")
+ }
// Create the section objects
lineSec := dwarfSecInfo{syms: []loader.Sym{lineSym}}
rangesSec := dwarfSecInfo{syms: []loader.Sym{rangesSym}}
frameSec := dwarfSecInfo{syms: []loader.Sym{frameSym}}
infoSec := dwarfSecInfo{syms: []loader.Sym{infoSym}}
+ var addrSec dwarfSecInfo
+ if buildcfg.Experiment.Dwarf5 {
+ addrHdr := d.writeDebugAddrHdr()
+ addrSec.syms = []loader.Sym{addrSym, addrHdr}
+ }
// Create any new symbols that will be needed during the
// parallel portion below.
us.lineProlog = mkAnonSym(sym.SDWARFLINES)
us.rangeProlog = mkAnonSym(sym.SDWARFRANGE)
us.infoEpilog = mkAnonSym(sym.SDWARFFCN)
+ us.addrsym = mkAnonSym(sym.SDWARFADDR)
}
var wg sync.WaitGroup
return syms
}
+ patchHdr := func(sec *dwarfSecInfo, len uint64) {
+ hdrsym := sec.syms[1]
+ len += uint64(d.ldr.SymSize(hdrsym))
+ su := d.ldr.MakeSymbolUpdater(hdrsym)
+ if isDwarf64(d.linkctxt) {
+ len -= 12 // sub size of length field
+ su.SetUint(d.arch, 4, uint64(len)) // 4 because of 0XFFFFFFFF
+ } else {
+ len -= 4 // subtract size of length field
+ su.SetUint32(d.arch, 0, uint32(len))
+ }
+ }
+
+ if buildcfg.Experiment.Dwarf5 {
+ // Compute total size of the .debug_addr unit syms.
+ var addrtot uint64
+ for i := 0; i < ncu; i++ {
+ addrtot += uint64(d.ldr.SymSize(unitSyms[i].addrsym))
+ }
+ // Call a helper to patch the length field in the header.
+ patchHdr(&addrSec, addrtot)
+ }
+
// Stitch together the results.
for i := 0; i < ncu; i++ {
r := &unitSyms[i]
infoSec.syms = append(infoSec.syms, markReachable(r.infosyms)...)
locSec.syms = append(locSec.syms, markReachable(r.locsyms)...)
rangesSec.syms = append(rangesSec.syms, markReachable(r.rangessyms)...)
+ if buildcfg.Experiment.Dwarf5 && r.addrsym != 0 {
+ addrSec.syms = append(addrSec.syms, r.addrsym)
+ }
}
dwarfp = append(dwarfp, lineSec)
dwarfp = append(dwarfp, frameSec)
dwarfp = append(dwarfp, locSec)
}
dwarfp = append(dwarfp, rangesSec)
+ if buildcfg.Experiment.Dwarf5 {
+ dwarfp = append(dwarfp, addrSec)
+ }
// Check to make sure we haven't listed any symbols more than once
// in the info section. This used to be done by setting and
}
secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts", "ranges"}
+ if buildcfg.Experiment.Dwarf5 {
+ secs = append(secs, "addr")
+ }
for _, sec := range secs {
add(".debug_" + sec)
if ctxt.IsExternal() {
defer dwsectCUSizeMu.Unlock()
dwsectCUSize[sname+"."+pkgname] += size
}
+
+// 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()
+}
maindie := findSubprogramDIE(t, ex, "main.main")
- // Collect the start/end PC for main.main
- lowpc := maindie.Val(dwarf.AttrLowpc).(uint64)
- highpc := maindie.Val(dwarf.AttrHighpc).(uint64)
+ // Collect the start/end PC for main.main. The format/class of the
+ // high PC attr may vary depending on which DWARF version we're generating;
+ // invoke a helper to handle the various possibilities.
+ // the low PC as opposed to an address; allow for both possibilities.
+ lowpc, highpc, perr := dwtest.SubprogLoAndHighPc(maindie)
+ if perr != nil {
+ t.Fatalf("main.main DIE malformed: %v", perr)
+ }
+ t.Logf("lo=0x%x hi=0x%x\n", lowpc, highpc)
// Now read the line table for the 'main' compilation unit.
mainIdx := ex.IdxFromOffset(maindie.Offset)
DWInfo *dwarf.DWDie // CU root DIE
FileTable []string // The file table used in this compilation unit.
- Consts LoaderSym // Package constants DIEs
- FuncDIEs []LoaderSym // Function DIE subtrees
- VarDIEs []LoaderSym // Global variable DIEs
- AbsFnDIEs []LoaderSym // Abstract function DIE subtrees
- RangeSyms []LoaderSym // Symbols for debug_range
- Textp []LoaderSym // Text symbols in this CU
+ Consts LoaderSym // Package constants DIEs
+ FuncDIEs []LoaderSym // Function DIE subtrees
+ VarDIEs []LoaderSym // Global variable DIEs
+ AbsFnDIEs []LoaderSym // Abstract function DIE subtrees
+ RangeSyms []LoaderSym // Symbols for debug_range
+ Textp []LoaderSym // Text symbols in this CU
+ Addrs map[LoaderSym]uint32 // slot in .debug_addr for fn sym (DWARF5)
}