汇编语言中的变量是什么?

3

我是x86汇编的新手,最近一直在使用nasm进行实验,并在Windows 10机器上运行程序。

我有以下代码:

global _start
extern  _GetStdHandle@4
extern  _WriteFile@20
extern  _ExitProcess@4
section .data
    message db "1234"
section .text
_start:
    call print
    call _ExitProcess@4
print:
    ; DWORD  bytes;    
    mov     ebp, esp
    sub     esp, 4

    ; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
    push    -11
    call    _GetStdHandle@4
    mov     ebx, eax

    ; WriteFile( hstdOut, message, length(message), &bytes, 0);
    push    0
    lea     eax, [ebp-4]
    push    eax
    push    4
    push    message
    push    ebx
    call    _WriteFile@20
    mov     esp, ebp
    ret

    ; ExitProcess(0)

我将使用以下命令进行组装:
nasm -f win32 out.asm
link out.obj /entry:start /subsystem:console "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x86\kernel32.lib"

当在命令提示符上运行时,它会按预期输出“1234”。

现在,当组装和运行以下代码时,程序直接推送“1234”,而不是消息:

global _start
extern  _GetStdHandle@4
extern  _WriteFile@20
extern  _ExitProcess@4
section .data
    message db "1234"
section .text
_start:
    call print
    call _ExitProcess@4
print:
    ; DWORD  bytes;    
    mov     ebp, esp
    sub     esp, 4

    ; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
    push    -11
    call    _GetStdHandle@4
    mov     ebx, eax

    ; WriteFile( hstdOut, message, length(message), &bytes, 0);
    push    0
    lea     eax, [ebp-4]
    push    eax
    push    4
    push    "1234"
    push    ebx
    call    _WriteFile@20
    mov     esp, ebp
    ret

它没有输出任何内容

为什么?消息有什么信息是“1234”没有的?当推送消息时,程序是否只是推送存储“1234”的内存的地址?如果是这样,我能否将“1234”存储在其他地方,然后推送其地址而不创建变量?


2
是的,你需要传递地址。是的,你可以将它存储在任何你喜欢的地方。至于你是否认为它是一个变量,几乎没有什么关系。 - Jester
1
在汇编中,另一种思考变量的方式是将其视为特定内存地址的标签。例如,message db "1234"message 是存放 "1234" 开头字节的地址。当你想要访问该地址上存储的内容时,可以通过在 [...] 中引用变量来解引用它,例如 [message] - David C. Rankin
1
好的,在“push o”行之前尝试将“1234”推入堆栈,然后将esp移动到edi,然后推入edi,而不是“1234”,看起来似乎可以正常工作。 - Bruno Jambeiro
2个回答

5

变量是一个逻辑结构 —— 一些短暂,一些长久。它们可以产生和消失。

相比之下,寄存器和内存是物理结构 —— 在某种意义上,它们总是存在的。

在汇编语言中,由人类或编译器生成,我们将需要我们的 C 代码、算法和伪代码中的逻辑变量映射到处理器中可用的物理存储器中。当一个变量的生命周期结束时,我们可以重用它所使用的物理存储器来实现另一个目的(另一个变量)。

汇编语言支持全局变量(完整的进程生命周期)和本地变量 —— 它们可以是存储在堆栈或 CPU 寄存器中的内存。当然,CPU 寄存器没有地址,因此无法通过(内存)引用进行传递。CPU 寄存器也无法进行索引,因此要对数组进行索引需要使用内存。


这似乎是正确的,也很有用要记住,但它没有回答我的问题。现在,请你详细阐述逻辑结构和物理结构之间的区别吗?例如,堆栈和全局变量是否位于内存中?如果是这样,我是否可以通过堆栈完全访问内存,还是只能访问其中一部分?(假设操作系统不会阻止我的程序) - Bruno Jambeiro
2
操作系统和CPU合谋虚拟化内存,使得每个进程看起来都有自己的地址空间,具有相似的地址。CPU知道(由操作系统编程)在执行指令时它正在工作的地址空间。栈和全局内存只是你的地址空间的一部分,因此它们具有不同的内存地址。正如我提到的,局部变量在某些条件下必须在RAM中(您希望通过引用传递它们或索引到它们),否则它们有可能成为CPU寄存器的候选对象。全局变量也需要RAM。 - Erik Eidt
感谢您的回答。 - Bruno Jambeiro

0
我会在堆栈上创建一个局部变量,就像这样:
print:
    ; DWORD  bytes;    
    mov     ebp, esp
    sub     esp, 12

    ; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
    push    -11
    call    _GetStdHandle@4
    mov     ebx, eax

    lea     ecx, [ebp-12]
    mov     dword ptr [ecx], “1234; WriteFile( hstdOut, message, length(message), &bytes, 0);
    push    0
    lea     eax, [ebp-4]
    push    eax
    push    4
    push    ecx
    push    ebx
    call    _WriteFile@20
    mov     esp, ebp
    ret

注意:语法mov ..., “1234”可能会或可能不会按照您的意愿执行,这取决于汇编器。我不记得微软汇编器如何处理它。如果它不能转换为0x34333231,请使用该常量代替。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接