如何打印字符串没有系统调用中的x86-64汇编(NASM)终端? [英] How to print a string to the terminal in x86-64 assembly (NASM) without syscall?
问题描述
我是新来组装,并希望首先尝试获得如何打印字符串到终端将工作一个直观的感受,无需经过操作系统抽象(Linux或OSX)去。
I am new to assembly, and want to first try to get an intuitive feel for how printing a string to the terminal would work, without going through the operating system abstraction (Linux or OSX).
TL;博士你怎么写到标准输出(打印到终端)上OSX在x86-64的组装与NASM,以尽可能最低的层次(即不系统调用)? BareMetal OS是如何做呢?
tl;dr How do you write to stdout (print to the terminal) in x86-64 assembly with NASM on OSX, at the lowest level possible (i.e. without syscall)? How is BareMetal OS doing this?
大多数例子表明类似这:
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
在那里,他们使用的是系统调用
来打印字符串,这是<一个href=\"http://stackoverflow.com/questions/27592937/where-are-the-system-$c$cs-coming-from-in-x86-64-assembly#comment43609669_27592971\">relying在操作系统。我不是在寻找这一点,但对于如何写一个字符串直接stdout中,在可能的最低程度。
In there, they are using syscall
to print the string, which is relying on the operating system. I am not looking for that, but for how to write a string to stdout directly, at the lowest level possible.
有是这样的外核项目, BareMetal OS 的,我认为是这样做的。虽然因为我是新来组装,我不知道够不够没有搞清楚他们是如何做到这一点。从它看起来虽然这两个重要文件是:
There is this exokernel project, BareMetal OS that I think is doing this. Though since I am new to assembly, I don't know enough yet to figure out how they accomplish this. From what it seems though, the two important files are:
- syscalls/screen.asm
- syscalls/string.asm
这似乎相关code打印是这样(来自这两个文件中提取):
It seems the relevant code to print is this (extracted from those two files):
;
; 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
但是,这并不完全看我(虽然我还不知道,因为我是新)
But that doesn't look complete to me (though I wouldn't know yet since I'm new).
我的问题是,沿着BareMetal OS片段,你怎么写标准输出的线路上OSX(打印到终端)的x86-64的组装与NASM?
So my question is, along the lines of that BareMetal OS snippet, how do you write to stdout (print to the terminal) in x86-64 assembly with NASM on OSX?
推荐答案
这是一个很好的锻炼。您将使用系统调用
(您无法访问标准输出
以其他方式),但你可以做一个裸机乱写任何外部库提供输出程序(比如调用的printf
)。作为x86_64的基本的裸机写于标准输出
一个例子,我组建了一个例子,没有任何内部或系统函数调用:
This is a good exercise. You will use syscall
(you cannot access stdout
otherwise), but you can do a "bare-metal" write without any external library providing the output routine (like calling printf
). As an example of the basic "bare-metal" write to stdout
in x86_64, I put together a example without any internal or system function calls:
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)写标准输出
。下面的 strprn
函数将写入任何字符串标准输出
。它调用 strsz
来得到长度,同时preserving堆栈上的目标指数。这减少了写一个字符串标准输出
和prevents很多repitition您的code的任务。
For general use, I split the process into two parts (1) getting the length and (2) writing to stdout
. Below the strprn
function will write any string to stdout
. It calls strsz
to get the length while preserving the destination index on the stack. This reduces the task of writing a string to stdout
and prevents a lot of repitition in your code.
; 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
要进一步自动化一般输出到标准输出
NASM宏提供了一个方便的解决方案。例如 STRN
(以下简称 string_n
)。它有两个参数,字符串的地址和字符数写的:
To further automate general output to stdout
NASM macros provide a convenient solution. Example strn
(short for string_n
). It takes two arguments, the addresses of the string, and the number of characters to write:
%macro strn 2
mov rax, 1
mov rdi, 1
mov rsi, %1
mov rdx, %2
syscall
%endmacro
有用的缩进,换行或写完整的字符串。你可以通过传递3个参数,包括目的地 RDI
进一步推广。
这篇关于如何打印字符串没有系统调用中的x86-64汇编(NASM)终端?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!