Home | History | Annotate | Download | only in src
      1 // Copyright (C) 2016 The Android Open Source Project
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include <header_abi_util.h>
     16 #include <ir_representation.h>
     17 
     18 #include <llvm/Support/CommandLine.h>
     19 #include <llvm/Support/raw_ostream.h>
     20 
     21 #include <memory>
     22 #include <mutex>
     23 #include <fstream>
     24 #include <iostream>
     25 #include <string>
     26 #include <thread>
     27 #include <vector>
     28 
     29 #include <stdlib.h>
     30 
     31 static constexpr std::size_t kSourcesPerBatchThread = 7;
     32 
     33 static llvm::cl::OptionCategory header_linker_category(
     34     "header-abi-linker options");
     35 
     36 static llvm::cl::list<std::string> dump_files(
     37     llvm::cl::Positional, llvm::cl::desc("<dump-files>"), llvm::cl::Required,
     38     llvm::cl::cat(header_linker_category), llvm::cl::OneOrMore);
     39 
     40 static llvm::cl::opt<std::string> linked_dump(
     41     "o", llvm::cl::desc("<linked dump>"), llvm::cl::Required,
     42     llvm::cl::cat(header_linker_category));
     43 
     44 static llvm::cl::list<std::string> exported_header_dirs(
     45     "I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::Prefix,
     46     llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category));
     47 
     48 static llvm::cl::opt<std::string> version_script(
     49     "v", llvm::cl::desc("<version_script>"), llvm::cl::Optional,
     50     llvm::cl::cat(header_linker_category));
     51 
     52 static llvm::cl::opt<std::string> api(
     53     "api", llvm::cl::desc("<api>"), llvm::cl::Optional,
     54     llvm::cl::cat(header_linker_category));
     55 
     56 static llvm::cl::opt<std::string> arch(
     57     "arch", llvm::cl::desc("<arch>"), llvm::cl::Optional,
     58     llvm::cl::cat(header_linker_category));
     59 
     60 static llvm::cl::opt<bool> no_filter(
     61     "no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional,
     62     llvm::cl::cat(header_linker_category));
     63 
     64 static llvm::cl::opt<std::string> so_file(
     65     "so", llvm::cl::desc("<path to so file>"), llvm::cl::Optional,
     66     llvm::cl::cat(header_linker_category));
     67 
     68 static llvm::cl::opt<abi_util::TextFormatIR> text_format(
     69     "text-format", llvm::cl::desc("Specify text format of abi dumps"),
     70     llvm::cl::values(clEnumValN(abi_util::TextFormatIR::ProtobufTextFormat,
     71                                 "ProtobufTextFormat", "ProtobufTextFormat"),
     72                      clEnumValEnd),
     73     llvm::cl::init(abi_util::TextFormatIR::ProtobufTextFormat),
     74     llvm::cl::cat(header_linker_category));
     75 
     76 class HeaderAbiLinker {
     77  public:
     78   HeaderAbiLinker(
     79       const std::vector<std::string> &dump_files,
     80       const std::vector<std::string> &exported_header_dirs,
     81       const std::string &version_script,
     82       const std::string &so_file,
     83       const std::string &linked_dump,
     84       const std::string &arch,
     85       const std::string &api)
     86     : dump_files_(dump_files), exported_header_dirs_(exported_header_dirs),
     87     version_script_(version_script), so_file_(so_file),
     88     out_dump_name_(linked_dump), arch_(arch), api_(api) {};
     89 
     90   bool LinkAndDump();
     91 
     92  private:
     93   template <typename T>
     94   bool LinkDecl(abi_util::IRDumper *dst,
     95                        std::set<std::string> *link_set,
     96                        std::set<std::string> *regex_matched_link_set,
     97                        const std::regex *vs_regex,
     98                        const abi_util::AbiElementMap<T> &src,
     99                        bool use_version_script);
    100 
    101   bool ParseVersionScriptFiles();
    102 
    103   bool ParseSoFile();
    104 
    105   bool LinkTypes(const abi_util::TextFormatToIRReader *ir_reader,
    106                  abi_util::IRDumper *ir_dumper);
    107 
    108   bool LinkFunctions(const abi_util::TextFormatToIRReader *ir_reader,
    109                      abi_util::IRDumper *ir_dumper);
    110 
    111   bool LinkGlobalVars(const abi_util::TextFormatToIRReader *ir_reader,
    112                       abi_util::IRDumper *ir_dumper);
    113 
    114   bool AddElfSymbols(abi_util::IRDumper *ir_dumper);
    115 
    116 
    117  private:
    118   const std::vector<std::string> &dump_files_;
    119   const std::vector<std::string> &exported_header_dirs_;
    120   const std::string &version_script_;
    121   const std::string &so_file_;
    122   const std::string &out_dump_name_;
    123   const std::string &arch_;
    124   const std::string &api_;
    125   // TODO: Add to a map of std::sets instead.
    126   std::set<std::string> exported_headers_;
    127   std::set<std::string> types_set_;
    128   std::set<std::string> function_decl_set_;
    129   std::set<std::string> globvar_decl_set_;
    130   // Version Script Regex Matching.
    131   std::set<std::string> functions_regex_matched_set;
    132   std::regex functions_vs_regex_;
    133   // Version Script Regex Matching.
    134   std::set<std::string> globvars_regex_matched_set;
    135   std::regex globvars_vs_regex_;
    136 };
    137 
    138 template <typename T, typename Iterable>
    139 static bool AddElfSymbols(abi_util::IRDumper *dst, const Iterable &symbols) {
    140   for (auto &&symbol : symbols) {
    141     T elf_symbol(symbol);
    142     if (!dst->AddElfSymbolMessageIR(&elf_symbol)) {
    143       return false;
    144     }
    145   }
    146   return true;
    147 }
    148 
    149 // To be called right after parsing the .so file / version script.
    150 bool HeaderAbiLinker::AddElfSymbols(abi_util::IRDumper *ir_dumper) {
    151   return ::AddElfSymbols<abi_util::ElfFunctionIR>(ir_dumper,
    152                                                   function_decl_set_) &&
    153       ::AddElfSymbols<abi_util::ElfObjectIR>(ir_dumper,
    154                                              globvar_decl_set_);
    155 }
    156 
    157 static void DeDuplicateAbiElementsThread(
    158     const std::vector<std::string> &dump_files,
    159     const std::set<std::string> *exported_headers,
    160     abi_util::TextFormatToIRReader *greader, std::mutex *greader_lock,
    161     std::atomic<std::size_t> *cnt) {
    162   std::unique_ptr<abi_util::TextFormatToIRReader> local_reader =
    163       abi_util::TextFormatToIRReader::CreateTextFormatToIRReader(
    164           text_format, exported_headers);
    165   auto begin_it = dump_files.begin();
    166   std::size_t num_sources = dump_files.size();
    167   while (1) {
    168     std::size_t i = cnt->fetch_add(kSourcesPerBatchThread);
    169     if (i >= num_sources) {
    170       break;
    171     }
    172     std::size_t end = std::min(i + kSourcesPerBatchThread, num_sources);
    173     for (auto it = begin_it; it != begin_it + end; it++) {
    174       std::unique_ptr<abi_util::TextFormatToIRReader> reader =
    175           abi_util::TextFormatToIRReader::CreateTextFormatToIRReader(
    176               text_format, exported_headers);
    177       assert(reader != nullptr);
    178       if (!reader->ReadDump(*it)) {
    179         llvm::errs() << "ReadDump failed\n";
    180         ::exit(1);
    181       }
    182       // This merge is needed since the iterators might not be contigous.
    183       local_reader->MergeGraphs(*reader);
    184     }
    185   }
    186   std::lock_guard<std::mutex> lock(*greader_lock);
    187   greader->MergeGraphs(*local_reader);
    188 }
    189 
    190 bool HeaderAbiLinker::LinkAndDump() {
    191   // If the user specifies that a version script should be used, use that.
    192   if (!so_file_.empty()) {
    193     exported_headers_ =
    194         abi_util::CollectAllExportedHeaders(exported_header_dirs_);
    195     if (!ParseSoFile()) {
    196       llvm::errs() << "Couldn't parse so file\n";
    197       return false;
    198     }
    199   } else if (!ParseVersionScriptFiles()) {
    200     llvm::errs() << "Failed to parse stub files for exported symbols\n";
    201     return false;
    202   }
    203   std::unique_ptr<abi_util::IRDumper> ir_dumper =
    204       abi_util::IRDumper::CreateIRDumper(text_format, out_dump_name_);
    205   assert(ir_dumper != nullptr);
    206   AddElfSymbols(ir_dumper.get());
    207   // Create a reader, on which we never actually call ReadDump(), since multiple
    208   // dump files are associated with it.
    209   std::unique_ptr<abi_util::TextFormatToIRReader> greader =
    210       abi_util::TextFormatToIRReader::CreateTextFormatToIRReader(
    211           text_format, &exported_headers_);
    212   std::size_t max_threads = std::thread::hardware_concurrency();
    213   std::size_t num_threads = kSourcesPerBatchThread < dump_files_.size() ?
    214                     std::min(dump_files_.size() / kSourcesPerBatchThread,
    215                              max_threads) : 0;
    216   std::vector<std::thread> threads;
    217   std::atomic<std::size_t> cnt(0);
    218   std::mutex greader_lock;
    219   for (std::size_t i = 1; i < num_threads; i++) {
    220     threads.emplace_back(DeDuplicateAbiElementsThread, dump_files_,
    221                          &exported_headers_, greader.get(), &greader_lock,
    222                          &cnt);
    223   }
    224   DeDuplicateAbiElementsThread(dump_files_, &exported_headers_, greader.get(),
    225                                &greader_lock, &cnt);
    226   for (auto &thread : threads) {
    227     thread.join();
    228   }
    229 
    230   if (!LinkTypes(greader.get(), ir_dumper.get()) ||
    231       !LinkFunctions(greader.get(), ir_dumper.get()) ||
    232       !LinkGlobalVars(greader.get(), ir_dumper.get())) {
    233     llvm::errs() << "Failed to link elements\n";
    234     return false;
    235   }
    236   if (!ir_dumper->Dump()) {
    237     llvm::errs() << "Serialization to ostream failed\n";
    238     return false;
    239   }
    240   return true;
    241 }
    242 
    243 static bool QueryRegexMatches(std::set<std::string> *regex_matched_link_set,
    244                               const std::regex *vs_regex,
    245                               const std::string &symbol) {
    246   assert(regex_matched_link_set != nullptr);
    247   assert(vs_regex != nullptr);
    248   if (regex_matched_link_set->find(symbol) != regex_matched_link_set->end()) {
    249     return false;
    250   }
    251   if (std::regex_search(symbol, *vs_regex)) {
    252     regex_matched_link_set->insert(symbol);
    253     return true;
    254   }
    255   return false;
    256 }
    257 
    258 static std::regex CreateRegexMatchExprFromSet(
    259     const std::set<std::string> &link_set) {
    260   std::string all_regex_match_str = "";
    261   std::set<std::string>::iterator it = link_set.begin();
    262   while (it != link_set.end()) {
    263     std::string regex_match_str_find_glob =
    264       abi_util::FindAndReplace(*it, "\\*", ".*");
    265     all_regex_match_str += "(\\b" + regex_match_str_find_glob + "\\b)";
    266     if (++it != link_set.end()) {
    267       all_regex_match_str += "|";
    268     }
    269   }
    270   if (all_regex_match_str == "") {
    271     return std::regex();
    272   }
    273   return std::regex(all_regex_match_str);
    274 }
    275 
    276 template <typename T>
    277 bool HeaderAbiLinker::LinkDecl(
    278     abi_util::IRDumper *dst, std::set<std::string> *link_set,
    279     std::set<std::string> *regex_matched_link_set, const std::regex *vs_regex,
    280     const  abi_util::AbiElementMap<T> &src, bool use_version_script_or_so) {
    281   assert(dst != nullptr);
    282   assert(link_set != nullptr);
    283   for (auto &&element : src) {
    284     // If we are not using a version script and exported headers are available,
    285     // filter out unexported abi.
    286     std::string source_file = element.second.GetSourceFile();
    287     // Builtin types will not have source file information.
    288     if (!exported_headers_.empty() && !source_file.empty() &&
    289         exported_headers_.find(source_file) ==
    290         exported_headers_.end()) {
    291       continue;
    292     }
    293     const std::string &element_str = element.first;
    294     // Check for the existence of the element in linked dump / symbol file.
    295     if (use_version_script_or_so) {
    296       std::set<std::string>::iterator it =
    297           link_set->find(element_str);
    298       if (it == link_set->end()) {
    299         if (!QueryRegexMatches(regex_matched_link_set, vs_regex, element_str)) {
    300           continue;
    301         }
    302       } else {
    303         // We get a pre-filled link name set while using version script.
    304         link_set->erase(*it); // Avoid multiple instances of the same symbol.
    305       }
    306     }
    307     if (!dst->AddLinkableMessageIR(&(element.second))) {
    308       llvm::errs() << "Failed to add element to linked dump\n";
    309       return false;
    310     }
    311   }
    312   return true;
    313 }
    314 
    315 bool HeaderAbiLinker::LinkTypes(const abi_util::TextFormatToIRReader *reader,
    316                                 abi_util::IRDumper *ir_dumper) {
    317   assert(reader != nullptr);
    318   assert(ir_dumper != nullptr);
    319   // Even if version scripts are available we take in types, since the symbols
    320   // in the version script might reference a type exposed by the library.
    321   return LinkDecl(ir_dumper, &types_set_, nullptr,
    322                   nullptr, reader->GetRecordTypes(), false) &&
    323       LinkDecl(ir_dumper, &types_set_, nullptr,
    324                nullptr, reader->GetEnumTypes(), false) &&
    325       LinkDecl(ir_dumper, &types_set_, nullptr, nullptr,
    326                reader->GetFunctionTypes(), false) &&
    327       LinkDecl(ir_dumper, &types_set_, nullptr,
    328                nullptr, reader->GetBuiltinTypes(), false) &&
    329       LinkDecl(ir_dumper, &types_set_, nullptr,
    330                nullptr, reader->GetPointerTypes(), false) &&
    331       LinkDecl(ir_dumper, &types_set_, nullptr,
    332                nullptr, reader->GetRvalueReferenceTypes(), false) &&
    333       LinkDecl(ir_dumper, &types_set_, nullptr,
    334                nullptr, reader->GetLvalueReferenceTypes(), false) &&
    335       LinkDecl(ir_dumper, &types_set_, nullptr,
    336                nullptr, reader->GetArrayTypes(), false) &&
    337       LinkDecl(ir_dumper, &types_set_, nullptr,
    338                nullptr, reader->GetQualifiedTypes(), false);
    339 }
    340 
    341 bool HeaderAbiLinker::LinkFunctions(
    342     const abi_util::TextFormatToIRReader *reader,
    343     abi_util::IRDumper *ir_dumper) {
    344 
    345   assert(reader != nullptr);
    346   return LinkDecl(ir_dumper, &function_decl_set_,
    347                   &functions_regex_matched_set, &functions_vs_regex_,
    348                   reader->GetFunctions(),
    349                   (!version_script_.empty() || !so_file_.empty()));
    350 }
    351 
    352 bool HeaderAbiLinker::LinkGlobalVars(
    353     const abi_util::TextFormatToIRReader *reader,
    354     abi_util::IRDumper *ir_dumper) {
    355 
    356   assert(reader != nullptr);
    357   return LinkDecl(ir_dumper, &globvar_decl_set_,
    358                   &globvars_regex_matched_set, &globvars_vs_regex_,
    359                   reader->GetGlobalVariables(),
    360                   (!version_script.empty() || !so_file_.empty()));
    361 }
    362 
    363 bool HeaderAbiLinker::ParseVersionScriptFiles() {
    364   abi_util::VersionScriptParser version_script_parser(version_script_, arch_,
    365                                                       api_);
    366   if (!version_script_parser.Parse()) {
    367     llvm::errs() << "Failed to parse version script\n";
    368     return false;
    369   }
    370   function_decl_set_ = version_script_parser.GetFunctions();
    371   globvar_decl_set_ = version_script_parser.GetGlobVars();
    372   std::set<std::string> function_regexs =
    373       version_script_parser.GetFunctionRegexs();
    374   std::set<std::string> globvar_regexs =
    375       version_script_parser.GetGlobVarRegexs();
    376   functions_vs_regex_ = CreateRegexMatchExprFromSet(function_regexs);
    377   globvars_vs_regex_ = CreateRegexMatchExprFromSet(globvar_regexs);
    378   return true;
    379 }
    380 
    381 bool HeaderAbiLinker::ParseSoFile() {
    382  auto Binary = llvm::object::createBinary(so_file_);
    383 
    384   if (!Binary) {
    385     llvm::errs() << "Couldn't really create object File \n";
    386     return false;
    387   }
    388   llvm::object::ObjectFile *objfile =
    389       llvm::dyn_cast<llvm::object::ObjectFile>(&(*Binary.get().getBinary()));
    390   if (!objfile) {
    391     llvm::errs() << "Not an object file\n";
    392     return false;
    393   }
    394 
    395   std::unique_ptr<abi_util::SoFileParser> so_parser =
    396       abi_util::SoFileParser::Create(objfile);
    397   if (so_parser == nullptr) {
    398     llvm::errs() << "Couldn't create soFile Parser\n";
    399     return false;
    400   }
    401   so_parser->GetSymbols();
    402   function_decl_set_ = so_parser->GetFunctions();
    403   globvar_decl_set_ = so_parser->GetGlobVars();
    404   return true;
    405 }
    406 
    407 int main(int argc, const char **argv) {
    408   llvm::cl::ParseCommandLineOptions(argc, argv, "header-linker");
    409   if (so_file.empty() && version_script.empty()) {
    410     llvm::errs() << "One of -so or -v needs to be specified\n";
    411     return -1;
    412   }
    413   if (no_filter) {
    414     static_cast<std::vector<std::string> &>(exported_header_dirs).clear();
    415   }
    416   HeaderAbiLinker Linker(dump_files, exported_header_dirs, version_script,
    417                          so_file, linked_dump, arch, api);
    418 
    419   if (!Linker.LinkAndDump()) {
    420     llvm::errs() << "Failed to link and dump elements\n";
    421     return -1;
    422   }
    423   return 0;
    424 }
    425