用堆栈推入 nasm
push and pop in nasm with stack
我在做教程和一些我不明白的代码,也许有人能帮助我吗?我会感谢你的帮助。
首先会调用sprint函数,在'sprint'函数中将edx,ecx,ebx,eax逐级压入栈中,然后'slen'函数被调用,'ebx' 将再次被压入堆栈,我不明白这一步,ebx 已经在堆栈中,因为我知道 'ebx' 现在是堆栈中仅次于 eax 的倒数第二个在调用 sprint 函数之后。
我想知道这里是否有 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 '
Blockquote
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
这两个函数是独立的(都是public API函数)。 sprint
在内部调用 slen
只是巧合,slen
不能假设它是从 sprint
调用的并且 ebx
已经被它保留了,它可以直接从用户代码调用,其中 ebx
可能不存储在堆栈中。
所以这两个函数都遵循调用约定(代码作者选择的那个,我从头上不知道它们,也不想猜测它是哪个,但是如果你在 linux并构建 elf32,它可能是标准的 linux 32b 调用约定(错误的猜测,看起来像 Irvine32 lib 调用约定,保留所有寄存器,但 eax
可能 return 值,感谢 Peter Cordes 的评论))。这意味着两个函数都必须独立地保留一些寄存器以符合约定,并且只有一些寄存器可以自由修改并 returned 处于修改状态。
as i know that 'ebx' is now the second last one on the stack after eax after sprint function is called
这也不是真的。 "stack" 只是普通的计算机内存,就像您的 .data
和 .bss
部分一样。它只是为您的应用进程保留的另一个内存。使它有些特别的是寄存器 esp
中的值,它指向 "top of the stack".
现在当你执行 push ebx
时,指令会将寄存器 ebx
中的 32 位值写入地址 esp-4
的计算机内存中(32 位 = 4 字节,这就是为什么 push
在 32b 模式下将堆栈指针移动 -4),并且还将更新 esp
以指向该位置(即 esp -= 4
)。
您的一个误解是 "ebx is stored",如果您重新阅读上面的描述,您会注意到堆栈内存中没有任何信息指出该值源自 ebx
。事实上,如果您将执行下一条指令 pop eax
,该值将毫无问题地恢复到寄存器 eax
中,而 esp += 4
会导致与 mov eax,ebx
完全相似的效果,但通过堆栈内存(比直接 mov
慢得多)。
另一个误解是"second last after eax"。 call
指令本身会将 return 地址压入堆栈,因此在 push ebx
之后的 slen
中,堆栈包含值:"ebx"、return 地址进入在 call slen
、"eax"、"ebx".
之后的下一条指令处冲刺
不要犹豫,使用指令参考指南来验证特定指令的确切作用,例如:
即使对于经验丰富的 asm 程序员来说,验证关于特定指令的任何假设也是很常见的,尤其是当涉及标志时,或者像 mul/div/lods/stos/xlatb/...
这样的隐式寄存器使用。不要仅凭指令名称来猜测,其中一些比常识预期的要棘手得多。
此外,只需在调试器中触发此代码、单步执行指令并查看 esp
和堆栈内存内容如何演变(这将清除此答案的第二部分),会容易得多关于 push/pop
的工作原理)。
我在做教程和一些我不明白的代码,也许有人能帮助我吗?我会感谢你的帮助。
首先会调用sprint函数,在'sprint'函数中将edx,ecx,ebx,eax逐级压入栈中,然后'slen'函数被调用,'ebx' 将再次被压入堆栈,我不明白这一步,ebx 已经在堆栈中,因为我知道 'ebx' 现在是堆栈中仅次于 eax 的倒数第二个在调用 sprint 函数之后。 我想知道这里是否有 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 '
Blockquote 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
这两个函数是独立的(都是public API函数)。 sprint
在内部调用 slen
只是巧合,slen
不能假设它是从 sprint
调用的并且 ebx
已经被它保留了,它可以直接从用户代码调用,其中 ebx
可能不存储在堆栈中。
所以这两个函数都遵循调用约定(代码作者选择的那个,我从头上不知道它们,也不想猜测它是哪个,但是如果你在 linux并构建 elf32,它可能是标准的 linux 32b 调用约定(错误的猜测,看起来像 Irvine32 lib 调用约定,保留所有寄存器,但 eax
可能 return 值,感谢 Peter Cordes 的评论))。这意味着两个函数都必须独立地保留一些寄存器以符合约定,并且只有一些寄存器可以自由修改并 returned 处于修改状态。
as i know that 'ebx' is now the second last one on the stack after eax after sprint function is called
这也不是真的。 "stack" 只是普通的计算机内存,就像您的 .data
和 .bss
部分一样。它只是为您的应用进程保留的另一个内存。使它有些特别的是寄存器 esp
中的值,它指向 "top of the stack".
现在当你执行 push ebx
时,指令会将寄存器 ebx
中的 32 位值写入地址 esp-4
的计算机内存中(32 位 = 4 字节,这就是为什么 push
在 32b 模式下将堆栈指针移动 -4),并且还将更新 esp
以指向该位置(即 esp -= 4
)。
您的一个误解是 "ebx is stored",如果您重新阅读上面的描述,您会注意到堆栈内存中没有任何信息指出该值源自 ebx
。事实上,如果您将执行下一条指令 pop eax
,该值将毫无问题地恢复到寄存器 eax
中,而 esp += 4
会导致与 mov eax,ebx
完全相似的效果,但通过堆栈内存(比直接 mov
慢得多)。
另一个误解是"second last after eax"。 call
指令本身会将 return 地址压入堆栈,因此在 push ebx
之后的 slen
中,堆栈包含值:"ebx"、return 地址进入在 call slen
、"eax"、"ebx".
不要犹豫,使用指令参考指南来验证特定指令的确切作用,例如:
即使对于经验丰富的 asm 程序员来说,验证关于特定指令的任何假设也是很常见的,尤其是当涉及标志时,或者像 mul/div/lods/stos/xlatb/...
这样的隐式寄存器使用。不要仅凭指令名称来猜测,其中一些比常识预期的要棘手得多。
此外,只需在调试器中触发此代码、单步执行指令并查看 esp
和堆栈内存内容如何演变(这将清除此答案的第二部分),会容易得多关于 push/pop
的工作原理)。