Eboot启动过程中,在经历了汇编的Startup.s后,跳转到了C语言的main.c文件的main函数中,该函数中实质上就是执行的BootloaderMain函数,那这样写的目的是什么呢?微软这样的设计是为了方便用户在Startup之后和BootloaderMain之前加入一些高级语言的处理代码。
BootloaderMain
函数的第一个任务就是进行全局变量重定位,即
KernelRelocte
函数的功能。至于为何要进行全局变量重定位,这里进行简单的描述。
Bootloader
通常是在
ROM
存储器重以
XIP
的方式启动运行的,在运行之初自己复制自身的镜像至
RAM
内存中。由于镜像所在的存储区域都是只读而不可写的,所以有必要将镜像中的数据段读到程序内存中来以保证其中的全局变量是可写的。
//
// KernelRelocate: move global variables to RAM
//
static BOOL KernelRelocate (ROMHDR *const pTOC)
{
ULONG loop;
COPYentry *cptr;
if (pTOC == (ROMHDR *const) -1)
{
return (FALSE); // spin forever!
}
// This is where the data sections become valid... don't read globals until after this
for (loop = 0; loop < pTOC->ulCopyEntries; loop++)
{
cptr = (COPYentry *)(pTOC->ulCopyOffset + loop*sizeof(COPYentry));
if (cptr->ulCopyLen)
memcpy((LPVOID)cptr->ulDest,(LPVOID)cptr->ulSource,cptr->ulCopyLen);
if (cptr->ulCopyLen != cptr->ulDestLen)
memset((LPVOID)(cptr->ulDest+cptr->ulCopyLen),0,cptr->ulDestLen-cptr->ulCopyLen);
}
return (TRUE);
}
// KernelRelocate: move global variables to RAM
//
static BOOL KernelRelocate (ROMHDR *const pTOC)
{
ULONG loop;
COPYentry *cptr;
if (pTOC == (ROMHDR *const) -1)
{
return (FALSE); // spin forever!
}
// This is where the data sections become valid... don't read globals until after this
for (loop = 0; loop < pTOC->ulCopyEntries; loop++)
{
cptr = (COPYentry *)(pTOC->ulCopyOffset + loop*sizeof(COPYentry));
if (cptr->ulCopyLen)
memcpy((LPVOID)cptr->ulDest,(LPVOID)cptr->ulSource,cptr->ulCopyLen);
if (cptr->ulCopyLen != cptr->ulDestLen)
memset((LPVOID)(cptr->ulDest+cptr->ulCopyLen),0,cptr->ulDestLen-cptr->ulCopyLen);
}
return (TRUE);
}
上面是KernelRelocate函数的实现,里面有两个结构体COPYentry和ROMHDR,它们的定义在文件\WINCE600\PUBLIC\COMMON\OAK\INC\romldr.h中,定义如下:
typedef struct COPYentry {
ULONG ulSource; // copy source address
ULONG ulDest; // copy destination address
ULONG ulCopyLen; // copy length
ULONG ulDestLen; // copy destination length
// (zero fill to end if > ulCopyLen)
} COPYentry;
typedef struct ROMHDR {
ULONG dllfirst; // first DLL address
ULONG dlllast; // last DLL address
ULONG physfirst; // first physical address
ULONG physlast; // highest physical address
ULONG nummods; // number of TOCentry's
ULONG ulRAMStart; // start of RAM
ULONG ulRAMFree; // start of RAM free space
ULONG ulRAMEnd; // end of RAM
ULONG ulCopyEntries; // number of copy section entries
ULONG ulCopyOffset; // offset to copy section
ULONG ulProfileLen; // length of PROFentries RAM
ULONG ulProfileOffset; // offset to PROFentries
ULONG numfiles; // number of FILES
ULONG ulKernelFlags; // optional kernel flags from ROMFLAGS .bib config option
ULONG ulFSRamPercent; // Percentage of RAM used for filesystem
// from FSRAMPERCENT .bib config option
// byte 0 = #4K chunks/Mbyte of RAM for filesystem 0-2Mbytes 0-255
// byte 1 = #4K chunks/Mbyte of RAM for filesystem 2-4Mbytes 0-255
// byte 2 = #4K chunks/Mbyte of RAM for filesystem 4-6Mbytes 0-255
// byte 3 = #4K chunks/Mbyte of RAM for filesystem > 6Mbytes 0-255
ULONG ulDrivglobStart; // device driver global starting address
ULONG ulDrivglobLen; // device driver global length
USHORT usCPUType; // CPU (machine) Type
USHORT usMiscFlags; // Miscellaneous flags
PVOID pExtensions; // pointer to ROM Header extensions
ULONG ulTrackingStart; // tracking memory starting address
ULONG ulTrackingLen; // tracking memory ending address
} ROMHDR;
ULONG ulSource; // copy source address
ULONG ulDest; // copy destination address
ULONG ulCopyLen; // copy length
ULONG ulDestLen; // copy destination length
// (zero fill to end if > ulCopyLen)
} COPYentry;
typedef struct ROMHDR {
ULONG dllfirst; // first DLL address
ULONG dlllast; // last DLL address
ULONG physfirst; // first physical address
ULONG physlast; // highest physical address
ULONG nummods; // number of TOCentry's
ULONG ulRAMStart; // start of RAM
ULONG ulRAMFree; // start of RAM free space
ULONG ulRAMEnd; // end of RAM
ULONG ulCopyEntries; // number of copy section entries
ULONG ulCopyOffset; // offset to copy section
ULONG ulProfileLen; // length of PROFentries RAM
ULONG ulProfileOffset; // offset to PROFentries
ULONG numfiles; // number of FILES
ULONG ulKernelFlags; // optional kernel flags from ROMFLAGS .bib config option
ULONG ulFSRamPercent; // Percentage of RAM used for filesystem
// from FSRAMPERCENT .bib config option
// byte 0 = #4K chunks/Mbyte of RAM for filesystem 0-2Mbytes 0-255
// byte 1 = #4K chunks/Mbyte of RAM for filesystem 2-4Mbytes 0-255
// byte 2 = #4K chunks/Mbyte of RAM for filesystem 4-6Mbytes 0-255
// byte 3 = #4K chunks/Mbyte of RAM for filesystem > 6Mbytes 0-255
ULONG ulDrivglobStart; // device driver global starting address
ULONG ulDrivglobLen; // device driver global length
USHORT usCPUType; // CPU (machine) Type
USHORT usMiscFlags; // Miscellaneous flags
PVOID pExtensions; // pointer to ROM Header extensions
ULONG ulTrackingStart; // tracking memory starting address
ULONG ulTrackingLen; // tracking memory ending address
} ROMHDR;
COPYentry结构体比价简单,就是定义了源数据的地址和长度、目的地址、目的地址的空间大小,从注释中可以看出,如果目的地址空间大于源数据的长度,则多余的空间填0。
ROMHDR
结构体较为负责,这里主要说一下
KernelRelocate
函数中用到的两个变量,
ulCopyEntries
表示的是全局变量重定位时需要搬移的条目数,
ulCopyOffset
表示的是全局变量重定位时搬移条目的偏移地址。
现在看
KernelRelocate
函数的实现就很简答了,
COPYentry
是对搬移条目的描述,通过从
pTOC
获得到一条一条的
COPYentry
,然后根据描述中的信息进行数据的搬移。
这里值得提到的是
pTOC
参数的定义,在
KernelRelocate
函数所在源文件的前面有如下定义:
ROMHDR * volatile const pTOC = (ROMHDR *)-1; // Gets replaced by RomLoader with real address
在
KernelRelocate
函数的一开始就判断
pTOC
是否等于
-1
,那么后面的搬移操作还会进行吗?显然要进行全局变量重定位的,要不该函数就没有意义了。那样问题的关键就在于
pTOC
是什么时候赋值的,以便为后面的重定位提供相关信息?
答案
:
pTOC
是在产生
ROM image
的时候由
WinCE
的编译系统赋值的,并同时为该指针指向的
ROMHDR
类型的内存区域填充数据内容的。具体填充过程的代码请见这篇文章
http://jamsan.blog.hexun.com/58621673_d.html
。那又为何要这样做呢?《
Windows CE
工程实践完全解析》中有这样的介绍,完整的编译过程分为
4
个过程:
Compile
、
Sysgen
、
Release Copy
、
MakeRun-Time Image
。
Compile
阶段负责将源代码翻译成二进制目标代码,
Sysgen
阶段将
Compile
生成的目标代码链接成可执行文件(
.exe
和
.dll
),
Release Copy
阶段负责将
Sysgen
生成的可执行文件以及其他系统运行需要的数据文件复制到指定的目录下,
MakeRun-Time Image
阶段最后将指定目录中的所有文件打包成操作系统镜像。
pTOC
指向的
ROMHDR
的数据内容依赖于
Sysgen
生成的可执行文件的内容,所以只能放到
MakeRun-Time Image
的阶段了。
本文转自jazka 51CTO博客,原文链接:http://blog.51cto.com/jazka/595261,如需转载请自行联系原作者