1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "oat_file_assistant_context.h"
18
19 #include <memory>
20 #include <optional>
21 #include <string>
22 #include <vector>
23
24 #include "android-base/logging.h"
25 #include "android-base/stringprintf.h"
26 #include "arch/instruction_set.h"
27 #include "base/array_ref.h"
28 #include "base/file_utils.h"
29 #include "base/logging.h"
30 #include "base/mem_map.h"
31 #include "class_linker.h"
32 #include "dex/art_dex_file_loader.h"
33 #include "gc/heap.h"
34 #include "gc/space/image_space.h"
35
36 namespace art HIDDEN {
37
38 using ::android::base::StringPrintf;
39 using ::art::gc::space::ImageSpace;
40
OatFileAssistantContext(std::unique_ptr<OatFileAssistantContext::RuntimeOptions> runtime_options)41 OatFileAssistantContext::OatFileAssistantContext(
42 std::unique_ptr<OatFileAssistantContext::RuntimeOptions> runtime_options)
43 : runtime_options_(std::move(runtime_options)) {
44 DCHECK_EQ(runtime_options_->boot_class_path.size(),
45 runtime_options_->boot_class_path_locations.size());
46 DCHECK_IMPLIES(
47 runtime_options_->boot_class_path_files.has_value(),
48 runtime_options_->boot_class_path.size() == runtime_options_->boot_class_path_files->size());
49 // Opening dex files and boot images require MemMap.
50 MemMap::Init();
51 }
52
OatFileAssistantContext(Runtime * runtime)53 OatFileAssistantContext::OatFileAssistantContext(Runtime* runtime)
54 : OatFileAssistantContext(std::make_unique<OatFileAssistantContext::RuntimeOptions>(
55 OatFileAssistantContext::RuntimeOptions{
56 .image_locations = runtime->GetImageLocations(),
57 .boot_class_path = runtime->GetBootClassPath(),
58 .boot_class_path_locations = runtime->GetBootClassPathLocations(),
59 .boot_class_path_files = !runtime->GetBootClassPathFiles().empty() ?
60 runtime->GetBootClassPathFiles() :
61 std::optional<ArrayRef<File>>(),
62 .deny_art_apex_data_files = runtime->DenyArtApexDataFiles(),
63 })) {
64 // Fetch boot image info from the runtime.
65 std::vector<BootImageInfo>& boot_image_info_list =
66 boot_image_info_list_by_isa_[kRuntimeQuickCodeISA];
67 for (const ImageSpace* image_space : runtime->GetHeap()->GetBootImageSpaces()) {
68 // We only need the checksum of the first component for each boot image. They are in image
69 // spaces that have a non-zero component count.
70 if (image_space->GetComponentCount() > 0) {
71 BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
72 boot_image_info.component_count = image_space->GetComponentCount();
73 ImageSpace::AppendImageChecksum(image_space->GetComponentCount(),
74 image_space->GetImageHeader().GetImageChecksum(),
75 &boot_image_info.checksum);
76 }
77 }
78
79 // Fetch BCP checksums from the runtime.
80 size_t bcp_index = 0;
81 std::vector<std::string>* current_bcp_checksums = nullptr;
82 const std::vector<const DexFile*>& bcp_dex_files = runtime->GetClassLinker()->GetBootClassPath();
83 for (size_t i = 0; i < bcp_dex_files.size();) {
84 uint32_t checksum = DexFileLoader::GetMultiDexChecksum(bcp_dex_files, &i);
85 DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
86 current_bcp_checksums = &bcp_checksums_by_index_[bcp_index++];
87 DCHECK_NE(current_bcp_checksums, nullptr);
88 current_bcp_checksums->push_back(StringPrintf("/%08x", checksum));
89 }
90 DCHECK_EQ(bcp_index, runtime_options_->boot_class_path.size());
91
92 // Fetch APEX versions from the runtime.
93 apex_versions_ = runtime->GetApexVersions();
94 }
95
GetRuntimeOptions() const96 const OatFileAssistantContext::RuntimeOptions& OatFileAssistantContext::GetRuntimeOptions() const {
97 return *runtime_options_;
98 }
99
FetchAll(std::string * error_msg)100 bool OatFileAssistantContext::FetchAll(std::string* error_msg) {
101 std::vector<InstructionSet> isas = GetSupportedInstructionSets(error_msg);
102 if (isas.empty()) {
103 return false;
104 }
105 for (InstructionSet isa : isas) {
106 GetBootImageInfoList(isa);
107 }
108 for (size_t i = 0; i < runtime_options_->boot_class_path.size(); i++) {
109 if (GetBcpChecksums(i, error_msg) == nullptr) {
110 return false;
111 }
112 }
113 GetApexVersions();
114 return true;
115 }
116
117 const std::vector<OatFileAssistantContext::BootImageInfo>&
GetBootImageInfoList(InstructionSet isa)118 OatFileAssistantContext::GetBootImageInfoList(InstructionSet isa) {
119 if (auto it = boot_image_info_list_by_isa_.find(isa); it != boot_image_info_list_by_isa_.end()) {
120 return it->second;
121 }
122
123 ImageSpace::BootImageLayout layout(
124 ArrayRef<const std::string>(runtime_options_->image_locations),
125 ArrayRef<const std::string>(runtime_options_->boot_class_path),
126 ArrayRef<const std::string>(runtime_options_->boot_class_path_locations),
127 runtime_options_->boot_class_path_files.value_or(ArrayRef<File>()),
128 /*boot_class_path_image_files=*/{},
129 /*boot_class_path_vdex_files=*/{},
130 /*boot_class_path_oat_files=*/{},
131 &GetApexVersions());
132
133 std::string error_msg;
134 if (!layout.LoadFromSystem(isa, /*allow_in_memory_compilation=*/false, &error_msg)) {
135 // At this point, `layout` contains nothing.
136 VLOG(oat) << "Some error occurred when loading boot images for oat file validation: "
137 << error_msg;
138 // Create an empty entry so that we don't have to retry when the function is called again.
139 return boot_image_info_list_by_isa_[isa];
140 }
141
142 std::vector<BootImageInfo>& boot_image_info_list = boot_image_info_list_by_isa_[isa];
143 for (const ImageSpace::BootImageLayout::ImageChunk& chunk : layout.GetChunks()) {
144 BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
145 boot_image_info.component_count = chunk.component_count;
146 ImageSpace::AppendImageChecksum(
147 chunk.component_count, chunk.checksum, &boot_image_info.checksum);
148 }
149 return boot_image_info_list;
150 }
151
GetBcpChecksums(size_t bcp_index,std::string * error_msg)152 const std::vector<std::string>* OatFileAssistantContext::GetBcpChecksums(size_t bcp_index,
153 std::string* error_msg) {
154 DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
155
156 if (auto it = bcp_checksums_by_index_.find(bcp_index); it != bcp_checksums_by_index_.end()) {
157 return &it->second;
158 }
159
160 std::optional<ArrayRef<File>> bcp_files = runtime_options_->boot_class_path_files;
161 File noFile;
162 File* file = bcp_files.has_value() ? &(*bcp_files)[bcp_index] : &noFile;
163 ArtDexFileLoader dex_loader(file, runtime_options_->boot_class_path[bcp_index]);
164 std::optional<uint32_t> checksum;
165 bool ok = dex_loader.GetMultiDexChecksum(&checksum, error_msg);
166 if (!ok) {
167 return nullptr;
168 }
169
170 DCHECK(checksum.has_value());
171 std::vector<std::string>& bcp_checksums = bcp_checksums_by_index_[bcp_index];
172 if (checksum.has_value()) {
173 bcp_checksums.push_back(StringPrintf("/%08x", checksum.value()));
174 }
175 return &bcp_checksums;
176 }
177
GetApexVersions()178 const std::string& OatFileAssistantContext::GetApexVersions() {
179 if (apex_versions_.has_value()) {
180 return apex_versions_.value();
181 }
182
183 apex_versions_ = Runtime::GetApexVersions(
184 ArrayRef<const std::string>(runtime_options_->boot_class_path_locations));
185 return apex_versions_.value();
186 }
187
188 } // namespace art
189