From 7a4257861d14c0fa18aeb6e6dd48df681f68f6d2 Mon Sep 17 00:00:00 2001 From: Frederic Riss Date: Wed, 5 Aug 2015 18:27:44 +0000 Subject: [PATCH] [dsymutil] Implement support for handling mach-o universal binaries as main input/output. The DWARF linker isn't touched by this, the implementation links individual files and merges them together into a fat binary by calling out to the 'lipo' utility. The main change is that the MachODebugMapParser can now return multiple debug maps for a single binary. The test just verifies that lipo would be invoked correctly, but doesn't actually generate a binary. This mimics the way clang tests its external iplatform tools integration. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@244087 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/tools/dsymutil/Inputs/fat-test.dylib | Bin 0 -> 13012 bytes test/tools/dsymutil/fat-binary-output.test | 32 ++++++++ tools/dsymutil/CMakeLists.txt | 1 + tools/dsymutil/DebugMap.cpp | 7 +- tools/dsymutil/DebugMap.h | 2 +- tools/dsymutil/MachODebugMapParser.cpp | 48 +++++++----- tools/dsymutil/MachOUtils.cpp | 86 +++++++++++++++++++++ tools/dsymutil/MachOUtils.h | 30 +++++++ tools/dsymutil/dsymutil.cpp | 56 +++++++++++--- tools/dsymutil/dsymutil.h | 13 ++-- 10 files changed, 235 insertions(+), 40 deletions(-) create mode 100755 test/tools/dsymutil/Inputs/fat-test.dylib create mode 100644 test/tools/dsymutil/fat-binary-output.test create mode 100644 tools/dsymutil/MachOUtils.cpp create mode 100644 tools/dsymutil/MachOUtils.h diff --git a/test/tools/dsymutil/Inputs/fat-test.dylib b/test/tools/dsymutil/Inputs/fat-test.dylib new file mode 100755 index 0000000000000000000000000000000000000000..4def340a9507e4e2a9587a555a17db8bcf4c5f81 GIT binary patch literal 13012 zcmeHNO-NKx6h4zyQi&E4Mw>w*G|&;W$%ulfF_NN;Mi3${&W|PNOw7Cqhg=AeZEIJX zTDOcAQA8Uz6;UmtUAs0Rw2Y9x@6Ee!t~%jh>FOLfbMHOxzI)I8?#$wR^Sxhw^+rUR zA|gB4!q17ce-HXWtg92T*Ur2ak-h!SJo)tN$1)~IxWEp0EBx&?k-O54p2z|CL%1=i z;qeqBtE@V$77WHrU^^W7`DToloArVLnl7{(8-J0$RWmnq9{Rm=`Vr=esa+@%(Ud#9|f=X4t%fmKR22UhZ1F zhi*77a7BD_!t3>NC9f}AnVw3EbcxLOW6rvU{kRr$XB)+sd)`Moj&?VE6i(y4*}LO+ z!p)1%8?l3!#C9KoL*`6ahs*5l{pa z0U!`DvqNzcA2hcniqh63^U)Qsum;OI5!Q+Bct6IIU?E<9em2Hwth=J@1jgDi>lBbj zF>d3cL0y(W7mvdkQHhP@Z;BfJJcs|Y(+@>J5l{pa0YyL&Py`eKML-cy z1hyUl%X8fDiH`QwL`OqBM@!>8M;s?~d5$v}{|nDiO*r$H%Ik!xw~Do?{M& zqn?8#%N%@-_5v}>$FSY7c4PH_?T%)nws#%g!Fg{Z`V0sJeB&Kj3kf9z*r%qTt|o(f z!zq&43JpdF_FHnL_->k!63&frt51k ztp1_{XW@NV{RPjcrU)nkihv@Z2q*%IfFhs>C<2OrBCrVvM9kCx1RYd!v^pdQ>o}|1 na5WsRe#6ynux7TpjREv6mvIt(JoC%z#> +ErrorOr>> DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose) { auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile); @@ -114,8 +114,9 @@ DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, if (auto EC = yin.error()) return EC; - - return std::move(Res); + std::vector> Result; + Result.push_back(std::move(Res)); + return std::move(Result); } } diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h index 29347ed0f50..9bf20c6db31 100644 --- a/tools/dsymutil/DebugMap.h +++ b/tools/dsymutil/DebugMap.h @@ -102,7 +102,7 @@ public: #endif /// Read a debug map for \a InputFile. - static ErrorOr> + static ErrorOr>> parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose); }; diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp index 33554f12122..aa034cb683c 100644 --- a/tools/dsymutil/MachODebugMapParser.cpp +++ b/tools/dsymutil/MachODebugMapParser.cpp @@ -27,10 +27,12 @@ public: MainBinaryHolder(Verbose), CurrentObjectHolder(Verbose), CurrentDebugMapObject(nullptr) {} - /// \brief Parses and returns the DebugMap of the input binary. + /// \brief Parses and returns the DebugMaps of the input binary. + /// The binary contains multiple maps in case it is a universal + /// binary. /// \returns an error in case the provided BinaryPath doesn't exist /// or isn't of a supported type. - ErrorOr> parse(); + ErrorOr>> parse(); private: std::string BinaryPath; @@ -55,6 +57,9 @@ private: const char *CurrentFunctionName; uint64_t CurrentFunctionAddress; + std::unique_ptr parseOneBinary(const MachOObjectFile &MainBinary, + StringRef BinaryPath); + void switchToNewDebugMapObject(StringRef Filename, sys::TimeValue Timestamp); void resetParserState(); uint64_t getMainBinarySymbolAddress(StringRef Name); @@ -110,19 +115,9 @@ void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename, loadCurrentObjectFileSymbols(*ErrOrAchObj); } -/// This main parsing routine tries to open the main binary and if -/// successful iterates over the STAB entries. The real parsing is -/// done in handleStabSymbolTableEntry. -ErrorOr> MachODebugMapParser::parse() { - auto MainBinOrError = - MainBinaryHolder.GetFilesAs(BinaryPath); - if (auto Error = MainBinOrError.getError()) - return Error; - - if (MainBinOrError->size() != 1) - return make_error_code(object::object_error::invalid_file_type); - - const MachOObjectFile &MainBinary = *MainBinOrError->front(); +std::unique_ptr +MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, + StringRef BinaryPath) { loadMainBinarySymbols(MainBinary); Result = make_unique(BinaryHolder::getTriple(MainBinary)); MainBinaryStrings = MainBinary.getStringTableData(); @@ -138,6 +133,22 @@ ErrorOr> MachODebugMapParser::parse() { return std::move(Result); } +/// This main parsing routine tries to open the main binary and if +/// successful iterates over the STAB entries. The real parsing is +/// done in handleStabSymbolTableEntry. +ErrorOr>> MachODebugMapParser::parse() { + auto MainBinOrError = + MainBinaryHolder.GetFilesAs(BinaryPath); + if (auto Error = MainBinOrError.getError()) + return Error; + + std::vector> Results; + for (const auto *Binary : *MainBinOrError) + Results.push_back(parseOneBinary(*Binary, BinaryPath)); + + return std::move(Results); +} + /// Interpret the STAB entries to fill the DebugMap. void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type, @@ -254,10 +265,9 @@ void MachODebugMapParser::loadMainBinarySymbols( namespace llvm { namespace dsymutil { -llvm::ErrorOr> parseDebugMap(StringRef InputFile, - StringRef PrependPath, - bool Verbose, - bool InputIsYAML) { +llvm::ErrorOr>> +parseDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose, + bool InputIsYAML) { if (!InputIsYAML) { MachODebugMapParser Parser(InputFile, PrependPath, Verbose); return Parser.parse(); diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp new file mode 100644 index 00000000000..15605fe00eb --- /dev/null +++ b/tools/dsymutil/MachOUtils.cpp @@ -0,0 +1,86 @@ +//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MachOUtils.h" +#include "dsymutil.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace dsymutil { +namespace MachOUtils { + +static bool runLipo(SmallVectorImpl &Args) { + auto Path = sys::findProgramByName("lipo"); + + if (!Path) { + errs() << "error: lipo: " << Path.getError().message() << "\n"; + return false; + } + + std::string ErrMsg; + int result = + sys::ExecuteAndWait(*Path, Args.data(), nullptr, nullptr, 0, 0, &ErrMsg); + if (result) { + errs() << "error: lipo: " << ErrMsg << "\n"; + return false; + } + + return true; +} + +bool generateUniversalBinary(SmallVectorImpl &ArchFiles, + StringRef OutputFileName, + const LinkOptions &Options) { + // No need to merge one file into a universal fat binary. First, try + // to move it (rename) to the final location. If that fails because + // of cross-device link issues then copy and delete. + if (ArchFiles.size() == 1) { + StringRef From(ArchFiles.front().Path); + if (sys::fs::rename(From, OutputFileName)) { + if (std::error_code EC = sys::fs::copy_file(From, OutputFileName)) { + errs() << "error: while copying " << From << " to " << OutputFileName + << ": " << EC.message() << "\n"; + return false; + } + sys::fs::remove(From); + } + return true; + } + + SmallVector Args; + Args.push_back("lipo"); + Args.push_back("-create"); + + for (auto &Thin : ArchFiles) + Args.push_back(Thin.Path.c_str()); + + // Align segments to match dsymutil-classic alignment + for (auto &Thin : ArchFiles) { + Args.push_back("-segalign"); + Args.push_back(Thin.Arch.c_str()); + Args.push_back("20"); + } + + Args.push_back("-output"); + Args.push_back(OutputFileName.data()); + Args.push_back(nullptr); + + if (Options.Verbose) { + outs() << "Running lipo\n"; + for (auto Arg : Args) + outs() << ' ' << ((Arg == nullptr) ? "\n" : Arg); + } + + return Options.NoOutput ? true : runLipo(Args); +} +} +} +} diff --git a/tools/dsymutil/MachOUtils.h b/tools/dsymutil/MachOUtils.h new file mode 100644 index 00000000000..f1b2ad9dadd --- /dev/null +++ b/tools/dsymutil/MachOUtils.h @@ -0,0 +1,30 @@ +//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H +#define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H + +#include +#include "llvm/ADT/StringRef.h" + +namespace llvm { +namespace dsymutil { +struct LinkOptions; +namespace MachOUtils { + +struct ArchAndFilename { + std::string Arch, Path; + ArchAndFilename(StringRef Arch, StringRef Path) : Arch(Arch), Path(Path) {} +}; + +bool generateUniversalBinary(SmallVectorImpl &ArchFiles, + StringRef OutputFileName, const LinkOptions &); +} +} +} +#endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp index 07fdf314c44..48eb9a14f0a 100644 --- a/tools/dsymutil/dsymutil.cpp +++ b/tools/dsymutil/dsymutil.cpp @@ -13,7 +13,9 @@ //===----------------------------------------------------------------------===// #include "DebugMap.h" +#include "MachOUtils.h" #include "dsymutil.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Options.h" #include "llvm/Support/PrettyStackTrace.h" @@ -68,7 +70,24 @@ static opt InputIsYAMLDebugMap( init(false), cat(DsymCategory)); } -static std::string getOutputFileName(llvm::StringRef InputFile) { +static std::string getOutputFileName(llvm::StringRef InputFile, + bool TempFile = false) { + if (TempFile) { + std::string OutputFile = (InputFile + ".tmp%%%%%%.dwarf").str(); + int FD; + llvm::SmallString<128> UniqueFile; + if (auto EC = llvm::sys::fs::createUniqueFile(OutputFile, FD, UniqueFile)) { + llvm::errs() << "error: failed to create temporary outfile '" + << OutputFile << "': " << EC.message() << '\n'; + return ""; + } + llvm::sys::RemoveFileOnSignal(UniqueFile); + // Close the file immediately. We know it is unique. It will be + // reopened and written to later. + llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true); + return UniqueFile.str(); + } + if (OutputFileOpt.empty()) { if (InputFile == "-") return "a.out.dwarf"; @@ -78,6 +97,8 @@ static std::string getOutputFileName(llvm::StringRef InputFile) { } void llvm::dsymutil::exitDsymutil(int ExitStatus) { + // Cleanup temporary files. + llvm::sys::RunInterruptHandlers(); exit(ExitStatus); } @@ -118,24 +139,39 @@ int main(int argc, char **argv) { } for (auto &InputFile : InputFiles) { - auto DebugMapPtrOrErr = + auto DebugMapPtrsOrErr = parseDebugMap(InputFile, OsoPrependPath, Verbose, InputIsYAMLDebugMap); - if (auto EC = DebugMapPtrOrErr.getError()) { + if (auto EC = DebugMapPtrsOrErr.getError()) { llvm::errs() << "error: cannot parse the debug map for \"" << InputFile << "\": " << EC.message() << '\n'; exitDsymutil(1); } - if (Verbose || DumpDebugMap) - (*DebugMapPtrOrErr)->print(llvm::outs()); + // If there is more than one link to execute, we need to generate + // temporary files. + bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1; + llvm::SmallVector TempFiles; + for (auto &Map : *DebugMapPtrsOrErr) { + if (Verbose || DumpDebugMap) + Map->print(llvm::outs()); + + if (DumpDebugMap) + continue; - if (DumpDebugMap) - continue; + std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles); + if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options)) + exitDsymutil(1); - std::string OutputFile = getOutputFileName(InputFile); - if (!linkDwarf(OutputFile, **DebugMapPtrOrErr, Options)) - exitDsymuti(1); + if (NeedsTempFiles) + TempFiles.emplace_back(Map->getTriple().getArchName().str(), + OutputFile); + } + + if (NeedsTempFiles && + !MachOUtils::generateUniversalBinary( + TempFiles, getOutputFileName(InputFile), Options)) + exitDsymutil(1); } exitDsymutil(0); diff --git a/tools/dsymutil/dsymutil.h b/tools/dsymutil/dsymutil.h index 7c1810f4fad..82f0deeb2e0 100644 --- a/tools/dsymutil/dsymutil.h +++ b/tools/dsymutil/dsymutil.h @@ -32,12 +32,12 @@ struct LinkOptions { LinkOptions() : Verbose(false), NoOutput(false) {} }; -/// \brief Extract the DebugMap from the given file. -/// The file has to be a MachO object file. -llvm::ErrorOr> parseDebugMap(StringRef InputFile, - StringRef PrependPath, - bool Verbose, - bool InputIsYAML); +/// \brief Extract the DebugMaps from the given file. +/// The file has to be a MachO object file. Multiple debug maps can be +/// returned when the file is universal (aka fat) binary. +llvm::ErrorOr>> +parseDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose, + bool InputIsYAML); /// \brief Link the Dwarf debuginfo as directed by the passed DebugMap /// \p DM into a DwarfFile named \p OutputFilename. @@ -48,7 +48,6 @@ bool linkDwarf(StringRef OutputFilename, const DebugMap &DM, /// \brief Exit the dsymutil process, cleaning up every temporary /// files that we created. LLVM_ATTRIBUTE_NORETURN void exitDsymutil(int ExitStatus); - } } #endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H -- 2.34.1