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