1 // Copyright (C) 2016 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 #pragma clang diagnostic push
16 #pragma clang diagnostic ignored "-Wunused-parameter"
17 #pragma clang diagnostic ignored "-Wnested-anon-types"
18 #include "proto/abi_dump.pb.h"
19 #pragma clang diagnostic pop
20
21 #include <header_abi_util.h>
22
23 #include <llvm/Support/CommandLine.h>
24 #include <llvm/Support/raw_ostream.h>
25
26 #include <google/protobuf/text_format.h>
27 #include <google/protobuf/io/zero_copy_stream_impl.h>
28
29 #include <memory>
30 #include <fstream>
31 #include <iostream>
32 #include <string>
33 #include <vector>
34
35 #include <stdlib.h>
36
37 static llvm::cl::OptionCategory header_linker_category(
38 "header-abi-linker options");
39
40 static llvm::cl::list<std::string> dump_files(
41 llvm::cl::Positional, llvm::cl::desc("<dump-files>"), llvm::cl::Required,
42 llvm::cl::cat(header_linker_category), llvm::cl::OneOrMore);
43
44 static llvm::cl::opt<std::string> linked_dump(
45 "o", llvm::cl::desc("<linked dump>"), llvm::cl::Required,
46 llvm::cl::cat(header_linker_category));
47
48 static llvm::cl::list<std::string> exported_header_dirs(
49 "I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::Prefix,
50 llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category));
51
52 static llvm::cl::opt<std::string> version_script(
53 "v", llvm::cl::desc("<version_script>"), llvm::cl::Optional,
54 llvm::cl::cat(header_linker_category));
55
56 static llvm::cl::opt<std::string> api(
57 "api", llvm::cl::desc("<api>"), llvm::cl::Optional,
58 llvm::cl::cat(header_linker_category));
59
60 static llvm::cl::opt<std::string> arch(
61 "arch", llvm::cl::desc("<arch>"), llvm::cl::Optional,
62 llvm::cl::cat(header_linker_category));
63
64 static llvm::cl::opt<bool> no_filter(
65 "no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional,
66 llvm::cl::cat(header_linker_category));
67
68 class HeaderAbiLinker {
69 public:
HeaderAbiLinker(const std::vector<std::string> & dump_files,const std::vector<std::string> & exported_header_dirs,const std::string & version_script,const std::string & linked_dump,const std::string & arch,const std::string & api)70 HeaderAbiLinker(
71 const std::vector<std::string> &dump_files,
72 const std::vector<std::string> &exported_header_dirs,
73 const std::string &version_script,
74 const std::string &linked_dump,
75 const std::string &arch,
76 const std::string &api)
77 : dump_files_(dump_files), exported_header_dirs_(exported_header_dirs),
78 version_script_(version_script), out_dump_name_(linked_dump), arch_(arch),
79 api_(api) {};
80
81 bool LinkAndDump();
82
83 private:
84 bool LinkRecords(const abi_dump::TranslationUnit &dump_tu,
85 abi_dump::TranslationUnit *linked_tu);
86
87 bool LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
88 abi_dump::TranslationUnit *linked_tu);
89
90 bool LinkEnums(const abi_dump::TranslationUnit &dump_tu,
91 abi_dump::TranslationUnit *linked_tu);
92
93 bool LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
94 abi_dump::TranslationUnit *linked_tu);
95
96 template <typename T>
97 inline bool LinkDecl(google::protobuf::RepeatedPtrField<T> *dst,
98 std::set<std::string> *link_set,
99 std::set<std::string> *regex_matched_link_set,
100 const std::regex *vs_regex,
101 const google::protobuf::RepeatedPtrField<T> &src,
102 bool use_version_script);
103
104 bool ParseVersionScriptFiles();
105
106 private:
107 const std::vector<std::string> &dump_files_;
108 const std::vector<std::string> &exported_header_dirs_;
109 const std::string &version_script_;
110 const std::string &out_dump_name_;
111 const std::string &arch_;
112 const std::string &api_;
113 // TODO: Add to a map of std::sets instead.
114 std::set<std::string> exported_headers_;
115 std::set<std::string> record_decl_set_;
116 std::set<std::string> function_decl_set_;
117 std::set<std::string> enum_decl_set_;
118 std::set<std::string> globvar_decl_set_;
119 // Version Script Regex Matching.
120 std::set<std::string> functions_regex_matched_set;
121 std::regex functions_vs_regex_;
122 // Version Script Regex Matching.
123 std::set<std::string> globvars_regex_matched_set;
124 std::regex globvars_vs_regex_;
125 };
126
LinkAndDump()127 bool HeaderAbiLinker::LinkAndDump() {
128 abi_dump::TranslationUnit linked_tu;
129 std::ofstream text_output(out_dump_name_);
130 google::protobuf::io::OstreamOutputStream text_os(&text_output);
131 // If a version script is available, we use that as a filter.
132 if (version_script.empty()) {
133 exported_headers_ =
134 abi_util::CollectAllExportedHeaders(exported_header_dirs_);
135 } else if (!ParseVersionScriptFiles()) {
136 llvm::errs() << "Failed to parse stub files for exported symbols\n";
137 return false;
138 }
139
140 for (auto &&i : dump_files_) {
141 abi_dump::TranslationUnit dump_tu;
142 std::ifstream input(i);
143 google::protobuf::io::IstreamInputStream text_is(&input);
144 if (!google::protobuf::TextFormat::Parse(&text_is, &dump_tu) ||
145 !LinkRecords(dump_tu, &linked_tu) ||
146 !LinkFunctions(dump_tu, &linked_tu) ||
147 !LinkEnums(dump_tu, &linked_tu) ||
148 !LinkGlobalVars(dump_tu, &linked_tu)) {
149 llvm::errs() << "Failed to link elements\n";
150 return false;
151 }
152 }
153
154 if (!google::protobuf::TextFormat::Print(linked_tu, &text_os)) {
155 llvm::errs() << "Serialization to ostream failed\n";
156 return false;
157 }
158 return true;
159 }
160
GetSymbol(const abi_dump::RecordDecl & element)161 static std::string GetSymbol(const abi_dump::RecordDecl &element) {
162 return element.mangled_record_name();
163 }
164
GetSymbol(const abi_dump::FunctionDecl & element)165 static std::string GetSymbol(const abi_dump::FunctionDecl &element) {
166 return element.mangled_function_name();
167 }
168
GetSymbol(const abi_dump::EnumDecl & element)169 static std::string GetSymbol(const abi_dump::EnumDecl &element) {
170 return element.basic_abi().linker_set_key();
171 }
172
GetSymbol(const abi_dump::GlobalVarDecl & element)173 static std::string GetSymbol(const abi_dump::GlobalVarDecl &element) {
174 return element.basic_abi().linker_set_key();
175 }
176
QueryRegexMatches(std::set<std::string> * regex_matched_link_set,const std::regex * vs_regex,const std::string & symbol)177 static bool QueryRegexMatches(std::set<std::string> *regex_matched_link_set,
178 const std::regex *vs_regex,
179 const std::string &symbol) {
180 assert(regex_matched_link_set != nullptr);
181 assert(vs_regex != nullptr);
182 if (regex_matched_link_set->find(symbol) != regex_matched_link_set->end()) {
183 return false;
184 }
185 if (std::regex_search(symbol, *vs_regex)) {
186 regex_matched_link_set->insert(symbol);
187 return true;
188 }
189 return false;
190 }
191
CreateRegexMatchExprFromSet(const std::set<std::string> & link_set)192 static std::regex CreateRegexMatchExprFromSet(
193 const std::set<std::string> &link_set) {
194 std::string all_regex_match_str = "";
195 std::set<std::string>::iterator it = link_set.begin();
196 while (it != link_set.end()) {
197 std::string regex_match_str_find_glob =
198 abi_util::FindAndReplace(*it, "\\*", ".*");
199 all_regex_match_str += "(\\b" + regex_match_str_find_glob + "\\b)";
200 if (++it != link_set.end()) {
201 all_regex_match_str += "|";
202 }
203 }
204 if (all_regex_match_str == "") {
205 return std::regex();
206 }
207 return std::regex(all_regex_match_str);
208 }
209
210 template <typename T>
LinkDecl(google::protobuf::RepeatedPtrField<T> * dst,std::set<std::string> * link_set,std::set<std::string> * regex_matched_link_set,const std::regex * vs_regex,const google::protobuf::RepeatedPtrField<T> & src,bool use_version_script)211 inline bool HeaderAbiLinker::LinkDecl(
212 google::protobuf::RepeatedPtrField<T> *dst,
213 std::set<std::string> *link_set,
214 std::set<std::string> *regex_matched_link_set, const std::regex *vs_regex,
215 const google::protobuf::RepeatedPtrField<T> &src, bool use_version_script) {
216 assert(dst != nullptr);
217 assert(link_set != nullptr);
218 for (auto &&element : src) {
219 // If we are not using a version script and exported headers are available,
220 // filter out unexported abi.
221 if (!exported_headers_.empty() &&
222 exported_headers_.find(element.source_file()) ==
223 exported_headers_.end()) {
224 continue;
225 }
226 // Check for the existence of the element in linked dump / symbol file.
227 if (!use_version_script) {
228 if (!link_set->insert(element.basic_abi().linker_set_key()).second) {
229 continue;
230 }
231 } else {
232 std::string element_str = GetSymbol(element);
233 std::set<std::string>::iterator it =
234 link_set->find(element_str);
235 if (it == link_set->end()) {
236 if (!QueryRegexMatches(regex_matched_link_set, vs_regex, element_str)) {
237 continue;
238 }
239 } else {
240 // We get a pre-filled link name set while using version script.
241 link_set->erase(*it); // Avoid multiple instances of the same symbol.
242 }
243 }
244 T *added_element = dst->Add();
245 if (!added_element) {
246 llvm::errs() << "Failed to add element to linked dump\n";
247 return false;
248 }
249 *added_element = element;
250 }
251 return true;
252 }
253
LinkRecords(const abi_dump::TranslationUnit & dump_tu,abi_dump::TranslationUnit * linked_tu)254 bool HeaderAbiLinker::LinkRecords(const abi_dump::TranslationUnit &dump_tu,
255 abi_dump::TranslationUnit *linked_tu) {
256 assert(linked_tu != nullptr);
257 // Even if version scripts are available we take in records, since the symbols
258 // in the version script might reference a record exposed by the library.
259 return LinkDecl(linked_tu->mutable_records(), &record_decl_set_, nullptr,
260 nullptr, dump_tu.records(), false);
261 }
262
LinkFunctions(const abi_dump::TranslationUnit & dump_tu,abi_dump::TranslationUnit * linked_tu)263 bool HeaderAbiLinker::LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
264 abi_dump::TranslationUnit *linked_tu) {
265 assert(linked_tu != nullptr);
266 return LinkDecl(linked_tu->mutable_functions(), &function_decl_set_,
267 &functions_regex_matched_set, &functions_vs_regex_,
268 dump_tu.functions(), (!version_script_.empty()));
269 }
270
LinkEnums(const abi_dump::TranslationUnit & dump_tu,abi_dump::TranslationUnit * linked_tu)271 bool HeaderAbiLinker::LinkEnums(const abi_dump::TranslationUnit &dump_tu,
272 abi_dump::TranslationUnit *linked_tu) {
273 assert(linked_tu != nullptr);
274 // Even if version scripts are available we take in records, since the symbols
275 // in the version script might reference an enum exposed by the library.
276 return LinkDecl(linked_tu->mutable_enums(), &enum_decl_set_, nullptr,
277 nullptr, dump_tu.enums(), false);
278 }
279
LinkGlobalVars(const abi_dump::TranslationUnit & dump_tu,abi_dump::TranslationUnit * linked_tu)280 bool HeaderAbiLinker::LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
281 abi_dump::TranslationUnit *linked_tu) {
282 assert(linked_tu != nullptr);
283 return LinkDecl(linked_tu->mutable_global_vars(), &globvar_decl_set_,
284 &globvars_regex_matched_set, &globvars_vs_regex_,
285 dump_tu.global_vars(), (!version_script.empty()));
286 }
287
ParseVersionScriptFiles()288 bool HeaderAbiLinker::ParseVersionScriptFiles() {
289 abi_util::VersionScriptParser version_script_parser(version_script_, arch_,
290 api_);
291 if (!version_script_parser.Parse()) {
292 return false;
293 }
294 function_decl_set_ = version_script_parser.GetFunctions();
295 globvar_decl_set_ = version_script_parser.GetGlobVars();
296 std::set<std::string> function_regexs =
297 version_script_parser.GetFunctionRegexs();
298 std::set<std::string> globvar_regexs =
299 version_script_parser.GetGlobVarRegexs();
300 functions_vs_regex_ = CreateRegexMatchExprFromSet(function_regexs);
301 globvars_vs_regex_ = CreateRegexMatchExprFromSet(globvar_regexs);
302 return true;
303 }
304
main(int argc,const char ** argv)305 int main(int argc, const char **argv) {
306 GOOGLE_PROTOBUF_VERIFY_VERSION;
307 llvm::cl::ParseCommandLineOptions(argc, argv, "header-linker");
308 if (no_filter) {
309 static_cast<std::vector<std::string> &>(exported_header_dirs).clear();
310 }
311 HeaderAbiLinker Linker(dump_files, exported_header_dirs,
312 version_script, linked_dump, arch, api);
313 if (!Linker.LinkAndDump()) {
314 return -1;
315 }
316 return 0;
317 }
318