gpt4 book ai didi

linux - 如何在没有系统调用的情况下在 x86-64 程序集 (NASM) 中将字符串打印到终端?

转载 作者:行者123 更新时间:2023-12-04 23:20:45 28 4
gpt4 key购买 nike

我是汇编新手,想首先尝试直观地了解将字符串打印到终端的工作方式,而无需通过操作系统抽象(Linux 或 OSX)。
(编者注:接受的答案仅涵盖 Linux。x86-64 MacOS 使用类似的系统调用约定,但调用号不同。)
tl;博士 您如何在 OSX 上使用 NASM 在 x86-64 程序集中以尽可能低的级别(即没有系统调用)写入标准输出(打印到终端)? BareMetal OS 是如何做到这一点的?
大多数示例显示类似 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
在那里,他们正在使用 syscall打印字符串,即 relying on the operating system .我不是在寻找那个,而是寻找如何在可能的最低级别直接将字符串写入标准输出。
有这个exokernel项目, BareMetal OS我认为是这样做的。虽然因为我是组装新手,我还没有足够的知识来弄清楚他们是如何做到这一点的。但从表面上看,这两个重要文件是:
  • syscalls/screen.asm
  • syscalls/string.asm

  • 似乎要打印的相关代码是这样的(从这两个文件中提取):
    ;
    ; 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 程序集中写入标准输出(打印到终端)?

    最佳答案

    这是一个很好的练习。您将使用 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 的目的地)来进一步概括。 .

    关于linux - 如何在没有系统调用的情况下在 x86-64 程序集 (NASM) 中将字符串打印到终端?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27594297/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com