From 0ccda7c2326e1dc4e0d5d601dcc06c4b576acb0e Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 19 Jun 2020 21:13:03 -0700 Subject: [PATCH] MachO: support `-syslibroot` This adds support for the `-syslibroot` option. This is required to make the library search order actually function. With this, it is now possible to link a test Darwin x86_64 program with lld on Darwin. Differential Revision: https://reviews.llvm.org/D82252 Reviewed By: Jez Ng --- lld/MachO/Driver.cpp | 60 +++++++++++++++++++++++++++++++--------- lld/test/MachO/search-paths.test | 4 +-- lld/test/MachO/syslibroot.test | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 lld/test/MachO/syslibroot.test diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index 6b25cd5..93dfb15 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -152,28 +152,49 @@ static bool isDirectory(StringRef option, StringRef path) { static void getSearchPaths(std::vector &paths, unsigned optionCode, opt::InputArgList &args, + const std::vector &roots, const SmallVector &systemPaths) { StringRef optionLetter{(optionCode == OPT_F ? "F" : "L")}; for (auto const &path : args::getStrings(args, optionCode)) { - if (isDirectory(optionLetter, path)) - paths.push_back(path); - } - if (!args.hasArg(OPT_Z) && Triple(sys::getProcessTriple()).isOSDarwin()) { - for (auto const &path : systemPaths) { + // NOTE: only absolute paths are re-rooted to syslibroot(s) + if (llvm::sys::path::is_absolute(path, llvm::sys::path::Style::posix)) { + for (StringRef root : roots) { + SmallString<261> buffer(root); + llvm::sys::path::append(buffer, path); + // Do not warn about paths that are computed via the syslib roots + if (llvm::sys::fs::is_directory(buffer)) + paths.push_back(saver.save(buffer.str())); + } + } else { if (isDirectory(optionLetter, path)) paths.push_back(path); } } + + // `-Z` suppresses the standard "system" search paths. + if (args.hasArg(OPT_Z)) + return; + + for (auto const &path : systemPaths) { + for (auto root : roots) { + SmallString<261> buffer(root); + llvm::sys::path::append(buffer, path); + if (isDirectory(optionLetter, buffer)) + paths.push_back(saver.save(buffer.str())); + } + } } -static void getLibrarySearchPaths(std::vector &paths, - opt::InputArgList &args) { - getSearchPaths(paths, OPT_L, args, {"/usr/lib", "/usr/local/lib"}); +static void getLibrarySearchPaths(opt::InputArgList &args, + const std::vector &roots, + std::vector &paths) { + getSearchPaths(paths, OPT_L, args, roots, {"/usr/lib", "/usr/local/lib"}); } -static void getFrameworkSearchPaths(std::vector &paths, - opt::InputArgList &args) { - getSearchPaths(paths, OPT_F, args, +static void getFrameworkSearchPaths(opt::InputArgList &args, + const std::vector &roots, + std::vector &paths) { + getSearchPaths(paths, OPT_F, args, roots, {"/Library/Frameworks", "/System/Library/Frameworks"}); } @@ -397,10 +418,22 @@ bool macho::link(llvm::ArrayRef argsArr, bool canExitEarly, config->installName = args.getLastArgValue(OPT_install_name, config->outputFile); config->headerPad = args::getHex(args, OPT_headerpad, /*Default=*/32); - getLibrarySearchPaths(config->librarySearchPaths, args); - getFrameworkSearchPaths(config->frameworkSearchPaths, args); config->outputType = args.hasArg(OPT_dylib) ? MH_DYLIB : MH_EXECUTE; + std::vector roots; + for (const Arg *arg : args.filtered(OPT_syslibroot)) + roots.push_back(arg->getValue()); + // NOTE: the final `-syslibroot` being `/` will ignore all roots + if (roots.size() && roots.back() == "/") + roots.clear(); + // NOTE: roots can never be empty - add an empty root to simplify the library + // and framework search path computation. + if (roots.empty()) + roots.emplace_back(""); + + getLibrarySearchPaths(args, roots, config->librarySearchPaths); + getFrameworkSearchPaths(args, roots, config->frameworkSearchPaths); + if (args.hasArg(OPT_v)) { message(getLLDVersion()); message(StringRef("Library search paths:") + @@ -455,6 +488,7 @@ bool macho::link(llvm::ArrayRef argsArr, bool canExitEarly, case OPT_install_name: case OPT_Z: case OPT_arch: + case OPT_syslibroot: // handled elsewhere break; default: diff --git a/lld/test/MachO/search-paths.test b/lld/test/MachO/search-paths.test index 124a2a0..8249f27 100644 --- a/lld/test/MachO/search-paths.test +++ b/lld/test/MachO/search-paths.test @@ -2,13 +2,13 @@ UNSUPPORTED: darwin RUN: mkdir -p %t1 %t2 -RUN: lld -flavor darwinnew -v -L%t1 -F%t2 2>&1 | FileCheck -DLDIR=%t1 -DFDIR=%t2 %s +RUN: lld -flavor darwinnew -Z -v -L%t1 -F%t2 2>&1 | FileCheck -DLDIR=%t1 -DFDIR=%t2 %s CHECK: Library search paths: CHECK-NEXT: [[LDIR]] CHECK-NEXT: Framework search paths: CHECK-NEXT: [[FDIR]] -RUN: lld -flavor darwinnew -v -L%t1 -F%t2 -Z 2>&1 | FileCheck -DLDIR=%t1 -DFDIR=%t2 --check-prefix=CHECK_Z %s +RUN: lld -flavor darwinnew -Z -v -L%t1 -F%t2 -Z 2>&1 | FileCheck -DLDIR=%t1 -DFDIR=%t2 --check-prefix=CHECK_Z %s CHECK_Z: Library search paths: CHECK_Z-NEXT: [[LDIR]] CHECK_Z-NEXT: Framework search paths: diff --git a/lld/test/MachO/syslibroot.test b/lld/test/MachO/syslibroot.test new file mode 100644 index 0000000..e9d87ab --- /dev/null +++ b/lld/test/MachO/syslibroot.test @@ -0,0 +1,55 @@ +# Ensure that a nonexistent path is ignored with a syslibroot + +RUN: lld -flavor darwinnew -v -syslibroot /var/empty | FileCheck %s -check-prefix CHECK-NONEXISTENT-SYSLIBROOT + +CHECK-NONEXISTENT-SYSLIBROOT: Library search paths: +CHECK-NONEXISTENT-SYSLIBROOT-NEXT: Framework search paths: + +RUN: mkdir -p %t/usr/lib +RUN: lld -flavor darwinnew -v -syslibroot %t | FileCheck %s -check-prefix CHECK-SYSLIBROOT -DROOT=%t + +CHECK-SYSLIBROOT: Library search paths: +CHECK-SYSLIBROOT-NEXT: [[ROOT]]/usr/lib + +RUN: mkdir -p %t/Library/libxml2-development +RUN: lld -flavor darwinnew -v -syslibroot %t -L /Library/libxml2-development | FileCheck %s -check-prefix CHECK-ABSOLUTE-PATH-REROOTED -DROOT=%t + +CHECK-ABSOLUTE-PATH-REROOTED: Library search paths: +CHECK-ABSOLUTE-PATH-REROOTED: [[ROOT]]/Library/libxml2-development +CHECK-ABSOLUTE-PATH-REROOTED: [[ROOT]]/usr/lib + +# NOTE: the match here is fuzzy because the default search paths exist on Linux +# and macOS, but not on Windows (that is we ignore `/var/empty`). This allows +# us to run the test uniformly on all the platforms. +RUN: lld -flavor darwinnew -v -syslibroot /var/empty -syslibroot / 2>&1 | FileCheck %s -check-prefix CHECK-SYSLIBROOT-IGNORED + +CHECK-SYSLIBROOT-IGNORED: /usr/lib +CHECK-SYSLIBROOT-IGNORED: /usr/local/lib + +RUN: mkdir -p %t.2/usr/lib +RUN: lld -flavor darwinnew -v -syslibroot %t -syslibroot %t.2 | FileCheck %s -check-prefix CHECK-SYSLIBROOT-MATRIX -DROOT=%t + +CHECK-SYSLIBROOT-MATRIX: Library search paths: +CHECK-SYSLIBROOT-MATRIX: [[ROOT]]/usr/lib +CHECK-SYSLIBROOT-MATRIX: [[ROOT]].2/usr/lib + +RUN: mkdir -p %t/System/Library/Frameworks +RUN: lld -flavor darwinnew -v -syslibroot %t | FileCheck %s -check-prefix CHECK-SYSLIBROOT-FRAMEWORK -DROOT=%t + +CHECK-SYSLIBROOT-FRAMEWORK: Framework search paths: +CHECK-SYSLIBROOT-FRAMEWORK: [[ROOT]]/System/Library/Frameworks + +# NOTE: the match here is fuzzy because the default search paths exist on Linux +# and macOS, but not on Windows (that is we ignore `/var/empty`). This allows +# us to run the test uniformly on all the platforms. +RUN: lld -flavor darwinnew -v -syslibroot /var/empty -syslibroot / 2>&1 | FileCheck %s -check-prefix CHECK-SYSLIBROOT-FRAMEWORK-IGNORED + +CHECK-SYSLIBROOT-FRAMEWORK-IGNORED: /System/Library/Framework + +RUN: mkdir -p %t/Library/Frameworks +RUN: mkdir -p %t.2/Library/Frameworks +RUN: lld -flavor darwinnew -v -syslibroot %t -syslibroot %t.2 -F /Library/Frameworks | FileCheck %s -check-prefix CHECK-SYSLIBROOT-FRAMEWORK-MATRIX -DROOT=%t + +CHECK-SYSLIBROOT-FRAMEWORK-MATRIX: Framework search paths: +CHECK-SYSLIBROOT-FRAMEWORK-MATRIX: [[ROOT]]/Library/Frameworks +CHECK-SYSLIBROOT-FRAMEWORK-MATRIX: [[ROOT]].2/Library/Frameworks -- 2.7.4