以下是x86汇编程序,旨在由NASM在64位CentOS上通过远程终端进行组装,当与C程序一起使用时,该程序可以正常工作。
section .data
section .text
global strlen
strlen:
push ebp
mov ebp, esp ; obtain the address of the
mov eax, DWORD [ebp+8] ; address of string to eax
xor ecx, ecx ; initialize counter to zero
count_loop:
mov bl, [eax] ; obtain the address of the 1st character
cmp bl, 0 ; check the null value
je length_exit ; exit if the null-character is reached
inc ecx ; increment counter
inc eax ; increment the address
jmp count_loop ; start the loop again
length_exit:
mov eax, ecx ; return ecx
pop ebp ;
ret
首先,它是32位还是64位程序?如果它是一个32位程序,为什么它在函数名称中没有下划线字符(_
)?
我知道以下代码部分正在创建一个堆栈框架:
push ebp
mov ebp, esp ; obtain the address of the
mov eax, DWORD [ebp+8] ; address of string to eax
但是,为什么我们需要保存ebp
?为什么我们不能写下面的内容? :
move eax, DWORD [esp+8]
而且,为什么我们需要这里的类型铸造?
我还需要这个程序的内存布局来理解堆栈机制。我在互联网上发现了很多图片,但我不确定哪一个是代表这个程序的合适图片。
如果它是一个32位程序,为什么它在函数名称中没有下划线字符(
_
)?
因为它不是Windows。
无论CPU架构如何,Linux / ELF系统都不会在任何模式下使用领先的_
。
为什么我们不能写下面的内容?:
move eax, DWORD [esp+8]
您可以。 (如果你正确拼写mov
)。实际上,编译器默认在启用优化时使用-fomit-frame-pointer
,因此它们仅在具有C99可变长度数组或alloca
的函数中使用EBP作为帧指针。
32位和64位模式允许ESP作为寻址模式的基地址,这与[sp+2]
无法编码的16位模式不同。
但请记住,如果你没有推动ebp
,ESP仍然指向返回地址,所以第一个arg将在[esp+4]
。
而且,为什么我们需要这里的类型铸造?
你没有。寄存器操作数表示操作数大小。
(它不是真正的类型转换,只是操作数大小说明符。它不会为你做float-int转换;你必须使用cvtss2si eax, [esp+4]
。)
你只需要mem的操作数大小说明符,像cmp dword [esp+4], 0
这样的立即指令,它们在字节/字/双字操作数大小之间是不明确的。或者像movzx eax, byte [esp+4]
这样的指令,其中寄存器操作数并不意味着内存操作数的大小。
原因源于指令的编码方案。 “英特尔手册”第2.1.5节中的表2.2显示了可能的组合。
它表明ESP
寄存器的(可能)编码用于指示SIB字节(指令扩展字节)如下:
[ - ] [ - ]命名法表示SIB遵循ModR / M字节。
因此,基本上ESP
寄存器的编码被重用于编码额外的指令字节SIB
字节。
在SIB字节中,[ESP]
的指令编码也是一个例外。看表2.3。