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