diff options
Diffstat (limited to 'gnu/llvm/lldb/examples/python')
36 files changed, 13672 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/examples/python/armv7_cortex_m_target_defintion.py b/gnu/llvm/lldb/examples/python/armv7_cortex_m_target_defintion.py new file mode 100755 index 00000000000..342de89c462 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/armv7_cortex_m_target_defintion.py @@ -0,0 +1,140 @@ +#!/usr/bin/python +#===-- armv7_cortex_m_target_definition.py.py ------------------*- C++ -*-===// +# +# The LLVM Compiler Infrastructure +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the GDB server +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the darwin version of +# GDB and allows you to connect to servers that use this register set. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/armv7_cortex_m_target_defintion.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB server. +#---------------------------------------------------------------------- + +from lldb import * + +# DWARF register numbers +name_to_dwarf_regnum = { + 'r0' : 0 , + 'r1' : 1 , + 'r2' : 2 , + 'r3' : 3 , + 'r4' : 4 , + 'r5' : 5 , + 'r6' : 6 , + 'r7' : 7 , + 'r9' : 8 , + 'r10' : 9 , + 'r11' : 10, + 'r12' : 11, + 'sp' : 12, + 'lr' : 13, + 'pc' : 14, + 'r15' : 15, + 'xpsr' : 16, +}; + +name_to_generic_regnum = { + 'pc' : LLDB_REGNUM_GENERIC_PC, + 'sp' : LLDB_REGNUM_GENERIC_SP, + 'r7' : LLDB_REGNUM_GENERIC_FP, + 'lr' : LLDB_REGNUM_GENERIC_RA, + 'r0' : LLDB_REGNUM_GENERIC_ARG1, + 'r1' : LLDB_REGNUM_GENERIC_ARG2, + 'r2' : LLDB_REGNUM_GENERIC_ARG3, + 'r3' : LLDB_REGNUM_GENERIC_ARG4 +}; + + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +armv7_register_infos = [ +{ 'name':'r0' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg1' }, +{ 'name':'r1' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg2' }, +{ 'name':'r2' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg3' }, +{ 'name':'r3' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg4' }, +{ 'name':'r4' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r5' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r6' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r7' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'fp' }, +{ 'name':'r8' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r9' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r10' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r11' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r12' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'sp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'r13' }, +{ 'name':'lr' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'r14' }, +{ 'name':'pc' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'r15' }, +{ 'name':'xpsr' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'cpsr' }, +]; + +g_target_definition = None + +def get_target_definition (): + global g_target_definition + if g_target_definition == None: + g_target_definition = {} + offset = 0 + for reg_info in armv7_register_infos: + reg_name = reg_info['name'] + + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize'] / 8 + + # Set the DWARF/eh_frame register number for this register if it has one + reg_num = get_reg_num(name_to_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['ehframe'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + g_target_definition['sets'] = ['General Purpose Registers'] + g_target_definition['registers'] = armv7_register_infos + g_target_definition['host-info'] = { 'triple' : 'armv7em--', 'endian': eByteOrderLittle } + g_target_definition['g-packet-size'] = offset + return g_target_definition + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition() diff --git a/gnu/llvm/lldb/examples/python/bsd.py b/gnu/llvm/lldb/examples/python/bsd.py new file mode 100755 index 00000000000..c66226e3710 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/bsd.py @@ -0,0 +1,565 @@ +#!/usr/bin/python +from __future__ import print_function + +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("error: modification time in debug map (%#08.8x) doesn't " + "match the .o file modification time (%#08.8x)" % ( + dmap_mtime, actual_mtime), file=result) + + +def print_file_missing_error(result, path): + print("error: file \"%s\" doesn't exist" % (path), file=result) + + +def print_multiple_object_matches(result, object_name, mtime, matches): + print("error: multiple matches for object '%s' with with " + "modification time %#08.8x:" % (object_name, mtime), file=result) + 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("error: no objects have a modification time that " + "matches %#08.8x for '%s'. Potential matches:" % ( + mtime, object_name), file=result) + Archive.dump_header(f=result) + for match in matches: + match.dump(f=result, flat=True) + else: + print("error: no object named \"%s\" found in archive:" % ( + object_name), file=result) + 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('%s' % (path), file=result) + 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('%s' % (path), end=' ', file=result) + 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('%s' % (path), end=' ', file=result) + print_file_missing_error(path) + continue + if not os.path.exists(archive_path): + num_errors += 1 + if options.errors: + print('%s' % (path), end=' ', file=result) + 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('1 match', file=result) + obj = matches[0] + if obj.date != dmap_mtime: + num_errors += 1 + if options.errors: + print('%s' % (path), end=' ', file=result) + print_mtime_error(result, dmap_mtime, obj.date) + elif num_matches == 0: + num_errors += 1 + if options.errors: + print('%s' % (path), end=' ', file=result) + print_archive_object_error(result, object_name, + dmap_mtime, archive) + elif num_matches > 1: + num_errors += 1 + if options.errors: + print('%s' % (path), end=' ', file=result) + print_multiple_object_matches(result, + object_name, + dmap_mtime, matches) + if num_errors > 0: + print("%u errors found" % (num_errors), file=result) + else: + print("No errors detected in debug map", file=result) + + +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)) diff --git a/gnu/llvm/lldb/examples/python/cmdtemplate.py b/gnu/llvm/lldb/examples/python/cmdtemplate.py new file mode 100644 index 00000000000..97af943e6de --- /dev/null +++ b/gnu/llvm/lldb/examples/python/cmdtemplate.py @@ -0,0 +1,149 @@ +#!/usr/bin/python + +# --------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +# --------------------------------------------------------------------- + +from __future__ import print_function + +import inspect +import lldb +import optparse +import shlex +import sys + + +class FrameStatCommand: + program = 'framestats' + + @classmethod + def register_lldb_command(cls, debugger, module_name): + parser = cls.create_options() + cls.__doc__ = parser.format_help() + # Add any commands contained in this module to LLDB + command = 'command script add -c %s.%s %s' % (module_name, + cls.__name__, + cls.program) + debugger.HandleCommand(command) + print('The "{0}" command has been installed, type "help {0}" or "{0} ' + '--help" for detailed help.'.format(cls.program)) + + @classmethod + def create_options(cls): + + usage = "usage: %prog [options]" + description = ('This command is meant to be an example of how to make ' + 'an LLDB command that does something useful, follows ' + 'best practices, and exploits the SB API. ' + 'Specifically, this command computes the aggregate ' + 'and average size of the variables in the current ' + 'frame and allows you to tweak exactly which variables ' + 'are to be accounted in the computation.') + + # Pass add_help_option = False, since this keeps the command in line + # with lldb commands, and we wire up "help command" to work by + # providing the long & short help methods below. + parser = optparse.OptionParser( + description=description, + prog=cls.program, + usage=usage, + add_help_option=False) + + parser.add_option( + '-i', + '--in-scope', + action='store_true', + dest='inscope', + help='in_scope_only = True', + default=True) + + parser.add_option( + '-a', + '--arguments', + action='store_true', + dest='arguments', + help='arguments = True', + default=True) + + parser.add_option( + '-l', + '--locals', + action='store_true', + dest='locals', + help='locals = True', + default=True) + + parser.add_option( + '-s', + '--statics', + action='store_true', + dest='statics', + help='statics = True', + default=True) + + return parser + + def get_short_help(self): + return "Example command for use in debugging" + + def get_long_help(self): + return self.help_string + + def __init__(self, debugger, unused): + self.parser = self.create_options() + self.help_string = self.parser.format_help() + + def __call__(self, debugger, command, exe_ctx, result): + # 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: + # if you don't handle exceptions, passing an incorrect argument to + # the OptionParser will cause LLDB to exit (courtesy of OptParse + # dealing with argument errors by throwing SystemExit) + result.SetError("option parsing failed") + return + + # Always get program state from the lldb.SBExecutionContext passed + # in as exe_ctx + frame = exe_ctx.GetFrame() + if not frame.IsValid(): + result.SetError("invalid frame") + return + + variables_list = frame.GetVariables( + options.arguments, + options.locals, + options.statics, + options.inscope) + variables_count = variables_list.GetSize() + if variables_count == 0: + print("no variables here", file=result) + return + total_size = 0 + for i in range(0, variables_count): + variable = variables_list.GetValueAtIndex(i) + variable_type = variable.GetType() + total_size = total_size + variable_type.GetByteSize() + average_size = float(total_size) / variables_count + print("Your frame has %d variables. Their total size " + "is %d bytes. The average size is %f bytes" % ( + variables_count, total_size, average_size), file=result) + # not returning anything is akin to returning success + + +def __lldb_init_module(debugger, dict): + # Register all classes that have a register_lldb_command method + for _name, cls in inspect.getmembers(sys.modules[__name__]): + if inspect.isclass(cls) and callable(getattr(cls, + "register_lldb_command", + None)): + cls.register_lldb_command(debugger, __name__) diff --git a/gnu/llvm/lldb/examples/python/crashlog.py b/gnu/llvm/lldb/examples/python/crashlog.py new file mode 100755 index 00000000000..b7b62acc60e --- /dev/null +++ b/gnu/llvm/lldb/examples/python/crashlog.py @@ -0,0 +1,1069 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb": +# +# cd /path/containing/crashlog.py +# lldb +# (lldb) script import crashlog +# "crashlog" command installed, type "crashlog --help" for detailed help +# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash +# +# The benefit of running the crashlog command inside lldb in the +# embedded python interpreter is when the command completes, there +# will be a target with all of the files loaded at the locations +# described in the crash log. Only the files that have stack frames +# in the backtrace will be loaded unless the "--load-all" option +# has been specified. This allows users to explore the program in the +# state it was in right at crash time. +# +# On MacOSX csh, tcsh: +# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) +# +# On MacOSX sh, bash: +# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash +#---------------------------------------------------------------------- + +from __future__ import print_function +import cmd +import datetime +import glob +import optparse +import os +import platform +import plistlib +import pprint # pp = pprint.PrettyPrinter(indent=4); pp.pprint(command_args) +import re +import shlex +import string +import subprocess +import sys +import time +import uuid + +def read_plist(s): + if sys.version_info.major == 3: + return plistlib.loads(s) + else: + return plistlib.readPlistFromString(s) + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True).decode("utf-8") + if xcode_dir: + lldb_python_dirs.append( + os.path.realpath( + xcode_dir + + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append( + xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append( + '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print('imported lldb from: "%s"' % (lldb_python_dir)) + success = True + break + if not success: + print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") + sys.exit(1) + +from lldb.utils import symbolication + +PARSE_MODE_NORMAL = 0 +PARSE_MODE_THREAD = 1 +PARSE_MODE_IMAGES = 2 +PARSE_MODE_THREGS = 3 +PARSE_MODE_SYSTEM = 4 + + +class CrashLog(symbolication.Symbolicator): + """Class that does parses darwin crash logs""" + parent_process_regex = re.compile('^Parent Process:\s*(.*)\[(\d+)\]') + thread_state_regex = re.compile('^Thread ([0-9]+) crashed with') + thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)') + app_backtrace_regex = re.compile( + '^Application Specific Backtrace ([0-9]+)([^:]*):(.*)') + version = r'(\(.+\)|(arm|x86_)[0-9a-z]+)\s+' + frame_regex = re.compile(r'^([0-9]+)' r'\s' # id + r'+(.+?)' r'\s+' # img_name + r'(' +version+ r')?' # img_version + r'(0x[0-9a-fA-F]{7}[0-9a-fA-F]+)' # addr + r' +(.*)' # offs + ) + null_frame_regex = re.compile(r'^([0-9]+)\s+\?\?\?\s+(0{7}0+) +(.*)') + image_regex_uuid = re.compile(r'(0x[0-9a-fA-F]+)' # img_lo + r'\s+' '-' r'\s+' # - + r'(0x[0-9a-fA-F]+)' r'\s+' # img_hi + r'[+]?(.+?)' r'\s+' # img_name + r'(' +version+ ')?' # img_version + r'(<([-0-9a-fA-F]+)>\s+)?' # img_uuid + r'(/.*)' # img_path + ) + empty_line_regex = re.compile('^$') + + class Thread: + """Class that represents a thread in a darwin crash log""" + + def __init__(self, index, app_specific_backtrace): + self.index = index + self.frames = list() + self.idents = list() + self.registers = dict() + self.reason = None + self.queue = None + self.app_specific_backtrace = app_specific_backtrace + + def dump(self, prefix): + if self.app_specific_backtrace: + print("%Application Specific Backtrace[%u] %s" % (prefix, self.index, self.reason)) + else: + print("%sThread[%u] %s" % (prefix, self.index, self.reason)) + if self.frames: + print("%s Frames:" % (prefix)) + for frame in self.frames: + frame.dump(prefix + ' ') + if self.registers: + print("%s Registers:" % (prefix)) + for reg in self.registers.keys(): + print("%s %-5s = %#16.16x" % (prefix, reg, self.registers[reg])) + + def dump_symbolicated(self, crash_log, options): + this_thread_crashed = self.app_specific_backtrace + if not this_thread_crashed: + this_thread_crashed = self.did_crash() + if options.crashed_only and this_thread_crashed == False: + return + + print("%s" % self) + #prev_frame_index = -1 + display_frame_idx = -1 + for frame_idx, frame in enumerate(self.frames): + disassemble = ( + this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth + if frame_idx == 0: + symbolicated_frame_addresses = crash_log.symbolicate( + frame.pc & crash_log.addr_mask, options.verbose) + else: + # Any frame above frame zero and we have to subtract one to + # get the previous line entry + symbolicated_frame_addresses = crash_log.symbolicate( + (frame.pc & crash_log.addr_mask) - 1, options.verbose) + + if symbolicated_frame_addresses: + symbolicated_frame_address_idx = 0 + for symbolicated_frame_address in symbolicated_frame_addresses: + display_frame_idx += 1 + print('[%3u] %s' % (frame_idx, symbolicated_frame_address)) + if (options.source_all or self.did_crash( + )) and display_frame_idx < options.source_frames and options.source_context: + source_context = options.source_context + line_entry = symbolicated_frame_address.get_symbol_context().line_entry + if line_entry.IsValid(): + strm = lldb.SBStream() + if line_entry: + lldb.debugger.GetSourceManager().DisplaySourceLinesWithLineNumbers( + line_entry.file, line_entry.line, source_context, source_context, "->", strm) + source_text = strm.GetData() + if source_text: + # Indent the source a bit + indent_str = ' ' + join_str = '\n' + indent_str + print('%s%s' % (indent_str, join_str.join(source_text.split('\n')))) + if symbolicated_frame_address_idx == 0: + if disassemble: + instructions = symbolicated_frame_address.get_instructions() + if instructions: + print() + symbolication.disassemble_instructions( + crash_log.get_target(), + instructions, + frame.pc, + options.disassemble_before, + options.disassemble_after, + frame.index > 0) + print() + symbolicated_frame_address_idx += 1 + else: + print(frame) + + def add_ident(self, ident): + if ident not in self.idents: + self.idents.append(ident) + + def did_crash(self): + return self.reason is not None + + def __str__(self): + if self.app_specific_backtrace: + s = "Application Specific Backtrace[%u]" % self.index + else: + s = "Thread[%u]" % self.index + if self.reason: + s += ' %s' % self.reason + return s + + class Frame: + """Class that represents a stack frame in a thread in a darwin crash log""" + + def __init__(self, index, pc, description): + self.pc = pc + self.description = description + self.index = index + + def __str__(self): + if self.description: + return "[%3u] 0x%16.16x %s" % ( + self.index, self.pc, self.description) + else: + return "[%3u] 0x%16.16x" % (self.index, self.pc) + + def dump(self, prefix): + print("%s%s" % (prefix, str(self))) + + class DarwinImage(symbolication.Image): + """Class that represents a binary images in a darwin crash log""" + dsymForUUIDBinary = '/usr/local/bin/dsymForUUID' + if not os.path.exists(dsymForUUIDBinary): + try: + dsymForUUIDBinary = subprocess.check_output('which dsymForUUID', + shell=True).decode("utf-8").rstrip('\n') + except: + dsymForUUIDBinary = "" + + dwarfdump_uuid_regex = re.compile( + 'UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') + + def __init__( + self, + text_addr_lo, + text_addr_hi, + identifier, + version, + uuid, + path, + verbose): + symbolication.Image.__init__(self, path, uuid) + self.add_section( + symbolication.Section( + text_addr_lo, + text_addr_hi, + "__TEXT")) + self.identifier = identifier + self.version = version + self.verbose = verbose + + def show_symbol_progress(self): + """ + Hide progress output and errors from system frameworks as they are plentiful. + """ + if self.verbose: + return True + return not (self.path.startswith("/System/Library/") or + self.path.startswith("/usr/lib/")) + + + def find_matching_slice(self): + dwarfdump_cmd_output = subprocess.check_output( + 'dwarfdump --uuid "%s"' % self.path, shell=True).decode("utf-8") + self_uuid = self.get_uuid() + for line in dwarfdump_cmd_output.splitlines(): + match = self.dwarfdump_uuid_regex.search(line) + if match: + dwarf_uuid_str = match.group(1) + dwarf_uuid = uuid.UUID(dwarf_uuid_str) + if self_uuid == dwarf_uuid: + self.resolved_path = self.path + self.arch = match.group(2) + return True + if not self.resolved_path: + self.unavailable = True + if self.show_symbol_progress(): + print(("error\n error: unable to locate '%s' with UUID %s" + % (self.path, self.get_normalized_uuid_string()))) + return False + + def locate_module_and_debug_symbols(self): + # Don't load a module twice... + if self.resolved: + return True + # Mark this as resolved so we don't keep trying + self.resolved = True + uuid_str = self.get_normalized_uuid_string() + if self.show_symbol_progress(): + print('Getting symbols for %s %s...' % (uuid_str, self.path), end=' ') + if os.path.exists(self.dsymForUUIDBinary): + dsym_for_uuid_command = '%s %s' % ( + self.dsymForUUIDBinary, uuid_str) + s = subprocess.check_output(dsym_for_uuid_command, shell=True) + if s: + try: + plist_root = read_plist(s) + except: + print(("Got exception: ", sys.exc_info()[1], " handling dsymForUUID output: \n", s)) + raise + if plist_root: + plist = plist_root[uuid_str] + if plist: + if 'DBGArchitecture' in plist: + self.arch = plist['DBGArchitecture'] + if 'DBGDSYMPath' in plist: + self.symfile = os.path.realpath( + plist['DBGDSYMPath']) + if 'DBGSymbolRichExecutable' in plist: + self.path = os.path.expanduser( + plist['DBGSymbolRichExecutable']) + self.resolved_path = self.path + if not self.resolved_path and os.path.exists(self.path): + if not self.find_matching_slice(): + return False + if not self.resolved_path and not os.path.exists(self.path): + try: + dsym = subprocess.check_output( + ["/usr/bin/mdfind", + "com_apple_xcode_dsym_uuids == %s"%uuid_str]).decode("utf-8")[:-1] + if dsym and os.path.exists(dsym): + print(('falling back to binary inside "%s"'%dsym)) + self.symfile = dsym + dwarf_dir = os.path.join(dsym, 'Contents/Resources/DWARF') + for filename in os.listdir(dwarf_dir): + self.path = os.path.join(dwarf_dir, filename) + if not self.find_matching_slice(): + return False + break + except: + pass + if (self.resolved_path and os.path.exists(self.resolved_path)) or ( + self.path and os.path.exists(self.path)): + print('ok') + return True + else: + self.unavailable = True + return False + + def __init__(self, path, verbose): + """CrashLog constructor that take a path to a darwin crash log file""" + symbolication.Symbolicator.__init__(self) + self.path = os.path.expanduser(path) + self.info_lines = list() + self.system_profile = list() + self.threads = list() + self.backtraces = list() # For application specific backtraces + self.idents = list() # A list of the required identifiers for doing all stack backtraces + self.crashed_thread_idx = -1 + self.version = -1 + self.error = None + self.target = None + self.verbose = verbose + # With possible initial component of ~ or ~user replaced by that user's + # home directory. + try: + f = open(self.path) + except IOError: + self.error = 'error: cannot open "%s"' % self.path + return + + self.file_lines = f.read().splitlines() + parse_mode = PARSE_MODE_NORMAL + thread = None + app_specific_backtrace = False + for line in self.file_lines: + # print line + line_len = len(line) + if line_len == 0: + if thread: + if parse_mode == PARSE_MODE_THREAD: + if thread.index == self.crashed_thread_idx: + thread.reason = '' + if self.thread_exception: + thread.reason += self.thread_exception + if self.thread_exception_data: + thread.reason += " (%s)" % self.thread_exception_data + if app_specific_backtrace: + self.backtraces.append(thread) + else: + self.threads.append(thread) + thread = None + else: + # only append an extra empty line if the previous line + # in the info_lines wasn't empty + if len(self.info_lines) > 0 and len(self.info_lines[-1]): + self.info_lines.append(line) + parse_mode = PARSE_MODE_NORMAL + # print 'PARSE_MODE_NORMAL' + elif parse_mode == PARSE_MODE_NORMAL: + if line.startswith('Process:'): + (self.process_name, pid_with_brackets) = line[ + 8:].strip().split(' [') + self.process_id = pid_with_brackets.strip('[]') + elif line.startswith('Path:'): + self.process_path = line[5:].strip() + elif line.startswith('Identifier:'): + self.process_identifier = line[11:].strip() + elif line.startswith('Version:'): + version_string = line[8:].strip() + matched_pair = re.search("(.+)\((.+)\)", version_string) + if matched_pair: + self.process_version = matched_pair.group(1) + self.process_compatability_version = matched_pair.group( + 2) + else: + self.process = version_string + self.process_compatability_version = version_string + elif self.parent_process_regex.search(line): + parent_process_match = self.parent_process_regex.search( + line) + self.parent_process_name = parent_process_match.group(1) + self.parent_process_id = parent_process_match.group(2) + elif line.startswith('Exception Type:'): + self.thread_exception = line[15:].strip() + continue + elif line.startswith('Exception Codes:'): + self.thread_exception_data = line[16:].strip() + continue + elif line.startswith('Exception Subtype:'): # iOS + self.thread_exception_data = line[18:].strip() + continue + elif line.startswith('Crashed Thread:'): + self.crashed_thread_idx = int(line[15:].strip().split()[0]) + continue + elif line.startswith('Triggered by Thread:'): # iOS + self.crashed_thread_idx = int(line[20:].strip().split()[0]) + continue + elif line.startswith('Report Version:'): + self.version = int(line[15:].strip()) + continue + elif line.startswith('System Profile:'): + parse_mode = PARSE_MODE_SYSTEM + continue + elif (line.startswith('Interval Since Last Report:') or + line.startswith('Crashes Since Last Report:') or + line.startswith('Per-App Interval Since Last Report:') or + line.startswith('Per-App Crashes Since Last Report:') or + line.startswith('Sleep/Wake UUID:') or + line.startswith('Anonymous UUID:')): + # ignore these + continue + elif line.startswith('Thread'): + thread_state_match = self.thread_state_regex.search(line) + if thread_state_match: + app_specific_backtrace = False + thread_state_match = self.thread_regex.search(line) + thread_idx = int(thread_state_match.group(1)) + parse_mode = PARSE_MODE_THREGS + thread = self.threads[thread_idx] + else: + thread_match = self.thread_regex.search(line) + if thread_match: + app_specific_backtrace = False + parse_mode = PARSE_MODE_THREAD + thread_idx = int(thread_match.group(1)) + thread = CrashLog.Thread(thread_idx, False) + continue + elif line.startswith('Binary Images:'): + parse_mode = PARSE_MODE_IMAGES + continue + elif line.startswith('Application Specific Backtrace'): + app_backtrace_match = self.app_backtrace_regex.search(line) + if app_backtrace_match: + parse_mode = PARSE_MODE_THREAD + app_specific_backtrace = True + idx = int(app_backtrace_match.group(1)) + thread = CrashLog.Thread(idx, True) + elif line.startswith('Last Exception Backtrace:'): # iOS + parse_mode = PARSE_MODE_THREAD + app_specific_backtrace = True + idx = 1 + thread = CrashLog.Thread(idx, True) + self.info_lines.append(line.strip()) + elif parse_mode == PARSE_MODE_THREAD: + if line.startswith('Thread'): + continue + if self.null_frame_regex.search(line): + print('warning: thread parser ignored null-frame: "%s"' % line) + continue + frame_match = self.frame_regex.search(line) + if frame_match: + (frame_id, frame_img_name, _, frame_img_version, _, + frame_addr, frame_ofs) = frame_match.groups() + ident = frame_img_name + thread.add_ident(ident) + if ident not in self.idents: + self.idents.append(ident) + thread.frames.append(CrashLog.Frame(int(frame_id), int( + frame_addr, 0), frame_ofs)) + else: + print('error: frame regex failed for line: "%s"' % line) + elif parse_mode == PARSE_MODE_IMAGES: + image_match = self.image_regex_uuid.search(line) + if image_match: + (img_lo, img_hi, img_name, _, img_version, _, + _, img_uuid, img_path) = image_match.groups() + image = CrashLog.DarwinImage(int(img_lo, 0), int(img_hi, 0), + img_name.strip(), + img_version.strip() + if img_version else "", + uuid.UUID(img_uuid), img_path, + self.verbose) + self.images.append(image) + else: + print("error: image regex failed for: %s" % line) + + elif parse_mode == PARSE_MODE_THREGS: + stripped_line = line.strip() + # "r12: 0x00007fff6b5939c8 r13: 0x0000000007000006 r14: 0x0000000000002a03 r15: 0x0000000000000c00" + reg_values = re.findall( + '([a-zA-Z0-9]+: 0[Xx][0-9a-fA-F]+) *', stripped_line) + for reg_value in reg_values: + # print 'reg_value = "%s"' % reg_value + (reg, value) = reg_value.split(': ') + # print 'reg = "%s"' % reg + # print 'value = "%s"' % value + thread.registers[reg.strip()] = int(value, 0) + elif parse_mode == PARSE_MODE_SYSTEM: + self.system_profile.append(line) + f.close() + + def dump(self): + print("Crash Log File: %s" % (self.path)) + if self.backtraces: + print("\nApplication Specific Backtraces:") + for thread in self.backtraces: + thread.dump(' ') + print("\nThreads:") + for thread in self.threads: + thread.dump(' ') + print("\nImages:") + for image in self.images: + image.dump(' ') + + def find_image_with_identifier(self, identifier): + for image in self.images: + if image.identifier == identifier: + return image + regex_text = '^.*\.%s$' % (re.escape(identifier)) + regex = re.compile(regex_text) + for image in self.images: + if regex.match(image.identifier): + return image + return None + + def create_target(self): + # print 'crashlog.create_target()...' + if self.target is None: + self.target = symbolication.Symbolicator.create_target(self) + if self.target: + return self.target + # We weren't able to open the main executable as, but we can still + # symbolicate + print('crashlog.create_target()...2') + if self.idents: + for ident in self.idents: + image = self.find_image_with_identifier(ident) + if image: + self.target = image.create_target() + if self.target: + return self.target # success + print('crashlog.create_target()...3') + for image in self.images: + self.target = image.create_target() + if self.target: + return self.target # success + print('crashlog.create_target()...4') + print('error: Unable to locate any executables from the crash log.') + print(' Try loading the executable into lldb before running crashlog') + print(' and/or make sure the .dSYM bundles can be found by Spotlight.') + return self.target + + def get_target(self): + return self.target + + +def usage(): + print("Usage: lldb-symbolicate.py [-n name] executable-image") + sys.exit(0) + + +class Interactive(cmd.Cmd): + '''Interactive prompt for analyzing one or more Darwin crash logs, type "help" to see a list of supported commands.''' + image_option_parser = None + + def __init__(self, crash_logs): + cmd.Cmd.__init__(self) + self.use_rawinput = False + self.intro = 'Interactive crashlogs prompt, type "help" to see a list of supported commands.' + self.crash_logs = crash_logs + self.prompt = '% ' + + def default(self, line): + '''Catch all for unknown command, which will exit the interpreter.''' + print("uknown 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_symbolicate(self, line): + description = '''Symbolicate one or more darwin crash log files by index to provide source file and line information, + inlined stack frames back to the concrete functions, and disassemble the location of the crash + for the first frame of the crashed thread.''' + option_parser = CreateSymbolicateCrashLogOptions( + 'symbolicate', description, False) + command_args = shlex.split(line) + try: + (options, args) = option_parser.parse_args(command_args) + except: + return + + if args: + # We have arguments, they must valid be crash log file indexes + for idx_str in args: + idx = int(idx_str) + if idx < len(self.crash_logs): + SymbolicateCrashLog(self.crash_logs[idx], options) + else: + print('error: crash log index %u is out of range' % (idx)) + else: + # No arguments, symbolicate all crash logs using the options + # provided + for idx in range(len(self.crash_logs)): + SymbolicateCrashLog(self.crash_logs[idx], options) + + def do_list(self, line=None): + '''Dump a list of all crash logs that are currently loaded. + + USAGE: list''' + print('%u crash logs are loaded:' % len(self.crash_logs)) + for (crash_log_idx, crash_log) in enumerate(self.crash_logs): + print('[%u] = %s' % (crash_log_idx, crash_log.path)) + + def do_image(self, line): + '''Dump information about one or more binary images in the crash log given an image basename, or all images if no arguments are provided.''' + usage = "usage: %prog [options] <PATH> [PATH ...]" + description = '''Dump information about one or more images in all crash logs. The <PATH> can be a full path, image basename, or partial path. Searches are done in this order.''' + command_args = shlex.split(line) + if not self.image_option_parser: + self.image_option_parser = optparse.OptionParser( + description=description, prog='image', usage=usage) + self.image_option_parser.add_option( + '-a', + '--all', + action='store_true', + help='show all images', + default=False) + try: + (options, args) = self.image_option_parser.parse_args(command_args) + except: + return + + if args: + for image_path in args: + fullpath_search = image_path[0] == '/' + for (crash_log_idx, crash_log) in enumerate(self.crash_logs): + matches_found = 0 + for (image_idx, image) in enumerate(crash_log.images): + if fullpath_search: + if image.get_resolved_path() == image_path: + matches_found += 1 + print('[%u] ' % (crash_log_idx), image) + else: + image_basename = image.get_resolved_path_basename() + if image_basename == image_path: + matches_found += 1 + print('[%u] ' % (crash_log_idx), image) + if matches_found == 0: + for (image_idx, image) in enumerate(crash_log.images): + resolved_image_path = image.get_resolved_path() + if resolved_image_path and string.find( + image.get_resolved_path(), image_path) >= 0: + print('[%u] ' % (crash_log_idx), image) + else: + for crash_log in self.crash_logs: + for (image_idx, image) in enumerate(crash_log.images): + print('[%u] %s' % (image_idx, image)) + return False + + +def interactive_crashlogs(options, args): + crash_log_files = list() + for arg in args: + for resolved_path in glob.glob(arg): + crash_log_files.append(resolved_path) + + crash_logs = list() + for crash_log_file in crash_log_files: + # print 'crash_log_file = "%s"' % crash_log_file + crash_log = CrashLog(crash_log_file, options.verbose) + if crash_log.error: + print(crash_log.error) + continue + if options.debug: + crash_log.dump() + if not crash_log.images: + print('error: no images in crash log "%s"' % (crash_log)) + continue + else: + crash_logs.append(crash_log) + + interpreter = Interactive(crash_logs) + # List all crash logs that were imported + interpreter.do_list() + interpreter.cmdloop() + + +def save_crashlog(debugger, command, exe_ctx, result, dict): + usage = "usage: %prog [options] <output-path>" + description = '''Export the state of current target into a crashlog file''' + parser = optparse.OptionParser( + description=description, + prog='save_crashlog', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + try: + (options, args) = parser.parse_args(shlex.split(command)) + except: + result.PutCString("error: invalid options") + return + if len(args) != 1: + result.PutCString( + "error: invalid arguments, a single output file is the only valid argument") + return + out_file = open(args[0], 'w') + if not out_file: + result.PutCString( + "error: failed to open file '%s' for writing...", + args[0]) + return + target = exe_ctx.target + if target: + identifier = target.executable.basename + process = exe_ctx.process + if process: + pid = process.id + if pid != lldb.LLDB_INVALID_PROCESS_ID: + out_file.write( + 'Process: %s [%u]\n' % + (identifier, pid)) + out_file.write('Path: %s\n' % (target.executable.fullpath)) + out_file.write('Identifier: %s\n' % (identifier)) + out_file.write('\nDate/Time: %s\n' % + (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + out_file.write( + 'OS Version: Mac OS X %s (%s)\n' % + (platform.mac_ver()[0], subprocess.check_output('sysctl -n kern.osversion', shell=True).decode("utf-8"))) + out_file.write('Report Version: 9\n') + for thread_idx in range(process.num_threads): + thread = process.thread[thread_idx] + out_file.write('\nThread %u:\n' % (thread_idx)) + for (frame_idx, frame) in enumerate(thread.frames): + frame_pc = frame.pc + frame_offset = 0 + if frame.function: + block = frame.GetFrameBlock() + block_range = block.range[frame.addr] + if block_range: + block_start_addr = block_range[0] + frame_offset = frame_pc - block_start_addr.load_addr + else: + frame_offset = frame_pc - frame.function.addr.load_addr + elif frame.symbol: + frame_offset = frame_pc - frame.symbol.addr.load_addr + out_file.write( + '%-3u %-32s 0x%16.16x %s' % + (frame_idx, frame.module.file.basename, frame_pc, frame.name)) + if frame_offset > 0: + out_file.write(' + %u' % (frame_offset)) + line_entry = frame.line_entry + if line_entry: + if options.verbose: + # This will output the fullpath + line + column + out_file.write(' %s' % (line_entry)) + else: + out_file.write( + ' %s:%u' % + (line_entry.file.basename, line_entry.line)) + column = line_entry.column + if column: + out_file.write(':%u' % (column)) + out_file.write('\n') + + out_file.write('\nBinary Images:\n') + for module in target.modules: + text_segment = module.section['__TEXT'] + if text_segment: + text_segment_load_addr = text_segment.GetLoadAddress(target) + if text_segment_load_addr != lldb.LLDB_INVALID_ADDRESS: + text_segment_end_load_addr = text_segment_load_addr + text_segment.size + identifier = module.file.basename + module_version = '???' + module_version_array = module.GetVersion() + if module_version_array: + module_version = '.'.join( + map(str, module_version_array)) + out_file.write( + ' 0x%16.16x - 0x%16.16x %s (%s - ???) <%s> %s\n' % + (text_segment_load_addr, + text_segment_end_load_addr, + identifier, + module_version, + module.GetUUIDString(), + module.file.fullpath)) + out_file.close() + else: + result.PutCString("error: invalid target") + + +def Symbolicate(debugger, command, result, dict): + try: + SymbolicateCrashLogs(shlex.split(command)) + except: + result.PutCString("error: python exception %s" % sys.exc_info()[0]) + + +def SymbolicateCrashLog(crash_log, options): + if crash_log.error: + print(crash_log.error) + return + if options.debug: + crash_log.dump() + if not crash_log.images: + print('error: no images in crash log') + return + + if options.dump_image_list: + print("Binary Images:") + for image in crash_log.images: + if options.verbose: + print(image.debug_dump()) + else: + print(image) + + target = crash_log.create_target() + if not target: + return + exe_module = target.GetModuleAtIndex(0) + images_to_load = list() + loaded_images = list() + if options.load_all_images: + # --load-all option was specified, load everything up + for image in crash_log.images: + images_to_load.append(image) + else: + # Only load the images found in stack frames for the crashed threads + if options.crashed_only: + for thread in crash_log.threads: + if thread.did_crash(): + for ident in thread.idents: + images = crash_log.find_images_with_identifier(ident) + if images: + for image in images: + images_to_load.append(image) + else: + print('error: can\'t find image for identifier "%s"' % ident) + else: + for ident in crash_log.idents: + images = crash_log.find_images_with_identifier(ident) + if images: + for image in images: + images_to_load.append(image) + else: + print('error: can\'t find image for identifier "%s"' % ident) + + for image in images_to_load: + if image not in loaded_images: + err = image.add_module(target) + if err: + print(err) + else: + # print 'loaded %s' % image + loaded_images.append(image) + + if crash_log.backtraces: + for thread in crash_log.backtraces: + thread.dump_symbolicated(crash_log, options) + print() + + for thread in crash_log.threads: + thread.dump_symbolicated(crash_log, options) + print() + + +def CreateSymbolicateCrashLogOptions( + command_name, + description, + add_interactive_options): + usage = "usage: %prog [options] <FILE> [FILE ...]" + option_parser = optparse.OptionParser( + description=description, prog='crashlog', usage=usage) + option_parser.add_option( + '--verbose', + '-v', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + option_parser.add_option( + '--debug', + '-g', + action='store_true', + dest='debug', + help='display verbose debug logging', + default=False) + option_parser.add_option( + '--load-all', + '-a', + action='store_true', + dest='load_all_images', + help='load all executable images, not just the images found in the crashed stack frames', + default=False) + option_parser.add_option( + '--images', + action='store_true', + dest='dump_image_list', + help='show image list', + default=False) + option_parser.add_option( + '--debug-delay', + type='int', + dest='debug_delay', + metavar='NSEC', + help='pause for NSEC seconds for debugger', + default=0) + option_parser.add_option( + '--crashed-only', + '-c', + action='store_true', + dest='crashed_only', + help='only symbolicate the crashed thread', + default=False) + option_parser.add_option( + '--disasm-depth', + '-d', + type='int', + dest='disassemble_depth', + help='set the depth in stack frames that should be disassembled (default is 1)', + default=1) + option_parser.add_option( + '--disasm-all', + '-D', + action='store_true', + dest='disassemble_all_threads', + help='enabled disassembly of frames on all threads (not just the crashed thread)', + default=False) + option_parser.add_option( + '--disasm-before', + '-B', + type='int', + dest='disassemble_before', + help='the number of instructions to disassemble before the frame PC', + default=4) + option_parser.add_option( + '--disasm-after', + '-A', + type='int', + dest='disassemble_after', + help='the number of instructions to disassemble after the frame PC', + default=4) + option_parser.add_option( + '--source-context', + '-C', + type='int', + metavar='NLINES', + dest='source_context', + help='show NLINES source lines of source context (default = 4)', + default=4) + option_parser.add_option( + '--source-frames', + type='int', + metavar='NFRAMES', + dest='source_frames', + help='show source for NFRAMES (default = 4)', + default=4) + option_parser.add_option( + '--source-all', + action='store_true', + dest='source_all', + help='show source for all threads, not just the crashed thread', + default=False) + if add_interactive_options: + option_parser.add_option( + '-i', + '--interactive', + action='store_true', + help='parse all crash logs and enter interactive mode', + default=False) + return option_parser + + +def SymbolicateCrashLogs(command_args): + description = '''Symbolicate one or more darwin crash log files to provide source file and line information, +inlined stack frames back to the concrete functions, and disassemble the location of the crash +for the first frame of the crashed thread. +If this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter +for use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been +created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows +you to explore the program as if it were stopped at the locations described in the crash log and functions can +be disassembled and lookups can be performed using the addresses found in the crash log.''' + option_parser = CreateSymbolicateCrashLogOptions( + 'crashlog', description, True) + try: + (options, args) = option_parser.parse_args(command_args) + except: + return + + if options.debug: + print('command_args = %s' % command_args) + print('options', options) + print('args', args) + + if options.debug_delay > 0: + print("Waiting %u seconds for debugger to attach..." % options.debug_delay) + time.sleep(options.debug_delay) + error = lldb.SBError() + + if args: + if options.interactive: + interactive_crashlogs(options, args) + else: + for crash_log_file in args: + crash_log = CrashLog(crash_log_file, options.verbose) + SymbolicateCrashLog(crash_log, options) +if __name__ == '__main__': + # Create a new debugger instance + lldb.debugger = lldb.SBDebugger.Create() + SymbolicateCrashLogs(sys.argv[1:]) + lldb.SBDebugger.Destroy(lldb.debugger) +elif getattr(lldb, 'debugger', None): + lldb.debugger.HandleCommand( + 'command script add -f lldb.macosx.crashlog.Symbolicate crashlog') + lldb.debugger.HandleCommand( + 'command script add -f lldb.macosx.crashlog.save_crashlog save_crashlog') + print('"crashlog" and "save_crashlog" command installed, use the "--help" option for detailed help') diff --git a/gnu/llvm/lldb/examples/python/delta.py b/gnu/llvm/lldb/examples/python/delta.py new file mode 100755 index 00000000000..1a1f060c5e5 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/delta.py @@ -0,0 +1,135 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# This module will enable GDB remote packet logging when the +# 'start_gdb_log' command is called with a filename to log to. When the +# 'stop_gdb_log' command is called, it will disable the logging and +# print out statistics about how long commands took to execute and also +# will primnt ou +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command. This can be done from the LLDB command line: +# (lldb) command script import /path/to/gdbremote.py +# Or it can be added to your ~/.lldbinit file so this module is always +# available. +#---------------------------------------------------------------------- + +from __future__ import print_function + +import optparse +import os +import shlex +import re +import tempfile + + +def start_gdb_log(debugger, command, result, dict): + '''Start logging GDB remote packets by enabling logging with timestamps and + thread safe logging. Follow a call to this function with a call to "stop_gdb_log" + in order to dump out the commands.''' + global log_file + if log_file: + result.PutCString( + 'error: logging is already in progress with file "%s"', + log_file) + else: + args_len = len(args) + if args_len == 0: + log_file = tempfile.mktemp() + elif len(args) == 1: + log_file = args[0] + + if log_file: + debugger.HandleCommand( + 'log enable --threadsafe --timestamp --file "%s" gdb-remote packets' % + log_file) + result.PutCString( + "GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." % + log_file) + return + + result.PutCString('error: invalid log file path') + result.PutCString(usage) + + +def parse_time_log(debugger, command, result, dict): + # Any commands whose names might be followed by more valid C identifier + # characters must be listed here + command_args = shlex.split(command) + parse_time_log_args(command_args) + + +def parse_time_log_args(command_args): + usage = "usage: parse_time_log [options] [<LOGFILEPATH>]" + description = '''Parse a log file that contains timestamps and convert the timestamps to delta times between log lines.''' + parser = optparse.OptionParser( + description=description, + prog='parse_time_log', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + for log_file in args: + parse_log_file(log_file, options) + + +def parse_log_file(file, options): + '''Parse a log file that was contains timestamps. These logs are typically + generated using: + (lldb) log enable --threadsafe --timestamp --file <FILE> .... + + This log file will contain timestamps and this function will then normalize + those packets to be relative to the first value timestamp that is found and + show delta times between log lines and also keep track of how long it takes + for GDB remote commands to make a send/receive round trip. This can be + handy when trying to figure out why some operation in the debugger is taking + a long time during a preset set of debugger commands.''' + + print('#----------------------------------------------------------------------') + print("# Log file: '%s'" % file) + print('#----------------------------------------------------------------------') + + timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$') + + base_time = 0.0 + last_time = 0.0 + file = open(file) + lines = file.read().splitlines() + for line in lines: + match = timestamp_regex.match(line) + if match: + curr_time = float(match.group(2)) + delta = 0.0 + if base_time: + delta = curr_time - last_time + else: + base_time = curr_time + + print('%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3))) + last_time = curr_time + else: + print(line) + + +if __name__ == '__main__': + import sys + parse_time_log_args(sys.argv[1:]) + +else: + import lldb + if lldb.debugger: + # This initializer is being run from LLDB in the embedded command interpreter + # Add any commands contained in this module to LLDB + lldb.debugger.HandleCommand( + 'command script add -f delta.parse_time_log parse_time_log') + print('The "parse_time_log" command is now installed and ready for use, type "parse_time_log --help" for more information') diff --git a/gnu/llvm/lldb/examples/python/diagnose_nsstring.py b/gnu/llvm/lldb/examples/python/diagnose_nsstring.py new file mode 100644 index 00000000000..4a8eee3c2dd --- /dev/null +++ b/gnu/llvm/lldb/examples/python/diagnose_nsstring.py @@ -0,0 +1,185 @@ +# This implements the "diagnose-nsstring" command, usually installed in the debug session like +# command script import lldb.diagnose +# it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the +# decisions it did and providing some useful context information that can +# be used for improving the formatter + +from __future__ import print_function + +import lldb + + +def read_memory(process, location, size): + data = "" + error = lldb.SBError() + for x in range(0, size - 1): + byte = process.ReadUnsignedFromMemory(x + location, 1, error) + if error.fail: + data = data + "err%s" % "" if x == size - 2 else ":" + else: + try: + data = data + "0x%x" % byte + if byte == 0: + data = data + "(\\0)" + elif byte == 0xa: + data = data + "(\\a)" + elif byte == 0xb: + data = data + "(\\b)" + elif byte == 0xc: + data = data + "(\\c)" + elif byte == '\n': + data = data + "(\\n)" + else: + data = data + "(%s)" % chr(byte) + if x < size - 2: + data = data + ":" + except Exception as e: + print(e) + return data + + +def diagnose_nsstring_Command_Impl(debugger, command, result, internal_dict): + """ + A command to diagnose the LLDB NSString data formatter + invoke as + (lldb) diagnose-nsstring <expr returning NSString> + e.g. + (lldb) diagnose-nsstring @"Hello world" + """ + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + if not target.IsValid() or not process.IsValid(): + return "unable to get target/process - cannot proceed" + options = lldb.SBExpressionOptions() + options.SetFetchDynamicValue() + error = lldb.SBError() + if frame.IsValid(): + nsstring = frame.EvaluateExpression(command, options) + else: + nsstring = target.EvaluateExpression(command, options) + print(str(nsstring), file=result) + nsstring_address = nsstring.GetValueAsUnsigned(0) + if nsstring_address == 0: + return "unable to obtain the string - cannot proceed" + expression = "\ +struct $__lldb__notInlineMutable {\ + char* buffer;\ + signed long length;\ + signed long capacity;\ + unsigned int hasGap:1;\ + unsigned int isFixedCapacity:1;\ + unsigned int isExternalMutable:1;\ + unsigned int capacityProvidedExternally:1;\n\ +#if __LP64__\n\ + unsigned long desiredCapacity:60;\n\ +#else\n\ + unsigned long desiredCapacity:28;\n\ +#endif\n\ + void* contentsAllocator;\ +};\ +\ +struct $__lldb__CFString {\ + void* _cfisa;\ + uint8_t _cfinfo[4];\ + uint32_t _rc;\ + union {\ + struct __inline1 {\ + signed long length;\ + } inline1;\ + struct __notInlineImmutable1 {\ + char* buffer;\ + signed long length;\ + void* contentsDeallocator;\ + } notInlineImmutable1;\ + struct __notInlineImmutable2 {\ + char* buffer;\ + void* contentsDeallocator;\ + } notInlineImmutable2;\ + struct $__lldb__notInlineMutable notInlineMutable;\ + } variants;\ +};\ +" + + expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address + # print expression + dumped = target.EvaluateExpression(expression, options) + print(str(dumped), file=result) + + little_endian = (target.byte_order == lldb.eByteOrderLittle) + ptr_size = target.addr_size + + info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex( + 0 if little_endian else 3).GetValueAsUnsigned(0) + is_mutable = (info_bits & 1) == 1 + is_inline = (info_bits & 0x60) == 0 + has_explicit_length = (info_bits & (1 | 4)) != 4 + is_unicode = (info_bits & 0x10) == 0x10 + is_special = ( + nsstring.GetDynamicValue( + lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2") + has_null = (info_bits & 8) == 8 + + print("\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \ + (info_bits, "yes" if is_mutable else "no", "yes" if is_inline else "no", "yes" if has_explicit_length else "no", "yes" if is_unicode else "no", "yes" if is_special else "no", "yes" if has_null else "no"), file=result) + + explicit_length_offset = 0 + if not has_null and has_explicit_length and not is_special: + explicit_length_offset = 2 * ptr_size + if is_mutable and not is_inline: + explicit_length_offset = explicit_length_offset + ptr_size + elif is_inline: + pass + elif not is_inline and not is_mutable: + explicit_length_offset = explicit_length_offset + ptr_size + else: + explicit_length_offset = 0 + + if explicit_length_offset == 0: + print("There is no explicit length marker - skipping this step\n", file=result) + else: + explicit_length_offset = nsstring_address + explicit_length_offset + explicit_length = process.ReadUnsignedFromMemory( + explicit_length_offset, 4, error) + print("Explicit length location is at 0x%x - read value is %d\n" % ( + explicit_length_offset, explicit_length), file=result) + + if is_mutable: + location = 2 * ptr_size + nsstring_address + location = process.ReadPointerFromMemory(location, error) + elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable: + location = 3 * ptr_size + nsstring_address + elif is_unicode: + location = 2 * ptr_size + nsstring_address + if is_inline: + if not has_explicit_length: + print("Unicode & Inline & !Explicit is a new combo - no formula for it", file=result) + else: + location += ptr_size + else: + location = process.ReadPointerFromMemory(location, error) + elif is_special: + location = nsstring_address + ptr_size + 4 + elif is_inline: + location = 2 * ptr_size + nsstring_address + if not has_explicit_length: + location += 1 + else: + location = 2 * ptr_size + nsstring_address + location = process.ReadPointerFromMemory(location, error) + print("Expected data location: 0x%x\n" % (location), file=result) + print("1K of data around location: %s\n" % read_memory( + process, location, 1024), file=result) + print("5K of data around string pointer: %s\n" % read_memory( + process, nsstring_address, 1024 * 5), file=result) + + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand( + "command script add -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" % + __name__) + print('The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.') + +__lldb_init_module(lldb.debugger, None) +__lldb_init_module = None diff --git a/gnu/llvm/lldb/examples/python/diagnose_unwind.py b/gnu/llvm/lldb/examples/python/diagnose_unwind.py new file mode 100644 index 00000000000..db3ff1952f3 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/diagnose_unwind.py @@ -0,0 +1,314 @@ +# This implements the "diagnose-unwind" command, usually installed +# in the debug session like +# command script import lldb.diagnose +# it is used when lldb's backtrace fails -- it collects and prints +# information about the stack frames, and tries an alternate unwind +# algorithm, that will help to understand why lldb's unwind algorithm +# did not succeed. +from __future__ import print_function + +import optparse +import lldb +import re +import shlex + +# Print the frame number, pc, frame pointer, module UUID and function name +# Returns the SBModule that contains the PC, if it could be found + + +def backtrace_print_frame(target, frame_num, addr, fp): + process = target.GetProcess() + addr_for_printing = addr + addr_width = process.GetAddressByteSize() * 2 + if frame_num > 0: + addr = addr - 1 + + sbaddr = lldb.SBAddress() + try: + sbaddr.SetLoadAddress(addr, target) + module_description = "" + if sbaddr.GetModule(): + module_filename = "" + module_uuid_str = sbaddr.GetModule().GetUUIDString() + if module_uuid_str is None: + module_uuid_str = "" + if sbaddr.GetModule().GetFileSpec(): + module_filename = sbaddr.GetModule().GetFileSpec().GetFilename() + if module_filename is None: + module_filename = "" + if module_uuid_str != "" or module_filename != "": + module_description = '%s %s' % ( + module_filename, module_uuid_str) + except Exception: + print('%2d: pc==0x%-*x fp==0x%-*x' % (frame_num, addr_width, addr_for_printing, addr_width, fp)) + return + + sym_ctx = target.ResolveSymbolContextForAddress( + sbaddr, lldb.eSymbolContextEverything) + if sym_ctx.IsValid() and sym_ctx.GetSymbol().IsValid(): + function_start = sym_ctx.GetSymbol().GetStartAddress().GetLoadAddress(target) + offset = addr - function_start + print('%2d: pc==0x%-*x fp==0x%-*x %s %s + %d' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description, sym_ctx.GetSymbol().GetName(), offset)) + else: + print('%2d: pc==0x%-*x fp==0x%-*x %s' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description)) + return sbaddr.GetModule() + +# A simple stack walk algorithm that follows the frame chain. +# Returns a two-element list; the first element is a list of modules +# seen and the second element is a list of addresses seen during the backtrace. + + +def simple_backtrace(debugger): + target = debugger.GetSelectedTarget() + process = target.GetProcess() + cur_thread = process.GetSelectedThread() + + initial_fp = cur_thread.GetFrameAtIndex(0).GetFP() + + # If the pseudoreg "fp" isn't recognized, on arm hardcode to r7 which is + # correct for Darwin programs. + if initial_fp == lldb.LLDB_INVALID_ADDRESS and target.triple[0:3] == "arm": + for reggroup in cur_thread.GetFrameAtIndex(1).registers: + if reggroup.GetName() == "General Purpose Registers": + for reg in reggroup: + if reg.GetName() == "r7": + initial_fp = int(reg.GetValue(), 16) + + module_list = [] + address_list = [cur_thread.GetFrameAtIndex(0).GetPC()] + this_module = backtrace_print_frame( + target, 0, cur_thread.GetFrameAtIndex(0).GetPC(), initial_fp) + print_stack_frame(process, initial_fp) + print("") + if this_module is not None: + module_list.append(this_module) + if cur_thread.GetNumFrames() < 2: + return [module_list, address_list] + + cur_fp = process.ReadPointerFromMemory(initial_fp, lldb.SBError()) + cur_pc = process.ReadPointerFromMemory( + initial_fp + process.GetAddressByteSize(), lldb.SBError()) + + frame_num = 1 + + while cur_pc != 0 and cur_fp != 0 and cur_pc != lldb.LLDB_INVALID_ADDRESS and cur_fp != lldb.LLDB_INVALID_ADDRESS: + address_list.append(cur_pc) + this_module = backtrace_print_frame(target, frame_num, cur_pc, cur_fp) + print_stack_frame(process, cur_fp) + print("") + if this_module is not None: + module_list.append(this_module) + frame_num = frame_num + 1 + next_pc = 0 + next_fp = 0 + if target.triple[ + 0:6] == "x86_64" or target.triple[ + 0:4] == "i386" or target.triple[ + 0:3] == "arm": + error = lldb.SBError() + next_pc = process.ReadPointerFromMemory( + cur_fp + process.GetAddressByteSize(), error) + if not error.Success(): + next_pc = 0 + next_fp = process.ReadPointerFromMemory(cur_fp, error) + if not error.Success(): + next_fp = 0 + # Clear the 0th bit for arm frames - this indicates it is a thumb frame + if target.triple[0:3] == "arm" and (next_pc & 1) == 1: + next_pc = next_pc & ~1 + cur_pc = next_pc + cur_fp = next_fp + this_module = backtrace_print_frame(target, frame_num, cur_pc, cur_fp) + print_stack_frame(process, cur_fp) + print("") + if this_module is not None: + module_list.append(this_module) + return [module_list, address_list] + + +def print_stack_frame(process, fp): + if fp == 0 or fp == lldb.LLDB_INVALID_ADDRESS or fp == 1: + return + addr_size = process.GetAddressByteSize() + addr = fp - (2 * addr_size) + i = 0 + outline = "Stack frame from $fp-%d: " % (2 * addr_size) + error = lldb.SBError() + try: + while i < 5 and error.Success(): + address = process.ReadPointerFromMemory( + addr + (i * addr_size), error) + outline += " 0x%x" % address + i += 1 + print(outline) + except Exception: + return + + +def diagnose_unwind(debugger, command, result, dict): + """ + Gather diagnostic information to help debug incorrect unwind (backtrace) + behavior in lldb. When there is a backtrace that doesn't look + correct, run this command with the correct thread selected and a + large amount of diagnostic information will be printed, it is likely + to be helpful when reporting the problem. + """ + + command_args = shlex.split(command) + parser = create_diagnose_unwind_options() + try: + (options, args) = parser.parse_args(command_args) + except: + return + target = debugger.GetSelectedTarget() + if target: + process = target.GetProcess() + if process: + thread = process.GetSelectedThread() + if thread: + lldb_versions_match = re.search( + r'[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?', + debugger.GetVersionString()) + lldb_version = 0 + lldb_minor = 0 + if len(lldb_versions_match.groups() + ) >= 1 and lldb_versions_match.groups()[0]: + lldb_major = int(lldb_versions_match.groups()[0]) + if len(lldb_versions_match.groups() + ) >= 5 and lldb_versions_match.groups()[4]: + lldb_minor = int(lldb_versions_match.groups()[4]) + + modules_seen = [] + addresses_seen = [] + + print('LLDB version %s' % debugger.GetVersionString()) + print('Unwind diagnostics for thread %d' % thread.GetIndexID()) + print("") + print("=============================================================================================") + print("") + print("OS plugin setting:") + debugger.HandleCommand( + "settings show target.process.python-os-plugin-path") + print("") + print("Live register context:") + thread.SetSelectedFrame(0) + debugger.HandleCommand("register read") + print("") + print("=============================================================================================") + print("") + print("lldb's unwind algorithm:") + print("") + frame_num = 0 + for frame in thread.frames: + if not frame.IsInlined(): + this_module = backtrace_print_frame( + target, frame_num, frame.GetPC(), frame.GetFP()) + print_stack_frame(process, frame.GetFP()) + print("") + if this_module is not None: + modules_seen.append(this_module) + addresses_seen.append(frame.GetPC()) + frame_num = frame_num + 1 + print("") + print("=============================================================================================") + print("") + print("Simple stack walk algorithm:") + print("") + (module_list, address_list) = simple_backtrace(debugger) + if module_list and module_list is not None: + modules_seen += module_list + if address_list and address_list is not None: + addresses_seen = set(addresses_seen) + addresses_seen.update(set(address_list)) + + print("") + print("=============================================================================================") + print("") + print("Modules seen in stack walks:") + print("") + modules_already_seen = set() + for module in modules_seen: + if module is not None and module.GetFileSpec().GetFilename() is not None: + if not module.GetFileSpec().GetFilename() in modules_already_seen: + debugger.HandleCommand( + 'image list %s' % + module.GetFileSpec().GetFilename()) + modules_already_seen.add( + module.GetFileSpec().GetFilename()) + + print("") + print("=============================================================================================") + print("") + print("Disassembly ofaddresses seen in stack walks:") + print("") + additional_addresses_to_disassemble = addresses_seen + for frame in thread.frames: + if not frame.IsInlined(): + print("--------------------------------------------------------------------------------------") + print("") + print("Disassembly of %s, frame %d, address 0x%x" % (frame.GetFunctionName(), frame.GetFrameID(), frame.GetPC())) + print("") + if target.triple[ + 0:6] == "x86_64" or target.triple[ + 0:4] == "i386": + debugger.HandleCommand( + 'disassemble -F att -a 0x%x' % frame.GetPC()) + else: + debugger.HandleCommand( + 'disassemble -a 0x%x' % + frame.GetPC()) + if frame.GetPC() in additional_addresses_to_disassemble: + additional_addresses_to_disassemble.remove( + frame.GetPC()) + + for address in list(additional_addresses_to_disassemble): + print("--------------------------------------------------------------------------------------") + print("") + print("Disassembly of 0x%x" % address) + print("") + if target.triple[ + 0:6] == "x86_64" or target.triple[ + 0:4] == "i386": + debugger.HandleCommand( + 'disassemble -F att -a 0x%x' % address) + else: + debugger.HandleCommand('disassemble -a 0x%x' % address) + + print("") + print("=============================================================================================") + print("") + additional_addresses_to_show_unwind = addresses_seen + for frame in thread.frames: + if not frame.IsInlined(): + print("--------------------------------------------------------------------------------------") + print("") + print("Unwind instructions for %s, frame %d" % (frame.GetFunctionName(), frame.GetFrameID())) + print("") + debugger.HandleCommand( + 'image show-unwind -a "0x%x"' % frame.GetPC()) + if frame.GetPC() in additional_addresses_to_show_unwind: + additional_addresses_to_show_unwind.remove( + frame.GetPC()) + + for address in list(additional_addresses_to_show_unwind): + print("--------------------------------------------------------------------------------------") + print("") + print("Unwind instructions for 0x%x" % address) + print("") + debugger.HandleCommand( + 'image show-unwind -a "0x%x"' % address) + + +def create_diagnose_unwind_options(): + usage = "usage: %prog" + description = '''Print diagnostic information about a thread backtrace which will help to debug unwind problems''' + parser = optparse.OptionParser( + description=description, + prog='diagnose_unwind', + usage=usage) + return parser + +lldb.debugger.HandleCommand( + 'command script add -f %s.diagnose_unwind diagnose-unwind' % + __name__) +print('The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.') diff --git a/gnu/llvm/lldb/examples/python/dict_utils.py b/gnu/llvm/lldb/examples/python/dict_utils.py new file mode 100755 index 00000000000..7cdd0ac6f3c --- /dev/null +++ b/gnu/llvm/lldb/examples/python/dict_utils.py @@ -0,0 +1,62 @@ + +class LookupDictionary(dict): + """ + a dictionary which can lookup value by key, or keys by value + """ + + def __init__(self, items=[]): + """items can be a list of pair_lists or a dictionary""" + dict.__init__(self, items) + + def get_keys_for_value(self, value, fail_value=None): + """find the key(s) as a list given a value""" + list_result = [item[0] for item in self.items() if item[1] == value] + if len(list_result) > 0: + return list_result + return fail_value + + def get_first_key_for_value(self, value, fail_value=None): + """return the first key of this dictionary given the value""" + list_result = [item[0] for item in self.items() if item[1] == value] + if len(list_result) > 0: + return list_result[0] + return fail_value + + def get_value(self, key, fail_value=None): + """find the value given a key""" + if key in self: + return self[key] + return fail_value + + +class Enum(LookupDictionary): + + def __init__(self, initial_value=0, items=[]): + """items can be a list of pair_lists or a dictionary""" + LookupDictionary.__init__(self, items) + self.value = initial_value + + def set_value(self, v): + v_typename = typeof(v).__name__ + if v_typename == 'str': + if str in self: + v = self[v] + else: + v = 0 + else: + self.value = v + + def get_enum_value(self): + return self.value + + def get_enum_name(self): + return self.__str__() + + def __str__(self): + s = self.get_first_key_for_value(self.value, None) + if s is None: + s = "%#8.8x" % self.value + return s + + def __repr__(self): + return self.__str__() diff --git a/gnu/llvm/lldb/examples/python/disasm-stress-test.py b/gnu/llvm/lldb/examples/python/disasm-stress-test.py new file mode 100755 index 00000000000..5d0ce96fbd6 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/disasm-stress-test.py @@ -0,0 +1,230 @@ +#!/usr/bin/python + +import argparse +import datetime +import re +import subprocess +import sys +import time + +parser = argparse.ArgumentParser( + description="Run an exhaustive test of the LLDB disassembler for a specific architecture.") + +parser.add_argument( + '--arch', + required=True, + action='store', + help='The architecture whose disassembler is to be tested') +parser.add_argument( + '--bytes', + required=True, + action='store', + type=int, + help='The byte width of instructions for that architecture') +parser.add_argument( + '--random', + required=False, + action='store_true', + help='Enables non-sequential testing') +parser.add_argument( + '--start', + required=False, + action='store', + type=int, + help='The first instruction value to test') +parser.add_argument( + '--skip', + required=False, + action='store', + type=int, + help='The interval between instructions to test') +parser.add_argument( + '--log', + required=False, + action='store', + help='A log file to write the most recent instruction being tested') +parser.add_argument( + '--time', + required=False, + action='store_true', + help='Every 100,000 instructions, print an ETA to standard out') +parser.add_argument( + '--lldb', + required=False, + action='store', + help='The path to LLDB.framework, if LLDB should be overridden') + +arguments = sys.argv[1:] + +arg_ns = parser.parse_args(arguments) + + +def AddLLDBToSysPathOnMacOSX(): + def GetLLDBFrameworkPath(): + lldb_path = subprocess.check_output(["xcrun", "-find", "lldb"]) + re_result = re.match("(.*)/Developer/usr/bin/lldb", lldb_path) + if re_result is None: + return None + xcode_contents_path = re_result.group(1) + return xcode_contents_path + "/SharedFrameworks/LLDB.framework" + + lldb_framework_path = GetLLDBFrameworkPath() + + if lldb_framework_path is None: + print("Couldn't find LLDB.framework") + sys.exit(-1) + + sys.path.append(lldb_framework_path + "/Resources/Python") + +if arg_ns.lldb is None: + AddLLDBToSysPathOnMacOSX() +else: + sys.path.append(arg_ns.lldb + "/Resources/Python") + +import lldb + +debugger = lldb.SBDebugger.Create() + +if debugger.IsValid() == False: + print("Couldn't create an SBDebugger") + sys.exit(-1) + +target = debugger.CreateTargetWithFileAndArch(None, arg_ns.arch) + +if target.IsValid() == False: + print("Couldn't create an SBTarget for architecture " + arg_ns.arch) + sys.exit(-1) + + +def ResetLogFile(log_file): + if log_file != sys.stdout: + log_file.seek(0) + + +def PrintByteArray(log_file, byte_array): + for byte in byte_array: + print(hex(byte) + " ", end=' ', file=log_file) + print(file=log_file) + + +class SequentialInstructionProvider: + + def __init__(self, byte_width, log_file, start=0, skip=1): + self.m_byte_width = byte_width + self.m_log_file = log_file + self.m_start = start + self.m_skip = skip + self.m_value = start + self.m_last = (1 << (byte_width * 8)) - 1 + + def PrintCurrentState(self, ret): + ResetLogFile(self.m_log_file) + print(self.m_value, file=self.m_log_file) + PrintByteArray(self.m_log_file, ret) + + def GetNextInstruction(self): + if self.m_value > self.m_last: + return None + ret = bytearray(self.m_byte_width) + for i in range(self.m_byte_width): + ret[self.m_byte_width - (i + 1)] = (self.m_value >> (i * 8)) & 255 + self.PrintCurrentState(ret) + self.m_value += self.m_skip + return ret + + def GetNumInstructions(self): + return (self.m_last - self.m_start) / self.m_skip + + def __iter__(self): + return self + + def next(self): + ret = self.GetNextInstruction() + if ret is None: + raise StopIteration + return ret + + +class RandomInstructionProvider: + + def __init__(self, byte_width, log_file): + self.m_byte_width = byte_width + self.m_log_file = log_file + self.m_random_file = open("/dev/random", 'r') + + def PrintCurrentState(self, ret): + ResetLogFile(self.m_log_file) + PrintByteArray(self.m_log_file, ret) + + def GetNextInstruction(self): + ret = bytearray(self.m_byte_width) + for i in range(self.m_byte_width): + ret[i] = self.m_random_file.read(1) + self.PrintCurrentState(ret) + return ret + + def __iter__(self): + return self + + def next(self): + ret = self.GetNextInstruction() + if ret is None: + raise StopIteration + return ret + +log_file = None + + +def GetProviderWithArguments(args): + global log_file + if args.log is not None: + log_file = open(args.log, 'w') + else: + log_file = sys.stdout + instruction_provider = None + if args.random: + instruction_provider = RandomInstructionProvider(args.bytes, log_file) + else: + start = 0 + skip = 1 + if args.start is not None: + start = args.start + if args.skip is not None: + skip = args.skip + instruction_provider = SequentialInstructionProvider( + args.bytes, log_file, start, skip) + return instruction_provider + +instruction_provider = GetProviderWithArguments(arg_ns) + +fake_address = lldb.SBAddress() + +actually_time = arg_ns.time and not arg_ns.random + +if actually_time: + num_instructions_logged = 0 + total_num_instructions = instruction_provider.GetNumInstructions() + start_time = time.time() + +for inst_bytes in instruction_provider: + if actually_time: + if (num_instructions_logged != 0) and ( + num_instructions_logged % 100000 == 0): + curr_time = time.time() + elapsed_time = curr_time - start_time + remaining_time = float( + total_num_instructions - num_instructions_logged) * ( + float(elapsed_time) / float(num_instructions_logged)) + print(str(datetime.timedelta(seconds=remaining_time))) + num_instructions_logged = num_instructions_logged + 1 + inst_list = target.GetInstructions(fake_address, inst_bytes) + if not inst_list.IsValid(): + print("Invalid instruction list", file=log_file) + continue + inst = inst_list.GetInstructionAtIndex(0) + if not inst.IsValid(): + print("Invalid instruction", file=log_file) + continue + instr_output_stream = lldb.SBStream() + inst.GetDescription(instr_output_stream) + print(instr_output_stream.GetData(), file=log_file) diff --git a/gnu/llvm/lldb/examples/python/disasm.py b/gnu/llvm/lldb/examples/python/disasm.py new file mode 100755 index 00000000000..819a0522388 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/disasm.py @@ -0,0 +1,126 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Developer/Library/PrivateFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Developer/Library/PrivateFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +import lldb +import os +import sys + + +def disassemble_instructions(insts): + for i in insts: + print(i) + + +def usage(): + print("Usage: disasm.py [-n name] executable-image") + print(" By default, it breaks at and disassembles the 'main' function.") + sys.exit(0) + +if len(sys.argv) == 2: + fname = 'main' + exe = sys.argv[1] +elif len(sys.argv) == 4: + if sys.argv[1] != '-n': + usage() + else: + fname = sys.argv[2] + exe = sys.argv[3] +else: + usage() + +# Create a new debugger instance +debugger = lldb.SBDebugger.Create() + +# When we step or continue, don't return from the function until the process +# stops. We do this by setting the async mode to false. +debugger.SetAsync(False) + +# Create a target from a file and arch +print("Creating a target for '%s'" % exe) + +target = debugger.CreateTargetWithFileAndArch(exe, lldb.LLDB_ARCH_DEFAULT) + +if target: + # If the target is valid set a breakpoint at main + main_bp = target.BreakpointCreateByName( + fname, target.GetExecutable().GetFilename()) + + print(main_bp) + + # Launch the process. Since we specified synchronous mode, we won't return + # from this function until we hit the breakpoint at main + process = target.LaunchSimple(None, None, os.getcwd()) + + # Make sure the launch went ok + if process: + # Print some simple process info + state = process.GetState() + print(process) + if state == lldb.eStateStopped: + # Get the first thread + thread = process.GetThreadAtIndex(0) + if thread: + # Print some simple thread info + print(thread) + # Get the first frame + frame = thread.GetFrameAtIndex(0) + if frame: + # Print some simple frame info + print(frame) + function = frame.GetFunction() + # See if we have debug info (a function) + if function: + # We do have a function, print some info for the + # function + print(function) + # Now get all instructions for this function and print + # them + insts = function.GetInstructions(target) + disassemble_instructions(insts) + else: + # See if we have a symbol in the symbol table for where + # we stopped + symbol = frame.GetSymbol() + if symbol: + # We do have a symbol, print some info for the + # symbol + print(symbol) + # Now get all instructions for this symbol and + # print them + insts = symbol.GetInstructions(target) + disassemble_instructions(insts) + + registerList = frame.GetRegisters() + print("Frame registers (size of register set = %d):" % registerList.GetSize()) + for value in registerList: + # print value + print("%s (number of children = %d):" % (value.GetName(), value.GetNumChildren())) + for child in value: + print("Name: ", child.GetName(), " Value: ", child.GetValue()) + + print("Hit the breakpoint at main, enter to continue and wait for program to exit or 'Ctrl-D'/'quit' to terminate the program") + next = sys.stdin.readline() + if not next or next.rstrip('\n') == 'quit': + print("Terminating the inferior process...") + process.Kill() + else: + # Now continue to the program exit + process.Continue() + # When we return from the above function we will hopefully be at the + # program exit. Print out some process info + print(process) + elif state == lldb.eStateExited: + print("Didn't hit the breakpoint at main, program has exited...") + else: + print("Unexpected process state: %s, killing process..." % debugger.StateAsCString(state)) + process.Kill() + + +lldb.SBDebugger.Terminate() diff --git a/gnu/llvm/lldb/examples/python/disassembly_mode.py b/gnu/llvm/lldb/examples/python/disassembly_mode.py new file mode 100644 index 00000000000..19647cc6555 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/disassembly_mode.py @@ -0,0 +1,48 @@ +""" Adds the 'toggle-disassembly' command to switch you into a disassembly only mode """ +import lldb + +class DisassemblyMode: + def __init__(self, debugger, unused): + self.dbg = debugger + self.interp = debugger.GetCommandInterpreter() + self.store_state() + self.mode_off = True + + def store_state(self): + self.dis_count = self.get_string_value("stop-disassembly-count") + self.dis_display = self.get_string_value("stop-disassembly-display") + self.before_count = self.get_string_value("stop-line-count-before") + self.after_count = self.get_string_value("stop-line-count-after") + + def get_string_value(self, setting): + result = lldb.SBCommandReturnObject() + self.interp.HandleCommand("settings show " + setting, result) + value = result.GetOutput().split(" = ")[1].rstrip("\n") + return value + + def set_value(self, setting, value): + result = lldb.SBCommandReturnObject() + self.interp.HandleCommand("settings set " + setting + " " + value, result) + + def __call__(self, debugger, command, exe_ctx, result): + if self.mode_off: + self.mode_off = False + self.store_state() + self.set_value("stop-disassembly-display","always") + self.set_value("stop-disassembly-count", "8") + self.set_value("stop-line-count-before", "0") + self.set_value("stop-line-count-after", "0") + result.AppendMessage("Disassembly mode on.") + else: + self.mode_off = True + self.set_value("stop-disassembly-display",self.dis_display) + self.set_value("stop-disassembly-count", self.dis_count) + self.set_value("stop-line-count-before", self.before_count) + self.set_value("stop-line-count-after", self.after_count) + result.AppendMessage("Disassembly mode off.") + + def get_short_help(self): + return "Toggles between a disassembly only mode and normal source mode\n" + +def __lldb_init_module(debugger, unused): + debugger.HandleCommand("command script add -c disassembly_mode.DisassemblyMode toggle-disassembly") diff --git a/gnu/llvm/lldb/examples/python/file_extract.py b/gnu/llvm/lldb/examples/python/file_extract.py new file mode 100755 index 00000000000..decbba0b299 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/file_extract.py @@ -0,0 +1,226 @@ +#! /usr/bin/env python + +import string +import struct +import sys + + +class FileExtract: + '''Decode binary data from a file''' + + def __init__(self, f, b='='): + '''Initialize with an open binary file and optional byte order''' + + self.file = f + self.byte_order = b + self.offsets = list() + + def set_byte_order(self, b): + '''Set the byte order, valid values are "big", "little", "swap", "native", "<", ">", "@", "="''' + if b == 'big': + self.byte_order = '>' + elif b == 'little': + self.byte_order = '<' + elif b == 'swap': + # swap what ever the current byte order is + self.byte_order = swap_unpack_char() + elif b == 'native': + self.byte_order = '=' + elif b == '<' or b == '>' or b == '@' or b == '=': + self.byte_order = b + else: + print("error: invalid byte order specified: '%s'" % b) + + def is_in_memory(self): + return False + + def seek(self, offset, whence=0): + if self.file: + return self.file.seek(offset, whence) + raise ValueError + + def tell(self): + if self.file: + return self.file.tell() + raise ValueError + + def read_size(self, byte_size): + s = self.file.read(byte_size) + if len(s) != byte_size: + return None + return s + + def push_offset_and_seek(self, offset): + '''Push the current file offset and seek to "offset"''' + self.offsets.append(self.file.tell()) + self.file.seek(offset, 0) + + def pop_offset_and_seek(self): + '''Pop a previously pushed file offset, or do nothing if there were no previously pushed offsets''' + if len(self.offsets) > 0: + self.file.seek(self.offsets.pop()) + + def get_sint8(self, fail_value=0): + '''Extract a single int8_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(1) + if s: + v, = struct.unpack(self.byte_order + 'b', s) + return v + else: + return fail_value + + def get_uint8(self, fail_value=0): + '''Extract a single uint8_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(1) + if s: + v, = struct.unpack(self.byte_order + 'B', s) + return v + else: + return fail_value + + def get_sint16(self, fail_value=0): + '''Extract a single int16_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(2) + if s: + v, = struct.unpack(self.byte_order + 'h', s) + return v + else: + return fail_value + + def get_uint16(self, fail_value=0): + '''Extract a single uint16_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(2) + if s: + v, = struct.unpack(self.byte_order + 'H', s) + return v + else: + return fail_value + + def get_sint32(self, fail_value=0): + '''Extract a single int32_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(4) + if s: + v, = struct.unpack(self.byte_order + 'i', s) + return v + else: + return fail_value + + def get_uint32(self, fail_value=0): + '''Extract a single uint32_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(4) + if s: + v, = struct.unpack(self.byte_order + 'I', s) + return v + else: + return fail_value + + def get_sint64(self, fail_value=0): + '''Extract a single int64_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(8) + if s: + v, = struct.unpack(self.byte_order + 'q', s) + return v + else: + return fail_value + + def get_uint64(self, fail_value=0): + '''Extract a single uint64_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(8) + if s: + v, = struct.unpack(self.byte_order + 'Q', s) + return v + else: + return fail_value + + def get_fixed_length_c_string( + self, + n, + fail_value='', + isprint_only_with_space_padding=False): + '''Extract a single fixed length C string from the binary file at the current file position, returns a single C string''' + s = self.read_size(n) + if s: + cstr, = struct.unpack(self.byte_order + ("%i" % n) + 's', s) + # Strip trialing NULLs + cstr = string.strip(cstr, "\0") + if isprint_only_with_space_padding: + for c in cstr: + if c in string.printable or ord(c) == 0: + continue + return fail_value + return cstr + else: + return fail_value + + def get_c_string(self): + '''Extract a single NULL terminated C string from the binary file at the current file position, returns a single C string''' + cstr = '' + byte = self.get_uint8() + while byte != 0: + cstr += "%c" % byte + byte = self.get_uint8() + return cstr + + def get_n_sint8(self, n, fail_value=0): + '''Extract "n" int8_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'b', s) + else: + return (fail_value,) * n + + def get_n_uint8(self, n, fail_value=0): + '''Extract "n" uint8_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'B', s) + else: + return (fail_value,) * n + + def get_n_sint16(self, n, fail_value=0): + '''Extract "n" int16_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(2 * n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'h', s) + else: + return (fail_value,) * n + + def get_n_uint16(self, n, fail_value=0): + '''Extract "n" uint16_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(2 * n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'H', s) + else: + return (fail_value,) * n + + def get_n_sint32(self, n, fail_value=0): + '''Extract "n" int32_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(4 * n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'i', s) + else: + return (fail_value,) * n + + def get_n_uint32(self, n, fail_value=0): + '''Extract "n" uint32_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(4 * n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'I', s) + else: + return (fail_value,) * n + + def get_n_sint64(self, n, fail_value=0): + '''Extract "n" int64_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(8 * n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'q', s) + else: + return (fail_value,) * n + + def get_n_uint64(self, n, fail_value=0): + '''Extract "n" uint64_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(8 * n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'Q', s) + else: + return (fail_value,) * n diff --git a/gnu/llvm/lldb/examples/python/gdb_disassemble.py b/gnu/llvm/lldb/examples/python/gdb_disassemble.py new file mode 100755 index 00000000000..65983e2e993 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/gdb_disassemble.py @@ -0,0 +1,26 @@ +import lldb + + +def disassemble(debugger, command, result, dict): + if lldb.frame.function: + instructions = lldb.frame.function.instructions + start_addr = lldb.frame.function.addr.load_addr + name = lldb.frame.function.name + elif lldb.frame.symbol: + instructions = lldb.frame.symbol.instructions + start_addr = lldb.frame.symbol.addr.load_addr + name = lldb.frame.symbol.name + + for inst in instructions: + inst_addr = inst.addr.load_addr + inst_offset = inst_addr - start_addr + comment = inst.comment + if comment: + print("<%s + %-4u> 0x%x %8s %s ; %s" % (name, inst_offset, inst_addr, inst.mnemonic, inst.operands, comment)) + else: + print("<%s + %-4u> 0x%x %8s %s" % (name, inst_offset, inst_addr, inst.mnemonic, inst.operands)) + +# Install the command when the module gets imported +lldb.debugger.HandleCommand( + 'command script add -f gdb_disassemble.disassemble gdb-disassemble') +print('Installed "gdb-disassemble" command for disassembly') diff --git a/gnu/llvm/lldb/examples/python/gdbremote.py b/gnu/llvm/lldb/examples/python/gdbremote.py new file mode 100755 index 00000000000..dcacbfccdaf --- /dev/null +++ b/gnu/llvm/lldb/examples/python/gdbremote.py @@ -0,0 +1,1630 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# This module will enable GDB remote packet logging when the +# 'start_gdb_log' command is called with a filename to log to. When the +# 'stop_gdb_log' command is called, it will disable the logging and +# print out statistics about how long commands took to execute and also +# will primnt ou +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command. This can be done from the LLDB command line: +# (lldb) command script import /path/to/gdbremote.py +# Or it can be added to your ~/.lldbinit file so this module is always +# available. +#---------------------------------------------------------------------- + +import binascii +import subprocess +import json +import math +import optparse +import os +import re +import shlex +import string +import sys +import tempfile +import xml.etree.ElementTree as ET + +#---------------------------------------------------------------------- +# Global variables +#---------------------------------------------------------------------- +g_log_file = '' +g_byte_order = 'little' +g_number_regex = re.compile('^(0x[0-9a-fA-F]+|[0-9]+)') +g_thread_id_regex = re.compile('^(-1|[0-9a-fA-F]+|0)') + + +class TerminalColors: + '''Simple terminal colors class''' + + def __init__(self, enabled=True): + # TODO: discover terminal type from "file" and disable if + # it can't handle the color codes + self.enabled = enabled + + def reset(self): + '''Reset all terminal colors and formatting.''' + if self.enabled: + return "\x1b[0m" + return '' + + def bold(self, on=True): + '''Enable or disable bold depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[1m" + else: + return "\x1b[22m" + return '' + + def italics(self, on=True): + '''Enable or disable italics depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[3m" + else: + return "\x1b[23m" + return '' + + def underline(self, on=True): + '''Enable or disable underline depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[4m" + else: + return "\x1b[24m" + return '' + + def inverse(self, on=True): + '''Enable or disable inverse depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[7m" + else: + return "\x1b[27m" + return '' + + def strike(self, on=True): + '''Enable or disable strike through depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[9m" + else: + return "\x1b[29m" + return '' + + def black(self, fg=True): + '''Set the foreground or background color to black. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[30m" + else: + return "\x1b[40m" + return '' + + def red(self, fg=True): + '''Set the foreground or background color to red. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[31m" + else: + return "\x1b[41m" + return '' + + def green(self, fg=True): + '''Set the foreground or background color to green. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[32m" + else: + return "\x1b[42m" + return '' + + def yellow(self, fg=True): + '''Set the foreground or background color to yellow. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[33m" + else: + return "\x1b[43m" + return '' + + def blue(self, fg=True): + '''Set the foreground or background color to blue. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[34m" + else: + return "\x1b[44m" + return '' + + def magenta(self, fg=True): + '''Set the foreground or background color to magenta. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[35m" + else: + return "\x1b[45m" + return '' + + def cyan(self, fg=True): + '''Set the foreground or background color to cyan. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[36m" + else: + return "\x1b[46m" + return '' + + def white(self, fg=True): + '''Set the foreground or background color to white. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[37m" + else: + return "\x1b[47m" + return '' + + def default(self, fg=True): + '''Set the foreground or background color to the default. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[39m" + else: + return "\x1b[49m" + return '' + + +def start_gdb_log(debugger, command, result, dict): + '''Start logging GDB remote packets by enabling logging with timestamps and + thread safe logging. Follow a call to this function with a call to "stop_gdb_log" + in order to dump out the commands.''' + global g_log_file + command_args = shlex.split(command) + usage = "usage: start_gdb_log [options] [<LOGFILEPATH>]" + description = '''The command enables GDB remote packet logging with timestamps. The packets will be logged to <LOGFILEPATH> if supplied, or a temporary file will be used. Logging stops when stop_gdb_log is called and the packet times will + be aggregated and displayed.''' + parser = optparse.OptionParser( + description=description, + prog='start_gdb_log', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + if g_log_file: + result.PutCString( + 'error: logging is already in progress with file "%s"' % + g_log_file) + else: + args_len = len(args) + if args_len == 0: + g_log_file = tempfile.mktemp() + elif len(args) == 1: + g_log_file = args[0] + + if g_log_file: + debugger.HandleCommand( + 'log enable --threadsafe --timestamp --file "%s" gdb-remote packets' % + g_log_file) + result.PutCString( + "GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." % + g_log_file) + return + + result.PutCString('error: invalid log file path') + result.PutCString(usage) + + +def stop_gdb_log(debugger, command, result, dict): + '''Stop logging GDB remote packets to the file that was specified in a call + to "start_gdb_log" and normalize the timestamps to be relative to the first + timestamp in the log file. Also print out statistics for how long each + command took to allow performance bottlenecks to be determined.''' + global g_log_file + # Any commands whose names might be followed by more valid C identifier + # characters must be listed here + command_args = shlex.split(command) + usage = "usage: stop_gdb_log [options]" + description = '''The command stops a previously enabled GDB remote packet logging command. Packet logging must have been previously enabled with a call to start_gdb_log.''' + parser = optparse.OptionParser( + description=description, + prog='stop_gdb_log', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + parser.add_option( + '-q', + '--quiet', + action='store_true', + dest='quiet', + help='display verbose debug info', + default=False) + parser.add_option( + '-C', + '--color', + action='store_true', + dest='color', + help='add terminal colors', + default=False) + parser.add_option( + '-c', + '--sort-by-count', + action='store_true', + dest='sort_count', + help='display verbose debug info', + default=False) + parser.add_option( + '-s', + '--symbolicate', + action='store_true', + dest='symbolicate', + help='symbolicate addresses in log using current "lldb.target"', + default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + options.colors = TerminalColors(options.color) + options.symbolicator = None + if options.symbolicate: + if lldb.target: + import lldb.utils.symbolication + options.symbolicator = lldb.utils.symbolication.Symbolicator() + options.symbolicator.target = lldb.target + else: + print("error: can't symbolicate without a target") + + if not g_log_file: + result.PutCString( + 'error: logging must have been previously enabled with a call to "stop_gdb_log"') + elif os.path.exists(g_log_file): + if len(args) == 0: + debugger.HandleCommand('log disable gdb-remote packets') + result.PutCString( + "GDB packet logging disabled. Logged packets are in '%s'" % + g_log_file) + parse_gdb_log_file(g_log_file, options) + else: + result.PutCString(usage) + else: + print('error: the GDB packet log file "%s" does not exist' % g_log_file) + + +def is_hex_byte(str): + if len(str) == 2: + return str[0] in string.hexdigits and str[1] in string.hexdigits + return False + +def get_hex_string_if_all_printable(str): + try: + s = binascii.unhexlify(str) + if all(c in string.printable for c in s): + return s + except TypeError: + pass + return None + +# global register info list +g_register_infos = list() +g_max_register_info_name_len = 0 + + +class RegisterInfo: + """Class that represents register information""" + + def __init__(self, kvp): + self.info = dict() + for kv in kvp: + key = kv[0] + value = kv[1] + self.info[key] = value + + def name(self): + '''Get the name of the register.''' + if self.info and 'name' in self.info: + return self.info['name'] + return None + + def bit_size(self): + '''Get the size in bits of the register.''' + if self.info and 'bitsize' in self.info: + return int(self.info['bitsize']) + return 0 + + def byte_size(self): + '''Get the size in bytes of the register.''' + return self.bit_size() / 8 + + def get_value_from_hex_string(self, hex_str): + '''Dump the register value given a native byte order encoded hex ASCII byte string.''' + encoding = self.info['encoding'] + bit_size = self.bit_size() + packet = Packet(hex_str) + if encoding == 'uint': + uval = packet.get_hex_uint(g_byte_order) + if bit_size == 8: + return '0x%2.2x' % (uval) + elif bit_size == 16: + return '0x%4.4x' % (uval) + elif bit_size == 32: + return '0x%8.8x' % (uval) + elif bit_size == 64: + return '0x%16.16x' % (uval) + bytes = list() + uval = packet.get_hex_uint8() + while uval is not None: + bytes.append(uval) + uval = packet.get_hex_uint8() + value_str = '0x' + if g_byte_order == 'little': + bytes.reverse() + for byte in bytes: + value_str += '%2.2x' % byte + return '%s' % (value_str) + + def __str__(self): + '''Dump the register info key/value pairs''' + s = '' + for key in self.info.keys(): + if s: + s += ', ' + s += "%s=%s " % (key, self.info[key]) + return s + + +class Packet: + """Class that represents a packet that contains string data""" + + def __init__(self, packet_str): + self.str = packet_str + + def peek_char(self): + ch = 0 + if self.str: + ch = self.str[0] + return ch + + def get_char(self): + ch = 0 + if self.str: + ch = self.str[0] + self.str = self.str[1:] + return ch + + def skip_exact_string(self, s): + if self.str and self.str.startswith(s): + self.str = self.str[len(s):] + return True + else: + return False + + def get_thread_id(self, fail_value=-1): + match = g_number_regex.match(self.str) + if match: + number_str = match.group(1) + self.str = self.str[len(number_str):] + return int(number_str, 0) + else: + return fail_value + + def get_hex_uint8(self): + if self.str and len(self.str) >= 2 and self.str[ + 0] in string.hexdigits and self.str[1] in string.hexdigits: + uval = int(self.str[0:2], 16) + self.str = self.str[2:] + return uval + return None + + def get_hex_uint16(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + return uval + + def get_hex_uint32(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 24 + return uval + + def get_hex_uint64(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 56 + uval |= self.get_hex_uint8() << 48 + uval |= self.get_hex_uint8() << 40 + uval |= self.get_hex_uint8() << 32 + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 32 + uval |= self.get_hex_uint8() << 40 + uval |= self.get_hex_uint8() << 48 + uval |= self.get_hex_uint8() << 56 + return uval + + def get_number(self, fail_value=-1): + '''Get a number from the packet. The number must be in big endian format and should be parsed + according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with + [1-9] means decimal, etc)''' + match = g_number_regex.match(self.str) + if match: + number_str = match.group(1) + self.str = self.str[len(number_str):] + return int(number_str, 0) + else: + return fail_value + + def get_hex_ascii_str(self, n=0): + hex_chars = self.get_hex_chars(n) + if hex_chars: + return binascii.unhexlify(hex_chars) + else: + return None + + def get_hex_chars(self, n=0): + str_len = len(self.str) + if n == 0: + # n was zero, so we need to determine all hex chars and + # stop when we hit the end of the string of a non-hex character + while n < str_len and self.str[n] in string.hexdigits: + n = n + 1 + else: + if n > str_len: + return None # Not enough chars + # Verify all chars are hex if a length was specified + for i in range(n): + if self.str[i] not in string.hexdigits: + return None # Not all hex digits + if n == 0: + return None + hex_str = self.str[0:n] + self.str = self.str[n:] + return hex_str + + def get_hex_uint(self, byte_order, n=0): + if byte_order == 'big': + hex_str = self.get_hex_chars(n) + if hex_str is None: + return None + return int(hex_str, 16) + else: + uval = self.get_hex_uint8() + if uval is None: + return None + uval_result = 0 + shift = 0 + while uval is not None: + uval_result |= (uval << shift) + shift += 8 + uval = self.get_hex_uint8() + return uval_result + + def get_key_value_pairs(self): + kvp = list() + if ';' in self.str: + key_value_pairs = string.split(self.str, ';') + for key_value_pair in key_value_pairs: + if len(key_value_pair): + kvp.append(string.split(key_value_pair, ':')) + return kvp + + def split(self, ch): + return string.split(self.str, ch) + + def split_hex(self, ch, byte_order): + hex_values = list() + strings = string.split(self.str, ch) + for str in strings: + hex_values.append(Packet(str).get_hex_uint(byte_order)) + return hex_values + + def __str__(self): + return self.str + + def __len__(self): + return len(self.str) + +g_thread_suffix_regex = re.compile(';thread:([0-9a-fA-F]+);') + + +def get_thread_from_thread_suffix(str): + if str: + match = g_thread_suffix_regex.match(str) + if match: + return int(match.group(1), 16) + return None + + +def cmd_qThreadStopInfo(options, cmd, args): + packet = Packet(args) + tid = packet.get_hex_uint('big') + print("get_thread_stop_info (tid = 0x%x)" % (tid)) + + +def cmd_stop_reply(options, cmd, args): + print("get_last_stop_info()") + return False + + +def rsp_stop_reply(options, cmd, cmd_args, rsp): + global g_byte_order + packet = Packet(rsp) + stop_type = packet.get_char() + if stop_type == 'T' or stop_type == 'S': + signo = packet.get_hex_uint8() + key_value_pairs = packet.get_key_value_pairs() + for key_value_pair in key_value_pairs: + key = key_value_pair[0] + if is_hex_byte(key): + reg_num = Packet(key).get_hex_uint8() + if reg_num < len(g_register_infos): + reg_info = g_register_infos[reg_num] + key_value_pair[0] = reg_info.name() + key_value_pair[1] = reg_info.get_value_from_hex_string( + key_value_pair[1]) + elif key == 'jthreads' or key == 'jstopinfo': + key_value_pair[1] = binascii.unhexlify(key_value_pair[1]) + key_value_pairs.insert(0, ['signal', signo]) + print('stop_reply():') + dump_key_value_pairs(key_value_pairs) + elif stop_type == 'W': + exit_status = packet.get_hex_uint8() + print('stop_reply(): exit (status=%i)' % exit_status) + elif stop_type == 'O': + print('stop_reply(): stdout = "%s"' % packet.str) + + +def cmd_unknown_packet(options, cmd, args): + if args: + print("cmd: %s, args: %s", cmd, args) + else: + print("cmd: %s", cmd) + return False + + +def cmd_qSymbol(options, cmd, args): + if args == ':': + print('ready to serve symbols') + else: + packet = Packet(args) + symbol_addr = packet.get_hex_uint('big') + if symbol_addr is None: + if packet.skip_exact_string(':'): + symbol_name = packet.get_hex_ascii_str() + print('lookup_symbol("%s") -> symbol not available yet' % (symbol_name)) + else: + print('error: bad command format') + else: + if packet.skip_exact_string(':'): + symbol_name = packet.get_hex_ascii_str() + print('lookup_symbol("%s") -> 0x%x' % (symbol_name, symbol_addr)) + else: + print('error: bad command format') + +def cmd_QSetWithHexString(options, cmd, args): + print('%s("%s")' % (cmd[:-1], binascii.unhexlify(args))) + +def cmd_QSetWithString(options, cmd, args): + print('%s("%s")' % (cmd[:-1], args)) + +def cmd_QSetWithUnsigned(options, cmd, args): + print('%s(%i)' % (cmd[:-1], int(args))) + +def rsp_qSymbol(options, cmd, cmd_args, rsp): + if len(rsp) == 0: + print("Unsupported") + else: + if rsp == "OK": + print("No more symbols to lookup") + else: + packet = Packet(rsp) + if packet.skip_exact_string("qSymbol:"): + symbol_name = packet.get_hex_ascii_str() + print('lookup_symbol("%s")' % (symbol_name)) + else: + print('error: response string should start with "qSymbol:": respnse is "%s"' % (rsp)) + + +def cmd_qXfer(options, cmd, args): + # $qXfer:features:read:target.xml:0,1ffff#14 + print("read target special data %s" % (args)) + return True + + +def rsp_qXfer(options, cmd, cmd_args, rsp): + data = string.split(cmd_args, ':') + if data[0] == 'features': + if data[1] == 'read': + filename, extension = os.path.splitext(data[2]) + if extension == '.xml': + response = Packet(rsp) + xml_string = response.get_hex_ascii_str() + if xml_string: + ch = xml_string[0] + if ch == 'l': + xml_string = xml_string[1:] + xml_root = ET.fromstring(xml_string) + for reg_element in xml_root.findall("./feature/reg"): + if not 'value_regnums' in reg_element.attrib: + reg_info = RegisterInfo([]) + if 'name' in reg_element.attrib: + reg_info.info[ + 'name'] = reg_element.attrib['name'] + else: + reg_info.info['name'] = 'unspecified' + if 'encoding' in reg_element.attrib: + reg_info.info['encoding'] = reg_element.attrib[ + 'encoding'] + else: + reg_info.info['encoding'] = 'uint' + if 'offset' in reg_element.attrib: + reg_info.info[ + 'offset'] = reg_element.attrib['offset'] + if 'bitsize' in reg_element.attrib: + reg_info.info[ + 'bitsize'] = reg_element.attrib['bitsize'] + g_register_infos.append(reg_info) + print('XML for "%s":' % (data[2])) + ET.dump(xml_root) + + +def cmd_A(options, cmd, args): + print('launch process:') + packet = Packet(args) + while True: + arg_len = packet.get_number() + if arg_len == -1: + break + if not packet.skip_exact_string(','): + break + arg_idx = packet.get_number() + if arg_idx == -1: + break + if not packet.skip_exact_string(','): + break + arg_value = packet.get_hex_ascii_str(arg_len) + print('argv[%u] = "%s"' % (arg_idx, arg_value)) + + +def cmd_qC(options, cmd, args): + print("query_current_thread_id()") + + +def rsp_qC(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + if packet.skip_exact_string("QC"): + tid = packet.get_thread_id() + print("current_thread_id = %#x" % (tid)) + else: + print("current_thread_id = old thread ID") + + +def cmd_query_packet(options, cmd, args): + if args: + print("%s%s" % (cmd, args)) + else: + print("%s" % (cmd)) + return False + + +def rsp_ok_error(rsp): + print("rsp: ", rsp) + + +def rsp_ok_means_supported(options, cmd, cmd_args, rsp): + if rsp == 'OK': + print("%s%s is supported" % (cmd, cmd_args)) + elif rsp == '': + print("%s%s is not supported" % (cmd, cmd_args)) + else: + print("%s%s -> %s" % (cmd, cmd_args, rsp)) + + +def rsp_ok_means_success(options, cmd, cmd_args, rsp): + if rsp == 'OK': + print("success") + elif rsp == '': + print("%s%s is not supported" % (cmd, cmd_args)) + else: + print("%s%s -> %s" % (cmd, cmd_args, rsp)) + + +def dump_key_value_pairs(key_value_pairs): + max_key_len = 0 + for key_value_pair in key_value_pairs: + key_len = len(key_value_pair[0]) + if max_key_len < key_len: + max_key_len = key_len + for key_value_pair in key_value_pairs: + key = key_value_pair[0] + value = key_value_pair[1] + unhex_value = get_hex_string_if_all_printable(value) + if unhex_value: + print("%*s = %s (%s)" % (max_key_len, key, value, unhex_value)) + else: + print("%*s = %s" % (max_key_len, key, value)) + + +def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp): + if rsp: + print('%s response:' % (cmd)) + packet = Packet(rsp) + key_value_pairs = packet.get_key_value_pairs() + dump_key_value_pairs(key_value_pairs) + else: + print("not supported") + + +def cmd_c(options, cmd, args): + print("continue()") + return False + + +def cmd_s(options, cmd, args): + print("step()") + return False + + +def cmd_qSpeedTest(options, cmd, args): + print(("qSpeedTest: cmd='%s', args='%s'" % (cmd, args))) + + +def rsp_qSpeedTest(options, cmd, cmd_args, rsp): + print(("qSpeedTest: rsp='%s' cmd='%s', args='%s'" % (rsp, cmd, args))) + + +def cmd_vCont(options, cmd, args): + if args == '?': + print("%s: get supported extended continue modes" % (cmd)) + else: + got_other_threads = 0 + s = '' + for thread_action in string.split(args[1:], ';'): + (short_action, thread) = string.split(thread_action, ':') + tid = int(thread, 16) + if short_action == 'c': + action = 'continue' + elif short_action == 's': + action = 'step' + elif short_action[0] == 'C': + action = 'continue with signal 0x%s' % (short_action[1:]) + elif short_action == 'S': + action = 'step with signal 0x%s' % (short_action[1:]) + else: + action = short_action + if s: + s += ', ' + if tid == -1: + got_other_threads = 1 + s += 'other-threads:' + else: + s += 'thread 0x%4.4x: %s' % (tid, action) + if got_other_threads: + print("extended_continue (%s)" % (s)) + else: + print("extended_continue (%s, other-threads: suspend)" % (s)) + return False + + +def rsp_vCont(options, cmd, cmd_args, rsp): + if cmd_args == '?': + # Skip the leading 'vCont;' + rsp = rsp[6:] + modes = string.split(rsp, ';') + s = "%s: supported extended continue modes include: " % (cmd) + + for i, mode in enumerate(modes): + if i: + s += ', ' + if mode == 'c': + s += 'continue' + elif mode == 'C': + s += 'continue with signal' + elif mode == 's': + s += 'step' + elif mode == 'S': + s += 'step with signal' + elif mode == 't': + s += 'stop' + # else: + # s += 'unrecognized vCont mode: ', str(mode) + print(s) + elif rsp: + if rsp[0] == 'T' or rsp[0] == 'S' or rsp[0] == 'W' or rsp[0] == 'X': + rsp_stop_reply(options, cmd, cmd_args, rsp) + return + if rsp[0] == 'O': + print("stdout: %s" % (rsp)) + return + else: + print("not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd, cmd_args, rsp)) + + +def cmd_vAttach(options, cmd, args): + (extra_command, args) = string.split(args, ';') + if extra_command: + print("%s%s(%s)" % (cmd, extra_command, args)) + else: + print("attach(pid = %u)" % int(args, 16)) + return False + + +def cmd_qRegisterInfo(options, cmd, args): + print('query_register_info(reg_num=%i)' % (int(args, 16))) + return False + + +def rsp_qRegisterInfo(options, cmd, cmd_args, rsp): + global g_max_register_info_name_len + print('query_register_info(reg_num=%i):' % (int(cmd_args, 16)), end=' ') + if len(rsp) == 3 and rsp[0] == 'E': + g_max_register_info_name_len = 0 + for reg_info in g_register_infos: + name_len = len(reg_info.name()) + if g_max_register_info_name_len < name_len: + g_max_register_info_name_len = name_len + print(' DONE') + else: + packet = Packet(rsp) + reg_info = RegisterInfo(packet.get_key_value_pairs()) + g_register_infos.append(reg_info) + print(reg_info) + return False + + +def cmd_qThreadInfo(options, cmd, args): + if cmd == 'qfThreadInfo': + query_type = 'first' + else: + query_type = 'subsequent' + print('get_current_thread_list(type=%s)' % (query_type)) + return False + + +def rsp_qThreadInfo(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + response_type = packet.get_char() + if response_type == 'm': + tids = packet.split_hex(';', 'big') + for i, tid in enumerate(tids): + if i: + print(',', end=' ') + print('0x%x' % (tid), end=' ') + print() + elif response_type == 'l': + print('END') + + +def rsp_hex_big_endian(options, cmd, cmd_args, rsp): + if rsp == '': + print("%s%s is not supported" % (cmd, cmd_args)) + else: + packet = Packet(rsp) + uval = packet.get_hex_uint('big') + print('%s: 0x%x' % (cmd, uval)) + + +def cmd_read_mem_bin(options, cmd, args): + # x0x7fff5fc39200,0x200 + packet = Packet(args) + addr = packet.get_hex_uint('big') + comma = packet.get_char() + size = packet.get_hex_uint('big') + print('binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)) + return False + + +def rsp_mem_bin_bytes(options, cmd, cmd_args, rsp): + packet = Packet(cmd_args) + addr = packet.get_hex_uint('big') + comma = packet.get_char() + size = packet.get_hex_uint('big') + print('memory:') + if size > 0: + dump_hex_memory_buffer(addr, rsp) + + +def cmd_read_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + comma = packet.get_char() + size = packet.get_hex_uint('big') + print('read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)) + return False + + +def dump_hex_memory_buffer(addr, hex_byte_str): + packet = Packet(hex_byte_str) + idx = 0 + ascii = '' + uval = packet.get_hex_uint8() + while uval is not None: + if ((idx % 16) == 0): + if ascii: + print(' ', ascii) + ascii = '' + print('0x%x:' % (addr + idx), end=' ') + print('%2.2x' % (uval), end=' ') + if 0x20 <= uval and uval < 0x7f: + ascii += '%c' % uval + else: + ascii += '.' + uval = packet.get_hex_uint8() + idx = idx + 1 + if ascii: + print(' ', ascii) + ascii = '' + + +def cmd_write_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + if packet.get_char() != ',': + print('error: invalid write memory command (missing comma after address)') + return + size = packet.get_hex_uint('big') + if packet.get_char() != ':': + print('error: invalid write memory command (missing colon after size)') + return + print('write_memory (addr = 0x%16.16x, size = %u, data:' % (addr, size)) + dump_hex_memory_buffer(addr, packet.str) + return False + + +def cmd_alloc_memory(options, cmd, args): + packet = Packet(args) + byte_size = packet.get_hex_uint('big') + if packet.get_char() != ',': + print('error: invalid allocate memory command (missing comma after address)') + return + print('allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size, byte_size, packet.str)) + return False + + +def rsp_alloc_memory(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + addr = packet.get_hex_uint('big') + print('addr = 0x%x' % addr) + + +def cmd_dealloc_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + if packet.get_char() != ',': + print('error: invalid allocate memory command (missing comma after address)') + else: + print('deallocate_memory (addr = 0x%x, permissions = %s)' % (addr, packet.str)) + return False + + +def rsp_memory_bytes(options, cmd, cmd_args, rsp): + addr = Packet(cmd_args).get_hex_uint('big') + dump_hex_memory_buffer(addr, rsp) + + +def get_register_name_equal_value(options, reg_num, hex_value_str): + if reg_num < len(g_register_infos): + reg_info = g_register_infos[reg_num] + value_str = reg_info.get_value_from_hex_string(hex_value_str) + s = reg_info.name() + ' = ' + if options.symbolicator: + symbolicated_addresses = options.symbolicator.symbolicate( + int(value_str, 0)) + if symbolicated_addresses: + s += options.colors.magenta() + s += '%s' % symbolicated_addresses[0] + s += options.colors.reset() + return s + s += value_str + return s + else: + reg_value = Packet(hex_value_str).get_hex_uint(g_byte_order) + return 'reg(%u) = 0x%x' % (reg_num, reg_value) + + +def cmd_read_one_reg(options, cmd, args): + packet = Packet(args) + reg_num = packet.get_hex_uint('big') + tid = get_thread_from_thread_suffix(packet.str) + name = None + if reg_num < len(g_register_infos): + name = g_register_infos[reg_num].name() + if packet.str: + packet.get_char() # skip ; + thread_info = packet.get_key_value_pairs() + tid = int(thread_info[0][1], 16) + s = 'read_register (reg_num=%u' % reg_num + if name: + s += ' (%s)' % (name) + if tid is not None: + s += ', tid = 0x%4.4x' % (tid) + s += ')' + print(s) + return False + + +def rsp_read_one_reg(options, cmd, cmd_args, rsp): + packet = Packet(cmd_args) + reg_num = packet.get_hex_uint('big') + print(get_register_name_equal_value(options, reg_num, rsp)) + + +def cmd_write_one_reg(options, cmd, args): + packet = Packet(args) + reg_num = packet.get_hex_uint('big') + if packet.get_char() != '=': + print('error: invalid register write packet') + else: + name = None + hex_value_str = packet.get_hex_chars() + tid = get_thread_from_thread_suffix(packet.str) + s = 'write_register (reg_num=%u' % reg_num + if name: + s += ' (%s)' % (name) + s += ', value = ' + s += get_register_name_equal_value(options, reg_num, hex_value_str) + if tid is not None: + s += ', tid = 0x%4.4x' % (tid) + s += ')' + print(s) + return False + + +def dump_all_regs(packet): + for reg_info in g_register_infos: + nibble_size = reg_info.bit_size() / 4 + hex_value_str = packet.get_hex_chars(nibble_size) + if hex_value_str is not None: + value = reg_info.get_value_from_hex_string(hex_value_str) + print('%*s = %s' % (g_max_register_info_name_len, reg_info.name(), value)) + else: + return + + +def cmd_read_all_regs(cmd, cmd_args): + packet = Packet(cmd_args) + packet.get_char() # toss the 'g' command character + tid = get_thread_from_thread_suffix(packet.str) + if tid is not None: + print('read_all_register(thread = 0x%4.4x)' % tid) + else: + print('read_all_register()') + return False + + +def rsp_read_all_regs(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + dump_all_regs(packet) + + +def cmd_write_all_regs(options, cmd, args): + packet = Packet(args) + print('write_all_registers()') + dump_all_regs(packet) + return False + +g_bp_types = ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"] + + +def cmd_bp(options, cmd, args): + if cmd == 'Z': + s = 'set_' + else: + s = 'clear_' + packet = Packet(args) + bp_type = packet.get_hex_uint('big') + packet.get_char() # Skip , + bp_addr = packet.get_hex_uint('big') + packet.get_char() # Skip , + bp_size = packet.get_hex_uint('big') + s += g_bp_types[bp_type] + s += " (addr = 0x%x, size = %u)" % (bp_addr, bp_size) + print(s) + return False + + +def cmd_mem_rgn_info(options, cmd, args): + packet = Packet(args) + packet.get_char() # skip ':' character + addr = packet.get_hex_uint('big') + print('get_memory_region_info (addr=0x%x)' % (addr)) + return False + + +def cmd_kill(options, cmd, args): + print('kill_process()') + return False + + +def cmd_jThreadsInfo(options, cmd, args): + print('jThreadsInfo()') + return False + + +def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args): + print('jGetLoadedDynamicLibrariesInfos()') + return False + + +def decode_packet(s, start_index=0): + # print '\ndecode_packet("%s")' % (s[start_index:]) + index = s.find('}', start_index) + have_escapes = index != -1 + if have_escapes: + normal_s = s[start_index:index] + else: + normal_s = s[start_index:] + # print 'normal_s = "%s"' % (normal_s) + if have_escapes: + escape_char = '%c' % (ord(s[index + 1]) ^ 0x20) + # print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char) + return normal_s + escape_char + decode_packet(s, index + 2) + else: + return normal_s + + +def rsp_json(options, cmd, cmd_args, rsp): + print('%s() reply:' % (cmd)) + json_tree = json.loads(rsp) + print(json.dumps(json_tree, indent=4, separators=(',', ': '))) + + +def rsp_jGetLoadedDynamicLibrariesInfos(options, cmd, cmd_args, rsp): + if cmd_args: + rsp_json(options, cmd, cmd_args, rsp) + else: + rsp_ok_means_supported(options, cmd, cmd_args, rsp) + +gdb_remote_commands = { + '\\?': {'cmd': cmd_stop_reply, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"}, + 'qThreadStopInfo': {'cmd': cmd_qThreadStopInfo, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"}, + 'QStartNoAckMode': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if no ack mode is supported"}, + 'QThreadSuffixSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if thread suffix is supported"}, + 'QListThreadsInStopReply': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if threads in stop reply packets are supported"}, + 'QSetDetachOnError:': {'cmd': cmd_QSetWithUnsigned, 'rsp': rsp_ok_means_success, 'name': "set if we should detach on error"}, + 'QSetDisableASLR:': {'cmd': cmd_QSetWithUnsigned, 'rsp': rsp_ok_means_success, 'name': "set if we should disable ASLR"}, + 'qLaunchSuccess': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "check on launch success for the A packet"}, + 'A': {'cmd': cmd_A, 'rsp': rsp_ok_means_success, 'name': "launch process"}, + 'QLaunchArch:': {'cmd': cmd_QSetWithString, 'rsp': rsp_ok_means_supported, 'name': "set the arch to launch in case the file contains multiple architectures"}, + 'qVAttachOrWaitSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "set the launch architecture"}, + 'qHostInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get host information"}, + 'qC': {'cmd': cmd_qC, 'rsp': rsp_qC, 'name': "return the current thread ID"}, + 'vCont': {'cmd': cmd_vCont, 'rsp': rsp_vCont, 'name': "extended continue command"}, + 'qSpeedTest': {'cmd':cmd_qSpeedTest, 'rsp': rsp_qSpeedTest, 'name': 'speed test packdet'}, + 'vAttach': {'cmd': cmd_vAttach, 'rsp': rsp_stop_reply, 'name': "attach to process"}, + 'c': {'cmd': cmd_c, 'rsp': rsp_stop_reply, 'name': "continue"}, + 's': {'cmd': cmd_s, 'rsp': rsp_stop_reply, 'name': "step"}, + 'qRegisterInfo': {'cmd': cmd_qRegisterInfo, 'rsp': rsp_qRegisterInfo, 'name': "query register info"}, + 'qfThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"}, + 'qsThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"}, + 'qShlibInfoAddr': {'cmd': cmd_query_packet, 'rsp': rsp_hex_big_endian, 'name': "get shared library info address"}, + 'qMemoryRegionInfo': {'cmd': cmd_mem_rgn_info, 'rsp': rsp_dump_key_value_pairs, 'name': "get memory region information"}, + 'qProcessInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get process info"}, + 'qSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query supported"}, + 'qXfer:': {'cmd': cmd_qXfer, 'rsp': rsp_qXfer, 'name': "qXfer"}, + 'qSymbol:': {'cmd': cmd_qSymbol, 'rsp': rsp_qSymbol, 'name': "qSymbol"}, + 'QSetSTDIN:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDIN prior to launching with A packet"}, + 'QSetSTDOUT:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDOUT prior to launching with A packet"}, + 'QSetSTDERR:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDERR prior to launching with A packet"}, + 'QEnvironment:' : {'cmd' : cmd_QSetWithString, 'rsp' : rsp_ok_means_success, 'name': "set an environment variable prior to launching with A packet"}, + 'QEnvironmentHexEncoded:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set an environment variable prior to launching with A packet"}, + 'x': {'cmd': cmd_read_mem_bin, 'rsp': rsp_mem_bin_bytes, 'name': "read memory binary"}, + 'X': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory binary"}, + 'm': {'cmd': cmd_read_memory, 'rsp': rsp_memory_bytes, 'name': "read memory"}, + 'M': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory"}, + '_M': {'cmd': cmd_alloc_memory, 'rsp': rsp_alloc_memory, 'name': "allocate memory"}, + '_m': {'cmd': cmd_dealloc_memory, 'rsp': rsp_ok_means_success, 'name': "deallocate memory"}, + 'p': {'cmd': cmd_read_one_reg, 'rsp': rsp_read_one_reg, 'name': "read single register"}, + 'P': {'cmd': cmd_write_one_reg, 'rsp': rsp_ok_means_success, 'name': "write single register"}, + 'g': {'cmd': cmd_read_all_regs, 'rsp': rsp_read_all_regs, 'name': "read all registers"}, + 'G': {'cmd': cmd_write_all_regs, 'rsp': rsp_ok_means_success, 'name': "write all registers"}, + 'z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "clear breakpoint or watchpoint"}, + 'Z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "set breakpoint or watchpoint"}, + 'k': {'cmd': cmd_kill, 'rsp': rsp_stop_reply, 'name': "kill process"}, + 'jThreadsInfo': {'cmd': cmd_jThreadsInfo, 'rsp': rsp_json, 'name': "JSON get all threads info"}, + 'jGetLoadedDynamicLibrariesInfos:': {'cmd': cmd_jGetLoadedDynamicLibrariesInfos, 'rsp': rsp_jGetLoadedDynamicLibrariesInfos, 'name': 'JSON get loaded dynamic libraries'}, +} + + +def calculate_mean_and_standard_deviation(floats): + sum = 0.0 + count = len(floats) + if count == 0: + return (0.0, 0.0) + for f in floats: + sum += f + mean = sum / count + accum = 0.0 + for f in floats: + delta = f - mean + accum += delta * delta + + std_dev = math.sqrt(accum / (count - 1)) + return (mean, std_dev) + + +def parse_gdb_log_file(path, options): + f = open(path) + parse_gdb_log(f, options) + f.close() + + +def round_up(n, incr): + return float(((int(n) + incr) / incr) * incr) + + +def plot_latencies(sec_times): + # import numpy as np + import matplotlib.pyplot as plt + + for (i, name) in enumerate(sec_times.keys()): + times = sec_times[name] + if len(times) <= 1: + continue + plt.subplot(2, 1, 1) + plt.title('Packet "%s" Times' % (name)) + plt.xlabel('Packet') + units = 'ms' + adj_times = [] + max_time = 0.0 + for time in times: + time = time * 1000.0 + adj_times.append(time) + if time > max_time: + max_time = time + if max_time < 1.0: + units = 'us' + max_time = 0.0 + for i in range(len(adj_times)): + adj_times[i] *= 1000.0 + if adj_times[i] > max_time: + max_time = adj_times[i] + plt.ylabel('Time (%s)' % (units)) + max_y = None + for i in [5.0, 10.0, 25.0, 50.0]: + if max_time < i: + max_y = round_up(max_time, i) + break + if max_y is None: + max_y = round_up(max_time, 100.0) + plt.ylim(0.0, max_y) + plt.plot(adj_times, 'o-') + plt.show() + + +def parse_gdb_log(file, options): + '''Parse a GDB log file that was generated by enabling logging with: + (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets + This log file will contain timestamps and this function will then normalize + those packets to be relative to the first value timestamp that is found and + show delta times between log lines and also keep track of how long it takes + for GDB remote commands to make a send/receive round trip. This can be + handy when trying to figure out why some operation in the debugger is taking + a long time during a preset set of debugger commands.''' + + tricky_commands = ['qRegisterInfo'] + timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$') + packet_name_regex = re.compile('([A-Za-z_]+)[^a-z]') + packet_transmit_name_regex = re.compile( + '(?P<direction>send|read) packet: (?P<packet>.*)') + packet_contents_name_regex = re.compile('\$([^#]*)#[0-9a-fA-F]{2}') + packet_checksum_regex = re.compile('.*#[0-9a-fA-F]{2}$') + packet_names_regex_str = '(' + \ + '|'.join(gdb_remote_commands.keys()) + ')(.*)' + packet_names_regex = re.compile(packet_names_regex_str) + + base_time = 0.0 + last_time = 0.0 + min_time = 100000000.0 + packet_total_times = {} + all_packet_times = [] + packet_times = {} + packet_counts = {} + lines = file.read().splitlines() + last_command = None + last_command_args = None + last_command_packet = None + hide_next_response = False + num_lines = len(lines) + skip_count = 0 + for (line_index, line) in enumerate(lines): + # See if we need to skip any lines + if skip_count > 0: + skip_count -= 1 + continue + m = packet_transmit_name_regex.search(line) + is_command = False + direction = None + if m: + direction = m.group('direction') + is_command = direction == 'send' + packet = m.group('packet') + sys.stdout.write(options.colors.green()) + if not options.quiet and not hide_next_response: + print('# ', line) + sys.stdout.write(options.colors.reset()) + + # print 'direction = "%s", packet = "%s"' % (direction, packet) + + if packet[0] == '+': + if is_command: + print('-->', end=' ') + else: + print('<--', end=' ') + if not options.quiet: + print('ACK') + continue + elif packet[0] == '-': + if is_command: + print('-->', end=' ') + else: + print('<--', end=' ') + if not options.quiet: + print('NACK') + continue + elif packet[0] == '$': + m = packet_contents_name_regex.match(packet) + if not m and packet[0] == '$': + multiline_packet = packet + idx = line_index + 1 + while idx < num_lines: + if not options.quiet and not hide_next_response: + print('# ', lines[idx]) + multiline_packet += lines[idx] + m = packet_contents_name_regex.match(multiline_packet) + if m: + packet = multiline_packet + skip_count = idx - line_index + break + else: + idx += 1 + if m: + if is_command: + print('-->', end=' ') + else: + print('<--', end=' ') + contents = decode_packet(m.group(1)) + if is_command: + hide_next_response = False + m = packet_names_regex.match(contents) + if m: + last_command = m.group(1) + if last_command == '?': + last_command = '\\?' + packet_name = last_command + last_command_args = m.group(2) + last_command_packet = contents + hide_next_response = gdb_remote_commands[last_command][ + 'cmd'](options, last_command, last_command_args) + else: + packet_match = packet_name_regex.match(contents) + if packet_match: + packet_name = packet_match.group(1) + for tricky_cmd in tricky_commands: + if packet_name.find(tricky_cmd) == 0: + packet_name = tricky_cmd + else: + packet_name = contents + last_command = None + last_command_args = None + last_command_packet = None + elif last_command: + gdb_remote_commands[last_command]['rsp']( + options, last_command, last_command_args, contents) + else: + print('error: invalid packet: "', packet, '"') + else: + print('???') + else: + print('## ', line) + match = timestamp_regex.match(line) + if match: + curr_time = float(match.group(2)) + if last_time and not is_command: + delta = curr_time - last_time + all_packet_times.append(delta) + delta = 0.0 + if base_time: + delta = curr_time - last_time + else: + base_time = curr_time + + if not is_command: + if line.find('read packet: $') >= 0 and packet_name: + if packet_name in packet_total_times: + packet_total_times[packet_name] += delta + packet_counts[packet_name] += 1 + else: + packet_total_times[packet_name] = delta + packet_counts[packet_name] = 1 + if packet_name not in packet_times: + packet_times[packet_name] = [] + packet_times[packet_name].append(delta) + packet_name = None + if min_time > delta: + min_time = delta + + if not options or not options.quiet: + print('%s%.6f %+.6f%s' % (match.group(1), + curr_time - base_time, + delta, + match.group(3))) + last_time = curr_time + # else: + # print line + (average, std_dev) = calculate_mean_and_standard_deviation(all_packet_times) + if average and std_dev: + print('%u packets with average packet time of %f and standard deviation of %f' % (len(all_packet_times), average, std_dev)) + if packet_total_times: + total_packet_time = 0.0 + total_packet_count = 0 + for key, vvv in packet_total_times.items(): + # print ' key = (%s) "%s"' % (type(key), key) + # print 'value = (%s) %s' % (type(vvv), vvv) + # if type(vvv) == 'float': + total_packet_time += vvv + for key, vvv in packet_counts.items(): + total_packet_count += vvv + + print('#------------------------------------------------------------') + print('# Packet timing summary:') + print('# Totals: time = %6f, count = %6d' % (total_packet_time, + total_packet_count)) + print('# Min packet time: time = %6f' % (min_time)) + print('#------------------------------------------------------------') + print('# Packet Time (sec) Percent Count Latency') + print('#------------------------- ----------- ------- ------ -------') + if options and options.sort_count: + res = sorted( + packet_counts, + key=packet_counts.__getitem__, + reverse=True) + else: + res = sorted( + packet_total_times, + key=packet_total_times.__getitem__, + reverse=True) + + if last_time > 0.0: + for item in res: + packet_total_time = packet_total_times[item] + packet_percent = ( + packet_total_time / total_packet_time) * 100.0 + packet_count = packet_counts[item] + print(" %24s %11.6f %5.2f%% %6d %9.6f" % ( + item, packet_total_time, packet_percent, packet_count, + float(packet_total_time) / float(packet_count))) + if options.plot: + plot_latencies(packet_times) + +if __name__ == '__main__': + usage = "usage: gdbremote [options]" + description = '''The command disassembles a GDB remote packet log.''' + parser = optparse.OptionParser( + description=description, + prog='gdbremote', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + parser.add_option( + '--plot', + action='store_true', + dest='plot', + help='plot packet latencies by packet type', + default=False) + parser.add_option( + '-q', + '--quiet', + action='store_true', + dest='quiet', + help='display verbose debug info', + default=False) + parser.add_option( + '-C', + '--color', + action='store_true', + dest='color', + help='add terminal colors', + default=False) + parser.add_option( + '-c', + '--sort-by-count', + action='store_true', + dest='sort_count', + help='display verbose debug info', + default=False) + parser.add_option( + '--crashlog', + type='string', + dest='crashlog', + help='symbolicate using a darwin crash log file', + default=False) + try: + (options, args) = parser.parse_args(sys.argv[1:]) + except: + print('error: argument error') + sys.exit(1) + + options.colors = TerminalColors(options.color) + options.symbolicator = None + if options.crashlog: + import lldb + lldb.debugger = lldb.SBDebugger.Create() + import lldb.macosx.crashlog + options.symbolicator = lldb.macosx.crashlog.CrashLog(options.crashlog) + print('%s' % (options.symbolicator)) + + # This script is being run from the command line, create a debugger in case we are + # going to use any debugger functions in our function. + if len(args): + for file in args: + print('#----------------------------------------------------------------------') + print("# GDB remote log file: '%s'" % file) + print('#----------------------------------------------------------------------') + parse_gdb_log_file(file, options) + if options.symbolicator: + print('%s' % (options.symbolicator)) + else: + parse_gdb_log(sys.stdin, options) + +else: + import lldb + if lldb.debugger: + # This initializer is being run from LLDB in the embedded command interpreter + # Add any commands contained in this module to LLDB + lldb.debugger.HandleCommand( + 'command script add -f gdbremote.start_gdb_log start_gdb_log') + lldb.debugger.HandleCommand( + 'command script add -f gdbremote.stop_gdb_log stop_gdb_log') + print('The "start_gdb_log" and "stop_gdb_log" commands are now installed and ready for use, type "start_gdb_log --help" or "stop_gdb_log --help" for more information') diff --git a/gnu/llvm/lldb/examples/python/globals.py b/gnu/llvm/lldb/examples/python/globals.py new file mode 100755 index 00000000000..3e773441642 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/globals.py @@ -0,0 +1,106 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# For the shells csh, tcsh: +# ( setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python ; ./globals.py <path> [<path> ...]) +# +# For the shells sh, bash: +# PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python ./globals.py <path> [<path> ...] +#---------------------------------------------------------------------- +from __future__ import print_function + +import lldb +import optparse +import os +import shlex +import sys + + +def get_globals(raw_path, options): + error = lldb.SBError() + # Resolve the path if needed + path = os.path.expanduser(raw_path) + # Create a target using path + options + target = lldb.debugger.CreateTarget( + path, options.arch, options.platform, False, error) + if target: + # Get the executable module + module = target.module[target.executable.basename] + if module: + # Keep track of which variables we have already looked up + global_names = list() + # Iterate through all symbols in the symbol table and watch for any + # DATA symbols + for symbol in module.symbols: + if symbol.type == lldb.eSymbolTypeData: + # The symbol is a DATA symbol, lets try and find all global variables + # that match this name and print them + global_name = symbol.name + # Make sure we don't lookup the same variable twice + if global_name not in global_names: + global_names.append(global_name) + # Find all global variables by name + global_variable_list = module.FindGlobalVariables( + target, global_name, lldb.UINT32_MAX) + if global_variable_list: + # Print results for anything that matched + for global_variable in global_variable_list: + # returns the global variable name as a string + print('name = %s' % global_variable.name) + # Returns the variable value as a string + print('value = %s' % global_variable.value) + print('type = %s' % global_variable.type) # Returns an lldb.SBType object + # Returns an lldb.SBAddress (section offset + # address) for this global + print('addr = %s' % global_variable.addr) + # Returns the file virtual address for this + # global + print('file_addr = 0x%x' % global_variable.addr.file_addr) + # returns the global variable value as a string + print('location = %s' % global_variable.location) + # Returns the size in bytes of this global + # variable + print('size = %s' % global_variable.size) + print() + + +def globals(command_args): + '''Extract all globals from any arguments which must be paths to object files.''' + usage = "usage: %prog [options] <PATH> [PATH ...]" + description = '''This command will find all globals in the specified object file and return an list() of lldb.SBValue objects (which might be empty).''' + parser = optparse.OptionParser( + description=description, + prog='globals', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + parser.add_option( + '-a', + '--arch', + type='string', + metavar='arch', + dest='arch', + help='Specify an architecture (or triple) to use when extracting from a file.') + parser.add_option( + '-p', + '--platform', + type='string', + metavar='platform', + dest='platform', + help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + try: + (options, args) = parser.parse_args(command_args) + except: + return + + for path in args: + get_globals(path, options) + +if __name__ == '__main__': + lldb.debugger = lldb.SBDebugger.Create() + globals(sys.argv[1:]) diff --git a/gnu/llvm/lldb/examples/python/in_call_stack.py b/gnu/llvm/lldb/examples/python/in_call_stack.py new file mode 100755 index 00000000000..0647f2ca342 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/in_call_stack.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand( + 'command alias in_call_stack breakpoint command add --python-function in_call_stack.in_call_stack -k name -v %1' + ) + + +def in_call_stack(frame, bp_loc, arg_dict, _): + """Only break if the given name is in the current call stack.""" + name = arg_dict.GetValueForKey('name').GetStringValue(1000) + thread = frame.GetThread() + found = False + for frame in thread.frames: + # Check the symbol. + symbol = frame.GetSymbol() + if symbol and name in frame.GetSymbol().GetName(): + return True + # Check the function. + function = frame.GetFunction() + if function and name in function.GetName(): + return True + return False diff --git a/gnu/llvm/lldb/examples/python/jump.py b/gnu/llvm/lldb/examples/python/jump.py new file mode 100644 index 00000000000..dc4cc48b144 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/jump.py @@ -0,0 +1,198 @@ +from __future__ import print_function + +import lldb +import re + + +def parse_linespec(linespec, frame, result): + """Handles a subset of GDB-style linespecs. Specifically: + + number - A line in the current file + +offset - The line /offset/ lines after this line + -offset - The line /offset/ lines before this line + filename:number - Line /number/ in file /filename/ + function - The start of /function/ + *address - The pointer target of /address/, which must be a literal (but see `` in LLDB) + + We explicitly do not handle filename:function because it is ambiguous in Objective-C. + + This function returns a list of addresses.""" + + breakpoint = None + target = frame.GetThread().GetProcess().GetTarget() + + matched = False + + if (not matched): + mo = re.match("^([0-9]+)$", linespec) + if (mo is not None): + matched = True + # print "Matched <linenum>" + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage( + "Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation( + line_entry.GetFileSpec(), line_number) + + if (not matched): + mo = re.match("^\+([0-9]+)$", linespec) + if (mo is not None): + matched = True + # print "Matched +<count>" + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage( + "Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation( + line_entry.GetFileSpec(), (line_entry.GetLine() + line_number)) + + if (not matched): + mo = re.match("^\-([0-9]+)$", linespec) + if (mo is not None): + matched = True + # print "Matched -<count>" + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage( + "Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation( + line_entry.GetFileSpec(), (line_entry.GetLine() - line_number)) + + if (not matched): + mo = re.match("^(.*):([0-9]+)$", linespec) + if (mo is not None): + matched = True + # print "Matched <filename>:<linenum>" + file_name = mo.group(1) + line_number = int(mo.group(2)) + breakpoint = target.BreakpointCreateByLocation( + file_name, line_number) + + if (not matched): + mo = re.match("\*((0x)?([0-9a-f]+))$", linespec) + if (mo is not None): + matched = True + # print "Matched <address-expression>" + address = int(mo.group(1), base=0) + breakpoint = target.BreakpointCreateByAddress(address) + + if (not matched): + # print "Trying <function-name>" + breakpoint = target.BreakpointCreateByName(linespec) + + num_locations = breakpoint.GetNumLocations() + + if (num_locations == 0): + result.AppendMessage( + "The line specification provided doesn't resolve to any addresses.") + + addr_list = [] + + for location_index in range(num_locations): + location = breakpoint.GetLocationAtIndex(location_index) + addr_list.append(location.GetAddress()) + + target.BreakpointDelete(breakpoint.GetID()) + + return addr_list + + +def usage_string(): + return """ Sets the program counter to a specific address. + +Syntax: jump <linespec> [<location-id>] + +Command Options Usage: + jump <linenum> + jump +<count> + jump -<count> + jump <filename>:<linenum> + jump <function-name> + jump *<address-expression> + +<location-id> serves to disambiguate when multiple locations could be meant.""" + + +def jump(debugger, command, result, internal_dict): + if (command == ""): + result.AppendMessage(usage_string()) + + args = command.split() + + if not debugger.IsValid(): + result.AppendMessage("Invalid debugger!") + return + + target = debugger.GetSelectedTarget() + if not target.IsValid(): + result.AppendMessage("jump requires a valid target.") + return + + process = target.GetProcess() + if not process.IsValid(): + result.AppendMessage("jump requires a valid process.") + return + + thread = process.GetSelectedThread() + if not thread.IsValid(): + result.AppendMessage("jump requires a valid thread.") + return + + frame = thread.GetSelectedFrame() + if not frame.IsValid(): + result.AppendMessage("jump requires a valid frame.") + return + + addresses = parse_linespec(args[0], frame, result) + + stream = lldb.SBStream() + + if len(addresses) == 0: + return + + desired_address = addresses[0] + + if len(addresses) > 1: + if len(args) == 2: + desired_index = int(args[1]) + if (desired_index >= 0) and (desired_index < len(addresses)): + desired_address = addresses[desired_index] + else: + result.AppendMessage( + "Desired index " + + args[1] + + " is not one of the options.") + return + else: + index = 0 + result.AppendMessage( + "The specified location resolves to multiple targets.") + for address in addresses: + stream.Clear() + address.GetDescription(stream) + result.AppendMessage( + " Location ID " + + str(index) + + ": " + + stream.GetData()) + index = index + 1 + result.AppendMessage( + "Please type 'jump " + + command + + " <location-id>' to choose one.") + return + + frame.SetPC(desired_address.GetLoadAddress(target)) + +if lldb.debugger: + # Module is being run inside the LLDB interpreter + jump.__doc__ = usage_string() + lldb.debugger.HandleCommand('command script add -f jump.jump jump') + print('The "jump" command has been installed, type "help jump" or "jump <ENTER>" for detailed help.') diff --git a/gnu/llvm/lldb/examples/python/lldb_module_utils.py b/gnu/llvm/lldb/examples/python/lldb_module_utils.py new file mode 100644 index 00000000000..2b2fea9d4f1 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/lldb_module_utils.py @@ -0,0 +1,192 @@ +#!/usr/bin/python +from __future__ import print_function + +import lldb +import optparse +import shlex +import string +import sys + + +class DumpLineTables: + command_name = "dump-line-tables" + short_decription = "Dumps full paths to compile unit files and optionally all line table files." + description = 'Dumps all line tables from all compile units for any modules specified as arguments. Specifying the --verbose flag will output address ranges for each line entry.' + usage = "usage: %prog [options] MODULE1 [MODULE2 ...]" + def create_options(self): + self.parser = optparse.OptionParser( + description=self.description, + prog=self.command_name, + usage=self.usage) + + self.parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='Display verbose output.', + default=False) + + def get_short_help(self): + return self.short_decription + + 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): + # 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: + # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + result.SetError("option parsing failed") + return + + # Always get program state from the SBExecutionContext passed in as exe_ctx + target = exe_ctx.GetTarget() + if not target.IsValid(): + result.SetError("invalid target") + return + + for module_path in args: + module = target.module[module_path] + if not module: + result.SetError('no module found that matches "%s".' % (module_path)) + return + num_cus = module.GetNumCompileUnits() + print('Module: "%s"' % (module.file.fullpath), end=' ', file=result) + if num_cus == 0: + print('no debug info.', file=result) + continue + print('has %u compile units:' % (num_cus), file=result) + for cu_idx in range(num_cus): + cu = module.GetCompileUnitAtIndex(cu_idx) + print(' Compile Unit: %s' % (cu.file.fullpath), file=result) + for line_idx in range(cu.GetNumLineEntries()): + line_entry = cu.GetLineEntryAtIndex(line_idx) + start_file_addr = line_entry.addr.file_addr + end_file_addr = line_entry.end_addr.file_addr + # If the two addresses are equal, this line table entry + # is a termination entry + if options.verbose: + if start_file_addr != end_file_addr: + result.PutCString( + ' [%#x - %#x): %s' % + (start_file_addr, end_file_addr, line_entry)) + else: + if start_file_addr == end_file_addr: + result.PutCString(' %#x: END' % + (start_file_addr)) + else: + result.PutCString( + ' %#x: %s' % + (start_file_addr, line_entry)) + if start_file_addr == end_file_addr: + result.PutCString("\n") + + +class DumpFiles: + command_name = "dump-files" + short_description = "Dumps full paths to compile unit files and optionally all line table files." + usage = "usage: %prog [options] MODULE1 [MODULE2 ...]" + description = '''This class adds a dump-files command to the LLDB interpreter. + +This command will dump all compile unit file paths found for each source file +for the binaries specified as arguments in the current target. Specify the +--support-files or -s option to see all file paths that a compile unit uses in +its lines tables. This is handy for troubleshooting why breakpoints aren't +working in IDEs that specify full paths to source files when setting file and +line breakpoints. Sometimes symlinks cause the debug info to contain the symlink +path and an IDE will resolve the path to the actual file and use the resolved +path when setting breakpoints. +''' + def create_options(self): + # Pass add_help_option = False, since this keeps the command in line with lldb commands, + # and we wire up "help command" to work by providing the long & short help methods below. + self.parser = optparse.OptionParser( + description = self.description, + prog = self.command_name, + usage = self.usage, + add_help_option = False) + + self.parser.add_option( + '-s', + '--support-files', + action = 'store_true', + dest = 'support_files', + help = 'Dumps full paths to all files used in a compile unit.', + default = False) + + def get_short_help(self): + return self.short_description + + 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): + # 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: + # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + result.SetError("option parsing failed") + return + + # Always get program state from the SBExecutionContext passed in as exe_ctx + target = exe_ctx.GetTarget() + if not target.IsValid(): + result.SetError("invalid target") + return + + if len(args) == 0: + result.SetError("one or more executable paths must be specified") + return + + for module_path in args: + module = target.module[module_path] + if not module: + result.SetError('no module found that matches "%s".' % (module_path)) + return + num_cus = module.GetNumCompileUnits() + print('Module: "%s"' % (module.file.fullpath), end=' ', file=result) + if num_cus == 0: + print('no debug info.', file=result) + continue + print('has %u compile units:' % (num_cus), file=result) + for i in range(num_cus): + cu = module.GetCompileUnitAtIndex(i) + print(' Compile Unit: %s' % (cu.file.fullpath), file=result) + if options.support_files: + num_support_files = cu.GetNumSupportFiles() + for j in range(num_support_files): + path = cu.GetSupportFileAtIndex(j).fullpath + print(' file[%u]: %s' % (j, path), file=result) + + +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.DumpLineTables %s' % (__name__, + DumpLineTables.command_name)) + debugger.HandleCommand( + 'command script add -c %s.DumpFiles %s' % (__name__, DumpFiles.command_name)) + print('The "%s" and "%s" commands have been installed.' % (DumpLineTables.command_name, + DumpFiles.command_name)) diff --git a/gnu/llvm/lldb/examples/python/lldbtk.py b/gnu/llvm/lldb/examples/python/lldbtk.py new file mode 100644 index 00000000000..3734b14f95b --- /dev/null +++ b/gnu/llvm/lldb/examples/python/lldbtk.py @@ -0,0 +1,618 @@ +#!/usr/bin/python +from __future__ import print_function + +import lldb +import shlex +import sys +try: + from tkinter import * + import tkinter.ttk as ttk +except ImportError: + from Tkinter import * + import ttk + + +class ValueTreeItemDelegate(object): + + def __init__(self, value): + self.value = value + + def get_item_dictionary(self): + name = self.value.name + if name is None: + name = '' + typename = self.value.type + if typename is None: + typename = '' + value = self.value.value + if value is None: + value = '' + summary = self.value.summary + if summary is None: + summary = '' + has_children = self.value.MightHaveChildren() + return {'#0': name, + 'typename': typename, + 'value': value, + 'summary': summary, + 'children': has_children, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + for i in range(self.value.num_children): + item_delegate = ValueTreeItemDelegate( + self.value.GetChildAtIndex(i)) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + + +class FrameTreeItemDelegate(object): + + def __init__(self, frame): + self.frame = frame + + def get_item_dictionary(self): + id = self.frame.GetFrameID() + name = 'frame #%u' % (id) + value = '0x%16.16x' % (self.frame.GetPC()) + stream = lldb.SBStream() + self.frame.GetDescription(stream) + summary = stream.GetData().split("`")[1] + return { + '#0': name, + 'value': value, + 'summary': summary, + 'children': self.frame.GetVariables( + True, + True, + True, + True).GetSize() > 0, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + variables = self.frame.GetVariables(True, True, True, True) + n = variables.GetSize() + for i in range(n): + item_delegate = ValueTreeItemDelegate(variables[i]) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + + +class ThreadTreeItemDelegate(object): + + def __init__(self, thread): + self.thread = thread + + def get_item_dictionary(self): + num_frames = self.thread.GetNumFrames() + name = 'thread #%u' % (self.thread.GetIndexID()) + value = '0x%x' % (self.thread.GetThreadID()) + summary = '%u frames' % (num_frames) + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': num_frames > 0, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + for frame in self.thread: + item_delegate = FrameTreeItemDelegate(frame) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + + +class ProcessTreeItemDelegate(object): + + def __init__(self, process): + self.process = process + + def get_item_dictionary(self): + id = self.process.GetProcessID() + num_threads = self.process.GetNumThreads() + value = str(self.process.GetProcessID()) + summary = self.process.target.executable.fullpath + return {'#0': 'process', + 'value': value, + 'summary': summary, + 'children': num_threads > 0, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + for thread in self.process: + item_delegate = ThreadTreeItemDelegate(thread) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + + +class TargetTreeItemDelegate(object): + + def __init__(self, target): + self.target = target + + def get_item_dictionary(self): + value = str(self.target.triple) + summary = self.target.executable.fullpath + return {'#0': 'target', + 'value': value, + 'summary': summary, + 'children': True, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + image_item_delegate = TargetImagesTreeItemDelegate(self.target) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + + +class TargetImagesTreeItemDelegate(object): + + def __init__(self, target): + self.target = target + + def get_item_dictionary(self): + value = str(self.target.triple) + summary = self.target.executable.fullpath + num_modules = self.target.GetNumModules() + return {'#0': 'images', + 'value': '', + 'summary': '%u images' % num_modules, + 'children': num_modules > 0, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + for i in range(self.target.GetNumModules()): + module = self.target.GetModuleAtIndex(i) + image_item_delegate = ModuleTreeItemDelegate( + self.target, module, i) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + + +class ModuleTreeItemDelegate(object): + + def __init__(self, target, module, index): + self.target = target + self.module = module + self.index = index + + def get_item_dictionary(self): + name = 'module %u' % (self.index) + value = self.module.file.basename + summary = self.module.file.dirname + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': True, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + sections_item_delegate = ModuleSectionsTreeItemDelegate( + self.target, self.module) + item_dicts.append(sections_item_delegate.get_item_dictionary()) + + symbols_item_delegate = ModuleSymbolsTreeItemDelegate( + self.target, self.module) + item_dicts.append(symbols_item_delegate.get_item_dictionary()) + + comp_units_item_delegate = ModuleCompileUnitsTreeItemDelegate( + self.target, self.module) + item_dicts.append(comp_units_item_delegate.get_item_dictionary()) + return item_dicts + + +class ModuleSectionsTreeItemDelegate(object): + + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'sections' + value = '' + summary = '%u sections' % (self.module.GetNumSections()) + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': True, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + num_sections = self.module.GetNumSections() + for i in range(num_sections): + section = self.module.GetSectionAtIndex(i) + image_item_delegate = SectionTreeItemDelegate(self.target, section) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + + +class SectionTreeItemDelegate(object): + + def __init__(self, target, section): + self.target = target + self.section = section + + def get_item_dictionary(self): + name = self.section.name + section_load_addr = self.section.GetLoadAddress(self.target) + if section_load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (section_load_addr) + else: + value = '0x%16.16x *' % (self.section.file_addr) + summary = '' + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': self.section.GetNumSubSections() > 0, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + num_sections = self.section.GetNumSubSections() + for i in range(num_sections): + section = self.section.GetSubSectionAtIndex(i) + image_item_delegate = SectionTreeItemDelegate(self.target, section) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + + +class ModuleCompileUnitsTreeItemDelegate(object): + + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'compile units' + value = '' + summary = '%u compile units' % (self.module.GetNumSections()) + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': self.module.GetNumCompileUnits() > 0, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + num_cus = self.module.GetNumCompileUnits() + for i in range(num_cus): + cu = self.module.GetCompileUnitAtIndex(i) + image_item_delegate = CompileUnitTreeItemDelegate(self.target, cu) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + + +class CompileUnitTreeItemDelegate(object): + + def __init__(self, target, cu): + self.target = target + self.cu = cu + + def get_item_dictionary(self): + name = self.cu.GetFileSpec().basename + value = '' + num_lines = self.cu.GetNumLineEntries() + summary = '' + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': num_lines > 0, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + item_delegate = LineTableTreeItemDelegate(self.target, self.cu) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + + +class LineTableTreeItemDelegate(object): + + def __init__(self, target, cu): + self.target = target + self.cu = cu + + def get_item_dictionary(self): + name = 'line table' + value = '' + num_lines = self.cu.GetNumLineEntries() + summary = '%u line entries' % (num_lines) + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': num_lines > 0, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + num_lines = self.cu.GetNumLineEntries() + for i in range(num_lines): + line_entry = self.cu.GetLineEntryAtIndex(i) + item_delegate = LineEntryTreeItemDelegate( + self.target, line_entry, i) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + + +class LineEntryTreeItemDelegate(object): + + def __init__(self, target, line_entry, index): + self.target = target + self.line_entry = line_entry + self.index = index + + def get_item_dictionary(self): + name = str(self.index) + address = self.line_entry.GetStartAddress() + load_addr = address.GetLoadAddress(self.target) + if load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (load_addr) + else: + value = '0x%16.16x *' % (address.file_addr) + summary = self.line_entry.GetFileSpec().fullpath + ':' + \ + str(self.line_entry.line) + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': False, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + return item_dicts + + +class InstructionTreeItemDelegate(object): + + def __init__(self, target, instr): + self.target = target + self.instr = instr + + def get_item_dictionary(self): + address = self.instr.GetAddress() + load_addr = address.GetLoadAddress(self.target) + if load_addr != lldb.LLDB_INVALID_ADDRESS: + name = '0x%16.16x' % (load_addr) + else: + name = '0x%16.16x *' % (address.file_addr) + value = self.instr.GetMnemonic( + self.target) + ' ' + self.instr.GetOperands(self.target) + summary = self.instr.GetComment(self.target) + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': False, + 'tree-item-delegate': self} + + +class ModuleSymbolsTreeItemDelegate(object): + + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'symbols' + value = '' + summary = '%u symbols' % (self.module.GetNumSymbols()) + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': True, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + num_symbols = self.module.GetNumSymbols() + for i in range(num_symbols): + symbol = self.module.GetSymbolAtIndex(i) + image_item_delegate = SymbolTreeItemDelegate( + self.target, symbol, i) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + + +class SymbolTreeItemDelegate(object): + + def __init__(self, target, symbol, index): + self.target = target + self.symbol = symbol + self.index = index + + def get_item_dictionary(self): + address = self.symbol.GetStartAddress() + name = '[%u]' % self.index + symbol_load_addr = address.GetLoadAddress(self.target) + if symbol_load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (symbol_load_addr) + else: + value = '0x%16.16x *' % (address.file_addr) + summary = self.symbol.name + return {'#0': name, + 'value': value, + 'summary': summary, + 'children': False, + 'tree-item-delegate': self} + + def get_child_item_dictionaries(self): + item_dicts = list() + return item_dicts + + +class DelegateTree(ttk.Frame): + + def __init__(self, column_dicts, delegate, title, name): + ttk.Frame.__init__(self, name=name) + self.pack(expand=Y, fill=BOTH) + self.master.title(title) + self.delegate = delegate + self.columns_dicts = column_dicts + self.item_id_to_item_dict = dict() + frame = Frame(self) + frame.pack(side=TOP, fill=BOTH, expand=Y) + self._create_treeview(frame) + self._populate_root() + + def _create_treeview(self, parent): + frame = ttk.Frame(parent) + frame.pack(side=TOP, fill=BOTH, expand=Y) + + column_ids = list() + for i in range(1, len(self.columns_dicts)): + column_ids.append(self.columns_dicts[i]['id']) + # create the tree and scrollbars + self.tree = ttk.Treeview(columns=column_ids) + + scroll_bar_v = ttk.Scrollbar(orient=VERTICAL, command=self.tree.yview) + scroll_bar_h = ttk.Scrollbar( + orient=HORIZONTAL, command=self.tree.xview) + self.tree['yscroll'] = scroll_bar_v.set + self.tree['xscroll'] = scroll_bar_h.set + + # setup column headings and columns properties + for columns_dict in self.columns_dicts: + self.tree.heading( + columns_dict['id'], + text=columns_dict['text'], + anchor=columns_dict['anchor']) + self.tree.column( + columns_dict['id'], + stretch=columns_dict['stretch']) + + # add tree and scrollbars to frame + self.tree.grid(in_=frame, row=0, column=0, sticky=NSEW) + scroll_bar_v.grid(in_=frame, row=0, column=1, sticky=NS) + scroll_bar_h.grid(in_=frame, row=1, column=0, sticky=EW) + + # set frame resizing priorities + frame.rowconfigure(0, weight=1) + frame.columnconfigure(0, weight=1) + + # action to perform when a node is expanded + self.tree.bind('<<TreeviewOpen>>', self._update_tree) + + def insert_items(self, parent_id, item_dicts): + for item_dict in item_dicts: + name = None + values = list() + first = True + for columns_dict in self.columns_dicts: + if first: + name = item_dict[columns_dict['id']] + first = False + else: + values.append(item_dict[columns_dict['id']]) + item_id = self.tree.insert(parent_id, # root item has an empty name + END, + text=name, + values=values) + self.item_id_to_item_dict[item_id] = item_dict + if item_dict['children']: + self.tree.insert(item_id, END, text='dummy') + + def _populate_root(self): + # use current directory as root node + self.insert_items('', self.delegate.get_child_item_dictionaries()) + + def _update_tree(self, event): + # user expanded a node - build the related directory + item_id = self.tree.focus() # the id of the expanded node + children = self.tree.get_children(item_id) + if len(children): + first_child = children[0] + # if the node only has a 'dummy' child, remove it and + # build new directory; skip if the node is already + # populated + if self.tree.item(first_child, option='text') == 'dummy': + self.tree.delete(first_child) + item_dict = self.item_id_to_item_dict[item_id] + item_dicts = item_dict[ + 'tree-item-delegate'].get_child_item_dictionaries() + self.insert_items(item_id, item_dicts) + + +@lldb.command("tk-variables") +def tk_variable_display(debugger, command, result, dict): + # needed for tree creation in TK library as it uses sys.argv... + sys.argv = ['tk-variables'] + target = debugger.GetSelectedTarget() + if not target: + print("invalid target", file=result) + return + process = target.GetProcess() + if not process: + print("invalid process", file=result) + return + thread = process.GetSelectedThread() + if not thread: + print("invalid thread", file=result) + return + frame = thread.GetSelectedFrame() + if not frame: + print("invalid frame", file=result) + return + # Parse command line args + command_args = shlex.split(command) + column_dicts = [{'id': '#0', 'text': 'Name', 'anchor': W, 'stretch': 0}, + {'id': 'typename', 'text': 'Type', 'anchor': W, 'stretch': 0}, + {'id': 'value', 'text': 'Value', 'anchor': W, 'stretch': 0}, + {'id': 'summary', 'text': 'Summary', 'anchor': W, 'stretch': 1}] + tree = DelegateTree( + column_dicts, + FrameTreeItemDelegate(frame), + 'Variables', + 'lldb-tk-variables') + tree.mainloop() + + +@lldb.command("tk-process") +def tk_process_display(debugger, command, result, dict): + # needed for tree creation in TK library as it uses sys.argv... + sys.argv = ['tk-process'] + target = debugger.GetSelectedTarget() + if not target: + print("invalid target", file=result) + return + process = target.GetProcess() + if not process: + print("invalid process", file=result) + return + # Parse command line args + columnd_dicts = [{'id': '#0', 'text': 'Name', 'anchor': W, 'stretch': 0}, + {'id': 'value', 'text': 'Value', 'anchor': W, 'stretch': 0}, + {'id': 'summary', 'text': 'Summary', 'anchor': W, 'stretch': 1}] + command_args = shlex.split(command) + tree = DelegateTree( + columnd_dicts, + ProcessTreeItemDelegate(process), + 'Process', + 'lldb-tk-process') + tree.mainloop() + + +@lldb.command("tk-target") +def tk_target_display(debugger, command, result, dict): + # needed for tree creation in TK library as it uses sys.argv... + sys.argv = ['tk-target'] + target = debugger.GetSelectedTarget() + if not target: + print("invalid target", file=result) + return + # Parse command line args + columnd_dicts = [{'id': '#0', 'text': 'Name', 'anchor': W, 'stretch': 0}, + {'id': 'value', 'text': 'Value', 'anchor': W, 'stretch': 0}, + {'id': 'summary', 'text': 'Summary', 'anchor': W, 'stretch': 1}] + command_args = shlex.split(command) + tree = DelegateTree( + columnd_dicts, + TargetTreeItemDelegate(target), + 'Target', + 'lldb-tk-target') + tree.mainloop() diff --git a/gnu/llvm/lldb/examples/python/mach_o.py b/gnu/llvm/lldb/examples/python/mach_o.py new file mode 100755 index 00000000000..eb97fe57cfc --- /dev/null +++ b/gnu/llvm/lldb/examples/python/mach_o.py @@ -0,0 +1,1846 @@ +#!/usr/bin/python +from __future__ import print_function + +import cmd +import dict_utils +import file_extract +import optparse +import re +import struct +import string +import io +import sys +import uuid + +# Mach header "magic" constants +MH_MAGIC = 0xfeedface +MH_CIGAM = 0xcefaedfe +MH_MAGIC_64 = 0xfeedfacf +MH_CIGAM_64 = 0xcffaedfe +FAT_MAGIC = 0xcafebabe +FAT_CIGAM = 0xbebafeca + +# Mach haeder "filetype" constants +MH_OBJECT = 0x00000001 +MH_EXECUTE = 0x00000002 +MH_FVMLIB = 0x00000003 +MH_CORE = 0x00000004 +MH_PRELOAD = 0x00000005 +MH_DYLIB = 0x00000006 +MH_DYLINKER = 0x00000007 +MH_BUNDLE = 0x00000008 +MH_DYLIB_STUB = 0x00000009 +MH_DSYM = 0x0000000a +MH_KEXT_BUNDLE = 0x0000000b + +# Mach haeder "flag" constant bits +MH_NOUNDEFS = 0x00000001 +MH_INCRLINK = 0x00000002 +MH_DYLDLINK = 0x00000004 +MH_BINDATLOAD = 0x00000008 +MH_PREBOUND = 0x00000010 +MH_SPLIT_SEGS = 0x00000020 +MH_LAZY_INIT = 0x00000040 +MH_TWOLEVEL = 0x00000080 +MH_FORCE_FLAT = 0x00000100 +MH_NOMULTIDEFS = 0x00000200 +MH_NOFIXPREBINDING = 0x00000400 +MH_PREBINDABLE = 0x00000800 +MH_ALLMODSBOUND = 0x00001000 +MH_SUBSECTIONS_VIA_SYMBOLS = 0x00002000 +MH_CANONICAL = 0x00004000 +MH_WEAK_DEFINES = 0x00008000 +MH_BINDS_TO_WEAK = 0x00010000 +MH_ALLOW_STACK_EXECUTION = 0x00020000 +MH_ROOT_SAFE = 0x00040000 +MH_SETUID_SAFE = 0x00080000 +MH_NO_REEXPORTED_DYLIBS = 0x00100000 +MH_PIE = 0x00200000 +MH_DEAD_STRIPPABLE_DYLIB = 0x00400000 +MH_HAS_TLV_DESCRIPTORS = 0x00800000 +MH_NO_HEAP_EXECUTION = 0x01000000 + +# Mach load command constants +LC_REQ_DYLD = 0x80000000 +LC_SEGMENT = 0x00000001 +LC_SYMTAB = 0x00000002 +LC_SYMSEG = 0x00000003 +LC_THREAD = 0x00000004 +LC_UNIXTHREAD = 0x00000005 +LC_LOADFVMLIB = 0x00000006 +LC_IDFVMLIB = 0x00000007 +LC_IDENT = 0x00000008 +LC_FVMFILE = 0x00000009 +LC_PREPAGE = 0x0000000a +LC_DYSYMTAB = 0x0000000b +LC_LOAD_DYLIB = 0x0000000c +LC_ID_DYLIB = 0x0000000d +LC_LOAD_DYLINKER = 0x0000000e +LC_ID_DYLINKER = 0x0000000f +LC_PREBOUND_DYLIB = 0x00000010 +LC_ROUTINES = 0x00000011 +LC_SUB_FRAMEWORK = 0x00000012 +LC_SUB_UMBRELLA = 0x00000013 +LC_SUB_CLIENT = 0x00000014 +LC_SUB_LIBRARY = 0x00000015 +LC_TWOLEVEL_HINTS = 0x00000016 +LC_PREBIND_CKSUM = 0x00000017 +LC_LOAD_WEAK_DYLIB = 0x00000018 | LC_REQ_DYLD +LC_SEGMENT_64 = 0x00000019 +LC_ROUTINES_64 = 0x0000001a +LC_UUID = 0x0000001b +LC_RPATH = 0x0000001c | LC_REQ_DYLD +LC_CODE_SIGNATURE = 0x0000001d +LC_SEGMENT_SPLIT_INFO = 0x0000001e +LC_REEXPORT_DYLIB = 0x0000001f | LC_REQ_DYLD +LC_LAZY_LOAD_DYLIB = 0x00000020 +LC_ENCRYPTION_INFO = 0x00000021 +LC_DYLD_INFO = 0x00000022 +LC_DYLD_INFO_ONLY = 0x00000022 | LC_REQ_DYLD +LC_LOAD_UPWARD_DYLIB = 0x00000023 | LC_REQ_DYLD +LC_VERSION_MIN_MACOSX = 0x00000024 +LC_VERSION_MIN_IPHONEOS = 0x00000025 +LC_FUNCTION_STARTS = 0x00000026 +LC_DYLD_ENVIRONMENT = 0x00000027 + +# Mach CPU constants +CPU_ARCH_MASK = 0xff000000 +CPU_ARCH_ABI64 = 0x01000000 +CPU_TYPE_ANY = 0xffffffff +CPU_TYPE_VAX = 1 +CPU_TYPE_MC680x0 = 6 +CPU_TYPE_I386 = 7 +CPU_TYPE_X86_64 = CPU_TYPE_I386 | CPU_ARCH_ABI64 +CPU_TYPE_MIPS = 8 +CPU_TYPE_MC98000 = 10 +CPU_TYPE_HPPA = 11 +CPU_TYPE_ARM = 12 +CPU_TYPE_MC88000 = 13 +CPU_TYPE_SPARC = 14 +CPU_TYPE_I860 = 15 +CPU_TYPE_ALPHA = 16 +CPU_TYPE_POWERPC = 18 +CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64 + +# VM protection constants +VM_PROT_READ = 1 +VM_PROT_WRITE = 2 +VM_PROT_EXECUTE = 4 + +# VM protection constants +N_STAB = 0xe0 +N_PEXT = 0x10 +N_TYPE = 0x0e +N_EXT = 0x01 + +# Values for nlist N_TYPE bits of the "Mach.NList.type" field. +N_UNDF = 0x0 +N_ABS = 0x2 +N_SECT = 0xe +N_PBUD = 0xc +N_INDR = 0xa + +# Section indexes for the "Mach.NList.sect_idx" fields +NO_SECT = 0 +MAX_SECT = 255 + +# Stab defines +N_GSYM = 0x20 +N_FNAME = 0x22 +N_FUN = 0x24 +N_STSYM = 0x26 +N_LCSYM = 0x28 +N_BNSYM = 0x2e +N_OPT = 0x3c +N_RSYM = 0x40 +N_SLINE = 0x44 +N_ENSYM = 0x4e +N_SSYM = 0x60 +N_SO = 0x64 +N_OSO = 0x66 +N_LSYM = 0x80 +N_BINCL = 0x82 +N_SOL = 0x84 +N_PARAMS = 0x86 +N_VERSION = 0x88 +N_OLEVEL = 0x8A +N_PSYM = 0xa0 +N_EINCL = 0xa2 +N_ENTRY = 0xa4 +N_LBRAC = 0xc0 +N_EXCL = 0xc2 +N_RBRAC = 0xe0 +N_BCOMM = 0xe2 +N_ECOMM = 0xe4 +N_ECOML = 0xe8 +N_LENG = 0xfe + +vm_prot_names = ['---', 'r--', '-w-', 'rw-', '--x', 'r-x', '-wx', 'rwx'] + + +def dump_memory(base_addr, data, hex_bytes_len, num_per_line): + hex_bytes = data.encode('hex') + if hex_bytes_len == -1: + hex_bytes_len = len(hex_bytes) + addr = base_addr + ascii_str = '' + i = 0 + while i < hex_bytes_len: + if ((i / 2) % num_per_line) == 0: + if i > 0: + print(' %s' % (ascii_str)) + ascii_str = '' + print('0x%8.8x:' % (addr + i), end=' ') + hex_byte = hex_bytes[i:i + 2] + print(hex_byte, end=' ') + int_byte = int(hex_byte, 16) + ascii_char = '%c' % (int_byte) + if int_byte >= 32 and int_byte < 127: + ascii_str += ascii_char + else: + ascii_str += '.' + i = i + 2 + if ascii_str: + if (i / 2) % num_per_line: + padding = num_per_line - ((i / 2) % num_per_line) + else: + padding = 0 + print('%*s%s' % (padding * 3 + 1, '', ascii_str)) + print() + + +class TerminalColors: + '''Simple terminal colors class''' + + def __init__(self, enabled=True): + # TODO: discover terminal type from "file" and disable if + # it can't handle the color codes + self.enabled = enabled + + def reset(self): + '''Reset all terminal colors and formatting.''' + if self.enabled: + return "\x1b[0m" + return '' + + def bold(self, on=True): + '''Enable or disable bold depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[1m" + else: + return "\x1b[22m" + return '' + + def italics(self, on=True): + '''Enable or disable italics depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[3m" + else: + return "\x1b[23m" + return '' + + def underline(self, on=True): + '''Enable or disable underline depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[4m" + else: + return "\x1b[24m" + return '' + + def inverse(self, on=True): + '''Enable or disable inverse depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[7m" + else: + return "\x1b[27m" + return '' + + def strike(self, on=True): + '''Enable or disable strike through depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[9m" + else: + return "\x1b[29m" + return '' + + def black(self, fg=True): + '''Set the foreground or background color to black. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[30m" + else: + return "\x1b[40m" + return '' + + def red(self, fg=True): + '''Set the foreground or background color to red. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[31m" + else: + return "\x1b[41m" + return '' + + def green(self, fg=True): + '''Set the foreground or background color to green. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[32m" + else: + return "\x1b[42m" + return '' + + def yellow(self, fg=True): + '''Set the foreground or background color to yellow. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[43m" + else: + return "\x1b[33m" + return '' + + def blue(self, fg=True): + '''Set the foreground or background color to blue. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[34m" + else: + return "\x1b[44m" + return '' + + def magenta(self, fg=True): + '''Set the foreground or background color to magenta. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[35m" + else: + return "\x1b[45m" + return '' + + def cyan(self, fg=True): + '''Set the foreground or background color to cyan. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[36m" + else: + return "\x1b[46m" + return '' + + def white(self, fg=True): + '''Set the foreground or background color to white. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[37m" + else: + return "\x1b[47m" + return '' + + def default(self, fg=True): + '''Set the foreground or background color to the default. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[39m" + else: + return "\x1b[49m" + return '' + + +def swap_unpack_char(): + """Returns the unpack prefix that will for non-native endian-ness.""" + if struct.pack('H', 1).startswith("\x00"): + return '<' + return '>' + + +def dump_hex_bytes(addr, s, bytes_per_line=16): + i = 0 + line = '' + for ch in s: + if (i % bytes_per_line) == 0: + if line: + print(line) + line = '%#8.8x: ' % (addr + i) + line += "%02X " % ord(ch) + i += 1 + print(line) + + +def dump_hex_byte_string_diff(addr, a, b, bytes_per_line=16): + i = 0 + line = '' + a_len = len(a) + b_len = len(b) + if a_len < b_len: + max_len = b_len + else: + max_len = a_len + tty_colors = TerminalColors(True) + for i in range(max_len): + ch = None + if i < a_len: + ch_a = a[i] + ch = ch_a + else: + ch_a = None + if i < b_len: + ch_b = b[i] + if not ch: + ch = ch_b + else: + ch_b = None + mismatch = ch_a != ch_b + if (i % bytes_per_line) == 0: + if line: + print(line) + line = '%#8.8x: ' % (addr + i) + if mismatch: + line += tty_colors.red() + line += "%02X " % ord(ch) + if mismatch: + line += tty_colors.default() + i += 1 + + print(line) + + +class Mach: + """Class that does everything mach-o related""" + + class Arch: + """Class that implements mach-o architectures""" + + def __init__(self, c=0, s=0): + self.cpu = c + self.sub = s + + def set_cpu_type(self, c): + self.cpu = c + + def set_cpu_subtype(self, s): + self.sub = s + + def set_arch(self, c, s): + self.cpu = c + self.sub = s + + def is_64_bit(self): + return (self.cpu & CPU_ARCH_ABI64) != 0 + + cpu_infos = [ + ["arm", CPU_TYPE_ARM, CPU_TYPE_ANY], + ["arm", CPU_TYPE_ARM, 0], + ["armv4", CPU_TYPE_ARM, 5], + ["armv6", CPU_TYPE_ARM, 6], + ["armv5", CPU_TYPE_ARM, 7], + ["xscale", CPU_TYPE_ARM, 8], + ["armv7", CPU_TYPE_ARM, 9], + ["armv7f", CPU_TYPE_ARM, 10], + ["armv7s", CPU_TYPE_ARM, 11], + ["armv7k", CPU_TYPE_ARM, 12], + ["armv7m", CPU_TYPE_ARM, 15], + ["armv7em", CPU_TYPE_ARM, 16], + ["ppc", CPU_TYPE_POWERPC, CPU_TYPE_ANY], + ["ppc", CPU_TYPE_POWERPC, 0], + ["ppc601", CPU_TYPE_POWERPC, 1], + ["ppc602", CPU_TYPE_POWERPC, 2], + ["ppc603", CPU_TYPE_POWERPC, 3], + ["ppc603e", CPU_TYPE_POWERPC, 4], + ["ppc603ev", CPU_TYPE_POWERPC, 5], + ["ppc604", CPU_TYPE_POWERPC, 6], + ["ppc604e", CPU_TYPE_POWERPC, 7], + ["ppc620", CPU_TYPE_POWERPC, 8], + ["ppc750", CPU_TYPE_POWERPC, 9], + ["ppc7400", CPU_TYPE_POWERPC, 10], + ["ppc7450", CPU_TYPE_POWERPC, 11], + ["ppc970", CPU_TYPE_POWERPC, 100], + ["ppc64", CPU_TYPE_POWERPC64, 0], + ["ppc970-64", CPU_TYPE_POWERPC64, 100], + ["i386", CPU_TYPE_I386, 3], + ["i486", CPU_TYPE_I386, 4], + ["i486sx", CPU_TYPE_I386, 0x84], + ["i386", CPU_TYPE_I386, CPU_TYPE_ANY], + ["x86_64", CPU_TYPE_X86_64, 3], + ["x86_64", CPU_TYPE_X86_64, CPU_TYPE_ANY], + ] + + def __str__(self): + for info in self.cpu_infos: + if self.cpu == info[1] and (self.sub & 0x00ffffff) == info[2]: + return info[0] + return "{0}.{1}".format(self.cpu, self.sub) + + class Magic(dict_utils.Enum): + + enum = { + 'MH_MAGIC': MH_MAGIC, + 'MH_CIGAM': MH_CIGAM, + 'MH_MAGIC_64': MH_MAGIC_64, + 'MH_CIGAM_64': MH_CIGAM_64, + 'FAT_MAGIC': FAT_MAGIC, + 'FAT_CIGAM': FAT_CIGAM + } + + def __init__(self, initial_value=0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + def is_skinny_mach_file(self): + return self.value == MH_MAGIC or self.value == MH_CIGAM or self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64 + + def is_universal_mach_file(self): + return self.value == FAT_MAGIC or self.value == FAT_CIGAM + + def unpack(self, data): + data.set_byte_order('native') + self.value = data.get_uint32() + + def get_byte_order(self): + if self.value == MH_CIGAM or self.value == MH_CIGAM_64 or self.value == FAT_CIGAM: + return swap_unpack_char() + else: + return '=' + + def is_64_bit(self): + return self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64 + + def __init__(self): + self.magic = Mach.Magic() + self.content = None + self.path = None + + def extract(self, path, extractor): + self.path = path + self.unpack(extractor) + + def parse(self, path): + self.path = path + try: + f = open(self.path) + file_extractor = file_extract.FileExtract(f, '=') + self.unpack(file_extractor) + # f.close() + except IOError as xxx_todo_changeme: + (errno, strerror) = xxx_todo_changeme.args + print("I/O error({0}): {1}".format(errno, strerror)) + except ValueError: + print("Could not convert data to an integer.") + except: + print("Unexpected error:", sys.exc_info()[0]) + raise + + def compare(self, rhs): + self.content.compare(rhs.content) + + def dump(self, options=None): + self.content.dump(options) + + def dump_header(self, dump_description=True, options=None): + self.content.dump_header(dump_description, options) + + def dump_load_commands(self, dump_description=True, options=None): + self.content.dump_load_commands(dump_description, options) + + def dump_sections(self, dump_description=True, options=None): + self.content.dump_sections(dump_description, options) + + def dump_section_contents(self, options): + self.content.dump_section_contents(options) + + def dump_symtab(self, dump_description=True, options=None): + self.content.dump_symtab(dump_description, options) + + def dump_symbol_names_matching_regex(self, regex, file=None): + self.content.dump_symbol_names_matching_regex(regex, file) + + def description(self): + return self.content.description() + + def unpack(self, data): + self.magic.unpack(data) + if self.magic.is_skinny_mach_file(): + self.content = Mach.Skinny(self.path) + elif self.magic.is_universal_mach_file(): + self.content = Mach.Universal(self.path) + else: + self.content = None + + if self.content is not None: + self.content.unpack(data, self.magic) + + def is_valid(self): + return self.content is not None + + class Universal: + + def __init__(self, path): + self.path = path + self.type = 'universal' + self.file_off = 0 + self.magic = None + self.nfat_arch = 0 + self.archs = list() + + def description(self): + s = '%#8.8x: %s (' % (self.file_off, self.path) + archs_string = '' + for arch in self.archs: + if len(archs_string): + archs_string += ', ' + archs_string += '%s' % arch.arch + s += archs_string + s += ')' + return s + + def unpack(self, data, magic=None): + self.file_off = data.tell() + if magic is None: + self.magic = Mach.Magic() + self.magic.unpack(data) + else: + self.magic = magic + self.file_off = self.file_off - 4 + # Universal headers are always in big endian + data.set_byte_order('big') + self.nfat_arch = data.get_uint32() + for i in range(self.nfat_arch): + self.archs.append(Mach.Universal.ArchInfo()) + self.archs[i].unpack(data) + for i in range(self.nfat_arch): + self.archs[i].mach = Mach.Skinny(self.path) + data.seek(self.archs[i].offset, 0) + skinny_magic = Mach.Magic() + skinny_magic.unpack(data) + self.archs[i].mach.unpack(data, skinny_magic) + + def compare(self, rhs): + print('error: comparing two universal files is not supported yet') + return False + + def dump(self, options): + if options.dump_header: + print() + print("Universal Mach File: magic = %s, nfat_arch = %u" % (self.magic, self.nfat_arch)) + print() + if self.nfat_arch > 0: + if options.dump_header: + self.archs[0].dump_header(True, options) + for i in range(self.nfat_arch): + self.archs[i].dump_flat(options) + if options.dump_header: + print() + for i in range(self.nfat_arch): + self.archs[i].mach.dump(options) + + def dump_header(self, dump_description=True, options=None): + if dump_description: + print(self.description()) + for i in range(self.nfat_arch): + self.archs[i].mach.dump_header(True, options) + print() + + def dump_load_commands(self, dump_description=True, options=None): + if dump_description: + print(self.description()) + for i in range(self.nfat_arch): + self.archs[i].mach.dump_load_commands(True, options) + print() + + def dump_sections(self, dump_description=True, options=None): + if dump_description: + print(self.description()) + for i in range(self.nfat_arch): + self.archs[i].mach.dump_sections(True, options) + print() + + def dump_section_contents(self, options): + for i in range(self.nfat_arch): + self.archs[i].mach.dump_section_contents(options) + print() + + def dump_symtab(self, dump_description=True, options=None): + if dump_description: + print(self.description()) + for i in range(self.nfat_arch): + self.archs[i].mach.dump_symtab(True, options) + print() + + def dump_symbol_names_matching_regex(self, regex, file=None): + for i in range(self.nfat_arch): + self.archs[i].mach.dump_symbol_names_matching_regex( + regex, file) + + class ArchInfo: + + def __init__(self): + self.arch = Mach.Arch(0, 0) + self.offset = 0 + self.size = 0 + self.align = 0 + self.mach = None + + def unpack(self, data): + # Universal headers are always in big endian + data.set_byte_order('big') + self.arch.cpu, self.arch.sub, self.offset, self.size, self.align = data.get_n_uint32( + 5) + + def dump_header(self, dump_description=True, options=None): + if options.verbose: + print("CPU SUBTYPE OFFSET SIZE ALIGN") + print("---------- ---------- ---------- ---------- ----------") + else: + print("ARCH FILEOFFSET FILESIZE ALIGN") + print("---------- ---------- ---------- ----------") + + def dump_flat(self, options): + if options.verbose: + print("%#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (self.arch.cpu, self.arch.sub, self.offset, self.size, self.align)) + else: + print("%-10s %#8.8x %#8.8x %#8.8x" % (self.arch, self.offset, self.size, self.align)) + + def dump(self): + print(" cputype: %#8.8x" % self.arch.cpu) + print("cpusubtype: %#8.8x" % self.arch.sub) + print(" offset: %#8.8x" % self.offset) + print(" size: %#8.8x" % self.size) + print(" align: %#8.8x" % self.align) + + def __str__(self): + return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % ( + self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) + + def __repr__(self): + return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % ( + self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) + + class Flags: + + def __init__(self, b): + self.bits = b + + def __str__(self): + s = '' + if self.bits & MH_NOUNDEFS: + s += 'MH_NOUNDEFS | ' + if self.bits & MH_INCRLINK: + s += 'MH_INCRLINK | ' + if self.bits & MH_DYLDLINK: + s += 'MH_DYLDLINK | ' + if self.bits & MH_BINDATLOAD: + s += 'MH_BINDATLOAD | ' + if self.bits & MH_PREBOUND: + s += 'MH_PREBOUND | ' + if self.bits & MH_SPLIT_SEGS: + s += 'MH_SPLIT_SEGS | ' + if self.bits & MH_LAZY_INIT: + s += 'MH_LAZY_INIT | ' + if self.bits & MH_TWOLEVEL: + s += 'MH_TWOLEVEL | ' + if self.bits & MH_FORCE_FLAT: + s += 'MH_FORCE_FLAT | ' + if self.bits & MH_NOMULTIDEFS: + s += 'MH_NOMULTIDEFS | ' + if self.bits & MH_NOFIXPREBINDING: + s += 'MH_NOFIXPREBINDING | ' + if self.bits & MH_PREBINDABLE: + s += 'MH_PREBINDABLE | ' + if self.bits & MH_ALLMODSBOUND: + s += 'MH_ALLMODSBOUND | ' + if self.bits & MH_SUBSECTIONS_VIA_SYMBOLS: + s += 'MH_SUBSECTIONS_VIA_SYMBOLS | ' + if self.bits & MH_CANONICAL: + s += 'MH_CANONICAL | ' + if self.bits & MH_WEAK_DEFINES: + s += 'MH_WEAK_DEFINES | ' + if self.bits & MH_BINDS_TO_WEAK: + s += 'MH_BINDS_TO_WEAK | ' + if self.bits & MH_ALLOW_STACK_EXECUTION: + s += 'MH_ALLOW_STACK_EXECUTION | ' + if self.bits & MH_ROOT_SAFE: + s += 'MH_ROOT_SAFE | ' + if self.bits & MH_SETUID_SAFE: + s += 'MH_SETUID_SAFE | ' + if self.bits & MH_NO_REEXPORTED_DYLIBS: + s += 'MH_NO_REEXPORTED_DYLIBS | ' + if self.bits & MH_PIE: + s += 'MH_PIE | ' + if self.bits & MH_DEAD_STRIPPABLE_DYLIB: + s += 'MH_DEAD_STRIPPABLE_DYLIB | ' + if self.bits & MH_HAS_TLV_DESCRIPTORS: + s += 'MH_HAS_TLV_DESCRIPTORS | ' + if self.bits & MH_NO_HEAP_EXECUTION: + s += 'MH_NO_HEAP_EXECUTION | ' + # Strip the trailing " |" if we have any flags + if len(s) > 0: + s = s[0:-2] + return s + + class FileType(dict_utils.Enum): + + enum = { + 'MH_OBJECT': MH_OBJECT, + 'MH_EXECUTE': MH_EXECUTE, + 'MH_FVMLIB': MH_FVMLIB, + 'MH_CORE': MH_CORE, + 'MH_PRELOAD': MH_PRELOAD, + 'MH_DYLIB': MH_DYLIB, + 'MH_DYLINKER': MH_DYLINKER, + 'MH_BUNDLE': MH_BUNDLE, + 'MH_DYLIB_STUB': MH_DYLIB_STUB, + 'MH_DSYM': MH_DSYM, + 'MH_KEXT_BUNDLE': MH_KEXT_BUNDLE + } + + def __init__(self, initial_value=0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + class Skinny: + + def __init__(self, path): + self.path = path + self.type = 'skinny' + self.data = None + self.file_off = 0 + self.magic = 0 + self.arch = Mach.Arch(0, 0) + self.filetype = Mach.FileType(0) + self.ncmds = 0 + self.sizeofcmds = 0 + self.flags = Mach.Flags(0) + self.uuid = None + self.commands = list() + self.segments = list() + self.sections = list() + self.symbols = list() + self.sections.append(Mach.Section()) + + def description(self): + return '%#8.8x: %s (%s)' % (self.file_off, self.path, self.arch) + + def unpack(self, data, magic=None): + self.data = data + self.file_off = data.tell() + if magic is None: + self.magic = Mach.Magic() + self.magic.unpack(data) + else: + self.magic = magic + self.file_off = self.file_off - 4 + data.set_byte_order(self.magic.get_byte_order()) + self.arch.cpu, self.arch.sub, self.filetype.value, self.ncmds, self.sizeofcmds, bits = data.get_n_uint32( + 6) + self.flags.bits = bits + + if self.is_64_bit(): + data.get_uint32() # Skip reserved word in mach_header_64 + + for i in range(0, self.ncmds): + lc = self.unpack_load_command(data) + self.commands.append(lc) + + def get_data(self): + if self.data: + self.data.set_byte_order(self.magic.get_byte_order()) + return self.data + return None + + def unpack_load_command(self, data): + lc = Mach.LoadCommand() + lc.unpack(self, data) + lc_command = lc.command.get_enum_value() + if (lc_command == LC_SEGMENT or + lc_command == LC_SEGMENT_64): + lc = Mach.SegmentLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_LOAD_DYLIB or + lc_command == LC_ID_DYLIB or + lc_command == LC_LOAD_WEAK_DYLIB or + lc_command == LC_REEXPORT_DYLIB): + lc = Mach.DylibLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_LOAD_DYLINKER or + lc_command == LC_SUB_FRAMEWORK or + lc_command == LC_SUB_CLIENT or + lc_command == LC_SUB_UMBRELLA or + lc_command == LC_SUB_LIBRARY or + lc_command == LC_ID_DYLINKER or + lc_command == LC_RPATH): + lc = Mach.LoadDYLDLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_DYLD_INFO_ONLY): + lc = Mach.DYLDInfoOnlyLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_SYMTAB): + lc = Mach.SymtabLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_DYSYMTAB): + lc = Mach.DYLDSymtabLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_UUID): + lc = Mach.UUIDLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_CODE_SIGNATURE or + lc_command == LC_SEGMENT_SPLIT_INFO or + lc_command == LC_FUNCTION_STARTS): + lc = Mach.DataBlobLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_UNIXTHREAD): + lc = Mach.UnixThreadLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_ENCRYPTION_INFO): + lc = Mach.EncryptionInfoLoadCommand(lc) + lc.unpack(self, data) + lc.skip(data) + return lc + + def compare(self, rhs): + print("\nComparing:") + print("a) %s %s" % (self.arch, self.path)) + print("b) %s %s" % (rhs.arch, rhs.path)) + result = True + if self.type == rhs.type: + for lhs_section in self.sections[1:]: + rhs_section = rhs.get_section_by_section(lhs_section) + if rhs_section: + print('comparing %s.%s...' % (lhs_section.segname, lhs_section.sectname), end=' ') + sys.stdout.flush() + lhs_data = lhs_section.get_contents(self) + rhs_data = rhs_section.get_contents(rhs) + if lhs_data and rhs_data: + if lhs_data == rhs_data: + print('ok') + else: + lhs_data_len = len(lhs_data) + rhs_data_len = len(rhs_data) + # if lhs_data_len < rhs_data_len: + # if lhs_data == rhs_data[0:lhs_data_len]: + # print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len) + # else: + # # TODO: check padding + # result = False + # elif lhs_data_len > rhs_data_len: + # if lhs_data[0:rhs_data_len] == rhs_data: + # print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len) + # else: + # # TODO: check padding + # result = False + # else: + result = False + print('error: sections differ') + # print 'a) %s' % (lhs_section) + # dump_hex_byte_string_diff(0, lhs_data, rhs_data) + # print 'b) %s' % (rhs_section) + # dump_hex_byte_string_diff(0, rhs_data, lhs_data) + elif lhs_data and not rhs_data: + print('error: section data missing from b:') + print('a) %s' % (lhs_section)) + print('b) %s' % (rhs_section)) + result = False + elif not lhs_data and rhs_data: + print('error: section data missing from a:') + print('a) %s' % (lhs_section)) + print('b) %s' % (rhs_section)) + result = False + elif lhs_section.offset or rhs_section.offset: + print('error: section data missing for both a and b:') + print('a) %s' % (lhs_section)) + print('b) %s' % (rhs_section)) + result = False + else: + print('ok') + else: + result = False + print('error: section %s is missing in %s' % (lhs_section.sectname, rhs.path)) + else: + print('error: comaparing a %s mach-o file with a %s mach-o file is not supported' % (self.type, rhs.type)) + result = False + if not result: + print('error: mach files differ') + return result + + def dump_header(self, dump_description=True, options=None): + if options.verbose: + print("MAGIC CPU SUBTYPE FILETYPE NUM CMDS SIZE CMDS FLAGS") + print("---------- ---------- ---------- ---------- -------- ---------- ----------") + else: + print("MAGIC ARCH FILETYPE NUM CMDS SIZE CMDS FLAGS") + print("------------ ---------- -------------- -------- ---------- ----------") + + def dump_flat(self, options): + if options.verbose: + print("%#8.8x %#8.8x %#8.8x %#8.8x %#8u %#8.8x %#8.8x" % (self.magic, self.arch.cpu, self.arch.sub, self.filetype.value, self.ncmds, self.sizeofcmds, self.flags.bits)) + else: + print("%-12s %-10s %-14s %#8u %#8.8x %s" % (self.magic, self.arch, self.filetype, self.ncmds, self.sizeofcmds, self.flags)) + + def dump(self, options): + if options.dump_header: + self.dump_header(True, options) + if options.dump_load_commands: + self.dump_load_commands(False, options) + if options.dump_sections: + self.dump_sections(False, options) + if options.section_names: + self.dump_section_contents(options) + if options.dump_symtab: + self.get_symtab() + if len(self.symbols): + self.dump_sections(False, options) + else: + print("No symbols") + if options.find_mangled: + self.dump_symbol_names_matching_regex(re.compile('^_?_Z')) + + def dump_header(self, dump_description=True, options=None): + if dump_description: + print(self.description()) + print("Mach Header") + print(" magic: %#8.8x %s" % (self.magic.value, self.magic)) + print(" cputype: %#8.8x %s" % (self.arch.cpu, self.arch)) + print(" cpusubtype: %#8.8x" % self.arch.sub) + print(" filetype: %#8.8x %s" % (self.filetype.get_enum_value(), self.filetype.get_enum_name())) + print(" ncmds: %#8.8x %u" % (self.ncmds, self.ncmds)) + print(" sizeofcmds: %#8.8x" % self.sizeofcmds) + print(" flags: %#8.8x %s" % (self.flags.bits, self.flags)) + + def dump_load_commands(self, dump_description=True, options=None): + if dump_description: + print(self.description()) + for lc in self.commands: + print(lc) + + def get_section_by_name(self, name): + for section in self.sections: + if section.sectname and section.sectname == name: + return section + return None + + def get_section_by_section(self, other_section): + for section in self.sections: + if section.sectname == other_section.sectname and section.segname == other_section.segname: + return section + return None + + def dump_sections(self, dump_description=True, options=None): + if dump_description: + print(self.description()) + num_sections = len(self.sections) + if num_sections > 1: + self.sections[1].dump_header() + for sect_idx in range(1, num_sections): + print("%s" % self.sections[sect_idx]) + + def dump_section_contents(self, options): + saved_section_to_disk = False + for sectname in options.section_names: + section = self.get_section_by_name(sectname) + if section: + sect_bytes = section.get_contents(self) + if options.outfile: + if not saved_section_to_disk: + outfile = open(options.outfile, 'w') + if options.extract_modules: + # print "Extracting modules from mach file..." + data = file_extract.FileExtract( + io.BytesIO(sect_bytes), self.data.byte_order) + version = data.get_uint32() + num_modules = data.get_uint32() + # print "version = %u, num_modules = %u" % + # (version, num_modules) + for i in range(num_modules): + data_offset = data.get_uint64() + data_size = data.get_uint64() + name_offset = data.get_uint32() + language = data.get_uint32() + flags = data.get_uint32() + data.seek(name_offset) + module_name = data.get_c_string() + # print "module[%u] data_offset = %#16.16x, + # data_size = %#16.16x, name_offset = + # %#16.16x (%s), language = %u, flags = + # %#x" % (i, data_offset, data_size, + # name_offset, module_name, language, + # flags) + data.seek(data_offset) + outfile.write(data.read_size(data_size)) + else: + print("Saving section %s to '%s'" % (sectname, options.outfile)) + outfile.write(sect_bytes) + outfile.close() + saved_section_to_disk = True + else: + print("error: you can only save a single section to disk at a time, skipping section '%s'" % (sectname)) + else: + print('section %s:\n' % (sectname)) + section.dump_header() + print('%s\n' % (section)) + dump_memory(0, sect_bytes, options.max_count, 16) + else: + print('error: no section named "%s" was found' % (sectname)) + + def get_segment(self, segname): + if len(self.segments) == 1 and self.segments[0].segname == '': + return self.segments[0] + for segment in self.segments: + if segment.segname == segname: + return segment + return None + + def get_first_load_command(self, lc_enum_value): + for lc in self.commands: + if lc.command.value == lc_enum_value: + return lc + return None + + def get_symtab(self): + if self.data and not self.symbols: + lc_symtab = self.get_first_load_command(LC_SYMTAB) + if lc_symtab: + symtab_offset = self.file_off + if self.data.is_in_memory(): + linkedit_segment = self.get_segment('__LINKEDIT') + if linkedit_segment: + linkedit_vmaddr = linkedit_segment.vmaddr + linkedit_fileoff = linkedit_segment.fileoff + symtab_offset = linkedit_vmaddr + lc_symtab.symoff - linkedit_fileoff + symtab_offset = linkedit_vmaddr + lc_symtab.stroff - linkedit_fileoff + else: + symtab_offset += lc_symtab.symoff + + self.data.seek(symtab_offset) + is_64 = self.is_64_bit() + for i in range(lc_symtab.nsyms): + nlist = Mach.NList() + nlist.unpack(self, self.data, lc_symtab) + self.symbols.append(nlist) + else: + print("no LC_SYMTAB") + + def dump_symtab(self, dump_description=True, options=None): + self.get_symtab() + if dump_description: + print(self.description()) + for i, symbol in enumerate(self.symbols): + print('[%5u] %s' % (i, symbol)) + + def dump_symbol_names_matching_regex(self, regex, file=None): + self.get_symtab() + for symbol in self.symbols: + if symbol.name and regex.search(symbol.name): + print(symbol.name) + if file: + file.write('%s\n' % (symbol.name)) + + def is_64_bit(self): + return self.magic.is_64_bit() + + class LoadCommand: + + class Command(dict_utils.Enum): + enum = { + 'LC_SEGMENT': LC_SEGMENT, + 'LC_SYMTAB': LC_SYMTAB, + 'LC_SYMSEG': LC_SYMSEG, + 'LC_THREAD': LC_THREAD, + 'LC_UNIXTHREAD': LC_UNIXTHREAD, + 'LC_LOADFVMLIB': LC_LOADFVMLIB, + 'LC_IDFVMLIB': LC_IDFVMLIB, + 'LC_IDENT': LC_IDENT, + 'LC_FVMFILE': LC_FVMFILE, + 'LC_PREPAGE': LC_PREPAGE, + 'LC_DYSYMTAB': LC_DYSYMTAB, + 'LC_LOAD_DYLIB': LC_LOAD_DYLIB, + 'LC_ID_DYLIB': LC_ID_DYLIB, + 'LC_LOAD_DYLINKER': LC_LOAD_DYLINKER, + 'LC_ID_DYLINKER': LC_ID_DYLINKER, + 'LC_PREBOUND_DYLIB': LC_PREBOUND_DYLIB, + 'LC_ROUTINES': LC_ROUTINES, + 'LC_SUB_FRAMEWORK': LC_SUB_FRAMEWORK, + 'LC_SUB_UMBRELLA': LC_SUB_UMBRELLA, + 'LC_SUB_CLIENT': LC_SUB_CLIENT, + 'LC_SUB_LIBRARY': LC_SUB_LIBRARY, + 'LC_TWOLEVEL_HINTS': LC_TWOLEVEL_HINTS, + 'LC_PREBIND_CKSUM': LC_PREBIND_CKSUM, + 'LC_LOAD_WEAK_DYLIB': LC_LOAD_WEAK_DYLIB, + 'LC_SEGMENT_64': LC_SEGMENT_64, + 'LC_ROUTINES_64': LC_ROUTINES_64, + 'LC_UUID': LC_UUID, + 'LC_RPATH': LC_RPATH, + 'LC_CODE_SIGNATURE': LC_CODE_SIGNATURE, + 'LC_SEGMENT_SPLIT_INFO': LC_SEGMENT_SPLIT_INFO, + 'LC_REEXPORT_DYLIB': LC_REEXPORT_DYLIB, + 'LC_LAZY_LOAD_DYLIB': LC_LAZY_LOAD_DYLIB, + 'LC_ENCRYPTION_INFO': LC_ENCRYPTION_INFO, + 'LC_DYLD_INFO': LC_DYLD_INFO, + 'LC_DYLD_INFO_ONLY': LC_DYLD_INFO_ONLY, + 'LC_LOAD_UPWARD_DYLIB': LC_LOAD_UPWARD_DYLIB, + 'LC_VERSION_MIN_MACOSX': LC_VERSION_MIN_MACOSX, + 'LC_VERSION_MIN_IPHONEOS': LC_VERSION_MIN_IPHONEOS, + 'LC_FUNCTION_STARTS': LC_FUNCTION_STARTS, + 'LC_DYLD_ENVIRONMENT': LC_DYLD_ENVIRONMENT + } + + def __init__(self, initial_value=0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + def __init__(self, c=None, l=0, o=0): + if c is not None: + self.command = c + else: + self.command = Mach.LoadCommand.Command(0) + self.length = l + self.file_off = o + + def unpack(self, mach_file, data): + self.file_off = data.tell() + self.command.value, self.length = data.get_n_uint32(2) + + def skip(self, data): + data.seek(self.file_off + self.length, 0) + + def __str__(self): + lc_name = self.command.get_enum_name() + return '%#8.8x: <%#4.4x> %-24s' % (self.file_off, + self.length, lc_name) + + class Section: + + def __init__(self): + self.index = 0 + self.is_64 = False + self.sectname = None + self.segname = None + self.addr = 0 + self.size = 0 + self.offset = 0 + self.align = 0 + self.reloff = 0 + self.nreloc = 0 + self.flags = 0 + self.reserved1 = 0 + self.reserved2 = 0 + self.reserved3 = 0 + + def unpack(self, is_64, data): + self.is_64 = is_64 + self.sectname = data.get_fixed_length_c_string(16, '', True) + self.segname = data.get_fixed_length_c_string(16, '', True) + if self.is_64: + self.addr, self.size = data.get_n_uint64(2) + self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.reserved3 = data.get_n_uint32( + 8) + else: + self.addr, self.size = data.get_n_uint32(2) + self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2 = data.get_n_uint32( + 7) + + def dump_header(self): + if self.is_64: + print("INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 RESERVED3 NAME") + print("===== ------------------ ------------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------") + else: + print("INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 NAME") + print("===== ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------") + + def __str__(self): + if self.is_64: + return "[%3u] %#16.16x %#16.16x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s" % ( + self.index, self.addr, self.size, self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.reserved3, self.segname, self.sectname) + else: + return "[%3u] %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s" % ( + self.index, self.addr, self.size, self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.segname, self.sectname) + + def get_contents(self, mach_file): + '''Get the section contents as a python string''' + if self.size > 0 and mach_file.get_segment( + self.segname).filesize > 0: + data = mach_file.get_data() + if data: + section_data_offset = mach_file.file_off + self.offset + # print '%s.%s is at offset 0x%x with size 0x%x' % + # (self.segname, self.sectname, section_data_offset, + # self.size) + data.push_offset_and_seek(section_data_offset) + bytes = data.read_size(self.size) + data.pop_offset_and_seek() + return bytes + return None + + class DylibLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.name = None + self.timestamp = 0 + self.current_version = 0 + self.compatibility_version = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + name_offset, self.timestamp, self.current_version, self.compatibility_version = data.get_n_uint32( + 4) + data.seek(self.file_off + name_offset, 0) + self.name = data.get_fixed_length_c_string(self.length - 24) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + s += "%#8.8x %#8.8x %#8.8x " % (self.timestamp, + self.current_version, + self.compatibility_version) + s += self.name + return s + + class LoadDYLDLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.name = None + + def unpack(self, mach_file, data): + data.get_uint32() + self.name = data.get_fixed_length_c_string(self.length - 12) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + s += "%s" % self.name + return s + + class UnixThreadLoadCommand(LoadCommand): + + class ThreadState: + + def __init__(self): + self.flavor = 0 + self.count = 0 + self.register_values = list() + + def unpack(self, data): + self.flavor, self.count = data.get_n_uint32(2) + self.register_values = data.get_n_uint32(self.count) + + def __str__(self): + s = "flavor = %u, count = %u, regs =" % ( + self.flavor, self.count) + i = 0 + for register_value in self.register_values: + if i % 8 == 0: + s += "\n " + s += " %#8.8x" % register_value + i += 1 + return s + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.reg_sets = list() + + def unpack(self, mach_file, data): + reg_set = Mach.UnixThreadLoadCommand.ThreadState() + reg_set.unpack(data) + self.reg_sets.append(reg_set) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + for reg_set in self.reg_sets: + s += "%s" % reg_set + return s + + class DYLDInfoOnlyLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.rebase_off = 0 + self.rebase_size = 0 + self.bind_off = 0 + self.bind_size = 0 + self.weak_bind_off = 0 + self.weak_bind_size = 0 + self.lazy_bind_off = 0 + self.lazy_bind_size = 0 + self.export_off = 0 + self.export_size = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.rebase_off, self.rebase_size, self.bind_off, self.bind_size, self.weak_bind_off, self.weak_bind_size, self.lazy_bind_off, self.lazy_bind_size, self.export_off, self.export_size = data.get_n_uint32( + 10) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + s += "rebase_off = %#8.8x, rebase_size = %u, " % ( + self.rebase_off, self.rebase_size) + s += "bind_off = %#8.8x, bind_size = %u, " % ( + self.bind_off, self.bind_size) + s += "weak_bind_off = %#8.8x, weak_bind_size = %u, " % ( + self.weak_bind_off, self.weak_bind_size) + s += "lazy_bind_off = %#8.8x, lazy_bind_size = %u, " % ( + self.lazy_bind_off, self.lazy_bind_size) + s += "export_off = %#8.8x, export_size = %u, " % ( + self.export_off, self.export_size) + return s + + class DYLDSymtabLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.ilocalsym = 0 + self.nlocalsym = 0 + self.iextdefsym = 0 + self.nextdefsym = 0 + self.iundefsym = 0 + self.nundefsym = 0 + self.tocoff = 0 + self.ntoc = 0 + self.modtaboff = 0 + self.nmodtab = 0 + self.extrefsymoff = 0 + self.nextrefsyms = 0 + self.indirectsymoff = 0 + self.nindirectsyms = 0 + self.extreloff = 0 + self.nextrel = 0 + self.locreloff = 0 + self.nlocrel = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.ilocalsym, self.nlocalsym, self.iextdefsym, self.nextdefsym, self.iundefsym, self.nundefsym, self.tocoff, self.ntoc, self.modtaboff, self.nmodtab, self.extrefsymoff, self.nextrefsyms, self.indirectsymoff, self.nindirectsyms, self.extreloff, self.nextrel, self.locreloff, self.nlocrel = data.get_n_uint32( + 18) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + # s += "ilocalsym = %u, nlocalsym = %u, " % (self.ilocalsym, self.nlocalsym) + # s += "iextdefsym = %u, nextdefsym = %u, " % (self.iextdefsym, self.nextdefsym) + # s += "iundefsym %u, nundefsym = %u, " % (self.iundefsym, self.nundefsym) + # s += "tocoff = %#8.8x, ntoc = %u, " % (self.tocoff, self.ntoc) + # s += "modtaboff = %#8.8x, nmodtab = %u, " % (self.modtaboff, self.nmodtab) + # s += "extrefsymoff = %#8.8x, nextrefsyms = %u, " % (self.extrefsymoff, self.nextrefsyms) + # s += "indirectsymoff = %#8.8x, nindirectsyms = %u, " % (self.indirectsymoff, self.nindirectsyms) + # s += "extreloff = %#8.8x, nextrel = %u, " % (self.extreloff, self.nextrel) + # s += "locreloff = %#8.8x, nlocrel = %u" % (self.locreloff, + # self.nlocrel) + s += "ilocalsym = %-10u, nlocalsym = %u\n" % ( + self.ilocalsym, self.nlocalsym) + s += " iextdefsym = %-10u, nextdefsym = %u\n" % ( + self.iextdefsym, self.nextdefsym) + s += " iundefsym = %-10u, nundefsym = %u\n" % ( + self.iundefsym, self.nundefsym) + s += " tocoff = %#8.8x, ntoc = %u\n" % ( + self.tocoff, self.ntoc) + s += " modtaboff = %#8.8x, nmodtab = %u\n" % ( + self.modtaboff, self.nmodtab) + s += " extrefsymoff = %#8.8x, nextrefsyms = %u\n" % ( + self.extrefsymoff, self.nextrefsyms) + s += " indirectsymoff = %#8.8x, nindirectsyms = %u\n" % ( + self.indirectsymoff, self.nindirectsyms) + s += " extreloff = %#8.8x, nextrel = %u\n" % ( + self.extreloff, self.nextrel) + s += " locreloff = %#8.8x, nlocrel = %u" % ( + self.locreloff, self.nlocrel) + return s + + class SymtabLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.symoff = 0 + self.nsyms = 0 + self.stroff = 0 + self.strsize = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.symoff, self.nsyms, self.stroff, self.strsize = data.get_n_uint32( + 4) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + s += "symoff = %#8.8x, nsyms = %u, stroff = %#8.8x, strsize = %u" % ( + self.symoff, self.nsyms, self.stroff, self.strsize) + return s + + class UUIDLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.uuid = None + + def unpack(self, mach_file, data): + uuid_data = data.get_n_uint8(16) + uuid_str = '' + for byte in uuid_data: + uuid_str += '%2.2x' % byte + self.uuid = uuid.UUID(uuid_str) + mach_file.uuid = self.uuid + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + s += self.uuid.__str__() + return s + + class DataBlobLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.dataoff = 0 + self.datasize = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.dataoff, self.datasize = data.get_n_uint32(2) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + s += "dataoff = %#8.8x, datasize = %u" % ( + self.dataoff, self.datasize) + return s + + class EncryptionInfoLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.cryptoff = 0 + self.cryptsize = 0 + self.cryptid = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.cryptoff, self.cryptsize, self.cryptid = data.get_n_uint32(3) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + s += "file-range = [%#8.8x - %#8.8x), cryptsize = %u, cryptid = %u" % ( + self.cryptoff, self.cryptoff + self.cryptsize, self.cryptsize, self.cryptid) + return s + + class SegmentLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.segname = None + self.vmaddr = 0 + self.vmsize = 0 + self.fileoff = 0 + self.filesize = 0 + self.maxprot = 0 + self.initprot = 0 + self.nsects = 0 + self.flags = 0 + + def unpack(self, mach_file, data): + is_64 = self.command.get_enum_value() == LC_SEGMENT_64 + self.segname = data.get_fixed_length_c_string(16, '', True) + if is_64: + self.vmaddr, self.vmsize, self.fileoff, self.filesize = data.get_n_uint64( + 4) + else: + self.vmaddr, self.vmsize, self.fileoff, self.filesize = data.get_n_uint32( + 4) + self.maxprot, self.initprot, self.nsects, self.flags = data.get_n_uint32( + 4) + mach_file.segments.append(self) + for i in range(self.nsects): + section = Mach.Section() + section.unpack(is_64, data) + section.index = len(mach_file.sections) + mach_file.sections.append(section) + + def __str__(self): + s = Mach.LoadCommand.__str__(self) + if self.command.get_enum_value() == LC_SEGMENT: + s += "%#8.8x %#8.8x %#8.8x %#8.8x " % ( + self.vmaddr, self.vmsize, self.fileoff, self.filesize) + else: + s += "%#16.16x %#16.16x %#16.16x %#16.16x " % ( + self.vmaddr, self.vmsize, self.fileoff, self.filesize) + s += "%s %s %3u %#8.8x" % (vm_prot_names[self.maxprot], vm_prot_names[ + self.initprot], self.nsects, self.flags) + s += ' ' + self.segname + return s + + class NList: + + class Type: + + class Stab(dict_utils.Enum): + enum = { + 'N_GSYM': N_GSYM, + 'N_FNAME': N_FNAME, + 'N_FUN': N_FUN, + 'N_STSYM': N_STSYM, + 'N_LCSYM': N_LCSYM, + 'N_BNSYM': N_BNSYM, + 'N_OPT': N_OPT, + 'N_RSYM': N_RSYM, + 'N_SLINE': N_SLINE, + 'N_ENSYM': N_ENSYM, + 'N_SSYM': N_SSYM, + 'N_SO': N_SO, + 'N_OSO': N_OSO, + 'N_LSYM': N_LSYM, + 'N_BINCL': N_BINCL, + 'N_SOL': N_SOL, + 'N_PARAMS': N_PARAMS, + 'N_VERSION': N_VERSION, + 'N_OLEVEL': N_OLEVEL, + 'N_PSYM': N_PSYM, + 'N_EINCL': N_EINCL, + 'N_ENTRY': N_ENTRY, + 'N_LBRAC': N_LBRAC, + 'N_EXCL': N_EXCL, + 'N_RBRAC': N_RBRAC, + 'N_BCOMM': N_BCOMM, + 'N_ECOMM': N_ECOMM, + 'N_ECOML': N_ECOML, + 'N_LENG': N_LENG + } + + def __init__(self, magic=0): + dict_utils.Enum.__init__(self, magic, self.enum) + + def __init__(self, t=0): + self.value = t + + def __str__(self): + n_type = self.value + if n_type & N_STAB: + stab = Mach.NList.Type.Stab(self.value) + return '%s' % stab + else: + type = self.value & N_TYPE + type_str = '' + if type == N_UNDF: + type_str = 'N_UNDF' + elif type == N_ABS: + type_str = 'N_ABS ' + elif type == N_SECT: + type_str = 'N_SECT' + elif type == N_PBUD: + type_str = 'N_PBUD' + elif type == N_INDR: + type_str = 'N_INDR' + else: + type_str = "??? (%#2.2x)" % type + if n_type & N_PEXT: + type_str += ' | PEXT' + if n_type & N_EXT: + type_str += ' | EXT ' + return type_str + + def __init__(self): + self.index = 0 + self.name_offset = 0 + self.name = 0 + self.type = Mach.NList.Type() + self.sect_idx = 0 + self.desc = 0 + self.value = 0 + + def unpack(self, mach_file, data, symtab_lc): + self.index = len(mach_file.symbols) + self.name_offset = data.get_uint32() + self.type.value, self.sect_idx = data.get_n_uint8(2) + self.desc = data.get_uint16() + if mach_file.is_64_bit(): + self.value = data.get_uint64() + else: + self.value = data.get_uint32() + data.push_offset_and_seek( + mach_file.file_off + + symtab_lc.stroff + + self.name_offset) + # print "get string for symbol[%u]" % self.index + self.name = data.get_c_string() + data.pop_offset_and_seek() + + def __str__(self): + name_display = '' + if len(self.name): + name_display = ' "%s"' % self.name + return '%#8.8x %#2.2x (%-20s) %#2.2x %#4.4x %16.16x%s' % (self.name_offset, + self.type.value, self.type, self.sect_idx, self.desc, self.value, name_display) + + class Interactive(cmd.Cmd): + '''Interactive command interpreter to mach-o files.''' + + def __init__(self, mach, options): + cmd.Cmd.__init__(self) + self.intro = 'Interactive mach-o command interpreter' + self.prompt = 'mach-o: %s %% ' % mach.path + self.mach = mach + self.options = options + + def default(self, line): + '''Catch all for unknown command, which will exit the interpreter.''' + print("uknown 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_header(self, line): + '''Dump mach-o file headers''' + self.mach.dump_header(True, self.options) + return False + + def do_load(self, line): + '''Dump all mach-o load commands''' + self.mach.dump_load_commands(True, self.options) + return False + + def do_sections(self, line): + '''Dump all mach-o sections''' + self.mach.dump_sections(True, self.options) + return False + + def do_symtab(self, line): + '''Dump all mach-o symbols in the symbol table''' + self.mach.dump_symtab(True, self.options) + return False + +if __name__ == '__main__': + parser = optparse.OptionParser( + description='A script that parses skinny and universal mach-o files.') + parser.add_option( + '--arch', + '-a', + type='string', + metavar='arch', + dest='archs', + action='append', + help='specify one or more architectures by name') + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + parser.add_option( + '-H', + '--header', + action='store_true', + dest='dump_header', + help='dump the mach-o file header', + default=False) + parser.add_option( + '-l', + '--load-commands', + action='store_true', + dest='dump_load_commands', + help='dump the mach-o load commands', + default=False) + parser.add_option( + '-s', + '--symtab', + action='store_true', + dest='dump_symtab', + help='dump the mach-o symbol table', + default=False) + parser.add_option( + '-S', + '--sections', + action='store_true', + dest='dump_sections', + help='dump the mach-o sections', + default=False) + parser.add_option( + '--section', + type='string', + metavar='sectname', + dest='section_names', + action='append', + help='Specify one or more section names to dump', + default=[]) + parser.add_option( + '-o', + '--out', + type='string', + dest='outfile', + help='Used in conjunction with the --section=NAME option to save a single section\'s data to disk.', + default=False) + parser.add_option( + '-i', + '--interactive', + action='store_true', + dest='interactive', + help='enable interactive mode', + default=False) + parser.add_option( + '-m', + '--mangled', + action='store_true', + dest='find_mangled', + help='dump all mangled names in a mach file', + default=False) + parser.add_option( + '-c', + '--compare', + action='store_true', + dest='compare', + help='compare two mach files', + default=False) + parser.add_option( + '-M', + '--extract-modules', + action='store_true', + dest='extract_modules', + help='Extract modules from file', + default=False) + parser.add_option( + '-C', + '--count', + type='int', + dest='max_count', + help='Sets the max byte count when dumping section data', + default=-1) + + (options, mach_files) = parser.parse_args() + if options.extract_modules: + if options.section_names: + print("error: can't use --section option with the --extract-modules option") + exit(1) + if not options.outfile: + print("error: the --output=FILE option must be specified with the --extract-modules option") + exit(1) + options.section_names.append("__apple_ast") + if options.compare: + if len(mach_files) == 2: + mach_a = Mach() + mach_b = Mach() + mach_a.parse(mach_files[0]) + mach_b.parse(mach_files[1]) + mach_a.compare(mach_b) + else: + print('error: --compare takes two mach files as arguments') + else: + if not (options.dump_header or options.dump_load_commands or options.dump_symtab or options.dump_sections or options.find_mangled or options.section_names): + options.dump_header = True + options.dump_load_commands = True + if options.verbose: + print('options', options) + print('mach_files', mach_files) + for path in mach_files: + mach = Mach() + mach.parse(path) + if options.interactive: + interpreter = Mach.Interactive(mach, options) + interpreter.cmdloop() + else: + mach.dump(options) diff --git a/gnu/llvm/lldb/examples/python/memory.py b/gnu/llvm/lldb/examples/python/memory.py new file mode 100755 index 00000000000..9f8f7e384c4 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/memory.py @@ -0,0 +1,277 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +#---------------------------------------------------------------------- + +from __future__ import print_function + +import platform +import os +import re +import sys +import subprocess + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True) + if xcode_dir: + lldb_python_dirs.append( + os.path.realpath( + xcode_dir + + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append( + xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append( + '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print('imported lldb from: "%s"' % (lldb_python_dir)) + success = True + break + if not success: + print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") + sys.exit(1) + +import optparse +import shlex +import string +import struct +import time + + +def append_data_callback(option, opt_str, value, parser): + if opt_str == "--uint8": + int8 = int(value, 0) + parser.values.data += struct.pack('1B', int8) + if opt_str == "--uint16": + int16 = int(value, 0) + parser.values.data += struct.pack('1H', int16) + if opt_str == "--uint32": + int32 = int(value, 0) + parser.values.data += struct.pack('1I', int32) + if opt_str == "--uint64": + int64 = int(value, 0) + parser.values.data += struct.pack('1Q', int64) + if opt_str == "--int8": + int8 = int(value, 0) + parser.values.data += struct.pack('1b', int8) + if opt_str == "--int16": + int16 = int(value, 0) + parser.values.data += struct.pack('1h', int16) + if opt_str == "--int32": + int32 = int(value, 0) + parser.values.data += struct.pack('1i', int32) + if opt_str == "--int64": + int64 = int(value, 0) + parser.values.data += struct.pack('1q', int64) + + +def create_memfind_options(): + usage = "usage: %prog [options] STARTADDR [ENDADDR]" + description = '''This command can find data in a specified address range. +Options are used to specify the data that is to be looked for and the options +can be specified multiple times to look for longer streams of data. +''' + parser = optparse.OptionParser( + description=description, + prog='memfind', + usage=usage) + parser.add_option( + '-s', + '--size', + type='int', + metavar='BYTESIZE', + dest='size', + help='Specify the byte size to search.', + default=0) + parser.add_option( + '--int8', + action="callback", + callback=append_data_callback, + type='string', + metavar='INT', + dest='data', + help='Specify a 8 bit signed integer value to search for in memory.', + default='') + parser.add_option( + '--int16', + action="callback", + callback=append_data_callback, + type='string', + metavar='INT', + dest='data', + help='Specify a 16 bit signed integer value to search for in memory.', + default='') + parser.add_option( + '--int32', + action="callback", + callback=append_data_callback, + type='string', + metavar='INT', + dest='data', + help='Specify a 32 bit signed integer value to search for in memory.', + default='') + parser.add_option( + '--int64', + action="callback", + callback=append_data_callback, + type='string', + metavar='INT', + dest='data', + help='Specify a 64 bit signed integer value to search for in memory.', + default='') + parser.add_option( + '--uint8', + action="callback", + callback=append_data_callback, + type='string', + metavar='INT', + dest='data', + help='Specify a 8 bit unsigned integer value to search for in memory.', + default='') + parser.add_option( + '--uint16', + action="callback", + callback=append_data_callback, + type='string', + metavar='INT', + dest='data', + help='Specify a 16 bit unsigned integer value to search for in memory.', + default='') + parser.add_option( + '--uint32', + action="callback", + callback=append_data_callback, + type='string', + metavar='INT', + dest='data', + help='Specify a 32 bit unsigned integer value to search for in memory.', + default='') + parser.add_option( + '--uint64', + action="callback", + callback=append_data_callback, + type='string', + metavar='INT', + dest='data', + help='Specify a 64 bit unsigned integer value to search for in memory.', + default='') + return parser + + +def memfind_command(debugger, command, result, dict): + # Use the Shell Lexer to properly parse up command options just like a + # shell would + command_args = shlex.split(command) + parser = create_memfind_options() + (options, args) = parser.parse_args(command_args) + # try: + # (options, args) = parser.parse_args(command_args) + # except: + # # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + # result.SetStatus (lldb.eReturnStatusFailed) + # print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string + # return + memfind(debugger.GetSelectedTarget(), options, args, result) + + +def print_error(str, show_usage, result): + print(str, file=result) + if show_usage: + print(create_memfind_options().format_help(), file=result) + + +def memfind(target, options, args, result): + num_args = len(args) + start_addr = 0 + if num_args == 1: + if options.size > 0: + print_error( + "error: --size must be specified if there is no ENDADDR argument", + True, + result) + return + start_addr = int(args[0], 0) + elif num_args == 2: + if options.size != 0: + print_error( + "error: --size can't be specified with an ENDADDR argument", + True, + result) + return + start_addr = int(args[0], 0) + end_addr = int(args[1], 0) + if start_addr >= end_addr: + print_error( + "error: inavlid memory range [%#x - %#x)" % + (start_addr, end_addr), True, result) + return + options.size = end_addr - start_addr + else: + print_error("error: memfind takes 1 or 2 arguments", True, result) + return + + if not options.data: + print('error: no data specified to search for', file=result) + return + + if not target: + print('error: invalid target', file=result) + return + process = target.process + if not process: + print('error: invalid process', file=result) + return + + error = lldb.SBError() + bytes = process.ReadMemory(start_addr, options.size, error) + if error.Success(): + num_matches = 0 + print("Searching memory range [%#x - %#x) for" % ( + start_addr, end_addr), end=' ', file=result) + for byte in options.data: + print('%2.2x' % ord(byte), end=' ', file=result) + print(file=result) + + match_index = string.find(bytes, options.data) + while match_index != -1: + num_matches = num_matches + 1 + print('%#x: %#x + %u' % (start_addr + + match_index, start_addr, match_index), file=result) + match_index = string.find(bytes, options.data, match_index + 1) + + if num_matches == 0: + print("error: no matches found", file=result) + else: + print('error: %s' % (error.GetCString()), file=result) + + +if __name__ == '__main__': + print('error: this script is designed to be used within the embedded script interpreter in LLDB') +elif getattr(lldb, 'debugger', None): + memfind_command.__doc__ = create_memfind_options().format_help() + lldb.debugger.HandleCommand( + 'command script add -f memory.memfind_command memfind') + print('"memfind" command installed, use the "--help" option for detailed help') diff --git a/gnu/llvm/lldb/examples/python/operating_system.py b/gnu/llvm/lldb/examples/python/operating_system.py new file mode 100644 index 00000000000..bfa13f0568e --- /dev/null +++ b/gnu/llvm/lldb/examples/python/operating_system.py @@ -0,0 +1,231 @@ +#!/usr/bin/python + +import lldb +import struct + + +class OperatingSystemPlugIn(object): + """Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class""" + + def __init__(self, process): + '''Initialization needs a valid.SBProcess object. + + This plug-in will get created after a live process is valid and has stopped for the + first time.''' + self.process = None + self.registers = None + self.threads = None + if isinstance(process, lldb.SBProcess) and process.IsValid(): + self.process = process + self.threads = None # Will be an dictionary containing info for each thread + + def get_target(self): + # NOTE: Don't use "lldb.target" when trying to get your target as the "lldb.target" + # tracks the current target in the LLDB command interpreter which isn't the + # correct thing to use for this plug-in. + return self.process.target + + def create_thread(self, tid, context): + if tid == 0x444444444: + thread_info = { + 'tid': tid, + 'name': 'four', + 'queue': 'queue4', + 'state': 'stopped', + 'stop_reason': 'none'} + self.threads.append(thread_info) + return thread_info + return None + + def get_thread_info(self): + if not self.threads: + # The sample dictionary below shows the values that can be returned for a thread + # tid => thread ID (mandatory) + # name => thread name (optional key/value pair) + # queue => thread dispatch queue name (optional key/value pair) + # state => thred state (mandatory, set to 'stopped' for now) + # stop_reason => thread stop reason. (mandatory, usually set to 'none') + # Possible values include: + # 'breakpoint' if the thread is stopped at a breakpoint + # 'none' thread is just stopped because the process is stopped + # 'trace' the thread just single stepped + # The usual value for this while threads are in memory is 'none' + # register_data_addr => the address of the register data in memory (optional key/value pair) + # Specifying this key/value pair for a thread will avoid a call to get_register_data() + # and can be used when your registers are in a thread context structure that is contiguous + # in memory. Don't specify this if your register layout in memory doesn't match the layout + # described by the dictionary returned from a call to the + # get_register_info() method. + self.threads = [{'tid': 0x111111111, + 'name': 'one', + 'queue': 'queue1', + 'state': 'stopped', + 'stop_reason': 'breakpoint'}, + {'tid': 0x222222222, + 'name': 'two', + 'queue': 'queue2', + 'state': 'stopped', + 'stop_reason': 'none'}, + {'tid': 0x333333333, + 'name': 'three', + 'queue': 'queue3', + 'state': 'stopped', + 'stop_reason': 'trace', + 'register_data_addr': 0x100000000}] + return self.threads + + def get_register_info(self): + if self.registers is None: + self.registers = dict() + triple = self.process.target.triple + if triple: + arch = triple.split('-')[0] + if arch == 'x86_64': + self.registers['sets'] = ['GPR', 'FPU', 'EXC'] + self.registers['registers'] = [ + {'name': 'rax', 'bitsize': 64, 'offset': 0, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 0, 'dwarf': 0}, + {'name': 'rbx', 'bitsize': 64, 'offset': 8, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 3, 'dwarf': 3}, + {'name': 'rcx', 'bitsize': 64, 'offset': 16, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 2, 'dwarf': 2, 'generic': 'arg4', 'alt-name': 'arg4', }, + {'name': 'rdx', 'bitsize': 64, 'offset': 24, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 1, 'dwarf': 1, 'generic': 'arg3', 'alt-name': 'arg3', }, + {'name': 'rdi', 'bitsize': 64, 'offset': 32, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 5, 'dwarf': 5, 'generic': 'arg1', 'alt-name': 'arg1', }, + {'name': 'rsi', 'bitsize': 64, 'offset': 40, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 4, 'dwarf': 4, 'generic': 'arg2', 'alt-name': 'arg2', }, + {'name': 'rbp', 'bitsize': 64, 'offset': 48, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 6, 'dwarf': 6, 'generic': 'fp', 'alt-name': 'fp', }, + {'name': 'rsp', 'bitsize': 64, 'offset': 56, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 7, 'dwarf': 7, 'generic': 'sp', 'alt-name': 'sp', }, + {'name': 'r8', 'bitsize': 64, 'offset': 64, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 8, 'dwarf': 8, 'generic': 'arg5', 'alt-name': 'arg5', }, + {'name': 'r9', 'bitsize': 64, 'offset': 72, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 9, 'dwarf': 9, 'generic': 'arg6', 'alt-name': 'arg6', }, + {'name': 'r10', 'bitsize': 64, 'offset': 80, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 10, 'dwarf': 10}, + {'name': 'r11', 'bitsize': 64, 'offset': 88, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 11, 'dwarf': 11}, + {'name': 'r12', 'bitsize': 64, 'offset': 96, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 12, 'dwarf': 12}, + {'name': 'r13', 'bitsize': 64, 'offset': 104, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 13, 'dwarf': 13}, + {'name': 'r14', 'bitsize': 64, 'offset': 112, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 14, 'dwarf': 14}, + {'name': 'r15', 'bitsize': 64, 'offset': 120, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 15, 'dwarf': 15}, + {'name': 'rip', 'bitsize': 64, 'offset': 128, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 16, 'dwarf': 16, 'generic': 'pc', 'alt-name': 'pc'}, + {'name': 'rflags', 'bitsize': 64, 'offset': 136, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'generic': 'flags', 'alt-name': 'flags'}, + {'name': 'cs', 'bitsize': 64, 'offset': 144, 'encoding': 'uint', 'format': 'hex', 'set': 0}, + {'name': 'fs', 'bitsize': 64, 'offset': 152, 'encoding': 'uint', 'format': 'hex', 'set': 0}, + {'name': 'gs', 'bitsize': 64, 'offset': 160, 'encoding': 'uint', 'format': 'hex', 'set': 0}, + ] + return self.registers + + def get_register_data(self, tid): + if tid == 0x111111111: + return struct.pack( + '21Q', + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21) + elif tid == 0x222222222: + return struct.pack( + '21Q', + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121) + elif tid == 0x333333333: + return struct.pack( + '21Q', + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221) + elif tid == 0x444444444: + return struct.pack( + '21Q', + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321) + else: + return struct.pack( + '21Q', + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421) + return None diff --git a/gnu/llvm/lldb/examples/python/performance.py b/gnu/llvm/lldb/examples/python/performance.py new file mode 100755 index 00000000000..aec6b307f87 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/performance.py @@ -0,0 +1,394 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +from __future__ import print_function + +import optparse +import os +import platform +import re +import resource +import sys +import subprocess +import time +import types + +#---------------------------------------------------------------------- +# Code that auto imports LLDB +#---------------------------------------------------------------------- +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True) + if xcode_dir: + lldb_python_dirs.append( + os.path.realpath( + xcode_dir + + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append( + xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append( + '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print('imported lldb from: "%s"' % (lldb_python_dir)) + success = True + break + if not success: + print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") + sys.exit(1) + + +class Timer: + + def __enter__(self): + self.start = time.clock() + return self + + def __exit__(self, *args): + self.end = time.clock() + self.interval = self.end - self.start + + +class Action(object): + """Class that encapsulates actions to take when a thread stops for a reason.""" + + def __init__(self, callback=None, callback_owner=None): + self.callback = callback + self.callback_owner = callback_owner + + def ThreadStopped(self, thread): + assert False, "performance.Action.ThreadStopped(self, thread) must be overridden in a subclass" + + +class PlanCompleteAction (Action): + + def __init__(self, callback=None, callback_owner=None): + Action.__init__(self, callback, callback_owner) + + def ThreadStopped(self, thread): + if thread.GetStopReason() == lldb.eStopReasonPlanComplete: + if self.callback: + if self.callback_owner: + self.callback(self.callback_owner, thread) + else: + self.callback(thread) + return True + return False + + +class BreakpointAction (Action): + + def __init__( + self, + callback=None, + callback_owner=None, + name=None, + module=None, + file=None, + line=None, + breakpoint=None): + Action.__init__(self, callback, callback_owner) + self.modules = lldb.SBFileSpecList() + self.files = lldb.SBFileSpecList() + self.breakpoints = list() + # "module" can be a list or a string + if breakpoint: + self.breakpoints.append(breakpoint) + else: + if module: + if isinstance(module, types.ListType): + for module_path in module: + self.modules.Append( + lldb.SBFileSpec(module_path, False)) + elif isinstance(module, types.StringTypes): + self.modules.Append(lldb.SBFileSpec(module, False)) + if name: + # "file" can be a list or a string + if file: + if isinstance(file, types.ListType): + self.files = lldb.SBFileSpecList() + for f in file: + self.files.Append(lldb.SBFileSpec(f, False)) + elif isinstance(file, types.StringTypes): + self.files.Append(lldb.SBFileSpec(file, False)) + self.breakpoints.append( + self.target.BreakpointCreateByName( + name, self.modules, self.files)) + elif file and line: + self.breakpoints.append( + self.target.BreakpointCreateByLocation( + file, line)) + + def ThreadStopped(self, thread): + if thread.GetStopReason() == lldb.eStopReasonBreakpoint: + for bp in self.breakpoints: + if bp.GetID() == thread.GetStopReasonDataAtIndex(0): + if self.callback: + if self.callback_owner: + self.callback(self.callback_owner, thread) + else: + self.callback(thread) + return True + return False + + +class TestCase: + """Class that aids in running performance tests.""" + + def __init__(self): + self.verbose = False + self.debugger = lldb.SBDebugger.Create() + self.target = None + self.process = None + self.thread = None + self.launch_info = None + self.done = False + self.listener = self.debugger.GetListener() + self.user_actions = list() + self.builtin_actions = list() + self.bp_id_to_dict = dict() + + def Setup(self, args): + self.launch_info = lldb.SBLaunchInfo(args) + + def Run(self, args): + assert False, "performance.TestCase.Run(self, args) must be subclassed" + + def Launch(self): + if self.target: + error = lldb.SBError() + self.process = self.target.Launch(self.launch_info, error) + if not error.Success(): + print("error: %s" % error.GetCString()) + if self.process: + self.process.GetBroadcaster().AddListener(self.listener, + lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitInterrupt) + return True + return False + + def WaitForNextProcessEvent(self): + event = None + if self.process: + while event is None: + process_event = lldb.SBEvent() + if self.listener.WaitForEvent(lldb.UINT32_MAX, process_event): + state = lldb.SBProcess.GetStateFromEvent(process_event) + if self.verbose: + print("event = %s" % (lldb.SBDebugger.StateAsCString(state))) + if lldb.SBProcess.GetRestartedFromEvent(process_event): + continue + if state == lldb.eStateInvalid or state == lldb.eStateDetached or state == lldb.eStateCrashed or state == lldb.eStateUnloaded or state == lldb.eStateExited: + event = process_event + self.done = True + elif state == lldb.eStateConnected or state == lldb.eStateAttaching or state == lldb.eStateLaunching or state == lldb.eStateRunning or state == lldb.eStateStepping or state == lldb.eStateSuspended: + continue + elif state == lldb.eStateStopped: + event = process_event + call_test_step = True + fatal = False + selected_thread = False + for thread in self.process: + frame = thread.GetFrameAtIndex(0) + select_thread = False + + stop_reason = thread.GetStopReason() + if self.verbose: + print("tid = %#x pc = %#x " % (thread.GetThreadID(), frame.GetPC()), end=' ') + if stop_reason == lldb.eStopReasonNone: + if self.verbose: + print("none") + elif stop_reason == lldb.eStopReasonTrace: + select_thread = True + if self.verbose: + print("trace") + elif stop_reason == lldb.eStopReasonPlanComplete: + select_thread = True + if self.verbose: + print("plan complete") + elif stop_reason == lldb.eStopReasonThreadExiting: + if self.verbose: + print("thread exiting") + elif stop_reason == lldb.eStopReasonExec: + if self.verbose: + print("exec") + elif stop_reason == lldb.eStopReasonInvalid: + if self.verbose: + print("invalid") + elif stop_reason == lldb.eStopReasonException: + select_thread = True + if self.verbose: + print("exception") + fatal = True + elif stop_reason == lldb.eStopReasonBreakpoint: + select_thread = True + bp_id = thread.GetStopReasonDataAtIndex(0) + bp_loc_id = thread.GetStopReasonDataAtIndex(1) + if self.verbose: + print("breakpoint id = %d.%d" % (bp_id, bp_loc_id)) + elif stop_reason == lldb.eStopReasonWatchpoint: + select_thread = True + if self.verbose: + print("watchpoint id = %d" % (thread.GetStopReasonDataAtIndex(0))) + elif stop_reason == lldb.eStopReasonSignal: + select_thread = True + if self.verbose: + print("signal %d" % (thread.GetStopReasonDataAtIndex(0))) + + if select_thread and not selected_thread: + self.thread = thread + selected_thread = self.process.SetSelectedThread( + thread) + + for action in self.user_actions: + action.ThreadStopped(thread) + + if fatal: + # if self.verbose: + # Xcode.RunCommand(self.debugger,"bt all",true) + sys.exit(1) + return event + + +class Measurement: + '''A class that encapsulates a measurement''' + + def __init__(self): + object.__init__(self) + + def Measure(self): + assert False, "performance.Measurement.Measure() must be subclassed" + + +class MemoryMeasurement(Measurement): + '''A class that can measure memory statistics for a process.''' + + def __init__(self, pid): + Measurement.__init__(self) + self.pid = pid + self.stats = [ + "rprvt", + "rshrd", + "rsize", + "vsize", + "vprvt", + "kprvt", + "kshrd", + "faults", + "cow", + "pageins"] + self.command = "top -l 1 -pid %u -stats %s" % ( + self.pid, ",".join(self.stats)) + self.value = dict() + + def Measure(self): + output = subprocess.getoutput(self.command).split("\n")[-1] + values = re.split('[-+\s]+', output) + for (idx, stat) in enumerate(values): + multiplier = 1 + if stat: + if stat[-1] == 'K': + multiplier = 1024 + stat = stat[:-1] + elif stat[-1] == 'M': + multiplier = 1024 * 1024 + stat = stat[:-1] + elif stat[-1] == 'G': + multiplier = 1024 * 1024 * 1024 + elif stat[-1] == 'T': + multiplier = 1024 * 1024 * 1024 * 1024 + stat = stat[:-1] + self.value[self.stats[idx]] = int(stat) * multiplier + + def __str__(self): + '''Dump the MemoryMeasurement current value''' + s = '' + for key in self.value.keys(): + if s: + s += "\n" + s += "%8s = %s" % (key, self.value[key]) + return s + + +class TesterTestCase(TestCase): + + def __init__(self): + TestCase.__init__(self) + self.verbose = True + self.num_steps = 5 + + def BreakpointHit(self, thread): + bp_id = thread.GetStopReasonDataAtIndex(0) + loc_id = thread.GetStopReasonDataAtIndex(1) + print("Breakpoint %i.%i hit: %s" % (bp_id, loc_id, thread.process.target.FindBreakpointByID(bp_id))) + thread.StepOver() + + def PlanComplete(self, thread): + if self.num_steps > 0: + thread.StepOver() + self.num_steps = self.num_steps - 1 + else: + thread.process.Kill() + + def Run(self, args): + self.Setup(args) + with Timer() as total_time: + self.target = self.debugger.CreateTarget(args[0]) + if self.target: + with Timer() as breakpoint_timer: + bp = self.target.BreakpointCreateByName("main") + print( + 'Breakpoint time = %.03f sec.' % + breakpoint_timer.interval) + + self.user_actions.append( + BreakpointAction( + breakpoint=bp, + callback=TesterTestCase.BreakpointHit, + callback_owner=self)) + self.user_actions.append( + PlanCompleteAction( + callback=TesterTestCase.PlanComplete, + callback_owner=self)) + + if self.Launch(): + while not self.done: + self.WaitForNextProcessEvent() + else: + print("error: failed to launch process") + else: + print("error: failed to create target with '%s'" % (args[0])) + print('Total time = %.03f sec.' % total_time.interval) + + +if __name__ == '__main__': + lldb.SBDebugger.Initialize() + test = TesterTestCase() + test.Run(sys.argv[1:]) + mem = MemoryMeasurement(os.getpid()) + mem.Measure() + print(str(mem)) + lldb.SBDebugger.Terminate() + # print "sleeeping for 100 seconds" + # time.sleep(100) diff --git a/gnu/llvm/lldb/examples/python/process_events.py b/gnu/llvm/lldb/examples/python/process_events.py new file mode 100755 index 00000000000..6039ebf0020 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/process_events.py @@ -0,0 +1,419 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +from __future__ import print_function + +import optparse +import os +import platform +import sys +import subprocess + +#---------------------------------------------------------------------- +# Code that auto imports LLDB +#---------------------------------------------------------------------- +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True) + if xcode_dir: + lldb_python_dirs.append( + os.path.realpath( + xcode_dir + + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append( + xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append( + '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print('imported lldb from: "%s"' % (lldb_python_dir)) + success = True + break + if not success: + print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") + sys.exit(1) + + +def print_threads(process, options): + if options.show_threads: + for thread in process: + print('%s %s' % (thread, thread.GetFrameAtIndex(0))) + + +def run_commands(command_interpreter, commands): + return_obj = lldb.SBCommandReturnObject() + for command in commands: + command_interpreter.HandleCommand(command, return_obj) + if return_obj.Succeeded(): + print(return_obj.GetOutput()) + else: + print(return_obj) + if options.stop_on_error: + break + + +def main(argv): + description = '''Debugs a program using the LLDB python API and uses asynchronous broadcast events to watch for process state changes.''' + epilog = '''Examples: + +#---------------------------------------------------------------------- +# Run "/bin/ls" with the arguments "-lAF /tmp/", and set a breakpoint +# at "malloc" and backtrace and read all registers each time we stop +#---------------------------------------------------------------------- +% ./process_events.py --breakpoint malloc --stop-command bt --stop-command 'register read' -- /bin/ls -lAF /tmp/ + +''' + optparse.OptionParser.format_epilog = lambda self, formatter: self.epilog + parser = optparse.OptionParser( + description=description, + prog='process_events', + usage='usage: process_events [options] program [arg1 arg2]', + epilog=epilog) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help="Enable verbose logging.", + default=False) + parser.add_option( + '-b', + '--breakpoint', + action='append', + type='string', + metavar='BPEXPR', + dest='breakpoints', + help='Breakpoint commands to create after the target has been created, the values will be sent to the "_regexp-break" command which supports breakpoints by name, file:line, and address.') + parser.add_option( + '-a', + '--arch', + type='string', + dest='arch', + help='The architecture to use when creating the debug target.', + default=None) + parser.add_option( + '--platform', + type='string', + metavar='platform', + dest='platform', + help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".', + default=None) + parser.add_option( + '-l', + '--launch-command', + action='append', + type='string', + metavar='CMD', + dest='launch_commands', + help='LLDB command interpreter commands to run once after the process has launched. This option can be specified more than once.', + default=[]) + parser.add_option( + '-s', + '--stop-command', + action='append', + type='string', + metavar='CMD', + dest='stop_commands', + help='LLDB command interpreter commands to run each time the process stops. This option can be specified more than once.', + default=[]) + parser.add_option( + '-c', + '--crash-command', + action='append', + type='string', + metavar='CMD', + dest='crash_commands', + help='LLDB command interpreter commands to run in case the process crashes. This option can be specified more than once.', + default=[]) + parser.add_option( + '-x', + '--exit-command', + action='append', + type='string', + metavar='CMD', + dest='exit_commands', + help='LLDB command interpreter commands to run once after the process has exited. This option can be specified more than once.', + default=[]) + parser.add_option( + '-T', + '--no-threads', + action='store_false', + dest='show_threads', + help="Don't show threads when process stops.", + default=True) + parser.add_option( + '--ignore-errors', + action='store_false', + dest='stop_on_error', + help="Don't stop executing LLDB commands if the command returns an error. This applies to all of the LLDB command interpreter commands that get run for launch, stop, crash and exit.", + default=True) + parser.add_option( + '-n', + '--run-count', + type='int', + dest='run_count', + metavar='N', + help='How many times to run the process in case the process exits.', + default=1) + parser.add_option( + '-t', + '--event-timeout', + type='int', + dest='event_timeout', + metavar='SEC', + help='Specify the timeout in seconds to wait for process state change events.', + default=lldb.UINT32_MAX) + parser.add_option( + '-e', + '--environment', + action='append', + type='string', + metavar='ENV', + dest='env_vars', + help='Environment variables to set in the inferior process when launching a process.') + parser.add_option( + '-d', + '--working-dir', + type='string', + metavar='DIR', + dest='working_dir', + help='The the current working directory when launching a process.', + default=None) + parser.add_option( + '-p', + '--attach-pid', + type='int', + dest='attach_pid', + metavar='PID', + help='Specify a process to attach to by process ID.', + default=-1) + parser.add_option( + '-P', + '--attach-name', + type='string', + dest='attach_name', + metavar='PROCESSNAME', + help='Specify a process to attach to by name.', + default=None) + parser.add_option( + '-w', + '--attach-wait', + action='store_true', + dest='attach_wait', + help='Wait for the next process to launch when attaching to a process by name.', + default=False) + try: + (options, args) = parser.parse_args(argv) + except: + return + + attach_info = None + launch_info = None + exe = None + if args: + exe = args.pop(0) + launch_info = lldb.SBLaunchInfo(args) + if options.env_vars: + launch_info.SetEnvironmentEntries(options.env_vars, True) + if options.working_dir: + launch_info.SetWorkingDirectory(options.working_dir) + elif options.attach_pid != -1: + if options.run_count == 1: + attach_info = lldb.SBAttachInfo(options.attach_pid) + else: + print("error: --run-count can't be used with the --attach-pid option") + sys.exit(1) + elif not options.attach_name is None: + if options.run_count == 1: + attach_info = lldb.SBAttachInfo( + options.attach_name, options.attach_wait) + else: + print("error: --run-count can't be used with the --attach-name option") + sys.exit(1) + else: + print('error: a program path for a program to debug and its arguments are required') + sys.exit(1) + + # Create a new debugger instance + debugger = lldb.SBDebugger.Create() + debugger.SetAsync(True) + command_interpreter = debugger.GetCommandInterpreter() + # Create a target from a file and arch + + if exe: + print("Creating a target for '%s'" % exe) + error = lldb.SBError() + target = debugger.CreateTarget( + exe, options.arch, options.platform, True, error) + + if target: + + # Set any breakpoints that were specified in the args if we are launching. We use the + # command line command to take advantage of the shorthand breakpoint + # creation + if launch_info and options.breakpoints: + for bp in options.breakpoints: + debugger.HandleCommand("_regexp-break %s" % (bp)) + run_commands(command_interpreter, ['breakpoint list']) + + for run_idx in range(options.run_count): + # Launch the process. Since we specified synchronous mode, we won't return + # from this function until we hit the breakpoint at main + error = lldb.SBError() + + if launch_info: + if options.run_count == 1: + print('Launching "%s"...' % (exe)) + else: + print('Launching "%s"... (launch %u of %u)' % (exe, run_idx + 1, options.run_count)) + + process = target.Launch(launch_info, error) + else: + if options.attach_pid != -1: + print('Attaching to process %i...' % (options.attach_pid)) + else: + if options.attach_wait: + print('Waiting for next to process named "%s" to launch...' % (options.attach_name)) + else: + print('Attaching to existing process named "%s"...' % (options.attach_name)) + process = target.Attach(attach_info, error) + + # Make sure the launch went ok + if process and process.GetProcessID() != lldb.LLDB_INVALID_PROCESS_ID: + + pid = process.GetProcessID() + print('Process is %i' % (pid)) + if attach_info: + # continue process if we attached as we won't get an + # initial event + process.Continue() + + listener = debugger.GetListener() + # sign up for process state change events + stop_idx = 0 + done = False + while not done: + event = lldb.SBEvent() + if listener.WaitForEvent(options.event_timeout, event): + if lldb.SBProcess.EventIsProcessEvent(event): + state = lldb.SBProcess.GetStateFromEvent(event) + if state == lldb.eStateInvalid: + # Not a state event + print('process event = %s' % (event)) + else: + print("process state changed event: %s" % (lldb.SBDebugger.StateAsCString(state))) + if state == lldb.eStateStopped: + if stop_idx == 0: + if launch_info: + print("process %u launched" % (pid)) + run_commands( + command_interpreter, ['breakpoint list']) + else: + print("attached to process %u" % (pid)) + for m in target.modules: + print(m) + if options.breakpoints: + for bp in options.breakpoints: + debugger.HandleCommand( + "_regexp-break %s" % (bp)) + run_commands( + command_interpreter, ['breakpoint list']) + run_commands( + command_interpreter, options.launch_commands) + else: + if options.verbose: + print("process %u stopped" % (pid)) + run_commands( + command_interpreter, options.stop_commands) + stop_idx += 1 + print_threads(process, options) + print("continuing process %u" % (pid)) + process.Continue() + elif state == lldb.eStateExited: + exit_desc = process.GetExitDescription() + if exit_desc: + print("process %u exited with status %u: %s" % (pid, process.GetExitStatus(), exit_desc)) + else: + print("process %u exited with status %u" % (pid, process.GetExitStatus())) + run_commands( + command_interpreter, options.exit_commands) + done = True + elif state == lldb.eStateCrashed: + print("process %u crashed" % (pid)) + print_threads(process, options) + run_commands( + command_interpreter, options.crash_commands) + done = True + elif state == lldb.eStateDetached: + print("process %u detached" % (pid)) + done = True + elif state == lldb.eStateRunning: + # process is running, don't say anything, + # we will always get one of these after + # resuming + if options.verbose: + print("process %u resumed" % (pid)) + elif state == lldb.eStateUnloaded: + print("process %u unloaded, this shouldn't happen" % (pid)) + done = True + elif state == lldb.eStateConnected: + print("process connected") + elif state == lldb.eStateAttaching: + print("process attaching") + elif state == lldb.eStateLaunching: + print("process launching") + else: + print('event = %s' % (event)) + else: + # timeout waiting for an event + print("no process event for %u seconds, killing the process..." % (options.event_timeout)) + done = True + # Now that we are done dump the stdout and stderr + process_stdout = process.GetSTDOUT(1024) + if process_stdout: + print("Process STDOUT:\n%s" % (process_stdout)) + while process_stdout: + process_stdout = process.GetSTDOUT(1024) + print(process_stdout) + process_stderr = process.GetSTDERR(1024) + if process_stderr: + print("Process STDERR:\n%s" % (process_stderr)) + while process_stderr: + process_stderr = process.GetSTDERR(1024) + print(process_stderr) + process.Kill() # kill the process + else: + if error: + print(error) + else: + if launch_info: + print('error: launch failed') + else: + print('error: attach failed') + + lldb.SBDebugger.Terminate() + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/gnu/llvm/lldb/examples/python/pytracer.py b/gnu/llvm/lldb/examples/python/pytracer.py new file mode 100644 index 00000000000..27f88d7a3ee --- /dev/null +++ b/gnu/llvm/lldb/examples/python/pytracer.py @@ -0,0 +1,361 @@ +from __future__ import print_function +import sys +import inspect +from collections import OrderedDict + + +class TracebackFancy: + + def __init__(self, traceback): + self.t = traceback + + def getFrame(self): + return FrameFancy(self.t.tb_frame) + + def getLineNumber(self): + return self.t.tb_lineno if self.t is not None else None + + def getNext(self): + return TracebackFancy(self.t.tb_next) + + def __str__(self): + if self.t is None: + return "" + str_self = "%s @ %s" % ( + self.getFrame().getName(), self.getLineNumber()) + return str_self + "\n" + self.getNext().__str__() + + +class ExceptionFancy: + + def __init__(self, frame): + self.etraceback = frame.f_exc_traceback + self.etype = frame.exc_type + self.evalue = frame.f_exc_value + + def __init__(self, tb, ty, va): + self.etraceback = tb + self.etype = ty + self.evalue = va + + def getTraceback(self): + return TracebackFancy(self.etraceback) + + def __nonzero__(self): + return self.etraceback is not None or self.etype is not None or self.evalue is not None + + def getType(self): + return str(self.etype) + + def getValue(self): + return self.evalue + + +class CodeFancy: + + def __init__(self, code): + self.c = code + + def getArgCount(self): + return self.c.co_argcount if self.c is not None else 0 + + def getFilename(self): + return self.c.co_filename if self.c is not None else "" + + def getVariables(self): + return self.c.co_varnames if self.c is not None else [] + + def getName(self): + return self.c.co_name if self.c is not None else "" + + def getFileName(self): + return self.c.co_filename if self.c is not None else "" + + +class ArgsFancy: + + def __init__(self, frame, arginfo): + self.f = frame + self.a = arginfo + + def __str__(self): + args, varargs, kwargs = self.getArgs(), self.getVarArgs(), self.getKWArgs() + ret = "" + count = 0 + size = len(args) + for arg in args: + ret = ret + ("%s = %s" % (arg, args[arg])) + count = count + 1 + if count < size: + ret = ret + ", " + if varargs: + if size > 0: + ret = ret + " " + ret = ret + "varargs are " + str(varargs) + if kwargs: + if size > 0: + ret = ret + " " + ret = ret + "kwargs are " + str(kwargs) + return ret + + def getNumArgs(wantVarargs=False, wantKWArgs=False): + args, varargs, keywords, values = self.a + size = len(args) + if varargs and wantVarargs: + size = size + len(self.getVarArgs()) + if keywords and wantKWArgs: + size = size + len(self.getKWArgs()) + return size + + def getArgs(self): + args, _, _, values = self.a + argWValues = OrderedDict() + for arg in args: + argWValues[arg] = values[arg] + return argWValues + + def getVarArgs(self): + _, vargs, _, _ = self.a + if vargs: + return self.f.f_locals[vargs] + return () + + def getKWArgs(self): + _, _, kwargs, _ = self.a + if kwargs: + return self.f.f_locals[kwargs] + return {} + + +class FrameFancy: + + def __init__(self, frame): + self.f = frame + + def getCaller(self): + return FrameFancy(self.f.f_back) + + def getLineNumber(self): + return self.f.f_lineno if self.f is not None else 0 + + def getCodeInformation(self): + return CodeFancy(self.f.f_code) if self.f is not None else None + + def getExceptionInfo(self): + return ExceptionFancy(self.f) if self.f is not None else None + + def getName(self): + return self.getCodeInformation().getName() if self.f is not None else "" + + def getFileName(self): + return self.getCodeInformation().getFileName() if self.f is not None else "" + + def getLocals(self): + return self.f.f_locals if self.f is not None else {} + + def getArgumentInfo(self): + return ArgsFancy( + self.f, inspect.getargvalues( + self.f)) if self.f is not None else None + + +class TracerClass: + + def callEvent(self, frame): + pass + + def lineEvent(self, frame): + pass + + def returnEvent(self, frame, retval): + pass + + def exceptionEvent(self, frame, exception, value, traceback): + pass + + def cCallEvent(self, frame, cfunct): + pass + + def cReturnEvent(self, frame, cfunct): + pass + + def cExceptionEvent(self, frame, cfunct): + pass + +tracer_impl = TracerClass() + + +def the_tracer_entrypoint(frame, event, args): + if tracer_impl is None: + return None + if event == "call": + call_retval = tracer_impl.callEvent(FrameFancy(frame)) + if not call_retval: + return None + return the_tracer_entrypoint + elif event == "line": + line_retval = tracer_impl.lineEvent(FrameFancy(frame)) + if not line_retval: + return None + return the_tracer_entrypoint + elif event == "return": + tracer_impl.returnEvent(FrameFancy(frame), args) + elif event == "exception": + exty, exva, extb = args + exception_retval = tracer_impl.exceptionEvent( + FrameFancy(frame), ExceptionFancy(extb, exty, exva)) + if not exception_retval: + return None + return the_tracer_entrypoint + elif event == "c_call": + tracer_impl.cCallEvent(FrameFancy(frame), args) + elif event == "c_return": + tracer_impl.cReturnEvent(FrameFancy(frame), args) + elif event == "c_exception": + tracer_impl.cExceptionEvent(FrameFancy(frame), args) + return None + + +def enable(t=None): + global tracer_impl + if t: + tracer_impl = t + sys.settrace(the_tracer_entrypoint) + + +def disable(): + sys.settrace(None) + + +class LoggingTracer: + + def callEvent(self, frame): + print("call " + frame.getName() + " from " + frame.getCaller().getName() + " @ " + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo())) + + def lineEvent(self, frame): + print("running " + frame.getName() + " @ " + str(frame.getLineNumber()) + " locals are " + str(frame.getLocals()) + " in " + frame.getFileName()) + + def returnEvent(self, frame, retval): + print("return from " + frame.getName() + " value is " + str(retval) + " locals are " + str(frame.getLocals())) + + def exceptionEvent(self, frame, exception): + print("exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber())) + print("tb: " + str(exception.getTraceback())) + +# the same functionality as LoggingTracer, but with a little more +# lldb-specific smarts + + +class LLDBAwareTracer: + + def callEvent(self, frame): + if frame.getName() == "<module>": + return + if frame.getName() == "run_one_line": + print("call run_one_line(%s)" % (frame.getArgumentInfo().getArgs()["input_string"])) + return + if "Python.framework" in frame.getFileName(): + print("call into Python at " + frame.getName()) + return + if frame.getName() == "__init__" and frame.getCaller().getName( + ) == "run_one_line" and frame.getCaller().getLineNumber() == 101: + return False + strout = "call " + frame.getName() + if (frame.getCaller().getFileName() == ""): + strout += " from LLDB - args are " + args = frame.getArgumentInfo().getArgs() + for arg in args: + if arg == "dict" or arg == "internal_dict": + continue + strout = strout + ("%s = %s " % (arg, args[arg])) + else: + strout += " from " + frame.getCaller().getName() + " @ " + \ + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo()) + print(strout) + + def lineEvent(self, frame): + if frame.getName() == "<module>": + return + if frame.getName() == "run_one_line": + print("running run_one_line(%s) @ %s" % (frame.getArgumentInfo().getArgs()["input_string"], frame.getLineNumber())) + return + if "Python.framework" in frame.getFileName(): + print("running into Python at " + frame.getName() + " @ " + str(frame.getLineNumber())) + return + strout = "running " + frame.getName() + " @ " + str(frame.getLineNumber()) + \ + " locals are " + if (frame.getCaller().getFileName() == ""): + locals = frame.getLocals() + for local in locals: + if local == "dict" or local == "internal_dict": + continue + strout = strout + ("%s = %s " % (local, locals[local])) + else: + strout = strout + str(frame.getLocals()) + strout = strout + " in " + frame.getFileName() + print(strout) + + def returnEvent(self, frame, retval): + if frame.getName() == "<module>": + return + if frame.getName() == "run_one_line": + print("return from run_one_line(%s) return value is %s" % (frame.getArgumentInfo().getArgs()["input_string"], retval)) + return + if "Python.framework" in frame.getFileName(): + print("return from Python at " + frame.getName() + " return value is " + str(retval)) + return + strout = "return from " + frame.getName() + " return value is " + \ + str(retval) + " locals are " + if (frame.getCaller().getFileName() == ""): + locals = frame.getLocals() + for local in locals: + if local == "dict" or local == "internal_dict": + continue + strout = strout + ("%s = %s " % (local, locals[local])) + else: + strout = strout + str(frame.getLocals()) + strout = strout + " in " + frame.getFileName() + print(strout) + + def exceptionEvent(self, frame, exception): + if frame.getName() == "<module>": + return + print("exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber())) + print("tb: " + str(exception.getTraceback())) + + +def f(x, y=None): + if x > 0: + return 2 + f(x - 2) + return 35 + + +def g(x): + return 1.134 / x + + +def print_keyword_args(**kwargs): + # kwargs is a dict of the keyword args passed to the function + for key, value in kwargs.items(): + print("%s = %s" % (key, value)) + + +def total(initial=5, *numbers, **keywords): + count = initial + for number in numbers: + count += number + for key in keywords: + count += keywords[key] + return count + +if __name__ == "__main__": + enable(LoggingTracer()) + f(5) + f(5, 1) + print_keyword_args(first_name="John", last_name="Doe") + total(10, 1, 2, 3, vegetables=50, fruits=100) + try: + g(0) + except: + pass + disable() diff --git a/gnu/llvm/lldb/examples/python/sbvalue.py b/gnu/llvm/lldb/examples/python/sbvalue.py new file mode 100755 index 00000000000..6e512998da2 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/sbvalue.py @@ -0,0 +1,268 @@ +#!/usr/bin/python + +import lldb + + +class value(object): + '''A class that wraps an lldb.SBValue object and returns an object that + can be used as an object with attribytes:\n + argv = a.value(lldb.frame.FindVariable('argv'))\n + argv.name - return the name of the value that this object contains\n + argv.type - return the lldb.SBType for this value + argv.type_name - return the name of the type + argv.size - return the byte size of this value + argv.is_in_scope - return true if this value is currently in scope + argv.is_pointer - return true if this value is a pointer + argv.format - return the current format for this value + argv.value - return the value's value as a string + argv.summary - return a summary of this value's value + argv.description - return the runtime description for this value + argv.location - return a string that represents the values location (address, register, etc) + argv.target - return the lldb.SBTarget for this value + argv.process - return the lldb.SBProcess for this value + argv.thread - return the lldb.SBThread for this value + argv.frame - return the lldb.SBFrame for this value + argv.num_children - return the number of children this value has + argv.children - return a list of sbvalue objects that represents all of the children of this value + ''' + + def __init__(self, sbvalue): + self.sbvalue = sbvalue + + def __nonzero__(self): + return self.sbvalue.__nonzero__() + + def __repr__(self): + return self.sbvalue.__repr__() + + def __str__(self): + return self.sbvalue.__str__() + + def __getitem__(self, key): + if isinstance(key, int): + return value( + self.sbvalue.GetChildAtIndex( + key, lldb.eNoDynamicValues, True)) + raise TypeError + + def __getattr__(self, name): + if name == 'name': + return self.sbvalue.GetName() + if name == 'type': + return self.sbvalue.GetType() + if name == 'type_name': + return self.sbvalue.GetTypeName() + if name == 'size': + return self.sbvalue.GetByteSize() + if name == 'is_in_scope': + return self.sbvalue.IsInScope() + if name == 'is_pointer': + return self.sbvalue.TypeIsPointerType() + if name == 'format': + return self.sbvalue.GetFormat() + if name == 'value': + return self.sbvalue.GetValue() + if name == 'summary': + return self.sbvalue.GetSummary() + if name == 'description': + return self.sbvalue.GetObjectDescription() + if name == 'location': + return self.sbvalue.GetLocation() + if name == 'target': + return self.sbvalue.GetTarget() + if name == 'process': + return self.sbvalue.GetProcess() + if name == 'thread': + return self.sbvalue.GetThread() + if name == 'frame': + return self.sbvalue.GetFrame() + if name == 'num_children': + return self.sbvalue.GetNumChildren() + if name == 'children': + # Returns an array of sbvalue objects, one for each child of + # the value for the lldb.SBValue + children = [] + for i in range(self.sbvalue.GetNumChildren()): + children.append( + value( + self.sbvalue.GetChildAtIndex( + i, + lldb.eNoDynamicValues, + True))) + return children + raise AttributeError + + +class variable(object): + '''A class that treats a lldb.SBValue and allows it to be used just as + a variable would be in code. So if you have a Point structure variable + in your code, you would be able to do: "pt.x + pt.y"''' + + def __init__(self, sbvalue): + self.sbvalue = sbvalue + + def __nonzero__(self): + return self.sbvalue.__nonzero__() + + def __repr__(self): + return self.sbvalue.__repr__() + + def __str__(self): + return self.sbvalue.__str__() + + def __getitem__(self, key): + # Allow array access if this value has children... + if isinstance(key, int): + return variable( + self.sbvalue.GetValueForExpressionPath( + "[%i]" % + key)) + raise TypeError + + def __getattr__(self, name): + child_sbvalue = self.sbvalue.GetChildMemberWithName(name) + if child_sbvalue: + return variable(child_sbvalue) + raise AttributeError + + def __add__(self, other): + return int(self) + int(other) + + def __sub__(self, other): + return int(self) - int(other) + + def __mul__(self, other): + return int(self) * int(other) + + def __floordiv__(self, other): + return int(self) // int(other) + + def __mod__(self, other): + return int(self) % int(other) + + def __divmod__(self, other): + return int(self) % int(other) + + def __pow__(self, other): + return int(self) ** int(other) + + def __lshift__(self, other): + return int(self) << int(other) + + def __rshift__(self, other): + return int(self) >> int(other) + + def __and__(self, other): + return int(self) & int(other) + + def __xor__(self, other): + return int(self) ^ int(other) + + def __or__(self, other): + return int(self) | int(other) + + def __div__(self, other): + return int(self) / int(other) + + def __truediv__(self, other): + return int(self) / int(other) + + def __iadd__(self, other): + result = self.__add__(other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __isub__(self, other): + result = self.__sub__(other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __imul__(self, other): + result = self.__mul__(other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __idiv__(self, other): + result = self.__div__(other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __itruediv__(self, other): + result = self.__truediv__(other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __ifloordiv__(self, other): + result = self.__floordiv__(self, other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __imod__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __ipow__(self, other): + result = self.__pow__(self, other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __ipow__(self, other, modulo): + result = self.__pow__(self, other, modulo) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __ilshift__(self, other): + result = self.__lshift__(self, other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __irshift__(self, other): + result = self.__rshift__(self, other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __iand__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __ixor__(self, other): + result = self.__xor__(self, other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __ior__(self, other): + result = self.__ior__(self, other) + self.sbvalue.SetValueFromCString(str(result)) + return result + + def __neg__(self): + return -int(self) + + def __pos__(self): + return +int(self) + + def __abs__(self): + return abs(int(self)) + + def __invert__(self): + return ~int(self) + + def __complex__(self): + return complex(int(self)) + + def __int__(self): + return self.sbvalue.GetValueAsSigned() + + def __long__(self): + return self.sbvalue.GetValueAsSigned() + + def __float__(self): + return float(self.sbvalue.GetValueAsSigned()) + + def __oct__(self): + return '0%o' % self.sbvalue.GetValueAsSigned() + + def __hex__(self): + return '0x%x' % self.sbvalue.GetValueAsSigned() diff --git a/gnu/llvm/lldb/examples/python/scripted_step.py b/gnu/llvm/lldb/examples/python/scripted_step.py new file mode 100644 index 00000000000..3c1d5d7a6c4 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/scripted_step.py @@ -0,0 +1,246 @@ +############################################################################# +# This script contains two trivial examples of simple "scripted step" classes. +# To fully understand how the lldb "Thread Plan" architecture works, read the +# comments at the beginning of ThreadPlan.h in the lldb sources. The python +# interface is a reduced version of the full internal mechanism, but captures +# most of the power with a much simpler interface. +# +# But I'll attempt a brief summary here. +# Stepping in lldb is done independently for each thread. Moreover, the stepping +# operations are stackable. So for instance if you did a "step over", and in +# the course of stepping over you hit a breakpoint, stopped and stepped again, +# the first "step-over" would be suspended, and the new step operation would +# be enqueued. Then if that step over caused the program to hit another breakpoint, +# lldb would again suspend the second step and return control to the user, so +# now there are two pending step overs. Etc. with all the other stepping +# operations. Then if you hit "continue" the bottom-most step-over would complete, +# and another continue would complete the first "step-over". +# +# lldb represents this system with a stack of "Thread Plans". Each time a new +# stepping operation is requested, a new plan is pushed on the stack. When the +# operation completes, it is pushed off the stack. +# +# The bottom-most plan in the stack is the immediate controller of stepping, +# most importantly, when the process resumes, the bottom most plan will get +# asked whether to set the program running freely, or to instruction-single-step +# the current thread. In the scripted interface, you indicate this by returning +# False or True respectively from the should_step method. +# +# Each time the process stops the thread plan stack for each thread that stopped +# "for a reason", Ii.e. a single-step completed on that thread, or a breakpoint +# was hit), is queried to determine how to proceed, starting from the most +# recently pushed plan, in two stages: +# +# 1) Each plan is asked if it "explains" the stop. The first plan to claim the +# stop wins. In scripted Thread Plans, this is done by returning True from +# the "explains_stop method. This is how, for instance, control is returned +# to the User when the "step-over" plan hits a breakpoint. The step-over +# plan doesn't explain the breakpoint stop, so it returns false, and the +# breakpoint hit is propagated up the stack to the "base" thread plan, which +# is the one that handles random breakpoint hits. +# +# 2) Then the plan that won the first round is asked if the process should stop. +# This is done in the "should_stop" method. The scripted plans actually do +# three jobs in should_stop: +# a) They determine if they have completed their job or not. If they have +# they indicate that by calling SetPlanComplete on their thread plan. +# b) They decide whether they want to return control to the user or not. +# They do this by returning True or False respectively. +# c) If they are not done, they set up whatever machinery they will use +# the next time the thread continues. +# +# Note that deciding to return control to the user, and deciding your plan +# is done, are orthgonal operations. You could set up the next phase of +# stepping, and then return True from should_stop, and when the user next +# "continued" the process your plan would resume control. Of course, the +# user might also "step-over" or some other operation that would push a +# different plan, which would take control till it was done. +# +# One other detail you should be aware of, if the plan below you on the +# stack was done, then it will be popped and the next plan will take control +# and its "should_stop" will be called. +# +# Note also, there should be another method called when your plan is popped, +# to allow you to do whatever cleanup is required. I haven't gotten to that +# yet. For now you should do that at the same time you mark your plan complete. +# +# 3) After the round of negotiation over whether to stop or not is done, all the +# plans get asked if they are "stale". If they are say they are stale +# then they will get popped. This question is asked with the "is_stale" method. +# +# This is useful, for instance, in the FinishPrintAndContinue plan. What might +# happen here is that after continuing but before the finish is done, the program +# could hit another breakpoint and stop. Then the user could use the step +# command repeatedly until they leave the frame of interest by stepping. +# In that case, the step plan is the one that will be responsible for stopping, +# and the finish plan won't be asked should_stop, it will just be asked if it +# is stale. In this case, if the step_out plan that the FinishPrintAndContinue +# plan is driving is stale, so is ours, and it is time to do our printing. +# +# Both examples show stepping through an address range for 20 bytes from the +# current PC. The first one does it by single stepping and checking a condition. +# It doesn't, however handle the case where you step into another frame while +# still in the current range in the starting frame. +# +# That is better handled in the second example by using the built-in StepOverRange +# thread plan. +# +# To use these stepping modes, you would do: +# +# (lldb) command script import scripted_step.py +# (lldb) thread step-scripted -C scripted_step.SimpleStep +# or +# +# (lldb) thread step-scripted -C scripted_step.StepWithPlan + +from __future__ import print_function + +import lldb + + +class SimpleStep: + + def __init__(self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPC() + + def explains_stop(self, event): + # We are stepping, so if we stop for any other reason, it isn't + # because of us. + if self.thread_plan.GetThread().GetStopReason() == lldb.eStopReasonTrace: + return True + else: + return False + + def should_stop(self, event): + cur_pc = self.thread_plan.GetThread().GetFrameAtIndex(0).GetPC() + + if cur_pc < self.start_address or cur_pc >= self.start_address + 20: + self.thread_plan.SetPlanComplete(True) + return True + else: + return False + + def should_step(self): + return True + + +class StepWithPlan: + + def __init__(self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPCAddress() + self.step_thread_plan = thread_plan.QueueThreadPlanForStepOverRange( + self.start_address, 20) + + def explains_stop(self, event): + # Since all I'm doing is running a plan, I will only ever get askedthis + # if myplan doesn't explain the stop, and in that caseI don'teither. + return False + + def should_stop(self, event): + if self.step_thread_plan.IsPlanComplete(): + self.thread_plan.SetPlanComplete(True) + return True + else: + return False + + def should_step(self): + return False + +# Here's another example which does "step over" through the current function, +# and when it stops at each line, it checks some condition (in this example the +# value of a variable) and stops if that condition is true. + + +class StepCheckingCondition: + + def __init__(self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_frame = thread_plan.GetThread().GetFrameAtIndex(0) + self.queue_next_plan() + + def queue_next_plan(self): + cur_frame = self.thread_plan.GetThread().GetFrameAtIndex(0) + cur_line_entry = cur_frame.GetLineEntry() + start_address = cur_line_entry.GetStartAddress() + end_address = cur_line_entry.GetEndAddress() + line_range = end_address.GetFileAddress() - start_address.GetFileAddress() + self.step_thread_plan = self.thread_plan.QueueThreadPlanForStepOverRange( + start_address, line_range) + + def explains_stop(self, event): + # We are stepping, so if we stop for any other reason, it isn't + # because of us. + return False + + def should_stop(self, event): + if not self.step_thread_plan.IsPlanComplete(): + return False + + frame = self.thread_plan.GetThread().GetFrameAtIndex(0) + if not self.start_frame.IsEqual(frame): + self.thread_plan.SetPlanComplete(True) + return True + + # This part checks the condition. In this case we are expecting + # some integer variable called "a", and will stop when it is 20. + a_var = frame.FindVariable("a") + + if not a_var.IsValid(): + print("A was not valid.") + return True + + error = lldb.SBError() + a_value = a_var.GetValueAsSigned(error) + if not error.Success(): + print("A value was not good.") + return True + + if a_value == 20: + self.thread_plan.SetPlanComplete(True) + return True + else: + self.queue_next_plan() + return False + + def should_step(self): + return True + +# Here's an example that steps out of the current frame, gathers some information +# and then continues. The information in this case is rax. Currently the thread +# plans are not a safe place to call lldb command-line commands, so the information +# is gathered through SB API calls. + + +class FinishPrintAndContinue: + + def __init__(self, thread_plan, dict): + self.thread_plan = thread_plan + self.step_out_thread_plan = thread_plan.QueueThreadPlanForStepOut( + 0, True) + self.thread = self.thread_plan.GetThread() + + def is_stale(self): + if self.step_out_thread_plan.IsPlanStale(): + self.do_print() + return True + else: + return False + + def explains_stop(self, event): + return False + + def should_stop(self, event): + if self.step_out_thread_plan.IsPlanComplete(): + self.do_print() + self.thread_plan.SetPlanComplete(True) + return False + + def do_print(self): + frame_0 = self.thread.frames[0] + rax_value = frame_0.FindRegister("rax") + if rax_value.GetError().Success(): + print("RAX on exit: ", rax_value.GetValue()) + else: + print("Couldn't get rax value:", rax_value.GetError().GetCString()) diff --git a/gnu/llvm/lldb/examples/python/shadow.py b/gnu/llvm/lldb/examples/python/shadow.py new file mode 100644 index 00000000000..b14467c52c9 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/shadow.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +from __future__ import print_function + +import lldb +import shlex + + +@lldb.command("shadow") +def check_shadow_command(debugger, command, exe_ctx, result, dict): + '''Check the currently selected stack frame for shadowed variables''' + process = exe_ctx.GetProcess() + state = process.GetState() + if state != lldb.eStateStopped: + print("process must be stopped, state is %s" % lldb.SBDebugger.StateAsCString( + state), file=result) + return + frame = exe_ctx.GetFrame() + if not frame: + print("invalid frame", file=result) + return + # Parse command line args + command_args = shlex.split(command) + # TODO: add support for using arguments that are passed to this command... + + # Make a dictionary of variable name to "SBBlock and SBValue" + shadow_dict = {} + + num_shadowed_variables = 0 + # Get the deepest most block from the current frame + block = frame.GetBlock() + # Iterate through the block and all of its parents + while block.IsValid(): + # Get block variables from the current block only + block_vars = block.GetVariables(frame, True, True, True, 0) + # Iterate through all variables in the current block + for block_var in block_vars: + # Since we can have multiple shadowed variables, we our variable + # name dictionary to have an array or "block + variable" pairs so + # We can correctly print out all shadowed variables and whow which + # blocks they come from + block_var_name = block_var.GetName() + if block_var_name in shadow_dict: + shadow_dict[block_var_name].append(block_var) + else: + shadow_dict[block_var_name] = [block_var] + # Get the parent block and continue + block = block.GetParent() + + num_shadowed_variables = 0 + if shadow_dict: + for name in shadow_dict.keys(): + shadow_vars = shadow_dict[name] + if len(shadow_vars) > 1: + print('"%s" is shadowed by the following declarations:' % (name)) + num_shadowed_variables += 1 + for shadow_var in shadow_vars: + print(str(shadow_var.GetDeclaration()), file=result) + if num_shadowed_variables == 0: + print('no variables are shadowed', file=result) diff --git a/gnu/llvm/lldb/examples/python/sources.py b/gnu/llvm/lldb/examples/python/sources.py new file mode 100644 index 00000000000..9684f7f6e78 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/sources.py @@ -0,0 +1,32 @@ +#!/usr/bin/python +from __future__ import print_function + +import lldb +import shlex + + +def dump_module_sources(module, result): + if module: + print("Module: %s" % (module.file), file=result) + for compile_unit in module.compile_units: + if compile_unit.file: + print(" %s" % (compile_unit.file), file=result) + + +def info_sources(debugger, command, result, dict): + description = '''This command will dump all compile units in any modules that are listed as arguments, or for all modules if no arguments are supplied.''' + module_names = shlex.split(command) + target = debugger.GetSelectedTarget() + if module_names: + for module_name in module_names: + dump_module_sources(target.module[module_name], result) + else: + for module in target.modules: + dump_module_sources(module, result) + + +def __lldb_init_module(debugger, dict): + # Add any commands contained in this module to LLDB + debugger.HandleCommand( + 'command script add -f sources.info_sources info_sources') + print('The "info_sources" command has been installed, type "help info_sources" or "info_sources --help" for detailed help.') diff --git a/gnu/llvm/lldb/examples/python/stacks.py b/gnu/llvm/lldb/examples/python/stacks.py new file mode 100755 index 00000000000..a676b82d097 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/stacks.py @@ -0,0 +1,68 @@ +#!/usr/bin/python +from __future__ import print_function +import lldb +import optparse +import shlex + + +def stack_frames(debugger, command, result, dict): + command_args = shlex.split(command) + usage = "usage: %prog [options] <PATH> [PATH ...]" + description = '''This command will enumerate all stack frames, print the stack size for each, and print an aggregation of which functions have the largest stack frame sizes at the end.''' + parser = optparse.OptionParser( + description=description, prog='ls', usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + target = debugger.GetSelectedTarget() + process = target.GetProcess() + + frame_info = {} + for thread in process: + last_frame = None + print("thread %u" % (thread.id)) + for frame in thread.frames: + if last_frame: + frame_size = 0 + if frame.idx == 1: + if frame.fp == last_frame.fp: + # No frame one the first frame (might be right at the + # entry point) + first_frame_size = 0 + frame_size = frame.fp - frame.sp + else: + # First frame that has a valid size + first_frame_size = last_frame.fp - last_frame.sp + print("<%#7x> %s" % (first_frame_size, last_frame)) + if first_frame_size: + name = last_frame.name + if name not in frame_info: + frame_info[name] = first_frame_size + else: + frame_info[name] += first_frame_size + else: + # Second or higher frame + frame_size = frame.fp - last_frame.fp + print("<%#7x> %s" % (frame_size, frame)) + if frame_size > 0: + name = frame.name + if name not in frame_info: + frame_info[name] = frame_size + else: + frame_info[name] += frame_size + last_frame = frame + print(frame_info) + + +lldb.debugger.HandleCommand( + "command script add -f stacks.stack_frames stack_frames") +print("A new command called 'stack_frames' was added, type 'stack_frames --help' for more information.") diff --git a/gnu/llvm/lldb/examples/python/step_and_print.py b/gnu/llvm/lldb/examples/python/step_and_print.py new file mode 100644 index 00000000000..41364ef97ba --- /dev/null +++ b/gnu/llvm/lldb/examples/python/step_and_print.py @@ -0,0 +1,24 @@ +""" Does a step-over then prints the local variables or only the ones passed in """ +import lldb + +class StepAndPrint: + def __init__(self, debugger, unused): + return + + def __call__(self, debugger, command, exe_ctx, result): + # Set the command to synchronous so the step will complete + # before we try to run the frame variable. + old_async = debugger.GetAsync() + debugger.SetAsync(False) + + debugger.HandleCommand("thread step-over") + print("---------- Values: -------------------\n") + debugger.HandleCommand("frame variable %s"%(command)) + + debugger.SetAsync(old_async) + + def get_short_help(self): + return "Does a step-over then runs frame variable passing the command args to it\n" + +def __lldb_init_module(debugger, unused): + debugger.HandleCommand("command script add -c step_and_print.StepAndPrint sap") diff --git a/gnu/llvm/lldb/examples/python/symbolication.py b/gnu/llvm/lldb/examples/python/symbolication.py new file mode 100755 index 00000000000..a6daa802cda --- /dev/null +++ b/gnu/llvm/lldb/examples/python/symbolication.py @@ -0,0 +1,724 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb": +# +# cd /path/containing/crashlog.py +# lldb +# (lldb) script import crashlog +# "crashlog" command installed, type "crashlog --help" for detailed help +# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash +# +# The benefit of running the crashlog command inside lldb in the +# embedded python interpreter is when the command completes, there +# will be a target with all of the files loaded at the locations +# described in the crash log. Only the files that have stack frames +# in the backtrace will be loaded unless the "--load-all" option +# has been specified. This allows users to explore the program in the +# state it was in right at crash time. +# +# On MacOSX csh, tcsh: +# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) +# +# On MacOSX sh, bash: +# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash +#---------------------------------------------------------------------- + +from __future__ import print_function +import lldb +import optparse +import os +import plistlib +import re +import shlex +import sys +import time +import uuid + + +class Address: + """Class that represents an address that will be symbolicated""" + + def __init__(self, target, load_addr): + self.target = target + self.load_addr = load_addr # The load address that this object represents + # the resolved lldb.SBAddress (if any), named so_addr for + # section/offset address + self.so_addr = None + self.sym_ctx = None # The cached symbol context for this address + # Any original textual description of this address to be used as a + # backup in case symbolication fails + self.description = None + self.symbolication = None # The cached symbolicated string that describes this address + self.inlined = False + + def __str__(self): + s = "%#16.16x" % (self.load_addr) + if self.symbolication: + s += " %s" % (self.symbolication) + elif self.description: + s += " %s" % (self.description) + elif self.so_addr: + s += " %s" % (self.so_addr) + return s + + def resolve_addr(self): + if self.so_addr is None: + self.so_addr = self.target.ResolveLoadAddress(self.load_addr) + return self.so_addr + + def is_inlined(self): + return self.inlined + + def get_symbol_context(self): + if self.sym_ctx is None: + sb_addr = self.resolve_addr() + if sb_addr: + self.sym_ctx = self.target.ResolveSymbolContextForAddress( + sb_addr, lldb.eSymbolContextEverything) + else: + self.sym_ctx = lldb.SBSymbolContext() + return self.sym_ctx + + def get_instructions(self): + sym_ctx = self.get_symbol_context() + if sym_ctx: + function = sym_ctx.GetFunction() + if function: + return function.GetInstructions(self.target) + return sym_ctx.GetSymbol().GetInstructions(self.target) + return None + + def symbolicate(self, verbose=False): + if self.symbolication is None: + self.symbolication = '' + self.inlined = False + sym_ctx = self.get_symbol_context() + if sym_ctx: + module = sym_ctx.GetModule() + if module: + # Print full source file path in verbose mode + if verbose: + self.symbolication += str(module.GetFileSpec()) + '`' + else: + self.symbolication += module.GetFileSpec().GetFilename() + '`' + function_start_load_addr = -1 + function = sym_ctx.GetFunction() + block = sym_ctx.GetBlock() + line_entry = sym_ctx.GetLineEntry() + symbol = sym_ctx.GetSymbol() + inlined_block = block.GetContainingInlinedBlock() + if function: + self.symbolication += function.GetName() + + if inlined_block: + self.inlined = True + self.symbolication += ' [inlined] ' + \ + inlined_block.GetInlinedName() + block_range_idx = inlined_block.GetRangeIndexForBlockAddress( + self.so_addr) + if block_range_idx < lldb.UINT32_MAX: + block_range_start_addr = inlined_block.GetRangeStartAddress( + block_range_idx) + function_start_load_addr = block_range_start_addr.GetLoadAddress( + self.target) + if function_start_load_addr == -1: + function_start_load_addr = function.GetStartAddress().GetLoadAddress(self.target) + elif symbol: + self.symbolication += symbol.GetName() + function_start_load_addr = symbol.GetStartAddress().GetLoadAddress(self.target) + else: + self.symbolication = '' + return False + + # Dump the offset from the current function or symbol if it + # is non zero + function_offset = self.load_addr - function_start_load_addr + if function_offset > 0: + self.symbolication += " + %u" % (function_offset) + elif function_offset < 0: + self.symbolication += " %i (invalid negative offset, file a bug) " % function_offset + + # Print out any line information if any is available + if line_entry.GetFileSpec(): + # Print full source file path in verbose mode + if verbose: + self.symbolication += ' at %s' % line_entry.GetFileSpec() + else: + self.symbolication += ' at %s' % line_entry.GetFileSpec().GetFilename() + self.symbolication += ':%u' % line_entry.GetLine() + column = line_entry.GetColumn() + if column > 0: + self.symbolication += ':%u' % column + return True + return False + + +class Section: + """Class that represents an load address range""" + sect_info_regex = re.compile('(?P<name>[^=]+)=(?P<range>.*)') + addr_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*$') + range_regex = re.compile( + '^\s*(?P<start>0x[0-9A-Fa-f]+)\s*(?P<op>[-+])\s*(?P<end>0x[0-9A-Fa-f]+)\s*$') + + def __init__(self, start_addr=None, end_addr=None, name=None): + self.start_addr = start_addr + self.end_addr = end_addr + self.name = name + + @classmethod + def InitWithSBTargetAndSBSection(cls, target, section): + sect_load_addr = section.GetLoadAddress(target) + if sect_load_addr != lldb.LLDB_INVALID_ADDRESS: + obj = cls( + sect_load_addr, + sect_load_addr + + section.size, + section.name) + return obj + else: + return None + + def contains(self, addr): + return self.start_addr <= addr and addr < self.end_addr + + def set_from_string(self, s): + match = self.sect_info_regex.match(s) + if match: + self.name = match.group('name') + range_str = match.group('range') + addr_match = self.addr_regex.match(range_str) + if addr_match: + self.start_addr = int(addr_match.group('start'), 16) + self.end_addr = None + return True + + range_match = self.range_regex.match(range_str) + if range_match: + self.start_addr = int(range_match.group('start'), 16) + self.end_addr = int(range_match.group('end'), 16) + op = range_match.group('op') + if op == '+': + self.end_addr += self.start_addr + return True + print('error: invalid section info string "%s"' % s) + print('Valid section info formats are:') + print('Format Example Description') + print('--------------------- -----------------------------------------------') + print('<name>=<base> __TEXT=0x123000 Section from base address only') + print('<name>=<base>-<end> __TEXT=0x123000-0x124000 Section from base address and end address') + print('<name>=<base>+<size> __TEXT=0x123000+0x1000 Section from base address and size') + return False + + def __str__(self): + if self.name: + if self.end_addr is not None: + if self.start_addr is not None: + return "%s=[0x%16.16x - 0x%16.16x)" % ( + self.name, self.start_addr, self.end_addr) + else: + if self.start_addr is not None: + return "%s=0x%16.16x" % (self.name, self.start_addr) + return self.name + return "<invalid>" + + +class Image: + """A class that represents an executable image and any associated data""" + + def __init__(self, path, uuid=None): + self.path = path + self.resolved_path = None + self.resolved = False + self.unavailable = False + self.uuid = uuid + self.section_infos = list() + self.identifier = None + self.version = None + self.arch = None + self.module = None + self.symfile = None + self.slide = None + + @classmethod + def InitWithSBTargetAndSBModule(cls, target, module): + '''Initialize this Image object with a module from a target.''' + obj = cls(module.file.fullpath, module.uuid) + obj.resolved_path = module.platform_file.fullpath + obj.resolved = True + for section in module.sections: + symb_section = Section.InitWithSBTargetAndSBSection( + target, section) + if symb_section: + obj.section_infos.append(symb_section) + obj.arch = module.triple + obj.module = module + obj.symfile = None + obj.slide = None + return obj + + def dump(self, prefix): + print("%s%s" % (prefix, self)) + + def debug_dump(self): + print('path = "%s"' % (self.path)) + print('resolved_path = "%s"' % (self.resolved_path)) + print('resolved = %i' % (self.resolved)) + print('unavailable = %i' % (self.unavailable)) + print('uuid = %s' % (self.uuid)) + print('section_infos = %s' % (self.section_infos)) + print('identifier = "%s"' % (self.identifier)) + print('version = %s' % (self.version)) + print('arch = %s' % (self.arch)) + print('module = %s' % (self.module)) + print('symfile = "%s"' % (self.symfile)) + print('slide = %i (0x%x)' % (self.slide, self.slide)) + + def __str__(self): + s = '' + if self.uuid: + s += "%s " % (self.get_uuid()) + if self.arch: + s += "%s " % (self.arch) + if self.version: + s += "%s " % (self.version) + resolved_path = self.get_resolved_path() + if resolved_path: + s += "%s " % (resolved_path) + for section_info in self.section_infos: + s += ", %s" % (section_info) + if self.slide is not None: + s += ', slide = 0x%16.16x' % self.slide + return s + + def add_section(self, section): + # print "added '%s' to '%s'" % (section, self.path) + self.section_infos.append(section) + + def get_section_containing_load_addr(self, load_addr): + for section_info in self.section_infos: + if section_info.contains(load_addr): + return section_info + return None + + def get_resolved_path(self): + if self.resolved_path: + return self.resolved_path + elif self.path: + return self.path + return None + + def get_resolved_path_basename(self): + path = self.get_resolved_path() + if path: + return os.path.basename(path) + return None + + def symfile_basename(self): + if self.symfile: + return os.path.basename(self.symfile) + return None + + def has_section_load_info(self): + return self.section_infos or self.slide is not None + + def load_module(self, target): + if self.unavailable: + return None # We already warned that we couldn't find this module, so don't return an error string + # Load this module into "target" using the section infos to + # set the section load addresses + if self.has_section_load_info(): + if target: + if self.module: + if self.section_infos: + num_sections_loaded = 0 + for section_info in self.section_infos: + if section_info.name: + section = self.module.FindSection( + section_info.name) + if section: + error = target.SetSectionLoadAddress( + section, section_info.start_addr) + if error.Success(): + num_sections_loaded += 1 + else: + return 'error: %s' % error.GetCString() + else: + return 'error: unable to find the section named "%s"' % section_info.name + else: + return 'error: unable to find "%s" section in "%s"' % ( + range.name, self.get_resolved_path()) + if num_sections_loaded == 0: + return 'error: no sections were successfully loaded' + else: + err = target.SetModuleLoadAddress( + self.module, self.slide) + if err.Fail(): + return err.GetCString() + return None + else: + return 'error: invalid module' + else: + return 'error: invalid target' + else: + return 'error: no section infos' + + def add_module(self, target): + '''Add the Image described in this object to "target" and load the sections if "load" is True.''' + if target: + # Try and find using UUID only first so that paths need not match + # up + uuid_str = self.get_normalized_uuid_string() + if uuid_str: + self.module = target.AddModule(None, None, uuid_str) + if not self.module: + self.locate_module_and_debug_symbols() + if self.unavailable: + return None + resolved_path = self.get_resolved_path() + self.module = target.AddModule( + resolved_path, str(self.arch), uuid_str, self.symfile) + if not self.module: + return 'error: unable to get module for (%s) "%s"' % ( + self.arch, self.get_resolved_path()) + if self.has_section_load_info(): + return self.load_module(target) + else: + return None # No sections, the module was added to the target, so success + else: + return 'error: invalid target' + + def locate_module_and_debug_symbols(self): + # By default, just use the paths that were supplied in: + # self.path + # self.resolved_path + # self.module + # self.symfile + # Subclasses can inherit from this class and override this function + self.resolved = True + return True + + def get_uuid(self): + if not self.uuid and self.module: + self.uuid = uuid.UUID(self.module.GetUUIDString()) + return self.uuid + + def get_normalized_uuid_string(self): + if self.uuid: + return str(self.uuid).upper() + return None + + def create_target(self): + '''Create a target using the information in this Image object.''' + if self.unavailable: + return None + + if self.locate_module_and_debug_symbols(): + resolved_path = self.get_resolved_path() + path_spec = lldb.SBFileSpec(resolved_path) + error = lldb.SBError() + target = lldb.debugger.CreateTarget( + resolved_path, self.arch, None, False, error) + if target: + self.module = target.FindModule(path_spec) + if self.has_section_load_info(): + err = self.load_module(target) + if err: + print('ERROR: ', err) + return target + else: + print('error: unable to create a valid target for (%s) "%s"' % (self.arch, self.path)) + else: + print('error: unable to locate main executable (%s) "%s"' % (self.arch, self.path)) + return None + + +class Symbolicator: + + def __init__(self): + """A class the represents the information needed to symbolicate addresses in a program""" + self.target = None + self.images = list() # a list of images to be used when symbolicating + self.addr_mask = 0xffffffffffffffff + + @classmethod + def InitWithSBTarget(cls, target): + obj = cls() + obj.target = target + obj.images = list() + triple = target.triple + if triple: + arch = triple.split('-')[0] + if "arm" in arch: + obj.addr_mask = 0xfffffffffffffffe + + for module in target.modules: + image = Image.InitWithSBTargetAndSBModule(target, module) + obj.images.append(image) + return obj + + def __str__(self): + s = "Symbolicator:\n" + if self.target: + s += "Target = '%s'\n" % (self.target) + s += "Target modules:\n" + for m in self.target.modules: + s += str(m) + "\n" + s += "Images:\n" + for image in self.images: + s += ' %s\n' % (image) + return s + + def find_images_with_identifier(self, identifier): + images = list() + for image in self.images: + if image.identifier == identifier: + images.append(image) + if len(images) == 0: + regex_text = '^.*\.%s$' % (re.escape(identifier)) + regex = re.compile(regex_text) + for image in self.images: + if regex.match(image.identifier): + images.append(image) + return images + + def find_image_containing_load_addr(self, load_addr): + for image in self.images: + if image.get_section_containing_load_addr(load_addr): + return image + return None + + def create_target(self): + if self.target: + return self.target + + if self.images: + for image in self.images: + self.target = image.create_target() + if self.target: + if self.target.GetAddressByteSize() == 4: + triple = self.target.triple + if triple: + arch = triple.split('-')[0] + if "arm" in arch: + self.addr_mask = 0xfffffffffffffffe + return self.target + return None + + def symbolicate(self, load_addr, verbose=False): + if not self.target: + self.create_target() + if self.target: + live_process = False + process = self.target.process + if process: + state = process.state + if state > lldb.eStateUnloaded and state < lldb.eStateDetached: + live_process = True + # If we don't have a live process, we can attempt to find the image + # that a load address belongs to and lazily load its module in the + # target, but we shouldn't do any of this if we have a live process + if not live_process: + image = self.find_image_containing_load_addr(load_addr) + if image: + image.add_module(self.target) + symbolicated_address = Address(self.target, load_addr) + if symbolicated_address.symbolicate(verbose): + if symbolicated_address.so_addr: + symbolicated_addresses = list() + symbolicated_addresses.append(symbolicated_address) + # See if we were able to reconstruct anything? + while True: + inlined_parent_so_addr = lldb.SBAddress() + inlined_parent_sym_ctx = symbolicated_address.sym_ctx.GetParentOfInlinedScope( + symbolicated_address.so_addr, inlined_parent_so_addr) + if not inlined_parent_sym_ctx: + break + if not inlined_parent_so_addr: + break + + symbolicated_address = Address( + self.target, inlined_parent_so_addr.GetLoadAddress( + self.target)) + symbolicated_address.sym_ctx = inlined_parent_sym_ctx + symbolicated_address.so_addr = inlined_parent_so_addr + symbolicated_address.symbolicate(verbose) + + # push the new frame onto the new frame stack + symbolicated_addresses.append(symbolicated_address) + + if symbolicated_addresses: + return symbolicated_addresses + else: + print('error: no target in Symbolicator') + return None + + +def disassemble_instructions( + target, + instructions, + pc, + insts_before_pc, + insts_after_pc, + non_zeroeth_frame): + lines = list() + pc_index = -1 + comment_column = 50 + for inst_idx, inst in enumerate(instructions): + inst_pc = inst.GetAddress().GetLoadAddress(target) + if pc == inst_pc: + pc_index = inst_idx + mnemonic = inst.GetMnemonic(target) + operands = inst.GetOperands(target) + comment = inst.GetComment(target) + lines.append("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands)) + if comment: + line_len = len(lines[-1]) + if line_len < comment_column: + lines[-1] += ' ' * (comment_column - line_len) + lines[-1] += "; %s" % comment + + if pc_index >= 0: + # If we are disassembling the non-zeroeth frame, we need to backup the + # PC by 1 + if non_zeroeth_frame and pc_index > 0: + pc_index = pc_index - 1 + if insts_before_pc == -1: + start_idx = 0 + else: + start_idx = pc_index - insts_before_pc + if start_idx < 0: + start_idx = 0 + if insts_before_pc == -1: + end_idx = inst_idx + else: + end_idx = pc_index + insts_after_pc + if end_idx > inst_idx: + end_idx = inst_idx + for i in range(start_idx, end_idx + 1): + if i == pc_index: + print(' -> ', lines[i]) + else: + print(' ', lines[i]) + + +def print_module_section_data(section): + print(section) + section_data = section.GetSectionData() + if section_data: + ostream = lldb.SBStream() + section_data.GetDescription(ostream, section.GetFileAddress()) + print(ostream.GetData()) + + +def print_module_section(section, depth): + print(section) + if depth > 0: + num_sub_sections = section.GetNumSubSections() + for sect_idx in range(num_sub_sections): + print_module_section( + section.GetSubSectionAtIndex(sect_idx), depth - 1) + + +def print_module_sections(module, depth): + for sect in module.section_iter(): + print_module_section(sect, depth) + + +def print_module_symbols(module): + for sym in module: + print(sym) + + +def Symbolicate(command_args): + + usage = "usage: %prog [options] <addr1> [addr2 ...]" + description = '''Symbolicate one or more addresses using LLDB's python scripting API..''' + parser = optparse.OptionParser( + description=description, + prog='crashlog.py', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + parser.add_option( + '-p', + '--platform', + type='string', + metavar='platform', + dest='platform', + help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + parser.add_option( + '-f', + '--file', + type='string', + metavar='file', + dest='file', + help='Specify a file to use when symbolicating') + parser.add_option( + '-a', + '--arch', + type='string', + metavar='arch', + dest='arch', + help='Specify a architecture to use when symbolicating') + parser.add_option( + '-s', + '--slide', + type='int', + metavar='slide', + dest='slide', + help='Specify the slide to use on the file specified with the --file option', + default=None) + parser.add_option( + '--section', + type='string', + action='append', + dest='section_strings', + help='specify <sect-name>=<start-addr> or <sect-name>=<start-addr>-<end-addr>') + try: + (options, args) = parser.parse_args(command_args) + except: + return + symbolicator = Symbolicator() + images = list() + if options.file: + image = Image(options.file) + image.arch = options.arch + # Add any sections that were specified with one or more --section + # options + if options.section_strings: + for section_str in options.section_strings: + section = Section() + if section.set_from_string(section_str): + image.add_section(section) + else: + sys.exit(1) + if options.slide is not None: + image.slide = options.slide + symbolicator.images.append(image) + + target = symbolicator.create_target() + if options.verbose: + print(symbolicator) + if target: + for addr_str in args: + addr = int(addr_str, 0) + symbolicated_addrs = symbolicator.symbolicate( + addr, options.verbose) + for symbolicated_addr in symbolicated_addrs: + print(symbolicated_addr) + print() + else: + print('error: no target for %s' % (symbolicator)) + +if __name__ == '__main__': + # Create a new debugger instance + lldb.debugger = lldb.SBDebugger.Create() + Symbolicate(sys.argv[1:]) diff --git a/gnu/llvm/lldb/examples/python/types.py b/gnu/llvm/lldb/examples/python/types.py new file mode 100755 index 00000000000..a401e373f48 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/types.py @@ -0,0 +1,357 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +#---------------------------------------------------------------------- + +from __future__ import print_function + +import platform +import os +import re +import signal +import sys +import subprocess + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True) + if xcode_dir: + lldb_python_dirs.append( + os.path.realpath( + xcode_dir + + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append( + xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append( + '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print('imported lldb from: "%s"' % (lldb_python_dir)) + success = True + break + if not success: + print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") + sys.exit(1) + +import optparse +import shlex +import time + + +def regex_option_callback(option, opt_str, value, parser): + if opt_str == "--std": + value = '^std::' + regex = re.compile(value) + parser.values.skip_type_regexes.append(regex) + + +def create_types_options(for_lldb_command): + if for_lldb_command: + usage = "usage: %prog [options]" + description = '''This command will help check for padding in between +base classes and members in structs and classes. It will summarize the types +and how much padding was found. If no types are specified with the --types TYPENAME +option, all structure and class types will be verified. If no modules are +specified with the --module option, only the target's main executable will be +searched. +''' + else: + usage = "usage: %prog [options] EXEPATH [EXEPATH ...]" + description = '''This command will help check for padding in between +base classes and members in structures and classes. It will summarize the types +and how much padding was found. One or more paths to executable files must be +specified and targets will be created with these modules. If no types are +specified with the --types TYPENAME option, all structure and class types will +be verified in all specified modules. +''' + parser = optparse.OptionParser( + description=description, + prog='framestats', + usage=usage) + if not for_lldb_command: + parser.add_option( + '-a', + '--arch', + type='string', + dest='arch', + help='The architecture to use when creating the debug target.', + default=None) + parser.add_option( + '-p', + '--platform', + type='string', + metavar='platform', + dest='platform', + help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + parser.add_option( + '-m', + '--module', + action='append', + type='string', + metavar='MODULE', + dest='modules', + help='Specify one or more modules which will be used to verify the types.', + default=[]) + parser.add_option( + '-d', + '--debug', + action='store_true', + dest='debug', + help='Pause 10 seconds to wait for a debugger to attach.', + default=False) + parser.add_option( + '-t', + '--type', + action='append', + type='string', + metavar='TYPENAME', + dest='typenames', + help='Specify one or more type names which should be verified. If no type names are specified, all class and struct types will be verified.', + default=[]) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='Enable verbose logging and information.', + default=False) + parser.add_option( + '-s', + '--skip-type-regex', + action="callback", + callback=regex_option_callback, + type='string', + metavar='REGEX', + dest='skip_type_regexes', + help='Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.', + default=[]) + parser.add_option( + '--std', + action="callback", + callback=regex_option_callback, + metavar='REGEX', + dest='skip_type_regexes', + help="Don't' recurse into types in the std namespace.", + default=[]) + return parser + + +def verify_type(target, options, type): + print(type) + typename = type.GetName() + # print 'type: %s' % (typename) + (end_offset, padding) = verify_type_recursive( + target, options, type, None, 0, 0, 0) + byte_size = type.GetByteSize() + # if end_offset < byte_size: + # last_member_padding = byte_size - end_offset + # print '%+4u <%u> padding' % (end_offset, last_member_padding) + # padding += last_member_padding + print('Total byte size: %u' % (byte_size)) + print('Total pad bytes: %u' % (padding)) + if padding > 0: + print('Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0)) + print() + + +def verify_type_recursive( + target, + options, + type, + member_name, + depth, + base_offset, + padding): + prev_end_offset = base_offset + typename = type.GetName() + byte_size = type.GetByteSize() + if member_name and member_name != typename: + print('%+4u <%3u> %s%s %s;' % (base_offset, byte_size, ' ' * depth, typename, member_name)) + else: + print('%+4u {%3u} %s%s' % (base_offset, byte_size, ' ' * depth, typename)) + + for type_regex in options.skip_type_regexes: + match = type_regex.match(typename) + if match: + return (base_offset + byte_size, padding) + + members = type.members + if members: + for member_idx, member in enumerate(members): + member_type = member.GetType() + member_canonical_type = member_type.GetCanonicalType() + member_type_class = member_canonical_type.GetTypeClass() + member_name = member.GetName() + member_offset = member.GetOffsetInBytes() + member_total_offset = member_offset + base_offset + member_byte_size = member_type.GetByteSize() + member_is_class_or_struct = False + if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass: + member_is_class_or_struct = True + if member_idx == 0 and member_offset == target.GetAddressByteSize( + ) and type.IsPolymorphicClass(): + ptr_size = target.GetAddressByteSize() + print('%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))) + prev_end_offset = ptr_size + else: + if prev_end_offset < member_total_offset: + member_padding = member_total_offset - prev_end_offset + padding = padding + member_padding + print('%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1))) + + if member_is_class_or_struct: + (prev_end_offset, + padding) = verify_type_recursive(target, + options, + member_canonical_type, + member_name, + depth + 1, + member_total_offset, + padding) + else: + prev_end_offset = member_total_offset + member_byte_size + member_typename = member_type.GetName() + if member.IsBitfield(): + print('%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member.GetBitfieldSizeInBits(), member_name)) + else: + print('%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member_name)) + + if prev_end_offset < byte_size: + last_member_padding = byte_size - prev_end_offset + print('%+4u <%3u> %s<PADDING>' % (prev_end_offset, last_member_padding, ' ' * (depth + 1))) + padding += last_member_padding + else: + if type.IsPolymorphicClass(): + ptr_size = target.GetAddressByteSize() + print('%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))) + prev_end_offset = ptr_size + prev_end_offset = base_offset + byte_size + + return (prev_end_offset, padding) + + +def check_padding_command(debugger, command, result, dict): + # Use the Shell Lexer to properly parse up command options just like a + # shell would + command_args = shlex.split(command) + parser = create_types_options(True) + try: + (options, args) = parser.parse_args(command_args) + except: + # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + result.SetStatus(lldb.eReturnStatusFailed) + # returning a string is the same as returning an error whose + # description is the string + return "option parsing failed" + verify_types(debugger.GetSelectedTarget(), options) + + +@lldb.command("parse_all_struct_class_types") +def parse_all_struct_class_types(debugger, command, result, dict): + command_args = shlex.split(command) + for f in command_args: + error = lldb.SBError() + target = debugger.CreateTarget(f, None, None, False, error) + module = target.GetModuleAtIndex(0) + print("Parsing all types in '%s'" % (module)) + types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) + for t in types: + print(t) + print("") + + +def verify_types(target, options): + + if not target: + print('error: invalid target') + return + + modules = list() + if len(options.modules) == 0: + # Append just the main executable if nothing was specified + module = target.modules[0] + if module: + modules.append(module) + else: + for module_name in options.modules: + module = lldb.target.module[module_name] + if module: + modules.append(module) + + if modules: + for module in modules: + print('module: %s' % (module.file)) + if options.typenames: + for typename in options.typenames: + types = module.FindTypes(typename) + if types.GetSize(): + print('Found %u types matching "%s" in "%s"' % (len(types), typename, module.file)) + for type in types: + verify_type(target, options, type) + else: + print('error: no type matches "%s" in "%s"' % (typename, module.file)) + else: + types = module.GetTypes( + lldb.eTypeClassClass | lldb.eTypeClassStruct) + print('Found %u types in "%s"' % (len(types), module.file)) + for type in types: + verify_type(target, options, type) + else: + print('error: no modules') + +if __name__ == '__main__': + debugger = lldb.SBDebugger.Create() + parser = create_types_options(False) + + # try: + (options, args) = parser.parse_args(sys.argv[1:]) + # except: + # print "error: option parsing failed" + # sys.exit(1) + + if options.debug: + print("Waiting for debugger to attach to process %d" % os.getpid()) + os.kill(os.getpid(), signal.SIGSTOP) + + for path in args: + # in a command - the lldb.* convenience variables are not to be used + # and their values (if any) are undefined + # this is the best practice to access those objects from within a + # command + error = lldb.SBError() + target = debugger.CreateTarget(path, + options.arch, + options.platform, + True, + error) + if error.Fail(): + print(error.GetCString()) + continue + verify_types(target, options) + +elif getattr(lldb, 'debugger', None): + lldb.debugger.HandleCommand( + 'command script add -f types.check_padding_command check_padding') + print('"check_padding" command installed, use the "--help" option for detailed help') diff --git a/gnu/llvm/lldb/examples/python/x86_64_linux_target_definition.py b/gnu/llvm/lldb/examples/python/x86_64_linux_target_definition.py new file mode 100644 index 00000000000..13bde540f7e --- /dev/null +++ b/gnu/llvm/lldb/examples/python/x86_64_linux_target_definition.py @@ -0,0 +1,774 @@ +#!/usr/bin/python +#===-- x86_64_linux_target_definition.py -----------------------------*- C++ -*-===// +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the GDB server +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the darwin version of +# GDB and allows you to connect to servers that use this register set. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_linux_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB server. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax': 0, + 'rdx': 1, + 'rcx': 2, + 'rbx': 3, + 'rsi': 4, + 'rdi': 5, + 'rbp': 6, + 'rsp': 7, + 'r8': 8, + 'r9': 9, + 'r10': 10, + 'r11': 11, + 'r12': 12, + 'r13': 13, + 'r14': 14, + 'r15': 15, + 'rip': 16, + 'xmm0': 17, + 'xmm1': 18, + 'xmm2': 19, + 'xmm3': 20, + 'xmm4': 21, + 'xmm5': 22, + 'xmm6': 23, + 'xmm7': 24, + 'xmm8': 25, + 'xmm9': 26, + 'xmm10': 27, + 'xmm11': 28, + 'xmm12': 29, + 'xmm13': 30, + 'xmm14': 31, + 'xmm15': 32, + 'stmm0': 33, + 'stmm1': 34, + 'stmm2': 35, + 'stmm3': 36, + 'stmm4': 37, + 'stmm5': 38, + 'stmm6': 39, + 'stmm7': 30, + 'ymm0': 41, + 'ymm1': 42, + 'ymm2': 43, + 'ymm3': 44, + 'ymm4': 45, + 'ymm5': 46, + 'ymm6': 47, + 'ymm7': 48, + 'ymm8': 49, + 'ymm9': 40, + 'ymm10': 41, + 'ymm11': 42, + 'ymm12': 43, + 'ymm13': 44, + 'ymm14': 45, + 'ymm15': 46 +} + +name_to_gdb_regnum = { + 'rax': 0, + 'rbx': 1, + 'rcx': 2, + 'rdx': 3, + 'rsi': 4, + 'rdi': 5, + 'rbp': 6, + 'rsp': 7, + 'r8': 8, + 'r9': 9, + 'r10': 10, + 'r11': 11, + 'r12': 12, + 'r13': 13, + 'r14': 14, + 'r15': 15, + 'rip': 16, + 'rflags': 17, + 'cs': 18, + 'ss': 19, + 'ds': 20, + 'es': 21, + 'fs': 22, + 'gs': 23, + 'stmm0': 24, + 'stmm1': 25, + 'stmm2': 26, + 'stmm3': 27, + 'stmm4': 28, + 'stmm5': 29, + 'stmm6': 30, + 'stmm7': 31, + 'fctrl': 32, + 'fstat': 33, + 'ftag': 34, + 'fiseg': 35, + 'fioff': 36, + 'foseg': 37, + 'fooff': 38, + 'fop': 39, + 'xmm0': 40, + 'xmm1': 41, + 'xmm2': 42, + 'xmm3': 43, + 'xmm4': 44, + 'xmm5': 45, + 'xmm6': 46, + 'xmm7': 47, + 'xmm8': 48, + 'xmm9': 49, + 'xmm10': 50, + 'xmm11': 51, + 'xmm12': 52, + 'xmm13': 53, + 'xmm14': 54, + 'xmm15': 55, + 'mxcsr': 56, + 'ymm0': 57, + 'ymm1': 58, + 'ymm2': 59, + 'ymm3': 60, + 'ymm4': 61, + 'ymm5': 62, + 'ymm6': 63, + 'ymm7': 64, + 'ymm8': 65, + 'ymm9': 66, + 'ymm10': 67, + 'ymm11': 68, + 'ymm12': 69, + 'ymm13': 70, + 'ymm14': 71, + 'ymm15': 72 +} + +name_to_generic_regnum = { + 'rip': LLDB_REGNUM_GENERIC_PC, + 'rsp': LLDB_REGNUM_GENERIC_SP, + 'rbp': LLDB_REGNUM_GENERIC_FP, + 'rdi': LLDB_REGNUM_GENERIC_ARG1, + 'rsi': LLDB_REGNUM_GENERIC_ARG2, + 'rdx': LLDB_REGNUM_GENERIC_ARG3, + 'rcx': LLDB_REGNUM_GENERIC_ARG4, + 'r8': LLDB_REGNUM_GENERIC_ARG5, + 'r9': LLDB_REGNUM_GENERIC_ARG6 +} + + +def get_reg_num(reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ + {'name': 'rax', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rbx', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rcx', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg4'}, + {'name': 'rdx', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg3'}, + {'name': 'rsi', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg2'}, + {'name': 'rdi', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg1'}, + {'name': 'rbp', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'fp'}, + {'name': 'rsp', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'sp'}, + {'name': 'r8', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg5'}, + {'name': 'r9', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg6'}, + {'name': 'r10', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r11', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r12', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r13', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r14', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r15', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rip', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'pc'}, + {'name': 'rflags', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'cs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ss', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ds', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'es', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'gs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'stmm0', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm1', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm2', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm3', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm4', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm5', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm6', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm7', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'fctrl', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fstat', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ftag', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fiseg', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fioff', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'foseg', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fooff', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fop', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'xmm0', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm1', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm2', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm3', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm4', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm5', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm6', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm7', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm8', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm9', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm10', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm11', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm12', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm13', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm14', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm15', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'mxcsr', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'orig_rax', 'set': 1, 'bitsize': 64, + 'encoding': eEncodingUint, 'format': eFormatHex}, + # Registers that are contained in or composed of one of more other + # registers + {'name': 'eax', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[31:0]'}, + {'name': 'ebx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[31:0]'}, + {'name': 'ecx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[31:0]'}, + {'name': 'edx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[31:0]'}, + {'name': 'edi', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[31:0]'}, + {'name': 'esi', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[31:0]'}, + {'name': 'ebp', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[31:0]'}, + {'name': 'esp', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[31:0]'}, + {'name': 'r8d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[31:0]'}, + {'name': 'r9d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[31:0]'}, + {'name': 'r10d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[31:0]'}, + {'name': 'r11d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[31:0]'}, + {'name': 'r12d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[31:0]'}, + {'name': 'r13d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[31:0]'}, + {'name': 'r14d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[31:0]'}, + {'name': 'r15d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[31:0]'}, + + {'name': 'ax', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[15:0]'}, + {'name': 'bx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[15:0]'}, + {'name': 'cx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[15:0]'}, + {'name': 'dx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[15:0]'}, + {'name': 'di', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[15:0]'}, + {'name': 'si', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[15:0]'}, + {'name': 'bp', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[15:0]'}, + {'name': 'sp', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[15:0]'}, + {'name': 'r8w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[15:0]'}, + {'name': 'r9w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[15:0]'}, + {'name': 'r10w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[15:0]'}, + {'name': 'r11w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[15:0]'}, + {'name': 'r12w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[15:0]'}, + {'name': 'r13w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[15:0]'}, + {'name': 'r14w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[15:0]'}, + {'name': 'r15w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[15:0]'}, + + {'name': 'ah', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[15:8]'}, + {'name': 'bh', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[15:8]'}, + {'name': 'ch', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[15:8]'}, + {'name': 'dh', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[15:8]'}, + + {'name': 'al', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[7:0]'}, + {'name': 'bl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[7:0]'}, + {'name': 'cl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[7:0]'}, + {'name': 'dl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[7:0]'}, + {'name': 'dil', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[7:0]'}, + {'name': 'sil', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[7:0]'}, + {'name': 'bpl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[7:0]'}, + {'name': 'spl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[7:0]'}, + {'name': 'r8l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[7:0]'}, + {'name': 'r9l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[7:0]'}, + {'name': 'r10l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[7:0]'}, + {'name': 'r11l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[7:0]'}, + {'name': 'r12l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[7:0]'}, + {'name': 'r13l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[7:0]'}, + {'name': 'r14l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[7:0]'}, + {'name': 'r15l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[7:0]'}, +] + +g_target_definition = None + + +def get_target_definition(): + global g_target_definition + if g_target_definition is None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register + # info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize'] // 8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = [ + 'General Purpose Registers', + 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition[ + 'host-info'] = {'triple': 'x86_64-*-linux', 'endian': eByteOrderLittle} + g_target_definition['g-packet-size'] = offset + g_target_definition['breakpoint-pc-offset'] = -1 + return g_target_definition + + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition() diff --git a/gnu/llvm/lldb/examples/python/x86_64_qemu_target_definition.py b/gnu/llvm/lldb/examples/python/x86_64_qemu_target_definition.py new file mode 100644 index 00000000000..aa081c16117 --- /dev/null +++ b/gnu/llvm/lldb/examples/python/x86_64_qemu_target_definition.py @@ -0,0 +1,772 @@ +#!/usr/bin/python +#===-- x86_64_qemu_target_definition.py -----------------------------*- C++ -*-===// +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the remote stub +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the user mode qemu on linux. +# The only difference with the Linux file is the absense of orig_rax register. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_qemu_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB stub. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax': 0, + 'rdx': 1, + 'rcx': 2, + 'rbx': 3, + 'rsi': 4, + 'rdi': 5, + 'rbp': 6, + 'rsp': 7, + 'r8': 8, + 'r9': 9, + 'r10': 10, + 'r11': 11, + 'r12': 12, + 'r13': 13, + 'r14': 14, + 'r15': 15, + 'rip': 16, + 'xmm0': 17, + 'xmm1': 18, + 'xmm2': 19, + 'xmm3': 20, + 'xmm4': 21, + 'xmm5': 22, + 'xmm6': 23, + 'xmm7': 24, + 'xmm8': 25, + 'xmm9': 26, + 'xmm10': 27, + 'xmm11': 28, + 'xmm12': 29, + 'xmm13': 30, + 'xmm14': 31, + 'xmm15': 32, + 'stmm0': 33, + 'stmm1': 34, + 'stmm2': 35, + 'stmm3': 36, + 'stmm4': 37, + 'stmm5': 38, + 'stmm6': 39, + 'stmm7': 30, + 'ymm0': 41, + 'ymm1': 42, + 'ymm2': 43, + 'ymm3': 44, + 'ymm4': 45, + 'ymm5': 46, + 'ymm6': 47, + 'ymm7': 48, + 'ymm8': 49, + 'ymm9': 40, + 'ymm10': 41, + 'ymm11': 42, + 'ymm12': 43, + 'ymm13': 44, + 'ymm14': 45, + 'ymm15': 46 +} + +name_to_gdb_regnum = { + 'rax': 0, + 'rbx': 1, + 'rcx': 2, + 'rdx': 3, + 'rsi': 4, + 'rdi': 5, + 'rbp': 6, + 'rsp': 7, + 'r8': 8, + 'r9': 9, + 'r10': 10, + 'r11': 11, + 'r12': 12, + 'r13': 13, + 'r14': 14, + 'r15': 15, + 'rip': 16, + 'rflags': 17, + 'cs': 18, + 'ss': 19, + 'ds': 20, + 'es': 21, + 'fs': 22, + 'gs': 23, + 'stmm0': 24, + 'stmm1': 25, + 'stmm2': 26, + 'stmm3': 27, + 'stmm4': 28, + 'stmm5': 29, + 'stmm6': 30, + 'stmm7': 31, + 'fctrl': 32, + 'fstat': 33, + 'ftag': 34, + 'fiseg': 35, + 'fioff': 36, + 'foseg': 37, + 'fooff': 38, + 'fop': 39, + 'xmm0': 40, + 'xmm1': 41, + 'xmm2': 42, + 'xmm3': 43, + 'xmm4': 44, + 'xmm5': 45, + 'xmm6': 46, + 'xmm7': 47, + 'xmm8': 48, + 'xmm9': 49, + 'xmm10': 50, + 'xmm11': 51, + 'xmm12': 52, + 'xmm13': 53, + 'xmm14': 54, + 'xmm15': 55, + 'mxcsr': 56, + 'ymm0': 57, + 'ymm1': 58, + 'ymm2': 59, + 'ymm3': 60, + 'ymm4': 61, + 'ymm5': 62, + 'ymm6': 63, + 'ymm7': 64, + 'ymm8': 65, + 'ymm9': 66, + 'ymm10': 67, + 'ymm11': 68, + 'ymm12': 69, + 'ymm13': 70, + 'ymm14': 71, + 'ymm15': 72 +} + +name_to_generic_regnum = { + 'rip': LLDB_REGNUM_GENERIC_PC, + 'rsp': LLDB_REGNUM_GENERIC_SP, + 'rbp': LLDB_REGNUM_GENERIC_FP, + 'rdi': LLDB_REGNUM_GENERIC_ARG1, + 'rsi': LLDB_REGNUM_GENERIC_ARG2, + 'rdx': LLDB_REGNUM_GENERIC_ARG3, + 'rcx': LLDB_REGNUM_GENERIC_ARG4, + 'r8': LLDB_REGNUM_GENERIC_ARG5, + 'r9': LLDB_REGNUM_GENERIC_ARG6 +} + + +def get_reg_num(reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ + {'name': 'rax', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rbx', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rcx', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg4'}, + {'name': 'rdx', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg3'}, + {'name': 'rsi', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg2'}, + {'name': 'rdi', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg1'}, + {'name': 'rbp', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'fp'}, + {'name': 'rsp', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'sp'}, + {'name': 'r8', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg5'}, + {'name': 'r9', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg6'}, + {'name': 'r10', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r11', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r12', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r13', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r14', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r15', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rip', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'pc'}, + {'name': 'rflags', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'cs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ss', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ds', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'es', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'gs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'stmm0', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm1', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm2', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm3', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm4', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm5', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm6', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm7', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'fctrl', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fstat', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ftag', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fiseg', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fioff', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'foseg', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fooff', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fop', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'xmm0', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm1', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm2', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm3', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm4', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm5', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm6', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm7', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm8', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm9', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm10', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm11', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm12', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm13', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm14', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm15', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'mxcsr', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + # Registers that are contained in or composed of one of more other + # registers + {'name': 'eax', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[31:0]'}, + {'name': 'ebx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[31:0]'}, + {'name': 'ecx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[31:0]'}, + {'name': 'edx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[31:0]'}, + {'name': 'edi', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[31:0]'}, + {'name': 'esi', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[31:0]'}, + {'name': 'ebp', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[31:0]'}, + {'name': 'esp', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[31:0]'}, + {'name': 'r8d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[31:0]'}, + {'name': 'r9d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[31:0]'}, + {'name': 'r10d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[31:0]'}, + {'name': 'r11d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[31:0]'}, + {'name': 'r12d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[31:0]'}, + {'name': 'r13d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[31:0]'}, + {'name': 'r14d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[31:0]'}, + {'name': 'r15d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[31:0]'}, + + {'name': 'ax', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[15:0]'}, + {'name': 'bx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[15:0]'}, + {'name': 'cx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[15:0]'}, + {'name': 'dx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[15:0]'}, + {'name': 'di', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[15:0]'}, + {'name': 'si', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[15:0]'}, + {'name': 'bp', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[15:0]'}, + {'name': 'sp', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[15:0]'}, + {'name': 'r8w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[15:0]'}, + {'name': 'r9w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[15:0]'}, + {'name': 'r10w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[15:0]'}, + {'name': 'r11w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[15:0]'}, + {'name': 'r12w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[15:0]'}, + {'name': 'r13w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[15:0]'}, + {'name': 'r14w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[15:0]'}, + {'name': 'r15w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[15:0]'}, + + {'name': 'ah', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[15:8]'}, + {'name': 'bh', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[15:8]'}, + {'name': 'ch', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[15:8]'}, + {'name': 'dh', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[15:8]'}, + + {'name': 'al', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[7:0]'}, + {'name': 'bl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[7:0]'}, + {'name': 'cl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[7:0]'}, + {'name': 'dl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[7:0]'}, + {'name': 'dil', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[7:0]'}, + {'name': 'sil', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[7:0]'}, + {'name': 'bpl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[7:0]'}, + {'name': 'spl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[7:0]'}, + {'name': 'r8l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[7:0]'}, + {'name': 'r9l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[7:0]'}, + {'name': 'r10l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[7:0]'}, + {'name': 'r11l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[7:0]'}, + {'name': 'r12l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[7:0]'}, + {'name': 'r13l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[7:0]'}, + {'name': 'r14l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[7:0]'}, + {'name': 'r15l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[7:0]'}, +] + +g_target_definition = None + + +def get_target_definition(): + global g_target_definition + if g_target_definition is None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register + # info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize'] // 8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = [ + 'General Purpose Registers', + 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition[ + 'host-info'] = {'triple': 'x86_64-*-linux', 'endian': eByteOrderLittle} + g_target_definition['g-packet-size'] = offset + g_target_definition['breakpoint-pc-offset'] = -1 + return g_target_definition + + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition() diff --git a/gnu/llvm/lldb/examples/python/x86_64_target_definition.py b/gnu/llvm/lldb/examples/python/x86_64_target_definition.py new file mode 100644 index 00000000000..3f7f60dec9b --- /dev/null +++ b/gnu/llvm/lldb/examples/python/x86_64_target_definition.py @@ -0,0 +1,777 @@ +#!/usr/bin/python +#===-- x86_64_target_definition.py -----------------------------*- C++ -*-===// +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the GDB server +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the darwin version of +# GDB and allows you to connect to servers that use this register set. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB server. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax': 0, + 'rdx': 1, + 'rcx': 2, + 'rbx': 3, + 'rsi': 4, + 'rdi': 5, + 'rbp': 6, + 'rsp': 7, + 'r8': 8, + 'r9': 9, + 'r10': 10, + 'r11': 11, + 'r12': 12, + 'r13': 13, + 'r14': 14, + 'r15': 15, + 'rip': 16, + 'xmm0': 17, + 'xmm1': 18, + 'xmm2': 19, + 'xmm3': 20, + 'xmm4': 21, + 'xmm5': 22, + 'xmm6': 23, + 'xmm7': 24, + 'xmm8': 25, + 'xmm9': 26, + 'xmm10': 27, + 'xmm11': 28, + 'xmm12': 29, + 'xmm13': 30, + 'xmm14': 31, + 'xmm15': 32, + 'stmm0': 33, + 'stmm1': 34, + 'stmm2': 35, + 'stmm3': 36, + 'stmm4': 37, + 'stmm5': 38, + 'stmm6': 39, + 'stmm7': 30, + 'ymm0': 41, + 'ymm1': 42, + 'ymm2': 43, + 'ymm3': 44, + 'ymm4': 45, + 'ymm5': 46, + 'ymm6': 47, + 'ymm7': 48, + 'ymm8': 49, + 'ymm9': 40, + 'ymm10': 41, + 'ymm11': 42, + 'ymm12': 43, + 'ymm13': 44, + 'ymm14': 45, + 'ymm15': 46 +} + +name_to_gdb_regnum = { + 'rax': 0, + 'rbx': 1, + 'rcx': 2, + 'rdx': 3, + 'rsi': 4, + 'rdi': 5, + 'rbp': 6, + 'rsp': 7, + 'r8': 8, + 'r9': 9, + 'r10': 10, + 'r11': 11, + 'r12': 12, + 'r13': 13, + 'r14': 14, + 'r15': 15, + 'rip': 16, + 'rflags': 17, + 'cs': 18, + 'ss': 19, + 'ds': 20, + 'es': 21, + 'fs': 22, + 'gs': 23, + 'stmm0': 24, + 'stmm1': 25, + 'stmm2': 26, + 'stmm3': 27, + 'stmm4': 28, + 'stmm5': 29, + 'stmm6': 30, + 'stmm7': 31, + 'fctrl': 32, + 'fstat': 33, + 'ftag': 34, + 'fiseg': 35, + 'fioff': 36, + 'foseg': 37, + 'fooff': 38, + 'fop': 39, + 'xmm0': 40, + 'xmm1': 41, + 'xmm2': 42, + 'xmm3': 43, + 'xmm4': 44, + 'xmm5': 45, + 'xmm6': 46, + 'xmm7': 47, + 'xmm8': 48, + 'xmm9': 49, + 'xmm10': 50, + 'xmm11': 51, + 'xmm12': 52, + 'xmm13': 53, + 'xmm14': 54, + 'xmm15': 55, + 'mxcsr': 56, + 'ymm0': 57, + 'ymm1': 58, + 'ymm2': 59, + 'ymm3': 60, + 'ymm4': 61, + 'ymm5': 62, + 'ymm6': 63, + 'ymm7': 64, + 'ymm8': 65, + 'ymm9': 66, + 'ymm10': 67, + 'ymm11': 68, + 'ymm12': 69, + 'ymm13': 70, + 'ymm14': 71, + 'ymm15': 72 +} + +name_to_generic_regnum = { + 'rip': LLDB_REGNUM_GENERIC_PC, + 'rsp': LLDB_REGNUM_GENERIC_SP, + 'rbp': LLDB_REGNUM_GENERIC_FP, + 'rdi': LLDB_REGNUM_GENERIC_ARG1, + 'rsi': LLDB_REGNUM_GENERIC_ARG2, + 'rdx': LLDB_REGNUM_GENERIC_ARG3, + 'rcx': LLDB_REGNUM_GENERIC_ARG4, + 'r8': LLDB_REGNUM_GENERIC_ARG5, + 'r9': LLDB_REGNUM_GENERIC_ARG6 +} + + +def get_reg_num(reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + + +def get_reg_num(reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ + {'name': 'rax', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rbx', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rcx', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg4'}, + {'name': 'rdx', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg3'}, + {'name': 'rsi', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg2'}, + {'name': 'rdi', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg1'}, + {'name': 'rbp', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'fp'}, + {'name': 'rsp', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'sp'}, + {'name': 'r8', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg5'}, + {'name': 'r9', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'arg6'}, + {'name': 'r10', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r11', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r12', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r13', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r14', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'r15', + 'set': 0, + 'bitsize': 64, + 'encoding': eEncodingUint, + 'format': eFormatAddressInfo}, + {'name': 'rip', 'set': 0, 'bitsize': 64, 'encoding': eEncodingUint, + 'format': eFormatAddressInfo, 'alt-name': 'pc'}, + {'name': 'rflags', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'cs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ss', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ds', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'es', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'gs', 'set': 0, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'stmm0', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm1', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm2', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm3', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm4', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm5', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm6', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'stmm7', + 'set': 1, + 'bitsize': 80, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'fctrl', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fstat', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'ftag', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fiseg', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fioff', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'foseg', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fooff', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'fop', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + {'name': 'xmm0', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm1', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm2', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm3', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm4', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm5', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm6', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm7', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm8', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm9', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm10', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm11', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm12', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm13', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm14', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'xmm15', + 'set': 1, + 'bitsize': 128, + 'encoding': eEncodingVector, + 'format': eFormatVectorOfUInt8}, + {'name': 'mxcsr', 'set': 1, 'bitsize': 32, + 'encoding': eEncodingUint, 'format': eFormatHex}, + # Registers that are contained in or composed of one of more other + # registers + {'name': 'eax', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[31:0]'}, + {'name': 'ebx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[31:0]'}, + {'name': 'ecx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[31:0]'}, + {'name': 'edx', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[31:0]'}, + {'name': 'edi', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[31:0]'}, + {'name': 'esi', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[31:0]'}, + {'name': 'ebp', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[31:0]'}, + {'name': 'esp', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[31:0]'}, + {'name': 'r8d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[31:0]'}, + {'name': 'r9d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[31:0]'}, + {'name': 'r10d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[31:0]'}, + {'name': 'r11d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[31:0]'}, + {'name': 'r12d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[31:0]'}, + {'name': 'r13d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[31:0]'}, + {'name': 'r14d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[31:0]'}, + {'name': 'r15d', + 'set': 0, + 'bitsize': 32, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[31:0]'}, + + {'name': 'ax', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[15:0]'}, + {'name': 'bx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[15:0]'}, + {'name': 'cx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[15:0]'}, + {'name': 'dx', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[15:0]'}, + {'name': 'di', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[15:0]'}, + {'name': 'si', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[15:0]'}, + {'name': 'bp', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[15:0]'}, + {'name': 'sp', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[15:0]'}, + {'name': 'r8w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[15:0]'}, + {'name': 'r9w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[15:0]'}, + {'name': 'r10w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[15:0]'}, + {'name': 'r11w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[15:0]'}, + {'name': 'r12w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[15:0]'}, + {'name': 'r13w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[15:0]'}, + {'name': 'r14w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[15:0]'}, + {'name': 'r15w', + 'set': 0, + 'bitsize': 16, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[15:0]'}, + + {'name': 'ah', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[15:8]'}, + {'name': 'bh', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[15:8]'}, + {'name': 'ch', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[15:8]'}, + {'name': 'dh', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[15:8]'}, + + {'name': 'al', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rax[7:0]'}, + {'name': 'bl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbx[7:0]'}, + {'name': 'cl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rcx[7:0]'}, + {'name': 'dl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdx[7:0]'}, + {'name': 'dil', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rdi[7:0]'}, + {'name': 'sil', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsi[7:0]'}, + {'name': 'bpl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rbp[7:0]'}, + {'name': 'spl', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'rsp[7:0]'}, + {'name': 'r8l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r8[7:0]'}, + {'name': 'r9l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r9[7:0]'}, + {'name': 'r10l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r10[7:0]'}, + {'name': 'r11l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r11[7:0]'}, + {'name': 'r12l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r12[7:0]'}, + {'name': 'r13l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r13[7:0]'}, + {'name': 'r14l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r14[7:0]'}, + {'name': 'r15l', + 'set': 0, + 'bitsize': 8, + 'encoding': eEncodingUint, + 'format': eFormatHex, + 'slice': 'r15[7:0]'}, +] + +g_target_definition = None + + +def get_target_definition(): + global g_target_definition + if g_target_definition is None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register + # info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize'] // 8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = [ + 'General Purpose Registers', + 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition[ + 'host-info'] = {'triple': 'x86_64-apple-macosx', 'endian': eByteOrderLittle} + g_target_definition['g-packet-size'] = offset + return g_target_definition + + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition() |