ASM到C:如何取消引用指针并添加偏移量?

时间:2018-10-26 12:57:28

标签: c pointers assembly x86-64 intel

我有点愚蠢,但是我在用C取消对指针的引用(+添加偏移量)感到很苦恼。 我要在C中重新创建的是这种行为:

movabs rax, 0xdeadbeef
add rax, 0xa
mov rax, QWORD PTR [rax]

因此,结尾处的rax应该是:*(0xdeadbeef+0xa) 尤其是与mov rax, QWORD PTR [rax]等效的方法将是无足轻重的,因为我需要使用计算出的值并检索此时存储的数据(=不同的地址)。

我尝试了很多事情,但这是我目前的阶段:

void *ptr = (void*)0xdeadbeef;
void *ptr2 = *(void*)(ptr+0xa);

翻译成这样的东西:

   0x7ffff7fe6050:      mov    QWORD PTR [rbp-0x38],rax
   0x7ffff7fe6054:      mov    rax,QWORD PTR [rbp-0x38]
   0x7ffff7fe6058:      add    rax,0xa

编辑:它实际上没有编译,在这里提供的C代码中我犯了一个错误,并且无法弄清楚实际上是哪个代码对此编译。无论如何,它并不重要,因为主要目标是将ASM转换为C,现在问题已解决。感谢大家的参与。

因此,前2行基本上没有用,仅将值添加到我的地址中,仅此而已。我需要将其解释为一个地址并在那个时候检索值。

此时存储在那些位置的数据无关紧要。本质上,我想做的就是在内存中找到一个特定的值,并且我知道一种添加偏移量和取消引用指针以达到目标的方法。最后一步只是那一次从我的地址到实际数据类型的类型转换。

我知道这对某些人来说似乎是微不足道的,但是我对C并不十分熟悉,所以我在这里苦苦挣扎...

1 个答案:

答案 0 :(得分:3)

您可以通过汇编时完成的数学运算将asm简化为一条指令。 movabs rax, [0xdeadbeef + 0xa]可以使用从64位绝对地址(https://felixcloutier.com/x86/MOV.html)加载的仅AL / AX / EAX / RAX格式的mov。 (它不适合在32位符号扩展的disp32中使用,因为低32位的高位已设置,与位置相关代码中的普通静态地址不同)。具有32位地址大小覆盖的常规mov也可以在大约7个字节中工作,因为您的地址确实适合零扩展的32位整数。

在C语言中,您也可以只用一条语句完成整个事情。无需过于复杂:您的地址是指向的指针,因此您需要将整数转换为x **类型。

void *ptr = *(const void**)(0xdeadbeefUL + 0xa);

在asm中,指针只是整数,因此使用整数而不是char*进行数学运算是有意义的。使其成为无符号保证了它零扩展到指针宽度而不是符号扩展。

(尽管C中的数字文字具有足以表示该值的类型,因此,在x86-64编译器上的0xdeadbeef将是int64_tlong long)。实际上不会得到0xdeadbeef是一个负32位int,它的符号扩展到0xffffffffdeadbeef。)

由于void没有大小,因此无法向void*添加/减去整数。并且void **上的指针将成sizeof(void*)的块。

为避免未定义的行为取消引用void**(在两个主流x86-64 ABI中均未对齐)的8 = alignof(void*),请使用memcpy。但是我认为您的示例地址只是一个伪造的示例。诸如gcc之类的主流x86编译器不会对未对齐地址做任何奇怪的事情来惩罚UB程序员,因此编译器输出将包含未对齐负载,这些负载在x86上可以正常工作。但是,在自动向量化时,您会遇到这种UB带来的问题。 Why does unaligned access to mmap'ed memory sometimes segfault on AMD64?


但是,如果您出于某种原因想要将其分解为多个asm语句,则您可以将其音译为多个C语句,如下所示:

uintptr_t wheres_the_beef = 0xdeadbeef;    // mov eax, 0xdeadbeef
wheres_the_beef += 0xa;                    // add eax, 0xa
void **address = (void**)wheres_the_beef;  // purely a cast, no asm instructions;
void *ptr = *address;                      // mov rax, [rax]

如果您想为指针添加字节偏移,则可以弄乱char*,但是这里确实没有意义。

同样,在大多数C实现中,它仍然具有未定义的行为,其中alignof(void*)大于1,因此void **address = (void**)wheres_the_beef创建了未对齐的指针。

(有趣的事实:在ISO C中,即使创建未对齐的指针也是UB。但是所有支持Intel内在函数的x86编译器都必须支持创建未对齐的指针,以便将它们传递给_mm_loadu_ps()之类的内在函数,因此仅实际上取消引用它们是x86编译器上的潜在问题。)