diff options
| -rw-r--r-- | lld/COFF/Driver.cpp | 6 | ||||
| -rw-r--r-- | lld/COFF/SymbolTable.cpp | 4 | ||||
| -rw-r--r-- | lld/COFF/SymbolTable.h | 2 | ||||
| -rw-r--r-- | lld/COFF/Symbols.h | 38 | ||||
| -rw-r--r-- | lld/COFF/Writer.cpp | 55 | ||||
| -rw-r--r-- | lld/test/COFF/pdb-safeseh.yaml | 85 | ||||
| -rw-r--r-- | lld/test/COFF/safeseh-diag-feat.test (renamed from lld/test/COFF/safeseh.test) | 0 | ||||
| -rw-r--r-- | lld/test/COFF/safeseh.s | 60 |
8 files changed, 213 insertions, 37 deletions
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index b5c0ac2200c..22efb312ae4 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -1026,10 +1026,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (Config->ImageBase == uint64_t(-1)) Config->ImageBase = getDefaultImageBase(); - Symtab.addRelative(mangle("__ImageBase"), 0); + Symtab.addSynthetic(mangle("__ImageBase"), nullptr); if (Config->Machine == I386) { - Config->SEHTable = Symtab.addRelative("___safe_se_handler_table", 0); - Config->SEHCount = Symtab.addAbsolute("___safe_se_handler_count", 0); + Symtab.addAbsolute("___safe_se_handler_table", 0); + Symtab.addAbsolute("___safe_se_handler_count", 0); } // We do not support /guard:cf (control flow protection) yet. diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index 5089825e62c..c06e42bb114 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -219,13 +219,13 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { return S; } -Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) { +Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) - replaceBody<DefinedRelative>(S, N, VA); + replaceBody<DefinedSynthetic>(S, N, C); else if (!isa<DefinedCOFF>(S->body())) reportDuplicate(S, nullptr); return S; diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index 0aa8a4593b5..ea74678c28d 100644 --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -85,7 +85,7 @@ public: // Creates an Undefined symbol for a given name. SymbolBody *addUndefined(StringRef Name); - Symbol *addRelative(StringRef N, uint64_t VA); + Symbol *addSynthetic(StringRef N, Chunk *C); Symbol *addAbsolute(StringRef N, uint64_t VA); Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias); diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h index 828744b8ff3..feebb6dde1f 100644 --- a/lld/COFF/Symbols.h +++ b/lld/COFF/Symbols.h @@ -50,13 +50,13 @@ public: DefinedImportThunkKind, DefinedImportDataKind, DefinedAbsoluteKind, - DefinedRelativeKind, + DefinedSyntheticKind, UndefinedKind, LazyKind, LastDefinedCOFFKind = DefinedCommonKind, - LastDefinedKind = DefinedRelativeKind, + LastDefinedKind = DefinedSyntheticKind, }; Kind kind() const { return static_cast<Kind>(SymbolKind); } @@ -112,11 +112,11 @@ public: // Returns the RVA relative to the beginning of the output section. // Used to implement SECREL relocation type. - uint64_t getSecrel(); + uint32_t getSecrel(); // Returns the output section index. // Used to implement SECTION relocation type. - uint64_t getSectionIndex(); + uint16_t getSectionIndex(); // Returns true if this symbol points to an executable (e.g. .text) section. // Used to implement ARM relocations. @@ -167,6 +167,7 @@ public: bool isCOMDAT() { return IsCOMDAT; } SectionChunk *getChunk() { return *Data; } uint32_t getValue() { return Sym->Value; } + uint32_t getSecrel(); private: SectionChunk **Data; @@ -221,24 +222,25 @@ private: uint64_t VA; }; -// This is a kind of absolute symbol but relative to the image base. -// Unlike absolute symbols, relocations referring this kind of symbols -// are subject of the base relocation. This type is used rarely -- -// mainly for __ImageBase. -class DefinedRelative : public Defined { +// This symbol is used for linker-synthesized symbols like __ImageBase and +// __safe_se_handler_table. +class DefinedSynthetic : public Defined { public: - explicit DefinedRelative(StringRef Name, uint64_t V = 0) - : Defined(DefinedRelativeKind, Name), RVA(V) {} + explicit DefinedSynthetic(StringRef Name, Chunk *C) + : Defined(DefinedSyntheticKind, Name), C(C) {} static bool classof(const SymbolBody *S) { - return S->kind() == DefinedRelativeKind; + return S->kind() == DefinedSyntheticKind; } - uint64_t getRVA() { return RVA; } - void setRVA(uint64_t V) { RVA = V; } + // A null chunk indicates that this is __ImageBase. Otherwise, this is some + // other synthesized chunk, like SEHTableChunk. + uint32_t getRVA() const { return C ? C->getRVA() : 0; } + uint32_t getSecrel() const { return C ? C->OutputSectionOff : 0; } + Chunk *getChunk() const { return C; } private: - uint64_t RVA; + Chunk *C; }; // This class represents a symbol defined in an archive file. It is @@ -355,8 +357,8 @@ inline uint64_t Defined::getRVA() { switch (kind()) { case DefinedAbsoluteKind: return cast<DefinedAbsolute>(this)->getRVA(); - case DefinedRelativeKind: - return cast<DefinedRelative>(this)->getRVA(); + case DefinedSyntheticKind: + return cast<DefinedSynthetic>(this)->getRVA(); case DefinedImportDataKind: return cast<DefinedImportData>(this)->getRVA(); case DefinedImportThunkKind: @@ -391,7 +393,7 @@ struct Symbol { // AlignedCharArrayUnion gives us a struct with a char array field that is // large and aligned enough to store any derived class of SymbolBody. llvm::AlignedCharArrayUnion< - DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy, + DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedSynthetic, Lazy, Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport> Body; diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 083839c2bab..03e5d4d675b 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -210,17 +210,36 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) { } } -uint64_t Defined::getSecrel() { - if (auto *D = dyn_cast<DefinedRegular>(this)) - return getRVA() - D->getChunk()->getOutputSection()->getRVA(); +uint32_t Defined::getSecrel() { + assert(this); + switch (kind()) { + case DefinedRegularKind: + return cast<DefinedRegular>(this)->getSecrel(); + case DefinedSyntheticKind: + return cast<DefinedSynthetic>(this)->getSecrel(); + default: + break; + } fatal("SECREL relocation points to a non-regular symbol: " + toString(*this)); } -uint64_t Defined::getSectionIndex() { +uint32_t DefinedRegular::getSecrel() { + assert(getChunk()->isLive() && "relocation against discarded section"); + uint64_t Diff = getRVA() - getChunk()->getOutputSection()->getRVA(); + assert(Diff < UINT32_MAX && "section offset too large"); + return (uint32_t)Diff; +} + +uint16_t Defined::getSectionIndex() { if (auto *D = dyn_cast<DefinedRegular>(this)) return D->getChunk()->getOutputSection()->SectionIndex; if (isa<DefinedAbsolute>(this)) return DefinedAbsolute::OutputSectionIndex; + if (auto *D = dyn_cast<DefinedSynthetic>(this)) { + if (!D->getChunk()) + return 0; + return D->getChunk()->getOutputSection()->SectionIndex; + } fatal("SECTION relocation points to a non-regular symbol: " + toString(*this)); } @@ -348,12 +367,19 @@ void Writer::createMiscChunks() { for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) { if (!File->SEHCompat) return; - for (SymbolBody *B : File->SEHandlers) - Handlers.insert(cast<Defined>(B)); + for (SymbolBody *B : File->SEHandlers) { + // Make sure the handler is still live. Assume all handlers are regular + // symbols. + auto *D = dyn_cast<DefinedRegular>(B); + if (D && D->getChunk()->isLive()) + Handlers.insert(D); + } } - SEHTable = make<SEHTableChunk>(Handlers); - RData->addChunk(SEHTable); + if (!Handlers.empty()) { + SEHTable = make<SEHTableChunk>(Handlers); + RData->addChunk(SEHTable); + } } // Create .idata section for the DLL-imported symbol table. @@ -445,7 +471,7 @@ size_t Writer::addEntryToStringTable(StringRef Str) { Optional<coff_symbol16> Writer::createSymbol(Defined *Def) { // Relative symbols are unrepresentable in a COFF symbol table. - if (isa<DefinedRelative>(Def)) + if (isa<DefinedSynthetic>(Def)) return None; if (auto *D = dyn_cast<DefinedRegular>(Def)) { @@ -758,10 +784,13 @@ void Writer::openFile(StringRef Path) { void Writer::fixSafeSEHSymbols() { if (!SEHTable) return; - if (auto *T = dyn_cast<DefinedRelative>(Config->SEHTable->body())) - T->setRVA(SEHTable->getRVA()); - if (auto *C = dyn_cast<DefinedAbsolute>(Config->SEHCount->body())) - C->setVA(SEHTable->getSize() / 4); + // Replace the absolute table symbol with a synthetic symbol pointing to the + // SEHTable chunk so that we can emit base relocations for it and resolve + // section relative relocations. + Symbol *T = Symtab->find("___safe_se_handler_table"); + Symbol *C = Symtab->find("___safe_se_handler_count"); + replaceBody<DefinedSynthetic>(T, T->body()->getName(), SEHTable); + cast<DefinedAbsolute>(C->body())->setVA(SEHTable->getSize() / 4); } // Handles /section options to allow users to overwrite diff --git a/lld/test/COFF/pdb-safeseh.yaml b/lld/test/COFF/pdb-safeseh.yaml new file mode 100644 index 00000000000..9faa5042924 --- /dev/null +++ b/lld/test/COFF/pdb-safeseh.yaml @@ -0,0 +1,85 @@ +# RUN: yaml2obj %s -o %t.obj +# RUN: lld-link -debug -entry:main -out:%t.exe -pdb:%t.pdb %t.obj +# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s + +# There is an S_GDATA32 symbol record with .secrel32 and .secidx relocations in +# it in this debug info. This is similar to the relocations in the loadcfg.obj +# file in the MSVC CRT. We need to make sure that our relocation logic matches +# MSVC's for these absolute, linker-provided symbols. + +# CHECK: Mod 0000 | +# CHECK-NEXT: - S_GDATA32 [size = 40] `___safe_se_handler_table` +# CHECK-NEXT: type = 0x0022 (unsigned long), addr = 0003:0000 +# CHECK-NEXT: Mod 0001 | `* Linker *`: + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: '.debug$S' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 1 + Subsections: + - !Symbols + Records: + - Kind: S_GDATA32 + DataSym: + Type: 34 + DisplayName: ___safe_se_handler_table + - !StringTable + Strings: + Relocations: + - VirtualAddress: 20 + SymbolName: ___safe_se_handler_table + Type: IMAGE_REL_I386_SECREL + - VirtualAddress: 24 + SymbolName: ___safe_se_handler_table + Type: IMAGE_REL_I386_SECTION + - Name: '.text$mn' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 488D0500000000C3 + Relocations: + - VirtualAddress: 3 + SymbolName: ___safe_se_handler_table + Type: IMAGE_REL_I386_REL32 +symbols: + - Name: '.debug$S' + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 372 + NumberOfRelocations: 6 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: '.text$mn' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 1092178131 + Number: 0 + - Name: _main + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: ___safe_se_handler_table + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... + diff --git a/lld/test/COFF/safeseh.test b/lld/test/COFF/safeseh-diag-feat.test index ed928a51350..ed928a51350 100644 --- a/lld/test/COFF/safeseh.test +++ b/lld/test/COFF/safeseh-diag-feat.test diff --git a/lld/test/COFF/safeseh.s b/lld/test/COFF/safeseh.s new file mode 100644 index 00000000000..83c15afbf93 --- /dev/null +++ b/lld/test/COFF/safeseh.s @@ -0,0 +1,60 @@ +# RUN: llvm-mc -triple i686-windows-msvc %s -filetype=obj -o %t.obj +# RUN: lld-link %t.obj -safeseh -out:%t.exe -opt:noref -entry:main +# RUN: llvm-readobj -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-NOGC +# RUN: lld-link %t.obj -safeseh -out:%t.exe -opt:ref -entry:main +# RUN: llvm-readobj -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-GC + +# CHECK-NOGC: LoadConfig [ +# CHECK-NOGC: Size: 0x48 +# CHECK-NOGC: SEHandlerTable: 0x401048 +# CHECK-NOGC: SEHandlerCount: 1 +# CHECK-NOGC: ] +# CHECK-NOGC: SEHTable [ +# CHECK-NOGC-NEXT: 0x402006 +# CHECK-NOGC-NEXT: ] + +# CHECK-GC: LoadConfig [ +# CHECK-GC: Size: 0x48 +# CHECK-GC: SEHandlerTable: 0x0 +# CHECK-GC: SEHandlerCount: 0 +# CHECK-GC: ] +# CHECK-GC-NOT: SEHTable + + + .def @feat.00; + .scl 3; + .type 0; + .endef + .globl @feat.00 +@feat.00 = 1 + + .def _main; + .scl 2; + .type 32; + .endef + .section .text,"xr",one_only,_main + .globl _main +_main: + movl $42, %eax + ret + +# This handler can be GCd, which will make the safeseh table empty, so it should +# appear null. + .def _my_handler; + .scl 3; + .type 32; + .endef + .section .text,"xr",one_only,_my_handler +_my_handler: + ret + +.safeseh _my_handler + + + .section .rdata,"dr" +.globl __load_config_used +__load_config_used: + .long 72 + .fill 60, 1, 0 + .long ___safe_se_handler_table + .long ___safe_se_handler_count |

