您的位置:首页 > 移动开发 > Android开发

Dalvik虚拟机【2】——Dex文件格式

2016-07-16 17:25 666 查看
整个文件的布局

文件头

索引区
string_ids

type_ids

proto_ids

field_ids

method_ids

数据区
class_def
class_data_item
code_item

data

附录
测试用的Dex的java代码

参考资料

整个文件的布局

整个Dex文件可以分成三部分,文件头、索引区、数据区,如下图所示。



文件头

记录Dex文件的概览、包含文件大小、校验码以及其他字段的偏移和大小

索引区

记录字符串常量、类型、方法原形、域、方法的信息的索引

数据区

包含类的定义区class_defs,该部分记录类信息;以及数据区data,该部分包含实际信息(字符串、代码)等;link_data是链接数据区,主要和依赖库有关

使用 010 Editor 编辑器加上它官方的Dex模板可以方便、直观查看Dex文件内容.

文件头

偏移地址字段名字大小(byte)说明
0Magic[8]8魔数,用于识别Dex文件,内容为”dex\n035\0”
8checksum4文件校验码
CSignature[20]SHA-1签名
20file_size4Dex文件总长度
24header_size4文件头大小,一般固定为0x70
28endan_tag4大小端标志,标志dex文件格式为小端,一般固定为0x12345678
2Clink_size4链接段的大小
30link_off4链接段的基址
34map_off4map item数据的基址
38string_ids_size4字符串常量列表的个数
3Cstring_ids_off4字符串常量列表的基址
40type_ids_size4类型的个数
44type_ids_off4类型的基址
48proto_ids_size4方法原形的个数
4Cproto_ids_off4方法原形的基址
50field_ids_size4域的个数
54field_ids_off4域的基址
58method_ids_size4方法的个数
5Cmethod_ids_off4方法的基址
60class_defs_size4class_def的个数
64class_defs_off4class_def的基址
68data_size4数据段的大小
6Cdata_off4数据段的基址

索引区

string_ids

这区域存储了字符串常量的索引信息。字符串常量并不仅仅是代码中定义的字符串,还包括所有的类名、方法名、类型名等信息.

该区域存储的是一个基址,也就是字符串实际内容在Dex文件的地址(在data区),它被Dalvik虚拟机读取后会转换成如下数据结构

// /android4.0.4/dalvik/libdex/DexClass.h
/*
* Direct-mapped "string_id_item".
*/
struct DexStringId {
u4 stringDataOff;      /* file offset to string_data_item */
};


根据偏移地址stringDataOff读取到字符串常量,该常量在内存的数据结构string_data_item为

struct string_data_item {
uleb128 utf16_size; //字符串长度
ubyte data; //字符串
}


用010 Editor查看一个测试用的Dex文件的string_ids区域如下



type_ids

这区域存储了dex 文件里的所有数据类型的索引信息,包括class类型,数组类型(array types)、基本类型(primitive types) 。其数据结构为

/*
* Direct-mapped "type_id_item".
*/
struct DexTypeId {
u4  descriptorIdx;      /* index into stringIds list for type descriptor */
};


和string_ids不一样,descriptorIdx并不是偏移地址,而是string_ids里的index序号,如descriptorIdx=8,则代表string_ids[8]=”V”,则代表类型为Void。如下图,根据文件头找到typd_ids的基址为A8H,size为7,从A8H第一个4字节为0x0003,即是string_idsp[3],即是”LFoo”,代表class类型Foo



proto_ids

proto是方法原形,包含方法的输入参数和输出参数,每个大小为12字节。其数据结构为

/*
* Direct-mapped "proto_id_item".
*/
struct DexProtoId {
u4  shortyIdx;          /* index into stringIds for shorty descriptor */
u4  returnTypeIdx;      /* index into typeIds list for return type */
u4  parametersOff;      /* file offset to type_list for parameter types */
};


shorty_idx

跟 type_ids 一样,它的值是一个string_ids的index号,最终是一个简短的字符串描述 ,

returnTypeIdx

返回类型,值是type_ids的index序号

parametersOff

参数的基址

如下图



field_ids

这个区域里有dex文件引用的所有field(就是类的属性,包括静态的)索引,每个大小为12字节。其数据结构为

struct DexFieldId {
u2  classIdx;           /* index into typeIds list for defining class */
u2  typeIdx;            /* index into typeIds for field type */
u4  nameIdx;            /* index into stringIds for field name */
};


classIdx

该field所属的class,它的值是type_ids的index号

typeIdx

该field的类型,它的值也是type_ids的index号

nameIdx

该field的名称,它的值是string_ids的index号

如下图



method_ids

这个区域有dex文件所有方法的索引,格式和field_ids类似,每个大小为12字节。其数据结构为

/*
* Direct-mapped "method_id_item".
*/
struct DexMethodId {
u2  classIdx;           /* index into typeIds list for defining class */
u2  protoIdx;           /* index into protoIds for method prototype */
u4  nameIdx;            /* index into stringIds for method name */
};


classIdx

该方法所属的class,它的值是type_ids的index号

protoIdx

该方法的原形,它的值是proto_ids的index号

nameIdx

该方法的名称,它的值是string_ids的index号

如下图



数据区

class_def

存储着Dex文件每一个类的相关信息,每个大小为32字节,其数据结构为

/*
* Direct-mapped "class_def_item".
*/
struct DexClassDef {
u4  classIdx;           /* index into typeIds for this class */
u4  accessFlags;
u4  superclassIdx;      /* index into typeIds for superclass */
u4  interfacesOff;      /* file offset to DexTypeList */
u4  sourceFileIdx;      /* index into stringIds for source file name */
u4  annotationsOff;     /* file offset to annotations_directory_item */
u4  classDataOff;       /* file offset to class_data_item */
u4  staticValuesOff;    /* file offset to DexEncodedArray */
};


classIdx

这个类的类型,值为type_ids的index

accessFlags

qccess标志,指示public、private等

superclassIdx

父类的类型,值为type_ids的index

interfacesOff

DexTypeList的偏移地址,该结构代表这个类所拥有的interface,若没有,值为0

sourceFileIdx

源文件名称,值为string_ids的index

annotationsOff

annotations_directory_item的偏移地址,在data区,代表注释,若没有注释,值为0

classDataOff

class_data_item的偏移地址,在data区,代表一个类的详细信息,包含field、method、method所执行的代码等,后面会详细介绍

staticValuesOff

值为偏移地址,指向类的static field

如下图



class_data_item

DexClassDef的classDataOff字段指示了class_data_item在Dex文件中的地址,读入内存后的数据结构为DexClassData,在DexClass.h文件中

/* expanded form of class_data_item. Note: If a particular item is
* absent (e.g., no static fields), then the corresponding pointer
* is set to NULL. */
struct DexClassData {
DexClassDataHeader header;//记录staticFields、instanceFields、directMethods、virtualMethods的size
DexField*          staticFields;//类的static域
DexField*          instanceFields;//类的实例域
DexMethod*         directMethods;//类的方法
DexMethod*         virtualMethods;//类的virtual方法
};


DexClassDataHeader、DexField、DexMethod数据结构如下

/* expanded form of a class_data_item header */
struct DexClassDataHeader {
u4 staticFieldsSize;
u4 instanceFieldsSize;
u4 directMethodsSize;
u4 virtualMethodsSize;
};

/* expanded form of encoded_field */
struct DexField {
u4 fieldIdx;    /* index to a field_id_item */
u4 accessFlags;
};

/* expanded form of encoded_method */
struct DexMethod {
u4 methodIdx;    /* index to a method_id_item */
u4 accessFlags;
u4 codeOff;      /* file offset to a code_item */
};


fieldIdx值为索引区的field_ids的index,methodIdx值为索引区的method_ids的index,重点看codeOff,值为偏移地址,指示code_item

code_item

code_item记录类的方法的运行时相关信息,其数据结构在DexFile.h中声明:

struct DexCode {
u2  registersSize;//寄存器的个数
u2  insSize;//输入参数的个数
u2  outsSize;//本段代码调用其他方法需要的参数
u2  triesSize;//try item结构的个数,
u4  debugInfoOff;       /* file offset to debug info stream */
u4  insnsSize;          /* 指令列表的大小,以16-bit为单位 */
u2  insns[1];//指令(就是字节码)
/* followed by optional u2 padding */
/* followed by try_item[triesSize] */
/* followed by uleb128 handlersSize */
/* followed by catch_handler_item[handlersSize] */
};


insnsSize和insns[1]

insnsSize指示了指令个数,insns[1]就是实际的指令,大小并不是1,而是insnsSize

try_item 和 catch_handler_item

在insns[1]后面可能还存在try_item和catch_handler_item,这2个用于捕获Java的异常,常见的Java代码有try catch

如下图



data

存储着字符串常量、字节码、map_item等实际内容

附录

测试用的Dex的java代码

Foo.java:

class Foo {
public static void main(String[] args) {
System.out.println("Hello, world");
}
}


参考资料

http://bbs.pediy.com/showthread.php?t=184761
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dalvik android