首页 > 解决方案 > 为什么我们不能使用 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]

而且,为什么我们需要在这里进行类型转换?

我还需要这个程序的内存布局来理解堆栈机制。我在网上找到了很多图片,但我不确定哪一张适合代表这个程序。

标签: linuxassemblyx86nasm

解决方案


如果是 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]寄存器操作数暗示内存操作数大小的指令。


推荐阅读