dex文件加载流程(二)

书接上文dex文件加载流程(一)(gal2xy.github.io)

一、DexFile_openDexFileNative()

学习完上一篇文章,我们知道:不管是用PathClassLoader还是DexClassLoader加载dex文件,它们最终都会调用openDexFileNative()方法,该方法对应art/runtime/native/dalivk_system_DexFile.cc目录下的DexFile_openDexFileNative()方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//路径:art/runtime/native/dalivk_system_DexFile.cc
static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
jstring javaSourceName,
jstring javaOutputName ATTRIBUTE_UNUSED,
jint flags ATTRIBUTE_UNUSED,
jobject class_loader,
jobjectArray dex_elements) {
ScopedUtfChars sourceName(env, javaSourceName);
if (sourceName.c_str() == nullptr) {
return 0;
}

Runtime* const runtime = Runtime::Current();
ClassLinker* linker = runtime->GetClassLinker();
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs;
const OatFile* oat_file = nullptr;
//打开oat文件
dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
class_loader,
dex_elements,
/*out*/ &oat_file,
/*out*/ &error_msgs);

if (!dex_files.empty()) {
jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
if (array == nullptr) {
ScopedObjectAccess soa(env);
for (auto& dex_file : dex_files) {
if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
dex_file.release();
}
}
}
return array;
} else {
......
}
}

整个代码关键部分是调用了OpenDexFilesFromOat()方法。

二、OpenDexFilesFromOat()

对应代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//路径:/art/runtime/oat_file_manager.cc
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
const char* dex_location,
jobject class_loader,
jobjectArray dex_elements,
const OatFile** out_oat_file,
std::vector<std::string>* error_msgs) {
......
//获取oat_file_assistant
OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, !runtime->IsAotCompiler());

......

const OatFile* source_oat_file = nullptr;
//判断是否有生成 oat 文件
if (!oat_file_assistant.IsUpToDate()) {
//执行MakeUpToDate生成oat文件
switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) {
......
}
}

// 通过oat_file_assistant.GetBestOatFile()获取oat文件
std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());

if (oat_file != nullptr) {
bool accept_oat_file = !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
if (!accept_oat_file) {
// 冲突检测失败,打印警告
......
}

if (accept_oat_file) {
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
source_oat_file = RegisterOatFile(std::move(oat_file));
*out_oat_file = source_oat_file;
}
}

std::vector<std::unique_ptr<const DexFile>> dex_files;

// 从oat文件中加载dex文件
if (source_oat_file != nullptr) {
bool added_image_space = false;
//if语句这一大块的作用是给oat文件开辟镜像空间?
if (source_oat_file->IsExecutable()) {
std::unique_ptr<gc::space::ImageSpace> image_space = kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr;
if (image_space != nullptr) {
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> h_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
// Can not load app image without class loader.
if (h_loader != nullptr) {
std::string temp_error_msg;
// Add image space has a race condition since other threads could be reading from the
// spaces array.
{
ScopedThreadSuspension sts(self, kSuspended);
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseAddRemoveAppImageSpace,
gc::kCollectorTypeAddRemoveAppImageSpace);
ScopedSuspendAll ssa("Add image space");
runtime->GetHeap()->AddSpace(image_space.get());
}
{
ScopedTrace trace2(StringPrintf("Adding image space for location %s", dex_location));
added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
h_loader,
dex_elements,
dex_location,
/*out*/&dex_files,
/*out*/&temp_error_msg);
}
if (added_image_space) {
image_space.release();
} else {
LOG(INFO) << "Failed to add image file " << temp_error_msg;
dex_files.clear();
{
ScopedThreadSuspension sts(self, kSuspended);
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseAddRemoveAppImageSpace,
gc::kCollectorTypeAddRemoveAppImageSpace);
ScopedSuspendAll ssa("Remove image space");
runtime->GetHeap()->RemoveSpace(image_space.get());
}
}
}
}
}
if (!added_image_space) {
DCHECK(dex_files.empty());
//执行LoadDexFiles加载dex文件
dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
}
if (dex_files.empty()) {//获取dex文件失败
error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation());
}
}

//从oat文件中加载dex文件失败
if (dex_files.empty()) {
if (oat_file_assistant.HasOriginalDexFiles()) {//判断是否有原始dex文件
if (Runtime::Current()->IsDexFileFallbackEnabled()) {//是否启用了Dex文件的回退机制
static constexpr bool kVerifyChecksum = true;
//调用DexFile::Open()打开dex文件
if (!DexFile::Open(dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) {
LOG(WARNING) << error_msg;
error_msgs->push_back("Failed to open dex files from " + std::string(dex_location) + " because: " + error_msg);
}
} else {
error_msgs->push_back("Fallback mode disabled, skipping dex files.");
}
} else {
error_msgs->push_back("No original dex files found for dex location " + std::string(dex_location));
}
}

return dex_files;
}

以上代码大致流程如下:

  1. 通过oat_file_assistant()获取oat_file_assistant对象。
  2. 通过oat_file_assistant.IsUpToDate()判断是否有oat文件,没有调用oat_file_assistant.MakeUpToDate()则生成oat文件。
  3. 通过oat_file_assistant.GetBestOatFile()获取oat文件
  4. 调用oat_file_assistant.LoadDexFiles()从oat文件中加载dex文件。
  5. 如果从oat文件中加载dex文件失败,则调用DexFile::Open()加载dex文件。

2.1 GetBestOatFile()

先来看GetBestOatFile()方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//路径:/art/runtime/oat_file_assistant.cc
std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
return GetBestInfo().ReleaseFileForUse();
}

OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {

if (dex_parent_writable_) {//dex文件父目录可写则选择odex
return odex_;
}

if (oat_.IsUseable()) {//oat文件可用选择oat
return oat_;
}

if (odex_.Status() == kOatUpToDate) {//odex文件最新
return odex_;
}

if (HasOriginalDexFiles()) {//如果有原始dex文件
return oat_;
}
//最糟糕的情况,有啥就选啥
return (odex_.Status() == kOatCannotOpen) ? oat_ : odex_;
}

std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() {
if (Status() == kOatUpToDate) {//oat文件是最新的则释放文件以供直接使用
return ReleaseFile();
}

VLOG(oat) << "Oat File Assistant: No relocated oat file found," << " attempting to fall back to interpreting oat file instead.";

if (Status() == kOatRelocationOutOfDate && !IsExecutable()) {//oat需要重定位且不可执行
return ReleaseFile();
}

if (Status() == kOatRelocationOutOfDate) {//oat需要重定位
// 设置加载不可执行,以确保能够解析oat中的dex代码
oat_file_assistant_->load_executable_ = false;
Reset();
if (IsUseable()) {
CHECK(!IsExecutable());
return ReleaseFile();
}
}
return std::unique_ptr<OatFile>();
}

std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFile() {
file_released_ = true;
return std::move(file_);
}

GetBestOatFile()方法中,首先调用GetBestInfo()来获取最佳的oat文件(代码中的oat_oat_/art/runtime/oat_file_assistant.cc定义),然后调用ReleaseFileForUse()方法根据oat文件的状态释放文件以供使用。

2.1.1 Status()

GetBestInfo()中使用的Status()方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//路径:/art/runtime/oat_file_assistant.cc
OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
if (!status_attempted_) {
status_attempted_ = true;
//打开oat文件
const OatFile* file = GetFile();
if (file == nullptr) {
// Check to see if there is a vdex file we can make use of.
std::string error_msg;
std::string vdex_filename = GetVdexFilename(filename_);
//打开vdex文件
std::unique_ptr<VdexFile> vdex = VdexFile::Open(vdex_filename,
/*writeable*/false,
/*low_4gb*/false,
/*unquicken*/false,
&error_msg);
if (vdex == nullptr) {
status_ = kOatCannotOpen;
VLOG(oat) << "unable to open vdex file " << vdex_filename << ": " << error_msg;
} else {
//检查vdex文件是否是最新的
if (oat_file_assistant_->DexChecksumUpToDate(*vdex, &error_msg)) {
// The vdex file does not contain enough information to determine
// whether it is up to date with respect to the boot image, so we
// assume it is out of date.
VLOG(oat) << error_msg;
status_ = kOatBootImageOutOfDate;
} else {
status_ = kOatDexOutOfDate;
}
}
} else {
status_ = oat_file_assistant_->GivenOatFileStatus(*file);
VLOG(oat) << file->GetLocation() << " is " << status_
<< " with filter " << file->GetCompilerFilter();
}
}
return status_;
}

首先通过GetFile()方法打开oat文件,如果有vdex文件,则调用VdexFile::Open()方法打开vdex文件。

2.1.1.1 GetFile()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
CHECK(!file_released_) << "GetFile called after oat file released.";
if (!load_attempted_) {
load_attempted_ = true;
if (filename_provided_) {
std::string error_msg;
file_.reset(OatFile::Open(filename_.c_str(),
filename_.c_str(),
nullptr,
nullptr,
oat_file_assistant_->load_executable_,
/*low_4gb*/false,
oat_file_assistant_->dex_location_.c_str(),
&error_msg));
if (file_.get() == nullptr) {
VLOG(oat) << "OatFileAssistant test for existing oat file "
<< filename_ << ": " << error_msg;
}
}
}
return file_.get();
}

调用OatFile::Open()打开oat文件。

OatFile::Open()VdexFile::Open()等讲解oat和vdex文件加载时再详细分析,这里先打住,毕竟我们现在跟踪的是dex文件加载。

2.2 LoadDexFiles()

再来看LoadDexFiles()方法,该方法的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//路径:/art/runtime/oat_file_assistant.cc
std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
const OatFile& oat_file, const char* dex_location) {
std::vector<std::unique_ptr<const DexFile>> dex_files;

// Load the main dex file.
std::string error_msg;

//通过oat_file.GetOatDexFile()获得oat、dex文件
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_location, nullptr, &error_msg);
if (oat_dex_file == nullptr) {
LOG(WARNING) << error_msg;
return std::vector<std::unique_ptr<const DexFile>>();
}

//通过oat_dex_file->OpenDexFile()打开dex文件
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
return std::vector<std::unique_ptr<const DexFile>>();
}
//添加dex文件到dex_files数组中
dex_files.push_back(std::move(dex_file));

// 看是否存在多个dex文件
for (size_t i = 1; ; i++) {
std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
//从oat中获取第i个dex文件
oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
if (oat_dex_file == nullptr) {
// There are no more multidex entries to load.
break;
}
//打开第i个dex文件
dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
return std::vector<std::unique_ptr<const DexFile>>();
}
//添加dex文件到dex_files数组中
dex_files.push_back(std::move(dex_file));
}
return dex_files;
}

主要是GetOatDexFile()OpenDexFile(),这两个方法与OpenDexFilesFromOat()方法中调用的Open()方法同在oat_file.cc文件中。

2.2.1 GetOatDexFile()

先来看GetOatDexFile(),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//路径:/art/runtime/oat_file.cc
const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
const uint32_t* dex_location_checksum,
std::string* error_msg) const {
const OatFile::OatDexFile* oat_dex_file = nullptr;
StringPiece key(dex_location);
//通过key在oat_dex_files_map中找到dex文件所在位置
auto primary_it = oat_dex_files_.find(key);
if (primary_it != oat_dex_files_.end()) {//dex文件不唯一
oat_dex_file = primary_it->second;
DCHECK(oat_dex_file != nullptr);
} else {
// This dex_location is not one of the dex locations directly mentioned in the
// oat file. The correct lookup is via the canonical location but first see in
// the secondary_oat_dex_files_ whether we've looked up this location before.
MutexLock mu(Thread::Current(), secondary_lookup_lock_);
auto secondary_lb = secondary_oat_dex_files_.lower_bound(key);
if (secondary_lb != secondary_oat_dex_files_.end() && key == secondary_lb->first) {
oat_dex_file = secondary_lb->second; // May be null.
} else {
// We haven't seen this dex_location before, we must check the canonical location.
std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location);
if (dex_canonical_location != dex_location) {
StringPiece canonical_key(dex_canonical_location);
auto canonical_it = oat_dex_files_.find(canonical_key);
if (canonical_it != oat_dex_files_.end()) {
oat_dex_file = canonical_it->second;
} // else keep null.
} // else keep null.

// Copy the key to the string_cache_ and store the result in secondary map.
string_cache_.emplace_back(key.data(), key.length());
StringPiece key_copy(string_cache_.back());
secondary_oat_dex_files_.PutBefore(secondary_lb, key_copy, oat_dex_file);
}
}
//oat_dex_file获取失败
if (oat_dex_file == nullptr) {
if (error_msg != nullptr) {
std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location);
*error_msg = "Failed to find OatDexFile for DexFile " + std::string(dex_location) + " (canonical path " + dex_canonical_location + ") in OatFile " + GetLocation();
}
return nullptr;
}
//验证oat_dex_file文件的校验码
if (dex_location_checksum != nullptr && oat_dex_file->GetDexFileLocationChecksum() != *dex_location_checksum) {
if (error_msg != nullptr) {
std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location);
std::string checksum = StringPrintf("0x%08x", oat_dex_file->GetDexFileLocationChecksum());
std::string required_checksum = StringPrintf("0x%08x", *dex_location_checksum);
*error_msg = "OatDexFile for DexFile " + std::string(dex_location) + " (canonical path " + dex_canonical_location + ") in OatFile " + GetLocation() + " has checksum " + checksum + " but " + required_checksum + " was required";
}
return nullptr;
}
return oat_dex_file;
}

2.2.2 OpenDexFile()

然后是OpenDexFile(),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
ScopedTrace trace(__PRETTY_FUNCTION__);
static constexpr bool kVerify = false;
static constexpr bool kVerifyChecksum = false;
return DexFile::Open(dex_file_pointer_,
FileSize(),
dex_file_location_,
dex_file_location_checksum_,
this,
kVerify,
kVerifyChecksum,
error_msg);
}

直接调用DexFile::Open()方法实现。

通过上述分析可知,在OpenDexFilesFromOat()中,无论是调用LoadDexFiles()方法加载dex文件,还是调用DexFile::Open()加载dex文件,最终殊途同归,都调用DexFile::Open()方法,只不过参数个数不同。

在下一篇文章中将沿着DexFile::Open()方法继续分析dex文件加载流程。

三、总览图


参考:

[原创]菜鸟学8.1版本dex加载流程笔记–第一篇:oat_file_manager与oat_file_assistant-Android安全-看雪-安全社区|安全招聘|kanxue.com

入门ART虚拟机(2)——加载DEX文件续 - 简书 (jianshu.com)

http://aospxref.com/android-8.0.0_r36/


dex文件加载流程(二)
http://example.com/2023/11/20/Android安全/dex文件加载流程(二)/
作者
gla2xy
发布于
2023年11月20日
许可协议