Skip to content

Commit a8608bf

Browse files
committed
Use Go stdlib dwarf functions to simplify DIE type handling logic. Use interface for process.DWARF struct
Signed-off-by: Dom Del Nano <[email protected]>
1 parent 3eaa5d9 commit a8608bf

File tree

4 files changed

+104
-183
lines changed

4 files changed

+104
-183
lines changed

internal/pkg/inject/consts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func FindOffset(id structfield.ID, info *process.Info) (structfield.OffsetKey, e
160160
return structfield.OffsetKey{}, err
161161
}
162162

163-
v, err := process.DWARF{Reader: data.Reader()}.GoStructField(id)
163+
v, err := process.NewDWARF(data).GoStructField(id)
164164
if err != nil {
165165
return structfield.OffsetKey{}, err
166166
}

internal/pkg/process/dwarf.go

Lines changed: 100 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,33 @@ func (g *GoStackABIArgTracker) PopLocation(typeClass TypeClass, typeSize uint64,
167167
}, nil
168168
}
169169

170-
// DWARF provides convenience in accessing DWARF debugging data.
171-
type DWARF struct {
170+
// typeInfo stores pre-computed properties for a DWARF type node.
171+
type typeInfo struct {
172+
size uint64
173+
align uint64
174+
class TypeClass
175+
nVars int
176+
}
177+
178+
type DWARF interface {
179+
GoFuncFieldArgs(fn string) (map[string]FuncFieldArg, error)
180+
GoStructField(id structfield.ID) (int64, error)
181+
}
182+
183+
func NewDWARF(d *dwarf.Data) DWARF {
184+
return dwarfReader{
185+
Data: d,
186+
Reader: d.Reader(),
187+
typeCache: make(map[dwarf.Offset]typeInfo),
188+
}
189+
}
190+
191+
// dwarfReader provides convenience in accessing DWARF debugging data.
192+
type dwarfReader struct {
193+
Data *dwarf.Data
172194
Reader *dwarf.Reader
173195
argTracker ArgTracker
196+
typeCache map[dwarf.Offset]typeInfo
174197
}
175198

176199
type FuncFieldArg struct {
@@ -179,7 +202,7 @@ type FuncFieldArg struct {
179202
RetArg bool `json:"ret_arg"` // true if this is a return argument
180203
}
181204

182-
func (d DWARF) DetectSourceABI() (ABI, error) {
205+
func (d dwarfReader) DetectSourceABI() (ABI, error) {
183206
cus, err := d.EntriesWithTag(dwarf.TagCompileUnit)
184207
if err != nil || len(cus) == 0 {
185208
return AbiUnknown, fmt.Errorf("No compile units found")
@@ -203,15 +226,15 @@ func (d DWARF) DetectSourceABI() (ABI, error) {
203226
return abi, nil
204227
}
205228

206-
func (d DWARF) IsRetArg(die *dwarf.Entry) bool {
229+
func (d dwarfReader) IsRetArg(die *dwarf.Entry) bool {
207230
if f, ok := d.Field(die, dwarf.AttrVarParam); ok {
208231
varParam := f.Val.(bool)
209232
return varParam
210233
}
211234
return false
212235
}
213236

214-
func (d DWARF) GetTypeDIE(entry *dwarf.Entry) (*dwarf.Entry, error) {
237+
func (d dwarfReader) GetTypeDIE(entry *dwarf.Entry) (*dwarf.Entry, error) {
215238
var field dwarf.Field
216239
var found bool
217240
var err error
@@ -269,162 +292,76 @@ func combineTypeClasses(a, b TypeClass) TypeClass {
269292
return a
270293
}
271294

272-
func (d DWARF) GetTypeClass(entry *dwarf.Entry) (TypeClass, error) {
273-
switch entry.Tag {
274-
case dwarf.TagPointerType, dwarf.TagSubroutineType:
275-
return TypeClassInt, nil
276-
case dwarf.TagBaseType:
277-
field, ok := d.Field(entry, dwarf.AttrEncoding)
278-
if !ok {
279-
return TypeClassNone, fmt.Errorf("failed to get encoding attribute: %w", ErrDWARFEntry)
280-
}
281-
encoding, ok := field.Val.(int64)
282-
if !ok {
283-
return TypeClassNone, fmt.Errorf("encoding attribute is not a valid string: %w", ErrDWARFEntry)
284-
}
285-
// TODO(ddelnano): Determine how the less common float types should be handled (DW_ATE_complex_float, DW_ATE_imaginary_float, etc.)
286-
// 0x04 == DW_ATE_float
287-
if encoding == 0x04 {
288-
return TypeClassFloat, nil
289-
}
290-
return TypeClassInt, nil
291-
case dwarf.TagStructType:
292-
structTypeClass := TypeClassNone
293-
for {
294-
die, err := d.Reader.Next()
295-
if errors.Is(err, io.EOF) || die == nil || die.Tag == 0 {
296-
return structTypeClass, nil
297-
}
295+
func (d *dwarfReader) getTypeInfo(t dwarf.Type) (typeInfo, error) {
296+
var ti typeInfo
297+
298+
switch tt := t.(type) {
299+
case *dwarf.PtrType, *dwarf.BoolType, *dwarf.FuncType,
300+
*dwarf.IntType, *dwarf.UintType:
301+
sz := uint64(tt.Size())
302+
ti = typeInfo{
303+
size: sz,
304+
align: sz,
305+
class: TypeClassInt,
306+
nVars: 1,
307+
}
308+
309+
case *dwarf.TypedefType:
310+
return d.getTypeInfo(tt.Type)
311+
case *dwarf.FloatType:
312+
sz := uint64(tt.Size())
313+
ti = typeInfo{
314+
size: sz,
315+
align: sz,
316+
class: TypeClassFloat,
317+
nVars: 1,
318+
}
319+
320+
case *dwarf.StructType:
321+
ti.class = TypeClassNone
322+
for _, f := range tt.Field {
323+
sub, err := d.getTypeInfo(f.Type)
298324
if err != nil {
299-
return 0, fmt.Errorf("error reading struct members: %w", err)
300-
}
301-
if die.Tag == dwarf.TagMember {
302-
offset := die.Offset
303-
typeDie, err := d.GetTypeDIE(die)
304-
if err != nil {
305-
return 0, fmt.Errorf("error getting type DIE for member: %w", err)
306-
}
307-
typeClass, err := d.GetTypeClass(typeDie)
308-
if err != nil {
309-
return 0, fmt.Errorf("error getting alignment size for member: %w", err)
310-
}
311-
d.Reader.Seek(offset)
312-
d.Reader.Next()
313-
structTypeClass = combineTypeClasses(structTypeClass, typeClass)
325+
return typeInfo{}, err
314326
}
327+
// Size must cover the highest field end.
328+
end := uint64(f.ByteOffset) + sub.size
329+
ti.size = max(ti.size, end)
330+
ti.align = max(ti.align, sub.align)
331+
ti.nVars += sub.nVars
332+
ti.class = combineTypeClasses(ti.class, sub.class)
333+
}
334+
if ti.class == TypeClassNone {
335+
ti.class = TypeClassMixed
315336
}
316-
return TypeClassMixed, nil
337+
317338
default:
318-
return TypeClassNone, fmt.Errorf("unsupported tag for type class: %v", entry.Tag)
339+
return typeInfo{}, fmt.Errorf("unsupported dwarf.Type %T", tt)
319340
}
320-
}
321341

322-
func (d DWARF) GetBaseOrStructTypeByteSize(entry *dwarf.Entry) (uint64, error) {
323-
field, ok := d.Field(entry, dwarf.AttrByteSize)
324-
if !ok {
325-
return 0, fmt.Errorf("failed to get byte size attribute: %w", ErrDWARFEntry)
326-
}
327-
byteSize, ok := field.Val.(int64)
328-
if !ok {
329-
return 0, fmt.Errorf("byte size attribute is not a valid int64: %w", ErrDWARFEntry)
342+
if ti.align == 0 {
343+
ti.align = ti.size
330344
}
331-
return uint64(byteSize), nil
345+
return ti, nil
332346
}
333347

334-
func (d DWARF) GetTypeByteSize(entry *dwarf.Entry) (uint64, error) {
335-
switch entry.Tag {
336-
case dwarf.TagPointerType, dwarf.TagSubroutineType:
337-
addrSize := d.Reader.AddressSize()
338-
if addrSize != 8 && addrSize != 4 {
339-
return 0, fmt.Errorf("unsupported address size: %d", addrSize)
340-
}
341-
return uint64(addrSize), nil
342-
case dwarf.TagBaseType, dwarf.TagStructType:
343-
return d.GetBaseOrStructTypeByteSize(entry)
344-
default:
345-
return 0, fmt.Errorf("unsupported tag for byte size: %v %v", entry.Tag, entry)
348+
// GetTypeInfo converts a DIE offset into size/align/class/var-count information
349+
// using the high-level *dwarf.Type API (no reader rewinds).
350+
func (d *dwarfReader) GetTypeInfo(off dwarf.Offset) (typeInfo, error) {
351+
if ti, ok := d.typeCache[off]; ok {
352+
return ti, nil
346353
}
347-
}
348-
349-
func (d DWARF) GetAlignmentSize(entry *dwarf.Entry) (uint64, error) {
350-
switch entry.Tag {
351-
case dwarf.TagPointerType, dwarf.TagSubroutineType:
352-
addrSize := d.Reader.AddressSize()
353-
if addrSize != 8 && addrSize != 4 {
354-
return 0, fmt.Errorf("unsupported address size: %d", addrSize)
355-
}
356-
return uint64(addrSize), nil // Assuming 64-bit pointers for simplicity
357-
case dwarf.TagBaseType:
358-
return d.GetBaseOrStructTypeByteSize(entry)
359-
case dwarf.TagStructType:
360-
maxSize := uint64(0)
361-
for {
362-
die, err := d.Reader.Next()
363-
if errors.Is(err, io.EOF) || die == nil || die.Tag == 0 {
364-
return maxSize, nil
365-
}
366-
if err != nil {
367-
return 0, fmt.Errorf("error reading struct members: %w", err)
368-
}
369-
if die.Tag == dwarf.TagMember {
370-
offset := die.Offset
371-
typeDie, err := d.GetTypeDIE(die)
372-
if err != nil {
373-
return 0, fmt.Errorf("error getting type DIE for member: %w", err)
374-
}
375-
size, err := d.GetAlignmentSize(typeDie)
376-
if err != nil {
377-
return 0, fmt.Errorf("error getting alignment size for member: %w", err)
378-
}
379-
maxSize = max(maxSize, size)
380-
d.Reader.Seek(offset)
381-
d.Reader.Next()
382-
}
383-
}
384-
return maxSize, nil
385-
default:
386-
return 0, fmt.Errorf("unsupported tag for alignment size: %v %+v", entry.Tag, entry)
354+
t, err := d.Data.Type(off)
355+
if err != nil {
356+
return typeInfo{}, err
387357
}
388-
}
389358

390-
func (d DWARF) GetNumVars(entry *dwarf.Entry) (int, error) {
391-
tag := entry.Tag
392-
switch tag {
393-
case dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagBaseType:
394-
return 1, nil
395-
case dwarf.TagStructType:
396-
numVars := 0
397-
for {
398-
die, err := d.Reader.Next()
399-
if errors.Is(err, io.EOF) || die == nil || die.Tag == 0 {
400-
return numVars, nil
401-
}
402-
if err != nil {
403-
return 0, fmt.Errorf("error reading struct members: %w", err)
404-
}
405-
if die.Tag == dwarf.TagMember {
406-
offset := die.Offset
407-
typeDie, err := d.GetTypeDIE(die)
408-
if err != nil {
409-
return 0, fmt.Errorf("error getting type DIE for member: %w", err)
410-
}
411-
vars, err := d.GetNumVars(typeDie)
412-
if err != nil {
413-
return 0, fmt.Errorf("error getting number of variables for member: %w", err)
414-
}
415-
numVars += vars
416-
// The type DIE is not in order, so we must seek back to the struct member DIE
417-
d.Reader.Seek(offset)
418-
d.Reader.Next() // Reset the reader to the member DIE
419-
}
420-
}
421-
return 0, errors.New("struct types are not implemented yet")
422-
default:
423-
return 0, errors.New(fmt.Sprintf("unsupported tag: %v", tag))
424-
}
359+
ti, err := d.getTypeInfo(t)
360+
d.typeCache[off] = ti
361+
return ti, err
425362
}
426363

427-
func (d DWARF) GoFuncFieldArgs(fn string) (map[string]FuncFieldArg, error) {
364+
func (d dwarfReader) GoFuncFieldArgs(fn string) (map[string]FuncFieldArg, error) {
428365
log.Printf("Searching for function %s in DWARF data\n", fn)
429366
abi, err := d.DetectSourceABI()
430367
if err != nil {
@@ -454,39 +391,23 @@ func (d DWARF) GoFuncFieldArgs(fn string) (map[string]FuncFieldArg, error) {
454391
}
455392

456393
argNames = append(argNames, name)
457-
isRetArg := d.IsRetArg(entry)
458-
// TODO(ddelnano): Clean up usage of GetTypeDIE and all of the d.Reader.Seek logic
459-
offset := entry.Offset
460-
typeDie, err := d.GetTypeDIE(entry)
461-
if err != nil {
462-
return nil, fmt.Errorf("failed to get type DIE for %s: %w", name, err)
463-
}
464-
typeSize, err := d.GetTypeByteSize(typeDie)
465-
if err != nil {
466-
return nil, fmt.Errorf("failed to get type size for %s: %w", name, err)
394+
typeDie, ok := d.Field(entry, dwarf.AttrType)
395+
if !ok {
396+
return nil, fmt.Errorf("failed to get type attribute for %s: %w", name, ErrDWARFEntry)
467397
}
468-
469-
typeClass, err := d.GetTypeClass(typeDie)
470-
if err != nil {
471-
return nil, fmt.Errorf("failed to get type class for %s: %w", name, err)
398+
typeOffset, ok := typeDie.Val.(dwarf.Offset)
399+
if !ok {
400+
return nil, fmt.Errorf("type attribute is not a valid dwarf.Offset for %s: %w", name, ErrDWARFEntry)
472401
}
402+
ti, err := d.GetTypeInfo(typeOffset)
473403

474-
d.Reader.Seek(typeDie.Offset)
475-
alignmentSize, err := d.GetAlignmentSize(typeDie)
476404
if err != nil {
477-
return nil, fmt.Errorf("failed to get alignment size for %s: %w", name, err)
405+
return nil, fmt.Errorf("failed to get type info for %s: %w", name, err)
478406
}
479407

480-
d.Reader.Seek(typeDie.Offset)
481-
numVars, err := d.GetNumVars(typeDie)
408+
isRetArg := d.IsRetArg(entry)
482409

483-
// Reset to the original position
484-
d.Reader.Seek(offset)
485-
d.Reader.Next()
486-
if err != nil {
487-
return nil, fmt.Errorf("failed to get number of variables for %s: %w", name, err)
488-
}
489-
funcField, err := d.argTracker.PopLocation(typeClass, typeSize, alignmentSize, numVars, isRetArg)
410+
funcField, err := d.argTracker.PopLocation(ti.class, ti.size, ti.align, ti.nVars, isRetArg)
490411
if err != nil {
491412
return nil, fmt.Errorf("failed to get location for %s: %w", name, err)
492413
}
@@ -498,7 +419,7 @@ func (d DWARF) GoFuncFieldArgs(fn string) (map[string]FuncFieldArg, error) {
498419

499420
// GoStructField returns the offset value of a Go struct field. If the struct
500421
// field cannot be found -1 and a non-nil error will be returned.
501-
func (d DWARF) GoStructField(id structfield.ID) (int64, error) {
422+
func (d dwarfReader) GoStructField(id structfield.ID) (int64, error) {
502423
strct := fmt.Sprintf("%s.%s", id.PkgPath, id.Struct)
503424
if !d.GoToEntry(dwarf.TagStructType, strct) {
504425
return -1, fmt.Errorf("struct %q not found", strct)
@@ -523,12 +444,12 @@ func (d DWARF) GoStructField(id structfield.ID) (int64, error) {
523444

524445
// GoToEntry reads until the entry with a tag equal to name is found. True is
525446
// returned if the entry is found, otherwise false is returned.
526-
func (d DWARF) GoToEntry(tag dwarf.Tag, name string) bool {
447+
func (d dwarfReader) GoToEntry(tag dwarf.Tag, name string) bool {
527448
_, err := d.Entry(tag, name)
528449
return err == nil
529450
}
530451

531-
func (d DWARF) EntriesWithTag(tag dwarf.Tag) ([]*dwarf.Entry, error) {
452+
func (d dwarfReader) EntriesWithTag(tag dwarf.Tag) ([]*dwarf.Entry, error) {
532453
entries := make([]*dwarf.Entry, 0)
533454
for {
534455
entry, err := d.Reader.Next()
@@ -545,7 +466,7 @@ func (d DWARF) EntriesWithTag(tag dwarf.Tag) ([]*dwarf.Entry, error) {
545466

546467
// Entry returns the entry with a tag equal to name. ErrDWARFEntry is returned
547468
// if the entry cannot be found.
548-
func (d DWARF) Entry(tag dwarf.Tag, name string) (*dwarf.Entry, error) {
469+
func (d dwarfReader) Entry(tag dwarf.Tag, name string) (*dwarf.Entry, error) {
549470
for {
550471
entry, err := d.Reader.Next()
551472
if errors.Is(err, io.EOF) || entry == nil {
@@ -566,7 +487,7 @@ func (d DWARF) Entry(tag dwarf.Tag, name string) (*dwarf.Entry, error) {
566487
// EntryInChildren returns the entry with a tag equal to name within the
567488
// children of the current entry. ErrDWARFEntry is returned if the entry cannot
568489
// be found.
569-
func (d DWARF) EntryInChildren(tag dwarf.Tag, name string) (*dwarf.Entry, error) {
490+
func (d dwarfReader) EntryInChildren(tag dwarf.Tag, name string) (*dwarf.Entry, error) {
570491
for {
571492
entry, err := d.Reader.Next()
572493
if errors.Is(err, io.EOF) || entry == nil || entry.Tag == 0 {
@@ -586,7 +507,7 @@ func (d DWARF) EntryInChildren(tag dwarf.Tag, name string) (*dwarf.Entry, error)
586507

587508
// Field returns the field from the entry e that has attribute a and true.
588509
// If no field is found, an empty field is returned with false.
589-
func (d DWARF) Field(e *dwarf.Entry, a dwarf.Attr) (dwarf.Field, bool) {
510+
func (d dwarfReader) Field(e *dwarf.Entry, a dwarf.Attr) (dwarf.Field, bool) {
590511
for _, f := range e.Field {
591512
if f.Attr == a {
592513
return f, true

0 commit comments

Comments
 (0)