简体   繁体   English

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

[英]How to call a C function from ASM in C?

I'm using this code but it gives me Segmentation fault when debugging. 我正在使用此代码,但调试时会出现Segmentation fault I'm compiling with MinGW: 我正在使用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;
}

The function address is ok. 功能地址可以。 Any ideas? 有任何想法吗?

Thanks in advance. 提前致谢。

First problem is the call, as @EricZ points out. @EricZ指出,第一个问题是通话。

However, fixing that won't necessarily make the segfault go away. 但是,修复该问题并不一定会使该段错误消失。 Your two moves are also causing a problem: 您的两个举动也引起了问题:

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

This is moving values 5 and 4 into locations whose addresses are in %eax and %ebx , respectively. 这会将值54移动到地址分别位于%eax%ebx In this particular program, those addresses haven't been defined. 在此特定程序中,尚未定义这些地址。 If you want to pass them as parameters, you need to push them onto the stack. 如果要将它们作为参数传递,则需要将它们压入堆栈。

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

Or, if the function expects them in the registers (I'm not saying it would in this case, but if ...), remove the parentheses: 或者,如果函数期望它们在寄存器中(我不是说在这种情况下会这样做,但是如果 ...),请删除括号:

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

You might want to read up on calling conventions, eg, http://en.wikipedia.org/wiki/X86_calling_conventions . 您可能需要阅读有关调用约定的信息,例如http://en.wikipedia.org/wiki/X86_calling_conventions

Generally, don't call the function by its hard-coded address. 通常,请勿通过其硬编码地址来调用该函数。 Try call the function name instead. 尝试改为调用函数名称。 Function name is effectively the function address. 函数名称实际上是函数地址。

call 0x00401334

==> ==>

call CallMe

This is what is really executing in your main() function: 这是在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

So the question is now: why a CALL 0x00401218 is assembled as CALL 0x008024C1 ? 所以现在的问题是:为什么将CALL 0x00401218组装为CALL 0x008024C1

Of course, you will see also that parameters are not being passed by registers, but by pushing them into the stack. 当然,您还将看到参数不是通过寄存器传递的,而是通过将它们压入堆栈传递的。

In fact, I've got it by using this: 实际上,我通过使用以下命令获得了它:

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

The problem I had before, about stack not bein balanced, was caused by a mistake in my code, that used addl %esp,8 instead of addl %esp,$8 . 我之前遇到的关于堆栈不平衡的问题是由我的代码中的错误引起的,该错误使用addl %esp,8而不是addl %esp,$8 The former (tries to) add the contents of memory at address 8 to ESP , instead of adding the constant 8 to ESP . 前者(尝试)将地址8的内存内容添加到ESP ,而不是将常量8添加到ESP

If you want to collect the result of your function and save it, for example, in a variable, you can do as this: 如果要收集函数结果并将其保存(例如,保存在变量中),可以执行以下操作:

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)
);

If you really want to call the function using its address, you can do it several ways: 如果您确实想使用其地址来调用该函数,则可以采用以下几种方法:

By putting the address in a variable, say, a void pointer: 通过将地址放在变量中,例如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)
);

Or by putting the address to call in a register: 或通过将要调用的地址放入寄存器中:

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)
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM