全局变量重定位和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句)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值