天天看點

OpenHarmony(9) —— load ELF

  還沒具體看Harmony HDF 驅動架構。

  Linux宏核心,但采用了LKM機制,可以動态增删子產品,增加了自由度。而對于微核心的思想,其實沒有“驅動”和“程式”之分。

  但還是喜歡linux insmod/rmmod *.ko這樣的子產品動态加載,LKM機制确實很友善。

  看看Linux怎麼實作的吧。

  編譯linux驅動需要用到linux源碼樹的symbols, 如System.map檔案等

  先看下别人的分析

《Linux核心子產品LKM的動态加載技術分析》 https://blog.csdn.net/ganggexiongqi/article/details/6788270

4       動态加載連結的實作細節

了解 LKM 動态加載連結的基礎知識之後,現在我們進一步探索子產品是如何進入核心的,以及在核心内部是如何管理子產品的。建構 LKM時,可以使用典型的使用者工具管理子產品:标準 insmod(安裝 LKM),rmmod(删除LKM),modprobe(insmod 和 rmmod 的包裝器),depmod(用于建立子產品依賴項),以及modinfo(用于為子產品宏查找值)。   4.1    LKM 的生命周期

在使用者空間中,insmod(插入子產品)啟動子產品加載過程。insmod 指令定義需要加載的子產品,并調用 init_module使用者空間系統調用,開始加載過程。2.6 版本核心的 insmod 指令經過修改後變得非常簡單,可以在核心中執行更多工作。insmod 并不進行所有必要的符号解析,它隻是通過 init_module函數将子產品二進制檔案複制到核心,然後由核心完成剩餘的任務。   init_module 函數通過系統調用層,進入核心到達核心函數 sys_init_module(參見圖3)。這是加載子產品的主要函數,它利用許多其他函數完成困難的工作。類似地,rmmod 指令會使 delete_module 執行 system call 調用,而 delete_module 最終會進入核心,并調用 sys_delete_module 将子產品從核心删除。

圖 3 加載和解除安裝子產品時用到的主要指令和函數   在子產品的加載和解除安裝期間,子產品子系統維護了一組簡單的狀态變量,用于表示子產品的操作。加載子產品時,狀态為MODULE_STATE_COMING。如果子產品已經加載并且可用,狀态為 MODULE_STATE_LIVE。此外,解除安裝子產品時,狀态為MODULE_STATE_GOING。   4.2    子產品加載細節

核心子產品的加載是通過insmod這個使用者空間工具實作的。insmod包含在modutils包裡[6]。我們感興趣的東西是insmod.c檔案裡的init_module()函數。   static int init_module(const char *m_name, struct obj_file *f,         unsigned long m_size, const char *blob_name,         unsigned int noload, unsigned int flag_load_map) { (1)     struct module *module;         struct obj_section *sec;         void *image;         int ret = 0;         tgt_long m_addr;           ....   (2)     module->init = obj_symbol_final_value(f, obj_find_symbol(f, " module_init")); (3)     module->cleanup = obj_symbol_final_value(f, obj_find_symbol(f, " module_exit"));           ....           if (ret == 0 && !noload) {                 fflush(stdout);         (4)             ret = sys_init_module(m_name, (struct module *) image);                 if (ret) {                         error("init_module: %m");                         lprintf(       "Hint: insmod errors can be caused by incorrect module parameters, "       "including invalid IO or IRQ parameters.\n"       "You may find more information in syslog or the output from dmesg");                 }         }   在 (1) 裡,函數向一個結構體子產品(struct module)填充了加載子產品必須的資料。需要關注的部分是 init和 cleanup。這是兩個函數指針,分别指向被加載子產品的 module_init() 和module_exit函數。   (2)裡面的 obj_find_symbol()函數周遊符号清單查找名字為module_init的符号,然後提取這個結構體符号(struct symbol)并把它傳遞給 obj_symbol_final_value()。後者從這個結構體符号提取出module_init函數的位址。同理,在 (3) 裡這個工作對于module_exit ()又重複了一遍。   當結構體子產品填充完畢後,(4) 使用了 sys_init_module() 這個系統調用(syscall)通知核心加載相應子產品。子產品加載過程中程式調用了 sys_init_module(),其中有我們感興趣的部分。   現在,我們看看加載子產品時的内部函數(參見圖 4)。當調用核心函數 sys_init_module時,會開始一個許可檢查,查明調用者是否有權執行這個操作(通過 capable 函數完成)。然後,調用 load_module函數,這個函數負責将子產品加載到核心并執行必要的調試。load_module函數傳回一個指向核心中最新加載配置設定的子產品引用。然後将這個子產品加載到系統内所有子產品的雙向連結清單上,并且通過 notifier清單通知正在等待子產品狀态改變的線程。最後,調用子產品的 init()函數,更新子產品狀态,表明子產品已經加載并且可用。

圖 4  内部(簡化的)子產品加載過程   加載子產品的内部細節是 ELF 子產品解析和操作。load_module函數(位于./linux/kernel/module.c)首先配置設定一塊用于容納整個 ELF 子產品的臨時記憶體。然後,通過 copy_from_user 函數将 ELF 子產品從使用者空間讀入到臨時記憶體。作為一個 ELF 對象,這個檔案的結構非常獨特,易于解析和驗證。   下一步是對加載的 ELF 映像執行一組健康檢查(它是有效的 ELF 檔案嗎?它适合目前的架構嗎?等等)。完成健康檢查後,就會解析ELF 映像,然後會為每個區段頭建立一組友善變量,簡化随後的通路。因為 ELF 對象的偏移量是基于 0的(除非重新配置設定),是以這些友善變量将相對偏移量包含到臨時記憶體塊中。在建立友善變量的過程中還會驗證 ELF 區段頭,確定加載的是有效子產品。   任何可選的子產品參數都從使用者空間加載到另一個已配置設定的核心記憶體塊(第 4 步),并且更新子產品狀态,表明子產品已加載(MODULE_STATE_COMING)。如果需要 per-CPU 資料(這在檢查區段頭時确定),那麼就配置設定 per-CPU 塊。   在前面的步驟,子產品區段被加載到核心(臨時)記憶體,并且知道哪個區段應該保持,哪個可以删除。步驟 7為記憶體中的子產品配置設定最終的位置,并移動必要的區段(ELF 頭中的SHF_ALLOC,或在執行期間占用記憶體的區段)。然後執行另一個配置設定,大小是子產品必要區段所需的大小。疊代臨時 ELF塊中的每個區段,并将需要執行的區段複制到新的塊中。接下來要進行一些額外的維護。同時還進行符号解析,可以解析位于核心中的符号(被編譯成核心映象),或臨時的符号(從其他子產品導出)。   然後為每個剩餘的區段疊代新的子產品并執行重新定位。這個步驟與架構有關,是以依賴于為架構(./linux/arch//kernel/module.c)定義的 helper 函數。最後,重新整理指令緩存(因為使用了臨時 .text 區段),執行一些額外的維護(釋放臨時子產品記憶體,設定系統檔案),并将子產品最終傳回到 load_module。   4.3    子產品解除安裝細節

解除安裝子產品的過程和加載子產品基本一樣,除了必須進行幾個健康檢查外(確定安全删除子產品)。解除安裝子產品過程首先在使用者空間調用 rmmod(删除子產品)指令。在 rmmod 指令内部,對 delete_module執行系統調用,它最終會導緻在核心内部調用 sys_delete_module(檢視圖 3)。圖 5 示範了删除子產品的基本操作過程。

圖 5 内部(簡化的)子產品解除安裝過程   當調用核心函數sys_delete_module(将要删除的子產品的名稱作為參數傳入)之後,第一步便是確定調用方具有權限。接下來會檢查一個清單,檢視是否存在依賴于這個子產品的其他子產品。這裡有一個名為 modules_which_use_me的清單,它包含每個依賴子產品的一個元素。如果這個清單為空,就不存在任何子產品依賴項,是以這個子產品就是要删除的子產品(否則會傳回一個錯誤)。接下來還要測試子產品是否加載。使用者可以在目前安裝的子產品上調用 rmmod,是以這個檢查確定子產品已經加載。在幾個維護檢查之後,倒數第二個步驟是調用子產品的exit 函數(子產品内部自帶)。最後,調用 free_module 函數。   調用 free_module 函數之後,您将發現子產品将被安全删除。該子產品不存在依賴項,是以可以開始子產品的核心清理過程。首先,從安裝期間添加的各種清單中(系統檔案、子產品清單等)删除子產品。其次,調用一個與架構相關的清理例程(可以在 ./linux/arch//kernel/module.c中找到)。然後疊代具有依賴性的子產品,并将這個子產品從這些清單中删除。最後,從核心的角度而言,清理已經完成,為子產品配置設定的各種記憶體已被釋放,包括參數記憶體、per-CPU 記憶體和子產品的 ELF 記憶體(core 和 init)。   4.4    為子產品管理優化核心

在許多應用程式中,動态加載子產品非常重要,但加載之後,就沒有必要解除安裝子產品。這允許核心在啟動時是動态的(根據找到的裝置加載子產品),但并不是在整個操作過程中都是動态的。如果不需要在加載之後解除安裝子產品,那麼可以進行一些優化,減少子產品管理所需的代碼。您可以 “取消” 核心配置選項CONFIG_MODULE_UNLOAD,删除大量與解除安裝子產品相關的核心功能。   要獲得子產品管理的細節,源代碼本身就是最佳的文檔。關于在子產品管理中調用的主要函數,請檢視./linux/kernel/module.c(以及./linux/include/linux/module.h 中的頭檔案)。您還可以在./linux/arch//kernel/module.c 中找到幾個與架構相關的函數。最後,可以在./linux/kernel/kmod.c 中找到核心自動加載函數(可以根據需要從核心自動加載子產品)。這個功能可以通過 CONFIG_KMOD配置選項啟用。

但本質上将,.ko仍舊是ELF類型,也可以看作是“應用程式”。

在harmony下

harmony/kernel/liteos_a/kernel/extended/dynload/

los_exec_elf.c 

los_load_elf.c

這兩個檔案的入口函數為 los_exec_elf.c 下的

INT32 LOS_DoExecveFile(const CHAR *fileName, CHAR * const *argv, CHAR * const *envp)

調用地方

./kernel/liteos_a/syscall/fs_syscall.c:383:    return LOS_DoExecveFile(fileName, argv, envp);

int SysExecve(const char *fileName, char *const *argv, char *const *envp)

{

    return LOS_DoExecveFile(fileName, argv, envp);

}

./kernel/liteos_a/syscall/syscall_lookup.h:43:SYSCALL_HAND_DEF(__NR_execve, SysExecve, int, ARG_NUM_3)

使用execve系統調用時調用。

繼續閱讀