diff options
Diffstat (limited to 'tools/hiddenapi/hiddenapi.cc')
-rw-r--r-- | tools/hiddenapi/hiddenapi.cc | 302 |
1 files changed, 129 insertions, 173 deletions
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc index 97e7f4cf3c..2e1ec5a541 100644 --- a/tools/hiddenapi/hiddenapi.cc +++ b/tools/hiddenapi/hiddenapi.cc @@ -61,18 +61,16 @@ NO_RETURN static void Usage(const char* fmt, ...) { va_end(ap); UsageError("Command: %s", CommandLine().c_str()); - UsageError("Usage: hiddenapi [options]..."); + UsageError("Usage: hiddenapi [command_name] [options]..."); UsageError(""); - UsageError(" --dex=<filename>: specify dex file whose members' access flags are to be set."); - UsageError(" At least one --dex parameter must be specified."); + UsageError(" Command \"encode\": encode API list membership in boot dex files"); + UsageError(" --dex=<filename>: dex file which belongs to boot class path,"); + UsageError(" the file will be overwritten"); UsageError(""); - UsageError(" --light-greylist=<filename>:"); - UsageError(" --dark-greylist=<filename>:"); - UsageError(" --blacklist=<filename>: text files with signatures of methods/fields to be marked"); - UsageError(" greylisted/blacklisted respectively. At least one list must be provided."); - UsageError(""); - UsageError(" --print-hidden-api: dump a list of marked methods/fields to the standard output."); - UsageError(" There is no indication which API category they belong to."); + UsageError(" --light-greylist=<filename>:"); + UsageError(" --dark-greylist=<filename>:"); + UsageError(" --blacklist=<filename>:"); + UsageError(" text files with signatures of methods/fields to be annotated"); UsageError(""); exit(EXIT_FAILURE); @@ -128,11 +126,6 @@ class DexMember { UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags); } - // Returns true if this member's API entry is in `list`. - bool IsOnApiList(const std::unordered_set<std::string>& list) const { - return list.find(GetApiEntry()) != list.end(); - } - // Constructs a string with a unique signature of this class member. std::string GetApiEntry() const { std::stringstream ss; @@ -164,112 +157,44 @@ class DexMember { const ClassDataItemIterator& it_; }; -class HiddenApi FINAL { +class ClassPath FINAL { public: - HiddenApi() : print_hidden_api_(false) {} - - void ParseArgs(int argc, char** argv) { - original_argc = argc; - original_argv = argv; - - android::base::InitLogging(argv); - - // Skip over the command name. - argv++; - argc--; - - if (argc == 0) { - Usage("No arguments specified"); - } + explicit ClassPath(const std::vector<std::string>& dex_paths) { + OpenDexFiles(dex_paths); + } - for (int i = 0; i < argc; ++i) { - const StringPiece option(argv[i]); - const bool log_options = false; - if (log_options) { - LOG(INFO) << "hiddenapi: option[" << i << "]=" << argv[i]; - } - if (option == "--print-hidden-api") { - print_hidden_api_ = true; - } else if (option.starts_with("--dex=")) { - dex_paths_.push_back(option.substr(strlen("--dex=")).ToString()); - } else if (option.starts_with("--light-greylist=")) { - light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString(); - } else if (option.starts_with("--dark-greylist=")) { - dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString(); - } else if (option.starts_with("--blacklist=")) { - blacklist_path_ = option.substr(strlen("--blacklist=")).ToString(); - } else { - Usage("Unknown argument '%s'", option.data()); + template<typename Fn> + void ForEachDexMember(Fn fn) { + for (auto& dex_file : dex_files_) { + for (uint32_t class_idx = 0; class_idx < dex_file->NumClassDefs(); ++class_idx) { + DexClass klass(*dex_file, class_idx); + const uint8_t* klass_data = klass.GetData(); + if (klass_data != nullptr) { + for (ClassDataItemIterator it(*dex_file, klass_data); it.HasNext(); it.Next()) { + DexMember member(klass, it); + fn(member); + } + } } } } - bool ProcessDexFiles() { - if (dex_paths_.empty()) { - Usage("No DEX files specified"); - } - - if (light_greylist_path_.empty() && dark_greylist_path_.empty() && blacklist_path_.empty()) { - Usage("No API file specified"); - } - - if (!light_greylist_path_.empty() && !OpenApiFile(light_greylist_path_, &light_greylist_)) { - return false; - } - - if (!dark_greylist_path_.empty() && !OpenApiFile(dark_greylist_path_, &dark_greylist_)) { - return false; - } - - if (!blacklist_path_.empty() && !OpenApiFile(blacklist_path_, &blacklist_)) { - return false; - } - - MemMap::Init(); - if (!OpenDexFiles()) { - return false; - } - - DCHECK(!dex_files_.empty()); + void UpdateDexChecksums() { for (auto& dex_file : dex_files_) { - CategorizeAllClasses(*dex_file.get()); + // Obtain a writeable pointer to the dex header. + DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader()); + // Recalculate checksum and overwrite the value in the header. + header->checksum_ = dex_file->CalculateChecksum(); } - - UpdateDexChecksums(); - return true; } private: - bool OpenApiFile(const std::string& path, std::unordered_set<std::string>* list) { - DCHECK(list->empty()); - DCHECK(!path.empty()); - - std::ifstream api_file(path, std::ifstream::in); - if (api_file.fail()) { - LOG(ERROR) << "Unable to open file '" << path << "' " << strerror(errno); - return false; - } - - for (std::string line; std::getline(api_file, line);) { - list->insert(line); - } - - api_file.close(); - return true; - } - - bool OpenDexFiles() { + void OpenDexFiles(const std::vector<std::string>& dex_paths) { ArtDexFileLoader dex_loader; - DCHECK(dex_files_.empty()); - - for (const std::string& filename : dex_paths_) { - std::string error_msg; - + std::string error_msg; + for (const std::string& filename : dex_paths) { File fd(filename.c_str(), O_RDWR, /* check_usage */ false); - if (fd.Fd() == -1) { - LOG(ERROR) << "Unable to open file '" << filename << "': " << strerror(errno); - return false; - } + CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno); // Memory-map the dex file with MAP_SHARED flag so that changes in memory // propagate to the underlying file. We run dex file verification as if @@ -283,96 +208,127 @@ class HiddenApi FINAL { /* verify_checksum */ true, /* mmap_shared */ true, &error_msg)); - if (dex_file.get() == nullptr) { - LOG(ERROR) << "Open failed for '" << filename << "' " << error_msg; - return false; - } + CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg; + CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'"; + CHECK(dex_file->EnableWrite()) + << "Failed to enable write permission for '" << filename << "'"; + dex_files_.push_back(std::move(dex_file)); + } + } - if (!dex_file->IsStandardDexFile()) { - LOG(ERROR) << "Expected a standard dex file '" << filename << "'"; - return false; - } + // Opened DEX files. Note that these are opened as `const` but may be written into. + std::vector<std::unique_ptr<const DexFile>> dex_files_; +}; - // Change the protection of the memory mapping to read-write. - if (!dex_file->EnableWrite()) { - LOG(ERROR) << "Failed to enable write permission for '" << filename << "'"; - return false; - } +class HiddenApi FINAL { + public: + HiddenApi() {} - dex_files_.push_back(std::move(dex_file)); + void Run(int argc, char** argv) { + switch (ParseArgs(argc, argv)) { + case Command::kEncode: + EncodeAccessFlags(); + break; } - return true; } - void CategorizeAllClasses(const DexFile& dex_file) { - for (uint32_t class_idx = 0; class_idx < dex_file.NumClassDefs(); ++class_idx) { - DexClass klass(dex_file, class_idx); - const uint8_t* klass_data = klass.GetData(); - if (klass_data == nullptr) { - continue; - } - - for (ClassDataItemIterator it(klass.GetDexFile(), klass_data); it.HasNext(); it.Next()) { - DexMember member(klass, it); - - // Catagorize member and overwrite its access flags. - // Note that if a member appears on multiple API lists, it will be categorized - // as the strictest. - bool is_hidden = true; - if (member.IsOnApiList(blacklist_)) { - member.SetHidden(HiddenApiAccessFlags::kBlacklist); - } else if (member.IsOnApiList(dark_greylist_)) { - member.SetHidden(HiddenApiAccessFlags::kDarkGreylist); - } else if (member.IsOnApiList(light_greylist_)) { - member.SetHidden(HiddenApiAccessFlags::kLightGreylist); - } else { - member.SetHidden(HiddenApiAccessFlags::kWhitelist); - is_hidden = false; - } + private: + enum class Command { + // Currently just one command. A "list" command will be added for generating + // a full list of boot class members. + kEncode, + }; + + Command ParseArgs(int argc, char** argv) { + // Skip over the binary's path. + argv++; + argc--; - if (print_hidden_api_ && is_hidden) { - std::cout << member.GetApiEntry() << std::endl; + if (argc > 0) { + const StringPiece command(argv[0]); + if (command == "encode") { + for (int i = 1; i < argc; ++i) { + const StringPiece option(argv[i]); + if (option.starts_with("--dex=")) { + boot_dex_paths_.push_back(option.substr(strlen("--dex=")).ToString()); + } else if (option.starts_with("--light-greylist=")) { + light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString(); + } else if (option.starts_with("--dark-greylist=")) { + dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString(); + } else if (option.starts_with("--blacklist=")) { + blacklist_path_ = option.substr(strlen("--blacklist=")).ToString(); + } else { + Usage("Unknown argument '%s'", option.data()); + } } + return Command::kEncode; + } else { + Usage("Unknown command '%s'", command.data()); } + } else { + Usage("No command specified"); } } - void UpdateDexChecksums() { - for (auto& dex_file : dex_files_) { - // Obtain a writeable pointer to the dex header. - DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader()); - // Recalculate checksum and overwrite the value in the header. - header->checksum_ = dex_file->CalculateChecksum(); + void EncodeAccessFlags() { + if (boot_dex_paths_.empty()) { + Usage("No boot DEX files specified"); } + + // Load dex signatures. + std::map<std::string, HiddenApiAccessFlags::ApiList> api_list; + OpenApiFile(light_greylist_path_, api_list, HiddenApiAccessFlags::kLightGreylist); + OpenApiFile(dark_greylist_path_, api_list, HiddenApiAccessFlags::kDarkGreylist); + OpenApiFile(blacklist_path_, api_list, HiddenApiAccessFlags::kBlacklist); + + // Open all dex files. + ClassPath boot_class_path(boot_dex_paths_); + + // Set access flags of all members. + boot_class_path.ForEachDexMember([&api_list](DexMember& boot_member) { + auto it = api_list.find(boot_member.GetApiEntry()); + if (it == api_list.end()) { + boot_member.SetHidden(HiddenApiAccessFlags::kWhitelist); + } else { + boot_member.SetHidden(it->second); + } + }); + + boot_class_path.UpdateDexChecksums(); } - // Print signatures of APIs which have been grey-/blacklisted. - bool print_hidden_api_; + void OpenApiFile(const std::string& path, + std::map<std::string, HiddenApiAccessFlags::ApiList>& api_list, + HiddenApiAccessFlags::ApiList membership) { + if (path.empty()) { + return; + } + + std::ifstream api_file(path, std::ifstream::in); + CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno); + + for (std::string line; std::getline(api_file, line);) { + CHECK(api_list.find(line) == api_list.end()) + << "Duplicate entry: " << line << " (" << api_list[line] << " and " << membership << ")"; + api_list.emplace(line, membership); + } + api_file.close(); + } // Paths to DEX files which should be processed. - std::vector<std::string> dex_paths_; + std::vector<std::string> boot_dex_paths_; // Paths to text files which contain the lists of API members. std::string light_greylist_path_; std::string dark_greylist_path_; std::string blacklist_path_; - - // Opened DEX files. Note that these are opened as `const` but eventually will be written into. - std::vector<std::unique_ptr<const DexFile>> dex_files_; - - // Signatures of DEX members loaded from `light_greylist_path_`, `dark_greylist_path_`, - // `blacklist_path_`. - std::unordered_set<std::string> light_greylist_; - std::unordered_set<std::string> dark_greylist_; - std::unordered_set<std::string> blacklist_; }; } // namespace art int main(int argc, char** argv) { - art::HiddenApi hiddenapi; - - // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. - hiddenapi.ParseArgs(argc, argv); - return hiddenapi.ProcessDexFiles() ? EXIT_SUCCESS : EXIT_FAILURE; + android::base::InitLogging(argv); + art::MemMap::Init(); + art::HiddenApi().Run(argc, argv); + return EXIT_SUCCESS; } |