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句)。