[LLD] Implement /guard:[no]ehcont

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D99078

GitOrigin-RevId: 184377da5c7ce624adf09341360c6cf8f31ebe8f
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 14d0a5a..083742d 100644
--- a/COFF/Chunks.cpp
+++ b/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<RVAFlag *>(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/COFF/Chunks.h b/COFF/Chunks.h
index e076d8e..49ee8b2 100644
--- a/COFF/Chunks.h
+++ b/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/COFF/Config.h b/COFF/Config.h
index bde7b5b..71ba27e 100644
--- a/COFF/Config.h
+++ b/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/COFF/Driver.cpp b/COFF/Driver.cpp
index cf96ecb..072af2b 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -2008,6 +2008,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/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 4c79113..0b01a12 100644
--- a/COFF/DriverUtils.cpp
+++ b/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/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index 37f6613..c7b75ba 100644
--- a/COFF/InputFiles.cpp
+++ b/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/COFF/InputFiles.h b/COFF/InputFiles.h
index 3fa6819..644369f 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -146,6 +146,7 @@
   ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; }
   ArrayRef<SectionChunk *> getGuardIATChunks() { return guardIATChunks; }
   ArrayRef<SectionChunk *> getGuardLJmpChunks() { return guardLJmpChunks; }
+  ArrayRef<SectionChunk *> getGuardEHContChunks() { return guardEHContChunks; }
   ArrayRef<Symbol *> getSymbols() { return symbols; }
 
   MutableArrayRef<Symbol *> 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<SectionChunk *> 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<SectionChunk *> guardFidChunks;
   std::vector<SectionChunk *> guardIATChunks;
   std::vector<SectionChunk *> guardLJmpChunks;
+  std::vector<SectionChunk *> 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/COFF/Writer.cpp b/COFF/Writer.cpp
index 344e83f..09c456e 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -231,7 +231,7 @@
                               ArrayRef<SectionChunk *> symIdxChunks,
                               std::vector<Symbol *> &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<Symbol *> 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<DefinedAbsolute>(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<RVATableChunk>(std::move(tableSymbols));
+  NonSectionChunk *tableChunk;
+  if (hasFlag)
+    tableChunk = make<RVAFlagTableChunk>(std::move(tableSymbols));
+  else
+    tableChunk = make<RVATableChunk>(std::move(tableSymbols));
   rdataSec->addChunk(tableChunk);
 
   Symbol *t = symtab->findUnderscore(tableSym);
   Symbol *c = symtab->findUnderscore(countSym);
   replaceSymbol<DefinedSynthetic>(t, t->getName(), tableChunk);
-  cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / 4);
+  cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));
 }
 
 // MinGW specific. Gather all relocations that are imported from a DLL even
diff --git a/test/COFF/gfids-corrupt.s b/test/COFF/gfids-corrupt.s
index a571f2f..159f9c9 100644
--- a/test/COFF/gfids-corrupt.s
+++ b/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/test/COFF/gfids-fallback.s b/test/COFF/gfids-fallback.s
index be94e2e..4b23f62 100644
--- a/test/COFF/gfids-fallback.s
+++ b/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/test/COFF/gfids-gc.s b/test/COFF/gfids-gc.s
index cf4b5fd..fb63c95 100644
--- a/test/COFF/gfids-gc.s
+++ b/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/test/COFF/gfids-icf.s b/test/COFF/gfids-icf.s
index 7cf7dda..afd0462 100644
--- a/test/COFF/gfids-icf.s
+++ b/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/test/COFF/giats.s b/test/COFF/giats.s
index fe4e33b..4a61472 100644
--- a/test/COFF/giats.s
+++ b/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/test/COFF/guard-ehcont.s b/test/COFF/guard-ehcont.s
new file mode 100644
index 0000000..2177143
--- /dev/null
+++ b/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/test/COFF/guard-longjmp.s b/test/COFF/guard-longjmp.s
index 51ca414..dbef819 100644
--- a/test/COFF/guard-longjmp.s
+++ b/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/test/COFF/guardcf-lto.ll b/test/COFF/guardcf-lto.ll
index 7811f44..5917044 100644
--- a/test/COFF/guardcf-lto.ll
+++ b/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.