Windows CE6.0操作系统映像文件格式的研究
把前段时间研究的关于BIN文件的一些认识,整理了一下,写了一篇文章,投了出去,将于年底发表。最近,也没有干什么事,一直闲着,偶尔看看C#,也没有什么好些的,预示将我写文章用到的素材,以及写文章用到的一些资料整理了一下,写了出来,文章已经发表,希望对同学们有个借鉴,请勿抄袭!
由Windows CE的开发工具Platform Build编译生成的Windows CE操作系统及Bootloader的镜像文件主要由两种格式类型——以.bin为文件名后缀的记录型镜像文件和以.nb0为后缀的原始型(raw)镜像文件,前者以记录(Record)为单位组织镜像的数据,后者则是镜像在嵌入式系统中运行时的二进制数据快照。
DownloaderImage函数执行的第一步是调用GetImageType函数获知镜像文件的格式类型。GetImageType也是BLCOMMON库函数,与DownloadImage函数在同一源文件中实现。GetImageType函数查询镜像文件格式类型的原理十分简单:每一个Windows CE镜像文件在文件数据的起始位置都有一个7字节的特征码(magic number),它与镜像文件的格式一一对应如下:
“N000FF\X0A”——BL_IMAGE_TYPE_MANIFEST
“X000FF\X0A”——BL_IMAGE_TYPE_MULTIXIP
“B000FF\X0A”——BL_IMAGE_TYPE_BIN
“S000FF\X0A”——BL_IMAGE_TYPE_SIGNED_BIN
“R000FF\X0A”——BL_IMAGE_TYPE_SIGNED_NB0
无特征码——BL_IMAGE_TYPE_UNKNOWN
typedef enum _BL_IMAGE_TYPE
{
BL_IMAGE_TYPE_MANIFEST = 0,
BL_IMAGE_TYPE_BIN,
BL_IMAGE_TYPE_NB0,
BL_IMAGE_TYPE_SIGNED_BIN,
BL_IMAGE_TYPE_SIGNED_NB0,
BL_IMAGE_TYPE_MULTIXIP,
BL_IMAGE_TYPE_UNKNOWN,
BL_IMAGE_TYPE_NOT_FOUND,
} BL_IMAGE_TYPE;
以上的前4种类型都是记录型镜像格式,只有BL_IMAGE_TYPE_SIGNED_NB0代表原始型镜像文件格式,此外DOwnloaderImage函数把无特征码的BL_IMAGE_TYPE_UNKNOWN类型也当作原始型镜像来处理。BL_IMAGE_TYPE_MULTIXIP类型在Windows CE 5.0和以前的版本中用于表示多区段的镜像文件,在Windows CE 6.0版本中使用BL_IMAGE_TYPE_MANIFEST类型处理多区段的操作系统镜像,不再支持BL_IMAGE_TYPE_MULTIXIP类型,仅为了在源代码中保持向下兼容而保留该类型的定义。
BL_IMAGE_TYPE_MANIFEST类型的镜像文件的数据是多区段的记录型镜像文件的区段(Region)信息。多区段的镜像,简单地说,就是操作系统或者BootLoader的二进制运行时二进制数据分散在不连续的物理存储区间。Manifest的字面意思是“载货单”,该类型的镜像文件并不是真正的Windows CE操作系统的或者BootLoader的二进制运行时数据,只是供下载多区段型镜像所使用的头信息。由于镜像数据的性质特殊,“N000FF\X0A”——BL_IMAGE_TYPE_MANIFEST类型的镜像文件——也称作Manifest数据——与一般的镜像文件数据的存放位置不同,它使用专门的DowloadManifest型全局变量g_DownloadManifest来存放。
DowloadManifest结构体类型在头文件%_WINCEROOT%\PUBLIC\COMMON\OAK\INC\blcommon.h中定义如下:
// Download manifest.
typedef struct _MultiBINInfo_
{
DWORD dwNumRegions; // Number of files to be downloaded,要下载的bin文件数目
RegionInfo Region[BL_MAX_BIN_REGIONS];
} MultiBINInfo, *PMultiBINInfo;
typedef MultiBINInfo DownloadManifest;
typedef MultiBINInfo *PDownloadManifest;
多区段意味着多文件,即一个操作系统或者BootLoader的二进制运行时数据存放在多个镜像文件中,这多个镜像文件的数据对应于可以连续也可以不连续的多个物理存储区域。DowloadManifest结构体的成员dwNumRegion就是多区段镜像的区段个数,也即是后续多个镜像文件的文件个数。Region数组成员负责具体存放各区段的信息,BL_MAX_BIN_REGIONS在头文件%_WINCEROOT%\PUBLIC\COMMON\OAK\INC\blcommon.h中定义为25,意味着多区段的镜像区段数最大不超过25。Region数组成员的元素类型RegionInfo又是一个在头文件blcommon.h中定义的结构体:
// Download file info (start address, length, filename, etc.).下载文件信息
typedef struct _RegionInfo_
{
DWORD dwRegionStart;
DWORD dwRegionLength;
CHAR szFileName[MAX_PATH];
} RegionInfo, *PRegionInfo;
RegionInfo结构体的3个成员dwRegionStart、dwRegionLength、szFileName含义分别是一个镜像区段在物理存储中的起始地址、以字节为单位的区段长度和与镜像区段相对应的镜像文件名。
对于BL_IMAGE_TYPE_MANIFEST类型的镜像文件,文件数据中紧随7字节的格式类型特征码之后下载端口读出的是4字节的校验数据,继之是4字节的区段个数。区段个数数据理所当然存放在g_DownloaderManifest全局变量的dwNumRegions成员中,随后依次读出的是镜像的g_DownloaderManifest.dwNumRegions个区段的属性数据——起始地址、区段长度、区段数据的镜像文件名,按顺序存放在g_DownloaderManifest.dwNumRegions数组的各个元素中。DownloaderImage函数调用CheckImageManifest函数读取4字节校验数据和随后的全部Manifest数据,然后CheckImageManifest函数调用VerifyChecksum函数对Manifest数据执行校验,不过校验范围仅限于存放在g_DownloaderManifest.Regions数组中的区段头信息的数据,不包括4字节的g_DownloaderManifest.dwNumRegions的数据。
BL_IMAGE_TYPE_MANIFEST类型在所有的镜像文件格式类型中比较另类,它只是供下载其他类型的镜像文件而是用的前导信息,本身并不包含有效的Windows CE操作系统或者BootLoader的二进制运行时数据。如果操作系统的镜像只有一个区段,或者使用自身包含有存储位置等头信息的记录型镜像文件格式,也可以不使用Manifest前导数据。BL_IMAGE_TYPE_MANIFEST类型的镜像文件下载完成以后,DownloaderImage函数并不立即执行返回,还要依据全局变量g_DownloaderManifest中的Manifest数据所提供的信息继续下载装载有真实的操作系统二进制数据的镜像文件。
BL_IMAGE_TYPE_BIN类型是最普通最常用的记录型镜像文件格式,该类型的镜像文件均以.bin为文件名后缀,DownloaderImage函数调用DownloadBin函数处理这种类型的镜像文件,DownloaderImage也是一个BLCOMMON库函数。在BL_IMAGE_TYPE_BIN类型的镜像文件中,紧随着最初的7字节特征码之后是该镜像文件在嵌入式系统中的目的物理存储起始地址和以字节为单位的长度,各占4字节,这两个数据的含义分别对应于RegionInfo结构体的dwRegionStart、dwRegionLength两成员。接下来的便是具体存放操作系统为二进制数据的拥有相同结构的多条记录(Record),每条记录由4字节的内存起始地址dwRecAddr、4字节的记录长度dwRecLen、4字节的校验码dwRecChk和dwRecLen个字节的记录数据组成。DownloaderImage函数依次从下载端口读出各条记录并且对它们执行校验,校验也是以记录为单位,待校验无误以后将去除头信息的记录数据存放到dwRecAddr指定地址的物理存储位置。如果记录的目的存储位置是FLlash存储设备,则通常要先缓存在RAM内存中,待整个镜像文件全部下载完再一起写入FLlash。
与BL_IMAGE_TYPE_BIN类型相似的最普通常用的原始型(Raw)镜像文件以.nb0为文件名后缀,它们使用DownloaderNB0函数进行处理。由于原始型镜像文件的数据只是物理存储中的Windows CE操作系统或者BootLoader的二进制运行时数据的一个最直接的快照,所以它是完全无格式的,甚至都没有7字节的特征码,在GetImageType函数中将这种镜像文件格式类型定义为BL_IMAGE_TYPE_UNKNOWN。通常情况下,DownloaderImage函数将无特征码的BL_IMAGE_TYPE_UNKNOWN类型都当作原始型镜像文件处理,但是在安全性较高的情况下(定义了宏SECURE_BOOTLOADER)无特征码的镜像文件是不应该被Bootloader接受的。原始型镜像文件的处理方法最简单,BootLoader只需从下载端口中读出全部的镜像数据并且存放到相应的存储位置,而且无需处理校验。原始型镜像文件的数据在物理存储中的起始地址和长度都取决于Manifest数据,因而.nb0型的镜像文件不能单独下载,BootLoader必须先下载作为它的前导属性信息的BL_IMAGE_TYPE_MANIFEST类型的镜像文件。
.bin文件是一个描述性质的映像文件,它由文件头(head)、镜像数据目的起始地址(ImageStart)、镜像数据长度(ImageLength)和多条相对独立的记录(record)构成。文件头(head)由7个字节组成,内容是:42 30 30 30 46 46 0A,即“B000FF\x0A”,这是判断镜像文件是.bin类型的依据。镜像数据目的起始地址(ImageStart)由4个字节组成,它定义了镜像文件解析后装载在内存中的起始地址。镜像数据长度(ImageLength)也由4个字节组成,它表示.bin镜像文件解析后在内存中占用的总的存储空间大小。每条记录(record)由4字节的起始存储地址(RecordStart)、4字节的数据长度(RecordLength)、4字节的校验码 (RecordCheckSum)和RecordLength个字节的记录数据(RecordData)组成。
由以上.bin的文件格式可知,.bin文件不是内存程序空间的一个简单的拷贝。所以它不能直接用串口烧写进内存或FLASH空间来直接运行,必须通过 PB下载,通过EBOOT按照.bin文件的格式解析出来具体的内容并重新装载后才能运行。
而.nb0文件就不一样,它是内存运行程序映像的一个硬拷贝,其数据内容和程序运行时在内存中的数据是一样的。鉴于此,.nb0可以通过串口下载到指定的位置直接运行。很明显,.nb0是一个不存在格式的映像文件。