diff options
author | Daniel Dunbar <daniel@zuster.org> | 2010-11-27 07:19:41 +0000 |
---|---|---|
committer | Daniel Dunbar <daniel@zuster.org> | 2010-11-27 07:19:41 +0000 |
commit | a956d8b71755b908d8a150736e8857d2214060c6 (patch) | |
tree | 6ac44d294bb7e144b41f7316b518250c8b07a61d | |
parent | bce55776af16b70408e2ae2e6d91f8ac1e43f6a7 (diff) | |
download | llvm-a956d8b71755b908d8a150736e8857d2214060c6.tar.gz llvm-a956d8b71755b908d8a150736e8857d2214060c6.tar.bz2 llvm-a956d8b71755b908d8a150736e8857d2214060c6.tar.xz |
Object/Mach-O: Add header and load command information.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@120198 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/llvm/Object/MachOFormat.h | 41 | ||||
-rw-r--r-- | include/llvm/Object/MachOObject.h | 47 | ||||
-rw-r--r-- | lib/Object/MachOObject.cpp | 83 | ||||
-rw-r--r-- | tools/macho-dump/macho-dump.cpp | 81 |
4 files changed, 228 insertions, 24 deletions
diff --git a/include/llvm/Object/MachOFormat.h b/include/llvm/Object/MachOFormat.h index 61a19643f0..87e4f4241c 100644 --- a/include/llvm/Object/MachOFormat.h +++ b/include/llvm/Object/MachOFormat.h @@ -22,6 +22,8 @@ #ifndef LLVM_OBJECT_MACHOFORMAT_H #define LLVM_OBJECT_MACHOFORMAT_H +#include "llvm/System/DataTypes.h" + namespace llvm { namespace object { @@ -83,13 +85,6 @@ namespace mach { /// Format information for Mach object files. namespace macho { - /// \brief Constants for header magic field. - enum HeaderMagic { - HM_Object32 = 0xFEEDFACE, ///< 32-bit mach object file - HM_Object64 = 0xFEEDFACF, ///< 64-bit mach object file - HM_Universal = 0xCAFEBABE ///< Universal object file - }; - /// \brief Constants for structure sizes. enum StructureSizes { Header32Size = 28, @@ -105,6 +100,29 @@ namespace macho { RelocationInfoSize = 8 }; + /// \brief Constants for header magic field. + enum HeaderMagic { + HM_Object32 = 0xFEEDFACE, ///< 32-bit mach object file + HM_Object64 = 0xFEEDFACF, ///< 64-bit mach object file + HM_Universal = 0xCAFEBABE ///< Universal object file + }; + + /// \brief Header common to all Mach object files. + struct Header { + uint32_t Magic; + uint32_t CPUType; + uint32_t CPUSubtype; + uint32_t FileType; + uint32_t NumLoadCommands; + uint32_t SizeOfLoadCommands; + uint32_t Flags; + }; + + /// \brief Extended header for 64-bit object files. + struct Header64Ext { + uint32_t Reserved; + }; + // See <mach-o/loader.h>. enum HeaderFileType { HFT_Object = 0x1 @@ -118,7 +136,14 @@ namespace macho { LCT_Segment = 0x1, LCT_Symtab = 0x2, LCT_Dysymtab = 0xb, - LCT_Segment64 = 0x19 + LCT_Segment64 = 0x19, + LCT_UUID = 0x1b + }; + + /// \brief Load command structure. + struct LoadCommand { + uint32_t Type; + uint32_t Size; }; // See <mach-o/nlist.h>. diff --git a/include/llvm/Object/MachOObject.h b/include/llvm/Object/MachOObject.h index 71164ac864..4900768681 100644 --- a/include/llvm/Object/MachOObject.h +++ b/include/llvm/Object/MachOObject.h @@ -12,6 +12,7 @@ #include <string> #include "llvm/ADT/OwningPtr.h" +#include "llvm/Object/MachOFormat.h" namespace llvm { @@ -40,6 +41,13 @@ namespace object { // objects which are in the current address space. class MachOObject { public: + struct LoadCommandInfo { + /// The load command information. + macho::LoadCommand Command; + + /// The offset to the start of the load command in memory. + uint64_t Offset; + }; private: OwningPtr<MemoryBuffer> Buffer; @@ -48,11 +56,23 @@ private: bool IsLittleEndian; /// Whether the object is 64-bit. bool Is64Bit; + /// Whether the object is swapped endianness from the host. + bool IsSwappedEndian; + + /// The cached information on the load commands. + LoadCommandInfo *LoadCommands; + mutable unsigned NumLoadedCommands; + + /// The cached copy of the header. + macho::Header Header; + macho::Header64Ext Header64Ext; private: MachOObject(MemoryBuffer *Buffer, bool IsLittleEndian, bool Is64Bit); public: + ~MachOObject(); + /// \brief Load a Mach-O object from a MemoryBuffer object. /// /// \param Buffer - The buffer to load the object from. This routine takes @@ -64,8 +84,33 @@ public: static MachOObject *LoadFromBuffer(MemoryBuffer *Buffer, std::string *ErrorStr = 0); - /// @name Object Header Information + /// @name File Information /// @{ + + bool isLittleEndian() const { return IsLittleEndian; } + bool is64Bit() const { return Is64Bit; } + + unsigned getHeaderSize() const { + return Is64Bit ? macho::Header64Size : macho::Header32Size; + } + + /// @} + /// @name Object Header Access + /// @{ + + const macho::Header &getHeader() const { return Header; } + const macho::Header64Ext &getHeader64Ext() const { + assert(is64Bit() && "Invalid access!"); + return Header64Ext; + } + + /// @} + /// @name Object Structure Access + /// @{ + + /// \brief Retrieve the information for the given load command. + const LoadCommandInfo &getLoadCommandInfo(unsigned Index) const; + /// @} }; diff --git a/lib/Object/MachOObject.cpp b/lib/Object/MachOObject.cpp index de87605e77..5c17dfcfe9 100644 --- a/lib/Object/MachOObject.cpp +++ b/lib/Object/MachOObject.cpp @@ -10,13 +10,49 @@ #include "llvm/Object/MachOObject.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/Host.h" +#include "llvm/System/SwapByteOrder.h" using namespace llvm; -using namespace object; +using namespace llvm::object; + +template<typename T> +static void SwapValue(T &Value) { + Value = sys::SwapByteOrder(Value); +} MachOObject::MachOObject(MemoryBuffer *Buffer_, bool IsLittleEndian_, bool Is64Bit_) - : Buffer(Buffer_), IsLittleEndian(IsLittleEndian_), Is64Bit(Is64Bit_) { + : Buffer(Buffer_), IsLittleEndian(IsLittleEndian_), Is64Bit(Is64Bit_), + IsSwappedEndian(IsLittleEndian != sys::isLittleEndianHost()), + LoadCommands(0), NumLoadedCommands(0) { + // Load the common header. + memcpy(&Header, Buffer->getBuffer().data(), sizeof(Header)); + if (IsSwappedEndian) { + SwapValue(Header.Magic); + SwapValue(Header.CPUType); + SwapValue(Header.CPUSubtype); + SwapValue(Header.FileType); + SwapValue(Header.NumLoadCommands); + SwapValue(Header.SizeOfLoadCommands); + SwapValue(Header.Flags); + } + + if (is64Bit()) { + memcpy(&Header64Ext, Buffer->getBuffer().data() + sizeof(Header), + sizeof(Header64Ext)); + if (IsSwappedEndian) { + SwapValue(Header64Ext.Reserved); + } + } + + // Create the load command array if sane. + if (getHeader().NumLoadCommands < (1 << 20)) + LoadCommands = new LoadCommandInfo[getHeader().NumLoadCommands]; +} + +MachOObject::~MachOObject() { + delete LoadCommands; } MachOObject *MachOObject::LoadFromBuffer(MemoryBuffer *Buffer, @@ -33,13 +69,54 @@ MachOObject *MachOObject::LoadFromBuffer(MemoryBuffer *Buffer, IsLittleEndian = true; Is64Bit = true; } else { - *ErrorStr = "not a Mach object file"; + if (ErrorStr) *ErrorStr = "not a Mach object file (invalid magic)"; + return 0; + } + + // Ensure that the at least the full header is present. + unsigned HeaderSize = Is64Bit ? macho::Header64Size : macho::Header32Size; + if (Buffer->getBufferSize() < HeaderSize) { + if (ErrorStr) *ErrorStr = "not a Mach object file (invalid header)"; return 0; } OwningPtr<MachOObject> Object(new MachOObject(Buffer, IsLittleEndian, Is64Bit)); + // Check for bogus number of load commands. + if (Object->getHeader().NumLoadCommands >= (1 << 20)) { + if (ErrorStr) *ErrorStr = "not a Mach object file (unreasonable header)"; + return 0; + } + if (ErrorStr) *ErrorStr = ""; return Object.take(); } + +const MachOObject::LoadCommandInfo & +MachOObject::getLoadCommandInfo(unsigned Index) const { + assert(Index < getHeader().NumLoadCommands && "Invalid index!"); + + // Load the command, if necessary. + if (Index >= NumLoadedCommands) { + uint64_t Offset; + if (Index == 0) { + Offset = getHeaderSize(); + } else { + const LoadCommandInfo &Prev = getLoadCommandInfo(Index - 1); + Offset = Prev.Offset + Prev.Command.Size; + } + + LoadCommandInfo &Info = LoadCommands[Index]; + memcpy(&Info.Command, Buffer->getBuffer().data() + Offset, + sizeof(macho::LoadCommand)); + if (IsSwappedEndian) { + SwapValue(Info.Command.Type); + SwapValue(Info.Command.Size); + } + Info.Offset = Offset; + NumLoadedCommands = Index + 1; + } + + return LoadCommands[Index]; +} diff --git a/tools/macho-dump/macho-dump.cpp b/tools/macho-dump/macho-dump.cpp index 945406a501..8bfd1890c6 100644 --- a/tools/macho-dump/macho-dump.cpp +++ b/tools/macho-dump/macho-dump.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Object/MachOObject.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" @@ -26,8 +27,62 @@ static cl::opt<bool> DumpSectionData("dump-section-data", cl::desc("Dump the contents of sections"), cl::init(false)); +/// + +static const char *ProgramName; + +static void Message(const char *Type, const Twine &Msg) { + errs() << ProgramName << ": " << Type << ": " << Msg << "\n"; +} + +static int Error(const Twine &Msg) { + Message("error", Msg); + return 1; +} + +static void Warning(const Twine &Msg) { + Message("warning", Msg); +} + +/// + +static int DumpHeader(MachOObject &Obj) { + // Read the header. + const macho::Header &Hdr = Obj.getHeader(); + outs() << "('cputype', " << Hdr.CPUType << ")\n"; + outs() << "('cpusubtype', " << Hdr.CPUSubtype << ")\n"; + outs() << "('filetype', " << Hdr.FileType << ")\n"; + outs() << "('num_load_commands', " << Hdr.NumLoadCommands << ")\n"; + outs() << "('load_commands_size', " << Hdr.SizeOfLoadCommands << ")\n"; + outs() << "('flag', " << Hdr.Flags << ")\n"; + + // Print extended header if 64-bit. + if (Obj.is64Bit()) { + const macho::Header64Ext &Hdr64 = Obj.getHeader64Ext(); + outs() << "('reserved', " << Hdr64.Reserved << ")\n"; + } + + return 0; +} + +static int DumpLoadCommand(MachOObject &Obj, unsigned Index) { + const MachOObject::LoadCommandInfo &LCI = Obj.getLoadCommandInfo(Index); + + outs() << " # Load Command " << Index << "\n" + << " (('command', " << LCI.Command.Type << ")\n" + << " ('size', " << LCI.Command.Size << ")\n"; + switch (LCI.Command.Type) { + default: + Warning("unknown load command: " + Twine(LCI.Command.Type)); + break; + } + outs() << " ),\n"; + + return 0; +} + int main(int argc, char **argv) { - const char *ProgramName = argv[0]; + ProgramName = argv[0]; llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. cl::ParseCommandLineOptions(argc, argv, "llvm Mach-O dumping tool\n"); @@ -36,21 +91,23 @@ int main(int argc, char **argv) { std::string ErrorStr; OwningPtr<MemoryBuffer> InputBuffer( MemoryBuffer::getFileOrSTDIN(InputFile, &ErrorStr)); - if (!InputBuffer) { - errs() << ProgramName << ": " << "unable to read input: '" - << ErrorStr << "'\n"; - return 1; - } + if (!InputBuffer) + return Error("unable to read input: '" + ErrorStr + "'"); // Construct the Mach-O wrapper object. OwningPtr<MachOObject> InputObject( MachOObject::LoadFromBuffer(InputBuffer.take(), &ErrorStr)); - if (!InputObject) { - errs() << ProgramName << ": " << "unable to load object: '" - << ErrorStr << "'\n"; - return 1; - } + if (!InputObject) + return Error("unable to load object: '" + ErrorStr + "'"); + + if (int Res = DumpHeader(*InputObject)) + return Res; + + // Print the load commands. + outs() << "('load_commands', [\n"; + for (unsigned i = 0; i != InputObject->getHeader().NumLoadCommands; ++i) + DumpLoadCommand(*InputObject, i); + outs() << "])\n"; - errs() << "ok\n"; return 0; } |