Files
archived-llvm/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp
Guillaume Chatelet bfa7de776d [LLVM][Alignment] Make functions using log of alignment explicit
Summary:
This patch renames functions that takes or returns alignment as log2, this patch will help with the transition to llvm::Align.
The renaming makes it explicit that we deal with log(alignment) instead of a power of two alignment.
A few renames uncovered dubious assignments:

 - `MirParser`/`MirPrinter` was expecting powers of two but `MachineFunction` and `MachineBasicBlock` were using deal with log2(align). This patch fixes it and updates the documentation.
 - `MachineBlockPlacement` exposes two flags (`align-all-blocks` and `align-all-nofallthru-blocks`) supposedly interpreted as power of two alignments, internally these values are interpreted as log2(align). This patch updates the documentation,
 - `MachineFunctionexposes` exposes `align-all-functions` also interpreted as power of two alignment, internally this value is interpreted as log2(align). This patch updates the documentation,

Reviewers: lattner, thegameg, courbet

Subscribers: dschuff, arsenm, jyknight, dylanmckay, sdardis, nemanjai, jvesely, nhaehnle, javed.absar, hiraditya, kbarton, fedor.sergeev, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, MaskRay, zzheng, edward-jones, atanasyan, rogfer01, MartinMosbeck, brucehoult, the_o, dexonsmith, PkmX, jocewei, jsji, Jim, s.egerton, llvm-commits, courbet

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D65945

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@371045 91177308-0d34-0410-b5e6-96231b3b80d8
2019-09-05 10:00:22 +00:00

279 lines
8.8 KiB
C++

//===-- MipsNaClELFStreamer.cpp - ELF Object Output for Mips NaCl ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// 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 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. It aligns call + branch delay to the bundle end,
// so that return address is always aligned to the start of next bundle.
//
//===----------------------------------------------------------------------===//
#include "Mips.h"
#include "MipsELFStreamer.h"
#include "MipsMCNaCl.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCELFStreamer.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
using namespace llvm;
#define DEBUG_TYPE "mips-mc-nacl"
namespace {
const unsigned IndirectBranchMaskReg = Mips::T6;
const unsigned LoadStoreStackMaskReg = Mips::T7;
/// Extend the generic MCELFStreamer class so that it can mask dangerous
/// instructions.
class MipsNaClELFStreamer : public MipsELFStreamer {
public:
MipsNaClELFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> TAB,
std::unique_ptr<MCObjectWriter> OW,
std::unique_ptr<MCCodeEmitter> Emitter)
: MipsELFStreamer(Context, std::move(TAB), std::move(OW),
std::move(Emitter)) {}
~MipsNaClELFStreamer() override = default;
private:
// Whether we started the sandboxing sequence for calls. Calls are bundled
// with branch delays and aligned to the bundle end.
bool PendingCall = false;
bool isIndirectJump(const MCInst &MI) {
if (MI.getOpcode() == Mips::JALR) {
// MIPS32r6/MIPS64r6 doesn't have a JR instruction and uses JALR instead.
// JALR is an indirect branch if the link register is $0.
assert(MI.getOperand(0).isReg());
return MI.getOperand(0).getReg() == Mips::ZERO;
}
return MI.getOpcode() == Mips::JR;
}
bool isStackPointerFirstOperand(const MCInst &MI) {
return (MI.getNumOperands() > 0 && MI.getOperand(0).isReg()
&& MI.getOperand(0).getReg() == Mips::SP);
}
bool isCall(const MCInst &MI, bool *IsIndirectCall) {
unsigned Opcode = MI.getOpcode();
*IsIndirectCall = false;
switch (Opcode) {
default:
return false;
case Mips::JAL:
case Mips::BAL:
case Mips::BAL_BR:
case Mips::BLTZAL:
case Mips::BGEZAL:
return true;
case Mips::JALR:
// JALR is only a call if the link register is not $0. Otherwise it's an
// indirect branch.
assert(MI.getOperand(0).isReg());
if (MI.getOperand(0).getReg() == Mips::ZERO)
return false;
*IsIndirectCall = true;
return true;
}
}
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));
MipsELFStreamer::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);
MipsELFStreamer::EmitInstruction(MI, STI);
EmitBundleUnlock();
}
// Sandbox memory access or SP change. Insert mask operation before and/or
// after the instruction.
void sandboxLoadStoreStackChange(const MCInst &MI, unsigned AddrIdx,
const MCSubtargetInfo &STI, bool MaskBefore,
bool MaskAfter) {
EmitBundleLock(false);
if (MaskBefore) {
// Sandbox memory access.
unsigned BaseReg = MI.getOperand(AddrIdx).getReg();
emitMask(BaseReg, LoadStoreStackMaskReg, STI);
}
MipsELFStreamer::EmitInstruction(MI, STI);
if (MaskAfter) {
// Sandbox SP change.
unsigned SPReg = MI.getOperand(0).getReg();
assert((Mips::SP == SPReg) && "Unexpected stack-pointer register.");
emitMask(SPReg, LoadStoreStackMaskReg, STI);
}
EmitBundleUnlock();
}
public:
/// This function is the one used to emit instruction data into the ELF
/// streamer. We override it to mask dangerous instructions.
void EmitInstruction(const MCInst &Inst,
const MCSubtargetInfo &STI) override {
// Sandbox indirect jumps.
if (isIndirectJump(Inst)) {
if (PendingCall)
report_fatal_error("Dangerous instruction in branch delay slot!");
sandboxIndirectJump(Inst, STI);
return;
}
// Sandbox loads, stores and SP changes.
unsigned AddrIdx;
bool IsStore;
bool IsMemAccess = isBasePlusOffsetMemoryAccess(Inst.getOpcode(), &AddrIdx,
&IsStore);
bool IsSPFirstOperand = isStackPointerFirstOperand(Inst);
if (IsMemAccess || IsSPFirstOperand) {
bool MaskBefore = (IsMemAccess
&& baseRegNeedsLoadStoreMask(Inst.getOperand(AddrIdx)
.getReg()));
bool MaskAfter = IsSPFirstOperand && !IsStore;
if (MaskBefore || MaskAfter) {
if (PendingCall)
report_fatal_error("Dangerous instruction in branch delay slot!");
sandboxLoadStoreStackChange(Inst, AddrIdx, STI, MaskBefore, MaskAfter);
return;
}
// fallthrough
}
// Sandbox calls by aligning call and branch delay to the bundle end.
// For indirect calls, emit the mask before the call.
bool IsIndirectCall;
if (isCall(Inst, &IsIndirectCall)) {
if (PendingCall)
report_fatal_error("Dangerous instruction in branch delay slot!");
// Start the sandboxing sequence by emitting call.
EmitBundleLock(true);
if (IsIndirectCall) {
unsigned TargetReg = Inst.getOperand(1).getReg();
emitMask(TargetReg, IndirectBranchMaskReg, STI);
}
MipsELFStreamer::EmitInstruction(Inst, STI);
PendingCall = true;
return;
}
if (PendingCall) {
// Finish the sandboxing sequence by emitting branch delay.
MipsELFStreamer::EmitInstruction(Inst, STI);
EmitBundleUnlock();
PendingCall = false;
return;
}
// None of the sandboxing applies, just emit the instruction.
MipsELFStreamer::EmitInstruction(Inst, STI);
}
};
} // end anonymous namespace
namespace llvm {
bool isBasePlusOffsetMemoryAccess(unsigned Opcode, unsigned *AddrIdx,
bool *IsStore) {
if (IsStore)
*IsStore = false;
switch (Opcode) {
default:
return false;
// Load instructions with base address register in position 1.
case Mips::LB:
case Mips::LBu:
case Mips::LH:
case Mips::LHu:
case Mips::LW:
case Mips::LWC1:
case Mips::LDC1:
case Mips::LL:
case Mips::LL_R6:
case Mips::LWL:
case Mips::LWR:
*AddrIdx = 1;
return true;
// Store instructions with base address register in position 1.
case Mips::SB:
case Mips::SH:
case Mips::SW:
case Mips::SWC1:
case Mips::SDC1:
case Mips::SWL:
case Mips::SWR:
*AddrIdx = 1;
if (IsStore)
*IsStore = true;
return true;
// Store instructions with base address register in position 2.
case Mips::SC:
case Mips::SC_R6:
*AddrIdx = 2;
if (IsStore)
*IsStore = true;
return true;
}
}
bool baseRegNeedsLoadStoreMask(unsigned Reg) {
// The contents of SP and thread pointer register do not require masking.
return Reg != Mips::SP && Reg != Mips::T8;
}
MCELFStreamer *createMipsNaClELFStreamer(MCContext &Context,
std::unique_ptr<MCAsmBackend> TAB,
std::unique_ptr<MCObjectWriter> OW,
std::unique_ptr<MCCodeEmitter> Emitter,
bool RelaxAll) {
MipsNaClELFStreamer *S = new MipsNaClELFStreamer(
Context, std::move(TAB), std::move(OW), std::move(Emitter));
if (RelaxAll)
S->getAssembler().setRelaxAll(true);
// Set bundle-alignment as required by the NaCl ABI for the target.
S->EmitBundleAlignMode(MIPS_NACL_BUNDLE_LOG_ALIGN);
return S;
}
} // end namespace llvm