gpt4 book ai didi

linux - 组装 - NASM 析因问题

转载 作者:太空宇宙 更新时间:2023-11-04 11:35:14 26 4
gpt4 key购买 nike

您好,我正在使用 NASM 在 Assembly 中编写阶乘函数。我必须使用俄语乘法代替 mul 来完成我的作业。我正在使用 32 位 Linux

这是我的阶乘代码

section .text
global factorial
extern rpmult

factorial:
push ebp
mov ebp, esp
sub esp, 4 ;creates memory for local variable at ebp-4
mov esi, [ebp+8] ; put n in esi
cmp esi, 1 ; n <= 1
jbe .done ; if so jump to done

.try:
mov [ebp-4],esi ;adds n temporarily into ebp-4
dec esi ; n - 1
push esi ; push arugment
call factorial ;call factorial again stores result in esi
add esp, 4 ;gets rid of the argument

mov edi, esi ;copies n - 1 into edi
mov esi,[ebp+4] ;gets the original value back (n)
call rpmult ;multiply
jmp .done ;once it reaches here, finished the function

.done:
mov esp, ebp ;restores esp
pop ebp
ret ;return the value

这是我的 rpmult 代码:

section .text

global rpmult

rpmult:
push ebp
mov ebp, esp
sub esp, 4 ;allocate m

mov dword [ebp-4], 0 ; m = 0;
.while:
test edi, edi ; x == 0?
je .done
test esi, esi ; y == 0?
je .done

test edi, 0x01 ; x is odd?
jz .shifts
add [ebp-4], esi ; m += y;

.shifts:
shr edi, 1 ; x >>= 1;
shl esi, 1 ; y <<= 1;
jmp .while

.done:
mov eax, [ebp-4]
;mov esp, ebp
;pop ebp
leave
ret

当我通过 C 程序使用函数时,说出 4 的阶乘!我明白了

4! = 13803416593125867520

我相信我的代码是正确的,但我不知道该怎么做。我需要让阶乘函数与 rpmult 函数一起用于我的期末考试。任何帮助表示赞赏!谢谢!

最佳答案

(注意:我重写了这个答案,在我更加清醒的时候再次查看它,并阅读了@lloydm 的评论。)

存在三个问题区域:

1) 递归的基本情况

调试递归函数时,首先检查基本情况总是明智的。

那么在计算 1! 时会发生什么?

factorial:
push ebp
mov ebp, esp
sub esp, 4 ;creates memory for local variable at ebp-4
mov esi, [ebp+8] ; put n in esi
cmp esi, 1 ; n <= 1
jbe .done ; if so jump to done
...
.done:
mov esp, ebp ;restores esp
pop ebp
ret ;return the value

这里已经有两个问题:

  1. 您希望这段代码在从 C 调用时能够正常工作,这意味着您需要遵循通常的调用约定(对于带有 gcc 的 Linux 来说,这意味着“cdecl”- 参见 http://en.wikipedia.org/wiki/X86_calling_conventions)。所以你需要保留esiediebpebx。但是这段代码覆盖了 esi 中的任何内容。当从 C 调用该函数时,这将导致不可预测的行为,因为 C 编译器生成的代码将假定调用 factorial 之前 esi 中的任何内容仍然存在当它返回时。只有先将它们的值保存在某个地方(并在返回之前恢复它们),才能使用这些寄存器。

  2. 返回值在 eax 中传递,但您并未在此处向 eax 中放入任何内容。您希望 1! 的答案是 1,而不是“此时 eax 中碰巧出现的任何随机垃圾”!

2)递归的递归情况

...
.try:
mov [ebp-4],esi ;adds n temporarily into ebp-4
dec esi ; n - 1
push esi ; push arugment
call factorial ;call factorial again stores result in esi
add esp, 4 ;gets rid of the argument
mov edi, esi ;copies n - 1 into edi
mov esi,[ebp+4] ;gets the original value back (n)
call rpmult ;multiply
jmp .done ;once it reaches here, finished the function
...
  1. edi,和esi一样,是需要保存的寄存器,如上所述。

  2. mov edi, esi ;copys n - 1 into edi 是错误的。您不想将 n - 1 放入 edi - 您正在尝试计算 (n-1)!*n ,所以你想将 (n-1)! 放入 edi,即递归调用计算的答案。正如@lloydm 指出的那样,它在 eax 中返回。 (我被我原来回答中的评论误导了,以为你真的想把 n - 1 放到 edi 中。那也行不通,因为 esicall factorial 之后不再包含 n - 1,因为您不遵循调用约定。)

  3. mov esi,[ebp+4] ;gets the original value back (n) 是错误的(正如我最初指出的); [ebp+4] 包含返回地址;这应该是 [ebp-4]

3) 奇怪的大值

4! = 13803416593125867520 是一个比它第一次出现时更奇怪的答案:它对于 32 位值来说太大了。 (十六进制:0xbf8f964200000000,因此它是一个 64 位值,前 32 位为大数,后 32 位为零。)

考虑到其他错误,您可能希望得到一个完全随机的值作为答案,但 factorial 返回一个 32 位随机值。那么为什么要在这里打印 64 位值呢? (如果你不是故意这样做,我想这可能与 C 代码做了一些奇怪的事情有关,因为 esiedi 没有被你的代码保留。 )

调试提示

不要一开始就试图找出为什么 factorial(5) 不起作用。使用 factorial(1) 尽可能简单地开始。然后处理 factorial(2)

关于linux - 组装 - NASM 析因问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8453666/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com