7
7
# This code is in the public domain
8
8
#-------------------------------------------------------------------------------
9
9
import os
10
- from collections import namedtuple
10
+ from collections import namedtuple , OrderedDict
11
11
from bisect import bisect_right
12
12
13
13
from ..construct .lib .container import Container
16
16
parse_cstring_from_stream )
17
17
from .structs import DWARFStructs
18
18
from .compileunit import CompileUnit
19
+ from .typeunit import TypeUnit
19
20
from .abbrevtable import AbbrevTable
20
21
from .lineprogram import LineProgram
21
22
from .callframe import CallFrameInfo
@@ -82,7 +83,8 @@ def __init__(self,
82
83
debug_rnglists_sec ,
83
84
debug_sup_sec ,
84
85
gnu_debugaltlink_sec ,
85
- gnu_debuglink_sec
86
+ gnu_debuglink_sec ,
87
+ debug_types_sec
86
88
):
87
89
""" config:
88
90
A DwarfConfig object
@@ -112,6 +114,7 @@ def __init__(self,
112
114
self .debug_sup_sec = debug_sup_sec
113
115
self .gnu_debugaltlink_sec = gnu_debugaltlink_sec
114
116
self .gnu_debuglink_sec = gnu_debuglink_sec
117
+ self .debug_types_sec = debug_types_sec
115
118
116
119
# Sets the supplementary_dwarfinfo to None. Client code can set this
117
120
# to something else, typically a DWARFInfo file read from an ELFFile
@@ -136,6 +139,9 @@ def __init__(self,
136
139
self ._cu_cache = []
137
140
self ._cu_offsets_map = []
138
141
142
+ # DWARF v4 type units by sig8 - OrderedDict created on Reference
143
+ self ._type_units_by_sig = None
144
+
139
145
@property
140
146
def has_debug_info (self ):
141
147
""" Return whether this contains debug information.
@@ -145,6 +151,11 @@ def has_debug_info(self):
145
151
"""
146
152
return bool (self .debug_info_sec )
147
153
154
+ def has_debug_types (self ):
155
+ """ Return whether this contains debug types information.
156
+ """
157
+ return bool (self .debug_types_sec )
158
+
148
159
def get_DIE_from_lut_entry (self , lut_entry ):
149
160
""" Get the DIE from the pubnames or putbtypes lookup table entry.
150
161
@@ -223,11 +234,32 @@ def get_CU_at(self, offset):
223
234
224
235
return self ._cached_CU_at_offset (offset )
225
236
237
+ def get_TU_by_sig8 (self , sig8 ):
238
+ """ Find and return a Type Unit referenced by its signature
239
+
240
+ sig8:
241
+ The 8 byte unique signature (as a 64-bit unsigned integer)
242
+
243
+ Returns the TU with the given type signature by parsing the
244
+ .debug_types section.
245
+
246
+ """
247
+ self ._parse_debug_types ()
248
+ tu = self ._type_units_by_sig .get (sig8 )
249
+ if tu is None :
250
+ raise KeyError ("Signature %016x not found in .debug_types" % sig8 )
251
+ return tu
252
+
226
253
def iter_CUs (self ):
227
254
""" Yield all the compile units (CompileUnit objects) in the debug info
228
255
"""
229
256
return self ._parse_CUs_iter ()
230
257
258
+ def iter_TUs (self ):
259
+ """Yield all the type units (TypeUnit objects) in the debug_types
260
+ """
261
+ return self ._parse_TUs_iter ()
262
+
231
263
def get_abbrev_table (self , offset ):
232
264
""" Get an AbbrevTable from the given offset in the debug_abbrev
233
265
section.
@@ -416,11 +448,53 @@ def _parse_CUs_iter(self, offset=0):
416
448
# Compute the offset of the next CU in the section. The unit_length
417
449
# field of the CU header contains its size not including the length
418
450
# field itself.
419
- offset = ( offset +
420
- cu ['unit_length' ] +
421
- cu .structs .initial_length_field_size ())
451
+ offset = (offset +
452
+ cu ['unit_length' ] +
453
+ cu .structs .initial_length_field_size ())
422
454
yield cu
423
455
456
+ def _parse_TUs_iter (self , offset = 0 ):
457
+ """ Iterate Type Unit objects in order of appearance in the debug_types section.
458
+
459
+ offset:
460
+ The offset of the first TU to yield. Additional iterations
461
+ will return the sequential unit objects.
462
+
463
+ See .iter_TUs().
464
+ """
465
+ if self .debug_types_sec is None :
466
+ return
467
+
468
+ while offset < self .debug_types_sec .size :
469
+ tu = self ._parse_TU_at_offset (offset )
470
+ # Compute the offset of the next TU in the section. The unit_length
471
+ # field of the TU header contains its size not including the length
472
+ # field itself.
473
+ offset = (offset +
474
+ tu ['unit_length' ] +
475
+ tu .structs .initial_length_field_size ())
476
+
477
+ yield tu
478
+
479
+ def _parse_debug_types (self ):
480
+ """ Check if the .debug_types section is previously parsed. If not,
481
+ parse all TUs and store them in an OrderedDict using their unique
482
+ 64-bit signature as the key.
483
+
484
+ See .get_TU_by_sig8().
485
+ """
486
+ if self ._type_units_by_sig is not None :
487
+ return
488
+ self ._type_units_by_sig = OrderedDict ()
489
+
490
+ if self .debug_types_sec is None :
491
+ return
492
+
493
+ # Collect all Type Units in the .debug_types section for access using
494
+ # their 8-byte unique signature
495
+ for tu in self ._parse_TUs_iter ():
496
+ self ._type_units_by_sig [tu ['signature' ]] = tu
497
+
424
498
def _cached_CU_at_offset (self , offset ):
425
499
""" Return the CU with unit header at the given offset into the
426
500
debug_info section from the cache. If not present, the unit is
@@ -493,6 +567,50 @@ def _parse_CU_at_offset(self, offset):
493
567
cu_offset = offset ,
494
568
cu_die_offset = cu_die_offset )
495
569
570
+ def _parse_TU_at_offset (self , offset ):
571
+ """ Parse and return a Type Unit (TU) at the given offset in the debug_types stream.
572
+ """
573
+ # Section 7.4 (32-bit and 64-bit DWARF Formats) of the DWARF spec v4
574
+ # states that the first 32-bit word of the TU header determines
575
+ # whether the TU is represented with 32-bit or 64-bit DWARF format.
576
+ #
577
+ # So we peek at the first word in the TU header to determine its
578
+ # dwarf format. Based on it, we then create a new DWARFStructs
579
+ # instance suitable for this TU and use it to parse the rest.
580
+ #
581
+ initial_length = struct_parse (
582
+ self .structs .the_Dwarf_uint32 , self .debug_types_sec .stream , offset )
583
+ dwarf_format = 64 if initial_length == 0xFFFFFFFF else 32
584
+
585
+ # Temporary structs for parsing the header
586
+ # The structs for the rest of the TUs depend on the header data.
587
+ tu_structs = DWARFStructs (
588
+ little_endian = self .config .little_endian ,
589
+ dwarf_format = dwarf_format ,
590
+ address_size = 4 ,
591
+ dwarf_version = 2 )
592
+
593
+ tu_header = struct_parse (
594
+ tu_structs .Dwarf_TU_header , self .debug_types_sec .stream , offset )
595
+
596
+ # structs for the rest of the TU, taking into account bit-width and DWARF version
597
+ tu_structs = DWARFStructs (
598
+ little_endian = self .config .little_endian ,
599
+ dwarf_format = dwarf_format ,
600
+ address_size = tu_header ['address_size' ],
601
+ dwarf_version = tu_header ['version' ])
602
+
603
+ tu_die_offset = self .debug_types_sec .stream .tell ()
604
+ dwarf_assert (
605
+ self ._is_supported_version (tu_header ['version' ]),
606
+ "Expected supported DWARF version. Got '%s'" % tu_header ['version' ])
607
+ return TypeUnit (
608
+ header = tu_header ,
609
+ dwarfinfo = self ,
610
+ structs = tu_structs ,
611
+ tu_offset = offset ,
612
+ tu_die_offset = tu_die_offset )
613
+
496
614
def _is_supported_version (self , version ):
497
615
""" DWARF version supported by this parser
498
616
"""
0 commit comments