| 1 | //===-- llvm-dwp.cpp - Split DWARF merging tool for llvm ------------------===// |
| 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 | // A utility for merging DWARF 5 Split DWARF .dwo files into .dwp (DWARF |
| 10 | // package files). |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | #include "llvm/DWP/DWP.h" |
| 14 | #include "llvm/ADT/STLExtras.h" |
| 15 | #include "llvm/ADT/SmallVector.h" |
| 16 | #include "llvm/ADT/Twine.h" |
| 17 | #include "llvm/BinaryFormat/ELF.h" |
| 18 | #include "llvm/DWP/DWPError.h" |
| 19 | #include "llvm/DWP/ELFWriter.h" |
| 20 | #include "llvm/Object/Decompressor.h" |
| 21 | #include "llvm/Object/ELFObjectFile.h" |
| 22 | #include "llvm/Support/EndianStream.h" |
| 23 | #include "llvm/Support/LEB128.h" |
| 24 | #include "llvm/Support/MathExtras.h" |
| 25 | #include <limits> |
| 26 | |
| 27 | using namespace llvm; |
| 28 | using namespace llvm::object; |
| 29 | |
| 30 | // Returns the size of debug_str_offsets section headers in bytes. |
| 31 | static uint64_t (DataExtractor StrOffsetsData, |
| 32 | uint16_t DwarfVersion) { |
| 33 | if (DwarfVersion <= 4) |
| 34 | return 0; // There is no header before dwarf 5. |
| 35 | uint64_t Offset = 0; |
| 36 | uint64_t Length = StrOffsetsData.getU32(offset_ptr: &Offset); |
| 37 | if (Length == llvm::dwarf::DW_LENGTH_DWARF64) |
| 38 | return 16; // unit length: 12 bytes, version: 2 bytes, padding: 2 bytes. |
| 39 | return 8; // unit length: 4 bytes, version: 2 bytes, padding: 2 bytes. |
| 40 | } |
| 41 | |
| 42 | static uint64_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) { |
| 43 | uint64_t Offset = 0; |
| 44 | DataExtractor AbbrevData(Abbrev, true); |
| 45 | while (AbbrevData.getULEB128(offset_ptr: &Offset) != AbbrCode) { |
| 46 | // Tag |
| 47 | AbbrevData.getULEB128(offset_ptr: &Offset); |
| 48 | // DW_CHILDREN |
| 49 | AbbrevData.getU8(offset_ptr: &Offset); |
| 50 | // Attributes |
| 51 | while (AbbrevData.getULEB128(offset_ptr: &Offset) | AbbrevData.getULEB128(offset_ptr: &Offset)) |
| 52 | ; |
| 53 | } |
| 54 | return Offset; |
| 55 | } |
| 56 | |
| 57 | static Expected<const char *> |
| 58 | (dwarf::Form Form, DataExtractor InfoData, uint64_t &InfoOffset, |
| 59 | StringRef StrOffsets, StringRef Str, uint16_t Version) { |
| 60 | if (Form == dwarf::DW_FORM_string) |
| 61 | return InfoData.getCStr(OffsetPtr: &InfoOffset); |
| 62 | uint64_t StrIndex; |
| 63 | switch (Form) { |
| 64 | case dwarf::DW_FORM_strx1: |
| 65 | StrIndex = InfoData.getU8(offset_ptr: &InfoOffset); |
| 66 | break; |
| 67 | case dwarf::DW_FORM_strx2: |
| 68 | StrIndex = InfoData.getU16(offset_ptr: &InfoOffset); |
| 69 | break; |
| 70 | case dwarf::DW_FORM_strx3: |
| 71 | StrIndex = InfoData.getU24(OffsetPtr: &InfoOffset); |
| 72 | break; |
| 73 | case dwarf::DW_FORM_strx4: |
| 74 | StrIndex = InfoData.getU32(offset_ptr: &InfoOffset); |
| 75 | break; |
| 76 | case dwarf::DW_FORM_strx: |
| 77 | case dwarf::DW_FORM_GNU_str_index: |
| 78 | StrIndex = InfoData.getULEB128(offset_ptr: &InfoOffset); |
| 79 | break; |
| 80 | default: |
| 81 | return make_error<DWPError>( |
| 82 | Args: "string field must be encoded with one of the following: " |
| 83 | "DW_FORM_string, DW_FORM_strx, DW_FORM_strx1, DW_FORM_strx2, " |
| 84 | "DW_FORM_strx3, DW_FORM_strx4, or DW_FORM_GNU_str_index." ); |
| 85 | } |
| 86 | DataExtractor StrOffsetsData(StrOffsets, true); |
| 87 | uint64_t StrOffsetsOffset = 4 * StrIndex; |
| 88 | StrOffsetsOffset += debugStrOffsetsHeaderSize(StrOffsetsData, DwarfVersion: Version); |
| 89 | |
| 90 | uint64_t StrOffset = StrOffsetsData.getU32(offset_ptr: &StrOffsetsOffset); |
| 91 | DataExtractor StrData(Str, true); |
| 92 | return StrData.getCStr(OffsetPtr: &StrOffset); |
| 93 | } |
| 94 | |
| 95 | static Expected<CompileUnitIdentifiers> |
| 96 | (InfoSectionUnitHeader &, StringRef Abbrev, |
| 97 | StringRef Info, StringRef StrOffsets, StringRef Str) { |
| 98 | DataExtractor InfoData(Info, true); |
| 99 | uint64_t Offset = Header.HeaderSize; |
| 100 | if (Header.Version >= 5 && Header.UnitType != dwarf::DW_UT_split_compile) |
| 101 | return make_error<DWPError>( |
| 102 | Args: std::string("unit type DW_UT_split_compile type not found in " |
| 103 | "debug_info header. Unexpected unit type 0x" + |
| 104 | utostr(X: Header.UnitType) + " found" )); |
| 105 | |
| 106 | CompileUnitIdentifiers ID; |
| 107 | |
| 108 | uint32_t AbbrCode = InfoData.getULEB128(offset_ptr: &Offset); |
| 109 | DataExtractor AbbrevData(Abbrev, true); |
| 110 | uint64_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode); |
| 111 | auto Tag = static_cast<dwarf::Tag>(AbbrevData.getULEB128(offset_ptr: &AbbrevOffset)); |
| 112 | if (Tag != dwarf::DW_TAG_compile_unit) |
| 113 | return make_error<DWPError>(Args: "top level DIE is not a compile unit" ); |
| 114 | // DW_CHILDREN |
| 115 | AbbrevData.getU8(offset_ptr: &AbbrevOffset); |
| 116 | uint32_t Name; |
| 117 | dwarf::Form Form; |
| 118 | while ((Name = AbbrevData.getULEB128(offset_ptr: &AbbrevOffset)) | |
| 119 | (Form = static_cast<dwarf::Form>( |
| 120 | AbbrevData.getULEB128(offset_ptr: &AbbrevOffset))) && |
| 121 | (Name != 0 || Form != 0)) { |
| 122 | switch (Name) { |
| 123 | case dwarf::DW_AT_name: { |
| 124 | Expected<const char *> EName = getIndexedString( |
| 125 | Form, InfoData, InfoOffset&: Offset, StrOffsets, Str, Version: Header.Version); |
| 126 | if (!EName) |
| 127 | return EName.takeError(); |
| 128 | ID.Name = *EName; |
| 129 | break; |
| 130 | } |
| 131 | case dwarf::DW_AT_GNU_dwo_name: |
| 132 | case dwarf::DW_AT_dwo_name: { |
| 133 | Expected<const char *> EName = getIndexedString( |
| 134 | Form, InfoData, InfoOffset&: Offset, StrOffsets, Str, Version: Header.Version); |
| 135 | if (!EName) |
| 136 | return EName.takeError(); |
| 137 | ID.DWOName = *EName; |
| 138 | break; |
| 139 | } |
| 140 | case dwarf::DW_AT_GNU_dwo_id: |
| 141 | Header.Signature = InfoData.getU64(offset_ptr: &Offset); |
| 142 | break; |
| 143 | default: |
| 144 | DWARFFormValue::skipValue( |
| 145 | Form, DebugInfoData: InfoData, OffsetPtr: &Offset, |
| 146 | FormParams: dwarf::FormParams({.Version: Header.Version, .AddrSize: Header.AddrSize, .Format: Header.Format})); |
| 147 | } |
| 148 | } |
| 149 | if (!Header.Signature) |
| 150 | return make_error<DWPError>(Args: "compile unit missing dwo_id" ); |
| 151 | ID.Signature = *Header.Signature; |
| 152 | return ID; |
| 153 | } |
| 154 | |
| 155 | static bool isSupportedSectionKind(DWARFSectionKind Kind) { |
| 156 | return Kind != DW_SECT_EXT_unknown; |
| 157 | } |
| 158 | |
| 159 | // Convert an internal section identifier into the index to use with |
| 160 | // UnitIndexEntry::Contributions. |
| 161 | static unsigned getContributionIndex(DWARFSectionKind Kind, |
| 162 | uint32_t IndexVersion) { |
| 163 | assert(serializeSectionKind(Kind, IndexVersion) >= DW_SECT_INFO); |
| 164 | return serializeSectionKind(Kind, IndexVersion) - DW_SECT_INFO; |
| 165 | } |
| 166 | |
| 167 | // Convert a UnitIndexEntry::Contributions index to the corresponding on-disk |
| 168 | // value of the section identifier. |
| 169 | static unsigned getOnDiskSectionId(unsigned Index) { |
| 170 | return Index + DW_SECT_INFO; |
| 171 | } |
| 172 | |
| 173 | static StringRef getSubsection(StringRef Section, |
| 174 | const DWARFUnitIndex::Entry &Entry, |
| 175 | DWARFSectionKind Kind) { |
| 176 | const auto *Off = Entry.getContribution(Sec: Kind); |
| 177 | if (!Off) |
| 178 | return StringRef(); |
| 179 | return Section.substr(Start: Off->getOffset(), N: Off->getLength()); |
| 180 | } |
| 181 | |
| 182 | static Error sectionOverflowErrorOrWarning(uint32_t PrevOffset, |
| 183 | uint32_t OverflowedOffset, |
| 184 | StringRef SectionName, |
| 185 | OnCuIndexOverflow OverflowOptValue, |
| 186 | bool &AnySectionOverflow) { |
| 187 | std::string Msg = |
| 188 | (SectionName + |
| 189 | Twine(" Section Contribution Offset overflow 4G. Previous Offset " ) + |
| 190 | Twine(PrevOffset) + Twine(", After overflow offset " ) + |
| 191 | Twine(OverflowedOffset) + Twine("." )) |
| 192 | .str(); |
| 193 | if (OverflowOptValue == OnCuIndexOverflow::Continue) { |
| 194 | WithColor::defaultWarningHandler(Warning: make_error<DWPError>(Args&: Msg)); |
| 195 | return Error::success(); |
| 196 | } else if (OverflowOptValue == OnCuIndexOverflow::SoftStop) { |
| 197 | AnySectionOverflow = true; |
| 198 | WithColor::defaultWarningHandler(Warning: make_error<DWPError>(Args&: Msg)); |
| 199 | return Error::success(); |
| 200 | } |
| 201 | return make_error<DWPError>(Args&: Msg); |
| 202 | } |
| 203 | |
| 204 | static Error addAllTypesFromDWP( |
| 205 | DWPWriter &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries, |
| 206 | const DWARFUnitIndex &TUIndex, DWPSectionId OutputSection, StringRef Types, |
| 207 | const UnitIndexEntry &TUEntry, uint32_t &TypesOffset, |
| 208 | unsigned TypesContributionIndex, OnCuIndexOverflow OverflowOptValue, |
| 209 | bool &AnySectionOverflow) { |
| 210 | Out.switchSection(Id: OutputSection); |
| 211 | for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) { |
| 212 | auto *I = E.getContributions(); |
| 213 | if (!I) |
| 214 | continue; |
| 215 | auto P = TypeIndexEntries.insert(KV: std::make_pair(x: E.getSignature(), y: TUEntry)); |
| 216 | if (!P.second) |
| 217 | continue; |
| 218 | auto &Entry = P.first->second; |
| 219 | // Zero out the debug_info contribution |
| 220 | Entry.Contributions[0] = {}; |
| 221 | for (auto Kind : TUIndex.getColumnKinds()) { |
| 222 | if (!isSupportedSectionKind(Kind)) |
| 223 | continue; |
| 224 | auto &C = |
| 225 | Entry.Contributions[getContributionIndex(Kind, IndexVersion: TUIndex.getVersion())]; |
| 226 | C.setOffset(C.getOffset() + I->getOffset()); |
| 227 | C.setLength(I->getLength()); |
| 228 | ++I; |
| 229 | } |
| 230 | auto &C = Entry.Contributions[TypesContributionIndex]; |
| 231 | Out.emitBytes(Data: Types.substr( |
| 232 | Start: C.getOffset() - |
| 233 | TUEntry.Contributions[TypesContributionIndex].getOffset(), |
| 234 | N: C.getLength())); |
| 235 | C.setOffset(TypesOffset); |
| 236 | uint32_t OldOffset = TypesOffset; |
| 237 | static_assert(sizeof(OldOffset) == sizeof(TypesOffset)); |
| 238 | TypesOffset += C.getLength(); |
| 239 | if (OldOffset > TypesOffset) { |
| 240 | if (Error Err = sectionOverflowErrorOrWarning(PrevOffset: OldOffset, OverflowedOffset: TypesOffset, |
| 241 | SectionName: "Types" , OverflowOptValue, |
| 242 | AnySectionOverflow)) |
| 243 | return Err; |
| 244 | if (AnySectionOverflow) { |
| 245 | TypesOffset = OldOffset; |
| 246 | return Error::success(); |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | return Error::success(); |
| 251 | } |
| 252 | |
| 253 | static Error addAllTypesFromTypesSection( |
| 254 | DWPWriter &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries, |
| 255 | DWPSectionId OutputSection, const std::vector<StringRef> &TypesSections, |
| 256 | const UnitIndexEntry &CUEntry, uint32_t &TypesOffset, |
| 257 | OnCuIndexOverflow OverflowOptValue, bool &AnySectionOverflow) { |
| 258 | for (StringRef Types : TypesSections) { |
| 259 | Out.switchSection(Id: OutputSection); |
| 260 | uint64_t Offset = 0; |
| 261 | DataExtractor Data(Types, true); |
| 262 | while (Data.isValidOffset(offset: Offset)) { |
| 263 | UnitIndexEntry Entry = CUEntry; |
| 264 | // Zero out the debug_info contribution |
| 265 | Entry.Contributions[0] = {}; |
| 266 | auto &C = Entry.Contributions[getContributionIndex(Kind: DW_SECT_EXT_TYPES, IndexVersion: 2)]; |
| 267 | C.setOffset(TypesOffset); |
| 268 | auto PrevOffset = Offset; |
| 269 | // Length of the unit, including the 4 byte length field. |
| 270 | C.setLength(Data.getU32(offset_ptr: &Offset) + 4); |
| 271 | |
| 272 | Data.getU16(offset_ptr: &Offset); // Version |
| 273 | Data.getU32(offset_ptr: &Offset); // Abbrev offset |
| 274 | Data.getU8(offset_ptr: &Offset); // Address size |
| 275 | auto Signature = Data.getU64(offset_ptr: &Offset); |
| 276 | Offset = PrevOffset + C.getLength32(); |
| 277 | |
| 278 | auto P = TypeIndexEntries.insert(KV: std::make_pair(x&: Signature, y&: Entry)); |
| 279 | if (!P.second) |
| 280 | continue; |
| 281 | |
| 282 | Out.emitBytes(Data: Types.substr(Start: PrevOffset, N: C.getLength32())); |
| 283 | uint32_t OldOffset = TypesOffset; |
| 284 | TypesOffset += C.getLength32(); |
| 285 | if (OldOffset > TypesOffset) { |
| 286 | if (Error Err = sectionOverflowErrorOrWarning(PrevOffset: OldOffset, OverflowedOffset: TypesOffset, |
| 287 | SectionName: "Types" , OverflowOptValue, |
| 288 | AnySectionOverflow)) |
| 289 | return Err; |
| 290 | if (AnySectionOverflow) { |
| 291 | TypesOffset = OldOffset; |
| 292 | return Error::success(); |
| 293 | } |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | return Error::success(); |
| 298 | } |
| 299 | |
| 300 | static std::string buildDWODescription(StringRef Name, StringRef DWPName, |
| 301 | StringRef DWOName) { |
| 302 | std::string Text = "\'" ; |
| 303 | Text += Name; |
| 304 | Text += '\''; |
| 305 | bool HasDWO = !DWOName.empty(); |
| 306 | bool HasDWP = !DWPName.empty(); |
| 307 | if (HasDWO || HasDWP) { |
| 308 | Text += " (from " ; |
| 309 | if (HasDWO) { |
| 310 | Text += '\''; |
| 311 | Text += DWOName; |
| 312 | Text += '\''; |
| 313 | } |
| 314 | if (HasDWO && HasDWP) |
| 315 | Text += " in " ; |
| 316 | if (!DWPName.empty()) { |
| 317 | Text += '\''; |
| 318 | Text += DWPName; |
| 319 | Text += '\''; |
| 320 | } |
| 321 | Text += ")" ; |
| 322 | } |
| 323 | return Text; |
| 324 | } |
| 325 | |
| 326 | static Error createError(StringRef Name, Error E) { |
| 327 | return make_error<DWPError>( |
| 328 | Args: ("failure while decompressing compressed section: '" + Name + "', " + |
| 329 | llvm::toString(E: std::move(E))) |
| 330 | .str()); |
| 331 | } |
| 332 | |
| 333 | static Error |
| 334 | handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections, |
| 335 | SectionRef Sec, StringRef Name, StringRef &Contents) { |
| 336 | auto *Obj = dyn_cast<ELFObjectFileBase>(Val: Sec.getObject()); |
| 337 | if (!Obj || |
| 338 | !(static_cast<ELFSectionRef>(Sec).getFlags() & ELF::SHF_COMPRESSED)) |
| 339 | return Error::success(); |
| 340 | bool IsLE = isa<object::ELF32LEObjectFile>(Val: Obj) || |
| 341 | isa<object::ELF64LEObjectFile>(Val: Obj); |
| 342 | bool Is64 = isa<object::ELF64LEObjectFile>(Val: Obj) || |
| 343 | isa<object::ELF64BEObjectFile>(Val: Obj); |
| 344 | Expected<Decompressor> Dec = Decompressor::create(Name, Data: Contents, IsLE, Is64Bit: Is64); |
| 345 | if (!Dec) |
| 346 | return createError(Name, E: Dec.takeError()); |
| 347 | |
| 348 | UncompressedSections.emplace_back(); |
| 349 | if (Error E = Dec->resizeAndDecompress(Out&: UncompressedSections.back())) |
| 350 | return createError(Name, E: std::move(E)); |
| 351 | |
| 352 | Contents = UncompressedSections.back(); |
| 353 | return Error::success(); |
| 354 | } |
| 355 | |
| 356 | static Error |
| 357 | buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE, |
| 358 | const CompileUnitIdentifiers &ID, StringRef DWPName) { |
| 359 | return make_error<DWPError>( |
| 360 | Args: std::string("duplicate DWO ID (" ) + utohexstr(X: PrevE.first) + ") in " + |
| 361 | buildDWODescription(Name: PrevE.second.Name, DWPName: PrevE.second.DWPName, |
| 362 | DWOName: PrevE.second.DWOName) + |
| 363 | " and " + buildDWODescription(Name: ID.Name, DWPName, DWOName: ID.DWOName)); |
| 364 | } |
| 365 | |
| 366 | // Create a mask so we don't trigger a emitIntValue() assert below if the |
| 367 | // NewOffset is over 4GB. |
| 368 | static void (DWPWriter &Out, DataExtractor &Data, |
| 369 | DenseMap<uint64_t, uint64_t> &OffsetRemapping, |
| 370 | uint64_t &Offset, const uint64_t Size, |
| 371 | uint32_t OldOffsetSize, uint32_t NewOffsetSize) { |
| 372 | const uint64_t NewOffsetMask = NewOffsetSize == 8 ? UINT64_MAX : UINT32_MAX; |
| 373 | while (Offset < Size) { |
| 374 | const uint64_t OldOffset = Data.getUnsigned(offset_ptr: &Offset, byte_size: OldOffsetSize); |
| 375 | const uint64_t NewOffset = OffsetRemapping[OldOffset]; |
| 376 | // Truncate the string offset like the old llvm-dwp would have if we aren't |
| 377 | // promoting the .debug_str_offsets to DWARF64. |
| 378 | Out.emitIntValue(Value: NewOffset & NewOffsetMask, Size: NewOffsetSize); |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | namespace llvm { |
| 383 | // Parse and return the header of an info section compile/type unit. |
| 384 | Expected<InfoSectionUnitHeader> (StringRef Info) { |
| 385 | InfoSectionUnitHeader ; |
| 386 | Error Err = Error::success(); |
| 387 | uint64_t Offset = 0; |
| 388 | DWARFDataExtractor InfoData(Info, true, 0); |
| 389 | std::tie(args&: Header.Length, args&: Header.Format) = |
| 390 | InfoData.getInitialLength(Off: &Offset, Err: &Err); |
| 391 | if (Err) |
| 392 | return make_error<DWPError>(Args: "cannot parse compile unit length: " + |
| 393 | llvm::toString(E: std::move(Err))); |
| 394 | |
| 395 | if (!InfoData.isValidOffset(offset: Offset + (Header.Length - 1))) { |
| 396 | return make_error<DWPError>( |
| 397 | Args: "compile unit exceeds .debug_info section range: " + |
| 398 | utostr(X: Offset + Header.Length) + " >= " + utostr(X: InfoData.size())); |
| 399 | } |
| 400 | |
| 401 | Header.Version = InfoData.getU16(offset_ptr: &Offset, Err: &Err); |
| 402 | if (Err) |
| 403 | return make_error<DWPError>(Args: "cannot parse compile unit version: " + |
| 404 | llvm::toString(E: std::move(Err))); |
| 405 | |
| 406 | uint64_t ; |
| 407 | if (Header.Version >= 5) { |
| 408 | // Size: Version (2), UnitType (1), AddrSize (1), DebugAbbrevOffset (4), |
| 409 | // Signature (8) |
| 410 | MinHeaderLength = 16; |
| 411 | } else { |
| 412 | // Size: Version (2), DebugAbbrevOffset (4), AddrSize (1) |
| 413 | MinHeaderLength = 7; |
| 414 | } |
| 415 | if (Header.Length < MinHeaderLength) { |
| 416 | return make_error<DWPError>(Args: "unit length is too small: expected at least " + |
| 417 | utostr(X: MinHeaderLength) + " got " + |
| 418 | utostr(X: Header.Length) + "." ); |
| 419 | } |
| 420 | if (Header.Version >= 5) { |
| 421 | Header.UnitType = InfoData.getU8(offset_ptr: &Offset); |
| 422 | Header.AddrSize = InfoData.getU8(offset_ptr: &Offset); |
| 423 | Header.DebugAbbrevOffset = InfoData.getU32(offset_ptr: &Offset); |
| 424 | Header.Signature = InfoData.getU64(offset_ptr: &Offset); |
| 425 | if (Header.UnitType == dwarf::DW_UT_split_type) { |
| 426 | // Type offset. |
| 427 | MinHeaderLength += 4; |
| 428 | if (Header.Length < MinHeaderLength) |
| 429 | return make_error<DWPError>(Args: "type unit is missing type offset" ); |
| 430 | InfoData.getU32(offset_ptr: &Offset); |
| 431 | } |
| 432 | } else { |
| 433 | // Note that, address_size and debug_abbrev_offset fields have switched |
| 434 | // places between dwarf version 4 and 5. |
| 435 | Header.DebugAbbrevOffset = InfoData.getU32(offset_ptr: &Offset); |
| 436 | Header.AddrSize = InfoData.getU8(offset_ptr: &Offset); |
| 437 | } |
| 438 | |
| 439 | Header.HeaderSize = Offset; |
| 440 | return Header; |
| 441 | } |
| 442 | |
| 443 | static void |
| 444 | writeStringsAndOffsets(DWPWriter &Out, DWPStringPool &Strings, |
| 445 | StringRef CurStrSection, StringRef CurStrOffsetSection, |
| 446 | uint16_t Version, SectionLengths &SectionLength, |
| 447 | const Dwarf64StrOffsetsPromotion StrOffsetsOptValue, |
| 448 | bool SingleInput) { |
| 449 | // Could possibly produce an error or warning if one of these was non-null but |
| 450 | // the other was null. |
| 451 | if (CurStrSection.empty() || CurStrOffsetSection.empty()) |
| 452 | return; |
| 453 | |
| 454 | // Fast path: when there is only one input, all strings are unique and offsets |
| 455 | // don't need remapping. Copy both sections directly without any hashing. |
| 456 | if (SingleInput && StrOffsetsOptValue != Dwarf64StrOffsetsPromotion::Always) { |
| 457 | Out.switchSection(Id: DS_Str); |
| 458 | Out.emitBytes(Data: CurStrSection); |
| 459 | Out.switchSection(Id: DS_StrOffsets); |
| 460 | Out.emitBytes(Data: CurStrOffsetSection); |
| 461 | return; |
| 462 | } |
| 463 | |
| 464 | DenseMap<uint64_t, uint64_t> OffsetRemapping; |
| 465 | // Pre-reserve based on estimated string count to avoid rehashing. |
| 466 | OffsetRemapping.reserve(NumEntries: CurStrSection.size() / 20); |
| 467 | |
| 468 | DataExtractor Data(CurStrSection, true); |
| 469 | uint64_t LocalOffset = 0; |
| 470 | uint64_t PrevOffset = 0; |
| 471 | |
| 472 | // Keep track if any new string offsets exceed UINT32_MAX. If any do, we can |
| 473 | // emit a DWARF64 .debug_str_offsets table for this compile unit. If the |
| 474 | // \a StrOffsetsOptValue argument is Dwarf64StrOffsetsPromotion::Always, then |
| 475 | // force the emission of DWARF64 .debug_str_offsets for testing. |
| 476 | uint32_t OldOffsetSize = 4; |
| 477 | uint32_t NewOffsetSize = |
| 478 | StrOffsetsOptValue == Dwarf64StrOffsetsPromotion::Always ? 8 : 4; |
| 479 | Out.switchSection(Id: DS_Str); |
| 480 | while (const char *S = Data.getCStr(OffsetPtr: &LocalOffset)) { |
| 481 | uint64_t NewOffset = Strings.getOffset(Str: S, Length: LocalOffset - PrevOffset); |
| 482 | OffsetRemapping[PrevOffset] = NewOffset; |
| 483 | // Only promote the .debug_str_offsets to DWARF64 if our setting allows it. |
| 484 | if (StrOffsetsOptValue != Dwarf64StrOffsetsPromotion::Disabled && |
| 485 | NewOffset > UINT32_MAX) { |
| 486 | NewOffsetSize = 8; |
| 487 | } |
| 488 | PrevOffset = LocalOffset; |
| 489 | } |
| 490 | |
| 491 | Data = DataExtractor(CurStrOffsetSection, true); |
| 492 | |
| 493 | Out.switchSection(Id: DS_StrOffsets); |
| 494 | |
| 495 | uint64_t Offset = 0; |
| 496 | uint64_t Size = CurStrOffsetSection.size(); |
| 497 | if (Version > 4) { |
| 498 | while (Offset < Size) { |
| 499 | const uint64_t = debugStrOffsetsHeaderSize(StrOffsetsData: Data, DwarfVersion: Version); |
| 500 | assert(HeaderSize <= Size - Offset && |
| 501 | "StrOffsetSection size is less than its header" ); |
| 502 | |
| 503 | uint64_t ContributionEnd = 0; |
| 504 | uint64_t ContributionSize = 0; |
| 505 | uint64_t = Offset; |
| 506 | if (HeaderSize == 8) { |
| 507 | ContributionSize = Data.getU32(offset_ptr: &HeaderLengthOffset); |
| 508 | } else if (HeaderSize == 16) { |
| 509 | OldOffsetSize = 8; |
| 510 | HeaderLengthOffset += 4; // skip the dwarf64 marker |
| 511 | ContributionSize = Data.getU64(offset_ptr: &HeaderLengthOffset); |
| 512 | } |
| 513 | ContributionEnd = ContributionSize + HeaderLengthOffset; |
| 514 | |
| 515 | StringRef = Data.getBytes(OffsetPtr: &Offset, Length: HeaderSize); |
| 516 | if (OldOffsetSize == 4 && NewOffsetSize == 8) { |
| 517 | // We had a DWARF32 .debug_str_offsets header, but we need to emit |
| 518 | // some string offsets that require 64 bit offsets on the .debug_str |
| 519 | // section. Emit the .debug_str_offsets header in DWARF64 format so we |
| 520 | // can emit string offsets that exceed UINT32_MAX without truncating |
| 521 | // the string offset. |
| 522 | |
| 523 | // 2 bytes for DWARF version, 2 bytes pad. |
| 524 | const uint64_t VersionPadSize = 4; |
| 525 | const uint64_t NewLength = |
| 526 | (ContributionSize - VersionPadSize) * 2 + VersionPadSize; |
| 527 | // Emit the DWARF64 length that starts with a 4 byte DW_LENGTH_DWARF64 |
| 528 | // value followed by the 8 byte updated length. |
| 529 | Out.emitIntValue(Value: llvm::dwarf::DW_LENGTH_DWARF64, Size: 4); |
| 530 | Out.emitIntValue(Value: NewLength, Size: 8); |
| 531 | // Emit DWARF version as a 2 byte integer. |
| 532 | Out.emitIntValue(Value: Version, Size: 2); |
| 533 | // Emit 2 bytes of padding. |
| 534 | Out.emitIntValue(Value: 0, Size: 2); |
| 535 | // Update the .debug_str_offsets section length contribution for the |
| 536 | // this .dwo file. |
| 537 | for (auto &Pair : SectionLength) { |
| 538 | if (Pair.first == DW_SECT_STR_OFFSETS) { |
| 539 | Pair.second = NewLength + 12; |
| 540 | break; |
| 541 | } |
| 542 | } |
| 543 | } else { |
| 544 | // Just emit the same .debug_str_offsets header. |
| 545 | Out.emitBytes(Data: HeaderBytes); |
| 546 | } |
| 547 | writeNewOffsetsTo(Out, Data, OffsetRemapping, Offset, Size: ContributionEnd, |
| 548 | OldOffsetSize, NewOffsetSize); |
| 549 | } |
| 550 | |
| 551 | } else { |
| 552 | assert(OldOffsetSize == NewOffsetSize); |
| 553 | writeNewOffsetsTo(Out, Data, OffsetRemapping, Offset, Size, OldOffsetSize, |
| 554 | NewOffsetSize); |
| 555 | } |
| 556 | } |
| 557 | |
| 558 | enum AccessField { Offset, Length }; |
| 559 | |
| 560 | static void |
| 561 | writeIndexTable(DWPWriter &Out, ArrayRef<unsigned> ContributionOffsets, |
| 562 | const MapVector<uint64_t, UnitIndexEntry> &IndexEntries, |
| 563 | const AccessField &Field) { |
| 564 | for (const auto &E : IndexEntries) |
| 565 | for (size_t I = 0; I != std::size(E.second.Contributions); ++I) |
| 566 | if (ContributionOffsets[I]) |
| 567 | Out.emitIntValue(Value: (Field == AccessField::Offset |
| 568 | ? E.second.Contributions[I].getOffset32() |
| 569 | : E.second.Contributions[I].getLength32()), |
| 570 | Size: 4); |
| 571 | } |
| 572 | |
| 573 | static void writeIndex(DWPWriter &Out, DWPSectionId Section, |
| 574 | ArrayRef<unsigned> ContributionOffsets, |
| 575 | const MapVector<uint64_t, UnitIndexEntry> &IndexEntries, |
| 576 | uint32_t IndexVersion) { |
| 577 | if (IndexEntries.empty()) |
| 578 | return; |
| 579 | |
| 580 | unsigned Columns = 0; |
| 581 | for (auto &C : ContributionOffsets) |
| 582 | if (C) |
| 583 | ++Columns; |
| 584 | |
| 585 | std::vector<unsigned> Buckets(NextPowerOf2(A: 3 * IndexEntries.size() / 2)); |
| 586 | uint64_t Mask = Buckets.size() - 1; |
| 587 | size_t I = 0; |
| 588 | for (const auto &P : IndexEntries) { |
| 589 | auto S = P.first; |
| 590 | auto H = S & Mask; |
| 591 | auto HP = ((S >> 32) & Mask) | 1; |
| 592 | while (Buckets[H]) { |
| 593 | assert(S != IndexEntries.begin()[Buckets[H] - 1].first && |
| 594 | "Duplicate unit" ); |
| 595 | H = (H + HP) & Mask; |
| 596 | } |
| 597 | Buckets[H] = I + 1; |
| 598 | ++I; |
| 599 | } |
| 600 | |
| 601 | Out.switchSection(Id: Section); |
| 602 | Out.emitIntValue(Value: IndexVersion, Size: 4); // Version |
| 603 | Out.emitIntValue(Value: Columns, Size: 4); // Columns |
| 604 | Out.emitIntValue(Value: IndexEntries.size(), Size: 4); // Num Units |
| 605 | Out.emitIntValue(Value: Buckets.size(), Size: 4); // Num Buckets |
| 606 | |
| 607 | // Write the signatures. |
| 608 | for (const auto &I : Buckets) |
| 609 | Out.emitIntValue(Value: I ? IndexEntries.begin()[I - 1].first : 0, Size: 8); |
| 610 | |
| 611 | // Write the indexes. |
| 612 | for (const auto &I : Buckets) |
| 613 | Out.emitIntValue(Value: I, Size: 4); |
| 614 | |
| 615 | // Write the column headers (which sections will appear in the table) |
| 616 | for (size_t I = 0; I != ContributionOffsets.size(); ++I) |
| 617 | if (ContributionOffsets[I]) |
| 618 | Out.emitIntValue(Value: getOnDiskSectionId(Index: I), Size: 4); |
| 619 | |
| 620 | // Write the offsets. |
| 621 | writeIndexTable(Out, ContributionOffsets, IndexEntries, Field: AccessField::Offset); |
| 622 | |
| 623 | // Write the lengths. |
| 624 | writeIndexTable(Out, ContributionOffsets, IndexEntries, Field: AccessField::Length); |
| 625 | } |
| 626 | |
| 627 | /// Map input ELF section names to DWP section IDs and DWARF section kinds. |
| 628 | static const StringMap<std::pair<DWPSectionId, DWARFSectionKind>> & |
| 629 | getKnownSections() { |
| 630 | static const StringMap<std::pair<DWPSectionId, DWARFSectionKind>> Map = { |
| 631 | {"debug_info.dwo" , {DS_Info, DW_SECT_INFO}}, |
| 632 | {"debug_types.dwo" , {DS_Types, DW_SECT_EXT_TYPES}}, |
| 633 | {"debug_str_offsets.dwo" , {DS_StrOffsets, DW_SECT_STR_OFFSETS}}, |
| 634 | {"debug_str.dwo" , {DS_Str, static_cast<DWARFSectionKind>(0)}}, |
| 635 | {"debug_loc.dwo" , {DS_Loc, DW_SECT_EXT_LOC}}, |
| 636 | {"debug_line.dwo" , {DS_Line, DW_SECT_LINE}}, |
| 637 | {"debug_macro.dwo" , {DS_Macro, DW_SECT_MACRO}}, |
| 638 | {"debug_abbrev.dwo" , {DS_Abbrev, DW_SECT_ABBREV}}, |
| 639 | {"debug_loclists.dwo" , {DS_Loclists, DW_SECT_LOCLISTS}}, |
| 640 | {"debug_rnglists.dwo" , {DS_Rnglists, DW_SECT_RNGLISTS}}, |
| 641 | {"debug_cu_index" , {DS_CUIndex, static_cast<DWARFSectionKind>(0)}}, |
| 642 | {"debug_tu_index" , {DS_TUIndex, static_cast<DWARFSectionKind>(0)}}, |
| 643 | }; |
| 644 | return Map; |
| 645 | } |
| 646 | |
| 647 | static Error handleSection( |
| 648 | const StringMap<std::pair<DWPSectionId, DWARFSectionKind>> &KnownSections, |
| 649 | const SectionRef &Section, DWPWriter &Out, |
| 650 | std::deque<SmallString<32>> &UncompressedSections, |
| 651 | uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry, |
| 652 | StringRef &CurStrSection, StringRef &CurStrOffsetSection, |
| 653 | std::vector<StringRef> &CurTypesSection, |
| 654 | std::vector<StringRef> &CurInfoSection, StringRef &AbbrevSection, |
| 655 | StringRef &CurCUIndexSection, StringRef &CurTUIndexSection, |
| 656 | SectionLengths &SectionLength) { |
| 657 | if (Section.isBSS()) |
| 658 | return Error::success(); |
| 659 | |
| 660 | if (Section.isVirtual()) |
| 661 | return Error::success(); |
| 662 | |
| 663 | Expected<StringRef> NameOrErr = Section.getName(); |
| 664 | if (!NameOrErr) |
| 665 | return NameOrErr.takeError(); |
| 666 | StringRef Name = *NameOrErr; |
| 667 | |
| 668 | Expected<StringRef> ContentsOrErr = Section.getContents(); |
| 669 | if (!ContentsOrErr) |
| 670 | return ContentsOrErr.takeError(); |
| 671 | StringRef Contents = *ContentsOrErr; |
| 672 | |
| 673 | if (auto Err = handleCompressedSection(UncompressedSections, Sec: Section, Name, |
| 674 | Contents)) |
| 675 | return Err; |
| 676 | |
| 677 | Name = Name.substr(Start: Name.find_first_not_of(Chars: "._" )); |
| 678 | |
| 679 | auto SectionPair = KnownSections.find(Key: Name); |
| 680 | if (SectionPair == KnownSections.end()) |
| 681 | return Error::success(); |
| 682 | |
| 683 | DWPSectionId SectionId = SectionPair->second.first; |
| 684 | DWARFSectionKind Kind = SectionPair->second.second; |
| 685 | |
| 686 | if (Kind) { |
| 687 | if (Kind != DW_SECT_EXT_TYPES && Kind != DW_SECT_INFO) |
| 688 | SectionLength.push_back(x: std::make_pair(x&: Kind, y: Contents.size())); |
| 689 | if (Kind == DW_SECT_ABBREV) |
| 690 | AbbrevSection = Contents; |
| 691 | } |
| 692 | |
| 693 | switch (SectionId) { |
| 694 | case DS_StrOffsets: |
| 695 | CurStrOffsetSection = Contents; |
| 696 | break; |
| 697 | case DS_Str: |
| 698 | CurStrSection = Contents; |
| 699 | break; |
| 700 | case DS_Types: |
| 701 | CurTypesSection.push_back(x: Contents); |
| 702 | break; |
| 703 | case DS_CUIndex: |
| 704 | CurCUIndexSection = Contents; |
| 705 | break; |
| 706 | case DS_TUIndex: |
| 707 | CurTUIndexSection = Contents; |
| 708 | break; |
| 709 | case DS_Info: |
| 710 | CurInfoSection.push_back(x: Contents); |
| 711 | break; |
| 712 | default: |
| 713 | // Pass-through: emit directly to output (zero-copy). |
| 714 | Out.switchSection(Id: SectionId); |
| 715 | Out.emitBytes(Data: Contents); |
| 716 | break; |
| 717 | } |
| 718 | return Error::success(); |
| 719 | } |
| 720 | |
| 721 | Error write(DWPWriter &Out, ArrayRef<std::string> Inputs, |
| 722 | OnCuIndexOverflow OverflowOptValue, |
| 723 | Dwarf64StrOffsetsPromotion StrOffsetsOptValue, |
| 724 | raw_pwrite_stream *OutputOS) { |
| 725 | const auto &KnownSections = getKnownSections(); |
| 726 | |
| 727 | MapVector<uint64_t, UnitIndexEntry> IndexEntries; |
| 728 | MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries; |
| 729 | |
| 730 | uint32_t ContributionOffsets[8] = {}; |
| 731 | uint16_t Version = 0; |
| 732 | uint32_t IndexVersion = 0; |
| 733 | StringRef FirstInput; |
| 734 | bool AnySectionOverflow = false; |
| 735 | |
| 736 | DWPStringPool Strings(Out); |
| 737 | |
| 738 | SmallVector<OwningBinary<object::ObjectFile>, 128> Objects; |
| 739 | Objects.reserve(N: Inputs.size()); |
| 740 | |
| 741 | std::deque<SmallString<32>> UncompressedSections; |
| 742 | |
| 743 | bool MachineSet = false; |
| 744 | |
| 745 | for (const auto &Input : Inputs) { |
| 746 | auto ErrOrObj = object::ObjectFile::createObjectFile(ObjectPath: Input); |
| 747 | if (!ErrOrObj) { |
| 748 | return handleErrors(E: ErrOrObj.takeError(), |
| 749 | Hs: [&](std::unique_ptr<ECError> EC) -> Error { |
| 750 | return createFileError(F: Input, E: Error(std::move(EC))); |
| 751 | }); |
| 752 | } |
| 753 | |
| 754 | auto &Obj = *ErrOrObj->getBinary(); |
| 755 | Objects.push_back(Elt: std::move(*ErrOrObj)); |
| 756 | |
| 757 | // Set output format metadata from the first input file. |
| 758 | if (!MachineSet) { |
| 759 | if (auto *ELFObj = dyn_cast<ELFObjectFileBase>(Val: &Obj)) { |
| 760 | Out.setMachine(ELFObj->getEMachine()); |
| 761 | Out.setOSABI(ELFObj->getEIdentOSABI()); |
| 762 | } else if (Obj.isWasm()) { |
| 763 | Out.setIsWASM(true); |
| 764 | } |
| 765 | MachineSet = true; |
| 766 | } |
| 767 | |
| 768 | UnitIndexEntry CurEntry = {}; |
| 769 | |
| 770 | StringRef CurStrSection; |
| 771 | StringRef CurStrOffsetSection; |
| 772 | std::vector<StringRef> CurTypesSection; |
| 773 | std::vector<StringRef> CurInfoSection; |
| 774 | StringRef AbbrevSection; |
| 775 | StringRef CurCUIndexSection; |
| 776 | StringRef CurTUIndexSection; |
| 777 | |
| 778 | // This maps each section contained in this file to its length. |
| 779 | // This information is later on used to calculate the contributions, |
| 780 | // i.e. offset and length, of each compile/type unit to a section. |
| 781 | SectionLengths SectionLength; |
| 782 | |
| 783 | for (const auto &Section : Obj.sections()) |
| 784 | if (auto Err = handleSection( |
| 785 | KnownSections, Section, Out, UncompressedSections, |
| 786 | ContributionOffsets, CurEntry, CurStrSection, CurStrOffsetSection, |
| 787 | CurTypesSection, CurInfoSection, AbbrevSection, CurCUIndexSection, |
| 788 | CurTUIndexSection, SectionLength)) |
| 789 | return Err; |
| 790 | |
| 791 | if (CurInfoSection.empty()) |
| 792 | continue; |
| 793 | |
| 794 | Expected<InfoSectionUnitHeader> = |
| 795 | parseInfoSectionUnitHeader(Info: CurInfoSection.front()); |
| 796 | if (!HeaderOrErr) |
| 797 | return HeaderOrErr.takeError(); |
| 798 | InfoSectionUnitHeader & = *HeaderOrErr; |
| 799 | |
| 800 | if (Version == 0) { |
| 801 | Version = Header.Version; |
| 802 | IndexVersion = Version < 5 ? 2 : 5; |
| 803 | FirstInput = Input; |
| 804 | } else if (Version != Header.Version) { |
| 805 | return make_error<DWPError>( |
| 806 | Args: "incompatible DWARF compile unit version: " + Input + " (version " + |
| 807 | utostr(X: Header.Version) + ") and " + FirstInput.str() + " (version " + |
| 808 | utostr(X: Version) + ")" ); |
| 809 | } |
| 810 | |
| 811 | writeStringsAndOffsets(Out, Strings, CurStrSection, CurStrOffsetSection, |
| 812 | Version: Header.Version, SectionLength, StrOffsetsOptValue, |
| 813 | SingleInput: Inputs.size() == 1); |
| 814 | |
| 815 | for (auto Pair : SectionLength) { |
| 816 | auto Index = getContributionIndex(Kind: Pair.first, IndexVersion); |
| 817 | CurEntry.Contributions[Index].setOffset(ContributionOffsets[Index]); |
| 818 | CurEntry.Contributions[Index].setLength(Pair.second); |
| 819 | uint32_t OldOffset = ContributionOffsets[Index]; |
| 820 | ContributionOffsets[Index] += CurEntry.Contributions[Index].getLength32(); |
| 821 | if (OldOffset > ContributionOffsets[Index]) { |
| 822 | uint32_t SectionIndex = 0; |
| 823 | for (auto &Section : Obj.sections()) { |
| 824 | if (SectionIndex == Index) { |
| 825 | if (Error Err = sectionOverflowErrorOrWarning( |
| 826 | PrevOffset: OldOffset, OverflowedOffset: ContributionOffsets[Index], SectionName: *Section.getName(), |
| 827 | OverflowOptValue, AnySectionOverflow)) |
| 828 | return Err; |
| 829 | } |
| 830 | ++SectionIndex; |
| 831 | } |
| 832 | if (AnySectionOverflow) |
| 833 | break; |
| 834 | } |
| 835 | } |
| 836 | |
| 837 | uint32_t &InfoSectionOffset = |
| 838 | ContributionOffsets[getContributionIndex(Kind: DW_SECT_INFO, IndexVersion)]; |
| 839 | if (CurCUIndexSection.empty()) { |
| 840 | bool FoundCUUnit = false; |
| 841 | Out.switchSection(Id: DS_Info); |
| 842 | for (StringRef Info : CurInfoSection) { |
| 843 | uint64_t UnitOffset = 0; |
| 844 | while (Info.size() > UnitOffset) { |
| 845 | Expected<InfoSectionUnitHeader> = |
| 846 | parseInfoSectionUnitHeader(Info: Info.substr(Start: UnitOffset, N: Info.size())); |
| 847 | if (!HeaderOrError) |
| 848 | return HeaderOrError.takeError(); |
| 849 | InfoSectionUnitHeader & = *HeaderOrError; |
| 850 | |
| 851 | UnitIndexEntry Entry = CurEntry; |
| 852 | auto &C = Entry.Contributions[getContributionIndex(Kind: DW_SECT_INFO, |
| 853 | IndexVersion)]; |
| 854 | C.setOffset(InfoSectionOffset); |
| 855 | C.setLength(Header.Length + 4); |
| 856 | |
| 857 | if (std::numeric_limits<uint32_t>::max() - InfoSectionOffset < |
| 858 | C.getLength32()) { |
| 859 | if (Error Err = sectionOverflowErrorOrWarning( |
| 860 | PrevOffset: InfoSectionOffset, OverflowedOffset: InfoSectionOffset + C.getLength32(), |
| 861 | SectionName: "debug_info" , OverflowOptValue, AnySectionOverflow)) |
| 862 | return Err; |
| 863 | if (AnySectionOverflow) { |
| 864 | FoundCUUnit = true; |
| 865 | break; |
| 866 | } |
| 867 | } |
| 868 | |
| 869 | UnitOffset += C.getLength32(); |
| 870 | if (Header.Version < 5 || |
| 871 | Header.UnitType == dwarf::DW_UT_split_compile) { |
| 872 | Expected<CompileUnitIdentifiers> EID = getCUIdentifiers( |
| 873 | Header, Abbrev: AbbrevSection, |
| 874 | Info: Info.substr(Start: UnitOffset - C.getLength32(), N: C.getLength32()), |
| 875 | StrOffsets: CurStrOffsetSection, Str: CurStrSection); |
| 876 | |
| 877 | if (!EID) |
| 878 | return createFileError(F: Input, E: EID.takeError()); |
| 879 | const auto &ID = *EID; |
| 880 | auto P = IndexEntries.insert(KV: std::make_pair(x: ID.Signature, y&: Entry)); |
| 881 | if (!P.second) |
| 882 | return buildDuplicateError(PrevE: *P.first, ID, DWPName: "" ); |
| 883 | P.first->second.Name = ID.Name; |
| 884 | P.first->second.DWOName = ID.DWOName; |
| 885 | |
| 886 | FoundCUUnit = true; |
| 887 | } else if (Header.UnitType == dwarf::DW_UT_split_type) { |
| 888 | auto P = TypeIndexEntries.insert( |
| 889 | KV: std::make_pair(x&: *Header.Signature, y&: Entry)); |
| 890 | if (!P.second) |
| 891 | continue; |
| 892 | } |
| 893 | Out.emitBytes( |
| 894 | Data: Info.substr(Start: UnitOffset - C.getLength32(), N: C.getLength32())); |
| 895 | InfoSectionOffset += C.getLength32(); |
| 896 | } |
| 897 | if (AnySectionOverflow) |
| 898 | break; |
| 899 | } |
| 900 | |
| 901 | if (!FoundCUUnit) |
| 902 | return make_error<DWPError>(Args: "no compile unit found in file: " + Input); |
| 903 | |
| 904 | if (IndexVersion == 2) { |
| 905 | // Add types from the .debug_types section from DWARF < 5. |
| 906 | if (Error Err = addAllTypesFromTypesSection( |
| 907 | Out, TypeIndexEntries, OutputSection: DS_Types, TypesSections: CurTypesSection, CUEntry: CurEntry, |
| 908 | TypesOffset&: ContributionOffsets[getContributionIndex(Kind: DW_SECT_EXT_TYPES, IndexVersion: 2)], |
| 909 | OverflowOptValue, AnySectionOverflow)) |
| 910 | return Err; |
| 911 | } |
| 912 | if (AnySectionOverflow) |
| 913 | break; |
| 914 | continue; |
| 915 | } |
| 916 | |
| 917 | if (CurInfoSection.size() != 1) |
| 918 | return make_error<DWPError>(Args: "expected exactly one occurrence of a debug " |
| 919 | "info section in a .dwp file" ); |
| 920 | StringRef DwpSingleInfoSection = CurInfoSection.front(); |
| 921 | |
| 922 | DWARFUnitIndex CUIndex(DW_SECT_INFO); |
| 923 | DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian()); |
| 924 | if (!CUIndex.parse(IndexData: CUIndexData)) |
| 925 | return make_error<DWPError>(Args: "failed to parse cu_index" ); |
| 926 | if (CUIndex.getVersion() != IndexVersion) |
| 927 | return make_error<DWPError>(Args: "incompatible cu_index versions, found " + |
| 928 | utostr(X: CUIndex.getVersion()) + |
| 929 | " and expecting " + utostr(X: IndexVersion)); |
| 930 | |
| 931 | Out.switchSection(Id: DS_Info); |
| 932 | for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) { |
| 933 | auto *I = E.getContributions(); |
| 934 | if (!I) |
| 935 | continue; |
| 936 | auto P = IndexEntries.insert(KV: std::make_pair(x: E.getSignature(), y&: CurEntry)); |
| 937 | StringRef CUInfoSection = |
| 938 | getSubsection(Section: DwpSingleInfoSection, Entry: E, Kind: DW_SECT_INFO); |
| 939 | Expected<InfoSectionUnitHeader> = |
| 940 | parseInfoSectionUnitHeader(Info: CUInfoSection); |
| 941 | if (!HeaderOrError) |
| 942 | return HeaderOrError.takeError(); |
| 943 | InfoSectionUnitHeader & = *HeaderOrError; |
| 944 | |
| 945 | Expected<CompileUnitIdentifiers> EID = getCUIdentifiers( |
| 946 | Header, Abbrev: getSubsection(Section: AbbrevSection, Entry: E, Kind: DW_SECT_ABBREV), |
| 947 | Info: CUInfoSection, |
| 948 | StrOffsets: getSubsection(Section: CurStrOffsetSection, Entry: E, Kind: DW_SECT_STR_OFFSETS), |
| 949 | Str: CurStrSection); |
| 950 | if (!EID) |
| 951 | return createFileError(F: Input, E: EID.takeError()); |
| 952 | const auto &ID = *EID; |
| 953 | if (!P.second) |
| 954 | return buildDuplicateError(PrevE: *P.first, ID, DWPName: Input); |
| 955 | auto &NewEntry = P.first->second; |
| 956 | NewEntry.Name = ID.Name; |
| 957 | NewEntry.DWOName = ID.DWOName; |
| 958 | NewEntry.DWPName = Input; |
| 959 | for (auto Kind : CUIndex.getColumnKinds()) { |
| 960 | if (!isSupportedSectionKind(Kind)) |
| 961 | continue; |
| 962 | auto &C = |
| 963 | NewEntry.Contributions[getContributionIndex(Kind, IndexVersion)]; |
| 964 | C.setOffset(C.getOffset() + I->getOffset()); |
| 965 | C.setLength(I->getLength()); |
| 966 | ++I; |
| 967 | } |
| 968 | unsigned Index = getContributionIndex(Kind: DW_SECT_INFO, IndexVersion); |
| 969 | auto &C = NewEntry.Contributions[Index]; |
| 970 | Out.emitBytes(Data: CUInfoSection); |
| 971 | C.setOffset(InfoSectionOffset); |
| 972 | InfoSectionOffset += C.getLength32(); |
| 973 | } |
| 974 | |
| 975 | if (!CurTUIndexSection.empty()) { |
| 976 | llvm::DWARFSectionKind TUSectionKind; |
| 977 | DWPSectionId OutSection; |
| 978 | StringRef TypeInputSection; |
| 979 | // Write type units into debug info section for DWARFv5. |
| 980 | if (Version >= 5) { |
| 981 | TUSectionKind = DW_SECT_INFO; |
| 982 | OutSection = DS_Info; |
| 983 | TypeInputSection = DwpSingleInfoSection; |
| 984 | } else { |
| 985 | // Write type units into debug types section for DWARF < 5. |
| 986 | if (CurTypesSection.size() != 1) |
| 987 | return make_error<DWPError>( |
| 988 | Args: "multiple type unit sections in .dwp file" ); |
| 989 | |
| 990 | TUSectionKind = DW_SECT_EXT_TYPES; |
| 991 | OutSection = DS_Types; |
| 992 | TypeInputSection = CurTypesSection.front(); |
| 993 | } |
| 994 | |
| 995 | DWARFUnitIndex TUIndex(TUSectionKind); |
| 996 | DataExtractor TUIndexData(CurTUIndexSection, Obj.isLittleEndian()); |
| 997 | if (!TUIndex.parse(IndexData: TUIndexData)) |
| 998 | return make_error<DWPError>(Args: "failed to parse tu_index" ); |
| 999 | if (TUIndex.getVersion() != IndexVersion) |
| 1000 | return make_error<DWPError>(Args: "incompatible tu_index versions, found " + |
| 1001 | utostr(X: TUIndex.getVersion()) + |
| 1002 | " and expecting " + utostr(X: IndexVersion)); |
| 1003 | |
| 1004 | unsigned TypesContributionIndex = |
| 1005 | getContributionIndex(Kind: TUSectionKind, IndexVersion); |
| 1006 | if (Error Err = addAllTypesFromDWP( |
| 1007 | Out, TypeIndexEntries, TUIndex, OutputSection: OutSection, Types: TypeInputSection, |
| 1008 | TUEntry: CurEntry, TypesOffset&: ContributionOffsets[TypesContributionIndex], |
| 1009 | TypesContributionIndex, OverflowOptValue, AnySectionOverflow)) |
| 1010 | return Err; |
| 1011 | } |
| 1012 | if (AnySectionOverflow) |
| 1013 | break; |
| 1014 | } |
| 1015 | |
| 1016 | if (Version < 5) { |
| 1017 | // Lie about there being no info contributions so the TU index only includes |
| 1018 | // the type unit contribution for DWARF < 5. In DWARFv5 the TU index has a |
| 1019 | // contribution to the info section, so we do not want to lie about it. |
| 1020 | ContributionOffsets[0] = 0; |
| 1021 | } |
| 1022 | writeIndex(Out, Section: DS_TUIndex, ContributionOffsets, IndexEntries: TypeIndexEntries, |
| 1023 | IndexVersion); |
| 1024 | |
| 1025 | if (Version < 5) { |
| 1026 | // Lie about the type contribution for DWARF < 5. In DWARFv5 the type |
| 1027 | // section does not exist, so no need to do anything about this. |
| 1028 | ContributionOffsets[getContributionIndex(Kind: DW_SECT_EXT_TYPES, IndexVersion: 2)] = 0; |
| 1029 | // Unlie about the info contribution |
| 1030 | ContributionOffsets[0] = 1; |
| 1031 | } |
| 1032 | |
| 1033 | writeIndex(Out, Section: DS_CUIndex, ContributionOffsets, IndexEntries, IndexVersion); |
| 1034 | |
| 1035 | // Write ELF output while input data is still alive (zero-copy chunks |
| 1036 | // reference mmap'd input data held by the Objects vector above). |
| 1037 | if (OutputOS) |
| 1038 | return Out.write(OS&: *OutputOS); |
| 1039 | |
| 1040 | return Error::success(); |
| 1041 | } |
| 1042 | |
| 1043 | //===----------------------------------------------------------------------===// |
| 1044 | // DWPWriter::writeELF — produce a minimal ELF64 relocatable object. |
| 1045 | //===----------------------------------------------------------------------===// |
| 1046 | |
| 1047 | Error DWPWriter::writeELF(raw_pwrite_stream &OS) { |
| 1048 | support::endian::Writer Wr(OS, llvm::endianness::little); |
| 1049 | |
| 1050 | // Section metadata table. |
| 1051 | struct SectionMeta { |
| 1052 | DWPSectionId Id; |
| 1053 | const char *Name; |
| 1054 | uint64_t Flags; |
| 1055 | uint64_t EntSize; |
| 1056 | }; |
| 1057 | static constexpr SectionMeta Meta[] = { |
| 1058 | {.Id: DS_Loclists, .Name: ".debug_loclists.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1059 | {.Id: DS_Loc, .Name: ".debug_loc.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1060 | {.Id: DS_Abbrev, .Name: ".debug_abbrev.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1061 | {.Id: DS_Line, .Name: ".debug_line.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1062 | {.Id: DS_Rnglists, .Name: ".debug_rnglists.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1063 | {.Id: DS_Macro, .Name: ".debug_macro.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1064 | {.Id: DS_Str, .Name: ".debug_str.dwo" , |
| 1065 | .Flags: ELF::SHF_EXCLUDE | ELF::SHF_MERGE | ELF::SHF_STRINGS, .EntSize: 1}, |
| 1066 | {.Id: DS_StrOffsets, .Name: ".debug_str_offsets.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1067 | {.Id: DS_Info, .Name: ".debug_info.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1068 | {.Id: DS_Types, .Name: ".debug_types.dwo" , .Flags: ELF::SHF_EXCLUDE, .EntSize: 0}, |
| 1069 | {.Id: DS_TUIndex, .Name: ".debug_tu_index" , .Flags: 0, .EntSize: 0}, |
| 1070 | {.Id: DS_CUIndex, .Name: ".debug_cu_index" , .Flags: 0, .EntSize: 0}, |
| 1071 | }; |
| 1072 | |
| 1073 | // Collect non-empty sections and build the section name string table. |
| 1074 | struct OutputEntry { |
| 1075 | const SectionData *Data; |
| 1076 | const char *Name; |
| 1077 | uint64_t Flags; |
| 1078 | uint64_t EntSize; |
| 1079 | uint32_t NameOffset; |
| 1080 | uint64_t FileOffset; // filled in during layout |
| 1081 | }; |
| 1082 | SmallVector<OutputEntry> Entries; |
| 1083 | |
| 1084 | SmallString<256> Strtab; |
| 1085 | Strtab.push_back(Elt: '\0'); // null string at offset 0 |
| 1086 | |
| 1087 | for (const auto &M : Meta) { |
| 1088 | if (Sections[M.Id].empty()) |
| 1089 | continue; |
| 1090 | uint32_t NameOff = Strtab.size(); |
| 1091 | Strtab.append(RHS: M.Name); |
| 1092 | Strtab.push_back(Elt: '\0'); |
| 1093 | Entries.push_back( |
| 1094 | Elt: {.Data: &Sections[M.Id], .Name: M.Name, .Flags: M.Flags, .EntSize: M.EntSize, .NameOffset: NameOff, .FileOffset: 0}); |
| 1095 | } |
| 1096 | |
| 1097 | // Add .strtab and .symtab name entries. |
| 1098 | uint32_t StrtabNameOff = Strtab.size(); |
| 1099 | Strtab.append(RHS: ".strtab" ); |
| 1100 | Strtab.push_back(Elt: '\0'); |
| 1101 | uint32_t SymtabNameOff = Strtab.size(); |
| 1102 | Strtab.append(RHS: ".symtab" ); |
| 1103 | Strtab.push_back(Elt: '\0'); |
| 1104 | |
| 1105 | // Layout: |
| 1106 | // [ELF Header] 64 bytes |
| 1107 | // [section data...] variable |
| 1108 | // [.strtab data] variable |
| 1109 | // [padding to 8-byte align] |
| 1110 | // [.symtab data] 24 bytes (one null entry) |
| 1111 | // [padding to 8-byte align] |
| 1112 | // [Section Header Table] 64 * NumSections bytes |
| 1113 | |
| 1114 | constexpr uint64_t EhdrSize = sizeof(ELF::Elf64_Ehdr); |
| 1115 | constexpr uint64_t SymEntSize = 24; |
| 1116 | |
| 1117 | uint64_t Offset = EhdrSize; |
| 1118 | for (auto &E : Entries) { |
| 1119 | E.FileOffset = Offset; |
| 1120 | Offset += E.Data->totalSize(); |
| 1121 | } |
| 1122 | |
| 1123 | uint64_t StrtabOffset = Offset; |
| 1124 | Offset += Strtab.size(); |
| 1125 | |
| 1126 | uint64_t SymtabOffset = alignTo(Value: Offset, Align: 8); |
| 1127 | Offset = SymtabOffset + SymEntSize; |
| 1128 | |
| 1129 | uint64_t SHTOffset = alignTo(Value: Offset, Align: 8); |
| 1130 | |
| 1131 | // Section indices: [0]=null, [1..N]=data, [N+1]=strtab, [N+2]=symtab |
| 1132 | uint32_t StrtabIdx = 1 + Entries.size(); |
| 1133 | uint32_t SymtabIdx = StrtabIdx + 1; |
| 1134 | uint32_t NumSections = SymtabIdx + 1; |
| 1135 | |
| 1136 | // --- Write ELF header --- |
| 1137 | ELF::writeHeader(W&: Wr, /*Is64Bit=*/true, OSABI: ELFOSABI, /*ABIVersion=*/0, EMachine: ELFMachine, |
| 1138 | /*EFlags=*/0, SHOff: SHTOffset, SHNum: NumSections, SHStrNdx: StrtabIdx); |
| 1139 | |
| 1140 | // --- Write section data --- |
| 1141 | for (const auto &E : Entries) |
| 1142 | E.Data->writeTo(OS); |
| 1143 | |
| 1144 | // --- Write .strtab --- |
| 1145 | OS.write(Ptr: Strtab.data(), Size: Strtab.size()); |
| 1146 | |
| 1147 | // --- Pad + write .symtab (one null symbol entry) --- |
| 1148 | OS.write_zeros(NumZeros: SymtabOffset - (StrtabOffset + Strtab.size())); |
| 1149 | OS.write_zeros(NumZeros: SymEntSize); |
| 1150 | |
| 1151 | // --- Pad for section header table --- |
| 1152 | uint64_t CurPos = SymtabOffset + SymEntSize; |
| 1153 | OS.write_zeros(NumZeros: SHTOffset - CurPos); |
| 1154 | |
| 1155 | // [0] ELF::SHT_NULL |
| 1156 | ELF::writeSectionHeader(W&: Wr, Is64Bit: true, Name: 0, Type: ELF::SHT_NULL, Flags: 0, Address: 0, Offset: 0, Size: 0, Link: 0, Info: 0, Alignment: 0, EntrySize: 0); |
| 1157 | |
| 1158 | // [1..N] data sections |
| 1159 | for (const auto &E : Entries) |
| 1160 | ELF::writeSectionHeader(W&: Wr, Is64Bit: true, Name: E.NameOffset, Type: ELF::SHT_PROGBITS, Flags: E.Flags, |
| 1161 | Address: 0, Offset: E.FileOffset, Size: E.Data->totalSize(), Link: 0, Info: 0, Alignment: 1, |
| 1162 | EntrySize: E.EntSize); |
| 1163 | |
| 1164 | // [N+1] .strtab |
| 1165 | ELF::writeSectionHeader(W&: Wr, Is64Bit: true, Name: StrtabNameOff, Type: ELF::SHT_STRTAB, Flags: 0, Address: 0, |
| 1166 | Offset: StrtabOffset, Size: Strtab.size(), Link: 0, Info: 0, Alignment: 1, EntrySize: 0); |
| 1167 | |
| 1168 | // [N+2] .symtab |
| 1169 | ELF::writeSectionHeader(W&: Wr, Is64Bit: true, Name: SymtabNameOff, Type: ELF::SHT_SYMTAB, Flags: 0, Address: 0, |
| 1170 | Offset: SymtabOffset, Size: SymEntSize, Link: StrtabIdx, Info: 1, Alignment: 8, |
| 1171 | EntrySize: SymEntSize); |
| 1172 | |
| 1173 | return Error::success(); |
| 1174 | } |
| 1175 | |
| 1176 | //===----------------------------------------------------------------------===// |
| 1177 | // DWPWriter::writeWASM — produce a minimal WASM object with custom sections. |
| 1178 | //===----------------------------------------------------------------------===// |
| 1179 | |
| 1180 | Error DWPWriter::writeWASM(raw_pwrite_stream &OS) { |
| 1181 | // Section name table (same names as ELF but without SHF_EXCLUDE flags). |
| 1182 | static constexpr struct { |
| 1183 | DWPSectionId Id; |
| 1184 | const char *Name; |
| 1185 | } Meta[] = { |
| 1186 | {.Id: DS_Loclists, .Name: ".debug_loclists.dwo" }, |
| 1187 | {.Id: DS_Loc, .Name: ".debug_loc.dwo" }, |
| 1188 | {.Id: DS_Abbrev, .Name: ".debug_abbrev.dwo" }, |
| 1189 | {.Id: DS_Line, .Name: ".debug_line.dwo" }, |
| 1190 | {.Id: DS_Rnglists, .Name: ".debug_rnglists.dwo" }, |
| 1191 | {.Id: DS_Macro, .Name: ".debug_macro.dwo" }, |
| 1192 | {.Id: DS_Str, .Name: ".debug_str.dwo" }, |
| 1193 | {.Id: DS_StrOffsets, .Name: ".debug_str_offsets.dwo" }, |
| 1194 | {.Id: DS_Info, .Name: ".debug_info.dwo" }, |
| 1195 | {.Id: DS_Types, .Name: ".debug_types.dwo" }, |
| 1196 | {.Id: DS_TUIndex, .Name: ".debug_tu_index" }, |
| 1197 | {.Id: DS_CUIndex, .Name: ".debug_cu_index" }, |
| 1198 | }; |
| 1199 | |
| 1200 | // WASM magic and version. |
| 1201 | OS.write(Ptr: "\0asm" , Size: 4); |
| 1202 | const uint8_t Version[] = {0x01, 0x00, 0x00, 0x00}; |
| 1203 | OS.write(Ptr: reinterpret_cast<const char *>(Version), Size: 4); |
| 1204 | |
| 1205 | // Emit each non-empty section as a WASM custom section (id=0). |
| 1206 | for (const auto &M : Meta) { |
| 1207 | const SectionData &SD = Sections[M.Id]; |
| 1208 | if (SD.empty()) |
| 1209 | continue; |
| 1210 | |
| 1211 | size_t NameLen = strlen(s: M.Name); |
| 1212 | uint64_t PayloadSize = SD.totalSize(); |
| 1213 | |
| 1214 | // Custom section payload = ULEB128(name_len) + name + data. |
| 1215 | uint8_t NameLenEncoded[10]; |
| 1216 | unsigned NameLenSize = encodeULEB128(Value: NameLen, p: NameLenEncoded); |
| 1217 | uint64_t SectionPayloadSize = NameLenSize + NameLen + PayloadSize; |
| 1218 | |
| 1219 | // Section header: id byte + ULEB128(section_payload_size). |
| 1220 | OS.write(C: 0x00); // Custom section id |
| 1221 | uint8_t SizeEncoded[10]; |
| 1222 | unsigned SizeLen = encodeULEB128(Value: SectionPayloadSize, p: SizeEncoded); |
| 1223 | OS.write(Ptr: reinterpret_cast<const char *>(SizeEncoded), Size: SizeLen); |
| 1224 | |
| 1225 | // Name |
| 1226 | OS.write(Ptr: reinterpret_cast<const char *>(NameLenEncoded), Size: NameLenSize); |
| 1227 | OS.write(Ptr: M.Name, Size: NameLen); |
| 1228 | |
| 1229 | // Data |
| 1230 | SD.writeTo(OS); |
| 1231 | } |
| 1232 | |
| 1233 | return Error::success(); |
| 1234 | } |
| 1235 | |
| 1236 | } // namespace llvm |
| 1237 | |