Files
clang-p2996/llvm/tools/obj2yaml/macho2yaml.cpp
Daniel Rodríguez Troitiño 42ad9bf95f [MachO] Support exports trie in both LC_DYLD_INFO and LC_DYLD_EXPORTS_TRIE
The exports trie used to be pointed by the information in LC_DYLD_INFO,
but when chained fixups are present, the exports trie is pointed by
LC_DYLD_EXPORTS_TRIE instead.

Modify the Object library to give access to the information pointed by
each of the load commands, and to fallback from one into the other when
the exports are requested.

Modify ObjectYAML to support dumping the export trie when pointed by
LC_DYLD_EXPORTS_TRIE and to parse the existence of a export trie also
when the load command is present.

This is a split of D134250 with improvements on top.

Reviewed By: alexander-shaposhnikov

Differential Revision: https://reviews.llvm.org/D134571
2022-11-22 17:44:46 -08:00

724 lines
26 KiB
C++

//===------ 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/Errc.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 dumpFunctionStarts(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);
void dumpChainedFixups(std::unique_ptr<MachOYAML::Object> &Y);
void dumpDataInCode(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);
dumpFunctionStarts(Y);
dumpChainedFixups(Y);
dumpDataInCode(Y);
}
void MachODumper::dumpFunctionStarts(std::unique_ptr<MachOYAML::Object> &Y) {
MachOYAML::LinkEditData &LEData = Y->LinkEdit;
auto FunctionStarts = Obj.getFunctionStarts();
for (auto Addr : FunctionStarts)
LEData.FunctionStarts.push_back(Addr);
}
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;
[[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;
[[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;
// The exports trie can be in LC_DYLD_INFO or LC_DYLD_EXPORTS_TRIE
auto ExportsTrie = Obj.getDyldInfoExportsTrie();
if (ExportsTrie.empty())
ExportsTrie = Obj.getDyldExportsTrie();
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));
}
void MachODumper::dumpChainedFixups(std::unique_ptr<MachOYAML::Object> &Y) {
MachOYAML::LinkEditData &LEData = Y->LinkEdit;
for (const auto &LC : Y->LoadCommands) {
if (LC.Data.load_command_data.cmd == llvm::MachO::LC_DYLD_CHAINED_FIXUPS) {
const MachO::linkedit_data_command &DC =
LC.Data.linkedit_data_command_data;
if (DC.dataoff) {
assert(DC.dataoff < Obj.getData().size());
assert(DC.dataoff + DC.datasize <= Obj.getData().size());
const char *Bytes = Obj.getData().data() + DC.dataoff;
for (size_t Idx = 0; Idx < DC.datasize; Idx++) {
LEData.ChainedFixups.push_back(Bytes[Idx]);
}
}
break;
}
}
}
void MachODumper::dumpDataInCode(std::unique_ptr<MachOYAML::Object> &Y) {
MachOYAML::LinkEditData &LEData = Y->LinkEdit;
MachO::linkedit_data_command DIC = Obj.getDataInCodeLoadCommand();
uint32_t NumEntries = DIC.datasize / sizeof(MachO::data_in_code_entry);
for (uint32_t Idx = 0; Idx < NumEntries; ++Idx) {
MachO::data_in_code_entry DICE =
Obj.getDataInCodeTableEntry(DIC.dataoff, Idx);
MachOYAML::DataInCodeEntry Entry{DICE.offset, DICE.length, DICE.kind};
LEData.DataInCode.emplace_back(Entry);
}
}
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");
}