Index: lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp =================================================================== --- lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp +++ lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp @@ -12,7 +12,8 @@ // before dangerous control-flow and memory access instructions. It inserts // address-masking instructions after instructions that change the stack // pointer. It ensures that the mask and the dangerous instruction are always -// emitted in the same bundle. +// emitted in the same bundle. It aligns call + branch delay to the bundle end, +// so that return address is always aligned to the start of next bundle. // //===----------------------------------------------------------------------===// @@ -36,11 +37,15 @@ public: MipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB, raw_ostream &OS, MCCodeEmitter *Emitter) - : MCELFStreamer(Context, TAB, OS, Emitter) {} + : MCELFStreamer(Context, TAB, OS, Emitter), SandboxingCall(false) {} ~MipsNaClELFStreamer() {} private: + MCInst Call; + bool SandboxingCall; + bool IsIndirectCall; + bool isIndirectJump(const MCInst &MI) { return MI.getOpcode() == Mips::JR || MI.getOpcode() == Mips::RET; } @@ -50,6 +55,25 @@ && MI.getOperand(0).getReg() == Mips::SP); } + bool isCall(unsigned Opcode, bool *IsIndirectCall) { + *IsIndirectCall = false; + + switch (Opcode) { + default: + return false; + + case Mips::JAL: + case Mips::BAL_BR: + case Mips::BLTZAL: + case Mips::BGEZAL: + return true; + + case Mips::JALR: + *IsIndirectCall = true; + return true; + } + } + void emitMask(unsigned AddrReg, unsigned MaskReg, const MCSubtargetInfo &STI) { MCInst MaskInst; @@ -92,6 +116,20 @@ EmitBundleUnlock(); } + // Sandbox calls by aligning call and branch delay to the bundle end. + // For indirect calls, emit the mask before the call. + void sandboxCall(const MCInst &Call, const MCInst &BranchDelay, + const MCSubtargetInfo &STI, bool IsIndirectCall) { + EmitBundleLock(true); + if (IsIndirectCall) { + unsigned TargetReg = Call.getOperand(1).getReg(); + emitMask(TargetReg, IndirectBranchMaskReg, STI); + } + MCELFStreamer::EmitInstruction(Call, STI); + MCELFStreamer::EmitInstruction(BranchDelay, STI); + EmitBundleUnlock(); + } + public: /// This function is the one used to emit instruction data into the ELF /// streamer. We override it to mask dangerous instructions. @@ -120,6 +158,17 @@ return; } + // Sandbox calls. + if (SandboxingCall) { + sandboxCall(Call, Inst, STI, IsIndirectCall); + SandboxingCall = false; + return; + } else if (isCall(Inst.getOpcode(), &IsIndirectCall)) { + Call = Inst; + SandboxingCall = true; + return; + } + // None of the sandboxing applies, just emit the instruction. MCELFStreamer::EmitInstruction(Inst, STI); } Index: test/MC/Mips/nacl-mask.s =================================================================== --- test/MC/Mips/nacl-mask.s +++ test/MC/Mips/nacl-mask.s @@ -9,6 +9,7 @@ # Test that address-masking sandboxing is added before indirect branches and # returns. + .align 4 test1: .set noreorder @@ -35,6 +36,7 @@ # Test that address-masking sandboxing is added before load instructions. + .align 4 test2: .set noreorder @@ -104,6 +106,7 @@ # Test that address-masking sandboxing is added before store instructions. + .align 4 test3: .set noreorder @@ -166,6 +169,7 @@ # Test that address-masking sandboxing is added after instructions that change # stack pointer. + .align 4 test4: .set noreorder @@ -217,3 +221,65 @@ # CHECK-NOT: and # CHECK: sw $sp, 123($sp) # CHECK-NOT: and + + + +# Test that call + branch delay is aligned at bundle end. Test that mask is +# added before indirect calls. + + .align 4 +test5: + .set noreorder + + jal func1 + addiu $4, $zero, 1 + + nop + bal func2 + addiu $4, $zero, 2 + + nop + nop + bltzal $t1, func3 + addiu $4, $zero, 3 + + nop + nop + nop + bgezal $t2, func4 + addiu $4, $zero, 4 + + jalr $t9 + addiu $4, $zero, 5 + +# CHECK-LABEL: test5: + +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: jal +# CHECK-NEXT: addiu $4, $zero, 1 + +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: bal +# CHECK-NEXT: addiu $4, $zero, 2 + +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: bltzal +# CHECK-NEXT: addiu $4, $zero, 3 + +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: nop + +# CHECK-NEXT: nop +# CHECK-NEXT: nop +# CHECK-NEXT: bgezal +# CHECK-NEXT: addiu $4, $zero, 4 + +# CHECK-NEXT: nop +# CHECK-NEXT: and $25, $25, $14 +# CHECK-NEXT: jalr $25 +# CHECK-NEXT: addiu $4, $zero, 5