簡體   English   中英

混合C和匯編。 在64位Linux上的“ Hello World”

[英]Mixing C and Assembly. `Hello World` on 64-bit Linux

基於本教程 ,我試圖將Hello World寫入64位Linux上的控制台。 編譯不會引起任何錯誤,但控制台上也不會顯示任何文本。 我不知道怎么了

write.s

.data
    SYSREAD = 0
    SYSWRITE = 1
    SYSEXIT = 60
    STDOUT = 1
    STDIN = 0
    EXIT_SUCCESS = 0

message: .ascii "Hello, world!\n"
message_len =  .-message

.text
.globl _write
_write:
    pushq %rbp
    movq %rsp, %rbp
    movq $SYSWRITE, %rax
    movq $STDOUT, %rdi
    movq $message, %rsi
    movq $message_len, %rdx
    syscall
    popq %rbp
    ret

main.c中:

extern void write(void);
int main (int argc, char **argv)
{
    write();
    return 0;
}

編譯:

as write.s -o write.o
gcc main.c -c -o main.o
gcc main.o write.o -o program
./program 

好的,所以我的代碼有兩個錯誤:

1)我將我的函數命名為“ write”,這是通用的c名稱,我需要重命名它。

2)在函數名稱中,我不應該使用下划線。

正確的代碼:

writehello.s

.data
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
STDOUT = 1
STDIN = 0
EXIT_SUCCESS = 0

message: .ascii "Hello, world!\n"
message_len =  .-message

.text
#.global main
#main:
#call write
#movq $SYSEXIT, %rax
#movq $EXIT_SUCCESS, %rdi
#syscall

#********
.global writehello
writehello:
pushq %rbp
movq %rsp, %rbp
movq $SYSWRITE, %rax
movq $STDOUT, %rdi
movq $message, %rsi
movq $message_len, %rdx
syscall
popq %rbp
ret

main.c中

extern void writehello(void);
int main (int argc, char **argv)
{
    writehello();
    return 0;
}

編譯保持原樣:)謝謝大家的幫助!

您正在閱讀的教程不太正確。 ELF(可執行和可鏈接格式)可執行文件中的全局符號有兩種不同的約定。 一種約定表示所有全局C符號應以_ ,另一種約定則不以C符號為前綴。 在GNU / Linux中,尤其是在x86-64 ABI中,全局符號不以_為前綴 但是,您鏈接的教程可能適用於其他不使用GNU libc的Linux / ELF編譯器。


現在,在原始代碼中發生的是,匯編程序函數在C代碼中將顯示為_write ,而不是write 而是在libc找到write符號( write(2)系統調用的包裝器):

ssize_t write(int fd, const void *buf, size_t count);

現在,您將此 write聲明為函數void write(void); ,當您調用它時會導致未定義的行為 您可以使用strace ./program來找出它產生的系統調用:

% strace ./program
...
write(1, "\246^P\313\374\177\0\0\0\0\0\0\0\0"..., 140723719521144) = -1 EFAULT (Bad address)
...

因此,它不使用預期的參數而是通過提供給glibc write包裝器的寄存器中的任何垃圾來調用write系統調用。 (實際上,這里已知“垃圾”-第一個參數是argc ,第二個參數是argv的值,第三個參數是char **environ的值)。 並且由於內核注意到以(void*)argv和140723719521144字節長開頭的緩沖區沒有完全包含在映射的地址空間中,因此它從該系統調用返回了EFAULT 結果:沒有崩潰,沒有消息。


在C中, write不是保留字。在POSIX中, write是一個函數,並且可能是宏。 可以覆蓋它,鏈接順序很重要-如果您編程定義write ,則將針對此定義鏈接其他代碼,而不是glibc中找到的代碼。 但是,這意味着其他調用write代碼最終將代替您的不兼容函數。

因此,解決方案是不要使用GNU libc或鏈接到的任何其他庫中的函數名稱。 因此,在匯編程序中,您可以使用:

.global writehello
writehello:

接着

extern void writehello(void);

正如您自己發現的那樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM