[英]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.