更新靜態(tài)庫的符號索引表
本小節的內容相對簡(jiǎn)單。前邊提到過(guò),靜態(tài)庫文件需要使用“ar”來(lái)創(chuàng )建和維護。當給靜態(tài)庫增建一個(gè)成員時(shí)(加入一個(gè).o文件到靜態(tài)庫中),“ar”可直接 將需要增加的.o文件簡(jiǎn)單的追加到靜態(tài)庫的末尾。之后當我們使用這個(gè)庫進(jìn)行連接生成可執行文件時(shí),鏈接程序“l(fā)d”卻提示錯誤,這可能是:主程序使用了之 前加入到庫中的.o文件中定義的一個(gè)函數或者全局變量,但連接程序無(wú)法找到這個(gè)函數或者變量。
這個(gè)問(wèn)題的原因是:之前我們將編譯完成的.o文件直接加入到了庫的末尾,卻并沒(méi)有更新庫的有效符號表。連接程序進(jìn)行連接時(shí),在靜態(tài)庫的符號索引表中無(wú)法定 位剛才加入的.o文件中定義的函數或者變量。這就需要在完成庫成員追加以后讓加入的所有.o文件中定義的函數(變量)有效,完成這個(gè)工作需要使用另外一個(gè) 工具“ranlib”來(lái)對靜態(tài)庫的符號索引表進(jìn)行更新。
我們所使用到的靜態(tài)庫(文檔文件)中,存在這樣一個(gè)特殊的成員,它的名字是“__.SYMDEF”。它包含了靜態(tài)庫中所有成員所定義的有效符號(函數名、 變量名)。因此,當為庫增加了一個(gè)成員時(shí),相應的就需要更新成員“__.SYMDEF”,否則所增加的成員中定義的所有的符號將無(wú)法被連接程序定位。完成 更新的命令是:
ranlib ARCHIVEFILE
通常在Makefile中我們可以這樣來(lái)實(shí)現:
libfoo.a: libfoo.a(x.o) libfoo.a(y.o) ...
ranlib libfoo.a
它所實(shí)現的是在更新靜態(tài)庫成員“x.o”和“y.o”之后,對靜態(tài)庫的成員“__.SYMDEF”進(jìn)行更新(更新庫的符號索引表)。
如果我們使用GNU ar工具來(lái)維護、管理靜態(tài)庫,我們就不需要考慮這一步。GNU ar本身已經(jīng)提供了在更新庫的同時(shí)更新符號索引表的功能(這是默認行為,也可以通過(guò)命令行選項控制ar的具體行為??蓞⒖?GNU ar工具的man手冊)。
GNU工具中ar是用來(lái)制作庫文件.a的,但同時(shí)還提供了一個(gè)ranlib,從手冊上看ranlib相當于ar -s,為什么這樣呢?
這是由于最早在Unix系統上ar程序是單純用來(lái)打包多個(gè).o到.a(類(lèi)似于tar做的事情),而不處理.o里的符號表。Linker程序則需 要.a文件提供一個(gè)完整的符號表,所以當時(shí)就寫(xiě)了單獨的ranlib程序用來(lái)產(chǎn)生linker所需要的符號信息。也就是說(shuō),產(chǎn)生一個(gè)對linker合 格的的.a文件需要做ar和ranlib兩步 。
很快,Unix廠(chǎng)商就發(fā)現ranlib做得事情完全可以合并到ar里面去,于是ar程序的升級版本就包括了ranlib的功能,但早期的很多項目的Makefile都已經(jīng)是按照兩步式的方法生成.a,所以為了保證這些早期文件的兼容性,ranlib被保留下來(lái)了。
如今,GNU/Linux系統上,ranlib依然存在,當然大部分項目已經(jīng)不使用它了,因為ar -s就做了ranlib的工作。
歷史通常是進(jìn)步和妥協(xié)的混合!