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