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