Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -21,6 +21,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/VirtualFileSystem.h" #include #include #include @@ -358,6 +359,10 @@ // this means to map the primary and thread stacks as PROT_MTE. Note: This is // not supported on Android 11 & 12. bool androidMemtagStack; + + // Virtual file system to use for library path resolution. + std::unique_ptr vfs; + bool vfsonly; }; // The only instance of Configuration struct. Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -1020,12 +1020,37 @@ return arg == "none" || arg == "warning" || arg == "error"; } +static std::pair, bool> +getVFS(const opt::InputArgList &args) { + using namespace llvm::vfs; + + const opt::Arg *arg = args.getLastArg(OPT_vfsoverlay, OPT_vfsreplace); + if (!arg) + return {}; + + auto bufOrErr = llvm::MemoryBuffer::getFile(arg->getValue()); + if (!bufOrErr) + return {}; + + std::unique_ptr vfs = vfs::getVFSFromYAML( + std::move(*bufOrErr), /*DiagHandler*/ nullptr, arg->getValue()); + + auto RedirectOnly = RedirectingFileSystem::RedirectKind::RedirectOnly; + bool vfsonly = arg->getSpelling() == "--vfsreplace"; + if (vfsonly) + reinterpret_cast(vfs.get())->setRedirection( + RedirectOnly); + return {std::move(vfs), vfsonly}; +} + // Initializes Config members by the command line options. static void readConfigs(opt::InputArgList &args) { errorHandler().verbose = args.hasArg(OPT_verbose); errorHandler().vsDiagnostics = args.hasArg(OPT_visual_studio_diagnostics_format, false); + std::tie(config->vfs, config->vfsonly) = getVFS(args); + config->allowMultipleDefinition = args.hasFlag(OPT_allow_multiple_definition, OPT_no_allow_multiple_definition, false) || Index: lld/ELF/DriverUtils.cpp =================================================================== --- lld/ELF/DriverUtils.cpp +++ lld/ELF/DriverUtils.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/Host.h" #include "llvm/Support/Path.h" #include "llvm/Support/TimeProfiler.h" +#include "llvm/Support/VirtualFileSystem.h" using namespace llvm; using namespace llvm::sys; @@ -207,13 +208,25 @@ // Find a file by concatenating given paths. If a resulting path // starts with "=", the character is replaced with a --sysroot value. -static Optional findFile(StringRef path1, const Twine &path2) { +static Optional findFile(StringRef path1, const Twine &path2, + llvm::vfs::FileSystem *vfs = nullptr, + bool vfsonly = false) { SmallString<128> s; if (path1.startswith("=")) path::append(s, config->sysroot, path1.substr(1), path2); else path::append(s, path1, path2); + Optional stat; + if (vfs) + if (auto statOrErr = vfs->status(s)) + stat = *statOrErr; + + if (vfsonly && !stat) + return None; + if (stat) + s = stat->getName(); + if (fs::exists(s)) return std::string(s); return None; @@ -221,7 +234,8 @@ Optional elf::findFromSearchPaths(StringRef path) { for (StringRef dir : config->searchPaths) - if (Optional s = findFile(dir, path)) + if (Optional s = + findFile(dir, path, config->vfs.get(), config->vfsonly)) return s; return None; } @@ -231,9 +245,11 @@ Optional elf::searchLibraryBaseName(StringRef name) { for (StringRef dir : config->searchPaths) { if (!config->isStatic) - if (Optional s = findFile(dir, "lib" + name + ".so")) + if (Optional s = findFile( + dir, "lib" + name + ".so", config->vfs.get(), config->vfsonly)) return s; - if (Optional s = findFile(dir, "lib" + name + ".a")) + if (Optional s = findFile(dir, "lib" + name + ".a", + config->vfs.get(), config->vfsonly)) return s; } return None; Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -728,3 +728,8 @@ "Instruct the dynamic loader to enable MTE protection for the heap", "">; defm android_memtag_mode: EEq<"android-memtag-mode", "Instruct the dynamic loader to start under MTE mode {async, sync, none}">; + +defm vfsoverlay: EEq<"vfsoverlay", + "Use the filesystem overlay yaml file to change the library search path">; +defm vfsreplace: EEq<"vfsreplace", + "Use the filesystem overlay yaml file to change the library search path, and don't fallback to the real filesystem">; Index: lld/test/ELF/vfsoverlay.test =================================================================== --- /dev/null +++ lld/test/ELF/vfsoverlay.test @@ -0,0 +1,99 @@ +# REQUIRES: x86 + +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-elf %t/lib.s -o %t/lib.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-elf %t/main.s -o %t/main.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-elf %t/deplibs.s -o %t/deplibs.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-elf %t/deplibs_fallback.s -o %t/deplibs_fallback.o +# RUN: llvm-ar rc %t/notliblib.a %t/lib.o +# RUN: cp %t/notliblib.a %t/libfallbackrealfs.a +# RUN: sed -e "s|REPLACE_LIBA|%t/notliblib.a|g;s|REPLACE_SCRIPT|%t/notlib.ld|g" %t/overlay.yaml.in > %t/overlay.yaml + +# RUN: ld.lld -o /dev/null %t/main.o -L/noexist -llib --vfsoverlay=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t +# RUN: ld.lld -o /dev/null %t/main.o -L/noexist -llib --vfsreplace=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t + +# RUN: ld.lld -o /dev/null %t/main.o -L/noexist -l:liblib.a --vfsoverlay=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t +# RUN: ld.lld -o /dev/null %t/main.o -L/noexist -l:liblib.a --vfsreplace=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t + +# RUN: ld.lld -o /dev/null %t/main.o -L=. -llib --sysroot="/noexist" --vfsoverlay=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t +# RUN: ld.lld -o /dev/null %t/main.o -L=. -llib --sysroot="/noexist" --vfsreplace=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t + +# RUN: ld.lld -o /dev/null %t/main.o -L%t -lfallbackrealfs --vfsoverlay=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t --check-prefix=FALLBACK +# RUN: not ld.lld -o /dev/null %t/main.o -L%t -lfallbackrealfs --vfsreplace=%t/overlay.yaml 2>&1 |\ +# RUN: FileCheck %s --check-prefix=NOTFOUND + +# RUN: ld.lld -o /dev/null %t/deplibs.o -L/noexist --vfsoverlay=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t +# RUN: ld.lld -o /dev/null %t/deplibs.o -L/noexist --vfsreplace=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t + +# RUN: ld.lld -o /dev/null %t/deplibs_fallback.o -L%t --vfsoverlay=%t/overlay.yaml --trace 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t --check-prefix=FALLBACK +# RUN: not ld.lld -o /dev/null %t/deplibs_fallback.o -L%t --vfsreplace=%t/overlay.yaml 2>&1 |\ +# RUN: FileCheck %s -DDIR=%t --check-prefix=NOTFOUND-DEPLIBS + +# Linker scripts are not emitted by --trace, but ensure that this links, thus the script was found. +# RUN: ld.lld -o /dev/null %t/main.o -L/noexist -T lib.ld --vfsoverlay=%t/overlay.yaml + +# CHECK: [[DIR]]{{[\\/]}}notliblib.a +# FALLBACK: [[DIR]]{{[\\/]}}libfallbackrealfs.a +# NOTFOUND: error: unable to find library -lfallbackrealfs +# NOTFOUND-DEPLIBS: unable to find library from dependent library specifier: fallbackrealfs + +#--- overlay.yaml.in +{ + 'version': 0, + 'roots' : [ + { + 'name': '/noexist', + 'type': 'directory', + 'contents': [ + { + 'name': 'liblib.a', + 'type': 'file', + 'external-contents': 'REPLACE_LIBA' + }, + { + 'name': 'lib.ld', + 'type': 'file', + 'external-contents': 'REPLACE_SCRIPT' + } + ] + } + ] +} + +#--- lib.s +.global sym +sym: + +#--- notlib.ld +sym = 0x0; + +#--- main.s +.global _start +_start: + call sym + +#--- deplibs.s +.global _start +_start: + call sym + +.section ".deplibs", "MS", @llvm_dependent_libraries, 1 + .asciz "lib" + +#--- deplibs_fallback.s +.global _start +_start: + call sym + +.section ".deplibs", "MS", @llvm_dependent_libraries, 1 + .asciz "fallbackrealfs"