blob: 7501661591f062f241b9351cff9ca4cd287592d6 [file] [log] [blame]
//===- MachOObjectFile.cpp - Mach-O object file binding -------------------===//
//
// 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 defines the MachOObjectFile class, which binds the MachOObject
// class to the generic ObjectFile wrapper.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolicFile.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SwapByteOrder.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <list>
#include <memory>
#include <system_error>
using namespace llvm;
using namespace object;
namespace {
struct section_base {
char sectname[16];
char segname[16];
};
} // end anonymous namespace
static Error malformedError(const Twine &Msg) {
return make_error<GenericBinaryError>("truncated or malformed object (" +
Msg + ")",
object_error::parse_failed);
}
// FIXME: Replace all uses of this function with getStructOrErr.
template <typename T>
static T getStruct(const MachOObjectFile &O, const char *P) {
// Don't read before the beginning or past the end of the file
if (P < O.getData().begin() || P + sizeof(T) > O.getData().end())
report_fatal_error("Malformed MachO file.");
T Cmd;
memcpy(&Cmd, P, sizeof(T));
if (O.isLittleEndian() != sys::IsLittleEndianHost)
MachO::swapStruct(Cmd);
return Cmd;
}
template <typename T>
static Expected<T> getStructOrErr(const MachOObjectFile &O, const char *P) {
// Don't read before the beginning or past the end of the file
if (P < O.getData().begin() || P + sizeof(T) > O.getData().end())
return malformedError("Structure read out-of-range");
T Cmd;
memcpy(&Cmd, P, sizeof(T));
if (O.isLittleEndian() != sys::IsLittleEndianHost)
MachO::swapStruct(Cmd);
return Cmd;
}
static const char *
getSectionPtr(const MachOObjectFile &O, MachOObjectFile::LoadCommandInfo L,
unsigned Sec) {
uintptr_t CommandAddr = reinterpret_cast<uintptr_t>(L.Ptr);
bool Is64 = O.is64Bit();
unsigned SegmentLoadSize = Is64 ? sizeof(MachO::segment_command_64) :
sizeof(MachO::segment_command);
unsigned SectionSize = Is64 ? sizeof(MachO::section_64) :
sizeof(MachO::section);
uintptr_t SectionAddr = CommandAddr + SegmentLoadSize + Sec * SectionSize;
return reinterpret_cast<const char*>(SectionAddr);
}
static const char *getPtr(const MachOObjectFile &O, size_t Offset) {
assert(Offset <= O.getData().size());
return O.getData().data() + Offset;
}
static MachO::nlist_base
getSymbolTableEntryBase(const MachOObjectFile &O, DataRefImpl DRI) {
const char *P = reinterpret_cast<const char *>(DRI.p);
return getStruct<MachO::nlist_base>(O, P);
}
static StringRef parseSegmentOrSectionName(const char *P) {
if (P[15] == 0)
// Null terminated.
return P;
// Not null terminated, so this is a 16 char string.
return StringRef(P, 16);
}
static unsigned getCPUType(const MachOObjectFile &O) {
return O.getHeader().cputype;
}
static unsigned getCPUSubType(const MachOObjectFile &O) {
return O.getHeader().cpusubtype;
}
static uint32_t
getPlainRelocationAddress(const MachO::any_relocation_info &RE) {
return RE.r_word0;
}
static unsigned
getScatteredRelocationAddress(const MachO::any_relocation_info &RE) {
return RE.r_word0 & 0xffffff;
}
static bool getPlainRelocationPCRel(const MachOObjectFile &O,
const MachO::any_relocation_info &RE) {
if (O.isLittleEndian())
return (RE.r_word1 >> 24) & 1;
return (RE.r_word1 >> 7) & 1;
}
static bool
getScatteredRelocationPCRel(const MachO::any_relocation_info &RE) {
return (RE.r_word0 >> 30) & 1;
}
static unsigned getPlainRelocationLength(const MachOObjectFile &O,
const MachO::any_relocation_info &RE) {
if (O.isLittleEndian())
return (RE.r_word1 >> 25) & 3;
return (RE.r_word1 >> 5) & 3;
}
static unsigned
getScatteredRelocationLength(const MachO::any_relocation_info &RE) {
return (RE.r_word0 >> 28) & 3;
}
static unsigned getPlainRelocationType(const MachOObjectFile &O,
const MachO::any_relocation_info &RE) {
if (O.isLittleEndian())
return RE.r_word1 >> 28;
return RE.r_word1 & 0xf;
}
static uint32_t getSectionFlags(const MachOObjectFile &O,
DataRefImpl Sec) {
if (O.is64Bit()) {
MachO::section_64 Sect = O.getSection64(Sec);
return Sect.flags;
}
MachO::section Sect = O.getSection(Sec);
return Sect.flags;
}
static Expected<MachOObjectFile::LoadCommandInfo>
getLoadCommandInfo(const MachOObjectFile &Obj, const char *Ptr,
uint32_t LoadCommandIndex) {
if (auto CmdOrErr = getStructOrErr<MachO::load_command>(Obj, Ptr)) {
if (CmdOrErr->cmdsize + Ptr > Obj.getData().end())
return malformedError("load command " + Twine(LoadCommandIndex) +
" extends past end of file");
if (CmdOrErr->cmdsize < 8)
return malformedError("load command " + Twine(LoadCommandIndex) +
" with size less than 8 bytes");
return MachOObjectFile::LoadCommandInfo({Ptr, *CmdOrErr});
} else
return CmdOrErr.takeError();
}
static Expected<MachOObjectFile::LoadCommandInfo>
getFirstLoadCommandInfo(const MachOObjectFile &Obj) {
unsigned HeaderSize = Obj.is64Bit() ? sizeof(MachO::mach_header_64)
: sizeof(MachO::mach_header);
if (sizeof(MachO::load_command) > Obj.getHeader().sizeofcmds)
return malformedError("load command 0 extends past the end all load "
"commands in the file");
return getLoadCommandInfo(Obj, getPtr(Obj, HeaderSize), 0);
}
static Expected<MachOObjectFile::LoadCommandInfo>
getNextLoadCommandInfo(const MachOObjectFile &Obj, uint32_t LoadCommandIndex,
const MachOObjectFile::LoadCommandInfo &L) {
unsigned HeaderSize = Obj.is64Bit() ? sizeof(MachO::mach_header_64)
: sizeof(MachO::mach_header);
if (L.Ptr + L.C.cmdsize + sizeof(MachO::load_command) >
Obj.getData().data() + HeaderSize + Obj.getHeader().sizeofcmds)
return malformedError("load command " + Twine(LoadCommandIndex + 1) +
" extends past the end all load commands in the file");
return getLoadCommandInfo(Obj, L.Ptr + L.C.cmdsize, LoadCommandIndex + 1);
}
template <typename T>
static void parseHeader(const MachOObjectFile &Obj, T &Header,
Error &Err) {
if (sizeof(T) > Obj.getData().size()) {
Err = malformedError("the mach header extends past the end of the "
"file");
return;
}
if (auto HeaderOrErr = getStructOrErr<T>(Obj, getPtr(Obj, 0)))
Header = *HeaderOrErr;
else
Err = HeaderOrErr.takeError();
}
// This is used to check for overlapping of Mach-O elements.
struct MachOElement {
uint64_t Offset;
uint64_t Size;
const char *Name;
};
static Error checkOverlappingElement(std::list<MachOElement> &Elements,
uint64_t Offset, uint64_t Size,
const char *Name) {
if (Size == 0)
return Error::success();
for (auto it = Elements.begin(); it != Elements.end(); ++it) {
const auto &E = *it;
if ((Offset >= E.Offset && Offset < E.Offset + E.Size) ||
(Offset + Size > E.Offset && Offset + Size < E.Offset + E.Size) ||
(Offset <= E.Offset && Offset + Size >= E.Offset + E.Size))
return malformedError(Twine(Name) + " at offset " + Twine(Offset) +
" with a size of " + Twine(Size) + ", overlaps " +
E.Name + " at offset " + Twine(E.Offset) + " with "
"a size of " + Twine(E.Size));
auto nt = it;
nt++;
if (nt != Elements.end()) {
const auto &N = *nt;
if (Offset + Size <= N.Offset) {
Elements.insert(nt, {Offset, Size, Name});
return Error::success();
}
}
}
Elements.push_back({Offset, Size, Name});
return Error::success();
}
// Parses LC_SEGMENT or LC_SEGMENT_64 load command, adds addresses of all
// sections to \param Sections, and optionally sets
// \param IsPageZeroSegment to true.
template <typename Segment, typename Section>
static Error parseSegmentLoadCommand(
const MachOObjectFile &Obj, const MachOObjectFile::LoadCommandInfo &Load,
SmallVectorImpl<const char *> &Sections, bool &IsPageZeroSegment,
uint32_t LoadCommandIndex, const char *CmdName, uint64_t SizeOfHeaders,
std::list<MachOElement> &Elements) {
const unsigned SegmentLoadSize = sizeof(Segment);
if (Load.C.cmdsize < SegmentLoadSize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" " + CmdName + " cmdsize too small");
if (auto SegOrErr = getStructOrErr<Segment>(Obj, Load.Ptr)) {
Segment S = SegOrErr.get();
const unsigned SectionSize = sizeof(Section);
uint64_t FileSize = Obj.getData().size();
if (S.nsects > std::numeric_limits<uint32_t>::max() / SectionSize ||
S.nsects * SectionSize > Load.C.cmdsize - SegmentLoadSize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" inconsistent cmdsize in " + CmdName +
" for the number of sections");
for (unsigned J = 0; J < S.nsects; ++J) {
const char *Sec = getSectionPtr(Obj, Load, J);
Sections.push_back(Sec);
auto SectionOrErr = getStructOrErr<Section>(Obj, Sec);
if (!SectionOrErr)
return SectionOrErr.takeError();
Section s = SectionOrErr.get();
if (Obj.getHeader().filetype != MachO::MH_DYLIB_STUB &&
Obj.getHeader().filetype != MachO::MH_DSYM &&
s.flags != MachO::S_ZEROFILL &&
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
s.offset > FileSize)
return malformedError("offset field of section " + Twine(J) + " in " +
CmdName + " command " + Twine(LoadCommandIndex) +
" extends past the end of the file");
if (Obj.getHeader().filetype != MachO::MH_DYLIB_STUB &&
Obj.getHeader().filetype != MachO::MH_DSYM &&
s.flags != MachO::S_ZEROFILL &&
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL && S.fileoff == 0 &&
s.offset < SizeOfHeaders && s.size != 0)
return malformedError("offset field of section " + Twine(J) + " in " +
CmdName + " command " + Twine(LoadCommandIndex) +
" not past the headers of the file");
uint64_t BigSize = s.offset;
BigSize += s.size;
if (Obj.getHeader().filetype != MachO::MH_DYLIB_STUB &&
Obj.getHeader().filetype != MachO::MH_DSYM &&
s.flags != MachO::S_ZEROFILL &&
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
BigSize > FileSize)
return malformedError("offset field plus size field of section " +
Twine(J) + " in " + CmdName + " command " +
Twine(LoadCommandIndex) +
" extends past the end of the file");
if (Obj.getHeader().filetype != MachO::MH_DYLIB_STUB &&
Obj.getHeader().filetype != MachO::MH_DSYM &&
s.flags != MachO::S_ZEROFILL &&
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL &&
s.size > S.filesize)
return malformedError("size field of section " +
Twine(J) + " in " + CmdName + " command " +
Twine(LoadCommandIndex) +
" greater than the segment");
if (Obj.getHeader().filetype != MachO::MH_DYLIB_STUB &&
Obj.getHeader().filetype != MachO::MH_DSYM && s.size != 0 &&
s.addr < S.vmaddr)
return malformedError("addr field of section " + Twine(J) + " in " +
CmdName + " command " + Twine(LoadCommandIndex) +
" less than the segment's vmaddr");
BigSize = s.addr;
BigSize += s.size;
uint64_t BigEnd = S.vmaddr;
BigEnd += S.vmsize;
if (S.vmsize != 0 && s.size != 0 && BigSize > BigEnd)
return malformedError("addr field plus size of section " + Twine(J) +
" in " + CmdName + " command " +
Twine(LoadCommandIndex) +
" greater than than "
"the segment's vmaddr plus vmsize");
if (Obj.getHeader().filetype != MachO::MH_DYLIB_STUB &&
Obj.getHeader().filetype != MachO::MH_DSYM &&
s.flags != MachO::S_ZEROFILL &&
s.flags != MachO::S_THREAD_LOCAL_ZEROFILL)
if (Error Err = checkOverlappingElement(Elements, s.offset, s.size,
"section contents"))
return Err;
if (s.reloff > FileSize)
return malformedError("reloff field of section " + Twine(J) + " in " +
CmdName + " command " + Twine(LoadCommandIndex) +
" extends past the end of the file");
BigSize = s.nreloc;
BigSize *= sizeof(struct MachO::relocation_info);
BigSize += s.reloff;
if (BigSize > FileSize)
return malformedError("reloff field plus nreloc field times sizeof("
"struct relocation_info) of section " +
Twine(J) + " in " + CmdName + " command " +
Twine(LoadCommandIndex) +
" extends past the end of the file");
if (Error Err = checkOverlappingElement(Elements, s.reloff, s.nreloc *
sizeof(struct
MachO::relocation_info),
"section relocation entries"))
return Err;
}
if (S.fileoff > FileSize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" fileoff field in " + CmdName +
" extends past the end of the file");
uint64_t BigSize = S.fileoff;
BigSize += S.filesize;
if (BigSize > FileSize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" fileoff field plus filesize field in " +
CmdName + " extends past the end of the file");
if (S.vmsize != 0 && S.filesize > S.vmsize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" filesize field in " + CmdName +
" greater than vmsize field");
IsPageZeroSegment |= StringRef("__PAGEZERO").equals(S.segname);
} else
return SegOrErr.takeError();
return Error::success();
}
static Error checkSymtabCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
const char **SymtabLoadCmd,
std::list<MachOElement> &Elements) {
if (Load.C.cmdsize < sizeof(MachO::symtab_command))
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_SYMTAB cmdsize too small");
if (*SymtabLoadCmd != nullptr)
return malformedError("more than one LC_SYMTAB command");
auto SymtabOrErr = getStructOrErr<MachO::symtab_command>(Obj, Load.Ptr);
if (!SymtabOrErr)
return SymtabOrErr.takeError();
MachO::symtab_command Symtab = SymtabOrErr.get();
if (Symtab.cmdsize != sizeof(MachO::symtab_command))
return malformedError("LC_SYMTAB command " + Twine(LoadCommandIndex) +
" has incorrect cmdsize");
uint64_t FileSize = Obj.getData().size();
if (Symtab.symoff > FileSize)
return malformedError("symoff field of LC_SYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end "
"of the file");
uint64_t SymtabSize = Symtab.nsyms;
const char *struct_nlist_name;
if (Obj.is64Bit()) {
SymtabSize *= sizeof(MachO::nlist_64);
struct_nlist_name = "struct nlist_64";
} else {
SymtabSize *= sizeof(MachO::nlist);
struct_nlist_name = "struct nlist";
}
uint64_t BigSize = SymtabSize;
BigSize += Symtab.symoff;
if (BigSize > FileSize)
return malformedError("symoff field plus nsyms field times sizeof(" +
Twine(struct_nlist_name) + ") of LC_SYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end "
"of the file");
if (Error Err = checkOverlappingElement(Elements, Symtab.symoff, SymtabSize,
"symbol table"))
return Err;
if (Symtab.stroff > FileSize)
return malformedError("stroff field of LC_SYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end "
"of the file");
BigSize = Symtab.stroff;
BigSize += Symtab.strsize;
if (BigSize > FileSize)
return malformedError("stroff field plus strsize field of LC_SYMTAB "
"command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
if (Error Err = checkOverlappingElement(Elements, Symtab.stroff,
Symtab.strsize, "string table"))
return Err;
*SymtabLoadCmd = Load.Ptr;
return Error::success();
}
static Error checkDysymtabCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
const char **DysymtabLoadCmd,
std::list<MachOElement> &Elements) {
if (Load.C.cmdsize < sizeof(MachO::dysymtab_command))
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_DYSYMTAB cmdsize too small");
if (*DysymtabLoadCmd != nullptr)
return malformedError("more than one LC_DYSYMTAB command");
auto DysymtabOrErr =
getStructOrErr<MachO::dysymtab_command>(Obj, Load.Ptr);
if (!DysymtabOrErr)
return DysymtabOrErr.takeError();
MachO::dysymtab_command Dysymtab = DysymtabOrErr.get();
if (Dysymtab.cmdsize != sizeof(MachO::dysymtab_command))
return malformedError("LC_DYSYMTAB command " + Twine(LoadCommandIndex) +
" has incorrect cmdsize");
uint64_t FileSize = Obj.getData().size();
if (Dysymtab.tocoff > FileSize)
return malformedError("tocoff field of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
uint64_t BigSize = Dysymtab.ntoc;
BigSize *= sizeof(MachO::dylib_table_of_contents);
BigSize += Dysymtab.tocoff;
if (BigSize > FileSize)
return malformedError("tocoff field plus ntoc field times sizeof(struct "
"dylib_table_of_contents) of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, Dysymtab.tocoff,
Dysymtab.ntoc * sizeof(struct
MachO::dylib_table_of_contents),
"table of contents"))
return Err;
if (Dysymtab.modtaboff > FileSize)
return malformedError("modtaboff field of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
BigSize = Dysymtab.nmodtab;
const char *struct_dylib_module_name;
uint64_t sizeof_modtab;
if (Obj.is64Bit()) {
sizeof_modtab = sizeof(MachO::dylib_module_64);
struct_dylib_module_name = "struct dylib_module_64";
} else {
sizeof_modtab = sizeof(MachO::dylib_module);
struct_dylib_module_name = "struct dylib_module";
}
BigSize *= sizeof_modtab;
BigSize += Dysymtab.modtaboff;
if (BigSize > FileSize)
return malformedError("modtaboff field plus nmodtab field times sizeof(" +
Twine(struct_dylib_module_name) + ") of LC_DYSYMTAB "
"command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
if (Error Err = checkOverlappingElement(Elements, Dysymtab.modtaboff,
Dysymtab.nmodtab * sizeof_modtab,
"module table"))
return Err;
if (Dysymtab.extrefsymoff > FileSize)
return malformedError("extrefsymoff field of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
BigSize = Dysymtab.nextrefsyms;
BigSize *= sizeof(MachO::dylib_reference);
BigSize += Dysymtab.extrefsymoff;
if (BigSize > FileSize)
return malformedError("extrefsymoff field plus nextrefsyms field times "
"sizeof(struct dylib_reference) of LC_DYSYMTAB "
"command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
if (Error Err = checkOverlappingElement(Elements, Dysymtab.extrefsymoff,
Dysymtab.nextrefsyms *
sizeof(MachO::dylib_reference),
"reference table"))
return Err;
if (Dysymtab.indirectsymoff > FileSize)
return malformedError("indirectsymoff field of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
BigSize = Dysymtab.nindirectsyms;
BigSize *= sizeof(uint32_t);
BigSize += Dysymtab.indirectsymoff;
if (BigSize > FileSize)
return malformedError("indirectsymoff field plus nindirectsyms field times "
"sizeof(uint32_t) of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, Dysymtab.indirectsymoff,
Dysymtab.nindirectsyms *
sizeof(uint32_t),
"indirect table"))
return Err;
if (Dysymtab.extreloff > FileSize)
return malformedError("extreloff field of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
BigSize = Dysymtab.nextrel;
BigSize *= sizeof(MachO::relocation_info);
BigSize += Dysymtab.extreloff;
if (BigSize > FileSize)
return malformedError("extreloff field plus nextrel field times sizeof"
"(struct relocation_info) of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, Dysymtab.extreloff,
Dysymtab.nextrel *
sizeof(MachO::relocation_info),
"external relocation table"))
return Err;
if (Dysymtab.locreloff > FileSize)
return malformedError("locreloff field of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
BigSize = Dysymtab.nlocrel;
BigSize *= sizeof(MachO::relocation_info);
BigSize += Dysymtab.locreloff;
if (BigSize > FileSize)
return malformedError("locreloff field plus nlocrel field times sizeof"
"(struct relocation_info) of LC_DYSYMTAB command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, Dysymtab.locreloff,
Dysymtab.nlocrel *
sizeof(MachO::relocation_info),
"local relocation table"))
return Err;
*DysymtabLoadCmd = Load.Ptr;
return Error::success();
}
static Error checkLinkeditDataCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
const char **LoadCmd, const char *CmdName,
std::list<MachOElement> &Elements,
const char *ElementName) {
if (Load.C.cmdsize < sizeof(MachO::linkedit_data_command))
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " cmdsize too small");
if (*LoadCmd != nullptr)
return malformedError("more than one " + Twine(CmdName) + " command");
auto LinkDataOrError =
getStructOrErr<MachO::linkedit_data_command>(Obj, Load.Ptr);
if (!LinkDataOrError)
return LinkDataOrError.takeError();
MachO::linkedit_data_command LinkData = LinkDataOrError.get();
if (LinkData.cmdsize != sizeof(MachO::linkedit_data_command))
return malformedError(Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " has incorrect cmdsize");
uint64_t FileSize = Obj.getData().size();
if (LinkData.dataoff > FileSize)
return malformedError("dataoff field of " + Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
uint64_t BigSize = LinkData.dataoff;
BigSize += LinkData.datasize;
if (BigSize > FileSize)
return malformedError("dataoff field plus datasize field of " +
Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, LinkData.dataoff,
LinkData.datasize, ElementName))
return Err;
*LoadCmd = Load.Ptr;
return Error::success();
}
static Error checkDyldInfoCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
const char **LoadCmd, const char *CmdName,
std::list<MachOElement> &Elements) {
if (Load.C.cmdsize < sizeof(MachO::dyld_info_command))
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " cmdsize too small");
if (*LoadCmd != nullptr)
return malformedError("more than one LC_DYLD_INFO and or LC_DYLD_INFO_ONLY "
"command");
auto DyldInfoOrErr =
getStructOrErr<MachO::dyld_info_command>(Obj, Load.Ptr);
if (!DyldInfoOrErr)
return DyldInfoOrErr.takeError();
MachO::dyld_info_command DyldInfo = DyldInfoOrErr.get();
if (DyldInfo.cmdsize != sizeof(MachO::dyld_info_command))
return malformedError(Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " has incorrect cmdsize");
uint64_t FileSize = Obj.getData().size();
if (DyldInfo.rebase_off > FileSize)
return malformedError("rebase_off field of " + Twine(CmdName) +
" command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
uint64_t BigSize = DyldInfo.rebase_off;
BigSize += DyldInfo.rebase_size;
if (BigSize > FileSize)
return malformedError("rebase_off field plus rebase_size field of " +
Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, DyldInfo.rebase_off,
DyldInfo.rebase_size,
"dyld rebase info"))
return Err;
if (DyldInfo.bind_off > FileSize)
return malformedError("bind_off field of " + Twine(CmdName) +
" command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
BigSize = DyldInfo.bind_off;
BigSize += DyldInfo.bind_size;
if (BigSize > FileSize)
return malformedError("bind_off field plus bind_size field of " +
Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, DyldInfo.bind_off,
DyldInfo.bind_size,
"dyld bind info"))
return Err;
if (DyldInfo.weak_bind_off > FileSize)
return malformedError("weak_bind_off field of " + Twine(CmdName) +
" command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
BigSize = DyldInfo.weak_bind_off;
BigSize += DyldInfo.weak_bind_size;
if (BigSize > FileSize)
return malformedError("weak_bind_off field plus weak_bind_size field of " +
Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, DyldInfo.weak_bind_off,
DyldInfo.weak_bind_size,
"dyld weak bind info"))
return Err;
if (DyldInfo.lazy_bind_off > FileSize)
return malformedError("lazy_bind_off field of " + Twine(CmdName) +
" command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
BigSize = DyldInfo.lazy_bind_off;
BigSize += DyldInfo.lazy_bind_size;
if (BigSize > FileSize)
return malformedError("lazy_bind_off field plus lazy_bind_size field of " +
Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, DyldInfo.lazy_bind_off,
DyldInfo.lazy_bind_size,
"dyld lazy bind info"))
return Err;
if (DyldInfo.export_off > FileSize)
return malformedError("export_off field of " + Twine(CmdName) +
" command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
BigSize = DyldInfo.export_off;
BigSize += DyldInfo.export_size;
if (BigSize > FileSize)
return malformedError("export_off field plus export_size field of " +
Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, DyldInfo.export_off,
DyldInfo.export_size,
"dyld export info"))
return Err;
*LoadCmd = Load.Ptr;
return Error::success();
}
static Error checkDylibCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex, const char *CmdName) {
if (Load.C.cmdsize < sizeof(MachO::dylib_command))
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " cmdsize too small");
auto CommandOrErr = getStructOrErr<MachO::dylib_command>(Obj, Load.Ptr);
if (!CommandOrErr)
return CommandOrErr.takeError();
MachO::dylib_command D = CommandOrErr.get();
if (D.dylib.name < sizeof(MachO::dylib_command))
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " name.offset field too small, not past "
"the end of the dylib_command struct");
if (D.dylib.name >= D.cmdsize)
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " name.offset field extends past the end "
"of the load command");
// Make sure there is a null between the starting offset of the name and
// the end of the load command.
uint32_t i;
const char *P = (const char *)Load.Ptr;
for (i = D.dylib.name; i < D.cmdsize; i++)
if (P[i] == '\0')
break;
if (i >= D.cmdsize)
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " library name extends past the end of the "
"load command");
return Error::success();
}
static Error checkDylibIdCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
const char **LoadCmd) {
if (Error Err = checkDylibCommand(Obj, Load, LoadCommandIndex,
"LC_ID_DYLIB"))
return Err;
if (*LoadCmd != nullptr)
return malformedError("more than one LC_ID_DYLIB command");
if (Obj.getHeader().filetype != MachO::MH_DYLIB &&
Obj.getHeader().filetype != MachO::MH_DYLIB_STUB)
return malformedError("LC_ID_DYLIB load command in non-dynamic library "
"file type");
*LoadCmd = Load.Ptr;
return Error::success();
}
static Error checkDyldCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex, const char *CmdName) {
if (Load.C.cmdsize < sizeof(MachO::dylinker_command))
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " cmdsize too small");
auto CommandOrErr = getStructOrErr<MachO::dylinker_command>(Obj, Load.Ptr);
if (!CommandOrErr)
return CommandOrErr.takeError();
MachO::dylinker_command D = CommandOrErr.get();
if (D.name < sizeof(MachO::dylinker_command))
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " name.offset field too small, not past "
"the end of the dylinker_command struct");
if (D.name >= D.cmdsize)
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " name.offset field extends past the end "
"of the load command");
// Make sure there is a null between the starting offset of the name and
// the end of the load command.
uint32_t i;
const char *P = (const char *)Load.Ptr;
for (i = D.name; i < D.cmdsize; i++)
if (P[i] == '\0')
break;
if (i >= D.cmdsize)
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " dyld name extends past the end of the "
"load command");
return Error::success();
}
static Error checkVersCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
const char **LoadCmd, const char *CmdName) {
if (Load.C.cmdsize != sizeof(MachO::version_min_command))
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " has incorrect cmdsize");
if (*LoadCmd != nullptr)
return malformedError("more than one LC_VERSION_MIN_MACOSX, "
"LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_TVOS or "
"LC_VERSION_MIN_WATCHOS command");
*LoadCmd = Load.Ptr;
return Error::success();
}
static Error checkNoteCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
std::list<MachOElement> &Elements) {
if (Load.C.cmdsize != sizeof(MachO::note_command))
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_NOTE has incorrect cmdsize");
auto NoteCmdOrErr = getStructOrErr<MachO::note_command>(Obj, Load.Ptr);
if (!NoteCmdOrErr)
return NoteCmdOrErr.takeError();
MachO::note_command Nt = NoteCmdOrErr.get();
uint64_t FileSize = Obj.getData().size();
if (Nt.offset > FileSize)
return malformedError("offset field of LC_NOTE command " +
Twine(LoadCommandIndex) + " extends "
"past the end of the file");
uint64_t BigSize = Nt.offset;
BigSize += Nt.size;
if (BigSize > FileSize)
return malformedError("size field plus offset field of LC_NOTE command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, Nt.offset, Nt.size,
"LC_NOTE data"))
return Err;
return Error::success();
}
static Error
parseBuildVersionCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
SmallVectorImpl<const char*> &BuildTools,
uint32_t LoadCommandIndex) {
auto BVCOrErr =
getStructOrErr<MachO::build_version_command>(Obj, Load.Ptr);
if (!BVCOrErr)
return BVCOrErr.takeError();
MachO::build_version_command BVC = BVCOrErr.get();
if (Load.C.cmdsize !=
sizeof(MachO::build_version_command) +
BVC.ntools * sizeof(MachO::build_tool_version))
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_BUILD_VERSION_COMMAND has incorrect cmdsize");
auto Start = Load.Ptr + sizeof(MachO::build_version_command);
BuildTools.resize(BVC.ntools);
for (unsigned i = 0; i < BVC.ntools; ++i)
BuildTools[i] = Start + i * sizeof(MachO::build_tool_version);
return Error::success();
}
static Error checkRpathCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex) {
if (Load.C.cmdsize < sizeof(MachO::rpath_command))
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_RPATH cmdsize too small");
auto ROrErr = getStructOrErr<MachO::rpath_command>(Obj, Load.Ptr);
if (!ROrErr)
return ROrErr.takeError();
MachO::rpath_command R = ROrErr.get();
if (R.path < sizeof(MachO::rpath_command))
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_RPATH path.offset field too small, not past "
"the end of the rpath_command struct");
if (R.path >= R.cmdsize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_RPATH path.offset field extends past the end "
"of the load command");
// Make sure there is a null between the starting offset of the path and
// the end of the load command.
uint32_t i;
const char *P = (const char *)Load.Ptr;
for (i = R.path; i < R.cmdsize; i++)
if (P[i] == '\0')
break;
if (i >= R.cmdsize)
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_RPATH library name extends past the end of the "
"load command");
return Error::success();
}
static Error checkEncryptCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
uint64_t cryptoff, uint64_t cryptsize,
const char **LoadCmd, const char *CmdName) {
if (*LoadCmd != nullptr)
return malformedError("more than one LC_ENCRYPTION_INFO and or "
"LC_ENCRYPTION_INFO_64 command");
uint64_t FileSize = Obj.getData().size();
if (cryptoff > FileSize)
return malformedError("cryptoff field of " + Twine(CmdName) +
" command " + Twine(LoadCommandIndex) + " extends "
"past the end of the file");
uint64_t BigSize = cryptoff;
BigSize += cryptsize;
if (BigSize > FileSize)
return malformedError("cryptoff field plus cryptsize field of " +
Twine(CmdName) + " command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
*LoadCmd = Load.Ptr;
return Error::success();
}
static Error checkLinkerOptCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex) {
if (Load.C.cmdsize < sizeof(MachO::linker_option_command))
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_LINKER_OPTION cmdsize too small");
auto LinkOptionOrErr =
getStructOrErr<MachO::linker_option_command>(Obj, Load.Ptr);
if (!LinkOptionOrErr)
return LinkOptionOrErr.takeError();
MachO::linker_option_command L = LinkOptionOrErr.get();
// Make sure the count of strings is correct.
const char *string = (const char *)Load.Ptr +
sizeof(struct MachO::linker_option_command);
uint32_t left = L.cmdsize - sizeof(struct MachO::linker_option_command);
uint32_t i = 0;
while (left > 0) {
while (*string == '\0' && left > 0) {
string++;
left--;
}
if (left > 0) {
i++;
uint32_t NullPos = StringRef(string, left).find('\0');
if (0xffffffff == NullPos)
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_LINKER_OPTION string #" + Twine(i) +
" is not NULL terminated");
uint32_t len = std::min(NullPos, left) + 1;
string += len;
left -= len;
}
}
if (L.count != i)
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_LINKER_OPTION string count " + Twine(L.count) +
" does not match number of strings");
return Error::success();
}
static Error checkSubCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex, const char *CmdName,
size_t SizeOfCmd, const char *CmdStructName,
uint32_t PathOffset, const char *PathFieldName) {
if (PathOffset < SizeOfCmd)
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " " + PathFieldName + ".offset field too "
"small, not past the end of the " + CmdStructName);
if (PathOffset >= Load.C.cmdsize)
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " " + PathFieldName + ".offset field "
"extends past the end of the load command");
// Make sure there is a null between the starting offset of the path and
// the end of the load command.
uint32_t i;
const char *P = (const char *)Load.Ptr;
for (i = PathOffset; i < Load.C.cmdsize; i++)
if (P[i] == '\0')
break;
if (i >= Load.C.cmdsize)
return malformedError("load command " + Twine(LoadCommandIndex) + " " +
CmdName + " " + PathFieldName + " name extends past "
"the end of the load command");
return Error::success();
}
static Error checkThreadCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo &Load,
uint32_t LoadCommandIndex,
const char *CmdName) {
if (Load.C.cmdsize < sizeof(MachO::thread_command))
return malformedError("load command " + Twine(LoadCommandIndex) +
CmdName + " cmdsize too small");
auto ThreadCommandOrErr =
getStructOrErr<MachO::thread_command>(Obj, Load.Ptr);
if (!ThreadCommandOrErr)
return ThreadCommandOrErr.takeError();
MachO::thread_command T = ThreadCommandOrErr.get();
const char *state = Load.Ptr + sizeof(MachO::thread_command);
const char *end = Load.Ptr + T.cmdsize;
uint32_t nflavor = 0;
uint32_t cputype = getCPUType(Obj);
while (state < end) {
if(state + sizeof(uint32_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
"flavor in " + CmdName + " extends past end of "
"command");
uint32_t flavor;
memcpy(&flavor, state, sizeof(uint32_t));
if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(flavor);
state += sizeof(uint32_t);
if(state + sizeof(uint32_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count in " + CmdName + " extends past end of "
"command");
uint32_t count;
memcpy(&count, state, sizeof(uint32_t));
if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(count);
state += sizeof(uint32_t);
if (cputype == MachO::CPU_TYPE_I386) {
if (flavor == MachO::x86_THREAD_STATE32) {
if (count != MachO::x86_THREAD_STATE32_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not x86_THREAD_STATE32_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a x86_THREAD_STATE32 flavor in " + CmdName +
" command");
if (state + sizeof(MachO::x86_thread_state32_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" x86_THREAD_STATE32 extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::x86_thread_state32_t);
} else {
return malformedError("load command " + Twine(LoadCommandIndex) +
" unknown flavor (" + Twine(flavor) + ") for "
"flavor number " + Twine(nflavor) + " in " +
CmdName + " command");
}
} else if (cputype == MachO::CPU_TYPE_X86_64) {
if (flavor == MachO::x86_THREAD_STATE) {
if (count != MachO::x86_THREAD_STATE_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not x86_THREAD_STATE_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a x86_THREAD_STATE flavor in " + CmdName +
" command");
if (state + sizeof(MachO::x86_thread_state_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" x86_THREAD_STATE extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::x86_thread_state_t);
} else if (flavor == MachO::x86_FLOAT_STATE) {
if (count != MachO::x86_FLOAT_STATE_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not x86_FLOAT_STATE_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a x86_FLOAT_STATE flavor in " + CmdName +
" command");
if (state + sizeof(MachO::x86_float_state_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" x86_FLOAT_STATE extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::x86_float_state_t);
} else if (flavor == MachO::x86_EXCEPTION_STATE) {
if (count != MachO::x86_EXCEPTION_STATE_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not x86_EXCEPTION_STATE_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a x86_EXCEPTION_STATE flavor in " + CmdName +
" command");
if (state + sizeof(MachO::x86_exception_state_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" x86_EXCEPTION_STATE extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::x86_exception_state_t);
} else if (flavor == MachO::x86_THREAD_STATE64) {
if (count != MachO::x86_THREAD_STATE64_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not x86_THREAD_STATE64_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a x86_THREAD_STATE64 flavor in " + CmdName +
" command");
if (state + sizeof(MachO::x86_thread_state64_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" x86_THREAD_STATE64 extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::x86_thread_state64_t);
} else if (flavor == MachO::x86_EXCEPTION_STATE64) {
if (count != MachO::x86_EXCEPTION_STATE64_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not x86_EXCEPTION_STATE64_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a x86_EXCEPTION_STATE64 flavor in " + CmdName +
" command");
if (state + sizeof(MachO::x86_exception_state64_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" x86_EXCEPTION_STATE64 extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::x86_exception_state64_t);
} else {
return malformedError("load command " + Twine(LoadCommandIndex) +
" unknown flavor (" + Twine(flavor) + ") for "
"flavor number " + Twine(nflavor) + " in " +
CmdName + " command");
}
} else if (cputype == MachO::CPU_TYPE_ARM) {
if (flavor == MachO::ARM_THREAD_STATE) {
if (count != MachO::ARM_THREAD_STATE_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not ARM_THREAD_STATE_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a ARM_THREAD_STATE flavor in " + CmdName +
" command");
if (state + sizeof(MachO::arm_thread_state32_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" ARM_THREAD_STATE extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::arm_thread_state32_t);
} else {
return malformedError("load command " + Twine(LoadCommandIndex) +
" unknown flavor (" + Twine(flavor) + ") for "
"flavor number " + Twine(nflavor) + " in " +
CmdName + " command");
}
} else if (cputype == MachO::CPU_TYPE_ARM64 ||
cputype == MachO::CPU_TYPE_ARM64_32) {
if (flavor == MachO::ARM_THREAD_STATE64) {
if (count != MachO::ARM_THREAD_STATE64_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not ARM_THREAD_STATE64_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a ARM_THREAD_STATE64 flavor in " + CmdName +
" command");
if (state + sizeof(MachO::arm_thread_state64_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" ARM_THREAD_STATE64 extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::arm_thread_state64_t);
} else {
return malformedError("load command " + Twine(LoadCommandIndex) +
" unknown flavor (" + Twine(flavor) + ") for "
"flavor number " + Twine(nflavor) + " in " +
CmdName + " command");
}
} else if (cputype == MachO::CPU_TYPE_POWERPC) {
if (flavor == MachO::PPC_THREAD_STATE) {
if (count != MachO::PPC_THREAD_STATE_COUNT)
return malformedError("load command " + Twine(LoadCommandIndex) +
" count not PPC_THREAD_STATE_COUNT for "
"flavor number " + Twine(nflavor) + " which is "
"a PPC_THREAD_STATE flavor in " + CmdName +
" command");
if (state + sizeof(MachO::ppc_thread_state32_t) > end)
return malformedError("load command " + Twine(LoadCommandIndex) +
" PPC_THREAD_STATE extends past end of "
"command in " + CmdName + " command");
state += sizeof(MachO::ppc_thread_state32_t);
} else {
return malformedError("load command " + Twine(LoadCommandIndex) +
" unknown flavor (" + Twine(flavor) + ") for "
"flavor number " + Twine(nflavor) + " in " +
CmdName + " command");
}
} else {
return malformedError("unknown cputype (" + Twine(cputype) + ") load "
"command " + Twine(LoadCommandIndex) + " for " +
CmdName + " command can't be checked");
}
nflavor++;
}
return Error::success();
}
static Error checkTwoLevelHintsCommand(const MachOObjectFile &Obj,
const MachOObjectFile::LoadCommandInfo
&Load,
uint32_t LoadCommandIndex,
const char **LoadCmd,
std::list<MachOElement> &Elements) {
if (Load.C.cmdsize != sizeof(MachO::twolevel_hints_command))
return malformedError("load command " + Twine(LoadCommandIndex) +
" LC_TWOLEVEL_HINTS has incorrect cmdsize");
if (*LoadCmd != nullptr)
return malformedError("more than one LC_TWOLEVEL_HINTS command");
auto HintsOrErr = getStructOrErr<MachO::twolevel_hints_command>(Obj, Load.Ptr);
if(!HintsOrErr)
return HintsOrErr.takeError();
MachO::twolevel_hints_command Hints = HintsOrErr.get();
uint64_t FileSize = Obj.getData().size();
if (Hints.offset > FileSize)
return malformedError("offset field of LC_TWOLEVEL_HINTS command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
uint64_t BigSize = Hints.nhints;
BigSize *= sizeof(MachO::twolevel_hint);
BigSize += Hints.offset;
if (BigSize > FileSize)
return malformedError("offset field plus nhints times sizeof(struct "
"twolevel_hint) field of LC_TWOLEVEL_HINTS command " +
Twine(LoadCommandIndex) + " extends past the end of "
"the file");
if (Error Err = checkOverlappingElement(Elements, Hints.offset, Hints.nhints *
sizeof(MachO::twolevel_hint),
"two level hints"))
return Err;
*LoadCmd = Load.Ptr;
return Error::success();
}
// Returns true if the libObject code does not support the load command and its
// contents. The cmd value it is treated as an unknown load command but with
// an error message that says the cmd value is obsolete.
static bool isLoadCommandObsolete(uint32_t cmd) {
if (cmd == MachO::LC_SYMSEG ||
cmd == MachO::LC_LOADFVMLIB ||
cmd == MachO::LC_IDFVMLIB ||
cmd == MachO::LC_IDENT ||
cmd == MachO::LC_FVMFILE ||
cmd == MachO::LC_PREPAGE ||
cmd == MachO::LC_PREBOUND_DYLIB ||
cmd == MachO::LC_TWOLEVEL_HINTS ||
cmd == MachO::LC_PREBIND_CKSUM)
return true;
return false;
}
Expected<std::unique_ptr<MachOObjectFile>>
MachOObjectFile::create(MemoryBufferRef Object, bool IsLittleEndian,
bool Is64Bits, uint32_t UniversalCputype,
uint32_t UniversalIndex) {
Error Err = Error::success();
std::unique_ptr<MachOObjectFile> Obj(
new MachOObjectFile(std::move(Object), IsLittleEndian,
Is64Bits, Err, UniversalCputype,
UniversalIndex));
if (Err)
return std::move(Err);
return std::move(Obj);
}
MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
bool Is64bits, Error &Err,
uint32_t UniversalCputype,
uint32_t UniversalIndex)
: ObjectFile(getMachOType(IsLittleEndian, Is64bits), Object) {
ErrorAsOutParameter ErrAsOutParam(&Err);
uint64_t SizeOfHeaders;
uint32_t cputype;
if (is64Bit()) {
parseHeader(*this, Header64, Err);
SizeOfHeaders = sizeof(MachO::mach_header_64);
cputype = Header64.cputype;
} else {
parseHeader(*this, Header, Err);
SizeOfHeaders = sizeof(MachO::mach_header);
cputype = Header.cputype;
}
if (Err)
return;
SizeOfHeaders += getHeader().sizeofcmds;
if (getData().data() + SizeOfHeaders > getData().end()) {
Err = malformedError("load commands extend past the end of the file");
return;
}
if (UniversalCputype != 0 && cputype != UniversalCputype) {
Err = malformedError("universal header architecture: " +
Twine(UniversalIndex) + "'s cputype does not match "
"object file's mach header");
return;
}
std::list<MachOElement> Elements;
Elements.push_back({0, SizeOfHeaders, "Mach-O headers"});
uint32_t LoadCommandCount = getHeader().ncmds;
LoadCommandInfo Load;
if (LoadCommandCount != 0) {
if (auto LoadOrErr = getFirstLoadCommandInfo(*this))
Load = *LoadOrErr;
else {
Err = LoadOrErr.takeError();
return;
}
}
const char *DyldIdLoadCmd = nullptr;
const char *FuncStartsLoadCmd = nullptr;
const char *SplitInfoLoadCmd = nullptr;
const char *CodeSignDrsLoadCmd = nullptr;
const char *CodeSignLoadCmd = nullptr;
const char *VersLoadCmd = nullptr;
const char *SourceLoadCmd = nullptr;
const char *EntryPointLoadCmd = nullptr;
const char *EncryptLoadCmd = nullptr;
const char *RoutinesLoadCmd = nullptr;
const char *UnixThreadLoadCmd = nullptr;
const char *TwoLevelHintsLoadCmd = nullptr;
for (unsigned I = 0; I < LoadCommandCount; ++I) {
if (is64Bit()) {
if (Load.C.cmdsize % 8 != 0) {
// We have a hack here to allow 64-bit Mach-O core files to have
// LC_THREAD commands that are only a multiple of 4 and not 8 to be
// allowed since the macOS kernel produces them.
if (getHeader().filetype != MachO::MH_CORE ||
Load.C.cmd != MachO::LC_THREAD || Load.C.cmdsize % 4) {
Err = malformedError("load command " + Twine(I) + " cmdsize not a "
"multiple of 8");
return;
}
}
} else {
if (Load.C.cmdsize % 4 != 0) {
Err = malformedError("load command " + Twine(I) + " cmdsize not a "
"multiple of 4");
return;
}
}
LoadCommands.push_back(Load);
if (Load.C.cmd == MachO::LC_SYMTAB) {
if ((Err = checkSymtabCommand(*this, Load, I, &SymtabLoadCmd, Elements)))
return;
} else if (Load.C.cmd == MachO::LC_DYSYMTAB) {
if ((Err = checkDysymtabCommand(*this, Load, I, &DysymtabLoadCmd,
Elements)))
return;
} else if (Load.C.cmd == MachO::LC_DATA_IN_CODE) {
if ((Err = checkLinkeditDataCommand(*this, Load, I, &DataInCodeLoadCmd,
"LC_DATA_IN_CODE", Elements,
"data in code info")))
return;
} else if (Load.C.cmd == MachO::LC_LINKER_OPTIMIZATION_HINT) {
if ((Err = checkLinkeditDataCommand(*this, Load, I, &LinkOptHintsLoadCmd,
"LC_LINKER_OPTIMIZATION_HINT",
Elements, "linker optimization "
"hints")))
return;
} else if (Load.C.cmd == MachO::LC_FUNCTION_STARTS) {
if ((Err = checkLinkeditDataCommand(*this, Load, I, &FuncStartsLoadCmd,
"LC_FUNCTION_STARTS", Elements,
"function starts data")))
return;
} else if (Load.C.cmd == MachO::LC_SEGMENT_SPLIT_INFO) {
if ((Err = checkLinkeditDataCommand(*this, Load, I, &SplitInfoLoadCmd,
"LC_SEGMENT_SPLIT_INFO", Elements,
"split info data")))
return;
} else if (Load.C.cmd == MachO::LC_DYLIB_CODE_SIGN_DRS) {
if ((Err = checkLinkeditDataCommand(*this, Load, I, &CodeSignDrsLoadCmd,
"LC_DYLIB_CODE_SIGN_DRS", Elements,
"code signing RDs data")))
return;
} else if (Load.C.cmd == MachO::LC_CODE_SIGNATURE) {
if ((Err = checkLinkeditDataCommand(*this, Load, I, &CodeSignLoadCmd,
"LC_CODE_SIGNATURE", Elements,
"code signature data")))
return;
} else if (Load.C.cmd == MachO::LC_DYLD_INFO) {
if ((Err = checkDyldInfoCommand(*this, Load, I, &DyldInfoLoadCmd,
"LC_DYLD_INFO", Elements)))
return;
} else if (Load.C.cmd == MachO::LC_DYLD_INFO_ONLY) {
if ((Err = checkDyldInfoCommand(*this, Load, I, &DyldInfoLoadCmd,
"LC_DYLD_INFO_ONLY", Elements)))
return;
} else if (Load.C.cmd == MachO::LC_UUID) {
if (Load.C.cmdsize != sizeof(MachO::uuid_command)) {
Err = malformedError("LC_UUID command " + Twine(I) + " has incorrect "
"cmdsize");
return;
}
if (UuidLoadCmd) {
Err = malformedError("more than one LC_UUID command");
return;
}
UuidLoadCmd = Load.Ptr;
} else if (Load.C.cmd == MachO::LC_SEGMENT_64) {
if ((Err = parseSegmentLoadCommand<MachO::segment_command_64,
MachO::section_64>(
*this, Load, Sections, HasPageZeroSegment, I,
"LC_SEGMENT_64", SizeOfHeaders, Elements)))
return;
} else if (Load.C.cmd == MachO::LC_SEGMENT) {
if ((Err = parseSegmentLoadCommand<MachO::segment_command,
MachO::section>(
*this, Load, Sections, HasPageZeroSegment, I,
"LC_SEGMENT", SizeOfHeaders, Elements)))
return;
} else if (Load.C.cmd == MachO::LC_ID_DYLIB) {
if ((Err = checkDylibIdCommand(*this, Load, I, &DyldIdLoadCmd)))
return;
} else if (Load.C.cmd == MachO::LC_LOAD_DYLIB) {
if ((Err = checkDylibCommand(*this, Load, I, "LC_LOAD_DYLIB")))
return;
Libraries.push_back(Load.Ptr);
} else if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB) {
if ((Err = checkDylibCommand(*this, Load, I, "LC_LOAD_WEAK_DYLIB")))
return;
Libraries.push_back(Load.Ptr);
} else if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB) {
if ((Err = checkDylibCommand(*this, Load, I, "LC_LAZY_LOAD_DYLIB")))
return;
Libraries.push_back(Load.Ptr);
} else if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB) {
if ((Err = checkDylibCommand(*this, Load, I, "LC_REEXPORT_DYLIB")))
return;
Libraries.push_back(Load.Ptr);
} else if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) {
if ((Err = checkDylibCommand(*this, Load, I, "LC_LOAD_UPWARD_DYLIB")))
return;
Libraries.push_back(Load.Ptr);
} else if (Load.C.cmd == MachO::LC_ID_DYLINKER) {
if ((Err = checkDyldCommand(*this, Load, I, "LC_ID_DYLINKER")))
return;
} else if (Load.C.cmd == MachO::LC_LOAD_DYLINKER) {
if ((Err = checkDyldCommand(*this, Load, I, "LC_LOAD_DYLINKER")))
return;
} else if (Load.C.cmd == MachO::LC_DYLD_ENVIRONMENT) {
if ((Err = checkDyldCommand(*this, Load, I, "LC_DYLD_ENVIRONMENT")))
return;
} else if (Load.C.cmd == MachO::LC_VERSION_MIN_MACOSX) {
if ((Err = checkVersCommand(*this, Load, I, &VersLoadCmd,
"LC_VERSION_MIN_MACOSX")))
return;
} else if (Load.C.cmd == MachO::LC_VERSION_MIN_IPHONEOS) {
if ((Err = checkVersCommand(*this, Load, I, &VersLoadCmd,
"LC_VERSION_MIN_IPHONEOS")))
return;
} else if (Load.C.cmd == MachO::LC_VERSION_MIN_TVOS) {
if ((Err = checkVersCommand(*this, Load, I, &VersLoadCmd,
"LC_VERSION_MIN_TVOS")))
return;
} else if (Load.C.cmd == MachO::LC_VERSION_MIN_WATCHOS) {
if ((Err = checkVersCommand(*this, Load, I, &VersLoadCmd,
"LC_VERSION_MIN_WATCHOS")))
return;
} else if (Load.C.cmd == MachO::LC_NOTE) {
if ((Err = checkNoteCommand(*this, Load, I, Elements)))
return;
} else if (Load.C.cmd == MachO::LC_BUILD_VERSION) {
if ((Err = parseBuildVersionCommand(*this, Load, BuildTools, I)))
return;
} else if (Load.C.cmd == MachO::LC_RPATH) {
if ((Err = checkRpathCommand(*this, Load, I)))
return;
} else if (Load.C.cmd == MachO::LC_SOURCE_VERSION) {
if (Load.C.cmdsize != sizeof(MachO::source_version_command)) {
Err = malformedError("LC_SOURCE_VERSION command " + Twine(I) +
" has incorrect cmdsize");
return;
}
if (SourceLoadCmd) {
Err = malformedError("more than one LC_SOURCE_VERSION command");
return;
}
SourceLoadCmd = Load.Ptr;
} else if (Load.C.cmd == MachO::LC_MAIN) {
if (Load.C.cmdsize != sizeof(MachO::entry_point_command)) {
Err = malformedError("LC_MAIN command " + Twine(I) +
" has incorrect cmdsize");
return;
}
if (EntryPointLoadCmd) {
Err = malformedError("more than one LC_MAIN command");
return;
}
EntryPointLoadCmd = Load.Ptr;
} else if (Load.C.cmd == MachO::LC_ENCRYPTION_INFO) {
if (Load.C.cmdsize != sizeof(MachO::encryption_info_command)) {
Err = malformedError("LC_ENCRYPTION_INFO command " + Twine(I) +
" has incorrect cmdsize");
return;
}
MachO::encryption_info_command E =
getStruct<MachO::encryption_info_command>(*this, Load.Ptr);
if ((Err = checkEncryptCommand(*this, Load, I, E.cryptoff, E.cryptsize,
&EncryptLoadCmd, "LC_ENCRYPTION_INFO")))
return;
} else if (Load.C.cmd == MachO::LC_ENCRYPTION_INFO_64) {
if (Load.C.cmdsize != sizeof(MachO::encryption_info_command_64)) {
Err = malformedError("LC_ENCRYPTION_INFO_64 command " + Twine(I) +
" has incorrect cmdsize");
return;
}
MachO::encryption_info_command_64 E =
getStruct<MachO::encryption_info_command_64>(*this, Load.Ptr);
if ((Err = checkEncryptCommand(*this, Load, I, E.cryptoff, E.cryptsize,
&EncryptLoadCmd, "LC_ENCRYPTION_INFO_64")))
return;
} else if (Load.C.cmd == MachO::LC_LINKER_OPTION) {
if ((Err = checkLinkerOptCommand(*this, Load, I)))
return;
} else if (Load.C.cmd == MachO::LC_SUB_FRAMEWORK) {
if (Load.C.cmdsize < sizeof(MachO::sub_framework_command)) {
Err = malformedError("load command " + Twine(I) +
" LC_SUB_FRAMEWORK cmdsize too small");
return;
}
MachO::sub_framework_command S =
getStruct<MachO::sub_framework_command>(*this, Load.Ptr);
if ((Err = checkSubCommand(*this, Load, I, "LC_SUB_FRAMEWORK",
sizeof(MachO::sub_framework_command),
"sub_framework_command", S.umbrella,
"umbrella")))
return;
} else if (Load.C.cmd == MachO::LC_SUB_UMBRELLA) {
if (Load.C.cmdsize < sizeof(MachO::sub_umbrella_command)) {
Err = malformedError("load command " + Twine(I) +
" LC_SUB_UMBRELLA cmdsize too small");
return;
}
MachO::sub_umbrella_command S =
getStruct<MachO::sub_umbrella_command>(*this, Load.Ptr);
if ((Err = checkSubCommand(*this, Load, I, "LC_SUB_UMBRELLA",
sizeof(MachO::sub_umbrella_command),
"sub_umbrella_command", S.sub_umbrella,
"sub_umbrella")))
return;
} else if (Load.C.cmd == MachO::LC_SUB_LIBRARY) {
if (Load.C.cmdsize < sizeof(MachO::sub_library_command)) {
Err = malformedError("load command " + Twine(I) +
" LC_SUB_LIBRARY cmdsize too small");
return;
}
MachO::sub_library_command S =
getStruct<MachO::sub_library_command>(*this, Load.Ptr);
if ((Err = checkSubCommand(*this, Load, I, "LC_SUB_LIBRARY",
sizeof(MachO::sub_library_command),
"sub_library_command", S.sub_library,
"sub_library")))
return;
} else if (Load.C.cmd == MachO::LC_SUB_CLIENT) {
if (Load.C.cmdsize < sizeof(MachO::sub_client_command)) {
Err = malformedError("load command " + Twine(I) +
" LC_SUB_CLIENT cmdsize too small");
return;
}
MachO::sub_client_command S =
getStruct<MachO::sub_client_command>(*this, Load.Ptr);
if ((Err = checkSubCommand(*this, Load, I, "LC_SUB_CLIENT",
sizeof(MachO::sub_client_command),
"sub_client_command", S.client, "client")))
return;
} else if (Load.C.cmd == MachO::LC_ROUTINES) {
if (Load.C.cmdsize != sizeof(MachO::routines_command)) {
Err = malformedError("LC_ROUTINES command " + Twine(I) +
" has incorrect cmdsize");
return;
}
if (RoutinesLoadCmd) {
Err = malformedError("more than one LC_ROUTINES and or LC_ROUTINES_64 "
"command");
return;
}
RoutinesLoadCmd = Load.Ptr;
} else if (Load.C.cmd == MachO::LC_ROUTINES_64) {
if (Load.C.cmdsize != sizeof(MachO::routines_command_64)) {
Err = malformedError("LC_ROUTINES_64 command " + Twine(I) +
" has incorrect cmdsize");
return;
}
if (RoutinesLoadCmd) {
Err = malformedError("more than one LC_ROUTINES_64 and or LC_ROUTINES "
"command");
return;
}
RoutinesLoadCmd = Load.Ptr;
} else if (Load.C.cmd == MachO::LC_UNIXTHREAD) {
if ((Err = checkThreadCommand(*this, Load, I, "LC_UNIXTHREAD")))
return;
if (UnixThreadLoadCmd) {
Err = malformedError("more than one LC_UNIXTHREAD command");
return;
}
UnixThreadLoadCmd = Load.Ptr;
} else if (Load.C.cmd == MachO::LC_THREAD) {
if ((Err = checkThreadCommand(*this, Load, I, "LC_THREAD")))
return;
// Note: LC_TWOLEVEL_HINTS is really obsolete and is not supported.
} else if (Load.C.cmd == MachO::LC_TWOLEVEL_HINTS) {
if ((Err = checkTwoLevelHintsCommand(*this, Load, I,
&TwoLevelHintsLoadCmd, Elements)))
return;
} else if (Load.C.cmd == MachO::LC_IDENT) {
// Note: LC_IDENT is ignored.
continue;
} else if (isLoadCommandObsolete(Load.C.cmd)) {
Err = malformedError("load command " + Twine(I) + " for cmd value of: " +
Twine(Load.C.cmd) + " is obsolete and not "
"supported");
return;
}
// TODO: generate a error for unknown load commands by default. But still
// need work out an approach to allow or not allow unknown values like this
// as an option for some uses like lldb.
if (I < LoadCommandCount - 1) {
if (auto LoadOrErr = getNextLoadCommandInfo(*this, I, Load))
Load = *LoadOrErr;
else {
Err = LoadOrErr.takeError();
return;
}
}
}
if (!SymtabLoadCmd) {
if (DysymtabLoadCmd) {
Err = malformedError("contains LC_DYSYMTAB load command without a "
"LC_SYMTAB load command");
return;
}
} else if (DysymtabLoadCmd) {
MachO::symtab_command Symtab =
getStruct<MachO::symtab_command>(*this, SymtabLoadCmd);
MachO::dysymtab_command Dysymtab =
getStruct<MachO::dysymtab_command>(*this, DysymtabLoadCmd);
if (Dysymtab.nlocalsym != 0 && Dysymtab.ilocalsym > Symtab.nsyms) {
Err = malformedError("ilocalsym in LC_DYSYMTAB load command "
"extends past the end of the symbol table");
return;
}
uint64_t BigSize = Dysymtab.ilocalsym;
BigSize += Dysymtab.nlocalsym;
if (Dysymtab.nlocalsym != 0 && BigSize > Symtab.nsyms) {
Err = malformedError("ilocalsym plus nlocalsym in LC_DYSYMTAB load "
"command extends past the end of the symbol table");
return;
}
if (Dysymtab.nextdefsym != 0 && Dysymtab.iextdefsym > Symtab.nsyms) {
Err = malformedError("iextdefsym in LC_DYSYMTAB load command "
"extends past the end of the symbol table");
return;
}
BigSize = Dysymtab.iextdefsym;
BigSize += Dysymtab.nextdefsym;
if (Dysymtab.nextdefsym != 0 && BigSize > Symtab.nsyms) {
Err = malformedError("iextdefsym plus nextdefsym in LC_DYSYMTAB "
"load command extends past the end of the symbol "
"table");
return;
}
if (Dysymtab.nundefsym != 0 && Dysymtab.iundefsym > Symtab.nsyms) {
Err = malformedError("iundefsym in LC_DYSYMTAB load command "
"extends past the end of the symbol table");
return;
}
BigSize = Dysymtab.iundefsym;
BigSize += Dysymtab.nundefsym;
if (Dysymtab.nundefsym != 0 && BigSize > Symtab.nsyms) {
Err = malformedError("iundefsym plus nundefsym in LC_DYSYMTAB load "
" command extends past the end of the symbol table");
return;
}
}
if ((getHeader().filetype == MachO::MH_DYLIB ||
getHeader().filetype == MachO::MH_DYLIB_STUB) &&
DyldIdLoadCmd == nullptr) {
Err = malformedError("no LC_ID_DYLIB load command in dynamic library "
"filetype");
return;
}
assert(LoadCommands.size() == LoadCommandCount);
Err = Error::success();
}
Error MachOObjectFile::checkSymbolTable() const {
uint32_t Flags = 0;
if (is64Bit()) {
MachO::mach_header_64 H_64 = MachOObjectFile::getHeader64();
Flags = H_64.flags;
} else {
MachO::mach_header H = MachOObjectFile::getHeader();
Flags = H.flags;
}
uint8_t NType = 0;
uint8_t NSect = 0;
uint16_t NDesc = 0;
uint32_t NStrx = 0;
uint64_t NValue = 0;
uint32_t SymbolIndex = 0;
MachO::symtab_command S = getSymtabLoadCommand();
for (const SymbolRef &Symbol : symbols()) {
DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
if (is64Bit()) {
MachO::nlist_64 STE_64 = getSymbol64TableEntry(SymDRI);
NType = STE_64.n_type;
NSect = STE_64.n_sect;
NDesc = STE_64.n_desc;
NStrx = STE_64.n_strx;
NValue = STE_64.n_value;
} else {
MachO::nlist STE = getSymbolTableEntry(SymDRI);
NType = STE.n_type;
NSect = STE.n_sect;
NDesc = STE.n_desc;
NStrx = STE.n_strx;
NValue = STE.n_value;
}
if ((NType & MachO::N_STAB) == 0) {
if ((NType & MachO::N_TYPE) == MachO::N_SECT) {
if (NSect == 0 || NSect > Sections.size())
return malformedError("bad section index: " + Twine((int)NSect) +
" for symbol at index " + Twine(SymbolIndex));
}
if ((NType & MachO::N_TYPE) == MachO::N_INDR) {
if (NValue >= S.strsize)
return malformedError("bad n_value: " + Twine((int)NValue) + " past "
"the end of string table, for N_INDR symbol at "
"index " + Twine(SymbolIndex));
}
if ((Flags & MachO::MH_TWOLEVEL) == MachO::MH_TWOLEVEL &&
(((NType & MachO::N_TYPE) == MachO::N_UNDF && NValue == 0) ||
(NType & MachO::N_TYPE) == MachO::N_PBUD)) {
uint32_t LibraryOrdinal = MachO::GET_LIBRARY_ORDINAL(NDesc);
if (LibraryOrdinal != 0 &&
LibraryOrdinal != MachO::EXECUTABLE_ORDINAL &&
LibraryOrdinal != MachO::DYNAMIC_LOOKUP_ORDINAL &&
LibraryOrdinal - 1 >= Libraries.size() ) {
return malformedError("bad library ordinal: " + Twine(LibraryOrdinal) +
" for symbol at index " + Twine(SymbolIndex));
}
}
}
if (NStrx >= S.strsize)
return malformedError("bad string table index: " + Twine((int)NStrx) +
" past the end of string table, for symbol at "
"index " + Twine(SymbolIndex));
SymbolIndex++;
}
return Error::success();
}
void MachOObjectFile::moveSymbolNext(DataRefImpl &Symb) const {
unsigned SymbolTableEntrySize = is64Bit() ?
sizeof(MachO::nlist_64) :
sizeof(MachO::nlist);
Symb.p += SymbolTableEntrySize;
}
Expected<StringRef> MachOObjectFile::getSymbolName(DataRefImpl Symb) const {
StringRef StringTable = getStringTableData();
MachO::nlist_base Entry = getSymbolTableEntryBase(*this, Symb);
if (Entry.n_strx == 0)
// A n_strx value of 0 indicates that no name is associated with a
// particular symbol table entry.
return StringRef();
const char *Start = &StringTable.data()[Entry.n_strx];
if (Start < getData().begin() || Start >= getData().end()) {
return malformedError("bad string index: " + Twine(Entry.n_strx) +
" for symbol at index " + Twine(getSymbolIndex(Symb)));
}
return StringRef(Start);
}
unsigned MachOObjectFile::getSectionType(SectionRef Sec) const {
DataRefImpl DRI = Sec.getRawDataRefImpl();
uint32_t Flags = getSectionFlags(*this, DRI);
return Flags & MachO::SECTION_TYPE;
}
uint64_t MachOObjectFile::getNValue(DataRefImpl Sym) const {
if (is64Bit()) {
MachO::nlist_64 Entry = getSymbol64TableEntry(Sym);
return Entry.n_value;
}
MachO::nlist Entry = getSymbolTableEntry(Sym);
return Entry.n_value;
}
// getIndirectName() returns the name of the alias'ed symbol who's string table
// index is in the n_value field.
std::error_code MachOObjectFile::getIndirectName(DataRefImpl Symb,
StringRef &Res) const {
StringRef StringTable = getStringTableData();
MachO::nlist_base Entry = getSymbolTableEntryBase(*this, Symb);
if ((Entry.n_type & MachO::N_TYPE) != MachO::N_INDR)
return object_error::parse_failed;
uint64_t NValue = getNValue(Symb);
if (NValue >= StringTable.size())
return object_error::parse_failed;
const char *Start = &StringTable.data()[NValue];
Res = StringRef(Start);
return std::error_code();
}
uint64_t MachOObjectFile::getSymbolValueImpl(DataRefImpl Sym) const {
return getNValue(Sym);
}
Expected<uint64_t> MachOObjectFile::getSymbolAddress(DataRefImpl Sym) const {
return getSymbolValue(Sym);
}
uint32_t MachOObjectFile::getSymbolAlignment(DataRefImpl DRI) const {
uint32_t Flags = cantFail(getSymbolFlags(DRI));
if (Flags & SymbolRef::SF_Common) {
MachO::nlist_base Entry = getSymbolTableEntryBase(*this, DRI);
return 1 << MachO::GET_COMM_ALIGN(Entry.n_desc);
}
return 0;
}
uint64_t MachOObjectFile::getCommonSymbolSizeImpl(DataRefImpl DRI) const {
return getNValue(DRI);
}
Expected<SymbolRef::Type>
MachOObjectFile::getSymbolType(DataRefImpl Symb) const {
MachO::nlist_base Entry = getSymbolTableEntryBase(*this, Symb);
uint8_t n_type = Entry.n_type;
// If this is a STAB debugging symbol, we can do nothing more.
if (n_type & MachO::N_STAB)
return SymbolRef::ST_Debug;
switch (n_type & MachO::N_TYPE) {
case MachO::N_UNDF :
return SymbolRef::ST_Unknown;
case MachO::N_SECT :
Expected<section_iterator> SecOrError = getSymbolSection(Symb);
if (!SecOrError)
return SecOrError.takeError();
section_iterator Sec = *SecOrError;
if (Sec == section_end())
return SymbolRef::ST_Other;
if (Sec->isData() || Sec->isBSS())
return SymbolRef::ST_Data;
return SymbolRef::ST_Function;
}
return SymbolRef::ST_Other;
}
Expected<uint32_t> MachOObjectFile::getSymbolFlags(DataRefImpl DRI) const {
MachO::nlist_base Entry = getSymbolTableEntryBase(*this, DRI);
uint8_t MachOType = Entry.n_type;
uint16_t MachOFlags = Entry.n_desc;
uint32_t Result = SymbolRef::SF_None;
if ((MachOType & MachO::N_TYPE) == MachO::N_INDR)
Result |= SymbolRef::SF_Indirect;
if (MachOType & MachO::N_STAB)
Result |= SymbolRef::SF_FormatSpecific;
if (MachOType & MachO::N_EXT) {
Result |= SymbolRef::SF_Global;
if ((MachOType & MachO::N_TYPE) == MachO::N_UNDF) {
if (getNValue(DRI))
Result |= SymbolRef::SF_Common;
else
Result |= SymbolRef::SF_Undefined;
}
if (!(MachOType & MachO::N_PEXT))
Result |= SymbolRef::SF_Exported;
}
if (MachOFlags & (MachO::N_WEAK_REF | MachO::N_WEAK_DEF))
Result |= SymbolRef::SF_Weak;
if (MachOFlags & (MachO::N_ARM_THUMB_DEF))
Result |= SymbolRef::SF_Thumb;
if ((MachOType & MachO::N_TYPE) == MachO::N_ABS)
Result |= SymbolRef::SF_Absolute;
return Result;
}
Expected<section_iterator>
MachOObjectFile::getSymbolSection(DataRefImpl Symb) const {
MachO::nlist_base Entry = getSymbolTableEntryBase(*this, Symb);
uint8_t index = Entry.n_sect;
if (index == 0)
return section_end();
DataRefImpl DRI;
DRI.d.a = index - 1;
if (DRI.d.a >= Sections.size()){
return malformedError("bad section index: " + Twine((int)index) +
" for symbol at index " + Twine(getSymbolIndex(Symb)));
}
return section_iterator(SectionRef(DRI, this));
}
unsigned MachOObjectFile::getSymbolSectionID(SymbolRef Sym) const {
MachO::nlist_base Entry =
getSymbolTableEntryBase(*this, Sym.getRawDataRefImpl());
return Entry.n_sect - 1;
}
void MachOObjectFile::moveSectionNext(DataRefImpl &Sec) const {
Sec.d.a++;
}
Expected<StringRef> MachOObjectFile::getSectionName(DataRefImpl Sec) const {
ArrayRef<char> Raw = getSectionRawName(Sec);
return parseSegmentOrSectionName(Raw.data());
}
uint64_t MachOObjectFile::getSectionAddress(DataRefImpl Sec) const {
if (is64Bit())
return getSection64(Sec).addr;
return getSection(Sec).addr;
}
uint64_t MachOObjectFile::getSectionIndex(DataRefImpl Sec) const {
return Sec.d.a;
}
uint64_t MachOObjectFile::getSectionSize(DataRefImpl Sec) const {
// In the case if a malformed Mach-O file where the section offset is past
// the end of the file or some part of the section size is past the end of
// the file return a size of zero or a size that covers the rest of the file
// but does not extend past the end of the file.
uint32_t SectOffset, SectType;
uint64_t SectSize;
if (is64Bit()) {
MachO::section_64 Sect = getSection64(Sec);
SectOffset = Sect.offset;
SectSize = Sect.size;
SectType = Sect.flags & MachO::SECTION_TYPE;
} else {
MachO::section Sect = getSection(Sec);
SectOffset = Sect.offset;
SectSize = Sect.size;
SectType = Sect.flags & MachO::SECTION_TYPE;
}
if (SectType == MachO::S_ZEROFILL || SectType == MachO::S_GB_ZEROFILL)
return SectSize;
uint64_t FileSize = getData().size();
if (SectOffset > FileSize)
return 0;
if (FileSize - SectOffset < SectSize)
return FileSize - SectOffset;
return SectSize;
}
ArrayRef<uint8_t> MachOObjectFile::getSectionContents(uint32_t Offset,
uint64_t Size) const {
return arrayRefFromStringRef(getData().substr(Offset, Size));
}
Expected<ArrayRef<uint8_t>>
MachOObjectFile::getSectionContents(DataRefImpl Sec) const {
uint32_t Offset;
uint64_t Size;
if (is64Bit()) {
MachO::section_64 Sect = getSection64(Sec);
Offset = Sect.offset;
Size = Sect.size;
} else {
MachO::section Sect = getSection(Sec);
Offset = Sect.offset;
Size = Sect.size;
}
return getSectionContents(Offset, Size);
}
uint64_t MachOObjectFile::getSectionAlignment(DataRefImpl Sec) const {
uint32_t Align;
if (is64Bit()) {
MachO::section_64 Sect = getSection64(Sec);
Align = Sect.align;
} else {
MachO::section Sect = getSection(Sec);
Align = Sect.align;
}
return uint64_t(1) << Align;
}
Expected<SectionRef> MachOObjectFile::getSection(unsigned SectionIndex) const {
if (SectionIndex < 1 || SectionIndex > Sections.size())
return malformedError("bad section index: " + Twine((int)SectionIndex));
DataRefImpl DRI;
DRI.d.a = SectionIndex - 1;
return SectionRef(DRI, this);
}
Expected<SectionRef> MachOObjectFile::getSection(StringRef SectionName) const {
for (const SectionRef &Section : sections()) {
auto NameOrErr = Section.getName();
if (!NameOrErr)
return NameOrErr.takeError();
if (*NameOrErr == SectionName)
return Section;
}
return errorCodeToError(object_error::parse_failed);
}
bool MachOObjectFile::isSectionCompressed(DataRefImpl Sec) const {
return false;
}
bool MachOObjectFile::isSectionText(DataRefImpl Sec) const {
uint32_t Flags = getSectionFlags(*this, Sec);
return Flags & MachO::S_ATTR_PURE_INSTRUCTIONS;
}
bool MachOObjectFile::isSectionData(DataRefImpl Sec) const {
uint32_t Flags = getSectionFlags(*this, Sec);
unsigned SectionType = Flags & MachO::SECTION_TYPE;
return !(Flags & MachO::S_ATTR_PURE_INSTRUCTIONS) &&
!(SectionType == MachO::S_ZEROFILL ||
SectionType == MachO::S_GB_ZEROFILL);
}
bool MachOObjectFile::isSectionBSS(DataRefImpl Sec) const {
uint32_t Flags = getSectionFlags(*this, Sec);
unsigned SectionType = Flags & MachO::SECTION_TYPE;
return !(Flags & MachO::S_ATTR_PURE_INSTRUCTIONS) &&
(SectionType == MachO::S_ZEROFILL ||
SectionType == MachO::S_GB_ZEROFILL);
}
bool MachOObjectFile::isDebugSection(DataRefImpl Sec) const {
Expected<StringRef> SectionNameOrErr = getSectionName(Sec);
if (!SectionNameOrErr) {
// TODO: Report the error message properly.
consumeError(SectionNameOrErr.takeError());
return false;
}
StringRef SectionName = SectionNameOrErr.get();
return SectionName.startswith("__debug") ||
SectionName.startswith("__zdebug") ||
SectionName.startswith("__apple") || SectionName == "__gdb_index" ||
SectionName == "__swift_ast";
}
namespace {
template <typename LoadCommandType>
ArrayRef<uint8_t> getSegmentContents(const MachOObjectFile &Obj,
MachOObjectFile::LoadCommandInfo LoadCmd,
StringRef SegmentName) {
auto SegmentOrErr = getStructOrErr<LoadCommandType>(Obj, LoadCmd.Ptr);
if (!SegmentOrErr) {
consumeError(SegmentOrErr.takeError());
return {};
}
auto &Segment = SegmentOrErr.get();
if (StringRef(Segment.segname, 16).startswith(SegmentName))
return arrayRefFromStringRef(Obj.getData().slice(
Segment.fileoff, Segment.fileoff + Segment.filesize));
return {};
}
} // namespace
ArrayRef<uint8_t>
MachOObjectFile::getSegmentContents(StringRef SegmentName) const {
for (auto LoadCmd : load_commands()) {
ArrayRef<uint8_t> Contents;
switch (LoadCmd.C.cmd) {
case MachO::LC_SEGMENT:
Contents = ::getSegmentContents<MachO::segment_command>(*this, LoadCmd,
SegmentName);
break;
case MachO::LC_SEGMENT_64:
Contents = ::getSegmentContents<MachO::segment_command_64>(*this, LoadCmd,
SegmentName);
break;
default:
continue;
}
if (!Contents.empty())
return Contents;
}
return {};
}
unsigned MachOObjectFile::getSectionID(SectionRef Sec) const {
return Sec.getRawDataRefImpl().d.a;
}
bool MachOObjectFile::isSectionVirtual(DataRefImpl Sec) const {
uint32_t Flags = getSectionFlags(*this, Sec);
unsigned SectionType = Flags & MachO::SECTION_TYPE;
return SectionType == MachO::S_ZEROFILL ||
SectionType == MachO::S_GB_ZEROFILL;
}
bool MachOObjectFile::isSectionBitcode(DataRefImpl Sec) const {
StringRef SegmentName = getSectionFinalSegmentName(Sec);
if (Expected<StringRef> NameOrErr = getSectionName(Sec))
return (SegmentName == "__LLVM" && *NameOrErr == "__bitcode");
return false;
}
bool MachOObjectFile::isSectionStripped(DataRefImpl Sec) const {
if (is64Bit())
return getSection64(Sec).offset == 0;
return getSection(Sec).offset == 0;
}
relocation_iterator MachOObjectFile::section_rel_begin(DataRefImpl Sec) const {
DataRefImpl Ret;
Ret.d.a = Sec.d.a;
Ret.d.b = 0;
return relocation_iterator(RelocationRef(Ret, this));
}
relocation_iterator
MachOObjectFile::section_rel_end(DataRefImpl Sec) const {
uint32_t Num;
if (is64Bit()) {
MachO::section_64 Sect = getSection64(Sec);
Num = Sect.nreloc;
} else {
MachO::section Sect = getSection(Sec);
Num = Sect.nreloc;
}
DataRefImpl Ret;
Ret.d.a = Sec.d.a;
Ret.d.b = Num;
return relocation_iterator(RelocationRef(Ret, this));
}
relocation_iterator MachOObjectFile::extrel_begin() const {
DataRefImpl Ret;
// for DYSYMTAB symbols, Ret.d.a == 0 for external relocations
Ret.d.a = 0; // Would normally be a section index.
Ret.d.b = 0; // Index into the external relocations
return relocation_iterator(RelocationRef(Ret, this));
}
relocation_iterator MachOObjectFile::extrel_end() const {
MachO::dysymtab_command DysymtabLoadCmd = getDysymtabLoadCommand();
DataRefImpl Ret;
// for DYSYMTAB symbols, Ret.d.a == 0 for external relocations
Ret.d.a = 0; // Would normally be a section index.
Ret.d.b = DysymtabLoadCmd.nextrel; // Index into the external relocations
return relocation_iterator(RelocationRef(Ret, this));
}
relocation_iterator MachOObjectFile::locrel_begin() const {
DataRefImpl Ret;
// for DYSYMTAB symbols, Ret.d.a == 1 for local relocations
Ret.d.a = 1; // Would normally be a section index.
Ret.d.b = 0; // Index into the local relocations
return relocation_iterator(RelocationRef(Ret, this));
}
relocation_iterator MachOObjectFile::locrel_end() const {
MachO::dysymtab_command DysymtabLoadCmd = getDysymtabLoadCommand();
DataRefImpl Ret;
// for DYSYMTAB symbols, Ret.d.a == 1 for local relocations
Ret.d.a = 1; // Would normally be a section index.
Ret.d.b = DysymtabLoadCmd.nlocrel; // Index into the local relocations
return relocation_iterator(RelocationRef(Ret, this));
}
void MachOObjectFile::moveRelocationNext(DataRefImpl &Rel) const {
++Rel.d.b;
}
uint64_t MachOObjectFile::getRelocationOffset(DataRefImpl Rel) const {
assert((getHeader().filetype == MachO::MH_OBJECT ||
getHeader().filetype == MachO::MH_KEXT_BUNDLE) &&
"Only implemented for MH_OBJECT && MH_KEXT_BUNDLE");
MachO::any_relocation_info RE = getRelocation(Rel);
return getAnyRelocationAddress(RE);
}
symbol_iterator
MachOObjectFile::getRelocationSymbol(DataRefImpl Rel) const {
MachO::any_relocation_info RE = getRelocation(Rel);
if (isRelocationScattered(RE))
return symbol_end();
uint32_t SymbolIdx = getPlainRelocationSymbolNum(RE);
bool isExtern = getPlainRelocationExternal(RE);
if (!isExtern)
return symbol_end();
MachO::symtab_command S = getSymtabLoadCommand();
unsigned SymbolTableEntrySize = is64Bit() ?
sizeof(MachO::nlist_64) :
sizeof(MachO::nlist);
uint64_t Offset = S.symoff + SymbolIdx * SymbolTableEntrySize;
DataRefImpl Sym;
Sym.p = reinterpret_cast<uintptr_t>(getPtr(*this, Offset));
return symbol_iterator(SymbolRef(Sym, this));
}
section_iterator
MachOObjectFile::getRelocationSection(DataRefImpl Rel) const {
return section_iterator(getAnyRelocationSection(getRelocation(Rel)));
}
uint64_t MachOObjectFile::getRelocationType(DataRefImpl Rel) const {
MachO::any_relocation_info RE = getRelocation(Rel);
return getAnyRelocationType(RE);
}
void MachOObjectFile::getRelocationTypeName(
DataRefImpl Rel, SmallVectorImpl<char> &Result) const {
StringRef res;
uint64_t RType = getRelocationType(Rel);
unsigned Arch = this->getArch();
switch (Arch) {
case Triple::x86: {
static const char *const Table[] = {
"GENERIC_RELOC_VANILLA",
"GENERIC_RELOC_PAIR",
"GENERIC_RELOC_SECTDIFF",
"GENERIC_RELOC_PB_LA_PTR",
"GENERIC_RELOC_LOCAL_SECTDIFF",
"GENERIC_RELOC_TLV" };
if (RType > 5)
res = "Unknown";
else
res = Table[RType];
break;
}
case Triple::x86_64: {
static const char *const Table[] = {
"X86_64_RELOC_UNSIGNED",
"X86_64_RELOC_SIGNED",
"X86_64_RELOC_BRANCH",
"X86_64_RELOC_GOT_LOAD",
"X86_64_RELOC_GOT",
"X86_64_RELOC_SUBTRACTOR",
"X86_64_RELOC_SIGNED_1",
"X86_64_RELOC_SIGNED_2",
"X86_64_RELOC_SIGNED_4",
"X86_64_RELOC_TLV" };
if (RType > 9)
res = "Unknown";
else
res = Table[RType];
break;
}
case Triple::arm: {
static const char *const Table[] = {
"ARM_RELOC_VANILLA",
"ARM_RELOC_PAIR",
"ARM_RELOC_SECTDIFF",
"ARM_RELOC_LOCAL_SECTDIFF",
"ARM_RELOC_PB_LA_PTR",
"ARM_RELOC_BR24",
"ARM_THUMB_RELOC_BR22",
"ARM_THUMB_32BIT_BRANCH",
"ARM_RELOC_HALF",
"ARM_RELOC_HALF_SECTDIFF" };
if (RType > 9)
res = "Unknown";
else
res = Table[RType];
break;
}
case Triple::aarch64:
case Triple::aarch64_32: {
static const char *const Table[] = {
"ARM64_RELOC_UNSIGNED", "ARM64_RELOC_SUBTRACTOR",
"ARM64_RELOC_BRANCH26", "ARM64_RELOC_PAGE21",
"ARM64_RELOC_PAGEOFF12", "ARM64_RELOC_GOT_LOAD_PAGE21",
"ARM64_RELOC_GOT_LOAD_PAGEOFF12", "ARM64_RELOC_POINTER_TO_GOT",
"ARM64_RELOC_TLVP_LOAD_PAGE21", "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
"ARM64_RELOC_ADDEND"
};
if (RType >= array_lengthof(Table))
res = "Unknown";
else
res = Table[RType];
break;
}
case Triple::ppc: {
static const char *const Table[] = {
"PPC_RELOC_VANILLA",
"PPC_RELOC_PAIR",
"PPC_RELOC_BR14",
"PPC_RELOC_BR24",
"PPC_RELOC_HI16",
"PPC_RELOC_LO16",
"PPC_RELOC_HA16",
"PPC_RELOC_LO14",
"PPC_RELOC_SECTDIFF",
"PPC_RELOC_PB_LA_PTR",
"PPC_RELOC_HI16_SECTDIFF",
"PPC_RELOC_LO16_SECTDIFF",
"PPC_RELOC_HA16_SECTDIFF",
"PPC_RELOC_JBSR",
"PPC_RELOC_LO14_SECTDIFF",
"PPC_RELOC_LOCAL_SECTDIFF" };
if (RType > 15)
res = "Unknown";
else
res = Table[RType];
break;
}
case Triple::UnknownArch:
res = "Unknown";
break;
}
Result.append(res.begin(), res.end());
}
uint8_t MachOObjectFile::getRelocationLength(DataRefImpl Rel) const {
MachO::any_relocation_info RE = getRelocation(Rel);
return getAnyRelocationLength(RE);
}
//
// guessLibraryShortName() is passed a name of a dynamic library and returns a
// guess on what the short name is. Then name is returned as a substring of the
// StringRef Name passed in. The name of the dynamic library is recognized as
// a framework if it has one of the two following forms:
// Foo.framework/Versions/A/Foo
// Foo.framework/Foo
// Where A and Foo can be any string. And may contain a trailing suffix
// starting with an underbar. If the Name is recognized as a framework then
// isFramework is set to true else it is set to false. If the Name has a
// suffix then Suffix is set to the substring in Name that contains the suffix
// else it is set to a NULL StringRef.
//
// The Name of the dynamic library is recognized as a library name if it has
// one of the two following forms:
// libFoo.A.dylib
// libFoo.dylib
//
// The library may have a suffix trailing the name Foo of the form:
// libFoo_profile.A.dylib
// libFoo_profile.dylib
// These dyld image suffixes are separated from the short name by a '_'
// character. Because the '_' character is commonly used to separate words in
// filenames guessLibraryShortName() cannot reliably separate a dylib's short
// name from an arbitrary image suffix; imagine if both the short name and the
// suffix contains an '_' character! To better deal with this ambiguity,
// guessLibraryShortName() will recognize only "_debug" and "_profile" as valid
// Suffix values. Calling code needs to be tolerant of guessLibraryShortName()
// guessing incorrectly.
//
// The Name of the dynamic library is also recognized as a library name if it
// has the following form:
// Foo.qtx
//
// If the Name of the dynamic library is none of the forms above then a NULL
// StringRef is returned.
StringRef MachOObjectFile::guessLibraryShortName(StringRef Name,
bool &isFramework,
StringRef &Suffix) {
StringRef Foo, F, DotFramework, V, Dylib, Lib, Dot, Qtx;
size_t a, b, c, d, Idx;
isFramework = false;
Suffix = StringRef();
// Pull off the last component and make Foo point to it
a = Name.rfind('/');
if (a == Name.npos || a == 0)
goto guess_library;
Foo = Name.slice(a+1, Name.npos);
// Look for a suffix starting with a '_'
Idx = Foo.rfind('_');
if (Idx != Foo