blob: 604799fb2737f2b097a09bff555bb4594b1eb481 [file] [log] [blame]
//===------ 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();
}