diff --git a/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-prepend-rpath.test b/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-prepend-rpath.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-prepend-rpath.test @@ -0,0 +1,63 @@ +## This test checks prepending a new LC_RPATH load command to a MachO binary. + +# RUN: yaml2obj %p/Inputs/i386.yaml -o %t.i386 +# RUN: llvm-install-name-tool -add_rpath @executable_path/. %t.i386 +# RUN: llvm-install-name-tool -prepend_rpath first_rpath %t.i386 +# RUN: llvm-objdump -p %t.i386 | FileCheck --check-prefix=NEW-RPATH %s + +# RUN: yaml2obj %p/Inputs/x86_64.yaml -o %t.x86_64 +# RUN: llvm-install-name-tool -add_rpath @executable_path/. %t.x86_64 +# RUN: llvm-install-name-tool -prepend_rpath first_rpath %t.x86_64 +# RUN: llvm-objdump -p %t.x86_64 | FileCheck --check-prefix=NEW-RPATH %s + +# NEW-RPATH: cmd LC_RPATH +# NEW-RPATH-NEXT: cmdsize +# NEW-RPATH-NEXT: first_rpath + +# NEW-RPATH: cmd LC_RPATH +# NEW-RPATH-NEXT: cmdsize +# NEW-RPATH-NEXT: @executable_path/. + +## Prepend with dylib loads: +# RUN: yaml2obj %p/Inputs/strip-all.yaml -o %t.dylib +# RUN: llvm-install-name-tool -prepend_rpath first_rpath %t.dylib +# RUN: llvm-objdump -p %t.dylib | FileCheck --check-prefix=DYLIB %s + +# DYLIB: cmd LC_RPATH +# DYLIB-NEXT: cmdsize +# DYLIB-NEXT: first_rpath + +# RUN: not llvm-install-name-tool -prepend_rpath first_rpath %t.i386 2>&1 | \ +# RUN: FileCheck --check-prefix=DUPLICATE-RPATH %s + +# DUPLICATE-RPATH: rpath first_rpath would create a duplicate load command + +## Prepend same RPATH twice: +# RUN: not llvm-install-name-tool -prepend_rpath @executable_X \ +# RUN: -prepend_rpath @executable_X %t.i386 2>&1 | \ +# RUN: FileCheck --check-prefix=DOUBLE %s + +# DOUBLE: rpath @executable_X would create a duplicate load command + +## Prepend and delete RPATH: +# RUN: not llvm-install-name-tool -prepend_rpath foo \ +# RUN: -delete_rpath foo %t.i386 2>&1 | \ +# RUN: FileCheck --check-prefix=DELETE %s + +# DELETE: cannot specify both -prepend_rpath foo and -delete_rpath foo + +## Prepend and replace RPATH: +# RUN: not llvm-install-name-tool -prepend_rpath foo \ +# RUN: -rpath foo bar %t.i386 2>&1 | \ +# RUN: FileCheck --check-prefix=REPLACE %s + +# REPLACE: cannot specify both -prepend_rpath foo and -rpath foo bar + +## Check that cmdsize accounts for NULL terminator: +# RUN: yaml2obj %p/Inputs/x86_64.yaml -o %t.x86_64 +# RUN: llvm-install-name-tool -prepend_rpath abcd %t.x86_64 +# RUN: llvm-objdump -p %t.x86_64 | FileCheck %s --check-prefix=RPATH-SIZE + +# RPATH-SIZE: cmd LC_RPATH +# RPATH-SIZE-NEXT: cmdsize 24 +# RPATH-SIZE-NEXT: path abcd diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h --- a/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/llvm/tools/llvm-objcopy/CopyConfig.h @@ -178,6 +178,7 @@ std::vector DumpSection; std::vector SymbolsToAdd; std::vector RPathToAdd; + std::vector RPathToPrepend; DenseMap RPathsToUpdate; DenseMap InstallNamesToUpdate; DenseSet RPathsToRemove; diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp --- a/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -895,6 +895,9 @@ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath)) Config.RPathToAdd.push_back(Arg->getValue()); + for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_prepend_rpath)) + Config.RPathToPrepend.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) { StringRef RPath = Arg->getValue(); @@ -904,6 +907,11 @@ errc::invalid_argument, "cannot specify both -add_rpath %s and -delete_rpath %s", RPath.str().c_str(), RPath.str().c_str()); + if (is_contained(Config.RPathToPrepend, RPath)) + return createStringError( + errc::invalid_argument, + "cannot specify both -prepend_rpath %s and -delete_rpath %s", + RPath.str().c_str(), RPath.str().c_str()); Config.RPathsToRemove.insert(RPath); } @@ -940,6 +948,13 @@ "cannot specify both -add_rpath " + *It3 + " and -rpath " + Old + " " + New); + // Cannot specify the same rpath under both -prepend_rpath and -rpath. + auto It4 = find_if(Config.RPathToPrepend, Match); + if (It4 != Config.RPathToPrepend.end()) + return createStringError(errc::invalid_argument, + "cannot specify both -prepend_rpath " + *It4 + + " and -rpath " + Old + " " + New); + Config.RPathsToUpdate.insert({Old, New}); } diff --git a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td --- a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td +++ b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td @@ -18,6 +18,9 @@ def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>, HelpText<"Add new rpath">; +def prepend_rpath : Option<["-", "--"], "prepend_rpath", KIND_SEPARATE>, + HelpText<"Add new rpath before other rpaths">; + def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>, HelpText<"Delete specified rpath">; diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -228,6 +228,22 @@ Obj.LoadCommands.push_back(buildRPathLoadCommand(RPath)); } + for (StringRef RPath : Config.RPathToPrepend) { + if (RPaths.count(RPath) != 0) + return createStringError(errc::invalid_argument, + "rpath " + RPath + + " would create a duplicate load command"); + + RPaths.insert(RPath); + Obj.LoadCommands.insert(Obj.LoadCommands.begin(), + buildRPathLoadCommand(RPath)); + } + + // Unlike appending rpaths, the indexes of subsequent load commands must + // be recalculated after prepending one. + if (!Config.RPathToPrepend.empty()) + Obj.updateLoadCommandIndexes(); + return Error::success(); }