全局變量重定位和KernelRelocate函數

 

1 爲什麼要對全局變量重定位

首先研究爲什麼要對Bootloader的全局變量執行重新定位的問題。在Bootloader的源代碼中不可避免的要定義一些全局變量,這些全局變量被放置在編譯得到的可執行二進制文件的數據段存儲區。Bootloader鏡像文件或在ROM中以XIP方式運行或被複制到一塊在.bib工程文件中定義爲RAMIMAGE的區域內,儘管這塊區域位於系統RAM存儲器中,但卻是被當作ROM來使用的。因此無論以何種方式運行,Bootloader鏡像所在的存儲區域都是隻讀的,所以有必要把鏡像中的數據段讀到程序內存中來以保證其中的全局變量可寫,這就是對Bootloader的全局變量進行重定位的原因。

此外,後面我們還會瞭解到Windows CE系統的全局變量也同樣需要重定位,實現全局變量重定位功能的函數有KernelRelocate,該函數位於%_winceroot%/private/winceos/coreos/nk/ldr/ldrcmn.c源文件。

2 認識pTOC指針

 

在繼續介紹全局變量重定位之前,有必要介紹一下pTOC指針。先看這個指針的定義,如下。

代碼1.摘錄自%_winceroot%/private/winceos/coreos/nk/ldr/ldrcmn.c

ROMHDR *const volatile pTOC=(ROMHR *) -1;      //Gets replaced by RomLoader with real address

在定義時,pTOC被定義成一個全局常量形式,讀者可以想想爲什麼不定義成全局變量的形式。此外,pTOC指向的是一個無效的地址,因爲給它賦值的地址值是-1。這讓它顯得有點神祕莫測,其實在ROMImage階段這個-1會被ROMImage.exe[1]改掉,這個後面也會說到。

接着要說的是pTOC指針指向的地址空間所保存的數據的類型爲ROMHDR,那麼ROMHDR是何許類型呢?下面我們找到ROMHR類型的定義原型,如下。

代碼2.摘錄自%_winceroot%/public/common/oak/inc/romldr.h

1       typedef struct ROMHDR {

2                ULONG     dllfirst;                        // first DLL address

3                ULONG     dlllast;                         // last DLL address

4                ULONG     physfirst;                    // first physical address

5                ULONG     physlast;                    // highest physical address

6                ULONG     nummods;                 // number of TOCentry's

7                ULONG     ulRAMStart;              // start of RAM

8                ULONG     ulRAMFree;               // start of RAM free space

9                ULONG     ulRAMEnd;                // end of RAM

10              ULONG     ulCopyEntries;          // number of copy section entries

11              ULONG     ulCopyOffset;            // offset to copy section

12              ULONG     ulProfileLen;              // length of PROFentries RAM

13              ULONG     ulProfileOffset;         // offset to PROFentries

14              ULONG     numfiles;                    // number of FILES

15              ULONG     ulKernelFlags;           // optional kernel flags from ROMFLAGS .bib config option

16              ULONG     ulFSRamPercent;     // Percentage of RAM used for filesystem

17                                                                      // from FSRAMPERCENT .bib config option

18                                                                      // byte 0 = #4K chunks/Mbyte of RAM for filesystem 0-2Mbytes 0-255

19                                                                      // byte 1 = #4K chunks/Mbyte of RAM for filesystem 2-4Mbytes 0-255

20                                                                      // byte 2 = #4K chunks/Mbyte of RAM for filesystem 4-6Mbytes 0-255

21                                                                      // byte 3 = #4K chunks/Mbyte of RAM for filesystem > 6Mbytes 0-255

 

22              ULONG     ulDrivglobStart;        // device driver global starting address

23              ULONG     ulDrivglobLen;          // device driver global length

24              USHORT   usCPUType;               // CPU (machine) Type

25              USHORT   usMiscFlags;             // Miscellaneous flags

26              PVOID      pExtensions;             // pointer to ROM Header extensions

27              ULONG     ulTrackingStart;        // tracking memory starting address

28              ULONG     ulTrackingLen;          // tracking memory ending address

29     } ROMHDR;

在ROMImage階段,ROMImage.exe會直接填充84字節數據構成ROMHDR

在ROMImage階段,ROMImage.exe會直接填充84字節數據構成ROMHDR數據結構,並修改pTOC指針的地址,將之指向填充後得到的ROMHDR數據結構。

暫且先不完全介紹ROMHDR各數據成員的含義,僅僅提一下與全局變量重定位操作有關的兩個成員,它們是ulCopyEntries和ulCopyOffset。其含義分別是CopyEntry的數量和第1個CopyEntry的地址。

3 如何實現全局變量的重定位

全局變量重定位由KernelRelocate函數實現,在分析KernelRelocate函數之前我們先認識一下CopyEntry。簡單的說CopyEntry就是一種表示拷貝入口信息的數據結構,我們可以從其定義加深對它的理解,其定義以及各數據成員函數如下。

代碼3.摘錄自%_winceroot%/public/common/oak/inc/romldr.h

1       typedef struct COPYentry {

2                ULONG     ulSource;                    // copy source address

3                ULONG     ulDest;                       // copy destination address

4                ULONG     ulCopyLen;                 // copy length

5                ULONG     ulDestLen;                 // copy destination length

6                                                                        // (zero fill to end if > ulCopyLen)

7       } COPYentry;

 

表.CopyEntry結構體數據成員含義

成員名

成員含義

ulSource

全局變量在ROM中的起始地址

ulDest

全局變量複製到RAM中的目的地址

ulCopyLen

全局變量真實的長度

ulDestLen

全局變量期望的長度

 

這裏對於ulCopyLen和ulDestLen所表示的全局變量的真實長度和期望長度做如下補充說明:ulDestLen一定不小於ulCopyLen,如果ulDestLen大於ulCopyLen則說明該region的全局變量除了有非零數據之外還存在若干字節的清零數據空間。

理解了CopyEntry之後,就很容易理解KernelRelocate函數拷貝全局變量的過程了,下面是KernelRelocate函數的源代碼。

代碼4.摘錄自%_winceroot%/private/winceos/coreos/nk/ldr/ldrcmn.c

1       void  KernelRelocate (ROMHDR *const pTOC){

2                ULONG              loop;

3                COPYentry        *cptr;

 

4                // copy globals

5                for (loop = 0; loop < pTOC->ulCopyEntries; loop++) {

6                          cptr = (COPYentry *)(pTOC->ulCopyOffset + loop*sizeof(COPYentry));

7                          if (cptr->ulCopyLen) {

8                                   memcpy((LPVOID)cptr->ulDest,(LPVOID)cptr->ulSource,cptr->ulCopyLen);

9                          }

10                       if (cptr->ulCopyLen != cptr->ulDestLen) {

11                                 memset((LPVOID)(cptr->ulDest+cptr->ulCopyLen),0,cptr->ulDestLen-cptr->ulCopyLen);

12                       }

13              }

14     }

第5~13句使用for語句構成循環結構,循環次數等於CopytEntry的數量,也就是pTOC指針所指向的數據結構中的ulCopyEntries成員的取值,每次循環都拷貝一次。拷貝之前先得到拷貝入口信息(第6句)。拷貝分兩步執行,首先,如果有數據拷貝(第7句),則拷貝這些數據(第8句);然後,如果期望的大小比實際大小大(第10句),則用0填充其餘部分(第11句)。

發佈了16 篇原創文章 · 獲贊 0 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章