1*515bc8c1Sserge-sans-paille#!/usr/bin/env python 2525cd59fSSerge Gueltonfrom __future__ import print_function 3de01668bSGreg Clayton 495c23f66SGreg Claytonimport cmd 5de01668bSGreg Claytonimport optparse 6de01668bSGreg Claytonimport os 7de01668bSGreg Claytonimport shlex 8de01668bSGreg Claytonimport struct 9de01668bSGreg Claytonimport sys 10de01668bSGreg Clayton 11de01668bSGreg ClaytonARMAG = "!<arch>\n" 12de01668bSGreg ClaytonSARMAG = 8 13de01668bSGreg ClaytonARFMAG = "`\n" 14de01668bSGreg ClaytonAR_EFMT1 = "#1/" 15de01668bSGreg Clayton 16de01668bSGreg Clayton 17de01668bSGreg Claytondef memdump(src, bytes_per_line=16, address=0): 18de01668bSGreg Clayton FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' 19de01668bSGreg Clayton for x in range(256)]) 20de01668bSGreg Clayton for i in range(0, len(src), bytes_per_line): 21de01668bSGreg Clayton s = src[i:i+bytes_per_line] 22de01668bSGreg Clayton hex_bytes = ' '.join(["%02x" % (ord(x)) for x in s]) 23de01668bSGreg Clayton ascii = s.translate(FILTER) 24de01668bSGreg Clayton print("%#08.8x: %-*s %s" % (address+i, bytes_per_line*3, hex_bytes, 25de01668bSGreg Clayton ascii)) 26de01668bSGreg Clayton 27de01668bSGreg Clayton 28de01668bSGreg Claytonclass Object(object): 29de01668bSGreg Clayton def __init__(self, file): 30de01668bSGreg Clayton def read_str(file, str_len): 31de01668bSGreg Clayton return file.read(str_len).rstrip('\0 ') 32de01668bSGreg Clayton 33de01668bSGreg Clayton def read_int(file, str_len, base): 34de01668bSGreg Clayton return int(read_str(file, str_len), base) 35de01668bSGreg Clayton 36de01668bSGreg Clayton self.offset = file.tell() 37de01668bSGreg Clayton self.file = file 38de01668bSGreg Clayton self.name = read_str(file, 16) 39de01668bSGreg Clayton self.date = read_int(file, 12, 10) 40de01668bSGreg Clayton self.uid = read_int(file, 6, 10) 41de01668bSGreg Clayton self.gid = read_int(file, 6, 10) 42de01668bSGreg Clayton self.mode = read_int(file, 8, 8) 43de01668bSGreg Clayton self.size = read_int(file, 10, 10) 44de01668bSGreg Clayton if file.read(2) != ARFMAG: 45de01668bSGreg Clayton raise ValueError('invalid BSD object at offset %#08.8x' % ( 46de01668bSGreg Clayton self.offset)) 47de01668bSGreg Clayton # If we have an extended name read it. Extended names start with 48de01668bSGreg Clayton name_len = 0 49de01668bSGreg Clayton if self.name.startswith(AR_EFMT1): 50de01668bSGreg Clayton name_len = int(self.name[len(AR_EFMT1):], 10) 51de01668bSGreg Clayton self.name = read_str(file, name_len) 52de01668bSGreg Clayton self.obj_offset = file.tell() 53de01668bSGreg Clayton self.obj_size = self.size - name_len 54de01668bSGreg Clayton file.seek(self.obj_size, 1) 55de01668bSGreg Clayton 56de01668bSGreg Clayton def dump(self, f=sys.stdout, flat=True): 57de01668bSGreg Clayton if flat: 58de01668bSGreg Clayton f.write('%#08.8x: %#08.8x %5u %5u %6o %#08.8x %s\n' % (self.offset, 59de01668bSGreg Clayton self.date, self.uid, self.gid, self.mode, self.size, 60de01668bSGreg Clayton self.name)) 61de01668bSGreg Clayton else: 62de01668bSGreg Clayton f.write('%#08.8x: \n' % self.offset) 63de01668bSGreg Clayton f.write(' name = "%s"\n' % self.name) 64de01668bSGreg Clayton f.write(' date = %#08.8x\n' % self.date) 65de01668bSGreg Clayton f.write(' uid = %i\n' % self.uid) 66de01668bSGreg Clayton f.write(' gid = %i\n' % self.gid) 67de01668bSGreg Clayton f.write(' mode = %o\n' % self.mode) 68de01668bSGreg Clayton f.write(' size = %#08.8x\n' % (self.size)) 69de01668bSGreg Clayton self.file.seek(self.obj_offset, 0) 70de01668bSGreg Clayton first_bytes = self.file.read(4) 71de01668bSGreg Clayton f.write('bytes = ') 72de01668bSGreg Clayton memdump(first_bytes) 73de01668bSGreg Clayton 74de01668bSGreg Clayton def get_bytes(self): 75de01668bSGreg Clayton saved_pos = self.file.tell() 76de01668bSGreg Clayton self.file.seek(self.obj_offset, 0) 77de01668bSGreg Clayton bytes = self.file.read(self.obj_size) 78de01668bSGreg Clayton self.file.seek(saved_pos, 0) 79de01668bSGreg Clayton return bytes 80de01668bSGreg Clayton 8195c23f66SGreg Clayton def save(self, path=None, overwrite=False): 8295c23f66SGreg Clayton ''' 8395c23f66SGreg Clayton Save the contents of the object to disk using 'path' argument as 8495c23f66SGreg Clayton the path, or save it to the current working directory using the 8595c23f66SGreg Clayton object name. 8695c23f66SGreg Clayton ''' 8795c23f66SGreg Clayton 8895c23f66SGreg Clayton if path is None: 8995c23f66SGreg Clayton path = self.name 9095c23f66SGreg Clayton if not overwrite and os.path.exists(path): 9195c23f66SGreg Clayton print('error: outfile "%s" already exists' % (path)) 9295c23f66SGreg Clayton return 9395c23f66SGreg Clayton print('Saving "%s" to "%s"...' % (self.name, path)) 9495c23f66SGreg Clayton with open(path, 'w') as f: 9595c23f66SGreg Clayton f.write(self.get_bytes()) 9695c23f66SGreg Clayton 97de01668bSGreg Clayton 98de01668bSGreg Claytonclass StringTable(object): 99de01668bSGreg Clayton def __init__(self, bytes): 100de01668bSGreg Clayton self.bytes = bytes 101de01668bSGreg Clayton 102de01668bSGreg Clayton def get_string(self, offset): 103de01668bSGreg Clayton length = len(self.bytes) 104de01668bSGreg Clayton if offset >= length: 105de01668bSGreg Clayton return None 106de01668bSGreg Clayton return self.bytes[offset:self.bytes.find('\0', offset)] 107de01668bSGreg Clayton 108de01668bSGreg Clayton 109de01668bSGreg Claytonclass Archive(object): 110de01668bSGreg Clayton def __init__(self, path): 111de01668bSGreg Clayton self.path = path 112de01668bSGreg Clayton self.file = open(path, 'r') 113de01668bSGreg Clayton self.objects = [] 114de01668bSGreg Clayton self.offset_to_object = {} 115de01668bSGreg Clayton if self.file.read(SARMAG) != ARMAG: 116de01668bSGreg Clayton print("error: file isn't a BSD archive") 117de01668bSGreg Clayton while True: 118de01668bSGreg Clayton try: 119de01668bSGreg Clayton self.objects.append(Object(self.file)) 120de01668bSGreg Clayton except ValueError: 121de01668bSGreg Clayton break 122de01668bSGreg Clayton 123de01668bSGreg Clayton def get_object_at_offset(self, offset): 124de01668bSGreg Clayton if offset in self.offset_to_object: 125de01668bSGreg Clayton return self.offset_to_object[offset] 126de01668bSGreg Clayton for obj in self.objects: 127de01668bSGreg Clayton if obj.offset == offset: 128de01668bSGreg Clayton self.offset_to_object[offset] = obj 129de01668bSGreg Clayton return obj 130de01668bSGreg Clayton return None 131de01668bSGreg Clayton 132de01668bSGreg Clayton def find(self, name, mtime=None, f=sys.stdout): 133de01668bSGreg Clayton ''' 134de01668bSGreg Clayton Find an object(s) by name with optional modification time. There 135de01668bSGreg Clayton can be multple objects with the same name inside and possibly with 136de01668bSGreg Clayton the same modification time within a BSD archive so clients must be 137de01668bSGreg Clayton prepared to get multiple results. 138de01668bSGreg Clayton ''' 139de01668bSGreg Clayton matches = [] 140de01668bSGreg Clayton for obj in self.objects: 141de01668bSGreg Clayton if obj.name == name and (mtime is None or mtime == obj.date): 142de01668bSGreg Clayton matches.append(obj) 143de01668bSGreg Clayton return matches 144de01668bSGreg Clayton 145de01668bSGreg Clayton @classmethod 146de01668bSGreg Clayton def dump_header(self, f=sys.stdout): 147de01668bSGreg Clayton f.write(' DATE UID GID MODE SIZE NAME\n') 148de01668bSGreg Clayton f.write(' ---------- ----- ----- ------ ---------- ' 149de01668bSGreg Clayton '--------------\n') 150de01668bSGreg Clayton 151de01668bSGreg Clayton def get_symdef(self): 152de01668bSGreg Clayton def get_uint32(file): 153de01668bSGreg Clayton '''Extract a uint32_t from the current file position.''' 154de01668bSGreg Clayton v, = struct.unpack('=I', file.read(4)) 155de01668bSGreg Clayton return v 156de01668bSGreg Clayton 157de01668bSGreg Clayton for obj in self.objects: 158de01668bSGreg Clayton symdef = [] 159de01668bSGreg Clayton if obj.name.startswith("__.SYMDEF"): 160de01668bSGreg Clayton self.file.seek(obj.obj_offset, 0) 161de01668bSGreg Clayton ranlib_byte_size = get_uint32(self.file) 162de01668bSGreg Clayton num_ranlib_structs = ranlib_byte_size/8 163de01668bSGreg Clayton str_offset_pairs = [] 164de01668bSGreg Clayton for _ in range(num_ranlib_structs): 165de01668bSGreg Clayton strx = get_uint32(self.file) 166de01668bSGreg Clayton offset = get_uint32(self.file) 167de01668bSGreg Clayton str_offset_pairs.append((strx, offset)) 168de01668bSGreg Clayton strtab_len = get_uint32(self.file) 169de01668bSGreg Clayton strtab = StringTable(self.file.read(strtab_len)) 170de01668bSGreg Clayton for s in str_offset_pairs: 171de01668bSGreg Clayton symdef.append((strtab.get_string(s[0]), s[1])) 172de01668bSGreg Clayton return symdef 173de01668bSGreg Clayton 174de01668bSGreg Clayton def get_object_dicts(self): 175de01668bSGreg Clayton ''' 176de01668bSGreg Clayton Returns an array of object dictionaries that contain they following 177de01668bSGreg Clayton keys: 178de01668bSGreg Clayton 'object': the actual bsd.Object instance 179de01668bSGreg Clayton 'symdefs': an array of symbol names that the object contains 180de01668bSGreg Clayton as found in the "__.SYMDEF" item in the archive 181de01668bSGreg Clayton ''' 182de01668bSGreg Clayton symdefs = self.get_symdef() 183de01668bSGreg Clayton symdef_dict = {} 184de01668bSGreg Clayton if symdefs: 185de01668bSGreg Clayton for (name, offset) in symdefs: 186de01668bSGreg Clayton if offset in symdef_dict: 187de01668bSGreg Clayton object_dict = symdef_dict[offset] 188de01668bSGreg Clayton else: 189de01668bSGreg Clayton object_dict = { 190de01668bSGreg Clayton 'object': self.get_object_at_offset(offset), 191de01668bSGreg Clayton 'symdefs': [] 192de01668bSGreg Clayton } 193de01668bSGreg Clayton symdef_dict[offset] = object_dict 194de01668bSGreg Clayton object_dict['symdefs'].append(name) 195de01668bSGreg Clayton object_dicts = [] 196de01668bSGreg Clayton for offset in sorted(symdef_dict): 197de01668bSGreg Clayton object_dicts.append(symdef_dict[offset]) 198de01668bSGreg Clayton return object_dicts 199de01668bSGreg Clayton 200de01668bSGreg Clayton def dump(self, f=sys.stdout, flat=True): 201de01668bSGreg Clayton f.write('%s:\n' % self.path) 202de01668bSGreg Clayton if flat: 203de01668bSGreg Clayton self.dump_header(f=f) 204de01668bSGreg Clayton for obj in self.objects: 205de01668bSGreg Clayton obj.dump(f=f, flat=flat) 206de01668bSGreg Clayton 20795c23f66SGreg Claytonclass Interactive(cmd.Cmd): 20895c23f66SGreg Clayton '''Interactive prompt for exploring contents of BSD archive files, type 20995c23f66SGreg Clayton "help" to see a list of supported commands.''' 21095c23f66SGreg Clayton image_option_parser = None 21195c23f66SGreg Clayton 21295c23f66SGreg Clayton def __init__(self, archives): 21395c23f66SGreg Clayton cmd.Cmd.__init__(self) 21495c23f66SGreg Clayton self.use_rawinput = False 21595c23f66SGreg Clayton self.intro = ('Interactive BSD archive prompt, type "help" to see a ' 21695c23f66SGreg Clayton 'list of supported commands.') 21795c23f66SGreg Clayton self.archives = archives 21895c23f66SGreg Clayton self.prompt = '% ' 21995c23f66SGreg Clayton 22095c23f66SGreg Clayton def default(self, line): 22195c23f66SGreg Clayton '''Catch all for unknown command, which will exit the interpreter.''' 22295c23f66SGreg Clayton print("unknown command: %s" % line) 22395c23f66SGreg Clayton return True 22495c23f66SGreg Clayton 22595c23f66SGreg Clayton def do_q(self, line): 22695c23f66SGreg Clayton '''Quit command''' 22795c23f66SGreg Clayton return True 22895c23f66SGreg Clayton 22995c23f66SGreg Clayton def do_quit(self, line): 23095c23f66SGreg Clayton '''Quit command''' 23195c23f66SGreg Clayton return True 23295c23f66SGreg Clayton 23395c23f66SGreg Clayton def do_extract(self, line): 23495c23f66SGreg Clayton args = shlex.split(line) 23595c23f66SGreg Clayton if args: 23695c23f66SGreg Clayton extracted = False 23795c23f66SGreg Clayton for object_name in args: 23895c23f66SGreg Clayton for archive in self.archives: 23995c23f66SGreg Clayton matches = archive.find(object_name) 24095c23f66SGreg Clayton if matches: 24195c23f66SGreg Clayton for object in matches: 24295c23f66SGreg Clayton object.save(overwrite=False) 24395c23f66SGreg Clayton extracted = True 24495c23f66SGreg Clayton if not extracted: 24595c23f66SGreg Clayton print('error: no object matches "%s" in any archives' % ( 24695c23f66SGreg Clayton object_name)) 24795c23f66SGreg Clayton else: 24895c23f66SGreg Clayton print('error: must specify the name of an object to extract') 24995c23f66SGreg Clayton 25095c23f66SGreg Clayton def do_ls(self, line): 25195c23f66SGreg Clayton args = shlex.split(line) 25295c23f66SGreg Clayton if args: 25395c23f66SGreg Clayton for object_name in args: 25495c23f66SGreg Clayton for archive in self.archives: 25595c23f66SGreg Clayton matches = archive.find(object_name) 25695c23f66SGreg Clayton if matches: 25795c23f66SGreg Clayton for object in matches: 25895c23f66SGreg Clayton object.dump(flat=False) 25995c23f66SGreg Clayton else: 26095c23f66SGreg Clayton print('error: no object matches "%s" in "%s"' % ( 26195c23f66SGreg Clayton object_name, archive.path)) 26295c23f66SGreg Clayton else: 26395c23f66SGreg Clayton for archive in self.archives: 26495c23f66SGreg Clayton archive.dump(flat=True) 26595c23f66SGreg Clayton print('') 26695c23f66SGreg Clayton 26795c23f66SGreg Clayton 268de01668bSGreg Clayton 269de01668bSGreg Claytondef main(): 270de01668bSGreg Clayton parser = optparse.OptionParser( 271de01668bSGreg Clayton prog='bsd', 272de01668bSGreg Clayton description='Utility for BSD archives') 273de01668bSGreg Clayton parser.add_option( 274de01668bSGreg Clayton '--object', 275de01668bSGreg Clayton type='string', 276de01668bSGreg Clayton dest='object_name', 277de01668bSGreg Clayton default=None, 278de01668bSGreg Clayton help=('Specify the name of a object within the BSD archive to get ' 279de01668bSGreg Clayton 'information on')) 280de01668bSGreg Clayton parser.add_option( 281de01668bSGreg Clayton '-s', '--symbol', 282de01668bSGreg Clayton type='string', 283de01668bSGreg Clayton dest='find_symbol', 284de01668bSGreg Clayton default=None, 285de01668bSGreg Clayton help=('Specify the name of a symbol within the BSD archive to get ' 286de01668bSGreg Clayton 'information on from SYMDEF')) 287de01668bSGreg Clayton parser.add_option( 288de01668bSGreg Clayton '--symdef', 289de01668bSGreg Clayton action='store_true', 290de01668bSGreg Clayton dest='symdef', 291de01668bSGreg Clayton default=False, 292de01668bSGreg Clayton help=('Dump the information in the SYMDEF.')) 293de01668bSGreg Clayton parser.add_option( 294de01668bSGreg Clayton '-v', '--verbose', 295de01668bSGreg Clayton action='store_true', 296de01668bSGreg Clayton dest='verbose', 297de01668bSGreg Clayton default=False, 298de01668bSGreg Clayton help='Enable verbose output') 299de01668bSGreg Clayton parser.add_option( 300de01668bSGreg Clayton '-e', '--extract', 301de01668bSGreg Clayton action='store_true', 302de01668bSGreg Clayton dest='extract', 303de01668bSGreg Clayton default=False, 304de01668bSGreg Clayton help=('Specify this to extract the object specified with the --object ' 305de01668bSGreg Clayton 'option. There must be only one object with a matching name or ' 306de01668bSGreg Clayton 'the --mtime option must be specified to uniquely identify a ' 307de01668bSGreg Clayton 'single object.')) 308de01668bSGreg Clayton parser.add_option( 309de01668bSGreg Clayton '-m', '--mtime', 310de01668bSGreg Clayton type='int', 311de01668bSGreg Clayton dest='mtime', 312de01668bSGreg Clayton default=None, 313de01668bSGreg Clayton help=('Specify the modification time of the object an object. This ' 314de01668bSGreg Clayton 'option is used with either the --object or --extract options.')) 315de01668bSGreg Clayton parser.add_option( 316de01668bSGreg Clayton '-o', '--outfile', 317de01668bSGreg Clayton type='string', 318de01668bSGreg Clayton dest='outfile', 319de01668bSGreg Clayton default=None, 320de01668bSGreg Clayton help=('Specify a different name or path for the file to extract when ' 321de01668bSGreg Clayton 'using the --extract option. If this option isn\'t specified, ' 322de01668bSGreg Clayton 'then the extracted object file will be extracted into the ' 323de01668bSGreg Clayton 'current working directory if a file doesn\'t already exist ' 324de01668bSGreg Clayton 'with that name.')) 32595c23f66SGreg Clayton parser.add_option( 32695c23f66SGreg Clayton '-i', '--interactive', 32795c23f66SGreg Clayton action='store_true', 32895c23f66SGreg Clayton dest='interactive', 32995c23f66SGreg Clayton default=False, 33095c23f66SGreg Clayton help=('Enter an interactive shell that allows users to interactively ' 33195c23f66SGreg Clayton 'explore contents of .a files.')) 332de01668bSGreg Clayton 333de01668bSGreg Clayton (options, args) = parser.parse_args(sys.argv[1:]) 334de01668bSGreg Clayton 33595c23f66SGreg Clayton if options.interactive: 33695c23f66SGreg Clayton archives = [] 33795c23f66SGreg Clayton for path in args: 33895c23f66SGreg Clayton archives.append(Archive(path)) 33995c23f66SGreg Clayton interpreter = Interactive(archives) 34095c23f66SGreg Clayton interpreter.cmdloop() 34195c23f66SGreg Clayton return 34295c23f66SGreg Clayton 343de01668bSGreg Clayton for path in args: 344de01668bSGreg Clayton archive = Archive(path) 345de01668bSGreg Clayton if options.object_name: 346de01668bSGreg Clayton print('%s:\n' % (path)) 347de01668bSGreg Clayton matches = archive.find(options.object_name, options.mtime) 348de01668bSGreg Clayton if matches: 349de01668bSGreg Clayton dump_all = True 350de01668bSGreg Clayton if options.extract: 351de01668bSGreg Clayton if len(matches) == 1: 352de01668bSGreg Clayton dump_all = False 35395c23f66SGreg Clayton matches[0].save(path=options.outfile, overwrite=False) 354de01668bSGreg Clayton else: 355de01668bSGreg Clayton print('error: multiple objects match "%s". Specify ' 356de01668bSGreg Clayton 'the modification time using --mtime.' % ( 357de01668bSGreg Clayton options.object_name)) 358de01668bSGreg Clayton if dump_all: 359de01668bSGreg Clayton for obj in matches: 360de01668bSGreg Clayton obj.dump(flat=False) 361de01668bSGreg Clayton else: 362de01668bSGreg Clayton print('error: object "%s" not found in archive' % ( 363de01668bSGreg Clayton options.object_name)) 364de01668bSGreg Clayton elif options.find_symbol: 365de01668bSGreg Clayton symdefs = archive.get_symdef() 366de01668bSGreg Clayton if symdefs: 367de01668bSGreg Clayton success = False 368de01668bSGreg Clayton for (name, offset) in symdefs: 369de01668bSGreg Clayton obj = archive.get_object_at_offset(offset) 370de01668bSGreg Clayton if name == options.find_symbol: 371de01668bSGreg Clayton print('Found "%s" in:' % (options.find_symbol)) 372de01668bSGreg Clayton obj.dump(flat=False) 373de01668bSGreg Clayton success = True 374de01668bSGreg Clayton if not success: 375de01668bSGreg Clayton print('Didn\'t find "%s" in any objects' % ( 376de01668bSGreg Clayton options.find_symbol)) 377de01668bSGreg Clayton else: 378de01668bSGreg Clayton print("error: no __.SYMDEF was found") 379de01668bSGreg Clayton elif options.symdef: 380de01668bSGreg Clayton object_dicts = archive.get_object_dicts() 381de01668bSGreg Clayton for object_dict in object_dicts: 382de01668bSGreg Clayton object_dict['object'].dump(flat=False) 383de01668bSGreg Clayton print("symbols:") 384de01668bSGreg Clayton for name in object_dict['symdefs']: 385de01668bSGreg Clayton print(" %s" % (name)) 386de01668bSGreg Clayton else: 387de01668bSGreg Clayton archive.dump(flat=not options.verbose) 388de01668bSGreg Clayton 389de01668bSGreg Clayton 390de01668bSGreg Claytonif __name__ == '__main__': 391de01668bSGreg Clayton main() 392de01668bSGreg Clayton 393de01668bSGreg Clayton 394de01668bSGreg Claytondef print_mtime_error(result, dmap_mtime, actual_mtime): 395525cd59fSSerge Guelton print("error: modification time in debug map (%#08.8x) doesn't " 396de01668bSGreg Clayton "match the .o file modification time (%#08.8x)" % ( 397525cd59fSSerge Guelton dmap_mtime, actual_mtime), file=result) 398de01668bSGreg Clayton 399de01668bSGreg Clayton 400de01668bSGreg Claytondef print_file_missing_error(result, path): 401525cd59fSSerge Guelton print("error: file \"%s\" doesn't exist" % (path), file=result) 402de01668bSGreg Clayton 403de01668bSGreg Clayton 404de01668bSGreg Claytondef print_multiple_object_matches(result, object_name, mtime, matches): 405525cd59fSSerge Guelton print("error: multiple matches for object '%s' with with " 406525cd59fSSerge Guelton "modification time %#08.8x:" % (object_name, mtime), file=result) 407de01668bSGreg Clayton Archive.dump_header(f=result) 408de01668bSGreg Clayton for match in matches: 409de01668bSGreg Clayton match.dump(f=result, flat=True) 410de01668bSGreg Clayton 411de01668bSGreg Clayton 412de01668bSGreg Claytondef print_archive_object_error(result, object_name, mtime, archive): 413de01668bSGreg Clayton matches = archive.find(object_name, f=result) 414de01668bSGreg Clayton if len(matches) > 0: 415525cd59fSSerge Guelton print("error: no objects have a modification time that " 416de01668bSGreg Clayton "matches %#08.8x for '%s'. Potential matches:" % ( 417525cd59fSSerge Guelton mtime, object_name), file=result) 418de01668bSGreg Clayton Archive.dump_header(f=result) 419de01668bSGreg Clayton for match in matches: 420de01668bSGreg Clayton match.dump(f=result, flat=True) 421de01668bSGreg Clayton else: 422525cd59fSSerge Guelton print("error: no object named \"%s\" found in archive:" % ( 423525cd59fSSerge Guelton object_name), file=result) 424de01668bSGreg Clayton Archive.dump_header(f=result) 425de01668bSGreg Clayton for match in archive.objects: 426de01668bSGreg Clayton match.dump(f=result, flat=True) 427de01668bSGreg Clayton # archive.dump(f=result, flat=True) 428de01668bSGreg Clayton 429de01668bSGreg Clayton 430de01668bSGreg Claytonclass VerifyDebugMapCommand: 431de01668bSGreg Clayton name = "verify-debug-map-objects" 432de01668bSGreg Clayton 433de01668bSGreg Clayton def create_options(self): 434de01668bSGreg Clayton usage = "usage: %prog [options]" 435de01668bSGreg Clayton description = '''This command reports any .o files that are missing 436de01668bSGreg Claytonor whose modification times don't match in the debug map of an executable.''' 437de01668bSGreg Clayton 438de01668bSGreg Clayton self.parser = optparse.OptionParser( 439de01668bSGreg Clayton description=description, 440de01668bSGreg Clayton prog=self.name, 441de01668bSGreg Clayton usage=usage, 442de01668bSGreg Clayton add_help_option=False) 443de01668bSGreg Clayton 444de01668bSGreg Clayton self.parser.add_option( 445de01668bSGreg Clayton '-e', '--errors', 446de01668bSGreg Clayton action='store_true', 447de01668bSGreg Clayton dest='errors', 448de01668bSGreg Clayton default=False, 449de01668bSGreg Clayton help="Only show errors") 450de01668bSGreg Clayton 451de01668bSGreg Clayton def get_short_help(self): 452de01668bSGreg Clayton return "Verify debug map object files." 453de01668bSGreg Clayton 454de01668bSGreg Clayton def get_long_help(self): 455de01668bSGreg Clayton return self.help_string 456de01668bSGreg Clayton 457de01668bSGreg Clayton def __init__(self, debugger, unused): 458de01668bSGreg Clayton self.create_options() 459de01668bSGreg Clayton self.help_string = self.parser.format_help() 460de01668bSGreg Clayton 461de01668bSGreg Clayton def __call__(self, debugger, command, exe_ctx, result): 462de01668bSGreg Clayton import lldb 463de01668bSGreg Clayton # Use the Shell Lexer to properly parse up command options just like a 464de01668bSGreg Clayton # shell would 465de01668bSGreg Clayton command_args = shlex.split(command) 466de01668bSGreg Clayton 467de01668bSGreg Clayton try: 468de01668bSGreg Clayton (options, args) = self.parser.parse_args(command_args) 469de01668bSGreg Clayton except: 470de01668bSGreg Clayton result.SetError("option parsing failed") 471de01668bSGreg Clayton return 472de01668bSGreg Clayton 473de01668bSGreg Clayton # Always get program state from the SBExecutionContext passed in 474de01668bSGreg Clayton target = exe_ctx.GetTarget() 475de01668bSGreg Clayton if not target.IsValid(): 476de01668bSGreg Clayton result.SetError("invalid target") 477de01668bSGreg Clayton return 478de01668bSGreg Clayton archives = {} 479de01668bSGreg Clayton for module_spec in args: 480de01668bSGreg Clayton module = target.module[module_spec] 481de01668bSGreg Clayton if not (module and module.IsValid()): 482de01668bSGreg Clayton result.SetError('error: invalid module specification: "%s". ' 483de01668bSGreg Clayton 'Specify the full path, basename, or UUID of ' 484de01668bSGreg Clayton 'a module ' % (module_spec)) 485de01668bSGreg Clayton return 486de01668bSGreg Clayton num_symbols = module.GetNumSymbols() 487de01668bSGreg Clayton num_errors = 0 488de01668bSGreg Clayton for i in range(num_symbols): 489de01668bSGreg Clayton symbol = module.GetSymbolAtIndex(i) 490de01668bSGreg Clayton if symbol.GetType() != lldb.eSymbolTypeObjectFile: 491de01668bSGreg Clayton continue 492de01668bSGreg Clayton path = symbol.GetName() 493de01668bSGreg Clayton if not path: 494de01668bSGreg Clayton continue 495de01668bSGreg Clayton # Extract the value of the symbol by dumping the 496de01668bSGreg Clayton # symbol. The value is the mod time. 497de01668bSGreg Clayton dmap_mtime = int(str(symbol).split('value = ') 498de01668bSGreg Clayton [1].split(',')[0], 16) 499de01668bSGreg Clayton if not options.errors: 500525cd59fSSerge Guelton print('%s' % (path), file=result) 501de01668bSGreg Clayton if os.path.exists(path): 502de01668bSGreg Clayton actual_mtime = int(os.stat(path).st_mtime) 503de01668bSGreg Clayton if dmap_mtime != actual_mtime: 504de01668bSGreg Clayton num_errors += 1 505de01668bSGreg Clayton if options.errors: 506525cd59fSSerge Guelton print('%s' % (path), end=' ', file=result) 507de01668bSGreg Clayton print_mtime_error(result, dmap_mtime, 508de01668bSGreg Clayton actual_mtime) 509de01668bSGreg Clayton elif path[-1] == ')': 510de01668bSGreg Clayton (archive_path, object_name) = path[0:-1].split('(') 511de01668bSGreg Clayton if not archive_path and not object_name: 512de01668bSGreg Clayton num_errors += 1 513de01668bSGreg Clayton if options.errors: 514525cd59fSSerge Guelton print('%s' % (path), end=' ', file=result) 515de01668bSGreg Clayton print_file_missing_error(path) 516de01668bSGreg Clayton continue 517de01668bSGreg Clayton if not os.path.exists(archive_path): 518de01668bSGreg Clayton num_errors += 1 519de01668bSGreg Clayton if options.errors: 520525cd59fSSerge Guelton print('%s' % (path), end=' ', file=result) 521de01668bSGreg Clayton print_file_missing_error(archive_path) 522de01668bSGreg Clayton continue 523de01668bSGreg Clayton if archive_path in archives: 524de01668bSGreg Clayton archive = archives[archive_path] 525de01668bSGreg Clayton else: 526de01668bSGreg Clayton archive = Archive(archive_path) 527de01668bSGreg Clayton archives[archive_path] = archive 528de01668bSGreg Clayton matches = archive.find(object_name, dmap_mtime) 529de01668bSGreg Clayton num_matches = len(matches) 530de01668bSGreg Clayton if num_matches == 1: 531525cd59fSSerge Guelton print('1 match', file=result) 532de01668bSGreg Clayton obj = matches[0] 533de01668bSGreg Clayton if obj.date != dmap_mtime: 534de01668bSGreg Clayton num_errors += 1 535de01668bSGreg Clayton if options.errors: 536525cd59fSSerge Guelton print('%s' % (path), end=' ', file=result) 537de01668bSGreg Clayton print_mtime_error(result, dmap_mtime, obj.date) 538de01668bSGreg Clayton elif num_matches == 0: 539de01668bSGreg Clayton num_errors += 1 540de01668bSGreg Clayton if options.errors: 541525cd59fSSerge Guelton print('%s' % (path), end=' ', file=result) 542de01668bSGreg Clayton print_archive_object_error(result, object_name, 543de01668bSGreg Clayton dmap_mtime, archive) 544de01668bSGreg Clayton elif num_matches > 1: 545de01668bSGreg Clayton num_errors += 1 546de01668bSGreg Clayton if options.errors: 547525cd59fSSerge Guelton print('%s' % (path), end=' ', file=result) 548de01668bSGreg Clayton print_multiple_object_matches(result, 549de01668bSGreg Clayton object_name, 550de01668bSGreg Clayton dmap_mtime, matches) 551de01668bSGreg Clayton if num_errors > 0: 552525cd59fSSerge Guelton print("%u errors found" % (num_errors), file=result) 553de01668bSGreg Clayton else: 554525cd59fSSerge Guelton print("No errors detected in debug map", file=result) 555de01668bSGreg Clayton 556de01668bSGreg Clayton 557de01668bSGreg Claytondef __lldb_init_module(debugger, dict): 558de01668bSGreg Clayton # This initializer is being run from LLDB in the embedded command 559de01668bSGreg Clayton # interpreter. 560de01668bSGreg Clayton # Add any commands contained in this module to LLDB 561de01668bSGreg Clayton debugger.HandleCommand( 562de01668bSGreg Clayton 'command script add -c %s.VerifyDebugMapCommand %s' % ( 563de01668bSGreg Clayton __name__, VerifyDebugMapCommand.name)) 564de01668bSGreg Clayton print('The "%s" command has been installed, type "help %s" for detailed ' 565de01668bSGreg Clayton 'help.' % (VerifyDebugMapCommand.name, VerifyDebugMapCommand.name)) 566