1 /* 2 * Copyright (C) 2015 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 "process/SymbolTable.h" 18 19 #include <iostream> 20 21 #include "android-base/logging.h" 22 #include "android-base/stringprintf.h" 23 #include "androidfw/AssetManager.h" 24 #include "androidfw/ResourceTypes.h" 25 26 #include "ConfigDescription.h" 27 #include "NameMangler.h" 28 #include "Resource.h" 29 #include "ResourceUtils.h" 30 #include "ValueVisitor.h" 31 #include "util/Util.h" 32 33 using ::android::StringPiece; 34 using ::android::StringPiece16; 35 36 namespace aapt { 37 38 SymbolTable::SymbolTable(NameMangler* mangler) 39 : mangler_(mangler), 40 delegate_(util::make_unique<DefaultSymbolTableDelegate>()), 41 cache_(200), 42 id_cache_(200) { 43 } 44 45 void SymbolTable::SetDelegate(std::unique_ptr<ISymbolTableDelegate> delegate) { 46 CHECK(delegate != nullptr) << "can't set a nullptr delegate"; 47 delegate_ = std::move(delegate); 48 49 // Clear the cache in case this delegate changes the order of lookup. 50 cache_.clear(); 51 } 52 53 void SymbolTable::AppendSource(std::unique_ptr<ISymbolSource> source) { 54 sources_.push_back(std::move(source)); 55 56 // We do not clear the cache, because sources earlier in the list take 57 // precedent. 58 } 59 60 void SymbolTable::PrependSource(std::unique_ptr<ISymbolSource> source) { 61 sources_.insert(sources_.begin(), std::move(source)); 62 63 // We must clear the cache in case we did a lookup before adding this 64 // resource. 65 cache_.clear(); 66 } 67 68 const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) { 69 const ResourceName* name_with_package = &name; 70 71 // Fill in the package name if necessary. 72 // If there is no package in `name`, we will need to copy the ResourceName 73 // and store it somewhere; we use the Maybe<> class to reserve storage. 74 Maybe<ResourceName> name_with_package_impl; 75 if (name.package.empty()) { 76 name_with_package_impl = ResourceName(mangler_->GetTargetPackageName(), name.type, name.entry); 77 name_with_package = &name_with_package_impl.value(); 78 } 79 80 // We store the name unmangled in the cache, so look it up as-is. 81 if (const std::shared_ptr<Symbol>& s = cache_.get(*name_with_package)) { 82 return s.get(); 83 } 84 85 // The name was not found in the cache. Mangle it (if necessary) and find it in our sources. 86 // Again, here we use a Maybe<> object to reserve storage if we need to mangle. 87 const ResourceName* mangled_name = name_with_package; 88 Maybe<ResourceName> mangled_name_impl; 89 if (mangler_->ShouldMangle(name_with_package->package)) { 90 mangled_name_impl = mangler_->MangleName(*name_with_package); 91 mangled_name = &mangled_name_impl.value(); 92 } 93 94 std::unique_ptr<Symbol> symbol = delegate_->FindByName(*mangled_name, sources_); 95 if (symbol == nullptr) { 96 return nullptr; 97 } 98 99 // Take ownership of the symbol into a shared_ptr. We do this because 100 // LruCache doesn't support unique_ptr. 101 std::shared_ptr<Symbol> shared_symbol(std::move(symbol)); 102 103 // Since we look in the cache with the unmangled, but package prefixed 104 // name, we must put the same name into the cache. 105 cache_.put(*name_with_package, shared_symbol); 106 107 if (shared_symbol->id) { 108 // The symbol has an ID, so we can also cache this! 109 id_cache_.put(shared_symbol->id.value(), shared_symbol); 110 } 111 112 // Returns the raw pointer. Callers are not expected to hold on to this 113 // between calls to Find*. 114 return shared_symbol.get(); 115 } 116 117 const SymbolTable::Symbol* SymbolTable::FindById(const ResourceId& id) { 118 if (const std::shared_ptr<Symbol>& s = id_cache_.get(id)) { 119 return s.get(); 120 } 121 122 // We did not find it in the cache, so look through the sources. 123 std::unique_ptr<Symbol> symbol = delegate_->FindById(id, sources_); 124 if (symbol == nullptr) { 125 return nullptr; 126 } 127 128 // Take ownership of the symbol into a shared_ptr. We do this because LruCache 129 // doesn't support unique_ptr. 130 std::shared_ptr<Symbol> shared_symbol(std::move(symbol)); 131 id_cache_.put(id, shared_symbol); 132 133 // Returns the raw pointer. Callers are not expected to hold on to this 134 // between calls to Find*. 135 return shared_symbol.get(); 136 } 137 138 const SymbolTable::Symbol* SymbolTable::FindByReference(const Reference& ref) { 139 // First try the ID. This is because when we lookup by ID, we only fill in the ID cache. 140 // Looking up by name fills in the name and ID cache. So a cache miss will cause a failed 141 // ID lookup, then a successful name lookup. Subsequent look ups will hit immediately 142 // because the ID is cached too. 143 // 144 // If we looked up by name first, a cache miss would mean we failed to lookup by name, then 145 // succeeded to lookup by ID. Subsequent lookups will miss then hit. 146 const SymbolTable::Symbol* symbol = nullptr; 147 if (ref.id) { 148 symbol = FindById(ref.id.value()); 149 } 150 151 if (ref.name && !symbol) { 152 symbol = FindByName(ref.name.value()); 153 } 154 return symbol; 155 } 156 157 std::unique_ptr<SymbolTable::Symbol> DefaultSymbolTableDelegate::FindByName( 158 const ResourceName& name, const std::vector<std::unique_ptr<ISymbolSource>>& sources) { 159 for (auto& source : sources) { 160 std::unique_ptr<SymbolTable::Symbol> symbol = source->FindByName(name); 161 if (symbol) { 162 return symbol; 163 } 164 } 165 return {}; 166 } 167 168 std::unique_ptr<SymbolTable::Symbol> DefaultSymbolTableDelegate::FindById( 169 ResourceId id, const std::vector<std::unique_ptr<ISymbolSource>>& sources) { 170 for (auto& source : sources) { 171 std::unique_ptr<SymbolTable::Symbol> symbol = source->FindById(id); 172 if (symbol) { 173 return symbol; 174 } 175 } 176 return {}; 177 } 178 179 std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName( 180 const ResourceName& name) { 181 Maybe<ResourceTable::SearchResult> result = table_->FindResource(name); 182 if (!result) { 183 if (name.type == ResourceType::kAttr) { 184 // Recurse and try looking up a private attribute. 185 return FindByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry)); 186 } 187 return {}; 188 } 189 190 ResourceTable::SearchResult sr = result.value(); 191 192 std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(); 193 symbol->is_public = (sr.entry->visibility.level == Visibility::Level::kPublic); 194 195 if (sr.package->id && sr.type->id && sr.entry->id) { 196 symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value()); 197 } 198 199 if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) { 200 const ConfigDescription kDefaultConfig; 201 ResourceConfigValue* config_value = sr.entry->FindValue(kDefaultConfig); 202 if (config_value) { 203 // This resource has an Attribute. 204 if (Attribute* attr = ValueCast<Attribute>(config_value->value.get())) { 205 symbol->attribute = std::make_shared<Attribute>(*attr); 206 } else { 207 return {}; 208 } 209 } 210 } 211 return symbol; 212 } 213 214 bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) { 215 int32_t cookie = 0; 216 return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie); 217 } 218 219 std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds() const { 220 std::map<size_t, std::string> package_map; 221 const android::ResTable& table = assets_.getResources(false); 222 const size_t package_count = table.getBasePackageCount(); 223 for (size_t i = 0; i < package_count; i++) { 224 package_map[table.getBasePackageId(i)] = 225 util::Utf16ToUtf8(android::StringPiece16(table.getBasePackageName(i).string())); 226 } 227 return package_map; 228 } 229 230 bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const { 231 return assets_.getResources(false).isPackageDynamic(packageId); 232 } 233 234 static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable( 235 const android::ResTable& table, ResourceId id) { 236 // Try as a bag. 237 const android::ResTable::bag_entry* entry; 238 ssize_t count = table.lockBag(id.id, &entry); 239 if (count < 0) { 240 table.unlockBag(entry); 241 return nullptr; 242 } 243 244 // We found a resource. 245 std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id); 246 247 // Check to see if it is an attribute. 248 for (size_t i = 0; i < (size_t)count; i++) { 249 if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) { 250 s->attribute = std::make_shared<Attribute>(entry[i].map.value.data); 251 break; 252 } 253 } 254 255 if (s->attribute) { 256 for (size_t i = 0; i < (size_t)count; i++) { 257 const android::ResTable_map& map_entry = entry[i].map; 258 if (Res_INTERNALID(map_entry.name.ident)) { 259 switch (map_entry.name.ident) { 260 case android::ResTable_map::ATTR_MIN: 261 s->attribute->min_int = static_cast<int32_t>(map_entry.value.data); 262 break; 263 case android::ResTable_map::ATTR_MAX: 264 s->attribute->max_int = static_cast<int32_t>(map_entry.value.data); 265 break; 266 } 267 continue; 268 } 269 270 android::ResTable::resource_name entry_name; 271 if (!table.getResourceName(map_entry.name.ident, false, &entry_name)) { 272 table.unlockBag(entry); 273 return nullptr; 274 } 275 276 Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(entry_name); 277 if (!parsed_name) { 278 return nullptr; 279 } 280 281 Attribute::Symbol symbol; 282 symbol.symbol.name = parsed_name.value(); 283 symbol.symbol.id = ResourceId(map_entry.name.ident); 284 symbol.value = map_entry.value.data; 285 s->attribute->symbols.push_back(std::move(symbol)); 286 } 287 } 288 table.unlockBag(entry); 289 return s; 290 } 291 292 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( 293 const ResourceName& name) { 294 const android::ResTable& table = assets_.getResources(false); 295 296 const std::u16string package16 = util::Utf8ToUtf16(name.package); 297 const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type)); 298 const std::u16string entry16 = util::Utf8ToUtf16(name.entry); 299 const std::u16string mangled_entry16 = 300 util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry)); 301 302 uint32_t type_spec_flags; 303 ResourceId res_id; 304 305 // There can be mangled resources embedded within other packages. Here we will 306 // look into each package and look-up the mangled name until we find the resource. 307 const size_t count = table.getBasePackageCount(); 308 for (size_t i = 0; i < count; i++) { 309 const android::String16 package_name = table.getBasePackageName(i); 310 StringPiece16 real_package16 = package16; 311 StringPiece16 real_entry16 = entry16; 312 std::u16string scratch_entry16; 313 if (StringPiece16(package_name) != package16) { 314 real_entry16 = mangled_entry16; 315 real_package16 = package_name.string(); 316 } 317 318 type_spec_flags = 0; 319 res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(), 320 type16.size(), real_package16.data(), real_package16.size(), 321 &type_spec_flags); 322 if (res_id.is_valid()) { 323 break; 324 } 325 } 326 327 if (!res_id.is_valid()) { 328 return {}; 329 } 330 331 std::unique_ptr<SymbolTable::Symbol> s; 332 if (name.type == ResourceType::kAttr) { 333 s = LookupAttributeInTable(table, res_id); 334 } else { 335 s = util::make_unique<SymbolTable::Symbol>(); 336 s->id = res_id; 337 s->is_dynamic = table.isResourceDynamic(res_id.id); 338 } 339 340 if (s) { 341 s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; 342 return s; 343 } 344 return {}; 345 } 346 347 static Maybe<ResourceName> GetResourceName(const android::ResTable& table, 348 ResourceId id) { 349 android::ResTable::resource_name res_name = {}; 350 if (!table.getResourceName(id.id, true, &res_name)) { 351 return {}; 352 } 353 return ResourceUtils::ToResourceName(res_name); 354 } 355 356 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById( 357 ResourceId id) { 358 if (!id.is_valid()) { 359 // Exit early and avoid the error logs from AssetManager. 360 return {}; 361 } 362 const android::ResTable& table = assets_.getResources(false); 363 Maybe<ResourceName> maybe_name = GetResourceName(table, id); 364 if (!maybe_name) { 365 return {}; 366 } 367 368 uint32_t type_spec_flags = 0; 369 table.getResourceFlags(id.id, &type_spec_flags); 370 371 std::unique_ptr<SymbolTable::Symbol> s; 372 if (maybe_name.value().type == ResourceType::kAttr) { 373 s = LookupAttributeInTable(table, id); 374 } else { 375 s = util::make_unique<SymbolTable::Symbol>(); 376 s->id = id; 377 s->is_dynamic = table.isResourceDynamic(id.id); 378 } 379 380 if (s) { 381 s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; 382 return s; 383 } 384 return {}; 385 } 386 387 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByReference( 388 const Reference& ref) { 389 // AssetManager always prefers IDs. 390 if (ref.id) { 391 return FindById(ref.id.value()); 392 } else if (ref.name) { 393 return FindByName(ref.name.value()); 394 } 395 return {}; 396 } 397 398 } // namespace aapt 399