| 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::(DataExtractor Data, uint64_t *OffsetPtr) { |
| 39 | clear(); |
| 40 | const uint64_t Offset = *OffsetPtr; |
| 41 | Error Err = Error::success(); |
| 42 | Code = Data.getULEB128(offset_ptr: OffsetPtr, Err: &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(offset_ptr: OffsetPtr, Err: &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 | Args: "abbreviation declaration requires a non-null tag" ); |
| 58 | } |
| 59 | uint8_t ChildrenByte = Data.getU8(offset_ptr: OffsetPtr, Err: &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(offset: *OffsetPtr)) { |
| 71 | auto A = static_cast<Attribute>(Data.getULEB128(offset_ptr: OffsetPtr, Err: &Err)); |
| 72 | if (Err) |
| 73 | return std::move(Err); |
| 74 | |
| 75 | auto F = static_cast<Form>(Data.getULEB128(offset_ptr: OffsetPtr, Err: &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 | Args: "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(Elt: 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(Form: F, Params: 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(Elt: AttributeSpec(A, F, ByteSize)); |
| 142 | } |
| 143 | return make_error<llvm::object::GenericBinaryError>( |
| 144 | Args: "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(Fmt: "{0}" , Vals: getTag()); |
| 151 | OS << "\tDW_CHILDREN_" << (hasChildren() ? "yes" : "no" ) << '\n'; |
| 152 | for (const AttributeSpec &Spec : AttributeSpecs) { |
| 153 | OS << formatv(Fmt: "\t{0}\t{1}" , Vals: Spec.Attr, Vals: 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(Form: AttributeSpecs[CurAttrIdx].Form, DebugInfoData, |
| 183 | OffsetPtr: &Offset, FormParams: 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(F: Spec.Form, |
| 197 | V: Spec.getImplicitConstValue()); |
| 198 | |
| 199 | DWARFFormValue FormValue(Spec.Form); |
| 200 | DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor(); |
| 201 | if (FormValue.extractValue(Data: DebugInfoData, OffsetPtr: &Offset, FormParams: U.getFormParams(), U: &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(AttrIndex: *MatchAttrIndex, DIEOffset, U); |
| 217 | |
| 218 | return getAttributeValueFromOffset(AttrIndex: *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, Params: 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 | |