xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.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/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"
10 #include "llvm/Support/Errc.h"
11 #include "llvm/Support/ErrorHandling.h"
12 #include "llvm/Support/raw_ostream.h"
13 #include <cassert>
14 #include <cinttypes>
15 #include <cstdint>
16 #include <optional>
17 
18 using namespace llvm;
19 using namespace dwarf;
20 
createUnspecified()21 UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; }
22 
createUndefined()23 UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; }
24 
createSame()25 UnwindLocation UnwindLocation::createSame() { return {Same}; }
26 
createIsConstant(int32_t Value)27 UnwindLocation UnwindLocation::createIsConstant(int32_t Value) {
28   return {Constant, InvalidRegisterNumber, Value, std::nullopt, false};
29 }
30 
createIsCFAPlusOffset(int32_t Offset)31 UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {
32   return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false};
33 }
34 
createAtCFAPlusOffset(int32_t Offset)35 UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {
36   return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true};
37 }
38 
39 UnwindLocation
createIsRegisterPlusOffset(uint32_t RegNum,int32_t Offset,std::optional<uint32_t> AddrSpace)40 UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
41                                            std::optional<uint32_t> AddrSpace) {
42   return {RegPlusOffset, RegNum, Offset, AddrSpace, false};
43 }
44 
45 UnwindLocation
createAtRegisterPlusOffset(uint32_t RegNum,int32_t Offset,std::optional<uint32_t> AddrSpace)46 UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
47                                            std::optional<uint32_t> AddrSpace) {
48   return {RegPlusOffset, RegNum, Offset, AddrSpace, true};
49 }
50 
createIsDWARFExpression(DWARFExpression Expr)51 UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {
52   return {Expr, false};
53 }
54 
createAtDWARFExpression(DWARFExpression Expr)55 UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) {
56   return {Expr, true};
57 }
58 
operator ==(const UnwindLocation & RHS) const59 bool UnwindLocation::operator==(const UnwindLocation &RHS) const {
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 
80 Expected<UnwindTable::RowContainer>
parseRows(const CFIProgram & CFIP,UnwindRow & Row,const RegisterLocations * InitialLocs)81 llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
82                        const RegisterLocations *InitialLocs) {
83   // All the unwinding rows parsed during processing of the CFI program.
84   UnwindTable::RowContainer Rows;
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(
103             errc::invalid_argument,
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(
162           *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset));
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())
176         return createStringError(errc::invalid_argument,
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:
187       case Triple::aarch64_be:
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(
206                 errc::invalid_argument,
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(
229             errc::not_supported,
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(
248               errc::invalid_argument,
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(
265           *RegNum, UnwindLocation::createUndefined());
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(
302           *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset));
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() =
330             UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0);
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(
343             errc::invalid_argument,
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() =
360           UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset);
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();
376       Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset(
377           *RegNum, *Offset, *CFAAddrSpace);
378       break;
379     }
380 
381     case dwarf::DW_CFA_def_cfa_expression:
382       Row.getCFAValue() =
383           UnwindLocation::createIsDWARFExpression(*Inst.Expression);
384       break;
385     }
386   }
387   return Rows;
388 }
389