[lld] r219268 - [mach-o] Support fat archives

Nick Kledzik kledzik at apple.com
Tue Oct 7 18:48:10 PDT 2014


Author: kledzik
Date: Tue Oct  7 20:48:10 2014
New Revision: 219268

URL: http://llvm.org/viewvc/llvm-project?rev=219268&view=rev
Log:
[mach-o] Support fat archives

mach-o supports "fat" files which are a header/table-of-contents followed by a
concatenation of mach-o files (or archives of mach-o files) built for
different architectures.  Previously, the support for fat files was in the
MachOReader, but that only supported fat .o files and dylibs (not archives).

The fix is to put the fat handing into MachOFileNode.  That way any input file
kind (including archives) can be fat.  MachOFileNode selects the sub-range
of the fat file that matches the arch being linked and creates a MemoryBuffer
for just that subrange.

Added:
    lld/trunk/test/mach-o/Inputs/libfoo.a
    lld/trunk/test/mach-o/fat-archive.yaml
Modified:
    lld/trunk/include/lld/Driver/DarwinInputGraph.h
    lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h
    lld/trunk/lib/Driver/DarwinInputGraph.cpp
    lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
    lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h
    lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp

Modified: lld/trunk/include/lld/Driver/DarwinInputGraph.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/Driver/DarwinInputGraph.h?rev=219268&r1=219267&r2=219268&view=diff
==============================================================================
--- lld/trunk/include/lld/Driver/DarwinInputGraph.h (original)
+++ lld/trunk/include/lld/Driver/DarwinInputGraph.h Tue Oct  7 20:48:10 2014
@@ -43,6 +43,8 @@ public:
   }
 
 private:
+ void narrowFatBuffer(StringRef filePath);
+
   MachOLinkingContext &_context;
   bool _isWholeArchive;
 };

Modified: lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h?rev=219268&r1=219267&r2=219268&view=diff
==============================================================================
--- lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h (original)
+++ lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h Tue Oct  7 20:48:10 2014
@@ -234,7 +234,7 @@ public:
   StringRef binderSymbolName() const;
 
   /// Used to keep track of direct and indirect dylibs.
-  void registerDylib(mach_o::MachODylibFile *dylib);
+  void registerDylib(mach_o::MachODylibFile *dylib) const;
 
   /// Used to find indirect dylibs. Instantiates a MachODylibFile if one
   /// has not already been made for the requested dylib.  Uses -L and -F
@@ -244,6 +244,11 @@ public:
   /// Creates a copy (owned by this MachOLinkingContext) of a string.
   StringRef copy(StringRef str) { return str.copy(_allocator); }
 
+  /// If the memoryBuffer is a fat file with a slice for the current arch,
+  /// this method will return the offset and size of that slice.
+  bool sliceFromFatFile(const MemoryBuffer &mb, uint32_t &offset,
+                        uint32_t &size);
+
   static bool isThinObjectFile(StringRef path, Arch &arch);
   static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype);
   static Arch archFromName(StringRef archName);
@@ -307,8 +312,8 @@ private:
   mutable std::unique_ptr<mach_o::ArchHandler> _archHandler;
   mutable std::unique_ptr<Writer> _writer;
   std::vector<SectionAlign> _sectAligns;
-  llvm::StringMap<mach_o::MachODylibFile*> _pathToDylibMap;
-  std::set<mach_o::MachODylibFile*> _allDylibs;
+  mutable llvm::StringMap<mach_o::MachODylibFile*> _pathToDylibMap;
+  mutable std::set<mach_o::MachODylibFile*> _allDylibs;
   mutable std::vector<std::unique_ptr<class MachOFileNode>> _indirectDylibs;
   ExportMode _exportMode;
   llvm::StringSet<> _exportedSymbols;

Modified: lld/trunk/lib/Driver/DarwinInputGraph.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/Driver/DarwinInputGraph.cpp?rev=219268&r1=219267&r2=219268&view=diff
==============================================================================
--- lld/trunk/lib/Driver/DarwinInputGraph.cpp (original)
+++ lld/trunk/lib/Driver/DarwinInputGraph.cpp Tue Oct  7 20:48:10 2014
@@ -16,8 +16,6 @@
 #include "lld/Core/Reference.h"
 #include "lld/Core/SharedLibraryFile.h"
 
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-
 namespace lld {
 
 /// \brief Parse the input file to lld::File.
@@ -34,6 +32,8 @@ std::error_code MachOFileNode::parse(con
   if (ctx.logInputFiles())
     diagnostics << *filePath << "\n";
 
+  narrowFatBuffer(*filePath);
+
   std::vector<std::unique_ptr<File>> parsedFiles;
   if (_isWholeArchive) {
     std::error_code ec = ctx.registry().parseFile(_buffer, parsedFiles);
@@ -65,5 +65,22 @@ std::error_code MachOFileNode::parse(con
 }
 
 
+/// If buffer contains a fat file, find required arch in fat buffer and
+/// switch buffer to point to just that required slice.
+void MachOFileNode::narrowFatBuffer(StringRef filePath) {
+  // Check if buffer is a "fat" file that contains needed arch.
+  uint32_t offset;
+  uint32_t size;
+  if (!_context.sliceFromFatFile(*_buffer, offset, size)) {
+    return;
+  }
+  // Create new buffer containing just the needed slice.
+  auto subuf = MemoryBuffer::getFileSlice(filePath, size, offset);
+  if (subuf.getError())
+    return;
+  // The assignment to _buffer will release previous buffer.
+  _buffer = std::move(subuf.get());
+}
+
 
 } // end namesapce lld

Modified: lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp?rev=219268&r1=219267&r2=219268&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp Tue Oct  7 20:48:10 2014
@@ -136,6 +136,12 @@ bool MachOLinkingContext::isThinObjectFi
   return mach_o::normalized::isThinObjectFile(path, arch);
 }
 
+bool MachOLinkingContext::sliceFromFatFile(const MemoryBuffer &mb,
+                                           uint32_t &offset,
+                                           uint32_t &size) {
+  return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size);
+}
+
 MachOLinkingContext::MachOLinkingContext()
     : _outputMachOType(MH_EXECUTE), _outputMachOTypeStatic(false),
       _doNothing(false), _pie(false), _arch(arch_unknown), _os(OS::macOSX),
@@ -625,7 +631,7 @@ bool MachOLinkingContext::createImplicit
 }
 
 
-void MachOLinkingContext::registerDylib(MachODylibFile *dylib) {
+void MachOLinkingContext::registerDylib(MachODylibFile *dylib) const {
   _allDylibs.insert(dylib);
   _pathToDylibMap[dylib->installName()] = dylib;
   // If path is different than install name, register path too.

Modified: lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h?rev=219268&r1=219267&r2=219268&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h (original)
+++ lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h Tue Oct  7 20:48:10 2014
@@ -256,6 +256,12 @@ struct NormalizedFile {
 /// Tests if a file is a non-fat mach-o object file.
 bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch);
 
+/// If the buffer is a fat file with the request arch, then this function
+/// returns true with 'offset' and 'size' set to location of the arch slice
+/// within the buffer.  Otherwise returns false;
+bool sliceFromFatFile(const MemoryBuffer &mb, MachOLinkingContext::Arch arch,
+                       uint32_t &offset, uint32_t &size);
+
 /// Reads a mach-o file and produces an in-memory normalized view.
 ErrorOr<std::unique_ptr<NormalizedFile>>
 readBinary(std::unique_ptr<MemoryBuffer> &mb,

Modified: lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp?rev=219268&r1=219267&r2=219268&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp Tue Oct  7 20:48:10 2014
@@ -170,6 +170,35 @@ bool isThinObjectFile(StringRef path, Ma
   return true;
 }
 
+
+bool sliceFromFatFile(const MemoryBuffer &mb, MachOLinkingContext::Arch arch,
+                                             uint32_t &offset, uint32_t &size) {
+  const char *start = mb.getBufferStart();
+  const llvm::MachO::fat_header *fh =
+      reinterpret_cast<const llvm::MachO::fat_header *>(start);
+  if (readBigEndian(fh->magic) != llvm::MachO::FAT_MAGIC)
+    return false;
+  uint32_t nfat_arch = readBigEndian(fh->nfat_arch);
+  const fat_arch *fstart =
+      reinterpret_cast<const fat_arch *>(start + sizeof(fat_header));
+  const fat_arch *fend =
+      reinterpret_cast<const fat_arch *>(start + sizeof(fat_header) +
+                                         sizeof(fat_arch) * nfat_arch);
+  const uint32_t reqCpuType = MachOLinkingContext::cpuTypeFromArch(arch);
+  const uint32_t reqCpuSubtype = MachOLinkingContext::cpuSubtypeFromArch(arch);
+  for (const fat_arch *fa = fstart; fa < fend; ++fa) {
+    if ((readBigEndian(fa->cputype) == reqCpuType) &&
+        (readBigEndian(fa->cpusubtype) == reqCpuSubtype)) {
+      offset = readBigEndian(fa->offset);
+      size = readBigEndian(fa->size);
+      if ((offset + size) > mb.getBufferSize())
+        return false;
+      return true;
+    }
+  }
+  return false;
+}
+
 /// Reads a mach-o file and produces an in-memory normalized view.
 ErrorOr<std::unique_ptr<NormalizedFile>>
 readBinary(std::unique_ptr<MemoryBuffer> &mb,
@@ -179,41 +208,17 @@ readBinary(std::unique_ptr<MemoryBuffer>
 
   const char *start = mb->getBufferStart();
   size_t objSize = mb->getBufferSize();
-
-  // Determine endianness and pointer size for mach-o file.
   const mach_header *mh = reinterpret_cast<const mach_header *>(start);
-  bool isFat = mh->magic == llvm::MachO::FAT_CIGAM ||
-               mh->magic == llvm::MachO::FAT_MAGIC;
-  if (isFat) {
-    uint32_t cputype = MachOLinkingContext::cpuTypeFromArch(arch);
-    uint32_t cpusubtype = MachOLinkingContext::cpuSubtypeFromArch(arch);
-    const fat_header *fh = reinterpret_cast<const fat_header *>(start);
-    uint32_t nfat_arch = readBigEndian(fh->nfat_arch);
-    const fat_arch *fa =
-        reinterpret_cast<const fat_arch *>(start + sizeof(fat_header));
-    bool foundArch = false;
-    while (nfat_arch-- > 0) {
-      if (readBigEndian(fa->cputype) == cputype &&
-          readBigEndian(fa->cpusubtype) == cpusubtype) {
-        foundArch = true;
-        break;
-      }
-      fa++;
-    }
-    if (!foundArch) {
-      return make_dynamic_error_code(Twine("file does not contain required"
-                                    " architecture ("
-                                    + MachOLinkingContext::nameFromArch(arch)
-                                    + ")" ));
-    }
-    objSize = readBigEndian(fa->size);
-    uint32_t offset = readBigEndian(fa->offset);
-    if ((offset + objSize) > mb->getBufferSize())
-      return make_error_code(llvm::errc::executable_format_error);
-    start += offset;
+
+  uint32_t sliceOffset;
+  uint32_t sliceSize;
+  if (sliceFromFatFile(*mb, arch, sliceOffset, sliceSize)) {
+    start = &start[sliceOffset];
+    objSize = sliceSize;
     mh = reinterpret_cast<const mach_header *>(start);
   }
 
+  // Determine endianness and pointer size for mach-o file.
   bool is64, swap;
   if (!isMachOHeader(mh, is64, swap))
     return make_error_code(llvm::errc::executable_format_error);
@@ -501,18 +506,20 @@ readBinary(std::unique_ptr<MemoryBuffer>
   return std::move(f);
 }
 
-
 class MachOReader : public Reader {
 public:
   MachOReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
 
   bool canParse(file_magic magic, StringRef ext,
                 const MemoryBuffer &mb) const override {
-    if (magic != llvm::sys::fs::file_magic::macho_object &&
-        magic != llvm::sys::fs::file_magic::macho_universal_binary &&
-        magic != llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib)
+    switch (magic) {
+    case llvm::sys::fs::file_magic::macho_object:
+    case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib:
+    case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib_stub:
+      return (mb.getBufferSize() > 32);
+    default:
       return false;
-    return (mb.getBufferSize() > 32);
+    }
   }
 
   std::error_code
@@ -548,5 +555,6 @@ void Registry::addSupportMachOObjects(Ma
                            new mach_o::MachOYamlIOTaggedDocumentHandler(arch)));
 }
 
+
 } // namespace lld
 

Added: lld/trunk/test/mach-o/Inputs/libfoo.a
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/mach-o/Inputs/libfoo.a?rev=219268&view=auto
==============================================================================
Binary files lld/trunk/test/mach-o/Inputs/libfoo.a (added) and lld/trunk/test/mach-o/Inputs/libfoo.a Tue Oct  7 20:48:10 2014 differ

Added: lld/trunk/test/mach-o/fat-archive.yaml
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/mach-o/fat-archive.yaml?rev=219268&view=auto
==============================================================================
--- lld/trunk/test/mach-o/fat-archive.yaml (added)
+++ lld/trunk/test/mach-o/fat-archive.yaml Tue Oct  7 20:48:10 2014
@@ -0,0 +1,45 @@
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t \
+# RUN:    -L %p/Inputs -lfoo %p/Inputs/libSystem.yaml
+# RUN: llvm-nm -m -n %t | FileCheck %s
+#
+# Test that fat archives are handled.
+#
+
+--- !mach-o
+arch:            x86_64
+file-type:       MH_OBJECT
+flags:           [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+  - segment:         __TEXT
+    section:         __text
+    type:            S_REGULAR
+    attributes:      [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+    alignment:       4
+    address:         0x0000000000000000
+    content:         [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10, 
+                       0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0, 
+                       0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 
+                       0x48, 0x83, 0xC4, 0x10, 0x5D, 0xC3 ]
+    relocations:     
+      - offset:          0x00000012
+        type:            X86_64_RELOC_BRANCH
+        length:          2
+        pc-rel:          true
+        extern:          true
+        symbol:          1
+global-symbols:
+  - name:            _main
+    type:            N_SECT
+    scope:           [ N_EXT ]
+    sect:            1
+    value:           0x0000000000000000
+undefined-symbols:
+  - name:            _foo
+    type:            N_UNDF
+    scope:           [ N_EXT ]
+    value:           0x0000000000000000
+
+...
+
+# CHECK:	{{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK:	{{[0-9a-f]+}} (__TEXT,__text) external _foo





More information about the llvm-commits mailing list