ELF(六)符號解析,此大多摘自深入理解作業系統

來源:互聯網
上載者:User
符號解析

         連結器解析符號引用的方法是將每個引用與它輸入的可重新導向目標檔案的符號表中的一個確定的符號定義聯絡起來。對於那些和引用定義在相同模組中的本地符號的引用,符號解析是非常簡單明了的。編譯器只允許每個模組中每個本地符號只有一個定義。編譯器還確保靜態本地變數,它們也會有本地連結器符號,擁有唯一的名字。

         不過對全域符號的解析就棘手多了。當編譯器遇到一個不是在當前模組中定義的符號(變數或符號)時,它會假設符號是在其他某個模組中定義的,生產一個連結器符號表條目,並把它交給連結器處理。如果連結器在它的任何輸入模組中都找不到這個引用的符號,它就會輸出一條(通常很難閱讀的)錯誤資訊並終止。比如,如果我們試著在一台Linux機器上編譯和連結下面的源檔案:

         voidfoo(void);

         intmain(){

                   foo();

                   return0;

         }

那麼編譯器會沒有障礙的運行,但是連結器無法解析對foo的引用時,它會終止:

unix>gcc -Wall -o2 -o linkerrorlinkerror.c

/tmp/ccSz5uti.o: In function 'main':

/tmp/ccSz5uti.o(.text+0x7):undefinedreference to 'foo'

collect2:   ldreturned 1 exit stauts

對全域符號的符號解析很棘手,還因為多個目標檔案可能會定義相同的符合.在這種情況中,連結器必須要麼標誌一個錯誤,要麼以某種方法選出一個定義並拋棄其他定義。Unix系統採納的方法涉及編譯器、彙編器和連結器之間的協作,這樣也可能給不警覺的程式員帶來麻煩。 連結器解析多重定義的全域符號

         在編譯時間,編譯器向彙編器輸出的每個全域符號,或者是強或者是弱,而彙編器把這個資訊隱含地編碼在可重定位目標檔案的符號表裡。函數和已初始化的全域變數是強符號,未初始化的全域變數是弱符號。

         根據強弱符號的定義,Unix連結器使用下面的規則來處理多重定義的符號:

1)不允許有多個強符號

2)如果有一個強符號和多個弱符號,那麼選擇強符號

3)如果有多個弱符號,那麼從這些弱符號中選擇任意一個

例如,假設我們試圖編譯和連結下面兩個c模組:

int main(){

         return0:

} foo1.c

int main(){

         return0;

} bar1.c

在這種情況下,連結器將產生一條錯誤資訊,因為強符號main被定義了兩次(規則1):

unix>gcc foo1.c bar1.c

****** :In function 'main':

****** :  mutipledefinition of 'main'

****** :  firstdefined here

相似的,連結器對於下面的模組也會產生一條錯誤資訊,因為強符號x被定義了兩次(規則2):

int x = 123;

int main(){

         return0;

}foo2.c

int x = 123;

void f(){}bar2.c

然而,如果在和一個模組裡x未被初始化,那麼連結器將安靜地選擇定義在另一個模組中的強符號(規則2):

#include <stdio.h>

void f();

int x = 123;

int main(){

         f();

         printf("x= %d\n",x);

         return0;

}foo3.c

int x;

void f(){

         x= 124;

}bar3.c

運行時函數f將x的值由123改為124,這會給main函數的作者帶來不受歡迎的意外。注意,連結器通常不會表明它檢測到多個x的定義:

unix> gcc -o foobar3 foo3.c bar3.c

unin>./foobar3

x = 124

如果x有兩個弱定義,也會發生相同的情況(規則3):

#include <stdio.h>

void f();

int x ;

int main(){

         x=123;

         f();

         printf("x= %d\n",x);

         return0;

}foo4.c

int x;

void f(){

x = 124;

}bar4.c

規則2和規則3的應用會造成一些不易察覺的執行階段錯誤,對於不警惕的程式員來說,這是很難解釋的,尤其當如果重複的符號定義還有不同的類型時,考慮下面的這個例子,其中x在一個模組中定義為int,在另一個模組中定義為double:

#include <stdio.h>

void f();

int x=15213;

int y = 15213;

int main(){

         f();

         printf("x= 0x%x y = 0x%x \n",x,y);

         return0;

}foo5.c

double x;

void f(){x = -0.0;}bar5.c

在一台IA32/Linux機器上,double類型是8個位元組,而int類型是4個位元組。因此,bar5.c的第6行中的賦值x=-0.0將用負零的雙精確度浮點表示覆蓋儲存空間中的x和y的位置。

linux>gcc -o foobar5 foo5.c bar5.c

linux>./foobar5

x = 0x0 y = 0x8000000

這是一個非常細微而令人討厭的錯誤,尤其是因為它是默默發生的,編譯系統不會給出警告,而且因為通常要在程式執行很久以後才表現出來,且遠離錯誤的發生地。當你懷疑由此錯誤時,用像gcc -fno-common這樣的選項調用連結器,在遇到多重定義的全域符號時,輸出一條警告。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.