gdb

一、看到别人文章写的好,忍不住抄袭过来,怕下次网址消失

https://www.jianshu.com/p/ed90aad9cb0a

二、内容

GDB初学者指南
Later_c44e
2019.04.22 15:02:59
字数 644阅读 461
从VS转gdb的人会觉得GDB比较难用,其实gdb使用熟练了比VS可能还方便。可以说不会gdb的人,没法真正懂Linux编程。
有正在学习C语言的朋友,可以进Q群121811911下载软件资料和视频,我们一起进步。
编译具有调试符号的程序
gcc编译时,带上-g选项才能产生具有调试符号的程序。才能供gdb进行有源码调试
$ gcc -g justtest.c
反汇编
disassemb命令是反汇编
(gdb) disassemble main
Dump of assembler code for function main: 0x000000000040057d <+0>: push %rbp 0x000000000040057e <+1>: mov %rsp,%rbp 0x0000000000400581 <+4>: sub $0x20,%rsp 0x0000000000400585 <+8>: mov %edi,-0x14(%rbp) 0x0000000000400588 <+11>: mov %rsi,-0x20(%rbp) 0x000000000040058c <+15>: movl $0x12345678,-0x4(%rbp) … 0x00000000004005c6 <+73>: retq
End of assembler dump.
/r选项是反汇编时带上机器码
(gdb) disassemble /r main
Dump of assembler code for function main: 0x000000000040057d <+0>: 55 push %rbp 0x000000000040057e <+1>: 48 89 e5 mov %rsp,%rbp 0x0000000000400581 <+4>: 48 83 ec 20 sub $0x20,%rsp 0x0000000000400585 <+8>: 89 7d ec mov %edi,-0x14(%rbp) 0x0000000000400588 <+11>: 48 89 75 e0 mov %rsi,-0x20(%rbp)
… 0x00000000004005bb <+62>: e8 90 fe ff ff callq 0x400450 <write@plt> 0x00000000004005c0 <+67>: b8 00 00 00 00 mov $0x0,%eax 0x00000000004005c5 <+72>: c9 leaveq 0x00000000004005c6 <+73>: c3 retq
End of assembler dump.
/m选项是反汇编时带上源码(如果有的话)
(gdb) disassemble /m main
Dump of assembler code for function main:
4 { 0x000000000040057d <+0>: push %rbp 0x000000000040057e <+1>: mov %rsp,%rbp 0x0000000000400581 <+4>: sub $0x20,%rsp 0x0000000000400585 <+8>: mov %edi,-0x14(%rbp) 0x0000000000400588 <+11>: mov %rsi,-0x20(%rbp)
5 int nValue = 0x12345678; 0x000000000040058c <+15>: movl $0x12345678,-0x4(%rbp)
6 printf(“%p:%08X\n”, &nValue, nValue); 0x0000000000400593 <+22>: mov -0x4(%rbp),%edx 0x0000000000400596 <+25>: lea -0x4(%rbp),%rax 0x000000000040059a <+29>: mov %rax,%rsi 0x000000000040059d <+32>: mov $0x400654,%edi 0x00000000004005a2 <+37>: mov $0x0,%eax 0x00000000004005a7 <+42>: callq 0x400460 <printf@plt>
7 write(0, “hello,world\n”, 13);

也可以两个选项一起用:
disassemble /rm main
设置反汇编的风格
反汇编的语法默认是GNU风格的,可以使用命令设置为Intel风格
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main: 0x000000000040057d <+0>: push rbp 0x000000000040057e <+1>: mov rbp,rsp 0x0000000000400581 <+4>: sub rsp,0x20 0x0000000000400585 <+8>: mov DWORD PTR [rbp-0x14],edi 0x0000000000400588 <+11>: mov QWORD PTR [rbp-0x20],rsi 0x000000000040058c <+15>: mov DWORD PTR [rbp-0x4],0x12345678 … 0x00000000004005c5 <+72>: leave 0x00000000004005c6 <+73>: ret
End of assembler dump.
单步调试
next:单步步过
step:单步步入
以上是源码级别的单步。如果是汇编级别的单步,则使用:
nexti/stepi(两者效果一样)
自动显示反汇编代码
如果gdb匹配了源文件,则即使使用stepi,那么自动回显的内容依然是源码级别的,需要手动敲disassemble来查看当前汇编内容,很不方便。这时,可以通过display命令达到自动回显的目的:
(gdb) help display
Print value of expression EXP each time the program stops.
/FMT may be used before EXP as in the “print” command.
/FMT “i” or “s” or including a size-letter is allowed,
as in the “x” command, and then EXP is used to get the address to examine
and examining is done as in the “x” command.
With no argument, display all currently requested auto-display expressions.
Use “undisplay” to cancel display requests previously made.
可知,display命令是在每次调试器断下来后,自动显示某个表达式。
我们通过命令:
display /5i $pc
然后每次停下,就会显示对应的反汇编了:
(gdb) stepi
6 printf(“%p:%08X\n”, &nValue, nValue);
6: x/5i $pc
=> 0x400593 <main+22>: mov -0x4(%rbp),%edx 0x400596 <main+25>: lea -0x4(%rbp),%rax 0x40059a <main+29>: mov %rax,%rsi 0x40059d <main+32>: mov $0x400654,%edi 0x4005a2 <main+37>: mov $0x0,%eax
普通断点
通过breakpoint命令下断点。breakpoint之后可接源文件行、函数名称、地址。如:
(gdb) break main
Note: breakpoint 1 also set at pc 0x40058c.
Breakpoint 3 at 0x40058c: file justtest.c, line 5.
(gdb) break 8
Breakpoint 4 at 0x4005c0: file justtest.c, line 8.
(gdb) break 0x4005c0
Note: breakpoint 4 also set at pc 0x4005c0.
Breakpoint 5 at 0x4005c0: file justtest.c, line 8.
可以使用info命令查看断点信息:
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040058c in main at justtest.c:5
2 breakpoint keep y 0x0000000000400593 in main at justtest.c:6
3 breakpoint keep y 0x00000000004005c0 in main at justtest.c:8
通过delete命令,可以删除指定编号的断点。
(gdb) delete breakpoints 1
也可以通过disable/enable来暂定或启用断点。
(gdb) disable breakpoints 1
内存断点
watch命令是下内存写断点:
watch ($rbp-4)
类似的,还有内存读断点rwatch,与内存访问断点(读与写)awatch命令。
查看表达式及内存寄存器等
命令查看内存
x命令(examine)命令可以用于查看内存。
(gdb) help x
Examine memory: x/FMT ADDRESS.
ADDRESS is an expression for the memory address to examine.
FMT is a repeat count followed by a format letter and a size letter.
Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal), t(binary), f(float), a(address), i(instruction), c(char), s(string) and z(hex, zero padded on the left).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).
The specified number of objects of the specified size are printed
according to the format.
如,以字节为单位,以16进制显示,查看4单位&nValue地址处的数据:
(gdb) x /4xb &nValue
0x7fffffffdf3c: 0x78 0x56 0x34 0x12
以10进制查看:
(gdb) x /4db &nValue
0x7fffffffdf3c: 120 86 52 18
按照float解释数据:
(gdb) x /1fw &nValue
0x7fffffffdf3c: 5.69045661e-28
按照double解释数据:
(gdb) x /1fg &nValue
0x7fffffffdf3c: 1.5089747817000635e-315
查看寄存器
(gdb) info registers
修改内存值
使用set命令
set *(type
)=newValue
如:
(gdb) set (int)0x7fffffffdf38=0x40490fda
有正在学习C语言的朋友,可以进Q群121811911下载软件资料和视频,我们一起进步。

三、在windows下进行反汇编研究

https://www.cnblogs.com/amanlikethis/p/3570832.html

四、gdb查看反汇编代码,返回值存放在哪里

如果是指针,一般有些话在$rax或者$eax里面。
但是我在自己跟踪的时候为什么没有发现?难道漏掉了?
问别人,一验证,果然如此!

但是如果是其它常数呢?存放在哪个寄存器?

多线程调试之痛,以及解决之道

1、文章来源

   https://www.cnblogs.com/frankbadpot/archive/2010/06/23/1762916.html

2、多线程调试之痛

调试器(如VS2008和老版GDB)往往只支持all-stop模式,调试多线程程序时,如果某个线程断在一个断点上,你的调试器会让整个程序freeze,直到你continue这个线程,程序中的其他线程才会继续运行。这个限制使得被调试的程序不能够像真实环境中那样运行--当某个线程断在一个断点上,让其他线程并行运行。
GDBv7.0引入的non-stop模式使得这个问题迎刃而解。在这个模式下,
    当某个或多个线程断在一个断点上,其他线程仍会并行运行
    你可以选择某个被断的线程,并让它继续运行
让我们想象一下,有了这个功能后
    当其他线程断在断点上时,程序里的定时器线程可以正常的运行了,从而避免不必要得超时 
    当其他线程断在断点上时,程序里的watchdog线程可以正常的运行了,从而避免嵌入式硬件以为系统崩溃而重启
    可以控制多个线程运行的顺序,从而重现deadlock场景了。由于GDB可以用python脚本驱动调试,理论上可以对程序在不同的线程运行顺序下进行自动化测试。

因此,non-stop模式理所当然成为多线程调试“必杀技”。这2009年下半年之后发布的Linux版本里都带有GDBv7.0之后的版本。很好奇,不知道VS2010里是不是也支持类似的调试模式了。

3、演示项目

A、源码

// gdb non-stop mode demo
// build instruction: g++ -g -o nonstop nonstop.cpp -lboost_thread

#include <iostream>
#include <boost/thread/thread.hpp>

struct op
{
        op(int id): m_id(id) {}
        void operator()()
        {
                std::cout << m_id << " begin" << std::endl;
                std::cout << m_id << " end" << std::endl;
        }
        int m_id;
};
int main(int argc, char ** argv)
{
        boost::thread t1(op(1)), t2(op(2)), t3(op(3));
        t1.join(); t2.join(); t3.join();
        return 0;
}

B. 把一下3行添加到~/.gdbinit来打开non-stop模式

set target-async 1
set pagination off
set non-stop on

C. 启动gdb,设断点,运行.可以看到主线程1是running,3个子线程都断在断点上,而不是只有一个子线程断在断点上.

~/devroot/nonstop$ gdb ./nonstop
GNU gdb (GDB) 7.0-ubuntu
Reading symbols from /home/frankwu/devroot/nonstop/nonstop...done.
(gdb) break 14
Breakpoint 1 at 0x402058: file nonstop.cpp, line 14.
(gdb) break 24
Breakpoint 3 at 0x401805: file nonstop.cpp, line 24.
(gdb) run
Starting program: /home/frankwu/devroot/nonstop/nonstop
[Thread debugging using libthread_db enabled]
[New Thread 0x7ffff6c89910 (LWP 2762)]
[New Thread 0x7ffff6488910 (LWP 2763)]
1 begin
Breakpoint 1, op::operator() (this=0x605118) at nonstop.cpp:14
14                  std::cout << m_id << " end" << std::endl;
2 begin
Breakpoint 1, op::operator() (this=0x605388) at nonstop.cpp:14
14                  std::cout << m_id << " end" << std::endl;
[New Thread 0x7ffff5c87910 (LWP 2764)]
3 begin
Breakpoint 1, op::operator() (this=0x605618) at nonstop.cpp:14
14                  std::cout << m_id << " end" << std::endl;
(gdb) info threads
  4 Thread 0x7ffff5c87910 (LWP 2764)  op::operator() (this=0x605618) at nonstop.cpp:14
  3 Thread 0x7ffff6488910 (LWP 2763)  op::operator() (this=0x605388) at nonstop.cpp:14
  2 Thread 0x7ffff6c89910 (LWP 2762)  op::operator() (this=0x605118) at nonstop.cpp:14
* 1 Thread 0x7ffff7fe3710 (LWP 2759)  (running)

D、 让线程3继续运行,注意我顾意把主线程1也continue,这是我发现的workaround,否则gdb不能切回thread 1.

(gdb) thread apply 3 1 continue

Thread 3 (Thread 0x7ffff6488910 (LWP 2763)):
Continuing.

Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
Continuing.
Cannot execute this command while the selected thread is running.
2 end
[Thread 0x7ffff6488910 (LWP 2763) exited]

warning: Unknown thread 3.

Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
Continuing.
Cannot execute this command while the selected thread is running.
(gdb) info threads
  4 Thread 0x7ffff5c87910 (LWP 2764)  op::operator() (this=0x605618) at nonstop.cpp:14
  2 Thread 0x7ffff6c89910 (LWP 2762)  op::operator() (this=0x605118) at nonstop.cpp:14
* 1 Thread 0x7ffff7fe3710 (LWP 2759)  (running)

E、 让另外两个线程继续运行而结束,主线程断在第24行,最后结束.

(gdb) thread apply 4 2 1 continue

Thread 4 (Thread 0x7ffff5c87910 (LWP 2764)):
Continuing.

Thread 2 (Thread 0x7ffff6c89910 (LWP 2762)):
Continuing.

Thread 1 (Thread 0x7ffff7fe3710 (LW命令P 2759)):
Continuing.
Cannot execute this command while the selected thread is running.
3 end
1 end
[Thread 0x7ffff5c87910 (LWP 2764) exited]
[Thread 0x7ffff6c89910 (LWP 2762) exited]

Breakpoint 3, main (argc=1, argv=0x7fffffffe348) at nonstop.cpp:24
24          return 0;

(gdb) continue
Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
Continuing.

Program exited normally.

设置 GDB 工程代码,在其它机器上调试时尤其重要

1、dir 命令,添加一个搜索路径

(gdb) dir source_dir/

2、set substitute-dir命令

 set substitute-path from_path  to_path

3、list命令

l 为 list 的缩写
查看30~90行之间的代码: l 30,90

4、总结

 在gdb里面查看源码,
list 一下提醒找不到相关文件,列出来的是绝对路径的完整文件名。
help files   看一下帮助,可以加载符号,源文件等,自己看一下。
dir 设置源码路径无效,show directories  看到设置成功,但是还是找不到文件。
应该是绝对路径的问题。
因为igcc 根据你编译的时候指定的是绝对路径还是  ../../XXX.cpp之类的相对路径,在生成debug_info的时候,也把这个路径保存为debug_info 里面的文件名字,就是最后 gdb list 里面找到的文件名字。
这个可以list  查看是不是绝对路径,然后可以用命令
 readelf -p .debug_str  exe_or_so_file 
看到里面保存是是完整的绝对路径。
gdb 的dir 命令添加的源码搜索路径只对相对路径的情况有效。
一个解决办法就是在gcc的时候,使用相对路径那么gdb里面你就可以使用 dir来设置了。像些CMake之类的,它喜欢使用绝对路径,有人可以写个脚本让它使用相对路径,参考  http://stackoverflow.com/questions/9607155/make-gcc-put-relative-filenames-in-debug-information
如果gcc里面-g  生成的debug_info 使用的绝对路径了,最简单的办法就是你把源码也放到相应的位置上去了。
但如果你不想使用这个绝对路径,那也还是有办法的。 
GDB还提供另外一个选择,可以让你修改源码搜索路径,把源码绝对路径里面的一个path映射到另一个path上去,这样即使你debug info了里面的是绝对路径,源码也可以放到另外的目录。
这就是命令
set substitute-path from_path  to_path
比如 list显示的源码是   /home/aaa/1.cpp
那么设置了  set substitute-path /home/aaa/   /home/bbb/

之后,即使你的源文件1.cpp 放在 /home/bbb下面也是可以找到的了。因为gdb帮你做了字符串替换

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注