1 | //===- SymbolTable.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 "SymbolTable.h" |
10 | #include "COFFLinkerContext.h" |
11 | #include "Config.h" |
12 | #include "Driver.h" |
13 | #include "LTO.h" |
14 | #include "PDB.h" |
15 | #include "Symbols.h" |
16 | #include "lld/Common/ErrorHandler.h" |
17 | #include "lld/Common/Memory.h" |
18 | #include "lld/Common/Timer.h" |
19 | #include "llvm/DebugInfo/DIContext.h" |
20 | #include "llvm/IR/LLVMContext.h" |
21 | #include "llvm/IR/Mangler.h" |
22 | #include "llvm/LTO/LTO.h" |
23 | #include "llvm/Object/COFFModuleDefinition.h" |
24 | #include "llvm/Support/Debug.h" |
25 | #include "llvm/Support/GlobPattern.h" |
26 | #include "llvm/Support/Parallel.h" |
27 | #include "llvm/Support/TimeProfiler.h" |
28 | #include "llvm/Support/raw_ostream.h" |
29 | #include <utility> |
30 | |
31 | using namespace llvm; |
32 | using namespace llvm::COFF; |
33 | using namespace llvm::object; |
34 | using namespace llvm::support; |
35 | |
36 | namespace lld::coff { |
37 | |
38 | StringRef ltrim1(StringRef s, const char *chars) { |
39 | if (!s.empty() && strchr(s: chars, c: s[0])) |
40 | return s.substr(Start: 1); |
41 | return s; |
42 | } |
43 | |
44 | static COFFSyncStream errorOrWarn(COFFLinkerContext &ctx) { |
45 | return {ctx, ctx.config.forceUnresolved ? DiagLevel::Warn : DiagLevel::Err}; |
46 | } |
47 | |
48 | // Causes the file associated with a lazy symbol to be linked in. |
49 | static void forceLazy(Symbol *s) { |
50 | s->pendingArchiveLoad = true; |
51 | switch (s->kind()) { |
52 | case Symbol::Kind::LazyArchiveKind: { |
53 | auto *l = cast<LazyArchive>(Val: s); |
54 | l->file->addMember(sym: l->sym); |
55 | break; |
56 | } |
57 | case Symbol::Kind::LazyObjectKind: { |
58 | InputFile *file = cast<LazyObject>(Val: s)->file; |
59 | // FIXME: Remove this once we resolve all defineds before all undefineds in |
60 | // ObjFile::initializeSymbols(). |
61 | if (!file->lazy) |
62 | return; |
63 | file->lazy = false; |
64 | file->symtab.ctx.driver.addFile(file); |
65 | break; |
66 | } |
67 | case Symbol::Kind::LazyDLLSymbolKind: { |
68 | auto *l = cast<LazyDLLSymbol>(Val: s); |
69 | l->file->makeImport(s: l->sym); |
70 | break; |
71 | } |
72 | default: |
73 | llvm_unreachable( |
74 | "symbol passed to forceLazy is not a LazyArchive or LazyObject"); |
75 | } |
76 | } |
77 | |
78 | // Returns the symbol in SC whose value is <= Addr that is closest to Addr. |
79 | // This is generally the global variable or function whose definition contains |
80 | // Addr. |
81 | static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) { |
82 | DefinedRegular *candidate = nullptr; |
83 | |
84 | for (Symbol *s : sc->file->getSymbols()) { |
85 | auto *d = dyn_cast_or_null<DefinedRegular>(Val: s); |
86 | if (!d || !d->data || d->file != sc->file || d->getChunk() != sc || |
87 | d->getValue() > addr || |
88 | (candidate && d->getValue() < candidate->getValue())) |
89 | continue; |
90 | |
91 | candidate = d; |
92 | } |
93 | |
94 | return candidate; |
95 | } |
96 | |
97 | static std::vector<std::string> getSymbolLocations(BitcodeFile *file) { |
98 | std::string res("\n>>> referenced by "); |
99 | StringRef source = file->obj->getSourceFileName(); |
100 | if (!source.empty()) |
101 | res += source.str() + "\n>>> "; |
102 | res += toString(file); |
103 | return {res}; |
104 | } |
105 | |
106 | static std::optional<std::pair<StringRef, uint32_t>> |
107 | getFileLineDwarf(const SectionChunk *c, uint32_t addr) { |
108 | std::optional<DILineInfo> optionalLineInfo = |
109 | c->file->getDILineInfo(offset: addr, sectionIndex: c->getSectionNumber() - 1); |
110 | if (!optionalLineInfo) |
111 | return std::nullopt; |
112 | const DILineInfo &lineInfo = *optionalLineInfo; |
113 | if (lineInfo.FileName == DILineInfo::BadString) |
114 | return std::nullopt; |
115 | return std::make_pair(x: saver().save(S: lineInfo.FileName), y: lineInfo.Line); |
116 | } |
117 | |
118 | static std::optional<std::pair<StringRef, uint32_t>> |
119 | getFileLine(const SectionChunk *c, uint32_t addr) { |
120 | // MinGW can optionally use codeview, even if the default is dwarf. |
121 | std::optional<std::pair<StringRef, uint32_t>> fileLine = |
122 | getFileLineCodeView(c, addr); |
123 | // If codeview didn't yield any result, check dwarf in MinGW mode. |
124 | if (!fileLine && c->file->symtab.ctx.config.mingw) |
125 | fileLine = getFileLineDwarf(c, addr); |
126 | return fileLine; |
127 | } |
128 | |
129 | // Given a file and the index of a symbol in that file, returns a description |
130 | // of all references to that symbol from that file. If no debug information is |
131 | // available, returns just the name of the file, else one string per actual |
132 | // reference as described in the debug info. |
133 | // Returns up to maxStrings string descriptions, along with the total number of |
134 | // locations found. |
135 | static std::pair<std::vector<std::string>, size_t> |
136 | getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) { |
137 | struct Location { |
138 | Symbol *sym; |
139 | std::pair<StringRef, uint32_t> fileLine; |
140 | }; |
141 | std::vector<Location> locations; |
142 | size_t numLocations = 0; |
143 | |
144 | for (Chunk *c : file->getChunks()) { |
145 | auto *sc = dyn_cast<SectionChunk>(Val: c); |
146 | if (!sc) |
147 | continue; |
148 | for (const coff_relocation &r : sc->getRelocs()) { |
149 | if (r.SymbolTableIndex != symIndex) |
150 | continue; |
151 | numLocations++; |
152 | if (locations.size() >= maxStrings) |
153 | continue; |
154 | |
155 | std::optional<std::pair<StringRef, uint32_t>> fileLine = |
156 | getFileLine(c: sc, addr: r.VirtualAddress); |
157 | Symbol *sym = getSymbol(sc, addr: r.VirtualAddress); |
158 | if (fileLine) |
159 | locations.push_back(x: {.sym: sym, .fileLine: *fileLine}); |
160 | else if (sym) |
161 | locations.push_back(x: {.sym: sym, .fileLine: {"", 0}}); |
162 | } |
163 | } |
164 | |
165 | if (maxStrings == 0) |
166 | return std::make_pair(x: std::vector<std::string>(), y&: numLocations); |
167 | |
168 | if (numLocations == 0) |
169 | return std::make_pair( |
170 | x: std::vector<std::string>{"\n>>> referenced by "+ toString(file)}, y: 1); |
171 | |
172 | std::vector<std::string> symbolLocations(locations.size()); |
173 | size_t i = 0; |
174 | for (Location loc : locations) { |
175 | llvm::raw_string_ostream os(symbolLocations[i++]); |
176 | os << "\n>>> referenced by "; |
177 | if (!loc.fileLine.first.empty()) |
178 | os << loc.fileLine.first << ":"<< loc.fileLine.second |
179 | << "\n>>> "; |
180 | os << toString(file); |
181 | if (loc.sym) |
182 | os << ":("<< toString(ctx: file->symtab.ctx, b&: *loc.sym) << ')'; |
183 | } |
184 | return std::make_pair(x&: symbolLocations, y&: numLocations); |
185 | } |
186 | |
187 | std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) { |
188 | return getSymbolLocations(file, symIndex, SIZE_MAX).first; |
189 | } |
190 | |
191 | static std::pair<std::vector<std::string>, size_t> |
192 | getSymbolLocations(InputFile *file, uint32_t symIndex, size_t maxStrings) { |
193 | if (auto *o = dyn_cast<ObjFile>(Val: file)) |
194 | return getSymbolLocations(file: o, symIndex, maxStrings); |
195 | if (auto *b = dyn_cast<BitcodeFile>(Val: file)) { |
196 | std::vector<std::string> symbolLocations = getSymbolLocations(file: b); |
197 | size_t numLocations = symbolLocations.size(); |
198 | if (symbolLocations.size() > maxStrings) |
199 | symbolLocations.resize(new_size: maxStrings); |
200 | return std::make_pair(x&: symbolLocations, y&: numLocations); |
201 | } |
202 | llvm_unreachable("unsupported file type passed to getSymbolLocations"); |
203 | return std::make_pair(x: std::vector<std::string>(), y: (size_t)0); |
204 | } |
205 | |
206 | // For an undefined symbol, stores all files referencing it and the index of |
207 | // the undefined symbol in each file. |
208 | struct UndefinedDiag { |
209 | Symbol *sym; |
210 | struct File { |
211 | InputFile *file; |
212 | uint32_t symIndex; |
213 | }; |
214 | std::vector<File> files; |
215 | }; |
216 | |
217 | void SymbolTable::reportUndefinedSymbol(const UndefinedDiag &undefDiag) { |
218 | auto diag = errorOrWarn(ctx); |
219 | diag << "undefined symbol: "<< printSymbol(sym: undefDiag.sym); |
220 | |
221 | const size_t maxUndefReferences = 3; |
222 | size_t numDisplayedRefs = 0, numRefs = 0; |
223 | for (const UndefinedDiag::File &ref : undefDiag.files) { |
224 | auto [symbolLocations, totalLocations] = getSymbolLocations( |
225 | file: ref.file, symIndex: ref.symIndex, maxStrings: maxUndefReferences - numDisplayedRefs); |
226 | |
227 | numRefs += totalLocations; |
228 | numDisplayedRefs += symbolLocations.size(); |
229 | for (const std::string &s : symbolLocations) |
230 | diag << s; |
231 | } |
232 | if (numDisplayedRefs < numRefs) |
233 | diag << "\n>>> referenced "<< numRefs - numDisplayedRefs << " more times"; |
234 | |
235 | // Hints |
236 | StringRef name = undefDiag.sym->getName(); |
237 | if (name.consume_front(Prefix: "__imp_")) { |
238 | Symbol *imp = find(name); |
239 | if (imp && imp->isLazy()) { |
240 | diag << "\nNOTE: a relevant symbol '"<< imp->getName() |
241 | << "' is available in "<< toString(file: imp->getFile()) |
242 | << " but cannot be used because it is not an import library."; |
243 | } |
244 | } |
245 | } |
246 | |
247 | void SymbolTable::loadMinGWSymbols() { |
248 | std::vector<Symbol *> undefs; |
249 | for (auto &i : symMap) { |
250 | Symbol *sym = i.second; |
251 | auto *undef = dyn_cast<Undefined>(Val: sym); |
252 | if (!undef) |
253 | continue; |
254 | if (undef->getWeakAlias()) |
255 | continue; |
256 | undefs.push_back(x: sym); |
257 | } |
258 | |
259 | for (auto sym : undefs) { |
260 | auto *undef = dyn_cast<Undefined>(Val: sym); |
261 | if (!undef) |
262 | continue; |
263 | if (undef->getWeakAlias()) |
264 | continue; |
265 | StringRef name = undef->getName(); |
266 | |
267 | if (machine == I386 && ctx.config.stdcallFixup) { |
268 | // Check if we can resolve an undefined decorated symbol by finding |
269 | // the intended target as an undecorated symbol (only with a leading |
270 | // underscore). |
271 | StringRef origName = name; |
272 | StringRef baseName = name; |
273 | // Trim down stdcall/fastcall/vectorcall symbols to the base name. |
274 | baseName = ltrim1(s: baseName, chars: "_@"); |
275 | baseName = baseName.substr(Start: 0, N: baseName.find(C: '@')); |
276 | // Add a leading underscore, as it would be in cdecl form. |
277 | std::string newName = ("_"+ baseName).str(); |
278 | Symbol *l; |
279 | if (newName != origName && (l = find(name: newName)) != nullptr) { |
280 | // If we found a symbol and it is lazy; load it. |
281 | if (l->isLazy() && !l->pendingArchiveLoad) { |
282 | Log(ctx) << "Loading lazy "<< l->getName() << " from " |
283 | << l->getFile()->getName() << " for stdcall fixup"; |
284 | forceLazy(s: l); |
285 | } |
286 | // If it's lazy or already defined, hook it up as weak alias. |
287 | if (l->isLazy() || isa<Defined>(Val: l)) { |
288 | if (ctx.config.warnStdcallFixup) |
289 | Warn(ctx) << "Resolving "<< origName << " by linking to " |
290 | << newName; |
291 | else |
292 | Log(ctx) << "Resolving "<< origName << " by linking to " |
293 | << newName; |
294 | undef->setWeakAlias(sym: l); |
295 | continue; |
296 | } |
297 | } |
298 | } |
299 | |
300 | if (ctx.config.autoImport) { |
301 | if (name.starts_with(Prefix: "__imp_")) |
302 | continue; |
303 | // If we have an undefined symbol, but we have a lazy symbol we could |
304 | // load, load it. |
305 | Symbol *l = find(name: ("__imp_"+ name).str()); |
306 | if (!l || l->pendingArchiveLoad || !l->isLazy()) |
307 | continue; |
308 | |
309 | Log(ctx) << "Loading lazy "<< l->getName() << " from " |
310 | << l->getFile()->getName() << " for automatic import"; |
311 | forceLazy(s: l); |
312 | } |
313 | } |
314 | } |
315 | |
316 | Defined *SymbolTable::impSymbol(StringRef name) { |
317 | if (name.starts_with(Prefix: "__imp_")) |
318 | return nullptr; |
319 | return dyn_cast_or_null<Defined>(Val: find(name: ("__imp_"+ name).str())); |
320 | } |
321 | |
322 | bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { |
323 | Defined *imp = impSymbol(name); |
324 | if (!imp) |
325 | return false; |
326 | |
327 | // Replace the reference directly to a variable with a reference |
328 | // to the import address table instead. This obviously isn't right, |
329 | // but we mark the symbol as isRuntimePseudoReloc, and a later pass |
330 | // will add runtime pseudo relocations for every relocation against |
331 | // this Symbol. The runtime pseudo relocation framework expects the |
332 | // reference itself to point at the IAT entry. |
333 | size_t impSize = 0; |
334 | if (isa<DefinedImportData>(Val: imp)) { |
335 | Log(ctx) << "Automatically importing "<< name << " from " |
336 | << cast<DefinedImportData>(Val: imp)->getDLLName(); |
337 | impSize = sizeof(DefinedImportData); |
338 | } else if (isa<DefinedRegular>(Val: imp)) { |
339 | Log(ctx) << "Automatically importing "<< name << " from " |
340 | << toString(file: cast<DefinedRegular>(Val: imp)->file); |
341 | impSize = sizeof(DefinedRegular); |
342 | } else { |
343 | Warn(ctx) << "unable to automatically import "<< name << " from " |
344 | << imp->getName() << " from "<< cast<DefinedRegular>(Val: imp)->file |
345 | << "; unexpected symbol type"; |
346 | return false; |
347 | } |
348 | sym->replaceKeepingName(other: imp, size: impSize); |
349 | sym->isRuntimePseudoReloc = true; |
350 | |
351 | // There may exist symbols named .refptr.<name> which only consist |
352 | // of a single pointer to <name>. If it turns out <name> is |
353 | // automatically imported, we don't need to keep the .refptr.<name> |
354 | // pointer at all, but redirect all accesses to it to the IAT entry |
355 | // for __imp_<name> instead, and drop the whole .refptr.<name> chunk. |
356 | DefinedRegular *refptr = |
357 | dyn_cast_or_null<DefinedRegular>(Val: find(name: (".refptr."+ name).str())); |
358 | if (refptr && refptr->getChunk()->getSize() == ctx.config.wordsize) { |
359 | SectionChunk *sc = dyn_cast_or_null<SectionChunk>(Val: refptr->getChunk()); |
360 | if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) { |
361 | Log(ctx) << "Replacing .refptr."<< name << " with "<< imp->getName(); |
362 | refptr->getChunk()->live = false; |
363 | refptr->replaceKeepingName(other: imp, size: impSize); |
364 | } |
365 | } |
366 | return true; |
367 | } |
368 | |
369 | /// Helper function for reportUnresolvable and resolveRemainingUndefines. |
370 | /// This function emits an "undefined symbol" diagnostic for each symbol in |
371 | /// undefs. If localImports is not nullptr, it also emits a "locally |
372 | /// defined symbol imported" diagnostic for symbols in localImports. |
373 | /// objFiles and bitcodeFiles (if not nullptr) are used to report where |
374 | /// undefined symbols are referenced. |
375 | void SymbolTable::reportProblemSymbols( |
376 | const SmallPtrSetImpl<Symbol *> &undefs, |
377 | const DenseMap<Symbol *, Symbol *> *localImports, bool needBitcodeFiles) { |
378 | // Return early if there is nothing to report (which should be |
379 | // the common case). |
380 | if (undefs.empty() && (!localImports || localImports->empty())) |
381 | return; |
382 | |
383 | for (Symbol *b : ctx.config.gcroot) { |
384 | if (undefs.count(Ptr: b)) |
385 | errorOrWarn(ctx) << "<root>: undefined symbol: "<< printSymbol(sym: b); |
386 | if (localImports) |
387 | if (Symbol *imp = localImports->lookup(Val: b)) |
388 | Warn(ctx) << "<root>: locally defined symbol imported: " |
389 | << printSymbol(sym: imp) << " (defined in " |
390 | << toString(file: imp->getFile()) << ") [LNK4217]"; |
391 | } |
392 | |
393 | std::vector<UndefinedDiag> undefDiags; |
394 | DenseMap<Symbol *, int> firstDiag; |
395 | |
396 | auto processFile = [&](InputFile *file, ArrayRef<Symbol *> symbols) { |
397 | uint32_t symIndex = (uint32_t)-1; |
398 | for (Symbol *sym : symbols) { |
399 | ++symIndex; |
400 | if (!sym) |
401 | continue; |
402 | if (undefs.count(Ptr: sym)) { |
403 | auto [it, inserted] = firstDiag.try_emplace(Key: sym, Args: undefDiags.size()); |
404 | if (inserted) |
405 | undefDiags.push_back(x: {.sym: sym, .files: {{.file: file, .symIndex: symIndex}}}); |
406 | else |
407 | undefDiags[it->second].files.push_back(x: {.file: file, .symIndex: symIndex}); |
408 | } |
409 | if (localImports) |
410 | if (Symbol *imp = localImports->lookup(Val: sym)) |
411 | Warn(ctx) << file |
412 | << ": locally defined symbol imported: "<< printSymbol(sym: imp) |
413 | << " (defined in "<< imp->getFile() << ") [LNK4217]"; |
414 | } |
415 | }; |
416 | |
417 | for (ObjFile *file : ctx.objFileInstances) |
418 | processFile(file, file->getSymbols()); |
419 | |
420 | if (needBitcodeFiles) |
421 | for (BitcodeFile *file : bitcodeFileInstances) |
422 | processFile(file, file->getSymbols()); |
423 | |
424 | for (const UndefinedDiag &undefDiag : undefDiags) |
425 | reportUndefinedSymbol(undefDiag); |
426 | } |
427 | |
428 | void SymbolTable::reportUnresolvable() { |
429 | SmallPtrSet<Symbol *, 8> undefs; |
430 | for (auto &i : symMap) { |
431 | Symbol *sym = i.second; |
432 | auto *undef = dyn_cast<Undefined>(Val: sym); |
433 | if (!undef || sym->deferUndefined) |
434 | continue; |
435 | if (undef->getWeakAlias()) |
436 | continue; |
437 | StringRef name = undef->getName(); |
438 | if (name.starts_with(Prefix: "__imp_")) { |
439 | Symbol *imp = find(name: name.substr(Start: strlen(s: "__imp_"))); |
440 | if (Defined *def = dyn_cast_or_null<Defined>(Val: imp)) { |
441 | def->isUsedInRegularObj = true; |
442 | continue; |
443 | } |
444 | } |
445 | if (name.contains(Other: "_PchSym_")) |
446 | continue; |
447 | if (ctx.config.autoImport && impSymbol(name)) |
448 | continue; |
449 | undefs.insert(Ptr: sym); |
450 | } |
451 | |
452 | reportProblemSymbols(undefs, /*localImports=*/nullptr, needBitcodeFiles: true); |
453 | } |
454 | |
455 | void SymbolTable::resolveRemainingUndefines() { |
456 | llvm::TimeTraceScope timeScope("Resolve remaining undefined symbols"); |
457 | SmallPtrSet<Symbol *, 8> undefs; |
458 | DenseMap<Symbol *, Symbol *> localImports; |
459 | |
460 | for (auto &i : symMap) { |
461 | Symbol *sym = i.second; |
462 | auto *undef = dyn_cast<Undefined>(Val: sym); |
463 | if (!undef) |
464 | continue; |
465 | if (!sym->isUsedInRegularObj) |
466 | continue; |
467 | |
468 | StringRef name = undef->getName(); |
469 | |
470 | // A weak alias may have been resolved, so check for that. |
471 | if (undef->resolveWeakAlias()) |
472 | continue; |
473 | |
474 | // If we can resolve a symbol by removing __imp_ prefix, do that. |
475 | // This odd rule is for compatibility with MSVC linker. |
476 | if (name.starts_with(Prefix: "__imp_")) { |
477 | auto findLocalSym = [&](StringRef n) { |
478 | Symbol *sym = find(name: n); |
479 | if (auto undef = dyn_cast_or_null<Undefined>(Val: sym)) { |
480 | // The unprefixed symbol might come later in symMap, so handle it now |
481 | // if needed. |
482 | if (!undef->resolveWeakAlias()) |
483 | sym = nullptr; |
484 | } |
485 | return sym; |
486 | }; |
487 | |
488 | StringRef impName = name.substr(Start: strlen(s: "__imp_")); |
489 | Symbol *imp = findLocalSym(impName); |
490 | if (!imp && isEC()) { |
491 | // Try to use the mangled symbol on ARM64EC. |
492 | std::optional<std::string> mangledName = |
493 | getArm64ECMangledFunctionName(Name: impName); |
494 | if (mangledName) |
495 | imp = findLocalSym(*mangledName); |
496 | if (!imp && impName.consume_front(Prefix: "aux_")) { |
497 | // If it's a __imp_aux_ symbol, try skipping the aux_ prefix. |
498 | imp = findLocalSym(impName); |
499 | if (!imp && (mangledName = getArm64ECMangledFunctionName(Name: impName))) |
500 | imp = findLocalSym(*mangledName); |
501 | } |
502 | } |
503 | if (imp && isa<Defined>(Val: imp)) { |
504 | auto *d = cast<Defined>(Val: imp); |
505 | replaceSymbol<DefinedLocalImport>(s: sym, arg&: ctx, arg&: name, arg&: d); |
506 | localImportChunks.push_back(x: cast<DefinedLocalImport>(Val: sym)->getChunk()); |
507 | localImports[sym] = d; |
508 | continue; |
509 | } |
510 | } |
511 | |
512 | // We don't want to report missing Microsoft precompiled headers symbols. |
513 | // A proper message will be emitted instead in PDBLinker::aquirePrecompObj |
514 | if (name.contains(Other: "_PchSym_")) |
515 | continue; |
516 | |
517 | if (ctx.config.autoImport && handleMinGWAutomaticImport(sym, name)) |
518 | continue; |
519 | |
520 | // Remaining undefined symbols are not fatal if /force is specified. |
521 | // They are replaced with dummy defined symbols. |
522 | if (ctx.config.forceUnresolved) |
523 | replaceSymbol<DefinedAbsolute>(s: sym, arg&: ctx, arg&: name, arg: 0); |
524 | undefs.insert(Ptr: sym); |
525 | } |
526 | |
527 | reportProblemSymbols( |
528 | undefs, localImports: ctx.config.warnLocallyDefinedImported ? &localImports : nullptr, |
529 | needBitcodeFiles: false); |
530 | } |
531 | |
532 | std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) { |
533 | bool inserted = false; |
534 | Symbol *&sym = symMap[CachedHashStringRef(name)]; |
535 | if (!sym) { |
536 | sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); |
537 | sym->isUsedInRegularObj = false; |
538 | sym->pendingArchiveLoad = false; |
539 | sym->canInline = true; |
540 | inserted = true; |
541 | |
542 | if (isEC() && name.starts_with(Prefix: "EXP+")) |
543 | expSymbols.push_back(x: sym); |
544 | } |
545 | return {sym, inserted}; |
546 | } |
547 | |
548 | std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) { |
549 | std::pair<Symbol *, bool> result = insert(name); |
550 | if (!file || !isa<BitcodeFile>(Val: file)) |
551 | result.first->isUsedInRegularObj = true; |
552 | return result; |
553 | } |
554 | |
555 | void SymbolTable::initializeLoadConfig() { |
556 | auto sym = |
557 | dyn_cast_or_null<DefinedRegular>(Val: findUnderscore(name: "_load_config_used")); |
558 | if (!sym) { |
559 | if (isEC()) { |
560 | Warn(ctx) << "EC version of '_load_config_used' is missing"; |
561 | return; |
562 | } |
563 | if (ctx.config.machine == ARM64X) { |
564 | Warn(ctx) << "native version of '_load_config_used' is missing for " |
565 | "ARM64X target"; |
566 | return; |
567 | } |
568 | if (ctx.config.guardCF != GuardCFLevel::Off) |
569 | Warn(ctx) |
570 | << "Control Flow Guard is enabled but '_load_config_used' is missing"; |
571 | if (ctx.config.dependentLoadFlags) |
572 | Warn(ctx) << "_load_config_used not found, /dependentloadflag will have " |
573 | "no effect"; |
574 | return; |
575 | } |
576 | |
577 | SectionChunk *sc = sym->getChunk(); |
578 | if (!sc->hasData) { |
579 | Err(ctx) << "_load_config_used points to uninitialized data"; |
580 | return; |
581 | } |
582 | uint64_t offsetInChunk = sym->getValue(); |
583 | if (offsetInChunk + 4 > sc->getSize()) { |
584 | Err(ctx) << "_load_config_used section chunk is too small"; |
585 | return; |
586 | } |
587 | |
588 | ArrayRef<uint8_t> secContents = sc->getContents(); |
589 | loadConfigSize = |
590 | *reinterpret_cast<const ulittle32_t *>(&secContents[offsetInChunk]); |
591 | if (offsetInChunk + loadConfigSize > sc->getSize()) { |
592 | Err(ctx) << "_load_config_used specifies a size larger than its containing " |
593 | "section chunk"; |
594 | return; |
595 | } |
596 | |
597 | uint32_t expectedAlign = ctx.config.is64() ? 8 : 4; |
598 | if (sc->getAlignment() < expectedAlign) |
599 | Warn(ctx) << "'_load_config_used' is misaligned (expected alignment to be " |
600 | << expectedAlign << " bytes, got "<< sc->getAlignment() |
601 | << " instead)"; |
602 | else if (!isAligned(Lhs: Align(expectedAlign), SizeInBytes: offsetInChunk)) |
603 | Warn(ctx) << "'_load_config_used' is misaligned (section offset is 0x" |
604 | << Twine::utohexstr(Val: sym->getValue()) << " not aligned to " |
605 | << expectedAlign << " bytes)"; |
606 | |
607 | loadConfigSym = sym; |
608 | } |
609 | |
610 | void SymbolTable::addEntryThunk(Symbol *from, Symbol *to) { |
611 | entryThunks.push_back(x: {from, to}); |
612 | } |
613 | |
614 | void SymbolTable::addExitThunk(Symbol *from, Symbol *to) { |
615 | exitThunks[from] = to; |
616 | } |
617 | |
618 | void SymbolTable::initializeECThunks() { |
619 | if (!isArm64EC(Machine: ctx.config.machine)) |
620 | return; |
621 | |
622 | for (auto it : entryThunks) { |
623 | auto *to = dyn_cast<Defined>(Val: it.second); |
624 | if (!to) |
625 | continue; |
626 | auto *from = dyn_cast<DefinedRegular>(Val: it.first); |
627 | // We need to be able to add padding to the function and fill it with an |
628 | // offset to its entry thunks. To ensure that padding the function is |
629 | // feasible, functions are required to be COMDAT symbols with no offset. |
630 | if (!from || !from->getChunk()->isCOMDAT() || |
631 | cast<DefinedRegular>(Val: from)->getValue()) { |
632 | Err(ctx) << "non COMDAT symbol '"<< from->getName() << "' in hybrid map"; |
633 | continue; |
634 | } |
635 | from->getChunk()->setEntryThunk(to); |
636 | } |
637 | |
638 | for (ImportFile *file : ctx.importFileInstances) { |
639 | if (!file->impchkThunk) |
640 | continue; |
641 | |
642 | Symbol *sym = exitThunks.lookup(Val: file->thunkSym); |
643 | if (!sym) |
644 | sym = exitThunks.lookup(Val: file->impECSym); |
645 | file->impchkThunk->exitThunk = dyn_cast_or_null<Defined>(Val: sym); |
646 | } |
647 | |
648 | // On ARM64EC, the __imp_ symbol references the auxiliary IAT, while the |
649 | // __imp_aux_ symbol references the regular IAT. However, x86_64 code expects |
650 | // both to reference the regular IAT, so adjust the symbol if necessary. |
651 | parallelForEach(R&: ctx.objFileInstances, Fn: [&](ObjFile *file) { |
652 | if (file->getMachineType() != AMD64) |
653 | return; |
654 | for (auto &sym : file->getMutableSymbols()) { |
655 | auto impSym = dyn_cast_or_null<DefinedImportData>(Val: sym); |
656 | if (impSym && impSym->file->impchkThunk && sym == impSym->file->impECSym) |
657 | sym = impSym->file->impSym; |
658 | } |
659 | }); |
660 | } |
661 | |
662 | Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, |
663 | bool overrideLazy) { |
664 | auto [s, wasInserted] = insert(name, file: f); |
665 | if (wasInserted || (s->isLazy() && overrideLazy)) { |
666 | replaceSymbol<Undefined>(s, arg&: name); |
667 | return s; |
668 | } |
669 | if (s->isLazy()) |
670 | forceLazy(s); |
671 | return s; |
672 | } |
673 | |
674 | Symbol *SymbolTable::addGCRoot(StringRef name, bool aliasEC) { |
675 | Symbol *b = addUndefined(name); |
676 | if (!b->isGCRoot) { |
677 | b->isGCRoot = true; |
678 | ctx.config.gcroot.push_back(x: b); |
679 | } |
680 | |
681 | // On ARM64EC, a symbol may be defined in either its mangled or demangled form |
682 | // (or both). Define an anti-dependency symbol that binds both forms, similar |
683 | // to how compiler-generated code references external functions. |
684 | if (aliasEC && isEC()) { |
685 | if (std::optional<std::string> mangledName = |
686 | getArm64ECMangledFunctionName(Name: name)) { |
687 | auto u = dyn_cast<Undefined>(Val: b); |
688 | if (u && !u->weakAlias) { |
689 | Symbol *t = addUndefined(name: saver().save(S: *mangledName)); |
690 | u->setWeakAlias(sym: t, antiDep: true); |
691 | } |
692 | } else if (std::optional<std::string> demangledName = |
693 | getArm64ECDemangledFunctionName(Name: name)) { |
694 | Symbol *us = addUndefined(name: saver().save(S: *demangledName)); |
695 | auto u = dyn_cast<Undefined>(Val: us); |
696 | if (u && !u->weakAlias) |
697 | u->setWeakAlias(sym: b, antiDep: true); |
698 | } |
699 | } |
700 | return b; |
701 | } |
702 | |
703 | // On ARM64EC, a function symbol may appear in both mangled and demangled forms: |
704 | // - ARM64EC archives contain only the mangled name, while the demangled symbol |
705 | // is defined by the object file as an alias. |
706 | // - x86_64 archives contain only the demangled name (the mangled name is |
707 | // usually defined by an object referencing the symbol as an alias to a guess |
708 | // exit thunk). |
709 | // - ARM64EC import files contain both the mangled and demangled names for |
710 | // thunks. |
711 | // If more than one archive defines the same function, this could lead |
712 | // to different libraries being used for the same function depending on how they |
713 | // are referenced. Avoid this by checking if the paired symbol is already |
714 | // defined before adding a symbol to the table. |
715 | template <typename T> |
716 | bool checkLazyECPair(SymbolTable *symtab, StringRef name, InputFile *f) { |
717 | if (name.starts_with(Prefix: "__imp_")) |
718 | return true; |
719 | std::string pairName; |
720 | if (std::optional<std::string> mangledName = |
721 | getArm64ECMangledFunctionName(Name: name)) |
722 | pairName = std::move(*mangledName); |
723 | else if (std::optional<std::string> demangledName = |
724 | getArm64ECDemangledFunctionName(Name: name)) |
725 | pairName = std::move(*demangledName); |
726 | else |
727 | return true; |
728 | |
729 | Symbol *sym = symtab->find(name: pairName); |
730 | if (!sym) |
731 | return true; |
732 | if (sym->pendingArchiveLoad) |
733 | return false; |
734 | if (auto u = dyn_cast<Undefined>(Val: sym)) |
735 | return !u->weakAlias || u->isAntiDep; |
736 | // If the symbol is lazy, allow it only if it originates from the same |
737 | // archive. |
738 | auto lazy = dyn_cast<T>(sym); |
739 | return lazy && lazy->file == f; |
740 | } |
741 | |
742 | void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { |
743 | StringRef name = sym.getName(); |
744 | if (isEC() && !checkLazyECPair<LazyArchive>(symtab: this, name, f)) |
745 | return; |
746 | auto [s, wasInserted] = insert(name); |
747 | if (wasInserted) { |
748 | replaceSymbol<LazyArchive>(s, arg&: f, arg: sym); |
749 | return; |
750 | } |
751 | auto *u = dyn_cast<Undefined>(Val: s); |
752 | if (!u || (u->weakAlias && !u->isECAlias(machine)) || s->pendingArchiveLoad) |
753 | return; |
754 | s->pendingArchiveLoad = true; |
755 | f->addMember(sym); |
756 | } |
757 | |
758 | void SymbolTable::addLazyObject(InputFile *f, StringRef n) { |
759 | assert(f->lazy); |
760 | if (isEC() && !checkLazyECPair<LazyObject>(symtab: this, name: n, f)) |
761 | return; |
762 | auto [s, wasInserted] = insert(name: n, file: f); |
763 | if (wasInserted) { |
764 | replaceSymbol<LazyObject>(s, arg&: f, arg&: n); |
765 | return; |
766 | } |
767 | auto *u = dyn_cast<Undefined>(Val: s); |
768 | if (!u || (u->weakAlias && !u->isECAlias(machine)) || s->pendingArchiveLoad) |
769 | return; |
770 | s->pendingArchiveLoad = true; |
771 | f->lazy = false; |
772 | ctx.driver.addFile(file: f); |
773 | } |
774 | |
775 | void SymbolTable::addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym, |
776 | StringRef n) { |
777 | auto [s, wasInserted] = insert(name: n); |
778 | if (wasInserted) { |
779 | replaceSymbol<LazyDLLSymbol>(s, arg&: f, arg&: sym, arg&: n); |
780 | return; |
781 | } |
782 | auto *u = dyn_cast<Undefined>(Val: s); |
783 | if (!u || (u->weakAlias && !u->isECAlias(machine)) || s->pendingArchiveLoad) |
784 | return; |
785 | s->pendingArchiveLoad = true; |
786 | f->makeImport(s: sym); |
787 | } |
788 | |
789 | static std::string getSourceLocationBitcode(BitcodeFile *file) { |
790 | std::string res("\n>>> defined at "); |
791 | StringRef source = file->obj->getSourceFileName(); |
792 | if (!source.empty()) |
793 | res += source.str() + "\n>>> "; |
794 | res += toString(file); |
795 | return res; |
796 | } |
797 | |
798 | static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc, |
799 | uint32_t offset, StringRef name) { |
800 | std::optional<std::pair<StringRef, uint32_t>> fileLine; |
801 | if (sc) |
802 | fileLine = getFileLine(c: sc, addr: offset); |
803 | if (!fileLine) |
804 | fileLine = file->getVariableLocation(var: name); |
805 | |
806 | std::string res; |
807 | llvm::raw_string_ostream os(res); |
808 | os << "\n>>> defined at "; |
809 | if (fileLine) |
810 | os << fileLine->first << ":"<< fileLine->second << "\n>>> "; |
811 | os << toString(file); |
812 | return res; |
813 | } |
814 | |
815 | static std::string getSourceLocation(InputFile *file, SectionChunk *sc, |
816 | uint32_t offset, StringRef name) { |
817 | if (!file) |
818 | return ""; |
819 | if (auto *o = dyn_cast<ObjFile>(Val: file)) |
820 | return getSourceLocationObj(file: o, sc, offset, name); |
821 | if (auto *b = dyn_cast<BitcodeFile>(Val: file)) |
822 | return getSourceLocationBitcode(file: b); |
823 | return "\n>>> defined at "+ toString(file); |
824 | } |
825 | |
826 | // Construct and print an error message in the form of: |
827 | // |
828 | // lld-link: error: duplicate symbol: foo |
829 | // >>> defined at bar.c:30 |
830 | // >>> bar.o |
831 | // >>> defined at baz.c:563 |
832 | // >>> baz.o |
833 | void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile, |
834 | SectionChunk *newSc, |
835 | uint32_t newSectionOffset) { |
836 | COFFSyncStream diag(ctx, ctx.config.forceMultiple ? DiagLevel::Warn |
837 | : DiagLevel::Err); |
838 | diag << "duplicate symbol: "<< printSymbol(sym: existing); |
839 | |
840 | DefinedRegular *d = dyn_cast<DefinedRegular>(Val: existing); |
841 | if (d && isa<ObjFile>(Val: d->getFile())) { |
842 | diag << getSourceLocation(file: d->getFile(), sc: d->getChunk(), offset: d->getValue(), |
843 | name: existing->getName()); |
844 | } else { |
845 | diag << getSourceLocation(file: existing->getFile(), sc: nullptr, offset: 0, name: ""); |
846 | } |
847 | diag << getSourceLocation(file: newFile, sc: newSc, offset: newSectionOffset, |
848 | name: existing->getName()); |
849 | } |
850 | |
851 | Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { |
852 | auto [s, wasInserted] = insert(name: n, file: nullptr); |
853 | s->isUsedInRegularObj = true; |
854 | if (wasInserted || isa<Undefined>(Val: s) || s->isLazy()) |
855 | replaceSymbol<DefinedAbsolute>(s, arg&: ctx, arg&: n, arg&: sym); |
856 | else if (auto *da = dyn_cast<DefinedAbsolute>(Val: s)) { |
857 | if (da->getVA() != sym.getValue()) |
858 | reportDuplicate(existing: s, newFile: nullptr); |
859 | } else if (!isa<DefinedCOFF>(Val: s)) |
860 | reportDuplicate(existing: s, newFile: nullptr); |
861 | return s; |
862 | } |
863 | |
864 | Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { |
865 | auto [s, wasInserted] = insert(name: n, file: nullptr); |
866 | s->isUsedInRegularObj = true; |
867 | if (wasInserted || isa<Undefined>(Val: s) || s->isLazy()) |
868 | replaceSymbol<DefinedAbsolute>(s, arg&: ctx, arg&: n, arg&: va); |
869 | else if (auto *da = dyn_cast<DefinedAbsolute>(Val: s)) { |
870 | if (da->getVA() != va) |
871 | reportDuplicate(existing: s, newFile: nullptr); |
872 | } else if (!isa<DefinedCOFF>(Val: s)) |
873 | reportDuplicate(existing: s, newFile: nullptr); |
874 | return s; |
875 | } |
876 | |
877 | Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { |
878 | auto [s, wasInserted] = insert(name: n, file: nullptr); |
879 | s->isUsedInRegularObj = true; |
880 | if (wasInserted || isa<Undefined>(Val: s) || s->isLazy()) |
881 | replaceSymbol<DefinedSynthetic>(s, arg&: n, arg&: c); |
882 | else if (!isa<DefinedCOFF>(Val: s)) |
883 | reportDuplicate(existing: s, newFile: nullptr); |
884 | return s; |
885 | } |
886 | |
887 | Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, |
888 | const coff_symbol_generic *sym, SectionChunk *c, |
889 | uint32_t sectionOffset, bool isWeak) { |
890 | auto [s, wasInserted] = insert(name: n, file: f); |
891 | if (wasInserted || !isa<DefinedRegular>(Val: s) || s->isWeak) |
892 | replaceSymbol<DefinedRegular>(s, arg&: f, arg&: n, /*IsCOMDAT*/ arg: false, |
893 | /*IsExternal*/ arg: true, arg&: sym, arg&: c, arg&: isWeak); |
894 | else if (!isWeak) |
895 | reportDuplicate(existing: s, newFile: f, newSc: c, newSectionOffset: sectionOffset); |
896 | return s; |
897 | } |
898 | |
899 | std::pair<DefinedRegular *, bool> |
900 | SymbolTable::addComdat(InputFile *f, StringRef n, |
901 | const coff_symbol_generic *sym) { |
902 | auto [s, wasInserted] = insert(name: n, file: f); |
903 | if (wasInserted || !isa<DefinedRegular>(Val: s)) { |
904 | replaceSymbol<DefinedRegular>(s, arg&: f, arg&: n, /*IsCOMDAT*/ arg: true, |
905 | /*IsExternal*/ arg: true, arg&: sym, arg: nullptr); |
906 | return {cast<DefinedRegular>(Val: s), true}; |
907 | } |
908 | auto *existingSymbol = cast<DefinedRegular>(Val: s); |
909 | if (!existingSymbol->isCOMDAT) |
910 | reportDuplicate(existing: s, newFile: f); |
911 | return {existingSymbol, false}; |
912 | } |
913 | |
914 | Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size, |
915 | const coff_symbol_generic *sym, CommonChunk *c) { |
916 | auto [s, wasInserted] = insert(name: n, file: f); |
917 | if (wasInserted || !isa<DefinedCOFF>(Val: s)) |
918 | replaceSymbol<DefinedCommon>(s, arg&: f, arg&: n, arg&: size, arg&: sym, arg&: c); |
919 | else if (auto *dc = dyn_cast<DefinedCommon>(Val: s)) |
920 | if (size > dc->getSize()) |
921 | replaceSymbol<DefinedCommon>(s, arg&: f, arg&: n, arg&: size, arg&: sym, arg&: c); |
922 | return s; |
923 | } |
924 | |
925 | DefinedImportData *SymbolTable::addImportData(StringRef n, ImportFile *f, |
926 | Chunk *&location) { |
927 | auto [s, wasInserted] = insert(name: n, file: nullptr); |
928 | s->isUsedInRegularObj = true; |
929 | if (wasInserted || isa<Undefined>(Val: s) || s->isLazy()) { |
930 | replaceSymbol<DefinedImportData>(s, arg&: n, arg&: f, arg&: location); |
931 | return cast<DefinedImportData>(Val: s); |
932 | } |
933 | |
934 | reportDuplicate(existing: s, newFile: f); |
935 | return nullptr; |
936 | } |
937 | |
938 | Defined *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, |
939 | ImportThunkChunk *chunk) { |
940 | auto [s, wasInserted] = insert(name, file: nullptr); |
941 | s->isUsedInRegularObj = true; |
942 | if (wasInserted || isa<Undefined>(Val: s) || s->isLazy()) { |
943 | replaceSymbol<DefinedImportThunk>(s, arg&: ctx, arg&: name, arg&: id, arg&: chunk); |
944 | return cast<Defined>(Val: s); |
945 | } |
946 | |
947 | reportDuplicate(existing: s, newFile: id->file); |
948 | return nullptr; |
949 | } |
950 | |
951 | void SymbolTable::addLibcall(StringRef name) { |
952 | Symbol *sym = findUnderscore(name); |
953 | if (!sym) |
954 | return; |
955 | |
956 | if (auto *l = dyn_cast<LazyArchive>(Val: sym)) { |
957 | MemoryBufferRef mb = l->getMemberBuffer(); |
958 | if (isBitcode(mb)) |
959 | addUndefined(name: sym->getName()); |
960 | } else if (LazyObject *o = dyn_cast<LazyObject>(Val: sym)) { |
961 | if (isBitcode(mb: o->file->mb)) |
962 | addUndefined(name: sym->getName()); |
963 | } |
964 | } |
965 | |
966 | Symbol *SymbolTable::find(StringRef name) const { |
967 | return symMap.lookup(Val: CachedHashStringRef(name)); |
968 | } |
969 | |
970 | Symbol *SymbolTable::findUnderscore(StringRef name) const { |
971 | if (machine == I386) |
972 | return find(name: ("_"+ name).str()); |
973 | return find(name); |
974 | } |
975 | |
976 | // Return all symbols that start with Prefix, possibly ignoring the first |
977 | // character of Prefix or the first character symbol. |
978 | std::vector<Symbol *> SymbolTable::getSymsWithPrefix(StringRef prefix) { |
979 | std::vector<Symbol *> syms; |
980 | for (auto pair : symMap) { |
981 | StringRef name = pair.first.val(); |
982 | if (name.starts_with(Prefix: prefix) || name.starts_with(Prefix: prefix.drop_front()) || |
983 | name.drop_front().starts_with(Prefix: prefix) || |
984 | name.drop_front().starts_with(Prefix: prefix.drop_front())) { |
985 | syms.push_back(x: pair.second); |
986 | } |
987 | } |
988 | return syms; |
989 | } |
990 | |
991 | Symbol *SymbolTable::findMangle(StringRef name) { |
992 | if (Symbol *sym = find(name)) { |
993 | if (auto *u = dyn_cast<Undefined>(Val: sym)) { |
994 | // We're specifically looking for weak aliases that ultimately resolve to |
995 | // defined symbols, hence the call to getWeakAlias() instead of just using |
996 | // the weakAlias member variable. This matches link.exe's behavior. |
997 | if (Symbol *weakAlias = u->getWeakAlias()) |
998 | return weakAlias; |
999 | } else { |
1000 | return sym; |
1001 | } |
1002 | } |
1003 | |
1004 | // Efficient fuzzy string lookup is impossible with a hash table, so iterate |
1005 | // the symbol table once and collect all possibly matching symbols into this |
1006 | // vector. Then compare each possibly matching symbol with each possible |
1007 | // mangling. |
1008 | std::vector<Symbol *> syms = getSymsWithPrefix(prefix: name); |
1009 | auto findByPrefix = [&syms](const Twine &t) -> Symbol * { |
1010 | std::string prefix = t.str(); |
1011 | for (auto *s : syms) |
1012 | if (s->getName().starts_with(Prefix: prefix)) |
1013 | return s; |
1014 | return nullptr; |
1015 | }; |
1016 | |
1017 | // For non-x86, just look for C++ functions. |
1018 | if (machine != I386) |
1019 | return findByPrefix("?"+ name + "@@Y"); |
1020 | |
1021 | if (!name.starts_with(Prefix: "_")) |
1022 | return nullptr; |
1023 | // Search for x86 stdcall function. |
1024 | if (Symbol *s = findByPrefix(name + "@")) |
1025 | return s; |
1026 | // Search for x86 fastcall function. |
1027 | if (Symbol *s = findByPrefix("@"+ name.substr(Start: 1) + "@")) |
1028 | return s; |
1029 | // Search for x86 vectorcall function. |
1030 | if (Symbol *s = findByPrefix(name.substr(Start: 1) + "@@")) |
1031 | return s; |
1032 | // Search for x86 C++ non-member function. |
1033 | return findByPrefix("?"+ name.substr(Start: 1) + "@@Y"); |
1034 | } |
1035 | |
1036 | bool SymbolTable::findUnderscoreMangle(StringRef sym) { |
1037 | Symbol *s = findMangle(name: mangle(sym)); |
1038 | return s && !isa<Undefined>(Val: s); |
1039 | } |
1040 | |
1041 | // Symbol names are mangled by prepending "_" on x86. |
1042 | StringRef SymbolTable::mangle(StringRef sym) { |
1043 | assert(machine != IMAGE_FILE_MACHINE_UNKNOWN); |
1044 | if (machine == I386) |
1045 | return saver().save(S: "_"+ sym); |
1046 | return sym; |
1047 | } |
1048 | |
1049 | StringRef SymbolTable::mangleMaybe(Symbol *s) { |
1050 | // If the plain symbol name has already been resolved, do nothing. |
1051 | Undefined *unmangled = dyn_cast<Undefined>(Val: s); |
1052 | if (!unmangled) |
1053 | return ""; |
1054 | |
1055 | // Otherwise, see if a similar, mangled symbol exists in the symbol table. |
1056 | Symbol *mangled = findMangle(name: unmangled->getName()); |
1057 | if (!mangled) |
1058 | return ""; |
1059 | |
1060 | // If we find a similar mangled symbol, make this an alias to it and return |
1061 | // its name. |
1062 | Log(ctx) << unmangled->getName() << " aliased to "<< mangled->getName(); |
1063 | unmangled->setWeakAlias(sym: addUndefined(name: mangled->getName())); |
1064 | return mangled->getName(); |
1065 | } |
1066 | |
1067 | // Windows specific -- find default entry point name. |
1068 | // |
1069 | // There are four different entry point functions for Windows executables, |
1070 | // each of which corresponds to a user-defined "main" function. This function |
1071 | // infers an entry point from a user-defined "main" function. |
1072 | StringRef SymbolTable::findDefaultEntry() { |
1073 | assert(ctx.config.subsystem != IMAGE_SUBSYSTEM_UNKNOWN && |
1074 | "must handle /subsystem before calling this"); |
1075 | |
1076 | if (ctx.config.mingw) |
1077 | return mangle(sym: ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI |
1078 | ? "WinMainCRTStartup" |
1079 | : "mainCRTStartup"); |
1080 | |
1081 | if (ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { |
1082 | if (findUnderscoreMangle(sym: "wWinMain")) { |
1083 | if (!findUnderscoreMangle(sym: "WinMain")) |
1084 | return mangle(sym: "wWinMainCRTStartup"); |
1085 | Warn(ctx) << "found both wWinMain and WinMain; using latter"; |
1086 | } |
1087 | return mangle(sym: "WinMainCRTStartup"); |
1088 | } |
1089 | if (findUnderscoreMangle(sym: "wmain")) { |
1090 | if (!findUnderscoreMangle(sym: "main")) |
1091 | return mangle(sym: "wmainCRTStartup"); |
1092 | Warn(ctx) << "found both wmain and main; using latter"; |
1093 | } |
1094 | return mangle(sym: "mainCRTStartup"); |
1095 | } |
1096 | |
1097 | WindowsSubsystem SymbolTable::inferSubsystem() { |
1098 | if (ctx.config.dll) |
1099 | return IMAGE_SUBSYSTEM_WINDOWS_GUI; |
1100 | if (ctx.config.mingw) |
1101 | return IMAGE_SUBSYSTEM_WINDOWS_CUI; |
1102 | // Note that link.exe infers the subsystem from the presence of these |
1103 | // functions even if /entry: or /nodefaultlib are passed which causes them |
1104 | // to not be called. |
1105 | bool haveMain = findUnderscoreMangle(sym: "main"); |
1106 | bool haveWMain = findUnderscoreMangle(sym: "wmain"); |
1107 | bool haveWinMain = findUnderscoreMangle(sym: "WinMain"); |
1108 | bool haveWWinMain = findUnderscoreMangle(sym: "wWinMain"); |
1109 | if (haveMain || haveWMain) { |
1110 | if (haveWinMain || haveWWinMain) { |
1111 | Warn(ctx) << "found "<< (haveMain ? "main": "wmain") << " and " |
1112 | << (haveWinMain ? "WinMain": "wWinMain") |
1113 | << "; defaulting to /subsystem:console"; |
1114 | } |
1115 | return IMAGE_SUBSYSTEM_WINDOWS_CUI; |
1116 | } |
1117 | if (haveWinMain || haveWWinMain) |
1118 | return IMAGE_SUBSYSTEM_WINDOWS_GUI; |
1119 | return IMAGE_SUBSYSTEM_UNKNOWN; |
1120 | } |
1121 | |
1122 | void SymbolTable::addUndefinedGlob(StringRef arg) { |
1123 | Expected<GlobPattern> pat = GlobPattern::create(Pat: arg); |
1124 | if (!pat) { |
1125 | Err(ctx) << "/includeglob: "<< toString(E: pat.takeError()); |
1126 | return; |
1127 | } |
1128 | |
1129 | SmallVector<Symbol *, 0> syms; |
1130 | forEachSymbol(callback: [&syms, &pat](Symbol *sym) { |
1131 | if (pat->match(S: sym->getName())) { |
1132 | syms.push_back(Elt: sym); |
1133 | } |
1134 | }); |
1135 | |
1136 | for (Symbol *sym : syms) |
1137 | addGCRoot(name: sym->getName()); |
1138 | } |
1139 | |
1140 | // Convert stdcall/fastcall style symbols into unsuffixed symbols, |
1141 | // with or without a leading underscore. (MinGW specific.) |
1142 | static StringRef killAt(StringRef sym, bool prefix) { |
1143 | if (sym.empty()) |
1144 | return sym; |
1145 | // Strip any trailing stdcall suffix |
1146 | sym = sym.substr(Start: 0, N: sym.find(C: '@', From: 1)); |
1147 | if (!sym.starts_with(Prefix: "@")) { |
1148 | if (prefix && !sym.starts_with(Prefix: "_")) |
1149 | return saver().save(S: "_"+ sym); |
1150 | return sym; |
1151 | } |
1152 | // For fastcall, remove the leading @ and replace it with an |
1153 | // underscore, if prefixes are used. |
1154 | sym = sym.substr(Start: 1); |
1155 | if (prefix) |
1156 | sym = saver().save(S: "_"+ sym); |
1157 | return sym; |
1158 | } |
1159 | |
1160 | static StringRef exportSourceName(ExportSource s) { |
1161 | switch (s) { |
1162 | case ExportSource::Directives: |
1163 | return "source file (directives)"; |
1164 | case ExportSource::Export: |
1165 | return "/export"; |
1166 | case ExportSource::ModuleDefinition: |
1167 | return "/def"; |
1168 | default: |
1169 | llvm_unreachable("unknown ExportSource"); |
1170 | } |
1171 | } |
1172 | |
1173 | // Performs error checking on all /export arguments. |
1174 | // It also sets ordinals. |
1175 | void SymbolTable::fixupExports() { |
1176 | llvm::TimeTraceScope timeScope("Fixup exports"); |
1177 | // Symbol ordinals must be unique. |
1178 | std::set<uint16_t> ords; |
1179 | for (Export &e : exports) { |
1180 | if (e.ordinal == 0) |
1181 | continue; |
1182 | if (!ords.insert(x: e.ordinal).second) |
1183 | Fatal(ctx) << "duplicate export ordinal: "<< e.name; |
1184 | } |
1185 | |
1186 | for (Export &e : exports) { |
1187 | if (!e.exportAs.empty()) { |
1188 | e.exportName = e.exportAs; |
1189 | continue; |
1190 | } |
1191 | |
1192 | StringRef sym = |
1193 | !e.forwardTo.empty() || e.extName.empty() ? e.name : e.extName; |
1194 | if (machine == I386 && sym.starts_with(Prefix: "_")) { |
1195 | // In MSVC mode, a fully decorated stdcall function is exported |
1196 | // as-is with the leading underscore (with type IMPORT_NAME). |
1197 | // In MinGW mode, a decorated stdcall function gets the underscore |
1198 | // removed, just like normal cdecl functions. |
1199 | if (ctx.config.mingw || !sym.contains(C: '@')) { |
1200 | e.exportName = sym.substr(Start: 1); |
1201 | continue; |
1202 | } |
1203 | } |
1204 | if (isEC() && !e.data && !e.constant) { |
1205 | if (std::optional<std::string> demangledName = |
1206 | getArm64ECDemangledFunctionName(Name: sym)) { |
1207 | e.exportName = saver().save(S: *demangledName); |
1208 | continue; |
1209 | } |
1210 | } |
1211 | e.exportName = sym; |
1212 | } |
1213 | |
1214 | if (ctx.config.killAt && machine == I386) { |
1215 | for (Export &e : exports) { |
1216 | e.name = killAt(sym: e.name, prefix: true); |
1217 | e.exportName = killAt(sym: e.exportName, prefix: false); |
1218 | e.extName = killAt(sym: e.extName, prefix: true); |
1219 | e.symbolName = killAt(sym: e.symbolName, prefix: true); |
1220 | } |
1221 | } |
1222 | |
1223 | // Uniquefy by name. |
1224 | DenseMap<StringRef, std::pair<Export *, unsigned>> map(exports.size()); |
1225 | std::vector<Export> v; |
1226 | for (Export &e : exports) { |
1227 | auto pair = map.insert(KV: std::make_pair(x&: e.exportName, y: std::make_pair(x: &e, y: 0))); |
1228 | bool inserted = pair.second; |
1229 | if (inserted) { |
1230 | pair.first->second.second = v.size(); |
1231 | v.push_back(x: e); |
1232 | continue; |
1233 | } |
1234 | Export *existing = pair.first->second.first; |
1235 | if (e == *existing || e.name != existing->name) |
1236 | continue; |
1237 | // If the existing export comes from .OBJ directives, we are allowed to |
1238 | // overwrite it with /DEF: or /EXPORT without any warning, as MSVC link.exe |
1239 | // does. |
1240 | if (existing->source == ExportSource::Directives) { |
1241 | *existing = e; |
1242 | v[pair.first->second.second] = e; |
1243 | continue; |
1244 | } |
1245 | if (existing->source == e.source) { |
1246 | Warn(ctx) << "duplicate "<< exportSourceName(s: existing->source) |
1247 | << " option: "<< e.name; |
1248 | } else { |
1249 | Warn(ctx) << "duplicate export: "<< e.name << " first seen in " |
1250 | << exportSourceName(s: existing->source) << ", now in " |
1251 | << exportSourceName(s: e.source); |
1252 | } |
1253 | } |
1254 | exports = std::move(v); |
1255 | |
1256 | // Sort by name. |
1257 | llvm::sort(C&: exports, Comp: [](const Export &a, const Export &b) { |
1258 | return a.exportName < b.exportName; |
1259 | }); |
1260 | } |
1261 | |
1262 | void SymbolTable::assignExportOrdinals() { |
1263 | // Assign unique ordinals if default (= 0). |
1264 | uint32_t max = 0; |
1265 | for (Export &e : exports) |
1266 | max = std::max(a: max, b: (uint32_t)e.ordinal); |
1267 | for (Export &e : exports) |
1268 | if (e.ordinal == 0) |
1269 | e.ordinal = ++max; |
1270 | if (max > std::numeric_limits<uint16_t>::max()) |
1271 | Fatal(ctx) << "too many exported symbols (got "<< max << ", max " |
1272 | << Twine(std::numeric_limits<uint16_t>::max()) << ")"; |
1273 | } |
1274 | |
1275 | void SymbolTable::parseModuleDefs(StringRef path) { |
1276 | llvm::TimeTraceScope timeScope("Parse def file"); |
1277 | std::unique_ptr<MemoryBuffer> mb = |
1278 | CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, |
1279 | /*RequiresNullTerminator=*/false, |
1280 | /*IsVolatile=*/true), |
1281 | "could not open "+ path); |
1282 | COFFModuleDefinition m = check(e: parseCOFFModuleDefinition( |
1283 | MB: mb->getMemBufferRef(), Machine: machine, MingwDef: ctx.config.mingw)); |
1284 | |
1285 | // Include in /reproduce: output if applicable. |
1286 | ctx.driver.takeBuffer(mb: std::move(mb)); |
1287 | |
1288 | if (ctx.config.outputFile.empty()) |
1289 | ctx.config.outputFile = std::string(saver().save(S: m.OutputFile)); |
1290 | ctx.config.importName = std::string(saver().save(S: m.ImportName)); |
1291 | if (m.ImageBase) |
1292 | ctx.config.imageBase = m.ImageBase; |
1293 | if (m.StackReserve) |
1294 | ctx.config.stackReserve = m.StackReserve; |
1295 | if (m.StackCommit) |
1296 | ctx.config.stackCommit = m.StackCommit; |
1297 | if (m.HeapReserve) |
1298 | ctx.config.heapReserve = m.HeapReserve; |
1299 | if (m.HeapCommit) |
1300 | ctx.config.heapCommit = m.HeapCommit; |
1301 | if (m.MajorImageVersion) |
1302 | ctx.config.majorImageVersion = m.MajorImageVersion; |
1303 | if (m.MinorImageVersion) |
1304 | ctx.config.minorImageVersion = m.MinorImageVersion; |
1305 | if (m.MajorOSVersion) |
1306 | ctx.config.majorOSVersion = m.MajorOSVersion; |
1307 | if (m.MinorOSVersion) |
1308 | ctx.config.minorOSVersion = m.MinorOSVersion; |
1309 | |
1310 | for (COFFShortExport e1 : m.Exports) { |
1311 | Export e2; |
1312 | // Renamed exports are parsed and set as "ExtName = Name". If Name has |
1313 | // the form "OtherDll.Func", it shouldn't be a normal exported |
1314 | // function but a forward to another DLL instead. This is supported |
1315 | // by both MS and GNU linkers. |
1316 | if (!e1.ExtName.empty() && e1.ExtName != e1.Name && |
1317 | StringRef(e1.Name).contains(C: '.')) { |
1318 | e2.name = saver().save(S: e1.ExtName); |
1319 | e2.forwardTo = saver().save(S: e1.Name); |
1320 | } else { |
1321 | e2.name = saver().save(S: e1.Name); |
1322 | e2.extName = saver().save(S: e1.ExtName); |
1323 | } |
1324 | e2.exportAs = saver().save(S: e1.ExportAs); |
1325 | e2.importName = saver().save(S: e1.ImportName); |
1326 | e2.ordinal = e1.Ordinal; |
1327 | e2.noname = e1.Noname; |
1328 | e2.data = e1.Data; |
1329 | e2.isPrivate = e1.Private; |
1330 | e2.constant = e1.Constant; |
1331 | e2.source = ExportSource::ModuleDefinition; |
1332 | exports.push_back(x: e2); |
1333 | } |
1334 | } |
1335 | |
1336 | // Parse a string of the form of "<from>=<to>". |
1337 | void SymbolTable::parseAlternateName(StringRef s) { |
1338 | auto [from, to] = s.split(Separator: '='); |
1339 | if (from.empty() || to.empty()) |
1340 | Fatal(ctx) << "/alternatename: invalid argument: "<< s; |
1341 | auto it = alternateNames.find(x: from); |
1342 | if (it != alternateNames.end() && it->second != to) |
1343 | Fatal(ctx) << "/alternatename: conflicts: "<< s; |
1344 | alternateNames.insert(position: it, x: std::make_pair(x&: from, y&: to)); |
1345 | } |
1346 | |
1347 | // Parses /aligncomm option argument. |
1348 | void SymbolTable::parseAligncomm(StringRef s) { |
1349 | auto [name, align] = s.split(Separator: ','); |
1350 | if (name.empty() || align.empty()) { |
1351 | Err(ctx) << "/aligncomm: invalid argument: "<< s; |
1352 | return; |
1353 | } |
1354 | int v; |
1355 | if (align.getAsInteger(Radix: 0, Result&: v)) { |
1356 | Err(ctx) << "/aligncomm: invalid argument: "<< s; |
1357 | return; |
1358 | } |
1359 | alignComm[std::string(name)] = std::max(a: alignComm[std::string(name)], b: 1 << v); |
1360 | } |
1361 | |
1362 | Symbol *SymbolTable::addUndefined(StringRef name) { |
1363 | return addUndefined(name, f: nullptr, overrideLazy: false); |
1364 | } |
1365 | |
1366 | std::string SymbolTable::printSymbol(Symbol *sym) const { |
1367 | std::string name = maybeDemangleSymbol(ctx, symName: sym->getName()); |
1368 | if (ctx.hybridSymtab) |
1369 | return name + (isEC() ? " (EC symbol)": " (native symbol)"); |
1370 | return name; |
1371 | } |
1372 | |
1373 | void SymbolTable::compileBitcodeFiles() { |
1374 | if (bitcodeFileInstances.empty()) |
1375 | return; |
1376 | |
1377 | llvm::TimeTraceScope timeScope("Compile bitcode"); |
1378 | ScopedTimer t(ctx.ltoTimer); |
1379 | lto.reset(p: new BitcodeCompiler(ctx)); |
1380 | for (BitcodeFile *f : bitcodeFileInstances) |
1381 | lto->add(f&: *f); |
1382 | for (InputFile *newObj : lto->compile()) { |
1383 | ObjFile *obj = cast<ObjFile>(Val: newObj); |
1384 | obj->parse(); |
1385 | ctx.objFileInstances.push_back(x: obj); |
1386 | } |
1387 | } |
1388 | |
1389 | } // namespace lld::coff |
1390 |
Definitions
- ltrim1
- errorOrWarn
- forceLazy
- getSymbol
- getSymbolLocations
- getFileLineDwarf
- getFileLine
- getSymbolLocations
- getSymbolLocations
- getSymbolLocations
- UndefinedDiag
- File
- reportUndefinedSymbol
- loadMinGWSymbols
- impSymbol
- handleMinGWAutomaticImport
- reportProblemSymbols
- reportUnresolvable
- resolveRemainingUndefines
- insert
- insert
- initializeLoadConfig
- addEntryThunk
- addExitThunk
- initializeECThunks
- addUndefined
- addGCRoot
- checkLazyECPair
- addLazyArchive
- addLazyObject
- addLazyDLLSymbol
- getSourceLocationBitcode
- getSourceLocationObj
- getSourceLocation
- reportDuplicate
- addAbsolute
- addAbsolute
- addSynthetic
- addRegular
- addComdat
- addCommon
- addImportData
- addImportThunk
- addLibcall
- find
- findUnderscore
- getSymsWithPrefix
- findMangle
- findUnderscoreMangle
- mangle
- mangleMaybe
- findDefaultEntry
- inferSubsystem
- addUndefinedGlob
- killAt
- exportSourceName
- fixupExports
- assignExportOrdinals
- parseModuleDefs
- parseAlternateName
- parseAligncomm
- addUndefined
- printSymbol
Learn to use CMake with our Intro Training
Find out more