diff --git a/include/llvm/Object/Binary.h b/include/llvm/Object/Binary.h index 78fcf6feb85..a3f5625cc9b 100644 --- a/include/llvm/Object/Binary.h +++ b/include/llvm/Object/Binary.h @@ -38,6 +38,7 @@ protected: enum { ID_Archive, + ID_MachOUniversalBinary, // Object and children. ID_StartObjects, ID_COFF, @@ -87,6 +88,10 @@ public: return TypeID == ID_Archive; } + bool isMachOUniversalBinary() const { + return TypeID == ID_MachOUniversalBinary; + } + bool isELF() const { return TypeID >= ID_ELF32L && TypeID <= ID_ELF64B; } diff --git a/include/llvm/Object/Error.h b/include/llvm/Object/Error.h index 32b834f2154..8b0570b02f8 100644 --- a/include/llvm/Object/Error.h +++ b/include/llvm/Object/Error.h @@ -24,6 +24,7 @@ const error_category &object_category(); struct object_error { enum Impl { success = 0, + arch_not_found, invalid_file_type, parse_failed, unexpected_eof diff --git a/include/llvm/Object/MachO.h b/include/llvm/Object/MachO.h index 1b9faaa9fd9..50435d6fe22 100644 --- a/include/llvm/Object/MachO.h +++ b/include/llvm/Object/MachO.h @@ -17,6 +17,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" #include "llvm/Object/MachOFormat.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/MachO.h" @@ -196,6 +197,8 @@ public: bool is64Bit() const; void ReadULEB128s(uint64_t Index, SmallVectorImpl &Out) const; + static Triple::ArchType getArch(uint32_t CPUType); + static bool classof(const Binary *v) { return v->isMachO(); } diff --git a/include/llvm/Object/MachOFormat.h b/include/llvm/Object/MachOFormat.h index ffca391ea22..96ee8a766b8 100644 --- a/include/llvm/Object/MachOFormat.h +++ b/include/llvm/Object/MachOFormat.h @@ -95,6 +95,8 @@ namespace macho { enum StructureSizes { Header32Size = 28, Header64Size = 32, + FatHeaderSize = 8, + FatArchHeaderSize = 20, SegmentLoadCommand32Size = 56, SegmentLoadCommand64Size = 72, Section32Size = 68, @@ -130,6 +132,22 @@ namespace macho { uint32_t Reserved; }; + /// \brief Header for universal object files. + struct FatHeader { + uint32_t Magic; + uint32_t NumFatArch; + }; + + /// \brief Header for a single-architecture object file in a + /// universal binary. + struct FatArchHeader { + uint32_t CPUType; + uint32_t CPUSubtype; + uint32_t Offset; + uint32_t Size; + uint32_t Align; + }; + // See . enum HeaderFileType { HFT_Object = 0x1 diff --git a/include/llvm/Object/MachOUniversal.h b/include/llvm/Object/MachOUniversal.h new file mode 100644 index 00000000000..57432826b8c --- /dev/null +++ b/include/llvm/Object/MachOUniversal.h @@ -0,0 +1,102 @@ +//===- MachOUniversal.h - Mach-O universal binaries -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares Mach-O fat/universal binaries. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJECT_MACHOUNIVERSAL_H +#define LLVM_OBJECT_MACHOUNIVERSAL_H + +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/MachOFormat.h" + +namespace llvm { +namespace object { + +class ObjectFile; + +class MachOUniversalBinary : public Binary { + virtual void anchor(); + + uint32_t NumberOfObjects; +public: + class ObjectForArch { + const MachOUniversalBinary *Parent; + /// \brief Index of object in the universal binary. + uint32_t Index; + /// \brief Descriptor of the object. + macho::FatArchHeader Header; + + public: + ObjectForArch(const MachOUniversalBinary *Parent, uint32_t Index); + + void clear() { + Parent = 0; + Index = 0; + } + + bool operator==(const ObjectForArch &Other) const { + return (Parent == Other.Parent) && (Index == Other.Index); + } + + ObjectForArch getNext() const { return ObjectForArch(Parent, Index + 1); } + uint32_t getCPUType() const { return Header.CPUType; } + + error_code getAsObjectFile(OwningPtr &Result) const; + }; + + class object_iterator { + ObjectForArch Obj; + public: + object_iterator(const ObjectForArch &Obj) : Obj(Obj) {} + const ObjectForArch* operator->() const { + return &Obj; + } + + bool operator==(const object_iterator &Other) const { + return Obj == Other.Obj; + } + bool operator!=(const object_iterator &Other) const { + return !(*this == Other); + } + + object_iterator& operator++() { // Preincrement + Obj = Obj.getNext(); + return *this; + } + }; + + MachOUniversalBinary(MemoryBuffer *Source, error_code &ec); + + object_iterator begin_objects() const { + return ObjectForArch(this, 0); + } + object_iterator end_objects() const { + return ObjectForArch(0, 0); + } + + uint32_t getNumberOfObjects() const { return NumberOfObjects; } + + // Cast methods. + static inline bool classof(Binary const *V) { + return V->isMachOUniversalBinary(); + } + + error_code getObjectForArch(Triple::ArchType Arch, + OwningPtr &Result) const; +}; + +} +} + +#endif diff --git a/include/llvm/Support/FileSystem.h b/include/llvm/Support/FileSystem.h index c1c1fc6ed6e..79f85539c03 100644 --- a/include/llvm/Support/FileSystem.h +++ b/include/llvm/Support/FileSystem.h @@ -199,6 +199,7 @@ struct file_magic { macho_bundle, ///< Mach-O Bundle file macho_dynamically_linked_shared_lib_stub, ///< Mach-O Shared lib stub macho_dsym_companion, ///< Mach-O dSYM companion file + macho_universal_binary, ///< Mach-O universal binary coff_object, ///< COFF object file pecoff_executable ///< PECOFF executable file }; diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index f0bd4e34a86..ee5d7226f45 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -527,6 +527,7 @@ ObjectImage *RuntimeDyld::loadObject(ObjectBuffer *InputBuffer) { case sys::fs::file_magic::archive: case sys::fs::file_magic::coff_object: case sys::fs::file_magic::pecoff_executable: + case sys::fs::file_magic::macho_universal_binary: report_fatal_error("Incompatible object format!"); } } else { diff --git a/lib/Object/Binary.cpp b/lib/Object/Binary.cpp index a1497708c1e..177c86c5465 100644 --- a/lib/Object/Binary.cpp +++ b/lib/Object/Binary.cpp @@ -20,6 +20,7 @@ // Include headers for createBinary. #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" +#include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" using namespace llvm; @@ -82,6 +83,12 @@ error_code object::createBinary(MemoryBuffer *Source, Result.swap(ret); return object_error::success; } + case sys::fs::file_magic::macho_universal_binary: { + OwningPtr ret(new MachOUniversalBinary(scopedSource.take(), ec)); + if (ec) return ec; + Result.swap(ret); + return object_error::success; + } case sys::fs::file_magic::coff_object: case sys::fs::file_magic::pecoff_executable: { OwningPtr ret( diff --git a/lib/Object/CMakeLists.txt b/lib/Object/CMakeLists.txt index cec0e283379..2c2cc8e4fb6 100644 --- a/lib/Object/CMakeLists.txt +++ b/lib/Object/CMakeLists.txt @@ -7,6 +7,7 @@ add_llvm_library(LLVMObject ELFYAML.cpp Error.cpp MachOObjectFile.cpp + MachOUniversal.cpp Object.cpp ObjectFile.cpp YAML.cpp diff --git a/lib/Object/Error.cpp b/lib/Object/Error.cpp index 7005a72d68b..47ce38c8883 100644 --- a/lib/Object/Error.cpp +++ b/lib/Object/Error.cpp @@ -34,6 +34,8 @@ std::string _object_error_category::message(int ev) const { object_error::Impl E = static_cast(ev); switch (E) { case object_error::success: return "Success"; + case object_error::arch_not_found: + return "No object file for requested architecture"; case object_error::invalid_file_type: return "The file was not recognized as a valid object file"; case object_error::parse_failed: diff --git a/lib/Object/MachOObjectFile.cpp b/lib/Object/MachOObjectFile.cpp index e62b5a48190..12090d6c300 100644 --- a/lib/Object/MachOObjectFile.cpp +++ b/lib/Object/MachOObjectFile.cpp @@ -1297,8 +1297,8 @@ StringRef MachOObjectFile::getFileFormatName() const { } } -unsigned MachOObjectFile::getArch() const { - switch (getCPUType(this)) { +Triple::ArchType MachOObjectFile::getArch(uint32_t CPUType) { + switch (CPUType) { case llvm::MachO::CPUTypeI386: return Triple::x86; case llvm::MachO::CPUTypeX86_64: @@ -1314,6 +1314,10 @@ unsigned MachOObjectFile::getArch() const { } } +unsigned MachOObjectFile::getArch() const { + return getArch(getCPUType(this)); +} + StringRef MachOObjectFile::getLoadName() const { // TODO: Implement report_fatal_error("get_load_name() unimplemented in MachOObjectFile"); diff --git a/lib/Object/MachOUniversal.cpp b/lib/Object/MachOUniversal.cpp new file mode 100644 index 00000000000..98f7198bdb1 --- /dev/null +++ b/lib/Object/MachOUniversal.cpp @@ -0,0 +1,139 @@ +//===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the MachOUniversalBinary class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/MachOUniversal.h" + +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace llvm; +using namespace object; + +template +static void SwapValue(T &Value) { + Value = sys::SwapByteOrder(Value); +} + +template +static void SwapStruct(T &Value); + +template<> +void SwapStruct(macho::FatHeader &H) { + SwapValue(H.Magic); + SwapValue(H.NumFatArch); +} + +template<> +void SwapStruct(macho::FatArchHeader &H) { + SwapValue(H.CPUType); + SwapValue(H.CPUSubtype); + SwapValue(H.Offset); + SwapValue(H.Size); + SwapValue(H.Align); +} + +template +static T getUniversalBinaryStruct(const char *Ptr) { + T Res; + memcpy(&Res, Ptr, sizeof(T)); + // Universal binary headers have big-endian byte order. + if (sys::IsLittleEndianHost) + SwapStruct(Res); + return Res; +} + +MachOUniversalBinary::ObjectForArch::ObjectForArch( + const MachOUniversalBinary *Parent, uint32_t Index) + : Parent(Parent), Index(Index) { + if (Parent == 0 || Index > Parent->getNumberOfObjects()) { + clear(); + } else { + // Parse object header. + StringRef ParentData = Parent->getData(); + const char *HeaderPos = ParentData.begin() + macho::FatHeaderSize + + Index * macho::FatArchHeaderSize; + Header = getUniversalBinaryStruct(HeaderPos); + if (ParentData.size() < Header.Offset + Header.Size) { + clear(); + } + } +} + +error_code MachOUniversalBinary::ObjectForArch::getAsObjectFile( + OwningPtr &Result) const { + if (Parent) { + StringRef ParentData = Parent->getData(); + StringRef ObjectData = ParentData.substr(Header.Offset, Header.Size); + Twine ObjectName = + Twine(Parent->getFileName()) + ":" + + Triple::getArchTypeName(MachOObjectFile::getArch(Header.CPUType)); + MemoryBuffer *ObjBuffer = MemoryBuffer::getMemBuffer( + ObjectData, ObjectName.str(), false); + if (ObjectFile *Obj = ObjectFile::createMachOObjectFile(ObjBuffer)) { + Result.reset(Obj); + return object_error::success; + } + } + return object_error::parse_failed; +} + +void MachOUniversalBinary::anchor() { } + +MachOUniversalBinary::MachOUniversalBinary(MemoryBuffer *Source, + error_code &ec) + : Binary(Binary::ID_MachOUniversalBinary, Source), + NumberOfObjects(0) { + if (Source->getBufferSize() < macho::FatHeaderSize) { + ec = object_error::invalid_file_type; + return; + } + // Check for magic value and sufficient header size. + StringRef Buf = getData(); + macho::FatHeader H = getUniversalBinaryStruct(Buf.begin()); + NumberOfObjects = H.NumFatArch; + uint32_t MinSize = macho::FatHeaderSize + + macho::FatArchHeaderSize * NumberOfObjects; + if (H.Magic != macho::HM_Universal || Buf.size() < MinSize) { + ec = object_error::parse_failed; + return; + } + ec = object_error::success; +} + +static bool getCTMForArch(Triple::ArchType Arch, mach::CPUTypeMachine &CTM) { + switch (Arch) { + case Triple::x86: CTM = mach::CTM_i386; return true; + case Triple::x86_64: CTM = mach::CTM_x86_64; return true; + case Triple::arm: CTM = mach::CTM_ARM; return true; + case Triple::sparc: CTM = mach::CTM_SPARC; return true; + case Triple::ppc: CTM = mach::CTM_PowerPC; return true; + case Triple::ppc64: CTM = mach::CTM_PowerPC64; return true; + default: return false; + } +} + +error_code +MachOUniversalBinary::getObjectForArch(Triple::ArchType Arch, + OwningPtr &Result) const { + mach::CPUTypeMachine CTM; + if (!getCTMForArch(Arch, CTM)) + return object_error::arch_not_found; + for (object_iterator I = begin_objects(), E = end_objects(); I != E; ++I) { + if (I->getCPUType() == static_cast(CTM)) + return I->getAsObjectFile(Result); + } + return object_error::arch_not_found; +} diff --git a/lib/Object/ObjectFile.cpp b/lib/Object/ObjectFile.cpp index 3ec29bf7e75..8dfc26508bc 100644 --- a/lib/Object/ObjectFile.cpp +++ b/lib/Object/ObjectFile.cpp @@ -46,6 +46,7 @@ ObjectFile *ObjectFile::createObjectFile(MemoryBuffer *Object) { case sys::fs::file_magic::unknown: case sys::fs::file_magic::bitcode: case sys::fs::file_magic::archive: + case sys::fs::file_magic::macho_universal_binary: return 0; case sys::fs::file_magic::elf_relocatable: case sys::fs::file_magic::elf_executable: diff --git a/lib/Support/PathV2.cpp b/lib/Support/PathV2.cpp index 24eac47eca0..05366202559 100644 --- a/lib/Support/PathV2.cpp +++ b/lib/Support/PathV2.cpp @@ -810,8 +810,7 @@ error_code has_magic(const Twine &path, const Twine &magic, bool &result) { // This is complicated by an overlap with Java class files. // See the Mach-O section in /usr/share/file/magic for details. if (Magic.size() >= 8 && Magic[7] < 43) - // FIXME: Universal Binary of any type. - return file_magic::macho_dynamically_linked_shared_lib; + return file_magic::macho_universal_binary; } break; diff --git a/test/Object/Inputs/macho-universal.x86_64.i386 b/test/Object/Inputs/macho-universal.x86_64.i386 new file mode 100755 index 00000000000..36d5fc29d68 Binary files /dev/null and b/test/Object/Inputs/macho-universal.x86_64.i386 differ diff --git a/test/Object/nm-universal-binary.test b/test/Object/nm-universal-binary.test new file mode 100644 index 00000000000..8febfdfab39 --- /dev/null +++ b/test/Object/nm-universal-binary.test @@ -0,0 +1,6 @@ +RUN: llvm-nm %p/Inputs/macho-universal.x86_64.i386 | FileCheck %s + +CHECK: macho-universal.x86_64.i386:x86_64 +CHECK: main +CHECK: macho-universal.x86_64.i386:i386 +CHECK: main diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index aa782aae40f..cb465207631 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -20,6 +20,7 @@ #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/Module.h" #include "llvm/Object/Archive.h" +#include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -402,6 +403,23 @@ static void DumpSymbolNamesFromFile(std::string &Filename) { } } } + } else if (magic == sys::fs::file_magic::macho_universal_binary) { + OwningPtr Bin; + if (error(object::createBinary(Buffer.take(), Bin), Filename)) + return; + + object::MachOUniversalBinary *UB = + cast(Bin.get()); + for (object::MachOUniversalBinary::object_iterator + I = UB->begin_objects(), + E = UB->end_objects(); + I != E; ++I) { + OwningPtr Obj; + if (!I->getAsObjectFile(Obj)) { + outs() << Obj->getFileName() << ":\n"; + DumpSymbolNamesFromObject(Obj.get()); + } + } } else if (magic.is_object()) { OwningPtr obj; if (error(object::createBinary(Buffer.take(), obj), Filename))