diff --git a/include/llvm/Object/MachO.h b/include/llvm/Object/MachO.h index 96b254c6a90..e93ebb85889 100644 --- a/include/llvm/Object/MachO.h +++ b/include/llvm/Object/MachO.h @@ -225,6 +225,9 @@ public: StringRef &Suffix); static Triple::ArchType getArch(uint32_t CPUType); + static Triple getArch(uint32_t CPUType, uint32_t CPUSubType); + static Triple getArch(StringRef ArchFlag); + static Triple getHostArch(); static bool classof(const Binary *v) { return v->isMachO(); diff --git a/include/llvm/Object/MachOUniversal.h b/include/llvm/Object/MachOUniversal.h index 268e1c839e6..e6677f5bf28 100644 --- a/include/llvm/Object/MachOUniversal.h +++ b/include/llvm/Object/MachOUniversal.h @@ -54,7 +54,8 @@ public: ObjectForArch getNext() const { return ObjectForArch(Parent, Index + 1); } uint32_t getCPUType() const { return Header.cputype; } std::string getArchTypeName() const { - return Triple::getArchTypeName(MachOObjectFile::getArch(Header.cputype)); + Triple T = MachOObjectFile::getArch(Header.cputype, Header.cpusubtype); + return T.getArchName(); } ErrorOr> getAsObjectFile() const; diff --git a/include/llvm/Support/MachO.h b/include/llvm/Support/MachO.h index 60f7918ae72..bd4dc2f6d1b 100644 --- a/include/llvm/Support/MachO.h +++ b/include/llvm/Support/MachO.h @@ -1015,8 +1015,8 @@ namespace llvm { enum : uint32_t { // Capability bits used in the definition of cpusubtype. - CPU_SUB_TYPE_MASK = 0xff000000, // Mask for architecture bits - CPU_SUB_TYPE_LIB64 = 0x80000000, // 64 bit libraries + CPU_SUBTYPE_MASK = 0xff000000, // Mask for architecture bits + CPU_SUBTYPE_LIB64 = 0x80000000, // 64 bit libraries // Special CPU subtype constants. CPU_SUBTYPE_MULTIPLE = ~0u diff --git a/lib/Object/MachOObjectFile.cpp b/lib/Object/MachOObjectFile.cpp index 5abd10068fa..74917a5326e 100644 --- a/lib/Object/MachOObjectFile.cpp +++ b/lib/Object/MachOObjectFile.cpp @@ -1511,6 +1511,108 @@ Triple::ArchType MachOObjectFile::getArch(uint32_t CPUType) { } } +Triple MachOObjectFile::getArch(uint32_t CPUType, uint32_t CPUSubType) { + switch (CPUType) { + case MachO::CPU_TYPE_I386: + switch (CPUSubType & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_I386_ALL: + return Triple("i386-apple-darwin"); + default: + return Triple(); + } + case MachO::CPU_TYPE_X86_64: + switch (CPUSubType & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_X86_64_ALL: + return Triple("x86_64-apple-darwin"); + case MachO::CPU_SUBTYPE_X86_64_H: + return Triple("x86_64h-apple-darwin"); + default: + return Triple(); + } + case MachO::CPU_TYPE_ARM: + switch (CPUSubType & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_ARM_V4T: + return Triple("armv4t-apple-darwin"); + case MachO::CPU_SUBTYPE_ARM_V5TEJ: + return Triple("armv5e-apple-darwin"); + case MachO::CPU_SUBTYPE_ARM_V6: + return Triple("armv6-apple-darwin"); + case MachO::CPU_SUBTYPE_ARM_V6M: + return Triple("armv6m-apple-darwin"); + case MachO::CPU_SUBTYPE_ARM_V7EM: + return Triple("armv7em-apple-darwin"); + case MachO::CPU_SUBTYPE_ARM_V7K: + return Triple("armv7k-apple-darwin"); + case MachO::CPU_SUBTYPE_ARM_V7M: + return Triple("armv7m-apple-darwin"); + case MachO::CPU_SUBTYPE_ARM_V7S: + return Triple("armv7s-apple-darwin"); + default: + return Triple(); + } + case MachO::CPU_TYPE_ARM64: + switch (CPUSubType & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_ARM64_ALL: + return Triple("arm64-apple-darwin"); + default: + return Triple(); + } + case MachO::CPU_TYPE_POWERPC: + switch (CPUSubType & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_POWERPC_ALL: + return Triple("ppc-apple-darwin"); + default: + return Triple(); + } + case MachO::CPU_TYPE_POWERPC64: + case MachO::CPU_SUBTYPE_POWERPC_ALL: + return Triple("ppc64-apple-darwin"); + switch (CPUSubType & ~MachO::CPU_SUBTYPE_MASK) { + default: + return Triple(); + } + default: + return Triple(); + } +} + +Triple MachOObjectFile::getHostArch() { + return Triple(sys::getDefaultTargetTriple()); +} + +Triple MachOObjectFile::getArch(StringRef ArchFlag) { + if (ArchFlag == "i386") + return Triple("i386-apple-darwin"); + else if (ArchFlag == "x86_64") + return Triple("x86_64-apple-darwin"); + else if (ArchFlag == "x86_64h") + return Triple("x86_64h-apple-darwin"); + else if (ArchFlag == "armv4t" || ArchFlag == "arm") + return Triple("armv4t-apple-darwin"); + else if (ArchFlag == "armv5e") + return Triple("armv5e-apple-darwin"); + else if (ArchFlag == "armv6") + return Triple("armv6-apple-darwin"); + else if (ArchFlag == "armv6m") + return Triple("armv6m-apple-darwin"); + else if (ArchFlag == "armv7em") + return Triple("armv7em-apple-darwin"); + else if (ArchFlag == "armv7k") + return Triple("armv7k-apple-darwin"); + else if (ArchFlag == "armv7k") + return Triple("armv7m-apple-darwin"); + else if (ArchFlag == "armv7s") + return Triple("armv7s-apple-darwin"); + else if (ArchFlag == "arm64") + return Triple("arm64-apple-darwin"); + else if (ArchFlag == "ppc") + return Triple("ppc-apple-darwin"); + else if (ArchFlag == "ppc64") + return Triple("ppc64-apple-darwin"); + else + return Triple(); +} + unsigned MachOObjectFile::getArch() const { return getArch(getCPUType(this)); } diff --git a/test/Object/nm-universal-binary.test b/test/Object/nm-universal-binary.test index 8992359a79b..889377b5b35 100644 --- a/test/Object/nm-universal-binary.test +++ b/test/Object/nm-universal-binary.test @@ -1,13 +1,21 @@ -RUN: llvm-nm %p/Inputs/macho-universal.x86_64.i386 \ +RUN: llvm-nm -arch all %p/Inputs/macho-universal.x86_64.i386 \ RUN: | FileCheck %s -check-prefix CHECK-OBJ -RUN: llvm-nm %p/Inputs/macho-universal-archive.x86_64.i386 \ +RUN: llvm-nm -arch x86_64 %p/Inputs/macho-universal.x86_64.i386 \ +RUN: | FileCheck %s -check-prefix CHECK-OBJ-x86_64 +RUN: llvm-nm -arch all %p/Inputs/macho-universal-archive.x86_64.i386 \ RUN: | FileCheck %s -check-prefix CHECK-AR +RUN: llvm-nm -arch i386 %p/Inputs/macho-universal-archive.x86_64.i386 \ +RUN: | FileCheck %s -check-prefix CHECK-AR-i386 CHECK-OBJ: macho-universal.x86_64.i386 (for architecture x86_64): CHECK-OBJ: 0000000100000f60 T _main CHECK-OBJ: macho-universal.x86_64.i386 (for architecture i386): CHECK-OBJ: 00001fa0 T _main +CHECK-OBJ-x86_64: 0000000100000000 T __mh_execute_header +CHECK-OBJ-x86_64: 0000000100000f60 T _main +CHECK-OBJ-x86_64: U dyld_stub_binder + CHECK-AR: macho-universal-archive.x86_64.i386(hello.o) (for architecture x86_64): CHECK-AR: 0000000000000068 s EH_frame0 CHECK-AR: 000000000000003b s L_.str @@ -17,3 +25,7 @@ CHECK-AR: U _printf CHECK-AR: macho-universal-archive.x86_64.i386(foo.o) (for architecture i386): CHECK-AR: 00000008 D _bar CHECK-AR: 00000000 T _foo + +CHECK-AR-i386: macho-universal-archive.x86_64.i386(foo.o): +CHECK-AR-i386: 00000008 D _bar +CHECK-AR-i386: 00000000 T _foo diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index a08f25a737d..e26bc650f0d 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -83,6 +83,11 @@ cl::opt BSDFormat("B", cl::desc("Alias for --format=bsd")); cl::opt POSIXFormat("P", cl::desc("Alias for --format=posix")); cl::opt DarwinFormat("m", cl::desc("Alias for --format=darwin")); +static cl::list ArchFlags("arch", + cl::desc("architecture(s) from a Mach-O file to dump"), + cl::ZeroOrMore); +bool ArchAll = false; + cl::opt PrintFileName( "print-file-name", cl::desc("Precede each symbol with the object file it came from")); @@ -720,6 +725,40 @@ static void dumpSymbolNamesFromObject(SymbolicFile *Obj, bool printName) { sortAndPrintSymbolList(Obj, printName); } +// checkMachOAndArchFlags() checks to see if the SymbolicFile is a Mach-O file +// and if it is and there is a list of architecture flags is specified then +// check to make sure this Mach-O file is one of those architectures or all +// architectures was specificed. If not then an error is generated and this +// routine returns false. Else it returns true. +static bool checkMachOAndArchFlags(SymbolicFile *O, std::string &Filename) { + if (isa(O) && !ArchAll && ArchFlags.size() != 0) { + MachOObjectFile *MachO = dyn_cast(O); + bool ArchFound = false; + MachO::mach_header H; + MachO::mach_header_64 H_64; + Triple T; + if (MachO->is64Bit()) { + H_64 = MachO->MachOObjectFile::getHeader64(); + T = MachOObjectFile::getArch(H_64.cputype, H_64.cpusubtype); + } else { + H = MachO->MachOObjectFile::getHeader(); + T = MachOObjectFile::getArch(H.cputype, H.cpusubtype); + } + unsigned i; + for (i = 0; i < ArchFlags.size(); ++i){ + if (ArchFlags[i] == T.getArchName()) + ArchFound = true; + break; + } + if (!ArchFound) { + error(ArchFlags[i], + "file: " + Filename + " does not contain architecture"); + return false; + } + } + return true; +} + static void dumpSymbolNamesFromFile(std::string &Filename) { std::unique_ptr Buffer; if (error(MemoryBuffer::getFileOrSTDIN(Filename, Buffer), Filename)) @@ -758,6 +797,8 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { if (ChildOrErr.getError()) continue; if (SymbolicFile *O = dyn_cast(&*ChildOrErr.get())) { + if (!checkMachOAndArchFlags(O, Filename)) + return; outs() << "\n"; if (isa(O)) { outs() << Filename << "(" << O->getFileName() << ")"; @@ -770,6 +811,98 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { return; } if (MachOUniversalBinary *UB = dyn_cast(Bin.get())) { + // If we have a list of architecture flags specified dump only those. + if (!ArchAll && ArchFlags.size() != 0) { + // Look for a slice in the universal binary that matches each ArchFlag. + bool ArchFound; + for (unsigned i = 0; i < ArchFlags.size(); ++i){ + ArchFound = false; + for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), + E = UB->end_objects(); + I != E; ++I) { + if (ArchFlags[i] == I->getArchTypeName()){ + ArchFound = true; + ErrorOr> ObjOrErr = + I->getAsObjectFile(); + std::unique_ptr A; + if (ObjOrErr) { + std::unique_ptr Obj = std::move(ObjOrErr.get()); + if (ArchFlags.size() > 1) { + outs() << "\n" << Obj->getFileName() + << " (for architecture " << I->getArchTypeName() << ")" + << ":\n"; + } + dumpSymbolNamesFromObject(Obj.get(), false); + } + else if (!I->getAsArchive(A)) { + for (Archive::child_iterator AI = A->child_begin(), + AE = A->child_end(); + AI != AE; ++AI) { + ErrorOr> ChildOrErr = + AI->getAsBinary(&Context); + if (ChildOrErr.getError()) + continue; + if (SymbolicFile *O = dyn_cast + (&*ChildOrErr.get())) { + outs() << "\n" << A->getFileName(); + outs() << "(" << O->getFileName() << ")"; + if (ArchFlags.size() > 1) { + outs() << " (for architecture " << I->getArchTypeName() + << ")"; + } + outs() << ":\n"; + dumpSymbolNamesFromObject(O, false); + } + } + } + } + } + if (!ArchFound) { + error(ArchFlags[i], + "file: " + Filename + " does not contain architecture"); + return; + } + } + return; + } + // No architecture flags were specified so if this contains a slice that + // matches the host architecture dump only that. + if (!ArchAll) { + StringRef HostArchName = + MachOObjectFile::getHostArch().getArchName(); + for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), + E = UB->end_objects(); + I != E; ++I) { + if (HostArchName == I->getArchTypeName()){ + ErrorOr> ObjOrErr = + I->getAsObjectFile(); + std::unique_ptr A; + if (ObjOrErr) { + std::unique_ptr Obj = std::move(ObjOrErr.get()); + dumpSymbolNamesFromObject(Obj.get(), false); + } + else if (!I->getAsArchive(A)) { + for (Archive::child_iterator AI = A->child_begin(), + AE = A->child_end(); + AI != AE; ++AI) { + ErrorOr> ChildOrErr = + AI->getAsBinary(&Context); + if (ChildOrErr.getError()) + continue; + if (SymbolicFile *O = dyn_cast + (&*ChildOrErr.get())) { + outs() << "\n" << A->getFileName() + << "(" << O->getFileName() << ")" << ":\n"; + dumpSymbolNamesFromObject(O, false); + } + } + } + return; + } + } + } + // Either all architectures have been specified or none have been specified + // and this does not contain the host architecture so dump all the slices. bool moreThanOneArch = UB->getNumberOfObjects() > 1; for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), E = UB->end_objects(); @@ -810,6 +943,8 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { return; } if (SymbolicFile *O = dyn_cast(Bin.get())) { + if (!checkMachOAndArchFlags(O, Filename)) + return; dumpSymbolNamesFromObject(O, true); return; } @@ -854,6 +989,18 @@ int main(int argc, char **argv) { MultipleFiles = true; } + for (unsigned i = 0; i < ArchFlags.size(); ++i){ + if (ArchFlags[i] == "all") { + ArchAll = true; + } + else { + Triple T = MachOObjectFile::getArch(ArchFlags[i]); + if (T.getArch() == Triple::UnknownArch) + error("Unknown architecture named '" + ArchFlags[i] + "'", + "for the -arch option"); + } + } + std::for_each(InputFilenames.begin(), InputFilenames.end(), dumpSymbolNamesFromFile);