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