如何打印字符串没有系统调用中的x86-64汇编(NASM)终端? [英] How to print a string to the terminal in x86-64 assembly (NASM) without syscall?

查看:145
本文介绍了如何打印字符串没有系统调用中的x86-64汇编(NASM)终端?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新来组装,并希望首先尝试获得如何打印字符串到终端将工作一个直观的感受,无需经过操作系统抽象(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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆