brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.5 KiB · 269e4dc Raw
333 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DWARFCFIChecker/DWARFCFIAnalysis.h"10#include "Registers.h"11#include "llvm/ADT/ArrayRef.h"12#include "llvm/ADT/SmallSet.h"13#include "llvm/ADT/SmallVector.h"14#include "llvm/ADT/Twine.h"15#include "llvm/DWARFCFIChecker/DWARFCFIState.h"16#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"17#include "llvm/MC/MCAsmInfo.h"18#include "llvm/MC/MCContext.h"19#include "llvm/MC/MCDwarf.h"20#include "llvm/MC/MCExpr.h"21#include "llvm/MC/MCInst.h"22#include "llvm/MC/MCInstrInfo.h"23#include "llvm/MC/MCRegister.h"24#include "llvm/MC/MCRegisterInfo.h"25#include "llvm/MC/MCStreamer.h"26#include "llvm/MC/MCSubtargetInfo.h"27#include "llvm/MC/TargetRegistry.h"28#include "llvm/Support/ErrorHandling.h"29#include "llvm/Support/FormatVariadic.h"30#include <optional>31 32using namespace llvm;33 34struct CFARegOffsetInfo {35  DWARFRegNum Reg;36  int64_t Offset;37 38  CFARegOffsetInfo(DWARFRegNum Reg, int64_t Offset)39      : Reg(Reg), Offset(Offset) {}40 41  bool operator==(const CFARegOffsetInfo &RHS) const {42    return Reg == RHS.Reg && Offset == RHS.Offset;43  }44};45 46static std::optional<CFARegOffsetInfo>47getCFARegOffsetInfo(const dwarf::UnwindRow &UnwindRow) {48  auto CFALocation = UnwindRow.getCFAValue();49  if (CFALocation.getLocation() !=50      dwarf::UnwindLocation::Location::RegPlusOffset)51    return std::nullopt;52 53  return CFARegOffsetInfo(CFALocation.getRegister(), CFALocation.getOffset());54}55 56static SmallSet<DWARFRegNum, 4>57getUnwindRuleRegSet(const dwarf::UnwindRow &UnwindRow, DWARFRegNum Reg) {58  auto MaybeLoc = UnwindRow.getRegisterLocations().getRegisterLocation(Reg);59  assert(MaybeLoc && "the register should be included in the unwinding row");60  auto Loc = *MaybeLoc;61 62  switch (Loc.getLocation()) {63  case dwarf::UnwindLocation::Location::Unspecified:64  case dwarf::UnwindLocation::Location::Undefined:65  case dwarf::UnwindLocation::Location::Constant:66  case dwarf::UnwindLocation::Location::CFAPlusOffset:67    // [CFA + offset] does not depend on any register because the CFA value is68    // constant throughout the entire frame; only the way to calculate it might69    // change.70  case dwarf::UnwindLocation::Location::DWARFExpr:71    // TODO: Expressions are not supported yet, but if they were to be72    // supported, all the registers used in an expression should extracted and73    // returned here.74    return {};75  case dwarf::UnwindLocation::Location::Same:76    return {Reg};77  case dwarf::UnwindLocation::Location::RegPlusOffset:78    return {Loc.getRegister()};79  }80  llvm_unreachable("Unknown dwarf::UnwindLocation::Location enum");81}82 83DWARFCFIAnalysis::DWARFCFIAnalysis(MCContext *Context, MCInstrInfo const &MCII,84                                   bool IsEH,85                                   ArrayRef<MCCFIInstruction> Prologue)86    : State(Context), Context(Context), MCII(MCII),87      MCRI(Context->getRegisterInfo()), IsEH(IsEH) {88 89  for (auto LLVMReg : getTrackingRegs(MCRI)) {90    if (MCRI->get(LLVMReg).IsArtificial || MCRI->get(LLVMReg).IsConstant)91      continue;92 93    DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);94    // TODO: this should be `undefined` instead of `same_value`, but because95    // initial frame state doesn't have any directives about callee saved96    // registers, every register is tracked. After initial frame state is97    // corrected, this should be changed.98    State.update(MCCFIInstruction::createSameValue(nullptr, Reg));99  }100 101  // TODO: Ignoring PC should be in the initial frame state.102  State.update(MCCFIInstruction::createUndefined(103      nullptr, MCRI->getDwarfRegNum(MCRI->getProgramCounter(), IsEH)));104 105  for (auto &&InitialFrameStateCFIDirective :106       Context->getAsmInfo()->getInitialFrameState())107    State.update(InitialFrameStateCFIDirective);108 109  auto MaybeCurrentRow = State.getCurrentUnwindRow();110  assert(MaybeCurrentRow && "there should be at least one row");111  auto MaybeCFA = getCFARegOffsetInfo(*MaybeCurrentRow);112  assert(MaybeCFA &&113         "the CFA information should be describable in [reg + offset] in here");114  auto CFA = *MaybeCFA;115 116  // TODO: CFA register callee value is CFA's value, this should be in initial117  // frame state.118  State.update(MCCFIInstruction::createOffset(nullptr, CFA.Reg, 0));119 120  // Applying the prologue after default assumptions to overwrite them.121  for (auto &&Directive : Prologue)122    State.update(Directive);123}124 125void DWARFCFIAnalysis::update(const MCInst &Inst,126                              ArrayRef<MCCFIInstruction> Directives) {127  const MCInstrDesc &MCInstInfo = MCII.get(Inst.getOpcode());128 129  auto MaybePrevRow = State.getCurrentUnwindRow();130  assert(MaybePrevRow && "the analysis should have initialized the "131                         "state with at least one row by now");132  auto PrevRow = *MaybePrevRow;133 134  for (auto &&Directive : Directives)135    State.update(Directive);136 137  SmallSet<DWARFRegNum, 4> Writes, Reads;138  for (unsigned I = 0; I < MCInstInfo.NumImplicitUses; I++)139    Reads.insert(MCRI->getDwarfRegNum(140        getSuperReg(MCRI, MCInstInfo.implicit_uses()[I]), IsEH));141  for (unsigned I = 0; I < MCInstInfo.NumImplicitDefs; I++)142    Writes.insert(MCRI->getDwarfRegNum(143        getSuperReg(MCRI, MCInstInfo.implicit_defs()[I]), IsEH));144 145  for (unsigned I = 0; I < Inst.getNumOperands(); I++) {146    auto &&Op = Inst.getOperand(I);147    if (Op.isReg()) {148      if (I < MCInstInfo.getNumDefs())149        Writes.insert(150            MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));151      else if (Op.getReg())152        Reads.insert(153            MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));154    }155  }156 157  auto MaybeNextRow = State.getCurrentUnwindRow();158  assert(MaybeNextRow && "previous row existed, so should the current row");159  auto NextRow = *MaybeNextRow;160 161  checkCFADiff(Inst, PrevRow, NextRow, Reads, Writes);162 163  for (auto LLVMReg : getTrackingRegs(MCRI)) {164    DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);165 166    checkRegDiff(Inst, Reg, PrevRow, NextRow, Reads, Writes);167  }168}169 170void DWARFCFIAnalysis::checkRegDiff(const MCInst &Inst, DWARFRegNum Reg,171                                    const dwarf::UnwindRow &PrevRow,172                                    const dwarf::UnwindRow &NextRow,173                                    const SmallSet<DWARFRegNum, 4> &Reads,174                                    const SmallSet<DWARFRegNum, 4> &Writes) {175  auto MaybePrevLoc = PrevRow.getRegisterLocations().getRegisterLocation(Reg);176  auto MaybeNextLoc = NextRow.getRegisterLocations().getRegisterLocation(Reg);177 178  // All the tracked registers are added during initiation. So if a register is179  // not added, should stay the same during execution and vice versa.180  if (!MaybePrevLoc) {181    assert(!MaybeNextLoc && "the register unwind info suddenly appeared here");182    return;183  }184  assert(MaybeNextLoc && "the register unwind info suddenly vanished here");185 186  auto PrevLoc = MaybePrevLoc.value();187  auto NextLoc = MaybeNextLoc.value();188 189  auto MaybeLLVMReg = MCRI->getLLVMRegNum(Reg, IsEH);190  if (!MaybeLLVMReg) {191    if (!(PrevLoc == NextLoc))192      Context->reportWarning(193          Inst.getLoc(),194          formatv("the dwarf register {0} does not have a LLVM number, but its "195                  "unwind info changed. Ignoring this change",196                  Reg));197    return;198  }199  const char *RegName = MCRI->getName(*MaybeLLVMReg);200 201  // Each case is annotated with its corresponding number as described in202  // `llvm/include/llvm/DWARFCFIChecker/DWARFCFIAnalysis.h`.203 204  // TODO: Expressions are not supported yet, but if they were to be supported,205  // note that structure equality for expressions is defined as follows: Two206  // expressions are structurally equal if they become the same after you207  // replace every operand with a placeholder.208 209  if (PrevLoc == NextLoc) { // Case 1210    for (DWARFRegNum UsedReg : getUnwindRuleRegSet(PrevRow, Reg))211      if (Writes.count(UsedReg)) { // Case 1.b212        auto MaybeLLVMUsedReg = MCRI->getLLVMRegNum(UsedReg, IsEH);213        assert(MaybeLLVMUsedReg && "instructions will always write to a "214                                   "register that has an LLVM register number");215        Context->reportError(216            Inst.getLoc(),217            formatv("changed register {1}, that register {0}'s unwinding rule "218                    "uses, but there is no CFI directives about it",219                    RegName, MCRI->getName(*MaybeLLVMUsedReg)));220        return;221      }222    return; // Case 1.a223  }224  // Case 2225  if (PrevLoc.getLocation() != NextLoc.getLocation()) { // Case 2.a226    Context->reportWarning(227        Inst.getLoc(),228        formatv("validating changes happening to register {0} unwinding "229                "rule structure is not implemented yet",230                RegName));231    return;232  }233  auto &&PrevRegSet = getUnwindRuleRegSet(PrevRow, Reg);234  if (PrevRegSet != getUnwindRuleRegSet(NextRow, Reg)) { // Case 2.b235    Context->reportWarning(236        Inst.getLoc(),237        formatv("validating changes happening to register {0} unwinding "238                "rule register set is not implemented yet",239                RegName));240    return;241  }242  // Case 2.c243  for (DWARFRegNum UsedReg : PrevRegSet)244    if (Writes.count(UsedReg)) { // Case 2.c.i245      Context->reportWarning(246          Inst.getLoc(),247          formatv("register {0} unwinding rule's offset is changed, and one of "248                  "the rule's registers is modified, but validating the "249                  "modification amount is not implemented yet",250                  RegName));251      return;252    }253  // Case 2.c.ii254  Context->reportError(255      Inst.getLoc(), formatv("register {0} unwinding rule's offset is changed, "256                             "but not any of the rule's registers are modified",257                             RegName));258}259 260void DWARFCFIAnalysis::checkCFADiff(const MCInst &Inst,261                                    const dwarf::UnwindRow &PrevRow,262                                    const dwarf::UnwindRow &NextRow,263                                    const SmallSet<DWARFRegNum, 4> &Reads,264                                    const SmallSet<DWARFRegNum, 4> &Writes) {265 266  auto MaybePrevCFA = getCFARegOffsetInfo(PrevRow);267  auto MaybeNextCFA = getCFARegOffsetInfo(NextRow);268 269  if (!MaybePrevCFA) {270    if (MaybeNextCFA) {271      Context->reportWarning(Inst.getLoc(),272                             "CFA rule changed to [reg + offset], this "273                             "transition will not be checked");274      return;275    }276 277    Context->reportWarning(Inst.getLoc(),278                           "CFA rule is not [reg + offset], not checking it");279    return;280  }281 282  if (!MaybeNextCFA) {283    Context->reportWarning(Inst.getLoc(),284                           "CFA rule changed from [reg + offset], this "285                           "transition will not be checked");286    return;287  }288 289  auto PrevCFA = *MaybePrevCFA;290  auto NextCFA = *MaybeNextCFA;291 292  auto MaybeLLVMPrevReg = MCRI->getLLVMRegNum(PrevCFA.Reg, IsEH);293  const char *PrevCFARegName =294      MaybeLLVMPrevReg ? MCRI->getName(*MaybeLLVMPrevReg) : "";295  auto MaybeLLVMNextReg = MCRI->getLLVMRegNum(NextCFA.Reg, IsEH);296  const char *NextCFARegName =297      MaybeLLVMNextReg ? MCRI->getName(*MaybeLLVMNextReg) : "";298 299  if (PrevCFA == NextCFA) {         // Case 1300    if (!Writes.count(PrevCFA.Reg)) // Case 1.a301      return;302    // Case 1.b303    Context->reportError(304        Inst.getLoc(),305        formatv("modified CFA register {0} but not changed CFA rule",306                PrevCFARegName));307    return;308  }309 310  if (PrevCFA.Reg != NextCFA.Reg) { // Case 2.b311    Context->reportWarning(312        Inst.getLoc(),313        formatv("CFA register changed from register {0} to register {1}, "314                "validating this change is not implemented yet",315                PrevCFARegName, NextCFARegName));316    return;317  }318  // Case 2.c319  if (Writes.count(PrevCFA.Reg)) { // Case 2.c.i320    Context->reportWarning(321        Inst.getLoc(), formatv("CFA offset is changed from {0} to {1}, and CFA "322                               "register {2} is modified, but validating the "323                               "modification amount is not implemented yet",324                               PrevCFA.Offset, NextCFA.Offset, PrevCFARegName));325    return;326  }327  // Case 2.c.ii328  Context->reportError(329      Inst.getLoc(),330      formatv("did not modify CFA register {0} but changed CFA rule",331              PrevCFARegName));332}333