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=@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/. + +--- !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/test/tools/llvm-objcopy/MachO/remove-lc-index-update.test b/llvm/test/tools/llvm-objcopy/MachO/remove-lc-index-update.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/remove-lc-index-update.test @@ -0,0 +1,343 @@ +## Check that removing load commands updates the indexes of special LCs: + +# RUN: yaml2obj %s -o %t +# RUN: llvm-install-name-tool -delete_rpath @executable_a/. %t +# RUN: llvm-objdump -p %t | FileCheck %s --check-prefix=INDEX + +# INDEX: Load command 3 +# INDEX-NEXT: cmd LC_DYLD_INFO_ONLY +# INDEX: Load command 5 +# INDEX-NEXT: cmd LC_DATA_IN_CODE +# INDEX: Load command 8 +# INDEX-NEXT: cmd LC_FUNCTION_STARTS +# INDEX: Load command 13 +# INDEX-NEXT: cmd LC_DYSYMTAB +# INDEX: Load command 14 +# INDEX-NEXT: cmd LC_SYMTAB + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x80000003 + filetype: 0x00000002 + ncmds: 16 + sizeofcmds: 1008 + flags: 0x00200085 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __PAGEZERO + vmaddr: 0 + vmsize: 4294967296 + fileoff: 0 + filesize: 0 + maxprot: 0 + initprot: 0 + nsects: 0 + flags: 0 + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __TEXT + vmaddr: 4294967296 + vmsize: 4096 + fileoff: 0 + filesize: 4096 + maxprot: 7 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000100000F90 + size: 36 + offset: 0x00000F90 + align: 4 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __unwind_info + segname: __TEXT + addr: 0x0000000100000FB4 + size: 72 + offset: 0x00000FB4 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __DATA + vmaddr: 4294971392 + vmsize: 4096 + fileoff: 4096 + filesize: 4096 + maxprot: 7 + initprot: 3 + nsects: 2 + flags: 0 + Sections: + - sectname: __data + segname: __DATA + addr: 0x0000000100001000 + size: 4 + offset: 0x00001000 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __common + segname: __DATA + addr: 0x0000000100001004 + size: 4 + offset: 0x00000000 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000001 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - cmd: LC_RPATH + cmdsize: 32 + path: 12 + PayloadString: '@executable_a/.' + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 0 + rebase_size: 0 + bind_off: 0 + bind_size: 0 + weak_bind_off: 0 + weak_bind_size: 0 + lazy_bind_off: 0 + lazy_bind_size: 0 + export_off: 8192 + export_size: 56 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4294975488 + vmsize: 4096 + fileoff: 8192 + filesize: 496 + maxprot: 7 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 8256 + datasize: 8 + - cmd: LC_LOAD_DYLINKER + cmdsize: 32 + name: 12 + PayloadString: '/usr/lib/dyld' + ZeroPadBytes: 7 + - cmd: LC_UUID + cmdsize: 24 + uuid: 56D84164-1DA8-393B-8589-BC4C6B56168E + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 8248 + datasize: 8 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 658944 + sdk: 658944 + ntools: 1 + Tools: + - tool: 3 + version: 29491968 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_MAIN + cmdsize: 24 + entryoff: 3984 + stacksize: 0 + - cmd: LC_LOAD_DYLIB + cmdsize: 56 + dylib: + name: 24 + timestamp: 2 + current_version: 82115073 + compatibility_version: 65536 + PayloadString: '/usr/lib/libSystem.B.dylib' + ZeroPadBytes: 6 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 11 + iextdefsym: 11 + nextdefsym: 3 + iundefsym: 14 + nundefsym: 1 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 8264 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 8264 + nsyms: 15 + stroff: 8504 + strsize: 192 +LinkEditData: + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + Children: + - TerminalSize: 0 + NodeOffset: 5 + Name: _ + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + Children: + - TerminalSize: 2 + NodeOffset: 38 + Name: _mh_execute_header + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 42 + Name: main + Flags: 0x0000000000000000 + Address: 0x0000000000000F90 + Other: 0x0000000000000000 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 47 + Name: foo + Flags: 0x0000000000000000 + Address: 0x0000000000001004 + Other: 0x0000000000000000 + ImportName: '' + NameList: + - n_strx: 50 + n_type: 0x64 + n_sect: 0 + n_desc: 0 + n_value: 0 + - n_strx: 92 + n_type: 0x64 + n_sect: 0 + n_desc: 0 + n_value: 0 + - n_strx: 100 + n_type: 0x66 + n_sect: 3 + n_desc: 1 + n_value: 1564714240 + - n_strx: 1 + n_type: 0x2E + n_sect: 1 + n_desc: 0 + n_value: 4294971280 + - n_strx: 164 + n_type: 0x24 + n_sect: 1 + n_desc: 0 + n_value: 4294971280 + - n_strx: 1 + n_type: 0x24 + n_sect: 0 + n_desc: 0 + n_value: 36 + - n_strx: 1 + n_type: 0x4E + n_sect: 1 + n_desc: 0 + n_value: 36 + - n_strx: 170 + n_type: 0x26 + n_sect: 3 + n_desc: 0 + n_value: 4294971392 + ## A local undefined SymDebugTable entry followed by + - n_strx: 175 + n_type: 0x20 + n_sect: 0 + n_desc: 0 + n_value: 0 + ## a defined SymDebugTable entry. + - n_strx: 1 + n_type: 0x64 + n_sect: 1 + n_desc: 0 + n_value: 0 + - n_strx: 180 + n_type: 0x0E + n_sect: 3 + n_desc: 0 + n_value: 4294971392 + - n_strx: 2 + n_type: 0x0F + n_sect: 1 + n_desc: 16 + n_value: 4294967296 + - n_strx: 22 + n_type: 0x0F + n_sect: 4 + n_desc: 0 + n_value: 4294971396 + - n_strx: 27 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 4294971280 + - n_strx: 33 + n_type: 0x01 + n_sect: 0 + n_desc: 256 + n_value: 0 + StringTable: + - ' ' + - __mh_execute_header + - _foo + - _main + - dyld_stub_binder + - '/Users/anonymous/llvm-project/llvm/build/' + - strip.c + - '/var/folders/g7/l0p5czpd2jz6w0gswg6cwgjc0000gn/T/strip-95c4bf.o' + - _main + - _bar + - _foo + - _bar + - '' + - '' + - '' + - '' + - '' + - '' + - '' +... 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 and 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 @@ -326,6 +326,11 @@ Error removeSections(function_ref &)> ToRemove); + + Error removeLoadCommands(function_ref ToRemove); + + void updateLoadCommandIndexes(); + 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,42 @@ std::end(Symbols)); } +void Object::updateLoadCommandIndexes() { + // Update indices of special load commands + for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) { + LoadCommand &LC = LoadCommands[Index]; + switch (LC.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; + } + } +} + +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()); + + updateLoadCommandIndexes(); + return Error::success(); +} + Error Object::removeSections( function_ref &)> ToRemove) { DenseMap OldIndexToSection;