Ejemplo n.º 1
0
    def __init__(self, args, first, _):
        cmd_parts = self.parse_args(args, [2, 3] if first else [1, 2], ';')
        self.arch = gdb.current_arch()

        if first:
            self.start_address = eval_int(cmd_parts.pop(0))
        end = cmd_parts.pop(0)
        self.end_address = None if end == 'NULL' else eval_int(end)
        self.count = eval_int(cmd_parts.pop(0)) if cmd_parts else None
Ejemplo n.º 2
0
    def update_exact(self, direction, addr):
        '''Add or remove a function from tracing that starts at a given memory
        address instead of whose name matches a regular expression.'''
        func_name, offset = func_and_offset(int(addr, base=0))
        # Checks we don't give an invalid address to remove_addr_trace(), or
        # add_tracer().
        if func_name is None or offset != 0:
            raise ValueError('{} does not start a function'.format(addr))

        if direction == '+':
            arch = gdb.current_arch()
            return add_tracer(helpers.FakeSymbol(func_name, addr), arch)

        remove_addr_trace(int(addr, 0))
Ejemplo n.º 3
0
    def __init__(self, maxdepth, file_regexp, start_expr, unique_funcs):
        self.func_stack = []
        self.arch = gdb.current_arch()
        self.maxdepth = maxdepth
        self.file_regexp = file_regexp
        # User asked for specific files, we only know the filename if there is
        # debugging information, hence ignore all functions that don't have
        # debugging info.
        self.dont_fallback = re.match(self.file_regexp, '') is not None
        self.unique_funcs = unique_funcs
        self.all_seen = set()

        # hypothetical_stack checks for recursion, but also allows the
        # hypothetical-call-stack walker to see what the current stack is.
        type(self).hypothetical_stack = []
        self.start_expr = start_expr
Ejemplo n.º 4
0
    def __init__(self, args, first, _):
        self.cmd_parts = self.parse_args(args, [3,3] if first else [2,2], ';')
        self.maxdepth = eval_int(self.cmd_parts[-1])
        self.file_regex = self.cmd_parts[-2].strip()
        # User asked for specific files, wo only know the filename if there is
        # debugging information, hence ignore all functions that don't have
        # debugging info.
        self.dont_fallback = re.match(self.file_regex, '') is not None

        self.func_stack = []
        # hypothetical_stack checks for recursion, but also allows the
        # hypothetical-call-stack walker to see what the current stack is.
        type(self).hypothetical_stack = []
        if first:
            self.__add_addr(eval_int(self.cmd_parts[0]), 0)
        self.arch = gdb.current_arch()
Ejemplo n.º 5
0
def trace_regexp(regexp):
    'Trace all functions matching `regexp` in the current symbol table.'
    file_regex, func_regex = file_func_split(regexp)
    if file_regex is None:
        file_regex = '.*' if gdb.parameter('call-graph-nondebug') else '.+'

    arch = gdb.current_arch()
    # We have to break on address to distinguish symbols with the same name in
    # different files.
    #
    # In order to have nice output, we create a string that describes the
    # function for a human -- though symbols with the same name will have the
    # same output for entry tracepoints.
    for symbol in gdb.search_symbols(func_regex, file_regex,
                                     gdb.parameter('call-graph-dynlibs')):
        add_tracer(symbol, arch)
Ejemplo n.º 6
0
 def __init__(self, start_expr, end_expr, count_expr):
     self.arch = gdb.current_arch()
     self.start_expr = start_expr
     self.end_expr = end_expr
     self.count_expr = count_expr
Ejemplo n.º 7
0
def function_disassembly(func_addr, arch=None, use_fallback=True):
    '''Return the disassembly and filename of the function at `func_addr`.

    If there are no debugging symbols, return the None as the filename.
        (function_disassembly, function_name or None, function_filename or None)

    If we have debugging information, but we can't find a function at the
    address specified, we return '' for the function_name and
    function_filename.

    If `use_fallback` is set to False, and there is are no debugging symbols
    for the current function, return (None, None, None).

    '''
    arch = arch or gdb.current_arch()

    try:
        func_block = gdb.block_for_pc(func_addr)

        # Bit of a hack, but I want the same thing to happen when gdb can't
        # find the object file (and hence raises an exception itself) as when
        # it just can't find the block.
        if func_block is None:
            raise RuntimeError('Cannot locate object file for block.')
    except RuntimeError as e:
        if e.args != ('Cannot locate object file for block.', ):
            raise e
    else:
        orig_block = func_block
        # This has happened a few times -- find out why/when.
        # When the file was compiled without debugging.
        if func_block is None:
            print(func_addr)

        # Often enough that it's a problem, despite being given the start of a
        # function as the address, we get a child block of the function.
        # Functions I've seen this happen with (all when inspecting a debug
        # build of neovim).
        #
        # src/valgrind.c je_valgrind_make_mem_undefined
        # src/valgrind.c je_valgrind_make_mem_defined
        # include/jemalloc/internal/atomic.h je_atomic_add_uint32
        #
        # I used the below to get this output.
        # if not func_block.function:
        #     print(func_addr)
        #     above = func_block.superblock.function
        #     if above:
        #         print(above.symtab.filename, above.name)

        while func_block and func_block.function is None:
            func_block = func_block.superblock

        if func_block:
            function_name = func_block.function.name
            function_filename = func_block.function.symtab.filename
        else:
            # Print this out for my information -- I don't know of a case when
            # this would happen, so if it does I have a chance to learn.
            print('Function not found at {}'.format(func_addr))
            func_block = orig_block
            function_name = ''
            function_filename = ''

        # No instruction is less than one byte.
        # If we provide an end point that is one byte less than the end of the
        # last instruction in the block, then this ends the disassembly on the
        # last instruction of the function.
        return (arch.disassemble(func_block.start, func_block.end-1),
                function_name,
                function_filename)

    if not use_fallback:
        return (None, None, None)

    # Fallback -- Use the gdb `disassemble` command to disassemble the entire
    # function, and find difference between the start and end of that function.

    # Rather than implementing a conversion function between a line in the
    # output of the `disassemble` command and a dictionary that matches the
    # output of the Architecture.disassemble() method, we just find the extent
    # of the current function with `disassemble {}`, and then return the list
    # made from Architecture.disassemble() directly.
    output = gdb.execute('disassemble {}'.format(func_addr), False, True)
    lines = output.splitlines()
    if not lines[0].startswith('Dump of assembler code for function'):
        raise RuntimeError('Failed to find function at {}'.format(func_addr))

    # Avoid the 'End of assembler dump.' line that is actually last.
    start_addr = int(lines[1].split()[0], base=16)
    last_pos = int(lines[-2].split()[0], base=16)
    function_name = re.search('Dump of assembler code for function (\S+):',
                              lines[0])
    function_name = function_name.groups()[0] if function_name else None
    return arch.disassemble(start_addr, last_pos), function_name, None