Path: blob/main/contrib/llvm-project/llvm/lib/DWARFCFIChecker/DWARFCFIAnalysis.cpp
213766 views
//===----------------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/DWARFCFIChecker/DWARFCFIAnalysis.h"9#include "Registers.h"10#include "llvm/ADT/ArrayRef.h"11#include "llvm/ADT/SmallSet.h"12#include "llvm/ADT/SmallVector.h"13#include "llvm/ADT/Twine.h"14#include "llvm/DWARFCFIChecker/DWARFCFIState.h"15#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"16#include "llvm/MC/MCAsmInfo.h"17#include "llvm/MC/MCContext.h"18#include "llvm/MC/MCDwarf.h"19#include "llvm/MC/MCExpr.h"20#include "llvm/MC/MCInst.h"21#include "llvm/MC/MCInstrInfo.h"22#include "llvm/MC/MCRegister.h"23#include "llvm/MC/MCRegisterInfo.h"24#include "llvm/MC/MCStreamer.h"25#include "llvm/MC/MCSubtargetInfo.h"26#include "llvm/MC/TargetRegistry.h"27#include "llvm/Support/ErrorHandling.h"28#include "llvm/Support/FormatVariadic.h"29#include <optional>3031using namespace llvm;3233struct CFARegOffsetInfo {34DWARFRegNum Reg;35int64_t Offset;3637CFARegOffsetInfo(DWARFRegNum Reg, int64_t Offset)38: Reg(Reg), Offset(Offset) {}3940bool operator==(const CFARegOffsetInfo &RHS) const {41return Reg == RHS.Reg && Offset == RHS.Offset;42}43};4445static std::optional<CFARegOffsetInfo>46getCFARegOffsetInfo(const dwarf::UnwindRow &UnwindRow) {47auto CFALocation = UnwindRow.getCFAValue();48if (CFALocation.getLocation() !=49dwarf::UnwindLocation::Location::RegPlusOffset)50return std::nullopt;5152return CFARegOffsetInfo(CFALocation.getRegister(), CFALocation.getOffset());53}5455static SmallSet<DWARFRegNum, 4>56getUnwindRuleRegSet(const dwarf::UnwindRow &UnwindRow, DWARFRegNum Reg) {57auto MaybeLoc = UnwindRow.getRegisterLocations().getRegisterLocation(Reg);58assert(MaybeLoc && "the register should be included in the unwinding row");59auto Loc = *MaybeLoc;6061switch (Loc.getLocation()) {62case dwarf::UnwindLocation::Location::Unspecified:63case dwarf::UnwindLocation::Location::Undefined:64case dwarf::UnwindLocation::Location::Constant:65case dwarf::UnwindLocation::Location::CFAPlusOffset:66// [CFA + offset] does not depend on any register because the CFA value is67// constant throughout the entire frame; only the way to calculate it might68// change.69case dwarf::UnwindLocation::Location::DWARFExpr:70// TODO: Expressions are not supported yet, but if they were to be71// supported, all the registers used in an expression should extracted and72// returned here.73return {};74case dwarf::UnwindLocation::Location::Same:75return {Reg};76case dwarf::UnwindLocation::Location::RegPlusOffset:77return {Loc.getRegister()};78}79llvm_unreachable("Unknown dwarf::UnwindLocation::Location enum");80}8182DWARFCFIAnalysis::DWARFCFIAnalysis(MCContext *Context, MCInstrInfo const &MCII,83bool IsEH,84ArrayRef<MCCFIInstruction> Prologue)85: State(Context), Context(Context), MCII(MCII),86MCRI(Context->getRegisterInfo()), IsEH(IsEH) {8788for (auto LLVMReg : getTrackingRegs(MCRI)) {89if (MCRI->get(LLVMReg).IsArtificial || MCRI->get(LLVMReg).IsConstant)90continue;9192DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);93// TODO: this should be `undefined` instead of `same_value`, but because94// initial frame state doesn't have any directives about callee saved95// registers, every register is tracked. After initial frame state is96// corrected, this should be changed.97State.update(MCCFIInstruction::createSameValue(nullptr, Reg));98}99100// TODO: Ignoring PC should be in the initial frame state.101State.update(MCCFIInstruction::createUndefined(102nullptr, MCRI->getDwarfRegNum(MCRI->getProgramCounter(), IsEH)));103104for (auto &&InitialFrameStateCFIDirective :105Context->getAsmInfo()->getInitialFrameState())106State.update(InitialFrameStateCFIDirective);107108auto MaybeCurrentRow = State.getCurrentUnwindRow();109assert(MaybeCurrentRow && "there should be at least one row");110auto MaybeCFA = getCFARegOffsetInfo(*MaybeCurrentRow);111assert(MaybeCFA &&112"the CFA information should be describable in [reg + offset] in here");113auto CFA = *MaybeCFA;114115// TODO: CFA register callee value is CFA's value, this should be in initial116// frame state.117State.update(MCCFIInstruction::createOffset(nullptr, CFA.Reg, 0));118119// Applying the prologue after default assumptions to overwrite them.120for (auto &&Directive : Prologue)121State.update(Directive);122}123124void DWARFCFIAnalysis::update(const MCInst &Inst,125ArrayRef<MCCFIInstruction> Directives) {126const MCInstrDesc &MCInstInfo = MCII.get(Inst.getOpcode());127128auto MaybePrevRow = State.getCurrentUnwindRow();129assert(MaybePrevRow && "the analysis should have initialized the "130"state with at least one row by now");131auto PrevRow = *MaybePrevRow;132133for (auto &&Directive : Directives)134State.update(Directive);135136SmallSet<DWARFRegNum, 4> Writes, Reads;137for (unsigned I = 0; I < MCInstInfo.NumImplicitUses; I++)138Reads.insert(MCRI->getDwarfRegNum(139getSuperReg(MCRI, MCInstInfo.implicit_uses()[I]), IsEH));140for (unsigned I = 0; I < MCInstInfo.NumImplicitDefs; I++)141Writes.insert(MCRI->getDwarfRegNum(142getSuperReg(MCRI, MCInstInfo.implicit_defs()[I]), IsEH));143144for (unsigned I = 0; I < Inst.getNumOperands(); I++) {145auto &&Op = Inst.getOperand(I);146if (Op.isReg()) {147if (I < MCInstInfo.getNumDefs())148Writes.insert(149MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));150else if (Op.getReg())151Reads.insert(152MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));153}154}155156auto MaybeNextRow = State.getCurrentUnwindRow();157assert(MaybeNextRow && "previous row existed, so should the current row");158auto NextRow = *MaybeNextRow;159160checkCFADiff(Inst, PrevRow, NextRow, Reads, Writes);161162for (auto LLVMReg : getTrackingRegs(MCRI)) {163DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);164165checkRegDiff(Inst, Reg, PrevRow, NextRow, Reads, Writes);166}167}168169void DWARFCFIAnalysis::checkRegDiff(const MCInst &Inst, DWARFRegNum Reg,170const dwarf::UnwindRow &PrevRow,171const dwarf::UnwindRow &NextRow,172const SmallSet<DWARFRegNum, 4> &Reads,173const SmallSet<DWARFRegNum, 4> &Writes) {174auto MaybePrevLoc = PrevRow.getRegisterLocations().getRegisterLocation(Reg);175auto MaybeNextLoc = NextRow.getRegisterLocations().getRegisterLocation(Reg);176177// All the tracked registers are added during initiation. So if a register is178// not added, should stay the same during execution and vice versa.179if (!MaybePrevLoc) {180assert(!MaybeNextLoc && "the register unwind info suddenly appeared here");181return;182}183assert(MaybeNextLoc && "the register unwind info suddenly vanished here");184185auto PrevLoc = MaybePrevLoc.value();186auto NextLoc = MaybeNextLoc.value();187188auto MaybeLLVMReg = MCRI->getLLVMRegNum(Reg, IsEH);189if (!MaybeLLVMReg) {190if (!(PrevLoc == NextLoc))191Context->reportWarning(192Inst.getLoc(),193formatv("the dwarf register {0} does not have a LLVM number, but its "194"unwind info changed. Ignoring this change",195Reg));196return;197}198const char *RegName = MCRI->getName(*MaybeLLVMReg);199200// Each case is annotated with its corresponding number as described in201// `llvm/include/llvm/DWARFCFIChecker/DWARFCFIAnalysis.h`.202203// TODO: Expressions are not supported yet, but if they were to be supported,204// note that structure equality for expressions is defined as follows: Two205// expressions are structurally equal if they become the same after you206// replace every operand with a placeholder.207208if (PrevLoc == NextLoc) { // Case 1209for (DWARFRegNum UsedReg : getUnwindRuleRegSet(PrevRow, Reg))210if (Writes.count(UsedReg)) { // Case 1.b211auto MaybeLLVMUsedReg = MCRI->getLLVMRegNum(UsedReg, IsEH);212assert(MaybeLLVMUsedReg && "instructions will always write to a "213"register that has an LLVM register number");214Context->reportError(215Inst.getLoc(),216formatv("changed register {1}, that register {0}'s unwinding rule "217"uses, but there is no CFI directives about it",218RegName, MCRI->getName(*MaybeLLVMUsedReg)));219return;220}221return; // Case 1.a222}223// Case 2224if (PrevLoc.getLocation() != NextLoc.getLocation()) { // Case 2.a225Context->reportWarning(226Inst.getLoc(),227formatv("validating changes happening to register {0} unwinding "228"rule structure is not implemented yet",229RegName));230return;231}232auto &&PrevRegSet = getUnwindRuleRegSet(PrevRow, Reg);233if (PrevRegSet != getUnwindRuleRegSet(NextRow, Reg)) { // Case 2.b234Context->reportWarning(235Inst.getLoc(),236formatv("validating changes happening to register {0} unwinding "237"rule register set is not implemented yet",238RegName));239return;240}241// Case 2.c242for (DWARFRegNum UsedReg : PrevRegSet)243if (Writes.count(UsedReg)) { // Case 2.c.i244Context->reportWarning(245Inst.getLoc(),246formatv("register {0} unwinding rule's offset is changed, and one of "247"the rule's registers is modified, but validating the "248"modification amount is not implemented yet",249RegName));250return;251}252// Case 2.c.ii253Context->reportError(254Inst.getLoc(), formatv("register {0} unwinding rule's offset is changed, "255"but not any of the rule's registers are modified",256RegName));257}258259void DWARFCFIAnalysis::checkCFADiff(const MCInst &Inst,260const dwarf::UnwindRow &PrevRow,261const dwarf::UnwindRow &NextRow,262const SmallSet<DWARFRegNum, 4> &Reads,263const SmallSet<DWARFRegNum, 4> &Writes) {264265auto MaybePrevCFA = getCFARegOffsetInfo(PrevRow);266auto MaybeNextCFA = getCFARegOffsetInfo(NextRow);267268if (!MaybePrevCFA) {269if (MaybeNextCFA) {270Context->reportWarning(Inst.getLoc(),271"CFA rule changed to [reg + offset], this "272"transition will not be checked");273return;274}275276Context->reportWarning(Inst.getLoc(),277"CFA rule is not [reg + offset], not checking it");278return;279}280281if (!MaybeNextCFA) {282Context->reportWarning(Inst.getLoc(),283"CFA rule changed from [reg + offset], this "284"transition will not be checked");285return;286}287288auto PrevCFA = *MaybePrevCFA;289auto NextCFA = *MaybeNextCFA;290291auto MaybeLLVMPrevReg = MCRI->getLLVMRegNum(PrevCFA.Reg, IsEH);292const char *PrevCFARegName =293MaybeLLVMPrevReg ? MCRI->getName(*MaybeLLVMPrevReg) : "";294auto MaybeLLVMNextReg = MCRI->getLLVMRegNum(NextCFA.Reg, IsEH);295const char *NextCFARegName =296MaybeLLVMNextReg ? MCRI->getName(*MaybeLLVMNextReg) : "";297298if (PrevCFA == NextCFA) { // Case 1299if (!Writes.count(PrevCFA.Reg)) // Case 1.a300return;301// Case 1.b302Context->reportError(303Inst.getLoc(),304formatv("modified CFA register {0} but not changed CFA rule",305PrevCFARegName));306return;307}308309if (PrevCFA.Reg != NextCFA.Reg) { // Case 2.b310Context->reportWarning(311Inst.getLoc(),312formatv("CFA register changed from register {0} to register {1}, "313"validating this change is not implemented yet",314PrevCFARegName, NextCFARegName));315return;316}317// Case 2.c318if (Writes.count(PrevCFA.Reg)) { // Case 2.c.i319Context->reportWarning(320Inst.getLoc(), formatv("CFA offset is changed from {0} to {1}, and CFA "321"register {2} is modified, but validating the "322"modification amount is not implemented yet",323PrevCFA.Offset, NextCFA.Offset, PrevCFARegName));324return;325}326// Case 2.c.ii327Context->reportError(328Inst.getLoc(),329formatv("did not modify CFA register {0} but changed CFA rule",330PrevCFARegName));331}332333334