From: Frederic Riss Date: Wed, 5 Aug 2015 18:27:44 +0000 (+0000) Subject: [dsymutil] Implement support for handling mach-o universal binaries as main input... X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=7a4257861d14c0fa18aeb6e6dd48df681f68f6d2 [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 --- diff --git a/test/tools/dsymutil/Inputs/fat-test.dylib b/test/tools/dsymutil/Inputs/fat-test.dylib new file mode 100755 index 00000000000..4def340a950 Binary files /dev/null and b/test/tools/dsymutil/Inputs/fat-test.dylib differ diff --git a/test/tools/dsymutil/fat-binary-output.test b/test/tools/dsymutil/fat-binary-output.test new file mode 100644 index 00000000000..ca631e41d8b --- /dev/null +++ b/test/tools/dsymutil/fat-binary-output.test @@ -0,0 +1,32 @@ +RUN: llvm-dsymutil -verbose -no-output %p/Inputs/fat-test.dylib -oso-prepend-path %p | FileCheck %s + +This test doesn't produce any filesytstem output, we just look at the verbose +log output. + +For each arch in the binary, check that we emit the right triple with the right +file and the right symbol inside it (each slice has a different symbol, so that +means that the logic is looking at the right file slice too). + +After the link of each architecture, check that lipo is correctly invoked to +generate the fat output binary. + +CHECK: triple: 'x86_64-apple-darwin' +CHECK: - filename: [[INPUTS_PATH:.*]]fat-test.o +CHECK: DW_AT_name{{.*}} "x86_64_var" + +CHECK: triple: 'i386-apple-darwin' +CHECK: - filename: [[INPUTS_PATH]]fat-test.o +CHECK: DW_AT_name{{.*}} "i386_var" + +CHECK: triple: 'x86_64h-apple-darwin' +CHECK: - filename: [[INPUTS_PATH]]fat-test.o +CHECK: DW_AT_name{{.*}} "x86_64h_var" + +CHECK: Running lipo +CHECK-NEXT: lipo -create +CHECK-SAME: [[INPUTS_PATH]]fat-test.dylib.tmp{{......}}.dwarf +CHECK-SAME: [[INPUTS_PATH]]fat-test.dylib.tmp{{......}}.dwarf +CHECK-SAME: [[INPUTS_PATH]]fat-test.dylib.tmp{{......}}.dwarf +CHECK-SAME: -segalign x86_64 20 -segalign i386 20 -segalign x86_64h 20 +CHECK-SAME: -output [[INPUTS_PATH]]fat-test.dylib.dwarf + diff --git a/tools/dsymutil/CMakeLists.txt b/tools/dsymutil/CMakeLists.txt index 88f9f1f083d..0f45d20de22 100644 --- a/tools/dsymutil/CMakeLists.txt +++ b/tools/dsymutil/CMakeLists.txt @@ -14,5 +14,6 @@ add_llvm_tool(llvm-dsymutil DebugMap.cpp DwarfLinker.cpp MachODebugMapParser.cpp + MachOUtils.cpp ) diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp index e29c885b5ac..387adc79276 100644 --- a/tools/dsymutil/DebugMap.cpp +++ b/tools/dsymutil/DebugMap.cpp @@ -97,7 +97,7 @@ struct YAMLContext { }; } -ErrorOr> +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