我是组装的新手,并且想要首先尝试直观地了解如何在终端上打印字符串而不需要进行操作系统抽象(Linux或OSX)的工作。
(编者注:接受的答案仅适用于Linux。x86-64 MacOS使用类似的系统调用约定,但使用不同的电话号码。)
tl; dr如何在可能的最低级别(即不使用syscall)下使用NASM在OSX上向x86-64程序集写入stdout(打印到终端)?BareMetal OS如何做到这一点?
大多数示例显示类似这样:
global start
section .text
start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, 13
syscall
mov eax, 60
xor rdi, rdi
syscall
message:
db "Hello world", 10
在那儿,他们syscall
用来打印字符串,这取决于操作系统。我不是在寻找那个,而是在如何以最低的级别直接将字符串写入stdout。
我认为有一个exokernel项目,即BareMetal OS。尽管由于我是组装的新手,所以我还不太了解他们是如何实现这一目标的。从表面上看,两个重要文件是:
似乎要打印的相关代码是这样的(从这两个文件中提取):
;
; Display text in terminal.
;
; IN: RSI = message location (zero-terminated string)
; OUT: All registers preserved
;
os_output:
push rcx
call os_string_length
call os_output_chars
pop rcx
ret
;
; Displays text.
;
; IN: RSI = message location (an ASCII string, not zero-terminated)
; RCX = number of chars to print
; OUT: All registers preserved
;
os_output_chars:
push rdi
push rsi
push rcx
push rax
cld ; Clear the direction flag.. we want to increment through the string
mov ah, 0x07 ; Store the attribute into AH so STOSW can be used later on
;
; Return length of a string.
;
; IN: RSI = string location
; OUT: RCX = length (not including the NULL terminator)
;
; All other registers preserved
;
os_string_length:
push rdi
push rax
xor ecx, ecx
xor eax, eax
mov rdi, rsi
not rcx
cld
repne scasb ; compare byte at RDI to value in AL
not rcx
dec rcx
pop rax
pop rdi
ret
但这对我来说似乎并不完整(尽管自从我是新人以来我还不知道)。
所以我的问题是,按照该BareMetal OS片段的方式,如何在OSX上使用NASM在x86-64程序集中写入stdout(打印到终端)?
这是一个很好的练习。您将使用syscall
(stdout
否则将无法访问),但是您可以进行“裸机”写入,而无需任何外部库提供输出例程(例如调用printf
)。作为stdout
在x86_64中写入基本“裸机”的示例,我整理了一个没有任何内部或系统函数调用的示例:
section .data
string1 db 0xa, " Hello StackOverflow!!!", 0xa, 0xa, 0
section .text
global _start
_start:
; calculate the length of string
mov rdi, string1 ; string1 to destination index
xor rcx, rcx ; zero rcx
not rcx ; set rcx = -1
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec rcx through NUL)
not rcx ; rev all bits of negative results in absolute value
dec rcx ; -1 to skip the null-terminator, rcx contains length
mov rdx, rcx ; put length in rdx
; write string to stdout
mov rsi, string1 ; string1 to source index
mov rax, 1 ; set write to command
mov rdi,rax ; set destination index to rax (stdout)
syscall ; call kernel
; exit
xor rdi,rdi ; zero rdi (rdi hold return value)
mov rax, 0x3c ; set syscall number to 60 (0x3c hex)
syscall ; call kernel
; Compile/Link
;
; nasm -f elf64 -o hello-stack_64.o hello-stack_64.asm
; ld -o hello-stack_64 hello-stack_64.o
输出:
$ ./hello-stack_64
Hello StackOverflow!!!
对于一般用途,我将过程分为两部分(1)获取长度和(2)写入stdout
。strprn
函数下面将向写入任何字符串stdout
。它调用strsz
以获取长度,同时在堆栈上保留目标索引。这减少了向其中写入字符串的任务,stdout
并防止了代码中的很多重复。
; szstr computes the lenght of a string.
; rdi - string address
; rdx - contains string length (returned)
section .text
strsz:
xor rcx, rcx ; zero rcx
not rcx ; set rcx = -1 (uses bitwise id: ~x = -x-1)
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec rcx through NUL)
not rcx ; rev all bits of negative -> absolute value
dec rcx ; -1 to skip the null-term, rcx contains length
mov rdx, rcx ; size returned in rdx, ready to call write
ret
; strprn writes a string to the file descriptor.
; rdi - string address
; rdx - contains string length
section .text
strprn:
push rdi ; push string address onto stack
call strsz ; call strsz to get length
pop rsi ; pop string to rsi (source index)
mov rax, 0x1 ; put write/stdout number in rax (both 1)
mov rdi, rax ; set destination index to rax (stdout)
syscall ; call kernel
ret
为了进一步自动化对stdout
NASM宏的常规输出,提供了一种方便的解决方案。示例strn
(的缩写string_n
)。它带有两个参数,即字符串的地址和要写入的字符数:
%macro strn 2
mov rax, 1
mov rdi, 1
mov rsi, %1
mov rdx, %2
syscall
%endmacro
对于缩进,换行或编写完整的字符串很有用。您可以通过传递3个参数(包括的目标位置)来进一步概括rdi
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句