【问题标题】:Loop with printf in NASM在 NASM 中使用 printf 循环
【发布时间】:2020-08-16 16:52:40
【问题描述】:

在第一个循环中我填充了array,然后我想打印这个数组,但是我会出错。这是Segmentation fault。我只是更改ecx 寄存器,因为这是我的_loop2 计数器。

extern  printf

SECTION .bss
    array resb 10

SECTION .data
    fmt: db "array[%d] = %d", 10, 0 ; The printf format, "\n",'0'


SECTION .text

global main

main:
    mov ecx, 0

_loop:
    inc ecx
    mov [array + ecx * 4], ecx
    cmp ecx, 10
    jnz _loop

    mov ecx, 0


_loop2:
    jmp print
    add ecx, 1
    cmp ecx, 10
    jnz _loop2
    
    ret

print:
    ;push    ebp        ; set up stack frame
    ;mov     ebp, esp
    push    ecx
    push    dword [array + ecx * 4] ; value of variable a SECOND
    push    dword fmt   ; address of ctrl string
    call    printf      ; Call C function
    
    add     esp, 8      ; stack (4 * 2)

    ;mov     esp, ebp   ; takedown stack frame
    ;pop     ebp        ; same as "leave" op

    mov     eax,0       ;  normal, no error, return value

    ;ret            ; return

【问题讨论】:

  • array 中有 10 个字节的空间,但您在那里存储了 10 个双字(= 40 个字节)。这不好。
  • 另外,_loop2 循环实际上从未运行,因为您无条件地跳过它。您是否尝试过使用调试器单步执行此代码?这是汇编编程的一个非常重要的实践,当你这样做时,我认为你会更好地理解你所写的内容。
  • @Nate Eldredge,我应该更改resb 的大小?
  • 更好,将其更改为resd 10,以便计算要为多少个双字预留空间。
  • 另外,你在调用printf之前推送了ecx,但你再也不会弹出它,所以如果你尝试返回,你将返回到被推送的地址,即0。但是您将ret 注释掉了,因此该函数甚至不会尝试返回,而是会继续执行内存中发生的任何垃圾。

标签: arrays loops assembly x86 nasm


【解决方案1】:

cmets中提到的几个问题:

  • array resb 10 将为 10 个字节保留空间,但您想在其中存储 10 个双字(40 个字节)。改为array resd 10

  • (由 Sep Roland 指出)在 _loop 中,您有一个不合时宜的错误;由于incmov 之前完成,您将在[array+4], [array+8], ... [array+40] 访问双字,其中最后一个超出范围。这就像在 C 中做 int array[10]; for (i=1; i <= 10; i++) array[i]=i; 一样,并且由于完全相同的原因是不正确的。一种解决方法是改用mov [array + ecx * 4 - 4], ecx

  • _loop2 之后你有jmp print,它将控制权转移到print 并且永远不会回来。由于您显然想将print 作为子例程调用并继续使用add ecx, 1 ; cmp ecx, 10 等执行,因此您需要call print 而不是jmp。并且还取消注释print 末尾的ret 以便它实际返回。除非您实际执行ret,否则汇编语言中的子程序不会自动返回;否则 CPU 将继续执行内存中的任何垃圾。

  • 在调用printf 之前,您有一个push ecx 来保存ecx 的值,这很好,因为printf 将覆盖该寄存器,但之后您需要pop ecx 才能取回该值并将堆栈放回原来的位置。

    具体来说,pop ecx 应该跟在add esp, 8 后面;堆栈是后进先出结构,push ecx 是在推入 printf 参数之前,因此您需要在从堆栈中删除这些参数后 pop ecx

  • mov eax, 0 作为print 末尾的返回值是不必要的,因为您从未在其他任何地方使用它。

通过这些更改,代码可以正常工作。

【讨论】:

  • 还有一个错误可以添加到您的答案中。填充数组的循环有缓冲区溢出!第一次写入到第二个元素,第十次写入经过存储。使用mov [array - 4 + ecx * 4], ecx 很容易解决。这将创建一个填充数字 1 到 10 的数组。
  • 是的。我将您的建议添加到答案中。
猜你喜欢
  • 1970-01-01
  • 2013-12-14
  • 1970-01-01
  • 1970-01-01
  • 2012-02-19
  • 2012-03-28
  • 2018-06-30
  • 2015-12-24
  • 1970-01-01
相关资源
最近更新 更多