diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp --- a/lld/MachO/DriverUtils.cpp +++ b/lld/MachO/DriverUtils.cpp @@ -216,10 +216,10 @@ file = make(**result, umbrella, isBundleLoader); // parseReexports() can recursively call loadDylib(). That's fine since - // we wrote DylibFile we just loaded to the loadDylib cache via the `file` - // reference. But the recursive load can grow loadDylibs, so the `file` - // reference might become invalid after parseReexports() -- so copy the - // pointer it refers to before going on. + // we wrote the DylibFile we just loaded to the loadDylib cache via the + // `file` reference. But the recursive load can grow loadDylibs, so the + // `file` reference might become invalid after parseReexports() -- so copy + // the pointer it refers to before continuing. newFile = file; if (newFile->exportingFile) newFile->parseReexports(**result); diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -127,16 +127,9 @@ static bool classof(const InputFile *f) { return f->kind() == OpaqueKind; } }; -// .dylib file +// .dylib or .tbd file class DylibFile : public InputFile { public: - explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella, - bool isBundleLoader = false); - - explicit DylibFile(const llvm::MachO::InterfaceFile &interface, - DylibFile *umbrella = nullptr, - bool isBundleLoader = false); - // Mach-O dylibs can re-export other dylibs as sub-libraries, meaning that the // symbols in those sub-libraries will be available under the umbrella // library's namespace. Those sub-libraries can also have their own @@ -144,6 +137,12 @@ // the root dylib to ensure symbols in the child library are correctly bound // to the root. On the other hand, if a dylib is being directly loaded // (through an -lfoo flag), then `umbrella` should be a nullptr. + explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella, + bool isBundleLoader = false); + explicit DylibFile(const llvm::MachO::InterfaceFile &interface, + DylibFile *umbrella = nullptr, + bool isBundleLoader = false); + void parseLoadCommands(MemoryBufferRef mb); void parseReexports(const llvm::MachO::InterfaceFile &interface); @@ -152,6 +151,7 @@ StringRef installName; DylibFile *exportingFile = nullptr; DylibFile *umbrella; + SmallVector rpaths; uint32_t compatibilityVersion = 0; uint32_t currentVersion = 0; int64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -760,7 +760,8 @@ resolveDylibPath((root + path).str())) return loadDylib(*dylibPath, umbrella); - // TODO: Expand @rpath, handle -dylib_file + // TODO: Handle -dylib_file + SmallString<128> newPath; if (config->outputType == MH_EXECUTE && path.consume_front("@executable_path/")) { @@ -771,6 +772,15 @@ } else if (path.consume_front("@loader_path/")) { path::append(newPath, sys::path::parent_path(umbrella->getName()), path); path = newPath; + } else if (path.startswith("@rpath/")) { + for (StringRef rpath : umbrella->rpaths) { + newPath.clear(); + if (rpath.consume_front("@loader_path/")) + path::append(newPath, sys::path::parent_path(umbrella->getName())); + path::append(newPath, rpath, path.drop_front(strlen("@rpath/"))); + if (Optional dylibPath = resolveDylibPath(newPath)) + return loadDylib(*dylibPath, umbrella); + } } if (currentTopLevelTapi) { @@ -854,6 +864,11 @@ if (!checkCompatibility(this)) return; + for (auto *cmd : findCommands(hdr, LC_RPATH)) { + StringRef rpath{reinterpret_cast(cmd) + cmd->path}; + rpaths.push_back(rpath); + } + // Initialize symbols. exportingFile = isImplicitlyLinked(installName) ? this : this->umbrella; if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) { diff --git a/lld/test/MachO/link-search-at-rpath.s b/lld/test/MachO/link-search-at-rpath.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/link-search-at-rpath.s @@ -0,0 +1,38 @@ +# REQUIRES: x86 + +# RUN: rm -rf %t; split-file %s %t +# RUN: mkdir %t/subdir +# RUN: mkdir %t/subdir2 + +# 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/main.s -o %t/main.o + +# RUN: %lld -dylib -install_name @rpath/libfoo.dylib %t/foo.o -o %t/subdir/libfoo.dylib + +# RUN: %lld -dylib -reexport_library %t/subdir/libfoo.dylib \ +# RUN: -rpath @loader_path/../foo \ +# RUN: -rpath @loader_path/../subdir \ +# RUN: -rpath @loader_path/../foo \ +# RUN: %t/bar.o -o %t/subdir2/libbar.dylib + +# RUN: %lld -lSystem %t/main.o %t/subdir2/libbar.dylib -o %t/test +# RUN: %lld -dylib -lSystem %t/main.o %t/subdir2/libbar.dylib -o %t/libtest.dylib + +#--- foo.s +.globl _foo +_foo: + retq + +#--- bar.s +.globl _bar +_bar: + retq + +#--- main.s +.section __TEXT,__text +.global _main +_main: + callq _foo + callq _bar + ret diff --git a/lld/test/MachO/rpath.s b/lld/test/MachO/rpath.s --- a/lld/test/MachO/rpath.s +++ b/lld/test/MachO/rpath.s @@ -3,11 +3,14 @@ # RUN: %lld -o %t %t.o ## Check that -rpath generates LC_RPATH. -# RUN: %lld -o %t %t.o -rpath /some/rpath +# RUN: %lld -o %t %t.o -rpath /some/rpath -rpath /another/rpath # RUN: llvm-objdump --macho --all-headers %t | FileCheck %s # CHECK: LC_RPATH # CHECK-NEXT: cmdsize 24 # CHECK-NEXT: path /some/rpath +# CHECK: LC_RPATH +# CHECK-NEXT: cmdsize 32 +# CHECK-NEXT: path /another/rpath .text .global _main