如何在C中从ASM调用C函数?

时间:2013-12-24 01:00:35

标签: c function assembly

我正在使用此代码,但在调试时它会给我Segmentation fault。我正在使用MinGW进行编译:

#include <stdio.h>
#include <stdlib.h>

int CallMe(int a, int b)
{
    printf("Called.");
    return a + b;
}

int main()
{
    printf("CallMe at 0x00%x\n", (unsigned int)&CallMe);
    printf("Calling CallMe... ");

    asm (
        "movl $5, (%eax);\r\n"
        "movl $4, (%ebx);\r\n"
        "call 0x00401334;\r\n"
    );

    printf("\n");

    return 0;
}

功能地址没问题。有什么想法吗?

提前致谢。

3 个答案:

答案 0 :(得分:4)

第一个问题是电话,正如@EricZ指出的那样。

然而,解决这个问题并不一定会使segfault消失。你的两个动作也引起了一个问题:

movl $5, (%eax)
movl $4, (%ebx)

这会将值54移动到地址分别位于%eax%ebx的位置。在这个特定的程序中,尚未定义这些地址。如果要将它们作为参数传递,则需要将它们推入堆栈。

pushl $5
pushl $4
call CallMe     % With some compilers, this may need to be _CallMe
addl  $8,%esp   % assuming a 32-bit processor

或者,如果函数在寄存器中需要它们(我不是说它会在这种情况下,但 if ...),删除括号:

movl $5, %eax
movl $4, %ebx

您可能希望了解调用约定,例如http://en.wikipedia.org/wiki/X86_calling_conventions

答案 1 :(得分:2)

通常,请勿通过硬编码地址调用该函数。请尝试调用函数名称。函数名称实际上是函数地址。

call 0x00401334

==&GT;

call CallMe

答案 2 :(得分:2)

这是main()函数中实际执行的内容:

0x401268 <main>:    push   %ebp
0x401269 <main+1>:  mov    %esp,%ebp
0x40126b <main+3>:  sub    $0x8,%esp
0x40126e <main+6>:  call   0x401350 <__main>
0x401273 <main+11>: add    $0xfffffff8,%esp
0x401276 <main+14>: push   $0x401218  <--- address of CallMe
0x40127b <main+19>: push   $0x401242
0x401280 <main+24>: call   0x4013c0 <printf>
0x401285 <main+29>: add    $0x10,%esp
0x401288 <main+32>: add    $0xfffffff4,%esp
0x40128b <main+35>: push   $0x401250
0x401290 <main+40>: call   0x4013c0 <printf>
0x401295 <main+45>: add    $0x10,%esp
0x401298 <main+48>: movl   $0x5,(%eax)  <--- begin of your ASM block
0x40129e <main+54>: movl   $0x4,(%ebx)
0x4012a4 <main+60>: call   0x8024c1  <--- eewww! wrong address

现在的问题是:为什么CALL 0x00401218汇编成CALL 0x008024C1

当然,您还会看到寄存器没有传递参数,而是将它们推入堆栈。

事实上,我通过使用它得到了它:

asm (
    "pushl $5;\r\n"
    "pushl $4;\r\n"
    "call _CallMe;\r\n"
    "addl %esp,$8;\r\n"
);

我之前遇到的问题,关于堆栈不平衡,是由于我的代码中的错误,使用了addl %esp,8而不是addl %esp,$8。前者(尝试)将地址8的内存内容添加到ESP,而不是将常量8添加到ESP

如果您想收集函数的结果并将其保存,例如,在变量中,您可以这样做:

int res;
...
...

asm (
    "pushl $5;\r\n"
    "pushl $4;\r\n"
    "call _CallMe;\r\n"
    "addl $8,%%esp;\r\n"
    "movl %%eax,%0;\r\n" : "=m" (res)
);

如果您真的想使用其地址调用该函数,可以采取以下几种方式:

将地址放在变量中,例如,void指针:

void *addr;

addr = (void *)CallMe;
asm (
    "pushl $5;\r\n"
    "pushl $4;\r\n"
    "calll *%1;\r\n"
    "addl $8,%%esp;\r\n"
    "movl %%eax,%0;\r\n" : "=m" (res) : "m" (addr)
);

或者通过将地址调用到寄存器中:

asm (
    "pushl $5;\r\n"
    "pushl $4;\r\n"
    "movl $0x00401218,%%eax;\r\n"
    "calll *%%eax;\r\n"
    "addl $8,%%esp;\r\n"
    "movl %%eax,%0;\r\n" : "=m" (res)
);