diff options
author | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/llvm14/tools/obj2yaml | |
parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
download | ydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz |
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/llvm14/tools/obj2yaml')
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/archive2yaml.cpp | 114 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/coff2yaml.cpp | 366 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/dwarf2yaml.cpp | 463 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/elf2yaml.cpp | 1596 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/macho2yaml.cpp | 672 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/minidump2yaml.cpp | 23 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/obj2yaml.cpp | 95 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/obj2yaml.h | 57 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/wasm2yaml.cpp | 405 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/xcoff2yaml.cpp | 152 | ||||
-rw-r--r-- | contrib/libs/llvm14/tools/obj2yaml/ya.make | 47 |
11 files changed, 3990 insertions, 0 deletions
diff --git a/contrib/libs/llvm14/tools/obj2yaml/archive2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/archive2yaml.cpp new file mode 100644 index 0000000000..c7b0ee4802 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/archive2yaml.cpp @@ -0,0 +1,114 @@ +//===------ utils/archive2yaml.cpp - obj2yaml conversion tool ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/ObjectYAML/ArchiveYAML.h" + +using namespace llvm; + +namespace { + +class ArchiveDumper { +public: + Expected<ArchYAML::Archive *> dump(MemoryBufferRef Source) { + StringRef Buffer = Source.getBuffer(); + assert(file_magic::archive == identify_magic(Buffer)); + + std::unique_ptr<ArchYAML::Archive> Obj = + std::make_unique<ArchYAML::Archive>(); + + StringRef Magic = "!<arch>\n"; + if (!Buffer.startswith(Magic)) + return createStringError(std::errc::not_supported, + "only regular archives are supported"); + Obj->Magic = Magic; + Buffer = Buffer.drop_front(Magic.size()); + + Obj->Members.emplace(); + while (!Buffer.empty()) { + uint64_t Offset = Buffer.data() - Source.getBuffer().data(); + if (Buffer.size() < sizeof(ArchiveHeader)) + return createStringError( + std::errc::illegal_byte_sequence, + "unable to read the header of a child at offset 0x%" PRIx64, + Offset); + + const ArchiveHeader &Hdr = + *reinterpret_cast<const ArchiveHeader *>(Buffer.data()); + Buffer = Buffer.drop_front(sizeof(ArchiveHeader)); + + auto ToString = [](ArrayRef<char> V) { + // We don't want to dump excessive spaces. + return StringRef(V.data(), V.size()).rtrim(' '); + }; + + ArchYAML::Archive::Child C; + C.Fields["Name"].Value = ToString(Hdr.Name); + C.Fields["LastModified"].Value = ToString(Hdr.LastModified); + C.Fields["UID"].Value = ToString(Hdr.UID); + C.Fields["GID"].Value = ToString(Hdr.GID); + C.Fields["AccessMode"].Value = ToString(Hdr.AccessMode); + StringRef SizeStr = ToString(Hdr.Size); + C.Fields["Size"].Value = SizeStr; + C.Fields["Terminator"].Value = ToString(Hdr.Terminator); + + uint64_t Size; + if (SizeStr.getAsInteger(10, Size)) + return createStringError( + std::errc::illegal_byte_sequence, + "unable to read the size of a child at offset 0x%" PRIx64 + " as integer: \"%s\"", + Offset, SizeStr.str().c_str()); + if (Buffer.size() < Size) + return createStringError( + std::errc::illegal_byte_sequence, + "unable to read the data of a child at offset 0x%" PRIx64 + " of size %" PRId64 ": the remaining archive size is %zu", + Offset, Size, Buffer.size()); + if (!Buffer.empty()) + C.Content = arrayRefFromStringRef(Buffer.take_front(Size)); + + const bool HasPaddingByte = (Size & 1) && Buffer.size() > Size; + if (HasPaddingByte) + C.PaddingByte = Buffer[Size]; + + Obj->Members->push_back(C); + // If the size is odd, consume a padding byte. + Buffer = Buffer.drop_front(HasPaddingByte ? Size + 1 : Size); + } + + return Obj.release(); + } + +private: + struct ArchiveHeader { + char Name[16]; + char LastModified[12]; + char UID[6]; + char GID[6]; + char AccessMode[8]; + char Size[10]; + char Terminator[2]; + }; +}; + +} // namespace + +Error archive2yaml(raw_ostream &Out, MemoryBufferRef Source) { + ArchiveDumper Dumper; + Expected<ArchYAML::Archive *> YAMLOrErr = Dumper.dump(Source); + if (!YAMLOrErr) + return YAMLOrErr.takeError(); + + std::unique_ptr<ArchYAML::Archive> YAML(YAMLOrErr.get()); + yaml::Output Yout(Out); + Yout << *YAML; + + return Error::success(); +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/coff2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/coff2yaml.cpp new file mode 100644 index 0000000000..604799fb27 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/coff2yaml.cpp @@ -0,0 +1,366 @@ +//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/Object/COFF.h" +#include "llvm/ObjectYAML/COFFYAML.h" +#include "llvm/ObjectYAML/CodeViewYAMLTypes.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace llvm; + +namespace { + +class COFFDumper { + const object::COFFObjectFile &Obj; + COFFYAML::Object YAMLObj; + template <typename T> + void dumpOptionalHeader(T OptionalHeader); + void dumpHeader(); + void dumpSections(unsigned numSections); + void dumpSymbols(unsigned numSymbols); + +public: + COFFDumper(const object::COFFObjectFile &Obj); + COFFYAML::Object &getYAMLObj(); +}; + +} + +COFFDumper::COFFDumper(const object::COFFObjectFile &Obj) : Obj(Obj) { + if (const object::pe32_header *PE32Header = Obj.getPE32Header()) + dumpOptionalHeader(PE32Header); + else if (const object::pe32plus_header *PE32PlusHeader = + Obj.getPE32PlusHeader()) + dumpOptionalHeader(PE32PlusHeader); + + dumpHeader(); + dumpSections(Obj.getNumberOfSections()); + dumpSymbols(Obj.getNumberOfSymbols()); +} + +template <typename T> void COFFDumper::dumpOptionalHeader(T OptionalHeader) { + YAMLObj.OptionalHeader = COFFYAML::PEHeader(); + YAMLObj.OptionalHeader->Header.AddressOfEntryPoint = + OptionalHeader->AddressOfEntryPoint; + YAMLObj.OptionalHeader->Header.ImageBase = OptionalHeader->ImageBase; + YAMLObj.OptionalHeader->Header.SectionAlignment = + OptionalHeader->SectionAlignment; + YAMLObj.OptionalHeader->Header.FileAlignment = OptionalHeader->FileAlignment; + YAMLObj.OptionalHeader->Header.MajorOperatingSystemVersion = + OptionalHeader->MajorOperatingSystemVersion; + YAMLObj.OptionalHeader->Header.MinorOperatingSystemVersion = + OptionalHeader->MinorOperatingSystemVersion; + YAMLObj.OptionalHeader->Header.MajorImageVersion = + OptionalHeader->MajorImageVersion; + YAMLObj.OptionalHeader->Header.MinorImageVersion = + OptionalHeader->MinorImageVersion; + YAMLObj.OptionalHeader->Header.MajorSubsystemVersion = + OptionalHeader->MajorSubsystemVersion; + YAMLObj.OptionalHeader->Header.MinorSubsystemVersion = + OptionalHeader->MinorSubsystemVersion; + YAMLObj.OptionalHeader->Header.Subsystem = OptionalHeader->Subsystem; + YAMLObj.OptionalHeader->Header.DLLCharacteristics = + OptionalHeader->DLLCharacteristics; + YAMLObj.OptionalHeader->Header.SizeOfStackReserve = + OptionalHeader->SizeOfStackReserve; + YAMLObj.OptionalHeader->Header.SizeOfStackCommit = + OptionalHeader->SizeOfStackCommit; + YAMLObj.OptionalHeader->Header.SizeOfHeapReserve = + OptionalHeader->SizeOfHeapReserve; + YAMLObj.OptionalHeader->Header.SizeOfHeapCommit = + OptionalHeader->SizeOfHeapCommit; + YAMLObj.OptionalHeader->Header.NumberOfRvaAndSize = + OptionalHeader->NumberOfRvaAndSize; + unsigned I = 0; + for (auto &DestDD : YAMLObj.OptionalHeader->DataDirectories) { + const object::data_directory *DD = Obj.getDataDirectory(I++); + if (!DD) + continue; + DestDD = COFF::DataDirectory(); + DestDD->RelativeVirtualAddress = DD->RelativeVirtualAddress; + DestDD->Size = DD->Size; + } +} + +void COFFDumper::dumpHeader() { + YAMLObj.Header.Machine = Obj.getMachine(); + YAMLObj.Header.Characteristics = Obj.getCharacteristics(); +} + +static void +initializeFileAndStringTable(const llvm::object::COFFObjectFile &Obj, + codeview::StringsAndChecksumsRef &SC) { + + ExitOnError Err("invalid .debug$S section"); + // Iterate all .debug$S sections looking for the checksums and string table. + // Exit as soon as both sections are found. + for (const auto &S : Obj.sections()) { + if (SC.hasStrings() && SC.hasChecksums()) + break; + + Expected<StringRef> SectionNameOrErr = S.getName(); + if (!SectionNameOrErr) { + consumeError(SectionNameOrErr.takeError()); + continue; + } + + ArrayRef<uint8_t> sectionData; + if ((*SectionNameOrErr) != ".debug$S") + continue; + + const object::coff_section *COFFSection = Obj.getCOFFSection(S); + + cantFail(Obj.getSectionContents(COFFSection, sectionData)); + + BinaryStreamReader Reader(sectionData, support::little); + uint32_t Magic; + + Err(Reader.readInteger(Magic)); + assert(Magic == COFF::DEBUG_SECTION_MAGIC && "Invalid .debug$S section!"); + + codeview::DebugSubsectionArray Subsections; + Err(Reader.readArray(Subsections, Reader.bytesRemaining())); + + SC.initialize(Subsections); + } +} + +void COFFDumper::dumpSections(unsigned NumSections) { + std::vector<COFFYAML::Section> &YAMLSections = YAMLObj.Sections; + codeview::StringsAndChecksumsRef SC; + initializeFileAndStringTable(Obj, SC); + + ExitOnError Err("invalid section table"); + StringMap<bool> SymbolUnique; + for (const auto &S : Obj.symbols()) { + StringRef Name = Err(Obj.getSymbolName(Obj.getCOFFSymbol(S))); + StringMap<bool>::iterator It; + bool Inserted; + std::tie(It, Inserted) = SymbolUnique.insert(std::make_pair(Name, true)); + if (!Inserted) + It->second = false; + } + + for (const auto &ObjSection : Obj.sections()) { + const object::coff_section *COFFSection = Obj.getCOFFSection(ObjSection); + COFFYAML::Section NewYAMLSection; + + if (Expected<StringRef> NameOrErr = ObjSection.getName()) + NewYAMLSection.Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + NewYAMLSection.Header.Characteristics = COFFSection->Characteristics; + NewYAMLSection.Header.VirtualAddress = COFFSection->VirtualAddress; + NewYAMLSection.Header.VirtualSize = COFFSection->VirtualSize; + NewYAMLSection.Header.NumberOfLineNumbers = + COFFSection->NumberOfLinenumbers; + NewYAMLSection.Header.NumberOfRelocations = + COFFSection->NumberOfRelocations; + NewYAMLSection.Header.PointerToLineNumbers = + COFFSection->PointerToLinenumbers; + NewYAMLSection.Header.PointerToRawData = COFFSection->PointerToRawData; + NewYAMLSection.Header.PointerToRelocations = + COFFSection->PointerToRelocations; + NewYAMLSection.Header.SizeOfRawData = COFFSection->SizeOfRawData; + uint32_t Shift = (COFFSection->Characteristics >> 20) & 0xF; + NewYAMLSection.Alignment = (1U << Shift) >> 1; + assert(NewYAMLSection.Alignment <= 8192); + + ArrayRef<uint8_t> sectionData; + if (!ObjSection.isBSS()) + cantFail(Obj.getSectionContents(COFFSection, sectionData)); + NewYAMLSection.SectionData = yaml::BinaryRef(sectionData); + + if (NewYAMLSection.Name == ".debug$S") + NewYAMLSection.DebugS = CodeViewYAML::fromDebugS(sectionData, SC); + else if (NewYAMLSection.Name == ".debug$T") + NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData, + NewYAMLSection.Name); + else if (NewYAMLSection.Name == ".debug$P") + NewYAMLSection.DebugP = CodeViewYAML::fromDebugT(sectionData, + NewYAMLSection.Name); + else if (NewYAMLSection.Name == ".debug$H") + NewYAMLSection.DebugH = CodeViewYAML::fromDebugH(sectionData); + + std::vector<COFFYAML::Relocation> Relocations; + for (const auto &Reloc : ObjSection.relocations()) { + const object::coff_relocation *reloc = Obj.getCOFFRelocation(Reloc); + COFFYAML::Relocation Rel; + object::symbol_iterator Sym = Reloc.getSymbol(); + Expected<StringRef> SymbolNameOrErr = Sym->getName(); + if (!SymbolNameOrErr) { + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(SymbolNameOrErr.takeError(), OS); + report_fatal_error(Twine(OS.str())); + } + if (SymbolUnique.lookup(*SymbolNameOrErr)) + Rel.SymbolName = *SymbolNameOrErr; + else + Rel.SymbolTableIndex = reloc->SymbolTableIndex; + Rel.VirtualAddress = reloc->VirtualAddress; + Rel.Type = reloc->Type; + Relocations.push_back(Rel); + } + NewYAMLSection.Relocations = Relocations; + YAMLSections.push_back(NewYAMLSection); + } +} + +static void +dumpFunctionDefinition(COFFYAML::Symbol *Sym, + const object::coff_aux_function_definition *ObjFD) { + COFF::AuxiliaryFunctionDefinition YAMLFD; + YAMLFD.TagIndex = ObjFD->TagIndex; + YAMLFD.TotalSize = ObjFD->TotalSize; + YAMLFD.PointerToLinenumber = ObjFD->PointerToLinenumber; + YAMLFD.PointerToNextFunction = ObjFD->PointerToNextFunction; + + Sym->FunctionDefinition = YAMLFD; +} + +static void +dumpbfAndEfLineInfo(COFFYAML::Symbol *Sym, + const object::coff_aux_bf_and_ef_symbol *ObjBES) { + COFF::AuxiliarybfAndefSymbol YAMLAAS; + YAMLAAS.Linenumber = ObjBES->Linenumber; + YAMLAAS.PointerToNextFunction = ObjBES->PointerToNextFunction; + + Sym->bfAndefSymbol = YAMLAAS; +} + +static void dumpWeakExternal(COFFYAML::Symbol *Sym, + const object::coff_aux_weak_external *ObjWE) { + COFF::AuxiliaryWeakExternal YAMLWE; + YAMLWE.TagIndex = ObjWE->TagIndex; + YAMLWE.Characteristics = ObjWE->Characteristics; + + Sym->WeakExternal = YAMLWE; +} + +static void +dumpSectionDefinition(COFFYAML::Symbol *Sym, + const object::coff_aux_section_definition *ObjSD, + bool IsBigObj) { + COFF::AuxiliarySectionDefinition YAMLASD; + int32_t AuxNumber = ObjSD->getNumber(IsBigObj); + YAMLASD.Length = ObjSD->Length; + YAMLASD.NumberOfRelocations = ObjSD->NumberOfRelocations; + YAMLASD.NumberOfLinenumbers = ObjSD->NumberOfLinenumbers; + YAMLASD.CheckSum = ObjSD->CheckSum; + YAMLASD.Number = AuxNumber; + YAMLASD.Selection = ObjSD->Selection; + + Sym->SectionDefinition = YAMLASD; +} + +static void +dumpCLRTokenDefinition(COFFYAML::Symbol *Sym, + const object::coff_aux_clr_token *ObjCLRToken) { + COFF::AuxiliaryCLRToken YAMLCLRToken; + YAMLCLRToken.AuxType = ObjCLRToken->AuxType; + YAMLCLRToken.SymbolTableIndex = ObjCLRToken->SymbolTableIndex; + + Sym->CLRToken = YAMLCLRToken; +} + +void COFFDumper::dumpSymbols(unsigned NumSymbols) { + ExitOnError Err("invalid symbol table"); + + std::vector<COFFYAML::Symbol> &Symbols = YAMLObj.Symbols; + for (const auto &S : Obj.symbols()) { + object::COFFSymbolRef Symbol = Obj.getCOFFSymbol(S); + COFFYAML::Symbol Sym; + Sym.Name = Err(Obj.getSymbolName(Symbol)); + Sym.SimpleType = COFF::SymbolBaseType(Symbol.getBaseType()); + Sym.ComplexType = COFF::SymbolComplexType(Symbol.getComplexType()); + Sym.Header.StorageClass = Symbol.getStorageClass(); + Sym.Header.Value = Symbol.getValue(); + Sym.Header.SectionNumber = Symbol.getSectionNumber(); + Sym.Header.NumberOfAuxSymbols = Symbol.getNumberOfAuxSymbols(); + + if (Symbol.getNumberOfAuxSymbols() > 0) { + ArrayRef<uint8_t> AuxData = Obj.getSymbolAuxData(Symbol); + if (Symbol.isFunctionDefinition()) { + // This symbol represents a function definition. + assert(Symbol.getNumberOfAuxSymbols() == 1 && + "Expected a single aux symbol to describe this function!"); + + const object::coff_aux_function_definition *ObjFD = + reinterpret_cast<const object::coff_aux_function_definition *>( + AuxData.data()); + dumpFunctionDefinition(&Sym, ObjFD); + } else if (Symbol.isFunctionLineInfo()) { + // This symbol describes function line number information. + assert(Symbol.getNumberOfAuxSymbols() == 1 && + "Expected a single aux symbol to describe this function!"); + + const object::coff_aux_bf_and_ef_symbol *ObjBES = + reinterpret_cast<const object::coff_aux_bf_and_ef_symbol *>( + AuxData.data()); + dumpbfAndEfLineInfo(&Sym, ObjBES); + } else if (Symbol.isAnyUndefined()) { + // This symbol represents a weak external definition. + assert(Symbol.getNumberOfAuxSymbols() == 1 && + "Expected a single aux symbol to describe this weak symbol!"); + + const object::coff_aux_weak_external *ObjWE = + reinterpret_cast<const object::coff_aux_weak_external *>( + AuxData.data()); + dumpWeakExternal(&Sym, ObjWE); + } else if (Symbol.isFileRecord()) { + // This symbol represents a file record. + Sym.File = StringRef(reinterpret_cast<const char *>(AuxData.data()), + Symbol.getNumberOfAuxSymbols() * + Obj.getSymbolTableEntrySize()) + .rtrim(StringRef("\0", /*length=*/1)); + } else if (Symbol.isSectionDefinition()) { + // This symbol represents a section definition. + assert(Symbol.getNumberOfAuxSymbols() == 1 && + "Expected a single aux symbol to describe this section!"); + + const object::coff_aux_section_definition *ObjSD = + reinterpret_cast<const object::coff_aux_section_definition *>( + AuxData.data()); + dumpSectionDefinition(&Sym, ObjSD, Symbol.isBigObj()); + } else if (Symbol.isCLRToken()) { + // This symbol represents a CLR token definition. + assert(Symbol.getNumberOfAuxSymbols() == 1 && + "Expected a single aux symbol to describe this CLR Token!"); + + const object::coff_aux_clr_token *ObjCLRToken = + reinterpret_cast<const object::coff_aux_clr_token *>( + AuxData.data()); + dumpCLRTokenDefinition(&Sym, ObjCLRToken); + } else { + llvm_unreachable("Unhandled auxiliary symbol!"); + } + } + Symbols.push_back(Sym); + } +} + +COFFYAML::Object &COFFDumper::getYAMLObj() { + return YAMLObj; +} + +std::error_code coff2yaml(raw_ostream &Out, const object::COFFObjectFile &Obj) { + COFFDumper Dumper(Obj); + + yaml::Output Yout(Out); + Yout << Dumper.getYAMLObj(); + + return std::error_code(); +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/dwarf2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/dwarf2yaml.cpp new file mode 100644 index 0000000000..8cae1f3c49 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/dwarf2yaml.cpp @@ -0,0 +1,463 @@ +//===------ dwarf2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" +#include "llvm/ObjectYAML/DWARFYAML.h" + +#include <algorithm> + +using namespace llvm; + +void dumpDebugAbbrev(DWARFContext &DCtx, DWARFYAML::Data &Y) { + auto AbbrevSetPtr = DCtx.getDebugAbbrev(); + if (AbbrevSetPtr) { + uint64_t AbbrevTableID = 0; + for (auto AbbrvDeclSet : *AbbrevSetPtr) { + Y.DebugAbbrev.emplace_back(); + Y.DebugAbbrev.back().ID = AbbrevTableID++; + for (auto AbbrvDecl : AbbrvDeclSet.second) { + DWARFYAML::Abbrev Abbrv; + Abbrv.Code = AbbrvDecl.getCode(); + Abbrv.Tag = AbbrvDecl.getTag(); + Abbrv.Children = AbbrvDecl.hasChildren() ? dwarf::DW_CHILDREN_yes + : dwarf::DW_CHILDREN_no; + for (auto Attribute : AbbrvDecl.attributes()) { + DWARFYAML::AttributeAbbrev AttAbrv; + AttAbrv.Attribute = Attribute.Attr; + AttAbrv.Form = Attribute.Form; + if (AttAbrv.Form == dwarf::DW_FORM_implicit_const) + AttAbrv.Value = Attribute.getImplicitConstValue(); + Abbrv.Attributes.push_back(AttAbrv); + } + Y.DebugAbbrev.back().Table.push_back(Abbrv); + } + } + } +} + +Error dumpDebugAddr(DWARFContext &DCtx, DWARFYAML::Data &Y) { + DWARFDebugAddrTable AddrTable; + DWARFDataExtractor AddrData(DCtx.getDWARFObj(), + DCtx.getDWARFObj().getAddrSection(), + DCtx.isLittleEndian(), /*AddressSize=*/0); + std::vector<DWARFYAML::AddrTableEntry> AddrTables; + uint64_t Offset = 0; + while (AddrData.isValidOffset(Offset)) { + // We ignore any errors that don't prevent parsing the section, since we can + // still represent such sections. + if (Error Err = AddrTable.extractV5(AddrData, &Offset, /*CUAddrSize=*/0, + consumeError)) + return Err; + AddrTables.emplace_back(); + for (uint64_t Addr : AddrTable.getAddressEntries()) { + // Currently, the parser doesn't support parsing an address table with non + // linear addresses (segment_selector_size != 0). The segment selectors + // are specified to be zero. + AddrTables.back().SegAddrPairs.push_back( + {/*SegmentSelector=*/0, /*Address=*/Addr}); + } + + AddrTables.back().Format = AddrTable.getFormat(); + AddrTables.back().Length = AddrTable.getLength(); + AddrTables.back().Version = AddrTable.getVersion(); + AddrTables.back().AddrSize = AddrTable.getAddressSize(); + AddrTables.back().SegSelectorSize = AddrTable.getSegmentSelectorSize(); + } + Y.DebugAddr = std::move(AddrTables); + return Error::success(); +} + +Error dumpDebugStrings(DWARFContext &DCtx, DWARFYAML::Data &Y) { + DataExtractor StrData = DCtx.getStringExtractor(); + uint64_t Offset = 0; + std::vector<StringRef> DebugStr; + Error Err = Error::success(); + while (StrData.isValidOffset(Offset)) { + const char *CStr = StrData.getCStr(&Offset, &Err); + if (Err) + return Err; + DebugStr.push_back(CStr); + } + + Y.DebugStrings = DebugStr; + return Err; +} + +Error dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) { + DWARFDataExtractor ArangesData(DCtx.getDWARFObj().getArangesSection(), + DCtx.isLittleEndian(), 0); + uint64_t Offset = 0; + DWARFDebugArangeSet Set; + std::vector<DWARFYAML::ARange> DebugAranges; + + // We ignore any errors that don't prevent parsing the section, since we can + // still represent such sections. These errors are recorded via the + // WarningHandler parameter of Set.extract(). + auto DiscardError = [](Error Err) { consumeError(std::move(Err)); }; + + while (ArangesData.isValidOffset(Offset)) { + if (Error E = Set.extract(ArangesData, &Offset, DiscardError)) + return E; + DWARFYAML::ARange Range; + Range.Format = Set.getHeader().Format; + Range.Length = Set.getHeader().Length; + Range.Version = Set.getHeader().Version; + Range.CuOffset = Set.getHeader().CuOffset; + Range.AddrSize = Set.getHeader().AddrSize; + Range.SegSize = Set.getHeader().SegSize; + for (auto Descriptor : Set.descriptors()) { + DWARFYAML::ARangeDescriptor Desc; + Desc.Address = Descriptor.Address; + Desc.Length = Descriptor.Length; + Range.Descriptors.push_back(Desc); + } + DebugAranges.push_back(Range); + } + + Y.DebugAranges = DebugAranges; + return ErrorSuccess(); +} + +Error dumpDebugRanges(DWARFContext &DCtx, DWARFYAML::Data &Y) { + // We are assuming all address byte sizes will be consistent across all + // compile units. + uint8_t AddrSize = 0; + for (const auto &CU : DCtx.compile_units()) { + const uint8_t CUAddrSize = CU->getAddressByteSize(); + if (AddrSize == 0) + AddrSize = CUAddrSize; + else if (CUAddrSize != AddrSize) + return createStringError(std::errc::invalid_argument, + "address sizes vary in different compile units"); + } + + DWARFDataExtractor Data(DCtx.getDWARFObj().getRangesSection().Data, + DCtx.isLittleEndian(), AddrSize); + uint64_t Offset = 0; + DWARFDebugRangeList DwarfRanges; + std::vector<DWARFYAML::Ranges> DebugRanges; + + while (Data.isValidOffset(Offset)) { + DWARFYAML::Ranges YamlRanges; + YamlRanges.Offset = Offset; + YamlRanges.AddrSize = AddrSize; + if (Error E = DwarfRanges.extract(Data, &Offset)) + return E; + for (const auto &RLE : DwarfRanges.getEntries()) + YamlRanges.Entries.push_back({RLE.StartAddress, RLE.EndAddress}); + DebugRanges.push_back(std::move(YamlRanges)); + } + + Y.DebugRanges = DebugRanges; + return ErrorSuccess(); +} + +static Optional<DWARFYAML::PubSection> +dumpPubSection(const DWARFContext &DCtx, const DWARFSection &Section, + bool IsGNUStyle) { + DWARFYAML::PubSection Y; + DWARFDataExtractor PubSectionData(DCtx.getDWARFObj(), Section, + DCtx.isLittleEndian(), 0); + DWARFDebugPubTable Table; + // We ignore any errors that don't prevent parsing the section, since we can + // still represent such sections. + Table.extract(PubSectionData, IsGNUStyle, + [](Error Err) { consumeError(std::move(Err)); }); + ArrayRef<DWARFDebugPubTable::Set> Sets = Table.getData(); + if (Sets.empty()) + return None; + + // FIXME: Currently, obj2yaml only supports dumping the first pubtable. + Y.Format = Sets[0].Format; + Y.Length = Sets[0].Length; + Y.Version = Sets[0].Version; + Y.UnitOffset = Sets[0].Offset; + Y.UnitSize = Sets[0].Size; + + for (const DWARFDebugPubTable::Entry &E : Sets[0].Entries) + Y.Entries.push_back(DWARFYAML::PubEntry{(uint32_t)E.SecOffset, + E.Descriptor.toBits(), E.Name}); + + return Y; +} + +void dumpDebugPubSections(DWARFContext &DCtx, DWARFYAML::Data &Y) { + const DWARFObject &D = DCtx.getDWARFObj(); + + Y.PubNames = + dumpPubSection(DCtx, D.getPubnamesSection(), /*IsGNUStyle=*/false); + Y.PubTypes = + dumpPubSection(DCtx, D.getPubtypesSection(), /*IsGNUStyle=*/false); + // TODO: Test dumping .debug_gnu_pubnames section. + Y.GNUPubNames = + dumpPubSection(DCtx, D.getGnuPubnamesSection(), /*IsGNUStyle=*/true); + // TODO: Test dumping .debug_gnu_pubtypes section. + Y.GNUPubTypes = + dumpPubSection(DCtx, D.getGnuPubtypesSection(), /*IsGNUStyle=*/true); +} + +void dumpDebugInfo(DWARFContext &DCtx, DWARFYAML::Data &Y) { + for (const auto &CU : DCtx.compile_units()) { + DWARFYAML::Unit NewUnit; + NewUnit.Format = CU->getFormat(); + NewUnit.Length = CU->getLength(); + NewUnit.Version = CU->getVersion(); + if (NewUnit.Version >= 5) + NewUnit.Type = (dwarf::UnitType)CU->getUnitType(); + const DWARFDebugAbbrev *DebugAbbrev = DCtx.getDebugAbbrev(); + NewUnit.AbbrevTableID = std::distance( + DebugAbbrev->begin(), + llvm::find_if( + *DebugAbbrev, + [&](const std::pair<uint64_t, DWARFAbbreviationDeclarationSet> &P) { + return P.first == CU->getAbbreviations()->getOffset(); + })); + NewUnit.AbbrOffset = CU->getAbbreviations()->getOffset(); + NewUnit.AddrSize = CU->getAddressByteSize(); + for (auto DIE : CU->dies()) { + DWARFYAML::Entry NewEntry; + DataExtractor EntryData = CU->getDebugInfoExtractor(); + uint64_t offset = DIE.getOffset(); + + assert(EntryData.isValidOffset(offset) && "Invalid DIE Offset"); + if (!EntryData.isValidOffset(offset)) + continue; + + NewEntry.AbbrCode = EntryData.getULEB128(&offset); + + auto AbbrevDecl = DIE.getAbbreviationDeclarationPtr(); + if (AbbrevDecl) { + for (const auto &AttrSpec : AbbrevDecl->attributes()) { + DWARFYAML::FormValue NewValue; + NewValue.Value = 0xDEADBEEFDEADBEEF; + DWARFDie DIEWrapper(CU.get(), &DIE); + auto FormValue = DIEWrapper.find(AttrSpec.Attr); + if (!FormValue) + return; + auto Form = FormValue.getValue().getForm(); + bool indirect = false; + do { + indirect = false; + switch (Form) { + case dwarf::DW_FORM_addr: + case dwarf::DW_FORM_GNU_addr_index: + if (auto Val = FormValue.getValue().getAsAddress()) + NewValue.Value = Val.getValue(); + break; + case dwarf::DW_FORM_ref_addr: + case dwarf::DW_FORM_ref1: + case dwarf::DW_FORM_ref2: + case dwarf::DW_FORM_ref4: + case dwarf::DW_FORM_ref8: + case dwarf::DW_FORM_ref_udata: + case dwarf::DW_FORM_ref_sig8: + if (auto Val = FormValue.getValue().getAsReferenceUVal()) + NewValue.Value = Val.getValue(); + break; + case dwarf::DW_FORM_exprloc: + case dwarf::DW_FORM_block: + case dwarf::DW_FORM_block1: + case dwarf::DW_FORM_block2: + case dwarf::DW_FORM_block4: + if (auto Val = FormValue.getValue().getAsBlock()) { + auto BlockData = Val.getValue(); + std::copy(BlockData.begin(), BlockData.end(), + std::back_inserter(NewValue.BlockData)); + } + NewValue.Value = NewValue.BlockData.size(); + break; + case dwarf::DW_FORM_data1: + case dwarf::DW_FORM_flag: + case dwarf::DW_FORM_data2: + case dwarf::DW_FORM_data4: + case dwarf::DW_FORM_data8: + case dwarf::DW_FORM_sdata: + case dwarf::DW_FORM_udata: + case dwarf::DW_FORM_ref_sup4: + case dwarf::DW_FORM_ref_sup8: + if (auto Val = FormValue.getValue().getAsUnsignedConstant()) + NewValue.Value = Val.getValue(); + break; + case dwarf::DW_FORM_string: + if (auto Val = dwarf::toString(FormValue)) + NewValue.CStr = *Val; + break; + case dwarf::DW_FORM_indirect: + indirect = true; + if (auto Val = FormValue.getValue().getAsUnsignedConstant()) { + NewValue.Value = Val.getValue(); + NewEntry.Values.push_back(NewValue); + Form = static_cast<dwarf::Form>(Val.getValue()); + } + break; + case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_sec_offset: + case dwarf::DW_FORM_GNU_ref_alt: + case dwarf::DW_FORM_GNU_strp_alt: + case dwarf::DW_FORM_line_strp: + case dwarf::DW_FORM_strp_sup: + case dwarf::DW_FORM_GNU_str_index: + case dwarf::DW_FORM_strx: + if (auto Val = FormValue.getValue().getAsCStringOffset()) + NewValue.Value = Val.getValue(); + break; + case dwarf::DW_FORM_flag_present: + NewValue.Value = 1; + break; + default: + break; + } + } while (indirect); + NewEntry.Values.push_back(NewValue); + } + } + + NewUnit.Entries.push_back(NewEntry); + } + Y.CompileUnits.push_back(NewUnit); + } +} + +bool dumpFileEntry(DataExtractor &Data, uint64_t &Offset, + DWARFYAML::File &File) { + File.Name = Data.getCStr(&Offset); + if (File.Name.empty()) + return false; + File.DirIdx = Data.getULEB128(&Offset); + File.ModTime = Data.getULEB128(&Offset); + File.Length = Data.getULEB128(&Offset); + return true; +} + +void dumpDebugLines(DWARFContext &DCtx, DWARFYAML::Data &Y) { + for (const auto &CU : DCtx.compile_units()) { + auto CUDIE = CU->getUnitDIE(); + if (!CUDIE) + continue; + if (auto StmtOffset = + dwarf::toSectionOffset(CUDIE.find(dwarf::DW_AT_stmt_list))) { + DWARFYAML::LineTable DebugLines; + DataExtractor LineData(DCtx.getDWARFObj().getLineSection().Data, + DCtx.isLittleEndian(), CU->getAddressByteSize()); + uint64_t Offset = *StmtOffset; + uint64_t LengthOrDWARF64Prefix = LineData.getU32(&Offset); + if (LengthOrDWARF64Prefix == dwarf::DW_LENGTH_DWARF64) { + DebugLines.Format = dwarf::DWARF64; + DebugLines.Length = LineData.getU64(&Offset); + } else { + DebugLines.Format = dwarf::DWARF32; + DebugLines.Length = LengthOrDWARF64Prefix; + } + assert(DebugLines.Length); + uint64_t LineTableLength = *DebugLines.Length; + uint64_t SizeOfPrologueLength = + DebugLines.Format == dwarf::DWARF64 ? 8 : 4; + DebugLines.Version = LineData.getU16(&Offset); + DebugLines.PrologueLength = + LineData.getUnsigned(&Offset, SizeOfPrologueLength); + assert(DebugLines.PrologueLength); + const uint64_t EndPrologue = *DebugLines.PrologueLength + Offset; + + DebugLines.MinInstLength = LineData.getU8(&Offset); + if (DebugLines.Version >= 4) + DebugLines.MaxOpsPerInst = LineData.getU8(&Offset); + DebugLines.DefaultIsStmt = LineData.getU8(&Offset); + DebugLines.LineBase = LineData.getU8(&Offset); + DebugLines.LineRange = LineData.getU8(&Offset); + DebugLines.OpcodeBase = LineData.getU8(&Offset); + + DebugLines.StandardOpcodeLengths.emplace(); + for (uint8_t i = 1; i < DebugLines.OpcodeBase; ++i) + DebugLines.StandardOpcodeLengths->push_back(LineData.getU8(&Offset)); + + while (Offset < EndPrologue) { + StringRef Dir = LineData.getCStr(&Offset); + if (!Dir.empty()) + DebugLines.IncludeDirs.push_back(Dir); + else + break; + } + + while (Offset < EndPrologue) { + DWARFYAML::File TmpFile; + if (dumpFileEntry(LineData, Offset, TmpFile)) + DebugLines.Files.push_back(TmpFile); + else + break; + } + + const uint64_t LineEnd = + LineTableLength + *StmtOffset + SizeOfPrologueLength; + while (Offset < LineEnd) { + DWARFYAML::LineTableOpcode NewOp = {}; + NewOp.Opcode = (dwarf::LineNumberOps)LineData.getU8(&Offset); + if (NewOp.Opcode == 0) { + auto StartExt = Offset; + NewOp.ExtLen = LineData.getULEB128(&Offset); + NewOp.SubOpcode = + (dwarf::LineNumberExtendedOps)LineData.getU8(&Offset); + switch (NewOp.SubOpcode) { + case dwarf::DW_LNE_set_address: + case dwarf::DW_LNE_set_discriminator: + NewOp.Data = LineData.getAddress(&Offset); + break; + case dwarf::DW_LNE_define_file: + dumpFileEntry(LineData, Offset, NewOp.FileEntry); + break; + case dwarf::DW_LNE_end_sequence: + break; + default: + while (Offset < StartExt + *NewOp.ExtLen) + NewOp.UnknownOpcodeData.push_back(LineData.getU8(&Offset)); + } + } else if (NewOp.Opcode < *DebugLines.OpcodeBase) { + switch (NewOp.Opcode) { + case dwarf::DW_LNS_copy: + case dwarf::DW_LNS_negate_stmt: + case dwarf::DW_LNS_set_basic_block: + case dwarf::DW_LNS_const_add_pc: + case dwarf::DW_LNS_set_prologue_end: + case dwarf::DW_LNS_set_epilogue_begin: + break; + + case dwarf::DW_LNS_advance_pc: + case dwarf::DW_LNS_set_file: + case dwarf::DW_LNS_set_column: + case dwarf::DW_LNS_set_isa: + NewOp.Data = LineData.getULEB128(&Offset); + break; + + case dwarf::DW_LNS_advance_line: + NewOp.SData = LineData.getSLEB128(&Offset); + break; + + case dwarf::DW_LNS_fixed_advance_pc: + NewOp.Data = LineData.getU16(&Offset); + break; + + default: + for (uint8_t i = 0; + i < + DebugLines.StandardOpcodeLengths.getValue()[NewOp.Opcode - 1]; + ++i) + NewOp.StandardOpcodeData.push_back(LineData.getULEB128(&Offset)); + } + } + DebugLines.Opcodes.push_back(NewOp); + } + Y.DebugLines.push_back(DebugLines); + } + } +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/elf2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/elf2yaml.cpp new file mode 100644 index 0000000000..9d1713c859 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/elf2yaml.cpp @@ -0,0 +1,1596 @@ +//===------ utils/elf2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/ObjectYAML/DWARFYAML.h" +#include "llvm/ObjectYAML/ELFYAML.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace llvm; + +namespace { + +template <class ELFT> +class ELFDumper { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + + ArrayRef<Elf_Shdr> Sections; + ArrayRef<Elf_Sym> SymTable; + + DenseMap<StringRef, uint32_t> UsedSectionNames; + std::vector<std::string> SectionNames; + Optional<uint32_t> ShStrTabIndex; + + DenseMap<StringRef, uint32_t> UsedSymbolNames; + std::vector<std::string> SymbolNames; + + BumpPtrAllocator StringAllocator; + + Expected<StringRef> getUniquedSectionName(const Elf_Shdr &Sec); + Expected<StringRef> getUniquedSymbolName(const Elf_Sym *Sym, + StringRef StrTable, + const Elf_Shdr *SymTab); + Expected<StringRef> getSymbolName(uint32_t SymtabNdx, uint32_t SymbolNdx); + + const object::ELFFile<ELFT> &Obj; + std::unique_ptr<DWARFContext> DWARFCtx; + + DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables; + + Expected<std::vector<ELFYAML::ProgramHeader>> + dumpProgramHeaders(ArrayRef<std::unique_ptr<ELFYAML::Chunk>> Sections); + + Optional<DWARFYAML::Data> + dumpDWARFSections(std::vector<std::unique_ptr<ELFYAML::Chunk>> &Sections); + + Error dumpSymbols(const Elf_Shdr *Symtab, + Optional<std::vector<ELFYAML::Symbol>> &Symbols); + Error dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab, + StringRef StrTable, ELFYAML::Symbol &S); + Expected<std::vector<std::unique_ptr<ELFYAML::Chunk>>> dumpSections(); + Error dumpCommonSection(const Elf_Shdr *Shdr, ELFYAML::Section &S); + Error dumpCommonRelocationSection(const Elf_Shdr *Shdr, + ELFYAML::RelocationSection &S); + template <class RelT> + Error dumpRelocation(const RelT *Rel, const Elf_Shdr *SymTab, + ELFYAML::Relocation &R); + + Expected<ELFYAML::AddrsigSection *> dumpAddrsigSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::LinkerOptionsSection *> + dumpLinkerOptionsSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::DependentLibrariesSection *> + dumpDependentLibrariesSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::CallGraphProfileSection *> + dumpCallGraphProfileSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::DynamicSection *> dumpDynamicSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::RelocationSection *> dumpRelocSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::RelrSection *> dumpRelrSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::RawContentSection *> + dumpContentSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::SymtabShndxSection *> + dumpSymtabShndxSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::NoBitsSection *> dumpNoBitsSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::HashSection *> dumpHashSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::NoteSection *> dumpNoteSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::GnuHashSection *> dumpGnuHashSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::VerdefSection *> dumpVerdefSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::SymverSection *> dumpSymverSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::VerneedSection *> dumpVerneedSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::GroupSection *> dumpGroupSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::ARMIndexTableSection *> + dumpARMIndexTableSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::MipsABIFlags *> dumpMipsABIFlags(const Elf_Shdr *Shdr); + Expected<ELFYAML::StackSizesSection *> + dumpStackSizesSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::BBAddrMapSection *> + dumpBBAddrMapSection(const Elf_Shdr *Shdr); + Expected<ELFYAML::RawContentSection *> + dumpPlaceholderSection(const Elf_Shdr *Shdr); + + bool shouldPrintSection(const ELFYAML::Section &S, const Elf_Shdr &SHdr, + Optional<DWARFYAML::Data> DWARF); + +public: + ELFDumper(const object::ELFFile<ELFT> &O, std::unique_ptr<DWARFContext> DCtx); + Expected<ELFYAML::Object *> dump(); +}; + +} + +template <class ELFT> +ELFDumper<ELFT>::ELFDumper(const object::ELFFile<ELFT> &O, + std::unique_ptr<DWARFContext> DCtx) + : Obj(O), DWARFCtx(std::move(DCtx)) {} + +template <class ELFT> +Expected<StringRef> +ELFDumper<ELFT>::getUniquedSectionName(const Elf_Shdr &Sec) { + unsigned SecIndex = &Sec - &Sections[0]; + if (!SectionNames[SecIndex].empty()) + return SectionNames[SecIndex]; + + auto NameOrErr = Obj.getSectionName(Sec); + if (!NameOrErr) + return NameOrErr; + StringRef Name = *NameOrErr; + // In some specific cases we might have more than one section without a + // name (sh_name == 0). It normally doesn't happen, but when we have this case + // it doesn't make sense to uniquify their names and add noise to the output. + if (Name.empty()) + return ""; + + std::string &Ret = SectionNames[SecIndex]; + + auto It = UsedSectionNames.insert({Name, 0}); + if (!It.second) + Ret = ELFYAML::appendUniqueSuffix(Name, Twine(++It.first->second)); + else + Ret = std::string(Name); + return Ret; +} + +template <class ELFT> +Expected<StringRef> +ELFDumper<ELFT>::getUniquedSymbolName(const Elf_Sym *Sym, StringRef StrTable, + const Elf_Shdr *SymTab) { + Expected<StringRef> SymbolNameOrErr = Sym->getName(StrTable); + if (!SymbolNameOrErr) + return SymbolNameOrErr; + StringRef Name = *SymbolNameOrErr; + if (Name.empty() && Sym->getType() == ELF::STT_SECTION) { + Expected<const Elf_Shdr *> ShdrOrErr = + Obj.getSection(*Sym, SymTab, ShndxTables.lookup(SymTab)); + if (!ShdrOrErr) + return ShdrOrErr.takeError(); + // The null section has no name. + return (*ShdrOrErr == nullptr) ? "" : getUniquedSectionName(**ShdrOrErr); + } + + // Symbols in .symtab can have duplicate names. For example, it is a common + // situation for local symbols in a relocatable object. Here we assign unique + // suffixes for such symbols so that we can differentiate them. + if (SymTab->sh_type == ELF::SHT_SYMTAB) { + unsigned Index = Sym - SymTable.data(); + if (!SymbolNames[Index].empty()) + return SymbolNames[Index]; + + auto It = UsedSymbolNames.insert({Name, 0}); + if (!It.second) + SymbolNames[Index] = + ELFYAML::appendUniqueSuffix(Name, Twine(++It.first->second)); + else + SymbolNames[Index] = std::string(Name); + return SymbolNames[Index]; + } + + return Name; +} + +template <class ELFT> +bool ELFDumper<ELFT>::shouldPrintSection(const ELFYAML::Section &S, + const Elf_Shdr &SHdr, + Optional<DWARFYAML::Data> DWARF) { + // We only print the SHT_NULL section at index 0 when it + // has at least one non-null field, because yaml2obj + // normally creates the zero section at index 0 implicitly. + if (S.Type == ELF::SHT_NULL && (&SHdr == &Sections[0])) { + const uint8_t *Begin = reinterpret_cast<const uint8_t *>(&SHdr); + const uint8_t *End = Begin + sizeof(Elf_Shdr); + return std::any_of(Begin, End, [](uint8_t V) { return V != 0; }); + } + + // Normally we use "DWARF:" to describe contents of DWARF sections. Sometimes + // the content of DWARF sections can be successfully parsed into the "DWARF:" + // entry but their section headers may have special flags, entry size, address + // alignment, etc. We will preserve the header for them under such + // circumstances. + StringRef SecName = S.Name.substr(1); + if (DWARF && DWARF->getNonEmptySectionNames().count(SecName)) { + if (const ELFYAML::RawContentSection *RawSec = + dyn_cast<const ELFYAML::RawContentSection>(&S)) { + if (RawSec->Type != ELF::SHT_PROGBITS || RawSec->Link || RawSec->Info || + RawSec->AddressAlign != yaml::Hex64{1} || RawSec->Address || + RawSec->EntSize) + return true; + + ELFYAML::ELF_SHF ShFlags = RawSec->Flags.getValueOr(ELFYAML::ELF_SHF(0)); + + if (SecName == "debug_str") + return ShFlags != ELFYAML::ELF_SHF(ELF::SHF_MERGE | ELF::SHF_STRINGS); + + return ShFlags != ELFYAML::ELF_SHF{0}; + } + } + + // Normally we use "Symbols:" and "DynamicSymbols:" to describe contents of + // symbol tables. We also build and emit corresponding string tables + // implicitly. But sometimes it is important to preserve positions and virtual + // addresses of allocatable sections, e.g. for creating program headers. + // Generally we are trying to reduce noise in the YAML output. Because + // of that we do not print non-allocatable versions of such sections and + // assume they are placed at the end. + // We also dump symbol tables when the Size field is set. It happens when they + // are empty, which should not normally happen. + if (S.Type == ELF::SHT_STRTAB || S.Type == ELF::SHT_SYMTAB || + S.Type == ELF::SHT_DYNSYM) { + return S.Size || S.Flags.getValueOr(ELFYAML::ELF_SHF(0)) & ELF::SHF_ALLOC; + } + + return true; +} + +template <class ELFT> +static void dumpSectionOffsets(const typename ELFT::Ehdr &Header, + ArrayRef<ELFYAML::ProgramHeader> Phdrs, + std::vector<std::unique_ptr<ELFYAML::Chunk>> &V, + ArrayRef<typename ELFT::Shdr> S) { + if (V.empty()) + return; + + uint64_t ExpectedOffset; + if (Header.e_phoff > 0) + ExpectedOffset = Header.e_phoff + Header.e_phentsize * Header.e_phnum; + else + ExpectedOffset = sizeof(typename ELFT::Ehdr); + + for (const std::unique_ptr<ELFYAML::Chunk> &C : + makeArrayRef(V).drop_front()) { + ELFYAML::Section &Sec = *cast<ELFYAML::Section>(C.get()); + const typename ELFT::Shdr &SecHdr = S[Sec.OriginalSecNdx]; + + ExpectedOffset = alignTo(ExpectedOffset, + SecHdr.sh_addralign ? SecHdr.sh_addralign : 1uLL); + + // We only set the "Offset" field when it can't be naturally derived + // from the offset and size of the previous section. This reduces + // the noise in the YAML output. + if (SecHdr.sh_offset != ExpectedOffset) + Sec.Offset = (yaml::Hex64)SecHdr.sh_offset; + + if (Sec.Type == ELF::SHT_NOBITS && + !ELFYAML::shouldAllocateFileSpace(Phdrs, + *cast<ELFYAML::NoBitsSection>(&Sec))) + ExpectedOffset = SecHdr.sh_offset; + else + ExpectedOffset = SecHdr.sh_offset + SecHdr.sh_size; + } +} + +template <class ELFT> Expected<ELFYAML::Object *> ELFDumper<ELFT>::dump() { + auto Y = std::make_unique<ELFYAML::Object>(); + + // Dump header. We do not dump EPh* and ESh* fields. When not explicitly set, + // the values are set by yaml2obj automatically and there is no need to dump + // them here. + Y->Header.Class = ELFYAML::ELF_ELFCLASS(Obj.getHeader().getFileClass()); + Y->Header.Data = ELFYAML::ELF_ELFDATA(Obj.getHeader().getDataEncoding()); + Y->Header.OSABI = Obj.getHeader().e_ident[ELF::EI_OSABI]; + Y->Header.ABIVersion = Obj.getHeader().e_ident[ELF::EI_ABIVERSION]; + Y->Header.Type = Obj.getHeader().e_type; + if (Obj.getHeader().e_machine != 0) + Y->Header.Machine = ELFYAML::ELF_EM(Obj.getHeader().e_machine); + Y->Header.Flags = Obj.getHeader().e_flags; + Y->Header.Entry = Obj.getHeader().e_entry; + + // Dump sections + auto SectionsOrErr = Obj.sections(); + if (!SectionsOrErr) + return SectionsOrErr.takeError(); + Sections = *SectionsOrErr; + SectionNames.resize(Sections.size()); + + if (Sections.size() > 0) { + ShStrTabIndex = Obj.getHeader().e_shstrndx; + if (*ShStrTabIndex == ELF::SHN_XINDEX) + ShStrTabIndex = Sections[0].sh_link; + // TODO: Set EShStrndx if the value doesn't represent a real section. + } + + // Normally an object that does not have sections has e_shnum == 0. + // Also, e_shnum might be 0, when the the number of entries in the section + // header table is larger than or equal to SHN_LORESERVE (0xff00). In this + // case the real number of entries is held in the sh_size member of the + // initial entry. We have a section header table when `e_shoff` is not 0. + if (Obj.getHeader().e_shoff != 0 && Obj.getHeader().e_shnum == 0) + Y->Header.EShNum = 0; + + // Dump symbols. We need to do this early because other sections might want + // to access the deduplicated symbol names that we also create here. + const Elf_Shdr *SymTab = nullptr; + const Elf_Shdr *DynSymTab = nullptr; + + for (const Elf_Shdr &Sec : Sections) { + if (Sec.sh_type == ELF::SHT_SYMTAB) { + SymTab = &Sec; + } else if (Sec.sh_type == ELF::SHT_DYNSYM) { + DynSymTab = &Sec; + } else if (Sec.sh_type == ELF::SHT_SYMTAB_SHNDX) { + // We need to locate SHT_SYMTAB_SHNDX sections early, because they + // might be needed for dumping symbols. + if (Expected<ArrayRef<Elf_Word>> TableOrErr = Obj.getSHNDXTable(Sec)) { + // The `getSHNDXTable` calls the `getSection` internally when validates + // the symbol table section linked to the SHT_SYMTAB_SHNDX section. + const Elf_Shdr *LinkedSymTab = cantFail(Obj.getSection(Sec.sh_link)); + if (!ShndxTables.insert({LinkedSymTab, *TableOrErr}).second) + return createStringError( + errc::invalid_argument, + "multiple SHT_SYMTAB_SHNDX sections are " + "linked to the same symbol table with index " + + Twine(Sec.sh_link)); + } else { + return createStringError(errc::invalid_argument, + "unable to read extended section indexes: " + + toString(TableOrErr.takeError())); + } + } + } + + if (SymTab) + if (Error E = dumpSymbols(SymTab, Y->Symbols)) + return std::move(E); + + if (DynSymTab) + if (Error E = dumpSymbols(DynSymTab, Y->DynamicSymbols)) + return std::move(E); + + // We dump all sections first. It is simple and allows us to verify that all + // sections are valid and also to generalize the code. But we are not going to + // keep all of them in the final output (see comments for + // 'shouldPrintSection()'). Undesired chunks will be removed later. + Expected<std::vector<std::unique_ptr<ELFYAML::Chunk>>> ChunksOrErr = + dumpSections(); + if (!ChunksOrErr) + return ChunksOrErr.takeError(); + std::vector<std::unique_ptr<ELFYAML::Chunk>> Chunks = std::move(*ChunksOrErr); + + std::vector<ELFYAML::Section *> OriginalOrder; + if (!Chunks.empty()) + for (const std::unique_ptr<ELFYAML::Chunk> &C : + makeArrayRef(Chunks).drop_front()) + OriginalOrder.push_back(cast<ELFYAML::Section>(C.get())); + + // Sometimes the order of sections in the section header table does not match + // their actual order. Here we sort sections by the file offset. + llvm::stable_sort(Chunks, [&](const std::unique_ptr<ELFYAML::Chunk> &A, + const std::unique_ptr<ELFYAML::Chunk> &B) { + return Sections[cast<ELFYAML::Section>(A.get())->OriginalSecNdx].sh_offset < + Sections[cast<ELFYAML::Section>(B.get())->OriginalSecNdx].sh_offset; + }); + + // Dump program headers. + Expected<std::vector<ELFYAML::ProgramHeader>> PhdrsOrErr = + dumpProgramHeaders(Chunks); + if (!PhdrsOrErr) + return PhdrsOrErr.takeError(); + Y->ProgramHeaders = std::move(*PhdrsOrErr); + + dumpSectionOffsets<ELFT>(Obj.getHeader(), Y->ProgramHeaders, Chunks, + Sections); + + // Dump DWARF sections. + Y->DWARF = dumpDWARFSections(Chunks); + + // We emit the "SectionHeaderTable" key when the order of sections in the + // sections header table doesn't match the file order. + const bool SectionsSorted = + llvm::is_sorted(Chunks, [&](const std::unique_ptr<ELFYAML::Chunk> &A, + const std::unique_ptr<ELFYAML::Chunk> &B) { + return cast<ELFYAML::Section>(A.get())->OriginalSecNdx < + cast<ELFYAML::Section>(B.get())->OriginalSecNdx; + }); + if (!SectionsSorted) { + std::unique_ptr<ELFYAML::SectionHeaderTable> SHT = + std::make_unique<ELFYAML::SectionHeaderTable>(/*IsImplicit=*/false); + SHT->Sections.emplace(); + for (ELFYAML::Section *S : OriginalOrder) + SHT->Sections->push_back({S->Name}); + Chunks.push_back(std::move(SHT)); + } + + llvm::erase_if(Chunks, [this, &Y](const std::unique_ptr<ELFYAML::Chunk> &C) { + if (isa<ELFYAML::SectionHeaderTable>(*C.get())) + return false; + + const ELFYAML::Section &S = cast<ELFYAML::Section>(*C.get()); + return !shouldPrintSection(S, Sections[S.OriginalSecNdx], Y->DWARF); + }); + + // The section header string table by default is assumed to be called + // ".shstrtab" and be in its own unique section. However, it's possible for it + // to be called something else and shared with another section. If the name + // isn't the default, provide this in the YAML. + if (ShStrTabIndex && *ShStrTabIndex != ELF::SHN_UNDEF && + *ShStrTabIndex < Sections.size()) { + StringRef ShStrtabName; + if (SymTab && SymTab->sh_link == *ShStrTabIndex) { + // Section header string table is shared with the symbol table. Use that + // section's name (usually .strtab). + ShStrtabName = cantFail(Obj.getSectionName(Sections[SymTab->sh_link])); + } else if (DynSymTab && DynSymTab->sh_link == *ShStrTabIndex) { + // Section header string table is shared with the dynamic symbol table. + // Use that section's name (usually .dynstr). + ShStrtabName = cantFail(Obj.getSectionName(Sections[DynSymTab->sh_link])); + } else { + // Otherwise, the section name potentially needs uniquifying. + ShStrtabName = cantFail(getUniquedSectionName(Sections[*ShStrTabIndex])); + } + if (ShStrtabName != ".shstrtab") + Y->Header.SectionHeaderStringTable = ShStrtabName; + } + + Y->Chunks = std::move(Chunks); + return Y.release(); +} + +template <class ELFT> +static bool isInSegment(const ELFYAML::Section &Sec, + const typename ELFT::Shdr &SHdr, + const typename ELFT::Phdr &Phdr) { + if (Sec.Type == ELF::SHT_NULL) + return false; + + // A section is within a segment when its location in a file is within the + // [p_offset, p_offset + p_filesz] region. + bool FileOffsetsMatch = + SHdr.sh_offset >= Phdr.p_offset && + (SHdr.sh_offset + SHdr.sh_size <= Phdr.p_offset + Phdr.p_filesz); + + bool VirtualAddressesMatch = SHdr.sh_addr >= Phdr.p_vaddr && + SHdr.sh_addr <= Phdr.p_vaddr + Phdr.p_memsz; + + if (FileOffsetsMatch) { + // An empty section on the edges of a program header can be outside of the + // virtual address space of the segment. This means it is not included in + // the segment and we should ignore it. + if (SHdr.sh_size == 0 && (SHdr.sh_offset == Phdr.p_offset || + SHdr.sh_offset == Phdr.p_offset + Phdr.p_filesz)) + return VirtualAddressesMatch; + return true; + } + + // SHT_NOBITS sections usually occupy no physical space in a file. Such + // sections belong to a segment when they reside in the segment's virtual + // address space. + if (Sec.Type != ELF::SHT_NOBITS) + return false; + return VirtualAddressesMatch; +} + +template <class ELFT> +Expected<std::vector<ELFYAML::ProgramHeader>> +ELFDumper<ELFT>::dumpProgramHeaders( + ArrayRef<std::unique_ptr<ELFYAML::Chunk>> Chunks) { + std::vector<ELFYAML::ProgramHeader> Ret; + Expected<typename ELFT::PhdrRange> PhdrsOrErr = Obj.program_headers(); + if (!PhdrsOrErr) + return PhdrsOrErr.takeError(); + + for (const typename ELFT::Phdr &Phdr : *PhdrsOrErr) { + ELFYAML::ProgramHeader PH; + PH.Type = Phdr.p_type; + PH.Flags = Phdr.p_flags; + PH.VAddr = Phdr.p_vaddr; + PH.PAddr = Phdr.p_paddr; + + // yaml2obj sets the alignment of a segment to 1 by default. + // We do not print the default alignment to reduce noise in the output. + if (Phdr.p_align != 1) + PH.Align = static_cast<llvm::yaml::Hex64>(Phdr.p_align); + + // Here we match sections with segments. + // It is not possible to have a non-Section chunk, because + // obj2yaml does not create Fill chunks. + for (const std::unique_ptr<ELFYAML::Chunk> &C : Chunks) { + ELFYAML::Section &S = cast<ELFYAML::Section>(*C.get()); + if (isInSegment<ELFT>(S, Sections[S.OriginalSecNdx], Phdr)) { + if (!PH.FirstSec) + PH.FirstSec = S.Name; + PH.LastSec = S.Name; + PH.Chunks.push_back(C.get()); + } + } + + Ret.push_back(PH); + } + + return Ret; +} + +template <class ELFT> +Optional<DWARFYAML::Data> ELFDumper<ELFT>::dumpDWARFSections( + std::vector<std::unique_ptr<ELFYAML::Chunk>> &Sections) { + DWARFYAML::Data DWARF; + for (std::unique_ptr<ELFYAML::Chunk> &C : Sections) { + if (!C->Name.startswith(".debug_")) + continue; + + if (ELFYAML::RawContentSection *RawSec = + dyn_cast<ELFYAML::RawContentSection>(C.get())) { + // FIXME: The dumpDebug* functions should take the content as stored in + // RawSec. Currently, they just use the last section with the matching + // name, which defeats this attempt to skip reading a section header + // string table with the same name as a DWARF section. + if (ShStrTabIndex && RawSec->OriginalSecNdx == *ShStrTabIndex) + continue; + Error Err = Error::success(); + cantFail(std::move(Err)); + + if (RawSec->Name == ".debug_aranges") + Err = dumpDebugARanges(*DWARFCtx.get(), DWARF); + else if (RawSec->Name == ".debug_str") + Err = dumpDebugStrings(*DWARFCtx.get(), DWARF); + else if (RawSec->Name == ".debug_ranges") + Err = dumpDebugRanges(*DWARFCtx.get(), DWARF); + else if (RawSec->Name == ".debug_addr") + Err = dumpDebugAddr(*DWARFCtx.get(), DWARF); + else + continue; + + // If the DWARF section cannot be successfully parsed, emit raw content + // instead of an entry in the DWARF section of the YAML. + if (Err) + consumeError(std::move(Err)); + else + RawSec->Content.reset(); + } + } + + if (DWARF.getNonEmptySectionNames().empty()) + return None; + return DWARF; +} + +template <class ELFT> +Expected<ELFYAML::RawContentSection *> +ELFDumper<ELFT>::dumpPlaceholderSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::RawContentSection>(); + if (Error E = dumpCommonSection(Shdr, *S.get())) + return std::move(E); + + // Normally symbol tables should not be empty. We dump the "Size" + // key when they are. + if ((Shdr->sh_type == ELF::SHT_SYMTAB || Shdr->sh_type == ELF::SHT_DYNSYM) && + !Shdr->sh_size) + S->Size.emplace(); + + return S.release(); +} + +template <class ELFT> +Expected<std::vector<std::unique_ptr<ELFYAML::Chunk>>> +ELFDumper<ELFT>::dumpSections() { + std::vector<std::unique_ptr<ELFYAML::Chunk>> Ret; + auto Add = [&](Expected<ELFYAML::Chunk *> SecOrErr) -> Error { + if (!SecOrErr) + return SecOrErr.takeError(); + Ret.emplace_back(*SecOrErr); + return Error::success(); + }; + + auto GetDumper = [this](unsigned Type) + -> std::function<Expected<ELFYAML::Chunk *>(const Elf_Shdr *)> { + if (Obj.getHeader().e_machine == ELF::EM_ARM && Type == ELF::SHT_ARM_EXIDX) + return [this](const Elf_Shdr *S) { return dumpARMIndexTableSection(S); }; + + if (Obj.getHeader().e_machine == ELF::EM_MIPS && + Type == ELF::SHT_MIPS_ABIFLAGS) + return [this](const Elf_Shdr *S) { return dumpMipsABIFlags(S); }; + + switch (Type) { + case ELF::SHT_DYNAMIC: + return [this](const Elf_Shdr *S) { return dumpDynamicSection(S); }; + case ELF::SHT_SYMTAB_SHNDX: + return [this](const Elf_Shdr *S) { return dumpSymtabShndxSection(S); }; + case ELF::SHT_REL: + case ELF::SHT_RELA: + return [this](const Elf_Shdr *S) { return dumpRelocSection(S); }; + case ELF::SHT_RELR: + return [this](const Elf_Shdr *S) { return dumpRelrSection(S); }; + case ELF::SHT_GROUP: + return [this](const Elf_Shdr *S) { return dumpGroupSection(S); }; + case ELF::SHT_NOBITS: + return [this](const Elf_Shdr *S) { return dumpNoBitsSection(S); }; + case ELF::SHT_NOTE: + return [this](const Elf_Shdr *S) { return dumpNoteSection(S); }; + case ELF::SHT_HASH: + return [this](const Elf_Shdr *S) { return dumpHashSection(S); }; + case ELF::SHT_GNU_HASH: + return [this](const Elf_Shdr *S) { return dumpGnuHashSection(S); }; + case ELF::SHT_GNU_verdef: + return [this](const Elf_Shdr *S) { return dumpVerdefSection(S); }; + case ELF::SHT_GNU_versym: + return [this](const Elf_Shdr *S) { return dumpSymverSection(S); }; + case ELF::SHT_GNU_verneed: + return [this](const Elf_Shdr *S) { return dumpVerneedSection(S); }; + case ELF::SHT_LLVM_ADDRSIG: + return [this](const Elf_Shdr *S) { return dumpAddrsigSection(S); }; + case ELF::SHT_LLVM_LINKER_OPTIONS: + return [this](const Elf_Shdr *S) { return dumpLinkerOptionsSection(S); }; + case ELF::SHT_LLVM_DEPENDENT_LIBRARIES: + return [this](const Elf_Shdr *S) { + return dumpDependentLibrariesSection(S); + }; + case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: + return + [this](const Elf_Shdr *S) { return dumpCallGraphProfileSection(S); }; + case ELF::SHT_LLVM_BB_ADDR_MAP: + return [this](const Elf_Shdr *S) { return dumpBBAddrMapSection(S); }; + case ELF::SHT_STRTAB: + case ELF::SHT_SYMTAB: + case ELF::SHT_DYNSYM: + // The contents of these sections are described by other parts of the YAML + // file. But we still want to dump them, because their properties can be + // important. See comments for 'shouldPrintSection()' for more details. + return [this](const Elf_Shdr *S) { return dumpPlaceholderSection(S); }; + default: + return nullptr; + } + }; + + for (const Elf_Shdr &Sec : Sections) { + // We have dedicated dumping functions for most of the section types. + // Try to use one of them first. + if (std::function<Expected<ELFYAML::Chunk *>(const Elf_Shdr *)> DumpFn = + GetDumper(Sec.sh_type)) { + if (Error E = Add(DumpFn(&Sec))) + return std::move(E); + continue; + } + + // Recognize some special SHT_PROGBITS sections by name. + if (Sec.sh_type == ELF::SHT_PROGBITS) { + auto NameOrErr = Obj.getSectionName(Sec); + if (!NameOrErr) + return NameOrErr.takeError(); + + if (ELFYAML::StackSizesSection::nameMatches(*NameOrErr)) { + if (Error E = Add(dumpStackSizesSection(&Sec))) + return std::move(E); + continue; + } + } + + if (Error E = Add(dumpContentSection(&Sec))) + return std::move(E); + } + + return std::move(Ret); +} + +template <class ELFT> +Error ELFDumper<ELFT>::dumpSymbols( + const Elf_Shdr *Symtab, Optional<std::vector<ELFYAML::Symbol>> &Symbols) { + if (!Symtab) + return Error::success(); + + auto SymtabOrErr = Obj.symbols(Symtab); + if (!SymtabOrErr) + return SymtabOrErr.takeError(); + + if (SymtabOrErr->empty()) + return Error::success(); + + auto StrTableOrErr = Obj.getStringTableForSymtab(*Symtab); + if (!StrTableOrErr) + return StrTableOrErr.takeError(); + + if (Symtab->sh_type == ELF::SHT_SYMTAB) { + SymTable = *SymtabOrErr; + SymbolNames.resize(SymTable.size()); + } + + Symbols.emplace(); + for (const auto &Sym : (*SymtabOrErr).drop_front()) { + ELFYAML::Symbol S; + if (auto EC = dumpSymbol(&Sym, Symtab, *StrTableOrErr, S)) + return EC; + Symbols->push_back(S); + } + + return Error::success(); +} + +template <class ELFT> +Error ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab, + StringRef StrTable, ELFYAML::Symbol &S) { + S.Type = Sym->getType(); + if (Sym->st_value) + S.Value = (yaml::Hex64)Sym->st_value; + if (Sym->st_size) + S.Size = (yaml::Hex64)Sym->st_size; + S.Other = Sym->st_other; + S.Binding = Sym->getBinding(); + + Expected<StringRef> SymbolNameOrErr = + getUniquedSymbolName(Sym, StrTable, SymTab); + if (!SymbolNameOrErr) + return SymbolNameOrErr.takeError(); + S.Name = SymbolNameOrErr.get(); + + if (Sym->st_shndx >= ELF::SHN_LORESERVE) { + S.Index = (ELFYAML::ELF_SHN)Sym->st_shndx; + return Error::success(); + } + + auto ShdrOrErr = Obj.getSection(*Sym, SymTab, ShndxTables.lookup(SymTab)); + if (!ShdrOrErr) + return ShdrOrErr.takeError(); + const Elf_Shdr *Shdr = *ShdrOrErr; + if (!Shdr) + return Error::success(); + + auto NameOrErr = getUniquedSectionName(*Shdr); + if (!NameOrErr) + return NameOrErr.takeError(); + S.Section = NameOrErr.get(); + + return Error::success(); +} + +template <class ELFT> +template <class RelT> +Error ELFDumper<ELFT>::dumpRelocation(const RelT *Rel, const Elf_Shdr *SymTab, + ELFYAML::Relocation &R) { + R.Type = Rel->getType(Obj.isMips64EL()); + R.Offset = Rel->r_offset; + R.Addend = 0; + + auto SymOrErr = Obj.getRelocationSymbol(*Rel, SymTab); + if (!SymOrErr) + return SymOrErr.takeError(); + + // We have might have a relocation with symbol index 0, + // e.g. R_X86_64_NONE or R_X86_64_GOTPC32. + const Elf_Sym *Sym = *SymOrErr; + if (!Sym) + return Error::success(); + + auto StrTabSec = Obj.getSection(SymTab->sh_link); + if (!StrTabSec) + return StrTabSec.takeError(); + auto StrTabOrErr = Obj.getStringTable(**StrTabSec); + if (!StrTabOrErr) + return StrTabOrErr.takeError(); + + Expected<StringRef> NameOrErr = + getUniquedSymbolName(Sym, *StrTabOrErr, SymTab); + if (!NameOrErr) + return NameOrErr.takeError(); + R.Symbol = NameOrErr.get(); + + return Error::success(); +} + +template <class ELFT> +Error ELFDumper<ELFT>::dumpCommonSection(const Elf_Shdr *Shdr, + ELFYAML::Section &S) { + // Dump fields. We do not dump the ShOffset field. When not explicitly + // set, the value is set by yaml2obj automatically. + S.Type = Shdr->sh_type; + if (Shdr->sh_flags) + S.Flags = static_cast<ELFYAML::ELF_SHF>(Shdr->sh_flags); + if (Shdr->sh_addr) + S.Address = static_cast<uint64_t>(Shdr->sh_addr); + S.AddressAlign = Shdr->sh_addralign; + + S.OriginalSecNdx = Shdr - &Sections[0]; + + Expected<StringRef> NameOrErr = getUniquedSectionName(*Shdr); + if (!NameOrErr) + return NameOrErr.takeError(); + S.Name = NameOrErr.get(); + + if (Shdr->sh_entsize != ELFYAML::getDefaultShEntSize<ELFT>( + Obj.getHeader().e_machine, S.Type, S.Name)) + S.EntSize = static_cast<llvm::yaml::Hex64>(Shdr->sh_entsize); + + if (Shdr->sh_link != ELF::SHN_UNDEF) { + Expected<const Elf_Shdr *> LinkSection = Obj.getSection(Shdr->sh_link); + if (!LinkSection) + return make_error<StringError>( + "unable to resolve sh_link reference in section '" + S.Name + + "': " + toString(LinkSection.takeError()), + inconvertibleErrorCode()); + + NameOrErr = getUniquedSectionName(**LinkSection); + if (!NameOrErr) + return NameOrErr.takeError(); + S.Link = NameOrErr.get(); + } + + return Error::success(); +} + +template <class ELFT> +Error ELFDumper<ELFT>::dumpCommonRelocationSection( + const Elf_Shdr *Shdr, ELFYAML::RelocationSection &S) { + if (Error E = dumpCommonSection(Shdr, S)) + return E; + + // Having a zero sh_info field is normal: .rela.dyn is a dynamic + // relocation section that normally has no value in this field. + if (!Shdr->sh_info) + return Error::success(); + + auto InfoSection = Obj.getSection(Shdr->sh_info); + if (!InfoSection) + return InfoSection.takeError(); + + Expected<StringRef> NameOrErr = getUniquedSectionName(**InfoSection); + if (!NameOrErr) + return NameOrErr.takeError(); + S.RelocatableSec = NameOrErr.get(); + + return Error::success(); +} + +template <class ELFT> +Expected<ELFYAML::StackSizesSection *> +ELFDumper<ELFT>::dumpStackSizesSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::StackSizesSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef<uint8_t> Content = *ContentOrErr; + DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4); + + std::vector<ELFYAML::StackSizeEntry> Entries; + DataExtractor::Cursor Cur(0); + while (Cur && Cur.tell() < Content.size()) { + uint64_t Address = Data.getAddress(Cur); + uint64_t Size = Data.getULEB128(Cur); + Entries.push_back({Address, Size}); + } + + if (Content.empty() || !Cur) { + // If .stack_sizes cannot be decoded, we dump it as an array of bytes. + consumeError(Cur.takeError()); + S->Content = yaml::BinaryRef(Content); + } else { + S->Entries = std::move(Entries); + } + + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::BBAddrMapSection *> +ELFDumper<ELFT>::dumpBBAddrMapSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::BBAddrMapSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef<uint8_t> Content = *ContentOrErr; + if (Content.empty()) + return S.release(); + + DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4); + + std::vector<ELFYAML::BBAddrMapEntry> Entries; + DataExtractor::Cursor Cur(0); + while (Cur && Cur.tell() < Content.size()) { + uint64_t Address = Data.getAddress(Cur); + uint64_t NumBlocks = Data.getULEB128(Cur); + std::vector<ELFYAML::BBAddrMapEntry::BBEntry> BBEntries; + // Read the specified number of BB entries, or until decoding fails. + for (uint64_t BlockID = 0; Cur && BlockID < NumBlocks; ++BlockID) { + uint64_t Offset = Data.getULEB128(Cur); + uint64_t Size = Data.getULEB128(Cur); + uint64_t Metadata = Data.getULEB128(Cur); + BBEntries.push_back({Offset, Size, Metadata}); + } + Entries.push_back({Address, /*NumBlocks=*/{}, BBEntries}); + } + + if (!Cur) { + // If the section cannot be decoded, we dump it as an array of bytes. + consumeError(Cur.takeError()); + S->Content = yaml::BinaryRef(Content); + } else { + S->Entries = std::move(Entries); + } + + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::AddrsigSection *> +ELFDumper<ELFT>::dumpAddrsigSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::AddrsigSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef<uint8_t> Content = *ContentOrErr; + DataExtractor::Cursor Cur(0); + DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0); + std::vector<ELFYAML::YAMLFlowString> Symbols; + while (Cur && Cur.tell() < Content.size()) { + uint64_t SymNdx = Data.getULEB128(Cur); + if (!Cur) + break; + + Expected<StringRef> SymbolName = getSymbolName(Shdr->sh_link, SymNdx); + if (!SymbolName || SymbolName->empty()) { + consumeError(SymbolName.takeError()); + Symbols.emplace_back( + StringRef(std::to_string(SymNdx)).copy(StringAllocator)); + continue; + } + + Symbols.emplace_back(*SymbolName); + } + + if (Cur) { + S->Symbols = std::move(Symbols); + return S.release(); + } + + consumeError(Cur.takeError()); + S->Content = yaml::BinaryRef(Content); + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::LinkerOptionsSection *> +ELFDumper<ELFT>::dumpLinkerOptionsSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::LinkerOptionsSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef<uint8_t> Content = *ContentOrErr; + if (Content.empty() || Content.back() != 0) { + S->Content = Content; + return S.release(); + } + + SmallVector<StringRef, 16> Strings; + toStringRef(Content.drop_back()).split(Strings, '\0'); + if (Strings.size() % 2 != 0) { + S->Content = Content; + return S.release(); + } + + S->Options.emplace(); + for (size_t I = 0, E = Strings.size(); I != E; I += 2) + S->Options->push_back({Strings[I], Strings[I + 1]}); + + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::DependentLibrariesSection *> +ELFDumper<ELFT>::dumpDependentLibrariesSection(const Elf_Shdr *Shdr) { + auto DL = std::make_unique<ELFYAML::DependentLibrariesSection>(); + if (Error E = dumpCommonSection(Shdr, *DL)) + return std::move(E); + + Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef<uint8_t> Content = *ContentOrErr; + if (!Content.empty() && Content.back() != 0) { + DL->Content = Content; + return DL.release(); + } + + DL->Libs.emplace(); + for (const uint8_t *I = Content.begin(), *E = Content.end(); I < E;) { + StringRef Lib((const char *)I); + DL->Libs->emplace_back(Lib); + I += Lib.size() + 1; + } + + return DL.release(); +} + +template <class ELFT> +Expected<ELFYAML::CallGraphProfileSection *> +ELFDumper<ELFT>::dumpCallGraphProfileSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::CallGraphProfileSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + ArrayRef<uint8_t> Content = *ContentOrErr; + const uint32_t SizeOfEntry = ELFYAML::getDefaultShEntSize<ELFT>( + Obj.getHeader().e_machine, S->Type, S->Name); + // Dump the section by using the Content key when it is truncated. + // There is no need to create either "Content" or "Entries" fields when the + // section is empty. + if (Content.empty() || Content.size() % SizeOfEntry != 0) { + if (!Content.empty()) + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + std::vector<ELFYAML::CallGraphEntryWeight> Entries(Content.size() / + SizeOfEntry); + DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0); + DataExtractor::Cursor Cur(0); + auto ReadEntry = [&](ELFYAML::CallGraphEntryWeight &E) { + E.Weight = Data.getU64(Cur); + if (!Cur) { + consumeError(Cur.takeError()); + return false; + } + return true; + }; + + for (ELFYAML::CallGraphEntryWeight &E : Entries) { + if (ReadEntry(E)) + continue; + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + S->Entries = std::move(Entries); + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::DynamicSection *> +ELFDumper<ELFT>::dumpDynamicSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::DynamicSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto DynTagsOrErr = Obj.template getSectionContentsAsArray<Elf_Dyn>(*Shdr); + if (!DynTagsOrErr) + return DynTagsOrErr.takeError(); + + S->Entries.emplace(); + for (const Elf_Dyn &Dyn : *DynTagsOrErr) + S->Entries->push_back({(ELFYAML::ELF_DYNTAG)Dyn.getTag(), Dyn.getVal()}); + + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::RelocationSection *> +ELFDumper<ELFT>::dumpRelocSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::RelocationSection>(); + if (auto E = dumpCommonRelocationSection(Shdr, *S)) + return std::move(E); + + auto SymTabOrErr = Obj.getSection(Shdr->sh_link); + if (!SymTabOrErr) + return SymTabOrErr.takeError(); + + if (Shdr->sh_size != 0) + S->Relocations.emplace(); + + if (Shdr->sh_type == ELF::SHT_REL) { + auto Rels = Obj.rels(*Shdr); + if (!Rels) + return Rels.takeError(); + for (const Elf_Rel &Rel : *Rels) { + ELFYAML::Relocation R; + if (Error E = dumpRelocation(&Rel, *SymTabOrErr, R)) + return std::move(E); + S->Relocations->push_back(R); + } + } else { + auto Rels = Obj.relas(*Shdr); + if (!Rels) + return Rels.takeError(); + for (const Elf_Rela &Rel : *Rels) { + ELFYAML::Relocation R; + if (Error E = dumpRelocation(&Rel, *SymTabOrErr, R)) + return std::move(E); + R.Addend = Rel.r_addend; + S->Relocations->push_back(R); + } + } + + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::RelrSection *> +ELFDumper<ELFT>::dumpRelrSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::RelrSection>(); + if (auto E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + if (Expected<ArrayRef<Elf_Relr>> Relrs = Obj.relrs(*Shdr)) { + S->Entries.emplace(); + for (Elf_Relr Rel : *Relrs) + S->Entries->emplace_back(Rel); + return S.release(); + } else { + // Ignore. We are going to dump the data as raw content below. + consumeError(Relrs.takeError()); + } + + Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + S->Content = *ContentOrErr; + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::RawContentSection *> +ELFDumper<ELFT>::dumpContentSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::RawContentSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + unsigned SecIndex = Shdr - &Sections[0]; + if (SecIndex != 0 || Shdr->sh_type != ELF::SHT_NULL) { + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + ArrayRef<uint8_t> Content = *ContentOrErr; + if (!Content.empty()) + S->Content = yaml::BinaryRef(Content); + } else { + S->Size = static_cast<llvm::yaml::Hex64>(Shdr->sh_size); + } + + if (Shdr->sh_info) + S->Info = static_cast<llvm::yaml::Hex64>(Shdr->sh_info); + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::SymtabShndxSection *> +ELFDumper<ELFT>::dumpSymtabShndxSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::SymtabShndxSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto EntriesOrErr = Obj.template getSectionContentsAsArray<Elf_Word>(*Shdr); + if (!EntriesOrErr) + return EntriesOrErr.takeError(); + + S->Entries.emplace(); + for (const Elf_Word &E : *EntriesOrErr) + S->Entries->push_back(E); + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::NoBitsSection *> +ELFDumper<ELFT>::dumpNoBitsSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::NoBitsSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + if (Shdr->sh_size) + S->Size = static_cast<llvm::yaml::Hex64>(Shdr->sh_size); + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::NoteSection *> +ELFDumper<ELFT>::dumpNoteSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::NoteSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + std::vector<ELFYAML::NoteEntry> Entries; + ArrayRef<uint8_t> Content = *ContentOrErr; + while (!Content.empty()) { + if (Content.size() < sizeof(Elf_Nhdr)) { + S->Content = yaml::BinaryRef(*ContentOrErr); + return S.release(); + } + + const Elf_Nhdr *Header = reinterpret_cast<const Elf_Nhdr *>(Content.data()); + if (Content.size() < Header->getSize()) { + S->Content = yaml::BinaryRef(*ContentOrErr); + return S.release(); + } + + Elf_Note Note(*Header); + Entries.push_back( + {Note.getName(), Note.getDesc(), (ELFYAML::ELF_NT)Note.getType()}); + + Content = Content.drop_front(Header->getSize()); + } + + S->Notes = std::move(Entries); + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::HashSection *> +ELFDumper<ELFT>::dumpHashSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::HashSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef<uint8_t> Content = *ContentOrErr; + if (Content.size() % 4 != 0 || Content.size() < 8) { + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + DataExtractor::Cursor Cur(0); + DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0); + uint64_t NBucket = Data.getU32(Cur); + uint64_t NChain = Data.getU32(Cur); + if (Content.size() != (2 + NBucket + NChain) * 4) { + S->Content = yaml::BinaryRef(Content); + if (Cur) + return S.release(); + llvm_unreachable("entries were not read correctly"); + } + + S->Bucket.emplace(NBucket); + for (uint32_t &V : *S->Bucket) + V = Data.getU32(Cur); + + S->Chain.emplace(NChain); + for (uint32_t &V : *S->Chain) + V = Data.getU32(Cur); + + if (Cur) + return S.release(); + llvm_unreachable("entries were not read correctly"); +} + +template <class ELFT> +Expected<ELFYAML::GnuHashSection *> +ELFDumper<ELFT>::dumpGnuHashSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::GnuHashSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + unsigned AddrSize = ELFT::Is64Bits ? 8 : 4; + ArrayRef<uint8_t> Content = *ContentOrErr; + DataExtractor Data(Content, Obj.isLE(), AddrSize); + + ELFYAML::GnuHashHeader Header; + DataExtractor::Cursor Cur(0); + uint64_t NBuckets = Data.getU32(Cur); + Header.SymNdx = Data.getU32(Cur); + uint64_t MaskWords = Data.getU32(Cur); + Header.Shift2 = Data.getU32(Cur); + + // Set just the raw binary content if we were unable to read the header + // or when the section data is truncated or malformed. + uint64_t Size = Data.getData().size() - Cur.tell(); + if (!Cur || (Size < MaskWords * AddrSize + NBuckets * 4) || + (Size % 4 != 0)) { + consumeError(Cur.takeError()); + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + S->Header = Header; + + S->BloomFilter.emplace(MaskWords); + for (llvm::yaml::Hex64 &Val : *S->BloomFilter) + Val = Data.getAddress(Cur); + + S->HashBuckets.emplace(NBuckets); + for (llvm::yaml::Hex32 &Val : *S->HashBuckets) + Val = Data.getU32(Cur); + + S->HashValues.emplace((Data.getData().size() - Cur.tell()) / 4); + for (llvm::yaml::Hex32 &Val : *S->HashValues) + Val = Data.getU32(Cur); + + if (Cur) + return S.release(); + llvm_unreachable("GnuHashSection was not read correctly"); +} + +template <class ELFT> +Expected<ELFYAML::VerdefSection *> +ELFDumper<ELFT>::dumpVerdefSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::VerdefSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto StringTableShdrOrErr = Obj.getSection(Shdr->sh_link); + if (!StringTableShdrOrErr) + return StringTableShdrOrErr.takeError(); + + auto StringTableOrErr = Obj.getStringTable(**StringTableShdrOrErr); + if (!StringTableOrErr) + return StringTableOrErr.takeError(); + + auto Contents = Obj.getSectionContents(*Shdr); + if (!Contents) + return Contents.takeError(); + + S->Entries.emplace(); + + llvm::ArrayRef<uint8_t> Data = *Contents; + const uint8_t *Buf = Data.data(); + while (Buf) { + const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(Buf); + ELFYAML::VerdefEntry Entry; + if (Verdef->vd_version != 1) + return createStringError(errc::invalid_argument, + "invalid SHT_GNU_verdef section version: " + + Twine(Verdef->vd_version)); + + if (Verdef->vd_flags != 0) + Entry.Flags = Verdef->vd_flags; + + if (Verdef->vd_ndx != 0) + Entry.VersionNdx = Verdef->vd_ndx; + + if (Verdef->vd_hash != 0) + Entry.Hash = Verdef->vd_hash; + + const uint8_t *BufAux = Buf + Verdef->vd_aux; + while (BufAux) { + const Elf_Verdaux *Verdaux = + reinterpret_cast<const Elf_Verdaux *>(BufAux); + Entry.VerNames.push_back( + StringTableOrErr->drop_front(Verdaux->vda_name).data()); + BufAux = Verdaux->vda_next ? BufAux + Verdaux->vda_next : nullptr; + } + + S->Entries->push_back(Entry); + Buf = Verdef->vd_next ? Buf + Verdef->vd_next : nullptr; + } + + if (Shdr->sh_info != S->Entries->size()) + S->Info = (llvm::yaml::Hex64)Shdr->sh_info; + + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::SymverSection *> +ELFDumper<ELFT>::dumpSymverSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::SymverSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto VersionsOrErr = Obj.template getSectionContentsAsArray<Elf_Half>(*Shdr); + if (!VersionsOrErr) + return VersionsOrErr.takeError(); + + S->Entries.emplace(); + for (const Elf_Half &E : *VersionsOrErr) + S->Entries->push_back(E); + + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::VerneedSection *> +ELFDumper<ELFT>::dumpVerneedSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::VerneedSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto Contents = Obj.getSectionContents(*Shdr); + if (!Contents) + return Contents.takeError(); + + auto StringTableShdrOrErr = Obj.getSection(Shdr->sh_link); + if (!StringTableShdrOrErr) + return StringTableShdrOrErr.takeError(); + + auto StringTableOrErr = Obj.getStringTable(**StringTableShdrOrErr); + if (!StringTableOrErr) + return StringTableOrErr.takeError(); + + S->VerneedV.emplace(); + + llvm::ArrayRef<uint8_t> Data = *Contents; + const uint8_t *Buf = Data.data(); + while (Buf) { + const Elf_Verneed *Verneed = reinterpret_cast<const Elf_Verneed *>(Buf); + + ELFYAML::VerneedEntry Entry; + Entry.Version = Verneed->vn_version; + Entry.File = + StringRef(StringTableOrErr->drop_front(Verneed->vn_file).data()); + + const uint8_t *BufAux = Buf + Verneed->vn_aux; + while (BufAux) { + const Elf_Vernaux *Vernaux = + reinterpret_cast<const Elf_Vernaux *>(BufAux); + + ELFYAML::VernauxEntry Aux; + Aux.Hash = Vernaux->vna_hash; + Aux.Flags = Vernaux->vna_flags; + Aux.Other = Vernaux->vna_other; + Aux.Name = + StringRef(StringTableOrErr->drop_front(Vernaux->vna_name).data()); + + Entry.AuxV.push_back(Aux); + BufAux = Vernaux->vna_next ? BufAux + Vernaux->vna_next : nullptr; + } + + S->VerneedV->push_back(Entry); + Buf = Verneed->vn_next ? Buf + Verneed->vn_next : nullptr; + } + + if (Shdr->sh_info != S->VerneedV->size()) + S->Info = (llvm::yaml::Hex64)Shdr->sh_info; + + return S.release(); +} + +template <class ELFT> +Expected<StringRef> ELFDumper<ELFT>::getSymbolName(uint32_t SymtabNdx, + uint32_t SymbolNdx) { + auto SymtabOrErr = Obj.getSection(SymtabNdx); + if (!SymtabOrErr) + return SymtabOrErr.takeError(); + + const Elf_Shdr *Symtab = *SymtabOrErr; + auto SymOrErr = Obj.getSymbol(Symtab, SymbolNdx); + if (!SymOrErr) + return SymOrErr.takeError(); + + auto StrTabOrErr = Obj.getStringTableForSymtab(*Symtab); + if (!StrTabOrErr) + return StrTabOrErr.takeError(); + return getUniquedSymbolName(*SymOrErr, *StrTabOrErr, Symtab); +} + +template <class ELFT> +Expected<ELFYAML::GroupSection *> +ELFDumper<ELFT>::dumpGroupSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::GroupSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + // Get symbol with index sh_info. This symbol's name is the signature of the group. + Expected<StringRef> SymbolName = getSymbolName(Shdr->sh_link, Shdr->sh_info); + if (!SymbolName) + return SymbolName.takeError(); + S->Signature = *SymbolName; + + auto MembersOrErr = Obj.template getSectionContentsAsArray<Elf_Word>(*Shdr); + if (!MembersOrErr) + return MembersOrErr.takeError(); + + S->Members.emplace(); + for (Elf_Word Member : *MembersOrErr) { + if (Member == llvm::ELF::GRP_COMDAT) { + S->Members->push_back({"GRP_COMDAT"}); + continue; + } + + Expected<const Elf_Shdr *> SHdrOrErr = Obj.getSection(Member); + if (!SHdrOrErr) + return SHdrOrErr.takeError(); + Expected<StringRef> NameOrErr = getUniquedSectionName(**SHdrOrErr); + if (!NameOrErr) + return NameOrErr.takeError(); + S->Members->push_back({*NameOrErr}); + } + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::ARMIndexTableSection *> +ELFDumper<ELFT>::dumpARMIndexTableSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique<ELFYAML::ARMIndexTableSection>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + if (ContentOrErr->size() % (sizeof(Elf_Word) * 2) != 0) { + S->Content = yaml::BinaryRef(*ContentOrErr); + return S.release(); + } + + ArrayRef<Elf_Word> Words( + reinterpret_cast<const Elf_Word *>(ContentOrErr->data()), + ContentOrErr->size() / sizeof(Elf_Word)); + + S->Entries.emplace(); + for (size_t I = 0, E = Words.size(); I != E; I += 2) + S->Entries->push_back({(yaml::Hex32)Words[I], (yaml::Hex32)Words[I + 1]}); + + return S.release(); +} + +template <class ELFT> +Expected<ELFYAML::MipsABIFlags *> +ELFDumper<ELFT>::dumpMipsABIFlags(const Elf_Shdr *Shdr) { + assert(Shdr->sh_type == ELF::SHT_MIPS_ABIFLAGS && + "Section type is not SHT_MIPS_ABIFLAGS"); + auto S = std::make_unique<ELFYAML::MipsABIFlags>(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + auto *Flags = reinterpret_cast<const object::Elf_Mips_ABIFlags<ELFT> *>( + ContentOrErr.get().data()); + S->Version = Flags->version; + S->ISALevel = Flags->isa_level; + S->ISARevision = Flags->isa_rev; + S->GPRSize = Flags->gpr_size; + S->CPR1Size = Flags->cpr1_size; + S->CPR2Size = Flags->cpr2_size; + S->FpABI = Flags->fp_abi; + S->ISAExtension = Flags->isa_ext; + S->ASEs = Flags->ases; + S->Flags1 = Flags->flags1; + S->Flags2 = Flags->flags2; + return S.release(); +} + +template <class ELFT> +static Error elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj, + std::unique_ptr<DWARFContext> DWARFCtx) { + ELFDumper<ELFT> Dumper(Obj, std::move(DWARFCtx)); + Expected<ELFYAML::Object *> YAMLOrErr = Dumper.dump(); + if (!YAMLOrErr) + return YAMLOrErr.takeError(); + + std::unique_ptr<ELFYAML::Object> YAML(YAMLOrErr.get()); + yaml::Output Yout(Out); + Yout << *YAML; + + return Error::success(); +} + +Error elf2yaml(raw_ostream &Out, const object::ObjectFile &Obj) { + std::unique_ptr<DWARFContext> DWARFCtx = DWARFContext::create(Obj); + if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(&Obj)) + return elf2yaml(Out, ELFObj->getELFFile(), std::move(DWARFCtx)); + + if (const auto *ELFObj = dyn_cast<object::ELF32BEObjectFile>(&Obj)) + return elf2yaml(Out, ELFObj->getELFFile(), std::move(DWARFCtx)); + + if (const auto *ELFObj = dyn_cast<object::ELF64LEObjectFile>(&Obj)) + return elf2yaml(Out, ELFObj->getELFFile(), std::move(DWARFCtx)); + + if (const auto *ELFObj = dyn_cast<object::ELF64BEObjectFile>(&Obj)) + return elf2yaml(Out, ELFObj->getELFFile(), std::move(DWARFCtx)); + + llvm_unreachable("unknown ELF file format"); +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/macho2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/macho2yaml.cpp new file mode 100644 index 0000000000..a6339b2663 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/macho2yaml.cpp @@ -0,0 +1,672 @@ +//===------ macho2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/ObjectYAML/DWARFYAML.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/LEB128.h" + +#include <string.h> // for memcpy + +using namespace llvm; + +class MachODumper { + + template <typename StructType> + Expected<const char *> processLoadCommandData( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + MachOYAML::Object &Y); + + const object::MachOObjectFile &Obj; + std::unique_ptr<DWARFContext> DWARFCtx; + unsigned RawSegment; + void dumpHeader(std::unique_ptr<MachOYAML::Object> &Y); + Error dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y); + void dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y); + void dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y); + void dumpBindOpcodes(std::vector<MachOYAML::BindOpcode> &BindOpcodes, + ArrayRef<uint8_t> OpcodeBuffer, bool Lazy = false); + void dumpExportTrie(std::unique_ptr<MachOYAML::Object> &Y); + void dumpSymbols(std::unique_ptr<MachOYAML::Object> &Y); + void dumpIndirectSymbols(std::unique_ptr<MachOYAML::Object> &Y); + + template <typename SectionType> + Expected<MachOYAML::Section> constructSectionCommon(SectionType Sec, + size_t SecIndex); + template <typename SectionType> + Expected<MachOYAML::Section> constructSection(SectionType Sec, + size_t SecIndex); + template <typename SectionType, typename SegmentType> + Expected<const char *> + extractSections(const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + std::vector<MachOYAML::Section> &Sections, + MachOYAML::Object &Y); + +public: + MachODumper(const object::MachOObjectFile &O, + std::unique_ptr<DWARFContext> DCtx, unsigned RawSegments) + : Obj(O), DWARFCtx(std::move(DCtx)), RawSegment(RawSegments) {} + Expected<std::unique_ptr<MachOYAML::Object>> dump(); +}; + +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + memcpy((void *)&(LC.Data.LCStruct##_data), LoadCmd.Ptr, \ + sizeof(MachO::LCStruct)); \ + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) \ + MachO::swapStruct(LC.Data.LCStruct##_data); \ + if (Expected<const char *> ExpectedEndPtr = \ + processLoadCommandData<MachO::LCStruct>(LC, LoadCmd, *Y.get())) \ + EndPtr = *ExpectedEndPtr; \ + else \ + return ExpectedEndPtr.takeError(); \ + break; + +template <typename SectionType> +Expected<MachOYAML::Section> +MachODumper::constructSectionCommon(SectionType Sec, size_t SecIndex) { + MachOYAML::Section TempSec; + memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16); + memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16); + TempSec.addr = Sec.addr; + TempSec.size = Sec.size; + TempSec.offset = Sec.offset; + TempSec.align = Sec.align; + TempSec.reloff = Sec.reloff; + TempSec.nreloc = Sec.nreloc; + TempSec.flags = Sec.flags; + TempSec.reserved1 = Sec.reserved1; + TempSec.reserved2 = Sec.reserved2; + TempSec.reserved3 = 0; + if (!MachO::isVirtualSection(Sec.flags & MachO::SECTION_TYPE)) + TempSec.content = + yaml::BinaryRef(Obj.getSectionContents(Sec.offset, Sec.size)); + + if (Expected<object::SectionRef> SecRef = Obj.getSection(SecIndex)) { + TempSec.relocations.reserve(TempSec.nreloc); + for (const object::RelocationRef &Reloc : SecRef->relocations()) { + const object::DataRefImpl Rel = Reloc.getRawDataRefImpl(); + const MachO::any_relocation_info RE = Obj.getRelocation(Rel); + MachOYAML::Relocation R; + R.address = Obj.getAnyRelocationAddress(RE); + R.is_pcrel = Obj.getAnyRelocationPCRel(RE); + R.length = Obj.getAnyRelocationLength(RE); + R.type = Obj.getAnyRelocationType(RE); + R.is_scattered = Obj.isRelocationScattered(RE); + R.symbolnum = (R.is_scattered ? 0 : Obj.getPlainRelocationSymbolNum(RE)); + R.is_extern = + (R.is_scattered ? false : Obj.getPlainRelocationExternal(RE)); + R.value = (R.is_scattered ? Obj.getScatteredRelocationValue(RE) : 0); + TempSec.relocations.push_back(R); + } + } else { + return SecRef.takeError(); + } + return TempSec; +} + +template <> +Expected<MachOYAML::Section> MachODumper::constructSection(MachO::section Sec, + size_t SecIndex) { + Expected<MachOYAML::Section> TempSec = constructSectionCommon(Sec, SecIndex); + if (TempSec) + TempSec->reserved3 = 0; + return TempSec; +} + +template <> +Expected<MachOYAML::Section> +MachODumper::constructSection(MachO::section_64 Sec, size_t SecIndex) { + Expected<MachOYAML::Section> TempSec = constructSectionCommon(Sec, SecIndex); + if (TempSec) + TempSec->reserved3 = Sec.reserved3; + return TempSec; +} + +static Error dumpDebugSection(StringRef SecName, DWARFContext &DCtx, + DWARFYAML::Data &DWARF) { + if (SecName == "__debug_abbrev") { + dumpDebugAbbrev(DCtx, DWARF); + return Error::success(); + } + if (SecName == "__debug_aranges") + return dumpDebugARanges(DCtx, DWARF); + if (SecName == "__debug_info") { + dumpDebugInfo(DCtx, DWARF); + return Error::success(); + } + if (SecName == "__debug_line") { + dumpDebugLines(DCtx, DWARF); + return Error::success(); + } + if (SecName.startswith("__debug_pub")) { + // FIXME: We should extract pub-section dumpers from this function. + dumpDebugPubSections(DCtx, DWARF); + return Error::success(); + } + if (SecName == "__debug_ranges") + return dumpDebugRanges(DCtx, DWARF); + if (SecName == "__debug_str") + return dumpDebugStrings(DCtx, DWARF); + return createStringError(errc::not_supported, + "dumping " + SecName + " section is not supported"); +} + +template <typename SectionType, typename SegmentType> +Expected<const char *> MachODumper::extractSections( + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + std::vector<MachOYAML::Section> &Sections, MachOYAML::Object &Y) { + auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; + const SectionType *Curr = + reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType)); + for (; reinterpret_cast<const void *>(Curr) < End; Curr++) { + SectionType Sec; + memcpy((void *)&Sec, Curr, sizeof(SectionType)); + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(Sec); + // For MachO section indices start from 1. + if (Expected<MachOYAML::Section> S = + constructSection(Sec, Sections.size() + 1)) { + StringRef SecName(S->sectname); + + // Copy data sections if requested. + if ((RawSegment & ::RawSegments::data) && + StringRef(S->segname).startswith("__DATA")) + S->content = + yaml::BinaryRef(Obj.getSectionContents(Sec.offset, Sec.size)); + + if (SecName.startswith("__debug_")) { + // If the DWARF section cannot be successfully parsed, emit raw content + // instead of an entry in the DWARF section of the YAML. + if (Error Err = dumpDebugSection(SecName, *DWARFCtx.get(), Y.DWARF)) + consumeError(std::move(Err)); + else + S->content.reset(); + } + Sections.push_back(std::move(*S)); + } else + return S.takeError(); + } + return reinterpret_cast<const char *>(Curr); +} + +template <typename StructType> +Expected<const char *> MachODumper::processLoadCommandData( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + MachOYAML::Object &Y) { + return LoadCmd.Ptr + sizeof(StructType); +} + +template <> +Expected<const char *> +MachODumper::processLoadCommandData<MachO::segment_command>( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + MachOYAML::Object &Y) { + return extractSections<MachO::section, MachO::segment_command>( + LoadCmd, LC.Sections, Y); +} + +template <> +Expected<const char *> +MachODumper::processLoadCommandData<MachO::segment_command_64>( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + MachOYAML::Object &Y) { + return extractSections<MachO::section_64, MachO::segment_command_64>( + LoadCmd, LC.Sections, Y); +} + +template <typename StructType> +const char * +readString(MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) { + auto Start = LoadCmd.Ptr + sizeof(StructType); + auto MaxSize = LoadCmd.C.cmdsize - sizeof(StructType); + auto Size = strnlen(Start, MaxSize); + LC.Content = StringRef(Start, Size).str(); + return Start + Size; +} + +template <> +Expected<const char *> +MachODumper::processLoadCommandData<MachO::dylib_command>( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + MachOYAML::Object &Y) { + return readString<MachO::dylib_command>(LC, LoadCmd); +} + +template <> +Expected<const char *> +MachODumper::processLoadCommandData<MachO::dylinker_command>( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + MachOYAML::Object &Y) { + return readString<MachO::dylinker_command>(LC, LoadCmd); +} + +template <> +Expected<const char *> +MachODumper::processLoadCommandData<MachO::rpath_command>( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + MachOYAML::Object &Y) { + return readString<MachO::rpath_command>(LC, LoadCmd); +} + +template <> +Expected<const char *> +MachODumper::processLoadCommandData<MachO::build_version_command>( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd, + MachOYAML::Object &Y) { + auto Start = LoadCmd.Ptr + sizeof(MachO::build_version_command); + auto NTools = LC.Data.build_version_command_data.ntools; + for (unsigned i = 0; i < NTools; ++i) { + auto Curr = Start + i * sizeof(MachO::build_tool_version); + MachO::build_tool_version BV; + memcpy((void *)&BV, Curr, sizeof(MachO::build_tool_version)); + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(BV); + LC.Tools.push_back(BV); + } + return Start + NTools * sizeof(MachO::build_tool_version); +} + +Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() { + auto Y = std::make_unique<MachOYAML::Object>(); + Y->IsLittleEndian = Obj.isLittleEndian(); + dumpHeader(Y); + if (Error Err = dumpLoadCommands(Y)) + return std::move(Err); + if (RawSegment & ::RawSegments::linkedit) + Y->RawLinkEditSegment = + yaml::BinaryRef(Obj.getSegmentContents("__LINKEDIT")); + else + dumpLinkEdit(Y); + + return std::move(Y); +} + +void MachODumper::dumpHeader(std::unique_ptr<MachOYAML::Object> &Y) { + Y->Header.magic = Obj.getHeader().magic; + Y->Header.cputype = Obj.getHeader().cputype; + Y->Header.cpusubtype = Obj.getHeader().cpusubtype; + Y->Header.filetype = Obj.getHeader().filetype; + Y->Header.ncmds = Obj.getHeader().ncmds; + Y->Header.sizeofcmds = Obj.getHeader().sizeofcmds; + Y->Header.flags = Obj.getHeader().flags; + Y->Header.reserved = 0; +} + +Error MachODumper::dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y) { + for (auto LoadCmd : Obj.load_commands()) { + MachOYAML::LoadCommand LC; + const char *EndPtr = LoadCmd.Ptr; + switch (LoadCmd.C.cmd) { + default: + memcpy((void *)&(LC.Data.load_command_data), LoadCmd.Ptr, + sizeof(MachO::load_command)); + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(LC.Data.load_command_data); + if (Expected<const char *> ExpectedEndPtr = + processLoadCommandData<MachO::load_command>(LC, LoadCmd, + *Y.get())) + EndPtr = *ExpectedEndPtr; + else + return ExpectedEndPtr.takeError(); + break; +#include "llvm/BinaryFormat/MachO.def" + } + auto RemainingBytes = LoadCmd.C.cmdsize - (EndPtr - LoadCmd.Ptr); + if (!std::all_of(EndPtr, &EndPtr[RemainingBytes], + [](const char C) { return C == 0; })) { + LC.PayloadBytes.insert(LC.PayloadBytes.end(), EndPtr, + &EndPtr[RemainingBytes]); + RemainingBytes = 0; + } + LC.ZeroPadBytes = RemainingBytes; + Y->LoadCommands.push_back(std::move(LC)); + } + return Error::success(); +} + +void MachODumper::dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y) { + dumpRebaseOpcodes(Y); + dumpBindOpcodes(Y->LinkEdit.BindOpcodes, Obj.getDyldInfoBindOpcodes()); + dumpBindOpcodes(Y->LinkEdit.WeakBindOpcodes, + Obj.getDyldInfoWeakBindOpcodes()); + dumpBindOpcodes(Y->LinkEdit.LazyBindOpcodes, Obj.getDyldInfoLazyBindOpcodes(), + true); + dumpExportTrie(Y); + dumpSymbols(Y); + dumpIndirectSymbols(Y); +} + +void MachODumper::dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y) { + MachOYAML::LinkEditData &LEData = Y->LinkEdit; + + auto RebaseOpcodes = Obj.getDyldInfoRebaseOpcodes(); + for (auto OpCode = RebaseOpcodes.begin(); OpCode != RebaseOpcodes.end(); + ++OpCode) { + MachOYAML::RebaseOpcode RebaseOp; + RebaseOp.Opcode = + static_cast<MachO::RebaseOpcode>(*OpCode & MachO::REBASE_OPCODE_MASK); + RebaseOp.Imm = *OpCode & MachO::REBASE_IMMEDIATE_MASK; + + unsigned Count; + uint64_t ULEB = 0; + + switch (RebaseOp.Opcode) { + case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + + ULEB = decodeULEB128(OpCode + 1, &Count); + RebaseOp.ExtraData.push_back(ULEB); + OpCode += Count; + LLVM_FALLTHROUGH; + // Intentionally no break here -- This opcode has two ULEB values + case MachO::REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + case MachO::REBASE_OPCODE_ADD_ADDR_ULEB: + case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + case MachO::REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + + ULEB = decodeULEB128(OpCode + 1, &Count); + RebaseOp.ExtraData.push_back(ULEB); + OpCode += Count; + break; + default: + break; + } + + LEData.RebaseOpcodes.push_back(RebaseOp); + + if (RebaseOp.Opcode == MachO::REBASE_OPCODE_DONE) + break; + } +} + +StringRef ReadStringRef(const uint8_t *Start) { + const uint8_t *Itr = Start; + for (; *Itr; ++Itr) + ; + return StringRef(reinterpret_cast<const char *>(Start), Itr - Start); +} + +void MachODumper::dumpBindOpcodes( + std::vector<MachOYAML::BindOpcode> &BindOpcodes, + ArrayRef<uint8_t> OpcodeBuffer, bool Lazy) { + for (auto OpCode = OpcodeBuffer.begin(); OpCode != OpcodeBuffer.end(); + ++OpCode) { + MachOYAML::BindOpcode BindOp; + BindOp.Opcode = + static_cast<MachO::BindOpcode>(*OpCode & MachO::BIND_OPCODE_MASK); + BindOp.Imm = *OpCode & MachO::BIND_IMMEDIATE_MASK; + + unsigned Count; + uint64_t ULEB = 0; + int64_t SLEB = 0; + + switch (BindOp.Opcode) { + case MachO::BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + ULEB = decodeULEB128(OpCode + 1, &Count); + BindOp.ULEBExtraData.push_back(ULEB); + OpCode += Count; + LLVM_FALLTHROUGH; + // Intentionally no break here -- this opcode has two ULEB values + + case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + case MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + case MachO::BIND_OPCODE_ADD_ADDR_ULEB: + case MachO::BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + ULEB = decodeULEB128(OpCode + 1, &Count); + BindOp.ULEBExtraData.push_back(ULEB); + OpCode += Count; + break; + + case MachO::BIND_OPCODE_SET_ADDEND_SLEB: + SLEB = decodeSLEB128(OpCode + 1, &Count); + BindOp.SLEBExtraData.push_back(SLEB); + OpCode += Count; + break; + + case MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + BindOp.Symbol = ReadStringRef(OpCode + 1); + OpCode += BindOp.Symbol.size() + 1; + break; + default: + break; + } + + BindOpcodes.push_back(BindOp); + + // Lazy bindings have DONE opcodes between operations, so we need to keep + // processing after a DONE. + if (!Lazy && BindOp.Opcode == MachO::BIND_OPCODE_DONE) + break; + } +} + +/*! + * /brief processes a node from the export trie, and its children. + * + * To my knowledge there is no documentation of the encoded format of this data + * other than in the heads of the Apple linker engineers. To that end hopefully + * this comment and the implementation below can serve to light the way for + * anyone crazy enough to come down this path in the future. + * + * This function reads and preserves the trie structure of the export trie. To + * my knowledge there is no code anywhere else that reads the data and preserves + * the Trie. LD64 (sources available at opensource.apple.com) has a similar + * implementation that parses the export trie into a vector. That code as well + * as LLVM's libObject MachO implementation were the basis for this. + * + * The export trie is an encoded trie. The node serialization is a bit awkward. + * The below pseudo-code is the best description I've come up with for it. + * + * struct SerializedNode { + * ULEB128 TerminalSize; + * struct TerminalData { <-- This is only present if TerminalSize > 0 + * ULEB128 Flags; + * ULEB128 Address; <-- Present if (! Flags & REEXPORT ) + * ULEB128 Other; <-- Present if ( Flags & REEXPORT || + * Flags & STUB_AND_RESOLVER ) + * char[] ImportName; <-- Present if ( Flags & REEXPORT ) + * } + * uint8_t ChildrenCount; + * Pair<char[], ULEB128> ChildNameOffsetPair[ChildrenCount]; + * SerializedNode Children[ChildrenCount] + * } + * + * Terminal nodes are nodes that represent actual exports. They can appear + * anywhere in the tree other than at the root; they do not need to be leaf + * nodes. When reading the data out of the trie this routine reads it in-order, + * but it puts the child names and offsets directly into the child nodes. This + * results in looping over the children twice during serialization and + * de-serialization, but it makes the YAML representation more human readable. + * + * Below is an example of the graph from a "Hello World" executable: + * + * ------- + * | '' | + * ------- + * | + * ------- + * | '_' | + * ------- + * | + * |----------------------------------------| + * | | + * ------------------------ --------------------- + * | '_mh_execute_header' | | 'main' | + * | Flags: 0x00000000 | | Flags: 0x00000000 | + * | Addr: 0x00000000 | | Addr: 0x00001160 | + * ------------------------ --------------------- + * + * This graph represents the trie for the exports "__mh_execute_header" and + * "_main". In the graph only the "_main" and "__mh_execute_header" nodes are + * terminal. +*/ + +const uint8_t *processExportNode(const uint8_t *CurrPtr, + const uint8_t *const End, + MachOYAML::ExportEntry &Entry) { + if (CurrPtr >= End) + return CurrPtr; + unsigned Count = 0; + Entry.TerminalSize = decodeULEB128(CurrPtr, &Count); + CurrPtr += Count; + if (Entry.TerminalSize != 0) { + Entry.Flags = decodeULEB128(CurrPtr, &Count); + CurrPtr += Count; + if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) { + Entry.Address = 0; + Entry.Other = decodeULEB128(CurrPtr, &Count); + CurrPtr += Count; + Entry.ImportName = std::string(reinterpret_cast<const char *>(CurrPtr)); + } else { + Entry.Address = decodeULEB128(CurrPtr, &Count); + CurrPtr += Count; + if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) { + Entry.Other = decodeULEB128(CurrPtr, &Count); + CurrPtr += Count; + } else + Entry.Other = 0; + } + } + uint8_t childrenCount = *CurrPtr++; + if (childrenCount == 0) + return CurrPtr; + + Entry.Children.insert(Entry.Children.begin(), (size_t)childrenCount, + MachOYAML::ExportEntry()); + for (auto &Child : Entry.Children) { + Child.Name = std::string(reinterpret_cast<const char *>(CurrPtr)); + CurrPtr += Child.Name.length() + 1; + Child.NodeOffset = decodeULEB128(CurrPtr, &Count); + CurrPtr += Count; + } + for (auto &Child : Entry.Children) { + CurrPtr = processExportNode(CurrPtr, End, Child); + } + return CurrPtr; +} + +void MachODumper::dumpExportTrie(std::unique_ptr<MachOYAML::Object> &Y) { + MachOYAML::LinkEditData &LEData = Y->LinkEdit; + auto ExportsTrie = Obj.getDyldInfoExportsTrie(); + processExportNode(ExportsTrie.begin(), ExportsTrie.end(), LEData.ExportTrie); +} + +template <typename nlist_t> +MachOYAML::NListEntry constructNameList(const nlist_t &nlist) { + MachOYAML::NListEntry NL; + NL.n_strx = nlist.n_strx; + NL.n_type = nlist.n_type; + NL.n_sect = nlist.n_sect; + NL.n_desc = nlist.n_desc; + NL.n_value = nlist.n_value; + return NL; +} + +void MachODumper::dumpSymbols(std::unique_ptr<MachOYAML::Object> &Y) { + MachOYAML::LinkEditData &LEData = Y->LinkEdit; + + for (auto Symbol : Obj.symbols()) { + MachOYAML::NListEntry NLE = + Obj.is64Bit() + ? constructNameList<MachO::nlist_64>( + Obj.getSymbol64TableEntry(Symbol.getRawDataRefImpl())) + : constructNameList<MachO::nlist>( + Obj.getSymbolTableEntry(Symbol.getRawDataRefImpl())); + LEData.NameList.push_back(NLE); + } + + StringRef RemainingTable = Obj.getStringTableData(); + while (RemainingTable.size() > 0) { + auto SymbolPair = RemainingTable.split('\0'); + RemainingTable = SymbolPair.second; + LEData.StringTable.push_back(SymbolPair.first); + } +} + +void MachODumper::dumpIndirectSymbols(std::unique_ptr<MachOYAML::Object> &Y) { + MachOYAML::LinkEditData &LEData = Y->LinkEdit; + + MachO::dysymtab_command DLC = Obj.getDysymtabLoadCommand(); + for (unsigned i = 0; i < DLC.nindirectsyms; ++i) + LEData.IndirectSymbols.push_back(Obj.getIndirectSymbolTableEntry(DLC, i)); +} + +Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj, + unsigned RawSegments) { + std::unique_ptr<DWARFContext> DCtx = DWARFContext::create(Obj); + MachODumper Dumper(Obj, std::move(DCtx), RawSegments); + Expected<std::unique_ptr<MachOYAML::Object>> YAML = Dumper.dump(); + if (!YAML) + return YAML.takeError(); + + yaml::YamlObjectFile YAMLFile; + YAMLFile.MachO = std::move(YAML.get()); + + yaml::Output Yout(Out); + Yout << YAMLFile; + return Error::success(); +} + +Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj, + unsigned RawSegments) { + yaml::YamlObjectFile YAMLFile; + YAMLFile.FatMachO.reset(new MachOYAML::UniversalBinary()); + MachOYAML::UniversalBinary &YAML = *YAMLFile.FatMachO; + YAML.Header.magic = Obj.getMagic(); + YAML.Header.nfat_arch = Obj.getNumberOfObjects(); + + for (auto Slice : Obj.objects()) { + MachOYAML::FatArch arch; + arch.cputype = Slice.getCPUType(); + arch.cpusubtype = Slice.getCPUSubType(); + arch.offset = Slice.getOffset(); + arch.size = Slice.getSize(); + arch.align = Slice.getAlign(); + arch.reserved = Slice.getReserved(); + YAML.FatArchs.push_back(arch); + + auto SliceObj = Slice.getAsObjectFile(); + if (!SliceObj) + return SliceObj.takeError(); + + std::unique_ptr<DWARFContext> DCtx = DWARFContext::create(*SliceObj.get()); + MachODumper Dumper(*SliceObj.get(), std::move(DCtx), RawSegments); + Expected<std::unique_ptr<MachOYAML::Object>> YAMLObj = Dumper.dump(); + if (!YAMLObj) + return YAMLObj.takeError(); + YAML.Slices.push_back(*YAMLObj.get()); + } + + yaml::Output Yout(Out); + Yout << YAML; + return Error::success(); +} + +Error macho2yaml(raw_ostream &Out, const object::Binary &Binary, + unsigned RawSegments) { + if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Binary)) + return macho2yaml(Out, *MachOObj, RawSegments); + + if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Binary)) + return macho2yaml(Out, *MachOObj, RawSegments); + + llvm_unreachable("unexpected Mach-O file format"); +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/minidump2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/minidump2yaml.cpp new file mode 100644 index 0000000000..7b25b1869c --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/minidump2yaml.cpp @@ -0,0 +1,23 @@ +//===- minidump2yaml.cpp - Minidump to yaml conversion tool -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/Object/Minidump.h" +#include "llvm/ObjectYAML/MinidumpYAML.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace llvm; + +Error minidump2yaml(raw_ostream &Out, const object::MinidumpFile &Obj) { + auto ExpectedObject = MinidumpYAML::Object::create(Obj); + if (!ExpectedObject) + return ExpectedObject.takeError(); + yaml::Output Output(Out); + Output << *ExpectedObject; + return llvm::Error::success(); +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.cpp new file mode 100644 index 0000000000..9c7a338585 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.cpp @@ -0,0 +1,95 @@ +//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/Minidump.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/InitLLVM.h" + +using namespace llvm; +using namespace llvm::object; + +static cl::opt<std::string> + InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); +static cl::bits<RawSegments> RawSegment( + "raw-segment", + cl::desc("Mach-O: dump the raw contents of the listed segments instead of " + "parsing them:"), + cl::values(clEnumVal(data, "__DATA"), clEnumVal(linkedit, "__LINKEDIT"))); + +static Error dumpObject(const ObjectFile &Obj) { + if (Obj.isCOFF()) + return errorCodeToError(coff2yaml(outs(), cast<COFFObjectFile>(Obj))); + + if (Obj.isXCOFF()) + return xcoff2yaml(outs(), cast<XCOFFObjectFile>(Obj)); + + if (Obj.isELF()) + return elf2yaml(outs(), Obj); + + if (Obj.isWasm()) + return errorCodeToError(wasm2yaml(outs(), cast<WasmObjectFile>(Obj))); + + llvm_unreachable("unexpected object file format"); +} + +static Error dumpInput(StringRef File) { + ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = + MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false, + /*RequiresNullTerminator=*/false); + if (std::error_code EC = FileOrErr.getError()) + return errorCodeToError(EC); + std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get(); + MemoryBufferRef MemBuf = Buffer->getMemBufferRef(); + if (file_magic::archive == identify_magic(MemBuf.getBuffer())) + return archive2yaml(outs(), MemBuf); + + Expected<std::unique_ptr<Binary>> BinOrErr = + createBinary(MemBuf, /*Context=*/nullptr); + if (!BinOrErr) + return BinOrErr.takeError(); + + Binary &Binary = *BinOrErr->get(); + // Universal MachO is not a subclass of ObjectFile, so it needs to be handled + // here with the other binary types. + if (Binary.isMachO() || Binary.isMachOUniversalBinary()) + return macho2yaml(outs(), Binary, RawSegment.getBits()); + if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) + return dumpObject(*Obj); + if (MinidumpFile *Minidump = dyn_cast<MinidumpFile>(&Binary)) + return minidump2yaml(outs(), *Minidump); + + return Error::success(); +} + +static void reportError(StringRef Input, Error Err) { + if (Input == "-") + Input = "<stdin>"; + std::string ErrMsg; + raw_string_ostream OS(ErrMsg); + logAllUnhandledErrors(std::move(Err), OS); + OS.flush(); + errs() << "Error reading file: " << Input << ": " << ErrMsg; + errs().flush(); +} + +int main(int argc, char *argv[]) { + InitLLVM X(argc, argv); + cl::ParseCommandLineOptions(argc, argv); + + if (Error Err = dumpInput(InputFilename)) { + reportError(InputFilename, std::move(Err)); + return 1; + } + + return 0; +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.h b/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.h new file mode 100644 index 0000000000..c026482eaf --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.h @@ -0,0 +1,57 @@ +//===------ utils/obj2yaml.hpp - obj2yaml conversion tool -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// This file declares some helper routines, and also the format-specific +// writers. To add a new format, add the declaration here, and, in a separate +// source file, implement it. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJ2YAML_OBJ2YAML_H +#define LLVM_TOOLS_OBJ2YAML_OBJ2YAML_H + +#include "llvm/Object/COFF.h" +#include "llvm/Object/Minidump.h" +#include "llvm/Object/Wasm.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/MemoryBufferRef.h" +#include <system_error> + +enum RawSegments : unsigned { none = 0, data = 1, linkedit = 1 << 1 }; +std::error_code coff2yaml(llvm::raw_ostream &Out, + const llvm::object::COFFObjectFile &Obj); +llvm::Error elf2yaml(llvm::raw_ostream &Out, + const llvm::object::ObjectFile &Obj); +llvm::Error macho2yaml(llvm::raw_ostream &Out, const llvm::object::Binary &Obj, + unsigned RawSegments); +llvm::Error minidump2yaml(llvm::raw_ostream &Out, + const llvm::object::MinidumpFile &Obj); +llvm::Error xcoff2yaml(llvm::raw_ostream &Out, + const llvm::object::XCOFFObjectFile &Obj); +std::error_code wasm2yaml(llvm::raw_ostream &Out, + const llvm::object::WasmObjectFile &Obj); +llvm::Error archive2yaml(llvm::raw_ostream &Out, llvm::MemoryBufferRef Source); + +// Forward decls for dwarf2yaml +namespace llvm { +class DWARFContext; +namespace DWARFYAML { +struct Data; +} +} + +void dumpDebugAbbrev(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y); +llvm::Error dumpDebugAddr(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y); +llvm::Error dumpDebugARanges(llvm::DWARFContext &DCtx, + llvm::DWARFYAML::Data &Y); +void dumpDebugPubSections(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y); +void dumpDebugInfo(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y); +void dumpDebugLines(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y); +llvm::Error dumpDebugRanges(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y); +llvm::Error dumpDebugStrings(llvm::DWARFContext &DCtx, + llvm::DWARFYAML::Data &Y); + +#endif diff --git a/contrib/libs/llvm14/tools/obj2yaml/wasm2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/wasm2yaml.cpp new file mode 100644 index 0000000000..e4a56524e3 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/wasm2yaml.cpp @@ -0,0 +1,405 @@ +//===------ utils/wasm2yaml.cpp - obj2yaml conversion tool ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/Object/COFF.h" +#include "llvm/ObjectYAML/WasmYAML.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace llvm; +using object::WasmSection; + +namespace { + +class WasmDumper { + const object::WasmObjectFile &Obj; + +public: + WasmDumper(const object::WasmObjectFile &O) : Obj(O) {} + + ErrorOr<WasmYAML::Object *> dump(); + + std::unique_ptr<WasmYAML::CustomSection> + dumpCustomSection(const WasmSection &WasmSec); +}; + +} // namespace + +static WasmYAML::Limits makeLimits(const wasm::WasmLimits &Limits) { + WasmYAML::Limits L; + L.Flags = Limits.Flags; + L.Minimum = Limits.Minimum; + L.Maximum = Limits.Maximum; + return L; +} + +static WasmYAML::Table makeTable(uint32_t Index, + const wasm::WasmTableType &Type) { + WasmYAML::Table T; + T.Index = Index; + T.ElemType = Type.ElemType; + T.TableLimits = makeLimits(Type.Limits); + return T; +} + +std::unique_ptr<WasmYAML::CustomSection> +WasmDumper::dumpCustomSection(const WasmSection &WasmSec) { + std::unique_ptr<WasmYAML::CustomSection> CustomSec; + if (WasmSec.Name == "dylink" || WasmSec.Name == "dylink.0") { + std::unique_ptr<WasmYAML::DylinkSection> DylinkSec = + std::make_unique<WasmYAML::DylinkSection>(); + const wasm::WasmDylinkInfo& Info = Obj.dylinkInfo(); + DylinkSec->MemorySize = Info.MemorySize; + DylinkSec->MemoryAlignment = Info.MemoryAlignment; + DylinkSec->TableSize = Info.TableSize; + DylinkSec->TableAlignment = Info.TableAlignment; + DylinkSec->Needed = Info.Needed; + for (const auto &Imp : Info.ImportInfo) + DylinkSec->ImportInfo.push_back({Imp.Module, Imp.Field, Imp.Flags}); + for (const auto &Exp : Info.ExportInfo) + DylinkSec->ExportInfo.push_back({Exp.Name, Exp.Flags}); + CustomSec = std::move(DylinkSec); + } else if (WasmSec.Name == "name") { + std::unique_ptr<WasmYAML::NameSection> NameSec = + std::make_unique<WasmYAML::NameSection>(); + for (const llvm::wasm::WasmDebugName &Name : Obj.debugNames()) { + WasmYAML::NameEntry NameEntry; + NameEntry.Name = Name.Name; + NameEntry.Index = Name.Index; + if (Name.Type == llvm::wasm::NameType::FUNCTION) { + NameSec->FunctionNames.push_back(NameEntry); + } else if (Name.Type == llvm::wasm::NameType::GLOBAL) { + NameSec->GlobalNames.push_back(NameEntry); + } else { + assert(Name.Type == llvm::wasm::NameType::DATA_SEGMENT); + NameSec->DataSegmentNames.push_back(NameEntry); + } + } + CustomSec = std::move(NameSec); + } else if (WasmSec.Name == "linking") { + std::unique_ptr<WasmYAML::LinkingSection> LinkingSec = + std::make_unique<WasmYAML::LinkingSection>(); + LinkingSec->Version = Obj.linkingData().Version; + + ArrayRef<StringRef> Comdats = Obj.linkingData().Comdats; + for (StringRef ComdatName : Comdats) + LinkingSec->Comdats.emplace_back(WasmYAML::Comdat{ComdatName, {}}); + for (auto &Func : Obj.functions()) { + if (Func.Comdat != UINT32_MAX) { + LinkingSec->Comdats[Func.Comdat].Entries.emplace_back( + WasmYAML::ComdatEntry{wasm::WASM_COMDAT_FUNCTION, Func.Index}); + } + } + + uint32_t SegmentIndex = 0; + for (const object::WasmSegment &Segment : Obj.dataSegments()) { + if (!Segment.Data.Name.empty()) { + WasmYAML::SegmentInfo SegmentInfo; + SegmentInfo.Name = Segment.Data.Name; + SegmentInfo.Index = SegmentIndex; + SegmentInfo.Alignment = Segment.Data.Alignment; + SegmentInfo.Flags = Segment.Data.LinkingFlags; + LinkingSec->SegmentInfos.push_back(SegmentInfo); + } + if (Segment.Data.Comdat != UINT32_MAX) { + LinkingSec->Comdats[Segment.Data.Comdat].Entries.emplace_back( + WasmYAML::ComdatEntry{wasm::WASM_COMDAT_DATA, SegmentIndex}); + } + SegmentIndex++; + } + uint32_t SectionIndex = 0; + for (const auto &Sec : Obj.sections()) { + const WasmSection &WasmSec = Obj.getWasmSection(Sec); + if (WasmSec.Comdat != UINT32_MAX) + LinkingSec->Comdats[WasmSec.Comdat].Entries.emplace_back( + WasmYAML::ComdatEntry{wasm::WASM_COMDAT_SECTION, SectionIndex}); + SectionIndex++; + } + + uint32_t SymbolIndex = 0; + for (const wasm::WasmSymbolInfo &Symbol : Obj.linkingData().SymbolTable) { + WasmYAML::SymbolInfo Info; + Info.Index = SymbolIndex++; + Info.Kind = static_cast<uint32_t>(Symbol.Kind); + Info.Name = Symbol.Name; + Info.Flags = Symbol.Flags; + switch (Symbol.Kind) { + case wasm::WASM_SYMBOL_TYPE_DATA: + Info.DataRef = Symbol.DataRef; + break; + case wasm::WASM_SYMBOL_TYPE_FUNCTION: + case wasm::WASM_SYMBOL_TYPE_GLOBAL: + case wasm::WASM_SYMBOL_TYPE_TABLE: + case wasm::WASM_SYMBOL_TYPE_TAG: + Info.ElementIndex = Symbol.ElementIndex; + break; + case wasm::WASM_SYMBOL_TYPE_SECTION: + Info.ElementIndex = Symbol.ElementIndex; + break; + } + LinkingSec->SymbolTable.emplace_back(Info); + } + + for (const wasm::WasmInitFunc &Func : Obj.linkingData().InitFunctions) { + WasmYAML::InitFunction F{Func.Priority, Func.Symbol}; + LinkingSec->InitFunctions.emplace_back(F); + } + + CustomSec = std::move(LinkingSec); + } else if (WasmSec.Name == "producers") { + std::unique_ptr<WasmYAML::ProducersSection> ProducersSec = + std::make_unique<WasmYAML::ProducersSection>(); + const llvm::wasm::WasmProducerInfo &Info = Obj.getProducerInfo(); + for (auto &E : Info.Languages) { + WasmYAML::ProducerEntry Producer; + Producer.Name = E.first; + Producer.Version = E.second; + ProducersSec->Languages.push_back(Producer); + } + for (auto &E : Info.Tools) { + WasmYAML::ProducerEntry Producer; + Producer.Name = E.first; + Producer.Version = E.second; + ProducersSec->Tools.push_back(Producer); + } + for (auto &E : Info.SDKs) { + WasmYAML::ProducerEntry Producer; + Producer.Name = E.first; + Producer.Version = E.second; + ProducersSec->SDKs.push_back(Producer); + } + CustomSec = std::move(ProducersSec); + } else if (WasmSec.Name == "target_features") { + std::unique_ptr<WasmYAML::TargetFeaturesSection> TargetFeaturesSec = + std::make_unique<WasmYAML::TargetFeaturesSection>(); + for (auto &E : Obj.getTargetFeatures()) { + WasmYAML::FeatureEntry Feature; + Feature.Prefix = E.Prefix; + Feature.Name = E.Name; + TargetFeaturesSec->Features.push_back(Feature); + } + CustomSec = std::move(TargetFeaturesSec); + } else { + CustomSec = std::make_unique<WasmYAML::CustomSection>(WasmSec.Name); + } + CustomSec->Payload = yaml::BinaryRef(WasmSec.Content); + return CustomSec; +} + +ErrorOr<WasmYAML::Object *> WasmDumper::dump() { + auto Y = std::make_unique<WasmYAML::Object>(); + + // Dump header + Y->Header.Version = Obj.getHeader().Version; + + // Dump sections + for (const auto &Sec : Obj.sections()) { + const WasmSection &WasmSec = Obj.getWasmSection(Sec); + std::unique_ptr<WasmYAML::Section> S; + switch (WasmSec.Type) { + case wasm::WASM_SEC_CUSTOM: { + if (WasmSec.Name.startswith("reloc.")) { + // Relocations are attached the sections they apply to rather than + // being represented as a custom section in the YAML output. + continue; + } + S = dumpCustomSection(WasmSec); + break; + } + case wasm::WASM_SEC_TYPE: { + auto TypeSec = std::make_unique<WasmYAML::TypeSection>(); + uint32_t Index = 0; + for (const auto &FunctionSig : Obj.types()) { + WasmYAML::Signature Sig; + Sig.Index = Index++; + for (const auto &ParamType : FunctionSig.Params) + Sig.ParamTypes.emplace_back(static_cast<uint32_t>(ParamType)); + for (const auto &ReturnType : FunctionSig.Returns) + Sig.ReturnTypes.emplace_back(static_cast<uint32_t>(ReturnType)); + TypeSec->Signatures.push_back(Sig); + } + S = std::move(TypeSec); + break; + } + case wasm::WASM_SEC_IMPORT: { + auto ImportSec = std::make_unique<WasmYAML::ImportSection>(); + for (auto &Import : Obj.imports()) { + WasmYAML::Import Im; + Im.Module = Import.Module; + Im.Field = Import.Field; + Im.Kind = Import.Kind; + switch (Im.Kind) { + case wasm::WASM_EXTERNAL_FUNCTION: + Im.SigIndex = Import.SigIndex; + break; + case wasm::WASM_EXTERNAL_GLOBAL: + Im.GlobalImport.Type = Import.Global.Type; + Im.GlobalImport.Mutable = Import.Global.Mutable; + break; + case wasm::WASM_EXTERNAL_TAG: + Im.SigIndex = Import.SigIndex; + break; + case wasm::WASM_EXTERNAL_TABLE: + // FIXME: Currently we always output an index of 0 for any imported + // table. + Im.TableImport = makeTable(0, Import.Table); + break; + case wasm::WASM_EXTERNAL_MEMORY: + Im.Memory = makeLimits(Import.Memory); + break; + } + ImportSec->Imports.push_back(Im); + } + S = std::move(ImportSec); + break; + } + case wasm::WASM_SEC_FUNCTION: { + auto FuncSec = std::make_unique<WasmYAML::FunctionSection>(); + for (const auto &Func : Obj.functions()) { + FuncSec->FunctionTypes.push_back(Func.SigIndex); + } + S = std::move(FuncSec); + break; + } + case wasm::WASM_SEC_TABLE: { + auto TableSec = std::make_unique<WasmYAML::TableSection>(); + for (const wasm::WasmTable &Table : Obj.tables()) { + TableSec->Tables.push_back(makeTable(Table.Index, Table.Type)); + } + S = std::move(TableSec); + break; + } + case wasm::WASM_SEC_MEMORY: { + auto MemorySec = std::make_unique<WasmYAML::MemorySection>(); + for (const wasm::WasmLimits &Memory : Obj.memories()) { + MemorySec->Memories.push_back(makeLimits(Memory)); + } + S = std::move(MemorySec); + break; + } + case wasm::WASM_SEC_TAG: { + auto TagSec = std::make_unique<WasmYAML::TagSection>(); + for (auto &Tag : Obj.tags()) { + TagSec->TagTypes.push_back(Tag.SigIndex); + } + S = std::move(TagSec); + break; + } + case wasm::WASM_SEC_GLOBAL: { + auto GlobalSec = std::make_unique<WasmYAML::GlobalSection>(); + for (auto &Global : Obj.globals()) { + WasmYAML::Global G; + G.Index = Global.Index; + G.Type = Global.Type.Type; + G.Mutable = Global.Type.Mutable; + G.InitExpr = Global.InitExpr; + GlobalSec->Globals.push_back(G); + } + S = std::move(GlobalSec); + break; + } + case wasm::WASM_SEC_START: { + auto StartSec = std::make_unique<WasmYAML::StartSection>(); + StartSec->StartFunction = Obj.startFunction(); + S = std::move(StartSec); + break; + } + case wasm::WASM_SEC_EXPORT: { + auto ExportSec = std::make_unique<WasmYAML::ExportSection>(); + for (auto &Export : Obj.exports()) { + WasmYAML::Export Ex; + Ex.Name = Export.Name; + Ex.Kind = Export.Kind; + Ex.Index = Export.Index; + ExportSec->Exports.push_back(Ex); + } + S = std::move(ExportSec); + break; + } + case wasm::WASM_SEC_ELEM: { + auto ElemSec = std::make_unique<WasmYAML::ElemSection>(); + for (auto &Segment : Obj.elements()) { + WasmYAML::ElemSegment Seg; + Seg.Flags = Segment.Flags; + Seg.TableNumber = Segment.TableNumber; + Seg.ElemKind = Segment.ElemKind; + Seg.Offset = Segment.Offset; + append_range(Seg.Functions, Segment.Functions); + ElemSec->Segments.push_back(Seg); + } + S = std::move(ElemSec); + break; + } + case wasm::WASM_SEC_CODE: { + auto CodeSec = std::make_unique<WasmYAML::CodeSection>(); + for (auto &Func : Obj.functions()) { + WasmYAML::Function Function; + Function.Index = Func.Index; + for (auto &Local : Func.Locals) { + WasmYAML::LocalDecl LocalDecl; + LocalDecl.Type = Local.Type; + LocalDecl.Count = Local.Count; + Function.Locals.push_back(LocalDecl); + } + Function.Body = yaml::BinaryRef(Func.Body); + CodeSec->Functions.push_back(Function); + } + S = std::move(CodeSec); + break; + } + case wasm::WASM_SEC_DATA: { + auto DataSec = std::make_unique<WasmYAML::DataSection>(); + for (const object::WasmSegment &Segment : Obj.dataSegments()) { + WasmYAML::DataSegment Seg; + Seg.SectionOffset = Segment.SectionOffset; + Seg.InitFlags = Segment.Data.InitFlags; + Seg.MemoryIndex = Segment.Data.MemoryIndex; + Seg.Offset = Segment.Data.Offset; + Seg.Content = yaml::BinaryRef(Segment.Data.Content); + DataSec->Segments.push_back(Seg); + } + S = std::move(DataSec); + break; + } + case wasm::WASM_SEC_DATACOUNT: { + auto DataCountSec = std::make_unique<WasmYAML::DataCountSection>(); + DataCountSec->Count = Obj.dataSegments().size(); + S = std::move(DataCountSec); + break; + } + default: + llvm_unreachable("Unknown section type"); + break; + } + for (const wasm::WasmRelocation &Reloc : WasmSec.Relocations) { + WasmYAML::Relocation R; + R.Type = Reloc.Type; + R.Index = Reloc.Index; + R.Offset = Reloc.Offset; + R.Addend = Reloc.Addend; + S->Relocations.push_back(R); + } + Y->Sections.push_back(std::move(S)); + } + + return Y.release(); +} + +std::error_code wasm2yaml(raw_ostream &Out, const object::WasmObjectFile &Obj) { + WasmDumper Dumper(Obj); + ErrorOr<WasmYAML::Object *> YAMLOrErr = Dumper.dump(); + if (std::error_code EC = YAMLOrErr.getError()) + return EC; + + std::unique_ptr<WasmYAML::Object> YAML(YAMLOrErr.get()); + yaml::Output Yout(Out); + Yout << *YAML; + + return std::error_code(); +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/xcoff2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/xcoff2yaml.cpp new file mode 100644 index 0000000000..882c410496 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/xcoff2yaml.cpp @@ -0,0 +1,152 @@ +//===------ xcoff2yaml.cpp - XCOFF YAMLIO implementation --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/ObjectYAML/XCOFFYAML.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace llvm; +using namespace llvm::object; +namespace { + +class XCOFFDumper { + const object::XCOFFObjectFile &Obj; + XCOFFYAML::Object YAMLObj; + void dumpHeader(); + Error dumpSections(); + Error dumpSymbols(); + template <typename Shdr, typename Reloc> + Error dumpSections(ArrayRef<Shdr> Sections); + +public: + XCOFFDumper(const object::XCOFFObjectFile &obj) : Obj(obj) {} + Error dump(); + XCOFFYAML::Object &getYAMLObj() { return YAMLObj; } +}; +} // namespace + +Error XCOFFDumper::dump() { + dumpHeader(); + if (Error E = dumpSections()) + return E; + return dumpSymbols(); +} + +void XCOFFDumper::dumpHeader() { + YAMLObj.Header.Magic = Obj.getMagic(); + YAMLObj.Header.NumberOfSections = Obj.getNumberOfSections(); + YAMLObj.Header.TimeStamp = Obj.getTimeStamp(); + YAMLObj.Header.SymbolTableOffset = Obj.is64Bit() + ? Obj.getSymbolTableOffset64() + : Obj.getSymbolTableOffset32(); + YAMLObj.Header.NumberOfSymTableEntries = + Obj.is64Bit() ? Obj.getNumberOfSymbolTableEntries64() + : Obj.getRawNumberOfSymbolTableEntries32(); + YAMLObj.Header.AuxHeaderSize = Obj.getOptionalHeaderSize(); + YAMLObj.Header.Flags = Obj.getFlags(); +} + +Error XCOFFDumper::dumpSections() { + if (Obj.is64Bit()) + return dumpSections<XCOFFSectionHeader64, XCOFFRelocation64>( + Obj.sections64()); + return dumpSections<XCOFFSectionHeader32, XCOFFRelocation32>( + Obj.sections32()); +} + +template <typename Shdr, typename Reloc> +Error XCOFFDumper::dumpSections(ArrayRef<Shdr> Sections) { + std::vector<XCOFFYAML::Section> &YamlSections = YAMLObj.Sections; + for (const Shdr &S : Sections) { + XCOFFYAML::Section YamlSec; + YamlSec.SectionName = S.getName(); + YamlSec.Address = S.PhysicalAddress; + YamlSec.Size = S.SectionSize; + YamlSec.NumberOfRelocations = S.NumberOfRelocations; + YamlSec.NumberOfLineNumbers = S.NumberOfLineNumbers; + YamlSec.FileOffsetToData = S.FileOffsetToRawData; + YamlSec.FileOffsetToRelocations = S.FileOffsetToRelocationInfo; + YamlSec.FileOffsetToLineNumbers = S.FileOffsetToLineNumberInfo; + YamlSec.Flags = S.Flags; + + // Dump section data. + if (S.FileOffsetToRawData) { + DataRefImpl SectionDRI; + SectionDRI.p = reinterpret_cast<uintptr_t>(&S); + Expected<ArrayRef<uint8_t>> SecDataRefOrErr = + Obj.getSectionContents(SectionDRI); + if (!SecDataRefOrErr) + return SecDataRefOrErr.takeError(); + YamlSec.SectionData = SecDataRefOrErr.get(); + } + + // Dump relocations. + if (S.NumberOfRelocations) { + auto RelRefOrErr = Obj.relocations<Shdr, Reloc>(S); + if (!RelRefOrErr) + return RelRefOrErr.takeError(); + for (const Reloc &R : RelRefOrErr.get()) { + XCOFFYAML::Relocation YamlRel; + YamlRel.Type = R.Type; + YamlRel.Info = R.Info; + YamlRel.SymbolIndex = R.SymbolIndex; + YamlRel.VirtualAddress = R.VirtualAddress; + YamlSec.Relocations.push_back(YamlRel); + } + } + YamlSections.push_back(YamlSec); + } + return Error::success(); +} + +Error XCOFFDumper::dumpSymbols() { + std::vector<XCOFFYAML::Symbol> &Symbols = YAMLObj.Symbols; + + for (const SymbolRef &S : Obj.symbols()) { + DataRefImpl SymbolDRI = S.getRawDataRefImpl(); + const XCOFFSymbolRef SymbolEntRef = Obj.toSymbolRef(SymbolDRI); + XCOFFYAML::Symbol Sym; + + Expected<StringRef> SymNameRefOrErr = Obj.getSymbolName(SymbolDRI); + if (!SymNameRefOrErr) { + return SymNameRefOrErr.takeError(); + } + Sym.SymbolName = SymNameRefOrErr.get(); + + Sym.Value = SymbolEntRef.getValue(); + + Expected<StringRef> SectionNameRefOrErr = + Obj.getSymbolSectionName(SymbolEntRef); + if (!SectionNameRefOrErr) + return SectionNameRefOrErr.takeError(); + + Sym.SectionName = SectionNameRefOrErr.get(); + + Sym.Type = SymbolEntRef.getSymbolType(); + Sym.StorageClass = SymbolEntRef.getStorageClass(); + Sym.NumberOfAuxEntries = SymbolEntRef.getNumberOfAuxEntries(); + + Symbols.push_back(std::move(Sym)); + } + + return Error::success(); +} + +Error xcoff2yaml(raw_ostream &Out, const object::XCOFFObjectFile &Obj) { + XCOFFDumper Dumper(Obj); + + if (Error E = Dumper.dump()) + return E; + + yaml::Output Yout(Out); + Yout << Dumper.getYAMLObj(); + + return Error::success(); +} diff --git a/contrib/libs/llvm14/tools/obj2yaml/ya.make b/contrib/libs/llvm14/tools/obj2yaml/ya.make new file mode 100644 index 0000000000..01f6a855f7 --- /dev/null +++ b/contrib/libs/llvm14/tools/obj2yaml/ya.make @@ -0,0 +1,47 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +LICENSE(Apache-2.0 WITH LLVM-exception) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +PEERDIR( + contrib/libs/llvm14 + contrib/libs/llvm14/lib/BinaryFormat + contrib/libs/llvm14/lib/Bitcode/Reader + contrib/libs/llvm14/lib/Bitstream/Reader + contrib/libs/llvm14/lib/DebugInfo/CodeView + contrib/libs/llvm14/lib/DebugInfo/DWARF + contrib/libs/llvm14/lib/Demangle + contrib/libs/llvm14/lib/IR + contrib/libs/llvm14/lib/MC + contrib/libs/llvm14/lib/MC/MCParser + contrib/libs/llvm14/lib/Object + contrib/libs/llvm14/lib/ObjectYAML + contrib/libs/llvm14/lib/Remarks + contrib/libs/llvm14/lib/Support + contrib/libs/llvm14/lib/TextAPI +) + +ADDINCL( + contrib/libs/llvm14/tools/obj2yaml +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + archive2yaml.cpp + coff2yaml.cpp + dwarf2yaml.cpp + elf2yaml.cpp + macho2yaml.cpp + minidump2yaml.cpp + obj2yaml.cpp + wasm2yaml.cpp + xcoff2yaml.cpp +) + +END() |