summaryrefslogtreecommitdiffstats
path: root/llvm/lib
diff options
context:
space:
mode:
authorEvgeniy Stepanov <eugeni.stepanov@gmail.com>2016-11-11 18:49:09 +0000
committerEvgeniy Stepanov <eugeni.stepanov@gmail.com>2016-11-11 18:49:09 +0000
commitf48ffab554fa77306246eaac63970d53229b0c0d (patch)
tree6ba0f0313f89f1b277f04e6a72209dcd5d8fd51a /llvm/lib
parent6285f5b441bf602319c2bc3b4ed04a1450aa8247 (diff)
downloadbcm5719-llvm-f48ffab554fa77306246eaac63970d53229b0c0d.tar.gz
bcm5719-llvm-f48ffab554fa77306246eaac63970d53229b0c0d.zip
[cfi] Implement cfi-icall using inline assembly.
The current implementation is emitting a global constant that happens to evaluate to the same bytes + relocation as a jump instruction on X86. This does not work for PIE executables and shared libraries though, because we end up with a wrong relocation type. And it has no chance of working on ARM/AArch64 which use different relocation types for jump instructions (R_ARM_JUMP24) that is never generated for data. This change replaces the constant with module-level inline assembly followed by a hidden declaration of the jump table. Works fine for ARM/AArch64, but has some drawbacks. * Extra symbols are added to the static symbol table, which inflate the size of the unstripped binary a little. Stripped binaries are not affected. This happens because jump table declarations must be external (because their body is in the inline asm). * Original functions that were anonymous are now named <original name>.cfi, and it affects symbolization sometimes. This is necessary because the only user of these functions is the (inline asm) jump table, so they had to be added to @llvm.used, which does not allow unnamed functions. llvm-svn: 286611
Diffstat (limited to 'llvm/lib')
-rw-r--r--llvm/lib/Transforms/IPO/LowerTypeTests.cpp234
1 files changed, 159 insertions, 75 deletions
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 8f9d665ef05..8631886a281 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -24,6 +24,7 @@
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Mangler.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/Pass.h"
@@ -31,6 +32,7 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
using namespace lowertypetests;
@@ -225,6 +227,8 @@ class LowerTypeTestsModule {
std::vector<ByteArrayInfo> ByteArrayInfos;
+ Mangler Mang;
+
BitSetInfo
buildBitSet(Metadata *TypeId,
const DenseMap<GlobalObject *, uint64_t> &GlobalLayout);
@@ -243,12 +247,13 @@ class LowerTypeTestsModule {
ArrayRef<GlobalVariable *> Globals);
unsigned getJumpTableEntrySize();
Type *getJumpTableEntryType();
- Constant *createJumpTableEntry(GlobalObject *Src, Function *Dest,
- unsigned Distance);
+ void createJumpTableEntry(raw_ostream &OS, Function *Dest, unsigned Distance);
+ void createJumpTableAlias(raw_ostream &OS, Function *Dest,
+ GlobalVariable *JumpTable, unsigned Distance);
void verifyTypeMDNode(GlobalObject *GO, MDNode *Type);
void buildBitSetsFromFunctions(ArrayRef<Metadata *> TypeIds,
ArrayRef<Function *> Functions);
- void buildBitSetsFromFunctionsX86(ArrayRef<Metadata *> TypeIds,
+ void buildBitSetsFromFunctionsNative(ArrayRef<Metadata *> TypeIds,
ArrayRef<Function *> Functions);
void buildBitSetsFromFunctionsWASM(ArrayRef<Metadata *> TypeIds,
ArrayRef<Function *> Functions);
@@ -627,53 +632,101 @@ void LowerTypeTestsModule::verifyTypeMDNode(GlobalObject *GO, MDNode *Type) {
}
static const unsigned kX86JumpTableEntrySize = 8;
+static const unsigned kARMJumpTableEntrySize = 4;
unsigned LowerTypeTestsModule::getJumpTableEntrySize() {
- return kX86JumpTableEntrySize;
+ switch (Arch) {
+ case Triple::x86:
+ case Triple::x86_64:
+ return kX86JumpTableEntrySize;
+ case Triple::arm:
+ case Triple::aarch64:
+ return kARMJumpTableEntrySize;
+ default:
+ report_fatal_error("Unsupported architecture for jump tables");
+ }
+}
+
+static bool isValidAsmUnquotedName(StringRef Name) {
+ if (Name.empty())
+ return false;
+
+ for (char C : Name) {
+ if (!((C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z') ||
+ (C >= '0' && C <= '9') || C == '_' || C == '$' || C == '.' ||
+ C == '@'))
+ return false;
+ }
+
+ return true;
}
// Create a constant representing a jump table entry for the target. This
// consists of an instruction sequence containing a relative branch to Dest. The
// constant will be laid out at address Src+(Len*Distance) where Len is the
// target-specific jump table entry size.
-Constant *LowerTypeTestsModule::createJumpTableEntry(GlobalObject *Src,
- Function *Dest,
- unsigned Distance) {
- const unsigned kJmpPCRel32Code = 0xe9;
- const unsigned kInt3Code = 0xcc;
-
- ConstantInt *Jmp = ConstantInt::get(Int8Ty, kJmpPCRel32Code);
-
- // Build a constant representing the displacement between the constant's
- // address and Dest. This will resolve to a PC32 relocation referring to Dest.
- Constant *DestInt = ConstantExpr::getPtrToInt(Dest, IntPtrTy);
- Constant *SrcInt = ConstantExpr::getPtrToInt(Src, IntPtrTy);
- Constant *Disp = ConstantExpr::getSub(DestInt, SrcInt);
- ConstantInt *DispOffset =
- ConstantInt::get(IntPtrTy, Distance * kX86JumpTableEntrySize + 5);
- Constant *OffsetedDisp = ConstantExpr::getSub(Disp, DispOffset);
- OffsetedDisp = ConstantExpr::getTruncOrBitCast(OffsetedDisp, Int32Ty);
-
- ConstantInt *Int3 = ConstantInt::get(Int8Ty, kInt3Code);
-
- Constant *Fields[] = {
- Jmp, OffsetedDisp, Int3, Int3, Int3,
- };
- return ConstantStruct::getAnon(Fields, /*Packed=*/true);
+void LowerTypeTestsModule::createJumpTableEntry(raw_ostream &OS, Function *Dest,
+ unsigned Distance) {
+ // FIXME: replace IR Mangler with TargetLoweringObjectFile interface.
+ // A private instance of Mangler we use here can not deal with unnamed
+ // symbols, as it may create colliding labels. Thankfully(?), the use of
+ // inline asm requires us to give names to all affected functions anyway.
+ assert(Dest->hasName() && "jumptable targets can not be anonymous");
+ SmallString<16> Name;
+ Mang.getNameWithPrefix(Name, Dest, /* CannotUsePrivateLabel */ false);
+
+ if (!isValidAsmUnquotedName(Name)) {
+ // We are going to emit a function call as textual asm. Escaped strings
+ // in such expressions are not well supported.
+ report_fatal_error(
+ "CFI-ICall does not allow special characters in a function name.");
+ }
+
+ if (Arch == Triple::x86 || Arch == Triple::x86_64) {
+ OS << "jmp " << Name << "@plt\n";
+ OS << "int3\nint3\nint3\n";
+ } else if (Arch == Triple::arm || Arch == Triple::aarch64) {
+ OS << "b " << Name << "\n";
+ } else {
+ report_fatal_error("Unsupported architecture for jump tables");
+ }
+}
+
+void LowerTypeTestsModule::createJumpTableAlias(raw_ostream &OS, Function *Dest,
+ GlobalVariable *JumpTable,
+ unsigned Distance) {
+ assert(Dest->hasName() && "jumptable targets can not be anonymous");
+ SmallString<16> Name;
+ Mang.getNameWithPrefix(Name, Dest, /* CannotUsePrivateLabel */ false);
+
+ if (!isValidAsmUnquotedName(Name)) {
+ // We are going to emit a function alias as textual asm. Escaped strings
+ // in such expressions are not well supported.
+ report_fatal_error(
+ "CFI-ICall does not allow special characters in a function name.");
+ }
+
+ if (Dest->isWeakForLinker())
+ OS << ".weak " << Name << "\n";
+ else if (!Dest->hasLocalLinkage())
+ OS << ".globl " << Name << "\n";
+ OS << ".type " << Name << ", function\n";
+ OS << Name << " = " << JumpTable->getName() << " + "
+ << (getJumpTableEntrySize() * Distance) << "\n";
+ OS << ".size " << Name << ", " << getJumpTableEntrySize() << "\n";
}
Type *LowerTypeTestsModule::getJumpTableEntryType() {
- return StructType::get(M.getContext(),
- {Int8Ty, Int32Ty, Int8Ty, Int8Ty, Int8Ty},
- /*Packed=*/true);
+ return ArrayType::get(Int8Ty, getJumpTableEntrySize());
}
/// Given a disjoint set of type identifiers and functions, build the bit sets
/// and lower the llvm.type.test calls, architecture dependently.
void LowerTypeTestsModule::buildBitSetsFromFunctions(
ArrayRef<Metadata *> TypeIds, ArrayRef<Function *> Functions) {
- if (Arch == Triple::x86 || Arch == Triple::x86_64)
- buildBitSetsFromFunctionsX86(TypeIds, Functions);
+ if (Arch == Triple::x86 || Arch == Triple::x86_64 || Arch == Triple::arm ||
+ Arch == Triple::aarch64)
+ buildBitSetsFromFunctionsNative(TypeIds, Functions);
else if (Arch == Triple::wasm32 || Arch == Triple::wasm64)
buildBitSetsFromFunctionsWASM(TypeIds, Functions);
else
@@ -682,7 +735,7 @@ void LowerTypeTestsModule::buildBitSetsFromFunctions(
/// Given a disjoint set of type identifiers and functions, build a jump table
/// for the functions, build the bit sets and lower the llvm.type.test calls.
-void LowerTypeTestsModule::buildBitSetsFromFunctionsX86(
+void LowerTypeTestsModule::buildBitSetsFromFunctionsNative(
ArrayRef<Metadata *> TypeIds, ArrayRef<Function *> Functions) {
// Unlike the global bitset builder, the function bitset builder cannot
// re-arrange functions in a particular order and base its calculations on the
@@ -717,39 +770,35 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsX86(
// mov h, %ecx
// ret
//
- // To create a jump table for these functions, we instruct the LLVM code
- // generator to output a jump table in the .text section. This is done by
- // representing the instructions in the jump table as an LLVM constant and
- // placing them in a global variable in the .text section. The end result will
- // (conceptually) look like this:
+ // We output the jump table as module-level inline asm string. The end result
+ // will (conceptually) look like this:
//
- // f:
- // jmp .Ltmp0 ; 5 bytes
+ // f = .cfi.jumptable
+ // g = .cfi.jumptable + 4
+ // h = .cfi.jumptable + 8
+ // .cfi.jumptable:
+ // jmp f.cfi ; 5 bytes
// int3 ; 1 byte
// int3 ; 1 byte
// int3 ; 1 byte
- //
- // g:
- // jmp .Ltmp1 ; 5 bytes
+ // jmp g.cfi ; 5 bytes
// int3 ; 1 byte
// int3 ; 1 byte
// int3 ; 1 byte
- //
- // h:
- // jmp .Ltmp2 ; 5 bytes
+ // jmp h.cfi ; 5 bytes
// int3 ; 1 byte
// int3 ; 1 byte
// int3 ; 1 byte
//
- // .Ltmp0:
+ // f.cfi:
// mov 0, %eax
// ret
//
- // .Ltmp1:
+ // g.cfi:
// mov 1, %eax
// ret
//
- // .Ltmp2:
+ // h.cfi:
// mov 2, %eax
// ret
//
@@ -763,6 +812,8 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsX86(
// normal case the check can be carried out using the same kind of simple
// arithmetic that we normally use for globals.
+ // FIXME: find a better way to represent the jumptable in the IR.
+
assert(!Functions.empty());
// Build a simple layout based on the regular layout of jump tables.
@@ -774,45 +825,78 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsX86(
// Create a constant to hold the jump table.
ArrayType *JumpTableType =
ArrayType::get(getJumpTableEntryType(), Functions.size());
- auto JumpTable = new GlobalVariable(M, JumpTableType,
- /*isConstant=*/true,
- GlobalValue::PrivateLinkage, nullptr);
- JumpTable->setSection(ObjectFormat == Triple::MachO
- ? "__TEXT,__text,regular,pure_instructions"
- : ".text");
+ auto JumpTable =
+ new GlobalVariable(M, JumpTableType,
+ /*isConstant=*/true, GlobalValue::ExternalLinkage,
+ nullptr, ".cfi.jumptable");
+ JumpTable->setVisibility(GlobalValue::HiddenVisibility);
lowerTypeTestCalls(TypeIds, JumpTable, GlobalLayout);
+ std::string AsmStr;
+ raw_string_ostream AsmOS(AsmStr);
+
// Build aliases pointing to offsets into the jump table, and replace
// references to the original functions with references to the aliases.
for (unsigned I = 0; I != Functions.size(); ++I) {
- Constant *CombinedGlobalElemPtr = ConstantExpr::getBitCast(
- ConstantExpr::getGetElementPtr(
- JumpTableType, JumpTable,
- ArrayRef<Constant *>{ConstantInt::get(IntPtrTy, 0),
- ConstantInt::get(IntPtrTy, I)}),
- Functions[I]->getType());
+ // Need a name for the asm label. Normally, unnamed functions get temporary
+ // asm labels in TargetLoweringObjectFile but we don't have access to that
+ // here.
+ if (!Functions[I]->hasName())
+ Functions[I]->setName("unnamed");
if (LinkerSubsectionsViaSymbols || Functions[I]->isDeclarationForLinker()) {
+ Constant *CombinedGlobalElemPtr = ConstantExpr::getBitCast(
+ ConstantExpr::getGetElementPtr(
+ JumpTableType, JumpTable,
+ ArrayRef<Constant *>{ConstantInt::get(IntPtrTy, 0),
+ ConstantInt::get(IntPtrTy, I)}),
+ Functions[I]->getType());
Functions[I]->replaceAllUsesWith(CombinedGlobalElemPtr);
+
+ if (Functions[I]->isWeakForLinker())
+ AsmOS << ".weak " << Functions[I]->getName() << "\n";
} else {
assert(Functions[I]->getType()->getAddressSpace() == 0);
- GlobalAlias *GAlias = GlobalAlias::create(Functions[I]->getValueType(), 0,
- Functions[I]->getLinkage(), "",
- CombinedGlobalElemPtr, &M);
- GAlias->setVisibility(Functions[I]->getVisibility());
- GAlias->takeName(Functions[I]);
- Functions[I]->replaceAllUsesWith(GAlias);
+
+ createJumpTableAlias(AsmOS, Functions[I], JumpTable, I);
+
+ Function *DeclAlias =
+ Function::Create(cast<FunctionType>(Functions[I]->getValueType()),
+ GlobalValue::ExternalLinkage, "", &M);
+ // Since the alias (DeclAlias) is actually a declaration, it can not have
+ // internal linkage. Compensate for that by giving it hidden visibility.
+ // With this we end up with a GOT relocation against a local symbol.
+ DeclAlias->setVisibility(Functions[I]->hasLocalLinkage()
+ ? GlobalValue::HiddenVisibility
+ : Functions[I]->getVisibility());
+ DeclAlias->takeName(Functions[I]);
+ // Unnamed functions can not be added to llvm.used.
+ Functions[I]->setName(DeclAlias->getName() + ".cfi");
+ Functions[I]->replaceAllUsesWith(DeclAlias);
}
if (!Functions[I]->isDeclarationForLinker())
- Functions[I]->setLinkage(GlobalValue::PrivateLinkage);
+ Functions[I]->setLinkage(GlobalValue::InternalLinkage);
}
- // Build and set the jump table's initializer.
- std::vector<Constant *> JumpTableEntries;
+ // Try to emit the jump table at the end of the text segment.
+ // Jump table must come after __cfi_check in the cross-dso mode.
+ // FIXME: this magic section name seems to do the trick.
+ AsmOS << ".section " << (ObjectFormat == Triple::MachO
+ ? "__TEXT,__text,regular,pure_instructions"
+ : ".text.cfi, \"ax\", @progbits")
+ << "\n";
+ // Align the whole table by entry size.
+ AsmOS << ".balign " << EntrySize << "\n";
+ AsmOS << JumpTable->getName() << ":\n";
for (unsigned I = 0; I != Functions.size(); ++I)
- JumpTableEntries.push_back(
- createJumpTableEntry(JumpTable, Functions[I], I));
- JumpTable->setInitializer(
- ConstantArray::get(JumpTableType, JumpTableEntries));
+ createJumpTableEntry(AsmOS, Functions[I], I);
+
+ M.appendModuleInlineAsm(AsmOS.str());
+
+ SmallVector<GlobalValue *, 16> Used;
+ Used.reserve(Functions.size());
+ for (auto *F : Functions)
+ Used.push_back(F);
+ appendToUsed(M, Used);
}
/// Assign a dummy layout using an incrementing counter, tag each function
OpenPOWER on IntegriCloud