【问题标题】:Nested loops in nasmnasm 中的嵌套循环
【发布时间】:2022-01-21 05:56:05
【问题描述】:

我在 Windows 10 64 位机器上用 nasm 编写了以下程序

extern _GetStdHandle@4
extern _WriteFile@20
extern _ReadFile@20
extern _ExitProcess@4

;ReadBufferSize
%define rbs 100

section .rodata
    w: dd 3
    h: dd 3
    text: db "abc", 10

section .data
    x: dd 4
    y: dd 4

section .bss
    stdout: resb 4
    stdin: resb 4
    readbuff: resb (rbs+2); allow for CR, LF
    bytecnt: resb 4

section .text
    _print:
        push ebp
        mov ebp, esp

        push 0
        lea eax, [ebp-4]
        push eax
        mov eax, [ebp+8]
        push dword [eax]
        push dword [ebp+12]
        push dword [stdout]
        call _WriteFile@20

        mov esp, ebp
        pop ebp
        ret 8

    _read:
        push ebp
        mov ebp, esp

        push 0
        push dword [ebp+8]
        push rbs
        push dword [ebp+12]
        push dword [stdin]
        call _ReadFile@20
        sub dword [bytecnt], 2; remove CR and LF

        mov esp, ebp
        pop ebp
        ret 8
    _draw:
        push ebp
        mov ebp, esp
        
        push dword [w]
        L1:
            push dword [h]
            L2:
                push text
                push x
                call _print
            dec dword [ebp-8]
            cmp dword [ebp-8], 0
            jg L2
        dec dword [ebp-4]
        cmp dword [ebp-4], 0
        jg L1

        mov esp, ebp
        pop ebp
        ret

    global _main
    _main:
        push -11
        call _GetStdHandle@4 ;Get Stdout handle
        mov [stdout], eax

        push -10
        call _GetStdHandle@4 ;Get Stdin handle
        mov [stdin], eax
        
        call _draw

        push 0
        call _ExitProcess@4

        hlt

我会清理它,但这可能会影响某些事情,所以我不会(抱歉)。 _draw 函数应该在屏幕上输出 "abc\n" 9 (3*3) (3 和 3,因为 wh 是 3) 次,但它只输出 5 次。据我检查,_print 按预期工作。如果有帮助,这是我的构建脚本:

@echo off
IF "%~2" EQU "" set f0rm4t=win32
IF "%~2" NEQ "" set f0rm4t=%~2
if exist %1.exe rm %1.exe
if exist %1.obj rm %1.obj
nasm -f %f0rm4t% %1.asm
if exist %1.exe goto run
if not exist %1.obj goto end
gcc %1.obj -o %1.exe
if %errorlevel% EQU 0 goto run
:run
%1.exe
echo ------------------------
if %errorlevel% EQU -1073741819 (
    echo exited with C0000005
    goto end
)
if %errorlevel% EQU 0 (
    echo exited successfully
    goto end
)
echo exited with return value %errorlevel%
:end

我使用build filename 命令构建,它可以正常运行。 我检查了我的逻辑,它看起来很好,所以它与嵌套循环有关,我不知道究竟是什么。 完成build命令输出:

C:\Users\User\files>build filename
abc
abc
abc
abc
abc
------------------------
exited successfully

C:\Users\User\files>

如果循环正常工作,则不可能得到 5 次输出,因为它应该是(外部循环的重复)*(内部循环的重复),但 5 是质数。我想不通,所以我把它留给了这个社区。祝您有美好的一天,提前致谢!

【问题讨论】:

  • 您的 _draw 函数推送但从不弹出,因此堆栈中充满了垃圾,而不是您想象的循环控制变量。
  • @ErikEidt: 嗯,循环控制变量是通过ebp 引用的,所以这些不受推送的影响,但你是对的,wh 的推送是正确的在这里似乎没有任何用处。
  • @500-InternalServerError 、wh 是在section .rodata 中声明的常量,我不想将它们用于循环控制变量

标签: windows assembly nasm nested-loops stack-memory


【解决方案1】:

在查看@prl 答案的开头之后,我意识到,就像他们说的那样,[ebp-8] 在内部循环的第一次迭代中递减为 0,而下一次 push [h] 推送到 [ebp-12],所以[ebp-8] 保持为 0。所以我首先要做的就是在跳转后添加一个pop edx(使用 edx 作为垃圾寄存器),如下所示:

    _draw:
        push ebp
        mov ebp, esp
        
        push dword [w]
        L1:
            push dword [h]
            L2:
                push text
                push x
                call _print
            dec dword [ebp-8]
            cmp dword [ebp-8], 0
            jg L2
            pop edx
        dec dword [ebp-4]
        cmp dword [ebp-4], 0
        jg L1
        pop edx

        mov esp, ebp
        pop ebp
        ret

这将清除堆栈中已经归零的变量并为新变量腾出空间,并且它可以工作。

但我认为@prl 的答案通常更适用于在堆栈上分配变量的技术,因此如果可以,请使用它,因为它也不会令人困惑。

【讨论】:

【解决方案2】:

第一次通过外循环时,[ebp-8] 倒计时到零。接下来两次通过外循环,[ebp-8] 已经为零(或更少),因此内循环只执行一次。总共有五次打印调用。

请注意,push [h] 的后续执行不会更改 [ebp-8]。 在第二次迭代中,它写入[ebp-12],在第三次迭代中,它写入[ebp-16],因为每次推送时堆栈指针都会继续向下增长。

要解决这个问题,我建议:

  1. mov ebp, esp 之后添加sub esp, 8
  2. push [w] 更改为mov eax, [w]; mov [ebp-4], eax
  3. push [h] 更改为mov eax, [h]; mov [ebp-8], eax

【讨论】:

  • 请注意,此处建议的修复基于最小化对程序的更改以解决问题。我建议进行其他更改:将循环计数器保存在寄存器中。 Dec 设置标志,因此您可以在 dec 之后立即使用 jg 或 jnz 而无需 cmp 指令。
猜你喜欢
  • 1970-01-01
  • 2021-01-27
  • 2015-06-05
  • 2015-11-22
  • 2011-01-20
  • 2017-09-18
  • 2017-05-22
  • 2020-07-28
  • 1970-01-01
相关资源
最近更新 更多