所以我正在学习使用 NASM 语法的 x86 Linux 程序集(哦,天哪,又不是这个,你们都在想)。我正在尝试制作一个子程序,它将简单地将 EAX 中的值打印到标准输出。代码运行并退出没有错误,但没有打印。我不知道为什么。首先,这是我正在处理的文件:
segment .bss
to_print: resd 1
segment .text
global print_eax_val
print_eax_val: ; (top)
push dword ebx ;Stack: edx
push dword ecx ; ecx
push dword edx ; ebx
; (bot)
mov ecx,eax ;ecx = eax
mov [to_print],ecx ;to_print = ecx
mov eax, 4 ;sys_write
mov ebx, 1 ;to stdout
add ecx, 47 ;add 47 for ASCII numbers
mov edx, 2 ;double word = 2 bytes
int 0x80
mov eax, [to_print] ;eax = original val
pop edx ;pop the registers back from the stack
pop ecx
pop ebx ;Stack: empty
ret
这是从我的主文件中调用的,看起来像这样(这可能无关紧要,除非我遗漏了一些激烈的东西)。
segment .data
hello db "Hello world!", 0
newline db 0xA
len equ $ - hello
len2 equ $ - newline
segment .text
extern print_nl
extern print_eax_val
global main
main:
enter 0,0
call print_nl
mov eax, 1
call print_eax_val
mov ebx, 0 ;exit code = 0 (normal)
mov eax, 1 ;exit command
int 0x80 ;ask kernel to quit
print_nl
只是另一个定义和打印换行符的子程序。这成功运行并按预期打印新行。
问题是否与我的sys_write
通话的长度参数有关?我给它 2,它是 a 的大小dword
,它是EAX
寄存器和我的to_print
标签的大小,我用resd 1
. 出于绝望,我尝试将长度更改为 1、4、8、16 和 32……没有任何效果。
编辑:对于任何想知道的人,这是我修复代码的方法:(我将在我更改的行上加上星号):
segment .bss
to_print: resd 1
segment .text
global print_eax_val
print_eax_val: ; (top)
push dword ebx ;Stack: edx
push dword ecx ; ecx
push dword edx ; ebx
; (bot)
mov ecx,eax ;ecx = eax
mov [to_print],ecx ;to_print = ecx
**** add dword [to_print], 48
mov eax, 4 ;sys_write
mov ebx, 1 ;to stdout
**** mov ecx, to_print
mov edx, 2
int 0x80
**** sub dword [to_print], 48
mov eax, [to_print] ;eax = original val
pop edx ;pop the registers back from the stack
pop ecx
pop ebx ;Stack: empty
ret
基本上,ecx
必须包含您要打印的块的地址,而不是值本身。正如所选答案中所指出的,这仅在 eax 在 0-9 范围内时才有效。
编辑 2:所以我对 sys_write 的第二个参数(存储在 中的那个)有点困惑edx
。我认为它只是指一些字节。因此,对于 a dword
,正如我使用的那样,在那里使用 4 是合适的,因为双字是 4 个字节或 32 位。我猜它起作用了,因为 x86 是小端的。所以在内存中, 的十六进制值to_print
如下所示:
90 00 00 00
并且提供的长度为 2,sys_write 得到:
90 00
所以幸运的是价值没有被破坏。
后来我将代码更改为存储to_print
为一个字节,使用resb 1
和访问它byte
而不是dword
......一个字节在这里很好,因为我知道我不会给出to_print
高于 9 的值。