GDB实战

代码介绍

libcp.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libcp.h"

int mycopy(char *a, int len)
{
        char *tmp = (char *)malloc(1024 * sizeof(char));

        memset(tmp, 10, 1024);

        memcpy(a, tmp, len);

        free(tmp);
        return 0;

}

libcp.h

#ifndef _C_TEST_H_
#define _C_TEST_H_

#ifdef __cplusplus
extern "C" {
#endif

int mycopy(char *a, int len);

#ifdef __cplusplus
};
#endif

#endif

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libcp.h"

/*
 * 主函数,简单测试
 * 测试 core文件, 
 * 测试 堆栈内存信息
 */
int main(void) {
        int i, n, ret;
        char *a = NULL;

        n = 20;

//		a = (char *)malloc(n * sizeof(char));
        ret = mycopy(a, 20);
        printf("ret = %d\n", ret);

        for(i = 0; i < n; i++)
        {
                printf("%d ", a[i]);
         }
        printf("\n");

        return 0;
}

编译

编译动态库

gcc -g -fPIC -shared -o libcp.so libcp.c

-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

编译主程序

gcc -g -rdynamic -Wall main.c -o main.out -L. -lcp

-g 是一个编译选项,即在源代码编译的过程中起作用,让gcc把更多调试信息(也就包括符号信息)收集起来并将存放到最终的可执行文件内。
-rdynamic 用来通知链接器将所有符号添加到动态符号表中;如果不加-rdynamic选项,可能会看不到动态库里的符号表;

xxx@xxx:~/gdb$ readelf -s main.out

Symbol table '.dynsym' contains 21 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND putchar@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mycopy
     7: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     8: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     9: 0000000000601048     0 NOTYPE  GLOBAL DEFAULT   25 _edata
    10: 0000000000601038     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
    11: 0000000000601050     0 NOTYPE  GLOBAL DEFAULT   26 _end
    12: 0000000000601038     0 NOTYPE  WEAK   DEFAULT   25 data_start
    13: 0000000000400930     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used
    14: 00000000004008b0   101 FUNC    GLOBAL DEFAULT   14 __libc_csu_init
    15: 0000000000400730    42 FUNC    GLOBAL DEFAULT   14 _start
    16: 0000000000601048     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
    17: 0000000000400826   137 FUNC    GLOBAL DEFAULT   14 main
    18: 00000000004006a8     0 FUNC    GLOBAL DEFAULT   11 _init
    19: 0000000000400920     2 FUNC    GLOBAL DEFAULT   14 __libc_csu_fini
    20: 0000000000400924     0 FUNC    GLOBAL DEFAULT   15 _fini

运行程序./main.out,提示error while loading shared libraries: libcp.so: cannot open shar
ed object file: No such file or directory,
/etc/ld.so.cache 文件搜寻要链接的动态库的。而 /etc/ld.so.cache 是 ldconfig 程序读取 /etc/ld.so.conf 文件生成的。(注意, /etc/ld.so.conf 中并不必包含 /lib 和 /usr/lib,ldconfig程序会自动搜索这两个目录)
如果把 libcp.so 所在的路径添加到 /etc/ld.so.conf 中,再以root权限运行 ldconfig 程序,更新 /etc/ld.so.cache ,main.out运行时,就可以找到 libcp.so。
但不太希望改变系统的东西,所以暂时声明一个环境变量。

export LD_LIBRARY_PATH=~/gdb:$LD_LIBRARY_PATH

环境准备

开启coredump

在~/.bashrc最后添加

ulimit -c unlimited

在/etc/sysctl.conf最后添加

kernel.core_pattern = ./core_%t_%p_%e
kernel.core_uses_pid = 1

core_pattern模板中使用变量见下面的列表:

%p所dump进程的进程ID
%u所dump进程的实际用户ID
%g所dump进程的实际组ID
%s导致本次core dump的信号
%t core dump的时间 (由1970年1月1日计起的秒数)
%h主机名
%e程序文件名

sysctl -p /etc/sysctl.conf

执行使其生效

GDB调试代码

案例1

1、执行程序

xxx@xxx:~/gdb$ ./main.out 
Segmentation fault (core dumped)

2、使用gdb进行调试

xxx@xxx:~/hxc/gdb$ gdb main.out core_1583756703_39387_main.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main.out...done.
[New LWP 39387]
Core was generated by `./main.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  __memcpy_sse2_unaligned ()
    at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:37
37	../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: No such file or directory.
(gdb) bt                       ==============>查看栈信息
#0  __memcpy_sse2_unaligned ()
    at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:37
#1  0x00007f65d1eba7dc in mycopy (a=0x0, len=30) at libcp.c:12
#2  0x000000000040084e in main () at main.c:18
(gdb) f 1                      =============>切换栈层
#1  0x00007f65d1eba7dc in mycopy (a=0x0, len=30) at libcp.c:12
12		memcpy(a, tmp, len);
(gdb) i f                      =============>打印该栈层的具体信息
Stack level 1, frame at 0x7ffd889693c0:
 rip = 0x7f65d1eba7dc in mycopy (libcp.c:12); saved rip = 0x40084e
 called by frame at 0x7ffd889693f0, caller of frame at 0x7ffd88969390
 source language c.
 Arglist at 0x7ffd889693b0, args: a=0x0, len=30
 Locals at 0x7ffd889693b0, Previous frame's sp is 0x7ffd889693c0
 Saved registers:
  rbp at 0x7ffd889693b0, rip at 0x7ffd889693b8
(gdb) 
 

通过调试我们发现段错误的原因是我们将一个空指针传入memcpy函数。
补充:段错误的原因:

  1. 没有权限,如往const修饰的内存中写数据;
  2. 读一个已经释放了的内存地址;
  3. 往一个指向0的内存写数据;
  4. 访问的局部变量没有初始化。
案例2

编译动态库时忘记使用-g选项;

将main.c进行修改

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libcp.h"

/*
 * 主函数,简单测试
 * 测试 core文件, 
 * 测试 堆栈内存信息
 */
int main(void) {
        int i, n, ret;
        char *a = NULL;

        n = 20;

		a = (char *)malloc(n * sizeof(char));
        ret = mycopy(a, 30);                 // =============》这里进行了修改
        printf("ret = %d\n", ret);

        for(i = 0; i < n; i++)
        {
                printf("%d ", a[i]);
         }
        printf("\n");

        return 0;
}

1、执行程序

xxx@xxx:~/gdb$ ./main.out 
*** Error in `./main.out': munmap_chunk(): invalid pointer: 0x0000000001a7a030 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f6587d747e5]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7f6587d81698]
libcp.so(mycopy+0x58)[0x7f65880c77e8]
./main.out(main+0x39)[0x40089f]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f6587d1d830]
./main.out(_start+0x29)[0x400799]

2、使用gdb调试

eason@eason-ubuntu:~/hxc/gdb$ gdb main.out core_1583758294_39765_main.out 
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main.out...done.
[New LWP 39765]
Core was generated by `./main.out'.
Program terminated with signal SIGABRT, Aborted.
#0  0x00007f948c598428 in __GI_raise (sig=sig@entry=6)
    at ../sysdeps/unix/sysv/linux/raise.c:54
54	../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007f948c598428 in __GI_raise (sig=sig@entry=6)
    at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007f948c59a02a in __GI_abort () at abort.c:89
#2  0x00007f948c5da7ea in __libc_message (do_abort=do_abort@entry=2, 
    fmt=fmt@entry=0x7f948c6f3ed8 "*** Error in `%s': %s: 0x%s ***\n")
    at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007f948c5e7698 in malloc_printerr (ar_ptr=0x0, ptr=<optimized out>, 
    str=0x7f948c6f3f00 "munmap_chunk(): invalid pointer", action=<optimized out>)
    at malloc.c:5006
#4  munmap_chunk (p=<optimized out>) at malloc.c:2842
#5  __GI___libc_free (mem=<optimized out>) at malloc.c:2963
#6  0x00007f948c92d7e8 in mycopy () from libcp.so
#7  0x000000000040089f in main () at main.c:18
(gdb) f 6
#6  0x00007f948c92d7e8 in mycopy () from libcp.so
(gdb) disassemble             =====================>反汇编代码
Dump of assembler code for function mycopy:
   0x00007f948c92d790 <+0>:	push   %rbp
   0x00007f948c92d791 <+1>:	mov    %rsp,%rbp
   0x00007f948c92d794 <+4>:	sub    $0x20,%rsp
   0x00007f948c92d798 <+8>:	mov    %rdi,-0x18(%rbp)
   0x00007f948c92d79c <+12>:	mov    %esi,-0x1c(%rbp)
   0x00007f948c92d79f <+15>:	mov    $0x400,%edi
   0x00007f948c92d7a4 <+20>:	callq  0x7f948c92d670 <malloc@plt>
   0x00007f948c92d7a9 <+25>:	mov    %rax,-0x8(%rbp)
   0x00007f948c92d7ad <+29>:	mov    -0x8(%rbp),%rax
   0x00007f948c92d7b1 <+33>:	mov    $0x400,%edx
   0x00007f948c92d7b6 <+38>:	mov    $0xa,%esi
   0x00007f948c92d7bb <+43>:	mov    %rax,%rdi
   0x00007f948c92d7be <+46>:	callq  0x7f948c92d650 <memset@plt>
   0x00007f948c92d7c3 <+51>:	mov    -0x1c(%rbp),%eax
   0x00007f948c92d7c6 <+54>:	movslq %eax,%rdx
   0x00007f948c92d7c9 <+57>:	mov    -0x8(%rbp),%rcx
   0x00007f948c92d7cd <+61>:	mov    -0x18(%rbp),%rax
   0x00007f948c92d7d1 <+65>:	mov    %rcx,%rsi
   0x00007f948c92d7d4 <+68>:	mov    %rax,%rdi
   0x00007f948c92d7d7 <+71>:	callq  0x7f948c92d660 <memcpy@plt>
   0x00007f948c92d7dc <+76>:	mov    -0x8(%rbp),%rax
   0x00007f948c92d7e0 <+80>:	mov    %rax,%rdi
   0x00007f948c92d7e3 <+83>:	callq  0x7f948c92d640 <free@plt>
=> 0x00007f948c92d7e8 <+88>:	mov    $0x0,%eax
   0x00007f948c92d7ed <+93>:	leaveq 
   0x00007f948c92d7ee <+94>:	retq   
End of assembler dump.
(gdb) shell echo free@plt |c++filt       ====================>去掉函数的名词修饰
free@plt
(gdb) 

当我们编译时没有打开调试-g或者-rdynamic选项,gdb调试时看不到代码,这时即使执行addr2line也无法找到代码

xxx@xxx:~/gdb$ addr2line -Cif -e main.out 0x7f948c92d7e8
??
??:0

可以使用disassemble打开该帧函数的反汇编代码,**=>**箭头出就是出错的地方。
补充:产生SIGABRT的原因:

  1. double free/free 没有初始化的地址或者错误的地址
  2. 堆越界
  3. assert

GDB其他技巧

info命令

info args 查看当前函数参数值
info locals 看当前函数栈上值信息
info registers 表示查看寄存器值.

x命令

查看内存,例如**x /23dw a 意思是 查看 从a地址开始 23个 4字节 有符号十进制数 输出.

用gdb查看内存格式: 
    x /nfu ptr

说明
x 是 examine 的缩写
n表示要显示的内存单元的个数

f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。

u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节

Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
t(binary), f(float), a(address), i(instruction), c(char) and s(string).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes)

ptr 表示从那个地址开始
多线程调试

info threads 查看所有运行的线程信息
thread 3表示切换到第三个线程, info threads 第一列id 就是 thread 切换的id.
set scheduler-locking on 开始多线程单独调试。设置 set scheduler-locking off 关闭,又会回到你调试这个, 其它线程不阻塞.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值