C语言 使用 IMUL 指令将数组中的值相乘会产生不正确的值



我正在学习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 中,该寄存器将在 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)

值不正确的原因

如果我们忽略低效率和不必要的代码并处理真正的问题,那么它归结为以下指令:

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视为内存操作数,并且您希望将 32 位值移动到EAXinum


调用约定

您似乎正在创建一个 32 位程序并在 32 位 Ubuntu 上运行它。我可以通过返回 -1210688460 的错误值来推断 32 位 Linux 的可能性。-1210688460 = 0xB7D65C34除以 -9,得到 804A06C。32 位 Linux 上的程序通常从 0x8048000 开始加载

无论是在 32 位 Linux 还是 64 位 Linux 上运行,与 32 位 C/C++ 程序链接的汇编代码都需要遵守CDECL调用约定:

中环

cdecl(代表 C 声明)是一种源自 C 编程语言的调用约定,许多 C 编译器将其用于 x86 体系结构。1 在 cdeccl 中,子例程参数在堆栈上传递。整数值和内存地址在 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-O3GCC编译器选项来启用优化,则程序可能无法按预期工作或完全崩溃。

相关内容

最新更新