本文介绍了gdb如何读取正在调试的程序/进程的寄存器值?寄存器与流程如何关联?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wrote a short program in c++ :

#include<iostream>
using namespace std;

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

出于好奇,我想了解一个隐藏的程序,所以我在玩gdb&当我在gdb中使用info registers时,出现了acrooss info registers命令.我得到这样的输出:

just out of curiosity i wanted to understand a program behind the hood so i was playing with gdb & came acrooss info registers command .when i use info registers in gdb i get output like this:

(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&这是我发现的,但仍然令人困惑.

I understand these are registers and their values but what I want to know is how/why are registers associated with a process. the values of registers should be changing continuously as different processes are scheduled by the operating system? I referred to the command info registers & this is what I found but this is still confusing.

推荐答案

每个线程/进程都有其自己的寄存器值.用户空间" 建筑状态 "(注册值)为通过系统调用或中断进入内核时保存. (在所有操作系统上都是如此).

Each thread/process has its own register values. The user-space "architectural state" (register values) is saved on entering the kernel via a system call or interrupt. (This is true on all OSes).

请参见看看Linux的系统调用入口点,而实际上是手写的asm将寄存器保存在进程的内核堆栈中. (在Linux中,每个线程都有自己的内核堆栈.)

See What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? for a look at Linux's system-call entry points, with the hand-written asm that actually saves registers on the process's kernel stack. (Each thread has its own kernel stack, in Linux).

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

In multi-tasking OSes in general, every process/thread has its own memory space for saving state, so context switches work by restoring the saved state from the thread being switched to. This is a bit of a simplification, because there's kernel state vs. saved user-space. state

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

So any time a process isn't actually running on a CPU core, its register values are saved in memory.

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

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

In Linux, this API is the ptrace(2) system call; it's what GDB uses to read register values and to single-step. Thus, GDB reads saved register values of the target process from memory, indirectly via the kernel. GDB's own code doesn't use any special x86 instructions, or even load / store from any special addresses; it just makes system calls because access to another process's state has to go through the kernel. (Well I think a process could map another process's memory into its own address space, if Linux even has a system call for that, but I think memory reads/writes actually go through ptrace just like register accesses.)

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

(I think) If the target process was currently executing (instead of suspended) when another process made a ptrace system call that read or wrote one of its register values, the kernel would have to interrupt it so its current state would be saved to memory. This doesn't normally happen with GDB: it only tries to read register values when it's suspended the target process.

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

ptrace is also what strace uses to trace system calls. See Playing with ptrace, Part I from Linux Journal. strace ./my_program is fantastically useful for systems programming, especially when making system calls from hand-written asm, to decode the args you're actually passing, and the return values.

脚注:

  1. 在Linux中,从内核上下文到内核上下文的实际切换是在内核内部进行的.这将仅"保存整数寄存器在内核堆栈上,将rsp设置在另一个线程的内核堆栈中的正确位置,然后恢复保存的寄存器.因此,有一个函数调用,当它返回时,将在内核模式下为新线程执行,并适当设置每个CPU的内核变量.新线程的用户空间状态最终将以与原来从用户空间进入内核的系统调用或中断返回而未调用调度程序的方式相同的方式恢复.即从系统调用或中断内核入口点保存的状态开始.懒/急于保存FPU状态是另一个难题.内核通常避免触摸FPU,这样就可以避免在进入内核并返回相同的用户空间进程时保存/恢复FPU状态.
  1. In Linux, the actual switch to a new thread happens inside the kernel, from kernel context to kernel context. This saves "only" the integer registers on the kernel stack, sets rsp to the right place in the other thread's kernel stack, then restores the saved registers. So there's a function call that, when it returns, is executing in kernel mode for the new thread, with per-CPU kernel variables set appropriately. User-space state for the new thread is eventually restored the same way it would have been if the system call or interrupt that originally entered the kernel from user-space had returned without calling the scheduler. i.e. from the state saved by the system call or interrupt kernel entry point. Lazy / eager FPU state saving is another complication; the kernel generally avoids touching the FPU so it can avoid saving/restoring FPU state when just entering the kernel and returning back to the same user-space process.

这篇关于gdb如何读取正在调试的程序/进程的寄存器值?寄存器与流程如何关联?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-12 00:51