【发布时间】:2016-08-19 13:45:20
【问题描述】:
我正在尝试学习如何使用 ptrace 库来跟踪所有系统调用及其参数。我坚持将参数传递给系统调用。
我浏览了许多在线资源和 SO 问题,发现在 64 位机器上,参数存储在寄存器 rax(sys call number), rdi, rsi, rdx, r10, r8, r9
以相同的顺序。检查this website。
为了确认这一点,我写了一个简单的 C 程序如下
#include<stdio.h>
#include<fcntl.h>
int main() {
printf("some print data");
open("/tmp/sprintf.c", O_RDWR);
}
并为此使用gcc -S t.c生成了汇编代码,但生成的汇编代码如下
.file "t.c"
.section .rodata
.LC0:
.string "some print data"
.LC1:
.string "/tmp/sprintf.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
movl $0, %eax
call printf
movl $2, %esi
movl $.LC1, %edi
movl $0, %eax
call open
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits
如您所见,此代码将参数存储在 esi 和 edi 上。
为什么会发生?
另外请指导我从 C 代码中的这些寄存器/内存位置访问这些传递的参数的最佳方法是什么?如何确定寄存器的内容是参数本身还是存储实际参数的内存位置?
谢谢!
【问题讨论】:
-
在 x86 64 位处理器上,如果您更改 32 位寄存器的内容,零扩展结果将放入整个 64 位寄存器。使用 32 位寄存器可以缩短指令的编码。 EDI是寄存器RDI的低32位,ESI是RSI的低32位
-
@MichaelPetch:我正在尝试通过 ptrace 调用访问这些寄存器。因此,从您的评论来看,即使我尝试访问 rdi,它也应该给我正确的数据,对吗?
-
如果你在 32 位寄存器中放置一个 vale,它应该与 64 位寄存器的值相同,但宽度为 64 位。所以看64位寄存器是正确的。因此,尽管写入了 EDI,但可以通过 64 位寄存器 RDI 查看内容。
-
谢谢!如果是字符串,值如何存储?它们是直接存储在寄存器中还是寄存器指向实际值所在的内存位置?
-
假设您使用的是 AT&T 语法,在这种情况下,如果您看到前面带有
$符号的标签,例如$.LC0,它告诉汇编程序使用标签(这是它的地址)而不是存储在该地址的内容。您通常将指针传递给字符串。movl $.LC0, %edi将字符串“some print data”的address移动到EDI中。