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函数的实现,里面有两个结构体COPYentryROMHDR,它们的定义在文件\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;
         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 p_w_picpath的时候由WinCE的编译系统赋值的,并同时为该指针指向的ROMHDR类型的内存区域填充数据内容的。具体填充过程的代码请见这篇文章http://jamsan.blog.hexun.com/58621673_d.html。那又为何要这样做呢?《Windows CE工程实践完全解析》中有这样的介绍,完整的编译过程分为4个过程:CompileSysgenRelease CopyMakeRun-Time ImageCompile阶段负责将源代码翻译成二进制目标代码,Sysgen阶段将Compile生成的目标代码链接成可执行文件(.exe.dll),Release Copy阶段负责将Sysgen生成的可执行文件以及其他系统运行需要的数据文件复制到指定的目录下,MakeRun-Time Image阶段最后将指定目录中的所有文件打包成操作系统镜像。pTOC指向的ROMHDR的数据内容依赖于Sysgen生成的可执行文件的内容,所以只能放到MakeRun-Time Image的阶段了。