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 import lldb
30 import commands
31 import optparse
32 import os
33 import plistlib
34 import re
35 import shlex
36 import sys
37 import time
38 import uuid
157
160 """Class that represents an load address range"""
161 sect_info_regex = re.compile('(?P<name>[^=]+)=(?P<range>.*)')
162 addr_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*$')
163 range_regex = re.compile(
164 '^\s*(?P<start>0x[0-9A-Fa-f]+)\s*(?P<op>[-+])\s*(?P<end>0x[0-9A-Fa-f]+)\s*$')
165
166 - def __init__(self, start_addr=None, end_addr=None, name=None):
170
171 @classmethod
183
186
188 match = self.sect_info_regex.match(s)
189 if match:
190 self.name = match.group('name')
191 range_str = match.group('range')
192 addr_match = self.addr_regex.match(range_str)
193 if addr_match:
194 self.start_addr = int(addr_match.group('start'), 16)
195 self.end_addr = None
196 return True
197
198 range_match = self.range_regex.match(range_str)
199 if range_match:
200 self.start_addr = int(range_match.group('start'), 16)
201 self.end_addr = int(range_match.group('end'), 16)
202 op = range_match.group('op')
203 if op == '+':
204 self.end_addr += self.start_addr
205 return True
206 print 'error: invalid section info string "%s"' % s
207 print 'Valid section info formats are:'
208 print 'Format Example Description'
209 print '--------------------- -----------------------------------------------'
210 print '<name>=<base> __TEXT=0x123000 Section from base address only'
211 print '<name>=<base>-<end> __TEXT=0x123000-0x124000 Section from base address and end address'
212 print '<name>=<base>+<size> __TEXT=0x123000+0x1000 Section from base address and size'
213 return False
214
216 if self.name:
217 if self.end_addr is not None:
218 if self.start_addr is not None:
219 return "%s=[0x%16.16x - 0x%16.16x)" % (
220 self.name, self.start_addr, self.end_addr)
221 else:
222 if self.start_addr is not None:
223 return "%s=0x%16.16x" % (self.name, self.start_addr)
224 return self.name
225 return "<invalid>"
226
229 """A class that represents an executable image and any associated data"""
230
232 self.path = path
233 self.resolved_path = None
234 self.resolved = False
235 self.unavailable = False
236 self.uuid = uuid
237 self.section_infos = list()
238 self.identifier = None
239 self.version = None
240 self.arch = None
241 self.module = None
242 self.symfile = None
243 self.slide = None
244
245 @classmethod
262
263 - def dump(self, prefix):
264 print "%s%s" % (prefix, self)
265
267 print 'path = "%s"' % (self.path)
268 print 'resolved_path = "%s"' % (self.resolved_path)
269 print 'resolved = %i' % (self.resolved)
270 print 'unavailable = %i' % (self.unavailable)
271 print 'uuid = %s' % (self.uuid)
272 print 'section_infos = %s' % (self.section_infos)
273 print 'identifier = "%s"' % (self.identifier)
274 print 'version = %s' % (self.version)
275 print 'arch = %s' % (self.arch)
276 print 'module = %s' % (self.module)
277 print 'symfile = "%s"' % (self.symfile)
278 print 'slide = %i (0x%x)' % (self.slide, self.slide)
279
281 s = ''
282 if self.uuid:
283 s += "%s " % (self.get_uuid())
284 if self.arch:
285 s += "%s " % (self.arch)
286 if self.version:
287 s += "%s " % (self.version)
288 resolved_path = self.get_resolved_path()
289 if resolved_path:
290 s += "%s " % (resolved_path)
291 for section_info in self.section_infos:
292 s += ", %s" % (section_info)
293 if self.slide is not None:
294 s += ', slide = 0x%16.16x' % self.slide
295 return s
296
298
299 self.section_infos.append(section)
300
302 for section_info in self.section_infos:
303 if section_info.contains(load_addr):
304 return section_info
305 return None
306
308 if self.resolved_path:
309 return self.resolved_path
310 elif self.path:
311 return self.path
312 return None
313
319
321 if self.symfile:
322 return os.path.basename(self.symfile)
323 return None
324
326 return self.section_infos or self.slide is not None
327
329 if self.unavailable:
330 return None
331
332
333 if self.has_section_load_info():
334 if target:
335 if self.module:
336 if self.section_infos:
337 num_sections_loaded = 0
338 for section_info in self.section_infos:
339 if section_info.name:
340 section = self.module.FindSection(
341 section_info.name)
342 if section:
343 error = target.SetSectionLoadAddress(
344 section, section_info.start_addr)
345 if error.Success():
346 num_sections_loaded += 1
347 else:
348 return 'error: %s' % error.GetCString()
349 else:
350 return 'error: unable to find the section named "%s"' % section_info.name
351 else:
352 return 'error: unable to find "%s" section in "%s"' % (
353 range.name, self.get_resolved_path())
354 if num_sections_loaded == 0:
355 return 'error: no sections were successfully loaded'
356 else:
357 err = target.SetModuleLoadAddress(
358 self.module, self.slide)
359 if err.Fail():
360 return err.GetCString()
361 return None
362 else:
363 return 'error: invalid module'
364 else:
365 return 'error: invalid target'
366 else:
367 return 'error: no section infos'
368
393
395
396
397
398
399
400
401 self.resolved = True
402 return True
403
408
410 if self.uuid:
411 return str(self.uuid).upper()
412 return None
413
438
441
443 """A class the represents the information needed to symbolicate addresses in a program"""
444 self.target = None
445 self.images = list()
446 self.addr_mask = 0xffffffffffffffff
447
448 @classmethod
463
465 s = "Symbolicator:\n"
466 if self.target:
467 s += "Target = '%s'\n" % (self.target)
468 s += "Target modules:\n"
469 for m in self.target.modules:
470 s += str(m) + "\n"
471 s += "Images:\n"
472 for image in self.images:
473 s += ' %s\n' % (image)
474 return s
475
477 images = list()
478 for image in self.images:
479 if image.identifier == identifier:
480 images.append(image)
481 if len(images) == 0:
482 regex_text = '^.*\.%s$' % (re.escape(identifier))
483 regex = re.compile(regex_text)
484 for image in self.images:
485 if regex.match(image.identifier):
486 images.append(image)
487 return images
488
494
511
513 if not self.target:
514 self.create_target()
515 if self.target:
516 live_process = False
517 process = self.target.process
518 if process:
519 state = process.state
520 if state > lldb.eStateUnloaded and state < lldb.eStateDetached:
521 live_process = True
522
523
524
525 if not live_process:
526 image = self.find_image_containing_load_addr(load_addr)
527 if image:
528 image.add_module(self.target)
529 symbolicated_address = Address(self.target, load_addr)
530 if symbolicated_address.symbolicate(verbose):
531 if symbolicated_address.so_addr:
532 symbolicated_addresses = list()
533 symbolicated_addresses.append(symbolicated_address)
534
535 while True:
536 inlined_parent_so_addr = lldb.SBAddress()
537 inlined_parent_sym_ctx = symbolicated_address.sym_ctx.GetParentOfInlinedScope(
538 symbolicated_address.so_addr, inlined_parent_so_addr)
539 if not inlined_parent_sym_ctx:
540 break
541 if not inlined_parent_so_addr:
542 break
543
544 symbolicated_address = Address(
545 self.target, inlined_parent_so_addr.GetLoadAddress(
546 self.target))
547 symbolicated_address.sym_ctx = inlined_parent_sym_ctx
548 symbolicated_address.so_addr = inlined_parent_so_addr
549 symbolicated_address.symbolicate(verbose)
550
551
552 symbolicated_addresses.append(symbolicated_address)
553
554 if symbolicated_addresses:
555 return symbolicated_addresses
556 else:
557 print 'error: no target in Symbolicator'
558 return None
559
560
561 -def disassemble_instructions(
562 target,
563 instructions,
564 pc,
565 insts_before_pc,
566 insts_after_pc,
567 non_zeroeth_frame):
568 lines = list()
569 pc_index = -1
570 comment_column = 50
571 for inst_idx, inst in enumerate(instructions):
572 inst_pc = inst.GetAddress().GetLoadAddress(target)
573 if pc == inst_pc:
574 pc_index = inst_idx
575 mnemonic = inst.GetMnemonic(target)
576 operands = inst.GetOperands(target)
577 comment = inst.GetComment(target)
578
579 lines.append("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands))
580 if comment:
581 line_len = len(lines[-1])
582 if line_len < comment_column:
583 lines[-1] += ' ' * (comment_column - line_len)
584 lines[-1] += "; %s" % comment
585
586 if pc_index >= 0:
587
588
589 if non_zeroeth_frame and pc_index > 0:
590 pc_index = pc_index - 1
591 if insts_before_pc == -1:
592 start_idx = 0
593 else:
594 start_idx = pc_index - insts_before_pc
595 if start_idx < 0:
596 start_idx = 0
597 if insts_before_pc == -1:
598 end_idx = inst_idx
599 else:
600 end_idx = pc_index + insts_after_pc
601 if end_idx > inst_idx:
602 end_idx = inst_idx
603 for i in range(start_idx, end_idx + 1):
604 if i == pc_index:
605 print ' -> ', lines[i]
606 else:
607 print ' ', lines[i]
608
617
626
631
634 for sym in module:
635 print sym
636
639
640 usage = "usage: %prog [options] <addr1> [addr2 ...]"
641 description = '''Symbolicate one or more addresses using LLDB's python scripting API..'''
642 parser = optparse.OptionParser(
643 description=description,
644 prog='crashlog.py',
645 usage=usage)
646 parser.add_option(
647 '-v',
648 '--verbose',
649 action='store_true',
650 dest='verbose',
651 help='display verbose debug info',
652 default=False)
653 parser.add_option(
654 '-p',
655 '--platform',
656 type='string',
657 metavar='platform',
658 dest='platform',
659 help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".')
660 parser.add_option(
661 '-f',
662 '--file',
663 type='string',
664 metavar='file',
665 dest='file',
666 help='Specify a file to use when symbolicating')
667 parser.add_option(
668 '-a',
669 '--arch',
670 type='string',
671 metavar='arch',
672 dest='arch',
673 help='Specify a architecture to use when symbolicating')
674 parser.add_option(
675 '-s',
676 '--slide',
677 type='int',
678 metavar='slide',
679 dest='slide',
680 help='Specify the slide to use on the file specified with the --file option',
681 default=None)
682 parser.add_option(
683 '--section',
684 type='string',
685 action='append',
686 dest='section_strings',
687 help='specify <sect-name>=<start-addr> or <sect-name>=<start-addr>-<end-addr>')
688 try:
689 (options, args) = parser.parse_args(command_args)
690 except:
691 return
692 symbolicator = Symbolicator()
693 images = list()
694 if options.file:
695 image = Image(options.file)
696 image.arch = options.arch
697
698
699 if options.section_strings:
700 for section_str in options.section_strings:
701 section = Section()
702 if section.set_from_string(section_str):
703 image.add_section(section)
704 else:
705 sys.exit(1)
706 if options.slide is not None:
707 image.slide = options.slide
708 symbolicator.images.append(image)
709
710 target = symbolicator.create_target()
711 if options.verbose:
712 print symbolicator
713 if target:
714 for addr_str in args:
715 addr = int(addr_str, 0)
716 symbolicated_addrs = symbolicator.symbolicate(
717 addr, options.verbose)
718 for symbolicated_addr in symbolicated_addrs:
719 print symbolicated_addr
720 print
721 else:
722 print 'error: no target for %s' % (symbolicator)
723
724 if __name__ == '__main__':
725
726 lldb.debugger = lldb.SBDebugger.Create()
727 Symbolicate(sys.argv[1:])
728