Mac*_*ack 4 c ubuntu x86 assembly gcc
我正在学习 ASM 语言并在 Ubuntu Eclipse C++ 上尝试 IMUL 函数,但由于某种原因,我似乎无法从我的代码中获得所需的输出。
必需的:
将整数数组 int_array 的负元素乘以指定的整数 inum
这是我上面的代码:
代码:
#include <stdio.h>
extern void multiply_function();
// Variables
int iaver, inum;
int int_ar[10] = {1,2,3,4,-9,6,7,8,9,10};
int main()
{
inum = 2;
multiply_function();
for(int i=0; i<10; i++){
printf("%d ",int_ar[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
ASM 代码:
extern int_ar
extern inum
global multiply_function
multiply_function:
enter 0,0
mov ecx, 10
mov eax, inum
multiply_loop:
cmp [int_ar +ecx*4-4], dword 0
jg .ifpositive
mov ebx, [int_ar +ecx*4-4]
imul ebx
cdq
mov [int_ar +ecx*4-4], eax
loop multiply_loop
leave
ret
.ifpositive:
loop multiply_loop
leave
ret
Run Code Online (Sandbox Code Playgroud)
对于一个数组:{1,2,3,4,-9,6,7,8,9,10}and inum,我得到的输出{1,2,3,4,-1210688460,6,7,8,9,10}提示发生了某种溢出。
关于 x86 汇编语言中的 IMUL 函数如何工作,我是否遗漏或理解错误?
我期望的输出是 {1,2,3,4,-18,6,7,8,9,10}
我对上述任务的思考过程:
1) 找出数组中哪些数组元素为负数,对于找到的每个正数元素,什么都不做,继续循环到下一个元素
cmp [int_ar +ecx*4-4], dword 0
jg .ifpositive
.ifpositive:
loop multiply_loop
leave
ret
Run Code Online (Sandbox Code Playgroud)
2) 找到负元素后,将其值移入寄存器 EBX,该寄存器将用作 IMUL SRC 功能中的 SRC。然后将寄存器 EAX 扩展到 EAX-EDX,结果存储在:
mov ebx, [int_ar +ecx*4-4]
imul ebx
cdq
Run Code Online (Sandbox Code Playgroud)
3) 使用 MOV 将结果移动到数组的负元素中:
mov [int_ar +ecx*4-4], eax
Run Code Online (Sandbox Code Playgroud)
4) 循环到下一个数组元素,重复上面的1)-3)
如果我们看看低效率和不需要的代码并处理真正的问题,它归结为以下指令:
mov eax, inum
Run Code Online (Sandbox Code Playgroud)
什么是inum?您在C 中创建并初始化了一个全局变量,调用方式inum为:
int iaver, inum;
[snip]
inum = 2;
Run Code Online (Sandbox Code Playgroud)
inum因为变量本质上是包含int(32 位值)的内存位置的标签。在您的汇编代码中,您需要将其inum视为指向值的指针,而不是值本身。在您的汇编代码中,您需要更改:
mov eax, inum
Run Code Online (Sandbox Code Playgroud)
到:
mov eax, [inum]
Run Code Online (Sandbox Code Playgroud)
您的版本所做的是将 的地址移动inum到EAX 中。您的代码最终将变量的地址乘以数组中的负数。这会导致您看到的错误值。周围的方括号inum告诉汇编器您想将其inum视为内存操作数,并且您想将 32 位值 at 移动inum到EAX 中。
您似乎正在创建一个 32 位程序并在 32 位 Ubuntu 上运行它。我可以通过返回的错误值 -1210688460 来推断 32 位 Linux 的可能性。-1210688460 = 0xB7D65C34 除以 -9 得到 804A06C。32 位 Linux 上的程序通常从 0x8048000 开始加载
无论是在 32 位 Linux 还是 64 位 Linux 上运行,与 32 位 C/C++ 程序链接的汇编代码都需要遵守CDECL调用约定:
cdecl
cdecl(代表 C 声明)是一种调用约定,它起源于 C 编程语言,被许多用于 x86 架构的 C 编译器使用。1在 cdecl 中,子程序参数在堆栈上传递。整数值和内存地址在 EAX 寄存器中返回,浮点值在 ST0 x87 寄存器中返回。寄存器 EAX、ECX 和 EDX 是调用者保存的,其余的都是被调用者保存的。调用新函数时,x87 浮点寄存器 ST0 到 ST7 必须为空(弹出或释放),退出函数时 ST1 到 ST7 必须为空。当不用于返回值时,ST0 也必须为空。
您的代码破坏了EAX、EBX、ECX和EDX。您可以随意销毁EAX、ECX和EDX的内容,但必须保留 EBX。如果不这样做,可能会导致调用该函数的C代码出现问题。在你做完enter 0,0指令之后,你可以push ebx在每条leave指令之前你可以做pop ebx
如果您要使用-O1、-O2或-O3 GCC编译器选项来启用优化,您的程序可能无法按预期运行或完全崩溃。