繁体   English   中英

gdb如何读取正在调试的程序/进程的寄存器值? 寄存器与流程如何关联?

[英]How does gdb read the register values of a program / process it's debugging? How are registers associated with a process?

我用c ++编写了一个简短的程序:

#include<iostream>
using namespace std;

    int main(){
    int x=10;
    int y=20;
    cout<< x+y <<endl;
    return 0;
    }

只是出于好奇,我想了解一个隐藏的程序,所以我在玩gdb并出现了acrooss info registers命令。当我在gdb使用info registers ,我会得到如下输出:

(gdb) info registers
rax            0x400756 4196182
rbx            0x0  0
rcx            0x6  6
rdx            0x7fffffffd418   140737488344088
rsi            0x7fffffffd408   140737488344072
rdi            0x1  1
rbp            0x7fffffffd320   0x7fffffffd320
rsp            0x7fffffffd320   0x7fffffffd320
r8             0x7ffff7ac1e80   140737348640384
r9             0x7ffff7dcfea0   140737351843488
r10            0x7fffffffd080   140737488343168
r11            0x7ffff773a410   140737344939024
r12            0x400660 4195936
r13            0x7fffffffd400   140737488344064
r14            0x0  0
r15            0x0  0
rip            0x40075a 0x40075a <main+4>
eflags         0x246    [ PF ZF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0

我了解这些是寄存器及其值,但是我想知道的是/为什么registersprocess相关联。 寄存器的值应随着操作系统调度的不同进程而不断变化? 我提到了命令info registers ,这是我发现的内容,但这仍然令人困惑。

info寄存器->打印所有寄存器的名称和值(浮点和矢量寄存器除外)(在所选堆栈帧中)。

寄存器一直在变化。 实际上,即使调试器也必须更改寄存器值,因为它必须自己运行。

但是,当您使用调试器查看程序时,调试器会挂起正在运行的进程。 作为挂起的一部分,CPU状态将保存到RAM。 调试器了解这一点,并且可以查看RAM中的挂起状态。 假设寄存器R1在挂起时已保存到地址0x1234 ,则调试器可以仅打印存储在该地址的字节。

每个线程/进程都有其自己的寄存器值。 用户空间的“ 体系结构状态 ”(寄存器值)在通过系统调用或中断进入内核时被保存。 (在所有操作系统上都是如此)。

请参阅如果以64位代码使用32位int 0x80 Linux ABI会发生什么? 查看Linux的系统调用入口点,以及实际将寄存器保存在进程内核堆栈中的手写asm。 (在Linux中,每个线程都有其自己的内核堆栈)。

通常,在多任务OS中,每个进程/线程都有其自己的用于保存状态的内存空间,因此上下文切换通过从要切换到的线程恢复保存的状态来进行。 这有点简化,因为存在内核状态与节省的用户空间。 状态1


因此,只要进程实际上不在CPU内核上运行,它的寄存器值就会保存在内存中。

操作系统提供了用于读取/写入其他进程的已保存寄存器状态和内存的API

在Linux中,此API是ptrace(2)系统调用。 这就是GDB用来读取寄存器值和单步执行的操作。 因此,GDB通过内核间接从内存中读取目标进程的已保存寄存器值。 GDB自己的代码不使用任何特殊的x86指令,甚至不从任何特殊地址加载/存储; 它只是进行系统调用,因为对另一个进程状态的访问必须通过内核。 (好吧,我认为一个进程可以将另一个进程的内存映射到它自己的地址空间,如果Linux甚至有系统调用的话,但是我认为内存读写实际上就像通过寄存器访问一样通过ptrace进行。)

(我认为)如果目标进程当前正在执行(而不是挂起),而另一个进程进行了ptrace系统调用来读取或写入其寄存器值之一,则内核将不得不中断它,以便将其当前状态保存到内存中。 对于GDB,通常不会发生这种情况:它仅在挂起目标进程时尝试读取寄存器值。


ptrace也是strace用来跟踪系统调用的内容。 请参阅《 Linux Journal》 第一部分的“使用ptrace玩 ”。 strace ./my_program对于系统编程非常有用,尤其是在通过手写asm进行系统调用,解码实际传递的args和返回值时。


脚注:

  1. 在Linux中,从内核上下文到内核上下文的实际切换是在内核内部进行的。 这将“仅”保存整数寄存器在内核堆栈上,将rsp设置在另一个线程的内核堆栈中的正确位置,然后恢复保存的寄存器。 因此,有一个函数调用在返回时以新线程的内核模式执行,并且适当设置了每个CPU内核变量。 新线程的用户空间状态最终将以与原来从用户空间进入内核的系统调用或中断返回而未调用调度程序的方式相同的方式恢复。 即从系统调用或中断内核入口点保存的状态开始。 懒/急于保存FPU状态是另一个难题。 内核通常避免触摸FPU,因此当进入内核并返回相同的用户空间进程时,它可以避免保存/恢复FPU状态。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM