161 changes: 158 additions & 3 deletions llvm/lib/Object/MachOObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,11 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
if ((Err = checkDyldInfoCommand(*this, Load, I, &DyldInfoLoadCmd,
"LC_DYLD_INFO_ONLY", Elements)))
return;
} else if (Load.C.cmd == MachO::LC_DYLD_CHAINED_FIXUPS) {
if ((Err = checkLinkeditDataCommand(
*this, Load, I, &DyldChainedFixupsLoadCmd,
"LC_DYLD_CHAINED_FIXUPS", Elements, "chained fixups")))
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 "
Expand Down Expand Up @@ -1595,9 +1600,9 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
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;
if ((Err = checkTwoLevelHintsCommand(*this, Load, I,
&TwoLevelHintsLoadCmd, Elements)))
return;
} else if (Load.C.cmd == MachO::LC_IDENT) {
// Note: LC_IDENT is ignored.
continue;
Expand Down Expand Up @@ -3185,6 +3190,106 @@ iterator_range<export_iterator> MachOObjectFile::exports(Error &Err) const {
return exports(Err, getDyldInfoExportsTrie(), this);
}

MachOAbstractFixupEntry::MachOAbstractFixupEntry(Error *E,
const MachOObjectFile *O)
: E(E), O(O) {
// Cache the vmaddress of __TEXT
for (const auto &Command : O->load_commands()) {
if (Command.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command SLC = O->getSegmentLoadCommand(Command);
if (StringRef(SLC.segname) == StringRef("__TEXT")) {
TextAddress = SLC.vmaddr;
break;
}
} else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
MachO::segment_command_64 SLC_64 = O->getSegment64LoadCommand(Command);
if (StringRef(SLC_64.segname) == StringRef("__TEXT")) {
TextAddress = SLC_64.vmaddr;
break;
}
}
}
}

int32_t MachOAbstractFixupEntry::segmentIndex() const { return SegmentIndex; }

uint64_t MachOAbstractFixupEntry::segmentOffset() const {
return SegmentOffset;
}

uint64_t MachOAbstractFixupEntry::segmentAddress() const {
return O->BindRebaseAddress(SegmentIndex, 0);
}

StringRef MachOAbstractFixupEntry::segmentName() const {
return O->BindRebaseSegmentName(SegmentIndex);
}

StringRef MachOAbstractFixupEntry::sectionName() const {
return O->BindRebaseSectionName(SegmentIndex, SegmentOffset);
}

uint64_t MachOAbstractFixupEntry::address() const {
return O->BindRebaseAddress(SegmentIndex, SegmentOffset);
}

StringRef MachOAbstractFixupEntry::symbolName() const { return SymbolName; }

int64_t MachOAbstractFixupEntry::addend() const { return Addend; }

uint32_t MachOAbstractFixupEntry::flags() const { return Flags; }

int MachOAbstractFixupEntry::ordinal() const { return Ordinal; }

StringRef MachOAbstractFixupEntry::typeName() const { return "unknown"; }

void MachOAbstractFixupEntry::moveToFirst() {
SegmentOffset = 0;
SegmentIndex = -1;
Ordinal = 0;
Flags = 0;
Addend = 0;
Done = false;
}

void MachOAbstractFixupEntry::moveToEnd() { Done = true; }

MachOChainedFixupEntry::MachOChainedFixupEntry(Error *E,
const MachOObjectFile *O,
FixupKind Kind, bool Parse)
: MachOAbstractFixupEntry(E, O), Kind(Kind) {
ErrorAsOutParameter e(E);
if (Parse) {
if (auto FixupTargetsOrErr = O->getDyldChainedFixupTargets())
FixupTargets = *FixupTargetsOrErr;
else {
*E = FixupTargetsOrErr.takeError();
return;
}
}
}

void MachOChainedFixupEntry::moveToFirst() {
MachOAbstractFixupEntry::moveToFirst();
FixupIndex = 0;
moveNext();
}

void MachOChainedFixupEntry::moveToEnd() {
MachOAbstractFixupEntry::moveToEnd();
}

void MachOChainedFixupEntry::moveNext() { Done = true; }

bool MachOChainedFixupEntry::operator==(
const MachOChainedFixupEntry &Other) const {
if (Done == Other.Done)
return true;
if ((FixupIndex == Other.FixupIndex))
return true;
return false;
}

MachORebaseEntry::MachORebaseEntry(Error *E, const MachOObjectFile *O,
ArrayRef<uint8_t> Bytes, bool is64Bit)
: E(E), O(O), Opcodes(Bytes), Ptr(Bytes.begin()),
Expand Down Expand Up @@ -4193,6 +4298,18 @@ iterator_range<bind_iterator> MachOObjectFile::weakBindTable(Error &Err) {
MachOBindEntry::Kind::Weak);
}

iterator_range<fixup_iterator>
MachOObjectFile::fixupTable(Error &Err,
MachOChainedFixupEntry::FixupKind Kind) {
MachOChainedFixupEntry Start(&Err, this, Kind, true);
Start.moveToFirst();

MachOChainedFixupEntry Finish(&Err, this, Kind, false);
Finish.moveToEnd();

return make_range(fixup_iterator(Start), fixup_iterator(Finish));
}

MachOObjectFile::load_command_iterator
MachOObjectFile::begin_load_commands() const {
return LoadCommands.begin();
Expand Down Expand Up @@ -4648,6 +4765,44 @@ ArrayRef<uint8_t> MachOObjectFile::getDyldInfoLazyBindOpcodes() const {
return makeArrayRef(Ptr, DyldInfo.lazy_bind_size);
}

Expected<std::vector<ChainedFixupTarget>>
MachOObjectFile::getDyldChainedFixupTargets() const {
// Load the dyld chained fixups load command.
if (!DyldChainedFixupsLoadCmd)
return std::vector<ChainedFixupTarget>();
auto DyldChainedFixupsOrErr = getStructOrErr<MachO::linkedit_data_command>(
*this, DyldChainedFixupsLoadCmd);
if (!DyldChainedFixupsOrErr)
return DyldChainedFixupsOrErr.takeError();
MachO::linkedit_data_command DyldChainedFixups = DyldChainedFixupsOrErr.get();

// If the load command is present but the data offset has been zeroed out,
// as is the case for dylib stubs, return an empty list of targets.
uint64_t CFHeaderOffset = DyldChainedFixups.dataoff;
std::vector<ChainedFixupTarget> Targets;
if (CFHeaderOffset == 0)
return Targets;

// Load the dyld chained fixups header.
const char *CFHeaderPtr = getPtr(*this, CFHeaderOffset);
auto CFHeaderOrErr =
getStructOrErr<MachO::dyld_chained_fixups_header>(*this, CFHeaderPtr);
if (!CFHeaderOrErr)
return CFHeaderOrErr.takeError();
MachO::dyld_chained_fixups_header CFHeader = CFHeaderOrErr.get();

// Reject unknown chained fixup formats.
if (CFHeader.fixups_version != 0)
return malformedError(Twine("bad chained fixups: unknown version: ") +
Twine(CFHeader.fixups_version));
if (CFHeader.imports_format < 1 || CFHeader.imports_format > 3)
return malformedError(
Twine("bad chained fixups: unknown imports format: ") +
Twine(CFHeader.imports_format));

return Targets;
}

ArrayRef<uint8_t> MachOObjectFile::getDyldInfoExportsTrie() const {
if (!DyldInfoLoadCmd)
return None;
Expand Down
11 changes: 11 additions & 0 deletions llvm/test/Object/AArch64/chained-fixups-header.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
RUN: cat %p/../Inputs/MachO/chained-fixups.yaml \
RUN: | sed 's/__LINKEDIT: 00000000/__LINKEDIT: AB000000/' \
RUN: | yaml2obj | not llvm-objdump --macho --dyld_info - 2>&1 \
RUN: | FileCheck %s --check-prefix=HEADER1
HEADER1: truncated or malformed object (bad chained fixups: unknown version: 171)

RUN: cat %p/../Inputs/MachO/chained-fixups.yaml \
RUN: | sed 's/1000000010000000/1000000AB0000000/' \
RUN: | yaml2obj | not llvm-objdump --macho --dyld_info - 2>&1 \
RUN: | FileCheck %s --check-prefix=HEADER2
HEADER2: truncated or malformed object (bad chained fixups: unknown imports format: 171)
173 changes: 173 additions & 0 deletions llvm/test/Object/Inputs/MachO/chained-fixups.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# This file was produced using:
# echo "int ext;" > a.c
# xcrun --sdk iphoneos clang -target arm64-apple-ios15.1 -o a.o a.c -c
# xcrun --sdk iphoneos clang -target arm64-apple-ios15.1 -dynamiclib a.o -o liba.dylib -install_name @executable_path/liba.dylib
# echo "extern int ext;" > b.c
# echo "int padding;" >> b.c
# echo "int *p = &ext + 4;" >> b.c
# xcrun --sdk iphoneos clang -target arm64-apple-ios15.1 -o b.o b.c -c
# xcrun --sdk iphoneos clang -target arm64-apple-ios15.1 -dynamiclib b.o -o libfixups.dylib -install_name @executable_path/libfixups.dylib -L. -la
--- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x100000C
cpusubtype: 0x0
filetype: 0x6
ncmds: 16
sizeofcmds: 816
flags: 0x100085
reserved: 0x0
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __TEXT
vmaddr: 0
vmsize: 16384
fileoff: 0
filesize: 16384
maxprot: 5
initprot: 5
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x4000
size: 0
offset: 0x4000
align: 0
reloff: 0x0
nreloc: 0
flags: 0x80000400
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
content: ''
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __DATA
vmaddr: 16384
vmsize: 16384
fileoff: 16384
filesize: 16384
maxprot: 3
initprot: 3
nsects: 1
flags: 0
Sections:
- sectname: __data
segname: __DATA
addr: 0x4000
size: 8
offset: 0x4000
align: 3
reloff: 0x0
nreloc: 0
flags: 0x0
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
content: '0000001000000080'
- cmd: LC_SEGMENT_64
cmdsize: 72
segname: __LINKEDIT
vmaddr: 32768
vmsize: 16384
fileoff: 32768
filesize: 160
maxprot: 1
initprot: 1
nsects: 0
flags: 0
- cmd: LC_ID_DYLIB
cmdsize: 64
dylib:
name: 24
timestamp: 1
current_version: 0
compatibility_version: 0
Content: '@executable_path/libfixups.dylib'
ZeroPadBytes: 8
- cmd: LC_DYLD_CHAINED_FIXUPS
cmdsize: 16
dataoff: 32768
datasize: 88
- cmd: LC_DYLD_EXPORTS_TRIE
cmdsize: 16
dataoff: 32856
datasize: 16
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 32880
nsyms: 2
stroff: 32912
strsize: 16
- cmd: LC_DYSYMTAB
cmdsize: 80
ilocalsym: 0
nlocalsym: 0
iextdefsym: 0
nextdefsym: 1
iundefsym: 1
nundefsym: 1
tocoff: 0
ntoc: 0
modtaboff: 0
nmodtab: 0
extrefsymoff: 0
nextrefsyms: 0
indirectsymoff: 0
nindirectsyms: 0
extreloff: 0
nextrel: 0
locreloff: 0
nlocrel: 0
- cmd: LC_UUID
cmdsize: 24
uuid: 56F7BCE0-C1A7-38E3-A90D-742D8E3D5FA9
- cmd: LC_BUILD_VERSION
cmdsize: 32
platform: 2
minos: 983296
sdk: 983552
ntools: 1
Tools:
- tool: 3
version: 46596096
- cmd: LC_SOURCE_VERSION
cmdsize: 16
version: 0
- cmd: LC_ENCRYPTION_INFO_64
cmdsize: 24
cryptoff: 16384
cryptsize: 0
cryptid: 0
pad: 0
- cmd: LC_LOAD_DYLIB
cmdsize: 56
dylib:
name: 24
timestamp: 2
current_version: 0
compatibility_version: 0
Content: '@executable_path/liba.dylib'
ZeroPadBytes: 5
- cmd: LC_LOAD_DYLIB
cmdsize: 56
dylib:
name: 24
timestamp: 2
current_version: 85917696
compatibility_version: 65536
Content: '/usr/lib/libSystem.B.dylib'
ZeroPadBytes: 6
- cmd: LC_FUNCTION_STARTS
cmdsize: 16
dataoff: 32872
datasize: 8
- cmd: LC_DATA_IN_CODE
cmdsize: 16
dataoff: 32880
datasize: 0
__LINKEDIT: 0000000020000000480000004C000000010000000100000000000000000000000300000000000000100000000000000018000000004006000040000000000000000000000100000001020000005F6578740000000000000000015F700006040080800100000000000000000000000000020000000F02000000400000000000000500000001000001000000000000000020005F70005F65787400000000000000
...
6 changes: 6 additions & 0 deletions llvm/test/tools/llvm-objdump/MachO/dyld_info.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
RUN: llvm-objdump --macho --dyld_info %p/Inputs/bind.macho-x86_64 \
RUN: | FileCheck %s --match-full-lines --strict-whitespace \
RUN: --implicit-check-not={{.}}

CHECK:{{.*}}bind.macho-x86_64:
CHECK:dyld information:
24 changes: 22 additions & 2 deletions llvm/tools/llvm-objdump/MachODump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ bool objdump::DataInCode;
bool objdump::FunctionStarts;
bool objdump::LinkOptHints;
bool objdump::InfoPlist;
bool objdump::DyldInfo;
bool objdump::DylibsUsed;
bool objdump::DylibId;
bool objdump::Verbose;
Expand Down Expand Up @@ -111,6 +112,7 @@ void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) {
FunctionStarts = InputArgs.hasArg(OBJDUMP_function_starts);
LinkOptHints = InputArgs.hasArg(OBJDUMP_link_opt_hints);
InfoPlist = InputArgs.hasArg(OBJDUMP_info_plist);
DyldInfo = InputArgs.hasArg(OBJDUMP_dyld_info);
DylibsUsed = InputArgs.hasArg(OBJDUMP_dylibs_used);
DylibId = InputArgs.hasArg(OBJDUMP_dylib_id);
Verbose = !InputArgs.hasArg(OBJDUMP_non_verbose);
Expand Down Expand Up @@ -1182,6 +1184,22 @@ static void PrintLinkOptHints(MachOObjectFile *O) {
}
}

static void printMachOChainedFixups(object::MachOObjectFile *Obj,
MachOChainedFixupEntry::FixupKind Type) {
Error Err = Error::success();
for (const object::MachOChainedFixupEntry &Entry :
Obj->fixupTable(Err, Type)) {
(void)Entry;
}
if (Err)
reportError(std::move(Err), Obj->getFileName());
}

static void PrintDyldInfo(MachOObjectFile *O) {
outs() << "dyld information:" << '\n';
printMachOChainedFixups(O, MachOChainedFixupEntry::FixupKind::Bind);
}

static void PrintDylibs(MachOObjectFile *O, bool JustId) {
unsigned Index = 0;
for (const auto &Load : O->load_commands()) {
Expand Down Expand Up @@ -1900,8 +1918,8 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
// UniversalHeaders or ArchiveHeaders.
if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase ||
Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols ||
DataInCode || FunctionStarts || LinkOptHints || DylibsUsed || DylibId ||
Rpaths || ObjcMetaData || (!FilterSections.empty())) {
DataInCode || FunctionStarts || LinkOptHints || DyldInfo || DylibsUsed ||
DylibId || Rpaths || ObjcMetaData || (!FilterSections.empty())) {
if (LeadingHeaders) {
outs() << Name;
if (!ArchiveMemberName.empty())
Expand Down Expand Up @@ -1970,6 +1988,8 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
DumpSectionContents(FileName, MachOOF, Verbose);
if (InfoPlist)
DumpInfoPlistSectionContents(FileName, MachOOF);
if (DyldInfo)
PrintDyldInfo(MachOOF);
if (DylibsUsed)
PrintDylibs(MachOOF, false);
if (DylibId)
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/llvm-objdump/MachODump.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void parseMachOOptions(const llvm::opt::InputArgList &InputArgs);
extern bool Bind;
extern bool DataInCode;
extern std::string DisSymName;
extern bool DyldInfo;
extern bool DylibId;
extern bool DylibsUsed;
extern bool ExportsTrie;
Expand Down
6 changes: 6 additions & 0 deletions llvm/tools/llvm-objdump/ObjdumpOpts.td
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ def info_plist : Flag<["--"], "info-plist">,
"Mach-O objects (requires --macho)">,
Group<grp_mach_o>;

def dyld_info : Flag<["--"], "dyld_info">,
HelpText<"Print bind and rebase information used by dyld to resolve "
"external references in a final linked binary "
"(requires --macho)">,
Group<grp_mach_o>;

def dylibs_used : Flag<["--"], "dylibs-used">,
HelpText<"Print the shared libraries used for linked "
"Mach-O files (requires --macho)">,
Expand Down
1 change: 0 additions & 1 deletion llvm/tools/llvm-objdump/OtoolOpts.td
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ def X : Flag<["-"], "X">, HelpText<"omit leading addresses or headers">;
// -addr_slide=arg
// -function_offsets


// Obsolete and unsupported:
def grp_obsolete : OptionGroup<"kind">,
HelpText<"Obsolete and unsupported flags">;
Expand Down
10 changes: 5 additions & 5 deletions llvm/tools/llvm-objdump/llvm-objdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2760,11 +2760,11 @@ int main(int argc, char **argv) {
!DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST &&
!Relocations && !SectionHeaders && !SectionContents && !SymbolTable &&
!DynamicSymbolTable && !UnwindInfo && !FaultMapSection &&
!(MachOOpt &&
(Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie ||
FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist ||
LazyBind || LinkOptHints || ObjcMetaData || Rebase || Rpaths ||
UniversalHeaders || WeakBind || !FilterSections.empty()))) {
!(MachOOpt && (Bind || DataInCode || DyldInfo || DylibId || DylibsUsed ||
ExportsTrie || FirstPrivateHeader || FunctionStarts ||
IndirectSymbols || InfoPlist || LazyBind || LinkOptHints ||
ObjcMetaData || Rebase || Rpaths || UniversalHeaders ||
WeakBind || !FilterSections.empty()))) {
T->printHelp(ToolName);
return 2;
}
Expand Down