diff --git a/llvm/docs/CommandGuide/dsymutil.rst b/llvm/docs/CommandGuide/dsymutil.rst --- a/llvm/docs/CommandGuide/dsymutil.rst +++ b/llvm/docs/CommandGuide/dsymutil.rst @@ -37,6 +37,10 @@ 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 diff --git a/llvm/test/tools/dsymutil/cmdline.test b/llvm/test/tools/dsymutil/cmdline.test --- a/llvm/test/tools/dsymutil/cmdline.test +++ b/llvm/test/tools/dsymutil/cmdline.test @@ -8,6 +8,7 @@ CHECK: -accelerator CHECK: -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 --- /dev/null +++ b/llvm/test/tools/dsymutil/fat-header.test @@ -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 diff --git a/llvm/tools/dsymutil/LinkUtils.h b/llvm/tools/dsymutil/LinkUtils.h --- a/llvm/tools/dsymutil/LinkUtils.h +++ b/llvm/tools/dsymutil/LinkUtils.h @@ -57,6 +57,9 @@ /// function. bool KeepFunctionForStatic = false; + /// Use a 64-bit header when emitting universal binaries. + bool Fat64 = false; + /// Number of threads. unsigned Threads = 1; diff --git a/llvm/tools/dsymutil/MachOUtils.h b/llvm/tools/dsymutil/MachOUtils.h --- a/llvm/tools/dsymutil/MachOUtils.h +++ b/llvm/tools/dsymutil/MachOUtils.h @@ -54,7 +54,7 @@ bool generateUniversalBinary(SmallVectorImpl &ArchFiles, StringRef OutputFileName, const LinkOptions &, - StringRef SDKPath); + StringRef SDKPath, bool Fat64 = false); bool generateDsymCompanion( llvm::IntrusiveRefCntPtr VFS, const DebugMap &DM, SymbolMapTranslator &Translator, MCStreamer &MS, raw_fd_ostream &OutFile, diff --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp --- a/llvm/tools/dsymutil/MachOUtils.cpp +++ b/llvm/tools/dsymutil/MachOUtils.cpp @@ -78,7 +78,8 @@ bool generateUniversalBinary(SmallVectorImpl &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 @@ 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 @@ 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()); diff --git a/llvm/tools/dsymutil/Options.td b/llvm/tools/dsymutil/Options.td --- a/llvm/tools/dsymutil/Options.td +++ b/llvm/tools/dsymutil/Options.td @@ -91,6 +91,10 @@ HelpText<"Alias for --flat">, Group; +def fat64: F<"fat64">, + HelpText<"Use a 64-bit header when emitting universal binaries.">, + Group; + def update: F<"update">, HelpText<"Updates existing dSYM files to contain the latest accelerator tables and other DWARF optimizations.">, Group; diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp --- a/llvm/tools/dsymutil/dsymutil.cpp +++ b/llvm/tools/dsymutil/dsymutil.cpp @@ -301,6 +301,7 @@ 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 @@ 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 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 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; } }