Index: lld/COFF/Config.h =================================================================== --- lld/COFF/Config.h +++ lld/COFF/Config.h @@ -211,6 +211,7 @@ uint32_t functionPadMin = 0; bool dynamicBase = true; bool allowBind = true; + bool cetCompat = false; bool nxCompat = true; bool allowIsolation = true; bool terminalServerAware = true; Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -1543,6 +1543,7 @@ !args.hasArg(OPT_profile)); config->integrityCheck = args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); + config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false); config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); for (auto *arg : args.filtered(OPT_swaprun)) parseSwaprun(arg->getValue()); Index: lld/COFF/Options.td =================================================================== --- lld/COFF/Options.td +++ lld/COFF/Options.td @@ -147,6 +147,8 @@ defm appcontainer : B<"appcontainer", "Image can only be run in an app container", "Image can run outside an app container (default)">; +defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack", + "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">; defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)", "Disable ASLR (default when /fixed)">; defm fixed : B<"fixed", "Disable base relocations", Index: lld/COFF/Writer.cpp =================================================================== --- lld/COFF/Writer.cpp +++ lld/COFF/Writer.cpp @@ -92,7 +92,7 @@ class DebugDirectoryChunk : public NonSectionChunk { public: - DebugDirectoryChunk(const std::vector &r, bool writeRepro) + DebugDirectoryChunk(const std::vector> &r, bool writeRepro) : records(r), writeRepro(writeRepro) {} size_t getSize() const override { @@ -102,11 +102,12 @@ void writeTo(uint8_t *b) const override { auto *d = reinterpret_cast(b); - for (const Chunk *record : records) { - OutputSection *os = record->getOutputSection(); - uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA()); - fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(), - record->getRVA(), offs); + for (const std::pair record : records) { + Chunk *c = record.second; + OutputSection *os = c->getOutputSection(); + uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA()); + fillEntry(d, record.first, c->getSize(), + c->getRVA(), offs); ++d; } @@ -141,7 +142,7 @@ } mutable std::vector timeDateStamps; - const std::vector &records; + const std::vector> &records; bool writeRepro; }; @@ -166,6 +167,22 @@ mutable codeview::DebugInfo *buildId = nullptr; }; +class ExtendedDllCharacteristicsChunk : public NonSectionChunk { +public: + ExtendedDllCharacteristicsChunk(unsigned C) + : Characteristics(C) {} + + size_t getSize() const override { + return sizeof(Characteristics); + } + + void writeTo(uint8_t *B) const override { + memcpy(B, &Characteristics, sizeof(ExtendedDLLCharacteristics)); + } + + uint32_t Characteristics = 0; +}; + // PartialSection represents a group of chunks that contribute to an // OutputSection. Collating a collection of PartialSections of same name and // characteristics constitutes the OutputSection. @@ -251,7 +268,7 @@ bool setNoSEHCharacteristic = false; DebugDirectoryChunk *debugDirectory = nullptr; - std::vector debugRecords; + std::vector> debugRecords; CVDebugRecordChunk *buildId = nullptr; ArrayRef sectionTable; @@ -921,8 +938,9 @@ // Create Debug Information Chunks OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; - if (config->debug || config->repro) { + if (config->debug || config->repro || config->cetCompat) { debugDirectory = make(debugRecords, config->repro); + debugDirectory->setAlignment(4); debugInfoSec->addChunk(debugDirectory); } @@ -932,10 +950,17 @@ // allowing a debugger to match a PDB and an executable. So we need it even // if we're ultimately not going to write CodeView data to the PDB. buildId = make(); - debugRecords.push_back(buildId); + debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId}); + } + + if (config->cetCompat) { + ExtendedDllCharacteristicsChunk *extendedDllChars = make(IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT); + debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars}); + } - for (Chunk *c : debugRecords) - debugInfoSec->addChunk(c); + if (debugRecords.size() > 0) { + for (std::pair r : debugRecords) + debugInfoSec->addChunk(r.second); } // Create SEH table. x86-only. Index: lld/test/COFF/options.test =================================================================== --- lld/test/COFF/options.test +++ lld/test/COFF/options.test @@ -50,6 +50,16 @@ # RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=NONXCOMPAT %s NONXCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT +# RUN: lld-link /out:%t.exe /entry:main /cetcompat %t.obj +# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CETCOMPAT %s +CETCOMPAT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT + +# RUN: lld-link /out:%t.exe /entry:main %t.obj +# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=NONCETCOMPAT %s +# RUN: lld-link /out:%t.exe /entry:main /cetcompat:no %t.obj +# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=NONCETCOMPAT %s +NONCETCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT + # RUN: lld-link /out:%t.exe /entry:main /swaprun:CD %t.obj # RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=SWAPCD %s # RUN: lld-link /out:%t.exe /entry:main /swaprun:cd,net %t.obj Index: llvm/include/llvm/BinaryFormat/COFF.h =================================================================== --- llvm/include/llvm/BinaryFormat/COFF.h +++ llvm/include/llvm/BinaryFormat/COFF.h @@ -642,6 +642,11 @@ IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 }; +enum ExtendedDLLCharacteristics : unsigned { + /// Image is CET compatible + IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT = 0x0001 +}; + enum DebugType : unsigned { IMAGE_DEBUG_TYPE_UNKNOWN = 0, IMAGE_DEBUG_TYPE_COFF = 1, @@ -660,6 +665,7 @@ IMAGE_DEBUG_TYPE_ILTCG = 14, IMAGE_DEBUG_TYPE_MPX = 15, IMAGE_DEBUG_TYPE_REPRO = 16, + IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20, }; enum BaseRelocationType : unsigned { Index: llvm/test/tools/llvm-readobj/coff-cet-compat.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-readobj/coff-cet-compat.test @@ -0,0 +1,19 @@ +RUN: llvm-readobj --coff-debug-directory %p/Inputs/has_cet.exe | FileCheck %s + +CHECK: DebugEntry { +CHECK: Characteristics: 0x0 +CHECK: TimeDateStamp: 2019-06-20 20:45:09 (0x5D0BF055) +CHECK: MajorVersion: 0x0 +CHECK: MinorVersion: 0x0 +CHECK: Type: ExtendedDLLCharacteristics (0x14) +CHECK: SizeOfData: 0x4 +CHECK: AddressOfRawData: 0x146A8 +CHECK: PointerToRawData: 0x138A8 +CHECK: ExtendedCharacteristics [ (0x1) +CHECK: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT (0x1) +CHECK: ] +CHECK: RawData ( +CHECK: 0000: 01000000 |....| +CHECK: ) +CHECK: } + Index: llvm/tools/llvm-readobj/COFFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/COFFDumper.cpp +++ llvm/tools/llvm-readobj/COFFDumper.cpp @@ -409,6 +409,10 @@ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE), }; +static const EnumEntry PEExtendedDLLCharacteristics[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT), +}; + static const EnumEntry ImageSectionCharacteristics[] = { LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD ), @@ -516,23 +520,24 @@ }; static const EnumEntry ImageDebugType[] = { - { "Unknown" , COFF::IMAGE_DEBUG_TYPE_UNKNOWN }, - { "COFF" , COFF::IMAGE_DEBUG_TYPE_COFF }, - { "CodeView" , COFF::IMAGE_DEBUG_TYPE_CODEVIEW }, - { "FPO" , COFF::IMAGE_DEBUG_TYPE_FPO }, - { "Misc" , COFF::IMAGE_DEBUG_TYPE_MISC }, - { "Exception" , COFF::IMAGE_DEBUG_TYPE_EXCEPTION }, - { "Fixup" , COFF::IMAGE_DEBUG_TYPE_FIXUP }, - { "OmapToSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC }, - { "OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC }, - { "Borland" , COFF::IMAGE_DEBUG_TYPE_BORLAND }, - { "Reserved10" , COFF::IMAGE_DEBUG_TYPE_RESERVED10 }, - { "CLSID" , COFF::IMAGE_DEBUG_TYPE_CLSID }, - { "VCFeature" , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE }, - { "POGO" , COFF::IMAGE_DEBUG_TYPE_POGO }, - { "ILTCG" , COFF::IMAGE_DEBUG_TYPE_ILTCG }, - { "MPX" , COFF::IMAGE_DEBUG_TYPE_MPX }, - { "Repro" , COFF::IMAGE_DEBUG_TYPE_REPRO }, + { "Unknown" , COFF::IMAGE_DEBUG_TYPE_UNKNOWN }, + { "COFF" , COFF::IMAGE_DEBUG_TYPE_COFF }, + { "CodeView" , COFF::IMAGE_DEBUG_TYPE_CODEVIEW }, + { "FPO" , COFF::IMAGE_DEBUG_TYPE_FPO }, + { "Misc" , COFF::IMAGE_DEBUG_TYPE_MISC }, + { "Exception" , COFF::IMAGE_DEBUG_TYPE_EXCEPTION }, + { "Fixup" , COFF::IMAGE_DEBUG_TYPE_FIXUP }, + { "OmapToSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC }, + { "OmapFromSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC }, + { "Borland" , COFF::IMAGE_DEBUG_TYPE_BORLAND }, + { "Reserved10" , COFF::IMAGE_DEBUG_TYPE_RESERVED10 }, + { "CLSID" , COFF::IMAGE_DEBUG_TYPE_CLSID }, + { "VCFeature" , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE }, + { "POGO" , COFF::IMAGE_DEBUG_TYPE_POGO }, + { "ILTCG" , COFF::IMAGE_DEBUG_TYPE_ILTCG }, + { "MPX" , COFF::IMAGE_DEBUG_TYPE_MPX }, + { "Repro" , COFF::IMAGE_DEBUG_TYPE_REPRO }, + { "ExtendedDLLCharacteristics", COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS }, }; static const EnumEntry @@ -736,12 +741,17 @@ W.printString("PDBFileName", PDBFileName); } } else if (D.SizeOfData != 0) { - // FIXME: Type values of 12 and 13 are commonly observed but are not in - // the documented type enum. Figure out what they mean. + // FIXME: Data visualization for IMAGE_DEBUG_TYPE_VC_FEATURE and + // IMAGE_DEBUG_TYPE_POGO? ArrayRef RawData; if (std::error_code EC = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, D.SizeOfData, RawData)) reportError(errorCodeToError(EC), Obj->getFileName()); + if (D.Type == COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) { + // FIXME right now the only possible value would fit in 8 bits, but that might change in the future + uint16_t Characteristics = RawData[0]; + W.printFlags ("ExtendedCharacteristics", Characteristics, makeArrayRef(PEExtendedDLLCharacteristics)); + } W.printBinaryBlock("RawData", RawData); } }