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