无参数传递或返回值的C程序汇编函数

时间:2009-09-07 15:57:56

标签: assembly c gcc function parameter-passing

我需要创建一个汇编函数,将两个正数相加,由C程序调用。

C程序看起来像这样:

#include <stdio.h>

int main(void){
int a = 0;
int b = 0;
int c = 0;
printf( "Enter first number: " );
scanf( "%d", &a );
printf( "Enter second number: " );
scanf( "%d", &b );
sum();
printf( "Answer is %d\n", sum );
}

要求是汇编函数(sum())不应传递任何参数,也不应返回任何值。此外,如果重要的话,汇编函数位于一个单独的文件sum.s中。

我尝试了很多,并且阅读了很多。不过,我无法访问main()内的变量。谢谢您的帮助。 :)

4 个答案:

答案 0 :(得分:3)

到目前为止,这里的答案是关于一些非常复杂和特定于编译器的解决方案(实际上它们还取决于可能用于执行编译的选项)。

我想知道对于这个分配,所有正在寻找的是使用全局变量来传递参数和结果吗?

依赖于另一个函数中局部变量的布局以及它们与返回地址位置的关系相当......疯狂。

答案 1 :(得分:2)

没有参数传递似乎有点奇怪的要求 - 它可能导致相当脆弱的代码;通常你会得到正常传递参数的C代码,汇编函数会将它们从调用约定定义的相应寄存器和/或堆栈中拉出来,并将结果写在那里。

不过,如果这真的是你想要的,那是可行的。您需要做的是阅读编译器的ABI文档,以确定它如何布置其堆栈帧。然后你的汇编函数需要找出调用者的堆栈帧所在的位置 - 一个指针或它的偏移量,以及返回地址和任何参数,通常在调用任何函数时被压入堆栈 - 因此局部变量的位置a,b和c在记忆中。布局将取决于ABI,当您阅读这些文档时,您将发现它还取决于调用约定以及您在调用者的范围内本地声明的内容;可能还有优化水平。因此,您生成的汇编函数将非常紧密地绑定到当前的实现 - 脆弱 - 并且如果几乎任何更改都可能会中断。一般解决方案是不可能的。

顺便提一下,在printf( "Answer is %d\n",总和);行中,我认为您的意思是c,而不是sumsum将产生sum()函数的地址,该函数可能由编译器硬连接并在链接时修复,因此无法使该符号打印运行时结果。

答案 2 :(得分:1)

当你调用sum()时,编译器会将(在堆栈上)推送到当前指令指针的地址,然后跳转到子程序的位置。推送当前的指令是为了使子程序可以通过弹出地址返回。

因此,在子程序中,第一件事(即在底部,因为堆栈从顶部向底部推动)是返回地址。紧接在堆栈上方的是调用子例程的例程的局部变量。

因此,如果esp是堆栈指针寄存器,而[esp]sp指向的内存中的位置,并且假设是32位代码,则{{1}子例程应该发现像sum[esp+4][esp+8]这样的地址包含局部变量的地址。

为了确认我所说的内容,我建议您查看C代码发出的程序集:通过使用调试器来反汇编机器代码,或者使用编译器命令行选项生成程序集 - 语言列表文件。


编辑:moonshadow说这是一个“脆弱”的解决方案。例如,'[esp+12]'变量是存储在堆栈中还是存储在寄存器中(或者它是否甚至根本定义,而不是编译器假设它是硬编码的常量零)可能会有所不同取决于是否启用了编译器优化。

答案 3 :(得分:0)

我建议对堆栈的构建方式进行假设。这就是我想象的方式:

在函数调用之前:

-------
|  a  |
-------
|  b  |
-------
|  c  |
-------

在函数调用之后:

------- TOS
|  a  |
------- TOS - 4
|  b  |
------- TOS - 8
|  c  |
------- TOS - 12
| ret |
| eip | 
------- TOS - 16

你有:

ESP = TOS - 16

现在:

mov eax, ss:[esp + 12] ; eax = a
add eax, ss:[esp +  8] ; eax += b
mov ss:[esp +  4], eax ; c   = eax
ret

应该做的伎俩