diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h --- a/lld/COFF/Chunks.h +++ b/lld/COFF/Chunks.h @@ -584,6 +584,17 @@ SymbolRVASet syms; }; +// Table which contains symbol RVAs with flags. Used for /guard:ehcont. +class RVAFlagTableChunk : public NonSectionChunk { +public: + explicit RVAFlagTableChunk(SymbolRVASet s) : syms(std::move(s)) {} + size_t getSize() const override { return syms.size() * 5; } + void writeTo(uint8_t *buf) const override; + +private: + SymbolRVASet syms; +}; + // Windows-specific. // This class represents a block in .reloc section. // See the PE/COFF spec 5.6 for details. diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -801,6 +801,25 @@ "RVA tables should be de-duplicated"); } +void RVAFlagTableChunk::writeTo(uint8_t *buf) const { + struct RVAFlag { + ulittle32_t RVA; + uint8_t flag; + }; + RVAFlag *begin = reinterpret_cast(buf); + size_t cnt = 0; + for (const ChunkAndOffset &co : syms) { + begin[cnt].RVA = co.inputChunk->getRVA() + co.offset; + begin[cnt].flag = 0; + ++cnt; + } + auto LT = [](RVAFlag &A, RVAFlag &B) { return A.RVA < B.RVA; }; + auto EQ = [](RVAFlag &A, RVAFlag &B) { return A.RVA == B.RVA; }; + std::sort(begin, begin + cnt, LT); + assert(std::unique(begin, begin + cnt, EQ) == begin + cnt && + "RVA tables should be de-duplicated"); +} + // MinGW specific, for the "automatic import of variables from DLLs" feature. size_t PseudoRelocTableChunk::getSize() const { if (relocs.empty()) diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -74,10 +74,12 @@ Fixup = 0x4, /// Relocation Table }; -enum class GuardCFLevel { - Off, - NoLongJmp, // Emit gfids but no longjmp tables - Full, // Enable all protections. +enum GuardCFLevel { + Off = 0x0, + CF = 0x1, /// Emit gfids tables + LongJmp = 0x2, /// Emit longjmp tables + EHCont = 0x4, /// Emit ehcont tables + All = 0x7 /// Enable all protections }; enum class ICFLevel { @@ -143,7 +145,7 @@ bool saveTemps = false; // /guard:cf - GuardCFLevel guardCF = GuardCFLevel::Off; + int guardCF = GuardCFLevel::Off; // Used for SafeSEH. bool safeSEH = false; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -1998,6 +1998,9 @@ symtab->addAbsolute(mangle("__guard_longjmp_table"), 0); // Needed for MSVC 2017 15.5 CRT. symtab->addAbsolute(mangle("__enclave_config"), 0); + // Needed for MSVC 2019 16.8 CRT. + symtab->addAbsolute(mangle("__guard_eh_cont_count"), 0); + symtab->addAbsolute(mangle("__guard_eh_cont_table"), 0); if (config->pseudoRelocs) { symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -102,9 +102,15 @@ if (arg.equals_lower("no")) config->guardCF = GuardCFLevel::Off; else if (arg.equals_lower("nolongjmp")) - config->guardCF = GuardCFLevel::NoLongJmp; - else if (arg.equals_lower("cf") || arg.equals_lower("longjmp")) - config->guardCF = GuardCFLevel::Full; + config->guardCF &= ~GuardCFLevel::LongJmp; + else if (arg.equals_lower("noehcont")) + config->guardCF &= ~GuardCFLevel::EHCont; + else if (arg.equals_lower("cf")) + config->guardCF = GuardCFLevel::CF; + else if (arg.equals_lower("longjmp")) + config->guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp; + else if (arg.equals_lower("ehcont")) + config->guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont; else fatal("invalid argument to /guard: " + arg); } diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -146,6 +146,7 @@ ArrayRef getGuardFidChunks() { return guardFidChunks; } ArrayRef getGuardIATChunks() { return guardIATChunks; } ArrayRef getGuardLJmpChunks() { return guardLJmpChunks; } + ArrayRef getGuardEHContChunks() { return guardEHContChunks; } ArrayRef getSymbols() { return symbols; } MutableArrayRef getMutableSymbols() { return symbols; } @@ -184,7 +185,7 @@ bool hasSafeSEH() { return feat00Flags & 0x1; } // True if this file was compiled with /guard:cf. - bool hasGuardCF() { return feat00Flags & 0x800; } + bool hasGuardCF() { return feat00Flags & 0x4800; } // Pointer to the PDB module descriptor builder. Various debug info records // will reference object files by "module index", which is here. Things like @@ -287,11 +288,12 @@ std::vector sxDataChunks; // Chunks containing symbol table indices of address taken symbols, address - // taken IAT entries, and longjmp targets. These are not linked into the - // final binary when /guard:cf is set. + // taken IAT entries, longjmp and ehcont targets. These are not linked into + // the final binary when /guard:cf is set. std::vector guardFidChunks; std::vector guardIATChunks; std::vector guardLJmpChunks; + std::vector guardEHContChunks; // This vector contains a list of all symbols defined or referenced by this // file. They are indexed such that you can get a Symbol by symbol diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -284,6 +284,8 @@ guardIATChunks.push_back(c); else if (name == ".gljmp$y") guardLJmpChunks.push_back(c); + else if (name == ".gehcont$y") + guardEHContChunks.push_back(c); else if (name == ".sxdata") sxDataChunks.push_back(c); else if (config->tailMerge && sec->NumberOfRelocations == 0 && diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -231,7 +231,7 @@ ArrayRef symIdxChunks, std::vector &symbols); void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, - StringRef countSym); + StringRef countSym, bool HasFlag=false); void setSectionPermissions(); void writeSections(); void writeBuildId(); @@ -1635,17 +1635,20 @@ SymbolRVASet giatsRVASet; std::vector giatsSymbols; SymbolRVASet longJmpTargets; + SymbolRVASet ehContTargets; for (ObjFile *file : ObjFile::instances) { // If the object was compiled with /guard:cf, the address taken symbols - // are in .gfids$y sections, and the longjmp targets are in .gljmp$y - // sections. If the object was not compiled with /guard:cf, we assume there - // were no setjmp targets, and that all code symbols with relocations are - // possibly address-taken. + // are in .gfids$y sections, the longjmp targets are in .gljmp$y sections, + // and ehcont targets are in .gehcont$y sections. If the object was not + // compiled with /guard:cf, we assume there were no setjmp and ehcont + // targets, and that all code symbols with relocations are possibly + // address-taken. if (file->hasGuardCF()) { markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms); markSymbolsForRVATable(file, file->getGuardIATChunks(), giatsRVASet); getSymbolsFromSections(file, file->getGuardIATChunks(), giatsSymbols); markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets); + markSymbolsForRVATable(file, file->getGuardEHContChunks(), ehContTargets); } else { markSymbolsWithRelocations(file, addressTakenSyms); } @@ -1682,16 +1685,23 @@ "__guard_iat_count"); // Add the longjmp target table unless the user told us not to. - if (config->guardCF == GuardCFLevel::Full) + if (config->guardCF & GuardCFLevel::LongJmp) maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table", "__guard_longjmp_count"); + // Add the ehcont target table unless the user told us not to. + if (config->guardCF & GuardCFLevel::EHCont) + maybeAddRVATable(std::move(ehContTargets), "__guard_eh_cont_table", + "__guard_eh_cont_count", true); + // Set __guard_flags, which will be used in the load config to indicate that // /guard:cf was enabled. uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) | uint32_t(coff_guard_flags::HasFidTable); - if (config->guardCF == GuardCFLevel::Full) + if (config->guardCF & GuardCFLevel::LongJmp) guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); + if (config->guardCF & GuardCFLevel::EHCont) + guardFlags |= uint32_t(coff_guard_flags::HasEHContTable); Symbol *flagSym = symtab->findUnderscore("__guard_flags"); cast(flagSym)->setVA(guardFlags); } @@ -1753,17 +1763,21 @@ // tableChunk so that we can emit base relocations for it and resolve section // relative relocations. void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, - StringRef countSym) { + StringRef countSym, bool HasFlag) { if (tableSymbols.empty()) return; - RVATableChunk *tableChunk = make(std::move(tableSymbols)); + NonSectionChunk *tableChunk; + if (HasFlag) + tableChunk = make(std::move(tableSymbols)); + else + tableChunk = make(std::move(tableSymbols)); rdataSec->addChunk(tableChunk); Symbol *t = symtab->findUnderscore(tableSym); Symbol *c = symtab->findUnderscore(countSym); replaceSymbol(t, t->getName(), tableChunk); - cast(c)->setVA(tableChunk->getSize() / 4); + cast(c)->setVA(tableChunk->getSize() / (HasFlag ? 5 : 4)); } // MinGW specific. Gather all relocations that are imported from a DLL even diff --git a/lld/test/COFF/gfids-corrupt.s b/lld/test/COFF/gfids-corrupt.s --- a/lld/test/COFF/gfids-corrupt.s +++ b/lld/test/COFF/gfids-corrupt.s @@ -1,6 +1,6 @@ # REQUIRES: x86 # RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj -# RUN: lld-link %t.obj -opt:noref -guard:nolongjmp -out:%t.exe -entry:main 2>&1 | FileCheck %s --check-prefix=ERRS +# RUN: lld-link %t.obj -opt:noref -guard:cf -out:%t.exe -entry:main 2>&1 | FileCheck %s --check-prefix=ERRS # RUN: llvm-readobj --file-headers --coff-load-config %t.exe | FileCheck %s # ERRS: warning: ignoring .gfids$y symbol table index section in object {{.*}}gfids-corrupt{{.*}} diff --git a/lld/test/COFF/gfids-fallback.s b/lld/test/COFF/gfids-fallback.s --- a/lld/test/COFF/gfids-fallback.s +++ b/lld/test/COFF/gfids-fallback.s @@ -1,7 +1,7 @@ # REQUIRES: x86 # RUN: grep -B99999 [S]PLITMARKER %s | llvm-mc -triple x86_64-windows-msvc -filetype=obj -o %t1.obj # RUN: grep -A99999 [S]PLITMARKER %s | llvm-mc -triple x86_64-windows-msvc -filetype=obj -o %t2.obj -# RUN: lld-link %t1.obj %t2.obj -guard:nolongjmp -out:%t.exe -entry:main -opt:noref +# RUN: lld-link %t1.obj %t2.obj -guard:cf -out:%t.exe -entry:main -opt:noref # RUN: llvm-readobj --file-headers --coff-load-config %t.exe | FileCheck %s # CHECK: ImageBase: 0x140000000 diff --git a/lld/test/COFF/gfids-gc.s b/lld/test/COFF/gfids-gc.s --- a/lld/test/COFF/gfids-gc.s +++ b/lld/test/COFF/gfids-gc.s @@ -1,10 +1,10 @@ # REQUIRES: x86 # RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj -# RUN: lld-link %t.obj -guard:nolongjmp -out:%t.exe -opt:noref -entry:main +# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:noref -entry:main # RUN: llvm-readobj --file-headers --coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-NOGC -# RUN: lld-link %t.obj -guard:nolongjmp -out:%t.exe -opt:noref -entry:main -debug:dwarf +# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:noref -entry:main -debug:dwarf # RUN: llvm-readobj --file-headers --coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-NOGC -# RUN: lld-link %t.obj -guard:nolongjmp -out:%t.exe -opt:ref -entry:main +# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:ref -entry:main # RUN: llvm-readobj --file-headers --coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-GC # This assembly is meant to mimic what CL emits for this kind of C code when diff --git a/lld/test/COFF/gfids-icf.s b/lld/test/COFF/gfids-icf.s --- a/lld/test/COFF/gfids-icf.s +++ b/lld/test/COFF/gfids-icf.s @@ -1,6 +1,6 @@ # REQUIRES: x86 # RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj -# RUN: lld-link %t.obj -guard:nolongjmp -out:%t.exe -opt:icf -entry:main +# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:icf -entry:main # RUN: llvm-readobj --file-headers --coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK # This assembly is meant to mimic what CL emits for this kind of C code: diff --git a/lld/test/COFF/giats.s b/lld/test/COFF/giats.s --- a/lld/test/COFF/giats.s +++ b/lld/test/COFF/giats.s @@ -8,7 +8,7 @@ # RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %basename_t.obj # Check that the Guard address-taken IAT entry tables are propagated to the final executable. -# RUN: lld-link %basename_t.obj -guard:cf -entry:main -out:%basename_t-nodelay.exe %basename_t-exp.lib +# RUN: lld-link %basename_t.obj -guard:cf -guard:longjmp -entry:main -out:%basename_t-nodelay.exe %basename_t-exp.lib # RUN: llvm-readobj --file-headers --coff-load-config %basename_t-nodelay.exe | FileCheck %s --check-prefix CHECK # CHECK: ImageBase: 0x140000000 @@ -28,7 +28,7 @@ # Check that the additional load thunk symbol is added to the GFIDs table. -# RUN: lld-link %basename_t.obj -guard:cf -entry:main -out:%basename_t-delay.exe %basename_t-exp.lib -alternatename:__delayLoadHelper2=main -delayload:%basename_t-exp.dll +# RUN: lld-link %basename_t.obj -guard:cf -guard:longjmp -entry:main -out:%basename_t-delay.exe %basename_t-exp.lib -alternatename:__delayLoadHelper2=main -delayload:%basename_t-exp.dll # RUN: llvm-readobj --file-headers --coff-load-config %basename_t-delay.exe | FileCheck %s --check-prefix DELAY-CHECK # DELAY-CHECK: ImageBase: 0x140000000 diff --git a/lld/test/COFF/guard-ehcont.s b/lld/test/COFF/guard-ehcont.s new file mode 100644 --- /dev/null +++ b/lld/test/COFF/guard-ehcont.s @@ -0,0 +1,224 @@ +# REQUIRES: x86 +# RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj +# RUN: lld-link %t.obj -guard:cf -guard:ehcont -out:%t.exe -entry:main +# RUN: llvm-readobj --file-headers --coff-load-config %t.exe | FileCheck %s + +# CHECK: ImageBase: 0x140000000 +# CHECK: LoadConfig [ +# CHECK: SEHandlerTable: 0x0 +# CHECK: SEHandlerCount: 0 +# CHECK: GuardCFCheckFunction: 0x0 +# CHECK: GuardCFCheckDispatch: 0x0 +# CHECK: GuardCFFunctionTable: 0x14000{{.*}} +# CHECK: GuardCFFunctionCount: 1 +# CHECK: GuardFlags: 0x400500 +# CHECK: GuardAddressTakenIatEntryTable: 0x0 +# CHECK: GuardAddressTakenIatEntryCount: 0 +# CHECK: GuardEHContinuationTable: 0x14000{{.*}} +# CHECK: GuardEHContinuationCount: 1 +# CHECK: ] +# CHECK: GuardEHContTable [ +# CHECK-NEXT: 0x14000{{.*}} +# CHECK-NEXT: ] + + +# This assembly is reduced from C code like: +# int main() +# { +# try { +# throw 3; +# } +# catch (int e) { +# return e != 3; +# } +# return 2; +# } + +# We need @feat.00 to have 0x4000 to indicate /guard:ehcont. + .def @feat.00; + .scl 3; + .type 0; + .endef + .globl @feat.00 +@feat.00 = 0x4000 + .def main; .scl 2; .type 32; .endef + .globl main # -- Begin function main + .p2align 4, 0x90 +main: # @main +.Lfunc_begin0: +.seh_proc main +.intel_syntax + .seh_handler __CxxFrameHandler3, @unwind, @except +# %bb.0: # %entry + push rbp + .seh_pushreg rbp + sub rsp, 64 + .seh_stackalloc 64 + lea rbp, [rsp + 64] + .seh_setframe rbp, 64 + .seh_endprologue + mov qword ptr [rbp - 16], -2 + mov dword ptr [rbp - 20], 0 + mov dword ptr [rbp - 24], 3 +.Ltmp0: + lea rdx, [rip + _TI1H] + lea rcx, [rbp - 24] + call _CxxThrowException +.Ltmp1: + jmp .LBB0_3 +.LBB0_2: # Block address taken + # %catchret.dest +$ehgcr_0_2: + mov eax, dword ptr [rbp - 20] + add rsp, 64 + pop rbp + ret +.LBB0_3: # %unreachable + int3 + .seh_handlerdata + .long ($cppxdata$main)@IMGREL + .text + .seh_endproc + .def "?catch$1@?0?main@4HA"; + .scl 3; + .type 32; + .endef + .p2align 4, 0x90 +"?catch$1@?0?main@4HA": +.seh_proc "?catch$1@?0?main@4HA" + .seh_handler __CxxFrameHandler3, @unwind, @except +.LBB0_1: # %catch + mov qword ptr [rsp + 16], rdx + push rbp + .seh_pushreg rbp + sub rsp, 32 + .seh_stackalloc 32 + lea rbp, [rdx + 64] + .seh_endprologue + mov eax, dword ptr [rbp - 4] + sub eax, 3 + setne al + movzx eax, al + mov dword ptr [rbp - 20], eax + lea rax, [rip + .LBB0_2] + add rsp, 32 + pop rbp + ret # CATCHRET + .def free; .scl 2; .type 32; .endef + .globl free +free: + ret + .def __CxxFrameHandler3; .scl 2; .type 32; .endef + .globl __CxxFrameHandler3 +__CxxFrameHandler3: + ret + .def _CxxThrowException; .scl 2; .type 32; .endef + .globl _CxxThrowException +_CxxThrowException: + ret +.Lfunc_end0: + .seh_handlerdata + .long ($cppxdata$main)@IMGREL + .text + .seh_endproc + .section .xdata,"dr" + .p2align 2 +$cppxdata$main: + .long 429065506 # MagicNumber + .long 2 # MaxState + .long ($stateUnwindMap$main)@IMGREL # UnwindMap + .long 1 # NumTryBlocks + .long ($tryMap$main)@IMGREL # TryBlockMap + .long 4 # IPMapEntries + .long ($ip2state$main)@IMGREL # IPToStateXData + .long 48 # UnwindHelp + .long 0 # ESTypeList + .long 1 # EHFlags +$stateUnwindMap$main: + .long -1 # ToState + .long 0 # Action + .long -1 # ToState + .long 0 # Action +$tryMap$main: + .long 0 # TryLow + .long 0 # TryHigh + .long 1 # CatchHigh + .long 1 # NumCatches + .long ($handlerMap$0$main)@IMGREL # HandlerArray +$handlerMap$0$main: + .long 0 # Adjectives + .long "??_R0H@8"@IMGREL # Type + .long 60 # CatchObjOffset + .long "?catch$1@?0?main@4HA"@IMGREL # Handler + .long 56 # ParentFrameOffset +$ip2state$main: + .long .Lfunc_begin0@IMGREL # IP + .long -1 # ToState + .long .Ltmp0@IMGREL+1 # IP + .long 0 # ToState + .long .Ltmp1@IMGREL+1 # IP + .long -1 # ToState + .long "?catch$1@?0?main@4HA"@IMGREL # IP + .long 1 # ToState + .text + # -- End function + .section .data,"dw",discard,"??_R0H@8" + .globl "??_R0H@8" # @"??_R0H@8" + .p2align 4 +"??_R0H@8": + .quad 0 + .quad 0 + .asciz ".H" + .zero 5 + + .section .xdata,"dr",discard,"_CT??_R0H@84" + .globl "_CT??_R0H@84" # @"_CT??_R0H@84" + .p2align 4 +"_CT??_R0H@84": + .long 1 # 0x1 + .long "??_R0H@8"@IMGREL + .long 0 # 0x0 + .long 4294967295 # 0xffffffff + .long 0 # 0x0 + .long 4 # 0x4 + .long 0 # 0x0 + + .section .xdata,"dr",discard,_CTA1H + .globl _CTA1H # @_CTA1H + .p2align 3 +_CTA1H: + .long 1 # 0x1 + .long "_CT??_R0H@84"@IMGREL + + .section .xdata,"dr",discard,_TI1H + .globl _TI1H # @_TI1H + .p2align 3 +_TI1H: + .long 0 # 0x0 + .long 0 # 0x0 + .long 0 # 0x0 + .long _CTA1H@IMGREL + + .section .gehcont$y,"dr" + .symidx $ehgcr_0_2 + .addrsig_sym _CxxThrowException + .addrsig_sym __CxxFrameHandler3 + .addrsig_sym "??_R0H@8" + .addrsig_sym __ImageBase + .section .rdata,"dr" +.globl _load_config_used +_load_config_used: + .long 312 + .fill 124, 1, 0 + .quad __guard_fids_table + .quad __guard_fids_count + .long __guard_flags + .fill 12, 1, 0 + .quad __guard_iat_table + .quad __guard_iat_count + .quad __guard_longjmp_table + .quad __guard_longjmp_count + .fill 72, 1, 0 + .quad __guard_eh_cont_table + .quad __guard_eh_cont_count + .fill 32, 1, 0 \ No newline at end of file diff --git a/lld/test/COFF/guard-longjmp.s b/lld/test/COFF/guard-longjmp.s --- a/lld/test/COFF/guard-longjmp.s +++ b/lld/test/COFF/guard-longjmp.s @@ -1,6 +1,6 @@ # REQUIRES: x86 # RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj -# RUN: lld-link %t.obj -guard:cf -out:%t.exe -entry:main +# RUN: lld-link %t.obj -guard:cf -guard:longjmp -out:%t.exe -entry:main # RUN: llvm-readobj --file-headers --coff-load-config %t.exe | FileCheck %s # CHECK: ImageBase: 0x140000000 @@ -100,5 +100,5 @@ .quad __guard_iat_table .quad __guard_iat_count .quad __guard_longjmp_table - .quad __guard_fids_count + .quad __guard_longjmp_count .fill 84, 1, 0 diff --git a/lld/test/COFF/guardcf-lto.ll b/lld/test/COFF/guardcf-lto.ll --- a/lld/test/COFF/guardcf-lto.ll +++ b/lld/test/COFF/guardcf-lto.ll @@ -8,7 +8,7 @@ ; RUN: llvm-mc -triple x86_64-windows-msvc -filetype=obj %S/Inputs/loadconfig-cfg-x64.s -o %t.ldcfg.obj ; RUN: llvm-as %s -o %t.bc -; RUN: lld-link -entry:main -guard:cf -dll %t.bc %t.lib %t.ldcfg.obj -out:%t2.dll +; RUN: lld-link -entry:main -guard:cf -guard:longjmp -dll %t.bc %t.lib %t.ldcfg.obj -out:%t2.dll ; RUN: llvm-readobj --coff-load-config %t2.dll | FileCheck %s ; There must be *two* entries in the table: DLL entry point, and my_handler. diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h --- a/llvm/include/llvm/Object/COFF.h +++ b/llvm/include/llvm/Object/COFF.h @@ -604,6 +604,7 @@ ProtectDelayLoadIAT = 0x00001000, DelayLoadIATSection = 0x00002000, // Delay load in separate section HasLongJmpTable = 0x00010000, + HasEHContTable = 0x00400000, FidTableHasFlags = 0x10000000, // Indicates that fid tables are 5 bytes }; @@ -661,6 +662,17 @@ support::ulittle16_t Reserved2; support::ulittle32_t GuardRFVerifyStackPointerFunctionPointer; support::ulittle32_t HotPatchTableOffset; + + // Added in MSVC 2019 + support::ulittle32_t Reserved3; + support::ulittle32_t EnclaveConfigurationPointer; + support::ulittle32_t VolatileMetadataPointer; + support::ulittle32_t GuardEHContinuationTable; + support::ulittle32_t GuardEHContinuationCount; + support::ulittle32_t GuardXFGCheckFunctionPointer; + support::ulittle32_t GuardXFGDispatchFunctionPointer; + support::ulittle32_t GuardXFGTableDispatchFunctionPointer; + support::ulittle32_t CastGuardOsDeterminedFailureMode; }; /// 64-bit load config (IMAGE_LOAD_CONFIG_DIRECTORY64) @@ -708,6 +720,17 @@ support::ulittle16_t Reserved2; support::ulittle64_t GuardRFVerifyStackPointerFunctionPointer; support::ulittle32_t HotPatchTableOffset; + + // Added in MSVC 2019 + support::ulittle32_t Reserved3; + support::ulittle64_t EnclaveConfigurationPointer; + support::ulittle64_t VolatileMetadataPointer; + support::ulittle64_t GuardEHContinuationTable; + support::ulittle64_t GuardEHContinuationCount; + support::ulittle64_t GuardXFGCheckFunctionPointer; + support::ulittle64_t GuardXFGDispatchFunctionPointer; + support::ulittle64_t GuardXFGTableDispatchFunctionPointer; + support::ulittle64_t CastGuardOsDeterminedFailureMode; }; struct coff_runtime_function_x64 { diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -71,6 +71,8 @@ uint64_t GuardIatTableCount = 0; uint64_t GuardLJmpTableVA = 0; uint64_t GuardLJmpTableCount = 0; + uint64_t GuardEHContTableVA = 0; + uint64_t GuardEHContTableCount = 0; }; class COFFDumper : public ObjDumper { @@ -793,19 +795,19 @@ printRVATable(Tables.SEHTableVA, Tables.SEHTableCount, 4); } + auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) { + uint8_t Flags = *reinterpret_cast(Entry + 4); + if (Flags) + OS << " flags " << utohexstr(Flags); + }; + if (Tables.GuardFidTableVA) { ListScope LS(W, "GuardFidTable"); - if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) { - auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) { - uint8_t Flags = *reinterpret_cast(Entry + 4); - if (Flags) - OS << " flags " << utohexstr(Flags); - }; + if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 5, PrintGuardFlags); - } else { + else printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 4); - } } if (Tables.GuardIatTableVA) { @@ -817,6 +819,12 @@ ListScope LS(W, "GuardLJmpTable"); printRVATable(Tables.GuardLJmpTableVA, Tables.GuardLJmpTableCount, 4); } + + if (Tables.GuardEHContTableVA) { + ListScope LS(W, "GuardEHContTable"); + printRVATable(Tables.GuardEHContTableVA, Tables.GuardEHContTableCount, 5, + PrintGuardFlags); + } } template @@ -875,8 +883,8 @@ Tables.GuardFidTableCount = Conf->GuardCFFunctionCount; Tables.GuardFlags = Conf->GuardFlags; - // Print the rest. (2017) - if (Conf->Size < sizeof(T)) + // Print everything before Reserved3. (2017) + if (Conf->Size < offsetof(T, Reserved3)) return; W.printHex("GuardAddressTakenIatEntryTable", Conf->GuardAddressTakenIatEntryTable); @@ -902,6 +910,17 @@ Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable; Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount; + + // Print the rest. (2019) + if (Conf->Size < sizeof(T)) + return; + W.printHex("EnclaveConfigurationPointer", Conf->EnclaveConfigurationPointer); + W.printHex("VolatileMetadataPointer", Conf->VolatileMetadataPointer); + W.printHex("GuardEHContinuationTable", Conf->GuardEHContinuationTable); + W.printNumber("GuardEHContinuationCount", Conf->GuardEHContinuationCount); + + Tables.GuardEHContTableVA = Conf->GuardEHContinuationTable; + Tables.GuardEHContTableCount = Conf->GuardEHContinuationCount; } void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) {