From 068a8c145ac387e4c3b8754b34b3d685b86cb7c5 Mon Sep 17 00:00:00 2001 From: Sasa Stankovic Date: Fri, 28 Feb 2014 10:00:38 +0000 Subject: [PATCH] [mips] Implement NaCl sandboxing of indirect jumps: * Align targets of indirect jumps to instruction bundle boundaries (in MI layer). * Add masking instructions before indirect jumps (in MC layer). Differential Revision: http://llvm-reviews.chandlerc.com/D2847 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@202479 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/MC/MCELFStreamer.h | 8 +- lib/Target/Mips/MCTargetDesc/MipsMCNaCl.h | 28 ++++++ .../Mips/MCTargetDesc/MipsMCTargetDesc.cpp | 11 ++- .../Mips/MCTargetDesc/MipsNaClELFStreamer.cpp | 97 +++++++++++++++++++ lib/Target/Mips/MipsAsmPrinter.cpp | 35 +++++++ lib/Target/Mips/MipsAsmPrinter.h | 2 + test/MC/Mips/nacl-align.ll | 96 ++++++++++++++++++ test/MC/Mips/nacl-mask.s | 28 ++++++ 8 files changed, 298 insertions(+), 7 deletions(-) create mode 100644 lib/Target/Mips/MCTargetDesc/MipsMCNaCl.h create mode 100644 lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp create mode 100644 test/MC/Mips/nacl-align.ll create mode 100644 test/MC/Mips/nacl-mask.s diff --git a/include/llvm/MC/MCELFStreamer.h b/include/llvm/MC/MCELFStreamer.h index f5704f0b868..46b32d0de6f 100644 --- a/include/llvm/MC/MCELFStreamer.h +++ b/include/llvm/MC/MCELFStreamer.h @@ -84,14 +84,14 @@ public: virtual void FinishImpl(); -private: - virtual void EmitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &); - virtual void EmitInstToData(const MCInst &Inst, const MCSubtargetInfo &); - virtual void EmitBundleAlignMode(unsigned AlignPow2); virtual void EmitBundleLock(bool AlignToEnd); virtual void EmitBundleUnlock(); +private: + virtual void EmitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &); + virtual void EmitInstToData(const MCInst &Inst, const MCSubtargetInfo &); + void fixSymbolsInTLSFixups(const MCExpr *expr); bool SeenIdent; diff --git a/lib/Target/Mips/MCTargetDesc/MipsMCNaCl.h b/lib/Target/Mips/MCTargetDesc/MipsMCNaCl.h new file mode 100644 index 00000000000..62d67d9f3f1 --- /dev/null +++ b/lib/Target/Mips/MCTargetDesc/MipsMCNaCl.h @@ -0,0 +1,28 @@ +//===-- MipsMCNaCl.h - NaCl-related declarations --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef MIPSMCNACL_H +#define MIPSMCNACL_H + +#include "llvm/MC/MCELFStreamer.h" + +namespace llvm { + +// Log2 of the NaCl MIPS sandbox's instruction bundle size. +static const unsigned MIPS_NACL_BUNDLE_ALIGN = 4u; + +// This function creates an MCELFStreamer for Mips NaCl. +MCELFStreamer *createMipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB, + raw_ostream &OS, + MCCodeEmitter *Emitter, + bool RelaxAll, bool NoExecStack); + +} + +#endif diff --git a/lib/Target/Mips/MCTargetDesc/MipsMCTargetDesc.cpp b/lib/Target/Mips/MCTargetDesc/MipsMCTargetDesc.cpp index faecde41b06..b663bf83405 100644 --- a/lib/Target/Mips/MCTargetDesc/MipsMCTargetDesc.cpp +++ b/lib/Target/Mips/MCTargetDesc/MipsMCTargetDesc.cpp @@ -11,9 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "MipsMCTargetDesc.h" #include "InstPrinter/MipsInstPrinter.h" #include "MipsMCAsmInfo.h" +#include "MipsMCNaCl.h" +#include "MipsMCTargetDesc.h" #include "MipsTargetStreamer.h" #include "llvm/ADT/Triple.h" #include "llvm/MC/MCCodeGenInfo.h" @@ -109,8 +110,12 @@ static MCStreamer *createMCStreamer(const Target &T, StringRef TT, raw_ostream &OS, MCCodeEmitter *Emitter, const MCSubtargetInfo &STI, bool RelaxAll, bool NoExecStack) { - MCStreamer *S = - createELFStreamer(Context, MAB, OS, Emitter, RelaxAll, NoExecStack); + MCStreamer *S; + if (!Triple(TT).isOSNaCl()) + S = createELFStreamer(Context, MAB, OS, Emitter, RelaxAll, NoExecStack); + else + S = createMipsNaClELFStreamer(Context, MAB, OS, Emitter, RelaxAll, + NoExecStack); new MipsTargetELFStreamer(*S, STI); return S; } diff --git a/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp b/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp new file mode 100644 index 00000000000..a442e5b31bb --- /dev/null +++ b/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp @@ -0,0 +1,97 @@ +//===-- MipsNaClELFStreamer.cpp - ELF Object Output for Mips NaCl ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements MCELFStreamer for Mips NaCl. It emits .o object files +// as required by NaCl's SFI sandbox. It inserts address-masking instructions +// before dangerous control-flow instructions. It aligns on bundle size all +// functions and all targets of indirect branches. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "mips-mc-nacl" + +#include "Mips.h" +#include "MipsMCNaCl.h" +#include "llvm/MC/MCELFStreamer.h" + +using namespace llvm; + +namespace { + +const unsigned IndirectBranchMaskReg = Mips::T6; + +/// Extend the generic MCELFStreamer class so that it can mask dangerous +/// instructions. + +class MipsNaClELFStreamer : public MCELFStreamer { +public: + MipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB, raw_ostream &OS, + MCCodeEmitter *Emitter) + : MCELFStreamer(Context, TAB, OS, Emitter) {} + + ~MipsNaClELFStreamer() {} + +private: + bool isIndirectJump(const MCInst &MI) { + return MI.getOpcode() == Mips::JR || MI.getOpcode() == Mips::RET; + } + + void emitMask(unsigned AddrReg, unsigned MaskReg, + const MCSubtargetInfo &STI) { + MCInst MaskInst; + MaskInst.setOpcode(Mips::AND); + MaskInst.addOperand(MCOperand::CreateReg(AddrReg)); + MaskInst.addOperand(MCOperand::CreateReg(AddrReg)); + MaskInst.addOperand(MCOperand::CreateReg(MaskReg)); + MCELFStreamer::EmitInstruction(MaskInst, STI); + } + + // Sandbox indirect branch or return instruction by inserting mask operation + // before it. + void sandboxIndirectJump(const MCInst &MI, const MCSubtargetInfo &STI) { + unsigned AddrReg = MI.getOperand(0).getReg(); + + EmitBundleLock(false); + emitMask(AddrReg, IndirectBranchMaskReg, STI); + MCELFStreamer::EmitInstruction(MI, STI); + EmitBundleUnlock(); + } + +public: + /// This function is the one used to emit instruction data into the ELF + /// streamer. We override it to mask dangerous instructions. + virtual void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) { + if (isIndirectJump(Inst)) + sandboxIndirectJump(Inst, STI); + else + MCELFStreamer::EmitInstruction(Inst, STI); + } +}; + +} // end anonymous namespace + +namespace llvm { + +MCELFStreamer *createMipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB, + raw_ostream &OS, + MCCodeEmitter *Emitter, bool RelaxAll, + bool NoExecStack) { + MipsNaClELFStreamer *S = new MipsNaClELFStreamer(Context, TAB, OS, Emitter); + if (RelaxAll) + S->getAssembler().setRelaxAll(true); + if (NoExecStack) + S->getAssembler().setNoExecStack(true); + + // Set bundle-alignment as required by the NaCl ABI for the target. + S->EmitBundleAlignMode(MIPS_NACL_BUNDLE_ALIGN); + + return S; +} + +} diff --git a/lib/Target/Mips/MipsAsmPrinter.cpp b/lib/Target/Mips/MipsAsmPrinter.cpp index 67c17a92f0f..d5df855a5ec 100644 --- a/lib/Target/Mips/MipsAsmPrinter.cpp +++ b/lib/Target/Mips/MipsAsmPrinter.cpp @@ -15,6 +15,7 @@ #define DEBUG_TYPE "mips-asm-printer" #include "InstPrinter/MipsInstPrinter.h" #include "MCTargetDesc/MipsBaseInfo.h" +#include "MCTargetDesc/MipsMCNaCl.h" #include "Mips.h" #include "MipsAsmPrinter.h" #include "MipsInstrInfo.h" @@ -27,6 +28,7 @@ #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineMemOperand.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/DataLayout.h" @@ -72,6 +74,11 @@ bool MipsAsmPrinter::runOnMachineFunction(MachineFunction &MF) { StubsNeeded[Symbol] = Signature; } MCP = MF.getConstantPool(); + + // In NaCl, all indirect jump targets must be aligned to bundle size. + if (Subtarget->isTargetNaCl()) + NaClAlignIndirectJumpTargets(MF); + AsmPrinter::runOnMachineFunction(MF); return true; } @@ -270,6 +277,12 @@ const char *MipsAsmPrinter::getCurrentABIString() const { void MipsAsmPrinter::EmitFunctionEntryLabel() { MipsTargetStreamer &TS = getTargetStreamer(); + + // NaCl sandboxing requires that indirect call instructions are masked. + // This means that function entry points should be bundle-aligned. + if (Subtarget->isTargetNaCl()) + EmitAlignment(std::max(MF->getAlignment(), MIPS_NACL_BUNDLE_ALIGN)); + if (Subtarget->inMicroMipsMode()) TS.emitDirectiveSetMicroMips(); // leave out until FSF available gas has micromips changes @@ -906,6 +919,28 @@ void MipsAsmPrinter::PrintDebugValueComment(const MachineInstr *MI, // TODO: implement } +// Align all targets of indirect branches on bundle size. Used only if target +// is NaCl. +void MipsAsmPrinter::NaClAlignIndirectJumpTargets(MachineFunction &MF) { + // Align all blocks that are jumped to through jump table. + if (MachineJumpTableInfo *JtInfo = MF.getJumpTableInfo()) { + const std::vector &JT = JtInfo->getJumpTables(); + for (unsigned I = 0; I < JT.size(); ++I) { + const std::vector &MBBs = JT[I].MBBs; + + for (unsigned J = 0; J < MBBs.size(); ++J) + MBBs[J]->setAlignment(MIPS_NACL_BUNDLE_ALIGN); + } + } + + // If basic block address is taken, block can be target of indirect branch. + for (MachineFunction::iterator MBB = MF.begin(), E = MF.end(); + MBB != E; ++MBB) { + if (MBB->hasAddressTaken()) + MBB->setAlignment(MIPS_NACL_BUNDLE_ALIGN); + } +} + // Force static initialization. extern "C" void LLVMInitializeMipsAsmPrinter() { RegisterAsmPrinter X(TheMipsTarget); diff --git a/lib/Target/Mips/MipsAsmPrinter.h b/lib/Target/Mips/MipsAsmPrinter.h index 2808b940da4..c99c6d357d0 100644 --- a/lib/Target/Mips/MipsAsmPrinter.h +++ b/lib/Target/Mips/MipsAsmPrinter.h @@ -73,6 +73,8 @@ private: void EmitFPCallStub(const char *, const Mips16HardFloatInfo::FuncSignature *); + void NaClAlignIndirectJumpTargets(MachineFunction &MF); + public: const MipsSubtarget *Subtarget; diff --git a/test/MC/Mips/nacl-align.ll b/test/MC/Mips/nacl-align.ll new file mode 100644 index 00000000000..6ec35473bc4 --- /dev/null +++ b/test/MC/Mips/nacl-align.ll @@ -0,0 +1,96 @@ +; RUN: llc -filetype=asm -mtriple=mipsel-none-nacl -relocation-model=static \ +; RUN: -O3 < %s + + +; This test tests that NaCl functions are bundle-aligned. + +define void @test0() { + ret void + +; CHECK: .align 4 +; CHECK-NOT: .align +; CHECK-LABEL: test0: + +} + + +; This test tests that blocks that are jumped to through jump table are +; bundle-aligned. + +define i32 @test1(i32 %i) { +entry: + switch i32 %i, label %default [ + i32 0, label %bb1 + i32 1, label %bb2 + i32 2, label %bb3 + i32 3, label %bb4 + ] + +bb1: + ret i32 111 +bb2: + ret i32 222 +bb3: + ret i32 333 +bb4: + ret i32 444 +default: + ret i32 555 + + +; CHECK-LABEL: test1: + +; CHECK: .align 4 +; CHECK-NEXT: ${{BB[0-9]+_[0-9]+}}: +; CHECK-NEXT: jr $ra +; CHECK-NEXT: addiu $2, $zero, 111 +; CHECK-NEXT: .align 4 +; CHECK-NEXT: ${{BB[0-9]+_[0-9]+}}: +; CHECK-NEXT: jr $ra +; CHECK-NEXT: addiu $2, $zero, 222 +; CHECK-NEXT: .align 4 +; CHECK-NEXT: ${{BB[0-9]+_[0-9]+}}: +; CHECK-NEXT: jr $ra +; CHECK-NEXT: addiu $2, $zero, 333 +; CHECK-NEXT: .align 4 +; CHECK-NEXT: ${{BB[0-9]+_[0-9]+}}: +; CHECK-NEXT: jr $ra +; CHECK-NEXT: addiu $2, $zero, 444 + +} + + +; This test tests that a block whose address is taken is bundle-aligned in NaCl. + +@bb_array = constant [2 x i8*] [i8* blockaddress(@test2, %bb1), + i8* blockaddress(@test2, %bb2)], align 4 + +define i32 @test2(i32 %i) { +entry: + %elementptr = getelementptr inbounds [2 x i8*]* @bb_array, i32 0, i32 %i + %0 = load i8** %elementptr, align 4 + indirectbr i8* %0, [label %bb1, label %bb2] + +bb1: + ret i32 111 +bb2: + ret i32 222 + + +; CHECK-LABEL: test2: + +; Note that there are two consecutive labels - one temporary and one for +; basic block. + +; CHECK: .align 4 +; CHECK-NEXT: ${{[a-zA-Z0-9]+}}: +; CHECK-NEXT: ${{BB[0-9]+_[0-9]+}}: +; CHECK-NEXT: jr $ra +; CHECK-NEXT: addiu $2, $zero, 111 +; CHECK-NEXT: .align 4 +; CHECK-NEXT: ${{[a-zA-Z0-9]+}}: +; CHECK-NEXT: ${{BB[0-9]+_[0-9]+}}: +; CHECK-NEXT: jr $ra +; CHECK-NEXT: addiu $2, $zero, 222 + +} diff --git a/test/MC/Mips/nacl-mask.s b/test/MC/Mips/nacl-mask.s new file mode 100644 index 00000000000..149412762ab --- /dev/null +++ b/test/MC/Mips/nacl-mask.s @@ -0,0 +1,28 @@ +# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-nacl %s \ +# RUN: | llvm-objdump -triple mipsel -disassemble -no-show-raw-insn - \ +# RUN: | FileCheck %s + +# This test tests that address-masking sandboxing is added when given assembly +# input. + +test1: + .set noreorder + + jr $a0 + nop + jr $ra + nop + +# CHECK-LABEL: test1: + +# CHECK: and $4, $4, $14 +# CHECK-NEXT: jr $4 + +# Check that additional nop is inserted, to align mask and jr to the next +# bundle. + +# CHECK-NEXT: nop +# CHECK-NEXT: nop + +# CHECK: and $ra, $ra, $14 +# CHECK-NEXT: jr $ra