// File format:
// VdexFile::Header fixed-length header
//
// DEX[0] array of the input DEX files
// DEX[1] the bytecode may have been quickened
// ...
// DEX[D]
// QuickeningInfo
// uint8[] quickening data
// unaligned_uint32_t[2][] table of offsets pair:
// uint32_t[0] contains code_item_offset
// uint32_t[1] contains quickening data offset from the start
// of QuickeningInfo
// unalgined_uint32_t[D] start offsets (from the start of QuickeningInfo) in previous
// table for each dex file
4.下面是 Load 函数,最终调用了Dlopen加载oat,获得dlopen_handle_
[C++] 纯文本查看复制代码
bool DlOpenOatFile::Load(const std::string& elf_filename,
uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
std::string* error_msg) {
// Use dlopen only when flagged to do so, and when it's OK to load things executable.
// TODO: Also try when not executable? The issue here could be re-mapping as writable (as
// !executable is a sign that we may want to patch), which may not be allowed for
// various reasons.
if (!kUseDlopen) {
*error_msg = "DlOpen is disabled.";
return false;
}
if (low_4gb) {
*error_msg = "DlOpen does not support low 4gb loading.";
return false;
}
if (writable) {
*error_msg = "DlOpen does not support writable loading.";
return false;
}
if (!executable) {
*error_msg = "DlOpen does not support non-executable loading.";
return false;
}
// dlopen always returns the same library if it is already opened on the host. For this reason
// we only use dlopen if we are the target or we do not already have the dex file opened. Having
// the same library loaded multiple times at different addresses is required for class unloading
// and for having dex caches arrays in the .bss section.
if (!kIsTargetBuild) {
if (!kUseDlopenOnHost) {
*error_msg = "DlOpen disabled for host.";
return false;
}
}
bool success = Dlopen(elf_filename, oat_file_begin, error_msg);//调用Dlopen加载oat,获得dlopen_handle_
DCHECK(dlopen_handle_ != nullptr || !success);
return success;
}
看一下Dlopen,最终调用了android_dlopen_ext或者dlopen
[C++] 纯文本查看复制代码
bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
uint8_t* oat_file_begin,
std::string* error_msg) {
#ifdef __APPLE__
// The dl_iterate_phdr syscall is missing. There is similar API on OSX,
// but let's fallback to the custom loading code for the time being.
UNUSED(elf_filename, oat_file_begin);
*error_msg = "Dlopen unsupported on Mac.";
return false;
#else
{
UniqueCPtr<char> absolute_path(realpath(elf_filename.c_str(), nullptr));
if (absolute_path == nullptr) {
*error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
return false;
}
#ifdef ART_TARGET_ANDROID
android_dlextinfo extinfo = {};
// typedef struct {
// uint64_t flags;
// void* reserved_addr;
// size_t reserved_size;
// int relro_fd;
// int library_fd;
// } android_dlextinfo;
extinfo.flags = ANDROID_DLEXT_FORCE_LOAD | // Force-load, don't reuse handle
// (open oat files multiple
// times).
ANDROID_DLEXT_FORCE_FIXED_VADDR; // Take a non-zero vaddr as absolute
// (non-pic boot image).
if (oat_file_begin != nullptr) { //
extinfo.flags |= ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS; // Use the requested addr if
extinfo.reserved_addr = oat_file_begin; // vaddr = 0.
} // (pic boot image).
dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);//这里oat_file_begin不为空如果调用android_dlopen_ext打开获得dlopen_handle_,在/bionic/libdl/libdl.c里
#else
UNUSED(oat_file_begin);
static_assert(!kIsTargetBuild || kIsTargetLinux, "host_dlopen_handles_ will leak handles");
MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_);
dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);//如果没有oat_file_begin,直接调用dlopen从路径加载获得dlopen_handle_
if (dlopen_handle_ != nullptr) {
if (!host_dlopen_handles_.insert(dlopen_handle_).second) {//把dlopen_handle_插入host_dlopen_handles_中
dlclose(dlopen_handle_);
dlopen_handle_ = nullptr;
*error_msg = StringPrintf("host dlopen re-opened '%s'", elf_filename.c_str());
return false;
}
}
#endif // ART_TARGET_ANDROID
}
if (dlopen_handle_ == nullptr) {
*error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror());
return false;
}
return true;
#endif
}
void DlOpenOatFile::PreSetup(const std::string& elf_filename) {//Ask the linker where it mmaped the file and notify our mmap wrapper of the regions
#ifdef __APPLE__
UNUSED(elf_filename);
LOG(FATAL) << "Should not reach here.";
UNREACHABLE();
#else
struct dl_iterate_context {
static int callback(struct dl_phdr_info *info, size_t /* size */, void *data) {
/*
struct dl_phdr_info {
ElfW(Addr) dlpi_addr;
const char* dlpi_name;
const ElfW(Phdr)* dlpi_phdr;
ElfW(Half) dlpi_phnum;}
*/
auto* context = reinterpret_cast<dl_iterate_context*>(data);
context->shared_objects_seen++; //这里是shared_objects_seen自增了,跟上面shared_objects_before对比
if (context->shared_objects_seen < context->shared_objects_before) { //只要shared_objects_seen小于shared_objects_before,就说明elf还没有遍历完,如果其他线程卸载了一个elf,这有可能出问题
// We haven't been called yet for anything we haven't seen before. Just continue.
// Note: this is aggressively optimistic. If another thread was unloading a library,
// we may miss out here. However, this does not happen often in practice.
return 0;
}
// See whether this callback corresponds to the file which we have just loaded.
bool contains_begin = false; // 一直遍历直到contains_begin也就是包含begin_,这个begin_通过函数Begin()获得也就是oat_file的begin_
for (int i = 0; i < info->dlpi_phnum; i++) {
if (info->dlpi_phdr[i].p_type == PT_LOAD) {
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
size_t memsz = info->dlpi_phdr[i].p_memsz;
if (vaddr <= context->begin_ && context->begin_ < vaddr + memsz) {
contains_begin = true;
break;
}
}
}
// Add dummy mmaps for this file.
if (contains_begin) { //一旦 contains_begin = true,遍历dlpi_phdr当p_type == PT_LOAD时通过MemMap::MapDummy根据segment 的vaddr, memsz装载segment到内存
for (int i = 0; i < info->dlpi_phnum; i++) {
if (info->dlpi_phdr[i].p_type == PT_LOAD) {
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
size_t memsz = info->dlpi_phdr[i].p_memsz;
MemMap* mmap = MemMap::MapDummy(info->dlpi_name, vaddr, memsz);
context->dlopen_mmaps_->push_back(std::unique_ptr<MemMap>(mmap));//把新建的mmap添加进dlopen_mmaps_
}
}
return 1; // Stop iteration and return 1 from dl_iterate_phdr. //结束循环
}
return 0; // Continue iteration and return 0 from dl_iterate_phdr when finished.
}
const uint8_t* const begin_; //begin_通过函数Begin()获得也就是oat_file的begin_
std::vector<std::unique_ptr<MemMap>>* const dlopen_mmaps_;
const size_t shared_objects_before; //上文PreLoad的dl_iterate_context通过dl_iterate_phdr遍历获得的加载的elf对象的个数
size_t shared_objects_seen; //本dl_iterate_context内部通过dl_iterate_phdr遍历获得的加载的elf对象的个数计数
};//到这一行struct dl_iterate_context结束
dl_iterate_context context = { Begin(), &dlopen_mmaps_, shared_objects_before_, 0}; //声明一个context
if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) { //这里调用dl_iterate_phdr,这个callback回调函数完成了oat_file各个segment的mmap
// Hm. Maybe our optimization went wrong. Try another time with shared_objects_before == 0
// before giving up. This should be unusual.
VLOG(oat) << "Need a second run in PreSetup, didn't find with shared_objects_before="
<< shared_objects_before_;
dl_iterate_context context0 = { Begin(), &dlopen_mmaps_, 0, 0};
if (dl_iterate_phdr(dl_iterate_context::callback, &context0) == 0) {
// OK, give up and print an error.
PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but cannot find its mmaps.";
}
}
#endif
}