Home | History | Annotate | Download | only in src
      1 // Copyright (C) 2017 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 
     17 #include <llvm/Support/raw_ostream.h>
     18 #include <llvm/Support/FileSystem.h>
     19 #include <llvm/Support/Path.h>
     20 
     21 #include <memory>
     22 #include <fstream>
     23 #include <iostream>
     24 #include <set>
     25 #include <unordered_set>
     26 #include <string>
     27 #include <vector>
     28 #include <regex>
     29 
     30 namespace abi_util {
     31 
     32 #define FUTURE_API 10000
     33 
     34 std::unordered_set<std::string> AllArches({"arm", "arm64", "x86", "x86_64",
     35                                         "mips", "mips64"});
     36 
     37 static bool StringContains(const std::string &line,
     38                            const std::string &substring) {
     39   return (line.find(substring) != std::string::npos);
     40 }
     41 
     42 static bool LineSatisfiesArch(const std::string &line,
     43                               const std::string arch) {
     44   bool has_arch_tags = false;
     45   for (auto &&possible_arch : AllArches) {
     46     if (StringContains(line, possible_arch)) {
     47       has_arch_tags = true;
     48       break;
     49     }
     50   }
     51   return (has_arch_tags && StringContains(line, arch)) || !has_arch_tags;
     52 }
     53 
     54 VersionScriptParser::VersionScriptParser(const std::string &version_script,
     55                                          const std::string &arch,
     56                                          const std::string &api) :
     57   version_script_(version_script), arch_(arch), api_(ApiStrToInt(api)) { }
     58 
     59 int VersionScriptParser::ApiStrToInt(const std::string &api) {
     60   // Follow what build/soong/cc/gen_stub_libs.py does.
     61   if (api == "current") {
     62     return FUTURE_API;
     63   }
     64   return std::stoi(api);
     65 }
     66 
     67 bool VersionScriptParser::SymbolInArchAndApiVersion(const std::string &line,
     68                                                     const std::string &arch,
     69                                                     int api) {
     70   // If the tags do not have an "introduced" requirement, the symbol is
     71   // exported.
     72   if (!StringContains(line, "introduced") && LineSatisfiesArch(line, arch)) {
     73     return true;
     74   }
     75   if (line == "future") {
     76     return api == FUTURE_API;
     77   }
     78   const std::string regex_match_string1 = " *introduced-" + arch + "=([0-9]+)";
     79   const std::string regex_match_string2 = " *introduced=([0-9]+)";
     80   std::smatch matcher1;
     81   std::smatch matcher2;
     82   std::regex match_clause1(regex_match_string1);
     83   std::regex match_clause2(regex_match_string2);
     84   int matched_api = -1;
     85   if (std::regex_search(line, matcher1, match_clause1)) {
     86     matched_api = std::stoi(matcher1.str(1));
     87   } else if ((std::regex_search(line, matcher2, match_clause2)) &&
     88     LineSatisfiesArch(line, arch)) {
     89     matched_api = std::stoi(matcher2.str(1));
     90   }
     91   // If the arch specific tag / version specific tag was found and the api level
     92   // required was greater than the api level offered.
     93   return (matched_api <=0 || api >= matched_api);
     94 }
     95 
     96 bool VersionScriptParser::SymbolExported(const std::string &line,
     97                                          const std::string &arch, int api) {
     98   // Empty line means that the symbol is exported
     99   if (line.empty() || SymbolInArchAndApiVersion(line, arch, api)) {
    100     return true;
    101   }
    102   return false;
    103 }
    104 
    105 void VersionScriptParser::AddToVars(std::string &symbol) {
    106   if (symbol.find("*") != std::string::npos) {
    107     globvar_regexs_.insert(symbol);
    108   } else {
    109     globvars_.insert(symbol);
    110   }
    111 }
    112 
    113 void VersionScriptParser::AddToFunctions(std::string &symbol) {
    114   if (symbol.find("*") != std::string::npos) {
    115     function_regexs_.insert(symbol);
    116   } else {
    117     functions_.insert(symbol);
    118   }
    119 }
    120 
    121 bool VersionScriptParser::ParseSymbolLine(const std::string &line) {
    122   //The symbol lies before the ; and the tags are after ;
    123   std::string::size_type pos = line.find(";");
    124   if (pos == std::string::npos) {
    125     llvm::errs() << "Couldn't find end of symbol" << line <<"\n";
    126     return false;
    127   }
    128   std::string symbol = line.substr(0, pos);
    129   std::string::size_type last_space = symbol.find_last_of(' ');
    130   symbol = symbol.substr(last_space + 1, pos);
    131   std::string tags = line.substr(pos + 1);
    132   if (SymbolExported(tags, arch_, api_)) {
    133     if (StringContains(tags, "var")) {
    134       AddToVars(symbol);
    135     } else {
    136       AddToFunctions(symbol);
    137     }
    138   }
    139   return true;
    140 }
    141 
    142 typedef VersionScriptParser::LineScope LineScope;
    143 
    144 LineScope VersionScriptParser::GetLineScope(std::string &line,
    145                                             LineScope scope) {
    146   if (StringContains(line, "local:")) {
    147     scope = LineScope::local;
    148   }
    149   return scope;
    150 }
    151 
    152 bool VersionScriptParser::ParseInnerBlock(std::ifstream &symbol_ifstream) {
    153   std::string line = "";
    154   LineScope scope = LineScope::global;
    155 
    156   while (std::getline(symbol_ifstream, line)) {
    157     if (line.find("}") != std::string::npos) {
    158       break;
    159     }
    160     if (line.c_str()[0] == '#') {
    161       continue;
    162     }
    163     scope = GetLineScope(line, scope);
    164     if (scope != LineScope::global || StringContains(line, "global:")) {
    165       continue;
    166     }
    167     ParseSymbolLine(line);
    168   }
    169   return true;
    170 }
    171 
    172 const std::set<std::string> &VersionScriptParser::GetFunctions() {
    173   return functions_;
    174 }
    175 
    176 const std::set<std::string> &VersionScriptParser::GetGlobVars() {
    177   return globvars_;
    178 }
    179 
    180 const std::set<std::string> &VersionScriptParser::GetFunctionRegexs() {
    181   return function_regexs_;
    182 }
    183 
    184 const std::set<std::string> &VersionScriptParser::GetGlobVarRegexs() {
    185   return globvar_regexs_;
    186 }
    187 
    188 bool VersionScriptParser::Parse() {
    189   std::ifstream symbol_ifstream(version_script_);
    190   if (!symbol_ifstream.is_open()) {
    191     llvm::errs() << "Failed to open version script file\n";
    192     return false;
    193   }
    194   std::string line = "";
    195 
    196   while (std::getline(symbol_ifstream, line)) {
    197     // Skip comment lines.
    198     if (line.c_str()[0] == '#') {
    199       continue;
    200     }
    201     if (StringContains(line, "{")) {
    202 
    203       if ((StringContains(line, "PRIVATE"))) {
    204         continue;
    205       }
    206       ParseInnerBlock(symbol_ifstream);
    207     }
    208   }
    209   return true;
    210 }
    211 
    212 } // namespace abi_util
    213