| 1 | //===- Symbols.h ------------------------------------------------*- C++ -*-===// |
| 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 | // This file defines various types of Symbols. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #ifndef LLD_ELF_SYMBOLS_H |
| 14 | #define LLD_ELF_SYMBOLS_H |
| 15 | |
| 16 | #include "Config.h" |
| 17 | #include "lld/Common/LLVM.h" |
| 18 | #include "lld/Common/Memory.h" |
| 19 | #include "llvm/ADT/DenseMap.h" |
| 20 | #include "llvm/Object/ELF.h" |
| 21 | #include "llvm/Support/Compiler.h" |
| 22 | #include <tuple> |
| 23 | |
| 24 | namespace lld::elf { |
| 25 | class CommonSymbol; |
| 26 | class Defined; |
| 27 | class OutputSection; |
| 28 | class SectionBase; |
| 29 | class InputSectionBase; |
| 30 | class SharedSymbol; |
| 31 | class Symbol; |
| 32 | class Undefined; |
| 33 | class LazySymbol; |
| 34 | class InputFile; |
| 35 | |
| 36 | // Returns a string representation for a symbol for diagnostics. |
| 37 | std::string toStr(Ctx &, const Symbol &); |
| 38 | const ELFSyncStream &operator<<(const ELFSyncStream &, const Symbol *); |
| 39 | |
| 40 | void printTraceSymbol(const Symbol &sym, StringRef name); |
| 41 | |
| 42 | enum { |
| 43 | NEEDS_GOT = 1 << 0, |
| 44 | NEEDS_PLT = 1 << 1, |
| 45 | HAS_DIRECT_RELOC = 1 << 2, |
| 46 | // True if this symbol needs a canonical PLT entry, or (during |
| 47 | // postScanRelocations) a copy relocation. |
| 48 | NEEDS_COPY = 1 << 3, |
| 49 | NEEDS_TLSDESC = 1 << 4, |
| 50 | NEEDS_TLSGD = 1 << 5, |
| 51 | NEEDS_TLSGD_TO_IE = 1 << 6, |
| 52 | NEEDS_GOT_DTPREL = 1 << 7, |
| 53 | NEEDS_TLSIE = 1 << 8, |
| 54 | NEEDS_GOT_AUTH = 1 << 9, |
| 55 | NEEDS_GOT_NONAUTH = 1 << 10, |
| 56 | NEEDS_TLSDESC_AUTH = 1 << 11, |
| 57 | NEEDS_TLSDESC_NONAUTH = 1 << 12, |
| 58 | }; |
| 59 | |
| 60 | // The base class for real symbol classes. |
| 61 | class Symbol { |
| 62 | public: |
| 63 | enum Kind { |
| 64 | PlaceholderKind, |
| 65 | DefinedKind, |
| 66 | CommonKind, |
| 67 | SharedKind, |
| 68 | UndefinedKind, |
| 69 | LazyKind, |
| 70 | }; |
| 71 | |
| 72 | Kind kind() const { return static_cast<Kind>(symbolKind); } |
| 73 | |
| 74 | // The file from which this symbol was created. |
| 75 | InputFile *file; |
| 76 | |
| 77 | // The default copy constructor is deleted due to atomic flags. Define one for |
| 78 | // places where no atomic is needed. |
| 79 | Symbol(const Symbol &o) { memcpy(dest: static_cast<void *>(this), src: &o, n: sizeof(o)); } |
| 80 | |
| 81 | protected: |
| 82 | const char *nameData; |
| 83 | // 32-bit size saves space. |
| 84 | uint32_t nameSize; |
| 85 | |
| 86 | public: |
| 87 | // The next three fields have the same meaning as the ELF symbol attributes. |
| 88 | // type and binding are placed in this order to optimize generating st_info, |
| 89 | // which is defined as (binding << 4) + (type & 0xf), on a little-endian |
| 90 | // system. |
| 91 | uint8_t type : 4; // symbol type |
| 92 | |
| 93 | // Symbol binding. This is not overwritten by replace() to track |
| 94 | // changes during resolution. In particular: |
| 95 | // - An undefined weak is still weak when it resolves to a shared library. |
| 96 | // - An undefined weak will not extract archive members, but we have to |
| 97 | // remember it is weak. |
| 98 | uint8_t binding : 4; |
| 99 | |
| 100 | uint8_t stOther; // st_other field value |
| 101 | |
| 102 | uint8_t symbolKind; |
| 103 | |
| 104 | // The partition whose dynamic symbol table contains this symbol's definition. |
| 105 | uint8_t partition; |
| 106 | |
| 107 | // True if this symbol is preemptible at load time. |
| 108 | // |
| 109 | // Primarily set in two locations, (a) parseVersionAndComputeIsPreemptible and |
| 110 | // (b) demoteSymbolsAndComputeIsPreemptible. |
| 111 | LLVM_PREFERRED_TYPE(bool) |
| 112 | uint8_t isPreemptible : 1; |
| 113 | |
| 114 | // True if the symbol was used for linking and thus need to be added to the |
| 115 | // output file's symbol table. This is true for all symbols except for |
| 116 | // unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that |
| 117 | // are unreferenced except by other bitcode objects. |
| 118 | LLVM_PREFERRED_TYPE(bool) |
| 119 | uint8_t isUsedInRegularObj : 1; |
| 120 | |
| 121 | // True if an undefined or shared symbol is used from a live section. |
| 122 | // |
| 123 | // NOTE: In Writer.cpp the field is used to mark local defined symbols |
| 124 | // which are referenced by relocations when -r or --emit-relocs is given. |
| 125 | LLVM_PREFERRED_TYPE(bool) |
| 126 | uint8_t used : 1; |
| 127 | |
| 128 | // Used by a Defined symbol with protected or default visibility, to record |
| 129 | // whether it is required to be exported into .dynsym. This is set when any of |
| 130 | // the following conditions hold: |
| 131 | // |
| 132 | // - If there is an interposable symbol from a DSO. Note: We also do this for |
| 133 | // STV_PROTECTED symbols which can't be interposed (to match BFD behavior). |
| 134 | // - If -shared or --export-dynamic is specified, any symbol in an object |
| 135 | // file/bitcode sets this property, unless suppressed by LTO |
| 136 | // canBeOmittedFromSymbolTable(). |
| 137 | LLVM_PREFERRED_TYPE(bool) |
| 138 | uint8_t isExported : 1; |
| 139 | |
| 140 | LLVM_PREFERRED_TYPE(bool) |
| 141 | uint8_t ltoCanOmit : 1; |
| 142 | |
| 143 | // True if this symbol is specified by --trace-symbol option. |
| 144 | LLVM_PREFERRED_TYPE(bool) |
| 145 | uint8_t traced : 1; |
| 146 | |
| 147 | // True if the name contains '@'. |
| 148 | LLVM_PREFERRED_TYPE(bool) |
| 149 | uint8_t hasVersionSuffix : 1; |
| 150 | |
| 151 | // Symbol visibility. This is the computed minimum visibility of all |
| 152 | // observed non-DSO symbols. |
| 153 | uint8_t visibility() const { return stOther & 3; } |
| 154 | void setVisibility(uint8_t visibility) { |
| 155 | stOther = (stOther & ~3) | visibility; |
| 156 | } |
| 157 | |
| 158 | uint8_t computeBinding(Ctx &) const; |
| 159 | bool isGlobal() const { return binding == llvm::ELF::STB_GLOBAL; } |
| 160 | bool isWeak() const { return binding == llvm::ELF::STB_WEAK; } |
| 161 | |
| 162 | bool isUndefined() const { return symbolKind == UndefinedKind; } |
| 163 | bool isCommon() const { return symbolKind == CommonKind; } |
| 164 | bool isDefined() const { return symbolKind == DefinedKind; } |
| 165 | bool isShared() const { return symbolKind == SharedKind; } |
| 166 | bool isPlaceholder() const { return symbolKind == PlaceholderKind; } |
| 167 | |
| 168 | bool isLocal() const { return binding == llvm::ELF::STB_LOCAL; } |
| 169 | |
| 170 | bool isLazy() const { return symbolKind == LazyKind; } |
| 171 | |
| 172 | // True if this is an undefined weak symbol. This only works once |
| 173 | // all input files have been added. |
| 174 | bool isUndefWeak() const { return isWeak() && isUndefined(); } |
| 175 | |
| 176 | StringRef getName() const { return {nameData, nameSize}; } |
| 177 | |
| 178 | void setName(StringRef s) { |
| 179 | nameData = s.data(); |
| 180 | nameSize = s.size(); |
| 181 | } |
| 182 | |
| 183 | void parseSymbolVersion(Ctx &); |
| 184 | |
| 185 | // Get the NUL-terminated version suffix ("", "@...", or "@@..."). |
| 186 | // |
| 187 | // For @@, the name has been truncated by insert(). For @, the name has been |
| 188 | // truncated by Symbol::parseSymbolVersion(ctx). |
| 189 | const char *getVersionSuffix() const { return nameData + nameSize; } |
| 190 | |
| 191 | uint32_t getGotIdx(Ctx &ctx) const { return ctx.symAux[auxIdx].gotIdx; } |
| 192 | uint32_t getPltIdx(Ctx &ctx) const { return ctx.symAux[auxIdx].pltIdx; } |
| 193 | uint32_t getTlsDescIdx(Ctx &ctx) const { |
| 194 | return ctx.symAux[auxIdx].tlsDescIdx; |
| 195 | } |
| 196 | uint32_t getTlsGdIdx(Ctx &ctx) const { return ctx.symAux[auxIdx].tlsGdIdx; } |
| 197 | |
| 198 | bool isInGot(Ctx &ctx) const { return getGotIdx(ctx) != uint32_t(-1); } |
| 199 | bool isInPlt(Ctx &ctx) const { return getPltIdx(ctx) != uint32_t(-1); } |
| 200 | |
| 201 | uint64_t getVA(Ctx &, int64_t addend = 0) const; |
| 202 | |
| 203 | uint64_t getGotOffset(Ctx &) const; |
| 204 | uint64_t getGotVA(Ctx &) const; |
| 205 | uint64_t getGotPltOffset(Ctx &) const; |
| 206 | uint64_t getGotPltVA(Ctx &) const; |
| 207 | uint64_t getPltVA(Ctx &) const; |
| 208 | uint64_t getSize() const; |
| 209 | OutputSection *getOutputSection() const; |
| 210 | |
| 211 | // The following two functions are used for symbol resolution. |
| 212 | // |
| 213 | // You are expected to call mergeProperties for all symbols in input |
| 214 | // files so that attributes that are attached to names rather than |
| 215 | // indivisual symbol (such as visibility) are merged together. |
| 216 | // |
| 217 | // Every time you read a new symbol from an input, you are supposed |
| 218 | // to call resolve() with the new symbol. That function replaces |
| 219 | // "this" object as a result of name resolution if the new symbol is |
| 220 | // more appropriate to be included in the output. |
| 221 | // |
| 222 | // For example, if "this" is an undefined symbol and a new symbol is |
| 223 | // a defined symbol, "this" is replaced with the new symbol. |
| 224 | void mergeProperties(const Symbol &other); |
| 225 | void resolve(Ctx &, const Undefined &other); |
| 226 | void resolve(Ctx &, const CommonSymbol &other); |
| 227 | void resolve(Ctx &, const Defined &other); |
| 228 | void resolve(Ctx &, const LazySymbol &other); |
| 229 | void resolve(Ctx &, const SharedSymbol &other); |
| 230 | |
| 231 | // If this is a lazy symbol, extract an input file and add the symbol |
| 232 | // in the file to the symbol table. Calling this function on |
| 233 | // non-lazy object causes a runtime error. |
| 234 | void (Ctx &) const; |
| 235 | |
| 236 | void checkDuplicate(Ctx &, const Defined &other) const; |
| 237 | |
| 238 | private: |
| 239 | bool shouldReplace(Ctx &, const Defined &other) const; |
| 240 | |
| 241 | protected: |
| 242 | Symbol(Kind k, InputFile *file, StringRef name, uint8_t binding, |
| 243 | uint8_t stOther, uint8_t type) |
| 244 | : file(file), nameData(name.data()), nameSize(name.size()), type(type), |
| 245 | binding(binding), stOther(stOther), symbolKind(k), ltoCanOmit(false), |
| 246 | archSpecificBit(false) {} |
| 247 | |
| 248 | void overwrite(Symbol &sym, Kind k) const { |
| 249 | if (sym.traced) |
| 250 | printTraceSymbol(sym: *this, name: sym.getName()); |
| 251 | sym.file = file; |
| 252 | sym.type = type; |
| 253 | sym.binding = binding; |
| 254 | sym.stOther = (stOther & ~3) | sym.visibility(); |
| 255 | sym.symbolKind = k; |
| 256 | } |
| 257 | |
| 258 | public: |
| 259 | // True if this symbol is in the Iplt sub-section of the Plt and the Igot |
| 260 | // sub-section of the .got.plt or .got. |
| 261 | LLVM_PREFERRED_TYPE(bool) |
| 262 | uint8_t isInIplt : 1; |
| 263 | |
| 264 | // True if this symbol needs a GOT entry and its GOT entry is actually in |
| 265 | // Igot. This will be true only for certain non-preemptible ifuncs. |
| 266 | LLVM_PREFERRED_TYPE(bool) |
| 267 | uint8_t gotInIgot : 1; |
| 268 | |
| 269 | // True if defined relative to a section discarded by ICF. |
| 270 | LLVM_PREFERRED_TYPE(bool) |
| 271 | uint8_t folded : 1; |
| 272 | |
| 273 | // Allow reuse of a bit between architecture-exclusive symbol flags. |
| 274 | // - needsTocRestore(): On PPC64, true if a call to this symbol needs to be |
| 275 | // followed by a restore of the toc pointer. |
| 276 | // - isTagged(): On AArch64, true if the symbol needs special relocation and |
| 277 | // metadata semantics because it's tagged, under the AArch64 MemtagABI. |
| 278 | LLVM_PREFERRED_TYPE(bool) |
| 279 | uint8_t archSpecificBit : 1; |
| 280 | bool needsTocRestore() const { return archSpecificBit; } |
| 281 | bool isTagged() const { return archSpecificBit; } |
| 282 | void setNeedsTocRestore(bool v) { archSpecificBit = v; } |
| 283 | void setIsTagged(bool v) { |
| 284 | archSpecificBit = v; |
| 285 | } |
| 286 | |
| 287 | // True if this symbol is defined by a symbol assignment or wrapped by --wrap. |
| 288 | // |
| 289 | // LTO shouldn't inline the symbol because it doesn't know the final content |
| 290 | // of the symbol. |
| 291 | LLVM_PREFERRED_TYPE(bool) |
| 292 | uint8_t scriptDefined : 1; |
| 293 | |
| 294 | // True if defined in a DSO. There may also be a definition in a relocatable |
| 295 | // object file. |
| 296 | LLVM_PREFERRED_TYPE(bool) |
| 297 | uint8_t dsoDefined : 1; |
| 298 | |
| 299 | // True if defined in a DSO as protected visibility. |
| 300 | LLVM_PREFERRED_TYPE(bool) |
| 301 | uint8_t dsoProtected : 1; |
| 302 | |
| 303 | // Temporary flags used to communicate which symbol entries need PLT and GOT |
| 304 | // entries during postScanRelocations(); |
| 305 | std::atomic<uint16_t> flags; |
| 306 | |
| 307 | // A ctx.symAux index used to access GOT/PLT entry indexes. This is allocated |
| 308 | // in postScanRelocations(). |
| 309 | uint32_t auxIdx; |
| 310 | uint32_t dynsymIndex; |
| 311 | |
| 312 | // If `file` is SharedFile (for SharedSymbol or copy-relocated Defined), this |
| 313 | // represents the Verdef index within the input DSO, which will be converted |
| 314 | // to a Verneed index in the output. Otherwise, this represents the Verdef |
| 315 | // index (VER_NDX_LOCAL, VER_NDX_GLOBAL, or a named version). |
| 316 | uint16_t versionId; |
| 317 | LLVM_PREFERRED_TYPE(bool) |
| 318 | uint8_t versionScriptAssigned : 1; |
| 319 | |
| 320 | // True if targeted by a range extension thunk. |
| 321 | LLVM_PREFERRED_TYPE(bool) |
| 322 | uint8_t thunkAccessed : 1; |
| 323 | |
| 324 | // True if the symbol is in the --dynamic-list file. A Defined symbol with |
| 325 | // protected or default visibility with this property is required to be |
| 326 | // exported into .dynsym. |
| 327 | LLVM_PREFERRED_TYPE(bool) |
| 328 | uint8_t inDynamicList : 1; |
| 329 | |
| 330 | // Used to track if there has been at least one undefined reference to the |
| 331 | // symbol. For Undefined and SharedSymbol, the binding may change to STB_WEAK |
| 332 | // if the first undefined reference from a non-shared object is weak. |
| 333 | LLVM_PREFERRED_TYPE(bool) |
| 334 | uint8_t referenced : 1; |
| 335 | |
| 336 | // Used to track if this symbol will be referenced after wrapping is performed |
| 337 | // (i.e. this will be true for foo if __real_foo is referenced, and will be |
| 338 | // true for __wrap_foo if foo is referenced). |
| 339 | LLVM_PREFERRED_TYPE(bool) |
| 340 | uint8_t referencedAfterWrap : 1; |
| 341 | |
| 342 | void setFlags(uint16_t bits) { |
| 343 | flags.fetch_or(i: bits, m: std::memory_order_relaxed); |
| 344 | } |
| 345 | bool hasFlag(uint16_t bit) const { |
| 346 | assert(llvm::has_single_bit(bit) && "bit must be a power of 2" ); |
| 347 | return flags.load(m: std::memory_order_relaxed) & bit; |
| 348 | } |
| 349 | |
| 350 | bool needsDynReloc() const { |
| 351 | return flags.load(m: std::memory_order_relaxed) & |
| 352 | (NEEDS_COPY | NEEDS_GOT | NEEDS_PLT | NEEDS_TLSDESC | NEEDS_TLSGD | |
| 353 | NEEDS_TLSGD_TO_IE | NEEDS_GOT_DTPREL | NEEDS_TLSIE); |
| 354 | } |
| 355 | void allocateAux(Ctx &ctx) { |
| 356 | assert(auxIdx == 0); |
| 357 | auxIdx = ctx.symAux.size(); |
| 358 | ctx.symAux.emplace_back(); |
| 359 | } |
| 360 | |
| 361 | bool isSection() const { return type == llvm::ELF::STT_SECTION; } |
| 362 | bool isTls() const { return type == llvm::ELF::STT_TLS; } |
| 363 | bool isFunc() const { return type == llvm::ELF::STT_FUNC; } |
| 364 | bool isGnuIFunc() const { return type == llvm::ELF::STT_GNU_IFUNC; } |
| 365 | bool isObject() const { return type == llvm::ELF::STT_OBJECT; } |
| 366 | bool isFile() const { return type == llvm::ELF::STT_FILE; } |
| 367 | }; |
| 368 | |
| 369 | // Represents a symbol that is defined in the current output file. |
| 370 | class Defined : public Symbol { |
| 371 | public: |
| 372 | Defined(Ctx &ctx, InputFile *file, StringRef name, uint8_t binding, |
| 373 | uint8_t stOther, uint8_t type, uint64_t value, uint64_t size, |
| 374 | SectionBase *section) |
| 375 | : Symbol(DefinedKind, file, name, binding, stOther, type), value(value), |
| 376 | size(size), section(section) { |
| 377 | } |
| 378 | void overwrite(Symbol &sym) const; |
| 379 | |
| 380 | static bool classof(const Symbol *s) { return s->isDefined(); } |
| 381 | |
| 382 | uint64_t value; |
| 383 | uint64_t size; |
| 384 | SectionBase *section; |
| 385 | }; |
| 386 | |
| 387 | // Represents a common symbol. |
| 388 | // |
| 389 | // On Unix, it is traditionally allowed to write variable definitions |
| 390 | // without initialization expressions (such as "int foo;") to header |
| 391 | // files. Such definition is called "tentative definition". |
| 392 | // |
| 393 | // Using tentative definition is usually considered a bad practice |
| 394 | // because you should write only declarations (such as "extern int |
| 395 | // foo;") to header files. Nevertheless, the linker and the compiler |
| 396 | // have to do something to support bad code by allowing duplicate |
| 397 | // definitions for this particular case. |
| 398 | // |
| 399 | // Common symbols represent variable definitions without initializations. |
| 400 | // The compiler creates common symbols when it sees variable definitions |
| 401 | // without initialization (you can suppress this behavior and let the |
| 402 | // compiler create a regular defined symbol by -fno-common). |
| 403 | // |
| 404 | // The linker allows common symbols to be replaced by regular defined |
| 405 | // symbols. If there are remaining common symbols after name resolution is |
| 406 | // complete, they are converted to regular defined symbols in a .bss |
| 407 | // section. (Therefore, the later passes don't see any CommonSymbols.) |
| 408 | class CommonSymbol : public Symbol { |
| 409 | public: |
| 410 | CommonSymbol(Ctx &ctx, InputFile *file, StringRef name, uint8_t binding, |
| 411 | uint8_t stOther, uint8_t type, uint64_t alignment, uint64_t size) |
| 412 | : Symbol(CommonKind, file, name, binding, stOther, type), |
| 413 | alignment(alignment), size(size) { |
| 414 | } |
| 415 | void overwrite(Symbol &sym) const { |
| 416 | Symbol::overwrite(sym, k: CommonKind); |
| 417 | auto &s = static_cast<CommonSymbol &>(sym); |
| 418 | s.alignment = alignment; |
| 419 | s.size = size; |
| 420 | } |
| 421 | |
| 422 | static bool classof(const Symbol *s) { return s->isCommon(); } |
| 423 | |
| 424 | uint32_t alignment; |
| 425 | uint64_t size; |
| 426 | }; |
| 427 | |
| 428 | class Undefined : public Symbol { |
| 429 | public: |
| 430 | Undefined(InputFile *file, StringRef name, uint8_t binding, uint8_t stOther, |
| 431 | uint8_t type, uint32_t discardedSecIdx = 0) |
| 432 | : Symbol(UndefinedKind, file, name, binding, stOther, type), |
| 433 | discardedSecIdx(discardedSecIdx) {} |
| 434 | void overwrite(Symbol &sym) const { |
| 435 | Symbol::overwrite(sym, k: UndefinedKind); |
| 436 | auto &s = static_cast<Undefined &>(sym); |
| 437 | s.discardedSecIdx = discardedSecIdx; |
| 438 | s.nonPrevailing = nonPrevailing; |
| 439 | } |
| 440 | |
| 441 | static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; } |
| 442 | |
| 443 | // The section index if in a discarded section, 0 otherwise. |
| 444 | uint32_t discardedSecIdx; |
| 445 | bool nonPrevailing = false; |
| 446 | }; |
| 447 | |
| 448 | class SharedSymbol : public Symbol { |
| 449 | public: |
| 450 | static bool classof(const Symbol *s) { return s->kind() == SharedKind; } |
| 451 | |
| 452 | SharedSymbol(InputFile &file, StringRef name, uint8_t binding, |
| 453 | uint8_t stOther, uint8_t type, uint64_t value, uint64_t size, |
| 454 | uint32_t alignment) |
| 455 | : Symbol(SharedKind, &file, name, binding, stOther, type), value(value), |
| 456 | size(size), alignment(alignment) { |
| 457 | dsoProtected = visibility() == llvm::ELF::STV_PROTECTED; |
| 458 | // GNU ifunc is a mechanism to allow user-supplied functions to |
| 459 | // resolve PLT slot values at load-time. This is contrary to the |
| 460 | // regular symbol resolution scheme in which symbols are resolved just |
| 461 | // by name. Using this hook, you can program how symbols are solved |
| 462 | // for you program. For example, you can make "memcpy" to be resolved |
| 463 | // to a SSE-enabled version of memcpy only when a machine running the |
| 464 | // program supports the SSE instruction set. |
| 465 | // |
| 466 | // Naturally, such symbols should always be called through their PLT |
| 467 | // slots. What GNU ifunc symbols point to are resolver functions, and |
| 468 | // calling them directly doesn't make sense (unless you are writing a |
| 469 | // loader). |
| 470 | // |
| 471 | // For DSO symbols, we always call them through PLT slots anyway. |
| 472 | // So there's no difference between GNU ifunc and regular function |
| 473 | // symbols if they are in DSOs. So we can handle GNU_IFUNC as FUNC. |
| 474 | if (this->type == llvm::ELF::STT_GNU_IFUNC) |
| 475 | this->type = llvm::ELF::STT_FUNC; |
| 476 | } |
| 477 | void overwrite(Symbol &sym) const { |
| 478 | Symbol::overwrite(sym, k: SharedKind); |
| 479 | auto &s = static_cast<SharedSymbol &>(sym); |
| 480 | s.dsoProtected = dsoProtected; |
| 481 | s.value = value; |
| 482 | s.size = size; |
| 483 | s.alignment = alignment; |
| 484 | } |
| 485 | |
| 486 | uint64_t value; // st_value |
| 487 | uint64_t size; // st_size |
| 488 | uint32_t alignment; |
| 489 | }; |
| 490 | |
| 491 | // LazySymbol symbols represent symbols in object files between --start-lib and |
| 492 | // --end-lib options. LLD also handles traditional archives as if all the files |
| 493 | // in the archive are surrounded by --start-lib and --end-lib. |
| 494 | // |
| 495 | // A special complication is the handling of weak undefined symbols. They should |
| 496 | // not load a file, but we have to remember we have seen both the weak undefined |
| 497 | // and the lazy. We represent that with a lazy symbol with a weak binding. This |
| 498 | // means that code looking for undefined symbols normally also has to take lazy |
| 499 | // symbols into consideration. |
| 500 | class LazySymbol : public Symbol { |
| 501 | public: |
| 502 | LazySymbol(InputFile &file) |
| 503 | : Symbol(LazyKind, &file, {}, llvm::ELF::STB_GLOBAL, |
| 504 | llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE) {} |
| 505 | void overwrite(Symbol &sym) const { Symbol::overwrite(sym, k: LazyKind); } |
| 506 | |
| 507 | static bool classof(const Symbol *s) { return s->kind() == LazyKind; } |
| 508 | }; |
| 509 | |
| 510 | // A buffer class that is large enough to hold any Symbol-derived |
| 511 | // object. We allocate memory using this class and instantiate a symbol |
| 512 | // using the placement new. |
| 513 | |
| 514 | // It is important to keep the size of SymbolUnion small for performance and |
| 515 | // memory usage reasons. 64 bytes is a soft limit based on the size of Defined |
| 516 | // on a 64-bit system. This is enforced by a static_assert in Symbols.cpp. |
| 517 | union SymbolUnion { |
| 518 | alignas(Defined) char a[sizeof(Defined)]; |
| 519 | alignas(CommonSymbol) char b[sizeof(CommonSymbol)]; |
| 520 | alignas(Undefined) char c[sizeof(Undefined)]; |
| 521 | alignas(SharedSymbol) char d[sizeof(SharedSymbol)]; |
| 522 | alignas(LazySymbol) char e[sizeof(LazySymbol)]; |
| 523 | }; |
| 524 | |
| 525 | template <typename... T> Defined *makeDefined(T &&...args) { |
| 526 | auto *sym = getSpecificAllocSingleton<SymbolUnion>().Allocate(); |
| 527 | memset(s: sym, c: 0, n: sizeof(Symbol)); |
| 528 | auto &s = *new (reinterpret_cast<Defined *>(sym)) Defined(std::forward<T>(args)...); |
| 529 | return &s; |
| 530 | } |
| 531 | |
| 532 | void reportDuplicate(Ctx &, const Symbol &sym, const InputFile *newFile, |
| 533 | InputSectionBase *errSec, uint64_t errOffset); |
| 534 | void maybeWarnUnorderableSymbol(Ctx &, const Symbol *sym); |
| 535 | bool computeIsPreemptible(Ctx &, const Symbol &sym); |
| 536 | void parseVersionAndComputeIsPreemptible(Ctx &); |
| 537 | |
| 538 | } // namespace lld::elf |
| 539 | |
| 540 | #endif |
| 541 | |