e6aa81c4688601cc7661b0deb8473c94453e7a2c
[pyelftools.git] / elftools / dwarf / structs.py
1 #-------------------------------------------------------------------------------
2 # elftools: dwarf/structs.py
3 #
4 # Encapsulation of Construct structs for parsing DWARF, adjusted for correct
5 # endianness and word-size.
6 #
7 # Eli Bendersky (eliben@gmail.com)
8 # This code is in the public domain
9 #-------------------------------------------------------------------------------
10 from ..construct import (
11 UBInt8, UBInt16, UBInt32, UBInt64,
12 ULInt8, ULInt16, ULInt32, ULInt64,
13 Adapter, Struct, ConstructError, If, RepeatUntil, Field, Rename,
14 )
15
16
17 class DWARFStructs(object):
18 """ Accessible attributes:
19
20 Dwarf_uint{8,16,32,64):
21 Data chunks of the common sizes
22
23 Dwarf_xword:
24 32-bit or 64-bit word, depending on dwarfclass
25
26 Dwarf_initial_length:
27 "Initial length field" encoding, as described in DWARFv3 spec
28 section 7.4
29
30 Dwarf_{u,s}leb128:
31 ULEB128 and SLEB128 variable-length encoding, as described in
32 DWARFv3 spec section 7.6
33 """
34 def __init__(self, little_endian=True, dwarfclass=32):
35 assert dwarfclass == 32 or dwarfclass == 64
36 self.little_endian = little_endian
37 self.dwarfclass = dwarfclass
38 self._create_structs()
39 self._create_leb128()
40
41 def _create_structs(self):
42 if self.little_endian:
43 self.Dwarf_uint8 = ULInt8
44 self.Dwarf_uint16 = ULInt16
45 self.Dwarf_uint32 = ULInt32
46 self.Dwarf_uint64 = ULInt64
47 self.Dwarf_xword = ULInt32 if self.dwarfclass == 32 else ULInt64
48 else:
49 self.Dwarf_uint8 = UBInt8
50 self.Dwarf_uint16 = UBInt16
51 self.Dwarf_uint32 = UBInt32
52 self.Dwarf_uint64 = UBInt64
53 self.Dwarf_xword = UBInt32 if self.dwarfclass == 32 else UBInt64
54
55 self._create_initial_length()
56
57 def _create_initial_length(self):
58 def _InitialLength(name):
59 # Adapts a Struct that parses forward a full initial length field.
60 # Only if the first word is the continuation value, the second
61 # word is parsed from the stream.
62 #
63 return _InitialLengthAdapter(
64 Struct(name,
65 self.Dwarf_uint32('first'),
66 If(lambda ctx: ctx.first == 0xFFFFFFFF,
67 self.Dwarf_uint64('second'),
68 elsevalue=None)))
69 self.Dwarf_initial_length = _InitialLength
70
71 def _create_leb128(self):
72 self.Dwarf_uleb128 = _ULEB128
73 self.Dwarf_sleb128 = _SLEB128
74
75
76 class _InitialLengthAdapter(Adapter):
77 """ A standard Construct adapter that expects a sub-construct
78 as a struct with one or two values (first, second).
79 """
80 def _decode(self, obj, context):
81 if obj.first < 0xFFFFFF00:
82 return obj.first
83 else:
84 if obj.first == 0xFFFFFFFF:
85 return obj.second
86 else:
87 raise ConstructError("Failed decoding initial length for %X" % (
88 obj.first))
89
90
91 def _LEB128_reader():
92 """ Read LEB128 variable-length data from the stream. The data is terminated
93 by a byte with 0 in its highest bit.
94 """
95 return RepeatUntil(
96 lambda obj, ctx: ord(obj) < 0x80,
97 Field(None, 1))
98
99
100 class _ULEB128Adapter(Adapter):
101 """ An adapter for ULEB128, given a sequence of bytes in a sub-construct.
102 """
103 def _decode(self, obj, context):
104 value = 0
105 for b in reversed(obj):
106 value = (value << 7) + (ord(b) & 0x7F)
107 return value
108
109
110 class _SLEB128Adapter(Adapter):
111 """ An adapter for SLEB128, given a sequence of bytes in a sub-construct.
112 """
113 def _decode(self, obj, context):
114 value = 0
115 for b in reversed(obj):
116 value = (value << 7) + (ord(b) & 0x7F)
117 if ord(obj[-1]) & 0x40:
118 # negative -> sign extend
119 #
120 value |= - (1 << (7 * len(obj)))
121 return value
122
123
124 def _ULEB128(name):
125 """ A construct creator for ULEB128 encoding.
126 """
127 return Rename(name, _ULEB128Adapter(_LEB128_reader()))
128
129
130 def _SLEB128(name):
131 """ A construct creator for SLEB128 encoding.
132 """
133 return Rename(name, _SLEB128Adapter(_LEB128_reader()))
134