diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt --- a/lld/MachO/CMakeLists.txt +++ b/lld/MachO/CMakeLists.txt @@ -8,6 +8,7 @@ Arch/X86_64.cpp Arch/ARM64.cpp UnwindInfoSection.cpp + Config.cpp Driver.cpp DriverUtils.cpp Dwarf.cpp diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -9,17 +9,24 @@ #ifndef LLD_MACHO_CONFIG_H #define LLD_MACHO_CONFIG_H +#include "InputFiles.h" + +#include "lld/Common/ErrorHandler.h" + #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/VersionTuple.h" #include "llvm/TextAPI/MachO/Architecture.h" #include "llvm/TextAPI/MachO/Platform.h" #include "llvm/TextAPI/MachO/Target.h" +#include #include namespace lld { @@ -65,6 +72,44 @@ bool match(llvm::StringRef symbolName) const; }; +// Helper class to export dependency info. +class DependencyTracker { +public: + explicit DependencyTracker(llvm::StringRef path) : path(path) {} + + // Adds the given path to the set of not-found files. + void logFileNotFound(std::string); + + // Writes the dependencies to specified path. + // The content is sorted by its Op Code, then within each section, + // alphabetical order. + void write(llvm::StringRef version, + const llvm::SetVector &inputs, + llvm::StringRef output); + +private: + llvm::StringRef path; + // The paths need to be alphabetically ordered. + // We need to own the paths because some of them are temporarily + // constructed. + std::set notFounds; + + // This is based only on observations. There is no real + // documentation on what the opcodes could be. + enum DepOpCode : uint32_t { + // Denotes the linker version. + kVersion = 0x00, + // Denotes the input files. + kInput = 0x10, + // Denotes the files that do not exist(?) + // FIXME: This one is weird. I(vyng) take it to mean all the files + // that the linker attempted to look at but which did not exist. + kNotFound = 0x11, + // Denotes the output files. + kOutput = 0x40, + }; +}; + struct Configuration { Symbol *entry; bool hasReexports = false; @@ -109,6 +154,20 @@ SymbolPatterns exportedSymbols; SymbolPatterns unexportedSymbols; + + std::unique_ptr dependencyTracker; + + template >> + void inline logFileNotFound(T path) { + if (dependencyTracker != nullptr) + dependencyTracker->logFileNotFound(std::string(path)); + } + + inline void logFileNotFoundStr(std::string path) { + if (dependencyTracker != nullptr) + dependencyTracker->logFileNotFound(std::move(path)); + } }; // The symbol with the highest priority should be ordered first in the output diff --git a/lld/MachO/Config.cpp b/lld/MachO/Config.cpp new file mode 100644 --- /dev/null +++ b/lld/MachO/Config.cpp @@ -0,0 +1,46 @@ +#include "Config.h" + +namespace lld { +namespace macho { + +void DependencyTracker::logFileNotFound(std::string path) { + notFounds.insert(std::move(path)); +} + +void DependencyTracker::write(llvm::StringRef version, + const llvm::SetVector &inputs, + llvm::StringRef output) { + + std::error_code ec; + llvm::raw_fd_ostream os(path, ec, llvm::sys::fs::OF_None); + if (ec) { + warn("Error writing dependency info to file"); + return; + } + + auto AddDep = [&os](uint32_t opcode, const llvm::StringRef &path) { + os << opcode; + os << path; + os << '\0'; + }; + + AddDep(DepOpCode::kVersion, version); + + // Sort the input by its names. + std::vector inputNames; + inputNames.reserve(inputs.size()); + for (InputFile *f : inputs) + inputNames.push_back(f->getName()); + llvm::sort(inputNames, [](const llvm::StringRef &a, + const llvm::StringRef &b) { return a < b; }); + for (const llvm::StringRef &in : inputNames) + AddDep(DepOpCode::kInput, in); + + for (const llvm::StringRef &f : notFounds) + AddDep(DepOpCode::kNotFound, f); + + AddDep(DepOpCode::kOutput, output); +} + +} // namespace macho +} // namespace lld diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -84,6 +84,8 @@ Twine location = base + ext; if (fs::exists(location)) return location.str(); + else + config->logFileNotFoundStr(std::move(location.str())); } } return {}; @@ -815,6 +817,15 @@ symtab = make(); target = createTargetInfo(args); + if (const Arg *arg = args.getLastArg(OPT_dependency_info)) { + llvm::StringRef path = arg->getValue(); + if (path.empty() || !fs::can_write(path)) + warn("Ignore dependency_info option since specified path is not " + "writeable."); + else + config->dependencyTracker = std::make_unique(path); + } + config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"), /*file=*/nullptr, /*isWeakRef=*/false); @@ -1054,6 +1065,10 @@ // Write to an output file. writeResult(); + + if (config->dependencyTracker != nullptr) + config->dependencyTracker->write(getLLDVersion(), inputFiles, + config->outputFile); } if (config->timeTraceEnabled) { diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp --- a/lld/MachO/DriverUtils.cpp +++ b/lld/MachO/DriverUtils.cpp @@ -164,12 +164,15 @@ // they are consistent. if (fs::exists(path)) return std::string(path); + else + config->logFileNotFound(path); SmallString<261> location = path; path::replace_extension(location, ".tbd"); if (fs::exists(location)) return std::string(location); - + else + config->logFileNotFound(location); return {}; } diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -505,7 +505,6 @@ def dependency_info : Separate<["-"], "dependency_info">, MetaVarName<"">, HelpText<"Dump dependency info">, - Flags<[HelpHidden]>, Group; def save_temps : Flag<["-"], "save-temps">, HelpText<"Save intermediate LTO compilation results">,