1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 12:41:49 +01:00

[CFGuard] Add address-taken IAT tables and delay-load support

This patch adds support for creating Guard Address-Taken IAT Entry Tables (.giats$y sections) in object files, matching the behavior of MSVC. These contain lists of address-taken imported functions, which are used by the linker to create the final GIATS table.
Additionally, if any DLLs are delay-loaded, the linker must look through the .giats tables and add the respective load thunks of address-taken imports to the GFIDS table, as these are also valid call targets.

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D87544
This commit is contained in:
Andrew Paverd 2020-11-17 18:02:13 -08:00 committed by Arthur Eubanks
parent a5a991435e
commit adbf373fb6
6 changed files with 102 additions and 10 deletions

View File

@ -215,6 +215,7 @@ protected:
MCSection *XDataSection = nullptr;
MCSection *SXDataSection = nullptr;
MCSection *GFIDsSection = nullptr;
MCSection *GIATsSection = nullptr;
MCSection *GLJMPSection = nullptr;
// XCOFF specific sections
@ -397,6 +398,7 @@ public:
MCSection *getXDataSection() const { return XDataSection; }
MCSection *getSXDataSection() const { return SXDataSection; }
MCSection *getGFIDsSection() const { return GFIDsSection; }
MCSection *getGIATsSection() const { return GIATsSection; }
MCSection *getGLJMPSection() const { return GLJMPSection; }
// XCOFF specific sections

View File

@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
//
// This file contains support for writing the metadata for Windows Control Flow
// Guard, including address-taken functions, and valid longjmp targets.
// Guard, including address-taken functions and valid longjmp targets.
//
//===----------------------------------------------------------------------===//
@ -17,8 +17,8 @@
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Metadata.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCStreamer.h"
@ -78,20 +78,50 @@ static bool isPossibleIndirectCallTarget(const Function *F) {
return false;
}
MCSymbol *WinCFGuard::lookupImpSymbol(const MCSymbol *Sym) {
if (Sym->getName().startswith("__imp_"))
return nullptr;
return Asm->OutContext.lookupSymbol(Twine("__imp_") + Sym->getName());
}
void WinCFGuard::endModule() {
const Module *M = Asm->MMI->getModule();
std::vector<const Function *> Functions;
for (const Function &F : *M)
if (isPossibleIndirectCallTarget(&F))
Functions.push_back(&F);
if (Functions.empty() && LongjmpTargets.empty())
std::vector<const MCSymbol *> GFIDsEntries;
std::vector<const MCSymbol *> GIATsEntries;
for (const Function &F : *M) {
if (isPossibleIndirectCallTarget(&F)) {
// If F is a dllimport and has an "__imp_" symbol already defined, add the
// "__imp_" symbol to the .giats section.
if (F.hasDLLImportStorageClass()) {
if (MCSymbol *impSym = lookupImpSymbol(Asm->getSymbol(&F))) {
GIATsEntries.push_back(impSym);
}
}
// Add the function's symbol to the .gfids section.
// Note: For dllimport functions, MSVC sometimes does not add this symbol
// to the .gfids section, but only adds the corresponding "__imp_" symbol
// to the .giats section. Here we always add the symbol to the .gfids
// section, since this does not introduce security risks.
GFIDsEntries.push_back(Asm->getSymbol(&F));
}
}
if (GFIDsEntries.empty() && GIATsEntries.empty() && LongjmpTargets.empty())
return;
// Emit the symbol index of each GFIDs entry to form the .gfids section.
auto &OS = *Asm->OutStreamer;
OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGFIDsSection());
for (const Function *F : Functions)
OS.EmitCOFFSymbolIndex(Asm->getSymbol(F));
for (const MCSymbol *S : GFIDsEntries)
OS.EmitCOFFSymbolIndex(S);
// Emit the symbol index of each longjmp target.
// Emit the symbol index of each GIATs entry to form the .giats section.
OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGIATsSection());
for (const MCSymbol *S : GIATsEntries) {
OS.EmitCOFFSymbolIndex(S);
}
// Emit the symbol index of each longjmp target to form the .gljmp section.
OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGLJMPSection());
for (const MCSymbol *S : LongjmpTargets) {
OS.EmitCOFFSymbolIndex(S);

View File

@ -24,6 +24,7 @@ class LLVM_LIBRARY_VISIBILITY WinCFGuard : public AsmPrinterHandler {
/// Target of directive emission.
AsmPrinter *Asm;
std::vector<const MCSymbol *> LongjmpTargets;
MCSymbol *lookupImpSymbol(const MCSymbol *Sym);
public:
WinCFGuard(AsmPrinter *A);

View File

@ -754,6 +754,11 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) {
COFF::IMAGE_SCN_MEM_READ,
SectionKind::getMetadata());
GIATsSection = Ctx->getCOFFSection(".giats$y",
COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ,
SectionKind::getMetadata());
GLJMPSection = Ctx->getCOFFSection(".gljmp$y",
COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ,

View File

@ -0,0 +1,44 @@
; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s
; Control Flow Guard is currently only available on Windows
declare dllimport i32 @target_func1()
declare dllimport i32 @target_func2()
declare dllimport i32 @target_func3()
@ptrs = dso_local local_unnamed_addr global [2 x void ()*] [void ()* bitcast (i32 ()* @target_func2 to void ()*), void ()* bitcast (i32 ()* @target_func3 to void ()*)], align 16
; Test address-taken functions from imported DLLs are correctly added to the
; Guard Address-Taken IAT Entry (.giats) and Guard Function ID (.gfids) sections.
define i32 @func_cf_giats1() {
entry:
; Since it is a dllimport, target_func1 will be represented as "__imp_target_func1" when it is
; stored in the function pointer. Therefore, the .giats section must contain "__imp_target_func1".
; Unlike MSVC, we also have "target_func1" in the .gfids section, since this is not a security risk.
%func_ptr = alloca i32 ()*, align 8
store i32 ()* @target_func1, i32 ()** %func_ptr, align 8
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
%1 = call i32 %0()
; target_func2 is called directly from a global array, so should only appear in the .gfids section.
%2 = load i32 ()*, i32 ()** bitcast ([2 x void ()*]* @ptrs to i32 ()**), align 8
%3 = call i32 %2()
; target_func3 is called both via a stored function pointer (as with target_func1) and via a gloabl
; array (as with target_func2), so "target_func3" must appear in .gfids and "__imp_target_func3" in .giats.
store i32 ()* @target_func3, i32 ()** %func_ptr, align 8
%4 = load i32 ()*, i32 ()** %func_ptr, align 8
%5 = call i32 %4()
%6 = load i32 ()*, i32 ()** bitcast (void ()** getelementptr inbounds ([2 x void ()*], [2 x void ()*]* @ptrs, i64 0, i64 1) to i32 ()**), align 8
%7 = call i32 %6()
ret i32 %5
}
; CHECK-LABEL: .section .gfids$y,"dr"
; CHECK-NEXT: .symidx target_func1
; CHECK-NEXT: .symidx target_func2
; CHECK-NEXT: .symidx target_func3
; CHECK-NOT: .symidx
; CHECK-LABEL: .section .giats$y,"dr"
; CHECK-NEXT: .symidx __imp_target_func1
; CHECK-NEXT: .symidx __imp_target_func3
; CHECK-NOT: .symidx
!llvm.module.flags = !{!0}
!0 = !{i32 2, !"cfguard", i32 2}

View File

@ -67,6 +67,8 @@ struct LoadConfigTables {
uint32_t GuardFlags = 0;
uint64_t GuardFidTableVA = 0;
uint64_t GuardFidTableCount = 0;
uint64_t GuardIatTableVA = 0;
uint64_t GuardIatTableCount = 0;
uint64_t GuardLJmpTableVA = 0;
uint64_t GuardLJmpTableCount = 0;
};
@ -807,6 +809,11 @@ void COFFDumper::printCOFFLoadConfig() {
}
}
if (Tables.GuardIatTableVA) {
ListScope LS(W, "GuardIatTable");
printRVATable(Tables.GuardIatTableVA, Tables.GuardIatTableCount, 4);
}
if (Tables.GuardLJmpTableVA) {
ListScope LS(W, "GuardLJmpTable");
printRVATable(Tables.GuardLJmpTableVA, Tables.GuardLJmpTableCount, 4);
@ -891,6 +898,9 @@ void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) {
Conf->GuardRFVerifyStackPointerFunctionPointer);
W.printHex("HotPatchTableOffset", Conf->HotPatchTableOffset);
Tables.GuardIatTableVA = Conf->GuardAddressTakenIatEntryTable;
Tables.GuardIatTableCount = Conf->GuardAddressTakenIatEntryCount;
Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable;
Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount;
}