可以通过阅读代码来帮助分析Image文件的格式。
首先,在art\runtime\Image.h文件中,有相关的定义:
byte magic_[4]; byte version_[4]; // Required base address for mapping the image. uint32_t image_begin_; // Image size, not page aligned. uint32_t image_size_; // Image bitmap offset in the file. uint32_t image_bitmap_offset_; // Size of the image bitmap. uint32_t image_bitmap_size_; // Checksum of the oat file we link to for load time sanity check. uint32_t oat_checksum_; // Start address for oat file. Will be before oat_data_begin_ for .so files. uint32_t oat_file_begin_; // Required oat address expected by image Method::GetCode() pointers. uint32_t oat_data_begin_; // End of oat data address range for this image file. uint32_t oat_data_end_; // End of oat file address range. will be after oat_data_end_ for // .so files. Used for positioning a following alloc spaces. uint32_t oat_file_end_; // Absolute address of an Object[] of objects needed to reinitialize from an image. uint32_t image_roots_;
下面一一说明:
1) 最先的4个字节是Image文件的magic code,其值在对应的art\runtime\Image.cc文件中有定义:
const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
2) 接下来的4个字节是Image文件的版本号,同样在Image.cc文件中有指定:
const byte ImageHeader::kImageVersion[] = { '0', '0', '5', '\0' };3) 再下来的4个字节指定了Image文件映射到内存中的起始地址;
4) 再下来的4个字节说明了Image文件的大小,这是没有根据页4K对齐后的大小;
5) 再下来的8个字节用来指定Image的bitmap,先4个字节说明bitmap相对文件头所在内存地址的偏移,后4个地址说明bitmap的具体大小,至于bitmap的具体作用,以后再说;
6) 再下来的4个字节指定了所要链接的oat文件的checksum,方便以后在运行时检查;
7) 再下来的4个字节是Boot Oat文件在内存中的起始地址,该地址同时也在包含Oat的Elf文件中指定,从而保证在dlopen后加载到这个特定的地址;
8) 再下来的4个字节是Boot Oat文件中数据段的起始地址,该地址和Boot Oat文件中符号oatdata指定的地址一样;
9) 再下来的4个字节是Boot Oat文件中数据段的结束地址,该值等于Boot Oat文件中符号oatlastword+4;
10) 再下来的4个字节是Boot Oat文件在内存中的结束地址,该值可以定位用于动态内存分配的内存段;
11) 最后的4个字节很关键,它是一个地址,指向了一个ObjectArray,里面包含了非常重要的对象。具体来说,一共有7个:
enum ImageRoot { kResolutionMethod, kCalleeSaveMethod, kRefsOnlySaveMethod, kRefsAndArgsSaveMethod, kOatLocation, kDexCaches, kClassRoots, kImageRootsMax, };就先说这么多,具体每个的作用稍后再分析。
目前为止,罗列了那么多概念,估计大家头也晕了,下面举个例子吧。笔者有一台Google Nexus 7二代设备,打开开发者选项,切换到ART运行环境,并且root过后,可以把Image文件(system@[email protected])和Boot Oat文件(system@[email protected])拿出来。首先,用二进制编辑工具打开.art文件,一探究竟:
从中我们可以看出,magic code是“art\n”,版本号是“005\0”,Image文件映射到内存中的起始地址是0x60000000,Image的大小是0xA9BDB0,Bitmap的偏移是0xA9C000,大小是0x2A6F8。0xA9C000+0x2A6F8=0xAC66F8,这个值正好是.art文件的大小。所以.art文件实际就是由Image加上其Bitmap组成。好,我们继续,接下来的0xE566C279是要加载的oat文件的checksum,.oat文件的起始地址是0x60A9C000,结束地址是0x64618000,.oat文件的数据段起始地址是0x60A9D000,结束地址是0x646161A8。所有这些绝对地址在.oat文件中也有记录,让我们用readelf打开.oat文件看看,Program Headers如下:
可以看到,映射到的物理地址强制写到0x60A9C000。再看看oatdata和oatlastword:
可以看到,.oat文件中记录的这些值和.art文件中所记录的是一致的。最后的4个字节为0x60A9BCC0,让我们移步到这个地址,看看有些什么东西:
这块应该就是一个ObjectArray,里面记录了7个Object,可以很轻易的看到,其中记录了oat文件的位置(kOatLocation,第5个对象)。