1*be691f3bSpatrick#!/usr/bin/env python 2061da546Spatrickfrom __future__ import print_function 3061da546Spatrick 4061da546Spatrickimport cmd 5061da546Spatrickimport optparse 6061da546Spatrickimport os 7061da546Spatrickimport shlex 8061da546Spatrickimport struct 9061da546Spatrickimport sys 10061da546Spatrick 11061da546SpatrickARMAG = "!<arch>\n" 12061da546SpatrickSARMAG = 8 13061da546SpatrickARFMAG = "`\n" 14061da546SpatrickAR_EFMT1 = "#1/" 15061da546Spatrick 16061da546Spatrick 17061da546Spatrickdef memdump(src, bytes_per_line=16, address=0): 18061da546Spatrick FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' 19061da546Spatrick for x in range(256)]) 20061da546Spatrick for i in range(0, len(src), bytes_per_line): 21061da546Spatrick s = src[i:i+bytes_per_line] 22061da546Spatrick hex_bytes = ' '.join(["%02x" % (ord(x)) for x in s]) 23061da546Spatrick ascii = s.translate(FILTER) 24061da546Spatrick print("%#08.8x: %-*s %s" % (address+i, bytes_per_line*3, hex_bytes, 25061da546Spatrick ascii)) 26061da546Spatrick 27061da546Spatrick 28061da546Spatrickclass Object(object): 29061da546Spatrick def __init__(self, file): 30061da546Spatrick def read_str(file, str_len): 31061da546Spatrick return file.read(str_len).rstrip('\0 ') 32061da546Spatrick 33061da546Spatrick def read_int(file, str_len, base): 34061da546Spatrick return int(read_str(file, str_len), base) 35061da546Spatrick 36061da546Spatrick self.offset = file.tell() 37061da546Spatrick self.file = file 38061da546Spatrick self.name = read_str(file, 16) 39061da546Spatrick self.date = read_int(file, 12, 10) 40061da546Spatrick self.uid = read_int(file, 6, 10) 41061da546Spatrick self.gid = read_int(file, 6, 10) 42061da546Spatrick self.mode = read_int(file, 8, 8) 43061da546Spatrick self.size = read_int(file, 10, 10) 44061da546Spatrick if file.read(2) != ARFMAG: 45061da546Spatrick raise ValueError('invalid BSD object at offset %#08.8x' % ( 46061da546Spatrick self.offset)) 47061da546Spatrick # If we have an extended name read it. Extended names start with 48061da546Spatrick name_len = 0 49061da546Spatrick if self.name.startswith(AR_EFMT1): 50061da546Spatrick name_len = int(self.name[len(AR_EFMT1):], 10) 51061da546Spatrick self.name = read_str(file, name_len) 52061da546Spatrick self.obj_offset = file.tell() 53061da546Spatrick self.obj_size = self.size - name_len 54061da546Spatrick file.seek(self.obj_size, 1) 55061da546Spatrick 56061da546Spatrick def dump(self, f=sys.stdout, flat=True): 57061da546Spatrick if flat: 58061da546Spatrick f.write('%#08.8x: %#08.8x %5u %5u %6o %#08.8x %s\n' % (self.offset, 59061da546Spatrick self.date, self.uid, self.gid, self.mode, self.size, 60061da546Spatrick self.name)) 61061da546Spatrick else: 62061da546Spatrick f.write('%#08.8x: \n' % self.offset) 63061da546Spatrick f.write(' name = "%s"\n' % self.name) 64061da546Spatrick f.write(' date = %#08.8x\n' % self.date) 65061da546Spatrick f.write(' uid = %i\n' % self.uid) 66061da546Spatrick f.write(' gid = %i\n' % self.gid) 67061da546Spatrick f.write(' mode = %o\n' % self.mode) 68061da546Spatrick f.write(' size = %#08.8x\n' % (self.size)) 69061da546Spatrick self.file.seek(self.obj_offset, 0) 70061da546Spatrick first_bytes = self.file.read(4) 71061da546Spatrick f.write('bytes = ') 72061da546Spatrick memdump(first_bytes) 73061da546Spatrick 74061da546Spatrick def get_bytes(self): 75061da546Spatrick saved_pos = self.file.tell() 76061da546Spatrick self.file.seek(self.obj_offset, 0) 77061da546Spatrick bytes = self.file.read(self.obj_size) 78061da546Spatrick self.file.seek(saved_pos, 0) 79061da546Spatrick return bytes 80061da546Spatrick 81061da546Spatrick def save(self, path=None, overwrite=False): 82061da546Spatrick ''' 83061da546Spatrick Save the contents of the object to disk using 'path' argument as 84061da546Spatrick the path, or save it to the current working directory using the 85061da546Spatrick object name. 86061da546Spatrick ''' 87061da546Spatrick 88061da546Spatrick if path is None: 89061da546Spatrick path = self.name 90061da546Spatrick if not overwrite and os.path.exists(path): 91061da546Spatrick print('error: outfile "%s" already exists' % (path)) 92061da546Spatrick return 93061da546Spatrick print('Saving "%s" to "%s"...' % (self.name, path)) 94061da546Spatrick with open(path, 'w') as f: 95061da546Spatrick f.write(self.get_bytes()) 96061da546Spatrick 97061da546Spatrick 98061da546Spatrickclass StringTable(object): 99061da546Spatrick def __init__(self, bytes): 100061da546Spatrick self.bytes = bytes 101061da546Spatrick 102061da546Spatrick def get_string(self, offset): 103061da546Spatrick length = len(self.bytes) 104061da546Spatrick if offset >= length: 105061da546Spatrick return None 106061da546Spatrick return self.bytes[offset:self.bytes.find('\0', offset)] 107061da546Spatrick 108061da546Spatrick 109061da546Spatrickclass Archive(object): 110061da546Spatrick def __init__(self, path): 111061da546Spatrick self.path = path 112061da546Spatrick self.file = open(path, 'r') 113061da546Spatrick self.objects = [] 114061da546Spatrick self.offset_to_object = {} 115061da546Spatrick if self.file.read(SARMAG) != ARMAG: 116061da546Spatrick print("error: file isn't a BSD archive") 117061da546Spatrick while True: 118061da546Spatrick try: 119061da546Spatrick self.objects.append(Object(self.file)) 120061da546Spatrick except ValueError: 121061da546Spatrick break 122061da546Spatrick 123061da546Spatrick def get_object_at_offset(self, offset): 124061da546Spatrick if offset in self.offset_to_object: 125061da546Spatrick return self.offset_to_object[offset] 126061da546Spatrick for obj in self.objects: 127061da546Spatrick if obj.offset == offset: 128061da546Spatrick self.offset_to_object[offset] = obj 129061da546Spatrick return obj 130061da546Spatrick return None 131061da546Spatrick 132061da546Spatrick def find(self, name, mtime=None, f=sys.stdout): 133061da546Spatrick ''' 134061da546Spatrick Find an object(s) by name with optional modification time. There 135061da546Spatrick can be multple objects with the same name inside and possibly with 136061da546Spatrick the same modification time within a BSD archive so clients must be 137061da546Spatrick prepared to get multiple results. 138061da546Spatrick ''' 139061da546Spatrick matches = [] 140061da546Spatrick for obj in self.objects: 141061da546Spatrick if obj.name == name and (mtime is None or mtime == obj.date): 142061da546Spatrick matches.append(obj) 143061da546Spatrick return matches 144061da546Spatrick 145061da546Spatrick @classmethod 146061da546Spatrick def dump_header(self, f=sys.stdout): 147061da546Spatrick f.write(' DATE UID GID MODE SIZE NAME\n') 148061da546Spatrick f.write(' ---------- ----- ----- ------ ---------- ' 149061da546Spatrick '--------------\n') 150061da546Spatrick 151061da546Spatrick def get_symdef(self): 152061da546Spatrick def get_uint32(file): 153061da546Spatrick '''Extract a uint32_t from the current file position.''' 154061da546Spatrick v, = struct.unpack('=I', file.read(4)) 155061da546Spatrick return v 156061da546Spatrick 157061da546Spatrick for obj in self.objects: 158061da546Spatrick symdef = [] 159061da546Spatrick if obj.name.startswith("__.SYMDEF"): 160061da546Spatrick self.file.seek(obj.obj_offset, 0) 161061da546Spatrick ranlib_byte_size = get_uint32(self.file) 162061da546Spatrick num_ranlib_structs = ranlib_byte_size/8 163061da546Spatrick str_offset_pairs = [] 164061da546Spatrick for _ in range(num_ranlib_structs): 165061da546Spatrick strx = get_uint32(self.file) 166061da546Spatrick offset = get_uint32(self.file) 167061da546Spatrick str_offset_pairs.append((strx, offset)) 168061da546Spatrick strtab_len = get_uint32(self.file) 169061da546Spatrick strtab = StringTable(self.file.read(strtab_len)) 170061da546Spatrick for s in str_offset_pairs: 171061da546Spatrick symdef.append((strtab.get_string(s[0]), s[1])) 172061da546Spatrick return symdef 173061da546Spatrick 174061da546Spatrick def get_object_dicts(self): 175061da546Spatrick ''' 176061da546Spatrick Returns an array of object dictionaries that contain they following 177061da546Spatrick keys: 178061da546Spatrick 'object': the actual bsd.Object instance 179061da546Spatrick 'symdefs': an array of symbol names that the object contains 180061da546Spatrick as found in the "__.SYMDEF" item in the archive 181061da546Spatrick ''' 182061da546Spatrick symdefs = self.get_symdef() 183061da546Spatrick symdef_dict = {} 184061da546Spatrick if symdefs: 185061da546Spatrick for (name, offset) in symdefs: 186061da546Spatrick if offset in symdef_dict: 187061da546Spatrick object_dict = symdef_dict[offset] 188061da546Spatrick else: 189061da546Spatrick object_dict = { 190061da546Spatrick 'object': self.get_object_at_offset(offset), 191061da546Spatrick 'symdefs': [] 192061da546Spatrick } 193061da546Spatrick symdef_dict[offset] = object_dict 194061da546Spatrick object_dict['symdefs'].append(name) 195061da546Spatrick object_dicts = [] 196061da546Spatrick for offset in sorted(symdef_dict): 197061da546Spatrick object_dicts.append(symdef_dict[offset]) 198061da546Spatrick return object_dicts 199061da546Spatrick 200061da546Spatrick def dump(self, f=sys.stdout, flat=True): 201061da546Spatrick f.write('%s:\n' % self.path) 202061da546Spatrick if flat: 203061da546Spatrick self.dump_header(f=f) 204061da546Spatrick for obj in self.objects: 205061da546Spatrick obj.dump(f=f, flat=flat) 206061da546Spatrick 207061da546Spatrickclass Interactive(cmd.Cmd): 208061da546Spatrick '''Interactive prompt for exploring contents of BSD archive files, type 209061da546Spatrick "help" to see a list of supported commands.''' 210061da546Spatrick image_option_parser = None 211061da546Spatrick 212061da546Spatrick def __init__(self, archives): 213061da546Spatrick cmd.Cmd.__init__(self) 214061da546Spatrick self.use_rawinput = False 215061da546Spatrick self.intro = ('Interactive BSD archive prompt, type "help" to see a ' 216061da546Spatrick 'list of supported commands.') 217061da546Spatrick self.archives = archives 218061da546Spatrick self.prompt = '% ' 219061da546Spatrick 220061da546Spatrick def default(self, line): 221061da546Spatrick '''Catch all for unknown command, which will exit the interpreter.''' 222061da546Spatrick print("unknown command: %s" % line) 223061da546Spatrick return True 224061da546Spatrick 225061da546Spatrick def do_q(self, line): 226061da546Spatrick '''Quit command''' 227061da546Spatrick return True 228061da546Spatrick 229061da546Spatrick def do_quit(self, line): 230061da546Spatrick '''Quit command''' 231061da546Spatrick return True 232061da546Spatrick 233061da546Spatrick def do_extract(self, line): 234061da546Spatrick args = shlex.split(line) 235061da546Spatrick if args: 236061da546Spatrick extracted = False 237061da546Spatrick for object_name in args: 238061da546Spatrick for archive in self.archives: 239061da546Spatrick matches = archive.find(object_name) 240061da546Spatrick if matches: 241061da546Spatrick for object in matches: 242061da546Spatrick object.save(overwrite=False) 243061da546Spatrick extracted = True 244061da546Spatrick if not extracted: 245061da546Spatrick print('error: no object matches "%s" in any archives' % ( 246061da546Spatrick object_name)) 247061da546Spatrick else: 248061da546Spatrick print('error: must specify the name of an object to extract') 249061da546Spatrick 250061da546Spatrick def do_ls(self, line): 251061da546Spatrick args = shlex.split(line) 252061da546Spatrick if args: 253061da546Spatrick for object_name in args: 254061da546Spatrick for archive in self.archives: 255061da546Spatrick matches = archive.find(object_name) 256061da546Spatrick if matches: 257061da546Spatrick for object in matches: 258061da546Spatrick object.dump(flat=False) 259061da546Spatrick else: 260061da546Spatrick print('error: no object matches "%s" in "%s"' % ( 261061da546Spatrick object_name, archive.path)) 262061da546Spatrick else: 263061da546Spatrick for archive in self.archives: 264061da546Spatrick archive.dump(flat=True) 265061da546Spatrick print('') 266061da546Spatrick 267061da546Spatrick 268061da546Spatrick 269061da546Spatrickdef main(): 270061da546Spatrick parser = optparse.OptionParser( 271061da546Spatrick prog='bsd', 272061da546Spatrick description='Utility for BSD archives') 273061da546Spatrick parser.add_option( 274061da546Spatrick '--object', 275061da546Spatrick type='string', 276061da546Spatrick dest='object_name', 277061da546Spatrick default=None, 278061da546Spatrick help=('Specify the name of a object within the BSD archive to get ' 279061da546Spatrick 'information on')) 280061da546Spatrick parser.add_option( 281061da546Spatrick '-s', '--symbol', 282061da546Spatrick type='string', 283061da546Spatrick dest='find_symbol', 284061da546Spatrick default=None, 285061da546Spatrick help=('Specify the name of a symbol within the BSD archive to get ' 286061da546Spatrick 'information on from SYMDEF')) 287061da546Spatrick parser.add_option( 288061da546Spatrick '--symdef', 289061da546Spatrick action='store_true', 290061da546Spatrick dest='symdef', 291061da546Spatrick default=False, 292061da546Spatrick help=('Dump the information in the SYMDEF.')) 293061da546Spatrick parser.add_option( 294061da546Spatrick '-v', '--verbose', 295061da546Spatrick action='store_true', 296061da546Spatrick dest='verbose', 297061da546Spatrick default=False, 298061da546Spatrick help='Enable verbose output') 299061da546Spatrick parser.add_option( 300061da546Spatrick '-e', '--extract', 301061da546Spatrick action='store_true', 302061da546Spatrick dest='extract', 303061da546Spatrick default=False, 304061da546Spatrick help=('Specify this to extract the object specified with the --object ' 305061da546Spatrick 'option. There must be only one object with a matching name or ' 306061da546Spatrick 'the --mtime option must be specified to uniquely identify a ' 307061da546Spatrick 'single object.')) 308061da546Spatrick parser.add_option( 309061da546Spatrick '-m', '--mtime', 310061da546Spatrick type='int', 311061da546Spatrick dest='mtime', 312061da546Spatrick default=None, 313061da546Spatrick help=('Specify the modification time of the object an object. This ' 314061da546Spatrick 'option is used with either the --object or --extract options.')) 315061da546Spatrick parser.add_option( 316061da546Spatrick '-o', '--outfile', 317061da546Spatrick type='string', 318061da546Spatrick dest='outfile', 319061da546Spatrick default=None, 320061da546Spatrick help=('Specify a different name or path for the file to extract when ' 321061da546Spatrick 'using the --extract option. If this option isn\'t specified, ' 322061da546Spatrick 'then the extracted object file will be extracted into the ' 323061da546Spatrick 'current working directory if a file doesn\'t already exist ' 324061da546Spatrick 'with that name.')) 325061da546Spatrick parser.add_option( 326061da546Spatrick '-i', '--interactive', 327061da546Spatrick action='store_true', 328061da546Spatrick dest='interactive', 329061da546Spatrick default=False, 330061da546Spatrick help=('Enter an interactive shell that allows users to interactively ' 331061da546Spatrick 'explore contents of .a files.')) 332061da546Spatrick 333061da546Spatrick (options, args) = parser.parse_args(sys.argv[1:]) 334061da546Spatrick 335061da546Spatrick if options.interactive: 336061da546Spatrick archives = [] 337061da546Spatrick for path in args: 338061da546Spatrick archives.append(Archive(path)) 339061da546Spatrick interpreter = Interactive(archives) 340061da546Spatrick interpreter.cmdloop() 341061da546Spatrick return 342061da546Spatrick 343061da546Spatrick for path in args: 344061da546Spatrick archive = Archive(path) 345061da546Spatrick if options.object_name: 346061da546Spatrick print('%s:\n' % (path)) 347061da546Spatrick matches = archive.find(options.object_name, options.mtime) 348061da546Spatrick if matches: 349061da546Spatrick dump_all = True 350061da546Spatrick if options.extract: 351061da546Spatrick if len(matches) == 1: 352061da546Spatrick dump_all = False 353061da546Spatrick matches[0].save(path=options.outfile, overwrite=False) 354061da546Spatrick else: 355061da546Spatrick print('error: multiple objects match "%s". Specify ' 356061da546Spatrick 'the modification time using --mtime.' % ( 357061da546Spatrick options.object_name)) 358061da546Spatrick if dump_all: 359061da546Spatrick for obj in matches: 360061da546Spatrick obj.dump(flat=False) 361061da546Spatrick else: 362061da546Spatrick print('error: object "%s" not found in archive' % ( 363061da546Spatrick options.object_name)) 364061da546Spatrick elif options.find_symbol: 365061da546Spatrick symdefs = archive.get_symdef() 366061da546Spatrick if symdefs: 367061da546Spatrick success = False 368061da546Spatrick for (name, offset) in symdefs: 369061da546Spatrick obj = archive.get_object_at_offset(offset) 370061da546Spatrick if name == options.find_symbol: 371061da546Spatrick print('Found "%s" in:' % (options.find_symbol)) 372061da546Spatrick obj.dump(flat=False) 373061da546Spatrick success = True 374061da546Spatrick if not success: 375061da546Spatrick print('Didn\'t find "%s" in any objects' % ( 376061da546Spatrick options.find_symbol)) 377061da546Spatrick else: 378061da546Spatrick print("error: no __.SYMDEF was found") 379061da546Spatrick elif options.symdef: 380061da546Spatrick object_dicts = archive.get_object_dicts() 381061da546Spatrick for object_dict in object_dicts: 382061da546Spatrick object_dict['object'].dump(flat=False) 383061da546Spatrick print("symbols:") 384061da546Spatrick for name in object_dict['symdefs']: 385061da546Spatrick print(" %s" % (name)) 386061da546Spatrick else: 387061da546Spatrick archive.dump(flat=not options.verbose) 388061da546Spatrick 389061da546Spatrick 390061da546Spatrickif __name__ == '__main__': 391061da546Spatrick main() 392061da546Spatrick 393061da546Spatrick 394061da546Spatrickdef print_mtime_error(result, dmap_mtime, actual_mtime): 395061da546Spatrick print("error: modification time in debug map (%#08.8x) doesn't " 396061da546Spatrick "match the .o file modification time (%#08.8x)" % ( 397061da546Spatrick dmap_mtime, actual_mtime), file=result) 398061da546Spatrick 399061da546Spatrick 400061da546Spatrickdef print_file_missing_error(result, path): 401061da546Spatrick print("error: file \"%s\" doesn't exist" % (path), file=result) 402061da546Spatrick 403061da546Spatrick 404061da546Spatrickdef print_multiple_object_matches(result, object_name, mtime, matches): 405061da546Spatrick print("error: multiple matches for object '%s' with with " 406061da546Spatrick "modification time %#08.8x:" % (object_name, mtime), file=result) 407061da546Spatrick Archive.dump_header(f=result) 408061da546Spatrick for match in matches: 409061da546Spatrick match.dump(f=result, flat=True) 410061da546Spatrick 411061da546Spatrick 412061da546Spatrickdef print_archive_object_error(result, object_name, mtime, archive): 413061da546Spatrick matches = archive.find(object_name, f=result) 414061da546Spatrick if len(matches) > 0: 415061da546Spatrick print("error: no objects have a modification time that " 416061da546Spatrick "matches %#08.8x for '%s'. Potential matches:" % ( 417061da546Spatrick mtime, object_name), file=result) 418061da546Spatrick Archive.dump_header(f=result) 419061da546Spatrick for match in matches: 420061da546Spatrick match.dump(f=result, flat=True) 421061da546Spatrick else: 422061da546Spatrick print("error: no object named \"%s\" found in archive:" % ( 423061da546Spatrick object_name), file=result) 424061da546Spatrick Archive.dump_header(f=result) 425061da546Spatrick for match in archive.objects: 426061da546Spatrick match.dump(f=result, flat=True) 427061da546Spatrick # archive.dump(f=result, flat=True) 428061da546Spatrick 429061da546Spatrick 430061da546Spatrickclass VerifyDebugMapCommand: 431061da546Spatrick name = "verify-debug-map-objects" 432061da546Spatrick 433061da546Spatrick def create_options(self): 434061da546Spatrick usage = "usage: %prog [options]" 435061da546Spatrick description = '''This command reports any .o files that are missing 436061da546Spatrickor whose modification times don't match in the debug map of an executable.''' 437061da546Spatrick 438061da546Spatrick self.parser = optparse.OptionParser( 439061da546Spatrick description=description, 440061da546Spatrick prog=self.name, 441061da546Spatrick usage=usage, 442061da546Spatrick add_help_option=False) 443061da546Spatrick 444061da546Spatrick self.parser.add_option( 445061da546Spatrick '-e', '--errors', 446061da546Spatrick action='store_true', 447061da546Spatrick dest='errors', 448061da546Spatrick default=False, 449061da546Spatrick help="Only show errors") 450061da546Spatrick 451061da546Spatrick def get_short_help(self): 452061da546Spatrick return "Verify debug map object files." 453061da546Spatrick 454061da546Spatrick def get_long_help(self): 455061da546Spatrick return self.help_string 456061da546Spatrick 457061da546Spatrick def __init__(self, debugger, unused): 458061da546Spatrick self.create_options() 459061da546Spatrick self.help_string = self.parser.format_help() 460061da546Spatrick 461061da546Spatrick def __call__(self, debugger, command, exe_ctx, result): 462061da546Spatrick import lldb 463061da546Spatrick # Use the Shell Lexer to properly parse up command options just like a 464061da546Spatrick # shell would 465061da546Spatrick command_args = shlex.split(command) 466061da546Spatrick 467061da546Spatrick try: 468061da546Spatrick (options, args) = self.parser.parse_args(command_args) 469061da546Spatrick except: 470061da546Spatrick result.SetError("option parsing failed") 471061da546Spatrick return 472061da546Spatrick 473061da546Spatrick # Always get program state from the SBExecutionContext passed in 474061da546Spatrick target = exe_ctx.GetTarget() 475061da546Spatrick if not target.IsValid(): 476061da546Spatrick result.SetError("invalid target") 477061da546Spatrick return 478061da546Spatrick archives = {} 479061da546Spatrick for module_spec in args: 480061da546Spatrick module = target.module[module_spec] 481061da546Spatrick if not (module and module.IsValid()): 482061da546Spatrick result.SetError('error: invalid module specification: "%s". ' 483061da546Spatrick 'Specify the full path, basename, or UUID of ' 484061da546Spatrick 'a module ' % (module_spec)) 485061da546Spatrick return 486061da546Spatrick num_symbols = module.GetNumSymbols() 487061da546Spatrick num_errors = 0 488061da546Spatrick for i in range(num_symbols): 489061da546Spatrick symbol = module.GetSymbolAtIndex(i) 490061da546Spatrick if symbol.GetType() != lldb.eSymbolTypeObjectFile: 491061da546Spatrick continue 492061da546Spatrick path = symbol.GetName() 493061da546Spatrick if not path: 494061da546Spatrick continue 495061da546Spatrick # Extract the value of the symbol by dumping the 496061da546Spatrick # symbol. The value is the mod time. 497061da546Spatrick dmap_mtime = int(str(symbol).split('value = ') 498061da546Spatrick [1].split(',')[0], 16) 499061da546Spatrick if not options.errors: 500061da546Spatrick print('%s' % (path), file=result) 501061da546Spatrick if os.path.exists(path): 502061da546Spatrick actual_mtime = int(os.stat(path).st_mtime) 503061da546Spatrick if dmap_mtime != actual_mtime: 504061da546Spatrick num_errors += 1 505061da546Spatrick if options.errors: 506061da546Spatrick print('%s' % (path), end=' ', file=result) 507061da546Spatrick print_mtime_error(result, dmap_mtime, 508061da546Spatrick actual_mtime) 509061da546Spatrick elif path[-1] == ')': 510061da546Spatrick (archive_path, object_name) = path[0:-1].split('(') 511061da546Spatrick if not archive_path and not object_name: 512061da546Spatrick num_errors += 1 513061da546Spatrick if options.errors: 514061da546Spatrick print('%s' % (path), end=' ', file=result) 515061da546Spatrick print_file_missing_error(path) 516061da546Spatrick continue 517061da546Spatrick if not os.path.exists(archive_path): 518061da546Spatrick num_errors += 1 519061da546Spatrick if options.errors: 520061da546Spatrick print('%s' % (path), end=' ', file=result) 521061da546Spatrick print_file_missing_error(archive_path) 522061da546Spatrick continue 523061da546Spatrick if archive_path in archives: 524061da546Spatrick archive = archives[archive_path] 525061da546Spatrick else: 526061da546Spatrick archive = Archive(archive_path) 527061da546Spatrick archives[archive_path] = archive 528061da546Spatrick matches = archive.find(object_name, dmap_mtime) 529061da546Spatrick num_matches = len(matches) 530061da546Spatrick if num_matches == 1: 531061da546Spatrick print('1 match', file=result) 532061da546Spatrick obj = matches[0] 533061da546Spatrick if obj.date != dmap_mtime: 534061da546Spatrick num_errors += 1 535061da546Spatrick if options.errors: 536061da546Spatrick print('%s' % (path), end=' ', file=result) 537061da546Spatrick print_mtime_error(result, dmap_mtime, obj.date) 538061da546Spatrick elif num_matches == 0: 539061da546Spatrick num_errors += 1 540061da546Spatrick if options.errors: 541061da546Spatrick print('%s' % (path), end=' ', file=result) 542061da546Spatrick print_archive_object_error(result, object_name, 543061da546Spatrick dmap_mtime, archive) 544061da546Spatrick elif num_matches > 1: 545061da546Spatrick num_errors += 1 546061da546Spatrick if options.errors: 547061da546Spatrick print('%s' % (path), end=' ', file=result) 548061da546Spatrick print_multiple_object_matches(result, 549061da546Spatrick object_name, 550061da546Spatrick dmap_mtime, matches) 551061da546Spatrick if num_errors > 0: 552061da546Spatrick print("%u errors found" % (num_errors), file=result) 553061da546Spatrick else: 554061da546Spatrick print("No errors detected in debug map", file=result) 555061da546Spatrick 556061da546Spatrick 557061da546Spatrickdef __lldb_init_module(debugger, dict): 558061da546Spatrick # This initializer is being run from LLDB in the embedded command 559061da546Spatrick # interpreter. 560061da546Spatrick # Add any commands contained in this module to LLDB 561061da546Spatrick debugger.HandleCommand( 562061da546Spatrick 'command script add -c %s.VerifyDebugMapCommand %s' % ( 563061da546Spatrick __name__, VerifyDebugMapCommand.name)) 564061da546Spatrick print('The "%s" command has been installed, type "help %s" for detailed ' 565061da546Spatrick 'help.' % (VerifyDebugMapCommand.name, VerifyDebugMapCommand.name)) 566