我正在学习ASM语言并在Ubuntu Eclipse C ++上试用IMUL函数,但出于某种原因我似乎无法从我的代码中获得所需的输出。
必需:
将整数数组int_array的负数元素乘以指定的整数inum
这是我的上述代码:
C代码:
#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]);
}
}
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
对于{1,2,3,4,-9,6,7,8,9,10}
和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
2)找到负面元素后,将其值移动到寄存器EBX中,该寄存器EBX将作为IMUL SRC功能中的SRC。然后将寄存器EAX扩展到EAX-EDX,其结果存储在:
mov ebx, [int_ar +ecx*4-4]
imul ebx
cdq
3)使用MOV:
将结果移动到数组的负元素中mov [int_ar +ecx*4-4], eax
4)循环到下一个数组元素并重复上述1)-3)
答案 0 :(得分:4)
如果我们回顾一下效率低下和不需要的代码并处理真正的问题,那就归结为这条指令:
mov eax, inum
什么是inum
?您在 C 中创建并初始化了一个名为inum
的全局变量,其中包含:
int iaver, inum;
[snip]
inum = 2;
inum
作为变量本质上是包含int
(32位值)的内存位置的标签。在汇编代码中,您需要将inum
视为指向值的指针,而不是值本身。在汇编代码中,您需要更改:
mov eax, inum
为:
mov eax, [inum]
您的版本所做的是将inum
的地址移至 EAX 。您的代码最终将变量的地址乘以数组中的负数。这会导致你看到错误的价值观。 inum
周围的方括号告诉汇编器要将inum
视为内存操作数,并且要将inum
处的32位值移动到 EAX
您似乎正在创建一个32位程序并在32位Ubuntu上运行它。我可以通过返回错误值-1210688460来推断32位Linux的可能性。 -1210688460 = 0xB7D65C34除以-9,得到804A06C。 32位Linux上的程序通常从0x8048000开始加载
无论是在32位Linux还是64位Linux上运行,与32位C / C ++程序链接的汇编代码都需要遵守CDECL calling convention:
的cdecl
cdecl(代表C声明)是一种调用约定,它源自C编程语言,并被许多C编译器用于x86架构。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 编译器选项来启用优化,您的程序可能按预期工作或完全崩溃。