我有一個 hello world 程式:
.global _start
.text
_start:
# write (1, msj, 13)
mov $1, %rax # system call 1 is write
mov $1, %rdi # file handler 1 is stdout
mov $message, %rsi # address of string to output
mov $13, %rdx # number of bytes
syscall
# exit(0)
mov $60, %rax # system call 60 is exit
xor %rdi, %rdi # we want to return code 0
syscall
message:
.ascii "Hello, world\n"
我可以將它組裝成一個目標檔案:
as hello.s -o hello.o
此目標檔案不可執行。當我嘗試執行它時,我得到:
bash: ./hello.o: cannot execute binary file: Exec format error
我需要呼叫聯結器以使其可行:
ld hello.o -o hello
此時,hello程式運行。然而,這里的聯結器的使用讓我感到困惑......我沒有在任何外部庫中進行鏈接!我似乎只是將目標檔案鏈接到任何東西。
聯結器為這樣一個“自包含”程式做什么?
uj5u.com熱心網友回復:
ELF 檔案有不同的型別,例如 ELFTYPE_EXEC(傳統的非 PIE 可執行檔案)或 ELFTYPE_REL(可重定位的目標檔案,通常帶有.o檔案名)。
as沒有輸出可執行檔案而不是目標檔案的特殊情況模式。還有其他匯編器,或者至少一個:FASM,它們確實有一種特殊的模式可以直接輸出 ELF 可執行檔案。
鑒于as產生的 ELF 目標檔案,您可以:
- 像你一樣將它鏈接到一個簡單的靜態可執行檔案中
- 將其鏈接到 PIE 可執行檔案中
- 將它鏈接到一個動態的可執行檔案,甚至可能是鏈接一些
.so共享庫的檔案;那些可能有靜態建構式(初始化函式)在你的_start. (例如,glibc 就是libc.so這樣做的,這就是為什么如果動態鏈接_start,它恰好可以在 Linux 上呼叫 libc 函式而無需手動呼叫 glibc init 函式。)
在.o因為沒有絕對地址已被選定為它在被加載,填補東西像您的64位絕對立即在被鏈接的需求mov $message, %rsi。
(如果您使用lea message(%rip), %rsi代碼將與位置無關,但.text和.rodata部分之間的距離尚不可知。盡管您將字串放在正確的位置,.text以便在您沒有選擇最少的情況下在匯編時得到解決將地址放入暫存器的有效方法,這樣就可以為您提供一個獨立的代碼 資料塊。但最有效的方法,mov $message, %esi還需要一個絕對(32 位)地址。)
as不知道你想做什么,GNU Binutils 主要是為編譯器后端撰寫的,所以沒有必要讓as能夠直接撰寫 ELF 型別的EXEC檔案變得更復雜,因為這就是ld它的用途。這就是 Unix 哲學,即制作能夠做好一件事的小型獨立工具。
如果你想用一個命令組裝 鏈接,制作一個 shell 腳本,或者使用一個編譯器前端:
gcc -nostdlib -static -no-pie start.s -o static_executable
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/365375.html
