How do I debug jonesforth with GDB?

前端 未结 2 610
离开以前
离开以前 2021-01-21 23:35

jonesforth is typically started as follows:

cat jonesforth.f  - | ./jonesforth

What\'s a good way to debug jonesforth?

相关标签:
2条回答
  • 2021-01-21 23:50

    On Ubuntu?

    If you're on Ubuntu, allow gdb to attach to running processes:

    echo 0 > /proc/sys/kernel/yama/ptrace_scope
    

    If you'd like that setting to remain across reboots:

    vim /etc/sysctl.d/10-ptrace.conf
    

    Update Makefile

    Add the g flag to your jonesforth Makefile recipe:

    jonesforth: jonesforth.S
        gcc -g -m32 -nostdlib -static $(BUILD_ID_NONE) -o $@ $<
    

    Starting gdb

    Then, start up jonesforth as usual in a terminal:

    cat jonesforth.f - | ./jonesforth
    

    In another terminal, start gdb and attach it to the running jonesforth:

    gdb --quiet --pid=`pgrep jonesforth`  ./jonesforth 
    

    Sample session

    Here's what I see when I start gdb:

    $ gdb --quiet --pid=`pgrep jonesforth`  ./jonesforth 
    Reading symbols from ./jonesforth...done.
    Attaching to program: /home/dharmatech/Dropbox/Documents/jonesforth-annexia/jonesforth, process 3406
    _KEY () at jonesforth.S:1290
    1290        test %eax,%eax      // If %eax <= 0, then exit.
    (gdb) 
    

    Jonesforth is waiting for us to enter something. It's in the _KEY assembly routine. This is indicated by gdb above. It also shows that line 1290 is the next one to execute. Here's the _KEY routine:

    _KEY:
        mov (currkey),%ebx
        cmp (bufftop),%ebx
        jge 1f          // exhausted the input buffer?
        xor %eax,%eax
        mov (%ebx),%al      // get next key from input buffer
        inc %ebx
        mov %ebx,(currkey)  // increment currkey
        ret
    
    1:  // Out of input; use read(2) to fetch more input from stdin.
        xor %ebx,%ebx       // 1st param: stdin
        mov $buffer,%ecx    // 2nd param: buffer
        mov %ecx,currkey
        mov $BUFFER_SIZE,%edx   // 3rd param: max length
        mov $__NR_read,%eax // syscall: read
        int $0x80
        test %eax,%eax      // If %eax <= 0, then exit.
        jbe 2f
        addl %eax,%ecx      // buffer+%eax = bufftop
        mov %ecx,bufftop
        jmp _KEY
    
    2:  // Error or end of input: exit the program.
        xor %ebx,%ebx
        mov $__NR_exit,%eax // syscall: exit
        int $0x80
    

    _KEY uses some variables in memory: buffer, currkey, and bufftop. It also uses a couple of registers. Let's use gdb's Auto Display feature to display these:

    display/8cb &buffer
    display/1xw &currkey
    display/1xw &bufftop
    display/x   $eax
    display/x   $ebx
    

    Now if we type display in gdb, we'll see all of those at once:

    (gdb) display
    1: x/8cb &buffer
    0x804c000:  97 'a'  98 'b'  108 'l' 121 'y' 46 '.'  32 ' '  32 ' '  84 'T'
    2: x/xw &currkey  0x8049d54:    0x0804c000
    3: x/xw &bufftop  0x8049d58:    0x0804c7e3
    4: /x $eax = 0xfffffe00
    5: /x $ebx = 0x0
    

    This might also be a good time to enable gdb's TUI:

    tui enable
    

    gdb should now look like this:

    OK, jonesforth is still waiting for input. So let's give it something:

    JONESFORTH VERSION 47 
    14499 CELLS REMAINING
    OK 123
    

    Alright, back in gdb, we can finally ask it to step:

    (gdb) s
    1: x/8cb &buffer
    0x804c000:      49 '1'  50 '2'  51 '3'  10 '\n' 46 '.'  32 ' '  32 ' '  84 'T'
    2: x/xw &currkey  0x8049d54:    0x0804c000
    3: x/xw &bufftop  0x8049d58:    0x0804c7e3
    4: /x $eax = 0x4
    5: /x $ebx = 0x0
    

    Hey, look at that! The first 3 characters in buffer are 1, 2, and 3.

    If %eax <= 0 the next step will jump to the 2f label. But as we can see above, %eax is 4. So it should just continue on.

    If we step through the next three lines, the bufftop will be set to the address of buffer incremented by 4 (three characters of '123' plus a newline character). The value in relation to the address of buffer checks out:

    3: x/xw &bufftop  0x8049d58:    0x0804c004
    

    Now that data has been read into the input buffer, _KEY will do its job and return back to the caller. Here's the next few instructions before the return:

    As you step through those, the auto display feature will show the variables and registers updating accordingly.

    0 讨论(0)
  • 2021-01-22 00:02

    My (quite limited!) experience is that the LLVM debugger lldb is much friendlier when tinkering with assembly-language programs.

    0 讨论(0)
提交回复
热议问题