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 "InputElement.h" |
13 | #include "MarkLive.h" |
14 | #include "SymbolTable.h" |
15 | #include "Writer.h" |
16 | #include "lld/Common/Args.h" |
17 | #include "lld/Common/CommonLinkerContext.h" |
18 | #include "lld/Common/ErrorHandler.h" |
19 | #include "lld/Common/Filesystem.h" |
20 | #include "lld/Common/Memory.h" |
21 | #include "lld/Common/Reproduce.h" |
22 | #include "lld/Common/Strings.h" |
23 | #include "lld/Common/Version.h" |
24 | #include "llvm/ADT/Twine.h" |
25 | #include "llvm/Config/llvm-config.h" |
26 | #include "llvm/Option/Arg.h" |
27 | #include "llvm/Option/ArgList.h" |
28 | #include "llvm/Support/CommandLine.h" |
29 | #include "llvm/Support/Parallel.h" |
30 | #include "llvm/Support/Path.h" |
31 | #include "llvm/Support/Process.h" |
32 | #include "llvm/Support/TarWriter.h" |
33 | #include "llvm/Support/TargetSelect.h" |
34 | #include "llvm/TargetParser/Host.h" |
35 | #include <optional> |
36 | |
37 | #define DEBUG_TYPE "lld" |
38 | |
39 | using namespace llvm; |
40 | using namespace llvm::object; |
41 | using namespace llvm::opt; |
42 | using namespace llvm::sys; |
43 | using namespace llvm::wasm; |
44 | |
45 | namespace lld::wasm { |
46 | Ctx ctx; |
47 | |
48 | void errorOrWarn(const llvm::Twine &msg) { |
49 | if (ctx.arg.noinhibitExec) |
50 | warn(msg); |
51 | else |
52 | error(msg); |
53 | } |
54 | |
55 | Ctx::Ctx() {} |
56 | |
57 | void Ctx::reset() { |
58 | arg.~Config(); |
59 | new (&arg) Config(); |
60 | objectFiles.clear(); |
61 | stubFiles.clear(); |
62 | sharedFiles.clear(); |
63 | bitcodeFiles.clear(); |
64 | lazyBitcodeFiles.clear(); |
65 | syntheticFunctions.clear(); |
66 | syntheticGlobals.clear(); |
67 | syntheticTables.clear(); |
68 | whyExtractRecords.clear(); |
69 | isPic = false; |
70 | legacyFunctionTable = false; |
71 | emitBssSegments = false; |
72 | sym = WasmSym{}; |
73 | } |
74 | |
75 | namespace { |
76 | |
77 | // Create enum with OPT_xxx values for each option in Options.td |
78 | enum { |
79 | OPT_INVALID = 0, |
80 | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), |
81 | #include "Options.inc" |
82 | #undef OPTION |
83 | }; |
84 | |
85 | // This function is called on startup. We need this for LTO since |
86 | // LTO calls LLVM functions to compile bitcode files to native code. |
87 | // Technically this can be delayed until we read bitcode files, but |
88 | // we don't bother to do lazily because the initialization is fast. |
89 | static void initLLVM() { |
90 | InitializeAllTargets(); |
91 | InitializeAllTargetMCs(); |
92 | InitializeAllAsmPrinters(); |
93 | InitializeAllAsmParsers(); |
94 | } |
95 | |
96 | class LinkerDriver { |
97 | public: |
98 | LinkerDriver(Ctx &); |
99 | void linkerMain(ArrayRef<const char *> argsArr); |
100 | |
101 | private: |
102 | void createFiles(opt::InputArgList &args); |
103 | void addFile(StringRef path); |
104 | void addLibrary(StringRef name); |
105 | |
106 | Ctx &ctx; |
107 | |
108 | // True if we are in --whole-archive and --no-whole-archive. |
109 | bool inWholeArchive = false; |
110 | |
111 | // True if we are in --start-lib and --end-lib. |
112 | bool inLib = false; |
113 | |
114 | std::vector<InputFile *> files; |
115 | }; |
116 | |
117 | static bool hasZOption(opt::InputArgList &args, StringRef key) { |
118 | bool ret = false; |
119 | for (const auto *arg : args.filtered(OPT_z)) |
120 | if (key == arg->getValue()) { |
121 | ret = true; |
122 | arg->claim(); |
123 | } |
124 | return ret; |
125 | } |
126 | } // anonymous namespace |
127 | |
128 | bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS, |
129 | llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { |
130 | // This driver-specific context will be freed later by unsafeLldMain(). |
131 | auto *context = new CommonLinkerContext; |
132 | |
133 | context->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); |
134 | context->e.cleanupCallback = []() { ctx.reset(); }; |
135 | context->e.logName = args::getFilenameWithoutExe(path: args[0]); |
136 | context->e.errorLimitExceededMsg = |
137 | "too many errors emitted, stopping now (use " |
138 | "-error-limit=0 to see all errors)"; |
139 | |
140 | symtab = make<SymbolTable>(); |
141 | |
142 | initLLVM(); |
143 | LinkerDriver(ctx).linkerMain(argsArr: args); |
144 | |
145 | return errorCount() == 0; |
146 | } |
147 | |
148 | #define OPTTABLE_STR_TABLE_CODE |
149 | #include "Options.inc" |
150 | #undef OPTTABLE_STR_TABLE_CODE |
151 | |
152 | #define OPTTABLE_PREFIXES_TABLE_CODE |
153 | #include "Options.inc" |
154 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
155 | |
156 | // Create table mapping all options defined in Options.td |
157 | static constexpr opt::OptTable::Info optInfo[] = { |
158 | #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \ |
159 | VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \ |
160 | VALUES) \ |
161 | {PREFIX, \ |
162 | NAME, \ |
163 | HELPTEXT, \ |
164 | HELPTEXTSFORVARIANTS, \ |
165 | METAVAR, \ |
166 | OPT_##ID, \ |
167 | opt::Option::KIND##Class, \ |
168 | PARAM, \ |
169 | FLAGS, \ |
170 | VISIBILITY, \ |
171 | OPT_##GROUP, \ |
172 | OPT_##ALIAS, \ |
173 | ALIASARGS, \ |
174 | VALUES}, |
175 | #include "Options.inc" |
176 | #undef OPTION |
177 | }; |
178 | |
179 | namespace { |
180 | class WasmOptTable : public opt::GenericOptTable { |
181 | public: |
182 | WasmOptTable() |
183 | : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, optInfo) {} |
184 | opt::InputArgList parse(ArrayRef<const char *> argv); |
185 | }; |
186 | } // namespace |
187 | |
188 | // Set color diagnostics according to -color-diagnostics={auto,always,never} |
189 | // or -no-color-diagnostics flags. |
190 | static void handleColorDiagnostics(opt::InputArgList &args) { |
191 | auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, |
192 | OPT_no_color_diagnostics); |
193 | if (!arg) |
194 | return; |
195 | auto &errs = errorHandler().errs(); |
196 | if (arg->getOption().getID() == OPT_color_diagnostics) { |
197 | errs.enable_colors(enable: true); |
198 | } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { |
199 | errs.enable_colors(enable: false); |
200 | } else { |
201 | StringRef s = arg->getValue(); |
202 | if (s == "always") |
203 | errs.enable_colors(enable: true); |
204 | else if (s == "never") |
205 | errs.enable_colors(enable: false); |
206 | else if (s != "auto") |
207 | error(msg: "unknown option: --color-diagnostics="+ s); |
208 | } |
209 | } |
210 | |
211 | static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { |
212 | if (auto *arg = args.getLastArg(OPT_rsp_quoting)) { |
213 | StringRef s = arg->getValue(); |
214 | if (s != "windows"&& s != "posix") |
215 | error(msg: "invalid response file quoting: "+ s); |
216 | if (s == "windows") |
217 | return cl::TokenizeWindowsCommandLine; |
218 | return cl::TokenizeGNUCommandLine; |
219 | } |
220 | if (Triple(sys::getProcessTriple()).isOSWindows()) |
221 | return cl::TokenizeWindowsCommandLine; |
222 | return cl::TokenizeGNUCommandLine; |
223 | } |
224 | |
225 | // Find a file by concatenating given paths. |
226 | static std::optional<std::string> findFile(StringRef path1, |
227 | const Twine &path2) { |
228 | SmallString<128> s; |
229 | path::append(s, path1, path2); |
230 | if (fs::exists(s)) |
231 | return std::string(s); |
232 | return std::nullopt; |
233 | } |
234 | |
235 | opt::InputArgList WasmOptTable::parse(ArrayRef<const char *> argv) { |
236 | SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size()); |
237 | |
238 | unsigned missingIndex; |
239 | unsigned missingCount; |
240 | |
241 | // We need to get the quoting style for response files before parsing all |
242 | // options so we parse here before and ignore all the options but |
243 | // --rsp-quoting. |
244 | opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount); |
245 | |
246 | // Expand response files (arguments in the form of @<filename>) |
247 | // and then parse the argument again. |
248 | cl::ExpandResponseFiles(Saver&: saver(), Tokenizer: getQuotingStyle(args), Argv&: vec); |
249 | args = this->ParseArgs(vec, missingIndex, missingCount); |
250 | |
251 | handleColorDiagnostics(args); |
252 | if (missingCount) |
253 | error(msg: Twine(args.getArgString(Index: missingIndex)) + ": missing argument"); |
254 | |
255 | for (auto *arg : args.filtered(OPT_UNKNOWN)) |
256 | error("unknown argument: "+ arg->getAsString(args)); |
257 | return args; |
258 | } |
259 | |
260 | // Currently we allow a ".imports" to live alongside a library. This can |
261 | // be used to specify a list of symbols which can be undefined at link |
262 | // time (imported from the environment. For example libc.a include an |
263 | // import file that lists the syscall functions it relies on at runtime. |
264 | // In the long run this information would be better stored as a symbol |
265 | // attribute/flag in the object file itself. |
266 | // See: https://github.com/WebAssembly/tool-conventions/issues/35 |
267 | static void readImportFile(StringRef filename) { |
268 | if (std::optional<MemoryBufferRef> buf = readFile(filename)) |
269 | for (StringRef sym : args::getLines(*buf)) |
270 | ctx.arg.allowUndefinedSymbols.insert(sym); |
271 | } |
272 | |
273 | // Returns slices of MB by parsing MB as an archive file. |
274 | // Each slice consists of a member file in the archive. |
275 | std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers( |
276 | MemoryBufferRef mb) { |
277 | std::unique_ptr<Archive> file = |
278 | CHECK(Archive::create(mb), |
279 | mb.getBufferIdentifier() + ": failed to parse archive"); |
280 | |
281 | std::vector<std::pair<MemoryBufferRef, uint64_t>> v; |
282 | Error err = Error::success(); |
283 | for (const Archive::Child &c : file->children(err)) { |
284 | MemoryBufferRef mbref = |
285 | CHECK(c.getMemoryBufferRef(), |
286 | mb.getBufferIdentifier() + |
287 | ": could not get the buffer for a child of the archive"); |
288 | v.push_back(std::make_pair(mbref, c.getChildOffset())); |
289 | } |
290 | if (err) |
291 | fatal(mb.getBufferIdentifier() + |
292 | ": Archive::children failed: "+ toString(std::move(err))); |
293 | |
294 | // Take ownership of memory buffers created for members of thin archives. |
295 | for (std::unique_ptr<MemoryBuffer> &mb : file->takeThinBuffers()) |
296 | make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); |
297 | |
298 | return v; |
299 | } |
300 | |
301 | void LinkerDriver::addFile(StringRef path) { |
302 | std::optional<MemoryBufferRef> buffer = readFile(path); |
303 | if (!buffer) |
304 | return; |
305 | MemoryBufferRef mbref = *buffer; |
306 | |
307 | switch (identify_magic(magic: mbref.getBuffer())) { |
308 | case file_magic::archive: { |
309 | SmallString<128> importFile = path; |
310 | path::replace_extension(path&: importFile, extension: ".imports"); |
311 | if (fs::exists(importFile)) |
312 | readImportFile(importFile.str()); |
313 | |
314 | auto members = getArchiveMembers(mbref); |
315 | |
316 | // Handle -whole-archive. |
317 | if (inWholeArchive) { |
318 | for (const auto &[m, offset] : members) { |
319 | auto *object = createObjectFile(m, path, offset); |
320 | files.push_back(object); |
321 | } |
322 | |
323 | return; |
324 | } |
325 | |
326 | std::unique_ptr<Archive> file = |
327 | CHECK(Archive::create(mbref), path + ": failed to parse archive"); |
328 | |
329 | for (const auto &[m, offset] : members) { |
330 | auto magic = identify_magic(m.getBuffer()); |
331 | if (magic == file_magic::wasm_object || magic == file_magic::bitcode) |
332 | files.push_back(createObjectFile(m, path, offset, true)); |
333 | else |
334 | warn(path + ": archive member '"+ m.getBufferIdentifier() + |
335 | "' is neither Wasm object file nor LLVM bitcode"); |
336 | } |
337 | |
338 | return; |
339 | } |
340 | case file_magic::bitcode: |
341 | case file_magic::wasm_object: { |
342 | auto obj = createObjectFile(mb: mbref, archiveName: "", offsetInArchive: 0, lazy: inLib); |
343 | if (ctx.arg.isStatic && isa<SharedFile>(obj)) { |
344 | error(msg: "attempted static link of dynamic object "+ path); |
345 | break; |
346 | } |
347 | files.push_back(obj); |
348 | break; |
349 | } |
350 | case file_magic::unknown: |
351 | if (mbref.getBuffer().starts_with(Prefix: "#STUB")) { |
352 | files.push_back(make<StubFile>(mbref)); |
353 | break; |
354 | } |
355 | [[fallthrough]]; |
356 | default: |
357 | error(msg: "unknown file type: "+ mbref.getBufferIdentifier()); |
358 | } |
359 | } |
360 | |
361 | static std::optional<std::string> findFromSearchPaths(StringRef path) { |
362 | for (StringRef dir : ctx.arg.searchPaths) |
363 | if (std::optional<std::string> s = findFile(path1: dir, path2: path)) |
364 | return s; |
365 | return std::nullopt; |
366 | } |
367 | |
368 | // This is for -l<basename>. We'll look for lib<basename>.a from |
369 | // search paths. |
370 | static std::optional<std::string> searchLibraryBaseName(StringRef name) { |
371 | for (StringRef dir : ctx.arg.searchPaths) { |
372 | if (!ctx.arg.isStatic) |
373 | if (std::optional<std::string> s = findFile(path1: dir, path2: "lib"+ name + ".so")) |
374 | return s; |
375 | if (std::optional<std::string> s = findFile(path1: dir, path2: "lib"+ name + ".a")) |
376 | return s; |
377 | } |
378 | return std::nullopt; |
379 | } |
380 | |
381 | // This is for -l<namespec>. |
382 | static std::optional<std::string> searchLibrary(StringRef name) { |
383 | if (name.starts_with(Prefix: ":")) |
384 | return findFromSearchPaths(path: name.substr(Start: 1)); |
385 | return searchLibraryBaseName(name); |
386 | } |
387 | |
388 | // Add a given library by searching it from input search paths. |
389 | void LinkerDriver::addLibrary(StringRef name) { |
390 | if (std::optional<std::string> path = searchLibrary(name)) |
391 | addFile(path: saver().save(S: *path)); |
392 | else |
393 | error(msg: "unable to find library -l"+ name, tag: ErrorTag::LibNotFound, args: {name}); |
394 | } |
395 | |
396 | void LinkerDriver::createFiles(opt::InputArgList &args) { |
397 | for (auto *arg : args) { |
398 | switch (arg->getOption().getID()) { |
399 | case OPT_library: |
400 | addLibrary(arg->getValue()); |
401 | break; |
402 | case OPT_INPUT: |
403 | addFile(arg->getValue()); |
404 | break; |
405 | case OPT_Bstatic: |
406 | ctx.arg.isStatic = true; |
407 | break; |
408 | case OPT_Bdynamic: |
409 | ctx.arg.isStatic = false; |
410 | break; |
411 | case OPT_whole_archive: |
412 | inWholeArchive = true; |
413 | break; |
414 | case OPT_no_whole_archive: |
415 | inWholeArchive = false; |
416 | break; |
417 | case OPT_start_lib: |
418 | if (inLib) |
419 | error("nested --start-lib"); |
420 | inLib = true; |
421 | break; |
422 | case OPT_end_lib: |
423 | if (!inLib) |
424 | error("stray --end-lib"); |
425 | inLib = false; |
426 | break; |
427 | } |
428 | } |
429 | if (files.empty() && errorCount() == 0) |
430 | error(msg: "no input files"); |
431 | } |
432 | |
433 | static StringRef getAliasSpelling(opt::Arg *arg) { |
434 | if (const opt::Arg *alias = arg->getAlias()) |
435 | return alias->getSpelling(); |
436 | return arg->getSpelling(); |
437 | } |
438 | |
439 | static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args, |
440 | unsigned id) { |
441 | auto *arg = args.getLastArg(id); |
442 | if (!arg) |
443 | return {"", ""}; |
444 | |
445 | StringRef s = arg->getValue(); |
446 | std::pair<StringRef, StringRef> ret = s.split(Separator: ';'); |
447 | if (ret.second.empty()) |
448 | error(getAliasSpelling(arg) + " expects 'old;new' format, but got "+ s); |
449 | return ret; |
450 | } |
451 | |
452 | // Parse options of the form "old;new[;extra]". |
453 | static std::tuple<StringRef, StringRef, StringRef> |
454 | getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) { |
455 | auto [oldDir, second] = getOldNewOptions(args, id); |
456 | auto [newDir, extraDir] = second.split(Separator: ';'); |
457 | return {oldDir, newDir, extraDir}; |
458 | } |
459 | |
460 | static StringRef getEntry(opt::InputArgList &args) { |
461 | auto *arg = args.getLastArg(OPT_entry, OPT_no_entry); |
462 | if (!arg) { |
463 | if (args.hasArg(OPT_relocatable)) |
464 | return ""; |
465 | if (args.hasArg(OPT_shared)) |
466 | return "__wasm_call_ctors"; |
467 | return "_start"; |
468 | } |
469 | if (arg->getOption().getID() == OPT_no_entry) |
470 | return ""; |
471 | return arg->getValue(); |
472 | } |
473 | |
474 | // Determines what we should do if there are remaining unresolved |
475 | // symbols after the name resolution. |
476 | static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { |
477 | UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols, |
478 | OPT_warn_unresolved_symbols, true) |
479 | ? UnresolvedPolicy::ReportError |
480 | : UnresolvedPolicy::Warn; |
481 | |
482 | if (auto *arg = args.getLastArg(OPT_unresolved_symbols)) { |
483 | StringRef s = arg->getValue(); |
484 | if (s == "ignore-all") |
485 | return UnresolvedPolicy::Ignore; |
486 | if (s == "import-dynamic") |
487 | return UnresolvedPolicy::ImportDynamic; |
488 | if (s == "report-all") |
489 | return errorOrWarn; |
490 | error(msg: "unknown --unresolved-symbols value: "+ s); |
491 | } |
492 | |
493 | return errorOrWarn; |
494 | } |
495 | |
496 | // Parse --build-id or --build-id=<style>. We handle "tree" as a |
497 | // synonym for "sha1" because all our hash functions including |
498 | // -build-id=sha1 are actually tree hashes for performance reasons. |
499 | static std::pair<BuildIdKind, SmallVector<uint8_t, 0>> |
500 | getBuildId(opt::InputArgList &args) { |
501 | auto *arg = args.getLastArg(OPT_build_id, OPT_build_id_eq); |
502 | if (!arg) |
503 | return {BuildIdKind::None, {}}; |
504 | |
505 | if (arg->getOption().getID() == OPT_build_id) |
506 | return {BuildIdKind::Fast, {}}; |
507 | |
508 | StringRef s = arg->getValue(); |
509 | if (s == "fast") |
510 | return {BuildIdKind::Fast, {}}; |
511 | if (s == "sha1"|| s == "tree") |
512 | return {BuildIdKind::Sha1, {}}; |
513 | if (s == "uuid") |
514 | return {BuildIdKind::Uuid, {}}; |
515 | if (s.starts_with(Prefix: "0x")) |
516 | return {BuildIdKind::Hexstring, parseHex(s: s.substr(Start: 2))}; |
517 | |
518 | if (s != "none") |
519 | error(msg: "unknown --build-id style: "+ s); |
520 | return {BuildIdKind::None, {}}; |
521 | } |
522 | |
523 | // Initializes Config members by the command line options. |
524 | static void readConfigs(opt::InputArgList &args) { |
525 | ctx.arg.allowMultipleDefinition = |
526 | hasZOption(args, "muldefs") || |
527 | args.hasFlag(OPT_allow_multiple_definition, |
528 | OPT_no_allow_multiple_definition, false); |
529 | ctx.arg.bsymbolic = args.hasArg(OPT_Bsymbolic); |
530 | ctx.arg.checkFeatures = |
531 | args.hasFlag(OPT_check_features, OPT_no_check_features, true); |
532 | ctx.arg.compressRelocations = args.hasArg(OPT_compress_relocations); |
533 | ctx.arg.demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true); |
534 | ctx.arg.disableVerify = args.hasArg(OPT_disable_verify); |
535 | ctx.arg.emitRelocs = args.hasArg(OPT_emit_relocs); |
536 | ctx.arg.experimentalPic = args.hasArg(OPT_experimental_pic); |
537 | ctx.arg.entry = getEntry(args); |
538 | ctx.arg.exportAll = args.hasArg(OPT_export_all); |
539 | ctx.arg.exportTable = args.hasArg(OPT_export_table); |
540 | ctx.arg.growableTable = args.hasArg(OPT_growable_table); |
541 | ctx.arg.noinhibitExec = args.hasArg(OPT_noinhibit_exec); |
542 | |
543 | if (args.hasArg(OPT_import_memory_with_name)) { |
544 | ctx.arg.memoryImport = |
545 | args.getLastArgValue(OPT_import_memory_with_name).split(","); |
546 | } else if (args.hasArg(OPT_import_memory)) { |
547 | ctx.arg.memoryImport = |
548 | std::pair<llvm::StringRef, llvm::StringRef>(defaultModule, memoryName); |
549 | } else { |
550 | ctx.arg.memoryImport = |
551 | std::optional<std::pair<llvm::StringRef, llvm::StringRef>>(); |
552 | } |
553 | |
554 | if (args.hasArg(OPT_export_memory_with_name)) { |
555 | ctx.arg.memoryExport = args.getLastArgValue(OPT_export_memory_with_name); |
556 | } else if (args.hasArg(OPT_export_memory)) { |
557 | ctx.arg.memoryExport = memoryName; |
558 | } else { |
559 | ctx.arg.memoryExport = std::optional<llvm::StringRef>(); |
560 | } |
561 | |
562 | ctx.arg.sharedMemory = args.hasArg(OPT_shared_memory); |
563 | ctx.arg.soName = args.getLastArgValue(OPT_soname); |
564 | ctx.arg.importTable = args.hasArg(OPT_import_table); |
565 | ctx.arg.importUndefined = args.hasArg(OPT_import_undefined); |
566 | ctx.arg.ltoo = args::getInteger(args, OPT_lto_O, 2); |
567 | if (ctx.arg.ltoo > 3) |
568 | error(msg: "invalid optimization level for LTO: "+ Twine(ctx.arg.ltoo)); |
569 | unsigned ltoCgo = |
570 | args::getInteger(args, OPT_lto_CGO, args::getCGOptLevel(ctx.arg.ltoo)); |
571 | if (auto level = CodeGenOpt::getLevel(OL: ltoCgo)) |
572 | ctx.arg.ltoCgo = *level; |
573 | else |
574 | error(msg: "invalid codegen optimization level for LTO: "+ Twine(ltoCgo)); |
575 | ctx.arg.ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1); |
576 | ctx.arg.ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq); |
577 | ctx.arg.ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager); |
578 | ctx.arg.mapFile = args.getLastArgValue(OPT_Map); |
579 | ctx.arg.optimize = args::getInteger(args, OPT_O, 1); |
580 | ctx.arg.outputFile = args.getLastArgValue(OPT_o); |
581 | ctx.arg.relocatable = args.hasArg(OPT_relocatable); |
582 | ctx.arg.rpath = args::getStrings(args, OPT_rpath); |
583 | ctx.arg.gcSections = |
584 | args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, !ctx.arg.relocatable); |
585 | for (auto *arg : args.filtered(OPT_keep_section)) |
586 | ctx.arg.keepSections.insert(arg->getValue()); |
587 | ctx.arg.mergeDataSegments = |
588 | args.hasFlag(OPT_merge_data_segments, OPT_no_merge_data_segments, |
589 | !ctx.arg.relocatable); |
590 | ctx.arg.pie = args.hasFlag(OPT_pie, OPT_no_pie, false); |
591 | ctx.arg.printGcSections = |
592 | args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); |
593 | ctx.arg.saveTemps = args.hasArg(OPT_save_temps); |
594 | ctx.arg.searchPaths = args::getStrings(args, OPT_library_path); |
595 | ctx.arg.shared = args.hasArg(OPT_shared); |
596 | ctx.arg.shlibSigCheck = !args.hasArg(OPT_no_shlib_sigcheck); |
597 | ctx.arg.stripAll = args.hasArg(OPT_strip_all); |
598 | ctx.arg.stripDebug = args.hasArg(OPT_strip_debug); |
599 | ctx.arg.stackFirst = args.hasArg(OPT_stack_first); |
600 | ctx.arg.trace = args.hasArg(OPT_trace); |
601 | ctx.arg.thinLTOCacheDir = args.getLastArgValue(OPT_thinlto_cache_dir); |
602 | ctx.arg.thinLTOCachePolicy = CHECK( |
603 | parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)), |
604 | "--thinlto-cache-policy: invalid cache policy"); |
605 | ctx.arg.thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files); |
606 | ctx.arg.thinLTOEmitIndexFiles = args.hasArg(OPT_thinlto_emit_index_files) || |
607 | args.hasArg(OPT_thinlto_index_only) || |
608 | args.hasArg(OPT_thinlto_index_only_eq); |
609 | ctx.arg.thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || |
610 | args.hasArg(OPT_thinlto_index_only_eq); |
611 | ctx.arg.thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq); |
612 | ctx.arg.thinLTOObjectSuffixReplace = |
613 | getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); |
614 | std::tie(ctx.arg.thinLTOPrefixReplaceOld, ctx.arg.thinLTOPrefixReplaceNew, |
615 | ctx.arg.thinLTOPrefixReplaceNativeObject) = |
616 | getOldNewOptionsExtra(args, OPT_thinlto_prefix_replace_eq); |
617 | if (ctx.arg.thinLTOEmitIndexFiles && !ctx.arg.thinLTOIndexOnly) { |
618 | if (args.hasArg(OPT_thinlto_object_suffix_replace_eq)) |
619 | error(msg: "--thinlto-object-suffix-replace is not supported with " |
620 | "--thinlto-emit-index-files"); |
621 | else if (args.hasArg(OPT_thinlto_prefix_replace_eq)) |
622 | error(msg: "--thinlto-prefix-replace is not supported with " |
623 | "--thinlto-emit-index-files"); |
624 | } |
625 | if (!ctx.arg.thinLTOPrefixReplaceNativeObject.empty() && |
626 | ctx.arg.thinLTOIndexOnlyArg.empty()) { |
627 | error(msg: "--thinlto-prefix-replace=old_dir;new_dir;obj_dir must be used with " |
628 | "--thinlto-index-only="); |
629 | } |
630 | ctx.arg.unresolvedSymbols = getUnresolvedSymbolPolicy(args); |
631 | ctx.arg.whyExtract = args.getLastArgValue(OPT_why_extract); |
632 | errorHandler().verbose = args.hasArg(OPT_verbose); |
633 | LLVM_DEBUG(errorHandler().verbose = true); |
634 | |
635 | ctx.arg.tableBase = args::getInteger(args, OPT_table_base, 0); |
636 | ctx.arg.globalBase = args::getInteger(args, OPT_global_base, 0); |
637 | ctx.arg.initialHeap = args::getInteger(args, OPT_initial_heap, 0); |
638 | ctx.arg.initialMemory = args::getInteger(args, OPT_initial_memory, 0); |
639 | ctx.arg.maxMemory = args::getInteger(args, OPT_max_memory, 0); |
640 | ctx.arg.noGrowableMemory = args.hasArg(OPT_no_growable_memory); |
641 | ctx.arg.zStackSize = |
642 | args::getZOptionValue(args, OPT_z, "stack-size", WasmDefaultPageSize); |
643 | ctx.arg.pageSize = args::getInteger(args, OPT_page_size, WasmDefaultPageSize); |
644 | if (ctx.arg.pageSize != 1 && ctx.arg.pageSize != WasmDefaultPageSize) |
645 | error(msg: "--page_size=N must be either 1 or 65536"); |
646 | |
647 | // -Bdynamic by default if -pie or -shared is specified. |
648 | if (ctx.arg.pie || ctx.arg.shared) |
649 | ctx.arg.isStatic = false; |
650 | |
651 | if (ctx.arg.maxMemory != 0 && ctx.arg.noGrowableMemory) { |
652 | // Erroring out here is simpler than defining precedence rules. |
653 | error(msg: "--max-memory is incompatible with --no-growable-memory"); |
654 | } |
655 | |
656 | // Default value of exportDynamic depends on `-shared` |
657 | ctx.arg.exportDynamic = |
658 | args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, ctx.arg.shared); |
659 | |
660 | // Parse wasm32/64. |
661 | if (auto *arg = args.getLastArg(OPT_m)) { |
662 | StringRef s = arg->getValue(); |
663 | if (s == "wasm32") |
664 | ctx.arg.is64 = false; |
665 | else if (s == "wasm64") |
666 | ctx.arg.is64 = true; |
667 | else |
668 | error(msg: "invalid target architecture: "+ s); |
669 | } |
670 | |
671 | // --threads= takes a positive integer and provides the default value for |
672 | // --thinlto-jobs=. |
673 | if (auto *arg = args.getLastArg(OPT_threads)) { |
674 | StringRef v(arg->getValue()); |
675 | unsigned threads = 0; |
676 | if (!llvm::to_integer(v, threads, 0) || threads == 0) |
677 | error(arg->getSpelling() + ": expected a positive integer, but got '"+ |
678 | arg->getValue() + "'"); |
679 | parallel::strategy = hardware_concurrency(ThreadCount: threads); |
680 | ctx.arg.thinLTOJobs = v; |
681 | } |
682 | if (auto *arg = args.getLastArg(OPT_thinlto_jobs)) |
683 | ctx.arg.thinLTOJobs = arg->getValue(); |
684 | |
685 | if (auto *arg = args.getLastArg(OPT_features)) { |
686 | ctx.arg.features = |
687 | std::optional<std::vector<std::string>>(std::vector<std::string>()); |
688 | for (StringRef s : arg->getValues()) |
689 | ctx.arg.features->push_back(std::string(s)); |
690 | } |
691 | |
692 | if (auto *arg = args.getLastArg(OPT_extra_features)) { |
693 | ctx.arg.extraFeatures = |
694 | std::optional<std::vector<std::string>>(std::vector<std::string>()); |
695 | for (StringRef s : arg->getValues()) |
696 | ctx.arg.extraFeatures->push_back(std::string(s)); |
697 | } |
698 | |
699 | // Legacy --allow-undefined flag which is equivalent to |
700 | // --unresolve-symbols=ignore + --import-undefined |
701 | if (args.hasArg(OPT_allow_undefined)) { |
702 | ctx.arg.importUndefined = true; |
703 | ctx.arg.unresolvedSymbols = UnresolvedPolicy::Ignore; |
704 | } |
705 | |
706 | if (args.hasArg(OPT_print_map)) |
707 | ctx.arg.mapFile = "-"; |
708 | |
709 | std::tie(ctx.arg.buildId, ctx.arg.buildIdVector) = getBuildId(args); |
710 | } |
711 | |
712 | // Some Config members do not directly correspond to any particular |
713 | // command line options, but computed based on other Config values. |
714 | // This function initialize such members. See Config.h for the details |
715 | // of these values. |
716 | static void setConfigs() { |
717 | ctx.isPic = ctx.arg.pie || ctx.arg.shared; |
718 | |
719 | if (ctx.isPic) { |
720 | if (ctx.arg.exportTable) |
721 | error(msg: "-shared/-pie is incompatible with --export-table"); |
722 | ctx.arg.importTable = true; |
723 | } else { |
724 | // Default table base. Defaults to 1, reserving 0 for the NULL function |
725 | // pointer. |
726 | if (!ctx.arg.tableBase) |
727 | ctx.arg.tableBase = 1; |
728 | // The default offset for static/global data, for when --global-base is |
729 | // not specified on the command line. The precise value of 1024 is |
730 | // somewhat arbitrary, and pre-dates wasm-ld (Its the value that |
731 | // emscripten used prior to wasm-ld). |
732 | if (!ctx.arg.globalBase && !ctx.arg.relocatable && !ctx.arg.stackFirst) |
733 | ctx.arg.globalBase = 1024; |
734 | } |
735 | |
736 | if (ctx.arg.relocatable) { |
737 | if (ctx.arg.exportTable) |
738 | error(msg: "--relocatable is incompatible with --export-table"); |
739 | if (ctx.arg.growableTable) |
740 | error(msg: "--relocatable is incompatible with --growable-table"); |
741 | // Ignore any --import-table, as it's redundant. |
742 | ctx.arg.importTable = true; |
743 | } |
744 | |
745 | if (ctx.arg.shared) { |
746 | if (ctx.arg.memoryExport.has_value()) { |
747 | error(msg: "--export-memory is incompatible with --shared"); |
748 | } |
749 | if (!ctx.arg.memoryImport.has_value()) { |
750 | ctx.arg.memoryImport = std::pair<llvm::StringRef, llvm::StringRef>( |
751 | defaultModule, memoryName); |
752 | } |
753 | } |
754 | |
755 | // If neither export-memory nor import-memory is specified, default to |
756 | // exporting memory under its default name. |
757 | if (!ctx.arg.memoryExport.has_value() && !ctx.arg.memoryImport.has_value()) { |
758 | ctx.arg.memoryExport = memoryName; |
759 | } |
760 | } |
761 | |
762 | // Some command line options or some combinations of them are not allowed. |
763 | // This function checks for such errors. |
764 | static void checkOptions(opt::InputArgList &args) { |
765 | if (!ctx.arg.stripDebug && !ctx.arg.stripAll && ctx.arg.compressRelocations) |
766 | error(msg: "--compress-relocations is incompatible with output debug" |
767 | " information. Please pass --strip-debug or --strip-all"); |
768 | |
769 | if (ctx.arg.ltoPartitions == 0) |
770 | error(msg: "--lto-partitions: number of threads must be > 0"); |
771 | if (!get_threadpool_strategy(ctx.arg.thinLTOJobs)) |
772 | error(msg: "--thinlto-jobs: invalid job count: "+ ctx.arg.thinLTOJobs); |
773 | |
774 | if (ctx.arg.pie && ctx.arg.shared) |
775 | error(msg: "-shared and -pie may not be used together"); |
776 | |
777 | if (ctx.arg.outputFile.empty() && !ctx.arg.thinLTOIndexOnly) |
778 | error(msg: "no output file specified"); |
779 | |
780 | if (ctx.arg.importTable && ctx.arg.exportTable) |
781 | error(msg: "--import-table and --export-table may not be used together"); |
782 | |
783 | if (ctx.arg.relocatable) { |
784 | if (!ctx.arg.entry.empty()) |
785 | error(msg: "entry point specified for relocatable output file"); |
786 | if (ctx.arg.gcSections) |
787 | error(msg: "-r and --gc-sections may not be used together"); |
788 | if (ctx.arg.compressRelocations) |
789 | error(msg: "-r -and --compress-relocations may not be used together"); |
790 | if (args.hasArg(OPT_undefined)) |
791 | error(msg: "-r -and --undefined may not be used together"); |
792 | if (ctx.arg.pie) |
793 | error(msg: "-r and -pie may not be used together"); |
794 | if (ctx.arg.sharedMemory) |
795 | error(msg: "-r and --shared-memory may not be used together"); |
796 | if (ctx.arg.globalBase) |
797 | error(msg: "-r and --global-base may not by used together"); |
798 | } |
799 | |
800 | // To begin to prepare for Module Linking-style shared libraries, start |
801 | // warning about uses of `-shared` and related flags outside of Experimental |
802 | // mode, to give anyone using them a heads-up that they will be changing. |
803 | // |
804 | // Also, warn about flags which request explicit exports. |
805 | if (!ctx.arg.experimentalPic) { |
806 | // -shared will change meaning when Module Linking is implemented. |
807 | if (ctx.arg.shared) { |
808 | warn(msg: "creating shared libraries, with -shared, is not yet stable"); |
809 | } |
810 | |
811 | // -pie will change meaning when Module Linking is implemented. |
812 | if (ctx.arg.pie) { |
813 | warn(msg: "creating PIEs, with -pie, is not yet stable"); |
814 | } |
815 | |
816 | if (ctx.arg.unresolvedSymbols == UnresolvedPolicy::ImportDynamic) { |
817 | warn(msg: "dynamic imports are not yet stable " |
818 | "(--unresolved-symbols=import-dynamic)"); |
819 | } |
820 | } |
821 | |
822 | if (ctx.arg.bsymbolic && !ctx.arg.shared) { |
823 | warn(msg: "-Bsymbolic is only meaningful when combined with -shared"); |
824 | } |
825 | |
826 | if (ctx.isPic) { |
827 | if (ctx.arg.globalBase) |
828 | error(msg: "--global-base may not be used with -shared/-pie"); |
829 | if (ctx.arg.tableBase) |
830 | error(msg: "--table-base may not be used with -shared/-pie"); |
831 | } |
832 | } |
833 | |
834 | static const char *getReproduceOption(opt::InputArgList &args) { |
835 | if (auto *arg = args.getLastArg(OPT_reproduce)) |
836 | return arg->getValue(); |
837 | return getenv(name: "LLD_REPRODUCE"); |
838 | } |
839 | |
840 | // Force Sym to be entered in the output. Used for -u or equivalent. |
841 | static Symbol *handleUndefined(StringRef name, const char *option) { |
842 | Symbol *sym = symtab->find(name); |
843 | if (!sym) |
844 | return nullptr; |
845 | |
846 | // Since symbol S may not be used inside the program, LTO may |
847 | // eliminate it. Mark the symbol as "used" to prevent it. |
848 | sym->isUsedInRegularObj = true; |
849 | |
850 | if (auto *lazySym = dyn_cast<LazySymbol>(sym)) { |
851 | lazySym->extract(); |
852 | if (!ctx.arg.whyExtract.empty()) |
853 | ctx.whyExtractRecords.emplace_back(option, sym->getFile(), *sym); |
854 | } |
855 | |
856 | return sym; |
857 | } |
858 | |
859 | static void handleLibcall(StringRef name) { |
860 | Symbol *sym = symtab->find(name); |
861 | if (sym && sym->isLazy() && isa<BitcodeFile>(sym->getFile())) { |
862 | if (!ctx.arg.whyExtract.empty()) |
863 | ctx.whyExtractRecords.emplace_back("<libcall>", sym->getFile(), *sym); |
864 | cast<LazySymbol>(sym)->extract(); |
865 | } |
866 | } |
867 | |
868 | static void writeWhyExtract() { |
869 | if (ctx.arg.whyExtract.empty()) |
870 | return; |
871 | |
872 | std::error_code ec; |
873 | raw_fd_ostream os(ctx.arg.whyExtract, ec, sys::fs::OF_None); |
874 | if (ec) { |
875 | error(msg: "cannot open --why-extract= file "+ ctx.arg.whyExtract + ": "+ |
876 | ec.message()); |
877 | return; |
878 | } |
879 | |
880 | os << "reference\textracted\tsymbol\n"; |
881 | for (auto &entry : ctx.whyExtractRecords) { |
882 | os << std::get<0>(entry) << '\t' << toString(std::get<1>(entry)) << '\t' |
883 | << toString(std::get<2>(entry)) << '\n'; |
884 | } |
885 | } |
886 | |
887 | // Equivalent of demote demoteSharedAndLazySymbols() in the ELF linker |
888 | static void demoteLazySymbols() { |
889 | for (Symbol *sym : symtab->symbols()) { |
890 | if (auto* s = dyn_cast<LazySymbol>(sym)) { |
891 | if (s->signature) { |
892 | LLVM_DEBUG(llvm::dbgs() |
893 | << "demoting lazy func: "<< s->getName() << "\n"); |
894 | replaceSymbol<UndefinedFunction>(s, s->getName(), std::nullopt, |
895 | std::nullopt, WASM_SYMBOL_BINDING_WEAK, |
896 | s->getFile(), s->signature); |
897 | } |
898 | } |
899 | } |
900 | } |
901 | |
902 | static UndefinedGlobal * |
903 | createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) { |
904 | auto *sym = cast<UndefinedGlobal>(symtab->addUndefinedGlobal( |
905 | name, importName: std::nullopt, importModule: std::nullopt, flags: WASM_SYMBOL_UNDEFINED, file: nullptr, type)); |
906 | ctx.arg.allowUndefinedSymbols.insert(sym->getName()); |
907 | sym->isUsedInRegularObj = true; |
908 | return sym; |
909 | } |
910 | |
911 | static InputGlobal *createGlobal(StringRef name, bool isMutable) { |
912 | llvm::wasm::WasmGlobal wasmGlobal; |
913 | bool is64 = ctx.arg.is64.value_or(false); |
914 | wasmGlobal.Type = {.Type: uint8_t(is64 ? WASM_TYPE_I64 : WASM_TYPE_I32), .Mutable: isMutable}; |
915 | wasmGlobal.InitExpr = intConst(value: 0, is64); |
916 | wasmGlobal.SymbolName = name; |
917 | return make<InputGlobal>(wasmGlobal, nullptr); |
918 | } |
919 | |
920 | static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable) { |
921 | InputGlobal *g = createGlobal(name, isMutable); |
922 | return symtab->addSyntheticGlobal(name, flags: WASM_SYMBOL_VISIBILITY_HIDDEN, global: g); |
923 | } |
924 | |
925 | static GlobalSymbol *createOptionalGlobal(StringRef name, bool isMutable) { |
926 | InputGlobal *g = createGlobal(name, isMutable); |
927 | return symtab->addOptionalGlobalSymbol(name, global: g); |
928 | } |
929 | |
930 | // Create ABI-defined synthetic symbols |
931 | static void createSyntheticSymbols() { |
932 | if (ctx.arg.relocatable) |
933 | return; |
934 | |
935 | static WasmSignature nullSignature = {{}, {}}; |
936 | static WasmSignature i32ArgSignature = {{}, {ValType::I32}}; |
937 | static WasmSignature i64ArgSignature = {{}, {ValType::I64}}; |
938 | static llvm::wasm::WasmGlobalType globalTypeI32 = {.Type: WASM_TYPE_I32, .Mutable: false}; |
939 | static llvm::wasm::WasmGlobalType globalTypeI64 = {.Type: WASM_TYPE_I64, .Mutable: false}; |
940 | static llvm::wasm::WasmGlobalType mutableGlobalTypeI32 = {.Type: WASM_TYPE_I32, |
941 | .Mutable: true}; |
942 | static llvm::wasm::WasmGlobalType mutableGlobalTypeI64 = {.Type: WASM_TYPE_I64, |
943 | .Mutable: true}; |
944 | ctx.sym.callCtors = symtab->addSyntheticFunction( |
945 | name: "__wasm_call_ctors", flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
946 | function: make<SyntheticFunction>(nullSignature, "__wasm_call_ctors")); |
947 | |
948 | bool is64 = ctx.arg.is64.value_or(false); |
949 | |
950 | if (ctx.isPic) { |
951 | ctx.sym.stackPointer = |
952 | createUndefinedGlobal(name: "__stack_pointer", type: ctx.arg.is64.value_or(false) |
953 | ? &mutableGlobalTypeI64 |
954 | : &mutableGlobalTypeI32); |
955 | // For PIC code, we import two global variables (__memory_base and |
956 | // __table_base) from the environment and use these as the offset at |
957 | // which to load our static data and function table. |
958 | // See: |
959 | // https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md |
960 | auto *globalType = is64 ? &globalTypeI64 : &globalTypeI32; |
961 | ctx.sym.memoryBase = createUndefinedGlobal("__memory_base", globalType); |
962 | ctx.sym.tableBase = createUndefinedGlobal("__table_base", globalType); |
963 | ctx.sym.memoryBase->markLive(); |
964 | ctx.sym.tableBase->markLive(); |
965 | } else { |
966 | // For non-PIC code |
967 | ctx.sym.stackPointer = createGlobalVariable(name: "__stack_pointer", isMutable: true); |
968 | ctx.sym.stackPointer->markLive(); |
969 | } |
970 | |
971 | if (ctx.arg.sharedMemory) { |
972 | ctx.sym.tlsBase = createGlobalVariable(name: "__tls_base", isMutable: true); |
973 | ctx.sym.tlsSize = createGlobalVariable(name: "__tls_size", isMutable: false); |
974 | ctx.sym.tlsAlign = createGlobalVariable(name: "__tls_align", isMutable: false); |
975 | ctx.sym.initTLS = symtab->addSyntheticFunction( |
976 | name: "__wasm_init_tls", flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
977 | function: make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature, |
978 | "__wasm_init_tls")); |
979 | } |
980 | } |
981 | |
982 | static void createOptionalSymbols() { |
983 | if (ctx.arg.relocatable) |
984 | return; |
985 | |
986 | ctx.sym.dsoHandle = symtab->addOptionalDataSymbol(name: "__dso_handle"); |
987 | |
988 | if (!ctx.arg.shared) |
989 | ctx.sym.dataEnd = symtab->addOptionalDataSymbol(name: "__data_end"); |
990 | |
991 | if (!ctx.isPic) { |
992 | ctx.sym.stackLow = symtab->addOptionalDataSymbol(name: "__stack_low"); |
993 | ctx.sym.stackHigh = symtab->addOptionalDataSymbol(name: "__stack_high"); |
994 | ctx.sym.globalBase = symtab->addOptionalDataSymbol(name: "__global_base"); |
995 | ctx.sym.heapBase = symtab->addOptionalDataSymbol(name: "__heap_base"); |
996 | ctx.sym.heapEnd = symtab->addOptionalDataSymbol(name: "__heap_end"); |
997 | ctx.sym.definedMemoryBase = symtab->addOptionalDataSymbol(name: "__memory_base"); |
998 | ctx.sym.definedTableBase = symtab->addOptionalDataSymbol(name: "__table_base"); |
999 | } |
1000 | |
1001 | ctx.sym.firstPageEnd = symtab->addOptionalDataSymbol(name: "__wasm_first_page_end"); |
1002 | if (ctx.sym.firstPageEnd) |
1003 | ctx.sym.firstPageEnd->setVA(ctx.arg.pageSize); |
1004 | |
1005 | // For non-shared memory programs we still need to define __tls_base since we |
1006 | // allow object files built with TLS to be linked into single threaded |
1007 | // programs, and such object files can contain references to this symbol. |
1008 | // |
1009 | // However, in this case __tls_base is immutable and points directly to the |
1010 | // start of the `.tdata` static segment. |
1011 | // |
1012 | // __tls_size and __tls_align are not needed in this case since they are only |
1013 | // needed for __wasm_init_tls (which we do not create in this case). |
1014 | if (!ctx.arg.sharedMemory) |
1015 | ctx.sym.tlsBase = createOptionalGlobal(name: "__tls_base", isMutable: false); |
1016 | } |
1017 | |
1018 | static void processStubLibrariesPreLTO() { |
1019 | log(msg: "-- processStubLibrariesPreLTO"); |
1020 | for (auto &stub_file : ctx.stubFiles) { |
1021 | LLVM_DEBUG(llvm::dbgs() |
1022 | << "processing stub file: "<< stub_file->getName() << "\n"); |
1023 | for (auto [name, deps]: stub_file->symbolDependencies) { |
1024 | auto* sym = symtab->find(name); |
1025 | // If the symbol is not present at all (yet), or if it is present but |
1026 | // undefined, then mark the dependent symbols as used by a regular |
1027 | // object so they will be preserved and exported by the LTO process. |
1028 | if (!sym || sym->isUndefined()) { |
1029 | for (const auto dep : deps) { |
1030 | auto* needed = symtab->find(dep); |
1031 | if (needed ) { |
1032 | needed->isUsedInRegularObj = true; |
1033 | // Like with handleLibcall we have to extract any LTO archive |
1034 | // members that might need to be exported due to stub library |
1035 | // symbols being referenced. Without this the LTO object could be |
1036 | // extracted during processStubLibraries, which is too late since |
1037 | // LTO has already being performed at that point. |
1038 | if (needed->isLazy() && isa<BitcodeFile>(needed->getFile())) { |
1039 | if (!ctx.arg.whyExtract.empty()) |
1040 | ctx.whyExtractRecords.emplace_back(toString(stub_file), |
1041 | needed->getFile(), *needed); |
1042 | cast<LazySymbol>(needed)->extract(); |
1043 | } |
1044 | } |
1045 | } |
1046 | } |
1047 | } |
1048 | } |
1049 | } |
1050 | |
1051 | static bool addStubSymbolDeps(const StubFile *stub_file, Symbol *sym, |
1052 | ArrayRef<StringRef> deps) { |
1053 | // The first stub library to define a given symbol sets this and |
1054 | // definitions in later stub libraries are ignored. |
1055 | if (sym->forceImport) |
1056 | return false; // Already handled |
1057 | sym->forceImport = true; |
1058 | if (sym->traced) |
1059 | message(msg: toString(file: stub_file) + ": importing "+ sym->getName()); |
1060 | else |
1061 | LLVM_DEBUG(llvm::dbgs() << toString(stub_file) << ": importing " |
1062 | << sym->getName() << "\n"); |
1063 | bool depsAdded = false; |
1064 | for (const auto dep : deps) { |
1065 | auto *needed = symtab->find(name: dep); |
1066 | if (!needed) { |
1067 | error(msg: toString(file: stub_file) + ": undefined symbol: "+ dep + |
1068 | ". Required by "+ toString(sym: *sym)); |
1069 | } else if (needed->isUndefined()) { |
1070 | error(msg: toString(file: stub_file) + ": undefined symbol: "+ toString(sym: *needed) + |
1071 | ". Required by "+ toString(sym: *sym)); |
1072 | } else { |
1073 | if (needed->traced) |
1074 | message(msg: toString(file: stub_file) + ": exported "+ toString(sym: *needed) + |
1075 | " due to import of "+ sym->getName()); |
1076 | else |
1077 | LLVM_DEBUG(llvm::dbgs() |
1078 | << "force export: "<< toString(*needed) << "\n"); |
1079 | needed->forceExport = true; |
1080 | if (auto *lazy = dyn_cast<LazySymbol>(needed)) { |
1081 | depsAdded = true; |
1082 | lazy->extract(); |
1083 | if (!ctx.arg.whyExtract.empty()) |
1084 | ctx.whyExtractRecords.emplace_back(toString(file: stub_file), |
1085 | sym->getFile(), *sym); |
1086 | } |
1087 | } |
1088 | } |
1089 | return depsAdded; |
1090 | } |
1091 | |
1092 | static void processStubLibraries() { |
1093 | log(msg: "-- processStubLibraries"); |
1094 | bool depsAdded = false; |
1095 | do { |
1096 | depsAdded = false; |
1097 | for (auto &stub_file : ctx.stubFiles) { |
1098 | LLVM_DEBUG(llvm::dbgs() |
1099 | << "processing stub file: "<< stub_file->getName() << "\n"); |
1100 | |
1101 | // First look for any imported symbols that directly match |
1102 | // the names of the stub imports |
1103 | for (auto [name, deps]: stub_file->symbolDependencies) { |
1104 | auto* sym = symtab->find(name); |
1105 | if (sym && sym->isUndefined()) { |
1106 | depsAdded |= addStubSymbolDeps(stub_file, sym, deps); |
1107 | } else { |
1108 | if (sym && sym->traced) |
1109 | message(toString(stub_file) + ": stub symbol not needed: "+ name); |
1110 | else |
1111 | LLVM_DEBUG(llvm::dbgs() |
1112 | << "stub symbol not needed: `"<< name << "`\n"); |
1113 | } |
1114 | } |
1115 | |
1116 | // Secondly looks for any symbols with an `importName` that matches |
1117 | for (Symbol *sym : symtab->symbols()) { |
1118 | if (sym->isUndefined() && sym->importName.has_value()) { |
1119 | auto it = stub_file->symbolDependencies.find(sym->importName.value()); |
1120 | if (it != stub_file->symbolDependencies.end()) { |
1121 | depsAdded |= addStubSymbolDeps(stub_file, sym, it->second); |
1122 | } |
1123 | } |
1124 | } |
1125 | } |
1126 | } while (depsAdded); |
1127 | |
1128 | log(msg: "-- done processStubLibraries"); |
1129 | } |
1130 | |
1131 | // Reconstructs command line arguments so that so that you can re-run |
1132 | // the same command with the same inputs. This is for --reproduce. |
1133 | static std::string createResponseFile(const opt::InputArgList &args) { |
1134 | SmallString<0> data; |
1135 | raw_svector_ostream os(data); |
1136 | |
1137 | // Copy the command line to the output while rewriting paths. |
1138 | for (auto *arg : args) { |
1139 | switch (arg->getOption().getID()) { |
1140 | case OPT_reproduce: |
1141 | break; |
1142 | case OPT_INPUT: |
1143 | os << quote(relativeToRoot(arg->getValue())) << "\n"; |
1144 | break; |
1145 | case OPT_o: |
1146 | // If -o path contains directories, "lld @response.txt" will likely |
1147 | // fail because the archive we are creating doesn't contain empty |
1148 | // directories for the output path (-o doesn't create directories). |
1149 | // Strip directories to prevent the issue. |
1150 | os << "-o "<< quote(sys::path::filename(arg->getValue())) << "\n"; |
1151 | break; |
1152 | default: |
1153 | os << toString(*arg) << "\n"; |
1154 | } |
1155 | } |
1156 | return std::string(data); |
1157 | } |
1158 | |
1159 | // The --wrap option is a feature to rename symbols so that you can write |
1160 | // wrappers for existing functions. If you pass `-wrap=foo`, all |
1161 | // occurrences of symbol `foo` are resolved to `wrap_foo` (so, you are |
1162 | // expected to write `wrap_foo` function as a wrapper). The original |
1163 | // symbol becomes accessible as `real_foo`, so you can call that from your |
1164 | // wrapper. |
1165 | // |
1166 | // This data structure is instantiated for each -wrap option. |
1167 | struct WrappedSymbol { |
1168 | Symbol *sym; |
1169 | Symbol *real; |
1170 | Symbol *wrap; |
1171 | }; |
1172 | |
1173 | static Symbol *addUndefined(StringRef name) { |
1174 | return symtab->addUndefinedFunction(name, importName: std::nullopt, importModule: std::nullopt, |
1175 | flags: WASM_SYMBOL_UNDEFINED, file: nullptr, signature: nullptr, |
1176 | isCalledDirectly: false); |
1177 | } |
1178 | |
1179 | // Handles -wrap option. |
1180 | // |
1181 | // This function instantiates wrapper symbols. At this point, they seem |
1182 | // like they are not being used at all, so we explicitly set some flags so |
1183 | // that LTO won't eliminate them. |
1184 | static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &args) { |
1185 | std::vector<WrappedSymbol> v; |
1186 | DenseSet<StringRef> seen; |
1187 | |
1188 | for (auto *arg : args.filtered(OPT_wrap)) { |
1189 | StringRef name = arg->getValue(); |
1190 | if (!seen.insert(name).second) |
1191 | continue; |
1192 | |
1193 | Symbol *sym = symtab->find(name); |
1194 | if (!sym) |
1195 | continue; |
1196 | |
1197 | Symbol *real = addUndefined(saver().save("__real_"+ name)); |
1198 | Symbol *wrap = addUndefined(saver().save("__wrap_"+ name)); |
1199 | v.push_back({sym, real, wrap}); |
1200 | |
1201 | // We want to tell LTO not to inline symbols to be overwritten |
1202 | // because LTO doesn't know the final symbol contents after renaming. |
1203 | real->canInline = false; |
1204 | sym->canInline = false; |
1205 | |
1206 | // Tell LTO not to eliminate these symbols. |
1207 | sym->isUsedInRegularObj = true; |
1208 | wrap->isUsedInRegularObj = true; |
1209 | real->isUsedInRegularObj = false; |
1210 | } |
1211 | return v; |
1212 | } |
1213 | |
1214 | // Do renaming for -wrap by updating pointers to symbols. |
1215 | // |
1216 | // When this function is executed, only InputFiles and symbol table |
1217 | // contain pointers to symbol objects. We visit them to replace pointers, |
1218 | // so that wrapped symbols are swapped as instructed by the command line. |
1219 | static void wrapSymbols(ArrayRef<WrappedSymbol> wrapped) { |
1220 | DenseMap<Symbol *, Symbol *> map; |
1221 | for (const WrappedSymbol &w : wrapped) { |
1222 | map[w.sym] = w.wrap; |
1223 | map[w.real] = w.sym; |
1224 | } |
1225 | |
1226 | // Update pointers in input files. |
1227 | parallelForEach(ctx.objectFiles, [&](InputFile *file) { |
1228 | MutableArrayRef<Symbol *> syms = file->getMutableSymbols(); |
1229 | for (size_t i = 0, e = syms.size(); i != e; ++i) |
1230 | if (Symbol *s = map.lookup(syms[i])) |
1231 | syms[i] = s; |
1232 | }); |
1233 | |
1234 | // Update pointers in the symbol table. |
1235 | for (const WrappedSymbol &w : wrapped) |
1236 | symtab->wrap(w.sym, w.real, w.wrap); |
1237 | } |
1238 | |
1239 | static void splitSections() { |
1240 | // splitIntoPieces needs to be called on each MergeInputChunk |
1241 | // before calling finalizeContents(). |
1242 | LLVM_DEBUG(llvm::dbgs() << "splitSections\n"); |
1243 | parallelForEach(ctx.objectFiles, [](ObjFile *file) { |
1244 | for (InputChunk *seg : file->segments) { |
1245 | if (auto *s = dyn_cast<MergeInputChunk>(seg)) |
1246 | s->splitIntoPieces(); |
1247 | } |
1248 | for (InputChunk *sec : file->customSections) { |
1249 | if (auto *s = dyn_cast<MergeInputChunk>(sec)) |
1250 | s->splitIntoPieces(); |
1251 | } |
1252 | }); |
1253 | } |
1254 | |
1255 | static bool isKnownZFlag(StringRef s) { |
1256 | // For now, we only support a very limited set of -z flags |
1257 | return s.starts_with(Prefix: "stack-size=") || s.starts_with(Prefix: "muldefs"); |
1258 | } |
1259 | |
1260 | // Report a warning for an unknown -z option. |
1261 | static void checkZOptions(opt::InputArgList &args) { |
1262 | for (auto *arg : args.filtered(OPT_z)) |
1263 | if (!isKnownZFlag(arg->getValue())) |
1264 | warn("unknown -z value: "+ StringRef(arg->getValue())); |
1265 | } |
1266 | |
1267 | LinkerDriver::LinkerDriver(Ctx &ctx) : ctx(ctx) {} |
1268 | |
1269 | void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { |
1270 | WasmOptTable parser; |
1271 | opt::InputArgList args = parser.parse(argv: argsArr.slice(1)); |
1272 | |
1273 | // Interpret these flags early because error()/warn() depend on them. |
1274 | auto &errHandler = errorHandler(); |
1275 | errHandler.errorLimit = args::getInteger(args, OPT_error_limit, 20); |
1276 | errHandler.fatalWarnings = |
1277 | args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); |
1278 | checkZOptions(args); |
1279 | |
1280 | // Handle --help |
1281 | if (args.hasArg(OPT_help)) { |
1282 | parser.printHelp(errHandler.outs(), |
1283 | (std::string(argsArr[0]) + " [options] file...").c_str(), |
1284 | "LLVM Linker", false); |
1285 | return; |
1286 | } |
1287 | |
1288 | // Handle -v or -version. |
1289 | if (args.hasArg(OPT_v) || args.hasArg(OPT_version)) |
1290 | errHandler.outs() << getLLDVersion() << "\n"; |
1291 | |
1292 | // Handle --reproduce |
1293 | if (const char *path = getReproduceOption(args)) { |
1294 | Expected<std::unique_ptr<TarWriter>> errOrWriter = |
1295 | TarWriter::create(path, path::stem(path)); |
1296 | if (errOrWriter) { |
1297 | tar = std::move(*errOrWriter); |
1298 | tar->append("response.txt", createResponseFile(args)); |
1299 | tar->append("version.txt", getLLDVersion() + "\n"); |
1300 | } else { |
1301 | error("--reproduce: "+ toString(errOrWriter.takeError())); |
1302 | } |
1303 | } |
1304 | |
1305 | // Parse and evaluate -mllvm options. |
1306 | std::vector<const char *> v; |
1307 | v.push_back("wasm-ld (LLVM option parsing)"); |
1308 | for (auto *arg : args.filtered(OPT_mllvm)) |
1309 | v.push_back(arg->getValue()); |
1310 | cl::ResetAllOptionOccurrences(); |
1311 | cl::ParseCommandLineOptions(argc: v.size(), argv: v.data()); |
1312 | |
1313 | readConfigs(args); |
1314 | setConfigs(); |
1315 | |
1316 | // The behavior of -v or --version is a bit strange, but this is |
1317 | // needed for compatibility with GNU linkers. |
1318 | if (args.hasArg(OPT_v) && !args.hasArg(OPT_INPUT)) |
1319 | return; |
1320 | if (args.hasArg(OPT_version)) |
1321 | return; |
1322 | |
1323 | createFiles(args); |
1324 | if (errorCount()) |
1325 | return; |
1326 | |
1327 | checkOptions(args); |
1328 | if (errorCount()) |
1329 | return; |
1330 | |
1331 | if (auto *arg = args.getLastArg(OPT_allow_undefined_file)) |
1332 | readImportFile(arg->getValue()); |
1333 | |
1334 | // Fail early if the output file or map file is not writable. If a user has a |
1335 | // long link, e.g. due to a large LTO link, they do not wish to run it and |
1336 | // find that it failed because there was a mistake in their command-line. |
1337 | if (auto e = tryCreateFile(path: ctx.arg.outputFile)) |
1338 | error(msg: "cannot open output file "+ ctx.arg.outputFile + ": "+ e.message()); |
1339 | if (auto e = tryCreateFile(path: ctx.arg.mapFile)) |
1340 | error(msg: "cannot open map file "+ ctx.arg.mapFile + ": "+ e.message()); |
1341 | if (errorCount()) |
1342 | return; |
1343 | |
1344 | // Handle --trace-symbol. |
1345 | for (auto *arg : args.filtered(OPT_trace_symbol)) |
1346 | symtab->trace(arg->getValue()); |
1347 | |
1348 | for (auto *arg : args.filtered(OPT_export_if_defined)) |
1349 | ctx.arg.exportedSymbols.insert(arg->getValue()); |
1350 | |
1351 | for (auto *arg : args.filtered(OPT_export)) { |
1352 | ctx.arg.exportedSymbols.insert(arg->getValue()); |
1353 | ctx.arg.requiredExports.push_back(arg->getValue()); |
1354 | } |
1355 | |
1356 | createSyntheticSymbols(); |
1357 | |
1358 | // Add all files to the symbol table. This will add almost all |
1359 | // symbols that we need to the symbol table. |
1360 | for (InputFile *f : files) |
1361 | symtab->addFile(f); |
1362 | if (errorCount()) |
1363 | return; |
1364 | |
1365 | // Handle the `--undefined <sym>` options. |
1366 | for (auto *arg : args.filtered(OPT_undefined)) |
1367 | handleUndefined(arg->getValue(), "<internal>"); |
1368 | |
1369 | // Handle the `--export <sym>` options |
1370 | // This works like --undefined but also exports the symbol if its found |
1371 | for (auto &iter : ctx.arg.exportedSymbols) |
1372 | handleUndefined(iter.first(), "--export"); |
1373 | |
1374 | Symbol *entrySym = nullptr; |
1375 | if (!ctx.arg.relocatable && !ctx.arg.entry.empty()) { |
1376 | entrySym = handleUndefined(name: ctx.arg.entry, option: "--entry"); |
1377 | if (entrySym && entrySym->isDefined()) |
1378 | entrySym->forceExport = true; |
1379 | else |
1380 | error(msg: "entry symbol not defined (pass --no-entry to suppress): "+ |
1381 | ctx.arg.entry); |
1382 | } |
1383 | |
1384 | // If the user code defines a `__wasm_call_dtors` function, remember it so |
1385 | // that we can call it from the command export wrappers. Unlike |
1386 | // `__wasm_call_ctors` which we synthesize, `__wasm_call_dtors` is defined |
1387 | // by libc/etc., because destructors are registered dynamically with |
1388 | // `__cxa_atexit` and friends. |
1389 | if (!ctx.arg.relocatable && !ctx.arg.shared && |
1390 | !ctx.sym.callCtors->isUsedInRegularObj && |
1391 | ctx.sym.callCtors->getName() != ctx.arg.entry && |
1392 | !ctx.arg.exportedSymbols.count(Key: ctx.sym.callCtors->getName())) { |
1393 | if (Symbol *callDtors = |
1394 | handleUndefined(name: "__wasm_call_dtors", option: "<internal>")) { |
1395 | if (auto *callDtorsFunc = dyn_cast<DefinedFunction>(callDtors)) { |
1396 | if (callDtorsFunc->signature && |
1397 | (!callDtorsFunc->signature->Params.empty() || |
1398 | !callDtorsFunc->signature->Returns.empty())) { |
1399 | error(msg: "__wasm_call_dtors must have no argument or return values"); |
1400 | } |
1401 | ctx.sym.callDtors = callDtorsFunc; |
1402 | } else { |
1403 | error(msg: "__wasm_call_dtors must be a function"); |
1404 | } |
1405 | } |
1406 | } |
1407 | |
1408 | if (errorCount()) |
1409 | return; |
1410 | |
1411 | // Create wrapped symbols for -wrap option. |
1412 | std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args); |
1413 | |
1414 | // If any of our inputs are bitcode files, the LTO code generator may create |
1415 | // references to certain library functions that might not be explicit in the |
1416 | // bitcode file's symbol table. If any of those library functions are defined |
1417 | // in a bitcode file in an archive member, we need to arrange to use LTO to |
1418 | // compile those archive members by adding them to the link beforehand. |
1419 | // |
1420 | // We only need to add libcall symbols to the link before LTO if the symbol's |
1421 | // definition is in bitcode. Any other required libcall symbols will be added |
1422 | // to the link after LTO when we add the LTO object file to the link. |
1423 | if (!ctx.bitcodeFiles.empty()) { |
1424 | llvm::Triple TT(ctx.bitcodeFiles.front()->obj->getTargetTriple()); |
1425 | for (auto *s : lto::LTO::getRuntimeLibcallSymbols(TT)) |
1426 | handleLibcall(s); |
1427 | } |
1428 | if (errorCount()) |
1429 | return; |
1430 | |
1431 | // We process the stub libraries once beofore LTO to ensure that any possible |
1432 | // required exports are preserved by the LTO process. |
1433 | processStubLibrariesPreLTO(); |
1434 | |
1435 | // Do link-time optimization if given files are LLVM bitcode files. |
1436 | // This compiles bitcode files into real object files. |
1437 | symtab->compileBitcodeFiles(); |
1438 | if (errorCount()) |
1439 | return; |
1440 | |
1441 | // The LTO process can generate new undefined symbols, specifically libcall |
1442 | // functions. Because those symbols might be declared in a stub library we |
1443 | // need the process the stub libraries once again after LTO to handle all |
1444 | // undefined symbols, including ones that didn't exist prior to LTO. |
1445 | processStubLibraries(); |
1446 | |
1447 | writeWhyExtract(); |
1448 | |
1449 | // Bail out if normal linked output is skipped due to LTO. |
1450 | if (ctx.arg.thinLTOIndexOnly) |
1451 | return; |
1452 | |
1453 | createOptionalSymbols(); |
1454 | |
1455 | // Resolve any variant symbols that were created due to signature |
1456 | // mismatchs. |
1457 | symtab->handleSymbolVariants(); |
1458 | if (errorCount()) |
1459 | return; |
1460 | |
1461 | // Apply symbol renames for -wrap. |
1462 | if (!wrapped.empty()) |
1463 | wrapSymbols(wrapped); |
1464 | |
1465 | for (auto &iter : ctx.arg.exportedSymbols) { |
1466 | Symbol *sym = symtab->find(iter.first()); |
1467 | if (sym && sym->isDefined()) |
1468 | sym->forceExport = true; |
1469 | } |
1470 | |
1471 | if (!ctx.arg.relocatable && !ctx.isPic) { |
1472 | // Add synthetic dummies for weak undefined functions. Must happen |
1473 | // after LTO otherwise functions may not yet have signatures. |
1474 | symtab->handleWeakUndefines(); |
1475 | } |
1476 | |
1477 | if (entrySym) |
1478 | entrySym->setHidden(false); |
1479 | |
1480 | if (errorCount()) |
1481 | return; |
1482 | |
1483 | // Split WASM_SEG_FLAG_STRINGS sections into pieces in preparation for garbage |
1484 | // collection. |
1485 | splitSections(); |
1486 | |
1487 | // Any remaining lazy symbols should be demoted to Undefined |
1488 | demoteLazySymbols(); |
1489 | |
1490 | // Do size optimizations: garbage collection |
1491 | markLive(); |
1492 | |
1493 | // Provide the indirect function table if needed. |
1494 | ctx.sym.indirectFunctionTable = |
1495 | symtab->resolveIndirectFunctionTable(/*required =*/false); |
1496 | |
1497 | if (errorCount()) |
1498 | return; |
1499 | |
1500 | // Write the result to the file. |
1501 | writeResult(); |
1502 | } |
1503 | |
1504 | } // namespace lld::wasm |
1505 |
Definitions
- ctx
- errorOrWarn
- Ctx
- reset
- initLLVM
- LinkerDriver
- hasZOption
- link
- optInfo
- WasmOptTable
- WasmOptTable
- handleColorDiagnostics
- getQuotingStyle
- findFile
- parse
- readImportFile
- getArchiveMembers
- addFile
- findFromSearchPaths
- searchLibraryBaseName
- searchLibrary
- addLibrary
- createFiles
- getAliasSpelling
- getOldNewOptions
- getOldNewOptionsExtra
- getEntry
- getUnresolvedSymbolPolicy
- getBuildId
- readConfigs
- setConfigs
- checkOptions
- getReproduceOption
- handleUndefined
- handleLibcall
- writeWhyExtract
- demoteLazySymbols
- createUndefinedGlobal
- createGlobal
- createGlobalVariable
- createOptionalGlobal
- createSyntheticSymbols
- createOptionalSymbols
- processStubLibrariesPreLTO
- addStubSymbolDeps
- processStubLibraries
- createResponseFile
- WrappedSymbol
- addUndefined
- addWrappedSymbols
- wrapSymbols
- splitSections
- isKnownZFlag
- checkZOptions
- LinkerDriver
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more