使用堆栈

时间:2017-11-01 09:09:15

标签: assembly nasm

我在代码中做了教程和一些事情,我不明白,也许任何人都可以帮助我?我很感激你的帮助。

首先调用函数sprint,然后在'sprint'函数中将edx,ecx,ebx,eax逐步推入堆栈,然后调用函数'slen',并调用'ebx '将再次被推入堆栈,我不明白这一步,ebx已经在堆栈上,因为我知道'ebx'现在是调用sprint函数后eax之后堆栈中的第二个。 我想知道这里有2个堆栈吗?或不 有人可以帮我解释一下吗?我会非常感激。

最好的关注

  

functions.asm

;------------------------------------------
; int slen(String message)
; String length calculation function
slen:
push    ebx
mov     ebx, eax

nextchar:
cmp     byte [eax], 0
jz      finished
inc     eax
jmp     nextchar

finished:
sub     eax, ebx
pop     ebx
ret


;------------------------------------------
; void sprint(String message)
; String printing function
sprint:
push    edx
push    ecx
push    ebx
push    eax
call    slen

mov     edx, eax
pop     eax

mov     ecx, eax
mov     ebx, 1
mov     eax, 4
int     80h

pop     ebx
pop     ecx
pop     edx
ret


;------------------------------------------
; void exit()
; Exit program and restore resources
quit:
mov     ebx, 0
mov     eax, 1
int     80h
ret '
  

块引用   的HelloWorld-inc.asm

; Hello World Program (External file include)
; Compile with: nasm -f elf helloworld-inc.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 helloworld-inc.o -o helloworld-inc
; Run with: ./helloworld-inc

%include        'functions.asm'                             ; include our external file

SECTION .data
msg1    db      'Hello, brave new world!', 0Ah              ; our first message string
msg2    db      'This is how we recycle in NASM.', 0Ah      ; our second message string

SECTION .text
global  _start

_start:

mov     eax, msg1       ; move the address of our first message string into EAX
call    sprint          ; call our string printing function

mov     eax, msg2       ; move the address of our second message string into EAX
call    sprint          ; call our string printing function

call    quit            ; call our quit function

1 个答案:

答案 0 :(得分:2)

这两个函数是独立的(两者都是公共API函数)。 sprint在内部调用slen只是巧合,slen无法假设它是从sprint调用的,ebx已经被它保留了,它可以直接从用户代码调用,其中ebx可能不会存储在堆栈中。

所以这两个函数都遵循调用约定(代码选择的那个,我从头开始不知道它们,也不想猜它是哪一个,但是如果你在linux中构建elf32,它可能是标准的linux 32b调用约定(错误的猜测,看起来像Irvine32 lib调用约定,保留除eax之外的所有可能返回值的寄存器,感谢Peter Cordes的评论))。这意味着两个函数必须独立地保留一些寄存器以符合约定,并且只有一些寄存器可以自由修改并以修改状态返回。

  

因为我知道'ebx'现在是调用sprint函数后eax后堆栈中的第二个最后一个

这也不是真的。 “堆栈”只是普通的计算机内存,就像您的.data.bss部分一样。它只是您的应用程序进程的另一个保留内存。寄存器esp中的值有点特殊,它指向“堆栈顶部”。

现在执行push ebx时,指令会将寄存器ebx中的32位值写入地址esp-4的计算机内存中(32位= 4字节,这就是为什么{{1}在32b模式下将堆栈指针移动-4,并且还会更新push以指向该位置(即esp)。

您的一个误解是“ebx已存储”,如果您将重新阅读上述说明,您可以注意到堆栈内存中没有信息,注意该值来自esp -= 4。事实上,如果您将执行下一条指令ebx,那么该值将会毫无问题地恢复到寄存器pop eaxeax,从而导致与esp += 4完全类似的效果,但是通过堆栈内存(比直接mov eax,ebx慢得多)。

另一个误解是“在eax之后的第二个”。 mov指令本身会将返回地址推送到堆栈,因此在call之后slen内堆栈包含值:“ebx”,在push ebx之后的下一条指令处将地址返回sprint ,“eax”,“ebx”。

请不要犹豫,使用指令参考指南来验证具体指令的作用,例如:

即使对于经验丰富的asm程序员来说,验证关于特定指令的任何假设,特别是涉及标志时,或者与call slen一样使用隐式寄存器时,这也很常见。不要仅仅根据教学名称来猜测,其中一些比常识所期望的要复杂得多。

此外,在调试器中触发此代码,逐步执行指令,并了解自己mul/div/lods/stos/xlatb/...和堆栈内存内容如何演变(这将清除此答案的第二部分如何确切esp有效。)