日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

【计算机系统基础】符号表、符号解析(详解)

發(fā)布時間:2024/3/24 windows 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【计算机系统基础】符号表、符号解析(详解) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、符號、符號表

符號:通俗地說,就是前面跟著類型(如int/void等)的函數(shù)名,或變量名。

????????? 注意:這里的變量必須是除了非靜態(tài)局部變量之外的其它變量。

所有符號載入史冊——符號表。

分類:

①Global symbols(模塊內部定義的全局符號,又稱全局符號) – 由模塊m定義并能被其他模塊引用的全局符號。

例如,非static函數(shù)和非static的全局變量(指不帶static的全局變量
如,main.c 中的全局變量名buf


②External symbols(外部定義的全局符號,又稱外部符號) – 由其他模塊定義并模塊m引用的全局符號(標志是extern,extern用來修飾全局變量,即聲明在“最外層”)(要體現(xiàn)引用,否則不會進入符號表)
如,main.c 中的函數(shù)名swap是一個外部符號。

i.它是一個聲明,但是也僅僅是告訴鏈接器,這個swap實際上來自外部。

ii.還必須在模塊m中引用,該符號才能出現(xiàn)在符號表里。對swap的引用,則體現(xiàn)在main函數(shù)中的swap();中。

當然,考試不會給出只定義/聲明,但后面沒有引用的例子。故了解即可。

【實際上,main里不寫extern都可以,它是個弱符號,后續(xù)鏈接會自動把在main里寫到的void swap的真正定義認為在swap.o中。只不過做題時,我們把extern int a這樣的a稱作外部符號(在外面有定義),int a這樣的a稱作全局符號(其在.bss)】


?Local symbols(本模塊的局部符號,又稱本地符號) – 由模塊m定義并能被本模塊引用的本地符號。例如,在模塊m中定義的帶static的函數(shù)和變量(無論定義變量的位置,是全局還是在函數(shù)中,只要變量前面有static,都算是(本地)符號!
如,swap.c 中的static變量名bufp1

【注】不關心局部變量(當然,也不叫符號)。

如果函數(shù)的聲明中帶有關鍵字extern,則暗示這個函數(shù)可能在別的模塊里定義,在此處只是一個聲明而非定義(如下區(qū)分)。extern...可簡單理解為是引用其他模塊定義過的東西,其變量(函數(shù))名屬于外部符號。它在當前文件的符號表中,在真正定義模塊的.data或.bss節(jié)中。

int a; // 定義一個變量, 不初始化int b = 10; // 定義一個變量, 同時進行初始化extern int c; // 聲明一個外部extern的int型變量avoid swap(); //函數(shù)的聲明【而非定義】void swap(){ //函數(shù)的定義......}

【做題總結】找符號,就找前面帶類型的&&(聲明在最外層的 || 前面帶extern的 || 前面帶static的)

如圖:紅為內部(全局)符號,藍為外部(全局)符號,綠為本地符號。

【符號表】

如圖:符號的偏移量或虛擬地址一目了然!

st_size可能是函數(shù)(代碼、指令占的字節(jié)數(shù))的字節(jié)數(shù),也可能是變量所占的字節(jié)數(shù),視情況而定。

st_info比較重要,包含的信息很多!

【例如】

main.o

?本圖中,swap是外部函數(shù),它的信息許多未知

在swap.o中

同樣,buf的相關信息,在swap.o中也是未知信息。而且,bufp1是未初始化變量,放在bss中(符號表中描述bufp1滿足4字節(jié)對齊,占4個字節(jié),可以理解為.o文件鏈接后映射到存儲器(實則為主存)中,bufp1是4字節(jié)對齊,占4個字節(jié))。

二、符號解析【重點!!!】

1、符號定義的實質:為函數(shù)名時,指代碼所在區(qū);為變量名時,指所占的靜態(tài)數(shù)據(jù)區(qū)。

2、全局符號的解析比較復雜,內部符號解析較簡單。所有定義符號的值,是其目標所在的首地址。

3、強符號/弱符號

接下來所說的全局變量/函數(shù)名包括:外部符號(即外部定義的全局符號)和全局符號(即模塊內部定義的全局符號)

已初始化的全局變量名、全局函數(shù)名強符號

未初始化的全局變量名和全局函數(shù)名【即光聲明不寫具體指令的函數(shù)(如前面的extern void swap();中的swap)】,是弱符號

【易錯】本地(static)符號不參與強弱討論。函數(shù)的“引用”不算強/弱符號。非靜態(tài)局部變量也談不上強/弱符號。

【練習】

p1中的var是強符號,p2中的var是弱符號。

【練習】指出強符號和弱符號

紅框里面是強符號,藍框里面是弱符號。

【做題總結】找強/弱符號,就找?guī)ь愋偷?amp;&(聲明在最外層的||前面帶extern的)[注意:不包括前面帶static的];然后看是否初始化 值 或 代碼!

解析有如下規(guī)則:

Rule 1: 強符號不能多次定義

強符號只能被定義一次,否則鏈接錯誤

Rule 2: 若一個符號被定義為一次強符號和多次弱符號,則按強定義為準

對弱符號的引用被解析為其強定義符號

Rule 3: 若有多個弱符號定義,則任選其中一個(一般選第一個)

使用命令 gcc –fno-common鏈接時,會告訴鏈接器在遇 到多個弱定義的全局符號時輸出一條警告信息。

【做題總結】找鏈接后符號的真正定義處,若是本地符號,則定義就在本地;若是全局符號(內部/外部),則根據(jù)上述規(guī)則找真正定義處。

如本題:紅色是強符號,藍色是弱符號。

y一次強定義,一次弱定義
z兩次弱定義
p1一次強定義,一次弱定義
main一次強定義

沒有兩次強定義,因此:鏈接不會報錯

但是,y和z的輸出結果到底是誰?

p1.c中的p1函數(shù),會將左端的y符號賦值為200(雖然y是用的main.c的強定義)

同理,z最后也會因為經(jīng)過p1()函數(shù),而變成2000.

【典例】

d最終輸出結果如何?

單純編譯p1.c的時候,會認為把1.0給double d(匯編也是浮點指令)。但最終鏈接后,1.0作為浮點數(shù),要放到int d中,因為main.c中d是強符號。具體如下:

d(1.0)的double浮點數(shù)為3FF0 0000 0000 0000H,它會把原來int d空間里的數(shù)據(jù)全部沖掉,換成0000 0000,把原來int x里的數(shù)據(jù)全部沖掉,變成3FF0 0000,為一個很大的數(shù)。如果在main.c中還定義了一個如short f的在.bss的變量,則無需考慮它。.data和.bss雖然說是連續(xù)的兩個空間,且.data在低地址,.bss在高地址。但由于.bss的對齊原因,兩者之間有很大的空隙,不會沖刷掉.bss空間的內容。

因此,鏈接可以通過是真的,但是程序結果出錯了。

【規(guī)律】待用以賦值的數(shù)到底是什么類型的機器數(shù),由本.c來決定(如double),因為這是在匯編及之前就完成的。而真正賦值給的對象,是強符號(同一名稱的符號若都是弱符號,則任選其一)。

注意區(qū)分:

整個存儲器鏡像(右圖)從低地址到高地址,是從下往上的(和棧幀一樣)。因此,我們在畫d和x的內容的時候,從下往上也應該是從低地址到高地址。對于.data節(jié)而言,先聲明的變量占據(jù)低地址空間。

【經(jīng)驗教訓】別用全局變量,用的時候也要賦初值!多用本地變量(static)。

三、庫

1、避免所有函數(shù)在一個源文件中,也不要一個源文件只包括一個函數(shù)。要雨露均沾。

2、靜態(tài)庫 (.a archive files)

將所有相關的目標模塊(.o)打包為一個單獨的庫文件(.a)【包含了許多.o文件】,稱為靜態(tài)庫文件 ,也稱存檔文件(archive)

在構建可執(zhí)行文件時只需指定庫文件名,鏈接器會自動到庫中尋找那些應用程序用到的目標模塊,并把用 到的模塊從庫中拷貝出來

在gcc命令行中無需明顯指定C標準庫libc.a(默認庫)

如果要修改一個.c文件,只需要將對應新生成的.o文件覆蓋到.a中,非常方便。

例子:

ar是一個歸檔程序。

$ ar rcs libc.a atoi.o printf.o … random.o

這樣,就可以生成名為libc.a的靜態(tài)庫

然后,如$ gcc –static –o myproc main.o ./mylib.a,則就可以使用(假設自定義的靜態(tài)庫是)mylib.a了!

【注意】在鏈接的時候,無需特別地指出libc.a的標準庫,鏈接器可以自動查詢。

3、若干集合

E 將被合并到一起以組成可執(zhí)行文件所有目標文件集合(可執(zhí)行文件+靜態(tài)庫文件)

U 未解析符號(未不對應定義符號關聯(lián)的的引用符號)的集合 (符號)

比如說:在一個.o文件用了max(a,b),但是卻沒有這個max符號,這個max現(xiàn)在就稱作未解析符號。

D 當前已被加入到E的所有目標文件中定義符號的集合(定義符號)

開始EUD為空,符號解析過程如下:(掃描文件的順序:按gcc指令的順序掃描)

①命令行中文件按照順序出現(xiàn),假如現(xiàn)在是f文件,鏈接器看它是什么文件:

是可重定位目標文件,就將f放到E中,并將f中未解析符號放到U,定義符號放到D。

是靜態(tài)庫文件:鏈接器嘗試把U中所有未解析符號與f中各個目標模塊中的定義的符號匹配如果f中的某個m模塊定義了U中的未解析符號x,就將:m放入E,x從U移到D,直到U和D不再變化。又假設f中的模塊n,里面的定義符號都沒在鏈接中用到,那么n就被扔了!(隨掃隨丟)庫文件里也有可能存在那種未解析符號,那么就把靜態(tài)庫里的未解析符號也放到U里去。

如圖例,處理main.o的時候,先把這個模塊放到E中。發(fā)現(xiàn)d是定義符號,那么d就加入D,而main.c第8行的d已經(jīng)被定義了,所以d不會被放到U里面。

②若往D中加入了一個已經(jīng)存在的符號(雙重定義),或掃描完所有文件時U非空,則連接器報錯并停止。否則,鏈接器將生成.o文件,最終U中一定為空,D中符號唯一

③最終還會自動檢索默認庫,也是匹配符號的過程。它不需要在 gcc -static 后明顯指出。

【注意】鏈接器對外部引用的解析

①順序掃描.o和.a,一般把靜態(tài)庫放到后面。如果靜態(tài)庫之間用相互引用關系,則必須按照引用關系在命令行中排列靜態(tài)庫文件,使得:對每個靜態(tài)庫目標模塊中的外部引用(定義)的符號,包含其定義的靜態(tài)庫文件排在其后面。【每個靜態(tài)庫中,可能有外引符號,因此,為方便理解:可以把靜態(tài)庫中用到的模塊,當成.o文件,理解成庫內模塊中的未定義符號也放到U。】

我們的目標是:每一個找不到家的孩子找到家,每一個未關聯(lián)符號U都找到它的定義!

②最終U中一定是空集,D每個符號都是唯一的!

假設調用關系如下:
?func.o → libx.a 和 liby.a 中的函數(shù)
?libx.a → libz.a 中的函數(shù)
?libx.a 和 liby.a 之間、liby.a 和 libz.a 相互獨立
?則以下幾個命令行都是可行的:(共三種)

- gcc -static -o myfunc func.o (剩下三個庫保證x在z的前面即可)

func.o → libx.a 和 liby.a 中的函數(shù)
libx.a → liby.a 同時 liby.a → libx.a

則下列都可以:(共兩種)

– gcc -static –o myfunc func.o libx.a liby.a libx.a – gcc -static –o myfunc func.o liby.a libx.a liby.a

【例題】

(1)顯然是$ gcc –static –o myproc p.o libx.a liby.a

(2)y和x庫相互解析,故為$gcc -static -o myproc p.o libx.a liby.a (注意:y里有的符號需要x解析,故繼續(xù))? libx.a

(3)先順著寫:

$gcc -static -o myproc p.o libx.a liby.a libz.a

發(fā)現(xiàn)y也是調用x,x再調用z

那么直接寫成:$gcc -static -o myproc p.o libx.a liby.a libx.a libz.a

【解讀】總之就是被外引的.a一定要在外引的.a的后面!

總結

以上是生活随笔為你收集整理的【计算机系统基础】符号表、符号解析(详解)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。