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,126 @@ +## 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={{@executable}} + +# RPATHS: @executable_b/. +# RPATHS: @executable_c/. +# RPATHS: @executable_d/. +# RPATHS: @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={{@executable}} + +# RPATHS-MULTIPLE: @executable_d/. +# RPATHS-MULTIPLE: @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={{@executable}} + +# DUPLICATE: @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/. + +## Check that removing load commands updates index for special LCs +# RUN: yaml2obj %s -o %t +# RUN: llvm-install-name-tool -delete_rpath @executable_d/. \ +# RUN: -delete_rpath @executable_e/. %t +# RUN: llvm-objdump -p %t | FileCheck %s --check-prefix=INDEX + +# INDEX: Load command 4 +# INDEX-NEXT: cmd LC_SYMTAB + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 7 + sizeofcmds: 336 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: '' + vmaddr: 0 + vmsize: 80 + fileoff: 368 + filesize: 80 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 15 + offset: 0x00000170 + align: 4 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - 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/.' + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 384 + nsyms: 1 + stroff: 400 + strsize: 8 +LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _main + - '' +... 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,39 @@ using namespace object; using SectionPred = std::function &Sec)>; +using LoadCommandPred = std::function; + +static Error removeLoadCommands(const CopyConfig &Config, Object &Obj) { + DenseSet RPathsToRemove(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 +254,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,38 @@ 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()); + + // Update indices of special load commands + for (auto It = LoadCommands.begin(); It != LoadCommands.end(); ++It) { + auto Index = std::distance(LoadCommands.begin(), It); + switch (It->MachOLoadCommand.load_command_data.cmd) { + case MachO::LC_SYMTAB: + SymTabCommandIndex = Index; + break; + case MachO::LC_DYSYMTAB: + DySymTabCommandIndex = Index; + break; + case MachO::LC_DYLD_INFO: + case MachO::LC_DYLD_INFO_ONLY: + DyLdInfoCommandIndex = Index; + break; + case MachO::LC_DATA_IN_CODE: + DataInCodeCommandIndex = Index; + break; + case MachO::LC_FUNCTION_STARTS: + FunctionStartsCommandIndex = Index; + break; + } + } + return Error::success(); +} + Error Object::removeSections( function_ref &)> ToRemove) { DenseMap OldIndexToSection;