xref: /freebsd/contrib/llvm-project/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric 
9*700637cbSDimitry Andric #include "llvm/DWARFCFIChecker/DWARFCFIState.h"
10*700637cbSDimitry Andric #include "llvm/BinaryFormat/Dwarf.h"
11*700637cbSDimitry Andric #include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"
12*700637cbSDimitry Andric #include "llvm/MC/MCDwarf.h"
13*700637cbSDimitry Andric #include "llvm/Support/Error.h"
14*700637cbSDimitry Andric #include "llvm/Support/ErrorHandling.h"
15*700637cbSDimitry Andric #include "llvm/Support/FormatVariadic.h"
16*700637cbSDimitry Andric #include <cassert>
17*700637cbSDimitry Andric #include <optional>
18*700637cbSDimitry Andric 
19*700637cbSDimitry Andric using namespace llvm;
20*700637cbSDimitry Andric 
getCurrentUnwindRow() const21*700637cbSDimitry Andric std::optional<dwarf::UnwindRow> DWARFCFIState::getCurrentUnwindRow() const {
22*700637cbSDimitry Andric   if (!IsInitiated)
23*700637cbSDimitry Andric     return std::nullopt;
24*700637cbSDimitry Andric   return Row;
25*700637cbSDimitry Andric }
26*700637cbSDimitry Andric 
update(const MCCFIInstruction & Directive)27*700637cbSDimitry Andric void DWARFCFIState::update(const MCCFIInstruction &Directive) {
28*700637cbSDimitry Andric   auto CFIP = convert(Directive);
29*700637cbSDimitry Andric 
30*700637cbSDimitry Andric   // This is a copy of the current row, its value will be updated by
31*700637cbSDimitry Andric   // `parseRows`.
32*700637cbSDimitry Andric   dwarf::UnwindRow NewRow = Row;
33*700637cbSDimitry Andric 
34*700637cbSDimitry Andric   // `parseRows` updates the current row by applying the `CFIProgram` to it.
35*700637cbSDimitry Andric   // During this process, it may create multiple rows preceding the newly
36*700637cbSDimitry Andric   // updated row and following the previous rows. These middle rows are stored
37*700637cbSDimitry Andric   // in `PrecedingRows`. For now, there is no need to store these rows in the
38*700637cbSDimitry Andric   // state, so they are ignored in the end.
39*700637cbSDimitry Andric   dwarf::UnwindTable::RowContainer PrecedingRows;
40*700637cbSDimitry Andric 
41*700637cbSDimitry Andric   // TODO: `.cfi_remember_state` and `.cfi_restore_state` directives are not
42*700637cbSDimitry Andric   // supported yet. The reason is that `parseRows` expects the stack of states
43*700637cbSDimitry Andric   // to be produced and used in a single `CFIProgram`. However, in this use
44*700637cbSDimitry Andric   // case, each instruction creates its own `CFIProgram`, which means the stack
45*700637cbSDimitry Andric   // of states is forgotten between instructions. To fix it, `parseRows` should
46*700637cbSDimitry Andric   // be refactored to read the current stack of states from the argument and
47*700637cbSDimitry Andric   // update it based on the `CFIProgram.`
48*700637cbSDimitry Andric   if (Error Err = parseRows(CFIP, NewRow, nullptr).takeError()) {
49*700637cbSDimitry Andric     Context->reportError(
50*700637cbSDimitry Andric         Directive.getLoc(),
51*700637cbSDimitry Andric         formatv("could not parse this CFI directive due to: {0}",
52*700637cbSDimitry Andric                 toString(std::move(Err))));
53*700637cbSDimitry Andric 
54*700637cbSDimitry Andric     // Proceed the analysis by ignoring this CFI directive.
55*700637cbSDimitry Andric     return;
56*700637cbSDimitry Andric   }
57*700637cbSDimitry Andric 
58*700637cbSDimitry Andric   Row = NewRow;
59*700637cbSDimitry Andric   IsInitiated = true;
60*700637cbSDimitry Andric }
61*700637cbSDimitry Andric 
convert(MCCFIInstruction Directive)62*700637cbSDimitry Andric dwarf::CFIProgram DWARFCFIState::convert(MCCFIInstruction Directive) {
63*700637cbSDimitry Andric   auto CFIP = dwarf::CFIProgram(
64*700637cbSDimitry Andric       /* CodeAlignmentFactor */ 1, /* DataAlignmentFactor */ 1,
65*700637cbSDimitry Andric       Context->getTargetTriple().getArch());
66*700637cbSDimitry Andric 
67*700637cbSDimitry Andric   auto MaybeCurrentRow = getCurrentUnwindRow();
68*700637cbSDimitry Andric   switch (Directive.getOperation()) {
69*700637cbSDimitry Andric   case MCCFIInstruction::OpSameValue:
70*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_same_value, Directive.getRegister());
71*700637cbSDimitry Andric     break;
72*700637cbSDimitry Andric   case MCCFIInstruction::OpRememberState:
73*700637cbSDimitry Andric     // TODO: remember state is not supported yet, the following line does not
74*700637cbSDimitry Andric     // work:
75*700637cbSDimitry Andric     // CFIP.addInstruction(dwarf::DW_CFA_remember_state);
76*700637cbSDimitry Andric     // The reason is explained in the `DWARFCFIState::update` method where
77*700637cbSDimitry Andric     // `dwarf::parseRows` is used.
78*700637cbSDimitry Andric     Context->reportWarning(Directive.getLoc(),
79*700637cbSDimitry Andric                            "this directive is not supported, ignoring it");
80*700637cbSDimitry Andric     break;
81*700637cbSDimitry Andric   case MCCFIInstruction::OpRestoreState:
82*700637cbSDimitry Andric     // TODO: restore state is not supported yet, the following line does not
83*700637cbSDimitry Andric     // work:
84*700637cbSDimitry Andric     // CFIP.addInstruction(dwarf::DW_CFA_restore_state);
85*700637cbSDimitry Andric     // The reason is explained in the `DWARFCFIState::update` method where
86*700637cbSDimitry Andric     // `dwarf::parseRows` is used.
87*700637cbSDimitry Andric     Context->reportWarning(Directive.getLoc(),
88*700637cbSDimitry Andric                            "this directive is not supported, ignoring it");
89*700637cbSDimitry Andric     break;
90*700637cbSDimitry Andric   case MCCFIInstruction::OpOffset:
91*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_offset, Directive.getRegister(),
92*700637cbSDimitry Andric                         Directive.getOffset());
93*700637cbSDimitry Andric     break;
94*700637cbSDimitry Andric   case MCCFIInstruction::OpLLVMDefAspaceCfa:
95*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_LLVM_def_aspace_cfa,
96*700637cbSDimitry Andric                         Directive.getRegister());
97*700637cbSDimitry Andric     break;
98*700637cbSDimitry Andric   case MCCFIInstruction::OpDefCfaRegister:
99*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_def_cfa_register,
100*700637cbSDimitry Andric                         Directive.getRegister());
101*700637cbSDimitry Andric     break;
102*700637cbSDimitry Andric   case MCCFIInstruction::OpDefCfaOffset:
103*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_def_cfa_offset, Directive.getOffset());
104*700637cbSDimitry Andric     break;
105*700637cbSDimitry Andric   case MCCFIInstruction::OpDefCfa:
106*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_def_cfa, Directive.getRegister(),
107*700637cbSDimitry Andric                         Directive.getOffset());
108*700637cbSDimitry Andric     break;
109*700637cbSDimitry Andric   case MCCFIInstruction::OpRelOffset:
110*700637cbSDimitry Andric     assert(
111*700637cbSDimitry Andric         IsInitiated &&
112*700637cbSDimitry Andric         "cannot define relative offset to a non-existing CFA unwinding rule");
113*700637cbSDimitry Andric 
114*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_offset, Directive.getRegister(),
115*700637cbSDimitry Andric                         Directive.getOffset() - Row.getCFAValue().getOffset());
116*700637cbSDimitry Andric     break;
117*700637cbSDimitry Andric   case MCCFIInstruction::OpAdjustCfaOffset:
118*700637cbSDimitry Andric     assert(IsInitiated &&
119*700637cbSDimitry Andric            "cannot adjust CFA offset of a non-existing CFA unwinding rule");
120*700637cbSDimitry Andric 
121*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_def_cfa_offset,
122*700637cbSDimitry Andric                         Directive.getOffset() + Row.getCFAValue().getOffset());
123*700637cbSDimitry Andric     break;
124*700637cbSDimitry Andric   case MCCFIInstruction::OpEscape:
125*700637cbSDimitry Andric     // TODO: DWARFExpressions are not supported yet, ignoring expression here.
126*700637cbSDimitry Andric     Context->reportWarning(Directive.getLoc(),
127*700637cbSDimitry Andric                            "this directive is not supported, ignoring it");
128*700637cbSDimitry Andric     break;
129*700637cbSDimitry Andric   case MCCFIInstruction::OpRestore:
130*700637cbSDimitry Andric     // The `.cfi_restore register` directive restores the register's unwinding
131*700637cbSDimitry Andric     // information to its CIE value. However, assemblers decide where CIE ends
132*700637cbSDimitry Andric     // and the FDE starts, so the functionality of this directive depends on the
133*700637cbSDimitry Andric     // assembler's decision and cannot be validated.
134*700637cbSDimitry Andric     Context->reportWarning(
135*700637cbSDimitry Andric         Directive.getLoc(),
136*700637cbSDimitry Andric         "this directive behavior depends on the assembler, ignoring it");
137*700637cbSDimitry Andric     break;
138*700637cbSDimitry Andric   case MCCFIInstruction::OpUndefined:
139*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_undefined, Directive.getRegister());
140*700637cbSDimitry Andric     break;
141*700637cbSDimitry Andric   case MCCFIInstruction::OpRegister:
142*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_register, Directive.getRegister(),
143*700637cbSDimitry Andric                         Directive.getRegister2());
144*700637cbSDimitry Andric     break;
145*700637cbSDimitry Andric   case MCCFIInstruction::OpWindowSave:
146*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_GNU_window_save);
147*700637cbSDimitry Andric     break;
148*700637cbSDimitry Andric   case MCCFIInstruction::OpNegateRAState:
149*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_AARCH64_negate_ra_state);
150*700637cbSDimitry Andric     break;
151*700637cbSDimitry Andric   case MCCFIInstruction::OpNegateRAStateWithPC:
152*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc);
153*700637cbSDimitry Andric     break;
154*700637cbSDimitry Andric   case MCCFIInstruction::OpGnuArgsSize:
155*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_GNU_args_size);
156*700637cbSDimitry Andric     break;
157*700637cbSDimitry Andric   case MCCFIInstruction::OpLabel:
158*700637cbSDimitry Andric     // `.cfi_label` does not have any functional effect on unwinding process.
159*700637cbSDimitry Andric     break;
160*700637cbSDimitry Andric   case MCCFIInstruction::OpValOffset:
161*700637cbSDimitry Andric     CFIP.addInstruction(dwarf::DW_CFA_val_offset, Directive.getRegister(),
162*700637cbSDimitry Andric                         Directive.getOffset());
163*700637cbSDimitry Andric     break;
164*700637cbSDimitry Andric   }
165*700637cbSDimitry Andric 
166*700637cbSDimitry Andric   return CFIP;
167*700637cbSDimitry Andric }
168