| //===------ utils/obj2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "obj2yaml.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) { | |
| const object::pe32_header *PE32Header = nullptr; | |
| Obj.getPE32Header(PE32Header); | |
| if (PE32Header) { | |
| dumpOptionalHeader(PE32Header); | |
| } else { | |
| const object::pe32plus_header *PE32PlusHeader = nullptr; | |
| Obj.getPE32PlusHeader(PE32PlusHeader); | |
| if (PE32PlusHeader) { | |
| 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.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; | |
| unsigned I = 0; | |
| for (auto &DestDD : YAMLObj.OptionalHeader->DataDirectories) { | |
| const object::data_directory *DD; | |
| if (Obj.getDataDirectory(I++, 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; | |
| StringRef SectionName; | |
| S.getName(SectionName); | |
| ArrayRef<uint8_t> sectionData; | |
| if (SectionName != ".debug$S") | |
| continue; | |
| const object::coff_section *COFFSection = Obj.getCOFFSection(S); | |
| 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); | |
| for (const auto &ObjSection : Obj.sections()) { | |
| const object::coff_section *COFFSection = Obj.getCOFFSection(ObjSection); | |
| COFFYAML::Section NewYAMLSection; | |
| ObjSection.getName(NewYAMLSection.Name); | |
| NewYAMLSection.Header.Characteristics = COFFSection->Characteristics; | |
| NewYAMLSection.Header.VirtualAddress = ObjSection.getAddress(); | |
| 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()) | |
| 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, ""); | |
| OS.flush(); | |
| report_fatal_error(Buf); | |
| } | |
| Rel.SymbolName = *SymbolNameOrErr; | |
| 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) { | |
| std::vector<COFFYAML::Symbol> &Symbols = YAMLObj.Symbols; | |
| for (const auto &S : Obj.symbols()) { | |
| object::COFFSymbolRef Symbol = Obj.getCOFFSymbol(S); | |
| COFFYAML::Symbol Sym; | |
| Obj.getSymbolName(Symbol, Sym.Name); | |
| 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(); | |
| } |