Skip to content

Commit 3fb120f

Browse files
committedAug 3, 2018
objdump: Better handling of Mach-O universal binaries
Summary: With Mach-O, there is a flag requirement discrepancy between working with universal binaries and thin binaries. Many flags that don't require the `-macho` flag (for example `-private-headers` and `-disassemble`) fail to work on universal binaries unless `-macho` is given. When this happens, the error message is unhelpful, stating: The file was not recognized as a valid object file. Which can lead to confusion. This change allows generic flags to be used on universal binaries with and without the `-macho` flag. This means flags that can be used for thin files can be used consistently with fat files too. To do this, the universal binary support within `ParseInputMachO()` is extracted into a new function. This new function is called directly from `DumpInput()` when the input binary is universal. Additionally the `-arch` flag validation in `ParseInputMachO()` was extracted to be reused. Reviewers: compnerd Reviewed By: compnerd Subscribers: keith, llvm-commits Differential Revision: https://reviews.llvm.org/D48702 llvm-svn: 338792
1 parent 1ba5e9a commit 3fb120f

File tree

5 files changed

+176
-153
lines changed

5 files changed

+176
-153
lines changed
 

‎llvm/test/tools/llvm-objdump/X86/macho-private-headers.test

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
// RUN: | FileCheck %s -check-prefix=THREAD
2020
// RUN: llvm-objdump -macho -p -arch i386 %p/Inputs/macho-universal.x86_64.i386 \
2121
// RUN: | FileCheck %s -check-prefix=FATi386
22+
// RUN: llvm-objdump -p -arch i386 %p/Inputs/macho-universal.x86_64.i386 \
23+
// RUN: | FileCheck %s -check-prefix=FATi386
2224
// RUN: llvm-objdump -p -non-verbose %p/Inputs/hello.obj.macho-x86_64 \
2325
// RUN: | FileCheck %s -check-prefix=NON_VERBOSE
2426
// RUN: llvm-objdump -p %p/Inputs/codesig.macho-x86_64 \

‎llvm/test/tools/llvm-objdump/X86/macho-universal-x86_64.i386.test

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
RUN: llvm-objdump %p/Inputs/macho-universal.x86_64.i386 -d -m -no-show-raw-insn -full-leading-addr -print-imm-hex -arch all \
22
RUN: | FileCheck %s -check-prefix UEXE-all
3+
RUN: llvm-objdump %p/Inputs/macho-universal.x86_64.i386 -d -no-show-raw-insn -full-leading-addr -print-imm-hex -arch all \
4+
RUN: | FileCheck %s -check-prefix UEXE-all
35
RUN: llvm-objdump %p/Inputs/macho-universal-archive.x86_64.i386 -d -m -no-show-raw-insn -full-leading-addr -print-imm-hex -arch i386 \
46
RUN: | FileCheck %s -check-prefix UArchive-i386
57
RUN: llvm-objdump %p/Inputs/macho-universal.x86_64.i386 -universal-headers -m \

‎llvm/tools/llvm-objdump/MachODump.cpp

+167-153
Original file line numberDiff line numberDiff line change
@@ -1937,11 +1937,7 @@ static void printArchiveHeaders(StringRef Filename, Archive *A, bool verbose,
19371937
report_error(StringRef(), Filename, std::move(Err), ArchitectureName);
19381938
}
19391939

1940-
// ParseInputMachO() parses the named Mach-O file in Filename and handles the
1941-
// -arch flags selecting just those slices as specified by them and also parses
1942-
// archive files. Then for each individual Mach-O file ProcessMachO() is
1943-
// called to process the file based on the command line options.
1944-
void llvm::ParseInputMachO(StringRef Filename) {
1940+
static bool ValidateArchFlags() {
19451941
// Check for -arch all and verifiy the -arch flags are valid.
19461942
for (unsigned i = 0; i < ArchFlags.size(); ++i) {
19471943
if (ArchFlags[i] == "all") {
@@ -1950,10 +1946,20 @@ void llvm::ParseInputMachO(StringRef Filename) {
19501946
if (!MachOObjectFile::isValidArch(ArchFlags[i])) {
19511947
errs() << "llvm-objdump: Unknown architecture named '" + ArchFlags[i] +
19521948
"'for the -arch option\n";
1953-
return;
1949+
return false;
19541950
}
19551951
}
19561952
}
1953+
return true;
1954+
}
1955+
1956+
// ParseInputMachO() parses the named Mach-O file in Filename and handles the
1957+
// -arch flags selecting just those slices as specified by them and also parses
1958+
// archive files. Then for each individual Mach-O file ProcessMachO() is
1959+
// called to process the file based on the command line options.
1960+
void llvm::ParseInputMachO(StringRef Filename) {
1961+
if (!ValidateArchFlags())
1962+
return;
19571963

19581964
// Attempt to open the binary.
19591965
Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename);
@@ -1989,191 +1995,199 @@ void llvm::ParseInputMachO(StringRef Filename) {
19891995
report_error(Filename, std::move(Err));
19901996
return;
19911997
}
1992-
if (UniversalHeaders) {
1993-
if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin))
1994-
printMachOUniversalHeaders(UB, !NonVerbose);
1995-
}
19961998
if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) {
1997-
// If we have a list of architecture flags specified dump only those.
1998-
if (!ArchAll && ArchFlags.size() != 0) {
1999-
// Look for a slice in the universal binary that matches each ArchFlag.
2000-
bool ArchFound;
2001-
for (unsigned i = 0; i < ArchFlags.size(); ++i) {
2002-
ArchFound = false;
2003-
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
2004-
E = UB->end_objects();
2005-
I != E; ++I) {
2006-
if (ArchFlags[i] == I->getArchFlagName()) {
2007-
ArchFound = true;
2008-
Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
2009-
I->getAsObjectFile();
2010-
std::string ArchitectureName = "";
2011-
if (ArchFlags.size() > 1)
2012-
ArchitectureName = I->getArchFlagName();
2013-
if (ObjOrErr) {
2014-
ObjectFile &O = *ObjOrErr.get();
2015-
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O))
2016-
ProcessMachO(Filename, MachOOF, "", ArchitectureName);
2017-
} else if (auto E = isNotObjectErrorInvalidFileType(
2018-
ObjOrErr.takeError())) {
2019-
report_error(Filename, StringRef(), std::move(E),
2020-
ArchitectureName);
2021-
continue;
2022-
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
2023-
I->getAsArchive()) {
2024-
std::unique_ptr<Archive> &A = *AOrErr;
2025-
outs() << "Archive : " << Filename;
2026-
if (!ArchitectureName.empty())
2027-
outs() << " (architecture " << ArchitectureName << ")";
2028-
outs() << "\n";
2029-
if (ArchiveHeaders)
2030-
printArchiveHeaders(Filename, A.get(), !NonVerbose,
2031-
ArchiveMemberOffsets, ArchitectureName);
2032-
Error Err = Error::success();
2033-
for (auto &C : A->children(Err)) {
2034-
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
2035-
if (!ChildOrErr) {
2036-
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
2037-
report_error(Filename, C, std::move(E), ArchitectureName);
2038-
continue;
2039-
}
2040-
if (MachOObjectFile *O =
2041-
dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
2042-
ProcessMachO(Filename, O, O->getFileName(), ArchitectureName);
2043-
}
2044-
if (Err)
2045-
report_error(Filename, std::move(Err));
2046-
} else {
2047-
consumeError(AOrErr.takeError());
2048-
error("Mach-O universal file: " + Filename + " for " +
2049-
"architecture " + StringRef(I->getArchFlagName()) +
2050-
" is not a Mach-O file or an archive file");
2051-
}
2052-
}
2053-
}
2054-
if (!ArchFound) {
2055-
errs() << "llvm-objdump: file: " + Filename + " does not contain "
2056-
<< "architecture: " + ArchFlags[i] + "\n";
2057-
return;
2058-
}
2059-
}
1999+
ParseInputMachO(UB);
2000+
return;
2001+
}
2002+
if (ObjectFile *O = dyn_cast<ObjectFile>(&Bin)) {
2003+
if (!checkMachOAndArchFlags(O, Filename))
20602004
return;
2061-
}
2062-
// No architecture flags were specified so if this contains a slice that
2063-
// matches the host architecture dump only that.
2064-
if (!ArchAll) {
2005+
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&*O))
2006+
ProcessMachO(Filename, MachOOF);
2007+
else
2008+
errs() << "llvm-objdump: '" << Filename << "': "
2009+
<< "Object is not a Mach-O file type.\n";
2010+
return;
2011+
}
2012+
llvm_unreachable("Input object can't be invalid at this point");
2013+
}
2014+
2015+
void llvm::ParseInputMachO(MachOUniversalBinary *UB) {
2016+
if (!ValidateArchFlags())
2017+
return;
2018+
2019+
auto Filename = UB->getFileName();
2020+
2021+
if (UniversalHeaders)
2022+
printMachOUniversalHeaders(UB, !NonVerbose);
2023+
2024+
// If we have a list of architecture flags specified dump only those.
2025+
if (!ArchAll && ArchFlags.size() != 0) {
2026+
// Look for a slice in the universal binary that matches each ArchFlag.
2027+
bool ArchFound;
2028+
for (unsigned i = 0; i < ArchFlags.size(); ++i) {
2029+
ArchFound = false;
20652030
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
2066-
E = UB->end_objects();
2067-
I != E; ++I) {
2068-
if (MachOObjectFile::getHostArch().getArchName() ==
2069-
I->getArchFlagName()) {
2070-
Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
2071-
std::string ArchiveName;
2072-
ArchiveName.clear();
2031+
E = UB->end_objects();
2032+
I != E; ++I) {
2033+
if (ArchFlags[i] == I->getArchFlagName()) {
2034+
ArchFound = true;
2035+
Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
2036+
I->getAsObjectFile();
2037+
std::string ArchitectureName = "";
2038+
if (ArchFlags.size() > 1)
2039+
ArchitectureName = I->getArchFlagName();
20732040
if (ObjOrErr) {
20742041
ObjectFile &O = *ObjOrErr.get();
20752042
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O))
2076-
ProcessMachO(Filename, MachOOF);
2043+
ProcessMachO(Filename, MachOOF, "", ArchitectureName);
20772044
} else if (auto E = isNotObjectErrorInvalidFileType(
2078-
ObjOrErr.takeError())) {
2079-
report_error(Filename, std::move(E));
2045+
ObjOrErr.takeError())) {
2046+
report_error(Filename, StringRef(), std::move(E),
2047+
ArchitectureName);
20802048
continue;
20812049
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
2082-
I->getAsArchive()) {
2050+
I->getAsArchive()) {
20832051
std::unique_ptr<Archive> &A = *AOrErr;
2084-
outs() << "Archive : " << Filename << "\n";
2052+
outs() << "Archive : " << Filename;
2053+
if (!ArchitectureName.empty())
2054+
outs() << " (architecture " << ArchitectureName << ")";
2055+
outs() << "\n";
20852056
if (ArchiveHeaders)
20862057
printArchiveHeaders(Filename, A.get(), !NonVerbose,
2087-
ArchiveMemberOffsets);
2058+
ArchiveMemberOffsets, ArchitectureName);
20882059
Error Err = Error::success();
20892060
for (auto &C : A->children(Err)) {
20902061
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
20912062
if (!ChildOrErr) {
20922063
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
2093-
report_error(Filename, C, std::move(E));
2064+
report_error(Filename, C, std::move(E), ArchitectureName);
20942065
continue;
20952066
}
20962067
if (MachOObjectFile *O =
20972068
dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
2098-
ProcessMachO(Filename, O, O->getFileName());
2069+
ProcessMachO(Filename, O, O->getFileName(), ArchitectureName);
20992070
}
21002071
if (Err)
21012072
report_error(Filename, std::move(Err));
21022073
} else {
21032074
consumeError(AOrErr.takeError());
2104-
error("Mach-O universal file: " + Filename + " for architecture " +
2105-
StringRef(I->getArchFlagName()) +
2075+
error("Mach-O universal file: " + Filename + " for " +
2076+
"architecture " + StringRef(I->getArchFlagName()) +
21062077
" is not a Mach-O file or an archive file");
21072078
}
2108-
return;
21092079
}
21102080
}
2081+
if (!ArchFound) {
2082+
errs() << "llvm-objdump: file: " + Filename + " does not contain "
2083+
<< "architecture: " + ArchFlags[i] + "\n";
2084+
return;
2085+
}
21112086
}
2112-
// Either all architectures have been specified or none have been specified
2113-
// and this does not contain the host architecture so dump all the slices.
2114-
bool moreThanOneArch = UB->getNumberOfObjects() > 1;
2087+
return;
2088+
}
2089+
// No architecture flags were specified so if this contains a slice that
2090+
// matches the host architecture dump only that.
2091+
if (!ArchAll) {
21152092
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
2116-
E = UB->end_objects();
2117-
I != E; ++I) {
2118-
Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
2119-
std::string ArchitectureName = "";
2120-
if (moreThanOneArch)
2121-
ArchitectureName = I->getArchFlagName();
2122-
if (ObjOrErr) {
2123-
ObjectFile &Obj = *ObjOrErr.get();
2124-
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj))
2125-
ProcessMachO(Filename, MachOOF, "", ArchitectureName);
2126-
} else if (auto E = isNotObjectErrorInvalidFileType(
2127-
ObjOrErr.takeError())) {
2128-
report_error(StringRef(), Filename, std::move(E), ArchitectureName);
2129-
continue;
2130-
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
2131-
I->getAsArchive()) {
2132-
std::unique_ptr<Archive> &A = *AOrErr;
2133-
outs() << "Archive : " << Filename;
2134-
if (!ArchitectureName.empty())
2135-
outs() << " (architecture " << ArchitectureName << ")";
2136-
outs() << "\n";
2137-
if (ArchiveHeaders)
2138-
printArchiveHeaders(Filename, A.get(), !NonVerbose,
2139-
ArchiveMemberOffsets, ArchitectureName);
2140-
Error Err = Error::success();
2141-
for (auto &C : A->children(Err)) {
2142-
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
2143-
if (!ChildOrErr) {
2144-
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
2145-
report_error(Filename, C, std::move(E), ArchitectureName);
2146-
continue;
2147-
}
2148-
if (MachOObjectFile *O =
2149-
dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
2150-
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O))
2151-
ProcessMachO(Filename, MachOOF, MachOOF->getFileName(),
2152-
ArchitectureName);
2093+
E = UB->end_objects();
2094+
I != E; ++I) {
2095+
if (MachOObjectFile::getHostArch().getArchName() ==
2096+
I->getArchFlagName()) {
2097+
Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
2098+
std::string ArchiveName;
2099+
ArchiveName.clear();
2100+
if (ObjOrErr) {
2101+
ObjectFile &O = *ObjOrErr.get();
2102+
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O))
2103+
ProcessMachO(Filename, MachOOF);
2104+
} else if (auto E = isNotObjectErrorInvalidFileType(
2105+
ObjOrErr.takeError())) {
2106+
report_error(Filename, std::move(E));
2107+
continue;
2108+
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
2109+
I->getAsArchive()) {
2110+
std::unique_ptr<Archive> &A = *AOrErr;
2111+
outs() << "Archive : " << Filename << "\n";
2112+
if (ArchiveHeaders)
2113+
printArchiveHeaders(Filename, A.get(), !NonVerbose,
2114+
ArchiveMemberOffsets);
2115+
Error Err = Error::success();
2116+
for (auto &C : A->children(Err)) {
2117+
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
2118+
if (!ChildOrErr) {
2119+
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
2120+
report_error(Filename, C, std::move(E));
2121+
continue;
2122+
}
2123+
if (MachOObjectFile *O =
2124+
dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
2125+
ProcessMachO(Filename, O, O->getFileName());
21532126
}
2127+
if (Err)
2128+
report_error(Filename, std::move(Err));
2129+
} else {
2130+
consumeError(AOrErr.takeError());
2131+
error("Mach-O universal file: " + Filename + " for architecture " +
2132+
StringRef(I->getArchFlagName()) +
2133+
" is not a Mach-O file or an archive file");
21542134
}
2155-
if (Err)
2156-
report_error(Filename, std::move(Err));
2157-
} else {
2158-
consumeError(AOrErr.takeError());
2159-
error("Mach-O universal file: " + Filename + " for architecture " +
2160-
StringRef(I->getArchFlagName()) +
2161-
" is not a Mach-O file or an archive file");
2135+
return;
21622136
}
21632137
}
2164-
return;
21652138
}
2166-
if (ObjectFile *O = dyn_cast<ObjectFile>(&Bin)) {
2167-
if (!checkMachOAndArchFlags(O, Filename))
2168-
return;
2169-
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&*O)) {
2170-
ProcessMachO(Filename, MachOOF);
2171-
} else
2172-
errs() << "llvm-objdump: '" << Filename << "': "
2173-
<< "Object is not a Mach-O file type.\n";
2174-
return;
2139+
// Either all architectures have been specified or none have been specified
2140+
// and this does not contain the host architecture so dump all the slices.
2141+
bool moreThanOneArch = UB->getNumberOfObjects() > 1;
2142+
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
2143+
E = UB->end_objects();
2144+
I != E; ++I) {
2145+
Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
2146+
std::string ArchitectureName = "";
2147+
if (moreThanOneArch)
2148+
ArchitectureName = I->getArchFlagName();
2149+
if (ObjOrErr) {
2150+
ObjectFile &Obj = *ObjOrErr.get();
2151+
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj))
2152+
ProcessMachO(Filename, MachOOF, "", ArchitectureName);
2153+
} else if (auto E = isNotObjectErrorInvalidFileType(
2154+
ObjOrErr.takeError())) {
2155+
report_error(StringRef(), Filename, std::move(E), ArchitectureName);
2156+
continue;
2157+
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
2158+
I->getAsArchive()) {
2159+
std::unique_ptr<Archive> &A = *AOrErr;
2160+
outs() << "Archive : " << Filename;
2161+
if (!ArchitectureName.empty())
2162+
outs() << " (architecture " << ArchitectureName << ")";
2163+
outs() << "\n";
2164+
if (ArchiveHeaders)
2165+
printArchiveHeaders(Filename, A.get(), !NonVerbose,
2166+
ArchiveMemberOffsets, ArchitectureName);
2167+
Error Err = Error::success();
2168+
for (auto &C : A->children(Err)) {
2169+
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
2170+
if (!ChildOrErr) {
2171+
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
2172+
report_error(Filename, C, std::move(E), ArchitectureName);
2173+
continue;
2174+
}
2175+
if (MachOObjectFile *O =
2176+
dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
2177+
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O))
2178+
ProcessMachO(Filename, MachOOF, MachOOF->getFileName(),
2179+
ArchitectureName);
2180+
}
2181+
}
2182+
if (Err)
2183+
report_error(Filename, std::move(Err));
2184+
} else {
2185+
consumeError(AOrErr.takeError());
2186+
error("Mach-O universal file: " + Filename + " for architecture " +
2187+
StringRef(I->getArchFlagName()) +
2188+
" is not a Mach-O file or an archive file");
2189+
}
21752190
}
2176-
llvm_unreachable("Input object can't be invalid at this point");
21772191
}
21782192

21792193
// The block of info used by the Symbolizer call backs.

‎llvm/tools/llvm-objdump/llvm-objdump.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "llvm/Object/COFFImportFile.h"
4343
#include "llvm/Object/ELFObjectFile.h"
4444
#include "llvm/Object/MachO.h"
45+
#include "llvm/Object/MachOUniversal.h"
4546
#include "llvm/Object/ObjectFile.h"
4647
#include "llvm/Object/Wasm.h"
4748
#include "llvm/Support/Casting.h"
@@ -2363,6 +2364,8 @@ static void DumpInput(StringRef file) {
23632364
DumpArchive(a);
23642365
else if (ObjectFile *o = dyn_cast<ObjectFile>(&Binary))
23652366
DumpObject(o);
2367+
else if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Binary))
2368+
ParseInputMachO(UB);
23662369
else
23672370
report_error(file, object_error::invalid_file_type);
23682371
}

‎llvm/tools/llvm-objdump/llvm-objdump.h

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace object {
2222
class COFFObjectFile;
2323
class COFFImportFile;
2424
class MachOObjectFile;
25+
class MachOUniversalBinary;
2526
class ObjectFile;
2627
class Archive;
2728
class RelocationRef;
@@ -71,6 +72,7 @@ extern cl::opt<DIDumpType> DwarfDumpType;
7172
void error(std::error_code ec);
7273
bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b);
7374
void ParseInputMachO(StringRef Filename);
75+
void ParseInputMachO(object::MachOUniversalBinary *UB);
7476
void printCOFFUnwindInfo(const object::COFFObjectFile* o);
7577
void printMachOUnwindInfo(const object::MachOObjectFile* o);
7678
void printMachOExportsTrie(const object::MachOObjectFile* o);

0 commit comments

Comments
 (0)
Please sign in to comment.