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