[dsymutil] Add the ability to generate universal binaries with a fat64 header
authorJonas Devlieghere <jonas@devlieghere.com>
Mon, 27 Mar 2023 16:25:21 +0000 (09:25 -0700)
committerJonas Devlieghere <jonas@devlieghere.com>
Mon, 27 Mar 2023 23:22:16 +0000 (16:22 -0700)
Add the ability to generate universal binaries with a fat64 header.

rdar://107223939

Differential revision: https://reviews.llvm.org/D146879

llvm/docs/CommandGuide/dsymutil.rst
llvm/test/tools/dsymutil/cmdline.test
llvm/test/tools/dsymutil/fat-header.test [new file with mode: 0644]
llvm/tools/dsymutil/LinkUtils.h
llvm/tools/dsymutil/MachOUtils.cpp
llvm/tools/dsymutil/MachOUtils.h
llvm/tools/dsymutil/Options.td
llvm/tools/dsymutil/dsymutil.cpp

index 7ee6566..d3ae16d 100644 (file)
@@ -37,6 +37,10 @@ OPTIONS
  Dump the *executable*'s debug-map (the list of the object files containing the
  debug information) in YAML format and exit. No DWARF link will take place.
 
+.. option:: --fat64
+
+ Use a 64-bit header when emitting universal binaries.
+
 .. option:: --flat, -f
 
  Produce a flat dSYM file. A ``.dwarf`` extension will be appended to the
index 03a57f6..bf256bd 100644 (file)
@@ -8,6 +8,7 @@ HELP: Dsymutil Options:
 CHECK: -accelerator
 CHECK: -arch <arch>
 CHECK: -dump-debug-map
+CHECK: -fat64
 CHECK: -flat
 CHECK: -gen-reproducer
 CHECK: -help
diff --git a/llvm/test/tools/dsymutil/fat-header.test b/llvm/test/tools/dsymutil/fat-header.test
new file mode 100644 (file)
index 0000000..91192b2
--- /dev/null
@@ -0,0 +1,10 @@
+REQUIRES: system-darwin
+
+RUN: dsymutil -oso-prepend-path %p %p/Inputs/fat-test.arm.dylib -o %t.fat32.dSYM
+RUN: llvm-objdump -m --universal-headers %t.fat32.dSYM/Contents/Resources/DWARF/fat-test.arm.dylib | FileCheck %s -check-prefixes=FAT32
+
+RUN: dsymutil -oso-prepend-path %p %p/Inputs/fat-test.arm.dylib -o %t.fat64.dSYM -fat64
+RUN: llvm-objdump -m --universal-headers %t.fat64.dSYM/Contents/Resources/DWARF/fat-test.arm.dylib | FileCheck %s -check-prefixes=FAT64
+
+FAT32: fat_magic FAT_MAGIC
+FAT64: fat_magic FAT_MAGIC_64
index f08d923..77f3608 100644 (file)
@@ -57,6 +57,9 @@ struct LinkOptions {
   /// function.
   bool KeepFunctionForStatic = false;
 
+  /// Use a 64-bit header when emitting universal binaries.
+  bool Fat64 = false;
+
   /// Number of threads.
   unsigned Threads = 1;
 
index 2d3437e..9d440e8 100644 (file)
@@ -78,7 +78,8 @@ static bool runLipo(StringRef SDKPath, SmallVectorImpl<StringRef> &Args) {
 
 bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles,
                              StringRef OutputFileName,
-                             const LinkOptions &Options, StringRef SDKPath) {
+                             const LinkOptions &Options, StringRef SDKPath,
+                             bool Fat64) {
   // No need to merge one file into a universal fat binary.
   if (ArchFiles.size() == 1) {
     if (auto E = ArchFiles.front().File->keep(OutputFileName)) {
@@ -97,7 +98,7 @@ bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles,
   for (auto &Thin : ArchFiles)
     Args.push_back(Thin.path());
 
-  // Align segments to match dsymutil-classic alignment
+  // Align segments to match dsymutil-classic alignment.
   for (auto &Thin : ArchFiles) {
     Thin.Arch = getArchName(Thin.Arch);
     Args.push_back("-segalign");
@@ -105,6 +106,10 @@ bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles,
     Args.push_back("20");
   }
 
+  // Use a 64-bit fat header if requested.
+  if (Fat64)
+    Args.push_back("-fat64");
+
   Args.push_back("-output");
   Args.push_back(OutputFileName.data());
 
index 8cc4e73..9d35813 100644 (file)
@@ -54,7 +54,7 @@ struct DwarfRelocationApplicationInfo {
 
 bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles,
                              StringRef OutputFileName, const LinkOptions &,
-                             StringRef SDKPath);
+                             StringRef SDKPath, bool Fat64 = false);
 bool generateDsymCompanion(
     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, const DebugMap &DM,
     SymbolMapTranslator &Translator, MCStreamer &MS, raw_fd_ostream &OutFile,
index d500762..8af6dbe 100644 (file)
@@ -91,6 +91,10 @@ def: Flag<["-"], "f">,
   HelpText<"Alias for --flat">,
   Group<grp_general>;
 
+def fat64: F<"fat64">,
+  HelpText<"Use a 64-bit header when emitting universal binaries.">,
+  Group<grp_general>;
+
 def update: F<"update">,
   HelpText<"Updates existing dSYM files to contain the latest accelerator tables and other DWARF optimizations.">,
   Group<grp_general>;
index 1da434c..d868459 100644 (file)
@@ -301,6 +301,7 @@ static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
   Options.LinkOpts.Update = Args.hasArg(OPT_update);
   Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);
   Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics);
+  Options.LinkOpts.Fat64 = Args.hasArg(OPT_fat64);
   Options.LinkOpts.KeepFunctionForStatic =
       Args.hasArg(OPT_keep_func_for_static);
 
@@ -780,40 +781,47 @@ int main(int argc, char **argv) {
       return EXIT_FAILURE;
 
     if (NeedsTempFiles) {
-      // Universal Mach-O files can't have an archicture slice that starts
-      // beyond the 4GB boundary. "lipo" can creeate a 64 bit universal header,
-      // but not all tools can parse these files so we want to return an error
-      // if the file can't be encoded as a file with a 32 bit universal header.
-      // To detect this, we check the size of each architecture's skinny Mach-O
-      // file and add up the offsets. If they exceed 4GB, then we return an
-      // error.
-
-      // First we compute the right offset where the first architecture will fit
-      // followin the 32 bit universal header. The 32 bit universal header
-      // starts with a uint32_t magic and a uint32_t number of architecture
-      // infos. Then it is followed by 5 uint32_t values for each architecture.
-      // So we set the start offset to the right value so we can calculate the
-      // exact offset that the first architecture slice can start at.
-      constexpr uint64_t MagicAndCountSize = 2 * 4;
-      constexpr uint64_t UniversalArchInfoSize = 5 * 4;
-      uint64_t FileOffset = MagicAndCountSize +
-          UniversalArchInfoSize * TempFiles.size();
-      for (const auto &File: TempFiles) {
-        ErrorOr<vfs::Status> stat = Options.LinkOpts.VFS->status(File.path());
-        if (!stat)
-          break;
-        if (FileOffset > UINT32_MAX) {
-          WithColor::error() << formatv(
-              "the universal binary has a slice with a starting offset ({0:x}) "
-              "that exceeds 4GB and will produce an invalid Mach-O file.",
-              FileOffset);
-          return EXIT_FAILURE;
+      const bool Fat64 = Options.LinkOpts.Fat64;
+      if (!Fat64) {
+        // Universal Mach-O files can't have an archicture slice that starts
+        // beyond the 4GB boundary. "lipo" can create a 64 bit universal
+        // header, but not all tools can parse these files so we want to return
+        // an error if the file can't be encoded as a file with a 32 bit
+        // universal header. To detect this, we check the size of each
+        // architecture's skinny Mach-O file and add up the offsets. If they
+        // exceed 4GB, then we return an error.
+
+        // First we compute the right offset where the first architecture will
+        // fit followin the 32 bit universal header. The 32 bit universal header
+        // starts with a uint32_t magic and a uint32_t number of architecture
+        // infos. Then it is followed by 5 uint32_t values for each
+        // architecture. So we set the start offset to the right value so we can
+        // calculate the exact offset that the first architecture slice can
+        // start at.
+        constexpr uint64_t MagicAndCountSize = 2 * 4;
+        constexpr uint64_t UniversalArchInfoSize = 5 * 4;
+        uint64_t FileOffset =
+            MagicAndCountSize + UniversalArchInfoSize * TempFiles.size();
+        for (const auto &File : TempFiles) {
+          ErrorOr<vfs::Status> stat = Options.LinkOpts.VFS->status(File.path());
+          if (!stat)
+            break;
+          if (FileOffset > UINT32_MAX) {
+            WithColor::error()
+                << formatv("the universal binary has a slice with a starting "
+                           "offset ({0:x}) that exceeds 4GB and will produce "
+                           "an invalid Mach-O file. Use the -fat64 flag to "
+                           "generate a universal binary with a 64-bit header "
+                           "but note that not all tools support this format.",
+                           FileOffset);
+            return EXIT_FAILURE;
+          }
+          FileOffset += stat->getSize();
         }
-        FileOffset += stat->getSize();
       }
-      if (!MachOUtils::generateUniversalBinary(TempFiles,
-                                               OutputLocationOrErr->DWARFFile,
-                                               Options.LinkOpts, SDKPath))
+      if (!MachOUtils::generateUniversalBinary(
+              TempFiles, OutputLocationOrErr->DWARFFile, Options.LinkOpts,
+              SDKPath, Fat64))
         return EXIT_FAILURE;
     }
   }