[英]Linux x86 NASM - Subroutine: Print a dword from EAX
所以我正在學習使用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
調用的length參數有關? 我給它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的值。
我在匯編程序中非常生疏,但看起來ECX包含值48,它應該包含要寫入的緩沖區的地址。
我認為你在print_eax_val
中的print_eax_val
是采用EAX的二進制值,將ASCII偏移量添加到數字0
(應該是48,而不是47),然后打印該單個字符。 要做到這一點,在存儲前值增加48 to_print
,把地址to_print
在ECX,長度(EDX)設置為1,因為你寫的只有一個字符。
現在請記住,這僅適用於0x0000和0x0009之間的EAX值。 當你超過9時,你會得到其他ASCII字符。
解釋如何獲取EAX的任意二進制值並將其轉換為可打印的十進制字符串遠遠超出了SO的范圍。
write
系統調用需要打印字符緩沖區,由%ecx
指向,其長度由%edx
。 另一方面,像%eax
這樣的寄存器包含一個32位整數。
因此,如果您確實要打印%eax
,則需要先將該整數轉換為字符序列。 您可以將其轉換為ASCII十進制,十六進制或任何其他您喜歡的基礎,但您需要將其轉換為字符。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.