diff options
Diffstat (limited to 'examples/python/bsd.py')
-rwxr-xr-x | examples/python/bsd.py | 564 |
1 files changed, 0 insertions, 564 deletions
diff --git a/examples/python/bsd.py b/examples/python/bsd.py deleted file mode 100755 index 3e9528c65845..000000000000 --- a/examples/python/bsd.py +++ /dev/null @@ -1,564 +0,0 @@ -#!/usr/bin/python - -import cmd -import optparse -import os -import shlex -import struct -import sys - -ARMAG = "!<arch>\n" -SARMAG = 8 -ARFMAG = "`\n" -AR_EFMT1 = "#1/" - - -def memdump(src, bytes_per_line=16, address=0): - FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' - for x in range(256)]) - for i in range(0, len(src), bytes_per_line): - s = src[i:i+bytes_per_line] - hex_bytes = ' '.join(["%02x" % (ord(x)) for x in s]) - ascii = s.translate(FILTER) - print("%#08.8x: %-*s %s" % (address+i, bytes_per_line*3, hex_bytes, - ascii)) - - -class Object(object): - def __init__(self, file): - def read_str(file, str_len): - return file.read(str_len).rstrip('\0 ') - - def read_int(file, str_len, base): - return int(read_str(file, str_len), base) - - self.offset = file.tell() - self.file = file - self.name = read_str(file, 16) - self.date = read_int(file, 12, 10) - self.uid = read_int(file, 6, 10) - self.gid = read_int(file, 6, 10) - self.mode = read_int(file, 8, 8) - self.size = read_int(file, 10, 10) - if file.read(2) != ARFMAG: - raise ValueError('invalid BSD object at offset %#08.8x' % ( - self.offset)) - # If we have an extended name read it. Extended names start with - name_len = 0 - if self.name.startswith(AR_EFMT1): - name_len = int(self.name[len(AR_EFMT1):], 10) - self.name = read_str(file, name_len) - self.obj_offset = file.tell() - self.obj_size = self.size - name_len - file.seek(self.obj_size, 1) - - def dump(self, f=sys.stdout, flat=True): - if flat: - f.write('%#08.8x: %#08.8x %5u %5u %6o %#08.8x %s\n' % (self.offset, - self.date, self.uid, self.gid, self.mode, self.size, - self.name)) - else: - f.write('%#08.8x: \n' % self.offset) - f.write(' name = "%s"\n' % self.name) - f.write(' date = %#08.8x\n' % self.date) - f.write(' uid = %i\n' % self.uid) - f.write(' gid = %i\n' % self.gid) - f.write(' mode = %o\n' % self.mode) - f.write(' size = %#08.8x\n' % (self.size)) - self.file.seek(self.obj_offset, 0) - first_bytes = self.file.read(4) - f.write('bytes = ') - memdump(first_bytes) - - def get_bytes(self): - saved_pos = self.file.tell() - self.file.seek(self.obj_offset, 0) - bytes = self.file.read(self.obj_size) - self.file.seek(saved_pos, 0) - return bytes - - def save(self, path=None, overwrite=False): - ''' - Save the contents of the object to disk using 'path' argument as - the path, or save it to the current working directory using the - object name. - ''' - - if path is None: - path = self.name - if not overwrite and os.path.exists(path): - print('error: outfile "%s" already exists' % (path)) - return - print('Saving "%s" to "%s"...' % (self.name, path)) - with open(path, 'w') as f: - f.write(self.get_bytes()) - - -class StringTable(object): - def __init__(self, bytes): - self.bytes = bytes - - def get_string(self, offset): - length = len(self.bytes) - if offset >= length: - return None - return self.bytes[offset:self.bytes.find('\0', offset)] - - -class Archive(object): - def __init__(self, path): - self.path = path - self.file = open(path, 'r') - self.objects = [] - self.offset_to_object = {} - if self.file.read(SARMAG) != ARMAG: - print("error: file isn't a BSD archive") - while True: - try: - self.objects.append(Object(self.file)) - except ValueError: - break - - def get_object_at_offset(self, offset): - if offset in self.offset_to_object: - return self.offset_to_object[offset] - for obj in self.objects: - if obj.offset == offset: - self.offset_to_object[offset] = obj - return obj - return None - - def find(self, name, mtime=None, f=sys.stdout): - ''' - Find an object(s) by name with optional modification time. There - can be multple objects with the same name inside and possibly with - the same modification time within a BSD archive so clients must be - prepared to get multiple results. - ''' - matches = [] - for obj in self.objects: - if obj.name == name and (mtime is None or mtime == obj.date): - matches.append(obj) - return matches - - @classmethod - def dump_header(self, f=sys.stdout): - f.write(' DATE UID GID MODE SIZE NAME\n') - f.write(' ---------- ----- ----- ------ ---------- ' - '--------------\n') - - def get_symdef(self): - def get_uint32(file): - '''Extract a uint32_t from the current file position.''' - v, = struct.unpack('=I', file.read(4)) - return v - - for obj in self.objects: - symdef = [] - if obj.name.startswith("__.SYMDEF"): - self.file.seek(obj.obj_offset, 0) - ranlib_byte_size = get_uint32(self.file) - num_ranlib_structs = ranlib_byte_size/8 - str_offset_pairs = [] - for _ in range(num_ranlib_structs): - strx = get_uint32(self.file) - offset = get_uint32(self.file) - str_offset_pairs.append((strx, offset)) - strtab_len = get_uint32(self.file) - strtab = StringTable(self.file.read(strtab_len)) - for s in str_offset_pairs: - symdef.append((strtab.get_string(s[0]), s[1])) - return symdef - - def get_object_dicts(self): - ''' - Returns an array of object dictionaries that contain they following - keys: - 'object': the actual bsd.Object instance - 'symdefs': an array of symbol names that the object contains - as found in the "__.SYMDEF" item in the archive - ''' - symdefs = self.get_symdef() - symdef_dict = {} - if symdefs: - for (name, offset) in symdefs: - if offset in symdef_dict: - object_dict = symdef_dict[offset] - else: - object_dict = { - 'object': self.get_object_at_offset(offset), - 'symdefs': [] - } - symdef_dict[offset] = object_dict - object_dict['symdefs'].append(name) - object_dicts = [] - for offset in sorted(symdef_dict): - object_dicts.append(symdef_dict[offset]) - return object_dicts - - def dump(self, f=sys.stdout, flat=True): - f.write('%s:\n' % self.path) - if flat: - self.dump_header(f=f) - for obj in self.objects: - obj.dump(f=f, flat=flat) - -class Interactive(cmd.Cmd): - '''Interactive prompt for exploring contents of BSD archive files, type - "help" to see a list of supported commands.''' - image_option_parser = None - - def __init__(self, archives): - cmd.Cmd.__init__(self) - self.use_rawinput = False - self.intro = ('Interactive BSD archive prompt, type "help" to see a ' - 'list of supported commands.') - self.archives = archives - self.prompt = '% ' - - def default(self, line): - '''Catch all for unknown command, which will exit the interpreter.''' - print("unknown command: %s" % line) - return True - - def do_q(self, line): - '''Quit command''' - return True - - def do_quit(self, line): - '''Quit command''' - return True - - def do_extract(self, line): - args = shlex.split(line) - if args: - extracted = False - for object_name in args: - for archive in self.archives: - matches = archive.find(object_name) - if matches: - for object in matches: - object.save(overwrite=False) - extracted = True - if not extracted: - print('error: no object matches "%s" in any archives' % ( - object_name)) - else: - print('error: must specify the name of an object to extract') - - def do_ls(self, line): - args = shlex.split(line) - if args: - for object_name in args: - for archive in self.archives: - matches = archive.find(object_name) - if matches: - for object in matches: - object.dump(flat=False) - else: - print('error: no object matches "%s" in "%s"' % ( - object_name, archive.path)) - else: - for archive in self.archives: - archive.dump(flat=True) - print('') - - - -def main(): - parser = optparse.OptionParser( - prog='bsd', - description='Utility for BSD archives') - parser.add_option( - '--object', - type='string', - dest='object_name', - default=None, - help=('Specify the name of a object within the BSD archive to get ' - 'information on')) - parser.add_option( - '-s', '--symbol', - type='string', - dest='find_symbol', - default=None, - help=('Specify the name of a symbol within the BSD archive to get ' - 'information on from SYMDEF')) - parser.add_option( - '--symdef', - action='store_true', - dest='symdef', - default=False, - help=('Dump the information in the SYMDEF.')) - parser.add_option( - '-v', '--verbose', - action='store_true', - dest='verbose', - default=False, - help='Enable verbose output') - parser.add_option( - '-e', '--extract', - action='store_true', - dest='extract', - default=False, - help=('Specify this to extract the object specified with the --object ' - 'option. There must be only one object with a matching name or ' - 'the --mtime option must be specified to uniquely identify a ' - 'single object.')) - parser.add_option( - '-m', '--mtime', - type='int', - dest='mtime', - default=None, - help=('Specify the modification time of the object an object. This ' - 'option is used with either the --object or --extract options.')) - parser.add_option( - '-o', '--outfile', - type='string', - dest='outfile', - default=None, - help=('Specify a different name or path for the file to extract when ' - 'using the --extract option. If this option isn\'t specified, ' - 'then the extracted object file will be extracted into the ' - 'current working directory if a file doesn\'t already exist ' - 'with that name.')) - parser.add_option( - '-i', '--interactive', - action='store_true', - dest='interactive', - default=False, - help=('Enter an interactive shell that allows users to interactively ' - 'explore contents of .a files.')) - - (options, args) = parser.parse_args(sys.argv[1:]) - - if options.interactive: - archives = [] - for path in args: - archives.append(Archive(path)) - interpreter = Interactive(archives) - interpreter.cmdloop() - return - - for path in args: - archive = Archive(path) - if options.object_name: - print('%s:\n' % (path)) - matches = archive.find(options.object_name, options.mtime) - if matches: - dump_all = True - if options.extract: - if len(matches) == 1: - dump_all = False - matches[0].save(path=options.outfile, overwrite=False) - else: - print('error: multiple objects match "%s". Specify ' - 'the modification time using --mtime.' % ( - options.object_name)) - if dump_all: - for obj in matches: - obj.dump(flat=False) - else: - print('error: object "%s" not found in archive' % ( - options.object_name)) - elif options.find_symbol: - symdefs = archive.get_symdef() - if symdefs: - success = False - for (name, offset) in symdefs: - obj = archive.get_object_at_offset(offset) - if name == options.find_symbol: - print('Found "%s" in:' % (options.find_symbol)) - obj.dump(flat=False) - success = True - if not success: - print('Didn\'t find "%s" in any objects' % ( - options.find_symbol)) - else: - print("error: no __.SYMDEF was found") - elif options.symdef: - object_dicts = archive.get_object_dicts() - for object_dict in object_dicts: - object_dict['object'].dump(flat=False) - print("symbols:") - for name in object_dict['symdefs']: - print(" %s" % (name)) - else: - archive.dump(flat=not options.verbose) - - -if __name__ == '__main__': - main() - - -def print_mtime_error(result, dmap_mtime, actual_mtime): - print >>result, ("error: modification time in debug map (%#08.8x) doesn't " - "match the .o file modification time (%#08.8x)" % ( - dmap_mtime, actual_mtime)) - - -def print_file_missing_error(result, path): - print >>result, "error: file \"%s\" doesn't exist" % (path) - - -def print_multiple_object_matches(result, object_name, mtime, matches): - print >>result, ("error: multiple matches for object '%s' with with " - "modification time %#08.8x:" % (object_name, mtime)) - Archive.dump_header(f=result) - for match in matches: - match.dump(f=result, flat=True) - - -def print_archive_object_error(result, object_name, mtime, archive): - matches = archive.find(object_name, f=result) - if len(matches) > 0: - print >>result, ("error: no objects have a modification time that " - "matches %#08.8x for '%s'. Potential matches:" % ( - mtime, object_name)) - Archive.dump_header(f=result) - for match in matches: - match.dump(f=result, flat=True) - else: - print >>result, "error: no object named \"%s\" found in archive:" % ( - object_name) - Archive.dump_header(f=result) - for match in archive.objects: - match.dump(f=result, flat=True) - # archive.dump(f=result, flat=True) - - -class VerifyDebugMapCommand: - name = "verify-debug-map-objects" - - def create_options(self): - usage = "usage: %prog [options]" - description = '''This command reports any .o files that are missing -or whose modification times don't match in the debug map of an executable.''' - - self.parser = optparse.OptionParser( - description=description, - prog=self.name, - usage=usage, - add_help_option=False) - - self.parser.add_option( - '-e', '--errors', - action='store_true', - dest='errors', - default=False, - help="Only show errors") - - def get_short_help(self): - return "Verify debug map object files." - - def get_long_help(self): - return self.help_string - - def __init__(self, debugger, unused): - self.create_options() - self.help_string = self.parser.format_help() - - def __call__(self, debugger, command, exe_ctx, result): - import lldb - # Use the Shell Lexer to properly parse up command options just like a - # shell would - command_args = shlex.split(command) - - try: - (options, args) = self.parser.parse_args(command_args) - except: - result.SetError("option parsing failed") - return - - # Always get program state from the SBExecutionContext passed in - target = exe_ctx.GetTarget() - if not target.IsValid(): - result.SetError("invalid target") - return - archives = {} - for module_spec in args: - module = target.module[module_spec] - if not (module and module.IsValid()): - result.SetError('error: invalid module specification: "%s". ' - 'Specify the full path, basename, or UUID of ' - 'a module ' % (module_spec)) - return - num_symbols = module.GetNumSymbols() - num_errors = 0 - for i in range(num_symbols): - symbol = module.GetSymbolAtIndex(i) - if symbol.GetType() != lldb.eSymbolTypeObjectFile: - continue - path = symbol.GetName() - if not path: - continue - # Extract the value of the symbol by dumping the - # symbol. The value is the mod time. - dmap_mtime = int(str(symbol).split('value = ') - [1].split(',')[0], 16) - if not options.errors: - print >>result, '%s' % (path) - if os.path.exists(path): - actual_mtime = int(os.stat(path).st_mtime) - if dmap_mtime != actual_mtime: - num_errors += 1 - if options.errors: - print >>result, '%s' % (path), - print_mtime_error(result, dmap_mtime, - actual_mtime) - elif path[-1] == ')': - (archive_path, object_name) = path[0:-1].split('(') - if not archive_path and not object_name: - num_errors += 1 - if options.errors: - print >>result, '%s' % (path), - print_file_missing_error(path) - continue - if not os.path.exists(archive_path): - num_errors += 1 - if options.errors: - print >>result, '%s' % (path), - print_file_missing_error(archive_path) - continue - if archive_path in archives: - archive = archives[archive_path] - else: - archive = Archive(archive_path) - archives[archive_path] = archive - matches = archive.find(object_name, dmap_mtime) - num_matches = len(matches) - if num_matches == 1: - print >>result, '1 match' - obj = matches[0] - if obj.date != dmap_mtime: - num_errors += 1 - if options.errors: - print >>result, '%s' % (path), - print_mtime_error(result, dmap_mtime, obj.date) - elif num_matches == 0: - num_errors += 1 - if options.errors: - print >>result, '%s' % (path), - print_archive_object_error(result, object_name, - dmap_mtime, archive) - elif num_matches > 1: - num_errors += 1 - if options.errors: - print >>result, '%s' % (path), - print_multiple_object_matches(result, - object_name, - dmap_mtime, matches) - if num_errors > 0: - print >>result, "%u errors found" % (num_errors) - else: - print >>result, "No errors detected in debug map" - - -def __lldb_init_module(debugger, dict): - # This initializer is being run from LLDB in the embedded command - # interpreter. - # Add any commands contained in this module to LLDB - debugger.HandleCommand( - 'command script add -c %s.VerifyDebugMapCommand %s' % ( - __name__, VerifyDebugMapCommand.name)) - print('The "%s" command has been installed, type "help %s" for detailed ' - 'help.' % (VerifyDebugMapCommand.name, VerifyDebugMapCommand.name)) |