• 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 #include "linker/module_merger.h"
16 #include "repr/ir_dumper.h"
17 #include "repr/ir_reader.h"
18 #include "repr/ir_representation.h"
19 #include "repr/symbol/so_file_parser.h"
20 #include "repr/symbol/version_script_parser.h"
21 #include "utils/command_line_utils.h"
22 #include "utils/source_path_utils.h"
23 
24 #include <llvm/ADT/Optional.h>
25 #include <llvm/Support/CommandLine.h>
26 #include <llvm/Support/raw_ostream.h>
27 
28 #include <fstream>
29 #include <functional>
30 #include <iostream>
31 #include <memory>
32 #include <string>
33 #include <thread>
34 #include <vector>
35 
36 #include <stdlib.h>
37 
38 
39 using namespace header_checker;
40 using header_checker::repr::TextFormatIR;
41 using header_checker::utils::CollectAllExportedHeaders;
42 using header_checker::utils::HideIrrelevantCommandLineOptions;
43 using header_checker::utils::ParseRootDirs;
44 using header_checker::utils::RootDir;
45 
46 
47 static llvm::cl::OptionCategory header_linker_category(
48     "header-abi-linker options");
49 
50 static llvm::cl::list<std::string> dump_files(
51     llvm::cl::Positional, llvm::cl::desc("<dump-files>"), llvm::cl::ZeroOrMore,
52     llvm::cl::cat(header_linker_category));
53 
54 static llvm::cl::opt<std::string> linked_dump(
55     "o", llvm::cl::desc("<linked dump>"), llvm::cl::Required,
56     llvm::cl::cat(header_linker_category));
57 
58 static llvm::cl::list<std::string> exported_header_dirs(
59     "I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::Prefix,
60     llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category));
61 
62 static llvm::cl::list<std::string> root_dirs(
63     "root-dir",
64     llvm::cl::desc("Specify the directory that the paths in the dump files "
65                    "are relative to. The format is <path>:<replacement> or "
66                    "<path>. If this option is not specified, it defaults to "
67                    "current working directory."),
68     llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category));
69 
70 static llvm::cl::opt<std::string> version_script(
71     "v", llvm::cl::desc("<version_script>"), llvm::cl::Optional,
72     llvm::cl::cat(header_linker_category));
73 
74 static llvm::cl::list<std::string> excluded_symbol_versions(
75     "exclude-symbol-version", llvm::cl::Optional,
76     llvm::cl::cat(header_linker_category));
77 
78 static llvm::cl::list<std::string> excluded_symbol_tags(
79     "exclude-symbol-tag", llvm::cl::Optional,
80     llvm::cl::cat(header_linker_category));
81 
82 static llvm::cl::opt<std::string> api(
83     "api", llvm::cl::desc("<api>"), llvm::cl::Optional,
84     llvm::cl::init("current"),
85     llvm::cl::cat(header_linker_category));
86 
87 static llvm::cl::opt<std::string> arch(
88     "arch", llvm::cl::desc("<arch>"), llvm::cl::Optional,
89     llvm::cl::cat(header_linker_category));
90 
91 static llvm::cl::opt<bool> no_filter(
92     "no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional,
93     llvm::cl::cat(header_linker_category));
94 
95 static llvm::cl::opt<std::string> so_file(
96     "so", llvm::cl::desc("<path to so file>"), llvm::cl::Optional,
97     llvm::cl::cat(header_linker_category));
98 
99 static llvm::cl::opt<TextFormatIR> input_format(
100     "input-format", llvm::cl::desc("Specify format of input dump files"),
101     llvm::cl::values(clEnumValN(TextFormatIR::ProtobufTextFormat,
102                                 "ProtobufTextFormat", "ProtobufTextFormat"),
103                      clEnumValN(TextFormatIR::Json, "Json", "JSON")),
104     llvm::cl::init(TextFormatIR::Json),
105     llvm::cl::cat(header_linker_category));
106 
107 static llvm::cl::opt<TextFormatIR> output_format(
108     "output-format", llvm::cl::desc("Specify format of output dump file"),
109     llvm::cl::values(clEnumValN(TextFormatIR::ProtobufTextFormat,
110                                 "ProtobufTextFormat", "ProtobufTextFormat"),
111                      clEnumValN(TextFormatIR::Json, "Json", "JSON")),
112     llvm::cl::init(TextFormatIR::Json),
113     llvm::cl::cat(header_linker_category));
114 
115 static llvm::cl::opt<std::size_t> sources_per_thread(
116     "sources-per-thread",
117     llvm::cl::desc("Specify number of input dump files each thread parses, for "
118                    "debugging merging types"),
119     llvm::cl::init(7), llvm::cl::Hidden);
120 
121 class HeaderAbiLinker {
122  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 & so_file,const std::string & linked_dump,const std::string & arch,const std::string & api,const std::vector<std::string> & excluded_symbol_versions,const std::vector<std::string> & excluded_symbol_tags)123   HeaderAbiLinker(
124       const std::vector<std::string> &dump_files,
125       const std::vector<std::string> &exported_header_dirs,
126       const std::string &version_script,
127       const std::string &so_file,
128       const std::string &linked_dump,
129       const std::string &arch,
130       const std::string &api,
131       const std::vector<std::string> &excluded_symbol_versions,
132       const std::vector<std::string> &excluded_symbol_tags)
133       : dump_files_(dump_files), exported_header_dirs_(exported_header_dirs),
134         version_script_(version_script), so_file_(so_file),
135         out_dump_name_(linked_dump), arch_(arch), api_(api),
136         excluded_symbol_versions_(excluded_symbol_versions),
137         excluded_symbol_tags_(excluded_symbol_tags) {}
138 
139   bool LinkAndDump();
140 
141  private:
142   template <typename T>
143   bool LinkDecl(repr::ModuleIR *dst,
144                 const repr::AbiElementMap<T> &src,
145                 const std::function<bool(const std::string &)> &symbol_filter);
146 
147   std::unique_ptr<linker::ModuleMerger> ReadInputDumpFiles();
148 
149   bool ReadExportedSymbols();
150 
151   bool ReadExportedSymbolsFromVersionScript();
152 
153   bool ReadExportedSymbolsFromSharedObjectFile();
154 
155   bool LinkTypes(const repr::ModuleIR &module, repr::ModuleIR *linked_module);
156 
157   bool LinkFunctions(const repr::ModuleIR &module,
158                      repr::ModuleIR *linked_module);
159 
160   bool LinkGlobalVars(const repr::ModuleIR &module,
161                       repr::ModuleIR *linked_module);
162 
163   bool LinkExportedSymbols(repr::ModuleIR *linked_module);
164 
165   bool LinkExportedSymbols(repr::ModuleIR *linked_module,
166                            const repr::ExportedSymbolSet &exported_symbols);
167 
168   template <typename SymbolMap>
169   bool LinkExportedSymbols(repr::ModuleIR *linked_module,
170                            const SymbolMap &symbols);
171 
172   // Check whether a symbol name is considered as exported.  If both
173   // `shared_object_symbols_` and `version_script_symbols_` exists, the symbol
174   // name must pass the `HasSymbol()` test in both cases.
175   bool IsSymbolExported(const std::string &name) const;
176 
177  private:
178   const std::vector<std::string> &dump_files_;
179   const std::vector<std::string> &exported_header_dirs_;
180   const std::string &version_script_;
181   const std::string &so_file_;
182   const std::string &out_dump_name_;
183   const std::string &arch_;
184   const std::string &api_;
185   const std::vector<std::string> &excluded_symbol_versions_;
186   const std::vector<std::string> &excluded_symbol_tags_;
187 
188   std::set<std::string> exported_headers_;
189 
190   // Exported symbols
191   std::unique_ptr<repr::ExportedSymbolSet> shared_object_symbols_;
192 
193   std::unique_ptr<repr::ExportedSymbolSet> version_script_symbols_;
194 };
195 
DeDuplicateAbiElementsThread(std::vector<std::string>::const_iterator dump_files_begin,std::vector<std::string>::const_iterator dump_files_end,const std::set<std::string> * exported_headers,linker::ModuleMerger * merger)196 static void DeDuplicateAbiElementsThread(
197     std::vector<std::string>::const_iterator dump_files_begin,
198     std::vector<std::string>::const_iterator dump_files_end,
199     const std::set<std::string> *exported_headers,
200     linker::ModuleMerger *merger) {
201   for (auto it = dump_files_begin; it != dump_files_end; it++) {
202     std::unique_ptr<repr::IRReader> reader =
203         repr::IRReader::CreateIRReader(input_format, exported_headers);
204     assert(reader != nullptr);
205     if (!reader->ReadDump(*it)) {
206       llvm::errs() << "ReadDump failed\n";
207       ::exit(1);
208     }
209     merger->MergeGraphs(reader->GetModule());
210   }
211 }
212 
ReadInputDumpFiles()213 std::unique_ptr<linker::ModuleMerger> HeaderAbiLinker::ReadInputDumpFiles() {
214   std::unique_ptr<linker::ModuleMerger> merger(
215       new linker::ModuleMerger(&exported_headers_));
216   std::size_t max_threads = std::thread::hardware_concurrency();
217   std::size_t num_threads = std::max<std::size_t>(
218       std::min(dump_files_.size() / sources_per_thread, max_threads), 1);
219   std::vector<std::thread> threads;
220   std::vector<linker::ModuleMerger> thread_mergers;
221   thread_mergers.reserve(num_threads - 1);
222 
223   std::size_t dump_files_index = 0;
224   std::size_t first_end_index = 0;
225   for (std::size_t i = 0; i < num_threads; i++) {
226     std::size_t cnt = dump_files_.size() / num_threads +
227                       (i < dump_files_.size() % num_threads ? 1 : 0);
228     if (i == 0) {
229       first_end_index = cnt;
230     } else {
231       thread_mergers.emplace_back(&exported_headers_);
232       threads.emplace_back(DeDuplicateAbiElementsThread,
233                            dump_files_.begin() + dump_files_index,
234                            dump_files_.begin() + dump_files_index + cnt,
235                            &exported_headers_, &thread_mergers.back());
236     }
237     dump_files_index += cnt;
238   }
239   assert(dump_files_index == dump_files_.size());
240 
241   DeDuplicateAbiElementsThread(dump_files_.begin(),
242                                dump_files_.begin() + first_end_index,
243                                &exported_headers_, merger.get());
244 
245   for (std::size_t i = 0; i < threads.size(); i++) {
246     threads[i].join();
247     merger->MergeGraphs(thread_mergers[i].GetModule());
248   }
249 
250   return merger;
251 }
252 
LinkAndDump()253 bool HeaderAbiLinker::LinkAndDump() {
254   // Extract exported functions and variables from a shared lib or a version
255   // script.
256   if (!ReadExportedSymbols()) {
257     return false;
258   }
259 
260   // Construct the list of exported headers for source location filtering.
261   exported_headers_ = CollectAllExportedHeaders(exported_header_dirs_,
262                                                 ParseRootDirs(root_dirs));
263 
264   // Read all input ABI dumps.
265   auto merger = ReadInputDumpFiles();
266 
267   const repr::ModuleIR &module = merger->GetModule();
268 
269   // Link input ABI dumps.
270   std::unique_ptr<repr::ModuleIR> linked_module(
271       new repr::ModuleIR(&exported_headers_));
272 
273   if (!LinkExportedSymbols(linked_module.get())) {
274     return false;
275   }
276 
277   if (!LinkTypes(module, linked_module.get()) ||
278       !LinkFunctions(module, linked_module.get()) ||
279       !LinkGlobalVars(module, linked_module.get())) {
280     llvm::errs() << "Failed to link elements\n";
281     return false;
282   }
283 
284   // Dump the linked module.
285   std::unique_ptr<repr::IRDumper> ir_dumper =
286       repr::IRDumper::CreateIRDumper(output_format, out_dump_name_);
287   assert(ir_dumper != nullptr);
288   if (!ir_dumper->Dump(*linked_module)) {
289     llvm::errs() << "Failed to serialize the linked output to ostream\n";
290     return false;
291   }
292 
293   return true;
294 }
295 
296 template <typename T>
LinkDecl(repr::ModuleIR * dst,const repr::AbiElementMap<T> & src,const std::function<bool (const std::string &)> & symbol_filter)297 bool HeaderAbiLinker::LinkDecl(
298     repr::ModuleIR *dst, const repr::AbiElementMap<T> &src,
299     const std::function<bool(const std::string &)> &symbol_filter) {
300   assert(dst != nullptr);
301   for (auto &&element : src) {
302     // If we are not using a version script and exported headers are available,
303     // filter out unexported abi.
304     std::string source_file = element.second.GetSourceFile();
305     // Builtin types will not have source file information.
306     if (!exported_headers_.empty() && !source_file.empty() &&
307         exported_headers_.find(source_file) == exported_headers_.end()) {
308       continue;
309     }
310     // Check for the existence of the element in version script / symbol file.
311     if (!symbol_filter(element.first)) {
312       continue;
313     }
314     if (!dst->AddLinkableMessage(element.second)) {
315       llvm::errs() << "Failed to add element to linked dump\n";
316       return false;
317     }
318   }
319   return true;
320 }
321 
LinkTypes(const repr::ModuleIR & module,repr::ModuleIR * linked_module)322 bool HeaderAbiLinker::LinkTypes(const repr::ModuleIR &module,
323                                 repr::ModuleIR *linked_module) {
324   auto no_filter = [](const std::string &symbol) { return true; };
325   return LinkDecl(linked_module, module.GetRecordTypes(), no_filter) &&
326          LinkDecl(linked_module, module.GetEnumTypes(), no_filter) &&
327          LinkDecl(linked_module, module.GetFunctionTypes(), no_filter) &&
328          LinkDecl(linked_module, module.GetBuiltinTypes(), no_filter) &&
329          LinkDecl(linked_module, module.GetPointerTypes(), no_filter) &&
330          LinkDecl(linked_module, module.GetRvalueReferenceTypes(), no_filter) &&
331          LinkDecl(linked_module, module.GetLvalueReferenceTypes(), no_filter) &&
332          LinkDecl(linked_module, module.GetArrayTypes(), no_filter) &&
333          LinkDecl(linked_module, module.GetQualifiedTypes(), no_filter);
334 }
335 
IsSymbolExported(const std::string & name) const336 bool HeaderAbiLinker::IsSymbolExported(const std::string &name) const {
337   if (shared_object_symbols_ && !shared_object_symbols_->HasSymbol(name)) {
338     return false;
339   }
340   if (version_script_symbols_ && !version_script_symbols_->HasSymbol(name)) {
341     return false;
342   }
343   return true;
344 }
345 
LinkFunctions(const repr::ModuleIR & module,repr::ModuleIR * linked_module)346 bool HeaderAbiLinker::LinkFunctions(const repr::ModuleIR &module,
347                                     repr::ModuleIR *linked_module) {
348   auto symbol_filter = [this](const std::string &linker_set_key) {
349     return IsSymbolExported(linker_set_key);
350   };
351   return LinkDecl(linked_module, module.GetFunctions(), symbol_filter);
352 }
353 
LinkGlobalVars(const repr::ModuleIR & module,repr::ModuleIR * linked_module)354 bool HeaderAbiLinker::LinkGlobalVars(const repr::ModuleIR &module,
355                                      repr::ModuleIR *linked_module) {
356   auto symbol_filter = [this](const std::string &linker_set_key) {
357     return IsSymbolExported(linker_set_key);
358   };
359   return LinkDecl(linked_module, module.GetGlobalVariables(), symbol_filter);
360 }
361 
362 template <typename SymbolMap>
LinkExportedSymbols(repr::ModuleIR * dst,const SymbolMap & symbols)363 bool HeaderAbiLinker::LinkExportedSymbols(repr::ModuleIR *dst,
364                                           const SymbolMap &symbols) {
365   for (auto &&symbol : symbols) {
366     if (!IsSymbolExported(symbol.first)) {
367       continue;
368     }
369     if (!dst->AddElfSymbol(symbol.second)) {
370       return false;
371     }
372   }
373   return true;
374 }
375 
LinkExportedSymbols(repr::ModuleIR * linked_module,const repr::ExportedSymbolSet & exported_symbols)376 bool HeaderAbiLinker::LinkExportedSymbols(
377     repr::ModuleIR *linked_module,
378     const repr::ExportedSymbolSet &exported_symbols) {
379   return (LinkExportedSymbols(linked_module, exported_symbols.GetFunctions()) &&
380           LinkExportedSymbols(linked_module, exported_symbols.GetVars()));
381 }
382 
LinkExportedSymbols(repr::ModuleIR * linked_module)383 bool HeaderAbiLinker::LinkExportedSymbols(repr::ModuleIR *linked_module) {
384   if (shared_object_symbols_) {
385     return LinkExportedSymbols(linked_module, *shared_object_symbols_);
386   }
387 
388   if (version_script_symbols_) {
389     return LinkExportedSymbols(linked_module, *version_script_symbols_);
390   }
391 
392   return false;
393 }
394 
ReadExportedSymbols()395 bool HeaderAbiLinker::ReadExportedSymbols() {
396   if (so_file_.empty() && version_script_.empty()) {
397     llvm::errs() << "Either shared lib or version script must be specified.\n";
398     return false;
399   }
400 
401   if (!so_file_.empty()) {
402     if (!ReadExportedSymbolsFromSharedObjectFile()) {
403       llvm::errs() << "Failed to parse the shared library (.so file): "
404                    << so_file_ << "\n";
405       return false;
406     }
407   }
408 
409   if (!version_script_.empty()) {
410     if (!ReadExportedSymbolsFromVersionScript()) {
411       llvm::errs() << "Failed to parse the version script: " << version_script_
412                    << "\n";
413       return false;
414     }
415   }
416 
417   return true;
418 }
419 
ReadExportedSymbolsFromVersionScript()420 bool HeaderAbiLinker::ReadExportedSymbolsFromVersionScript() {
421   llvm::Optional<utils::ApiLevel> api_level = utils::ParseApiLevel(api_);
422   if (!api_level) {
423     llvm::errs() << "-api must be either \"current\" or an integer (e.g. 21)\n";
424     return false;
425   }
426 
427   std::ifstream stream(version_script_, std::ios_base::in);
428   if (!stream) {
429     llvm::errs() << "Failed to open version script file\n";
430     return false;
431   }
432 
433   repr::VersionScriptParser parser;
434   parser.SetArch(arch_);
435   parser.SetApiLevel(api_level.getValue());
436   for (auto &&version : excluded_symbol_versions_) {
437     parser.AddExcludedSymbolVersion(version);
438   }
439   for (auto &&tag : excluded_symbol_tags_) {
440     parser.AddExcludedSymbolTag(tag);
441   }
442 
443   version_script_symbols_ = parser.Parse(stream);
444   if (!version_script_symbols_) {
445     llvm::errs() << "Failed to parse version script file\n";
446     return false;
447   }
448 
449   return true;
450 }
451 
ReadExportedSymbolsFromSharedObjectFile()452 bool HeaderAbiLinker::ReadExportedSymbolsFromSharedObjectFile() {
453   std::unique_ptr<repr::SoFileParser> so_parser =
454       repr::SoFileParser::Create(so_file_);
455   if (!so_parser) {
456     return false;
457   }
458 
459   shared_object_symbols_ = so_parser->Parse();
460   if (!shared_object_symbols_) {
461     llvm::errs() << "Failed to parse shared object file\n";
462     return false;
463   }
464 
465   return true;
466 }
467 
main(int argc,const char ** argv)468 int main(int argc, const char **argv) {
469   HideIrrelevantCommandLineOptions(header_linker_category);
470   llvm::cl::ParseCommandLineOptions(argc, argv, "header-linker");
471 
472   if (so_file.empty() && version_script.empty()) {
473     llvm::errs() << "One of -so or -v needs to be specified\n";
474     return -1;
475   }
476 
477   if (no_filter) {
478     static_cast<std::vector<std::string> &>(exported_header_dirs).clear();
479   }
480 
481   HeaderAbiLinker Linker(dump_files, exported_header_dirs, version_script,
482                          so_file, linked_dump, arch, api,
483                          excluded_symbol_versions,
484                          excluded_symbol_tags);
485 
486   if (!Linker.LinkAndDump()) {
487     llvm::errs() << "Failed to link and dump elements\n";
488     return -1;
489   }
490 
491   return 0;
492 }
493