動態模組載入和ELF Loader

2021-06-16 06:46:06 字數 2689 閱讀 5802

[**

很早很早以前就想在嵌入式系統上實現動態模組載入的功能了,期間走了些彎路,直到最近,才完整地在嵌入式系統上實現動態模組載入。 

=== 動態模組載入的好處 === 

動態模組載入的好處很多,例如,當你公升級乙個系統的時候,可以只公升級乙個模組,而不必公升級整個系統。你可以把不同的模組放在不同的介質上,並實施不同等級的保護,例如bios部分進行寫保護。 

有些系統允許使用者進行二次開發,這個時候幾乎一定是需要動態載入功能的,因為你不希望使用者需要鏈結整個系統才能夠進行二次開發,而且你可能希望支援多個使用者模組,彼此不相互依賴,彼此不干擾。 

=== background === 

一般來說,c的編譯器編譯出來的**,由以下幾個重要的部分: 

.code: **段 

.data: 有初值的資料段 

.bss: 無初值的資料段 

通常還有.rodata,是唯讀的資料段,在嵌入式系統中經常可以合併到.code段中. 

注: .code, .data 和.bss這些段的命名不同的編譯器可能會有不同。 

由於不同段在實際執行的時候可能會被載入到不同的介質,例如.code和.rodata可以放在nor flash上而.data,.bss放入ram中,或者要滿足所謂的scatter loading,因此編譯器會努力使段可以自由移動。 

但是要做到這一點,並不容易。 

在**段中執行的指令,要獲取資料段中的資料,方法有: 

a) 通過當前pc值+偏移量 

b) 通過絕對位址 

c) 通過中間暫存器,暫存器裡面: 

c.1) 存放絕對位址 

c.2) 偏移量 

方法b通常只在cisc中存在,許多risc機器由於指令長度受限制,並不存在方法b。 

因此,從這裡可以看出,要做到各段可以自由移動,有幾種方法: 

1) 保留乙個暫存器專門用於指示資料段的起始位址 

2) 執行前修改指令 

3) 保留一小塊資料段和**段的相對位置不變,此片資料段作為指向實際資料段的入口表, 執行前修改此表。 

方法1和方法3通常會結合起來一起用,動態鏈結庫就是用了這種技術。 

方發2是一種通用的方法,實際上聯結器就是這樣生成可執行檔案的。 

=== arm aif === 

arm公司的編譯器有一項特殊的功能,即可以產生一種可自我重定位的可執行檔案,即aif格式。 

在aif檔案中,包含了乙個aif頭和一小段由編譯器產生的重定位**。執行aif格式的檔案,只需要告訴它起始位址,這段重定位**就會負責修改餘下的一些必要的資訊達到重定位目的。目前還沒有充分的公開的文件解釋aif內部的詳細工作機制。 

在我過去的一些專案中,aif工作的很好,但是執行時外部無法獲取aif檔案的更多資訊,例如你無法去呼叫aif映像中的某乙個函式,因為你不知道它的位址。另外,aif的執行映像中,.data必須緊跟在.code之後,對於想重定位到flash中執行的嵌入式系統就行不通了。 

=== elf === 

elf檔案是最常見的目標檔案格式,它可能有很多副檔名,例如.o,.so,或者最終的可執行檔案也是elf。 

elf有幾種: 

* 可重定位 

* 可動態鏈結 

* 可執行 

* 可執行+可重定位 

可執行的elf如果沒有可重定位資訊,那就只能靠虛擬記憶體系統來支援它執行。但是對於許多嵌入式系統,有可能連mmu都不具備,因此我們只關心可重定位的elf。(可動態鏈結elf實際上也是可重定位的一種,附加很多額外資訊) 

有關elf的詳細資訊,請參閱: 

=== elf loader === 

我花了不少時間尋找小型的elf loader實現,但是真正適合嵌入式系統的卻不多。 

+contiki os: 

在contiki os裡面,有乙個很有趣的elf loader實現,嗯,其實contiki os有很多有意思的東西

+uclinux: 

uclinux也是乙個很有意思的例子。由於uclinux沒有啟用虛擬記憶體系統,因此它在載入可執行檔案的時候,就要進行重定位。為了加速重定位和減小elf檔案的體積,uclinux提供了特殊的工具鏈,在產生elf之前進行部分的「預重定位」,最後elf中只需要攜帶很小體積的重定位資訊。 

+其它rtos: 

其它嵌入式os,如vxworks也實現了elf loader, ecos的elf loader看起來尚未完整。 

+linux kernel: 

哦,差點忘了乙個最重要的,linux kernel。 

linux kernel的模組是可以通過insmod動態地加入核心。雖然linux的使用者空間程式執行在虛擬記憶體中,整個核心的空間確只有乙個。一些奉行micro kernel的人批評linux的這種方式,但是乙個單一空間的核心執行效率卻是最高的。 

在2.6核心中,模組重定位工作不再由insmod來完成,而是由核心來做所有的重定位工作。實現**在:kernel/module.c中。 

剝去那些處理特殊section的**,linux核心模組載入部分的**其實是非常簡單明瞭的,而且linux支援數十種架構意味著你幾乎不要擔心架構移植的問題。 

=== 結論 === 

在嵌入式系統中實現動態模組載入的技術是成熟的,可靠的,可以借鑑的開發原始碼的實現例子也有不少。乙個參考資料: 我最近在乙個嵌入式rtos上實現的elf loader,執行在arm7 cpu上,從nand flash中載入乙個400k左右的elf,耗時大約0.5秒。 

=== the end === 

Lua動態載入模組

function reload modulename package.loaded modulename nil require modulename end 使用以上 即可重新載入乙個檔案。這樣修改完lua 後,可以不用重啟程式立刻生效。模組a a function a.test1 print 1...

nginx載入動態模組

不使用動態模組時 使用動態模組 不是所有的nginx模組都可以以動態模組方式加入nginx 載入方式參見nginx模組載入 需要注意的是 with http image filter module dynamic 如果採用動態模組的載入方式,那麼需要將 usr local src nginx 1.8...

Python動態載入模組

需求 實現乙個簡單的pyton程式,接收兩個引數 plugin name,data,根據不同的plugin name定位到不同的邏輯處理模組並進行輸出。實現方案 使用python的庫函式 load source,將外掛程式模組載入到乙個dict中key為模組名稱,value為類的例項,核心 def ...