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 @@ -1996,6 +1996,8 @@ symtab->addAbsolute(mangle("__guard_iat_table"), 0); symtab->addAbsolute(mangle("__guard_longjmp_count"), 0); symtab->addAbsolute(mangle("__guard_longjmp_table"), 0); + symtab->addAbsolute(mangle("__guard_ehcont_count"), 0); + symtab->addAbsolute(mangle("__guard_ehcont_table"), 0); // Needed for MSVC 2017 15.5 CRT. symtab->addAbsolute(mangle("__enclave_config"), 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 @@ -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_ehcont_table", + "__guard_ehcont_count"); + // 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); } 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,14 @@ support::ulittle16_t Reserved2; support::ulittle32_t GuardRFVerifyStackPointerFunctionPointer; support::ulittle32_t HotPatchTableOffset; + + // Added in MSVC 2019 + // FIXME: Maybe not accurate. + support::ulittle32_t Reserved3; + support::ulittle32_t EnclaveConfigurationPointer; + support::ulittle32_t VolatileMetadataPointer; + support::ulittle32_t GuardEHContinuationTable; + support::ulittle32_t GuardEHContinuationCount; }; /// 64-bit load config (IMAGE_LOAD_CONFIG_DIRECTORY64) @@ -708,6 +717,14 @@ support::ulittle16_t Reserved2; support::ulittle64_t GuardRFVerifyStackPointerFunctionPointer; support::ulittle32_t HotPatchTableOffset; + + // Added in MSVC 2019 + // FIXME: Maybe not accurate. + support::ulittle32_t Reserved3; + support::ulittle64_t EnclaveConfigurationPointer; + support::ulittle64_t VolatileMetadataPointer; + support::ulittle64_t GuardEHContinuationTable; + support::ulittle64_t GuardEHContinuationCount; }; 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 @@ -896,12 +904,19 @@ W.printHex("GuardRFVerifyStackPointerFunctionPointer", Conf->GuardRFVerifyStackPointerFunctionPointer); W.printHex("HotPatchTableOffset", Conf->HotPatchTableOffset); + W.printHex("EnclaveConfigurationPointer", Conf->EnclaveConfigurationPointer); + W.printHex("VolatileMetadataPointer", Conf->VolatileMetadataPointer); + W.printHex("GuardEHContinuationTable", Conf->GuardEHContinuationTable); + W.printHex("GuardEHContinuationCount", Conf->GuardEHContinuationCount); Tables.GuardIatTableVA = Conf->GuardAddressTakenIatEntryTable; Tables.GuardIatTableCount = Conf->GuardAddressTakenIatEntryCount; Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable; Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount; + + Tables.GuardEHContTableVA = Conf->GuardEHContinuationTable; + Tables.GuardEHContTableCount = Conf->GuardEHContinuationCount; } void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) {