[英]Newline byte 0Ah being ignored by x86_64 system call print program
我在創建 NASM x86_64 程序時遵循了一個簡單的教程,該程序使用定義的函數來打印變量,並在末尾添加新行。 sprintLF 調用 sprint,它依次打印 rax 中設置了適當系統調用的任何內容。 返回時 sprintLF 用 0Ah 更新 rax 換行代碼,然后將其壓入堆棧並將 rax 重新分配給 0Ah 的堆棧地址,然后再次調用 sprint 並將換行代碼寫入 stdout。 在我在 gdb 中調試 sprint 的所有代碼下面,這表明所有正確的寄存器都存儲了與系統調用 4 關聯的值,我不知道為什么成功打印了變量字符串但換行符卻沒有。
呼叫代碼
;; Hello World Program (Externam file include)
;; Compile with: nasm -f elf64 helloworld-if.asm
;; Link with ld helloworld-if.o -o helloworld-if
;; Run with ./helloworld-inc
%include 'function.asm' ; include our external file
SECTION .data
msg1 db 'Hello, brave new world!', 0h ;our first message string add null terminating byte
msg2 db 'This is how we recycle in NASM.', 0h ; our second message string add null terminating byte
SECTION .text
global _start
_start:
mov rax, msg1 ; mov the address of our first message string into RAX
call sprintLF ; call our string printing function
mov rax, msg2 ; move the address of our second message string into RAX
call sprintLF ; call our string printing function
call quit ; call our quit function
實用功能
; -------------------------------------------------------------------------------------------------------------------
; int slen(String message)
; String length calculation function
slen: ; this is our first function declaration
push rbx ; push the value in RBX onto the stack to preserve it while we use RBX in this function
mov rbx, rax ; move this address in RAX into RBX ( Both point to the same segment in memory)
nextchar:
cmp byte [rax], 0 ; this is the same as lesson 3
jz finished
inc rax
jmp nextchar
finished:
sub rax, rbx
pop rbx ; pop the value on the stack back into RBX
ret ; return to where the function was called
;; ---------------------------------------------------------------------------------------------------------
;; void sprint(String message)
;; String printing function
sprint:
push rdx
push rcx
push rbx
push rax
call slen
mov rdx, rax
pop rax
mov rcx, rax
mov rbx, 1
mov rax, 4
int 80h
pop rbx
pop rcx
pop rdx
ret
;; ----------------------------------------------------------------------------------------------------------
;; void sprintLF(String message)
;; String printing with line feed function
sprintLF:
call sprint
push rax ; push rax onto the stack to preserve it while we use the rax register in this function
mov rax, 0Ah ; push 0Ah into rax, 0Ah is the ascii character for a linefeed
push rax ; push the linefeede onto the stack so we can get the address
mov rax, rsp ; move the address of the current stack pointer into rax for sprint -> because write requires a memory address
call sprint ; call our sprint function
pop rax ; restore out linefeed character from the stack
pop rax ; return to our program
ret
;; -----------------------------------------------------------------------------------------------------------
;; void exit()
;; Exit program restore resources
quit:
mov rbx, 0
mov rax, 1
int 80h
ret
用於執行代碼和輸出的命令如下:
nasm -f elf64 helloworld-if.asm
ld helloworld-if.o -o hellworld-if
./hellworld-if
Hello, brave new world!This is how we recycle in NASM.
在另一個程序中,我嘗試在將參數放入堆棧后打印參數,同樣發生了同樣的情況,所以我只能猜測系統調用不喜歡從堆棧中獲取它的值,但我是匯編的新手,這讓我感到困惑。
您一直在嘗試將使用int0x80
32 位 Linux 代碼轉換為 64 位代碼。 雖然這可以在很多情況下工作,但它並不適用於所有情況。 int 0x80
是 32 位系統調用接口,但是由於 Linux 內核中內置的 IA32 兼容性(大多數發行版的默認設置),您仍然可以使用int 0x80
。 問題在於,當內核處理您的int 0x80
請求時,只能識別寄存器的低 32 位。
您第一個問題中的代碼沒有出現任何問題,但此代碼不起作用。 原因是 RSP 中的堆棧指針通常是無法用 32 位值尋址的地址。 當您執行mov rax,rsp
,RSP 的完整 64 位值將移至 RAX,但sprint
的int 0x80
調用只會看到 RAX 的底部 32 位(EAX 寄存器)。
解決這個問題的方法是使用 64 位syscall
接口。 不幸的是,傳入的系統調用號和寄存器參數發生了變化。 Ryan Chapman 的博客有一個很好的 64 位syscall
系統調用號及其參數表。
表中的sys_write
系統調用號和參數是:
根據此信息,您可以通過執行以下操作將sprint
轉換為使用syscall
接口:
sprint:
push r11 ; R11 and RCX are clobbered by syscall as well
push rcx
push rdx
push rsi
push rdi
push rax
call slen
mov rdx, rax ; RDX = number of characters to print
pop rax
mov rsi, rax ; RSI = address of characters to print
mov rdi, 1 ; RDI = file descriptor (1=STDOUT)
mov rax, 1 ; System call number 1 = sys_write
syscall ; 64-bit system call (rather than int 0x80)
pop rdi
pop rsi
pop rdx
pop rcx
pop r11
ret
這是相當低效的,它可以被清理。 我以這種方式呈現它,以便您可以從原始代碼的角度理解更改。 我已經評論了相關的行。
注意:您真的應該使用 Ryan Chapman 的表作為指南將所有int 0x80
調用轉換為syscall
。 我把它作為 OP 的練習。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.