static int patchoat_oat()

in patchoat/patchoat.cc [1026:1228]


static int patchoat_oat(TimingLogger& timings,
                        InstructionSet isa,
                        const std::string& patched_image_location,
                        off_t base_delta,
                        bool base_delta_set,
                        int input_oat_fd,
                        const std::string& input_oat_location,
                        std::string input_oat_filename,
                        bool have_input_oat,
                        int output_oat_fd,
                        std::string output_oat_filename,
                        bool have_output_oat,
                        bool lock_output,
                        bool debug) {
  {
    // Only 1 of these may be set.
    uint32_t cnt = 0;
    cnt += (base_delta_set) ? 1 : 0;
    cnt += (!patched_image_location.empty()) ? 1 : 0;
    if (cnt > 1) {
      Usage("Only one of --base-offset-delta or --patched-image-location may be used.");
    } else if (cnt == 0) {
      Usage("Must specify --base-offset-delta or --patched-image-location.");
    }
  }

  if (!have_input_oat || !have_output_oat) {
    Usage("Both input and output oat must be supplied to patch an app odex.");
  }

  if (!input_oat_location.empty()) {
    if (!LocationToFilename(input_oat_location, isa, &input_oat_filename)) {
      Usage("Unable to find filename for input oat location %s", input_oat_location.c_str());
    }
    if (debug) {
      LOG(INFO) << "Using input-oat-file " << input_oat_filename;
    }
  }

  bool match_delta = false;
  if (!patched_image_location.empty()) {
    std::string system_filename;
    bool has_system = false;
    std::string cache_filename;
    bool has_cache = false;
    bool has_android_data_unused = false;
    bool is_global_cache = false;
    if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
                                                  &system_filename, &has_system, &cache_filename,
                                                  &has_android_data_unused, &has_cache,
                                                  &is_global_cache)) {
      Usage("Unable to determine image file for location %s", patched_image_location.c_str());
    }
    std::string patched_image_filename;
    if (has_cache) {
      patched_image_filename = cache_filename;
    } else if (has_system) {
      LOG(WARNING) << "Only image file found was in /system for image location "
          << patched_image_location;
      patched_image_filename = system_filename;
    } else {
      Usage("Unable to determine image file for location %s", patched_image_location.c_str());
    }
    if (debug) {
      LOG(INFO) << "Using patched-image-file " << patched_image_filename;
    }

    base_delta_set = true;
    match_delta = true;
    std::string error_msg;
    if (!ReadBaseDelta(patched_image_filename.c_str(), &base_delta, &error_msg)) {
      Usage(error_msg.c_str(), patched_image_filename.c_str());
    }
  }

  if (!IsAligned<kPageSize>(base_delta)) {
    Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize);
  }

  // Do we need to cleanup output files if we fail?
  bool new_oat_out = false;

  std::unique_ptr<File> input_oat;
  std::unique_ptr<File> output_oat;

  if (input_oat_fd != -1) {
    if (input_oat_filename.empty()) {
      input_oat_filename = "input-oat-file";
    }
    input_oat.reset(new File(input_oat_fd, input_oat_filename, false));
    if (input_oat_fd == output_oat_fd) {
      input_oat.get()->DisableAutoClose();
    }
    if (input_oat == nullptr) {
      // Unlikely, but ensure exhaustive logging in non-0 exit code case
      LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
      return EXIT_FAILURE;
    }
  } else {
    CHECK(!input_oat_filename.empty());
    input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
    if (input_oat == nullptr) {
      int err = errno;
      LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
          << ": " << strerror(err) << "(" << err << ")";
      return EXIT_FAILURE;
    }
  }

  std::string error_msg;
  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE, &error_msg));
  if (elf.get() == nullptr) {
    LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
    return EXIT_FAILURE;
  }
  if (!elf->HasSection(".text.oat_patches")) {
    LOG(ERROR) << "missing oat patch section in input oat file " << input_oat->GetPath();
    return EXIT_FAILURE;
  }

  if (output_oat_fd != -1) {
    if (output_oat_filename.empty()) {
      output_oat_filename = "output-oat-file";
    }
    output_oat.reset(new File(output_oat_fd, output_oat_filename, true));
    if (output_oat == nullptr) {
      // Unlikely, but ensure exhaustive logging in non-0 exit code case
      LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
    }
  } else {
    CHECK(!output_oat_filename.empty());
    output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
    if (output_oat == nullptr) {
      int err = errno;
      LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
          << ": " << strerror(err) << "(" << err << ")";
    }
  }

  // TODO: get rid of this.
  auto cleanup = [&output_oat_filename, &new_oat_out](bool success) {
    if (!success) {
      if (new_oat_out) {
        CHECK(!output_oat_filename.empty());
        unlink(output_oat_filename.c_str());
      }
    }

    if (kIsDebugBuild) {
      LOG(INFO) << "Cleaning up.. success? " << success;
    }
  };

  if (output_oat.get() == nullptr) {
    cleanup(false);
    return EXIT_FAILURE;
  }

  if (match_delta) {
    // Figure out what the current delta is so we can match it to the desired delta.
    off_t current_delta = 0;
    if (!ReadOatPatchDelta(elf.get(), &current_delta, &error_msg)) {
      LOG(ERROR) << "Unable to get current delta: " << error_msg;
      cleanup(false);
      return EXIT_FAILURE;
    }
    // Before this line base_delta is the desired final delta. We need it to be the actual amount to
    // change everything by. We subtract the current delta from it to make it this.
    base_delta -= current_delta;
    if (!IsAligned<kPageSize>(base_delta)) {
      LOG(ERROR) << "Given image file was relocated by an illegal delta";
      cleanup(false);
      return false;
    }
  }

  if (debug) {
    LOG(INFO) << "moving offset by " << base_delta
        << " (0x" << std::hex << base_delta << ") bytes or "
        << std::dec << (base_delta/kPageSize) << " pages.";
  }

  ScopedFlock output_oat_lock;
  if (lock_output) {
    if (!output_oat_lock.Init(output_oat.get(), &error_msg)) {
      LOG(ERROR) << "Unable to lock output oat " << output_oat->GetPath() << ": " << error_msg;
      cleanup(false);
      return EXIT_FAILURE;
    }
  }

  TimingLogger::ScopedTiming pt("patch oat", &timings);
  bool ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
                             output_oat_fd >= 0,  // was it opened from FD?
                             new_oat_out);
  ret = FinishFile(output_oat.get(), ret);

  if (kIsDebugBuild) {
    LOG(INFO) << "Exiting with return ... " << ret;
  }
  cleanup(ret);
  return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}