90cbaa189e5cb5f8ccee6506314eeeeac255b987
[pyelftools.git] / elftools / dwarf / descriptions.py
1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/descriptions.py
3 #
4 # Textual descriptions of the various values and enums of DWARF
5 #
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
9 from collections import defaultdict
10
11 from .constants import *
12 from .dwarf_expr import DWARFExprParser
13 from .die import DIE
14 from ..common.utils import preserve_stream_pos, dwarf_assert, bytes2str
15 from .callframe import instruction_name, CIE, FDE
16
17
18 def set_global_machine_arch(machine_arch):
19 global _MACHINE_ARCH
20 _MACHINE_ARCH = machine_arch
21
22
23 def describe_attr_value(attr, die, section_offset):
24 """ Given an attribute attr, return the textual representation of its
25 value, suitable for tools like readelf.
26
27 To cover all cases, this function needs some extra arguments:
28
29 die: the DIE this attribute was extracted from
30 section_offset: offset in the stream of the section the DIE belongs to
31 """
32 descr_func = _ATTR_DESCRIPTION_MAP[attr.form]
33 val_description = descr_func(attr, die, section_offset)
34
35 # For some attributes we can display further information
36 extra_info_func = _EXTRA_INFO_DESCRIPTION_MAP[attr.name]
37 extra_info = extra_info_func(attr, die, section_offset)
38 return str(val_description) + '\t' + extra_info
39
40
41 def describe_CFI_instructions(entry):
42 """ Given a CFI entry (CIE or FDE), return the textual description of its
43 instructions.
44 """
45 def _assert_FDE_instruction(instr):
46 dwarf_assert(
47 isinstance(entry, FDE),
48 'Unexpected instruction "%s" for a CIE' % instr)
49
50 def _full_reg_name(regnum):
51 regname = describe_reg_name(regnum, _MACHINE_ARCH, False)
52 if regname:
53 return 'r%s (%s)' % (regnum, regname)
54 else:
55 return 'r%s' % regnum
56
57 if isinstance(entry, CIE):
58 cie = entry
59 else: # FDE
60 cie = entry.cie
61 pc = entry['initial_location']
62
63 s = ''
64 for instr in entry.instructions:
65 name = instruction_name(instr.opcode)
66
67 if name in ('DW_CFA_offset',
68 'DW_CFA_offset_extended', 'DW_CFA_offset_extended_sf',
69 'DW_CFA_val_offset', 'DW_CFA_val_offset_sf'):
70 s += ' %s: %s at cfa%+d\n' % (
71 name, _full_reg_name(instr.args[0]),
72 instr.args[1] * cie['data_alignment_factor'])
73 elif name in ( 'DW_CFA_restore', 'DW_CFA_restore_extended',
74 'DW_CFA_undefined', 'DW_CFA_same_value',
75 'DW_CFA_def_cfa_register'):
76 s += ' %s: %s\n' % (name, _full_reg_name(instr.args[0]))
77 elif name == 'DW_CFA_register':
78 s += ' %s: %s in %s' % (
79 name, _full_reg_name(instr.args[0]),
80 _full_reg_name(instr.args[1]))
81 elif name == 'DW_CFA_set_loc':
82 pc = instr.args[0]
83 s += ' %s: %08x\n' % (name, pc)
84 elif name in ( 'DW_CFA_advance_loc1', 'DW_CFA_advance_loc2',
85 'DW_CFA_advance_loc4', 'DW_CFA_advance_loc'):
86 _assert_FDE_instruction(instr)
87 factored_offset = instr.args[0] * cie['code_alignment_factor']
88 s += ' %s: %s to %08x\n' % (
89 name, factored_offset, factored_offset + pc)
90 pc += factored_offset
91 elif name in ( 'DW_CFA_remember_state', 'DW_CFA_restore_state',
92 'DW_CFA_nop'):
93 s += ' %s\n' % name
94 elif name == 'DW_CFA_def_cfa':
95 s += ' %s: %s ofs %s\n' % (
96 name, _full_reg_name(instr.args[0]), instr.args[1])
97 elif name == 'DW_CFA_def_cfa_sf':
98 s += ' %s: %s ofs %s\n' % (
99 name, _full_reg_name(instr.args[0]),
100 instr.args[1] * cie['data_alignment_factor'])
101 elif name in ('DW_CFA_def_cfa_offset', 'DW_CFA_GNU_args_size'):
102 s += ' %s: %s\n' % (name, instr.args[0])
103 elif name == 'DW_CFA_def_cfa_expression':
104 expr_dumper = ExprDumper(entry.structs)
105 # readelf output is missing a colon for DW_CFA_def_cfa_expression
106 s += ' %s (%s)\n' % (name, expr_dumper.dump_expr(instr.args[0]))
107 elif name == 'DW_CFA_expression':
108 expr_dumper = ExprDumper(entry.structs)
109 s += ' %s: %s (%s)\n' % (
110 name, _full_reg_name(instr.args[0]),
111 expr_dumper.dump_expr(instr.args[1]))
112 else:
113 s += ' %s: <??>\n' % name
114
115 return s
116
117
118 def describe_CFI_register_rule(rule):
119 s = _DESCR_CFI_REGISTER_RULE_TYPE[rule.type]
120 if rule.type in ('OFFSET', 'VAL_OFFSET'):
121 s += '%+d' % rule.arg
122 elif rule.type == 'REGISTER':
123 s += describe_reg_name(rule.arg)
124 return s
125
126
127 def describe_CFI_CFA_rule(rule):
128 if rule.expr:
129 return 'exp'
130 else:
131 return '%s%+d' % (describe_reg_name(rule.reg), rule.offset)
132
133
134 def describe_DWARF_expr(expr, structs, cu_offset=None):
135 """ Textual description of a DWARF expression encoded in 'expr'.
136 structs should come from the entity encompassing the expression - it's
137 needed to be able to parse it correctly.
138 """
139 # Since this function can be called a lot, initializing a fresh new
140 # ExprDumper per call is expensive. So a rudimentary caching scheme is in
141 # place to create only one such dumper per instance of structs.
142 cache_key = id(structs)
143 if cache_key not in _DWARF_EXPR_DUMPER_CACHE:
144 _DWARF_EXPR_DUMPER_CACHE[cache_key] = \
145 ExprDumper(structs)
146 dwarf_expr_dumper = _DWARF_EXPR_DUMPER_CACHE[cache_key]
147 return '(' + dwarf_expr_dumper.dump_expr(expr, cu_offset) + ')'
148
149
150 def describe_reg_name(regnum, machine_arch=None, default=True):
151 """ Provide a textual description for a register name, given its serial
152 number. The number is expected to be valid.
153 """
154 if machine_arch is None:
155 machine_arch = _MACHINE_ARCH
156
157 if machine_arch == 'x86':
158 return _REG_NAMES_x86[regnum]
159 elif machine_arch == 'x64':
160 return _REG_NAMES_x64[regnum]
161 elif machine_arch == 'AArch64':
162 return _REG_NAMES_AArch64[regnum]
163 elif default:
164 return 'r%s' % regnum
165 else:
166 return None
167
168 def describe_form_class(form):
169 """For a given form name, determine its value class.
170
171 For example, given 'DW_FORM_data1' returns 'constant'.
172
173 For some forms, like DW_FORM_indirect and DW_FORM_sec_offset, the class is
174 not hard-coded and extra information is required. For these, None is
175 returned.
176 """
177 return _FORM_CLASS[form]
178
179
180 #-------------------------------------------------------------------------------
181
182 # The machine architecture. Set globally via set_global_machine_arch
183 #
184 _MACHINE_ARCH = None
185
186
187 def _describe_attr_ref(attr, die, section_offset):
188 return '<0x%x>' % (attr.value + die.cu.cu_offset)
189
190 def _describe_attr_value_passthrough(attr, die, section_offset):
191 return attr.value
192
193 def _describe_attr_hex(attr, die, section_offset):
194 return '0x%x' % (attr.value)
195
196 def _describe_attr_hex_addr(attr, die, section_offset):
197 return '<0x%x>' % (attr.value)
198
199 def _describe_attr_split_64bit(attr, die, section_offset):
200 low_word = attr.value & 0xFFFFFFFF
201 high_word = (attr.value >> 32) & 0xFFFFFFFF
202 return '0x%x 0x%x' % (low_word, high_word)
203
204 def _describe_attr_strp(attr, die, section_offset):
205 return '(indirect string, offset: 0x%x): %s' % (
206 attr.raw_value, bytes2str(attr.value))
207
208 def _describe_attr_line_strp(attr, die, section_offset):
209 return '(indirect line string, offset: 0x%x): %s' % (
210 attr.raw_value, bytes2str(attr.value))
211
212 def _describe_attr_string(attr, die, section_offset):
213 return bytes2str(attr.value)
214
215 def _describe_attr_debool(attr, die, section_offset):
216 """ To be consistent with readelf, generate 1 for True flags, 0 for False
217 flags.
218 """
219 return '1' if attr.value else '0'
220
221 def _describe_attr_present(attr, die, section_offset):
222 """ Some forms may simply mean that an attribute is present,
223 without providing any value.
224 """
225 return '1'
226
227 def _describe_attr_block(attr, die, section_offset):
228 s = '%s byte block: ' % len(attr.value)
229 s += ' '.join('%x' % item for item in attr.value) + ' '
230 return s
231
232
233 _ATTR_DESCRIPTION_MAP = defaultdict(
234 lambda: _describe_attr_value_passthrough, # default_factory
235
236 DW_FORM_ref1=_describe_attr_ref,
237 DW_FORM_ref2=_describe_attr_ref,
238 DW_FORM_ref4=_describe_attr_ref,
239 DW_FORM_ref8=_describe_attr_split_64bit,
240 DW_FORM_ref_udata=_describe_attr_ref,
241 DW_FORM_ref_addr=_describe_attr_hex_addr,
242 DW_FORM_data4=_describe_attr_hex,
243 DW_FORM_data8=_describe_attr_hex,
244 DW_FORM_addr=_describe_attr_hex,
245 DW_FORM_sec_offset=_describe_attr_hex,
246 DW_FORM_flag=_describe_attr_debool,
247 DW_FORM_data1=_describe_attr_value_passthrough,
248 DW_FORM_data2=_describe_attr_value_passthrough,
249 DW_FORM_sdata=_describe_attr_value_passthrough,
250 DW_FORM_udata=_describe_attr_value_passthrough,
251 DW_FORM_string=_describe_attr_string,
252 DW_FORM_strp=_describe_attr_strp,
253 DW_FORM_line_strp=_describe_attr_line_strp,
254 DW_FORM_block1=_describe_attr_block,
255 DW_FORM_block2=_describe_attr_block,
256 DW_FORM_block4=_describe_attr_block,
257 DW_FORM_block=_describe_attr_block,
258 DW_FORM_flag_present=_describe_attr_present,
259 DW_FORM_exprloc=_describe_attr_block,
260 DW_FORM_ref_sig8=_describe_attr_ref,
261 )
262
263 _FORM_CLASS = dict(
264 DW_FORM_addr='address',
265 DW_FORM_block2='block',
266 DW_FORM_block4='block',
267 DW_FORM_data2='constant',
268 DW_FORM_data4='constant',
269 DW_FORM_data8='constant',
270 DW_FORM_string='string',
271 DW_FORM_block='block',
272 DW_FORM_block1='block',
273 DW_FORM_data1='constant',
274 DW_FORM_flag='flag',
275 DW_FORM_sdata='constant',
276 DW_FORM_strp='string',
277 DW_FORM_udata='constant',
278 DW_FORM_ref_addr='reference',
279 DW_FORM_ref1='reference',
280 DW_FORM_ref2='reference',
281 DW_FORM_ref4='reference',
282 DW_FORM_ref8='reference',
283 DW_FORM_ref_udata='reference',
284 DW_FORM_indirect=None,
285 DW_FORM_sec_offset=None,
286 DW_FORM_exprloc='exprloc',
287 DW_FORM_flag_present='flag',
288 DW_FORM_ref_sig8='reference',
289 )
290
291 _DESCR_DW_INL = {
292 DW_INL_not_inlined: '(not inlined)',
293 DW_INL_inlined: '(inlined)',
294 DW_INL_declared_not_inlined: '(declared as inline but ignored)',
295 DW_INL_declared_inlined: '(declared as inline and inlined)',
296 }
297
298 _DESCR_DW_LANG = {
299 DW_LANG_C89: '(ANSI C)',
300 DW_LANG_C: '(non-ANSI C)',
301 DW_LANG_Ada83: '(Ada)',
302 DW_LANG_C_plus_plus: '(C++)',
303 DW_LANG_Cobol74: '(Cobol 74)',
304 DW_LANG_Cobol85: '(Cobol 85)',
305 DW_LANG_Fortran77: '(FORTRAN 77)',
306 DW_LANG_Fortran90: '(Fortran 90)',
307 DW_LANG_Pascal83: '(ANSI Pascal)',
308 DW_LANG_Modula2: '(Modula 2)',
309 DW_LANG_Java: '(Java)',
310 DW_LANG_C99: '(ANSI C99)',
311 DW_LANG_Ada95: '(ADA 95)',
312 DW_LANG_Fortran95: '(Fortran 95)',
313 DW_LANG_PLI: '(PLI)',
314 DW_LANG_ObjC: '(Objective C)',
315 DW_LANG_ObjC_plus_plus: '(Objective C++)',
316 DW_LANG_UPC: '(Unified Parallel C)',
317 DW_LANG_D: '(D)',
318 DW_LANG_Python: '(Python)',
319 DW_LANG_OpenCL: '(OpenCL)',
320 DW_LANG_Go: '(Go)',
321 DW_LANG_Modula3: '(Modula 3)',
322 DW_LANG_Haskell: '(Haskell)',
323 DW_LANG_C_plus_plus_03: '(C++03)',
324 DW_LANG_C_plus_plus_11: '(C++11)',
325 DW_LANG_OCaml: '(OCaml)',
326 DW_LANG_Rust: '(Rust)',
327 DW_LANG_C11: '(C11)',
328 DW_LANG_Swift: '(Swift)',
329 DW_LANG_Julia: '(Julia)',
330 DW_LANG_Dylan: '(Dylan)',
331 DW_LANG_C_plus_plus_14: '(C++14)',
332 DW_LANG_Fortran03: '(Fortran 03)',
333 DW_LANG_Fortran08: '(Fortran 08)',
334 DW_LANG_RenderScript: '(RenderScript)',
335 DW_LANG_BLISS: '(Bliss)', # Not in binutils
336 DW_LANG_Mips_Assembler: '(MIPS assembler)',
337 DW_LANG_HP_Bliss: '(HP Bliss)',
338 DW_LANG_HP_Basic91: '(HP Basic 91)',
339 DW_LANG_HP_Pascal91: '(HP Pascal 91)',
340 DW_LANG_HP_IMacro: '(HP IMacro)',
341 DW_LANG_HP_Assembler: '(HP assembler)'
342 }
343
344 _DESCR_DW_ATE = {
345 DW_ATE_void: '(void)',
346 DW_ATE_address: '(machine address)',
347 DW_ATE_boolean: '(boolean)',
348 DW_ATE_complex_float: '(complex float)',
349 DW_ATE_float: '(float)',
350 DW_ATE_signed: '(signed)',
351 DW_ATE_signed_char: '(signed char)',
352 DW_ATE_unsigned: '(unsigned)',
353 DW_ATE_unsigned_char: '(unsigned char)',
354 DW_ATE_imaginary_float: '(imaginary float)',
355 DW_ATE_decimal_float: '(decimal float)',
356 DW_ATE_packed_decimal: '(packed_decimal)',
357 DW_ATE_numeric_string: '(numeric_string)',
358 DW_ATE_edited: '(edited)',
359 DW_ATE_signed_fixed: '(signed_fixed)',
360 DW_ATE_unsigned_fixed: '(unsigned_fixed)',
361 DW_ATE_UTF: '(unicode string)',
362 DW_ATE_HP_float80: '(HP_float80)',
363 DW_ATE_HP_complex_float80: '(HP_complex_float80)',
364 DW_ATE_HP_float128: '(HP_float128)',
365 DW_ATE_HP_complex_float128: '(HP_complex_float128)',
366 DW_ATE_HP_floathpintel: '(HP_floathpintel)',
367 DW_ATE_HP_imaginary_float80: '(HP_imaginary_float80)',
368 DW_ATE_HP_imaginary_float128: '(HP_imaginary_float128)',
369 }
370
371 _DESCR_DW_ACCESS = {
372 DW_ACCESS_public: '(public)',
373 DW_ACCESS_protected: '(protected)',
374 DW_ACCESS_private: '(private)',
375 }
376
377 _DESCR_DW_VIS = {
378 DW_VIS_local: '(local)',
379 DW_VIS_exported: '(exported)',
380 DW_VIS_qualified: '(qualified)',
381 }
382
383 _DESCR_DW_VIRTUALITY = {
384 DW_VIRTUALITY_none: '(none)',
385 DW_VIRTUALITY_virtual: '(virtual)',
386 DW_VIRTUALITY_pure_virtual: '(pure virtual)',
387 }
388
389 _DESCR_DW_ID_CASE = {
390 DW_ID_case_sensitive: '(case_sensitive)',
391 DW_ID_up_case: '(up_case)',
392 DW_ID_down_case: '(down_case)',
393 DW_ID_case_insensitive: '(case_insensitive)',
394 }
395
396 _DESCR_DW_CC = {
397 DW_CC_normal: '(normal)',
398 DW_CC_program: '(program)',
399 DW_CC_nocall: '(nocall)',
400 }
401
402 _DESCR_DW_ORD = {
403 DW_ORD_row_major: '(row major)',
404 DW_ORD_col_major: '(column major)',
405 }
406
407 _DESCR_CFI_REGISTER_RULE_TYPE = dict(
408 UNDEFINED='u',
409 SAME_VALUE='s',
410 OFFSET='c',
411 VAL_OFFSET='v',
412 REGISTER='',
413 EXPRESSION='exp',
414 VAL_EXPRESSION='vexp',
415 ARCHITECTURAL='a',
416 )
417
418 def _make_extra_mapper(mapping, default, default_interpolate_value=False):
419 """ Create a mapping function from attribute parameters to an extra
420 value that should be displayed.
421 """
422 def mapper(attr, die, section_offset):
423 if default_interpolate_value:
424 d = default % attr.value
425 else:
426 d = default
427 return mapping.get(attr.value, d)
428 return mapper
429
430
431 def _make_extra_string(s=''):
432 """ Create an extra function that just returns a constant string.
433 """
434 def extra(attr, die, section_offset):
435 return s
436 return extra
437
438
439 _DWARF_EXPR_DUMPER_CACHE = {}
440
441 def _location_list_extra(attr, die, section_offset):
442 # According to section 2.6 of the DWARF spec v3, class loclistptr means
443 # a location list, and class block means a location expression.
444 # DW_FORM_sec_offset is new in DWARFv4 as a section offset.
445 if attr.form in ('DW_FORM_data4', 'DW_FORM_data8', 'DW_FORM_sec_offset'):
446 return '(location list)'
447 else:
448 return describe_DWARF_expr(attr.value, die.cu.structs, die.cu.cu_offset)
449
450
451 def _data_member_location_extra(attr, die, section_offset):
452 # According to section 5.5.6 of the DWARF spec v4, a data member location
453 # can be an integer offset, or a location description.
454 #
455 if attr.form in ('DW_FORM_data1', 'DW_FORM_data2',
456 'DW_FORM_data4', 'DW_FORM_data8',
457 'DW_FORM_sdata'):
458 return '' # No extra description needed
459 else:
460 return describe_DWARF_expr(attr.value, die.cu.structs, die.cu.cu_offset)
461
462
463 def _import_extra(attr, die, section_offset):
464 # For DW_AT_import the value points to a DIE (that can be either in the
465 # current DIE's CU or in another CU, depending on the FORM). The extra
466 # information for it is the abbreviation number in this DIE and its tag.
467 if attr.form == 'DW_FORM_ref_addr':
468 # Absolute offset value
469 ref_die_offset = section_offset + attr.value
470 else:
471 # Relative offset to the current DIE's CU
472 ref_die_offset = attr.value + die.cu.cu_offset
473
474 # Now find the CU this DIE belongs to (since we have to find its abbrev
475 # table). This is done by linearly scanning through all CUs, looking for
476 # one spanning an address space containing the referred DIE's offset.
477 for cu in die.dwarfinfo.iter_CUs():
478 if cu['unit_length'] + cu.cu_offset > ref_die_offset >= cu.cu_offset:
479 # Once we have the CU, we can actually parse this DIE from the
480 # stream.
481 with preserve_stream_pos(die.stream):
482 ref_die = DIE(cu, die.stream, ref_die_offset)
483 #print '&&& ref_die', ref_die
484 return '[Abbrev Number: %s (%s)]' % (
485 ref_die.abbrev_code, ref_die.tag)
486
487 return '[unknown]'
488
489
490 _EXTRA_INFO_DESCRIPTION_MAP = defaultdict(
491 lambda: _make_extra_string(''), # default_factory
492
493 DW_AT_inline=_make_extra_mapper(
494 _DESCR_DW_INL, '(Unknown inline attribute value: %x',
495 default_interpolate_value=True),
496 DW_AT_language=_make_extra_mapper(
497 _DESCR_DW_LANG, '(Unknown: %x)', default_interpolate_value=True),
498 DW_AT_encoding=_make_extra_mapper(_DESCR_DW_ATE, '(unknown type)'),
499 DW_AT_accessibility=_make_extra_mapper(
500 _DESCR_DW_ACCESS, '(unknown accessibility)'),
501 DW_AT_visibility=_make_extra_mapper(
502 _DESCR_DW_VIS, '(unknown visibility)'),
503 DW_AT_virtuality=_make_extra_mapper(
504 _DESCR_DW_VIRTUALITY, '(unknown virtuality)'),
505 DW_AT_identifier_case=_make_extra_mapper(
506 _DESCR_DW_ID_CASE, '(unknown case)'),
507 DW_AT_calling_convention=_make_extra_mapper(
508 _DESCR_DW_CC, '(unknown convention)'),
509 DW_AT_ordering=_make_extra_mapper(
510 _DESCR_DW_ORD, '(undefined)'),
511 DW_AT_frame_base=_location_list_extra,
512 DW_AT_location=_location_list_extra,
513 DW_AT_string_length=_location_list_extra,
514 DW_AT_return_addr=_location_list_extra,
515 DW_AT_data_member_location=_data_member_location_extra,
516 DW_AT_vtable_elem_location=_location_list_extra,
517 DW_AT_segment=_location_list_extra,
518 DW_AT_static_link=_location_list_extra,
519 DW_AT_use_location=_location_list_extra,
520 DW_AT_allocated=_location_list_extra,
521 DW_AT_associated=_location_list_extra,
522 DW_AT_data_location=_location_list_extra,
523 DW_AT_stride=_location_list_extra,
524 DW_AT_call_value=_location_list_extra,
525 DW_AT_import=_import_extra,
526 DW_AT_GNU_call_site_value=_location_list_extra,
527 DW_AT_GNU_call_site_data_value=_location_list_extra,
528 DW_AT_GNU_call_site_target=_location_list_extra,
529 DW_AT_GNU_call_site_target_clobbered=_location_list_extra,
530 )
531
532 # 8 in a line, for easier counting
533 _REG_NAMES_x86 = [
534 'eax', 'ecx', 'edx', 'ebx', 'esp', 'ebp', 'esi', 'edi',
535 'eip', 'eflags', '<none>', 'st0', 'st1', 'st2', 'st3', 'st4',
536 'st5', 'st6', 'st7', '<none>', '<none>', 'xmm0', 'xmm1', 'xmm2',
537 'xmm3', 'xmm4', 'xmm5', 'xmm6', 'xmm7', 'mm0', 'mm1', 'mm2',
538 'mm3', 'mm4', 'mm5', 'mm6', 'mm7', 'fcw', 'fsw', 'mxcsr',
539 'es', 'cs', 'ss', 'ds', 'fs', 'gs', '<none>', '<none>', 'tr', 'ldtr'
540 ]
541
542 _REG_NAMES_x64 = [
543 'rax', 'rdx', 'rcx', 'rbx', 'rsi', 'rdi', 'rbp', 'rsp',
544 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15',
545 'rip', 'xmm0', 'xmm1', 'xmm2', 'xmm3', 'xmm4', 'xmm5', 'xmm6',
546 'xmm7', 'xmm8', 'xmm9', 'xmm10', 'xmm11', 'xmm12', 'xmm13', 'xmm14',
547 'xmm15', 'st0', 'st1', 'st2', 'st3', 'st4', 'st5', 'st6',
548 'st7', 'mm0', 'mm1', 'mm2', 'mm3', 'mm4', 'mm5', 'mm6',
549 'mm7', 'rflags', 'es', 'cs', 'ss', 'ds', 'fs', 'gs',
550 '<none>', '<none>', 'fs.base', 'gs.base', '<none>', '<none>', 'tr', 'ldtr',
551 'mxcsr', 'fcw', 'fsw'
552 ]
553
554 # https://developer.arm.com/documentation/ihi0057/e/?lang=en#dwarf-register-names
555 _REG_NAMES_AArch64 = [
556 'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7',
557 'x8', 'x9', 'x10', 'x11', 'x12', 'x13', 'x14', 'x15',
558 'x16', 'x17', 'x18', 'x19', 'x20', 'x21', 'x22', 'x23',
559 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30', 'sp',
560 '<none>', 'ELR_mode', 'RA_SIGN_STATE', '<none>', '<none>', '<none>', '<none>', '<none>',
561 '<none>', '<none>', '<none>', '<none>', '<none>', '<none>', 'VG', 'FFR',
562 'p0', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7',
563 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', 'p14', 'p15',
564 'v0', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7',
565 'v8', 'v9', 'v10', 'v11', 'v12', 'v13', 'v14', 'v15',
566 'v16', 'v17', 'v18', 'v19', 'v20', 'v21', 'v22', 'v23',
567 'v24', 'v25', 'v26', 'v27', 'v28', 'v29', 'v30', 'v31',
568 'z0', 'z1', 'z2', 'z3', 'z4', 'z5', 'z6', 'z7',
569 'z8', 'z9', 'z10', 'z11', 'z12', 'z13', 'z14', 'z15',
570 'z16', 'z17', 'z18', 'z19', 'z20', 'z21', 'z22', 'z23',
571 'z24', 'z25', 'z26', 'z27', 'z28', 'z29', 'z30', 'z31'
572 ]
573
574
575 class ExprDumper(object):
576 """ A dumper for DWARF expressions that dumps a textual
577 representation of the complete expression.
578
579 Usage: after creation, call dump_expr repeatedly - it's stateless.
580 """
581 def __init__(self, structs):
582 self.structs = structs
583 self.expr_parser = DWARFExprParser(self.structs)
584 self._init_lookups()
585
586 def dump_expr(self, expr, cu_offset=None):
587 """ Parse and dump a DWARF expression. expr should be a list of
588 (integer) byte values. cu_offset is the cu_offset
589 value from the CU object where the expression resides.
590 Only affects a handful of GNU opcodes, if None is provided,
591 that's not a crash condition, only the expression dump will
592 not be consistent of that of readelf.
593
594 Returns a string representing the expression.
595 """
596 parsed = self.expr_parser.parse_expr(expr)
597 s = []
598 for deo in parsed:
599 s.append(self._dump_to_string(deo.op, deo.op_name, deo.args, cu_offset))
600 return '; '.join(s)
601
602 def _init_lookups(self):
603 self._ops_with_decimal_arg = set([
604 'DW_OP_const1u', 'DW_OP_const1s', 'DW_OP_const2u', 'DW_OP_const2s',
605 'DW_OP_const4u', 'DW_OP_const4s', 'DW_OP_const8u', 'DW_OP_const8s',
606 'DW_OP_constu', 'DW_OP_consts', 'DW_OP_pick', 'DW_OP_plus_uconst',
607 'DW_OP_bra', 'DW_OP_skip', 'DW_OP_fbreg', 'DW_OP_piece',
608 'DW_OP_deref_size', 'DW_OP_xderef_size', 'DW_OP_regx',])
609
610 for n in range(0, 32):
611 self._ops_with_decimal_arg.add('DW_OP_breg%s' % n)
612
613 self._ops_with_two_decimal_args = set(['DW_OP_bregx', 'DW_OP_bit_piece'])
614
615 self._ops_with_hex_arg = set(
616 ['DW_OP_addr', 'DW_OP_call2', 'DW_OP_call4', 'DW_OP_call_ref'])
617
618 def _dump_to_string(self, opcode, opcode_name, args, cu_offset=None):
619 # Some GNU ops contain an offset from the current CU as an argument,
620 # but readelf emits those ops with offset from the info section
621 # so we need the base offset of the parent CU.
622 # If omitted, arguments on some GNU opcodes will be off.
623 if cu_offset is None:
624 cu_offset = 0
625
626 if len(args) == 0:
627 if opcode_name.startswith('DW_OP_reg'):
628 regnum = int(opcode_name[9:])
629 return '%s (%s)' % (
630 opcode_name,
631 describe_reg_name(regnum, _MACHINE_ARCH))
632 else:
633 return opcode_name
634 elif opcode_name in self._ops_with_decimal_arg:
635 if opcode_name.startswith('DW_OP_breg'):
636 regnum = int(opcode_name[10:])
637 return '%s (%s): %s' % (
638 opcode_name,
639 describe_reg_name(regnum, _MACHINE_ARCH),
640 args[0])
641 elif opcode_name.endswith('regx'):
642 # applies to both regx and bregx
643 return '%s: %s (%s)' % (
644 opcode_name,
645 args[0],
646 describe_reg_name(args[0], _MACHINE_ARCH))
647 else:
648 return '%s: %s' % (opcode_name, args[0])
649 elif opcode_name in self._ops_with_hex_arg:
650 return '%s: %x' % (opcode_name, args[0])
651 elif opcode_name in self._ops_with_two_decimal_args:
652 return '%s: %s %s' % (opcode_name, args[0], args[1])
653 elif opcode_name in ('DW_OP_GNU_entry_value', 'DW_OP_entry_value'):
654 return '%s: (%s)' % (opcode_name, ','.join([self._dump_to_string(deo.op, deo.op_name, deo.args, cu_offset) for deo in args[0]]))
655 elif opcode_name == 'DW_OP_implicit_value':
656 return "%s %s byte block: %s" % (opcode_name, len(args[0]), ''.join(["%x " % b for b in args[0]]))
657 elif opcode_name == 'DW_OP_GNU_parameter_ref':
658 return "%s: <0x%x>" % (opcode_name, args[0] + cu_offset)
659 elif opcode_name in ('DW_OP_GNU_implicit_pointer', 'DW_OP_implicit_pointer'):
660 return "%s: <0x%x> %d" % (opcode_name, args[0], args[1])
661 elif opcode_name in ('DW_OP_GNU_convert', 'DW_OP_convert'):
662 return "%s <0x%x>" % (opcode_name, args[0] + cu_offset)
663 elif opcode_name in ('DW_OP_GNU_deref_type', 'DW_OP_deref_type'):
664 return "%s: %d <0x%x>" % (opcode_name, args[0], args[1] + cu_offset)
665 elif opcode_name in ('DW_OP_GNU_const_type', 'DW_OP_const_type'):
666 return "%s: <0x%x> %d byte block: %s " % (opcode_name, args[0] + cu_offset, len(args[1]), ' '.join("%x" % b for b in args[1]))
667 elif opcode_name in ('DW_OP_GNU_regval_type', 'DW_OP_regval_type'):
668 return "%s: %d (%s) <0x%x>" % (opcode_name, args[0], describe_reg_name(args[0], _MACHINE_ARCH), args[1] + cu_offset)
669 else:
670 return '<unknown %s>' % opcode_name