1//===- DWARFDebugPubTable.cpp ---------------------------------------------===//
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/DWARFDebugPubTable.h"
10#include "llvm/ADT/StringRef.h"
11#include "llvm/BinaryFormat/Dwarf.h"
12#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
13#include "llvm/Support/DataExtractor.h"
14#include "llvm/Support/Errc.h"
15#include "llvm/Support/FormatAdapters.h"
16#include "llvm/Support/FormatVariadic.h"
17#include "llvm/Support/raw_ostream.h"
18#include <cstdint>
19
20using namespace llvm;
21using namespace dwarf;
22
23void DWARFDebugPubTable::extract(
24 DWARFDataExtractor Data, bool GnuStyle,
25 function_ref<void(Error)> RecoverableErrorHandler) {
26 this->GnuStyle = GnuStyle;
27 Sets.clear();
28 uint64_t Offset = 0;
29 while (Data.isValidOffset(offset: Offset)) {
30 uint64_t SetOffset = Offset;
31 Sets.push_back(x: {});
32 Set &NewSet = Sets.back();
33
34 DataExtractor::Cursor C(Offset);
35 std::tie(args&: NewSet.Length, args&: NewSet.Format) = Data.getInitialLength(C);
36 if (!C) {
37 // Drop the newly added set because it does not contain anything useful
38 // to dump.
39 Sets.pop_back();
40 RecoverableErrorHandler(createStringError(
41 EC: errc::invalid_argument,
42 Fmt: "name lookup table at offset 0x%" PRIx64 " parsing failed: %s",
43 Vals: SetOffset, Vals: toString(E: C.takeError()).c_str()));
44 return;
45 }
46
47 Offset = C.tell() + NewSet.Length;
48 DWARFDataExtractor SetData(Data, Offset);
49 const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(Format: NewSet.Format);
50
51 NewSet.Version = SetData.getU16(C);
52 NewSet.Offset = SetData.getRelocatedValue(C, Size: OffsetSize);
53 NewSet.Size = SetData.getUnsigned(C, Size: OffsetSize);
54
55 if (!C) {
56 // Preserve the newly added set because at least some fields of the header
57 // are read and can be dumped.
58 RecoverableErrorHandler(
59 createStringError(EC: errc::invalid_argument,
60 Fmt: "name lookup table at offset 0x%" PRIx64
61 " does not have a complete header: %s",
62 Vals: SetOffset, Vals: toString(E: C.takeError()).c_str()));
63 continue;
64 }
65
66 while (C) {
67 uint64_t DieRef = SetData.getUnsigned(C, Size: OffsetSize);
68 if (DieRef == 0)
69 break;
70 uint8_t IndexEntryValue = GnuStyle ? SetData.getU8(C) : 0;
71 StringRef Name = SetData.getCStrRef(C);
72 if (C)
73 NewSet.Entries.push_back(
74 x: {.SecOffset: DieRef, .Descriptor: PubIndexEntryDescriptor(IndexEntryValue), .Name: Name});
75 }
76
77 if (!C) {
78 RecoverableErrorHandler(createStringError(
79 EC: errc::invalid_argument,
80 Fmt: "name lookup table at offset 0x%" PRIx64 " parsing failed: %s",
81 Vals: SetOffset, Vals: toString(E: C.takeError()).c_str()));
82 continue;
83 }
84 if (C.tell() != Offset)
85 RecoverableErrorHandler(createStringError(
86 EC: errc::invalid_argument,
87 Fmt: "name lookup table at offset 0x%" PRIx64
88 " has a terminator at offset 0x%" PRIx64
89 " before the expected end at 0x%" PRIx64,
90 Vals: SetOffset, Vals: C.tell() - OffsetSize, Vals: Offset - OffsetSize));
91 }
92}
93
94void DWARFDebugPubTable::dump(raw_ostream &OS) const {
95 for (const Set &S : Sets) {
96 int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(Format: S.Format);
97 OS << "length = "
98 << formatv(Fmt: "0x{0:x-}",
99 Vals: fmt_align(Item: S.Length, Where: AlignStyle::Right, Amount: OffsetDumpWidth, Fill: '0'));
100 OS << ", format = " << dwarf::FormatString(Format: S.Format);
101 OS << ", version = " << formatv(Fmt: "{0:x4}", Vals: S.Version);
102 OS << ", unit_offset = "
103 << formatv(Fmt: "0x{0:x-}",
104 Vals: fmt_align(Item: S.Offset, Where: AlignStyle::Right, Amount: OffsetDumpWidth, Fill: '0'));
105 OS << ", unit_size = "
106 << formatv(Fmt: "0x{0:x-}",
107 Vals: fmt_align(Item: S.Size, Where: AlignStyle::Right, Amount: OffsetDumpWidth, Fill: '0'))
108 << '\n';
109 OS << (GnuStyle ? "Offset Linkage Kind Name\n"
110 : "Offset Name\n");
111
112 for (const Entry &E : S.Entries) {
113 OS << formatv(Fmt: "0x{0:x-} ", Vals: fmt_align(Item: E.SecOffset, Where: AlignStyle::Right,
114 Amount: OffsetDumpWidth, Fill: '0'));
115 if (GnuStyle) {
116 StringRef EntryLinkage =
117 GDBIndexEntryLinkageString(Linkage: E.Descriptor.Linkage);
118 StringRef EntryKind = dwarf::GDBIndexEntryKindString(Kind: E.Descriptor.Kind);
119 OS << formatv(Fmt: "{0,-8}", Vals: EntryLinkage.data()) << ' '
120 << formatv(Fmt: "{0,-8}", Vals: EntryKind.data()) << ' ';
121 }
122 OS << '\"' << E.Name << "\"\n";
123 }
124 }
125}
126