diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -76,16 +76,19 @@ } } -static Optional -findAlongPathsWithExtensions(StringRef name, ArrayRef extensions) { +// Search for all possible combinations of `{root}/{name}.{extension}`. +// If \p extensions are not specified, then just search for `{root}/{name}`. +static Optional +findPathCombination(const Twine &name, const std::vector &roots, + ArrayRef extensions = {""}) { SmallString<261> base; - for (StringRef dir : config->librarySearchPaths) { + for (StringRef dir : roots) { base = dir; - path::append(base, Twine("lib") + name); + path::append(base, name); for (StringRef ext : extensions) { Twine location = base + ext; if (fs::exists(location)) - return location.str(); + return saver.save(location.str()); else depTracker->logFileNotFound(location); } @@ -93,14 +96,29 @@ return {}; } -static Optional findLibrary(StringRef name) { +static Optional findLibrary(StringRef name) { if (config->searchDylibsFirst) { - if (Optional path = - findAlongPathsWithExtensions(name, {".tbd", ".dylib"})) + if (Optional path = findPathCombination( + "lib" + name, config->librarySearchPaths, {".tbd", ".dylib"})) return path; - return findAlongPathsWithExtensions(name, {".a"}); + return findPathCombination("lib" + name, config->librarySearchPaths, + {".a"}); } - return findAlongPathsWithExtensions(name, {".tbd", ".dylib", ".a"}); + return findPathCombination("lib" + name, config->librarySearchPaths, + {".tbd", ".dylib", ".a"}); +} + +// If -syslibroot is specified, absolute paths to non-object files may be +// rerooted. +static StringRef rerootPath(StringRef path) { + if (!path::is_absolute(path, path::Style::posix) || path.endswith(".o")) + return path; + + if (Optional rerootedPath = + findPathCombination(path, config->systemLibraryRoots)) + return *rerootedPath; + + return path; } static Optional findFramework(StringRef name) { @@ -337,7 +355,7 @@ } static void addLibrary(StringRef name, bool isWeak) { - if (Optional path = findLibrary(name)) { + if (Optional path = findLibrary(name)) { auto *dylibFile = dyn_cast_or_null(addFile(*path, false)); if (isWeak && dylibFile) dylibFile->forceWeakImport = true; @@ -396,7 +414,7 @@ return; MemoryBufferRef mbref = *buffer; for (StringRef path : args::getLines(mbref)) - addFile(path, false); + addFile(rerootPath(path), false); } // An order file has one entry per line, in the following format: @@ -814,18 +832,18 @@ switch (opt.getID()) { case OPT_INPUT: - addFile(arg->getValue(), false); + addFile(rerootPath(arg->getValue()), false); break; case OPT_weak_library: - if (auto *dylibFile = - dyn_cast_or_null(addFile(arg->getValue(), false))) + if (auto *dylibFile = dyn_cast_or_null( + addFile(rerootPath(arg->getValue()), false))) dylibFile->forceWeakImport = true; break; case OPT_filelist: addFileList(arg->getValue()); break; case OPT_force_load: - addFile(arg->getValue(), true); + addFile(rerootPath(arg->getValue()), true); break; case OPT_l: case OPT_weak_l: diff --git a/lld/test/MachO/dependency-info.s b/lld/test/MachO/dependency-info.s --- a/lld/test/MachO/dependency-info.s +++ b/lld/test/MachO/dependency-info.s @@ -20,7 +20,7 @@ # CHECK-DAG: input-file: {{.*}}{{[/\]}}main.o # CHECK-DAG: input-file: {{.*}}bar.o -# CHECK-NEXT: not-found: {{.*}}{{[/\]}}libdyld.{{.*}} +# CHECK: not-found: {{.*}}{{[/\]}}libdyld.{{.*}} ## There could be more not-found here but we are not checking those because it's brittle. # CHECK: output-file: {{.*}}{{[/\]}}test.out diff --git a/lld/test/MachO/reroot-path.s b/lld/test/MachO/reroot-path.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/reroot-path.s @@ -0,0 +1,64 @@ +# REQUIRES: x86 +# UNSUPPORTED: system-windows +## FIXME: In principle this test should pass on Windows +# RUN: rm -rf %t; split-file %s %t + +## This test verifies that we attempt to re-root absolute paths if -syslibroot +## is specified. Therefore we would like to be able to specify an absolute path +## without worrying that it may match an actual file on the system outside the +## syslibroot. `chroot` would do the job but isn't cross-platform, so I've used +## this %t/%:t hack instead. +# RUN: mkdir -p %t/%:t + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/bar.s -o %t/bar.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o + +## bar.a is under %t/%:t, and so verifies that rerooting happens. foo.a isn't, +## and therefore verifies that we still fall back to the original path if no +## file exists at the rerooted path. +# RUN: llvm-ar rcs %t/foo.a %t/foo.o +# RUN: %lld -dylib %t/foo.o -o %t/libfoo.dylib +# RUN: llvm-ar rcs %t/%:t/bar.a %t/bar.o +# RUN: %lld -dylib %t/bar.o -o %t/%:t/libbar.dylib + +## Test our various file-loading flags to make sure all bases are covered. +# RUN: %lld -lSystem -syslibroot %t %t/foo.a %t/bar.a %t/test.o -o /dev/null -t | FileCheck %s -DDIR="%t/%:t" +# RUN: %lld -lSystem -syslibroot %t -force_load %t/foo.a -force_load %t/bar.a %t/test.o -o /dev/null -t | FileCheck %s -DDIR="%t/%:t" +# RUN: %lld -lSystem -syslibroot %t %t/libfoo.dylib %t/libbar.dylib %t/test.o -o /dev/null -t | FileCheck %s -DDIR="%t/%:t" +# RUN: %lld -lSystem -syslibroot %t -weak_library %t/libfoo.dylib -weak_library %t/libbar.dylib %t/test.o -o /dev/null -t | FileCheck %s -DDIR="%t/%:t" +# RUN: echo "%t/libfoo.dylib" > %t/filelist +# RUN: echo "%t/libbar.dylib" >> %t/filelist +# RUN: %lld -lSystem -syslibroot %t -filelist %t/filelist %t/test.o -o /dev/null -t | FileCheck %s -DDIR="%t/%:t" +# CHECK: [[DIR]]/{{(lib)?}}bar + +## Paths to object files don't get rerooted. +# RUN: mv %t/bar.o %t/%:t/bar.o +# RUN: not %lld -lSystem -syslibroot %t %t/foo.o %t/bar.o %t/test.o -o \ +# RUN: /dev/null 2>&1 | FileCheck %s --check-prefix=OBJ +# OBJ: error: cannot open {{.*[\\/]}}bar.o: {{[Nn]}}o such file or directory + +## Now create a "decoy" libfoo.dylib under %t/%:t to demonstrate that the +## rerooted path takes precedence over the original path. We will get an +## undefined symbol error since we aren't loading %t/libfoo.dylib. +# RUN: cp %t/%:t/libbar.dylib %t/%:t/libfoo.dylib +# RUN: not %lld -lSystem -syslibroot %t %t/libfoo.dylib %t/libbar.dylib %t/test.o \ +# RUN: -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNDEF +# UNDEF: error: undefined symbol: _foo + +#--- foo.s +.globl _foo +_foo: + +#--- bar.s +.globl _bar +_bar: + +#--- test.s +.text +.globl _main + +_main: + callq _foo + callq _bar + ret