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
19using namespace llvm;
20
21std::optional<dwarf::UnwindRow> DWARFCFIState::getCurrentUnwindRow() const {
22 if (!IsInitiated)
23 return std::nullopt;
24 return Row;
25}
26
27void 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, CurrRow&: NewRow, InitialLocs: nullptr).takeError()) {
49 Context->reportError(
50 L: Directive.getLoc(),
51 Msg: formatv(Fmt: "could not parse this CFI directive due to: {0}",
52 Vals: toString(E: std::move(Err))));
53
54 // Proceed the analysis by ignoring this CFI directive.
55 return;
56 }
57
58 Row = std::move(NewRow);
59 IsInitiated = true;
60}
61
62dwarf::CFIProgram DWARFCFIState::convert(MCCFIInstruction Directive) {
63 auto CFIP = dwarf::CFIProgram(
64 /* CodeAlignmentFactor */ 1, /* DataAlignmentFactor */ 1,
65 Context->getTargetTriple().getArch());
66
67 switch (Directive.getOperation()) {
68 case MCCFIInstruction::OpSameValue:
69 CFIP.addInstruction(Opcode: dwarf::DW_CFA_same_value, Operand1: Directive.getRegister());
70 break;
71 case MCCFIInstruction::OpRememberState:
72 // TODO: remember state is not supported yet, the following line does not
73 // work:
74 // CFIP.addInstruction(dwarf::DW_CFA_remember_state);
75 // The reason is explained in the `DWARFCFIState::update` method where
76 // `dwarf::parseRows` is used.
77 Context->reportWarning(L: Directive.getLoc(),
78 Msg: "this directive is not supported, ignoring it");
79 break;
80 case MCCFIInstruction::OpRestoreState:
81 // TODO: restore state is not supported yet, the following line does not
82 // work:
83 // CFIP.addInstruction(dwarf::DW_CFA_restore_state);
84 // The reason is explained in the `DWARFCFIState::update` method where
85 // `dwarf::parseRows` is used.
86 Context->reportWarning(L: Directive.getLoc(),
87 Msg: "this directive is not supported, ignoring it");
88 break;
89 case MCCFIInstruction::OpOffset:
90 CFIP.addInstruction(Opcode: dwarf::DW_CFA_offset, Operand1: Directive.getRegister(),
91 Operand2: Directive.getOffset());
92 break;
93 case MCCFIInstruction::OpLLVMDefAspaceCfa:
94 CFIP.addInstruction(Opcode: dwarf::DW_CFA_LLVM_def_aspace_cfa,
95 Operand1: Directive.getRegister());
96 break;
97 case MCCFIInstruction::OpDefCfaRegister:
98 CFIP.addInstruction(Opcode: dwarf::DW_CFA_def_cfa_register,
99 Operand1: Directive.getRegister());
100 break;
101 case MCCFIInstruction::OpDefCfaOffset:
102 CFIP.addInstruction(Opcode: dwarf::DW_CFA_def_cfa_offset, Operand1: Directive.getOffset());
103 break;
104 case MCCFIInstruction::OpDefCfa:
105 CFIP.addInstruction(Opcode: dwarf::DW_CFA_def_cfa, Operand1: Directive.getRegister(),
106 Operand2: Directive.getOffset());
107 break;
108 case MCCFIInstruction::OpRelOffset:
109 assert(
110 IsInitiated &&
111 "cannot define relative offset to a non-existing CFA unwinding rule");
112
113 CFIP.addInstruction(Opcode: dwarf::DW_CFA_offset, Operand1: Directive.getRegister(),
114 Operand2: Directive.getOffset() - Row.getCFAValue().getOffset());
115 break;
116 case MCCFIInstruction::OpAdjustCfaOffset:
117 assert(IsInitiated &&
118 "cannot adjust CFA offset of a non-existing CFA unwinding rule");
119
120 CFIP.addInstruction(Opcode: dwarf::DW_CFA_def_cfa_offset,
121 Operand1: Directive.getOffset() + Row.getCFAValue().getOffset());
122 break;
123 case MCCFIInstruction::OpEscape:
124 // TODO: DWARFExpressions are not supported yet, ignoring expression here.
125 Context->reportWarning(L: Directive.getLoc(),
126 Msg: "this directive is not supported, ignoring it");
127 break;
128 case MCCFIInstruction::OpRestore:
129 // The `.cfi_restore register` directive restores the register's unwinding
130 // information to its CIE value. However, assemblers decide where CIE ends
131 // and the FDE starts, so the functionality of this directive depends on the
132 // assembler's decision and cannot be validated.
133 Context->reportWarning(
134 L: Directive.getLoc(),
135 Msg: "this directive behavior depends on the assembler, ignoring it");
136 break;
137 case MCCFIInstruction::OpUndefined:
138 CFIP.addInstruction(Opcode: dwarf::DW_CFA_undefined, Operand1: Directive.getRegister());
139 break;
140 case MCCFIInstruction::OpRegister:
141 CFIP.addInstruction(Opcode: dwarf::DW_CFA_register, Operand1: Directive.getRegister(),
142 Operand2: Directive.getRegister2());
143 break;
144 case MCCFIInstruction::OpWindowSave:
145 CFIP.addInstruction(Opcode: dwarf::DW_CFA_GNU_window_save);
146 break;
147 case MCCFIInstruction::OpNegateRAState:
148 CFIP.addInstruction(Opcode: dwarf::DW_CFA_AARCH64_negate_ra_state);
149 break;
150 case MCCFIInstruction::OpNegateRAStateWithPC:
151 CFIP.addInstruction(Opcode: dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc);
152 break;
153 case MCCFIInstruction::OpGnuArgsSize:
154 CFIP.addInstruction(Opcode: dwarf::DW_CFA_GNU_args_size);
155 break;
156 case MCCFIInstruction::OpLabel:
157 // `.cfi_label` does not have any functional effect on unwinding process.
158 break;
159 case MCCFIInstruction::OpValOffset:
160 CFIP.addInstruction(Opcode: dwarf::DW_CFA_val_offset, Operand1: Directive.getRegister(),
161 Operand2: Directive.getOffset());
162 break;
163 }
164
165 return CFIP;
166}
167