【问题标题】:How to print a character in Linux x86 NASM?如何在 Linux x86 NASM 中打印字符?
【发布时间】:2014-02-21 03:41:58
【问题描述】:

我正在尝试使用 NASM 打印单个字符或数字,目标是 x86 GNU/Linux 架构。

这是我正在使用的代码:

section .text
    global _start

_start:

    ; Linux printing preparation
    mov eax,4            
    mov ebx,1       

    ; Print 'A' character 
    mov ecx,'A'     ; ecx should contain the value to print
    mov edx,1       ; edx should contain how many characters to print
    int 80h

    ; System exit
    mov eax,1            
    mov ebx,0            
    int 80h

但是,运行此代码不会打印任何内容。我做错了什么?

【问题讨论】:

标签: linux assembly x86 nasm system-calls


【解决方案1】:

ecx 应该包含一个指向 char 缓冲区开头的指针。所以你必须有你的缓冲区在内存中。您可以执行以下操作:

; Print 'A' character 
mov   eax, 4      ; __NR_write from asm/unistd_32.h (32-bit int 0x80 ABI)
mov   ebx, 1      ; stdout fileno

push  'A'
mov   ecx, esp    ; esp now points to your char
mov   edx, 1      ; edx should contain how many characters to print
int   80h         ; sys_write(1, "A", 1)

; return value in EAX = 1 (byte written), or error (-errno)

add   esp, 4      ; restore esp if necessary

您可以mov byte [esp], 'A' 或任何其他地址,如果可以覆盖堆栈上的任何内容。

或者您可以在section .rodata 中拥有一个字符数组,而不是即时存储。


使a write() system callconst void *buf arg 是一个小数字(如'A')将使它返回-EFAULT 而不会打印任何内容。无论如何,内核都必须检查指针,系统调用会返回错误,而不是在错误的指针上引发 SIGSEGV。

使用strace ./my_program 跟踪您实际进行的系统调用,包括解码返回值。

【讨论】:

    【解决方案2】:

    您正在执行的系统调用需要ecx 包含内存中的地址。这可以是任意文字地址(即在您的代码中,"A" 转换为地址 041h)、堆栈上的地址或由程序中的标签定义的地址。

    这是在内存中定义一个字节并将其写入终端的标准输出流的示例:

        section .rodata  ; This section contains read-only data
    buffer:    db 'A'    ; Define our single character in memory
    
        section .text
        global start
    _start:
        ; Prints the letter 'A', then exits
    
        mov eax, 4      ; sys_write()
        mov ebx, 1      ; ... to STDOUT
        mov ecx, buffer ; ... using the following memory address
        mov edx, 1      ; ... and only print one character
        int 80h         ; SYSCALL
    
        mov eax, 1      ; Return to system
        mov ebx, 0      ; Exit zero, success
        int 80h         ; SYSCALL
    

    【讨论】:

    • 您的 cmets 不太正确:eax=4 只是 sys_write。不一定是 TTY。无论文件描述符是否在 TTY、常规文件、网络套接字、管道、块或字符设备等上打开,您都使用相同的系统调用。请注意,如果您以 ./a.out > A.txt 运行它,您的程序仍然可以工作,在这种情况下,STDOUT 不是 TTY。
    • 另外,您可以将buffer 放入.rodata,因为它是只读的。
    • 谢谢@PeterCordes:好的,我同意。我假设这个人想打印到他们的终端,所以我以故事的形式写了我的评论,而不是确切地解释 sys_write 的工作原理。
    • 对,但你的故事有情节漏洞。为你修好了。
    • 好吧,也删除了对 TTY 作品的提及。如果人们知道 TTY 是什么,他们可能知道 STDOUT 是什么,并且只说 STDOUT 可以避免说任何关于 TTY 的事情并不总是正确的。 (虽然我不认为说 STDOUT 通常是“TTY”是模棱两可的。在 Linux 上,对于一个交互式进程来说,/dev/stdout 在当前进程的控制 tty /dev/tty 上打开,这通常是正确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 2011-10-17
    相关资源
    最近更新 更多