xref: /llvm-project-15.0.7/lld/wasm/Driver.cpp (revision 433eecad)
1 //===- Driver.cpp ---------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lld/Common/Driver.h"
10 #include "Config.h"
11 #include "InputChunks.h"
12 #include "InputGlobal.h"
13 #include "MarkLive.h"
14 #include "SymbolTable.h"
15 #include "Writer.h"
16 #include "lld/Common/Args.h"
17 #include "lld/Common/ErrorHandler.h"
18 #include "lld/Common/Memory.h"
19 #include "lld/Common/Strings.h"
20 #include "lld/Common/Threads.h"
21 #include "lld/Common/Version.h"
22 #include "llvm/ADT/Twine.h"
23 #include "llvm/Object/Wasm.h"
24 #include "llvm/Option/Arg.h"
25 #include "llvm/Option/ArgList.h"
26 #include "llvm/Support/CommandLine.h"
27 #include "llvm/Support/Path.h"
28 #include "llvm/Support/Process.h"
29 #include "llvm/Support/TargetSelect.h"
30 
31 #define DEBUG_TYPE "lld"
32 
33 using namespace llvm;
34 using namespace llvm::object;
35 using namespace llvm::sys;
36 using namespace llvm::wasm;
37 
38 using namespace lld;
39 using namespace lld::wasm;
40 
41 Configuration *lld::wasm::Config;
42 
43 namespace {
44 
45 // Create enum with OPT_xxx values for each option in Options.td
46 enum {
47   OPT_INVALID = 0,
48 #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
49 #include "Options.inc"
50 #undef OPTION
51 };
52 
53 // This function is called on startup. We need this for LTO since
54 // LTO calls LLVM functions to compile bitcode files to native code.
55 // Technically this can be delayed until we read bitcode files, but
56 // we don't bother to do lazily because the initialization is fast.
57 static void initLLVM() {
58   InitializeAllTargets();
59   InitializeAllTargetMCs();
60   InitializeAllAsmPrinters();
61   InitializeAllAsmParsers();
62 }
63 
64 class LinkerDriver {
65 public:
66   void link(ArrayRef<const char *> ArgsArr);
67 
68 private:
69   void createFiles(opt::InputArgList &Args);
70   void addFile(StringRef Path);
71   void addLibrary(StringRef Name);
72 
73   // True if we are in --whole-archive and --no-whole-archive.
74   bool InWholeArchive = false;
75 
76   std::vector<InputFile *> Files;
77 };
78 } // anonymous namespace
79 
80 bool lld::wasm::link(ArrayRef<const char *> Args, bool CanExitEarly,
81                      raw_ostream &Error) {
82   errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
83   errorHandler().ErrorOS = &Error;
84   errorHandler().ColorDiagnostics = Error.has_colors();
85   errorHandler().ErrorLimitExceededMsg =
86       "too many errors emitted, stopping now (use "
87       "-error-limit=0 to see all errors)";
88 
89   Config = make<Configuration>();
90   Symtab = make<SymbolTable>();
91 
92   initLLVM();
93   LinkerDriver().link(Args);
94 
95   // Exit immediately if we don't need to return to the caller.
96   // This saves time because the overhead of calling destructors
97   // for all globally-allocated objects is not negligible.
98   if (CanExitEarly)
99     exitLld(errorCount() ? 1 : 0);
100 
101   freeArena();
102   return !errorCount();
103 }
104 
105 // Create prefix string literals used in Options.td
106 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
107 #include "Options.inc"
108 #undef PREFIX
109 
110 // Create table mapping all options defined in Options.td
111 static const opt::OptTable::Info OptInfo[] = {
112 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \
113   {X1, X2, X10,         X11,         OPT_##ID, opt::Option::KIND##Class,       \
114    X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12},
115 #include "Options.inc"
116 #undef OPTION
117 };
118 
119 namespace {
120 class WasmOptTable : public llvm::opt::OptTable {
121 public:
122   WasmOptTable() : OptTable(OptInfo) {}
123   opt::InputArgList parse(ArrayRef<const char *> Argv);
124 };
125 } // namespace
126 
127 // Set color diagnostics according to -color-diagnostics={auto,always,never}
128 // or -no-color-diagnostics flags.
129 static void handleColorDiagnostics(opt::InputArgList &Args) {
130   auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
131                               OPT_no_color_diagnostics);
132   if (!Arg)
133     return;
134   if (Arg->getOption().getID() == OPT_color_diagnostics) {
135     errorHandler().ColorDiagnostics = true;
136   } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
137     errorHandler().ColorDiagnostics = false;
138   } else {
139     StringRef S = Arg->getValue();
140     if (S == "always")
141       errorHandler().ColorDiagnostics = true;
142     else if (S == "never")
143       errorHandler().ColorDiagnostics = false;
144     else if (S != "auto")
145       error("unknown option: --color-diagnostics=" + S);
146   }
147 }
148 
149 // Find a file by concatenating given paths.
150 static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) {
151   SmallString<128> S;
152   path::append(S, Path1, Path2);
153   if (fs::exists(S))
154     return S.str().str();
155   return None;
156 }
157 
158 opt::InputArgList WasmOptTable::parse(ArrayRef<const char *> Argv) {
159   SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
160 
161   unsigned MissingIndex;
162   unsigned MissingCount;
163 
164   // Expand response files (arguments in the form of @<filename>)
165   cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Vec);
166 
167   opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
168 
169   handleColorDiagnostics(Args);
170   for (auto *Arg : Args.filtered(OPT_UNKNOWN))
171     error("unknown argument: " + Arg->getSpelling());
172   return Args;
173 }
174 
175 // Currently we allow a ".imports" to live alongside a library. This can
176 // be used to specify a list of symbols which can be undefined at link
177 // time (imported from the environment.  For example libc.a include an
178 // import file that lists the syscall functions it relies on at runtime.
179 // In the long run this information would be better stored as a symbol
180 // attribute/flag in the object file itself.
181 // See: https://github.com/WebAssembly/tool-conventions/issues/35
182 static void readImportFile(StringRef Filename) {
183   if (Optional<MemoryBufferRef> Buf = readFile(Filename))
184     for (StringRef Sym : args::getLines(*Buf))
185       Config->AllowUndefinedSymbols.insert(Sym);
186 }
187 
188 // Returns slices of MB by parsing MB as an archive file.
189 // Each slice consists of a member file in the archive.
190 std::vector<MemoryBufferRef> static getArchiveMembers(MemoryBufferRef MB) {
191   std::unique_ptr<Archive> File =
192       CHECK(Archive::create(MB),
193             MB.getBufferIdentifier() + ": failed to parse archive");
194 
195   std::vector<MemoryBufferRef> V;
196   Error Err = Error::success();
197   for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
198     Archive::Child C =
199         CHECK(COrErr, MB.getBufferIdentifier() +
200                           ": could not get the child of the archive");
201     MemoryBufferRef MBRef =
202         CHECK(C.getMemoryBufferRef(),
203               MB.getBufferIdentifier() +
204                   ": could not get the buffer for a child of the archive");
205     V.push_back(MBRef);
206   }
207   if (Err)
208     fatal(MB.getBufferIdentifier() +
209           ": Archive::children failed: " + toString(std::move(Err)));
210 
211   // Take ownership of memory buffers created for members of thin archives.
212   for (std::unique_ptr<MemoryBuffer> &MB : File->takeThinBuffers())
213     make<std::unique_ptr<MemoryBuffer>>(std::move(MB));
214 
215   return V;
216 }
217 
218 void LinkerDriver::addFile(StringRef Path) {
219   Optional<MemoryBufferRef> Buffer = readFile(Path);
220   if (!Buffer.hasValue())
221     return;
222   MemoryBufferRef MBRef = *Buffer;
223 
224   switch (identify_magic(MBRef.getBuffer())) {
225   case file_magic::archive: {
226     // Handle -whole-archive.
227     if (InWholeArchive) {
228       for (MemoryBufferRef &M : getArchiveMembers(MBRef))
229         Files.push_back(createObjectFile(M, Path));
230       return;
231     }
232 
233     SmallString<128> ImportFile = Path;
234     path::replace_extension(ImportFile, ".imports");
235     if (fs::exists(ImportFile))
236       readImportFile(ImportFile.str());
237 
238     Files.push_back(make<ArchiveFile>(MBRef));
239     return;
240   }
241   case file_magic::bitcode:
242   case file_magic::wasm_object:
243     Files.push_back(createObjectFile(MBRef));
244     break;
245   default:
246     error("unknown file type: " + MBRef.getBufferIdentifier());
247   }
248 }
249 
250 // Add a given library by searching it from input search paths.
251 void LinkerDriver::addLibrary(StringRef Name) {
252   for (StringRef Dir : Config->SearchPaths) {
253     if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a")) {
254       addFile(*S);
255       return;
256     }
257   }
258 
259   error("unable to find library -l" + Name);
260 }
261 
262 void LinkerDriver::createFiles(opt::InputArgList &Args) {
263   for (auto *Arg : Args) {
264     switch (Arg->getOption().getUnaliasedOption().getID()) {
265     case OPT_l:
266       addLibrary(Arg->getValue());
267       break;
268     case OPT_INPUT:
269       addFile(Arg->getValue());
270       break;
271     case OPT_whole_archive:
272       InWholeArchive = true;
273       break;
274     case OPT_no_whole_archive:
275       InWholeArchive = false;
276       break;
277     }
278   }
279 }
280 
281 static StringRef getEntry(opt::InputArgList &Args) {
282   auto *Arg = Args.getLastArg(OPT_entry, OPT_no_entry);
283   if (!Arg) {
284     if (Args.hasArg(OPT_relocatable))
285       return "";
286     if (Args.hasArg(OPT_shared))
287       return "__wasm_call_ctors";
288     return "_start";
289   }
290   if (Arg->getOption().getID() == OPT_no_entry)
291     return "";
292   return Arg->getValue();
293 }
294 
295 // Some Config members do not directly correspond to any particular
296 // command line options, but computed based on other Config values.
297 // This function initialize such members. See Config.h for the details
298 // of these values.
299 static void setConfigs(opt::InputArgList &Args) {
300   Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
301   Config->CheckFeatures =
302       Args.hasFlag(OPT_check_features, OPT_no_check_features, true);
303   Config->CompressRelocations = Args.hasArg(OPT_compress_relocations);
304   Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
305   Config->DisableVerify = Args.hasArg(OPT_disable_verify);
306   Config->Entry = getEntry(Args);
307   Config->ExportAll = Args.hasArg(OPT_export_all);
308   Config->ExportDynamic = Args.hasFlag(OPT_export_dynamic,
309       OPT_no_export_dynamic, false);
310   Config->ExportTable = Args.hasArg(OPT_export_table);
311   errorHandler().FatalWarnings =
312       Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
313   Config->ImportMemory = Args.hasArg(OPT_import_memory);
314   Config->SharedMemory = Args.hasArg(OPT_shared_memory);
315   Config->ImportTable = Args.hasArg(OPT_import_table);
316   Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
317   Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
318   Config->Optimize = args::getInteger(Args, OPT_O, 0);
319   Config->OutputFile = Args.getLastArgValue(OPT_o);
320   Config->Relocatable = Args.hasArg(OPT_relocatable);
321   Config->GcSections =
322       Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, !Config->Relocatable);
323   Config->MergeDataSegments =
324       Args.hasFlag(OPT_merge_data_segments, OPT_no_merge_data_segments,
325                    !Config->Relocatable);
326   Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false);
327   Config->PrintGcSections =
328       Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
329   Config->SaveTemps = Args.hasArg(OPT_save_temps);
330   Config->SearchPaths = args::getStrings(Args, OPT_L);
331   Config->Shared = Args.hasArg(OPT_shared);
332   Config->StripAll = Args.hasArg(OPT_strip_all);
333   Config->StripDebug = Args.hasArg(OPT_strip_debug);
334   Config->StackFirst = Args.hasArg(OPT_stack_first);
335   Config->Trace = Args.hasArg(OPT_trace);
336   Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir);
337   Config->ThinLTOCachePolicy = CHECK(
338       parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)),
339       "--thinlto-cache-policy: invalid cache policy");
340   Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u);
341   errorHandler().Verbose = Args.hasArg(OPT_verbose);
342   LLVM_DEBUG(errorHandler().Verbose = true);
343   ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
344 
345   Config->InitialMemory = args::getInteger(Args, OPT_initial_memory, 0);
346   Config->GlobalBase = args::getInteger(Args, OPT_global_base, 1024);
347   Config->MaxMemory = args::getInteger(Args, OPT_max_memory, 0);
348   Config->ZStackSize =
349       args::getZOptionValue(Args, OPT_z, "stack-size", WasmPageSize);
350 
351   if (auto *Arg = Args.getLastArg(OPT_features)) {
352     Config->Features =
353         llvm::Optional<std::vector<std::string>>(std::vector<std::string>());
354     for (StringRef S : Arg->getValues())
355       Config->Features->push_back(S);
356   }
357 }
358 
359 // Some command line options or some combinations of them are not allowed.
360 // This function checks for such errors.
361 static void checkOptions(opt::InputArgList &Args) {
362   if (!Config->StripDebug && !Config->StripAll && Config->CompressRelocations)
363     error("--compress-relocations is incompatible with output debug"
364           " information. Please pass --strip-debug or --strip-all");
365 
366   if (Config->LTOO > 3)
367     error("invalid optimization level for LTO: " + Twine(Config->LTOO));
368   if (Config->LTOPartitions == 0)
369     error("--lto-partitions: number of threads must be > 0");
370   if (Config->ThinLTOJobs == 0)
371     error("--thinlto-jobs: number of threads must be > 0");
372 
373   if (Config->Pie && Config->Shared)
374     error("-shared and -pie may not be used together");
375 
376   if (Config->OutputFile.empty())
377     error("no output file specified");
378 
379   if (Config->ImportTable && Config->ExportTable)
380     error("--import-table and --export-table may not be used together");
381 
382   if (Config->Relocatable) {
383     if (!Config->Entry.empty())
384       error("entry point specified for relocatable output file");
385     if (Config->GcSections)
386       error("-r and --gc-sections may not be used together");
387     if (Config->CompressRelocations)
388       error("-r -and --compress-relocations may not be used together");
389     if (Args.hasArg(OPT_undefined))
390       error("-r -and --undefined may not be used together");
391     if (Config->Pie)
392       error("-r and -pie may not be used together");
393   }
394 }
395 
396 // Force Sym to be entered in the output. Used for -u or equivalent.
397 static Symbol *handleUndefined(StringRef Name) {
398   Symbol *Sym = Symtab->find(Name);
399   if (!Sym)
400     return nullptr;
401 
402   // Since symbol S may not be used inside the program, LTO may
403   // eliminate it. Mark the symbol as "used" to prevent it.
404   Sym->IsUsedInRegularObj = true;
405 
406   if (auto *LazySym = dyn_cast<LazySymbol>(Sym))
407     LazySym->fetch();
408 
409   return Sym;
410 }
411 
412 static UndefinedGlobal *
413 createUndefinedGlobal(StringRef Name, llvm::wasm::WasmGlobalType *Type) {
414   auto *Sym =
415       cast<UndefinedGlobal>(Symtab->addUndefinedGlobal(Name, Name,
416                                                        DefaultModule, 0,
417                                                        nullptr, Type));
418   Config->AllowUndefinedSymbols.insert(Sym->getName());
419   Sym->IsUsedInRegularObj = true;
420   return Sym;
421 }
422 
423 // Create ABI-defined synthetic symbols
424 static void createSyntheticSymbols() {
425   static WasmSignature NullSignature = {{}, {}};
426   static llvm::wasm::WasmGlobalType GlobalTypeI32 = {WASM_TYPE_I32, false};
427   static llvm::wasm::WasmGlobalType MutableGlobalTypeI32 = {WASM_TYPE_I32,
428                                                             true};
429 
430   if (!Config->Relocatable) {
431     WasmSym::CallCtors = Symtab->addSyntheticFunction(
432         "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
433         make<SyntheticFunction>(NullSignature, "__wasm_call_ctors"));
434 
435     if (Config->Pic) {
436       // For PIC code we create a synthetic function call __wasm_apply_relocs
437       // and add this as the first call in __wasm_call_ctors.
438       // We also unconditionally export
439       WasmSym::ApplyRelocs = Symtab->addSyntheticFunction(
440           "__wasm_apply_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
441           make<SyntheticFunction>(NullSignature, "__wasm_apply_relocs"));
442     }
443   }
444 
445   // The __stack_pointer is imported in the shared library case, and exported
446   // in the non-shared (executable) case.
447   if (Config->Shared) {
448     WasmSym::StackPointer =
449         createUndefinedGlobal("__stack_pointer", &MutableGlobalTypeI32);
450   } else {
451     llvm::wasm::WasmGlobal Global;
452     Global.Type = {WASM_TYPE_I32, true};
453     Global.InitExpr.Value.Int32 = 0;
454     Global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
455     Global.SymbolName = "__stack_pointer";
456     auto *StackPointer = make<InputGlobal>(Global, nullptr);
457     StackPointer->Live = true;
458     // For non-PIC code
459     // TODO(sbc): Remove WASM_SYMBOL_VISIBILITY_HIDDEN when the mutable global
460     // spec proposal is implemented in all major browsers.
461     // See: https://github.com/WebAssembly/mutable-global
462     WasmSym::StackPointer = Symtab->addSyntheticGlobal(
463         "__stack_pointer", WASM_SYMBOL_VISIBILITY_HIDDEN, StackPointer);
464     WasmSym::HeapBase = Symtab->addSyntheticDataSymbol("__heap_base", 0);
465     WasmSym::DataEnd = Symtab->addSyntheticDataSymbol("__data_end", 0);
466 
467     // These two synthetic symbols exist purely for the embedder so we always
468     // want to export them.
469     WasmSym::HeapBase->ForceExport = true;
470     WasmSym::DataEnd->ForceExport = true;
471   }
472 
473   if (Config->Pic) {
474     // For PIC code, we import two global variables (__memory_base and
475     // __table_base) from the environment and use these as the offset at
476     // which to load our static data and function table.
477     // See:
478     // https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
479     WasmSym::MemoryBase =
480         createUndefinedGlobal("__memory_base", &GlobalTypeI32);
481     WasmSym::TableBase = createUndefinedGlobal("__table_base", &GlobalTypeI32);
482     WasmSym::MemoryBase->markLive();
483     WasmSym::TableBase->markLive();
484   }
485 
486   WasmSym::DsoHandle = Symtab->addSyntheticDataSymbol(
487       "__dso_handle", WASM_SYMBOL_VISIBILITY_HIDDEN);
488 }
489 
490 void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
491   WasmOptTable Parser;
492   opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
493 
494   // Handle --help
495   if (Args.hasArg(OPT_help)) {
496     Parser.PrintHelp(outs(),
497                      (std::string(ArgsArr[0]) + " [options] file...").c_str(),
498                      "LLVM Linker", false);
499     return;
500   }
501 
502   // Handle --version
503   if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v)) {
504     outs() << getLLDVersion() << "\n";
505     return;
506   }
507 
508   // Parse and evaluate -mllvm options.
509   std::vector<const char *> V;
510   V.push_back("wasm-ld (LLVM option parsing)");
511   for (auto *Arg : Args.filtered(OPT_mllvm))
512     V.push_back(Arg->getValue());
513   cl::ParseCommandLineOptions(V.size(), V.data());
514 
515   errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
516 
517   setConfigs(Args);
518   checkOptions(Args);
519 
520   if (auto *Arg = Args.getLastArg(OPT_allow_undefined_file))
521     readImportFile(Arg->getValue());
522 
523   if (!Args.hasArg(OPT_INPUT)) {
524     error("no input files");
525     return;
526   }
527 
528   Config->Pic = Config->Pie || Config->Shared;
529 
530   if (Config->Pic) {
531     if (Config->ExportTable)
532       error("-shared/-pie is incompatible with --export-table");
533     Config->ImportTable = true;
534   }
535 
536   // Handle --trace-symbol.
537   for (auto *Arg : Args.filtered(OPT_trace_symbol))
538     Symtab->trace(Arg->getValue());
539 
540   if (!Config->Relocatable)
541     createSyntheticSymbols();
542 
543   if (Config->Shared) {
544     Config->ImportMemory = true;
545     Config->ExportDynamic = true;
546     Config->AllowUndefined = true;
547   }
548 
549   createFiles(Args);
550   if (errorCount())
551     return;
552 
553   // Add all files to the symbol table. This will add almost all
554   // symbols that we need to the symbol table.
555   for (InputFile *F : Files)
556     Symtab->addFile(F);
557   if (errorCount())
558     return;
559 
560   // Handle the `--undefined <sym>` options.
561   for (auto *Arg : Args.filtered(OPT_undefined))
562     handleUndefined(Arg->getValue());
563 
564   Symbol *EntrySym = nullptr;
565   if (!Config->Relocatable && !Config->Entry.empty()) {
566     EntrySym = handleUndefined(Config->Entry);
567     if (EntrySym && EntrySym->isDefined())
568       EntrySym->ForceExport = true;
569     else
570       error("entry symbol not defined (pass --no-entry to supress): " +
571             Config->Entry);
572   }
573 
574   if (errorCount())
575     return;
576 
577   // Handle the `--export <sym>` options
578   // This works like --undefined but also exports the symbol if its found
579   for (auto *Arg : Args.filtered(OPT_export))
580     handleUndefined(Arg->getValue());
581 
582   // Do link-time optimization if given files are LLVM bitcode files.
583   // This compiles bitcode files into real object files.
584   Symtab->addCombinedLTOObject();
585   if (errorCount())
586     return;
587 
588   // Resolve any variant symbols that were created due to signature
589   // mismatchs.
590   Symtab->handleSymbolVariants();
591   if (errorCount())
592     return;
593 
594   for (auto *Arg : Args.filtered(OPT_export)) {
595     Symbol *Sym = Symtab->find(Arg->getValue());
596     if (Sym && Sym->isDefined())
597       Sym->ForceExport = true;
598     else if (!Config->AllowUndefined)
599       error(Twine("symbol exported via --export not found: ") +
600             Arg->getValue());
601   }
602 
603   if (!Config->Relocatable) {
604     // Add synthetic dummies for weak undefined functions.  Must happen
605     // after LTO otherwise functions may not yet have signatures.
606     Symtab->handleWeakUndefines();
607 
608     // Make sure we have resolved all symbols.
609     if (!Config->AllowUndefined)
610       Symtab->reportRemainingUndefines();
611   }
612 
613   if (EntrySym)
614     EntrySym->setHidden(false);
615 
616   if (errorCount())
617     return;
618 
619   // Do size optimizations: garbage collection
620   markLive();
621 
622   // Write the result to the file.
623   writeResult();
624 }
625