Home | History | Annotate | Download | only in hiddenapi
      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 <unordered_set>
     20 
     21 #include "android-base/stringprintf.h"
     22 #include "android-base/strings.h"
     23 
     24 #include "base/os.h"
     25 #include "base/unix_file/fd_file.h"
     26 #include "dex/art_dex_file_loader.h"
     27 #include "dex/dex_file-inl.h"
     28 #include "dex/hidden_api_access_flags.h"
     29 #include "mem_map.h"
     30 
     31 namespace art {
     32 
     33 static int original_argc;
     34 static char** original_argv;
     35 
     36 static std::string CommandLine() {
     37   std::vector<std::string> command;
     38   for (int i = 0; i < original_argc; ++i) {
     39     command.push_back(original_argv[i]);
     40   }
     41   return android::base::Join(command, ' ');
     42 }
     43 
     44 static void UsageErrorV(const char* fmt, va_list ap) {
     45   std::string error;
     46   android::base::StringAppendV(&error, fmt, ap);
     47   LOG(ERROR) << error;
     48 }
     49 
     50 static void UsageError(const char* fmt, ...) {
     51   va_list ap;
     52   va_start(ap, fmt);
     53   UsageErrorV(fmt, ap);
     54   va_end(ap);
     55 }
     56 
     57 NO_RETURN static void Usage(const char* fmt, ...) {
     58   va_list ap;
     59   va_start(ap, fmt);
     60   UsageErrorV(fmt, ap);
     61   va_end(ap);
     62 
     63   UsageError("Command: %s", CommandLine().c_str());
     64   UsageError("Usage: hiddenapi [options]...");
     65   UsageError("");
     66   UsageError("  --dex=<filename>: specify dex file whose members' access flags are to be set.");
     67   UsageError("      At least one --dex parameter must be specified.");
     68   UsageError("");
     69   UsageError("  --light-greylist=<filename>:");
     70   UsageError("  --dark-greylist=<filename>:");
     71   UsageError("  --blacklist=<filename>: text files with signatures of methods/fields to be marked");
     72   UsageError("      greylisted/blacklisted respectively. At least one list must be provided.");
     73   UsageError("");
     74   UsageError("  --print-hidden-api: dump a list of marked methods/fields to the standard output.");
     75   UsageError("      There is no indication which API category they belong to.");
     76   UsageError("");
     77 
     78   exit(EXIT_FAILURE);
     79 }
     80 
     81 class DexClass {
     82  public:
     83   DexClass(const DexFile& dex_file, uint32_t idx)
     84       : dex_file_(dex_file), class_def_(dex_file.GetClassDef(idx)) {}
     85 
     86   const DexFile& GetDexFile() const { return dex_file_; }
     87 
     88   const dex::TypeIndex GetClassIndex() const { return class_def_.class_idx_; }
     89 
     90   const uint8_t* GetData() const { return dex_file_.GetClassData(class_def_); }
     91 
     92   const char* GetDescriptor() const { return dex_file_.GetClassDescriptor(class_def_); }
     93 
     94  private:
     95   const DexFile& dex_file_;
     96   const DexFile::ClassDef& class_def_;
     97 };
     98 
     99 class DexMember {
    100  public:
    101   DexMember(const DexClass& klass, const ClassDataItemIterator& it)
    102       : klass_(klass), it_(it) {
    103     DCHECK_EQ(it_.IsAtMethod() ? GetMethodId().class_idx_ : GetFieldId().class_idx_,
    104               klass_.GetClassIndex());
    105   }
    106 
    107   // Sets hidden bits in access flags and writes them back into the DEX in memory.
    108   // Note that this will not update the cached data of ClassDataItemIterator
    109   // until it iterates over this item again and therefore will fail a CHECK if
    110   // it is called multiple times on the same DexMember.
    111   void SetHidden(HiddenApiAccessFlags::ApiList value) {
    112     const uint32_t old_flags = it_.GetRawMemberAccessFlags();
    113     const uint32_t new_flags = HiddenApiAccessFlags::EncodeForDex(old_flags, value);
    114     CHECK_EQ(UnsignedLeb128Size(new_flags), UnsignedLeb128Size(old_flags));
    115 
    116     // Locate the LEB128-encoded access flags in class data.
    117     // `ptr` initially points to the next ClassData item. We iterate backwards
    118     // until we hit the terminating byte of the previous Leb128 value.
    119     const uint8_t* ptr = it_.DataPointer();
    120     if (it_.IsAtMethod()) {
    121       ptr = ReverseSearchUnsignedLeb128(ptr);
    122       DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), it_.GetMethodCodeItemOffset());
    123     }
    124     ptr = ReverseSearchUnsignedLeb128(ptr);
    125     DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags);
    126 
    127     // Overwrite the access flags.
    128     UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
    129   }
    130 
    131   // Returns true if this member's API entry is in `list`.
    132   bool IsOnApiList(const std::unordered_set<std::string>& list) const {
    133     return list.find(GetApiEntry()) != list.end();
    134   }
    135 
    136   // Constructs a string with a unique signature of this class member.
    137   std::string GetApiEntry() const {
    138     std::stringstream ss;
    139     ss << klass_.GetDescriptor() << "->";
    140     if (it_.IsAtMethod()) {
    141       const DexFile::MethodId& mid = GetMethodId();
    142       ss << klass_.GetDexFile().GetMethodName(mid)
    143          << klass_.GetDexFile().GetMethodSignature(mid).ToString();
    144     } else {
    145       const DexFile::FieldId& fid = GetFieldId();
    146       ss << klass_.GetDexFile().GetFieldName(fid) << ":"
    147          << klass_.GetDexFile().GetFieldTypeDescriptor(fid);
    148     }
    149     return ss.str();
    150   }
    151 
    152  private:
    153   inline const DexFile::MethodId& GetMethodId() const {
    154     DCHECK(it_.IsAtMethod());
    155     return klass_.GetDexFile().GetMethodId(it_.GetMemberIndex());
    156   }
    157 
    158   inline const DexFile::FieldId& GetFieldId() const {
    159     DCHECK(!it_.IsAtMethod());
    160     return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex());
    161   }
    162 
    163   const DexClass& klass_;
    164   const ClassDataItemIterator& it_;
    165 };
    166 
    167 class HiddenApi FINAL {
    168  public:
    169   HiddenApi() : print_hidden_api_(false) {}
    170 
    171   void ParseArgs(int argc, char** argv) {
    172     original_argc = argc;
    173     original_argv = argv;
    174 
    175     android::base::InitLogging(argv);
    176 
    177     // Skip over the command name.
    178     argv++;
    179     argc--;
    180 
    181     if (argc == 0) {
    182       Usage("No arguments specified");
    183     }
    184 
    185     for (int i = 0; i < argc; ++i) {
    186       const StringPiece option(argv[i]);
    187       const bool log_options = false;
    188       if (log_options) {
    189         LOG(INFO) << "hiddenapi: option[" << i << "]=" << argv[i];
    190       }
    191       if (option == "--print-hidden-api") {
    192         print_hidden_api_ = true;
    193       } else if (option.starts_with("--dex=")) {
    194         dex_paths_.push_back(option.substr(strlen("--dex=")).ToString());
    195       } else if (option.starts_with("--light-greylist=")) {
    196         light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString();
    197       } else if (option.starts_with("--dark-greylist=")) {
    198         dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString();
    199       } else if (option.starts_with("--blacklist=")) {
    200         blacklist_path_ = option.substr(strlen("--blacklist=")).ToString();
    201       } else {
    202         Usage("Unknown argument '%s'", option.data());
    203       }
    204     }
    205   }
    206 
    207   bool ProcessDexFiles() {
    208     if (dex_paths_.empty()) {
    209       Usage("No DEX files specified");
    210     }
    211 
    212     if (light_greylist_path_.empty() && dark_greylist_path_.empty() && blacklist_path_.empty()) {
    213       Usage("No API file specified");
    214     }
    215 
    216     if (!light_greylist_path_.empty() && !OpenApiFile(light_greylist_path_, &light_greylist_)) {
    217       return false;
    218     }
    219 
    220     if (!dark_greylist_path_.empty() && !OpenApiFile(dark_greylist_path_, &dark_greylist_)) {
    221       return false;
    222     }
    223 
    224     if (!blacklist_path_.empty() && !OpenApiFile(blacklist_path_, &blacklist_)) {
    225       return false;
    226     }
    227 
    228     MemMap::Init();
    229     if (!OpenDexFiles()) {
    230       return false;
    231     }
    232 
    233     DCHECK(!dex_files_.empty());
    234     for (auto& dex_file : dex_files_) {
    235       CategorizeAllClasses(*dex_file.get());
    236     }
    237 
    238     UpdateDexChecksums();
    239     return true;
    240   }
    241 
    242  private:
    243   bool OpenApiFile(const std::string& path, std::unordered_set<std::string>* list) {
    244     DCHECK(list->empty());
    245     DCHECK(!path.empty());
    246 
    247     std::ifstream api_file(path, std::ifstream::in);
    248     if (api_file.fail()) {
    249       LOG(ERROR) << "Unable to open file '" << path << "' " << strerror(errno);
    250       return false;
    251     }
    252 
    253     for (std::string line; std::getline(api_file, line);) {
    254       list->insert(line);
    255     }
    256 
    257     api_file.close();
    258     return true;
    259   }
    260 
    261   bool OpenDexFiles() {
    262     ArtDexFileLoader dex_loader;
    263     DCHECK(dex_files_.empty());
    264 
    265     for (const std::string& filename : dex_paths_) {
    266       std::string error_msg;
    267 
    268       File fd(filename.c_str(), O_RDWR, /* check_usage */ false);
    269       if (fd.Fd() == -1) {
    270         LOG(ERROR) << "Unable to open file '" << filename << "': " << strerror(errno);
    271         return false;
    272       }
    273 
    274       // Memory-map the dex file with MAP_SHARED flag so that changes in memory
    275       // propagate to the underlying file. We run dex file verification as if
    276       // the dex file was not in boot claass path to check basic assumptions,
    277       // such as that at most one of public/private/protected flag is set.
    278       // We do those checks here and skip them when loading the processed file
    279       // into boot class path.
    280       std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
    281                                                                  /* location */ filename,
    282                                                                  /* verify */ true,
    283                                                                  /* verify_checksum */ true,
    284                                                                  /* mmap_shared */ true,
    285                                                                  &error_msg));
    286       if (dex_file.get() == nullptr) {
    287         LOG(ERROR) << "Open failed for '" << filename << "' " << error_msg;
    288         return false;
    289       }
    290 
    291       if (!dex_file->IsStandardDexFile()) {
    292         LOG(ERROR) << "Expected a standard dex file '" << filename << "'";
    293         return false;
    294       }
    295 
    296       // Change the protection of the memory mapping to read-write.
    297       if (!dex_file->EnableWrite()) {
    298         LOG(ERROR) << "Failed to enable write permission for '" << filename << "'";
    299         return false;
    300       }
    301 
    302       dex_files_.push_back(std::move(dex_file));
    303     }
    304     return true;
    305   }
    306 
    307   void CategorizeAllClasses(const DexFile& dex_file) {
    308     for (uint32_t class_idx = 0; class_idx < dex_file.NumClassDefs(); ++class_idx) {
    309       DexClass klass(dex_file, class_idx);
    310       const uint8_t* klass_data = klass.GetData();
    311       if (klass_data == nullptr) {
    312         continue;
    313       }
    314 
    315       for (ClassDataItemIterator it(klass.GetDexFile(), klass_data); it.HasNext(); it.Next()) {
    316         DexMember member(klass, it);
    317 
    318         // Catagorize member and overwrite its access flags.
    319         // Note that if a member appears on multiple API lists, it will be categorized
    320         // as the strictest.
    321         bool is_hidden = true;
    322         if (member.IsOnApiList(blacklist_)) {
    323           member.SetHidden(HiddenApiAccessFlags::kBlacklist);
    324         } else if (member.IsOnApiList(dark_greylist_)) {
    325           member.SetHidden(HiddenApiAccessFlags::kDarkGreylist);
    326         } else if (member.IsOnApiList(light_greylist_)) {
    327           member.SetHidden(HiddenApiAccessFlags::kLightGreylist);
    328         } else {
    329           member.SetHidden(HiddenApiAccessFlags::kWhitelist);
    330           is_hidden = false;
    331         }
    332 
    333         if (print_hidden_api_ && is_hidden) {
    334           std::cout << member.GetApiEntry() << std::endl;
    335         }
    336       }
    337     }
    338   }
    339 
    340   void UpdateDexChecksums() {
    341     for (auto& dex_file : dex_files_) {
    342       // Obtain a writeable pointer to the dex header.
    343       DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
    344       // Recalculate checksum and overwrite the value in the header.
    345       header->checksum_ = dex_file->CalculateChecksum();
    346     }
    347   }
    348 
    349   // Print signatures of APIs which have been grey-/blacklisted.
    350   bool print_hidden_api_;
    351 
    352   // Paths to DEX files which should be processed.
    353   std::vector<std::string> dex_paths_;
    354 
    355   // Paths to text files which contain the lists of API members.
    356   std::string light_greylist_path_;
    357   std::string dark_greylist_path_;
    358   std::string blacklist_path_;
    359 
    360   // Opened DEX files. Note that these are opened as `const` but eventually will be written into.
    361   std::vector<std::unique_ptr<const DexFile>> dex_files_;
    362 
    363   // Signatures of DEX members loaded from `light_greylist_path_`, `dark_greylist_path_`,
    364   // `blacklist_path_`.
    365   std::unordered_set<std::string> light_greylist_;
    366   std::unordered_set<std::string> dark_greylist_;
    367   std::unordered_set<std::string> blacklist_;
    368 };
    369 
    370 }  // namespace art
    371 
    372 int main(int argc, char** argv) {
    373   art::HiddenApi hiddenapi;
    374 
    375   // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
    376   hiddenapi.ParseArgs(argc, argv);
    377   return hiddenapi.ProcessDexFiles() ? EXIT_SUCCESS : EXIT_FAILURE;
    378 }
    379