連結器、載入器與目的檔格式

節區與區段(sections versus segments)

想像一份長文件,你可以用兩種不同方式描述它。文字編輯看到的是章節、註腳與索引——細緻、具名的片段。印刷工人只看到哪幾頁印在哪張紙上、每張紙要亮面還是霧面。同一份文件,兩種視角,對應兩種不同的工作。ELF 檔正是用這兩種方式來描述的:節區(編輯的視角,給連結器)與區段(印刷工人的視角,給載入器)。

節區列在節區標頭表裡。每個節區有名字(.text、.data、.rodata、.bss、.symtab、.rela.text 等等)、型別與旗標,它把為了連結而屬於一起的位元組分組——所有程式碼、所有唯讀資料、所有重定位項目等等。連結器一生都在合併來自多個 .o 檔的同名節區。區段列在程式標頭表裡,每個區段是檔案中的一段連續範圍(可能涵蓋多個節區)外加它應取得的記憶體權限:典型上一個可讀可執行的區段裝 .text 與 .rodata,一個可讀可寫的區段裝 .data 與 .bss。載入時載入器走過各區段,做出類似 mmap() 的映射;它根本不在意節區名字。

兩者的關係是多對一:通常數個節區被綑成一個區段,挑選方式是讓需要相同權限的節區放在一起。可重定位的 .o 檔有節區但通常沒有有用的區段(它本來就不打算執行);完成的可執行檔或共享物件兩者都有。一個常見誤解是 .bss 在檔案裡佔空間——它不佔;它的區段在載入時保留填零的記憶體,而檔案只存它的大小。剝除(strip)一個二進位檔可以移除許多節區而不碰區段,所以程式仍能執行,但更難除錯。

$ readelf -l ./a.out # 程式標頭(區段)與對應 Segment Sections... 02 .text .rodata (R E) 03 .data .bss (RW )

readelf -l 顯示區段,以及每個區段裝哪些節區:程式碼加唯讀資料拿到讀加執行,資料加 bss 拿到讀加寫。

連結器讀節區;載入器讀區段。被剝除的二進位檔可能完全沒有節區標頭表卻仍能完美執行,因為執行只需要區段。

又稱
section header table vs program header table節區標頭表與程式標頭表