SylixOS下Shell命令行執行模塊內函數

1. 適用範圍

  本文檔介紹了在Shell命令行執行內核模塊內函數實現原理。
  在VxWorks中,系統自帶有在Shell命令行直接執行驅動、應用內函數的功能,此功能完善了驅動開發工程師、應用開發工程師的開發、調試的手段。爲了讓這類工程師能快速適應SylixOS,據此開發了類似的功能模塊,目前第一版支持在Shell命令行執行內核模塊內的函數。

2. SylixOS內核模塊動態加載原理

2.1 SylixOS中的ELF文件

  SylixOS中的ELF文件主要有三種:
- obj文件: 一個源文件編譯完成後,編譯器將源文件內所有函數的指令塊拼接形成TEXT節,將數據塊拼接行成DATA節,同樣還會根據需要生成其它節(如符號表、重定位表)。這些節拼接在一起形成obj文件。
- 內核模塊文件: 內核模塊文件是多個obj文件組合形成的一個大文件,它將多個obj文件生成的TEXT節、DATA節、符號表、重定位表各自拼接爲更大的TEXT節、DATA節、符號表和重定位表。
- 位置無關ELF文件: SylixOS的應用程序和動態庫都使用位置無關ELF文件格式,它支持代碼段共享和寫時拷貝。

2.2 內核模塊的ELF文件格式

  內核模塊包括ELF頭、程序頭表(也可以沒有)和段節頭表。
2.2.1 ELF頭
  ELF頭描述了整個文件的基本屬性,比如ELF文件版本,目標機器型號,程序入口地址等,如下所示。

typedef struct elf32_hdr {    
    unsigned char        e_ident[EI_NIDENT];         /*標示該文件爲可執行的object文件     */
    Elf32_Half          e_type;                     /*標示object文件的類型             */
    Elf32_Half          e_machine;                  /*指出該object需要的體系結構        */
    Elf32_Word          e_version;                  /*確定object的文件版本             */
    Elf32_Addr          e_entry;                    /*是系統第一個傳輸控制的虛擬地址      */
    Elf32_Off           e_phoff;                    /*保持了程序頭表在文件中的偏移量      */
    Elf32_Off           e_shoff;                    /*保持着段節頭表在文件中的偏移量      */
    Elf32_Word          e_flags;                    /*保存着相關文件的處理器標誌         */
    Elf32_Half          e_ehsize;                   /*保存着ELF頭大小                  */
    Elf32_Half          e_phentsize;                /*一個程序頭的大小                  */
    Elf32_Half          e_phnum;                    /*程序頭表的個數                    */
    Elf32_Half          e_shentsize;                /*一個段節頭的大小                  */
    Elf32_Half          e_shnum;                    /*段節頭表中的段節頭數目             */
    Elf32_Half          e_shstrndx;                 /*段節名字符表相關入口的段節頭表索引   */
} Elf32_Ehdr;

2.2.2 ELF文件頭校驗
    SylixOS中使用insmod命令或modulereg命令加載內核模塊時,首先會讀取ELF文件的文件頭,並校驗ELF文件頭的有效性。
    在SylixOS中,ELF文件頭的e_ident數值應固定爲0x7f、‘E’、‘L’、‘F’四個字節;e_machine會根據arm、PowerPC、x86等不同架構爲不同數值,以此來區分該ELF文件是否是架構適配的文件。
2.2.3 獲取內核模塊版本
    SylixOS的ELF文件中都包含有“__sylixos_version”符號,該符號數值爲一個字符串,字符串的內容爲ELF內核模塊版本。SylixOS將該版本與內核版本進行比較,確定ELF文件與SylixOS版本是否兼容。

2.3 ELF文件加載

    SylixOS會根據ELF文件頭中的e_type數值對ELF文件進行不同種類的加載操作。ET_REL爲可重定位文件,內核模塊屬於可重定位文件;ET_EXEC和ET_DYN爲可執行文件,應用程序、動態庫屬於可執行文件。
    根據e_shentsize和s_shnum可以計算出整個ELF段節頭表的大小,根據e_shoff可以獲取ELF段節頭表的偏移地址,由此將整個段節頭表讀入內存。
    根據每個段節頭表中的每個段節頭的信息可以獲取每個段的類型、大小、存儲地址,由此將每個段的內容讀入內存。
2.3.1 段節頭
段節頭表中的每一個段節頭都有如下所示的結構。

typedef struct {
    Elf32_Word      sh_name;                        /* 段名稱,值爲段頭字符表的索引   */
    Elf32_Word      sh_type;                        /* 段類型                     */
    Elf32_Word      sh_flags;                       /* 段屬性                     */
    Elf32_Addr      sh_addr;                        /* 段在內存中的位置             */
    Elf32_Off       sh_offset;                      /* 段字節偏移量(從文件開始計數)   */
    Elf32_Word      sh_size;                        /* 段的字節大小                 */
    Elf32_Word      sh_link;                        /* 段報頭表的索引連接            */
    Elf32_Word      sh_info;                        /* 保存額外信息                 */
    Elf32_Word      sh_addralign;                   /* 段地址對齊的約束              */
    Elf32_Word      sh_entsize;                     /* 段中每個入口的字節大小         */
} Elf32_Shdr;

2.3.2 加載各個段
   首先爲符號表、重定位段、字符串表申請內存,然後將這些段讀入內存。
   符號表的sh_type爲SHT_SYMTAB,重定位節的sh_type爲SHT_RELA(帶加數的重定位項)和SHT_REL(不帶加數的重定位項),字符串表的sh_type爲SHT_STRTAB。
   其次爲數據段、代碼段申請內存,並將數據段和代碼段讀入內存。
2.3.3 重定位操作
   在處理目標文件時,鏈接器會遍歷所有的重定位條目,碰到外部引用時,鏈接器會找到外部引用函數的確切地址,並且把它寫回到指令操作數所佔用的地址單元。像這樣的操作,稱之爲重定位操作。對於每個需要重定位的代碼段或數據段,都會有一個相應的重定位表。
   SylixOS加載內核模塊時,會對重定位節進行重定位處理,本文對重定位暫不做具體分析。

2.4 導出符號表

2.4.1 符號表

typedef struct elf32_sym {
   Elf32_Word          st_name;                        /* 符號名                        */
   Elf32_Addr          st_value;                       /* 符號相應的值                   */
   Elf32_Word          st_size;                        /* 符號佔用的字節大小              */    
   unsigned char       st_info;                        /* 符號類型和綁定信息              */    
   unsigned char       st_other;                       /* 目前爲0                       */
   Elf32_Half          st_shndx;                       /* 符號所在的段                   */
} Elf32_Sym;
  1. 符號類型

  • STT_NOTYPE表示是未知類型符號;

  • STT_OBJECT表示該符號是數據對象,比如變量、數組等;

  • STT_FUNC表示該符號是個函數或其他可執行代碼;

  • STT_SECTION表示該符號表示一個段,這種符號必須是STB_LOCAL的;

  • STT_FILE表示該符號表示文件名,一般都是該目標文件所對應的源文件名。

符號作用域

  • STB_LOCAL表示是局部符號,對於目標文件的外部不可見;

  • STB_GLOBAL表示是全局符號,外部可見;

  • STB_WEAK表示是弱引用。

符號所在段

  • SHN_UNDEF表示該符號未定義,該符號在本目標文件被引用到,但是定義在其他目標文件中;

  • SHN_COMMON表示該符號是一個“COMMON塊”類型的符號,一般來說,未初始化的全局符號定義就是這種類型。

2.4.2 目前的導出符號表操作
  SylixOS中insmod命令或modulereg命令進行內核模塊加載時,只會將全局符號進行導出。在模塊控制塊LW_LD_EXEC_MODULE中定義了符號緩衝,用於存儲模塊內的符號節點。當有符號需要導出時,從模塊的符號緩衝內開闢內存節點,存儲符號的字符串,符號的入口地址等信息。
  並且將該符號佔用的所有內存節點按照哈希表排序,以方便快速查找與定位。

3. Shell命令行執行模塊內函數實現

3.1 構建模塊內部函數映射表

  由於目前系統只會對全局符號進行導出,導致當需執行模塊內部非全局符號時就無法獲取其入口地址,所以需要對模塊內部函數構建函數映射表。
  構建模塊內部函數映射表的流程與insmod的流程基本類似,需要重新對ELF文件進行解析,但是有兩點區別:
  第一點,無需對函數進行重定位。因爲內核模塊此時已經加載,在獲取函數的相對偏移地址後,只需獲取內核模塊加載在內存後的首地址,即可計算出內核模塊內函數在內存中的實際地址。
  第二點,在導出符號時,應該修改過濾條件,使內部函數也可以加入到內核的符號緩衝中。

3.2 功能模塊的使用

3.2.1 解析模塊ELF文件
  相關功能被製作成SymbolShell.ko,使用功能前首先加載SymbolShell.ko文件,同時加載測試用的test_module.ko,如下圖所示。

這裏寫圖片描述


  SymbolShell.ko會註冊call命令,此命令提供瞭如下圖所示的幾個功能。

這裏寫圖片描述


3.2.2 打印模塊內函數及其相對偏移
  該模塊以會話的概念管理對某個模塊內函數的調用,想調用某模塊內的函數時,首先需要進入該模塊的會話。
  以test_module.ko爲例,如下圖所示使用call –m test_module.ko命令進入test_module.ko的會話。然後使用call –l命令即可查看test_module.ko內的符號。

這裏寫圖片描述


3.2.3 執行模塊內函數
  使用call test_func即可調用test_module.ko內的test_func函數,如下圖所示,最終使用call –e命令可退出當前模塊的會話。

這裏寫圖片描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章