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

ASM to C: how to dereference a pointer and add an offset?

我觉得有点笨,但我正在为在 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,现在问题已解决。感谢大家的参与

所以前两行基本上没有用,只是将值添加到我的地址,仅此而已。不过,我需要将它解释为一个地址并在那时检索值。

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

我知道这对你们中的一些人来说似乎微不足道,但我对 C 不是很熟悉,所以我在这里挣扎...

您可以将 asm 简化为一条指令,并在 assemble 时间完成数学运算。 movabs rax, [0xdeadbeef + 0xa] 可以使用从 64 位绝对地址 (https://felixcloutier.com/x86/MOV.html) 加载的 AL/AX/EAX/RAX-only 形式的 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 作为符号扩展为 0xffffffffdeadbeef 的负 32 位 int。)

由于 void 没有大小,您不能对 void* 添加/减去整数。 void ** 上的指针数学将在 sizeof(void*).

的块中

为了避免取消引用 8 = alignof(void*) 未对齐的 void** 的未定义行为(在主流 x86-64 ABI 中),您需要使用 memcpy。但我假设您的示例地址只是一个假示例。像 gcc 这样的主流 x86 编译器不会对未对齐的地址做任何奇怪的事情来惩罚程序员的 UB,因此编译器输出将包含未对齐的负载,这在 x86 上运行良好。但是当自动矢量化时,你可以 运行 解决这种 UB 的问题。


但是如果你出于某种原因想要将事情分解成多个 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 编译器上的一个潜在问题。)