ARMELF檔案格式與GNUARMLinker機制

2021-03-04 09:56:13 字數 5097 閱讀 3581

這裡所說的arm系統基本檔案格式,都是在基於arm的嵌入式系統開發中常會碰到的檔案格式。

arm系統基本檔案格式有三種:

1) bin,平板式二進位制格式,一般用於直接燒寫到flash中,也可以用於載入到monitor程式中。

2) elf,executable and linkable format,一種通用的object檔案格式,一般由gnu ***piler collection (gcc)產生。

3) axf,bin格式的擴充套件版,主體部分同bin,在檔案頭和尾加入了除錯用的資訊,用於axd。

本文主要討論bin與elf。

首先說明,elf格式是一種object檔案格式。一般object檔案都可以分成三類:可重定位object檔案,可執行object檔案,共享object檔案。

elf格式檔案也可以分成這三種。

首先說說可重定位object檔案。這種object檔案一般由gcc中的assembler(as)產生(請不要認為gcc只是編譯器),裡面除了二進位制

的機器**,還有一些可用於進行重定位的資訊。它主要是作為linker(ld)的輸入,linker將跟據這些資訊,將需要重定位的符號重定位,進而產

生可執行的object檔案。elf格式的可重定位object檔案由header與section組成。

header 包括

elf header 與 section header. elf header 位於檔案的頭部,用於儲存目標機器的架構,大小端配

置,elf header大小,object檔案型別,section header 在檔案中的偏移,section header 的大

小,section header 中的專案數等資訊。section header 則定義了檔案中每個section 的型別,位置,大小等資訊。

linker就是通過查詢elf header,找到section header 的入口,再在section header 中找到相應的

section 入口,進而定位到目標section 的。

section 包括

.text :經過編譯的機器**。

.rodata :唯讀的資料,例如printf(「hello!」)中的字串hello。

.data :已初始化的全域性變數,區域性變數將在執行時被存放在堆疊中,不會在.data或 .bss段中出現。

.bss :未初始化的全域性變數,在這裡只是乙個佔位符,在object檔案中並沒有實際的儲存空間。

.symtab :符號表,用於存放程式中被定義的或被引用到的全域性變數和函式的資訊。

.rel.text :

乙個儲存著一系列在.text中的位置的列表。這些位置將在linker把這個檔案與其它object檔案合併時被修改,一般來說,這些位置都是儲存著一

些引用到全域性變數或者外部函式的指令。引用區域性變數或者本地函式的指令是不需要被修改的,因為區域性變數和本地函式的位址一般都是使用pc相對偏移位址的。

需要注意的是,這個section 和下面的.rel.data在執行時並不需要,生成可執行的elf object檔案時會去掉這個section。

.rel.data :儲存全域性變數的重定位資訊。一般來說,如果乙個全域性變數它的初

始化值是另乙個全域性變數的位址,或者是外部函式的位址,那麼它就需要被重定位。

.debug :儲存debug資訊。

.strtab : 乙個字串表,儲存著.

symtab和.debug ,和各個section的名字。.symtab,.

debug 和section table裡面,凡是儲存name的域,其實都是儲存了乙個偏移值,通過這個偏移值在這個字串表裡面可以找到相應得字串。

下面仔細討論一下.symtab:

每乙個可重定位的object檔案,都會有乙個.symtab。這個符號表儲存了在這個object檔案中所有被定義的和被引用的符號。

當源程式是c 語言程式時,.symtab 中的符號直接**於c編譯器(cc1)。這裡所說的符號主要有三種:

1) 在這個object檔案中被定義的可以被其他object檔案全域性符號。在c語言源程式中,主要就是那些非靜態(沒有static 修飾的)的全域性變數和非靜態的函式。在arm組合語言中,就是那些被export 指令匯出的變數。

2) 在這個object檔案中引用到,但是在其他檔案中定義的全域性變數。在arm組合語言中就是通過import命令引入的變數

3) 本地變數。本地變數只在本object檔案內可見。這裡的本地變數指的是聯結器本地變數,應該和一般的程式本地變數作區別。這裡所指的本地變數,包括用

static 修飾的全域性變數,object檔案中section名稱,源**檔名稱。一般意義上的本地變數,是在執行時由系統的執行時環境管理的,linker並不關心。

每個符合上面條件的符號在.symtab檔案中都會有乙個資料項。這個資料項的資料結構是:

typedef structelf_sym

現在假設組成應用的各個模組都已經被彙編,構建出了可重定位的object檔案。這些object的結構都是一樣的,有各自的.text, .

data section, 有各自的.symtab. gcc下一步要做的就是使用linker (ld),把這些object檔案,加上必要的庫連線成具有絕對執行時位址的可執行檔案,就是可執行的elf格式的檔案。

linker 的連線動作可以分為兩部分:

1) 符號解析。確定引用符號的指向。

2) 符號重定位。合併section, 分配執行時環境位址,引用符號重定位。

符號解析:

在乙個object檔案中,有指令定義了符號,也有指令引用了符號。可能存在這樣一種情況,乙個被引用到的符號,有多重的定義。符號解析的作用就是確定,在這個object檔案中,乙個符號引用真正引用的是哪個符號。

在編譯的時候,除了在本檔案中定義的全域性變數會由編譯器生成乙個符號表項之外,當發現乙個被引用到的符號在本檔案中並沒有被定義,編譯器也會自動產生乙個符號表項,把確定這些引用的工作留給linker。彙編器在彙編時將讀取這些符號表項,生成.symtab。

在讀取的過程中,如果發現有在無法確定的符號引用項,彙編器會為這些符號額外生成乙個資料項,稱作重定位資料項,存放於rel.text或rel.data section中,交由linker確定。

下面是重定位資料項(relocation entry)的資料結構:

typedef structelf32_rel

linker 需要解析的,就是那些被生成了重定位資料項的引用。linker將根據c語言定義的規則,對於每乙個重定位資料項,在輸入的各個object檔案中查詢適合的符號,把這個符號填入symbol項中。但是由於還不知道這個符號的真實位址,所以現在就算知道了引用的真實指向,但我們還是不能確定這個引用指向的位址。

符號重定位:

符號重定位用來解決上面的問題。linker首先進行section 的合併。linker合併

object檔案的過程很簡單,一般就是相同屬性的section合併,例如不同object檔案的.text section 將被合併成乙個.text。

同樣,.symtab section也被合併成乙個.symtab。

這裡面涉及到兩個問題:

1) 各個object檔案合併的順序。這個問題涉及到最終指令和符號的執行位址。最為重要的是,究竟是哪個section排在最前頭?

在arm raw 系統得開發過程中,這個最為重要。

arm系統cpu上電後,系統會自動的從0x00000000位址取指令並執行,這個位址上對映著儲存器。這個動作是不可程式設計的。所以排在最前面的

section一定要包含有程式的入口點,否則系統無法正常執行。

2) 輸入段與輸出端之間的對應關係。理論上,任何section,都可以被隨意的對映到乙個輸出段中。乙個.

data section是可以與乙個.text section 組成輸出乙個.text的。

當然這樣的動作毫無意義。

我們必須告訴linker使用那些section作為輸入,產生乙個輸出section.

以上這兩個問題,都是通過乙個稱為連線

指令碼的檔案控制的。linker通過讀取連線指令碼,來決定section 從輸入到輸出的對映,設定程式的入口點,設定哪個section應該在整個可執行檔案的頭部等問題。

連線指令碼還有另外乙個作用,那就是指定每個section的位址。在section 合併完成後,linker將跟據.symtab,對符號進行統一的編址,分配乙個絕對的執行時位址。

這個位址是以section位址作為基位址的。假設.text section的位址是0x00000000,那麼.

text裡面的符號將以0x00000000這個位址作為基準位址。指定section位址的工作也是由連線指令碼完成。在嵌入式開發中常見的在編譯工程時需指定的text_base, data_base等引數,最後會被加入到連線指令碼中,從而完成section的位址分配。

以上兩步完成後,linker 執行引用符號重定位操作。linker遍歷.rel section (包括.

rel text 和 .rel data),對於其中的每個資料項,根據symbol域到.symtab 中查出相應的引用的真實位址(經過上面的位址分配,現在.

symtab裡面的符號都具有絕對的執行位址),再根據offset域提供的偏移,將這個位址填入相應的位置上。

至此,符號重定位工作全部完成。linker刪除用於儲存重定位資訊的rel.text和rel.

data section,加入乙個segment header和乙個.init section。生成可執行的elf格式的object檔案。

segment header

儲存了用於作業系統記憶體對映的資訊。.init section 包含了乙個_init 的函式。程式載入時,作業系統的程式載入器通過讀取

segment header,將程式載入到使用者記憶體空間,並根據segment header裡面對映資訊,分別將.text 段和.data段對映到適當的位址上。

然後再呼叫.init中的_init函式,完成初始化工作。

由於elf檔案具有通用性強的優點,現在流行的開發模式是,先通過編譯工具生成elf檔案格式的可執行檔案,在使用外部工具,抽離出elf檔案中的相應部分,生成bin檔案。例如著名的

gnu bootloader u-boot,就採用了這種做法,編譯器工具集是gcc,bin生成工具是elf2bin。arm公司著名的開發環境

ads,雖然使用的是自家的armcc,和armcpp編譯器,但他們的工作方式卻是與gnu gcc如出一轍。

消防檔案格式

單位消防基礎資料記錄 單位名稱 建檔日期年月 目錄1 單位基本情況 2 單位總平面圖 3 消防安全管理組織機構 4 重點單位及消防安全責任人 5 單位消防安全制度 6 消防設施 滅火器材基本情況 7 消防設施器材配置及管理登記明細表 8 消防設施定期檢查 檢測 維修保養記錄9 滅火和應急疏散預案 1...

消防檔案格式

單位消防基礎資料記錄 單位名稱 建檔日期年月 目錄1 單位基本情況 2 單位總平面圖 3 消防安全管理組織機構 4 重點單位及消防安全責任人 5 單位消防安全制度 6 消防設施 滅火器材基本情況 7 消防設施器材配置及管理登記明細表 8 消防設施定期檢查 檢測 維修保養記錄9 滅火和應急疏散預案 1...

投標檔案格式

報建編號 標段號 專案施工招標 投標檔案 投標人 單位公章 法定代表人或其委託 人 簽字或蓋章 年月日目錄 一 商務標 包括但不僅限於以下內容 一 投標承諾書 原件 投標函 原件 投標函附錄a 上海市建設工程施工投標標書情況彙總表 原件 投標函附錄b 原件 二 法定代表人證明 原件 授權委託書 原件...