
This adds code to AArch64 function prologues to protect against stack clash attacks by probing (writing to) the stack at regular enough intervals to ensure that the guard page cannot be skipped over. The patch depends on and maintains the following invariants: Upon function entry the caller guarantees that it has probed the stack (e.g. performed a store) at some address [sp, #N], where`0 <= N <= 1024`. This invariant comes from a requirement for compatibility with GCC. Any address range in the allocated stack, no smaller than stack-probe-size bytes contains at least one probe At any time the stack pointer is above or in the guard page Probes are performed in descreasing address order The stack-probe-size is a function attribute that can be set by a platform to correspond to the guard page size. By default, the stack probe size is 4KiB, which is a safe default as this is the smallest possible page size for AArch64. Linux uses a 64KiB guard for AArch64, so this can be overridden by the stack-probe-size function attribute. For small frames without a frame pointer (<= 240 bytes), no probes are needed. For larger frame sizes, LLVM always stores x29 to the stack. This serves as an implicit stack probe. Thus, while allocating stack objects the compiler assumes that the stack has been probed at [sp]. There are multiple probing sequences that can be emitted, depending on the size of the stack allocation: A straight-line sequence of subtracts and stores, used when the allocation size is smaller than 5 guard pages. A loop allocating and probing one page size per iteration, plus at most a single probe to deal with the remainder, used when the allocation size is larger but still known at compile time. A loop which moves the SP down to the target value held in a register (or a loop, moving a scratch register to the target value help in SP), used when the allocation size is not known at compile-time, such as when allocating space for SVE values, or when over-aligning the stack. This is emitted in AArch64InstrInfo because it will also be used for dynamic allocas in a future patch. A single probe where the amount of stack adjustment is unknown, but is known to be less than or equal to a page size. --------- Co-authored-by: Oliver Stannard <oliver.stannard@linaro.org>
180 lines
7.8 KiB
C++
180 lines
7.8 KiB
C++
//==-- AArch64FrameLowering.h - TargetFrameLowering for AArch64 --*- C++ -*-==//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
//
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_LIB_TARGET_AARCH64_AARCH64FRAMELOWERING_H
|
|
#define LLVM_LIB_TARGET_AARCH64_AARCH64FRAMELOWERING_H
|
|
|
|
#include "llvm/Support/TypeSize.h"
|
|
#include "llvm/CodeGen/TargetFrameLowering.h"
|
|
|
|
namespace llvm {
|
|
|
|
class AArch64FrameLowering : public TargetFrameLowering {
|
|
public:
|
|
explicit AArch64FrameLowering()
|
|
: TargetFrameLowering(StackGrowsDown, Align(16), 0, Align(16),
|
|
true /*StackRealignable*/) {}
|
|
|
|
void resetCFIToInitialState(MachineBasicBlock &MBB) const override;
|
|
|
|
MachineBasicBlock::iterator
|
|
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator I) const override;
|
|
|
|
/// emitProlog/emitEpilog - These methods insert prolog and epilog code into
|
|
/// the function.
|
|
void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
|
|
void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
|
|
|
|
bool enableCFIFixup(MachineFunction &MF) const override;
|
|
|
|
bool canUseAsPrologue(const MachineBasicBlock &MBB) const override;
|
|
|
|
StackOffset getFrameIndexReference(const MachineFunction &MF, int FI,
|
|
Register &FrameReg) const override;
|
|
StackOffset resolveFrameIndexReference(const MachineFunction &MF, int FI,
|
|
Register &FrameReg, bool PreferFP,
|
|
bool ForSimm) const;
|
|
StackOffset resolveFrameOffsetReference(const MachineFunction &MF,
|
|
int64_t ObjectOffset, bool isFixed,
|
|
bool isSVE, Register &FrameReg,
|
|
bool PreferFP, bool ForSimm) const;
|
|
bool spillCalleeSavedRegisters(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MI,
|
|
ArrayRef<CalleeSavedInfo> CSI,
|
|
const TargetRegisterInfo *TRI) const override;
|
|
|
|
bool
|
|
restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MI,
|
|
MutableArrayRef<CalleeSavedInfo> CSI,
|
|
const TargetRegisterInfo *TRI) const override;
|
|
|
|
/// Can this function use the red zone for local allocations.
|
|
bool canUseRedZone(const MachineFunction &MF) const;
|
|
|
|
bool hasFP(const MachineFunction &MF) const override;
|
|
bool hasReservedCallFrame(const MachineFunction &MF) const override;
|
|
|
|
bool assignCalleeSavedSpillSlots(MachineFunction &MF,
|
|
const TargetRegisterInfo *TRI,
|
|
std::vector<CalleeSavedInfo> &CSI,
|
|
unsigned &MinCSFrameIndex,
|
|
unsigned &MaxCSFrameIndex) const override;
|
|
|
|
void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs,
|
|
RegScavenger *RS) const override;
|
|
|
|
/// Returns true if the target will correctly handle shrink wrapping.
|
|
bool enableShrinkWrapping(const MachineFunction &MF) const override {
|
|
return true;
|
|
}
|
|
|
|
bool enableStackSlotScavenging(const MachineFunction &MF) const override;
|
|
TargetStackID::Value getStackIDForScalableVectors() const override;
|
|
|
|
void processFunctionBeforeFrameFinalized(MachineFunction &MF,
|
|
RegScavenger *RS) const override;
|
|
|
|
void
|
|
processFunctionBeforeFrameIndicesReplaced(MachineFunction &MF,
|
|
RegScavenger *RS) const override;
|
|
|
|
unsigned getWinEHParentFrameOffset(const MachineFunction &MF) const override;
|
|
|
|
unsigned getWinEHFuncletFrameSize(const MachineFunction &MF) const;
|
|
|
|
StackOffset
|
|
getFrameIndexReferencePreferSP(const MachineFunction &MF, int FI,
|
|
Register &FrameReg,
|
|
bool IgnoreSPUpdates) const override;
|
|
StackOffset getNonLocalFrameIndexReference(const MachineFunction &MF,
|
|
int FI) const override;
|
|
int getSEHFrameIndexOffset(const MachineFunction &MF, int FI) const;
|
|
|
|
bool isSupportedStackID(TargetStackID::Value ID) const override {
|
|
switch (ID) {
|
|
default:
|
|
return false;
|
|
case TargetStackID::Default:
|
|
case TargetStackID::ScalableVector:
|
|
case TargetStackID::NoAlloc:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool isStackIdSafeForLocalArea(unsigned StackId) const override {
|
|
// We don't support putting SVE objects into the pre-allocated local
|
|
// frame block at the moment.
|
|
return StackId != TargetStackID::ScalableVector;
|
|
}
|
|
|
|
void
|
|
orderFrameObjects(const MachineFunction &MF,
|
|
SmallVectorImpl<int> &ObjectsToAllocate) const override;
|
|
|
|
private:
|
|
/// Returns true if a homogeneous prolog or epilog code can be emitted
|
|
/// for the size optimization. If so, HOM_Prolog/HOM_Epilog pseudo
|
|
/// instructions are emitted in place. When Exit block is given, this check is
|
|
/// for epilog.
|
|
bool homogeneousPrologEpilog(MachineFunction &MF,
|
|
MachineBasicBlock *Exit = nullptr) const;
|
|
|
|
/// Returns true if CSRs should be paired.
|
|
bool producePairRegisters(MachineFunction &MF) const;
|
|
|
|
bool shouldCombineCSRLocalStackBump(MachineFunction &MF,
|
|
uint64_t StackBumpBytes) const;
|
|
|
|
int64_t estimateSVEStackObjectOffsets(MachineFrameInfo &MF) const;
|
|
int64_t assignSVEStackObjectOffsets(MachineFrameInfo &MF,
|
|
int &MinCSFrameIndex,
|
|
int &MaxCSFrameIndex) const;
|
|
bool shouldCombineCSRLocalStackBumpInEpilogue(MachineBasicBlock &MBB,
|
|
unsigned StackBumpBytes) const;
|
|
void emitCalleeSavedGPRLocations(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MBBI) const;
|
|
void emitCalleeSavedSVELocations(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MBBI) const;
|
|
void emitCalleeSavedGPRRestores(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MBBI) const;
|
|
void emitCalleeSavedSVERestores(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MBBI) const;
|
|
void allocateStackSpace(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MBBI,
|
|
int64_t RealignmentPadding, StackOffset AllocSize,
|
|
bool NeedsWinCFI, bool *HasWinCFI, bool EmitCFI,
|
|
StackOffset InitialOffset, bool FollowupAllocs) const;
|
|
|
|
/// Emit target zero call-used regs.
|
|
void emitZeroCallUsedRegs(BitVector RegsToZero,
|
|
MachineBasicBlock &MBB) const override;
|
|
|
|
/// Replace a StackProbe stub (if any) with the actual probe code inline
|
|
void inlineStackProbe(MachineFunction &MF,
|
|
MachineBasicBlock &PrologueMBB) const override;
|
|
|
|
void inlineStackProbeFixed(MachineBasicBlock::iterator MBBI,
|
|
Register ScratchReg, int64_t FrameSize,
|
|
StackOffset CFAOffset) const;
|
|
|
|
MachineBasicBlock::iterator
|
|
inlineStackProbeLoopExactMultiple(MachineBasicBlock::iterator MBBI,
|
|
int64_t NegProbeSize,
|
|
Register TargetReg) const;
|
|
};
|
|
|
|
} // End llvm namespace
|
|
|
|
#endif
|