1//===- DWARFDebugMacro.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/DWARFDebugMacro.h"
10#include "llvm/ADT/DenseMap.h"
11#include "llvm/BinaryFormat/Dwarf.h"
12#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
13#include "llvm/DebugInfo/DWARF/DWARFDie.h"
14#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
15#include "llvm/Support/Errc.h"
16#include "llvm/Support/WithColor.h"
17#include "llvm/Support/raw_ostream.h"
18#include <cstdint>
19
20using namespace llvm;
21using namespace dwarf;
22
23DwarfFormat DWARFDebugMacro::MacroHeader::getDwarfFormat() const {
24 return Flags & MACRO_OFFSET_SIZE ? DWARF64 : DWARF32;
25}
26
27uint8_t DWARFDebugMacro::MacroHeader::getOffsetByteSize() const {
28 return getDwarfOffsetByteSize(Format: getDwarfFormat());
29}
30
31void DWARFDebugMacro::MacroHeader::dumpMacroHeader(raw_ostream &OS) const {
32 // FIXME: Add support for dumping opcode_operands_table
33 OS << format(Fmt: "macro header: version = 0x%04" PRIx16, Vals: Version)
34 << format(Fmt: ", flags = 0x%02" PRIx8, Vals: Flags)
35 << ", format = " << FormatString(Format: getDwarfFormat());
36 if (Flags & MACRO_DEBUG_LINE_OFFSET)
37 OS << format(Fmt: ", debug_line_offset = 0x%0*" PRIx64, Vals: 2 * getOffsetByteSize(),
38 Vals: DebugLineOffset);
39 OS << "\n";
40}
41
42void DWARFDebugMacro::dump(raw_ostream &OS) const {
43 unsigned IndLevel = 0;
44 for (const auto &Macros : MacroLists) {
45 OS << format(Fmt: "0x%08" PRIx64 ":\n", Vals: Macros.Offset);
46 if (Macros.IsDebugMacro)
47 Macros.Header.dumpMacroHeader(OS);
48 for (const Entry &E : Macros.Macros) {
49 // There should not be DW_MACINFO_end_file when IndLevel is Zero. However,
50 // this check handles the case of corrupted ".debug_macinfo" section.
51 if (IndLevel > 0)
52 IndLevel -= (E.Type == DW_MACINFO_end_file);
53 // Print indentation.
54 for (unsigned I = 0; I < IndLevel; I++)
55 OS << " ";
56 IndLevel += (E.Type == DW_MACINFO_start_file);
57 // Based on which version we are handling choose appropriate macro forms.
58 if (Macros.IsDebugMacro)
59 WithColor(OS, HighlightColor::Macro).get()
60 << (Macros.Header.Version < 5 ? GnuMacroString(Encoding: E.Type)
61 : MacroString(Encoding: E.Type));
62 else
63 WithColor(OS, HighlightColor::Macro).get() << MacinfoString(Encoding: E.Type);
64 switch (E.Type) {
65 default:
66 // Got a corrupted ".debug_macinfo/.debug_macro" section (invalid
67 // macinfo type).
68 break;
69 // debug_macro and debug_macinfo share some common encodings.
70 // DW_MACRO_define == DW_MACINFO_define
71 // DW_MACRO_undef == DW_MACINFO_undef
72 // DW_MACRO_start_file == DW_MACINFO_start_file
73 // DW_MACRO_end_file == DW_MACINFO_end_file
74 // For readability/uniformity we are using DW_MACRO_*.
75 //
76 // The GNU .debug_macro extension's entries have the same encoding
77 // as DWARF 5's DW_MACRO_* entries, so we only use the latter here.
78 case DW_MACRO_define:
79 case DW_MACRO_undef:
80 case DW_MACRO_define_strp:
81 case DW_MACRO_undef_strp:
82 case DW_MACRO_define_strx:
83 case DW_MACRO_undef_strx:
84 OS << " - lineno: " << E.Line;
85 OS << " macro: " << E.MacroStr;
86 break;
87 case DW_MACRO_start_file:
88 OS << " - lineno: " << E.Line;
89 OS << " filenum: " << E.File;
90 break;
91 case DW_MACRO_import:
92 OS << format(Fmt: " - import offset: 0x%0*" PRIx64,
93 Vals: 2 * Macros.Header.getOffsetByteSize(), Vals: E.ImportOffset);
94 break;
95 case DW_MACRO_end_file:
96 break;
97 case DW_MACINFO_vendor_ext:
98 OS << " - constant: " << E.ExtConstant;
99 OS << " string: " << E.ExtStr;
100 break;
101 }
102 OS << "\n";
103 }
104 }
105}
106
107Error DWARFDebugMacro::parseImpl(
108 std::optional<DWARFUnitVector::compile_unit_range> Units,
109 std::optional<DataExtractor> StringExtractor, DWARFDataExtractor Data,
110 bool IsMacro) {
111 uint64_t Offset = 0;
112 MacroList *M = nullptr;
113 using MacroToUnitsMap = DenseMap<uint64_t, DWARFUnit *>;
114 MacroToUnitsMap MacroToUnits;
115 if (IsMacro && Data.isValidOffset(offset: Offset)) {
116 // Keep a mapping from Macro contribution to CUs, this will
117 // be needed while retrieving macro from DW_MACRO_define_strx form.
118 for (const auto &U : *Units)
119 if (auto CUDIE = U->getUnitDIE())
120 // Skip units which does not contibutes to macro section.
121 if (auto MacroOffset = toSectionOffset(V: CUDIE.find(Attr: DW_AT_macros)))
122 MacroToUnits.try_emplace(Key: *MacroOffset, Args: U.get());
123 }
124 while (Data.isValidOffset(offset: Offset)) {
125 if (!M) {
126 MacroLists.emplace_back();
127 M = &MacroLists.back();
128 M->Offset = Offset;
129 M->IsDebugMacro = IsMacro;
130 if (IsMacro) {
131 auto Err = M->Header.parseMacroHeader(Data, Offset: &Offset);
132 if (Err)
133 return Err;
134 }
135 }
136 // A macro list entry consists of:
137 M->Macros.emplace_back();
138 Entry &E = M->Macros.back();
139 // 1. Macinfo type
140 E.Type = Data.getULEB128(offset_ptr: &Offset);
141
142 if (E.Type == 0) {
143 // Reached end of a ".debug_macinfo/debug_macro" section contribution.
144 M = nullptr;
145 continue;
146 }
147
148 switch (E.Type) {
149 default:
150 // Got a corrupted ".debug_macinfo" section (invalid macinfo type).
151 // Push the corrupted entry to the list and halt parsing.
152 E.Type = DW_MACINFO_invalid;
153 return Error::success();
154 // debug_macro and debug_macinfo share some common encodings.
155 // DW_MACRO_define == DW_MACINFO_define
156 // DW_MACRO_undef == DW_MACINFO_undef
157 // DW_MACRO_start_file == DW_MACINFO_start_file
158 // DW_MACRO_end_file == DW_MACINFO_end_file
159 // For readibility/uniformity we are using DW_MACRO_*.
160 case DW_MACRO_define:
161 case DW_MACRO_undef:
162 // 2. Source line
163 E.Line = Data.getULEB128(offset_ptr: &Offset);
164 // 3. Macro string
165 E.MacroStr = Data.getCStr(OffsetPtr: &Offset);
166 break;
167 case DW_MACRO_define_strp:
168 case DW_MACRO_undef_strp: {
169 if (!IsMacro) {
170 // DW_MACRO_define_strp is a new form introduced in DWARFv5, it is
171 // not supported in debug_macinfo[.dwo] sections. Assume it as an
172 // invalid entry, push it and halt parsing.
173 E.Type = DW_MACINFO_invalid;
174 return Error::success();
175 }
176 uint64_t StrOffset = 0;
177 // 2. Source line
178 E.Line = Data.getULEB128(offset_ptr: &Offset);
179 // 3. Macro string
180 StrOffset =
181 Data.getRelocatedValue(Size: M->Header.getOffsetByteSize(), Off: &Offset);
182 assert(StringExtractor && "String Extractor not found");
183 E.MacroStr = StringExtractor->getCStr(OffsetPtr: &StrOffset);
184 break;
185 }
186 case DW_MACRO_define_strx:
187 case DW_MACRO_undef_strx: {
188 if (!IsMacro) {
189 // DW_MACRO_define_strx is a new form introduced in DWARFv5, it is
190 // not supported in debug_macinfo[.dwo] sections. Assume it as an
191 // invalid entry, push it and halt parsing.
192 E.Type = DW_MACINFO_invalid;
193 return Error::success();
194 }
195 E.Line = Data.getULEB128(offset_ptr: &Offset);
196 auto MacroContributionOffset = MacroToUnits.find(Val: M->Offset);
197 if (MacroContributionOffset == MacroToUnits.end())
198 return createStringError(EC: errc::invalid_argument,
199 S: "Macro contribution of the unit not found");
200 Expected<uint64_t> StrOffset =
201 MacroContributionOffset->second->getStringOffsetSectionItem(
202 Index: Data.getULEB128(offset_ptr: &Offset));
203 if (!StrOffset)
204 return StrOffset.takeError();
205 E.MacroStr =
206 MacroContributionOffset->second->getStringExtractor().getCStr(
207 OffsetPtr: &*StrOffset);
208 break;
209 }
210 case DW_MACRO_start_file:
211 // 2. Source line
212 E.Line = Data.getULEB128(offset_ptr: &Offset);
213 // 3. Source file id
214 E.File = Data.getULEB128(offset_ptr: &Offset);
215 break;
216 case DW_MACRO_end_file:
217 break;
218 case DW_MACRO_import:
219 E.ImportOffset =
220 Data.getRelocatedValue(Size: M->Header.getOffsetByteSize(), Off: &Offset);
221 break;
222 case DW_MACINFO_vendor_ext:
223 // 2. Vendor extension constant
224 E.ExtConstant = Data.getULEB128(offset_ptr: &Offset);
225 // 3. Vendor extension string
226 E.ExtStr = Data.getCStr(OffsetPtr: &Offset);
227 break;
228 }
229 }
230 return Error::success();
231}
232
233Error DWARFDebugMacro::MacroHeader::parseMacroHeader(DWARFDataExtractor Data,
234 uint64_t *Offset) {
235 Version = Data.getU16(offset_ptr: Offset);
236 uint8_t FlagData = Data.getU8(offset_ptr: Offset);
237
238 // FIXME: Add support for parsing opcode_operands_table
239 if (FlagData & MACRO_OPCODE_OPERANDS_TABLE)
240 return createStringError(EC: errc::not_supported,
241 S: "opcode_operands_table is not supported");
242 Flags = FlagData;
243 if (Flags & MACRO_DEBUG_LINE_OFFSET)
244 DebugLineOffset = Data.getUnsigned(offset_ptr: Offset, byte_size: getOffsetByteSize());
245 return Error::success();
246}
247