1 //===- DWARFAbbreviationDeclaration.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/DWARFAbbreviationDeclaration.h"
10 
11 #include "llvm/BinaryFormat/Dwarf.h"
12 #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
13 #include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
14 #include "llvm/DebugInfo/DWARF/DWARFUnit.h"
15 #include "llvm/Support/DataExtractor.h"
16 #include "llvm/Support/FormatVariadic.h"
17 #include "llvm/Support/raw_ostream.h"
18 #include <cstddef>
19 #include <cstdint>
20 
21 using namespace llvm;
22 using namespace dwarf;
23 
24 void DWARFAbbreviationDeclaration::clear() {
25   Code = 0;
26   Tag = DW_TAG_null;
27   CodeByteSize = 0;
28   HasChildren = false;
29   AttributeSpecs.clear();
30   FixedAttributeSize.reset();
31 }
32 
33 DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration() {
34   clear();
35 }
36 
37 llvm::Expected<DWARFAbbreviationDeclaration::ExtractState>
38 DWARFAbbreviationDeclaration::extract(DataExtractor Data, uint64_t *OffsetPtr) {
39   clear();
40   const uint64_t Offset = *OffsetPtr;
41   Error Err = Error::success();
42   Code = Data.getULEB128(OffsetPtr, &Err);
43   if (Err)
44     return std::move(Err);
45 
46   if (Code == 0)
47     return ExtractState::Complete;
48 
49   CodeByteSize = *OffsetPtr - Offset;
50   Tag = static_cast<llvm::dwarf::Tag>(Data.getULEB128(OffsetPtr, &Err));
51   if (Err)
52     return std::move(Err);
53 
54   if (Tag == DW_TAG_null) {
55     clear();
56     return make_error<llvm::object::GenericBinaryError>(
57         "abbreviation declaration requires a non-null tag");
58   }
59   uint8_t ChildrenByte = Data.getU8(OffsetPtr, &Err);
60   if (Err)
61     return std::move(Err);
62 
63   HasChildren = (ChildrenByte == DW_CHILDREN_yes);
64   // Assign a value to our optional FixedAttributeSize member variable. If
65   // this member variable still has a value after the while loop below, then
66   // all attribute data in this abbreviation declaration has a fixed byte size.
67   FixedAttributeSize = FixedSizeInfo();
68 
69   // Read all of the abbreviation attributes and forms.
70   while (Data.isValidOffset(*OffsetPtr)) {
71     auto A = static_cast<Attribute>(Data.getULEB128(OffsetPtr, &Err));
72     if (Err)
73       return std::move(Err);
74 
75     auto F = static_cast<Form>(Data.getULEB128(OffsetPtr, &Err));
76     if (Err)
77       return std::move(Err);
78 
79     // We successfully reached the end of this abbreviation declaration
80     // since both attribute and form are zero. There may be more abbreviation
81     // declarations afterwards.
82     if (!A && !F)
83       return ExtractState::MoreItems;
84 
85     if (!A || !F) {
86       // Attribute and form pairs must either both be non-zero, in which case
87       // they are added to the abbreviation declaration, or both be zero to
88       // terminate the abbrevation declaration. In this case only one was
89       // zero which is an error.
90       clear();
91       return make_error<llvm::object::GenericBinaryError>(
92           "malformed abbreviation declaration attribute. Either the attribute "
93           "or the form is zero while the other is not");
94     }
95 
96     bool IsImplicitConst = (F == DW_FORM_implicit_const);
97     if (IsImplicitConst) {
98       int64_t V = Data.getSLEB128(OffsetPtr);
99       AttributeSpecs.push_back(AttributeSpec(A, F, V));
100       continue;
101     }
102     std::optional<uint8_t> ByteSize;
103     // If this abbrevation still has a fixed byte size, then update the
104     // FixedAttributeSize as needed.
105     switch (F) {
106     case DW_FORM_addr:
107       if (FixedAttributeSize)
108         ++FixedAttributeSize->NumAddrs;
109       break;
110 
111     case DW_FORM_ref_addr:
112       if (FixedAttributeSize)
113         ++FixedAttributeSize->NumRefAddrs;
114       break;
115 
116     case DW_FORM_strp:
117     case DW_FORM_GNU_ref_alt:
118     case DW_FORM_GNU_strp_alt:
119     case DW_FORM_line_strp:
120     case DW_FORM_sec_offset:
121     case DW_FORM_strp_sup:
122       if (FixedAttributeSize)
123         ++FixedAttributeSize->NumDwarfOffsets;
124       break;
125 
126     default:
127       // The form has a byte size that doesn't depend on Params.
128       // If it's a fixed size, keep track of it.
129       if ((ByteSize = dwarf::getFixedFormByteSize(F, dwarf::FormParams()))) {
130         if (FixedAttributeSize)
131           FixedAttributeSize->NumBytes += *ByteSize;
132         break;
133       }
134       // Indicate we no longer have a fixed byte size for this
135       // abbreviation by clearing the FixedAttributeSize optional value
136       // so it doesn't have a value.
137       FixedAttributeSize.reset();
138       break;
139     }
140     // Record this attribute and its fixed size if it has one.
141     AttributeSpecs.push_back(AttributeSpec(A, F, ByteSize));
142   }
143   return make_error<llvm::object::GenericBinaryError>(
144       "abbreviation declaration attribute list was not terminated with a null "
145       "entry");
146 }
147 
148 void DWARFAbbreviationDeclaration::dump(raw_ostream &OS) const {
149   OS << '[' << getCode() << "] ";
150   OS << formatv("{0}", getTag());
151   OS << "\tDW_CHILDREN_" << (hasChildren() ? "yes" : "no") << '\n';
152   for (const AttributeSpec &Spec : AttributeSpecs) {
153     OS << formatv("\t{0}\t{1}", Spec.Attr, Spec.Form);
154     if (Spec.isImplicitConst())
155       OS << '\t' << Spec.getImplicitConstValue();
156     OS << '\n';
157   }
158   OS << '\n';
159 }
160 
161 std::optional<uint32_t>
162 DWARFAbbreviationDeclaration::findAttributeIndex(dwarf::Attribute Attr) const {
163   for (uint32_t i = 0, e = AttributeSpecs.size(); i != e; ++i) {
164     if (AttributeSpecs[i].Attr == Attr)
165       return i;
166   }
167   return std::nullopt;
168 }
169 
170 uint64_t DWARFAbbreviationDeclaration::getAttributeOffsetFromIndex(
171     uint32_t AttrIndex, uint64_t DIEOffset, const DWARFUnit &U) const {
172   DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor();
173 
174   // Add the byte size of ULEB that for the abbrev Code so we can start
175   // skipping the attribute data.
176   uint64_t Offset = DIEOffset + CodeByteSize;
177   for (uint32_t CurAttrIdx = 0; CurAttrIdx != AttrIndex; ++CurAttrIdx)
178     // Match Offset along until we get to the attribute we want.
179     if (auto FixedSize = AttributeSpecs[CurAttrIdx].getByteSize(U))
180       Offset += *FixedSize;
181     else
182       DWARFFormValue::skipValue(AttributeSpecs[CurAttrIdx].Form, DebugInfoData,
183                                 &Offset, U.getFormParams());
184   return Offset;
185 }
186 
187 std::optional<DWARFFormValue>
188 DWARFAbbreviationDeclaration::getAttributeValueFromOffset(
189     uint32_t AttrIndex, uint64_t Offset, const DWARFUnit &U) const {
190   assert(AttributeSpecs.size() > AttrIndex &&
191          "Attribute Index is out of bounds.");
192 
193   // We have arrived at the attribute to extract, extract if from Offset.
194   const AttributeSpec &Spec = AttributeSpecs[AttrIndex];
195   if (Spec.isImplicitConst())
196     return DWARFFormValue::createFromSValue(Spec.Form,
197                                             Spec.getImplicitConstValue());
198 
199   DWARFFormValue FormValue(Spec.Form);
200   DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor();
201   if (FormValue.extractValue(DebugInfoData, &Offset, U.getFormParams(), &U))
202     return FormValue;
203   return std::nullopt;
204 }
205 
206 std::optional<DWARFFormValue>
207 DWARFAbbreviationDeclaration::getAttributeValue(const uint64_t DIEOffset,
208                                                 const dwarf::Attribute Attr,
209                                                 const DWARFUnit &U) const {
210   // Check if this abbreviation has this attribute without needing to skip
211   // any data so we can return quickly if it doesn't.
212   std::optional<uint32_t> MatchAttrIndex = findAttributeIndex(Attr);
213   if (!MatchAttrIndex)
214     return std::nullopt;
215 
216   uint64_t Offset = getAttributeOffsetFromIndex(*MatchAttrIndex, DIEOffset, U);
217 
218   return getAttributeValueFromOffset(*MatchAttrIndex, Offset, U);
219 }
220 
221 size_t DWARFAbbreviationDeclaration::FixedSizeInfo::getByteSize(
222     const DWARFUnit &U) const {
223   size_t ByteSize = NumBytes;
224   if (NumAddrs)
225     ByteSize += NumAddrs * U.getAddressByteSize();
226   if (NumRefAddrs)
227     ByteSize += NumRefAddrs * U.getRefAddrByteSize();
228   if (NumDwarfOffsets)
229     ByteSize += NumDwarfOffsets * U.getDwarfOffsetByteSize();
230   return ByteSize;
231 }
232 
233 std::optional<int64_t> DWARFAbbreviationDeclaration::AttributeSpec::getByteSize(
234     const DWARFUnit &U) const {
235   if (isImplicitConst())
236     return 0;
237   if (ByteSize.HasByteSize)
238     return ByteSize.ByteSize;
239   std::optional<int64_t> S;
240   auto FixedByteSize = dwarf::getFixedFormByteSize(Form, U.getFormParams());
241   if (FixedByteSize)
242     S = *FixedByteSize;
243   return S;
244 }
245 
246 std::optional<size_t> DWARFAbbreviationDeclaration::getFixedAttributesByteSize(
247     const DWARFUnit &U) const {
248   if (FixedAttributeSize)
249     return FixedAttributeSize->getByteSize(U);
250   return std::nullopt;
251 }
252