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_t
(long 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 编译器上的一个潜在问题。)
我觉得有点笨,但我正在为在 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_t
(long 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 编译器上的一个潜在问题。)