@@ -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
176199type 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