linux - 为什么我们不能使用 esp 寄存器直接引用内存地址?
问题描述
以下是一个 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。
Linux/ELF 系统在任何模式下都不使用前导_
,无论 CPU 架构如何。
为什么我们不能只写以下内容?:
move eax, DWORD [esp+8]
你可以。(如果你拼写mov
正确)。事实上,编译器默认在-fomit-frame-pointer
启用优化时使用,因此它们仅在具有 C99 可变长度数组的函数中使用 EBP 作为帧指针,或者alloca
.
32 位和 64 位模式允许 ESP 成为寻址模式的基地址,这与不可编码的 16 位[sp+2]
模式不同。
但请记住,如果您还没有 push ebp
,ESP 仍然指向返回地址,所以第一个 arg 将在[esp+4]
。
而且,为什么我们需要在这里进行类型转换?
你没有。寄存器操作数意味着操作数大小。
(而且它不是真正的类型转换,只是操作数大小说明符。它不会为您进行浮点整数转换;您必须使用cvtss2si eax, [esp+4]
它。)
您只需要mem 的操作数大小说明符,立即指令如cmp dword [esp+4], 0
,这在字节/字/双字操作数大小之间是不明确的。或者对于诸如movzx eax, byte [esp+4]
寄存器操作数不暗示内存操作数大小的指令。
推荐阅读
- rust - 是 mem::forget(mem::uninitialized()) 定义的行为吗?
- ios - 键盘使用 React 将移动视图上的 div 向上推
- vba - If, Then, 仅粘贴值
- xamarin.forms - 在后台渲染控件
- android - #Button new on click listener
- awk - 使用 awk 限制透明大页面的输出
- html - v-for 用于动态图像网格的 vuejs 中的逻辑
- c# - 将对象的参数传递和更改为 do while 循环
- c - 使用 Semaphor、Mutex 和 PThread 进行多线程
- php - 需要仅使用 CURL 在 PHP 中使用 OAuth 2.0 访问 Google 购物 API 的示例