我最近在Windows中使用GCC练习x86程序集。我现在正在混合汇编和C代码用于测试目的。我遇到了一些奇怪的东西,我无法用现有的知识来解释,所以我求助于SO。
让我们看一个最小的例子。我们在C代码中有一个 test_func()函数,我们希望将其转换为程序集,然后再从C代码中调用它。这个功能虽然是从项目调用其他功能,但其中存在问题。从内部装配中调用其他一些C代码函数会给我一个分段错误。
我们想要在汇编中看到的功能:
#include "rf_string.h"
void test_func()
{//nonsense function, just calling another C function of the project
RF_String s;
rfString_Init(&s,"anything");
}
要求gcc为此吐出汇编代码给我们:
.file "to_asm.c"
.section .rdata,"dr"
.align 4
LC0:
.ascii "anything\0"
.text
.globl _test_func
.def _test_func; .scl 2; .type 32; .endef
_test_func:
LFB6:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $40, %esp
movl $LC0, 4(%esp)
leal -16(%ebp), %eax
movl %eax, (%esp)
movl _rfString_Init, %eax
call *%eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE6:
然后通过将其保存到名为“asm_test.S”的文件中并将其包含到项目的构建过程中,它构建并链接正常。所以我们尝试制作一个简单的程序来调用函数。
int main()
{
test_func();
return 0;
}
调试时出现问题。我从汇编代码中调用另一个C函数时出现了段错误。对于我尝试从汇编代码中调用的任何其他函数,都会发生这种情况,它与我使用的函数没有任何关系。
我认为这与函数调用有关:
movl %eax, (%esp)
movl _rfString_Init, %eax
call *%eax
为什么这样做,这是什么意思?具体为什么不简单地调用函数? %eax寄存器之前的*是什么?我还需要补充一点,如果我用下面的函数简单调用替换最后两行,一切似乎工作得非常好,这真让我感到困惑。
call _rfString_Init
另一个小问题是关于所有这些cfi_directives。如果我要通过回答SO中提出的问题来判断它们似乎是出于异常处理目的而生成的。问题是,就代码的功能而言,我可以安全地忽略它们吗?
答案 0 :(得分:1)
下面:
movl _rfString_Init, %eax
call *%eax
请注意$
之前没有_rfString_Init
(与movl $LC0, 4(%esp)
不同)。这意味着_rfString_Init
不是立即常量(例如,函数或对象的常量地址或只是常量),而是一个内存变量。
因此,movl
使用名为eax
的变量的内容加载_rfString_Init
。
然后call
会调用eax
中包含的地址。这个星是一些语法糖,表明控制是通过引用/指针间接传递的。
因此,rfString_Init
实际上是指向函数的指针。在你的C代码中查找它!
当然,如果你试图将控制转移到指针中包含的字节,那么没有任何好处,因为它的数据不会被解释为代码。此外,底层内存可能被配置为不可执行。
我对这些指令了解不多。它们也可以用于调试。无论如何,他们不会在你看到的地方插入代码。