gdb调试core dump文件之二

1 目录

2 corrupt stack?

3 info registers

4 (gdb)x的使用

5 X86上崩溃

6 调试线程

1 目录

1.1 目录指定dir

在GDB中使用dir命令来添加新的源代码路径。这个命令允许我指定一个或多个目录,GDB会在这些目录中查找源代码文件。

gdb ./your_executable # 启动可执行文件
(gdb) dir /path/to/your/source/code # 使用dir命令添加源代码路径
(gdb) show directories # 验证源代码路径已被添加:

1.2 目录替换set substitute-path

如果知道调试信息中的旧路径和新路径,就可以使用set substitute-path命令将旧路径替换为新路径。
info

  • 启动GDB并加载你的可执行文件

    • gdb ./your_executable
  • 使用set substitute-path命令替换路径:

    • (gdb) set substitute-path /old/source/path /new/source/path
  • 列出所有已知的源文件,验证它们是否正确指向了新的路径。

    • (gdb) info sources

1.3 示例

假设我的调试信息中包含的旧路径是/home/user/old_project/src,而你希望替换为新的路径/home/user/new_project/src,可以按如下步骤操作:

  • 启动GDB:
    • gdb ./your_executable
  • 设置路径替换:
    • (gdb) set substitute-path /home/user/old_project/src /home/user/new_project/src
  • 确认路径已替换:
    • (gdb) info sources

查看源文件路径是否指向了新的路径。

通过这些方法,就可以在GDB调试时正确地加载源代码,即使调试信息中的路径不正确。

2 Backtrace stopped: previous frame identical to this frame (corrupt stack?)

在GDB调试时,如果遇到错误信息 Backtrace stopped: previous frame identical to this frame (corrupt stack?),通常表明程序的调用栈已损坏。这种情况可能由多种原因引起,例如:

  • 堆栈溢出:函数递归太深或局部变量过多,导致超出栈的限制
  • 堆栈损坏:由于缓冲区溢出等问题,导致堆栈上的返回地址或帧指针被意外覆盖
  • 错误的编译优化:某些编译器优化可能会导致调试信息不准确。
  • 未初始化或非法指针:使用未初始化的指针或非法访问内存。

2.1 解决方法

  1. 检查代码中的缓冲区溢出

    缓冲区溢出是堆栈损坏的常见原因。确保所有数组和缓冲区的访问都在其边界内。

     #include <stdio.h>
     #include <string.h>
    
     void example_function(char *input) {
         char buffer[10];
         // Potential buffer overflow
         strcpy(buffer, input);
     }
    
     int main() {
         char input[] = "This string is too long and will overflow the buffer";
         example_function(input);
         return 0;
     }
    

    在这个例子中,如果 input 超过 buffer 的大小,将导致缓冲区溢出,可能破坏堆栈。

  • 参考栈大小命令:ulimit -s,一般为8192KB,即8M
  1. 使用调试工具

    使用工具如 valgrind 或 AddressSanitizer 可以帮助检测内存错误。

  • Valgrind:

    valgrind --tool=memcheck ./your_program

  • AddressSanitizer:

    在编译时添加 -fsanitize=address 选项:

    gcc -fsanitize=address -g -o your_program your_program.c
    ./your_program

  1. 调整编译优化

    编译时使用较低的优化级别,可能会改善调试信息的准确性。

     gcc -O0 -g -o your_program your_program.c
    
  2. 检查递归和栈大小

如果函数递归深度过大局部变量占用太多栈空间,可能导致栈溢出。确保递归基准情况和合理的栈空间分配。

void recursive_function(int count) {
    if (count > 0) {
        recursive_function(count - 1);
    }
}

2.2 分析堆栈损坏

如果依然出现堆栈损坏问题,可以使用GDB的其他功能进一步分析:

  • 查看寄存器和堆栈:

    (gdb) info registers
    (gdb) x/32x $sp

  • 检查帧指针:

    (gdb) info frame
    (gdb) info locals

使用内存调试工具

valgrind 和 AddressSanitizer 是强大的内存调试工具,可以检测出内存管理问题和未定义行为。

  • Valgrind:

    valgrind --tool=memcheck --leak-check=full ./your_program

  • AddressSanitizer:

编译时添加 -fsanitize=address 选项:

gcc -fsanitize=address -O0 -g -o your_program your_program.c
./your_program

2.3 结论

堆栈损坏问题通常与内存管理错误或递归深度有关。通过检查代码中的缓冲区溢出,使用内存调试工具,调整编译优化级别,以及检查递归和栈大小,可以有效解决此类问题。使用GDB查看寄存器和堆栈,以及检查帧指针和局部变量,可以帮助进一步分析和诊断问题。

3 info registers

在GDB中,使用info registers命令,可以查看程序当前状态下的所有寄存器的值。

寄存器是CPU中的小型存储单元,用于存放数据地址指令等信息。info registers命令在调试时非常有用,可以帮助你进行以下操作:

  1. 检查程序状态
  • 查看寄存器内容:寄存器存储了CPU在执行当前指令时的各种信息,例如
    • 程序计数器(PC):
    • 堆栈指针(SP):
    • 基址寄存器(BP):

通过查看这些寄存器的值,可以了解程序的执行状态,定位程序的执行位置。

(gdb) info registers

这将显示所有寄存器的当前值,例如:

eax            0x0      0
ebx            0x1      1
ecx            0x2      2
edx            0x3      3
eip            0x401000 0x401000
esp            0xfffdf000 0xfffdf000
ebp            0xfffdf020 0xfffdf020
  1. 调试程序崩溃
  • 定位崩溃点:当程序崩溃时,查看寄存器可以帮助确定崩溃发生的位置。程序计数器(EIP或PC)的值通常显示了崩溃发生的指令地址。

    • PC(Program Counter): 在ARM下的当前崩溃指令所在处,
    • EIP(Extended Instruction Pointer): EIP寄存器是x86架构处理器中的一个重要寄存器,‌用于存储下一条指令的内存地址。‌即当前执行的指令存放的位置
  • 分析调用栈:寄存器中的堆栈指针(SP)基址寄存器(BP)的值可以帮助你查看函数调用栈,了解程序的调用路径和函数的局部变量。

  1. 理解程序的执行流程
  • 查看寄存器变化:在程序执行过程中,寄存器的值会不断变化。通过查看寄存器的值,可以跟踪程序的执行路径,理解不同部分代码如何影响程序的状态。
  1. 调试复杂问题
  • 调试优化代码:优化后的代码可能会对寄存器进行复杂的操作。查看寄存器可以帮助我理解编译器如何优化你的代码,从而帮助解决可能出现的复杂问题。
  1. 设置断点和观察点
  • 设置条件断点:可以根据寄存器的值设置条件断点,例如在特定寄存器值下停止程序执行。

      (gdb) break some_function if eax == 0x1
    
  • 观察寄存器变化:可以使用watch命令监视寄存器的值,查看其在程序执行过程中的变化。

      (gdb) watch eax
    

示例,以下是一个在GDB中查看寄存器的示例:

gdb ./your_program
#设置断点并运行程序:
(gdb) break main
(gdb) run

查看寄存器:

(gdb) info registers

输出可能包括:

eax            0x0      0
ebx            0x0      0
ecx            0x0      0
edx            0x0      0
eip            0x400123 0x400123 <main+3>
esp            0xfffdf000 0xfffdf000
ebp            0xfffdf020 0xfffdf020

在这个示例中,eip寄存器的值(0x400123)表示程序计数器当前指向的地址,这有助于定位程序执行到的位置。

总结

info registers命令提供了对寄存器状态的全面视图,在调试过程中非常重要。通过分析寄存器的内容,可以帮助理解程序的执行状态、定位崩溃点、跟踪程序路径,并解决复杂的调试问题。

3.1 示例分析1141

在GDB调试中,查看寄存器(info registers)有助于了解程序的执行状态。在提供的输出中,可以看到ARM架构下各个寄存器的当前值。以下是对这些寄存器的一些解释:

3.1.1 CPU寄存器内容

(gdb) info registers

r0             0xb3c872a8          3016258216
r1             0xffff7e7c          4294934140
r2             0x4c378ce0          1278708960
r3             0x4c370bdc          1278675932
r4             0xb2d00588          2999977352
r5             0xb24fcb18          2991573784
r6             0xf7b2e7b0          4155697072
r7             0xffff7e7c          4294934140
r8             0xbe3c426           199476262
r9             0xbeec7441          3203167297
r10            0xebc8948f          3955790991
r11            0x3fef8e1c          1072664092
r12            0x0                 0
sp             0xb24fc9fc          0xb24fc9fc
lr             0xb5c19735          -1245604043
pc             0xb341f2b8          0xb341f2b8 <memcpy+72>
cpsr           0x200f0010          537853968
fpscr          0x8000001b          -2147483621

3.1.2 ARM寄存器说明

通用寄存器(R0-R12),共13个:

  • r0-r3:通常用于函数参数传递和返回值。这里共4个寄存器
  • r4-r11:通用寄存器,函数调用中使用的临时寄存器。这里共8个寄存器
  • r12(IP): 一般用于过程间调用时的临时寄存器。最后一个通用寄存器

特殊寄存器:

  • sp(Stack Pointer):栈指针,指向当前栈顶地址。
  • lr(Link Register):链接寄存器,存储函数调用的返回地址。
  • pc(Program Counter):程序计数器,存储下一条将要执行的指令地址。
  • cpsr(Current Program Status Register):当前程序状态寄存器,包含条件标志状态信息
  • fpscr(Floating Point Status and Control Register):浮点状态和控制寄存器。

3.1.3 寄存器值分析

r0             0xb3c872a8          3016258216
r1             0xffff7e7c          4294934140
r2             0x4c378ce0          1278708960
r3             0x4c370bdc          1278675932
r4             0xb2d00588          2999977352
r5             0xb24fcb18          2991573784
r6             0xf7b2e7b0          4155697072
r7             0xffff7e7c          4294934140
r8             0xbe3c426           199476262
r9             0xbeec7441          3203167297
r10            0xebc8948f          3955790991
r11            0x3fef8e1c          1072664092
r12            0x0                 0
sp             0xb24fc9fc          0xb24fc9fc
lr             0xb5c19735          -1245604043
pc             0xb341f2b8          0xb341f2b8 <memcpy+72>
cpsr           0x200f0010          537853968
fpscr          0x8000001b          -2147483621

特别关注的寄存器

  • 程序计数器(PC):

      pc 0xb341f2b8 0xb341f2b8 <memcpy+72>
    

    程序计数器当前指向memcpy函数的偏移量为72的指令。

  • 链接寄存器(LR):

      lr 0xb5c19735 -1245604043
    

    链接寄存器保存了调用函数的返回地址,通常用于返回到调用函数。

  • 栈指针(SP):

      sp 0xb24fc9fc 0xb24fc9fc
    

    栈指针,当前指向栈顶。

  • 当前程序状态寄存器(CPSR):

      cpsr 0x200f0010 537853968
    

    包含条件标志(例如零、进位、负等)和其他状态信息。

  • 浮点状态和控制寄存器(FPSCR):

      fpscr 0x8000001b -2147483621
    

    浮点操作的状态和控制信息。

3.1.4 使用寄存器信息进行调试

查看寄存器的值可以帮助我

  • 分析崩溃位置:pc指向的指令地址(例如,在memcpy中)可能是导致崩溃的原因。
  • 检查函数调用链:通过lr,可以找到调用链的返回地址,进一步分析调用路径。
  • 了解栈的状态:sp指向当前栈顶,可以查看栈上的数据调用帧
  • 分析程序状态:cpsr中的标志位可以帮助你理解程序在执行特定指令时的状态,例如是否发生了溢出零结果等。

示例:调试崩溃

假设我的程序在调用memcpy时崩溃,就可以使用以下步骤进行调试:

查看寄存器:

(gdb) info registers

#检查PC寄存器:
(gdb) x/i $pc       # 这将显示pc指向的指令。

#检查栈内容:
(gdb) x/16x $sp     # 这将显示当前栈顶的内容,有助于理解函数调用链和局部变量。

#检查返回地址:
(gdb) x/i $lr       # 这将显示lr指向的返回地址指令。

通过这些步骤,可以更深入地分析和调试程序崩溃或异常行为。

3.1.5 使用反汇编查看崩溃处memcpy+72

(gdb) disassemble memcpy
Dump of assembler code for function memcpy:
0xb341f270 <+0>:	push	{r0, r4, lr}
0xb341f274 <+4>:	subs	r2, r2, #4
0xb341f278 <+8>:	blt	0xb341f32c <memcpy+188>
0xb341f27c <+12>:	ands	r12, r0, #3
0xb341f280 <+16>:	pld	[r1]
0xb341f284 <+20>:	bne	0xb341f34c <memcpy+220>
0xb341f288 <+24>:	ands	r12, r1, #3
0xb341f28c <+28>:	bne	0xb341f37c <memcpy+268>
0xb341f290 <+32>:	subs	r2, r2, #28
0xb341f294 <+36>:	push	{r5, r6, r7, r8}
0xb341f298 <+40>:	blt	0xb341f2d0 <memcpy+96>
0xb341f29c <+44>:	pld	[r1]
0xb341f2a0 <+48>:	subs	r2, r2, #96	; 0x60
0xb341f2a4 <+52>:	pld	[r1, #28]
0xb341f2a8 <+56>:	blt	0xb341f2b8 <memcpy+72>
0xb341f2ac <+60>:	pld	[r1, #60]	; 0x3c
0xb341f2b0 <+64>:	pld	[r1, #92]	; 0x5c
0xb341f2b4 <+68>:	pld	[r1, #124]	; 0x7c
=> 0xb341f2b8 <+72>:	ldm	r1!, {r3, r4, r5, r6, r7, r8, r12, lr}
0xb341f2bc <+76>:	subs	r2, r2, #32
0xb341f2c0 <+80>:	stmia	r0!, {r3, r4, r5, r6, r7, r8, r12, lr}

在这个这个例子中,<+72> 处的指令是ldm r1!, {r3, r4, r5, r6, r7, r8, r12, lr}。通过查看这条指令,可以更好地理解程序在执行时的状态和可能出现的问题。

<memcpy+72> 提供了调试信息,表示当前执行的指令在 memcpy 函数内的偏移量为 72 字节。通过反汇编 memcpy 函数并定位到偏移量 72 处的指令,就可以更好地理解程序的执行状态,有助于调试和分析问题。

4 (gdb)x的使用

在GDB中,x命令用于检查内存的内容。这个命令非常灵活,可以根据指定的格式和数量来查看内存。以下是x命令的详细用法和常见示例:

4.1 基本语法

x/[数量][格式][大小] 地址

  • 数量(count):要显示的单元数量。

  • 格式(format):显示内存内容的格式。

  • 大小(size):每个单元的大小。

  • 地址(address):要查看的内存地址。

  • 格式说明

  • x:十六进制

  • d:十进制

  • u:无符号十进制

  • o:八进制

  • t:二进制

  • f:浮点数

  • a:地址

  • i:指令

  • c:字符

  • s:字符串

  • 大小说明

  • b:字节(8位)

  • h:半字(16位)

  • w:字(32位)

  • g:巨字(64位)

4.2 示例

  1. 查看内存中的整数

    假设想查看地址0x601000处的内存内容,并以十六进制格式显示4个字(32位)的值:

     x/4xw 0x601000
    

    输出示例:

     0x601000: 0x00000001 0x00000002 0x00000003 0x00000004
    
  2. 查看内存中的字符

    假设你想查看地址0x601000处的内存内容,并以字符格式显示16个字节:

     x/16cb 0x601000
    

    输出示例:

     0x601000:  72 'H' 101 'e' 108 'l' 108 'l' 111 'o' 32 ' ' 119 'w' 111 'o'
     0x601008:  114 'r' 108 'l' 100 'd' 33 '!' 0 '\000' 0 '\000' 0 '\000' 0 '\000'
    
  3. 查看内存中的字符串

    假设想查看地址0x601000处的内存内容,并以字符串格式显示:

     x/s 0x601000
    

    输出示例:

     0x601000: "Hello, world!"
    
  4. 查看内存中的浮点数

    假设想查看地址0x601000处的内存内容,并以浮点数格式显示4个单元:

     x/4fw 0x601000
    

    输出示例:

     0x601000: 1.0 2.0 3.0 4.0
    
  5. 查看内存中的指令

    假设想查看地址0x400000处的内存内容,并以指令格式显示10条指令:

     x/10i 0x400000
    

    输出示例:

     0x400000:  mov    eax,0x1
     0x400005:  mov    ebx,0x2
     0x40000a:  add    eax,ebx
     0x40000c:  ret
     ...
    
  6. 查看当前栈顶内容

    假设想查看当前栈顶的内容,可以使用栈指针($sps):

     x/16x $sp
    

    输出示例:

     0xbffff7f0: 0x00000000 0x08048484 0xbffff808 0x080483f7
     0xbffff800: 0xbffff818 0x00000000 0x00000000 0x00000000
     0xbffff810: 0x00000000 0x00000000 0x00000000 0x00000000
     0xbffff820: 0x00000000 0x00000000 0x00000000 0x00000000
    

4.3 总结

x命令在GDB中非常强大和灵活,可以根据需要查看内存的不同部分和格式。通过熟练掌握x命令的使用,可以更有效地调试程序,分析内存内容和程序行为。

5 X86上崩溃

5.1 堆栈信息

#0  0xb140478f in RKD::WobjGettorStationary::GetTargetWobj() const () from /home/ae/workspace/code/newARCS/src/packfiles/x86-lib/libarcs-rkd.so
#1  0xb0c39fdd in InterpolationProcess::InterpolateSubTrajPos(RKD::TrajectoryInterface*, double, double, bool) () from /home/ae/workspace/code/newARCS/src/packfiles/x86-lib/libRkdInterface.so
#2  0xb0c3e827 in InterpolationProcess::InterpolatePos(RKD::TrajectoryInterface*, double, double, bool, int&) () from /home/ae/workspace/code/newARCS/src/packfiles/x86-lib/libRkdInterface.so
#3  0xb0c423f1 in InterpolationProcess::Interpolate(int&) () from /home/ae/workspace/code/newARCS/src/packfiles/x86-lib/libRkdInterface.so
#4  0xb67ad82f in RobotInterpolation::ProcessNormalMode (this=0x8452758) at /home/ae/workspace/code/newARCS/src/rtt/mechunit/robot_interpolation.cpp:1575
#5  0xb67a8668 in RobotInterpolation::step (this=0x8452758) at /home/ae/workspace/code/newARCS/src/rtt/mechunit/robot_interpolation.cpp:603
#6  0xb4ef5b97 in ChannelInterpTask::step (this=0x83d3f48) at /home/ae/workspace/code/newARCS/src/rtt/ChannelInterpTask.cpp:29
#7  0xb52cc998 in RTT::Activity::step (this=0x83d8178) at /home/ae/workspace/code/newARCS/src/rtt/Activity.cpp:95
#8  0xb5644afd in RTT::os::thread_function (t=0x83d8180) at /home/ae/workspace/code/newARCS/src/rtt/os/Thread.cpp:135
#9  0xb564ff0b in RTT::os::rtos_posix_thread_wrapper (cookie=0x83cbc78) at /home/ae/workspace/code/newARCS/src/rtt/os/gnulinux/fosi_internal.cpp:101
#10 0xb148d295 in start_thread (arg=0xaecfeb40) at pthread_create.c:333
#11 0xb0e6a1ce in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:114

5.2 崩溃处的寄存器信息

(gdb) info registers

eax            0xd2f1a9fc          -755914244
ecx            0x12                18
edx            0xac163360          -1407831200
ebx            0xb0c5f000          -1329205248
esp            0xaecfd304          0xaecfd304
ebp            0xaecfd668          0xaecfd668
esi            0xac17b320          -1407732960
edi            0x4                 4
eip            0xb140478f          0xb140478f <Test() const+15>
eflags         0x10282             [ SF IF RF ]
cs             0x73                115
ss             0x7b                123
ds             0x7b                123
es             0x7b                123
fs             0x0                 0
gs             0x33                51

进一步排查,该指令处的汇编代码:x/10i $eip # 查看从eip寄存器指向的地址开始的10条汇编代码

(gdb) x/10i $eip
=> 0xb140478f <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+15>:	fldl   0x4c(%eax)
0xb1404792 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+18>:	lea    0x4(%eax),%esi
0xb1404795 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+21>:	mov    %edx,%edi
0xb1404797 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+23>:	rep movsl %ds:(%esi),%es:(%edi)
0xb1404799 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+25>:	fstpl  0x48(%edx)
0xb140479c <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+28>:	fldl   0x54(%eax)
0xb140479f <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+31>:	fstpl  0x50(%edx)
0xb14047a2 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+34>:	fldl   0x5c(%eax)
0xb14047a5 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+37>:	mov    %edx,%eax
0xb14047a7 <_ZNK3RKD20WobjGettorStationary13GetTargetWobjEv+39>:	fstpl  0x58(%edx)

或者直接用disassemble $eip,可以看到该指针上下文的汇编代码

    0xb1404780 <+0>:	push   %edi
    0xb1404781 <+1>:	mov    $0x12,%ecx
    0xb1404786 <+6>:	push   %esi
    0xb1404787 <+7>:	mov    0x10(%esp),%eax
    0xb140478b <+11>:	mov    0xc(%esp),%edx
=>  0xb140478f <+15>:	fldl   0x4c(%eax)
    0xb1404792 <+18>:	lea    0x4(%eax),%esi
    0xb1404795 <+21>:	mov    %edx,%edi
    0xb1404797 <+23>:	rep movsl %ds:(%esi),%es:(%edi)
    0xb1404799 <+25>:	fstpl  0x48(%edx)
    0xb140479c <+28>:	fldl   0x54(%eax)
    0xb140479f <+31>:	fstpl  0x50(%edx)
    0xb14047a2 <+34>:	fldl   0x5c(%eax)
    0xb14047a5 <+37>:	mov    %edx,%eax
    0xb14047a7 <+39>:	fstpl  0x58(%edx)
    0xb14047aa <+42>:	pop    %esi
    0xb14047ab <+43>:	pop    %edi
    0xb14047ac <+44>:	ret    $0x4

在 GDB 调试时,查看 info registers 命令的输出,可以看到所有 CPU 寄存器的当前状态。每个寄存器存储的信息可能有助于你理解程序的执行情况及其崩溃的原因。以下是寄存器输出的详细说明:

5.3 寄存器说明

  • eax, ecx, edx, ebx, esi, edi: 通用寄存器,用于存储函数的参数、返回值以及临时数据。
  • esp: 栈指针,指向当前栈的顶端。
  • ebp: 基址指针,指向当前栈帧的基址,用于访问函数的参数和局部变量。
  • eip: 指令指针,指向下一条将要执行的指令的地址。
  • eflags: 标志寄存器,包含条件码标志、控制标志、系统标志等。
  • cs: 代码段寄存器,指示代码段的起始地址。
  • ss: 栈段寄存器,指示栈段的起始地址。
  • ds, es, fs, gs: 数据段寄存器,用于指示数据段的起始地址。

5.4 具体解释

  • eax: 0xd2f1a9fc (-755914244) - 通常用于存储函数返回值或临时数据。
  • ecx: 0x12 (18) - 通常用于循环计数或传递函数参数。
  • edx: 0xac163360 (-1407831200) - 用于存储函数参数或临时数据。
  • ebx: 0xb0c5f000 (-1329205248) - 通常用于存储基址或临时数据。
  • esp: 0xaecfd304 (0xaecfd304) - 当前栈指针,指向栈顶。
  • ebp: 0xaecfd668 (0xaecfd668) - 当前栈帧基址,用于访问函数的参数和局部变量。
  • esi: 0xac17b320 (-1407732960) - 通常用于存储源地址。
  • edi: 0x4 (4) - 通常用于存储目标地址。
  • eip: 0xb140478f (0xb140478f <RKD::WobjGettorStationary::GetTargetWobj() const+15>) - 当前指令指针,指向下一条将要执行的指令`。
  • eflags: 0x10282 [ SF IF RF ] - 标志寄存器,包含条件码和其他控制标志。
    • SF (Sign Flag) - 标志结果为负值。
    • IF (Interrupt Enable Flag) - 允许中断。
    • RF (Resume Flag) - 控制调试断点的处理。

5.5 分析步骤

  • 查看指令指针 (eip):

    当前指令指针 (eip) 指向 0xb140478f,这是函数 RKD::WobjGettorStationary::GetTargetWobj() const 的某个位置(+15 字节)。这可能是导致 SIGSEGV 的位置。

  • 查看栈指针 (esp) 和基址指针 (ebp):

    esp 指向 0xaecfd304,ebp 指向 0xaecfd668。可以通过查看栈上的数据来了解崩溃前的调用情况和参数

  • 查看通用寄存器:

    可以检查通用寄存器的值是否在预期范围内,特别是 eax, ecx, edx, ebx, esi, 和 edi,以确定它们是否包含非法地址或未初始化的值。

  • 查看调用堆栈:

  • 使用 bt(backtrace)命令查看调用堆栈,找出导致崩溃的调用路径。

  • 检查源代码: 使用 list 命令查看崩溃位置的源代码,结合寄存器和堆栈信息,找出问题所在。

    (gdb) list *0xb140478f

进一步调试
根据以上信息,可以进一步使用 GDB 的其他命令进行调试:

  • 检查内存内容:

    (gdb) x/10i $eip # 查看指令指针周围的指令
    (gdb) x/10x $esp # 查看栈指针周围的内存

  • 打印变量和表达式:

    (gdb) print variable_name
    (gdb) info locals # 打印局部变量
    (gdb) info args # 打印函数参数

通过这些步骤,可以更详细地分析崩溃原因,找出非法内存访问的根本问题

6 调试线程

崩溃信息如下

Thread 17 "ABC" received signal SIGSEGV, Segmentation fault.

具体含义

  • Thread 17: 指出这是程序的第 17 个线程。多线程程序中会有多个线程同时执行,这个线程编号有助于我确定出错的具体线程。
  • ABC: 线程名称。如果程序为线程命名,这里会显示线程的名称,有助于识别线程的功能或角色。
  • received signal SIGSEGV: 程序收到了 SIGSEGV 信号,这是一个表示非法内存访问的信号。
  • segmentation fault: 即分段错误,表示程序尝试访问未分配的内存或进行非法的内存访问。

可能的原因

  • 访问空指针: 试图解引用一个空指针(NULL 指针)。
  • 访问未初始化的指针: 使用了尚未初始化的指针。
  • 缓冲区溢出: 超出数组或缓冲区的边界进行访问。
  • 无效指针: 使用了一个已经被释放的指针。
  • 越界访问: 超出分配内存区域的边界进行访问。

调试步骤

  • 查看崩溃位置: 使用 bt 命令(backtrace)查看调用堆栈,找出崩溃的具体位置。

      (gdb) bt
    
  • 查看当前线程信息: 确认当前线程的上下文和寄存器信息。

      (gdb) info threads
      (gdb) thread apply all bt  thread apply [thread-id-list | all] args
    
  • 切换到出错线程: 使用 thread 命令切换到特定线程。

      (gdb) thread 17
      (gdb) thread apply 17 bt
    
  • 查看代码位置: 使用 list 命令查看出错位置的源码。

      (gdb) list
    
  • 查看变量值: 使用 print 命令查看关键变量的值,以确定是否存在非法值或无效指针。

      (gdb) print variable_name
    
  • 检查内存地址: 查看访问的内存地址是否合法。

      (gdb) info registers
      (gdb) x/10x $sp  # 查看栈上的内存
    
posted @ 2024-07-29 09:40  绍荣  阅读(5)  评论(0编辑  收藏  举报