0

我需要创建一个汇编函数,将两个正数相加,由 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

4 回答 4

3

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

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

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

于 2009-09-07T17:10:23.297 回答
2

没有参数传递似乎有点奇怪——它可能会导致代码非常脆弱;通常,您将让 C 代码正常传递参数,并且汇编函数将从调用约定定义的适当寄存器和/或堆栈中提取它们,并将结果写入那里。

不过,如果这确实是您想要的,那是可行的。您需要做的是阅读编译器的 ABI 文档以了解它如何布置其堆栈框架。然后,您的汇编函数将需要确定调用者的堆栈帧在哪里——指向它的指针或偏移量,以及返回地址和任何参数,通常在调用任何函数时被压入堆栈——因此局部变量在哪里a、b 和 c 在内存中。布局将取决于 ABI,当您阅读这些文档时,您会发现它还取决于调用约定以及您在调用者的范围内本地声明的内容;以及可能的优化级别。因此,您生成的汇编函数将非常紧密地绑定到您当前的实现 - 脆弱 - 如果几乎有任何变化,可能会中断。

顺便说一句,在你的printf( "Answer is %d\n",总和);线上,我认为你的意思是c,不是sumsum将产生 sum() 函数的地址,该地址可能会被编译器硬连线并在链接时固定,因此无法使该符号打印运行时结果。

于 2009-09-07T16:12:27.250 回答
1

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

因此,在子程序内部,首先(即在底部,因为堆栈从上到下推送)是返回地址。紧接在堆栈之上的是调用子程序的程序的局部变量。

因此,如果esp是堆栈指针寄存器,并且[esp]是内存中sp指向的位置,并且假设是 32 位代码,则sum子程序应该找到类似[esp+4]、 、 的地址[esp+8],并[esp+12]包含局部变量的地址。

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


编辑:moonshadow 关于这是一个“脆弱”的解决方案是正确的。例如, ' c' 变量是存储在堆栈中还是存储在寄存器中(或者它是否被定义,而不是编译器假设它是一个硬编码的常量零)可能会有所不同,这取决于编译器是否优化已启用。

于 2009-09-07T16:10:17.520 回答
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

应该做的伎俩

于 2009-09-07T16:16:09.860 回答