Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/gwt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C中从ASM调用C函数?_C_Function_Assembly - Fatal编程技术网

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

如何在C中从ASM调用C函数?,c,function,assembly,C,Function,Assembly,我正在使用这段代码,但调试时它会给我分段错误。我正在和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("Cal

我正在使用这段代码,但调试时它会给我
分段错误
。我正在和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;
}
#包括
#包括
int CallMe(int a,int b)
{
printf(“称为”);
返回a+b;
}
int main()
{
printf(“在0x00%x\n处调用我”,(无符号int)和调用我);
printf(“呼叫呼叫我…”);
asm(
movl$5,(%eax);\r\n
movl$4,(%ebx);\r\n
“调用0x00401334;\r\n”
);
printf(“\n”);
返回0;
}
功能地址正常。有什么想法吗


提前感谢。

通常,不要通过硬编码地址调用函数。尝试调用函数名。函数名实际上是函数地址

call 0x00401334
==>


通常,不要通过硬编码地址调用函数。尝试调用函数名。函数名实际上是函数地址

call 0x00401334
==>


正如@EricZ指出的,第一个问题是呼叫

然而,修复这一问题并不一定能消除故障。你的两个动作也造成了一个问题:

movl $5, (%eax)
movl $4, (%ebx)
这将值
5
4
移动到地址分别位于
%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
或者,如果函数希望它们出现在寄存器中(我不是说在这种情况下会出现,但如果…),请删除括号:

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

您可能需要仔细阅读调用约定,例如。

正如@EricZ所指出的,第一个问题是调用

然而,修复这一问题并不一定能消除故障。你的两个动作也造成了一个问题:

movl $5, (%eax)
movl $4, (%ebx)
这将值
5
4
移动到地址分别位于
%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
或者,如果函数希望它们出现在寄存器中(我不是说在这种情况下会出现,但如果…),请删除括号:

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

您可能需要详细了解调用约定,例如。

这是您的
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
我以前遇到的关于堆栈不平衡的问题是由我的代码中的一个错误引起的,该错误使用了
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)
);

这就是
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
我以前遇到的关于堆栈不平衡的问题是由我的代码中的一个错误引起的,该错误使用了
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)
);

尝试将-S选项传递给编译器以查看生成了什么程序集(输出将是一个.S文件)。将此与尝试在C中直接调用函数(不使用asm块)时进行比较。这会给你一个很好的线索。如果没有,请在此处发布两个输出。您如何确定“函数地址正常?”首先,它可能不正常,如果操作系统使用地址空间布局随机化(ASLR),它甚至可能在运行时随机更改。@datenwolf,代码打印函数地址。我假设它按照call指令打印相同的地址。@davmac:嗯,printf调用实际上是错误的。它应该使用
%p
格式并强制转换为
void*
,这样才有用。GCC内联程序集还允许在调用指令中使用符号,避免了一起硬编码的需要。为什么不直接调用函数名?尝试将-S选项传递给编译器,以查看生成了什么程序集(输出将是一个.S文件)。将此与尝试在C中直接调用函数(不使用asm块)时进行比较。这会给你一个很好的线索。如果没有,请在此处发布两个输出。您如何确定“函数地址正常?”首先,它可能不正常,如果操作系统使用地址空间布局随机化(ASLR),它甚至可能在运行时随机更改。@datenwolf,代码打印函数地址。我假设它按照call指令打印相同的地址。@davmac:嗯,printf调用实际上是错误的。它应该使用
%p
格式并强制转换为
void*
,这样才有用。GCC内联汇编还允许在调用指令中使用符号,避免了硬编码的需要。为什么不直接调用函数名呢?这只是因为C不会损坏链接器名,因此当链接器看到“CallMe”时,它知道它应该是什么函数。请注意,C++中不能这样做,因为函数名并不总是与您使用的函数匹配(可能的话,它们可能取决于编译器)。虽然这是一个值得推荐的建议,但它实际上不是对提出的问题的回答,应该是注释。谢谢您的回答。使用
call CallMe
会给我一个
未定义的ref