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 = boot_image_info_list_by_isa_[kRuntimeISA];
66 for (const ImageSpace* image_space : runtime->GetHeap()->GetBootImageSpaces()) {
67 // We only need the checksum of the first component for each boot image. They are in image
68 // spaces that have a non-zero component count.
69 if (image_space->GetComponentCount() > 0) {
70 BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
71 boot_image_info.component_count = image_space->GetComponentCount();
72 ImageSpace::AppendImageChecksum(image_space->GetComponentCount(),
73 image_space->GetImageHeader().GetImageChecksum(),
74 &boot_image_info.checksum);
75 }
76 }
77
78 // Fetch BCP checksums from the runtime.
79 size_t bcp_index = 0;
80 std::vector<std::string>* current_bcp_checksums = nullptr;
81 const std::vector<const DexFile*>& bcp_dex_files = runtime->GetClassLinker()->GetBootClassPath();
82 for (size_t i = 0; i < bcp_dex_files.size();) {
83 uint32_t checksum = DexFileLoader::GetMultiDexChecksum(bcp_dex_files, &i);
84 DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
85 current_bcp_checksums = &bcp_checksums_by_index_[bcp_index++];
86 DCHECK_NE(current_bcp_checksums, nullptr);
87 current_bcp_checksums->push_back(StringPrintf("/%08x", checksum));
88 }
89 DCHECK_EQ(bcp_index, runtime_options_->boot_class_path.size());
90
91 // Fetch APEX versions from the runtime.
92 apex_versions_ = runtime->GetApexVersions();
93 }
94
GetRuntimeOptions() const95 const OatFileAssistantContext::RuntimeOptions& OatFileAssistantContext::GetRuntimeOptions() const {
96 return *runtime_options_;
97 }
98
FetchAll(std::string * error_msg)99 bool OatFileAssistantContext::FetchAll(std::string* error_msg) {
100 std::vector<InstructionSet> isas = GetSupportedInstructionSets(error_msg);
101 if (isas.empty()) {
102 return false;
103 }
104 for (InstructionSet isa : isas) {
105 GetBootImageInfoList(isa);
106 }
107 for (size_t i = 0; i < runtime_options_->boot_class_path.size(); i++) {
108 if (GetBcpChecksums(i, error_msg) == nullptr) {
109 return false;
110 }
111 }
112 GetApexVersions();
113 return true;
114 }
115
116 const std::vector<OatFileAssistantContext::BootImageInfo>&
GetBootImageInfoList(InstructionSet isa)117 OatFileAssistantContext::GetBootImageInfoList(InstructionSet isa) {
118 if (auto it = boot_image_info_list_by_isa_.find(isa); it != boot_image_info_list_by_isa_.end()) {
119 return it->second;
120 }
121
122 ImageSpace::BootImageLayout layout(
123 ArrayRef<const std::string>(runtime_options_->image_locations),
124 ArrayRef<const std::string>(runtime_options_->boot_class_path),
125 ArrayRef<const std::string>(runtime_options_->boot_class_path_locations),
126 runtime_options_->boot_class_path_files.value_or(ArrayRef<File>()),
127 /*boot_class_path_image_files=*/{},
128 /*boot_class_path_vdex_files=*/{},
129 /*boot_class_path_oat_files=*/{},
130 &GetApexVersions());
131
132 std::string error_msg;
133 if (!layout.LoadFromSystem(isa, /*allow_in_memory_compilation=*/false, &error_msg)) {
134 // At this point, `layout` contains nothing.
135 VLOG(oat) << "Some error occurred when loading boot images for oat file validation: "
136 << error_msg;
137 // Create an empty entry so that we don't have to retry when the function is called again.
138 return boot_image_info_list_by_isa_[isa];
139 }
140
141 std::vector<BootImageInfo>& boot_image_info_list = boot_image_info_list_by_isa_[isa];
142 for (const ImageSpace::BootImageLayout::ImageChunk& chunk : layout.GetChunks()) {
143 BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
144 boot_image_info.component_count = chunk.component_count;
145 ImageSpace::AppendImageChecksum(
146 chunk.component_count, chunk.checksum, &boot_image_info.checksum);
147 }
148 return boot_image_info_list;
149 }
150
GetBcpChecksums(size_t bcp_index,std::string * error_msg)151 const std::vector<std::string>* OatFileAssistantContext::GetBcpChecksums(size_t bcp_index,
152 std::string* error_msg) {
153 DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
154
155 if (auto it = bcp_checksums_by_index_.find(bcp_index); it != bcp_checksums_by_index_.end()) {
156 return &it->second;
157 }
158
159 std::optional<ArrayRef<File>> bcp_files = runtime_options_->boot_class_path_files;
160 File noFile;
161 File* file = bcp_files.has_value() ? &(*bcp_files)[bcp_index] : &noFile;
162 ArtDexFileLoader dex_loader(file, runtime_options_->boot_class_path[bcp_index]);
163 std::optional<uint32_t> checksum;
164 bool ok = dex_loader.GetMultiDexChecksum(&checksum, error_msg);
165 if (!ok) {
166 return nullptr;
167 }
168
169 DCHECK(checksum.has_value());
170 std::vector<std::string>& bcp_checksums = bcp_checksums_by_index_[bcp_index];
171 if (checksum.has_value()) {
172 bcp_checksums.push_back(StringPrintf("/%08x", checksum.value()));
173 }
174 return &bcp_checksums;
175 }
176
GetApexVersions()177 const std::string& OatFileAssistantContext::GetApexVersions() {
178 if (apex_versions_.has_value()) {
179 return apex_versions_.value();
180 }
181
182 apex_versions_ = Runtime::GetApexVersions(
183 ArrayRef<const std::string>(runtime_options_->boot_class_path_locations));
184 return apex_versions_.value();
185 }
186
187 } // namespace art
188