| 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-exception |
| 6 | // |
| 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 | |
| 32 | using namespace llvm; |
| 33 | |
| 34 | struct 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 | |
| 46 | static std::optional<CFARegOffsetInfo> |
| 47 | getCFARegOffsetInfo(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 | |
| 56 | static SmallSet<DWARFRegNum, 4> |
| 57 | getUnwindRuleRegSet(const dwarf::UnwindRow &UnwindRow, DWARFRegNum Reg) { |
| 58 | auto MaybeLoc = UnwindRow.getRegisterLocations().getRegisterLocation(RegNum: 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 is |
| 68 | // constant throughout the entire frame; only the way to calculate it might |
| 69 | // change. |
| 70 | case dwarf::UnwindLocation::Location::DWARFExpr: |
| 71 | // TODO: Expressions are not supported yet, but if they were to be |
| 72 | // supported, all the registers used in an expression should extracted and |
| 73 | // 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 | |
| 83 | DWARFCFIAnalysis::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(Reg: LLVMReg).IsArtificial || MCRI->get(Reg: LLVMReg).IsConstant) |
| 91 | continue; |
| 92 | |
| 93 | DWARFRegNum Reg = MCRI->getDwarfRegNum(Reg: LLVMReg, isEH: IsEH); |
| 94 | // TODO: this should be `undefined` instead of `same_value`, but because |
| 95 | // initial frame state doesn't have any directives about callee saved |
| 96 | // registers, every register is tracked. After initial frame state is |
| 97 | // corrected, this should be changed. |
| 98 | State.update(Directive: MCCFIInstruction::createSameValue(L: nullptr, Register: Reg)); |
| 99 | } |
| 100 | |
| 101 | // TODO: Ignoring PC should be in the initial frame state. |
| 102 | State.update(Directive: MCCFIInstruction::createUndefined( |
| 103 | L: nullptr, Register: MCRI->getDwarfRegNum(Reg: MCRI->getProgramCounter(), isEH: IsEH))); |
| 104 | |
| 105 | for (auto &&InitialFrameStateCFIDirective : |
| 106 | Context->getAsmInfo()->getInitialFrameState()) |
| 107 | State.update(Directive: InitialFrameStateCFIDirective); |
| 108 | |
| 109 | auto MaybeCurrentRow = State.getCurrentUnwindRow(); |
| 110 | assert(MaybeCurrentRow && "there should be at least one row" ); |
| 111 | auto MaybeCFA = getCFARegOffsetInfo(UnwindRow: *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 initial |
| 117 | // frame state. |
| 118 | State.update(Directive: MCCFIInstruction::createOffset(L: nullptr, Register: CFA.Reg, Offset: 0)); |
| 119 | |
| 120 | // Applying the prologue after default assumptions to overwrite them. |
| 121 | for (auto &&Directive : Prologue) |
| 122 | State.update(Directive); |
| 123 | } |
| 124 | |
| 125 | void DWARFCFIAnalysis::update(const MCInst &Inst, |
| 126 | ArrayRef<MCCFIInstruction> Directives) { |
| 127 | const MCInstrDesc &MCInstInfo = MCII.get(Opcode: 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; |
| 138 | for (unsigned I = 0; I < MCInstInfo.NumImplicitDefs; I++) |
| 139 | Writes.insert(V: MCRI->getDwarfRegNum( |
| 140 | Reg: getSuperReg(MCRI, Reg: MCInstInfo.implicit_defs()[I]), isEH: IsEH)); |
| 141 | |
| 142 | for (unsigned I = 0; I < Inst.getNumOperands(); I++) { |
| 143 | auto &&Op = Inst.getOperand(i: I); |
| 144 | if (Op.isReg()) { |
| 145 | if (I < MCInstInfo.getNumDefs()) |
| 146 | Writes.insert( |
| 147 | V: MCRI->getDwarfRegNum(Reg: getSuperReg(MCRI, Reg: Op.getReg()), isEH: IsEH)); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | auto MaybeNextRow = State.getCurrentUnwindRow(); |
| 152 | assert(MaybeNextRow && "previous row existed, so should the current row" ); |
| 153 | auto NextRow = *MaybeNextRow; |
| 154 | |
| 155 | checkCFADiff(Inst, PrevRow, NextRow, Writes); |
| 156 | |
| 157 | for (auto LLVMReg : getTrackingRegs(MCRI)) { |
| 158 | DWARFRegNum Reg = MCRI->getDwarfRegNum(Reg: LLVMReg, isEH: IsEH); |
| 159 | |
| 160 | checkRegDiff(Inst, Reg, PrevRow, NextRow, Writes); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | void DWARFCFIAnalysis::checkRegDiff(const MCInst &Inst, DWARFRegNum Reg, |
| 165 | const dwarf::UnwindRow &PrevRow, |
| 166 | const dwarf::UnwindRow &NextRow, |
| 167 | const SmallSet<DWARFRegNum, 4> &Writes) { |
| 168 | auto MaybePrevLoc = PrevRow.getRegisterLocations().getRegisterLocation(RegNum: Reg); |
| 169 | auto MaybeNextLoc = NextRow.getRegisterLocations().getRegisterLocation(RegNum: Reg); |
| 170 | |
| 171 | // All the tracked registers are added during initiation. So if a register is |
| 172 | // not added, should stay the same during execution and vice versa. |
| 173 | if (!MaybePrevLoc) { |
| 174 | assert(!MaybeNextLoc && "the register unwind info suddenly appeared here" ); |
| 175 | return; |
| 176 | } |
| 177 | assert(MaybeNextLoc && "the register unwind info suddenly vanished here" ); |
| 178 | |
| 179 | auto PrevLoc = MaybePrevLoc.value(); |
| 180 | auto NextLoc = MaybeNextLoc.value(); |
| 181 | |
| 182 | auto MaybeLLVMReg = MCRI->getLLVMRegNum(RegNum: Reg, isEH: IsEH); |
| 183 | if (!MaybeLLVMReg) { |
| 184 | if (!(PrevLoc == NextLoc)) |
| 185 | Context->reportWarning( |
| 186 | L: Inst.getLoc(), |
| 187 | Msg: formatv(Fmt: "the dwarf register {0} does not have a LLVM number, but its " |
| 188 | "unwind info changed. Ignoring this change" , |
| 189 | Vals&: Reg)); |
| 190 | return; |
| 191 | } |
| 192 | const char *RegName = MCRI->getName(RegNo: *MaybeLLVMReg); |
| 193 | |
| 194 | // Each case is annotated with its corresponding number as described in |
| 195 | // `llvm/include/llvm/DWARFCFIChecker/DWARFCFIAnalysis.h`. |
| 196 | |
| 197 | // TODO: Expressions are not supported yet, but if they were to be supported, |
| 198 | // note that structure equality for expressions is defined as follows: Two |
| 199 | // expressions are structurally equal if they become the same after you |
| 200 | // replace every operand with a placeholder. |
| 201 | |
| 202 | if (PrevLoc == NextLoc) { // Case 1 |
| 203 | for (DWARFRegNum UsedReg : getUnwindRuleRegSet(UnwindRow: PrevRow, Reg)) |
| 204 | if (Writes.count(V: UsedReg)) { // Case 1.b |
| 205 | auto MaybeLLVMUsedReg = MCRI->getLLVMRegNum(RegNum: UsedReg, isEH: IsEH); |
| 206 | assert(MaybeLLVMUsedReg && "instructions will always write to a " |
| 207 | "register that has an LLVM register number" ); |
| 208 | Context->reportError( |
| 209 | L: Inst.getLoc(), |
| 210 | Msg: formatv(Fmt: "changed register {1}, that register {0}'s unwinding rule " |
| 211 | "uses, but there is no CFI directives about it" , |
| 212 | Vals&: RegName, Vals: MCRI->getName(RegNo: *MaybeLLVMUsedReg))); |
| 213 | return; |
| 214 | } |
| 215 | return; // Case 1.a |
| 216 | } |
| 217 | // Case 2 |
| 218 | if (PrevLoc.getLocation() != NextLoc.getLocation()) { // Case 2.a |
| 219 | Context->reportWarning( |
| 220 | L: Inst.getLoc(), |
| 221 | Msg: formatv(Fmt: "validating changes happening to register {0} unwinding " |
| 222 | "rule structure is not implemented yet" , |
| 223 | Vals&: RegName)); |
| 224 | return; |
| 225 | } |
| 226 | auto &&PrevRegSet = getUnwindRuleRegSet(UnwindRow: PrevRow, Reg); |
| 227 | if (PrevRegSet != getUnwindRuleRegSet(UnwindRow: NextRow, Reg)) { // Case 2.b |
| 228 | Context->reportWarning( |
| 229 | L: Inst.getLoc(), |
| 230 | Msg: formatv(Fmt: "validating changes happening to register {0} unwinding " |
| 231 | "rule register set is not implemented yet" , |
| 232 | Vals&: RegName)); |
| 233 | return; |
| 234 | } |
| 235 | // Case 2.c |
| 236 | for (DWARFRegNum UsedReg : PrevRegSet) |
| 237 | if (Writes.count(V: UsedReg)) { // Case 2.c.i |
| 238 | Context->reportWarning( |
| 239 | L: Inst.getLoc(), |
| 240 | Msg: formatv(Fmt: "register {0} unwinding rule's offset is changed, and one of " |
| 241 | "the rule's registers is modified, but validating the " |
| 242 | "modification amount is not implemented yet" , |
| 243 | Vals&: RegName)); |
| 244 | return; |
| 245 | } |
| 246 | // Case 2.c.ii |
| 247 | Context->reportError( |
| 248 | L: Inst.getLoc(), Msg: formatv(Fmt: "register {0} unwinding rule's offset is changed, " |
| 249 | "but not any of the rule's registers are modified" , |
| 250 | Vals&: RegName)); |
| 251 | } |
| 252 | |
| 253 | void DWARFCFIAnalysis::checkCFADiff(const MCInst &Inst, |
| 254 | const dwarf::UnwindRow &PrevRow, |
| 255 | const dwarf::UnwindRow &NextRow, |
| 256 | const SmallSet<DWARFRegNum, 4> &Writes) { |
| 257 | |
| 258 | auto MaybePrevCFA = getCFARegOffsetInfo(UnwindRow: PrevRow); |
| 259 | auto MaybeNextCFA = getCFARegOffsetInfo(UnwindRow: NextRow); |
| 260 | |
| 261 | if (!MaybePrevCFA) { |
| 262 | if (MaybeNextCFA) { |
| 263 | Context->reportWarning(L: Inst.getLoc(), |
| 264 | Msg: "CFA rule changed to [reg + offset], this " |
| 265 | "transition will not be checked" ); |
| 266 | return; |
| 267 | } |
| 268 | |
| 269 | Context->reportWarning(L: Inst.getLoc(), |
| 270 | Msg: "CFA rule is not [reg + offset], not checking it" ); |
| 271 | return; |
| 272 | } |
| 273 | |
| 274 | if (!MaybeNextCFA) { |
| 275 | Context->reportWarning(L: Inst.getLoc(), |
| 276 | Msg: "CFA rule changed from [reg + offset], this " |
| 277 | "transition will not be checked" ); |
| 278 | return; |
| 279 | } |
| 280 | |
| 281 | auto PrevCFA = *MaybePrevCFA; |
| 282 | auto NextCFA = *MaybeNextCFA; |
| 283 | |
| 284 | auto MaybeLLVMPrevReg = MCRI->getLLVMRegNum(RegNum: PrevCFA.Reg, isEH: IsEH); |
| 285 | const char *PrevCFARegName = |
| 286 | MaybeLLVMPrevReg ? MCRI->getName(RegNo: *MaybeLLVMPrevReg) : "" ; |
| 287 | auto MaybeLLVMNextReg = MCRI->getLLVMRegNum(RegNum: NextCFA.Reg, isEH: IsEH); |
| 288 | const char *NextCFARegName = |
| 289 | MaybeLLVMNextReg ? MCRI->getName(RegNo: *MaybeLLVMNextReg) : "" ; |
| 290 | |
| 291 | if (PrevCFA == NextCFA) { // Case 1 |
| 292 | if (!Writes.count(V: PrevCFA.Reg)) // Case 1.a |
| 293 | return; |
| 294 | // Case 1.b |
| 295 | Context->reportError( |
| 296 | L: Inst.getLoc(), |
| 297 | Msg: formatv(Fmt: "modified CFA register {0} but not changed CFA rule" , |
| 298 | Vals&: PrevCFARegName)); |
| 299 | return; |
| 300 | } |
| 301 | |
| 302 | if (PrevCFA.Reg != NextCFA.Reg) { // Case 2.b |
| 303 | Context->reportWarning( |
| 304 | L: Inst.getLoc(), |
| 305 | Msg: formatv(Fmt: "CFA register changed from register {0} to register {1}, " |
| 306 | "validating this change is not implemented yet" , |
| 307 | Vals&: PrevCFARegName, Vals&: NextCFARegName)); |
| 308 | return; |
| 309 | } |
| 310 | // Case 2.c |
| 311 | if (Writes.count(V: PrevCFA.Reg)) { // Case 2.c.i |
| 312 | Context->reportWarning( |
| 313 | L: Inst.getLoc(), Msg: formatv(Fmt: "CFA offset is changed from {0} to {1}, and CFA " |
| 314 | "register {2} is modified, but validating the " |
| 315 | "modification amount is not implemented yet" , |
| 316 | Vals&: PrevCFA.Offset, Vals&: NextCFA.Offset, Vals&: PrevCFARegName)); |
| 317 | return; |
| 318 | } |
| 319 | // Case 2.c.ii |
| 320 | Context->reportError( |
| 321 | L: Inst.getLoc(), |
| 322 | Msg: formatv(Fmt: "did not modify CFA register {0} but changed CFA rule" , |
| 323 | Vals&: PrevCFARegName)); |
| 324 | } |
| 325 | |