Android运行时ART简要介绍和学习计划

Android运行时ART简要介绍和学习计划 

 

 

 

 

 

Android ART Oat文件格式简析

 

在上篇中,我们分析到了OatFile的begin_和end_变量分别被指定到了符号oatdata和oatlastword指定的位置。那么指定的这一段数据到底是什么呢?本文会接下来分析。

首先来看OatFile::Setup的实现:

 

bool OatFile::Setup() {
  if (!GetOatHeader().IsValid()) {
    LOG(WARNING) << "Invalid oat magic for " << GetLocation();
    return false;
  }
  ......

GetOatHeader?这是什么东东?接下来看看OatFile::GetOatHeader的实现:

 

const OatHeader& OatFile::GetOatHeader() const {
  return *reinterpret_cast<const OatHeader*>(Begin());
}

很简单,就是将前面设置的begin_指向的内存,强制转换成OatHeader。OatHeader又是什么东西?翻出定义看看,只看成员变量定义:

 

private:
  uint8_t magic_[4];
  uint8_t version_[4];
  uint32_t adler32_checksum_;

  InstructionSet instruction_set_;
  uint32_t dex_file_count_;
  uint32_t executable_offset_;
  uint32_t interpreter_to_interpreter_bridge_offset_;
  uint32_t interpreter_to_compiled_code_bridge_offset_;
  uint32_t jni_dlsym_lookup_offset_;
  uint32_t portable_resolution_trampoline_offset_;
  uint32_t portable_to_interpreter_bridge_offset_;
  uint32_t quick_resolution_trampoline_offset_;
  uint32_t quick_to_interpreter_bridge_offset_;

  uint32_t image_file_location_oat_checksum_;
  uint32_t image_file_location_oat_data_begin_;
  uint32_t image_file_location_size_;
  uint8_t image_file_location_data_[0];  // note variable width data at end

  DISALLOW_COPY_AND_ASSIGN(OatHeader);
};

我们也对照着把文件中的内容拿出来看看,还是以系统的Boot Oat(system@framework@boot.oat)为例, oatdata值为0x60a9d000,而通过看Program Header,可以知道elf文件起始被指定映射到了0x60a9c000:

hex 16进制, 4C   对应10进制, 76   对应二进制, 01001100  对应 缩写/字符 就是, 'L'
所以,可以知道,oatdata指向的位置在文件中的偏移是0x60a9d000-0x60a9c000=0x1000。拿出二进制编辑工具看看那里有什么:


顿时明白了一切,原来所谓oat文件,其实就是影藏在elf文件中的一个子文件,其有特殊的头和数据格式。我们对照着实际的数据一一做个分析:

1)首先是oat文件的magic code:

 

const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };


2)然后是oat文件的版本号:

 

 

const uint8_t OatHeader::kOatVersion[] = { '0', '0', '7', '\0' };


3)接下来是什么checksum,不太重要,暂时忽略;

 

4)接下来指定oat文件支持的CPU指令集,有以下几种:

 

enum InstructionSet {
  kNone,
  kArm,
  kThumb2,
  kX86,
  kMips
};


实际文件中的值是2,对应于Thumb2指令集,也是ARM处理器支持的一种指令集啦。

 

5)接下来是指定Dex文件的数量,看来例子中的oat文件共包含15个Dex文件;

      不明明是oat文件嘛,怎么又来Dex文件啦?其实oat文件是通过Dex文件转换过来的,并且oat文件包含了一个完整的初始的Dex文件,这也就解释了为什么Dex文件转换成oat文件后比原来要大很多。关于这点,先解释到这里,后面碰到的时候还会更具体的分析。

6)可执行部分的偏移,其值是0x18CE000。值得注意的,这里的偏移就不是相对于elf文件头的了,而是相对于oat文件头的。前面说过,oatdata的值为0x60a9d000,所以0x60A9D000+0x18CE000=0x6236B000,正好等于oatexec指向的地址:

而这个虚拟地址0x6236B000,正好是Program Header第三项指定段的起始地址,该段的属性是可读可执行(RE)。真好,全都对应上了。

7)接下来还有一堆偏移,后面碰到再解释;

8)紧接着这些offset的应该是关于Image的一些信息,例子中的文件这些都是0,所以跳过。

好的,解释完OatHeaader后,我们回到OatFile::Setup继续往下看:

 

  const byte* oat = Begin();
  oat += sizeof(OatHeader);
  if (oat > End()) {
    LOG(ERROR) << "In oat file " << GetLocation() << " found truncated OatHeader";
    return false;
  }

  oat += GetOatHeader().GetImageFileLocationSize();
  if (oat > End()) {
    LOG(ERROR) << "In oat file " << GetLocation() << " found truncated image file location: "
               << reinterpret_cast<const void*>(Begin())
               << "+" << sizeof(OatHeader)
               << "+" << GetOatHeader().GetImageFileLocationSize()
               << "<=" << reinterpret_cast<const void*>(End());
    return false;
  }
  ......

这段代码比较容易理解,局部变量oat先获得Oat文件头的位置,然后加上OatHeader结构体的大小,最后还要加上记录Image文件位置的字符串的长度。这时oat变量指向的内存地址刚好就是跳过OatHeader后的位置,应该就是数据区了。好,接着看:

 

 

  for (size_t i = 0; i < GetOatHeader().GetDexFileCount(); i++) {
    size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
    if (dex_file_location_size == 0U) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " with empty location name";
      return false;
    }
    oat += sizeof(dex_file_location_size);
    if (oat > End()) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " truncated after dex file location size";
      return false;
    }

    const char* dex_file_location_data = reinterpret_cast<const char*>(oat);
    oat += dex_file_location_size;
    if (oat > End()) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " with truncated dex file location";
      return false;
    }

    std::string dex_file_location(dex_file_location_data, dex_file_location_size);

    uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
    oat += sizeof(dex_file_checksum);
    if (oat > End()) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " for "<< dex_file_location
                 << " truncated after dex file checksum";
      return false;
    }

    uint32_t dex_file_offset = *reinterpret_cast<const uint32_t*>(oat);
    if (dex_file_offset == 0U) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " for "<< dex_file_location
                 << " with zero dex file offset";
      return false;
    }
    if (dex_file_offset > Size()) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " for "<< dex_file_location
                 << " with dex file offset" << dex_file_offset << " > " << Size();
      return false;
    }
    oat += sizeof(dex_file_offset);
    if (oat > End()) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " for "<< dex_file_location
                 << " truncated after dex file offset";
      return false;
    }
    ......
  }

 

最开始,根据前面得到的Dex文件数进行遍历。为了方便理解,我们还是结合实际文件中的值来看:

首先得到的是dex文件位置字符串的大小,这里是0x21,就是有33个字符。紧接着的就是具体的dex文件的位置信息,这里是”/system/framework/core-libart.jar“,可以数数,刚好是33个字符。再下面4个字节是对应此dex文件的checksum。再下面记录的是dex文件相对oat头地址的偏移,此例中是0xD1B0,由于这是相对于oat头位置的偏移,加上oat头相对于elf头的0x1000偏移,所以应该在0xE1B0处找,我们看看那有什么:


看到没有,是不是非常熟悉呢?众里寻他千百度,暮然回首,那人却在灯火阑珊处,这里就是转换前的dex文件所在地呀。好了,接着分析剩下的代码:

 

    const uint8_t* dex_file_pointer = Begin() + dex_file_offset;
    if (!DexFile::IsMagicValid(dex_file_pointer)) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " for "<< dex_file_location
                 << " with invalid dex file magic: " << dex_file_pointer;
      return false;
    }
    if (!DexFile::IsVersionValid(dex_file_pointer)) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " for "<< dex_file_location
                 << " with invalid dex file version: " << dex_file_pointer;
      return false;
    }
    const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
    const uint32_t* methods_offsets_pointer = reinterpret_cast<const uint32_t*>(oat);

    oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);
    if (oat > End()) {
      LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
                 << " for "<< dex_file_location
                 << " with truncated method offsets";
      return false;
    }

    oat_dex_files_.Put(dex_file_location, new OatDexFile(this,
                                                         dex_file_location,
                                                         dex_file_checksum,
                                                         dex_file_pointer,
                                                         methods_offsets_pointer));
  }

此时,局部变量dex_file_pointer已经指向了dex文件的位置。代码会先验证一下该dex文件的magic code和版本号。接着局部变量header被强制类型转换成DexFile::Header类型,表示dex的文件头位置。而另一个局部变量methods_offsets_pointer指向了前面dex文件偏移的后4个字节,局部变量名的意思是指向一个什么方法偏移指针数组,而且这个数组成员的个数,似乎还跟dex文件头中的class_defs_size_有联系。很奇怪是吧,这是什么东西呢?那么 class_defs_size_代表什么意思?熟悉dex文件格式的人应该都知道,这里稍微提一下,它其实就表示dex文件中共包含了几个类。再结合变量名联想一下,猜测这个指针指向的是一个数组,元素个数就是dex文件中定义的类的个数,数组内的元素都是一些偏移,指向的是一组方法,这些方法我猜测就对应的是各个类内部定义的方法。空口无凭,我们来简单验证一下,首先看看对应的dex文件中有几个类:

 

为什么在这,我不解释。好,一共有0x853个类。而methos_offsets_pointer_指向的值是0x106D。好,我们在简单计算一下0x106D+4*0x853=0x31B9,这个位置有什么呢,我们再看看:

看见了没有,刚好是表示下一个dex文件信息的开头。那数组中具体每个偏移指到的是什么呢?先别忙,我们再接下来分析。OatFile::Setup还剩下最后一部分:

 

    oat_dex_files_.Put(dex_file_location, new OatDexFile(this,
                                                         dex_file_location,
                                                         dex_file_checksum,
                                                         dex_file_pointer,
                                                         methods_offsets_pointer));
  }
  return true;
}

往自己的内部变量oat_dex_files_中插入一项,插入的是什么呢?创建了一个OatDexFile的对象,这又是什么?从中能不能知道前面问题的答案呢,让我们来看看OatFile::OatDexFile::GetOatClass函数:

 

 

  const OatFile::OatClass* OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const {
  uint32_t oat_class_offset = oat_class_offsets_pointer_[class_def_index];

  const byte* oat_class_pointer = oat_file_->Begin() + oat_class_offset;
  CHECK_LT(oat_class_pointer, oat_file_->End()) << oat_file_->GetLocation();
  mirror::Class::Status status = *reinterpret_cast<const mirror::Class::Status*>(oat_class_pointer);

  const byte* methods_pointer = oat_class_pointer + sizeof(status);
  CHECK_LT(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();

  return new OatClass(oat_file_,
                      status,
                      reinterpret_cast<const OatMethodOffsets*>(methods_pointer));
}

这个函数是用来在Oat文件中找所谓OatClass的。其中oat_class_offsets_pointer_是前面提到的那个偏移数组,从中我们大致可以了解到底这些偏移指向的东西是什么。首先,根据传入的类定义序号找到相应的偏移,然后加上oat文件头地址,得到绝对地址,付给变量oat_class_pointer,从字面来看是所谓的oat类指针,这又是什么呢?接着强制转换成mirror::Class::Status,这是一个枚举值:

 

 

  enum Status {
    kStatusError = -1,
    kStatusNotReady = 0,
    kStatusIdx = 1,  // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
    kStatusLoaded = 2,  // DEX idx values resolved.
    kStatusResolved = 3,  // Part of linking.
    kStatusVerifying = 4,  // In the process of being verified.
    kStatusRetryVerificationAtRuntime = 5,  // Compile time verification failed, retry at runtime.
    kStatusVerifyingAtRuntime = 6,  // Retrying verification at runtime.
    kStatusVerified = 7,  // Logically part of linking; done pre-init.
    kStatusInitializing = 8,  // Class init in progress.
    kStatusInitialized = 9,  // Ready to go.
  };

这些应该表明该类的当前状态。除去 mirror::Class::Status枚举的长度后,接下来指向的是OatMethodOffsets结构体数组,定义如下:

 

 

class PACKED(4) OatMethodOffsets {
 public:
  ......
  uint32_t code_offset_;
  uint32_t frame_size_in_bytes_;
  uint32_t core_spill_mask_;
  uint32_t fp_spill_mask_;
  uint32_t mapping_table_offset_;
  uint32_t vmap_table_offset_;
  uint32_t gc_map_offset_;
};

 

共由7个变量组成,第一个从字面上看是代码偏移。

我们还是顺着刚才的例子,找第一个偏移看看那到底有什么:


哦,看来该类应该已经初始化完成了(状态值为9),第一个方法的代码段偏移是0x18CE045,第二个方法的代码段偏移是0x18CE0D4。
好了,分析到这里应该已经差不多了,下面简单总结一下什么是oat文件,以及它的一些特性:

1)oat文件其实是包含在一个elf文件中的,符号oatdata和oatlastword分别指定了oat文件在elf文件中的头和尾的位置,符号oatexec指向可执行段的位置;

2)对于包含oat的elf文件来说,如果是Boot Oat,则其是要被加载到一个固定的地址上的,具体来说是紧接着Image文件之后。而对于普通应用程序的oat文件来说,可以被加载到内存中的任何位置;

3)oat文件有自己的头和格式,并且其内部包含了一个完整的dex文件。

 

相关文章

  oat += GetOatHeader().GetImageFileLocationSize();
  if (oat > End()) {
    LOG(ERROR) << "In oat file " << GetLocation() << " found truncated image file location: "
               << reinterpret_cast<const void*>(Begin())
               << "+" << sizeof(OatHeader)
               << "+" << GetOatHeader().GetImageFileLocationSize()
               << "<=" << reinterpret_cast<const void*>(End());
    return false;
  }


 Android运行时ART简要介绍和学习计划
  https://blog.csdn.net/Luoshengyang/article/details/39256813
  
  oat += GetOatHeader().GetImageFileLocationSize();
  if (oat > End()) {
    LOG(ERROR) << "In oat file " << GetLocation() << " found truncated image file location: "
               << reinterpret_cast<const void*>(Begin())
               << "+" << sizeof(OatHeader)
               << "+" << GetOatHeader().GetImageFileLocationSize()
               << "<=" << reinterpret_cast<const void*>(End());
    return false;
  }


 const byte* oat = Begin();

reinterpret_cast<const void*>(End());

   通过OatFile类的成员函数Setup的第二部分代码的分析,我们就知道了,紧接着在OAT头后面的是Image空间文件路径,如图3所示:
   
    size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
    
    
    
    
    reinterpret_cast<const uint32_t*>(oat)
    
    reinterpret_cast<const char*>(oat);
    
    
      for (size_t i = 0; i < GetOatHeader().GetDexFileCount(); i++) {
    size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
    ......
 
    oat += sizeof(dex_file_location_size);
    ......
 
    const char* dex_file_location_data = reinterpret_cast<const char*>(oat);
    oat += dex_file_location_size;
    ......
 
    std::string dex_file_location(dex_file_location_data, dex_file_location_size);
 
    uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
    oat += sizeof(dex_file_checksum);
    ......
 
    uint32_t dex_file_offset = *reinterpret_cast<const uint32_t*>(oat);
    ......
    
    oat += sizeof(dex_file_offset);
    ......
 
    const uint8_t* dex_file_pointer = Begin() + dex_file_offset;
    if (!DexFile::IsMagicValid(dex_file_pointer)) {
      ......
      return false;
    }
    if (!DexFile::IsVersionValid(dex_file_pointer)) {
      ......
      return false;
    }
 
    const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
    const uint32_t* methods_offsets_pointer = reinterpret_cast<const uint32_t*>(oat);
 
    oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);
    ......
 
    oat_dex_files_.Put(dex_file_location, new OatDexFile(this,
                                                         dex_file_location,
                                                         dex_file_checksum,
                                                         dex_file_pointer,
                                                         methods_offsets_pointer));
  }
  return true;
}

   这部分代码用来获得包含在oatdata段的DEX文件描述信息。每一个DEX文件记录在oatdata段的描述信息包括:

        1. DEX文件路径大小,保存在变量dex_file_location_size中;

        2. DEX文件路径,保存在变量dex_file_location_data中;

        3. DEX文件检验和,保存在变量dex_file_checksum中;

        4. DEX文件内容在oatdata段的偏移,保存在变量dex_file_offset中;

        5. DEX文件包含的类的本地机器指令信息偏移数组,保存在变量methods_offsets_pointer中;

        在上述五个信息中,最重要的就是第4个和第5个信息了。

        通过第4个信息,我们可以在oatdata段中找到对应的DEX文件的内容。DEX文件最开始部分是一个DEX文件头,上述代码通过检查DEX文件头的魔数和版本号来确保变量dex_file_offset指向的位置确实是一个DEX文件。
        
        
        
        PQGetvalue()返回 char * 文档。 (uint32_t *)会将 char * 变成指向未读取的32位整数的指针, * 之前,将取消引用此值以获取实际值(无符号,32位整数),最后 ntohl 32位整数的平台,这可能意味着原始的存储格式是在网络顺序。

如果我们拆分该代码,那将给: p> 

  //从数据库获取值作为char * 
 char * in_database = PQgetvalue(result,i,0); 
 //将指针转换为指向无符号32位整数的指针
 uint32_t * ptr =(uint32_t *)in_database; 
 //解除指针获取实际存储的值
 uint32_t stored_value = * ptr; 
 //将该值转换为CPU的本地整数
 uint32_t ip = ntohl(stored_value); 
        
        

将[32] byte转换为[8] uint32

data := [32]byte{1, 0, 0, 0, 2}
ints := [8]uint32{}

for i := 0; i < len(data); i += 4 {
    ints[i/4] = binary.LittleEndian.Uint32(data[i:])
}

fmt.Println(ints)
Output (try it on the Go Playground):

[1 2 0 0 0 0 0 0]


    
    整型的每一种都有无符号(unsigned)和有符号(signed)两种类型(float和double总是带符号的),在默认情况下声明的整型变量都是有符号的类型(char有点特别),如果需声明无符号类型的话就需要在类型前加上unsigned。无符号版本和有符号版本的区别就是无符号类型能保存2倍于有符号类型的正整数数据,比如16位系统中一个int能存储的数据的范围为-32768~32767,而unsigned能存储的数据范围则是0~65535。由于在计算机中,整数是以补码形式存放的。根据最高位的不同,如果是1,有符号数的话就是负数;如果是无符号数,则都解释为正数。同时在相同位数的情况下,所能表达的整数范围变大。另外,unsigned若省略后一个关键字,大多数编译器都会认为是unsigned int。

----------------

数据类型 -- uint32_t 类型

 
数据类型 -- uint32_t 类型
1>. 在写程序时注意"无符号类型"的使用, 各种类型边界值的情况.
如:
a> 当某个数据不可能为负数时我们一定要考虑用以下类型:
unsigned char, unsigned int, uint32_t, size_t, uint64_t, unsigned long int, 
b> 当有些数据你不知道是正负时一定不要用"a>"中的类型, 不然他永远也不可能为负.

c> 数据的边界值要多注意, 如:
uint32_t    a, b, c;
uint64_t    m;

m = a * b + c;
在该运算中可能出现错误, "a*b"的类型可能超过uint32_t的最大值,这时一定不要忘了类型转换.
m = ((uint64_t)a) * b + c;

2>. 在适当的时候要会自我定义数据类型.
我们都知道linux C开发中的常见扩展数据类型的定义有:uint8_t, uint16_t, uint32_t, uint64_t, size_t, ssize_t, off_t .... 他之所以要自己定义出数据类型是有道理的, 如: typdef unsigned int uint32_t; 表示uint32_t为32位无符号类型数据, 其实size_t也是32位无符号数据类型, 为什么不直接写"unsigned int"呢?
为了程序的可扩展性, 假如将来我们需要的数据大小变成了64bit时,我们只需要将typedef long long size_t就可以了, 不然我们可要修改好多好多的地方了. 这种设计我们同样可以应用到自己的开发中来,当自己设计一个int类型保存某种数据时,但你又没把握将来是不是要用long int时你可以引用一个自己定义的数据类型的啊!


stdint.h
-----------------------------
typedef unsigned int            uint32_t;


uint32_t.c
-----------------------------
#include <stdio.h>
#if 0       
#include <stdint.h>    // uint32_t
#endif      

typedef unsigned int       uint32_t;
typedef unsigned long long uint64_t;

int main()
{
uint32_t a; 
a = 12; 
printf("a = %x\n", a);
printf("sizeof(a) = %d\n", sizeof(a));

uint64_t b; 
b = 12; 
printf("b = %x\n", b);
printf("sizeof(b) = %d\n", sizeof(b));
}   

a = c
sizeof(a) = 4
b = c
sizeof(b) = 8
    
    
    
    const char* dex_file_location_data = 
    
    reinterpret_cast<const char*>(oat);
    
    
    

ART           Dalvik Dex 


道,ART除了实现Java虚拟机接口之外,其内部还有垃圾收集机制,同时还有Java核心类库调用,因此,随着对ART的深入分析,我们就认为这个结论是不矛盾的了。


AndroidRuntime::start


void AndroidRuntime::start(const char* className, const char* options)  
{  
    ......  
      
    /* start the virtual machine */  
    JniInvocation jni_invocation;  
    jni_invocation.Init(NULL);  
    JNIEnv* env;  
    if (startVm(&mJavaVM, &env) != 0) {  
        return;  
    }  
      
    ......  
      
    /* 
     * Start VM.  This thread becomes the main thread of the VM, and will 
     * not return until the VM exits. 
     */  
    char* slashClassName = toSlashClassName(className);  
    jclass startClass = env->FindClass(slashClassName);  
    if (startClass == NULL) {  
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);  
        /* keep going */  
    } else {  
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",  
        "([Ljava/lang/String;)V");  
        if (startMeth == NULL) {  
            ALOGE("JavaVM unable to find main() in '%s'\n", className);  
            /* keep going */  
        } else {  
            env->CallStaticVoidMethod(startClass, startMeth, strArray);  
      
#if 0  
            if (env->ExceptionCheck())  
                threadExitUncaughtException(env);  
#endif  
        }  
    }  
          
    ......  
}  

Dex

dex2oat oat

        综上所述,接下来我们就按照以下几个情景来分析ART的工作原理:

        1. ART加载oat文件的过程。

        2. ART查找类和方法的过程。

        3. ART查找类方法的本地机器指令的过程。

        4. Dalvik虚拟机的垃圾收集过程。

        5. ART的垃圾收集过程。

oatdata      oatexec    oatlastword

static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,  
    const char* output_file_name, const char* dexopt_flags)  
{  
    static const char* DEX2OAT_BIN = "/system/bin/dex2oat";  
    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig  
    char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];  
    char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];  
    char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];  
    char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX];  
  
    sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);  
    sprintf(zip_location_arg, "--zip-location=%s", input_file_name);  
    sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);  
    sprintf(oat_location_arg, "--oat-location=%s", output_file_name);  
  
    ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);  
    execl(DEX2OAT_BIN, DEX2OAT_BIN,  
          zip_fd_arg, zip_location_arg,  
          oat_fd_arg, oat_location_arg,  
          (char*) NULL);  
    ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));  
}  


OAT文件的生成过程主要就是涉及到将包含在APK里面的classes.dex文件的DEX字节码翻译成本地机器指令

class.dex     这相当于是编写一个输入文件为DEX、输出文件为OAT的编译器

make_pair
 

SystemServer系统进程以及Android应用程序进程的ART虚拟机都是直接从Zygote进程fork出来共享的。”

Android

Create ART  

Runtime


instance_ = new Runtime


UniquePtr
         gc::Heap ();

JavaVMExt

Thread* self =Thread Attach (main)

const std::string option(options[i].first)

parsed->image+= framework/boot.art

/system/framework/boot.art

framework/boot.art

/data/dalvik-cache
system@framework/ boot class.dex

dex2oat  dex-file oat-file


std::vector<char*> char_args;
  for (std::vector<std::string>::iterator it = arg_vector.begin(); it != arg_vector.end();
      ++it) {
    char_args.push_back(const_cast<char*>(it->c_str()));
  }
  char_args.push_back(NULL);

  
  char* vector
  


ART 生成本地机器    OAT


这个OAT头描述了OAT文件所包含的DEX文件的信息,以及定义在这些DEX文件里面的类方法所对应的本地机器指令在内存的位置。


OatHeader。OatHeader又是什么东西?翻出定义看看,只看成员变量定义:
private:
  uint8_t magic_[4];
  uint8_t version_[4];
  uint32_t adler32_checksum_;

  InstructionSet instruction_set_;
  uint32_t dex_file_count_;
  uint32_t executable_offset_;
  uint32_t interpreter_to_interpreter_bridge_offset_;
  uint32_t interpreter_to_compiled_code_bridge_offset_;
  uint32_t jni_dlsym_lookup_offset_;
  uint32_t portable_resolution_trampoline_offset_;
  uint32_t portable_to_interpreter_bridge_offset_;
  uint32_t quick_resolution_trampoline_offset_;
  uint32_t quick_to_interpreter_bridge_offset_;

  uint32_t image_file_location_oat_checksum_;
  uint32_t image_file_location_oat_data_begin_;
  uint32_t image_file_location_size_;
  uint8_t image_file_location_data_[0];  // note variable width data at end

  DISALLOW_COPY_AND_ASSIGN(OatHeader);
};

  size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);

首先得到的是dex文件位置字符串的大小,这里是0x21,就是有33个字符。紧接着的就是具体的dex文件的位置信息,这里是”/system/framework/core-libart.jar“,可以数数,刚好是33个字符。再下面4个字节是对应此dex文件的checksum。再下面记录的是dex文件相对oat头地址的偏移,此例中是0xD1B0,由于这是相对于oat头位置的偏移,加上oat头相对于elf头的0x1000偏移,所以应该在0xE1B0处找,我们看看那有什么:

'0' '0' '2' '1' 0x21   
'/' 's' 'y'        
 ”/system/framework/core-libart.jar“

 image_file_location_oat_checksum_
 
 image_file_location_oat_data_begin_
 
 
 system@framework@boot.oat
 
  ImageSpace类的静态成员函数GenerateImage实际上就调用dex2oat工具在/data/dalvik-cache目录下生成两个文件:system@framework@boot.art@classes.dex和system@framework@boot.art@classes.oat。
 
 
 
 
    uint32_t dex_file_offset = *reinterpret_cast<const uint32_t*>(oat);
    ......
    
    oat += sizeof(dex_file_offset);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值