[lld] r217189 - [mach-o] Let darwin driver infer arch from .o files if -arch not used.

Nick Kledzik kledzik at apple.com
Thu Sep 4 13:08:31 PDT 2014


Author: kledzik
Date: Thu Sep  4 15:08:30 2014
New Revision: 217189

URL: http://llvm.org/viewvc/llvm-project?rev=217189&view=rev
Log:
[mach-o] Let darwin driver infer arch from .o files if -arch not used.

Mach-O has a "fat" (or "universal") variant where the same contents built for
different architectures are concatenated into one file with a table-of-contents
header at the start.  But this leaves a dilemma for the linker - which
architecture to use.

Normally, the linker command line -arch is used to force which slice of any fat
files are used.  The clang compiler always passes -arch to the linker when
invoking it.  But some Makefiles invoke the linker directly and don’t specify
the -arch option.  For those cases, the linker scans all input files in command
line order and finds the first non-fat object file.  Whatever architecture it
is becomes the architecture for the link.

Added:
    lld/trunk/test/mach-o/infer-arch.yaml
Modified:
    lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h
    lld/trunk/lib/Driver/DarwinLdDriver.cpp
    lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
    lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h
    lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
    lld/trunk/unittests/DriverTests/DarwinLdDriverTest.cpp

Modified: lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h?rev=217189&r1=217188&r2=217189&view=diff
==============================================================================
--- lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h (original)
+++ lld/trunk/include/lld/ReaderWriter/MachOLinkingContext.h Thu Sep  4 15:08:30 2014
@@ -222,6 +222,7 @@ public:
   /// Creates a copy (owned by this MachOLinkingContext) of a string.
   StringRef copy(StringRef str) { return str.copy(_allocator); }
 
+  static bool isThinObjectFile(StringRef path, Arch &arch);
   static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype);
   static Arch archFromName(StringRef archName);
   static StringRef nameFromArch(Arch arch);

Modified: lld/trunk/lib/Driver/DarwinLdDriver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/Driver/DarwinLdDriver.cpp?rev=217189&r1=217188&r2=217189&view=diff
==============================================================================
--- lld/trunk/lib/Driver/DarwinLdDriver.cpp (original)
+++ lld/trunk/lib/Driver/DarwinLdDriver.cpp Thu Sep  4 15:08:30 2014
@@ -243,6 +243,20 @@ bool DarwinLdDriver::parse(int argc, con
       return false;
     }
   }
+  // If no -arch specified, scan input files to find first non-fat .o file.
+  if ((arch == MachOLinkingContext::arch_unknown)
+      && !parsedArgs->getLastArg(OPT_test_file_usage)) {
+    for (auto &inFile: parsedArgs->filtered(OPT_INPUT)) {
+      // This is expensive because it opens and maps the file.  But that is
+      // ok because no -arch is rare.
+      if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch))
+        break;
+    }
+    if (arch == MachOLinkingContext::arch_unknown) {
+      diagnostics << "error: -arch not specified and could not be inferred\n";
+      return false;
+    }
+  }
 
   // Handle -macosx_version_min or -ios_version_min
   MachOLinkingContext::OS os = MachOLinkingContext::OS::macOSX;

Modified: lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp?rev=217189&r1=217188&r2=217189&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/MachOLinkingContext.cpp Thu Sep  4 15:08:30 2014
@@ -11,6 +11,7 @@
 
 #include "ArchHandler.h"
 #include "File.h"
+#include "MachONormalizedFile.h"
 #include "MachOPasses.h"
 
 #include "lld/Core/PassManager.h"
@@ -125,6 +126,10 @@ uint32_t MachOLinkingContext::cpuSubtype
   llvm_unreachable("Unknown arch type");
 }
 
+bool MachOLinkingContext::isThinObjectFile(StringRef path, Arch &arch) {
+  return mach_o::normalized::isThinObjectFile(path, arch);
+}
+
 MachOLinkingContext::MachOLinkingContext()
     : _outputMachOType(MH_EXECUTE), _outputMachOTypeStatic(false),
       _doNothing(false), _arch(arch_unknown), _os(OS::macOSX), _osMinVersion(0),

Modified: lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h?rev=217189&r1=217188&r2=217189&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h (original)
+++ lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFile.h Thu Sep  4 15:08:30 2014
@@ -252,6 +252,8 @@ struct NormalizedFile {
   BumpPtrAllocator            ownedAllocations;
 };
 
+/// Tests if a file is a non-fat mach-o object file.
+bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch);
 
 /// Reads a mach-o file and produces an in-memory normalized view.
 ErrorOr<std::unique_ptr<NormalizedFile>>

Modified: lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp?rev=217189&r1=217188&r2=217189&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp (original)
+++ lld/trunk/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp Thu Sep  4 15:08:30 2014
@@ -117,6 +117,59 @@ template <typename T> static T readBigEn
   return t;
 }
 
+
+static bool isMachOHeader(const mach_header *mh, bool &is64, bool &swap) {
+  switch (mh->magic) {
+  case llvm::MachO::MH_MAGIC:
+    is64 = false;
+    swap = false;
+    return true;
+  case llvm::MachO::MH_MAGIC_64:
+    is64 = true;
+    swap = false;
+    return true;
+  case llvm::MachO::MH_CIGAM:
+    is64 = false;
+    swap = true;
+    return true;
+  case llvm::MachO::MH_CIGAM_64:
+    is64 = true;
+    swap = true;
+    return true;
+  default:
+    return false;
+  }
+}
+
+
+bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch) {
+  // Try opening and mapping file at path.
+  ErrorOr<std::unique_ptr<MemoryBuffer>> b = MemoryBuffer::getFileOrSTDIN(path);
+  if (b.getError())
+    return false;
+
+  // If file length < 32 it is too small to be mach-o object file.
+  StringRef fileBuffer = b->get()->getBuffer();
+  if (fileBuffer.size() < 32)
+    return false;
+
+  // If file buffer does not start with MH_MAGIC (and variants), not obj file.
+  const mach_header *mh = reinterpret_cast<const mach_header *>(
+                                                            fileBuffer.begin());
+  bool is64, swap;
+  if (!isMachOHeader(mh, is64, swap))
+    return false;
+
+  // If not MH_OBJECT, not object file.
+  if (read32(swap, mh->filetype) != MH_OBJECT)
+    return false;
+
+  // Lookup up arch from cpu/subtype pair.
+  arch = MachOLinkingContext::archFromCpuType(read32(swap, mh->cputype),
+                                              read32(swap, mh->cpusubtype));
+  return true;
+}
+
 /// Reads a mach-o file and produces an in-memory normalized view.
 ErrorOr<std::unique_ptr<NormalizedFile>>
 readBinary(std::unique_ptr<MemoryBuffer> &mb,
@@ -162,26 +215,8 @@ readBinary(std::unique_ptr<MemoryBuffer>
   }
 
   bool is64, swap;
-  switch (mh->magic) {
-  case llvm::MachO::MH_MAGIC:
-    is64 = false;
-    swap = false;
-    break;
-  case llvm::MachO::MH_MAGIC_64:
-    is64 = true;
-    swap = false;
-    break;
-  case llvm::MachO::MH_CIGAM:
-    is64 = false;
-    swap = true;
-    break;
-  case llvm::MachO::MH_CIGAM_64:
-    is64 = true;
-    swap = true;
-    break;
-  default:
+  if (!isMachOHeader(mh, is64, swap))
     return make_error_code(llvm::errc::executable_format_error);
-  }
 
   // Endian swap header, if needed.
   mach_header headerCopy;

Added: lld/trunk/test/mach-o/infer-arch.yaml
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/mach-o/infer-arch.yaml?rev=217189&view=auto
==============================================================================
--- lld/trunk/test/mach-o/infer-arch.yaml (added)
+++ lld/trunk/test/mach-o/infer-arch.yaml Thu Sep  4 15:08:30 2014
@@ -0,0 +1,29 @@
+# RUN: lld -flavor darwin -arch i386 -macosx_version_min 10.8 %s -r -o %t \
+# RUN: && lld -flavor darwin -r %t -o %t2 -print_atoms | FileCheck %s
+#
+# Test linker can detect architecture without -arch option.
+#
+
+--- !mach-o
+arch:            x86
+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 ]
+    address:         0x0000000000000000
+    content:         [ 0xC3 ]
+global-symbols:
+  - name:            _foo
+    type:            N_SECT
+    scope:           [ N_EXT ]
+    sect:            1
+    value:           0x0000000000000000
+
+...
+
+
+# CHECK: defined-atoms:   
+# CHECK:  - name:            _foo

Modified: lld/trunk/unittests/DriverTests/DarwinLdDriverTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/unittests/DriverTests/DarwinLdDriverTest.cpp?rev=217189&r1=217188&r2=217189&view=diff
==============================================================================
--- lld/trunk/unittests/DriverTests/DarwinLdDriverTest.cpp (original)
+++ lld/trunk/unittests/DriverTests/DarwinLdDriverTest.cpp Thu Sep  4 15:08:30 2014
@@ -30,7 +30,7 @@ protected:
 }
 
 TEST_F(DarwinLdParserTest, Basic) {
-  EXPECT_TRUE(parse("ld", "foo.o", "bar.o", nullptr));
+  EXPECT_TRUE(parse("ld", "foo.o", "bar.o", "-arch", "i386", nullptr));
   EXPECT_FALSE(_context.allowRemainingUndefines());
   EXPECT_FALSE(_context.deadStrip());
   EXPECT_EQ(2, inputFileCount());
@@ -39,45 +39,40 @@ TEST_F(DarwinLdParserTest, Basic) {
 }
 
 TEST_F(DarwinLdParserTest, Output) {
-  EXPECT_TRUE(parse("ld", "-o", "my.out", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-o", "my.out", "foo.o", "-arch", "i386", nullptr));
   EXPECT_EQ("my.out", _context.outputPath());
 }
 
 TEST_F(DarwinLdParserTest, Dylib) {
-  EXPECT_TRUE(parse("ld", "-dylib", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-dylib", "foo.o", "-arch", "i386", nullptr));
   EXPECT_EQ(llvm::MachO::MH_DYLIB, _context.outputMachOType());
 }
 
 TEST_F(DarwinLdParserTest, Relocatable) {
-  EXPECT_TRUE(parse("ld", "-r", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-r", "foo.o", "-arch", "i386", nullptr));
   EXPECT_EQ(llvm::MachO::MH_OBJECT, _context.outputMachOType());
 }
 
 TEST_F(DarwinLdParserTest, Bundle) {
-  EXPECT_TRUE(parse("ld", "-bundle", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-bundle", "foo.o", "-arch", "i386", nullptr));
   EXPECT_EQ(llvm::MachO::MH_BUNDLE, _context.outputMachOType());
 }
 
 TEST_F(DarwinLdParserTest, Preload) {
-  EXPECT_TRUE(parse("ld", "-preload", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-preload", "foo.o", "-arch", "i386", nullptr));
   EXPECT_EQ(llvm::MachO::MH_PRELOAD, _context.outputMachOType());
 }
 
 TEST_F(DarwinLdParserTest, Static) {
-  EXPECT_TRUE(parse("ld", "-static", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-static", "foo.o", "-arch", "i386", nullptr));
   EXPECT_EQ(llvm::MachO::MH_EXECUTE, _context.outputMachOType());
 }
 
 TEST_F(DarwinLdParserTest, Entry) {
-  EXPECT_TRUE(parse("ld", "-e", "entryFunc", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-e", "entryFunc", "foo.o", "-arch", "i386",nullptr));
   EXPECT_EQ("entryFunc", _context.entrySymbolName());
 }
 
-TEST_F(DarwinLdParserTest, OutputPath) {
-  EXPECT_TRUE(parse("ld", "-o", "foo", "foo.o", nullptr));
-  EXPECT_EQ("foo", _context.outputPath());
-}
-
 TEST_F(DarwinLdParserTest, DeadStrip) {
   EXPECT_TRUE(parse("ld", "-arch", "x86_64", "-dead_strip", "foo.o", nullptr));
   EXPECT_TRUE(_context.deadStrip());
@@ -130,42 +125,48 @@ TEST_F(DarwinLdParserTest, Arch_armv7s)
 }
 
 TEST_F(DarwinLdParserTest, MinMacOSX10_7) {
-  EXPECT_TRUE(parse("ld", "-macosx_version_min", "10.7", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-macosx_version_min", "10.7", "foo.o",
+                    "-arch", "x86_64", nullptr));
   EXPECT_EQ(MachOLinkingContext::OS::macOSX, _context.os());
   EXPECT_TRUE(_context.minOS("10.7", ""));
   EXPECT_FALSE(_context.minOS("10.8", ""));
 }
 
 TEST_F(DarwinLdParserTest, MinMacOSX10_8) {
-  EXPECT_TRUE(parse("ld", "-macosx_version_min", "10.8.3", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-macosx_version_min", "10.8.3", "foo.o",
+                    "-arch", "x86_64", nullptr));
   EXPECT_EQ(MachOLinkingContext::OS::macOSX, _context.os());
   EXPECT_TRUE(_context.minOS("10.7", ""));
   EXPECT_TRUE(_context.minOS("10.8", ""));
 }
 
 TEST_F(DarwinLdParserTest, iOS5) {
-  EXPECT_TRUE(parse("ld", "-ios_version_min", "5.0", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-ios_version_min", "5.0", "foo.o",
+                    "-arch", "armv7", nullptr));
   EXPECT_EQ(MachOLinkingContext::OS::iOS, _context.os());
   EXPECT_TRUE(_context.minOS("", "5.0"));
   EXPECT_FALSE(_context.minOS("", "6.0"));
 }
 
 TEST_F(DarwinLdParserTest, iOS6) {
-  EXPECT_TRUE(parse("ld", "-ios_version_min", "6.0", "foo.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-ios_version_min", "6.0", "foo.o", "-arch", "armv7",
+                    nullptr));
   EXPECT_EQ(MachOLinkingContext::OS::iOS, _context.os());
   EXPECT_TRUE(_context.minOS("", "5.0"));
   EXPECT_TRUE(_context.minOS("", "6.0"));
 }
 
 TEST_F(DarwinLdParserTest, iOS_Simulator5) {
-  EXPECT_TRUE(parse("ld", "-ios_simulator_version_min", "5.0", "a.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-ios_simulator_version_min", "5.0", "a.o",
+                    "-arch", "i386", nullptr));
   EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _context.os());
   EXPECT_TRUE(_context.minOS("", "5.0"));
   EXPECT_FALSE(_context.minOS("", "6.0"));
 }
 
 TEST_F(DarwinLdParserTest, iOS_Simulator6) {
-  EXPECT_TRUE(parse("ld", "-ios_simulator_version_min", "6.0", "a.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-ios_simulator_version_min", "6.0", "a.o",
+                    "-arch", "i386", nullptr));
   EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _context.os());
   EXPECT_TRUE(_context.minOS("", "5.0"));
   EXPECT_TRUE(_context.minOS("", "6.0"));
@@ -173,58 +174,67 @@ TEST_F(DarwinLdParserTest, iOS_Simulator
 
 TEST_F(DarwinLdParserTest, compatibilityVersion) {
   EXPECT_TRUE(
-      parse("ld", "-dylib", "-compatibility_version", "1.2.3", "a.o", nullptr));
+      parse("ld", "-dylib", "-compatibility_version", "1.2.3", "a.o",
+             "-arch", "i386",nullptr));
   EXPECT_EQ(_context.compatibilityVersion(), 0x10203U);
 }
 
 TEST_F(DarwinLdParserTest, compatibilityVersionInvalidType) {
   EXPECT_FALSE(parse("ld", "-bundle", "-compatibility_version", "1.2.3", "a.o",
-                     nullptr));
+                     "-arch", "i386",nullptr));
 }
 
 TEST_F(DarwinLdParserTest, compatibilityVersionInvalidValue) {
   EXPECT_FALSE(parse("ld", "-bundle", "-compatibility_version", "1,2,3", "a.o",
-                     nullptr));
+                     "-arch", "i386", nullptr));
 }
 
 TEST_F(DarwinLdParserTest, currentVersion) {
   EXPECT_TRUE(
-      parse("ld", "-dylib", "-current_version", "1.2.3", "a.o", nullptr));
+      parse("ld", "-dylib", "-current_version", "1.2.3", "a.o", "-arch", "i386",
+            nullptr));
   EXPECT_EQ(_context.currentVersion(), 0x10203U);
 }
 
 TEST_F(DarwinLdParserTest, currentVersionInvalidType) {
   EXPECT_FALSE(
-      parse("ld", "-bundle", "-current_version", "1.2.3", "a.o", nullptr));
+      parse("ld", "-bundle", "-current_version", "1.2.3", "a.o",
+            "-arch", "i386", nullptr));
 }
 
 TEST_F(DarwinLdParserTest, currentVersionInvalidValue) {
   EXPECT_FALSE(
-      parse("ld", "-bundle", "-current_version", "1,2,3", "a.o", nullptr));
+      parse("ld", "-bundle", "-current_version", "1,2,3", "a.o",
+            "-arch", "i386", nullptr));
 }
 
 TEST_F(DarwinLdParserTest, bundleLoader) {
   EXPECT_TRUE(
-      parse("ld", "-bundle", "-bundle_loader", "/bin/ls", "a.o", nullptr));
+      parse("ld", "-bundle", "-bundle_loader", "/bin/ls", "a.o",
+             "-arch", "i386", nullptr));
   EXPECT_EQ(_context.bundleLoader(), "/bin/ls");
 }
 
 TEST_F(DarwinLdParserTest, bundleLoaderInvalidType) {
-  EXPECT_FALSE(parse("ld", "-bundle_loader", "/bin/ls", "a.o", nullptr));
+  EXPECT_FALSE(parse("ld", "-bundle_loader", "/bin/ls", "a.o", "-arch", "i386",
+                     nullptr));
 }
 
 TEST_F(DarwinLdParserTest, deadStrippableDylib) {
   EXPECT_TRUE(
-      parse("ld", "-dylib", "-mark_dead_strippable_dylib", "a.o", nullptr));
+      parse("ld", "-dylib", "-mark_dead_strippable_dylib", "a.o",
+            "-arch", "i386", nullptr));
   EXPECT_EQ(true, _context.deadStrippableDylib());
 }
 
 TEST_F(DarwinLdParserTest, deadStrippableDylibInvalidType) {
-  EXPECT_FALSE(parse("ld", "-mark_dead_strippable_dylib", "a.o", nullptr));
+  EXPECT_FALSE(parse("ld", "-mark_dead_strippable_dylib", "a.o",
+                     "-arch", "i386", nullptr));
 }
 
 TEST_F(DarwinLdParserTest, llvmOptions) {
-  EXPECT_TRUE(parse("ld", "-mllvm", "-debug-only", "-mllvm", "foo", "a.o", nullptr));
+  EXPECT_TRUE(parse("ld", "-mllvm", "-debug-only", "-mllvm", "foo", "a.o",
+                    "-arch", "i386", nullptr));
   const std::vector<const char *> &options = _context.llvmOptions();
   EXPECT_EQ(options.size(), 2UL);
   EXPECT_EQ(strcmp(options[0],"-debug-only"), 0);





More information about the llvm-commits mailing list