在這個簡單的程式中,我在main中得到了compute的重定位,但在compute2中沒有:
static int compute2()
{
return 2;
}
int compute(); }
{
return 1;
}
int main(); }
{
return compute() compute2()。
}
我用gcc -c main.cpp在Ubuntu 21.10上使用gcc 11.2.0編譯這個程式。
下面是objdump對main的描述:
000000000000001e <main> 。
1e: f3 0f 1e fa endbr64
22: 55 push rbp
23: 48 89 e5 mov rbp,rsp
26: 53 push rbx
27: e8 00 00 00 呼叫 2c < main 0xe> 28: R_X86_64_PLT32 compute()-0x4
2c: 89 c3 mov ebx,eax
2e: e8 cd ff ff ff call 0 <compute2()>
33: 01 d8 add eax,ebx
35: 48 8b 5d f8 mov rbx,QWORD PTR [rbp-0x8] 。
39: c9 leave
3a: c3 ret
正如你所看到的,對于呼叫compute2(內部鏈接),有一個相對的跳轉,沒有重定位。但是對于呼叫compute(外部鏈接),有一個重定位,即使所有三個函式都在同一個物件檔案的同一個部分。
為什么需要這種重定位?我以為聯結器不會分割一個部分,所以無論這個部分在哪里被加載,相對地址都應該是一樣的?為什么鏈接似乎會影響到這一點?
uj5u.com熱心網友回復:
這并不是說重定位本身是需要的,而是編譯器選擇通過PLT來做指示(因為可能的符號互置,或者在主可執行檔案或早期共享lib定義符號的情況下)。注意重定位型別R_X86_64_PLT32。
如果你看一下編譯器的asm輸出(不是對.o的反匯編),你會看到call compute@plt。
一個靜態函式肯定總是使用同一翻譯單元中的定義,但其他全域符號的定義可以優先使用。
這應該是對全域符號的定義。
這應該只發生在 https://godbolt.org/z/qYYWsYf6a
顯示GCC的 注意,當鏈接一個可執行檔案(而不是共享庫)時,呼叫應該被 "放松 "為直接呼叫,不需要通過PLT。 因此,GCC可以將所有的呼叫排放為 如果你也使用
你可以使用 對于你的情況, 也請看 uj5u.com熱心網友回復: 我相信這個行為的實作是為了實作符號互置--通過將 而你的-fPIC,而不是構建主可執行檔案本身(-fPIE在大多數現代發行版中是默認開啟的),對于定義在同一個.c(翻譯單元)中的符號。
-fPIE仍在使用call compute。 顯然,Ubuntu啟用了一些其他的選項,使之有所不同? (Godbolt的GCC沒有默認啟用大多數發行版的一些東西,所以你需要一些選項來匹配Ubuntu上GCC的配置方式。 -fstack-protector-strong并不相關,我不知道還有什么會相關。
call foo@plt.-fno-plt,呼叫將以call *foo@gotplt(%rip)的形式發出,這需要6個位元組,所以將其放松為直接5位元組的呼叫rel32需要一個位元組的填充;ld使用一個無意義的地址大小前綴。 (參見我對不能在64位Linux上從匯編(yasm)代碼中呼叫C標準庫函式的回答,以獲得一個例子。)
如果你一開始就不想要這個PLT間接性,你可以為該符號設定ELF可見性=隱藏。 在制作共享庫時,這是一個非常好的主意,因為在這種情況下,聯結器將無法通過PLT來放松對你不打算允許符號插入的函式的內部呼叫的所有指示。
-fvisibility=hidden來使其成為所有原型的默認值,所以呼叫將使用call rel32,而不是通過PLT間接呼叫(或使用-fno-plt的 GOT)。 然后對于任何共享庫想要匯出的函式或變數,使用__attribute__((visibility("default")))-fvisibility=hidden可能會解決你所遇到的問題,即使你沒有構建可以進入共享庫的代碼,GCC也會不必要地進行間接處理(使用-fPIC)。
-fno-plt存在之前;所以至少這個想法已經實作了,而且現在是一些發行版二進制軟體包的默認值,比如Arch GNU/Linux。)compute呼叫暴露為一個可重定位的操作碼,你可以像> LD_PRELOAD=custom_compute.so ./main
compute呼叫將被重新定位到.so中定義的自定義compute函式。
這個功能對于像compute2這樣的靜態函式來說是無效的--這些函式是內部鏈接的,不應該用于符號互置。
LD_PRELOAD,而是更普遍地與共享庫有關 - 例如,在這個例子中,如果有兩個共享庫被加載,都定義了compute - 第二個庫對compute的呼叫將被重定位到第一個庫的函式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/320405.html
標籤:
上一篇:FASMx86架構中的字串反轉
