![为什么 PTRACE_PEEKUSER 不允许读取 x86_64 上的 FPU 寄存器?](https://linux22.com/image/221681/%E4%B8%BA%E4%BB%80%E4%B9%88%20PTRACE_PEEKUSER%20%E4%B8%8D%E5%85%81%E8%AE%B8%E8%AF%BB%E5%8F%96%20x86_64%20%E4%B8%8A%E7%9A%84%20FPU%20%E5%AF%84%E5%AD%98%E5%99%A8%EF%BC%9F.png)
PTRACE_PEEKUSER
被记录为从该区域读取一个单词user
,其在 x86_64 上的部分布局是:
struct user
{
struct user_regs_struct regs;
int u_fpvalid;
struct user_fpregs_struct i387;
//...
int u_debugreg [8];
};
虽然人们可以愉快地使用或PTRACE_PEEKUSER
内的偏移量进行调用,但传递位于 内的偏移量会无条件返回。regs
u_debugreg
i387
EIO
查看内核中的代码,我可以看到内部读取的regs
和u_debugreg
是唯一受支持的偏移量:
case PTRACE_PEEKUSR: {
unsigned long tmp;
ret = -EIO;
if ((addr & (sizeof(data) - 1)) || addr >= sizeof(struct user))
break;
tmp = 0; /* Default return condition */
if (addr < sizeof(struct user_regs_struct))
tmp = getreg(child, addr);
else if (addr >= offsetof(struct user, u_debugreg[0]) &&
addr <= offsetof(struct user, u_debugreg[7])) {
addr -= offsetof(struct user, u_debugreg[0]);
tmp = ptrace_get_debugreg(child, addr / sizeof(data));
}
ret = put_user(tmp, datap);
break;
}
我知道里面i387
不支持阅读。我知道我可以通过调用来读取这些寄存器PTRACE_GETFPREGS
。我的问题是:是否有特定原因导致禁用使用PTRACE_PEEKUSER
和写入这些寄存器?PTRACE_POKEUSER
我的猜测是因为大多数字段都user_fpregs_struct
小于64位,因此读写可能会覆盖多个寄存器。但是,我正在编写一个公开的资源,并将对此进行注释,所以我宁愿确定我的猜测是正确的。
答案1
只是猜测:您想读取数学协处理器寄存器。你检查过内核中i387
的成员何时更新吗?struct user
我没有,而且我可能是错的,但我的猜测并不常见,但在诸如核心转储之类的情况下。
来自 arch/x86/include/asm/user_64.h
struct user {
/* We start with the registers, to mimic the way that "memory" is returned
from the ptrace(3,...) function. */
struct user_regs_struct regs; /* Where the registers are actually stored */
/* ptrace does not yet supply these. Someday.... */
int u_fpvalid; /* True if math co-processor being used. */
/* for this mess. Not yet used. */
int pad0;
struct user_i387_struct i387; /* Math Co-processor registers. */
:
unsigned long u_debugreg[8];
:
};
因此,读取数学协处理器寄存器最好通过从 FPU 读取来完成。如果您检查内核如何执行PTRACE_GETFPREGS
,您会发现它是从 FPU 而不是成员获取的struct user
i387
。使用 编写数学协处理器寄存器也是如此PTRACE_SETFPREGS
。
从arch_ptrace
case PTRACE_GETFPREGS: /* Get the child FPU state. */
return copy_regset_to_user(child,
task_user_regset_view(current),
REGSET_FP,
0, sizeof(struct user_i387_struct),
datap);
task_user_regset_view
x86_64_regsets
用于读取的返回值xfpregs_get
。
static struct user_regset x86_64_regsets[] __ro_after_init = {
[REGSET_FP] = {
.core_note_type = NT_PRFPREG,
.n = sizeof(struct user_i387_struct) / sizeof(long),
.size = sizeof(long), .align = sizeof(long),
.active = regset_xregset_fpregs_active, .regset_get = xfpregs_get, .set = xfpregs_set
},
}
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
:
#ifdef CONFIG_X86_64
return &user_x86_64_view;
#endif
}