Process Analysis of ART Loading OAT File

  The OAT file is a private ELF file format after Android introduced the ART virtual machine. It not only contains the local machine instructions translated from the DEX file, but also contains the original DEX file content. Android uses /system/bin/dex2oat (we can also compile a debug version of dex2oatd) to compile DEX files into OAT files. The main entry of dex2oat is the main function:

/art/dex2oat/dex2oat.cc

int main(int argc, char** argv) {
  return art::dex2oat(argc, argv);
}

  dex2oat can not only compile the OAT file of the app, but also the boot.oat and boot.art files, which is determined by the incoming "-image=" parameter. Let's talk about the process of compiling boot.oat and boot.art. In the process of generating boot.oat and boot.art on the host, the parameters we get are:

dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[0]=--runtime-arg
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:1059] dex2oat: option[1]=-Xms64m
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[2]=--runtime-arg
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:1059] dex2oat: option[3]=-Xmx64m
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[4]=--image-classes=frameworks/base/preloaded-classes
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[5]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/com.mstar.android_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[6]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/ngbj_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[7]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/tvm_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[8]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[9]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/conscrypt_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[10]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/okhttp_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[11]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/core-junit_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[12]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[13]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[14]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[15]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[16]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/voip-common_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[17]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[18]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/mms-common_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[19]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/android.policy_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[20]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/apache-xml_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[21]=--dex-location=/system/framework/com.mstar.android.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[22]=--dex-location=/system/framework/ngbj.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[23]=--dex-location=/system/framework/tvm.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[24]=--dex-location=/system/framework/core-libart.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[25]=--dex-location=/system/framework/conscrypt.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[26]=--dex-location=/system/framework/okhttp.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[27]=--dex-location=/system/framework/core-junit.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[28]=--dex-location=/system/framework/bouncycastle.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[29]=--dex-location=/system/framework/ext.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[30]=--dex-location=/system/framework/framework.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[31]=--dex-location=/system/framework/telephony-common.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[32]=--dex-location=/system/framework/voip-common.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[33]=--dex-location=/system/framework/ims-common.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[34]=--dex-location=/system/framework/mms-common.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[35]=--dex-location=/system/framework/android.policy.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[36]=--dex-location=/system/framework/apache-xml.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[37]=--oat-symbols=out/target/product/ponkan/symbols/system/framework/arm/boot.oat
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[38]=--oat-file=out/target/product/ponkan/dex_bootjars/system/framework/arm/boot.oat
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[39]=--oat-location=/system/framework/arm/boot.oat
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[40]=--image=out/target/product/ponkan/dex_bootjars/system/framework/arm/boot.art
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[41]=--base=0x70000000
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[42]=--instruction-set=arm
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[43]=--instruction-set-features=div
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[44]=--android-root=out/target/product/ponkan/system
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[45]=--include-patch-information
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[46]=--runtime-arg
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:1059] dex2oat: option[47]=-Xnorelocate
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[48]=--no-include-debug-symbols

  Explain the meaning of these parameters:
–runtime-arg: Indicates that the next parameter is a runtime parameter, and these parameters will be used to create the art virtual machine inside the dex2oat process.
–image-classes: The parameter value indicates the path of some classes to be compiled into the image. Here are the preloaded classes used by Zygote. The path in the host is frameworks/base/preloaded-classes, and the path in the target is / system/etc/preloaded-classes.
–dex-file: Indicates the dex file, jar file or apk file to be compiled
–dex-location: The jar file corresponding to the “-dex-file” parameter (corresponding to the path on the device)
–oat-symbols: oat file path with symbol
–oat-file: output oat file name
–oat-location: path of output oat file (corresponding to the path on the device) –image:
path of generated image
–base: offset base address
– instruction-set: instruction set
--instruction-set-features: instruction set parameters --android
-root: the path of the library used by portable linking --include
-patch-information: includes patch information at compile time, which can be rewritten without reprogramming Positioning
--no-include-debug-symbols: do not include symbols for debug
  The above is to compile boot.oat and boot.art on the host, and finally put them under /system/framework/arm(arm64)/ of the device. But sometimes these two files are not compiled on the host, so the compilation step needs to be performed at runtime.
  The runtime launched in Zygote uses "-Ximage:" to specify the name of the image to be launched. When this parameter is not specified, set the specified image to "/system/framework/boot.art". If image_file_name is not empty, ImageSpace::Create will be called to create boot.oat and boot.art.

/art/runtime/gc/heap.cc

  //image_file_name为image的名字
  if (!image_file_name.empty()) {
    std::string error_msg;
    space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
                                                               image_instruction_set,
                                                               &error_msg);

/art/runtime/gc/space/image_space.cc

ImageSpace* ImageSpace::Create(const char* image_location,
                               const InstructionSet image_isa,
                               std::string* error_msg) {
  std::string system_filename;
  bool has_system = false;
  std::string cache_filename;
  bool has_cache = false;
  bool dalvik_cache_exists = false;
  bool is_global_cache = true;
  const bool found_image = FindImageFilename(image_location, image_isa, &system_filename,
                                             &has_system, &cache_filename, &dalvik_cache_exists,
                                             &has_cache, &is_global_cache);

  ImageSpace* space;
  bool relocate = Runtime::Current()->ShouldRelocate();
  bool can_compile = Runtime::Current()->IsImageDex2OatEnabled();
  //若在/system/framework/arm(arm64)找到boot.art或者在/data/dalvik-cache/arm(arm64)找到system@[email protected],found_image为true
  if (found_image) {
    const std::string* image_filename;
    bool is_system = false;
    bool relocated_version_used = false;
    //relocate重定位用于首次开机
    if (relocate) {
      //找不到/data/dalvik-cache/arm(arm64)目录直接返回null
      if (!dalvik_cache_exists) {
        *error_msg = StringPrintf("Requiring relocation for image '%s' at '%s' but we do not have "
                                  "any dalvik_cache to find/place it in.",
                                  image_location, system_filename.c_str());
        return nullptr;
      }
      //若在/system/framework/arm(arm64)找到boot.art,has_system为true
      if (has_system) {
        //同时在/data/dalvik-cache/arm(arm64)找到system@[email protected]且checksum匹配
        if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
          // We already have a relocated version
          //将/data/dalvik-cache/arm(arm64)下的image文件作为启动image
          image_filename = &cache_filename;
          relocated_version_used = true;
        } else {//system目录下有image,而/data目录下没有image或者image不匹配checksum
          // We cannot have a relocated version, Relocate the system one and use it.

          std::string reason;
          bool success;

          // Check whether we are allowed to relocate.
          if (!can_compile) {
            reason = "Image dex2oat disabled by -Xnoimage-dex2oat.";
            success = false;
          } else if (!ImageCreationAllowed(is_global_cache, &reason)) {
            // Whether we can write to the cache.
            success = false;
          } else {
            //relocate system下的image到/data/dalvik-cache/arm(arm64)下
            // Try to relocate.
            success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &reason);
          }

          if (success) {
            //relocate成功,将/data下的image作为启动image
            relocated_version_used = true;
            image_filename = &cache_filename;
          } else {
            *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s",
                                      image_location, system_filename.c_str(),
                                      cache_filename.c_str(), reason.c_str());
            // We failed to create files, remove any possibly garbage output.
            // Since ImageCreationAllowed was true above, we are the zygote
            // and therefore the only process expected to generate these for
            // the device.
            PruneDexCache(image_isa);
            return nullptr;
          }
        }
      } else {//system下没有image,这是data下一定有image
        CHECK(has_cache);
        // We can just use cache's since it should be fine. This might or might not be relocated.
        //依然优先使用/data下的image作为启动image
        image_filename = &cache_filename;
      }
    } else {//非relocate情况
      //data和system下都有image
      if (has_system && has_cache) {
        // Check they have the same cksum. If they do use the cache. Otherwise system.
        if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
          //checksum匹配的话优先使用/data下面的image
          image_filename = &cache_filename;
          relocated_version_used = true;
        } else {
          //checksum不匹配则使用/system下面的image
          image_filename = &system_filename;
          is_system = true;
        }
      } else if (has_system) {//只有/system下面的image,则使用这个image
        image_filename = &system_filename;
        is_system = true;
      } else {//使用/data下面的image
        CHECK(has_cache);
        image_filename = &cache_filename;
      }
    }
    {
      // Note that we must not use the file descriptor associated with
      // ScopedFlock::GetFile to Init the image file. We want the file
      // descriptor (and the associated exclusive lock) to be released when
      // we leave Create.
      ScopedFlock image_lock;
      image_lock.Init(image_filename->c_str(), error_msg);
      VLOG(startup) << "Using image file " << image_filename->c_str() << " for image location "
                    << image_location;
      // If we are in /system we can assume the image is good. We can also
      // assume this if we are using a relocated image (i.e. image checksum
      // matches) since this is only different by the offset. We need this to
      // make sure that host tests continue to work.
      //使用之前定下来的image文件进行imagespace的初始化操作
      space = ImageSpace::Init(image_filename->c_str(), image_location,
                               !(is_system || relocated_version_used), error_msg);
    }
    if (space != nullptr) {
      return space;
    }

    if (relocated_version_used) {
      // Something is wrong with the relocated copy (even though checksums match). Cleanup.
      // This can happen if the .oat is corrupt, since the above only checks the .art checksums.
      // TODO: Check the oat file validity earlier.
      *error_msg = StringPrintf("Attempted to use relocated version of %s at %s generated from %s "
                                "but image failed to load: %s",
                                image_location, cache_filename.c_str(), system_filename.c_str(),
                                error_msg->c_str());
      PruneDexCache(image_isa);
      return nullptr;
    } else if (is_system) {
      // If the /system file exists, it should be up-to-date, don't try to generate it.
      *error_msg = StringPrintf("Failed to load /system image '%s': %s",
                                image_filename->c_str(), error_msg->c_str());
      return nullptr;
    } else {
      // Otherwise, log a warning and fall through to GenerateImage.
      LOG(WARNING) << *error_msg;
    }
  }
  //以下是/data和/system下面都没image的情况
  if (!can_compile) {//需要允许dex2oat
    *error_msg = "Not attempting to compile image because -Xnoimage-dex2oat";
    return nullptr;
  } else if (!dalvik_cache_exists) {//data/dalvik-cache/arm(arm64)目录必须存在
    *error_msg = StringPrintf("No place to put generated image.");
    return nullptr;
  } else if (!ImageCreationAllowed(is_global_cache, error_msg)) {//允许创建image
    return nullptr;
  } else if (!GenerateImage(cache_filename, image_isa, error_msg)) {
    //创建boot.art和boot.oat到/data/dalvik-cache/arm(arm64)下
    *error_msg = StringPrintf("Failed to generate image '%s': %s",
                              cache_filename.c_str(), error_msg->c_str());
    // We failed to create files, remove any possibly garbage output.
    // Since ImageCreationAllowed was true above, we are the zygote
    // and therefore the only process expected to generate these for
    // the device.
    PruneDexCache(image_isa);
    return nullptr;
  } else {
    // Check whether there is enough space left over after we have generated the image.
    if (!CheckSpace(cache_filename, error_msg)) {
      // No. Delete the generated image and try to run out of the dex files.
      PruneDexCache(image_isa);
      return nullptr;
    }

    // Note that we must not use the file descriptor associated with
    // ScopedFlock::GetFile to Init the image file. We want the file
    // descriptor (and the associated exclusive lock) to be released when
    // we leave Create.
    ScopedFlock image_lock;
    image_lock.Init(cache_filename.c_str(), error_msg);
    //使用创建的image初始化imagespace
    space = ImageSpace::Init(cache_filename.c_str(), image_location, true, error_msg);
    if (space == nullptr) {
      *error_msg = StringPrintf("Failed to load generated image '%s': %s",
                                cache_filename.c_str(), error_msg->c_str());
    }
    return space;
  }
}

  If not specified with the "-Xbootclasspath:" parameter, the bootclasspath at runtime is the value of the variable BOOTCLASSPATH. As you can see, GenerateImage is to generate various parameters of dex2oat and pass it to the exec command for execution.

18:46:07:223I/art     ( 1953): GenerateImage: /system/bin/dex2oat --image=/data/dalvik-cache/arm/system@framework@boot.art --dex-file=/system/framework/com.mstar.android.jar --dex-file=/system/framework/ngbj.jar --dex-file=/system/framework/tvm.jar --dex-file=/system/framework/core-libart.jar --dex-file=/system/framework/conscrypt.jar --dex-file=/system/framework/okhttp.jar --dex-file=/system/framework/core-junit.jar --dex-file=/system/framework/bouncycastle.jar --dex-file=/system/framework/ext.jar --dex-file=/system/framework/framework.jar --dex-file=/system/framework/telephony-common.jar --dex-file=/system/framework/voip-common.jar --dex-file=/system/framework/ims-common.jar --dex-file=/system/framework/mms-common.jar --dex-file=/system/framework/android.policy.jar --dex-file=/system/framework/apache-xml.jar --oat-file=/data/dalvik-cache/arm/system@framework@boot.oat --instruction-set=arm --instruction-set-features=div --base=0x70a72000 --runtime-arg -Xms64m --runtime-arg -Xmx64m --image-classes=/system/etc/preloaded-classes

/art/runtime/gc/space/image_space.cc

static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa,
                          std::string* error_msg) {
  const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
  std::vector<std::string> boot_class_path;
  Split(boot_class_path_string, ':', boot_class_path);
  if (boot_class_path.empty()) {
    *error_msg = "Failed to generate image because no boot class path specified";
    return false;
  }
  // We should clean up so we are more likely to have room for the image.
  if (Runtime::Current()->IsZygote()) {
    LOG(INFO) << "Pruning dalvik-cache since we are generating an image and will need to recompile";
    PruneDexCache(image_isa);
  }

  std::vector<std::string> arg_vector;

  std::string dex2oat(Runtime::Current()->GetCompilerExecutable());
  arg_vector.push_back(dex2oat);// /system/bin/dex2oat或者/system/bin/dex2oatd(debug版本)为第一个参数

  std::string image_option_string("--image=");
  image_option_string += image_filename;
  arg_vector.push_back(image_option_string);
  // /data/dalvik-cache/arm(arm64)/system@[email protected]为第二个参数
  //后续的几个参数为"--dex-file=xxx",xxx分别是bootclasspath的以冒号分隔的值
  for (size_t i = 0; i < boot_class_path.size(); i++) {
    arg_vector.push_back(std::string("--dex-file=") + boot_class_path[i]);
  }
  // 这个参数是"--oat-file=/data/dalvik-cache/arm(arm64)/system@[email protected]"
  std::string oat_file_option_string("--oat-file=");
  oat_file_option_string += ImageHeader::GetOatLocationFromImageLocation(image_filename);
  arg_vector.push_back(oat_file_option_string);

  //一些编译及指令集相关的参数
  Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&arg_vector);
  CHECK_EQ(image_isa, kRuntimeISA)
      << "We should always be generating an image for the current isa.";
  //选择一个合适的基址
  int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
                                                    ART_BASE_ADDRESS_MAX_DELTA);
  LOG(INFO) << "Using an offset of 0x" << std::hex << base_offset << " from default "
            << "art base address of 0x" << std::hex << ART_BASE_ADDRESS;
  arg_vector.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + base_offset));

  if (!kIsTargetBuild) {
    arg_vector.push_back("--host");
  }

  const std::vector<std::string>& compiler_options = Runtime::Current()->GetImageCompilerOptions();
  for (size_t i = 0; i < compiler_options.size(); ++i) {
    arg_vector.push_back(compiler_options[i].c_str());
  }

  std::string command_line(Join(arg_vector, ' '));
  LOG(INFO) << "GenerateImage: " << command_line;
  return Exec(arg_vector, error_msg);
}

  Back to the execution process of dex2oat. Simply put, it is to explain the parameters -> create an internal virtual machine -> compile and generate oat files -> generate image files.

/art/dex2oat/dex2oat.cc

  //这里创建dex2oat内部虚拟机
  if (!Dex2Oat::Create(&p_dex2oat,
                       runtime_options,
                       *compiler_options,
                       compiler_kind,
                       instruction_set,
                       instruction_set_features,
                       verification_results.get(),
                       &method_inliner_map,
                       thread_count)) {
    LOG(ERROR) << "Failed to create dex2oat";
    timings.EndTiming();
    oat_file->Erase();
    return EXIT_FAILURE;
  }
  ...
  //编译生成oat文件
  std::unique_ptr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
                                                                        android_root,
                                                                        is_host,
                                                                        dex_files,
                                                                        oat_file.get(),
                                                                        oat_location,
                                                                        bitcode_filename,
                                                                        image,
                                                                        image_classes,
                                                                        compiled_classes,
                                                                        dump_stats,
                                                                        dump_passes,
                                                                        timings,
                                                                        compiler_phases_timings,
                                                                        swap_fd,
                                                                        profile_file,
                                                                        key_value_store.get()));
   ...
   //生成image文件(需要指定"--image="参数)
   if (image) {
    TimingLogger::ScopedTiming t("dex2oat ImageWriter", &timings);
    bool image_creation_success = dex2oat->CreateImageFile(image_filename,
                                                           image_base,
                                                           oat_unstripped,
                                                           oat_location,
                                                           *compiler.get());

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325586836&siteId=291194637