LLVM 22.0.0git
DWARFUnwindTable.cpp
Go to the documentation of this file.
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
10#include "llvm/Support/Errc.h"
13#include <cassert>
14#include <cinttypes>
15#include <cstdint>
16#include <optional>
17
18using namespace llvm;
19using namespace dwarf;
20
22
24
26
28 return {Constant, InvalidRegisterNumber, Value, std::nullopt, false};
29}
30
32 return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false};
33}
34
36 return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true};
37}
38
41 std::optional<uint32_t> AddrSpace) {
42 return {RegPlusOffset, RegNum, Offset, AddrSpace, false};
43}
44
47 std::optional<uint32_t> AddrSpace) {
48 return {RegPlusOffset, RegNum, Offset, AddrSpace, true};
49}
50
52 return {Expr, false};
53}
54
56 return {Expr, true};
57}
58
60 if (Kind != RHS.Kind)
61 return false;
62 switch (Kind) {
63 case Unspecified:
64 case Undefined:
65 case Same:
66 return true;
67 case CFAPlusOffset:
68 return Offset == RHS.Offset && Dereference == RHS.Dereference;
69 case RegPlusOffset:
70 return RegNum == RHS.RegNum && Offset == RHS.Offset &&
71 Dereference == RHS.Dereference;
72 case DWARFExpr:
73 return *Expr == *RHS.Expr && Dereference == RHS.Dereference;
74 case Constant:
75 return Offset == RHS.Offset;
76 }
77 return false;
78}
79
82 const RegisterLocations *InitialLocs) {
83 // All the unwinding rows parsed during processing of the CFI program.
85
86 // State consists of CFA value and register locations.
87 std::vector<std::pair<UnwindLocation, RegisterLocations>> States;
88 for (const CFIProgram::Instruction &Inst : CFIP) {
89 switch (Inst.Opcode) {
90 case dwarf::DW_CFA_set_loc: {
91 // The DW_CFA_set_loc instruction takes a single operand that
92 // represents a target address. The required action is to create a new
93 // table row using the specified address as the location. All other
94 // values in the new row are initially identical to the current row.
95 // The new location value is always greater than the current one. If
96 // the segment_size field of this FDE's CIE is non- zero, the initial
97 // location is preceded by a segment selector of the given length
98 llvm::Expected<uint64_t> NewAddress = Inst.getOperandAsUnsigned(CFIP, 0);
99 if (!NewAddress)
100 return NewAddress.takeError();
101 if (*NewAddress <= Row.getAddress())
102 return createStringError(
104 "%s with adrress 0x%" PRIx64 " which must be greater than the "
105 "current row address 0x%" PRIx64,
106 CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress,
107 Row.getAddress());
108 Rows.push_back(Row);
109 Row.setAddress(*NewAddress);
110 break;
111 }
112
113 case dwarf::DW_CFA_advance_loc:
114 case dwarf::DW_CFA_advance_loc1:
115 case dwarf::DW_CFA_advance_loc2:
116 case dwarf::DW_CFA_advance_loc4: {
117 // The DW_CFA_advance instruction takes a single operand that
118 // represents a constant delta. The required action is to create a new
119 // table row with a location value that is computed by taking the
120 // current entry’s location value and adding the value of delta *
121 // code_alignment_factor. All other values in the new row are initially
122 // identical to the current row.
123 Rows.push_back(Row);
124 llvm::Expected<uint64_t> Offset = Inst.getOperandAsUnsigned(CFIP, 0);
125 if (!Offset)
126 return Offset.takeError();
127 Row.slideAddress(*Offset);
128 break;
129 }
130
131 case dwarf::DW_CFA_restore:
132 case dwarf::DW_CFA_restore_extended: {
133 // The DW_CFA_restore instruction takes a single operand (encoded with
134 // the opcode) that represents a register number. The required action
135 // is to change the rule for the indicated register to the rule
136 // assigned it by the initial_instructions in the CIE.
137 if (InitialLocs == nullptr)
138 return createStringError(
139 errc::invalid_argument, "%s encountered while parsing a CIE",
140 CFIP.callFrameString(Inst.Opcode).str().c_str());
141 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
142 if (!RegNum)
143 return RegNum.takeError();
144 if (std::optional<UnwindLocation> O =
145 InitialLocs->getRegisterLocation(*RegNum))
146 Row.getRegisterLocations().setRegisterLocation(*RegNum, *O);
147 else
148 Row.getRegisterLocations().removeRegisterLocation(*RegNum);
149 break;
150 }
151
152 case dwarf::DW_CFA_offset:
153 case dwarf::DW_CFA_offset_extended:
154 case dwarf::DW_CFA_offset_extended_sf: {
155 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
156 if (!RegNum)
157 return RegNum.takeError();
158 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
159 if (!Offset)
160 return Offset.takeError();
161 Row.getRegisterLocations().setRegisterLocation(
163 break;
164 }
165
166 case dwarf::DW_CFA_nop:
167 break;
168
169 case dwarf::DW_CFA_remember_state:
170 States.push_back(
171 std::make_pair(Row.getCFAValue(), Row.getRegisterLocations()));
172 break;
173
174 case dwarf::DW_CFA_restore_state:
175 if (States.empty())
177 "DW_CFA_restore_state without a matching "
178 "previous DW_CFA_remember_state");
179 Row.getCFAValue() = States.back().first;
180 Row.getRegisterLocations() = States.back().second;
181 States.pop_back();
182 break;
183
184 case dwarf::DW_CFA_GNU_window_save:
185 switch (CFIP.triple()) {
186 case Triple::aarch64:
188 case Triple::aarch64_32: {
189 // DW_CFA_GNU_window_save is used for different things on different
190 // architectures. For aarch64 it is known as
191 // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the
192 // value of the return address state between 1 and 0. If there is
193 // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it
194 // should be initially set to 1.
195 constexpr uint32_t AArch64DWARFPAuthRaState = 34;
196 auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
197 AArch64DWARFPAuthRaState);
198 if (LRLoc) {
199 if (LRLoc->getLocation() == UnwindLocation::Constant) {
200 // Toggle the constant value from 0 to 1 or 1 to 0.
201 LRLoc->setConstant(LRLoc->getConstant() ^ 1);
202 Row.getRegisterLocations().setRegisterLocation(
203 AArch64DWARFPAuthRaState, *LRLoc);
204 } else {
205 return createStringError(
207 "%s encountered when existing rule for this register is not "
208 "a constant",
209 CFIP.callFrameString(Inst.Opcode).str().c_str());
210 }
211 } else {
212 Row.getRegisterLocations().setRegisterLocation(
213 AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1));
214 }
215 break;
216 }
217
218 case Triple::sparc:
219 case Triple::sparcv9:
220 case Triple::sparcel:
221 for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) {
222 Row.getRegisterLocations().setRegisterLocation(
223 RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8));
224 }
225 break;
226
227 default: {
228 return createStringError(
230 "DW_CFA opcode %#x is not supported for architecture %s",
231 Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str());
232
233 break;
234 }
235 }
236 break;
237
238 case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: {
239 constexpr uint32_t AArch64DWARFPAuthRaState = 34;
240 auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
241 AArch64DWARFPAuthRaState);
242 if (LRLoc) {
243 if (LRLoc->getLocation() == UnwindLocation::Constant) {
244 // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0.
245 LRLoc->setConstant(LRLoc->getConstant() ^ 0x3);
246 } else {
247 return createStringError(
249 "%s encountered when existing rule for this register is not "
250 "a constant",
251 CFIP.callFrameString(Inst.Opcode).str().c_str());
252 }
253 } else {
254 Row.getRegisterLocations().setRegisterLocation(
255 AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3));
256 }
257 break;
258 }
259
260 case dwarf::DW_CFA_undefined: {
261 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
262 if (!RegNum)
263 return RegNum.takeError();
264 Row.getRegisterLocations().setRegisterLocation(
266 break;
267 }
268
269 case dwarf::DW_CFA_same_value: {
270 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
271 if (!RegNum)
272 return RegNum.takeError();
273 Row.getRegisterLocations().setRegisterLocation(
274 *RegNum, UnwindLocation::createSame());
275 break;
276 }
277
278 case dwarf::DW_CFA_GNU_args_size:
279 break;
280
281 case dwarf::DW_CFA_register: {
282 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
283 if (!RegNum)
284 return RegNum.takeError();
285 llvm::Expected<uint64_t> NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1);
286 if (!NewRegNum)
287 return NewRegNum.takeError();
288 Row.getRegisterLocations().setRegisterLocation(
289 *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0));
290 break;
291 }
292
293 case dwarf::DW_CFA_val_offset:
294 case dwarf::DW_CFA_val_offset_sf: {
295 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
296 if (!RegNum)
297 return RegNum.takeError();
298 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
299 if (!Offset)
300 return Offset.takeError();
301 Row.getRegisterLocations().setRegisterLocation(
303 break;
304 }
305
306 case dwarf::DW_CFA_expression: {
307 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
308 if (!RegNum)
309 return RegNum.takeError();
310 Row.getRegisterLocations().setRegisterLocation(
311 *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression));
312 break;
313 }
314
315 case dwarf::DW_CFA_val_expression: {
316 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
317 if (!RegNum)
318 return RegNum.takeError();
319 Row.getRegisterLocations().setRegisterLocation(
320 *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression));
321 break;
322 }
323
324 case dwarf::DW_CFA_def_cfa_register: {
325 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
326 if (!RegNum)
327 return RegNum.takeError();
328 if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset)
329 Row.getCFAValue() =
331 else
332 Row.getCFAValue().setRegister(*RegNum);
333 break;
334 }
335
336 case dwarf::DW_CFA_def_cfa_offset:
337 case dwarf::DW_CFA_def_cfa_offset_sf: {
338 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 0);
339 if (!Offset)
340 return Offset.takeError();
341 if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) {
342 return createStringError(
344 "%s found when CFA rule was not RegPlusOffset",
345 CFIP.callFrameString(Inst.Opcode).str().c_str());
346 }
347 Row.getCFAValue().setOffset(*Offset);
348 break;
349 }
350
351 case dwarf::DW_CFA_def_cfa:
352 case dwarf::DW_CFA_def_cfa_sf: {
353 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
354 if (!RegNum)
355 return RegNum.takeError();
356 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
357 if (!Offset)
358 return Offset.takeError();
359 Row.getCFAValue() =
361 break;
362 }
363
364 case dwarf::DW_CFA_LLVM_def_aspace_cfa:
365 case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: {
366 llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
367 if (!RegNum)
368 return RegNum.takeError();
369 llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
370 if (!Offset)
371 return Offset.takeError();
372 llvm::Expected<uint32_t> CFAAddrSpace =
373 Inst.getOperandAsUnsigned(CFIP, 2);
374 if (!CFAAddrSpace)
375 return CFAAddrSpace.takeError();
377 *RegNum, *Offset, *CFAAddrSpace);
378 break;
379 }
380
381 case dwarf::DW_CFA_def_cfa_expression:
382 Row.getCFAValue() =
384 break;
385 }
386 }
387 return Rows;
388}
Value * RHS
Tagged union holding either a T or a Error.
Definition: Error.h:485
Error takeError()
Take ownership of the stored error.
Definition: Error.h:612
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:233
@ aarch64_be
Definition: Triple.h:55
@ aarch64_32
Definition: Triple.h:56
static LLVM_ABI StringRef getArchTypeName(ArchType Kind)
Get the canonical name for the Kind architecture.
Definition: Triple.cpp:24
LLVM Value Representation.
Definition: Value.h:75
Represent a sequence of Call Frame Information instructions that, when read in order,...
Triple::ArchType triple() const
LLVM_ABI StringRef callFrameString(unsigned Opcode) const
Get a DWARF CFI call frame string for the given DW_CFA opcode.
A class that can track all registers with locations in a UnwindRow object.
std::optional< UnwindLocation > getRegisterLocation(uint32_t RegNum) const
Return the location for the register in RegNum if there is a location.
A class that represents a location for the Call Frame Address (CFA) or a register.
static LLVM_ABI UnwindLocation createUndefined()
Create a location where the value is undefined and not available.
static LLVM_ABI UnwindLocation createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, std::optional< uint32_t > AddrSpace=std::nullopt)
static LLVM_ABI UnwindLocation createIsRegisterPlusOffset(uint32_t Reg, int32_t Off, std::optional< uint32_t > AddrSpace=std::nullopt)
Create a location where the saved value is in (Deref == false) or at (Deref == true) a regiser plus a...
static LLVM_ABI UnwindLocation createAtDWARFExpression(DWARFExpression Expr)
static LLVM_ABI UnwindLocation createUnspecified()
Create a location whose rule is set to Unspecified.
LLVM_ABI bool operator==(const UnwindLocation &RHS) const
static LLVM_ABI UnwindLocation createIsDWARFExpression(DWARFExpression Expr)
Create a location whose value is the result of evaluating a DWARF expression.
@ Undefined
Register is not available and can't be recovered.
@ Constant
Value is a constant value contained in "Offset": reg = Offset.
@ DWARFExpr
Register or CFA value is in or at a value found by evaluating a DWARF expression: reg = eval(dwarf_ex...
@ Same
Register value is in the register, nothing needs to be done to unwind it: reg = reg.
@ CFAPlusOffset
Register is in or at the CFA plus an offset: reg = CFA + offset reg = defef(CFA + offset)
@ RegPlusOffset
Register or CFA is in or at a register plus offset, optionally in an address space: reg = reg + offse...
static LLVM_ABI UnwindLocation createIsConstant(int32_t Value)
static LLVM_ABI UnwindLocation createAtCFAPlusOffset(int32_t Off)
static LLVM_ABI UnwindLocation createSame()
Create a location where the value is known to be in the register itself.
static LLVM_ABI UnwindLocation createIsCFAPlusOffset(int32_t Off)
Create a location that is in (Deref == false) or at (Deref == true) the CFA plus an offset.
A class that represents a single row in the unwind table that is decoded by parsing the DWARF Call Fr...
std::vector< UnwindRow > RowContainer
constexpr uint32_t InvalidRegisterNumber
LLVM_ABI Expected< UnwindTable::RowContainer > parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, const RegisterLocations *InitialLocs)
Parse the information in the CFIProgram and update the CurrRow object that the state machine describe...
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:477
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition: Error.h:1305
An instruction consists of a DWARF CFI opcode and an optional sequence of operands.