
This PR adds a minimal version of `UnwindInfoChecker` described in [here](https://discourse.llvm.org/t/rfc-dwarf-cfi-validation/86936). This implementation looks into the modified registers by each instruction and checks: - If one of them is the CFA register, and it informs the user if the CFA data is not modified as well. - If one of them is used in another register's unwinding rule, it informs the user if the unwind info is not modified after this instruction. This implementation does not support DWARF expressions and treats them as unknown unwinding rules.
89 lines
3.3 KiB
C++
89 lines
3.3 KiB
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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/DWARFCFIChecker/DWARFCFIFunctionFrameStreamer.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCDwarf.h"
|
|
#include "llvm/MC/MCInst.h"
|
|
#include "llvm/MC/MCInstrInfo.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include <optional>
|
|
|
|
using namespace llvm;
|
|
|
|
void CFIFunctionFrameStreamer::updateReceiver(
|
|
const std::optional<MCInst> &NewInst) {
|
|
assert(hasUnfinishedDwarfFrameInfo() &&
|
|
"should have an unfinished DWARF frame here");
|
|
assert(!FrameIndices.empty() &&
|
|
"there should be an index available for the current frame");
|
|
assert(FrameIndices.size() == LastInstructions.size());
|
|
assert(LastInstructions.size() == LastDirectiveIndices.size());
|
|
|
|
auto Frames = getDwarfFrameInfos();
|
|
assert(FrameIndices.back() < Frames.size());
|
|
unsigned LastDirectiveIndex = LastDirectiveIndices.back();
|
|
unsigned CurrentDirectiveIndex =
|
|
Frames[FrameIndices.back()].Instructions.size();
|
|
assert(CurrentDirectiveIndex >= LastDirectiveIndex);
|
|
|
|
const MCDwarfFrameInfo *LastFrame = &Frames[FrameIndices.back()];
|
|
ArrayRef<MCCFIInstruction> Directives;
|
|
if (LastDirectiveIndex < CurrentDirectiveIndex) {
|
|
Directives = ArrayRef<MCCFIInstruction>(LastFrame->Instructions);
|
|
Directives =
|
|
Directives.drop_front(LastDirectiveIndex)
|
|
.drop_back(LastFrame->Instructions.size() - CurrentDirectiveIndex);
|
|
}
|
|
|
|
auto MaybeLastInstruction = LastInstructions.back();
|
|
if (MaybeLastInstruction)
|
|
// The directives are associated with an instruction.
|
|
Receiver->emitInstructionAndDirectives(*MaybeLastInstruction, Directives);
|
|
else
|
|
// The directives are the prologue directives.
|
|
Receiver->startFunctionFrame(false /* TODO: should put isEH here */,
|
|
Directives);
|
|
|
|
// Update the internal state for the top frame.
|
|
LastInstructions.back() = NewInst;
|
|
LastDirectiveIndices.back() = CurrentDirectiveIndex;
|
|
}
|
|
|
|
void CFIFunctionFrameStreamer::emitInstruction(const MCInst &Inst,
|
|
const MCSubtargetInfo &STI) {
|
|
if (hasUnfinishedDwarfFrameInfo())
|
|
// Send the last instruction with the unsent directives already in the frame
|
|
// to the receiver.
|
|
updateReceiver(Inst);
|
|
}
|
|
|
|
void CFIFunctionFrameStreamer::emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) {
|
|
LastInstructions.push_back(std::nullopt);
|
|
LastDirectiveIndices.push_back(0);
|
|
FrameIndices.push_back(getNumFrameInfos());
|
|
|
|
MCStreamer::emitCFIStartProcImpl(Frame);
|
|
}
|
|
|
|
void CFIFunctionFrameStreamer::emitCFIEndProcImpl(MCDwarfFrameInfo &CurFrame) {
|
|
// Send the last instruction with the final directives of the current frame to
|
|
// the receiver.
|
|
updateReceiver(std::nullopt);
|
|
|
|
assert(!FrameIndices.empty() && "There should be at least one frame to pop");
|
|
LastDirectiveIndices.pop_back();
|
|
LastInstructions.pop_back();
|
|
FrameIndices.pop_back();
|
|
|
|
Receiver->finishFunctionFrame();
|
|
|
|
MCStreamer::emitCFIEndProcImpl(CurFrame);
|
|
}
|