1//===- DWARFDebugFrame.h - Parsing of .debug_frame ------------------------===//
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/DWARFDebugFrame.h"
10#include "llvm/ADT/DenseMap.h"
11#include "llvm/ADT/StringExtras.h"
12#include "llvm/ADT/StringRef.h"
13#include "llvm/BinaryFormat/Dwarf.h"
14#include "llvm/DebugInfo/DIContext.h"
15#include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h"
16#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
17#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h"
18#include "llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h"
19#include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h"
20#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
21#include "llvm/Support/Compiler.h"
22#include "llvm/Support/DataExtractor.h"
23#include "llvm/Support/Errc.h"
24#include "llvm/Support/Error.h"
25#include "llvm/Support/ErrorHandling.h"
26#include "llvm/Support/Format.h"
27#include "llvm/Support/FormatAdapters.h"
28#include "llvm/Support/FormatVariadic.h"
29#include "llvm/Support/raw_ostream.h"
30#include <cassert>
31#include <cinttypes>
32#include <cstdint>
33#include <optional>
34
35using namespace llvm;
36using namespace dwarf;
37
38Expected<UnwindTable> llvm::dwarf::createUnwindTable(const FDE *Fde) {
39 const CIE *Cie = Fde->getLinkedCIE();
40 if (Cie == nullptr)
41 return createStringError(EC: errc::invalid_argument,
42 Fmt: "unable to get CIE for FDE at offset 0x%" PRIx64,
43 Vals: Fde->getOffset());
44
45 // Rows will be empty if there are no CFI instructions.
46 if (Cie->cfis().empty() && Fde->cfis().empty())
47 return UnwindTable({});
48
49 UnwindTable::RowContainer CieRows;
50 UnwindRow Row;
51 Row.setAddress(Fde->getInitialLocation());
52 if (Error CieError = parseRows(CFIP: Cie->cfis(), CurrRow&: Row, InitialLocs: nullptr).moveInto(Value&: CieRows))
53 return std::move(CieError);
54 // We need to save the initial locations of registers from the CIE parsing
55 // in case we run into DW_CFA_restore or DW_CFA_restore_extended opcodes.
56 UnwindTable::RowContainer FdeRows;
57 const RegisterLocations InitialLocs = Row.getRegisterLocations();
58 if (Error FdeError =
59 parseRows(CFIP: Fde->cfis(), CurrRow&: Row, InitialLocs: &InitialLocs).moveInto(Value&: FdeRows))
60 return std::move(FdeError);
61
62 UnwindTable::RowContainer AllRows;
63 AllRows.insert(position: AllRows.end(), first: CieRows.begin(), last: CieRows.end());
64 AllRows.insert(position: AllRows.end(), first: FdeRows.begin(), last: FdeRows.end());
65
66 // May be all the CFI instructions were DW_CFA_nop amd Row becomes empty.
67 // Do not add that to the unwind table.
68 if (Row.getRegisterLocations().hasLocations() ||
69 Row.getCFAValue().getLocation() != UnwindLocation::Unspecified)
70 AllRows.push_back(x: Row);
71 return UnwindTable(std::move(AllRows));
72}
73
74Expected<UnwindTable> llvm::dwarf::createUnwindTable(const CIE *Cie) {
75 // Rows will be empty if there are no CFI instructions.
76 if (Cie->cfis().empty())
77 return UnwindTable({});
78
79 UnwindTable::RowContainer Rows;
80 UnwindRow Row;
81 if (Error CieError = parseRows(CFIP: Cie->cfis(), CurrRow&: Row, InitialLocs: nullptr).moveInto(Value&: Rows))
82 return std::move(CieError);
83 // May be all the CFI instructions were DW_CFA_nop amd Row becomes empty.
84 // Do not add that to the unwind table.
85 if (Row.getRegisterLocations().hasLocations() ||
86 Row.getCFAValue().getLocation() != UnwindLocation::Unspecified)
87 Rows.push_back(x: Row);
88 return UnwindTable(std::move(Rows));
89}
90
91// Returns the CIE identifier to be used by the requested format.
92// CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5.
93// For CIE ID in .eh_frame sections see
94// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
95constexpr uint64_t getCIEId(bool IsDWARF64, bool IsEH) {
96 if (IsEH)
97 return 0;
98 if (IsDWARF64)
99 return DW64_CIE_ID;
100 return DW_CIE_ID;
101}
102
103void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
104 // A CIE with a zero length is a terminator entry in the .eh_frame section.
105 if (DumpOpts.IsEH && Length == 0) {
106 OS << formatv(Fmt: "{0:x-8}", Vals: Offset) << " ZERO terminator\n";
107 return;
108 }
109
110 OS << formatv(Fmt: "{0:x-8}", Vals: Offset)
111 << formatv(Fmt: " {0:x-}",
112 Vals: fmt_align(Item: Length, Where: AlignStyle::Right, Amount: IsDWARF64 ? 16 : 8, Fill: '0'))
113 << formatv(Fmt: " {0:x-}",
114 Vals: fmt_align(Item: getCIEId(IsDWARF64, IsEH: DumpOpts.IsEH), Where: AlignStyle::Right,
115 Amount: IsDWARF64 && !DumpOpts.IsEH ? 16 : 8, Fill: '0'))
116 << " CIE\n"
117 << " Format: " << FormatString(IsDWARF64) << "\n";
118 if (DumpOpts.IsEH && Version != 1)
119 OS << "WARNING: unsupported CIE version\n";
120 OS << formatv(Fmt: " Version: {0}\n", Vals: Version)
121 << " Augmentation: \"" << Augmentation << "\"\n";
122 if (Version >= 4) {
123 OS << formatv(Fmt: " Address size: {0}\n", Vals: (uint32_t)AddressSize);
124 OS << formatv(Fmt: " Segment desc size: {0}\n",
125 Vals: (uint32_t)SegmentDescriptorSize);
126 }
127 OS << formatv(Fmt: " Code alignment factor: {0}\n",
128 Vals: (uint32_t)CodeAlignmentFactor);
129 OS << formatv(Fmt: " Data alignment factor: {0}\n", Vals: (int32_t)DataAlignmentFactor);
130 OS << formatv(Fmt: " Return address column: {0}\n",
131 Vals: (int32_t)ReturnAddressRegister);
132 if (Personality)
133 OS << formatv(Fmt: " Personality Address: {0:x-16}\n", Vals: *Personality);
134 if (!AugmentationData.empty()) {
135 OS << " Augmentation data: ";
136 for (uint8_t Byte : AugmentationData)
137 OS << ' ' << hexdigit(X: Byte >> 4) << hexdigit(X: Byte & 0xf);
138 OS << "\n";
139 }
140 OS << "\n";
141 printCFIProgram(P: CFIs, OS, DumpOpts, /*IndentLevel=*/1,
142 /*InitialLocation=*/Address: {});
143 OS << "\n";
144
145 if (Expected<UnwindTable> RowsOrErr = createUnwindTable(Cie: this))
146 printUnwindTable(Rows: *RowsOrErr, OS, DumpOpts, IndentLevel: 1);
147 else {
148 DumpOpts.RecoverableErrorHandler(joinErrors(
149 E1: createStringError(EC: errc::invalid_argument,
150 S: "decoding the CIE opcodes into rows failed"),
151 E2: RowsOrErr.takeError()));
152 }
153 OS << "\n";
154}
155
156void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
157 OS << formatv(Fmt: "{0:x-8}", Vals: Offset)
158 << formatv(Fmt: " {0:x-}",
159 Vals: fmt_align(Item: Length, Where: AlignStyle::Right, Amount: IsDWARF64 ? 16 : 8, Fill: '0'))
160 << formatv(Fmt: " {0:x-}", Vals: fmt_align(Item: CIEPointer, Where: AlignStyle::Right,
161 Amount: IsDWARF64 && !DumpOpts.IsEH ? 16 : 8, Fill: '0'))
162 << " FDE cie=";
163 if (LinkedCIE)
164 OS << formatv(Fmt: "{0:x-8}", Vals: LinkedCIE->getOffset());
165 else
166 OS << "<invalid offset>";
167 OS << formatv(Fmt: " pc={0:x-8}...{1:x-8}\n", Vals: InitialLocation,
168 Vals: InitialLocation + AddressRange);
169 OS << " Format: " << FormatString(IsDWARF64) << "\n";
170 if (LSDAAddress)
171 OS << formatv(Fmt: " LSDA Address: {0:x-16}\n", Vals: *LSDAAddress);
172 printCFIProgram(P: CFIs, OS, DumpOpts, /*IndentLevel=*/1, Address: InitialLocation);
173 OS << "\n";
174
175 if (Expected<UnwindTable> RowsOrErr = createUnwindTable(Fde: this))
176 printUnwindTable(Rows: *RowsOrErr, OS, DumpOpts, IndentLevel: 1);
177 else {
178 DumpOpts.RecoverableErrorHandler(joinErrors(
179 E1: createStringError(EC: errc::invalid_argument,
180 S: "decoding the FDE opcodes into rows failed"),
181 E2: RowsOrErr.takeError()));
182 }
183 OS << "\n";
184}
185
186DWARFDebugFrame::DWARFDebugFrame(Triple::ArchType Arch,
187 bool IsEH, uint64_t EHFrameAddress)
188 : Arch(Arch), IsEH(IsEH), EHFrameAddress(EHFrameAddress) {}
189
190DWARFDebugFrame::~DWARFDebugFrame() = default;
191
192[[maybe_unused]] static void dumpDataAux(DataExtractor Data, uint64_t Offset,
193 int Length) {
194 errs() << "DUMP: ";
195 for (int i = 0; i < Length; ++i) {
196 uint8_t c = Data.getU8(offset_ptr: &Offset);
197 errs().write_hex(N: c); errs() << " ";
198 }
199 errs() << "\n";
200}
201
202Error DWARFDebugFrame::parse(DWARFDataExtractor Data) {
203 uint64_t Offset = 0;
204 DenseMap<uint64_t, CIE *> CIEs;
205
206 while (Data.isValidOffset(offset: Offset)) {
207 uint64_t StartOffset = Offset;
208
209 uint64_t Length;
210 DwarfFormat Format;
211 std::tie(args&: Length, args&: Format) = Data.getInitialLength(Off: &Offset);
212 bool IsDWARF64 = Format == DWARF64;
213
214 // If the Length is 0, then this CIE is a terminator. We add it because some
215 // dumper tools might need it to print something special for such entries
216 // (e.g. llvm-objdump --dwarf=frames prints "ZERO terminator").
217 if (Length == 0) {
218 auto Cie = std::make_unique<CIE>(
219 args&: IsDWARF64, args&: StartOffset, args: 0, args: 0, args: SmallString<8>(), args: 0, args: 0, args: 0, args: 0, args: 0,
220 args: SmallString<8>(), args: 0, args: 0, args: std::nullopt, args: std::nullopt, args: Arch);
221 CIEs[StartOffset] = Cie.get();
222 Entries.push_back(x: std::move(Cie));
223 break;
224 }
225
226 // At this point, Offset points to the next field after Length.
227 // Length is the structure size excluding itself. Compute an offset one
228 // past the end of the structure (needed to know how many instructions to
229 // read).
230 uint64_t StartStructureOffset = Offset;
231 uint64_t EndStructureOffset = Offset + Length;
232
233 // The Id field's size depends on the DWARF format
234 Error Err = Error::success();
235 uint64_t Id = Data.getRelocatedValue(Size: (IsDWARF64 && !IsEH) ? 8 : 4, Off: &Offset,
236 /*SectionIndex=*/nullptr, Err: &Err);
237 if (Err)
238 return Err;
239
240 if (Id == getCIEId(IsDWARF64, IsEH)) {
241 uint8_t Version = Data.getU8(offset_ptr: &Offset);
242 const char *Augmentation = Data.getCStr(OffsetPtr: &Offset);
243 StringRef AugmentationString(Augmentation ? Augmentation : "");
244 uint8_t AddressSize = Version < 4 ? Data.getAddressSize() :
245 Data.getU8(offset_ptr: &Offset);
246 Data.setAddressSize(AddressSize);
247 uint8_t SegmentDescriptorSize = Version < 4 ? 0 : Data.getU8(offset_ptr: &Offset);
248 uint64_t CodeAlignmentFactor = Data.getULEB128(offset_ptr: &Offset);
249 int64_t DataAlignmentFactor = Data.getSLEB128(OffsetPtr: &Offset);
250 uint64_t ReturnAddressRegister =
251 Version == 1 ? Data.getU8(offset_ptr: &Offset) : Data.getULEB128(offset_ptr: &Offset);
252
253 // Parse the augmentation data for EH CIEs
254 StringRef AugmentationData("");
255 uint32_t FDEPointerEncoding = DW_EH_PE_absptr;
256 uint32_t LSDAPointerEncoding = DW_EH_PE_omit;
257 std::optional<uint64_t> Personality;
258 std::optional<uint32_t> PersonalityEncoding;
259 if (IsEH) {
260 std::optional<uint64_t> AugmentationLength;
261 uint64_t StartAugmentationOffset;
262 uint64_t EndAugmentationOffset;
263
264 // Walk the augmentation string to get all the augmentation data.
265 for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) {
266 switch (AugmentationString[i]) {
267 default:
268 return createStringError(
269 EC: errc::invalid_argument,
270 Fmt: "unknown augmentation character %c in entry at 0x%" PRIx64,
271 Vals: AugmentationString[i], Vals: StartOffset);
272 case 'L':
273 LSDAPointerEncoding = Data.getU8(offset_ptr: &Offset);
274 break;
275 case 'P': {
276 if (Personality)
277 return createStringError(
278 EC: errc::invalid_argument,
279 Fmt: "duplicate personality in entry at 0x%" PRIx64, Vals: StartOffset);
280 PersonalityEncoding = Data.getU8(offset_ptr: &Offset);
281 Personality = Data.getEncodedPointer(
282 Offset: &Offset, Encoding: *PersonalityEncoding,
283 PCRelOffset: EHFrameAddress ? EHFrameAddress + Offset : 0);
284 break;
285 }
286 case 'R':
287 FDEPointerEncoding = Data.getU8(offset_ptr: &Offset);
288 break;
289 case 'S':
290 // Current frame is a signal trampoline.
291 break;
292 case 'z':
293 if (i)
294 return createStringError(
295 EC: errc::invalid_argument,
296 Fmt: "'z' must be the first character at 0x%" PRIx64, Vals: StartOffset);
297 // Parse the augmentation length first. We only parse it if
298 // the string contains a 'z'.
299 AugmentationLength = Data.getULEB128(offset_ptr: &Offset);
300 StartAugmentationOffset = Offset;
301 EndAugmentationOffset = Offset + *AugmentationLength;
302 break;
303 case 'B':
304 // B-Key is used for signing functions associated with this
305 // augmentation string
306 break;
307 // This stack frame contains MTE tagged data, so needs to be
308 // untagged on unwind.
309 case 'G':
310 break;
311 }
312 }
313
314 if (AugmentationLength) {
315 if (Offset != EndAugmentationOffset)
316 return createStringError(EC: errc::invalid_argument,
317 Fmt: "parsing augmentation data at 0x%" PRIx64
318 " failed",
319 Vals: StartOffset);
320 AugmentationData = Data.getData().slice(Start: StartAugmentationOffset,
321 End: EndAugmentationOffset);
322 }
323 }
324
325 auto Cie = std::make_unique<CIE>(
326 args&: IsDWARF64, args&: StartOffset, args&: Length, args&: Version, args&: AugmentationString,
327 args&: AddressSize, args&: SegmentDescriptorSize, args&: CodeAlignmentFactor,
328 args&: DataAlignmentFactor, args&: ReturnAddressRegister, args&: AugmentationData,
329 args&: FDEPointerEncoding, args&: LSDAPointerEncoding, args&: Personality,
330 args&: PersonalityEncoding, args: Arch);
331 CIEs[StartOffset] = Cie.get();
332 Entries.emplace_back(args: std::move(Cie));
333 } else {
334 // FDE
335 uint64_t CIEPointer = Id;
336 uint64_t InitialLocation = 0;
337 uint64_t AddressRange = 0;
338 std::optional<uint64_t> LSDAAddress;
339 CIE *Cie = CIEs[IsEH ? (StartStructureOffset - CIEPointer) : CIEPointer];
340
341 if (IsEH) {
342 // The address size is encoded in the CIE we reference.
343 if (!Cie)
344 return createStringError(EC: errc::invalid_argument,
345 Fmt: "parsing FDE data at 0x%" PRIx64
346 " failed due to missing CIE",
347 Vals: StartOffset);
348 if (auto Val =
349 Data.getEncodedPointer(Offset: &Offset, Encoding: Cie->getFDEPointerEncoding(),
350 PCRelOffset: EHFrameAddress + Offset)) {
351 InitialLocation = *Val;
352 }
353 if (auto Val = Data.getEncodedPointer(
354 Offset: &Offset, Encoding: Cie->getFDEPointerEncoding(), PCRelOffset: 0)) {
355 AddressRange = *Val;
356 }
357
358 StringRef AugmentationString = Cie->getAugmentationString();
359 if (!AugmentationString.empty()) {
360 // Parse the augmentation length and data for this FDE.
361 uint64_t AugmentationLength = Data.getULEB128(offset_ptr: &Offset);
362
363 uint64_t EndAugmentationOffset = Offset + AugmentationLength;
364
365 // Decode the LSDA if the CIE augmentation string said we should.
366 if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) {
367 LSDAAddress = Data.getEncodedPointer(
368 Offset: &Offset, Encoding: Cie->getLSDAPointerEncoding(),
369 PCRelOffset: EHFrameAddress ? Offset + EHFrameAddress : 0);
370 }
371
372 if (Offset != EndAugmentationOffset)
373 return createStringError(EC: errc::invalid_argument,
374 Fmt: "parsing augmentation data at 0x%" PRIx64
375 " failed",
376 Vals: StartOffset);
377 }
378 } else {
379 InitialLocation = Data.getRelocatedAddress(Off: &Offset);
380 AddressRange = Data.getRelocatedAddress(Off: &Offset);
381 }
382
383 Entries.emplace_back(args: new FDE(IsDWARF64, StartOffset, Length, CIEPointer,
384 InitialLocation, AddressRange, Cie,
385 LSDAAddress, Arch));
386 }
387
388 if (Error E =
389 Entries.back()->cfis().parse(Data, Offset: &Offset, EndOffset: EndStructureOffset))
390 return E;
391
392 if (Offset != EndStructureOffset)
393 return createStringError(
394 EC: errc::invalid_argument,
395 Fmt: "parsing entry instructions at 0x%" PRIx64 " failed", Vals: StartOffset);
396 }
397
398 return Error::success();
399}
400
401FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const {
402 auto It = partition_point(Range: Entries, P: [=](const std::unique_ptr<FrameEntry> &E) {
403 return E->getOffset() < Offset;
404 });
405 if (It != Entries.end() && (*It)->getOffset() == Offset)
406 return It->get();
407 return nullptr;
408}
409
410void DWARFDebugFrame::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
411 std::optional<uint64_t> Offset) const {
412 DumpOpts.IsEH = IsEH;
413 if (Offset) {
414 if (auto *Entry = getEntryAtOffset(Offset: *Offset))
415 Entry->dump(OS, DumpOpts);
416 return;
417 }
418
419 OS << "\n";
420 for (const auto &Entry : Entries)
421 Entry->dump(OS, DumpOpts);
422}
423