• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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