annotate lldb/examples/python/bsd.py @ 236:c4bab56944e8 llvm-original

LLVM 16
author kono
date Wed, 09 Nov 2022 17:45:10 +0900
parents 79ff65ed7e25
children 1f2b6ac9f198
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
1 #!/usr/bin/env python
150
anatofuz
parents:
diff changeset
2
anatofuz
parents:
diff changeset
3 import cmd
anatofuz
parents:
diff changeset
4 import optparse
anatofuz
parents:
diff changeset
5 import os
anatofuz
parents:
diff changeset
6 import shlex
anatofuz
parents:
diff changeset
7 import struct
anatofuz
parents:
diff changeset
8 import sys
anatofuz
parents:
diff changeset
9
anatofuz
parents:
diff changeset
10 ARMAG = "!<arch>\n"
anatofuz
parents:
diff changeset
11 SARMAG = 8
anatofuz
parents:
diff changeset
12 ARFMAG = "`\n"
anatofuz
parents:
diff changeset
13 AR_EFMT1 = "#1/"
anatofuz
parents:
diff changeset
14
anatofuz
parents:
diff changeset
15
anatofuz
parents:
diff changeset
16 def memdump(src, bytes_per_line=16, address=0):
anatofuz
parents:
diff changeset
17 FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.'
anatofuz
parents:
diff changeset
18 for x in range(256)])
anatofuz
parents:
diff changeset
19 for i in range(0, len(src), bytes_per_line):
anatofuz
parents:
diff changeset
20 s = src[i:i+bytes_per_line]
anatofuz
parents:
diff changeset
21 hex_bytes = ' '.join(["%02x" % (ord(x)) for x in s])
anatofuz
parents:
diff changeset
22 ascii = s.translate(FILTER)
anatofuz
parents:
diff changeset
23 print("%#08.8x: %-*s %s" % (address+i, bytes_per_line*3, hex_bytes,
anatofuz
parents:
diff changeset
24 ascii))
anatofuz
parents:
diff changeset
25
anatofuz
parents:
diff changeset
26
anatofuz
parents:
diff changeset
27 class Object(object):
anatofuz
parents:
diff changeset
28 def __init__(self, file):
anatofuz
parents:
diff changeset
29 def read_str(file, str_len):
anatofuz
parents:
diff changeset
30 return file.read(str_len).rstrip('\0 ')
anatofuz
parents:
diff changeset
31
anatofuz
parents:
diff changeset
32 def read_int(file, str_len, base):
anatofuz
parents:
diff changeset
33 return int(read_str(file, str_len), base)
anatofuz
parents:
diff changeset
34
anatofuz
parents:
diff changeset
35 self.offset = file.tell()
anatofuz
parents:
diff changeset
36 self.file = file
anatofuz
parents:
diff changeset
37 self.name = read_str(file, 16)
anatofuz
parents:
diff changeset
38 self.date = read_int(file, 12, 10)
anatofuz
parents:
diff changeset
39 self.uid = read_int(file, 6, 10)
anatofuz
parents:
diff changeset
40 self.gid = read_int(file, 6, 10)
anatofuz
parents:
diff changeset
41 self.mode = read_int(file, 8, 8)
anatofuz
parents:
diff changeset
42 self.size = read_int(file, 10, 10)
anatofuz
parents:
diff changeset
43 if file.read(2) != ARFMAG:
anatofuz
parents:
diff changeset
44 raise ValueError('invalid BSD object at offset %#08.8x' % (
anatofuz
parents:
diff changeset
45 self.offset))
anatofuz
parents:
diff changeset
46 # If we have an extended name read it. Extended names start with
anatofuz
parents:
diff changeset
47 name_len = 0
anatofuz
parents:
diff changeset
48 if self.name.startswith(AR_EFMT1):
anatofuz
parents:
diff changeset
49 name_len = int(self.name[len(AR_EFMT1):], 10)
anatofuz
parents:
diff changeset
50 self.name = read_str(file, name_len)
anatofuz
parents:
diff changeset
51 self.obj_offset = file.tell()
anatofuz
parents:
diff changeset
52 self.obj_size = self.size - name_len
anatofuz
parents:
diff changeset
53 file.seek(self.obj_size, 1)
anatofuz
parents:
diff changeset
54
anatofuz
parents:
diff changeset
55 def dump(self, f=sys.stdout, flat=True):
anatofuz
parents:
diff changeset
56 if flat:
anatofuz
parents:
diff changeset
57 f.write('%#08.8x: %#08.8x %5u %5u %6o %#08.8x %s\n' % (self.offset,
anatofuz
parents:
diff changeset
58 self.date, self.uid, self.gid, self.mode, self.size,
anatofuz
parents:
diff changeset
59 self.name))
anatofuz
parents:
diff changeset
60 else:
anatofuz
parents:
diff changeset
61 f.write('%#08.8x: \n' % self.offset)
anatofuz
parents:
diff changeset
62 f.write(' name = "%s"\n' % self.name)
anatofuz
parents:
diff changeset
63 f.write(' date = %#08.8x\n' % self.date)
anatofuz
parents:
diff changeset
64 f.write(' uid = %i\n' % self.uid)
anatofuz
parents:
diff changeset
65 f.write(' gid = %i\n' % self.gid)
anatofuz
parents:
diff changeset
66 f.write(' mode = %o\n' % self.mode)
anatofuz
parents:
diff changeset
67 f.write(' size = %#08.8x\n' % (self.size))
anatofuz
parents:
diff changeset
68 self.file.seek(self.obj_offset, 0)
anatofuz
parents:
diff changeset
69 first_bytes = self.file.read(4)
anatofuz
parents:
diff changeset
70 f.write('bytes = ')
anatofuz
parents:
diff changeset
71 memdump(first_bytes)
anatofuz
parents:
diff changeset
72
anatofuz
parents:
diff changeset
73 def get_bytes(self):
anatofuz
parents:
diff changeset
74 saved_pos = self.file.tell()
anatofuz
parents:
diff changeset
75 self.file.seek(self.obj_offset, 0)
anatofuz
parents:
diff changeset
76 bytes = self.file.read(self.obj_size)
anatofuz
parents:
diff changeset
77 self.file.seek(saved_pos, 0)
anatofuz
parents:
diff changeset
78 return bytes
anatofuz
parents:
diff changeset
79
anatofuz
parents:
diff changeset
80 def save(self, path=None, overwrite=False):
anatofuz
parents:
diff changeset
81 '''
anatofuz
parents:
diff changeset
82 Save the contents of the object to disk using 'path' argument as
anatofuz
parents:
diff changeset
83 the path, or save it to the current working directory using the
anatofuz
parents:
diff changeset
84 object name.
anatofuz
parents:
diff changeset
85 '''
anatofuz
parents:
diff changeset
86
anatofuz
parents:
diff changeset
87 if path is None:
anatofuz
parents:
diff changeset
88 path = self.name
anatofuz
parents:
diff changeset
89 if not overwrite and os.path.exists(path):
anatofuz
parents:
diff changeset
90 print('error: outfile "%s" already exists' % (path))
anatofuz
parents:
diff changeset
91 return
anatofuz
parents:
diff changeset
92 print('Saving "%s" to "%s"...' % (self.name, path))
anatofuz
parents:
diff changeset
93 with open(path, 'w') as f:
anatofuz
parents:
diff changeset
94 f.write(self.get_bytes())
anatofuz
parents:
diff changeset
95
anatofuz
parents:
diff changeset
96
anatofuz
parents:
diff changeset
97 class StringTable(object):
anatofuz
parents:
diff changeset
98 def __init__(self, bytes):
anatofuz
parents:
diff changeset
99 self.bytes = bytes
anatofuz
parents:
diff changeset
100
anatofuz
parents:
diff changeset
101 def get_string(self, offset):
anatofuz
parents:
diff changeset
102 length = len(self.bytes)
anatofuz
parents:
diff changeset
103 if offset >= length:
anatofuz
parents:
diff changeset
104 return None
anatofuz
parents:
diff changeset
105 return self.bytes[offset:self.bytes.find('\0', offset)]
anatofuz
parents:
diff changeset
106
anatofuz
parents:
diff changeset
107
anatofuz
parents:
diff changeset
108 class Archive(object):
anatofuz
parents:
diff changeset
109 def __init__(self, path):
anatofuz
parents:
diff changeset
110 self.path = path
anatofuz
parents:
diff changeset
111 self.file = open(path, 'r')
anatofuz
parents:
diff changeset
112 self.objects = []
anatofuz
parents:
diff changeset
113 self.offset_to_object = {}
anatofuz
parents:
diff changeset
114 if self.file.read(SARMAG) != ARMAG:
anatofuz
parents:
diff changeset
115 print("error: file isn't a BSD archive")
anatofuz
parents:
diff changeset
116 while True:
anatofuz
parents:
diff changeset
117 try:
anatofuz
parents:
diff changeset
118 self.objects.append(Object(self.file))
anatofuz
parents:
diff changeset
119 except ValueError:
anatofuz
parents:
diff changeset
120 break
anatofuz
parents:
diff changeset
121
anatofuz
parents:
diff changeset
122 def get_object_at_offset(self, offset):
anatofuz
parents:
diff changeset
123 if offset in self.offset_to_object:
anatofuz
parents:
diff changeset
124 return self.offset_to_object[offset]
anatofuz
parents:
diff changeset
125 for obj in self.objects:
anatofuz
parents:
diff changeset
126 if obj.offset == offset:
anatofuz
parents:
diff changeset
127 self.offset_to_object[offset] = obj
anatofuz
parents:
diff changeset
128 return obj
anatofuz
parents:
diff changeset
129 return None
anatofuz
parents:
diff changeset
130
anatofuz
parents:
diff changeset
131 def find(self, name, mtime=None, f=sys.stdout):
anatofuz
parents:
diff changeset
132 '''
anatofuz
parents:
diff changeset
133 Find an object(s) by name with optional modification time. There
anatofuz
parents:
diff changeset
134 can be multple objects with the same name inside and possibly with
anatofuz
parents:
diff changeset
135 the same modification time within a BSD archive so clients must be
anatofuz
parents:
diff changeset
136 prepared to get multiple results.
anatofuz
parents:
diff changeset
137 '''
anatofuz
parents:
diff changeset
138 matches = []
anatofuz
parents:
diff changeset
139 for obj in self.objects:
anatofuz
parents:
diff changeset
140 if obj.name == name and (mtime is None or mtime == obj.date):
anatofuz
parents:
diff changeset
141 matches.append(obj)
anatofuz
parents:
diff changeset
142 return matches
anatofuz
parents:
diff changeset
143
anatofuz
parents:
diff changeset
144 @classmethod
anatofuz
parents:
diff changeset
145 def dump_header(self, f=sys.stdout):
anatofuz
parents:
diff changeset
146 f.write(' DATE UID GID MODE SIZE NAME\n')
anatofuz
parents:
diff changeset
147 f.write(' ---------- ----- ----- ------ ---------- '
anatofuz
parents:
diff changeset
148 '--------------\n')
anatofuz
parents:
diff changeset
149
anatofuz
parents:
diff changeset
150 def get_symdef(self):
anatofuz
parents:
diff changeset
151 def get_uint32(file):
anatofuz
parents:
diff changeset
152 '''Extract a uint32_t from the current file position.'''
anatofuz
parents:
diff changeset
153 v, = struct.unpack('=I', file.read(4))
anatofuz
parents:
diff changeset
154 return v
anatofuz
parents:
diff changeset
155
anatofuz
parents:
diff changeset
156 for obj in self.objects:
anatofuz
parents:
diff changeset
157 symdef = []
anatofuz
parents:
diff changeset
158 if obj.name.startswith("__.SYMDEF"):
anatofuz
parents:
diff changeset
159 self.file.seek(obj.obj_offset, 0)
anatofuz
parents:
diff changeset
160 ranlib_byte_size = get_uint32(self.file)
anatofuz
parents:
diff changeset
161 num_ranlib_structs = ranlib_byte_size/8
anatofuz
parents:
diff changeset
162 str_offset_pairs = []
anatofuz
parents:
diff changeset
163 for _ in range(num_ranlib_structs):
anatofuz
parents:
diff changeset
164 strx = get_uint32(self.file)
anatofuz
parents:
diff changeset
165 offset = get_uint32(self.file)
anatofuz
parents:
diff changeset
166 str_offset_pairs.append((strx, offset))
anatofuz
parents:
diff changeset
167 strtab_len = get_uint32(self.file)
anatofuz
parents:
diff changeset
168 strtab = StringTable(self.file.read(strtab_len))
anatofuz
parents:
diff changeset
169 for s in str_offset_pairs:
anatofuz
parents:
diff changeset
170 symdef.append((strtab.get_string(s[0]), s[1]))
anatofuz
parents:
diff changeset
171 return symdef
anatofuz
parents:
diff changeset
172
anatofuz
parents:
diff changeset
173 def get_object_dicts(self):
anatofuz
parents:
diff changeset
174 '''
anatofuz
parents:
diff changeset
175 Returns an array of object dictionaries that contain they following
anatofuz
parents:
diff changeset
176 keys:
anatofuz
parents:
diff changeset
177 'object': the actual bsd.Object instance
anatofuz
parents:
diff changeset
178 'symdefs': an array of symbol names that the object contains
anatofuz
parents:
diff changeset
179 as found in the "__.SYMDEF" item in the archive
anatofuz
parents:
diff changeset
180 '''
anatofuz
parents:
diff changeset
181 symdefs = self.get_symdef()
anatofuz
parents:
diff changeset
182 symdef_dict = {}
anatofuz
parents:
diff changeset
183 if symdefs:
anatofuz
parents:
diff changeset
184 for (name, offset) in symdefs:
anatofuz
parents:
diff changeset
185 if offset in symdef_dict:
anatofuz
parents:
diff changeset
186 object_dict = symdef_dict[offset]
anatofuz
parents:
diff changeset
187 else:
anatofuz
parents:
diff changeset
188 object_dict = {
anatofuz
parents:
diff changeset
189 'object': self.get_object_at_offset(offset),
anatofuz
parents:
diff changeset
190 'symdefs': []
anatofuz
parents:
diff changeset
191 }
anatofuz
parents:
diff changeset
192 symdef_dict[offset] = object_dict
anatofuz
parents:
diff changeset
193 object_dict['symdefs'].append(name)
anatofuz
parents:
diff changeset
194 object_dicts = []
anatofuz
parents:
diff changeset
195 for offset in sorted(symdef_dict):
anatofuz
parents:
diff changeset
196 object_dicts.append(symdef_dict[offset])
anatofuz
parents:
diff changeset
197 return object_dicts
anatofuz
parents:
diff changeset
198
anatofuz
parents:
diff changeset
199 def dump(self, f=sys.stdout, flat=True):
anatofuz
parents:
diff changeset
200 f.write('%s:\n' % self.path)
anatofuz
parents:
diff changeset
201 if flat:
anatofuz
parents:
diff changeset
202 self.dump_header(f=f)
anatofuz
parents:
diff changeset
203 for obj in self.objects:
anatofuz
parents:
diff changeset
204 obj.dump(f=f, flat=flat)
anatofuz
parents:
diff changeset
205
anatofuz
parents:
diff changeset
206 class Interactive(cmd.Cmd):
anatofuz
parents:
diff changeset
207 '''Interactive prompt for exploring contents of BSD archive files, type
anatofuz
parents:
diff changeset
208 "help" to see a list of supported commands.'''
anatofuz
parents:
diff changeset
209 image_option_parser = None
anatofuz
parents:
diff changeset
210
anatofuz
parents:
diff changeset
211 def __init__(self, archives):
anatofuz
parents:
diff changeset
212 cmd.Cmd.__init__(self)
anatofuz
parents:
diff changeset
213 self.use_rawinput = False
anatofuz
parents:
diff changeset
214 self.intro = ('Interactive BSD archive prompt, type "help" to see a '
anatofuz
parents:
diff changeset
215 'list of supported commands.')
anatofuz
parents:
diff changeset
216 self.archives = archives
anatofuz
parents:
diff changeset
217 self.prompt = '% '
anatofuz
parents:
diff changeset
218
anatofuz
parents:
diff changeset
219 def default(self, line):
anatofuz
parents:
diff changeset
220 '''Catch all for unknown command, which will exit the interpreter.'''
anatofuz
parents:
diff changeset
221 print("unknown command: %s" % line)
anatofuz
parents:
diff changeset
222 return True
anatofuz
parents:
diff changeset
223
anatofuz
parents:
diff changeset
224 def do_q(self, line):
anatofuz
parents:
diff changeset
225 '''Quit command'''
anatofuz
parents:
diff changeset
226 return True
anatofuz
parents:
diff changeset
227
anatofuz
parents:
diff changeset
228 def do_quit(self, line):
anatofuz
parents:
diff changeset
229 '''Quit command'''
anatofuz
parents:
diff changeset
230 return True
anatofuz
parents:
diff changeset
231
anatofuz
parents:
diff changeset
232 def do_extract(self, line):
anatofuz
parents:
diff changeset
233 args = shlex.split(line)
anatofuz
parents:
diff changeset
234 if args:
anatofuz
parents:
diff changeset
235 extracted = False
anatofuz
parents:
diff changeset
236 for object_name in args:
anatofuz
parents:
diff changeset
237 for archive in self.archives:
anatofuz
parents:
diff changeset
238 matches = archive.find(object_name)
anatofuz
parents:
diff changeset
239 if matches:
anatofuz
parents:
diff changeset
240 for object in matches:
anatofuz
parents:
diff changeset
241 object.save(overwrite=False)
anatofuz
parents:
diff changeset
242 extracted = True
anatofuz
parents:
diff changeset
243 if not extracted:
anatofuz
parents:
diff changeset
244 print('error: no object matches "%s" in any archives' % (
anatofuz
parents:
diff changeset
245 object_name))
anatofuz
parents:
diff changeset
246 else:
anatofuz
parents:
diff changeset
247 print('error: must specify the name of an object to extract')
anatofuz
parents:
diff changeset
248
anatofuz
parents:
diff changeset
249 def do_ls(self, line):
anatofuz
parents:
diff changeset
250 args = shlex.split(line)
anatofuz
parents:
diff changeset
251 if args:
anatofuz
parents:
diff changeset
252 for object_name in args:
anatofuz
parents:
diff changeset
253 for archive in self.archives:
anatofuz
parents:
diff changeset
254 matches = archive.find(object_name)
anatofuz
parents:
diff changeset
255 if matches:
anatofuz
parents:
diff changeset
256 for object in matches:
anatofuz
parents:
diff changeset
257 object.dump(flat=False)
anatofuz
parents:
diff changeset
258 else:
anatofuz
parents:
diff changeset
259 print('error: no object matches "%s" in "%s"' % (
anatofuz
parents:
diff changeset
260 object_name, archive.path))
anatofuz
parents:
diff changeset
261 else:
anatofuz
parents:
diff changeset
262 for archive in self.archives:
anatofuz
parents:
diff changeset
263 archive.dump(flat=True)
anatofuz
parents:
diff changeset
264 print('')
anatofuz
parents:
diff changeset
265
anatofuz
parents:
diff changeset
266
anatofuz
parents:
diff changeset
267
anatofuz
parents:
diff changeset
268 def main():
anatofuz
parents:
diff changeset
269 parser = optparse.OptionParser(
anatofuz
parents:
diff changeset
270 prog='bsd',
anatofuz
parents:
diff changeset
271 description='Utility for BSD archives')
anatofuz
parents:
diff changeset
272 parser.add_option(
anatofuz
parents:
diff changeset
273 '--object',
anatofuz
parents:
diff changeset
274 type='string',
anatofuz
parents:
diff changeset
275 dest='object_name',
anatofuz
parents:
diff changeset
276 default=None,
anatofuz
parents:
diff changeset
277 help=('Specify the name of a object within the BSD archive to get '
anatofuz
parents:
diff changeset
278 'information on'))
anatofuz
parents:
diff changeset
279 parser.add_option(
anatofuz
parents:
diff changeset
280 '-s', '--symbol',
anatofuz
parents:
diff changeset
281 type='string',
anatofuz
parents:
diff changeset
282 dest='find_symbol',
anatofuz
parents:
diff changeset
283 default=None,
anatofuz
parents:
diff changeset
284 help=('Specify the name of a symbol within the BSD archive to get '
anatofuz
parents:
diff changeset
285 'information on from SYMDEF'))
anatofuz
parents:
diff changeset
286 parser.add_option(
anatofuz
parents:
diff changeset
287 '--symdef',
anatofuz
parents:
diff changeset
288 action='store_true',
anatofuz
parents:
diff changeset
289 dest='symdef',
anatofuz
parents:
diff changeset
290 default=False,
anatofuz
parents:
diff changeset
291 help=('Dump the information in the SYMDEF.'))
anatofuz
parents:
diff changeset
292 parser.add_option(
anatofuz
parents:
diff changeset
293 '-v', '--verbose',
anatofuz
parents:
diff changeset
294 action='store_true',
anatofuz
parents:
diff changeset
295 dest='verbose',
anatofuz
parents:
diff changeset
296 default=False,
anatofuz
parents:
diff changeset
297 help='Enable verbose output')
anatofuz
parents:
diff changeset
298 parser.add_option(
anatofuz
parents:
diff changeset
299 '-e', '--extract',
anatofuz
parents:
diff changeset
300 action='store_true',
anatofuz
parents:
diff changeset
301 dest='extract',
anatofuz
parents:
diff changeset
302 default=False,
anatofuz
parents:
diff changeset
303 help=('Specify this to extract the object specified with the --object '
anatofuz
parents:
diff changeset
304 'option. There must be only one object with a matching name or '
anatofuz
parents:
diff changeset
305 'the --mtime option must be specified to uniquely identify a '
anatofuz
parents:
diff changeset
306 'single object.'))
anatofuz
parents:
diff changeset
307 parser.add_option(
anatofuz
parents:
diff changeset
308 '-m', '--mtime',
anatofuz
parents:
diff changeset
309 type='int',
anatofuz
parents:
diff changeset
310 dest='mtime',
anatofuz
parents:
diff changeset
311 default=None,
anatofuz
parents:
diff changeset
312 help=('Specify the modification time of the object an object. This '
anatofuz
parents:
diff changeset
313 'option is used with either the --object or --extract options.'))
anatofuz
parents:
diff changeset
314 parser.add_option(
anatofuz
parents:
diff changeset
315 '-o', '--outfile',
anatofuz
parents:
diff changeset
316 type='string',
anatofuz
parents:
diff changeset
317 dest='outfile',
anatofuz
parents:
diff changeset
318 default=None,
anatofuz
parents:
diff changeset
319 help=('Specify a different name or path for the file to extract when '
anatofuz
parents:
diff changeset
320 'using the --extract option. If this option isn\'t specified, '
anatofuz
parents:
diff changeset
321 'then the extracted object file will be extracted into the '
anatofuz
parents:
diff changeset
322 'current working directory if a file doesn\'t already exist '
anatofuz
parents:
diff changeset
323 'with that name.'))
anatofuz
parents:
diff changeset
324 parser.add_option(
anatofuz
parents:
diff changeset
325 '-i', '--interactive',
anatofuz
parents:
diff changeset
326 action='store_true',
anatofuz
parents:
diff changeset
327 dest='interactive',
anatofuz
parents:
diff changeset
328 default=False,
anatofuz
parents:
diff changeset
329 help=('Enter an interactive shell that allows users to interactively '
anatofuz
parents:
diff changeset
330 'explore contents of .a files.'))
anatofuz
parents:
diff changeset
331
anatofuz
parents:
diff changeset
332 (options, args) = parser.parse_args(sys.argv[1:])
anatofuz
parents:
diff changeset
333
anatofuz
parents:
diff changeset
334 if options.interactive:
anatofuz
parents:
diff changeset
335 archives = []
anatofuz
parents:
diff changeset
336 for path in args:
anatofuz
parents:
diff changeset
337 archives.append(Archive(path))
anatofuz
parents:
diff changeset
338 interpreter = Interactive(archives)
anatofuz
parents:
diff changeset
339 interpreter.cmdloop()
anatofuz
parents:
diff changeset
340 return
anatofuz
parents:
diff changeset
341
anatofuz
parents:
diff changeset
342 for path in args:
anatofuz
parents:
diff changeset
343 archive = Archive(path)
anatofuz
parents:
diff changeset
344 if options.object_name:
anatofuz
parents:
diff changeset
345 print('%s:\n' % (path))
anatofuz
parents:
diff changeset
346 matches = archive.find(options.object_name, options.mtime)
anatofuz
parents:
diff changeset
347 if matches:
anatofuz
parents:
diff changeset
348 dump_all = True
anatofuz
parents:
diff changeset
349 if options.extract:
anatofuz
parents:
diff changeset
350 if len(matches) == 1:
anatofuz
parents:
diff changeset
351 dump_all = False
anatofuz
parents:
diff changeset
352 matches[0].save(path=options.outfile, overwrite=False)
anatofuz
parents:
diff changeset
353 else:
anatofuz
parents:
diff changeset
354 print('error: multiple objects match "%s". Specify '
anatofuz
parents:
diff changeset
355 'the modification time using --mtime.' % (
anatofuz
parents:
diff changeset
356 options.object_name))
anatofuz
parents:
diff changeset
357 if dump_all:
anatofuz
parents:
diff changeset
358 for obj in matches:
anatofuz
parents:
diff changeset
359 obj.dump(flat=False)
anatofuz
parents:
diff changeset
360 else:
anatofuz
parents:
diff changeset
361 print('error: object "%s" not found in archive' % (
anatofuz
parents:
diff changeset
362 options.object_name))
anatofuz
parents:
diff changeset
363 elif options.find_symbol:
anatofuz
parents:
diff changeset
364 symdefs = archive.get_symdef()
anatofuz
parents:
diff changeset
365 if symdefs:
anatofuz
parents:
diff changeset
366 success = False
anatofuz
parents:
diff changeset
367 for (name, offset) in symdefs:
anatofuz
parents:
diff changeset
368 obj = archive.get_object_at_offset(offset)
anatofuz
parents:
diff changeset
369 if name == options.find_symbol:
anatofuz
parents:
diff changeset
370 print('Found "%s" in:' % (options.find_symbol))
anatofuz
parents:
diff changeset
371 obj.dump(flat=False)
anatofuz
parents:
diff changeset
372 success = True
anatofuz
parents:
diff changeset
373 if not success:
anatofuz
parents:
diff changeset
374 print('Didn\'t find "%s" in any objects' % (
anatofuz
parents:
diff changeset
375 options.find_symbol))
anatofuz
parents:
diff changeset
376 else:
anatofuz
parents:
diff changeset
377 print("error: no __.SYMDEF was found")
anatofuz
parents:
diff changeset
378 elif options.symdef:
anatofuz
parents:
diff changeset
379 object_dicts = archive.get_object_dicts()
anatofuz
parents:
diff changeset
380 for object_dict in object_dicts:
anatofuz
parents:
diff changeset
381 object_dict['object'].dump(flat=False)
anatofuz
parents:
diff changeset
382 print("symbols:")
anatofuz
parents:
diff changeset
383 for name in object_dict['symdefs']:
anatofuz
parents:
diff changeset
384 print(" %s" % (name))
anatofuz
parents:
diff changeset
385 else:
anatofuz
parents:
diff changeset
386 archive.dump(flat=not options.verbose)
anatofuz
parents:
diff changeset
387
anatofuz
parents:
diff changeset
388
anatofuz
parents:
diff changeset
389 if __name__ == '__main__':
anatofuz
parents:
diff changeset
390 main()
anatofuz
parents:
diff changeset
391
anatofuz
parents:
diff changeset
392
anatofuz
parents:
diff changeset
393 def print_mtime_error(result, dmap_mtime, actual_mtime):
anatofuz
parents:
diff changeset
394 print("error: modification time in debug map (%#08.8x) doesn't "
anatofuz
parents:
diff changeset
395 "match the .o file modification time (%#08.8x)" % (
anatofuz
parents:
diff changeset
396 dmap_mtime, actual_mtime), file=result)
anatofuz
parents:
diff changeset
397
anatofuz
parents:
diff changeset
398
anatofuz
parents:
diff changeset
399 def print_file_missing_error(result, path):
anatofuz
parents:
diff changeset
400 print("error: file \"%s\" doesn't exist" % (path), file=result)
anatofuz
parents:
diff changeset
401
anatofuz
parents:
diff changeset
402
anatofuz
parents:
diff changeset
403 def print_multiple_object_matches(result, object_name, mtime, matches):
anatofuz
parents:
diff changeset
404 print("error: multiple matches for object '%s' with with "
anatofuz
parents:
diff changeset
405 "modification time %#08.8x:" % (object_name, mtime), file=result)
anatofuz
parents:
diff changeset
406 Archive.dump_header(f=result)
anatofuz
parents:
diff changeset
407 for match in matches:
anatofuz
parents:
diff changeset
408 match.dump(f=result, flat=True)
anatofuz
parents:
diff changeset
409
anatofuz
parents:
diff changeset
410
anatofuz
parents:
diff changeset
411 def print_archive_object_error(result, object_name, mtime, archive):
anatofuz
parents:
diff changeset
412 matches = archive.find(object_name, f=result)
anatofuz
parents:
diff changeset
413 if len(matches) > 0:
anatofuz
parents:
diff changeset
414 print("error: no objects have a modification time that "
anatofuz
parents:
diff changeset
415 "matches %#08.8x for '%s'. Potential matches:" % (
anatofuz
parents:
diff changeset
416 mtime, object_name), file=result)
anatofuz
parents:
diff changeset
417 Archive.dump_header(f=result)
anatofuz
parents:
diff changeset
418 for match in matches:
anatofuz
parents:
diff changeset
419 match.dump(f=result, flat=True)
anatofuz
parents:
diff changeset
420 else:
anatofuz
parents:
diff changeset
421 print("error: no object named \"%s\" found in archive:" % (
anatofuz
parents:
diff changeset
422 object_name), file=result)
anatofuz
parents:
diff changeset
423 Archive.dump_header(f=result)
anatofuz
parents:
diff changeset
424 for match in archive.objects:
anatofuz
parents:
diff changeset
425 match.dump(f=result, flat=True)
anatofuz
parents:
diff changeset
426 # archive.dump(f=result, flat=True)
anatofuz
parents:
diff changeset
427
anatofuz
parents:
diff changeset
428
anatofuz
parents:
diff changeset
429 class VerifyDebugMapCommand:
anatofuz
parents:
diff changeset
430 name = "verify-debug-map-objects"
anatofuz
parents:
diff changeset
431
anatofuz
parents:
diff changeset
432 def create_options(self):
anatofuz
parents:
diff changeset
433 usage = "usage: %prog [options]"
anatofuz
parents:
diff changeset
434 description = '''This command reports any .o files that are missing
anatofuz
parents:
diff changeset
435 or whose modification times don't match in the debug map of an executable.'''
anatofuz
parents:
diff changeset
436
anatofuz
parents:
diff changeset
437 self.parser = optparse.OptionParser(
anatofuz
parents:
diff changeset
438 description=description,
anatofuz
parents:
diff changeset
439 prog=self.name,
anatofuz
parents:
diff changeset
440 usage=usage,
anatofuz
parents:
diff changeset
441 add_help_option=False)
anatofuz
parents:
diff changeset
442
anatofuz
parents:
diff changeset
443 self.parser.add_option(
anatofuz
parents:
diff changeset
444 '-e', '--errors',
anatofuz
parents:
diff changeset
445 action='store_true',
anatofuz
parents:
diff changeset
446 dest='errors',
anatofuz
parents:
diff changeset
447 default=False,
anatofuz
parents:
diff changeset
448 help="Only show errors")
anatofuz
parents:
diff changeset
449
anatofuz
parents:
diff changeset
450 def get_short_help(self):
anatofuz
parents:
diff changeset
451 return "Verify debug map object files."
anatofuz
parents:
diff changeset
452
anatofuz
parents:
diff changeset
453 def get_long_help(self):
anatofuz
parents:
diff changeset
454 return self.help_string
anatofuz
parents:
diff changeset
455
anatofuz
parents:
diff changeset
456 def __init__(self, debugger, unused):
anatofuz
parents:
diff changeset
457 self.create_options()
anatofuz
parents:
diff changeset
458 self.help_string = self.parser.format_help()
anatofuz
parents:
diff changeset
459
anatofuz
parents:
diff changeset
460 def __call__(self, debugger, command, exe_ctx, result):
anatofuz
parents:
diff changeset
461 import lldb
anatofuz
parents:
diff changeset
462 # Use the Shell Lexer to properly parse up command options just like a
anatofuz
parents:
diff changeset
463 # shell would
anatofuz
parents:
diff changeset
464 command_args = shlex.split(command)
anatofuz
parents:
diff changeset
465
anatofuz
parents:
diff changeset
466 try:
anatofuz
parents:
diff changeset
467 (options, args) = self.parser.parse_args(command_args)
anatofuz
parents:
diff changeset
468 except:
anatofuz
parents:
diff changeset
469 result.SetError("option parsing failed")
anatofuz
parents:
diff changeset
470 return
anatofuz
parents:
diff changeset
471
anatofuz
parents:
diff changeset
472 # Always get program state from the SBExecutionContext passed in
anatofuz
parents:
diff changeset
473 target = exe_ctx.GetTarget()
anatofuz
parents:
diff changeset
474 if not target.IsValid():
anatofuz
parents:
diff changeset
475 result.SetError("invalid target")
anatofuz
parents:
diff changeset
476 return
anatofuz
parents:
diff changeset
477 archives = {}
anatofuz
parents:
diff changeset
478 for module_spec in args:
anatofuz
parents:
diff changeset
479 module = target.module[module_spec]
anatofuz
parents:
diff changeset
480 if not (module and module.IsValid()):
anatofuz
parents:
diff changeset
481 result.SetError('error: invalid module specification: "%s". '
anatofuz
parents:
diff changeset
482 'Specify the full path, basename, or UUID of '
anatofuz
parents:
diff changeset
483 'a module ' % (module_spec))
anatofuz
parents:
diff changeset
484 return
anatofuz
parents:
diff changeset
485 num_symbols = module.GetNumSymbols()
anatofuz
parents:
diff changeset
486 num_errors = 0
anatofuz
parents:
diff changeset
487 for i in range(num_symbols):
anatofuz
parents:
diff changeset
488 symbol = module.GetSymbolAtIndex(i)
anatofuz
parents:
diff changeset
489 if symbol.GetType() != lldb.eSymbolTypeObjectFile:
anatofuz
parents:
diff changeset
490 continue
anatofuz
parents:
diff changeset
491 path = symbol.GetName()
anatofuz
parents:
diff changeset
492 if not path:
anatofuz
parents:
diff changeset
493 continue
anatofuz
parents:
diff changeset
494 # Extract the value of the symbol by dumping the
anatofuz
parents:
diff changeset
495 # symbol. The value is the mod time.
anatofuz
parents:
diff changeset
496 dmap_mtime = int(str(symbol).split('value = ')
anatofuz
parents:
diff changeset
497 [1].split(',')[0], 16)
anatofuz
parents:
diff changeset
498 if not options.errors:
anatofuz
parents:
diff changeset
499 print('%s' % (path), file=result)
anatofuz
parents:
diff changeset
500 if os.path.exists(path):
anatofuz
parents:
diff changeset
501 actual_mtime = int(os.stat(path).st_mtime)
anatofuz
parents:
diff changeset
502 if dmap_mtime != actual_mtime:
anatofuz
parents:
diff changeset
503 num_errors += 1
anatofuz
parents:
diff changeset
504 if options.errors:
anatofuz
parents:
diff changeset
505 print('%s' % (path), end=' ', file=result)
anatofuz
parents:
diff changeset
506 print_mtime_error(result, dmap_mtime,
anatofuz
parents:
diff changeset
507 actual_mtime)
anatofuz
parents:
diff changeset
508 elif path[-1] == ')':
anatofuz
parents:
diff changeset
509 (archive_path, object_name) = path[0:-1].split('(')
anatofuz
parents:
diff changeset
510 if not archive_path and not object_name:
anatofuz
parents:
diff changeset
511 num_errors += 1
anatofuz
parents:
diff changeset
512 if options.errors:
anatofuz
parents:
diff changeset
513 print('%s' % (path), end=' ', file=result)
anatofuz
parents:
diff changeset
514 print_file_missing_error(path)
anatofuz
parents:
diff changeset
515 continue
anatofuz
parents:
diff changeset
516 if not os.path.exists(archive_path):
anatofuz
parents:
diff changeset
517 num_errors += 1
anatofuz
parents:
diff changeset
518 if options.errors:
anatofuz
parents:
diff changeset
519 print('%s' % (path), end=' ', file=result)
anatofuz
parents:
diff changeset
520 print_file_missing_error(archive_path)
anatofuz
parents:
diff changeset
521 continue
anatofuz
parents:
diff changeset
522 if archive_path in archives:
anatofuz
parents:
diff changeset
523 archive = archives[archive_path]
anatofuz
parents:
diff changeset
524 else:
anatofuz
parents:
diff changeset
525 archive = Archive(archive_path)
anatofuz
parents:
diff changeset
526 archives[archive_path] = archive
anatofuz
parents:
diff changeset
527 matches = archive.find(object_name, dmap_mtime)
anatofuz
parents:
diff changeset
528 num_matches = len(matches)
anatofuz
parents:
diff changeset
529 if num_matches == 1:
anatofuz
parents:
diff changeset
530 print('1 match', file=result)
anatofuz
parents:
diff changeset
531 obj = matches[0]
anatofuz
parents:
diff changeset
532 if obj.date != dmap_mtime:
anatofuz
parents:
diff changeset
533 num_errors += 1
anatofuz
parents:
diff changeset
534 if options.errors:
anatofuz
parents:
diff changeset
535 print('%s' % (path), end=' ', file=result)
anatofuz
parents:
diff changeset
536 print_mtime_error(result, dmap_mtime, obj.date)
anatofuz
parents:
diff changeset
537 elif num_matches == 0:
anatofuz
parents:
diff changeset
538 num_errors += 1
anatofuz
parents:
diff changeset
539 if options.errors:
anatofuz
parents:
diff changeset
540 print('%s' % (path), end=' ', file=result)
anatofuz
parents:
diff changeset
541 print_archive_object_error(result, object_name,
anatofuz
parents:
diff changeset
542 dmap_mtime, archive)
anatofuz
parents:
diff changeset
543 elif num_matches > 1:
anatofuz
parents:
diff changeset
544 num_errors += 1
anatofuz
parents:
diff changeset
545 if options.errors:
anatofuz
parents:
diff changeset
546 print('%s' % (path), end=' ', file=result)
anatofuz
parents:
diff changeset
547 print_multiple_object_matches(result,
anatofuz
parents:
diff changeset
548 object_name,
anatofuz
parents:
diff changeset
549 dmap_mtime, matches)
anatofuz
parents:
diff changeset
550 if num_errors > 0:
anatofuz
parents:
diff changeset
551 print("%u errors found" % (num_errors), file=result)
anatofuz
parents:
diff changeset
552 else:
anatofuz
parents:
diff changeset
553 print("No errors detected in debug map", file=result)
anatofuz
parents:
diff changeset
554
anatofuz
parents:
diff changeset
555
anatofuz
parents:
diff changeset
556 def __lldb_init_module(debugger, dict):
anatofuz
parents:
diff changeset
557 # This initializer is being run from LLDB in the embedded command
anatofuz
parents:
diff changeset
558 # interpreter.
anatofuz
parents:
diff changeset
559 # Add any commands contained in this module to LLDB
anatofuz
parents:
diff changeset
560 debugger.HandleCommand(
anatofuz
parents:
diff changeset
561 'command script add -c %s.VerifyDebugMapCommand %s' % (
anatofuz
parents:
diff changeset
562 __name__, VerifyDebugMapCommand.name))
anatofuz
parents:
diff changeset
563 print('The "%s" command has been installed, type "help %s" for detailed '
anatofuz
parents:
diff changeset
564 'help.' % (VerifyDebugMapCommand.name, VerifyDebugMapCommand.name))