• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
StringContains(const std::string & line,const std::string & substring)37 static bool StringContains(const std::string &line,
38                            const std::string &substring) {
39   return (line.find(substring) != std::string::npos);
40 }
41 
LineSatisfiesArch(const std::string & line,const std::string arch)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 
VersionScriptParser(const std::string & version_script,const std::string & arch,const std::string & api)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 
ApiStrToInt(const std::string & api)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 
SymbolInArchAndApiVersion(const std::string & line,const std::string & arch,int api)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 
SymbolExported(const std::string & line,const std::string & arch,int api)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 
AddToVars(std::string & symbol)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 
AddToFunctions(std::string & symbol)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 
ParseSymbolLine(const std::string & line)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 
GetLineScope(std::string & line,LineScope scope)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 
ParseInnerBlock(std::ifstream & symbol_ifstream)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 
GetFunctions()172 const std::set<std::string> &VersionScriptParser::GetFunctions() {
173   return functions_;
174 }
175 
GetGlobVars()176 const std::set<std::string> &VersionScriptParser::GetGlobVars() {
177   return globvars_;
178 }
179 
GetFunctionRegexs()180 const std::set<std::string> &VersionScriptParser::GetFunctionRegexs() {
181   return function_regexs_;
182 }
183 
GetGlobVarRegexs()184 const std::set<std::string> &VersionScriptParser::GetGlobVarRegexs() {
185   return globvar_regexs_;
186 }
187 
Parse()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