我在代码中做了教程和一些事情,我不明白,也许任何人都可以帮助我?我很感激你的帮助。
首先调用函数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
答案 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 eax
和eax
,从而导致与esp += 4
完全类似的效果,但是通过堆栈内存(比直接mov eax,ebx
慢得多)。
另一个误解是“在eax之后的第二个”。 mov
指令本身会将返回地址推送到堆栈,因此在call
之后slen
内堆栈包含值:“ebx”,在push ebx
之后的下一条指令处将地址返回sprint ,“eax”,“ebx”。
请不要犹豫,使用指令参考指南来验证具体指令的作用,例如:
即使对于经验丰富的asm程序员来说,验证关于特定指令的任何假设,特别是涉及标志时,或者与call slen
一样使用隐式寄存器时,这也很常见。不要仅仅根据教学名称来猜测,其中一些比常识所期望的要复杂得多。
此外,在调试器中触发此代码,逐步执行指令,并了解自己mul/div/lods/stos/xlatb/...
和堆栈内存内容如何演变(这将清除此答案的第二部分如何确切esp
有效。)