【问题标题】:Need some advice with NASM loop需要一些关于 NASM 循环的建议
【发布时间】:2015-12-06 03:10:36
【问题描述】:

我正在尝试创建一个从 0 到 10 打印但有一些错误的 while 循环...

用这些编译:

nasm -f elf myprog.asm
gcc -m32 -o myprog myprog.o

错误:

在输出中你可以看到 134513690..很多.... 最后一行是segmentation fault

这是代码:

SECTION .text

global main

extern printf

main:
  xor eax,eax        ; eax = 0

myloop:
  cmp eax,10         ; eax = 10?
  je finish          ; If true finish

  push eax           ; Save eax value
  push number        ; push  number value on stack
  call printf

  pop eax
  inc eax            ; eax + 1
  add esp,80         ; Im not sure what is this
  jmp myloop         ; jump to myloop

number db "%d",10,0 ; This is how i print the numbers

finish:
  mov eax,1
  mov ebx,0
  int 0x80

【问题讨论】:

  • 您应该在 main 的开始/结束处保存/恢复 ebx,并将其用于循环计数器。 EBX 在普通 ABI 中的函数调用中保留:stackoverflow.com/a/34100481/224132
  • 谢谢,我试试看:)

标签: linux assembly x86 nasm


【解决方案1】:

这段代码有一个真正的错误;函数调用清理不太正确。我会将myloop 部分更改为这样:

myloop:
  cmp eax,10         ; eax = 10?
  je finish          ; If true finish

  push eax           ; Save eax value
  push number        ; push  number value on stack
  call printf

  add esp, 4         ; move past the `push number` line
  pop eax
  inc eax            ; eax + 1
  jmp myloop         ; jump to myloop

最大的区别在于,不是将 80 添加到 esp(我不确定您为什么要这样做),而是仅添加推入的参数的大小。此外,以前错误的值被弹出为eax,但切换addpop 的顺序可以解决此问题。

【讨论】:

  • 谢谢,这很完美,我是汇编语言的新手,这对我很有帮助,我花了一周时间,但非常感谢:D
  • 很高兴为您提供帮助。我自己没有做太多的汇编编程,但是这些知识在调试 C 代码时肯定会有所帮助:)
【解决方案2】:

几个问题,你需要把“数字”按地址而不是数字。

push dword number

调用 printf 后,需要恢复栈,ESP。 基本上,当您“推送”一个寄存器时,它会存储在堆栈中。由于您推送了两次(两个参数),因此您需要恢复 8 个字节。

当您“pop eax”时,您正在检索堆栈的顶部,即“数字”,而不是计数器。因此,您只需要这样做

pop eax
pop eax

那么不需要通过添加来恢复ESP,因为它是通过弹出来完成的。 基本上,在第一次迭代之后,eax 指向一个地址,所以它永远不会等于 10。

进一步阅读堆栈指针和基指针: Ebp, esp and stack frame in assembly with nasm

【讨论】:

  • add esp, 4 是首选(在您不再关心堆栈上的值的情况下)弹出,因为它不需要像 pop 那样访问内存,
  • @MichaelPetch:实际上,取决于上下文add esp, 4 可能会更慢。堆栈引擎必须插入一个额外的 uop 以将 OOO 核心的 esp 值与堆栈引擎的偏移量同步。 pop 是单指令,加载端口资源通常不是瓶颈。但是这个答案应该建议使用 call-preserved register 像 ebx 作为循环变量,而不是 eax。
  • 至于关于使用不同寄存器的评论,那部分是非常正确的,为什么我实际上没有赞成这个答案。额外寄存器的混乱只是因为我们碰巧使用了一个将被破坏的寄存器。最好的解决方法是使用不同的。
  • @MichaelPetch:英特尔从 Pentium-M 开始就有堆栈引擎,AMD 从 K10 开始就有。没有人关心代码将如何在不相关的旧 CPU 上运行;这些核心在任何地方都找不到,是吗?我的意思是,有历史的兴趣,并理解为什么老 asm 会做出这样的选择。但是为了告诉新手他们应该记住的事情,我尽量保持简单。这也是我抱怨 8086 答案的指令选择的原因。他们可能永远不会针对硬件 8086 进行优化,所以还不如学习在现代硬件上好的习语。特别是。当两种方式在 HW 8086 上相等时。
  • 抱歉,为了让他更好地理解它的工作原理,我给了他弹出两次而不是加 8 的答案。
猜你喜欢
  • 1970-01-01
  • 2014-01-19
  • 1970-01-01
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
相关资源
最近更新 更多