// Copyright (C) 2017 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include <header_abi_util.h> #include <llvm/Support/raw_ostream.h> #include <llvm/Support/FileSystem.h> #include <llvm/Support/Path.h> #include <memory> #include <fstream> #include <iostream> #include <set> #include <unordered_set> #include <string> #include <vector> #include <regex> namespace abi_util { #define FUTURE_API 10000 std::unordered_set<std::string> AllArches({"arm", "arm64", "x86", "x86_64", "mips", "mips64"}); static bool StringContains(const std::string &line, const std::string &substring) { return (line.find(substring) != std::string::npos); } static bool LineSatisfiesArch(const std::string &line, const std::string arch) { bool has_arch_tags = false; for (auto &&possible_arch : AllArches) { if (StringContains(line, possible_arch)) { has_arch_tags = true; break; } } return (has_arch_tags && StringContains(line, arch)) || !has_arch_tags; } VersionScriptParser::VersionScriptParser(const std::string &version_script, const std::string &arch, const std::string &api) : version_script_(version_script), arch_(arch), api_(ApiStrToInt(api)) { } int VersionScriptParser::ApiStrToInt(const std::string &api) { // Follow what build/soong/cc/gen_stub_libs.py does. if (api == "current") { return FUTURE_API; } return std::stoi(api); } bool VersionScriptParser::SymbolInArchAndApiVersion(const std::string &line, const std::string &arch, int api) { // If the tags do not have an "introduced" requirement, the symbol is // exported. if (!StringContains(line, "introduced") && LineSatisfiesArch(line, arch)) { return true; } if (line == "future") { return api == FUTURE_API; } const std::string regex_match_string1 = " *introduced-" + arch + "=([0-9]+)"; const std::string regex_match_string2 = " *introduced=([0-9]+)"; std::smatch matcher1; std::smatch matcher2; std::regex match_clause1(regex_match_string1); std::regex match_clause2(regex_match_string2); int matched_api = -1; if (std::regex_search(line, matcher1, match_clause1)) { matched_api = std::stoi(matcher1.str(1)); } else if ((std::regex_search(line, matcher2, match_clause2)) && LineSatisfiesArch(line, arch)) { matched_api = std::stoi(matcher2.str(1)); } // If the arch specific tag / version specific tag was found and the api level // required was greater than the api level offered. return (matched_api <=0 || api >= matched_api); } bool VersionScriptParser::SymbolExported(const std::string &line, const std::string &arch, int api) { // Empty line means that the symbol is exported if (line.empty() || SymbolInArchAndApiVersion(line, arch, api)) { return true; } return false; } void VersionScriptParser::AddToVars(std::string &symbol) { if (symbol.find("*") != std::string::npos) { globvar_regexs_.insert(symbol); } else { globvars_.insert(symbol); } } void VersionScriptParser::AddToFunctions(std::string &symbol) { if (symbol.find("*") != std::string::npos) { function_regexs_.insert(symbol); } else { functions_.insert(symbol); } } bool VersionScriptParser::ParseSymbolLine(const std::string &line) { //The symbol lies before the ; and the tags are after ; std::string::size_type pos = line.find(";"); if (pos == std::string::npos) { llvm::errs() << "Couldn't find end of symbol" << line <<"\n"; return false; } std::string symbol = line.substr(0, pos); std::string::size_type last_space = symbol.find_last_of(' '); symbol = symbol.substr(last_space + 1, pos); std::string tags = line.substr(pos + 1); if (SymbolExported(tags, arch_, api_)) { if (StringContains(tags, "var")) { AddToVars(symbol); } else { AddToFunctions(symbol); } } return true; } typedef VersionScriptParser::LineScope LineScope; LineScope VersionScriptParser::GetLineScope(std::string &line, LineScope scope) { if (StringContains(line, "local:")) { scope = LineScope::local; } return scope; } bool VersionScriptParser::ParseInnerBlock(std::ifstream &symbol_ifstream) { std::string line = ""; LineScope scope = LineScope::global; while (std::getline(symbol_ifstream, line)) { if (line.find("}") != std::string::npos) { break; } if (line.c_str()[0] == '#') { continue; } scope = GetLineScope(line, scope); if (scope != LineScope::global || StringContains(line, "global:")) { continue; } ParseSymbolLine(line); } return true; } const std::set<std::string> &VersionScriptParser::GetFunctions() { return functions_; } const std::set<std::string> &VersionScriptParser::GetGlobVars() { return globvars_; } const std::set<std::string> &VersionScriptParser::GetFunctionRegexs() { return function_regexs_; } const std::set<std::string> &VersionScriptParser::GetGlobVarRegexs() { return globvar_regexs_; } bool VersionScriptParser::Parse() { std::ifstream symbol_ifstream(version_script_); if (!symbol_ifstream.is_open()) { llvm::errs() << "Failed to open version script file\n"; return false; } std::string line = ""; while (std::getline(symbol_ifstream, line)) { // Skip comment lines. if (line.c_str()[0] == '#') { continue; } if (StringContains(line, "{")) { if ((StringContains(line, "PRIVATE"))) { continue; } ParseInnerBlock(symbol_ifstream); } } return true; } } // namespace abi_util