diff --git a/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-delete-rpath.test b/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-delete-rpath.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-delete-rpath.test @@ -0,0 +1,75 @@ +## This test checks deleting a LC_RPATH load command from a MachO binary. + +# RUN: yaml2obj %s -o %t + +## Deleting a single RPath entry: +# RUN: llvm-install-name-tool -delete_rpath @executable_a/. %t +# RUN: llvm-objdump -p %t | \ +# RUN: FileCheck %s --check-prefix=RPATHS --implicit-check-not={{path[[:space:]]@executable_[a]}} + +# RPATHS: path @executable_b/. +# RPATHS: path @executable_c/. +# RPATHS: path @executable_d/. +# RPATHS: path @executable_e/. + +## Deleting multiple RPath entries: +# RUN: llvm-install-name-tool -delete_rpath @executable_b/. \ +# RUN: -delete_rpath @executable_c/. %t +# RUN: llvm-objdump -p %t | \ +# RUN: FileCheck %s --check-prefix=RPATHS-MULTIPLE --implicit-check-not={{path[[:space:]]@executable_[abc]}} + +# RPATHS-MULTIPLE: path @executable_d/. +# RPATHS-MULTIPLE: path @executable_e/. + +## Duplicate delete_rpath commands: +# RUN: llvm-install-name-tool -delete_rpath @executable_d/. \ +# RUN: -delete_rpath @executable_d/. %t +# RUN: llvm-objdump -p %t | \ +# RUN: FileCheck %s --check-prefix=DUPLICATE --implicit-check-not={{path[[:space:]]@executable_[abcd]}} + +# DUPLICATE: path @executable_e/. + +## Deleting a nonexistent RPath: +# RUN: not llvm-install-name-tool -delete_rpath @executable_a/. %t 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ABSENT-RPATH + +# ABSENT-RPATH: no LC_RPATH load command with path: @executable_a/. + +## Adding and deleting RPATH at the same time: +# RUN: not llvm-install-name-tool -add_rpath @executable_b/. \ +# RUN: -delete_rpath @executable_b/. %t 2>&1 | \ +# RUN: FileCheck %s --check-prefix=COMBINED + +# COMBINED: cannot specify both -add_rpath @executable_b/. and -delete_rpath @executable_b/. + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 5 + sizeofcmds: 160 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_RPATH + cmdsize: 32 + path: 12 + PayloadString: '@executable_a/.' + - cmd: LC_RPATH + cmdsize: 32 + path: 12 + PayloadString: '@executable_b/.' + - cmd: LC_RPATH + cmdsize: 32 + path: 12 + PayloadString: '@executable_c/.' + - cmd: LC_RPATH + cmdsize: 32 + path: 12 + PayloadString: '@executable_d/.' + - cmd: LC_RPATH + cmdsize: 32 + path: 12 + PayloadString: '@executable_e/.' 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 @@ -12,6 +12,7 @@ #include "ELF/ELFConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" @@ -177,6 +178,7 @@ std::vector DumpSection; std::vector SymbolsToAdd; std::vector RPathToAdd; + DenseSet RPathsToRemove; // Section matchers NameMatcher KeepSection; 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 @@ -826,6 +826,19 @@ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath)) Config.RPathToAdd.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) { + StringRef RPath = Arg->getValue(); + + // Cannot add & delete the same rpath at the same time. + if (is_contained(Config.RPathToAdd, RPath)) + return createStringError( + errc::invalid_argument, + "cannot specify both -add_rpath %s and -delete_rpath %s", + RPath.str().c_str(), RPath.str().c_str()); + + Config.RPathsToRemove.insert(RPath); + } + SmallVector Positional; for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN)) return createStringError(errc::invalid_argument, "unknown argument '%s'", 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,5 +18,8 @@ def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>, HelpText<"Add new rpath">; +def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>, + HelpText<"Delete specified rpath">; + def version : Flag<["--"], "version">, HelpText<"Print the version and exit.">; 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 @@ -10,6 +10,7 @@ #include "../CopyConfig.h" #include "MachOReader.h" #include "MachOWriter.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" @@ -19,6 +20,40 @@ using namespace object; using SectionPred = std::function &Sec)>; +using LoadCommandPred = std::function; + +static Error removeLoadCommands(const CopyConfig &Config, Object &Obj) { + DenseSet RPathsToRemove; + + RPathsToRemove.insert(Config.RPathsToRemove.begin(), + Config.RPathsToRemove.end()); + LoadCommandPred RemovePred = [&RPathsToRemove](const LoadCommand &LC) { + if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) { + StringRef RPath = + StringRef(reinterpret_cast(LC.Payload.data()), + LC.Payload.size()) + .rtrim('\0'); + if (RPathsToRemove.count(RPath)) { + RPathsToRemove.erase(RPath); + return true; + } + } + return false; + }; + + if (Error E = Obj.removeLoadCommands(RemovePred)) + return E; + + // Emit an error if the Mach-O binary does not contain an rpath path name + // specified in -delete_rpath. + for (StringRef RPath : Config.RPathsToRemove) { + if (RPathsToRemove.count(RPath)) + return createStringError(errc::invalid_argument, + "no LC_RPATH load command with path: %s", + RPath.str().c_str()); + } + return Error::success(); +} static Error removeSections(const CopyConfig &Config, Object &Obj) { SectionPred RemovePred = [](const std::unique_ptr
&) { @@ -220,6 +255,9 @@ return E; } + if (Error E = removeLoadCommands(Config, Obj)) + return E; + for (StringRef RPath : Config.RPathToAdd) { for (LoadCommand &LC : Obj.LoadCommands) { if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH && diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h --- a/llvm/tools/llvm-objcopy/MachO/Object.h +++ b/llvm/tools/llvm-objcopy/MachO/Object.h @@ -323,6 +323,9 @@ Error removeSections(function_ref &)> ToRemove); + + Error removeLoadCommands(function_ref ToRemove); + void addLoadCommand(LoadCommand LC); /// Creates a new segment load command in the object and returns a reference diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp --- a/llvm/tools/llvm-objcopy/MachO/Object.cpp +++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -32,6 +32,15 @@ std::end(Symbols)); } +Error Object::removeLoadCommands( + function_ref ToRemove) { + auto It = std::stable_partition( + LoadCommands.begin(), LoadCommands.end(), + [&](const LoadCommand &LC) { return !ToRemove(LC); }); + LoadCommands.erase(It, LoadCommands.end()); + return Error::success(); +} + Error Object::removeSections( function_ref &)> ToRemove) { DenseMap OldIndexToSection;