-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathdie_information.py
255 lines (222 loc) · 9.37 KB
/
die_information.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# Copyright 2016 MongoDB Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from elftools.dwarf.die import DIE
from dwarf_expr import describe_DWARF_expr
from disasm_demangler import demangle
# Maps of each DIE offset to the corresponding DIE.
# Used when trying to follow DIE type references, i.e., a DW_TAG_typedef DIE that has a DW_AT_type
# attribute that refers to a DW_TAG_base_type DIE.
die_list = {}
# Maps a DWARF tag to the modifier symbol that represents it.
# May be incomplete. Will be filled in as missing cases are found.
tag_modifier_map = {
'DW_TAG_pointer_type': '*',
'DW_TAG_const_type': 'const',
'DW_TAG_reference_type': '&',
'DW_TAG_array_type': '[]',
}
# Clears the die then maps each DIE offset to the corresponding DIE.
def reset_die_list(cu):
global die_list
# Would it be better to do die_list.clear()?
die_list = {}
try:
# TODO: This process breaks around DIE-offset 1093103
# See https://github.com/eliben/pyelftools/issues/112 for more information
# For now we will stop the process altogether if the DIE tree cannot be fully parsed.
# Realistically, however, the program should be able to return a partial solution on partial
# success, instead of completely failing.
for die in cu.iter_DIEs():
die_list[die.offset] = die
return True
except:
return False
class MemberInformation(dict):
def __init__(self, name, type, depth, offset, modifiers=None):
return dict.__init__(self,
name=name,
type=type,
depth=depth,
offset=offset,
modifiers=modifiers,)
@staticmethod
def member(child, depth, offset):
global die_list
name = None
typeName = None
if child.attributes.get('DW_AT_name'):
name = child.attributes.get('DW_AT_name').value
elif isType(child, 'DW_TAG_union_type'):
name = "(anonymous union)"
else:
name = "Cannot find the name of this member"
if child.attributes.get('DW_AT_type'):
typeRef = child.attributes.get('DW_AT_type').value
childType = die_list[child.cu.cu_offset + typeRef]
typeName, modifiers = getTypeAndModifiers(childType)
else:
typeName = None
modifiers = None
# member offset
offsetAttr = child.attributes.get('DW_AT_data_member_location')
if child.attributes.get('DW_AT_external'):
# If a child is external, then it's not an instance variable, so we don't care. Return
# None and handle this in the next level up.
return None
elif offsetAttr and offset is not None:
offset += offsetAttr.value
else:
offset = None
return MemberInformation(
name,
typeName,
depth,
offset,
modifiers)
# Creates a MemberInformation dict for a DIE that's known to be the parent of another DIE.
# The only real difference is that we repalce the name field with '(parent)', and that we can
# assume that it won't be an anonymous union.
# Since most of the code is so similar, this should later be refactored.
@staticmethod
def parent(parent, depth, offset):
if offset:
offsetAttr = parent.attributes.get('DW_AT_data_member_location')
if offset is not None and offsetAttr is not None:
offset += offsetAttr.value
else:
offset = None
if parent.attributes.get('DW_AT_type'):
typeRef = parent.attributes.get('DW_AT_type').value
parentType = die_list[parent.cu.cu_offset + typeRef]
typeName, modifiers = getTypeAndModifiers(parentType)
else:
typeName = None
modifiers = None
return MemberInformation(
'(parent)',
typeName,
depth,
offset,
modifiers)
class DIEInformation(dict):
def __init__(self, die):
name = getFullTypeName(die)
if name is None:
# If a type is unnamed, we don't really want to have anything to do it, as it's likely a
# modifier type (e.g, DW_TAG_pointer_type, DW_TAG_const_type, etc.)
return
tag = die.tag
size = None
if die.attributes.get('DW_AT_byte_size'):
size = die.attributes.get('DW_AT_byte_size').value
subtype = None
if die.attributes.get('DW_AT_type'):
typeRef = die.attributes.get('DW_AT_type').value
subtypeDie = die_list[die.cu.cu_offset + typeRef]
subtype = getFullTypeName(subtypeDie)
if not subtype:
subtype = "(cannot find the type)"
members = getMembers(die)
vtable = getVtable(die)
dict.__init__(self,
name=name,
tag=tag,
size=size,
subtype=subtype,
members=members,
vtable=vtable)
# Given a type DIE, get the full name of that type, e.g, mongo::Value
def getFullTypeName(die):
if die.attributes.get('DW_AT_name'):
name = die.attributes.get('DW_AT_name').value
parent = die.get_parent()
while parent and parent.tag != 'DW_TAG_compile_unit':
if parent.attributes.get('DW_AT_name'):
name = parent.attributes.get('DW_AT_name').value + '::' + name
parent = parent.get_parent()
return name
else:
return None
# Given a type DIE, returns the name of the highest-level named type, along with any modifiers
# (e.g, "const *") or None if there aren't any, returned as a 2-tuple.
def getTypeAndModifiers(die):
global die_list
if die.attributes.get('DW_AT_name'):
return (getFullTypeName(die), None,)
elif die.attributes.get('DW_AT_type') and die.tag in tag_modifier_map:
typeRef = die.attributes.get('DW_AT_type').value
typeDie = die_list[die.cu.cu_offset + typeRef]
typeName, modifiers = getTypeAndModifiers(typeDie)
return (typeName, modifiers + ' ' + tag_modifier_map[die.tag]
if modifiers is not None else
tag_modifier_map[die.tag])
else:
modifier = tag_modifier_map[die.tag] if die.tag in tag_modifier_map else None
# The assumption made here is that if a type is unnamed, then it's void. I don't know if
# this is true.
return ("void", modifier)
# Given a type die, get a list of MemberInformation dicts for the members of that type
def getMembers(typeDie, depth=0, offset=0):
global die_list
members = []
for child in typeDie.iter_children():
if child.tag == 'DW_TAG_member':
memberInfo = MemberInformation.member(child, depth, offset)
elif child.tag == 'DW_TAG_inheritance':
memberInfo = MemberInformation.parent(child, depth, offset)
elif child.tag =='DW_TAG_typedef':
# TODO: Handle typedefs as members
continue
else:
continue
if memberInfo is None:
continue
members.append(memberInfo)
if memberInfo['offset'] is not None:
_offset = memberInfo['offset']
else:
_offset = None
if child.attributes.get('DW_AT_type'):
typeRef = child.attributes.get('DW_AT_type').value
typeDie = die_list[child.cu.cu_offset + typeRef]
members += getMembers(typeDie, depth+1, _offset)
return members
def isType(die, typeName):
if not die.attributes.get('DW_AT_type'):
return False
typeRef = die.attributes.get('DW_AT_type').value
typeDie = die_list[die.cu.cu_offset + typeRef]
if typeDie.tag == typeName:
return True
return False
def getVtable(typeDie):
global die_list
vtable = []
for child in typeDie.iter_children():
if child.tag == 'DW_TAG_subprogram' \
and child.attributes.get('DW_AT_virtuality') \
and child.attributes.get('DW_AT_vtable_elem_location'):
elem_location = child.attributes.get('DW_AT_vtable_elem_location')
if elem_location.form == 'DW_FORM_exprloc':
loc_pieces = describe_DWARF_expr(elem_location.value, child.cu.structs)
index = loc_pieces[0]
if child.attributes.get('DW_AT_linkage_name'):
name = child.attributes.get('DW_AT_linkage_name').value
elif child.attributes.get('DW_AT_name'):
name = child.attributes.get('DW_AT_name').value
else:
name = "(Cannot determine name)"
vtable.append({"index": index, "name": demangle(name)});
elif elem_location.form == 'DW_FORM_loclistptr':
print 'Cannot currently handle form DW_FORM_loclistptr'
else:
print 'Unexpected form {} for vtable_elem_location'.format(elem_location.form)
return vtable