Dex文件結構
文件頭
typedef struct {
u1 magic[MAGIC_LENGTH]; /* includes version number */
u4 checksum; /* adler32 校驗剩餘長度的文件 */
u1 signature[kSHA1DigestLen]; /* SHA-1 文件簽名 */
u4 fileSize; /* length of entire file */
u4 headerSize; /* offset to start of next section */
u4 endianTag;
u4 linkSize;
u4 linkOff;
u4 mapOff;
u4 stringIdsSize; //字符串表大小 偏移
u4 stringIdsOff;
u4 typeIdsSize; //類型表 大小偏移
u4 typeIdsOff;
u4 protoIdsSize; //原型表 大小 偏移
u4 protoIdsOff;
u4 fieldIdsSize; //字段表 大小 偏移
u4 fieldIdsOff;
u4 methodIdsSize; //函數表 大小 偏移
u4 methodIdsOff;
u4 classDefsSize; //類定義表 大小 偏移
u4 classDefsOff;
u4 dataSize; //數據段 大小 偏移
u4 dataOff;
}DexHeader;
DexHeader由於是定長結構 直接格式化就好
Leb128編碼
每個LEB128由1到5個字節組成,所有字節組合到一起代表一個32位值。除了最後一個字節的最高標誌位爲0,
其它的爲1.剩下的7位爲有效負荷,第二個字節的7位接上。有符號LEB128的符號由最後字節的有效負荷最高位決定。
例如:0x7f80
01111111 10000000
按無符號leb128解析 0x3f80
按有符號leb128解析 -128 (注意先轉補碼)
具體解析算法在示例代碼中
字符串表
字符串表包含了dex文件/代碼中使用到的字符串
字符串表存放的是StringId,具體字符串值在數據段data中
typedef struct {
u4 stringDataOff; /* string_data_item 偏移 */
}DexStringId;
struct string_data_item {
u2 uleb128; //字符串長度
u1 str[1]; //字符串內容
}
string_data_item 起始2字節是uleb128編碼,解碼後可得到字符串的長度
類型表
typedef struct {
u4 descriptorIdx; /* 指向一個string_id的index */
}DexTypeId;
字段表
typedef struct {
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 */
}DexFieldId;
Field描述的是一個類中的成員變量/靜態變量
原型表
typedef struct {
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 */
}DexProtoId;
Proto原型描述的是一個函數的返回類型 參數類型列表
由於參數可能是多個 parametersOff指向的是一個 type_list結構
typedef struct {
u2 typeIdx; /* index into typeIds */
}DexTypeItem;
typedef struct {
u4 size; /* #of entries in list */
DexTypeItem list[1]; /* entries */
}DexTypeList;
如果parametersOff爲0 表示該函數沒有參數
函數表
typedef struct {
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 */
}DexMethodId;
Method描述的是函數所在類 原型 名稱
類數據
typedef struct{
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 */
}DexClassDef;
superclassIdx 爲0表示父類是 java/lang/Object
interfacesOff/annotationsOff/classDataOff/staticValuesOff 都由可能是0 表示類中沒有該類型的數據,例如一個標記類 可能classDataOff就會爲0 因爲沒有定義任何函數/字段
sourceFileIdx 可能會是一個無效的id
#define kDexNoIndex 0xffffffff /* not a valid index value */
classDataOff 表示類數據的偏移 指向的是class_data結構
struct class_data{
u4_uleb128 staticFieldsSize;
u4_uleb128 instanceFieldsSize;
u4_uleb128 directMethodsSize;
u4_uleb128 virtualMethodsSize;
DexField staticFields[staticFieldsSize];
DexField instanceFields[instanceFieldsSize];
DexMethod directMethods[directMethodsSize];
DexMethod virtualMethods[virtualMethodsSize];
}
//encoded field
typedef struct {
//origin type is uleb128
u4 fieldIdx; /* 指向一個字段表裏的index */
u4 accessFlags;
}DexField;
//encoded method
typedef struct{
//origin type is uleb128
u4 methodIdx; /* 指向一個函數表裏的index */
u4 accessFlags;
u4 codeOff; /* DexCode 偏移*/
}DexMethod;
typedef struct {
u2 registersSize; //代碼塊內使用到的寄存器個數
u2 insSize; //入參字數
u2 outsSize; //出參字數
u2 triesSize; //try_catch個數
u4 debugInfoOff; /* file offset to debug info stream */
u4 insnsSize; /*字節碼數目*/
u2 insns[1]; //字節碼內容
//下面的內容都是當 triesSize>0的時候纔會出現
//padding 使try-handler-table 和 字節碼之間 四字節對齊
/* followed by optional u2 padding */
//try_cat處理表內容 這裏實現的是class文件中的try-handler-table
/* followed by try_item[triesSize] */
/* followed by uleb128 handlersSize */
/* followed by catch_handler_item[handlersSize] */
}DexCode;
dex字節碼的翻譯和class字節碼翻譯差不多,對着規範翻譯就好
綜述
android vm 採用dex字節碼而不是class字節碼的優勢?
- dex文件由多個class文件合併而來,把多個常量池合併到一個常量池,避免了常量冗餘,有利於運行時的常量內存共享
- 加載一個dex可以加載多個相互依賴的class,減少了文件io
- arm cpu具有較多的通用寄存器,vm設計基於寄存器的執行流程,會加速函數的傳參和執行