zoukankan      html  css  js  c++  java
  • 【gdb】A brief introduction on how to use gdb

    Summary

    Gdb CommandAbbreviation commandDescription
    gdb ./binary_name   start gdb 
    run command_line r command_line Start the program being debugged, possibly with command line arguments args. [run]
    break function b function Set a breakpoint at the beginning of function [break]
    break filename:linenumber
    b filename:linenumber Set a breakpoint at line number of the current file. [break]
    delete n   Delete breakpoint number n [delete]
    info break info b List all breakpoints [info]
    list [optional_line]  l [optional_line] List next listsize lines. If optional_line is given, list the lines centered around optional_line. [list]
    backtrace bt print callstack
    frame number f number jump to caller functions
    print var_name[expression] p var_name[expression] print variable, expression(any kind of combinations of variables)
    continue c run until next breakpoint
    next n Step over step by step
    step s Step into functions. [step]
    finish   Step out of the current function. Execute the rest of the current function. [finish]
    watch var_name[*(int*)add]   Set a watchpoint for an expression. gdb will break when the expression expr is written into by the program and its value changes.
    watch -l var_name   Set a watchpoint for an expression. gdb will break when the expression expr is written into by the program and its value changes.
    rwatch  [-l] var_name   Set a watchpoint that will break when the value of expr is read by the program.
    awatch  [-l] var_name   Set a watchpoint that will break when expr is either read from or written into by the program.
    q   quit gdb [quit]

    An example

    Here is an example code. This example code store two strings together. The memory layout is: [str1_len, str1, str2_len, str2]. str2_len locates in the middle of the memory. It's possible that str1 is longer than the max string size( g_str_max_size), then str2 will overrite parts of str1.

     1 #include "stdio.h"
     2 #include "stdlib.h"
     3 #include "string.h"
     4 #include "assert.h"
     5 const int g_str_max_size = 10;
     6 void store_string(void *mem, char *str)
     7 {
     8     int len = strlen(str);
     9     *(int*)mem = len;
    10     strcpy(mem + sizeof(int), str);
    11 }
    12 void print_strings(void *mem)
    13 {
    14     printf("Length of string a:  %d
    ", *(int*) mem);
    15     printf("string s: %s
    
    ", (char*)(mem + sizeof(int)));
    16     
    17     printf("Length of string b:  %d
    ", *(int*) (mem + (g_str_max_size * sizeof(char) + sizeof(int))));
    18     printf("string s: %s
    ", (char*)(mem + (g_str_max_size * sizeof(char) + sizeof(int) * 2)));
    19 }
    20 int main(int argc, char *argv[])
    21 {
    22     if(argc != 3)
    23     {
    24         printf("usage: ./two_strings string1 string2
    ");
    25         return 0;
    26     }
    27     
    28     void *mem = malloc(2 * (g_str_max_size * sizeof(char) + sizeof(int)));
    29     memset(mem, 0, 2 * (g_str_max_size * sizeof(char) + sizeof(int)));
    30     
    31     // char *str1 = "string b.";
    32     // char *str2 = "This is string a.";
    33     // assert(strlen(str1) < g_str_max_size);
    34     // assert(strlen(str2) < g_str_max_size);
    35     
    36     // strlen1 --> *(int*) mem + sizeof(int)
    37     // strlen2 --> *(int*)(mem + (g_str_max_size * sizeof(char) + 2 * sizeof(int)))
    38     store_string(mem                                                , argv[1]);
    39     store_string(mem + (g_str_max_size * sizeof(char) + sizeof(int)), argv[2]);
    40     
    41     print_strings(mem);
    42     
    43     return 0;
    44 }
    View Code

    To use GDB, we'd bettter build the binary without optimization flags, such as -O0. Let's build and run it:

    $ gcc -O0 -g mem_pool.c -o mem_pool
    $ ./mem_pool "This is string a." "string b."
    Length of string a:  17
    string s: This is st
    Length of string b:  9
    string s: string b.
    $

    Oops, we can see that str1 is only partly printed. We will use gdb to find out why it happens.

    start GDB: gdb ./binary_name

    $ gdb ./mem_pool
    ....(gdb informations)
    (gdb) r "This is string a." "string b."
    Starting program: /home/jxion/jp4/depot/lechin/users/jxion/test_toys/test_gdb/mem_pool "This is string a." "string b."
    Length of string a:  17
    string s: This is st
    Length of string b:  9
    string s: string b.
    [Inferior 1 (process 22105) exited normally]

    You can find we can re-produce the issue in gdb.

    set breakpoint: 'break file_name:line_number'(or 'b file_name:line_number')

    (gdb) b mem_pool.c:18
    Breakpoint 1 at 0x400701: file mem_pool.c, line 18.
    (gdb) r
    Starting program: /home/jxion/jp4/depot/lechin/users/jxion/test_toys/test_gdb/mem_pool "This is string a." "string b."
    Length of string a:  17
    Breakpoint 1, print_strings (mem=0x602010) at mem_pool.c:18

    Show the nearby code lines: "list" or "l"

    The breakpoint line will be in the midlle. If you want to let the line13 in the middle, use "list 13" or "l 13".

    (gdb) list
    13      }
    14
    15      void print_strings(void *mem)
    16      {
    17          printf("Length of string a:  %d
    ", *(int*) mem);
    18          printf("string s: %s
    
    ", (char*)(mem + sizeof(int)));
    19
    20          printf("Length of string b:  %d
    ", *(int*) (mem + (g_str_max_size * sizeof(char) + sizeof(int))));
    21          printf("string s: %s
    ", (char*)(mem + (g_str_max_size * sizeof(char) + sizeof(int) * 2)));
    22      }
    (gdb)

    Show the call stack: "backtrace" or "bt"

    (gdb) bt
    #0  print_strings (mem=0x602010) at mem_pool.c:18
    #1  0x0000000000400818 in main (argc=3, argv=0x7fffffffd4e8) at mem_pool.c:45
    (gdb)

    Show some variables: "print var_name" or "p var_name"

    You can even operate the variable as your need. Such as "print var1*var2".

    (gdb) p (int)mem
    $11 = 6299664
    (gdb) p (int)mem * 100
    $12 = 629966400
    (gdb) p *(int*) mem + *((int*) mem + 1)
    $13 = 1936287845
    (gdb)

    Jump to caller to show caller's variables: "frame n" or "f n"

    You can use "bt" to show the callstack, and then jump to any callstack you like by "frame n"

    (gdb) bt
    #0  print_strings (mem=0x602010) at mem_pool.c:18
    #1  0x0000000000400818 in main (argc=3, argv=0x7fffffffd4e8) at mem_pool.c:45
    (gdb) f 1
    #1  0x0000000000400818 in main (argc=3, argv=0x7fffffffd4e8) at mem_pool.c:45
    45          print_strings(mem);
    (gdb) l
    40          // strlen1 --> *(int*) mem + sizeof(int)
    41          // strlen2 --> *(int*)(mem + (g_str_max_size * sizeof(char) + 2 * sizeof(int)))
    42          store_string(mem                                                , argv[1]);
    43          store_string(mem + (g_str_max_size * sizeof(char) + sizeof(int)), argv[2]);
    44
    45          print_strings(mem);
    46
    47          return 0;
    48      }
    (gdb) p argv[1]
    $14 = 0x7fffffffd8a1 "This is string a."
    (gdb)

    run next step (step over): "next" or "n"

    (gdb) n
    string s: This is st
    20          printf("Length of string b:  %d
    ", *(int*) (mem + (g_str_max_size * sizeof(char) + sizeof(int))));
    (gdb) n
    Length of string b:  9
    21          printf("string s: %s
    ", (char*)(mem + (g_str_max_size * sizeof(char) + sizeof(int) * 2)));
    (gdb)

    run until next breakpoint: "continue" or "cont" or "c"

    (gdb) cont
    Continuing.
    string s: string b.
    [Inferior 1 (process 22118) exited normally]
    (gdb)

    Other general used commands

    See the summary table.

    Use watchpoint: "watch var_name" or "watch *(int*)mem_address"

    Find the var_name or memory address to watch

    Use breakpoint we can see that str1 is modified when the program breaks in line 18. We need to locate where the str1 is modified. As the str1's memory address is solid all the time, we can use watchpoint to watch who modify the memory.

    As the memory may modified outside of the function, we need to use the second type of "watch" command.

    The easy and more readable way(Recommended):

    ATTENTION: watch -l *(char*)(mem + sizeof(int)+10)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    (gdb) p (char*)(mem + sizeof(int))
    $9 = 0x602014 "this is st"
    (gdb) p (char*)(mem + sizeof(int)+10)
    $10 = 0x60201e ""
    (gdb) help watch
    Set a watchpoint for an expression.
    Usage: watch [-l|-location] EXPRESSION
    A watchpoint stops execution of your program whenever the value of
    an expression changes.
    If -l or -location is given, this evaluates EXPRESSION and watches
    the memory to which it refers.
    (gdb) watch -l *(char*)(mem + sizeof(int)+10)
    Hardware watchpoint 6: -location *(char*)(mem + sizeof(int)+10)

    The harder way:

    (gdb) p (char*)(mem + (sizeof(int)*1))
    $18 = 0x602014 "This is st	"
    (gdb) p (char*)(mem + (sizeof(int)*1) + 10)
    $19 = 0x60201e "	"
    (gdb) p (mem + (sizeof(int)*1) + 10)
    $20 = (void *) 0x60201e
    (gdb) watch *(char *) 0x60201e
    Hardware watchpoint 2: *(char *) 0x60201e
    (gdb)

    Re-run the programe to locate which code line change the value

    Now we have find the address where the content is modified incorectly. And it has already been modified, so we need to re-run the program from the start. From my understanding, the memory will not change not matter how many times you run the program as lone as you don't quit the gdb.

    (gdb) r
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /home/jxion/jp4/depot/lechin/users/jxion/test_toys/test_gdb/mem_pool "This is string a." "string b."
    Hardware watchpoint 2: *(char *) 0x60201e
    Old value = <unreadable>
    New value = 0 '00'
    memset () at ../sysdeps/x86_64/memset.S:65
    65      ../sysdeps/x86_64/memset.S: No such file or directory.
    (gdb) bt
    #0  memset () at ../sysdeps/x86_64/memset.S:65
    #1  0x00000000004007c9 in main (argc=3, argv=0x7fffffffd4e8) at mem_pool.c:33
    (gdb) c
    Continuing.
    Hardware watchpoint 2: *(char *) 0x60201e
    Old value = 0 '00'
    New value = 114 'r'
    __strcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:792
    792     ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: No such file or directory.
    (gdb) bt
    #0  __strcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:792
    #1  0x00000000004006dc in store_string (mem=0x602010, str=0x7fffffffd8a1 "This is string a.") at mem_pool.c:12
    #2  0x00000000004007e3 in main (argc=3, argv=0x7fffffffd4e8) at mem_pool.c:42
    (gdb) c
    Continuing.
    Hardware watchpoint 2: *(char *) 0x60201e
    Old value = 114 'r'
    New value = 9 '	'
    store_string (mem=0x60201e, str=0x7fffffffd8b3 "string b.") at mem_pool.c:12
    12          strcpy(mem + sizeof(int), str);
    (gdb) bt
    #0  store_string (mem=0x60201e, str=0x7fffffffd8b3 "string b.") at mem_pool.c:12
    #1  0x000000000040080c in main (argc=3, argv=0x7fffffffd4e8) at mem_pool.c:43
    (gdb)

    We can see that the address's content is modified three times:

    • 1st time: it's set to 0 by memset().
    • 2nd time: it's set to 'r' by store_string(). from the callstack and the arguments, it's storing the str1.
    • 3rd time: it's set to ' ' by store_string(). It's storing str2. And we can find that this address is overwriten here.

    Reference

  • 相关阅读:
    ES6-->ECMAScript 6.0 新增方法,一些基本语法
    初识 Nodejs (了解Nodejs)
    Vue框架初识
    python语法入门之流程控制
    python中基本运算符
    格式化输出
    基本数据类型
    变量,解释器,垃圾回收机制,小整数池总结
    编程语言发展史
    计算机基础
  • 原文地址:https://www.cnblogs.com/xjsxjtu/p/4625056.html
Copyright © 2011-2022 走看看