How to execute finish and then another command from inside commands?

前端 未结 5 1438
长情又很酷
长情又很酷 2020-12-16 15:01

This is a reduced example of the structure of my code:

void increment(int j);

int main()
{
  int i = 0;

  while(1) {
    i = increment(i);
  }

  return 0;         


        
5条回答
  •  执念已碎
    2020-12-16 15:53

    GDB breakpoint command lists are limited in that they ignore any command after the first stepping/continue command (as of March, 2017, GDB 7.12). This is documented in the GDB manual where this motivated with the current implementation not being capable to execute two command lists at the same time (cf. GDB #10852 - command sequences interrupted unexpectedly).

    This limitation is only enforced with a stepping/continue command directly present in a command list. Thus, one can hack around this - but the limitation still applies and e.g. the GDB manual warns in the Python API Section: 'You should not alter the execution state of the inferior (i.e., step, next, etc.)'

    Thus, when the need arises to execute GDB commands on function entry and function exit, the reliable solution is to use multiple breakpoints and split the command lists. That means that additional breakpoints need to be set for each return instruction of the function under investigation.

    This can be done similar to:

    (gdb) b my_function
    (gdb) commands
    silent
    printf "my_function: %d -> ", j
    end
    (gdb) set pagination off
    (gdb) set logging file gdb.log
    (gdb) set logging overwrite on
    (gdb) set logging on
    (gdb) disas my_function
    (gdb) set logging off
    (gdb) shell grep ret gdb.log
    0x00007ffff76ad095 <+245>:  retq
    (gdb) b *0x00007ffff76ad095
    (gdb) commands
    silent
    printf "%lu\n", $rax
    end
    

    What register contains the return value depends on the calling conventions and is architecture dependent. On x86-64 it is in $rax. Other choices are $eax on x86-32, $o0 on SPARC, $r0 on ARM etc.

    The creation of the additional breakpoints can be automated in GDB using its scripting support.

    Python

    Recent GDB versions come with a Python API that is well suited for this automation. GDB packages provided by distributions usually enable Python support, by default.

    As a first example, automatically set breakpoints on each ret instruction of a given function:

    (gdb) py fn='myfunc'; list(map(lambda l: gdb.execute('b *{}'.format(l[0])), \
        filter(lambda l : l[2].startswith('ret'), map(lambda s : s.split(), \
            gdb.execute('disas '+fn, to_string=True).splitlines()))))
    

    (assumes GDB was compiled with Python3 support, e.g. the Fedora 25 one)

    For automating the creation of breakpoints that print the return value (i.e. the value of register $rax) and then continue the gdb.Breakpoint class needs to be subclassed:

    py
    class RBP(gdb.Breakpoint):
      def stop(self):
        print(gdb.parse_and_eval('$rax'))
        return False
    
    end
    

    Then a breakpoint can be created like this:

    py RBP('*0x000055555555894e')
    

    Combining both steps for creating a new custom command:

    py
    class Pret_Cmd(gdb.Command):
      '''print return value via breakpoint command
    
      pret FUNCTION
      '''
      def __init__(self):
        super().__init__('pret', gdb.COMMAND_BREAKPOINTS)
    
      def install(self, fn):
        for l in filter(lambda l : l[2].startswith('ret'),
            map(lambda s : s.split(),
                gdb.execute('disas '+fn, to_string=True).splitlines())):
          RBP('*{}'.format(l[0]))
    
      def invoke(self, arg, from_tty):
        self.install(arg)
    
    Pret_Cmd()
    end
    

    Example of using this new command:

    (gdb) help breakpoints
    (gdb) help pret
    (gdb) pret myfunc
    

    Guile

    In case you don't like Python and/or have a GDB that has Python support disabled - but Guile support enabled - one can also automatically set the breakpoints via Guile.

    The custom command definition in Guile:

    (gdb) gu (use-modules (gdb))
    (gdb) gu
    (register-command!
      (make-command "pret" #:command-class COMMAND_BREAKPOINTS #:doc
        "print return value via breakpoint command\n\npret FUNCTION"
        #:invoke
        (lambda (fn)
          (map (lambda (x)
                 (let ((bp (make-breakpoint (string-append "*" x))))
                   (register-breakpoint! bp)
                   (set-breakpoint-stop!
                     bp
                     (lambda (x)
                       (display (parse-and-eval "$rax"))
                       (newline)
                       #f))
                   bp))
               (map (lambda (x) (list-ref x 0))
                    (filter
                      (lambda (x)
                        (and (not (null? x))
                             (string-prefix? "ret" (list-ref x 2))))
                      (map (lambda (x) (string-tokenize x))
                           (string-split
                             (execute
                               (string-append "disas " fn)
                               #:to-string
                               #t)
                             #\newline))))))))
    
    end
    

提交回复
热议问题