xref: /freebsd/contrib/llvm-project/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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