1 /* 2 * Copyright (C) 2017 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 #define ATRACE_TAG ATRACE_TAG_RESOURCES 18 19 #include "androidfw/Idmap.h" 20 21 #include "android-base/logging.h" 22 #include "android-base/stringprintf.h" 23 #include "androidfw/misc.h" 24 #include "androidfw/ResourceTypes.h" 25 #include "androidfw/Util.h" 26 #include "utils/ByteOrder.h" 27 #include "utils/Trace.h" 28 29 #ifdef _WIN32 30 #ifdef ERROR 31 #undef ERROR 32 #endif 33 #endif 34 35 using ::android::base::StringPrintf; 36 37 namespace android { 38 39 // See frameworks/base/cmds/idmap2/include/idmap2/Idmap.h for full idmap file format specification. 40 struct Idmap_header { 41 // Always 0x504D4449 ('IDMP') 42 uint32_t magic; 43 uint32_t version; 44 45 uint32_t target_crc32; 46 uint32_t overlay_crc32; 47 48 uint32_t fulfilled_policies; 49 uint32_t enforce_overlayable; 50 51 // overlay_path, target_path, and other string values encoded in the idmap header and read and 52 // stored in separate structures. This allows the idmap header data to be casted to this struct 53 // without having to read/store each header entry separately. 54 }; 55 56 struct Idmap_data_header { 57 uint32_t target_entry_count; 58 uint32_t target_inline_entry_count; 59 uint32_t overlay_entry_count; 60 61 uint32_t string_pool_index_offset; 62 }; 63 64 struct Idmap_target_entry { 65 uint32_t target_id; 66 uint32_t overlay_id; 67 }; 68 69 struct Idmap_target_entry_inline { 70 uint32_t target_id; 71 Res_value value; 72 }; 73 74 struct Idmap_overlay_entry { 75 uint32_t overlay_id; 76 uint32_t target_id; 77 }; 78 79 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) 80 : data_header_(loaded_idmap->data_header_), 81 idmap_string_pool_(loaded_idmap->string_pool_.get()) { }; 82 83 OverlayStringPool::~OverlayStringPool() { 84 uninit(); 85 } 86 87 base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const { 88 const size_t offset = dtohl(data_header_->string_pool_index_offset); 89 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { 90 return idmap_string_pool_->stringAt(idx - offset); 91 } 92 93 return ResStringPool::stringAt(idx); 94 } 95 96 base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const { 97 const size_t offset = dtohl(data_header_->string_pool_index_offset); 98 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { 99 return idmap_string_pool_->string8At(idx - offset); 100 } 101 102 return ResStringPool::string8At(idx); 103 } 104 105 size_t OverlayStringPool::size() const { 106 return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U); 107 } 108 109 OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header, 110 const Idmap_overlay_entry* entries, 111 uint8_t target_assigned_package_id) 112 : data_header_(data_header), 113 entries_(entries), 114 target_assigned_package_id_(target_assigned_package_id) { }; 115 116 status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const { 117 const Idmap_overlay_entry* first_entry = entries_; 118 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count); 119 auto entry = std::lower_bound(first_entry, end_entry, *resId, 120 [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) { 121 return dtohl(e1.overlay_id) < overlay_id; 122 }); 123 124 if (entry == end_entry || dtohl(entry->overlay_id) != *resId) { 125 // A mapping for the target resource id could not be found. 126 return DynamicRefTable::lookupResourceId(resId); 127 } 128 129 *resId = (0x00FFFFFFU & dtohl(entry->target_id)) 130 | (((uint32_t) target_assigned_package_id_) << 24U); 131 return NO_ERROR; 132 } 133 134 status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const { 135 return DynamicRefTable::lookupResourceId(resId); 136 } 137 138 IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, 139 const Idmap_target_entry* entries, 140 const Idmap_target_entry_inline* inline_entries, 141 uint8_t target_assigned_package_id, 142 const OverlayDynamicRefTable* overlay_ref_table) 143 : data_header_(data_header), 144 entries_(entries), 145 inline_entries_(inline_entries), 146 target_assigned_package_id_(target_assigned_package_id), 147 overlay_ref_table_(overlay_ref_table) { } 148 149 IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { 150 if ((target_res_id >> 24U) != target_assigned_package_id_) { 151 // The resource id must have the same package id as the target package. 152 return {}; 153 } 154 155 // The resource ids encoded within the idmap are build-time resource ids so do not consider the 156 // package id when determining if the resource in the target package is overlaid. 157 target_res_id &= 0x00FFFFFFU; 158 159 // Check if the target resource is mapped to an overlay resource. 160 auto first_entry = entries_; 161 auto end_entry = entries_ + dtohl(data_header_->target_entry_count); 162 auto entry = std::lower_bound(first_entry, end_entry, target_res_id, 163 [](const Idmap_target_entry& e, const uint32_t target_id) { 164 return (0x00FFFFFFU & dtohl(e.target_id)) < target_id; 165 }); 166 167 if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) { 168 uint32_t overlay_resource_id = dtohl(entry->overlay_id); 169 // Lookup the resource without rewriting the overlay resource id back to the target resource id 170 // being looked up. 171 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id); 172 return Result(overlay_resource_id); 173 } 174 175 // Check if the target resources is mapped to an inline table entry. 176 auto first_inline_entry = inline_entries_; 177 auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count); 178 auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id, 179 [](const Idmap_target_entry_inline& e, 180 const uint32_t target_id) { 181 return (0x00FFFFFFU & dtohl(e.target_id)) < target_id; 182 }); 183 184 if (inline_entry != end_inline_entry && 185 (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) { 186 return Result(inline_entry->value); 187 } 188 return {}; 189 } 190 191 namespace { 192 template <typename T> 193 const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const std::string& label, 194 size_t count = 1) { 195 if (!util::IsFourByteAligned(*in_out_data_ptr)) { 196 LOG(ERROR) << "Idmap " << label << " is not word aligned."; 197 return {}; 198 } 199 if ((*in_out_size / sizeof(T)) < count) { 200 LOG(ERROR) << "Idmap too small for the number of " << label << " entries (" 201 << count << ")."; 202 return nullptr; 203 } 204 auto data_ptr = *in_out_data_ptr; 205 const size_t read_size = sizeof(T) * count; 206 *in_out_data_ptr += read_size; 207 *in_out_size -= read_size; 208 return reinterpret_cast<const T*>(data_ptr); 209 } 210 211 std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size, 212 const std::string& label) { 213 const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label + " length"); 214 if (len == nullptr) { 215 return {}; 216 } 217 const auto* data = ReadType<char>(in_out_data_ptr, in_out_size, label, *len); 218 if (data == nullptr) { 219 return {}; 220 } 221 // Strings are padded to the next 4 byte boundary. 222 const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U; 223 for (uint32_t i = 0; i < padding_size; i++) { 224 if (**in_out_data_ptr != 0) { 225 LOG(ERROR) << " Idmap padding of " << label << " is non-zero."; 226 return {}; 227 } 228 *in_out_data_ptr += sizeof(uint8_t); 229 *in_out_size -= sizeof(uint8_t); 230 } 231 return std::string_view(data, *len); 232 } 233 } // namespace 234 235 LoadedIdmap::LoadedIdmap(std::string&& idmap_path, 236 const Idmap_header* header, 237 const Idmap_data_header* data_header, 238 const Idmap_target_entry* target_entries, 239 const Idmap_target_entry_inline* target_inline_entries, 240 const Idmap_overlay_entry* overlay_entries, 241 std::unique_ptr<ResStringPool>&& string_pool, 242 std::string_view overlay_apk_path, 243 std::string_view target_apk_path) 244 : header_(header), 245 data_header_(data_header), 246 target_entries_(target_entries), 247 target_inline_entries_(target_inline_entries), 248 overlay_entries_(overlay_entries), 249 string_pool_(std::move(string_pool)), 250 idmap_path_(std::move(idmap_path)), 251 overlay_apk_path_(overlay_apk_path), 252 target_apk_path_(target_apk_path), 253 idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {} 254 255 std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path, 256 const StringPiece& idmap_data) { 257 ATRACE_CALL(); 258 size_t data_size = idmap_data.size(); 259 auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()); 260 261 // Parse the idmap header 262 auto header = ReadType<Idmap_header>(&data_ptr, &data_size, "header"); 263 if (header == nullptr) { 264 return {}; 265 } 266 if (dtohl(header->magic) != kIdmapMagic) { 267 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)", 268 dtohl(header->magic), kIdmapMagic); 269 return {}; 270 } 271 if (dtohl(header->version) != kIdmapCurrentVersion) { 272 // We are strict about versions because files with this format are generated at runtime and 273 // don't need backwards compatibility. 274 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)", 275 dtohl(header->version), kIdmapCurrentVersion); 276 return {}; 277 } 278 std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path"); 279 if (!overlay_path) { 280 return {}; 281 } 282 std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path"); 283 if (!target_path) { 284 return {}; 285 } 286 if (!ReadString(&data_ptr, &data_size, "target name") || 287 !ReadString(&data_ptr, &data_size, "debug info")) { 288 return {}; 289 } 290 291 // Parse the idmap data blocks. Currently idmap2 can only generate one data block. 292 auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header"); 293 if (data_header == nullptr) { 294 return {}; 295 } 296 auto target_entries = ReadType<Idmap_target_entry>(&data_ptr, &data_size, "target", 297 dtohl(data_header->target_entry_count)); 298 if (target_entries == nullptr) { 299 return {}; 300 } 301 auto target_inline_entries = ReadType<Idmap_target_entry_inline>( 302 &data_ptr, &data_size, "target inline", dtohl(data_header->target_inline_entry_count)); 303 if (target_inline_entries == nullptr) { 304 return {}; 305 } 306 auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline", 307 dtohl(data_header->overlay_entry_count)); 308 if (overlay_entries == nullptr) { 309 return {}; 310 } 311 std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool"); 312 if (!string_pool) { 313 return {}; 314 } 315 auto idmap_string_pool = util::make_unique<ResStringPool>(); 316 if (!string_pool->empty()) { 317 const status_t err = idmap_string_pool->setTo(string_pool->data(), string_pool->size()); 318 if (err != NO_ERROR) { 319 LOG(ERROR) << "idmap string pool corrupt."; 320 return {}; 321 } 322 } 323 324 if (data_size != 0) { 325 LOG(ERROR) << "idmap parsed with " << data_size << "bytes remaining"; 326 return {}; 327 } 328 329 // Can't use make_unique because LoadedIdmap constructor is private. 330 return std::unique_ptr<LoadedIdmap>( 331 new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries, 332 target_inline_entries, overlay_entries, std::move(idmap_string_pool), 333 *target_path, *overlay_path)); 334 } 335 336 bool LoadedIdmap::IsUpToDate() const { 337 return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str()); 338 } 339 340 } // namespace android 341