所以我正在学习使用NASM语法的x86 Linux程序集(哦,上帝,不是这个,你们都在想)。我正在尝试创建一个子程序,只需将EAX中的值打印到stdout即可。代码运行并退出而没有错误,但没有打印。我无法弄清楚为什么。首先,这是我正在处理的文件:
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,这是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
中的那个)有点困惑。我认为它只是指了一些字节。因此,对于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的值。
答案 0 :(得分:4)
我在汇编程序中非常生疏,但看起来ECX包含值48,它应该包含要写入的缓冲区的地址。
我假设你在print_eax_val
中想要的是取EAX的二进制值,将ASCII偏移量添加到数字0
(应该是48,而不是47),然后打印该单个字符。为此,在将值存储在to_print
之前添加48,将to_print
的地址放在ECX中,并将长度(EDX)设置为1,因为您只写了一个字符。
现在请记住,这仅适用于0x0000和0x0009之间的EAX值。当你超过9时,你将获得其他ASCII字符。
解释如何获取EAX的任意二进制值并将其转换为可打印的十进制字符串远远超出SO的范围。
答案 1 :(得分:3)
write
系统调用需要打印一个字符缓冲区,由%ecx
指向,长度由%edx
给出。另一方面,像%eax
这样的寄存器包含一个32位整数。
因此,如果您确实要打印%eax
,则需要先将该整数转换为字符序列。您可以将其转换为ASCII十进制,十六进制或任何其他您喜欢的基础,但您需要将其转换为字符。