linux - make - ubuntu gcc hello world




在Windows上的Ubuntu上的Bash上彙編編譯可執行文件不會產生輸出 (2)

我一直在尋找彙編教程,我正在嘗試運行一個hello world程序。 我在Windows上使用Ubuntu上的Bash。

這是集會:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov edx,len     ;message length
    mov ecx,msg     ;message to write
    mov ebx,1       ;file descriptor (stdout)
    mov eax,4       ;system call number (sys_write)
    int 0x80        ;call kernel

    mov eax,1       ;system call number (sys_exit)
    int 0x80        ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string

我正在使用這些命令來創建可執行文件:

nasm -f elf64 hello.asm -o hello.o
ld -o hello hello.o -m elf_x86_64

我運行它:

./hello

然後該程序似乎在沒有分段錯誤或錯誤的情況下運行,但它不產生輸出。

我無法弄清楚為什麼代碼不會產生輸出,但我想知道在Windows上使用Ubuntu上的Bash是否與它有關? 為什麼不產生輸出,我該如何解決?


問題出在Ubuntu for Windows(適用於Linux的Windows子系統)上。 它僅支持64位 syscall 接口, 而不 支持 32位x86 int 0x80 系統調用機制。

除了無法在64位二進製文件中使用 int 0x80 (32位兼容性)之外,Windows上的Ubuntu(WSL) 也不支持運行32位可執行文件

您需要使用 int 0x80 轉換為 syscall 。 這並不困難。 一組不同的寄存器用於 syscall ,系統調用號與32位對應號不同。 Ryan Chapman的博客 提供了有關 syscall 接口,系統調用及其參數的信息。 Sys_writeSys_exit 以這種方式定義:

%rax  System call  %rdi               %rsi              %rdx          %r10 %r8 %r9
----------------------------------------------------------------------------------
0     sys_read     unsigned int fd    char *buf         size_t count          
1     sys_write    unsigned int fd    const char *buf   size_t count
60    sys_exit     int error_code     

使用 syscall 還可以使用 RCX R11 寄存器。 他們被認為是不穩定的 在 syscall 之後不要依賴它們是相同的值。

您的代碼可以修改為:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov edx,len     ;message length
    mov rsi,msg     ;message to write
    mov edi,1       ;file descriptor (stdout)
    mov eax,edi     ;system call number (sys_write)
    syscall         ;call kernel

    xor edi, edi    ;Return value = 0
    mov eax,60      ;system call number (sys_exit)
    syscall         ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string

注意:在64位代碼中,如果指令的 目標 寄存器是32位(如 EAX EBX EDI ESI 等),則 處理器零將結果擴展到 64位寄存器 的高32 位。 mov edi,1mov rdi,1 具有相同的效果。

這個答案不是編寫64位代碼的入門讀物,而是關於使用 syscall 接口的。 如果您對編寫調用 C 庫的代碼的細微差別感興趣,並且符合64位System V ABI,那麼可以使用合理的教程來啟動 Ray Toal的NASM教程 。 他討論了堆棧對齊,紅區,寄存器用法以及64位System V調用約定的基本概述。


正如Ross Ridge的評論中已經指出的那樣,在編譯64位時不要使用32位內核函數調用。

編譯為32位或將代碼“轉換”為64位系統調用。 這可能是這樣的:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov rdx,len     ;message length
    mov rsi,msg     ;message to write
    mov rdi,1       ;file descriptor (stdout)
    mov rax,1       ;system call number (sys_write)
    syscall         ;call kernel

    mov rax,60      ;system call number (sys_exit)
    mov rdi,0       ;add this to output error code 0(to indicate program terminated without errors)
    syscall         ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string






windows-subsystem-for-linux