使用三重嵌套for循环的无限循环(32位汇编)

时间:2016-09-25 04:09:27

标签: assembly x86 nasm 32-bit

下面我尝试在32位汇编中实现的C ++代码如下:

for(int ebx = 3; ebx < 10; ebx++){
      print("LO");
      for(int esi = 2; esi < ebx; esi++){
           PRINT("L1");
           for(int ebp = 0; ebp < esi; ebp++){
                PRINT("L2");
           }
      }
}

这是我的汇编代码:

SECTION .data                    ; Section containing initialized data
helloWorld0: dw "L1",10,0
helloWorld1: dw "L2",10,0
helloWorld2: dw "L3",10,0

SECTION .bss                     ; Section containing uninitialized data
SECTION .text                    ; Section containing code
extern printf                    ; Print function from glibc
global main                      ; Linker needs this to find the entry point
main:
nop                              ; This no-op keeps gdb happy
push     ebp                     ; Set up stack frame for debugger
mov      ebp,esp
push     ebx                     ; Must preserve EBP, EBX, ESI & EDI
push     esi
push     edi
; Everything before this is boilerplate; use it for all  apps

mov ebx, 3                      ;L1
mov esi, 2                      ;L2
mov ebp, 0                      ;L3
L1:
push    helloWorld0
call    printf

L2:
push    helloWorld1
call    printf

L3:
push    helloWorld2
call    printf
inc     ebp
cmp     ebp, esi
jne     L3

inc     esi
cmp     esi, ebx
jne     L2

mov     esi, 2
inc     ebx
cmp     ebx, 10
jne     L1
; Everything after this is boilerplate; use it for all apps
pop     edi                     ; Restore saved registers
pop     esi
pop     ebx
mov     esp,ebp                 ; Destroy stack frame before returning
pop     ebp
ret                              ; Return control to Linux

似乎代码永远不会确定ebp = esi的时间。我是汇编语言的新手,所以我的教授提供了样板。我使用了EBP,ESI和EBX,因为它们被保留下来使用。关于什么导致第三个嵌套循环无限循环的任何想法?

1 个答案:

答案 0 :(得分:2)

在你的C中,int ebp = 0位于两个外循环中,并在每个时间进入内循环之前运行。这不在你的asm中。

另外,不要在功能中修改EBP。你的教授的样板制作一个&#34;堆栈框架&#34; (google it),使用EBP作为帧指针。在函数结束时,mov esp,ebp用于清理。 (好的,因为你的代码推动了printf的args但是在printf返回后没有调整ESP!)

可能未来的讲座将讨论使用[ebp + 8][ebp - 4]或其他方式访问函数args和locals。现在,只要知道这个框架指针的废话浪费了另一个宝贵的寄存器,所以你无法使用它。 (即,您可以使用的8 x86整数寄存器中只有3个寄存器,并且未被函数调用修改为EDI,ESI和EBX。)

re:Cedric关于分支条件的观点:

你的循环很好,除非我错过了什么。

你的循环都具有方便的属性,它们应该总是运行一次,因此你可以将它们写成do{}while()循环,而无需在顶部进行额外的检查。这意味着它们可以在asm中很好地实现,底部的检查就像你一样。

因此,例如,printf(即循环体)应该在第一个内循环上运行两次:一次使用EBP = 0,一次使用EBP = 1。之后,增量将使EBP = 2,因此CMP / JNE将失效,因为EBP == ESI == 2.

对于外部循环,逻辑是相同的。整个内部循环是&#34;循环体&#34;,它在递增计数器之前运行。所以你很好。

您可以等效地使用CMP / JL进行签名比较,并在计数器小于限制时重新运行循环体。那将与C匹配。它也会改变故障模式。 2 ^ 32次穿过内环(直到inc ebp缠绕并达到2)。对于JL,如果循环计数器太高以至于我们不应该首先进入循环,那么当前错误的失败模式将只通过内循环一次。

通常在asm中,它更容易倒数到零,例如dec ebp / jnz不需要CMP。转换循环来做到这一点并非易事,因为外部循环计数器充当内部循环的上限。但是应该可以。你没有打印出循环中的计数器值,所以它实际上并不重要,只是循环行程计数。