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 #include <fstream> 18 #include <iostream> 19 #include <map> 20 #include <set> 21 #include <string> 22 #include <string_view> 23 24 #include "android-base/stringprintf.h" 25 #include "android-base/strings.h" 26 27 #include "base/bit_utils.h" 28 #include "base/hiddenapi_flags.h" 29 #include "base/mem_map.h" 30 #include "base/os.h" 31 #include "base/stl_util.h" 32 #include "base/string_view_cpp20.h" 33 #include "base/unix_file/fd_file.h" 34 #include "dex/art_dex_file_loader.h" 35 #include "dex/class_accessor-inl.h" 36 #include "dex/dex_file-inl.h" 37 38 namespace art { 39 namespace hiddenapi { 40 41 const char kErrorHelp[] = "\nSee go/hiddenapi-error for help."; 42 43 static int original_argc; 44 static char** original_argv; 45 46 static std::string CommandLine() { 47 std::vector<std::string> command; 48 command.reserve(original_argc); 49 for (int i = 0; i < original_argc; ++i) { 50 command.push_back(original_argv[i]); 51 } 52 return android::base::Join(command, ' '); 53 } 54 55 static void UsageErrorV(const char* fmt, va_list ap) { 56 std::string error; 57 android::base::StringAppendV(&error, fmt, ap); 58 LOG(ERROR) << error; 59 } 60 61 static void UsageError(const char* fmt, ...) { 62 va_list ap; 63 va_start(ap, fmt); 64 UsageErrorV(fmt, ap); 65 va_end(ap); 66 } 67 68 NO_RETURN static void Usage(const char* fmt, ...) { 69 va_list ap; 70 va_start(ap, fmt); 71 UsageErrorV(fmt, ap); 72 va_end(ap); 73 74 UsageError("Command: %s", CommandLine().c_str()); 75 UsageError("Usage: hiddenapi [command_name] [options]..."); 76 UsageError(""); 77 UsageError(" Command \"encode\": encode API list membership in boot dex files"); 78 UsageError(" --input-dex=<filename>: dex file which belongs to boot class path"); 79 UsageError(" --output-dex=<filename>: file to write encoded dex into"); 80 UsageError(" input and output dex files are paired in order of appearance"); 81 UsageError(""); 82 UsageError(" --api-flags=<filename>:"); 83 UsageError(" CSV file with signatures of methods/fields and their respective flags"); 84 UsageError(""); 85 UsageError(" --no-force-assign-all:"); 86 UsageError(" Disable check that all dex entries have been assigned a flag"); 87 UsageError(""); 88 UsageError(" Command \"list\": dump lists of public and private API"); 89 UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path"); 90 UsageError(" --public-stub-classpath=<filenames>:"); 91 UsageError(" --system-stub-classpath=<filenames>:"); 92 UsageError(" --test-stub-classpath=<filenames>:"); 93 UsageError(" --core-platform-stub-classpath=<filenames>:"); 94 UsageError(" colon-separated list of dex/apk files which form API stubs of boot"); 95 UsageError(" classpath. Multiple classpaths can be specified"); 96 UsageError(""); 97 UsageError(" --out-api-flags=<filename>: output file for a CSV file with API flags"); 98 UsageError(""); 99 100 exit(EXIT_FAILURE); 101 } 102 103 template<typename E> 104 static bool Contains(const std::vector<E>& vec, const E& elem) { 105 return std::find(vec.begin(), vec.end(), elem) != vec.end(); 106 } 107 108 class DexClass : public ClassAccessor { 109 public: 110 explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {} 111 112 const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); } 113 114 const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; } 115 116 bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); } 117 118 std::string_view GetSuperclassDescriptor() const { 119 return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : ""; 120 } 121 122 std::set<std::string_view> GetInterfaceDescriptors() const { 123 std::set<std::string_view> list; 124 const dex::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef()); 125 for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) { 126 list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_)); 127 } 128 return list; 129 } 130 131 inline bool IsPublic() const { return HasAccessFlags(kAccPublic); } 132 inline bool IsInterface() const { return HasAccessFlags(kAccInterface); } 133 134 inline bool Equals(const DexClass& other) const { 135 bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0; 136 137 if (equals) { 138 LOG(FATAL) << "Class duplication: " << GetDescriptor() << " in " << dex_file_.GetLocation() 139 << " and " << other.dex_file_.GetLocation(); 140 } 141 142 return equals; 143 } 144 145 private: 146 uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; } 147 bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; } 148 149 static std::string JoinStringSet(const std::set<std::string_view>& s) { 150 return "{" + ::android::base::Join(std::vector<std::string>(s.begin(), s.end()), ",") + "}"; 151 } 152 }; 153 154 class DexMember { 155 public: 156 DexMember(const DexClass& klass, const ClassAccessor::Field& item) 157 : klass_(klass), item_(item), is_method_(false) { 158 DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx()); 159 } 160 161 DexMember(const DexClass& klass, const ClassAccessor::Method& item) 162 : klass_(klass), item_(item), is_method_(true) { 163 DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx()); 164 } 165 166 inline const DexClass& GetDeclaringClass() const { return klass_; } 167 168 inline bool IsMethod() const { return is_method_; } 169 inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); } 170 inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); } 171 172 inline bool IsPublicOrProtected() const { 173 return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected); 174 } 175 176 // Constructs a string with a unique signature of this class member. 177 std::string GetApiEntry() const { 178 std::stringstream ss; 179 ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":") 180 << GetSignature(); 181 return ss.str(); 182 } 183 184 inline bool operator==(const DexMember& other) const { 185 // These need to match if they should resolve to one another. 186 bool equals = IsMethod() == other.IsMethod() && 187 GetName() == other.GetName() && 188 GetSignature() == other.GetSignature(); 189 190 // Sanity checks if they do match. 191 if (equals) { 192 CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod()); 193 } 194 195 return equals; 196 } 197 198 private: 199 inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); } 200 inline uint32_t HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; } 201 202 inline std::string_view GetName() const { 203 return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId()) 204 : item_.GetDexFile().GetFieldName(GetFieldId()); 205 } 206 207 inline std::string GetSignature() const { 208 return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString() 209 : item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId()); 210 } 211 212 inline const ClassAccessor::Method& GetMethod() const { 213 DCHECK(IsMethod()); 214 return down_cast<const ClassAccessor::Method&>(item_); 215 } 216 217 inline const dex::MethodId& GetMethodId() const { 218 DCHECK(IsMethod()); 219 return item_.GetDexFile().GetMethodId(item_.GetIndex()); 220 } 221 222 inline const dex::FieldId& GetFieldId() const { 223 DCHECK(!IsMethod()); 224 return item_.GetDexFile().GetFieldId(item_.GetIndex()); 225 } 226 227 const DexClass& klass_; 228 const ClassAccessor::BaseItem& item_; 229 const bool is_method_; 230 }; 231 232 class ClassPath final { 233 public: 234 ClassPath(const std::vector<std::string>& dex_paths, bool open_writable) { 235 OpenDexFiles(dex_paths, open_writable); 236 } 237 238 template<typename Fn> 239 void ForEachDexClass(Fn fn) { 240 for (auto& dex_file : dex_files_) { 241 for (ClassAccessor accessor : dex_file->GetClasses()) { 242 fn(DexClass(accessor)); 243 } 244 } 245 } 246 247 template<typename Fn> 248 void ForEachDexMember(Fn fn) { 249 ForEachDexClass([&fn](const DexClass& klass) { 250 for (const ClassAccessor::Field& field : klass.GetFields()) { 251 fn(DexMember(klass, field)); 252 } 253 for (const ClassAccessor::Method& method : klass.GetMethods()) { 254 fn(DexMember(klass, method)); 255 } 256 }); 257 } 258 259 std::vector<const DexFile*> GetDexFiles() const { 260 return MakeNonOwningPointerVector(dex_files_); 261 } 262 263 void UpdateDexChecksums() { 264 for (auto& dex_file : dex_files_) { 265 // Obtain a writeable pointer to the dex header. 266 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader()); 267 // Recalculate checksum and overwrite the value in the header. 268 header->checksum_ = dex_file->CalculateChecksum(); 269 } 270 } 271 272 private: 273 void OpenDexFiles(const std::vector<std::string>& dex_paths, bool open_writable) { 274 ArtDexFileLoader dex_loader; 275 std::string error_msg; 276 277 if (open_writable) { 278 for (const std::string& filename : dex_paths) { 279 File fd(filename.c_str(), O_RDWR, /* check_usage= */ false); 280 CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno); 281 282 // Memory-map the dex file with MAP_SHARED flag so that changes in memory 283 // propagate to the underlying file. We run dex file verification as if 284 // the dex file was not in boot claass path to check basic assumptions, 285 // such as that at most one of public/private/protected flag is set. 286 // We do those checks here and skip them when loading the processed file 287 // into boot class path. 288 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(), 289 /* location= */ filename, 290 /* verify= */ true, 291 /* verify_checksum= */ true, 292 /* mmap_shared= */ true, 293 &error_msg)); 294 CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg; 295 CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'"; 296 CHECK(dex_file->EnableWrite()) 297 << "Failed to enable write permission for '" << filename << "'"; 298 dex_files_.push_back(std::move(dex_file)); 299 } 300 } else { 301 for (const std::string& filename : dex_paths) { 302 bool success = dex_loader.Open(filename.c_str(), 303 /* location= */ filename, 304 /* verify= */ true, 305 /* verify_checksum= */ true, 306 &error_msg, 307 &dex_files_); 308 CHECK(success) << "Open failed for '" << filename << "' " << error_msg; 309 } 310 } 311 } 312 313 // Opened dex files. Note that these are opened as `const` but may be written into. 314 std::vector<std::unique_ptr<const DexFile>> dex_files_; 315 }; 316 317 class HierarchyClass final { 318 public: 319 HierarchyClass() {} 320 321 void AddDexClass(const DexClass& klass) { 322 CHECK(dex_classes_.empty() || klass.Equals(dex_classes_.front())); 323 dex_classes_.push_back(klass); 324 } 325 326 void AddExtends(HierarchyClass& parent) { 327 CHECK(!Contains(extends_, &parent)); 328 CHECK(!Contains(parent.extended_by_, this)); 329 extends_.push_back(&parent); 330 parent.extended_by_.push_back(this); 331 } 332 333 const DexClass& GetOneDexClass() const { 334 CHECK(!dex_classes_.empty()); 335 return dex_classes_.front(); 336 } 337 338 // See comment on Hierarchy::ForEachResolvableMember. 339 template<typename Fn> 340 bool ForEachResolvableMember(const DexMember& other, Fn fn) { 341 std::vector<HierarchyClass*> visited; 342 return ForEachResolvableMember_Impl(other, fn, true, true, visited); 343 } 344 345 // Returns true if this class contains at least one member matching `other`. 346 bool HasMatchingMember(const DexMember& other) { 347 return ForEachMatchingMember(other, [](const DexMember&) { return true; }); 348 } 349 350 // Recursively iterates over all subclasses of this class and invokes `fn` 351 // on each one. If `fn` returns false for a particular subclass, exploring its 352 // subclasses is skipped. 353 template<typename Fn> 354 void ForEachSubClass(Fn fn) { 355 for (HierarchyClass* subclass : extended_by_) { 356 if (fn(subclass)) { 357 subclass->ForEachSubClass(fn); 358 } 359 } 360 } 361 362 private: 363 template<typename Fn> 364 bool ForEachResolvableMember_Impl(const DexMember& other, 365 Fn fn, 366 bool allow_explore_up, 367 bool allow_explore_down, 368 std::vector<HierarchyClass*> visited) { 369 if (std::find(visited.begin(), visited.end(), this) == visited.end()) { 370 visited.push_back(this); 371 } else { 372 return false; 373 } 374 375 // First try to find a member matching `other` in this class. 376 bool found = ForEachMatchingMember(other, fn); 377 378 // If not found, see if it is inherited from parents. Note that this will not 379 // revisit parents already in `visited`. 380 if (!found && allow_explore_up) { 381 for (HierarchyClass* superclass : extends_) { 382 found |= superclass->ForEachResolvableMember_Impl( 383 other, 384 fn, 385 /* allow_explore_up */ true, 386 /* allow_explore_down */ false, 387 visited); 388 } 389 } 390 391 // If this is a virtual method, continue exploring into subclasses so as to visit 392 // all overriding methods. Allow subclasses to explore their superclasses if this 393 // is an interface. This is needed to find implementations of this interface's 394 // methods inherited from superclasses (b/122551864). 395 if (allow_explore_down && other.IsVirtualMethod()) { 396 for (HierarchyClass* subclass : extended_by_) { 397 subclass->ForEachResolvableMember_Impl( 398 other, 399 fn, 400 /* allow_explore_up */ GetOneDexClass().IsInterface(), 401 /* allow_explore_down */ true, 402 visited); 403 } 404 } 405 406 return found; 407 } 408 409 template<typename Fn> 410 bool ForEachMatchingMember(const DexMember& other, Fn fn) { 411 bool found = false; 412 auto compare_member = [&](const DexMember& member) { 413 // TODO(dbrazdil): Check whether class of `other` can access `member`. 414 if (member == other) { 415 found = true; 416 fn(member); 417 } 418 }; 419 for (const DexClass& dex_class : dex_classes_) { 420 for (const ClassAccessor::Field& field : dex_class.GetFields()) { 421 compare_member(DexMember(dex_class, field)); 422 } 423 for (const ClassAccessor::Method& method : dex_class.GetMethods()) { 424 compare_member(DexMember(dex_class, method)); 425 } 426 } 427 return found; 428 } 429 430 // DexClass entries of this class found across all the provided dex files. 431 std::vector<DexClass> dex_classes_; 432 433 // Classes which this class inherits, or interfaces which it implements. 434 std::vector<HierarchyClass*> extends_; 435 436 // Classes which inherit from this class. 437 std::vector<HierarchyClass*> extended_by_; 438 }; 439 440 class Hierarchy final { 441 public: 442 explicit Hierarchy(ClassPath& classpath) : classpath_(classpath) { 443 BuildClassHierarchy(); 444 } 445 446 // Perform an operation for each member of the hierarchy which could potentially 447 // be the result of method/field resolution of `other`. 448 // The function `fn` should accept a DexMember reference and return true if 449 // the member was changed. This drives a performance optimization which only 450 // visits overriding members the first time the overridden member is visited. 451 // Returns true if at least one resolvable member was found. 452 template<typename Fn> 453 bool ForEachResolvableMember(const DexMember& other, Fn fn) { 454 HierarchyClass* klass = FindClass(other.GetDeclaringClass().GetDescriptor()); 455 return (klass != nullptr) && klass->ForEachResolvableMember(other, fn); 456 } 457 458 // Returns true if `member`, which belongs to this classpath, is visible to 459 // code in child class loaders. 460 bool IsMemberVisible(const DexMember& member) { 461 if (!member.IsPublicOrProtected()) { 462 // Member is private or package-private. Cannot be visible. 463 return false; 464 } else if (member.GetDeclaringClass().IsPublic()) { 465 // Member is public or protected, and class is public. It must be visible. 466 return true; 467 } else if (member.IsConstructor()) { 468 // Member is public or protected constructor and class is not public. 469 // Must be hidden because it cannot be implicitly exposed by a subclass. 470 return false; 471 } else { 472 // Member is public or protected method, but class is not public. Check if 473 // it is exposed through a public subclass. 474 // Example code (`foo` exposed by ClassB): 475 // class ClassA { public void foo() { ... } } 476 // public class ClassB extends ClassA {} 477 HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor()); 478 CHECK(klass != nullptr); 479 bool visible = false; 480 klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) { 481 if (subclass->HasMatchingMember(member)) { 482 // There is a member which matches `member` in `subclass`, either 483 // a virtual method overriding `member` or a field overshadowing 484 // `member`. In either case, `member` remains hidden. 485 CHECK(member.IsVirtualMethod() || !member.IsMethod()); 486 return false; // do not explore deeper 487 } else if (subclass->GetOneDexClass().IsPublic()) { 488 // `subclass` inherits and exposes `member`. 489 visible = true; 490 return false; // do not explore deeper 491 } else { 492 // `subclass` inherits `member` but does not expose it. 493 return true; // explore deeper 494 } 495 }); 496 return visible; 497 } 498 } 499 500 private: 501 HierarchyClass* FindClass(const std::string_view& descriptor) { 502 auto it = classes_.find(descriptor); 503 if (it == classes_.end()) { 504 return nullptr; 505 } else { 506 return &it->second; 507 } 508 } 509 510 void BuildClassHierarchy() { 511 // Create one HierarchyClass entry in `classes_` per class descriptor 512 // and add all DexClass objects with the same descriptor to that entry. 513 classpath_.ForEachDexClass([this](const DexClass& klass) { 514 classes_[klass.GetDescriptor()].AddDexClass(klass); 515 }); 516 517 // Connect each HierarchyClass to its successors and predecessors. 518 for (auto& entry : classes_) { 519 HierarchyClass& klass = entry.second; 520 const DexClass& dex_klass = klass.GetOneDexClass(); 521 522 if (!dex_klass.HasSuperclass()) { 523 CHECK(dex_klass.GetInterfaceDescriptors().empty()) 524 << "java/lang/Object should not implement any interfaces"; 525 continue; 526 } 527 528 HierarchyClass* superclass = FindClass(dex_klass.GetSuperclassDescriptor()); 529 CHECK(superclass != nullptr) 530 << "Superclass " << dex_klass.GetSuperclassDescriptor() 531 << " of class " << dex_klass.GetDescriptor() << " from dex file \"" 532 << dex_klass.GetDexFile().GetLocation() << "\" was not found. " 533 << "Either the superclass is missing or it appears later in the classpath spec."; 534 klass.AddExtends(*superclass); 535 536 for (const std::string_view& iface_desc : dex_klass.GetInterfaceDescriptors()) { 537 HierarchyClass* iface = FindClass(iface_desc); 538 CHECK(iface != nullptr); 539 klass.AddExtends(*iface); 540 } 541 } 542 } 543 544 ClassPath& classpath_; 545 std::map<std::string_view, HierarchyClass> classes_; 546 }; 547 548 // Builder of dex section containing hiddenapi flags. 549 class HiddenapiClassDataBuilder final { 550 public: 551 explicit HiddenapiClassDataBuilder(const DexFile& dex_file) 552 : num_classdefs_(dex_file.NumClassDefs()), 553 next_class_def_idx_(0u), 554 class_def_has_non_zero_flags_(false), 555 dex_file_has_non_zero_flags_(false), 556 data_(sizeof(uint32_t) * (num_classdefs_ + 1), 0u) { 557 *GetSizeField() = GetCurrentDataSize(); 558 } 559 560 // Notify the builder that new flags for the next class def 561 // will be written now. The builder records the current offset 562 // into the header. 563 void BeginClassDef(uint32_t idx) { 564 CHECK_EQ(next_class_def_idx_, idx); 565 CHECK_LT(idx, num_classdefs_); 566 GetOffsetArray()[idx] = GetCurrentDataSize(); 567 class_def_has_non_zero_flags_ = false; 568 } 569 570 // Notify the builder that all flags for this class def have been 571 // written. The builder updates the total size of the data struct 572 // and may set offset for class def in header to zero if no data 573 // has been written. 574 void EndClassDef(uint32_t idx) { 575 CHECK_EQ(next_class_def_idx_, idx); 576 CHECK_LT(idx, num_classdefs_); 577 578 ++next_class_def_idx_; 579 580 if (!class_def_has_non_zero_flags_) { 581 // No need to store flags for this class. Remove the written flags 582 // and set offset in header to zero. 583 data_.resize(GetOffsetArray()[idx]); 584 GetOffsetArray()[idx] = 0u; 585 } 586 587 dex_file_has_non_zero_flags_ |= class_def_has_non_zero_flags_; 588 589 if (idx == num_classdefs_ - 1) { 590 if (dex_file_has_non_zero_flags_) { 591 // This was the last class def and we have generated non-zero hiddenapi 592 // flags. Update total size in the header. 593 *GetSizeField() = GetCurrentDataSize(); 594 } else { 595 // This was the last class def and we have not generated any non-zero 596 // hiddenapi flags. Clear all the data. 597 data_.clear(); 598 } 599 } 600 } 601 602 // Append flags at the end of the data struct. This should be called 603 // between BeginClassDef and EndClassDef in the order of appearance of 604 // fields/methods in the class data stream. 605 void WriteFlags(const ApiList& flags) { 606 uint32_t dex_flags = flags.GetDexFlags(); 607 EncodeUnsignedLeb128(&data_, dex_flags); 608 class_def_has_non_zero_flags_ |= (dex_flags != 0u); 609 } 610 611 // Return backing data, assuming that all flags have been written. 612 const std::vector<uint8_t>& GetData() const { 613 CHECK_EQ(next_class_def_idx_, num_classdefs_) << "Incomplete data"; 614 return data_; 615 } 616 617 private: 618 // Returns pointer to the size field in the header of this dex section. 619 uint32_t* GetSizeField() { 620 // Assume malloc() aligns allocated memory to at least uint32_t. 621 CHECK(IsAligned<sizeof(uint32_t)>(data_.data())); 622 return reinterpret_cast<uint32_t*>(data_.data()); 623 } 624 625 // Returns pointer to array of offsets (indexed by class def indices) in the 626 // header of this dex section. 627 uint32_t* GetOffsetArray() { return &GetSizeField()[1]; } 628 uint32_t GetCurrentDataSize() const { return data_.size(); } 629 630 // Number of class defs in this dex file. 631 const uint32_t num_classdefs_; 632 633 // Next expected class def index. 634 uint32_t next_class_def_idx_; 635 636 // Whether non-zero flags have been encountered for this class def. 637 bool class_def_has_non_zero_flags_; 638 639 // Whether any non-zero flags have been encountered for this dex file. 640 bool dex_file_has_non_zero_flags_; 641 642 // Vector containing the data of the built data structure. 643 std::vector<uint8_t> data_; 644 }; 645 646 // Edits a dex file, inserting a new HiddenapiClassData section. 647 class DexFileEditor final { 648 public: 649 DexFileEditor(const DexFile& old_dex, const std::vector<uint8_t>& hiddenapi_class_data) 650 : old_dex_(old_dex), 651 hiddenapi_class_data_(hiddenapi_class_data), 652 loaded_dex_header_(nullptr), 653 loaded_dex_maplist_(nullptr) {} 654 655 // Copies dex file into a backing data vector, appends the given HiddenapiClassData 656 // and updates the MapList. 657 void Encode() { 658 // We do not support non-standard dex encodings, e.g. compact dex. 659 CHECK(old_dex_.IsStandardDexFile()); 660 661 // If there are no data to append, copy the old dex file and return. 662 if (hiddenapi_class_data_.empty()) { 663 AllocateMemory(old_dex_.Size()); 664 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false); 665 return; 666 } 667 668 // Find the old MapList, find its size. 669 const dex::MapList* old_map = old_dex_.GetMapList(); 670 CHECK_LT(old_map->size_, std::numeric_limits<uint32_t>::max()); 671 672 // Compute the size of the new dex file. We append the HiddenapiClassData, 673 // one MapItem and possibly some padding to align the new MapList. 674 CHECK(IsAligned<kMapListAlignment>(old_dex_.Size())) 675 << "End of input dex file is not 4-byte aligned, possibly because its MapList is not " 676 << "at the end of the file."; 677 size_t size_delta = 678 RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(dex::MapItem); 679 size_t new_size = old_dex_.Size() + size_delta; 680 AllocateMemory(new_size); 681 682 // Copy the old dex file into the backing data vector. Load the copied 683 // dex file to obtain pointers to its header and MapList. 684 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false); 685 ReloadDex(/* verify= */ false); 686 687 // Truncate the new dex file before the old MapList. This assumes that 688 // the MapList is the last entry in the dex file. This is currently true 689 // for our tooling. 690 // TODO: Implement the general case by zero-ing the old MapList (turning 691 // it into padding. 692 RemoveOldMapList(); 693 694 // Append HiddenapiClassData. 695 size_t payload_offset = AppendHiddenapiClassData(); 696 697 // Wrute new MapList with an entry for HiddenapiClassData. 698 CreateMapListWithNewItem(payload_offset); 699 700 // Check that the pre-computed size matches the actual size. 701 CHECK_EQ(offset_, new_size); 702 703 // Reload to all data structures. 704 ReloadDex(/* verify= */ false); 705 706 // Update the dex checksum. 707 UpdateChecksum(); 708 709 // Run DexFileVerifier on the new dex file as a CHECK. 710 ReloadDex(/* verify= */ true); 711 } 712 713 // Writes the edited dex file into a file. 714 void WriteTo(const std::string& path) { 715 CHECK(!data_.empty()); 716 std::ofstream ofs(path.c_str(), std::ofstream::out | std::ofstream::binary); 717 ofs.write(reinterpret_cast<const char*>(data_.data()), data_.size()); 718 ofs.flush(); 719 CHECK(ofs.good()); 720 ofs.close(); 721 } 722 723 private: 724 static constexpr size_t kMapListAlignment = 4u; 725 static constexpr size_t kHiddenapiClassDataAlignment = 4u; 726 727 void ReloadDex(bool verify) { 728 std::string error_msg; 729 DexFileLoader loader; 730 loaded_dex_ = loader.Open( 731 data_.data(), 732 data_.size(), 733 "test_location", 734 old_dex_.GetLocationChecksum(), 735 /* oat_dex_file= */ nullptr, 736 /* verify= */ verify, 737 /* verify_checksum= */ verify, 738 &error_msg); 739 if (loaded_dex_.get() == nullptr) { 740 LOG(FATAL) << "Failed to load edited dex file: " << error_msg; 741 UNREACHABLE(); 742 } 743 744 // Load the location of header and map list before we start editing the file. 745 loaded_dex_header_ = const_cast<DexFile::Header*>(&loaded_dex_->GetHeader()); 746 loaded_dex_maplist_ = const_cast<dex::MapList*>(loaded_dex_->GetMapList()); 747 } 748 749 DexFile::Header& GetHeader() const { 750 CHECK(loaded_dex_header_ != nullptr); 751 return *loaded_dex_header_; 752 } 753 754 dex::MapList& GetMapList() const { 755 CHECK(loaded_dex_maplist_ != nullptr); 756 return *loaded_dex_maplist_; 757 } 758 759 void AllocateMemory(size_t total_size) { 760 data_.clear(); 761 data_.resize(total_size); 762 CHECK(IsAligned<kMapListAlignment>(data_.data())); 763 CHECK(IsAligned<kHiddenapiClassDataAlignment>(data_.data())); 764 offset_ = 0; 765 } 766 767 uint8_t* GetCurrentDataPtr() { 768 return data_.data() + offset_; 769 } 770 771 void UpdateDataSize(off_t delta, bool update_header) { 772 offset_ += delta; 773 if (update_header) { 774 DexFile::Header& header = GetHeader(); 775 header.file_size_ += delta; 776 header.data_size_ += delta; 777 } 778 } 779 780 template<typename T> 781 T* Append(const T* src, size_t len, bool update_header = true) { 782 CHECK_LE(offset_ + len, data_.size()); 783 uint8_t* dst = GetCurrentDataPtr(); 784 memcpy(dst, src, len); 785 UpdateDataSize(len, update_header); 786 return reinterpret_cast<T*>(dst); 787 } 788 789 void InsertPadding(size_t alignment) { 790 size_t len = RoundUp(offset_, alignment) - offset_; 791 std::vector<uint8_t> padding(len, 0); 792 Append(padding.data(), padding.size()); 793 } 794 795 void RemoveOldMapList() { 796 size_t map_size = GetMapList().Size(); 797 uint8_t* map_start = reinterpret_cast<uint8_t*>(&GetMapList()); 798 CHECK_EQ(map_start + map_size, GetCurrentDataPtr()) << "MapList not at the end of dex file"; 799 UpdateDataSize(-static_cast<off_t>(map_size), /* update_header= */ true); 800 CHECK_EQ(map_start, GetCurrentDataPtr()); 801 loaded_dex_maplist_ = nullptr; // do not use this map list any more 802 } 803 804 void CreateMapListWithNewItem(size_t payload_offset) { 805 InsertPadding(/* alignment= */ kMapListAlignment); 806 807 size_t new_map_offset = offset_; 808 dex::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size()); 809 810 // Check last map entry is a pointer to itself. 811 dex::MapItem& old_item = map->list_[map->size_ - 1]; 812 CHECK(old_item.type_ == DexFile::kDexTypeMapList); 813 CHECK_EQ(old_item.size_, 1u); 814 CHECK_EQ(old_item.offset_, GetHeader().map_off_); 815 816 // Create a new MapItem entry with new MapList details. 817 dex::MapItem new_item; 818 new_item.type_ = old_item.type_; 819 new_item.unused_ = 0u; // initialize to ensure dex output is deterministic (b/119308882) 820 new_item.size_ = old_item.size_; 821 new_item.offset_ = new_map_offset; 822 823 // Update pointer in the header. 824 GetHeader().map_off_ = new_map_offset; 825 826 // Append a new MapItem and return its pointer. 827 map->size_++; 828 Append(&new_item, sizeof(dex::MapItem)); 829 830 // Change penultimate entry to point to metadata. 831 old_item.type_ = DexFile::kDexTypeHiddenapiClassData; 832 old_item.size_ = 1u; // there is only one section 833 old_item.offset_ = payload_offset; 834 } 835 836 size_t AppendHiddenapiClassData() { 837 size_t payload_offset = offset_; 838 CHECK_EQ(kMapListAlignment, kHiddenapiClassDataAlignment); 839 CHECK(IsAligned<kHiddenapiClassDataAlignment>(payload_offset)) 840 << "Should not need to align the section, previous data was already aligned"; 841 Append(hiddenapi_class_data_.data(), hiddenapi_class_data_.size()); 842 return payload_offset; 843 } 844 845 void UpdateChecksum() { 846 GetHeader().checksum_ = loaded_dex_->CalculateChecksum(); 847 } 848 849 const DexFile& old_dex_; 850 const std::vector<uint8_t>& hiddenapi_class_data_; 851 852 std::vector<uint8_t> data_; 853 size_t offset_; 854 855 std::unique_ptr<const DexFile> loaded_dex_; 856 DexFile::Header* loaded_dex_header_; 857 dex::MapList* loaded_dex_maplist_; 858 }; 859 860 class HiddenApi final { 861 public: 862 HiddenApi() : force_assign_all_(true) {} 863 864 void Run(int argc, char** argv) { 865 switch (ParseArgs(argc, argv)) { 866 case Command::kEncode: 867 EncodeAccessFlags(); 868 break; 869 case Command::kList: 870 ListApi(); 871 break; 872 } 873 } 874 875 private: 876 enum class Command { 877 kEncode, 878 kList, 879 }; 880 881 Command ParseArgs(int argc, char** argv) { 882 // Skip over the binary's path. 883 argv++; 884 argc--; 885 886 if (argc > 0) { 887 const char* raw_command = argv[0]; 888 const std::string_view command(raw_command); 889 if (command == "encode") { 890 for (int i = 1; i < argc; ++i) { 891 const char* raw_option = argv[i]; 892 const std::string_view option(raw_option); 893 if (StartsWith(option, "--input-dex=")) { 894 boot_dex_paths_.push_back(std::string(option.substr(strlen("--input-dex=")))); 895 } else if (StartsWith(option, "--output-dex=")) { 896 output_dex_paths_.push_back(std::string(option.substr(strlen("--output-dex=")))); 897 } else if (StartsWith(option, "--api-flags=")) { 898 api_flags_path_ = std::string(option.substr(strlen("--api-flags="))); 899 } else if (option == "--no-force-assign-all") { 900 force_assign_all_ = false; 901 } else { 902 Usage("Unknown argument '%s'", raw_option); 903 } 904 } 905 return Command::kEncode; 906 } else if (command == "list") { 907 for (int i = 1; i < argc; ++i) { 908 const char* raw_option = argv[i]; 909 const std::string_view option(raw_option); 910 if (StartsWith(option, "--boot-dex=")) { 911 boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex=")))); 912 } else if (StartsWith(option, "--public-stub-classpath=")) { 913 stub_classpaths_.push_back(std::make_pair( 914 std::string(option.substr(strlen("--public-stub-classpath="))), 915 ApiStubs::Kind::kPublicApi)); 916 } else if (StartsWith(option, "--system-stub-classpath=")) { 917 stub_classpaths_.push_back(std::make_pair( 918 std::string(option.substr(strlen("--system-stub-classpath="))), 919 ApiStubs::Kind::kSystemApi)); 920 } else if (StartsWith(option, "--test-stub-classpath=")) { 921 stub_classpaths_.push_back(std::make_pair( 922 std::string(option.substr(strlen("--test-stub-classpath="))), 923 ApiStubs::Kind::kTestApi)); 924 } else if (StartsWith(option, "--core-platform-stub-classpath=")) { 925 stub_classpaths_.push_back(std::make_pair( 926 std::string(option.substr(strlen("--core-platform-stub-classpath="))), 927 ApiStubs::Kind::kCorePlatformApi)); 928 } else if (StartsWith(option, "--out-api-flags=")) { 929 api_flags_path_ = std::string(option.substr(strlen("--out-api-flags="))); 930 } else { 931 Usage("Unknown argument '%s'", raw_option); 932 } 933 } 934 return Command::kList; 935 } else { 936 Usage("Unknown command '%s'", raw_command); 937 } 938 } else { 939 Usage("No command specified"); 940 } 941 } 942 943 void EncodeAccessFlags() { 944 if (boot_dex_paths_.empty()) { 945 Usage("No input DEX files specified"); 946 } else if (output_dex_paths_.size() != boot_dex_paths_.size()) { 947 Usage("Number of input DEX files does not match number of output DEX files"); 948 } 949 950 // Load dex signatures. 951 std::map<std::string, ApiList> api_list = OpenApiFile(api_flags_path_); 952 953 // Iterate over input dex files and insert HiddenapiClassData sections. 954 for (size_t i = 0; i < boot_dex_paths_.size(); ++i) { 955 const std::string& input_path = boot_dex_paths_[i]; 956 const std::string& output_path = output_dex_paths_[i]; 957 958 ClassPath boot_classpath({ input_path }, /* open_writable= */ false); 959 std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles(); 960 CHECK_EQ(input_dex_files.size(), 1u); 961 const DexFile& input_dex = *input_dex_files[0]; 962 963 HiddenapiClassDataBuilder builder(input_dex); 964 boot_classpath.ForEachDexClass([&](const DexClass& boot_class) { 965 builder.BeginClassDef(boot_class.GetClassDefIndex()); 966 if (boot_class.GetData() != nullptr) { 967 auto fn_shared = [&](const DexMember& boot_member) { 968 auto it = api_list.find(boot_member.GetApiEntry()); 969 bool api_list_found = (it != api_list.end()); 970 CHECK(!force_assign_all_ || api_list_found) 971 << "Could not find hiddenapi flags for dex entry: " << boot_member.GetApiEntry(); 972 builder.WriteFlags(api_list_found ? it->second : ApiList::Whitelist()); 973 }; 974 auto fn_field = [&](const ClassAccessor::Field& boot_field) { 975 fn_shared(DexMember(boot_class, boot_field)); 976 }; 977 auto fn_method = [&](const ClassAccessor::Method& boot_method) { 978 fn_shared(DexMember(boot_class, boot_method)); 979 }; 980 boot_class.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method); 981 } 982 builder.EndClassDef(boot_class.GetClassDefIndex()); 983 }); 984 985 DexFileEditor dex_editor(input_dex, builder.GetData()); 986 dex_editor.Encode(); 987 dex_editor.WriteTo(output_path); 988 } 989 } 990 991 std::map<std::string, ApiList> OpenApiFile(const std::string& path) { 992 CHECK(!path.empty()); 993 std::ifstream api_file(path, std::ifstream::in); 994 CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno); 995 996 std::map<std::string, ApiList> api_flag_map; 997 998 size_t line_number = 1; 999 for (std::string line; std::getline(api_file, line); line_number++) { 1000 // Every line contains a comma separated list with the signature as the 1001 // first element and the api flags as the rest 1002 std::vector<std::string> values = android::base::Split(line, ","); 1003 CHECK_GT(values.size(), 1u) << path << ":" << line_number 1004 << ": No flags found: " << line << kErrorHelp; 1005 1006 const std::string& signature = values[0]; 1007 1008 CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number 1009 << ": Duplicate entry: " << signature << kErrorHelp; 1010 1011 ApiList membership; 1012 1013 bool success = ApiList::FromNames(values.begin() + 1, values.end(), &membership); 1014 CHECK(success) << path << ":" << line_number 1015 << ": Some flags were not recognized: " << line << kErrorHelp; 1016 CHECK(membership.IsValid()) << path << ":" << line_number 1017 << ": Invalid combination of flags: " << line << kErrorHelp; 1018 1019 api_flag_map.emplace(signature, membership); 1020 } 1021 1022 api_file.close(); 1023 return api_flag_map; 1024 } 1025 1026 void ListApi() { 1027 if (boot_dex_paths_.empty()) { 1028 Usage("No boot DEX files specified"); 1029 } else if (stub_classpaths_.empty()) { 1030 Usage("No stub DEX files specified"); 1031 } else if (api_flags_path_.empty()) { 1032 Usage("No output path specified"); 1033 } 1034 1035 // Complete list of boot class path members. The associated boolean states 1036 // whether it is public (true) or private (false). 1037 std::map<std::string, std::set<std::string_view>> boot_members; 1038 1039 // Deduplicate errors before printing them. 1040 std::set<std::string> unresolved; 1041 1042 // Open all dex files. 1043 ClassPath boot_classpath(boot_dex_paths_, /* open_writable= */ false); 1044 Hierarchy boot_hierarchy(boot_classpath); 1045 1046 // Mark all boot dex members private. 1047 boot_classpath.ForEachDexMember([&](const DexMember& boot_member) { 1048 boot_members[boot_member.GetApiEntry()] = {}; 1049 }); 1050 1051 // Resolve each SDK dex member against the framework and mark it white. 1052 for (const auto& cp_entry : stub_classpaths_) { 1053 ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"), 1054 /* open_writable= */ false); 1055 Hierarchy stub_hierarchy(stub_classpath); 1056 const ApiStubs::Kind stub_api = cp_entry.second; 1057 1058 stub_classpath.ForEachDexMember( 1059 [&](const DexMember& stub_member) { 1060 if (!stub_hierarchy.IsMemberVisible(stub_member)) { 1061 // Typically fake constructors and inner-class `this` fields. 1062 return; 1063 } 1064 bool resolved = boot_hierarchy.ForEachResolvableMember( 1065 stub_member, 1066 [&](const DexMember& boot_member) { 1067 std::string entry = boot_member.GetApiEntry(); 1068 auto it = boot_members.find(entry); 1069 CHECK(it != boot_members.end()); 1070 it->second.insert(ApiStubs::ToString(stub_api)); 1071 }); 1072 if (!resolved) { 1073 unresolved.insert(stub_member.GetApiEntry()); 1074 } 1075 }); 1076 } 1077 1078 // Print errors. 1079 for (const std::string& str : unresolved) { 1080 LOG(WARNING) << "unresolved: " << str; 1081 } 1082 1083 // Write into public/private API files. 1084 std::ofstream file_flags(api_flags_path_.c_str()); 1085 for (const auto& entry : boot_members) { 1086 if (entry.second.empty()) { 1087 file_flags << entry.first << std::endl; 1088 } else { 1089 file_flags << entry.first << ","; 1090 file_flags << android::base::Join(entry.second, ",") << std::endl; 1091 } 1092 } 1093 file_flags.close(); 1094 } 1095 1096 // Whether to check that all dex entries have been assigned flags. 1097 // Defaults to true. 1098 bool force_assign_all_; 1099 1100 // Paths to DEX files which should be processed. 1101 std::vector<std::string> boot_dex_paths_; 1102 1103 // Output paths where modified DEX files should be written. 1104 std::vector<std::string> output_dex_paths_; 1105 1106 // Set of public API stub classpaths. Each classpath is formed by a list 1107 // of DEX/APK files in the order they appear on the classpath. 1108 std::vector<std::pair<std::string, ApiStubs::Kind>> stub_classpaths_; 1109 1110 // Path to CSV file containing the list of API members and their flags. 1111 // This could be both an input and output path. 1112 std::string api_flags_path_; 1113 }; 1114 1115 } // namespace hiddenapi 1116 } // namespace art 1117 1118 int main(int argc, char** argv) { 1119 art::hiddenapi::original_argc = argc; 1120 art::hiddenapi::original_argv = argv; 1121 android::base::InitLogging(argv); 1122 art::MemMap::Init(); 1123 art::hiddenapi::HiddenApi().Run(argc, argv); 1124 return EXIT_SUCCESS; 1125 } 1126