diff --git a/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-change.test b/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-change.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-change.test @@ -0,0 +1,107 @@ +## This test checks updating a dependent shared library install name in a MachO binary. + +# RUN: yaml2obj %s -o %t + +## Specifying -change once: +# RUN: cp %t %t.copy +# RUN: llvm-install-name-tool -change /usr/dylib/LOAD /usr/long/long/dylib/LOAD %t.copy +# RUN: llvm-objdump -p %t.copy | FileCheck %s --check-prefix=CHANGE --implicit-check-not=/usr + +# CHANGE: /usr/long/long/dylib/LOAD +# CHANGE: /usr/dylib/WEAK + +## Specifying -change multiple times: +# RUN: cp %t %t.copy +# RUN: llvm-install-name-tool -change /usr/dylib/WEAK /usr/sh/WEAK \ +# RUN: -change /usr/dylib/LOAD /usr/sh/LOAD %t.copy +# RUN: llvm-objdump -p %t.copy | FileCheck %s --check-prefix=CHANGE-MULTIPLE --implicit-check-not=/usr + +# CHANGE-MULTIPLE: /usr/sh/LOAD +# CHANGE-MULTIPLE: /usr/sh/WEAK + +## Changing same dependent library name multiple times: +# RUN: cp %t %t.copy +# RUN: llvm-install-name-tool -change /usr/dylib/LOAD /usr/LOAD \ +# RUN: -change /usr/dylib/LOAD /usr/XXXX %t.copy +# RUN: llvm-objdump -p %t.copy | FileCheck %s --check-prefix=CHANGE-REPEAT --implicit-check-not=/usr + +# CHANGE-REPEAT: /usr/LOAD +# CHANGE-REPEAT: /usr/dylib/WEAK + +## Specifying dependent library names in a chain: +# RUN: cp %t %t.copy +# RUN: llvm-install-name-tool -change /usr/dylib/LOAD /usr/XX/LOAD \ +# RUN: -change /usr/XX/LOAD /usr/YY/LOAD %t.copy +# RUN: llvm-objdump -p %t.copy | FileCheck %s --check-prefix=CHANGE-CHAIN --implicit-check-not=/usr + +# CHANGE-CHAIN: /usr/XX/LOAD +# CHANGE-CHAIN: /usr/dylib/WEAK + +## Changing multiple dependent library names where one exists and the other doesn't: +# RUN: cp %t %t.copy +# RUN: llvm-install-name-tool -change /usr/dylib/LOAD /usr/JOJO/LOAD \ +# RUN: -change /usr/BIZARRE /usr/KOKO/LOAD %t.copy +# RUN: llvm-objdump -p %t.copy | FileCheck %s --check-prefix=CHANGE-SWITCH --implicit-check-not=/usr + +# CHANGE-SWITCH: /usr/JOJO/LOAD +# CHANGE-SWITCH: /usr/dylib/WEAK + +## Changing to a common dependent library name: +# RUN: cp %t %t.copy +# RUN: llvm-install-name-tool -change /usr/dylib/LOAD /usr/COMMON \ +# RUN: -change /usr/dylib/WEAK /usr/COMMON %t.copy +# RUN: llvm-objdump -p %t.copy | FileCheck %s --check-prefix=CHANGE-COMMON --implicit-check-not=/usr + +# CHANGE-COMMON: /usr/COMMON +# CHANGE-COMMON: /usr/COMMON + +## Change all common dependent library names at once: +# RUN: llvm-install-name-tool -change /usr/COMMON /usr/ONCE %t.copy +# RUN: llvm-objdump -p %t.copy | FileCheck %s --check-prefix=CHANGE-ONCE --implicit-check-not=/usr + +# CHANGE-ONCE: /usr/ONCE +# CHANGE-ONCE: /usr/ONCE + +## Check that -change option has no effect if the binary doesn't contain old install name: +# RUN: cp %t %t1 +# RUN: llvm-install-name-tool -change /usr/JOJO/LOAD /usr/XX/LOAD \ +# RUN: -change /usr/KOKO/WEAK /usr/YY/WEAK %t +# RUN: cmp %t %t1 + +## Missing a -change argument: +# RUN: not llvm-install-name-tool %t -change /usr/ONCE 2>&1 | \ +# RUN: FileCheck %s --check-prefix=MISSING + +## Missing both -change arguments: +# RUN: not llvm-install-name-tool %t -change 2>&1 | \ +# RUN: FileCheck %s --check-prefix=MISSING + +# MISSING: missing argument to -change option + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 2 + sizeofcmds: 80 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_LOAD_DYLIB + cmdsize: 40 + dylib: + name: 24 + timestamp: 2 + current_version: 82115073 + compatibility_version: 65536 + PayloadString: '/usr/dylib/LOAD' + - cmd: LC_LOAD_WEAK_DYLIB + cmdsize: 40 + dylib: + name: 24 + timestamp: 2 + current_version: 82115073 + compatibility_version: 65536 + PayloadString: '/usr/dylib/WEAK' 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 @@ -179,6 +179,7 @@ std::vector SymbolsToAdd; std::vector RPathToAdd; std::vector> RPathsToUpdate; + std::vector> InstallNamesToUpdate; DenseSet RPathsToRemove; // install-name-tool's id option 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 @@ -907,6 +907,11 @@ if (auto *Arg = InputArgs.getLastArg(INSTALL_NAME_TOOL_id)) Config.SharedLibId = Arg->getValue(); + for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_change)) { + Config.InstallNamesToUpdate.emplace_back(Arg->getValue(0), + Arg->getValue(1)); + } + 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 @@ -27,5 +27,8 @@ def id : Option<["-","--"], "id", KIND_SEPARATE>, HelpText<"Change dynamic shared library id">; +def change: MultiArg<["-", "--"], "change", 2>, + HelpText<"Change dependent shared library install name">; + 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 @@ -273,15 +273,30 @@ for (std::unique_ptr
&Sec : LC.Sections) Sec->Relocations.clear(); - if (Config.SharedLibId) { - StringRef Id = Config.SharedLibId.getValue(); - if (Id.empty()) - return createStringError(errc::invalid_argument, - "cannot specify an empty id"); - for (LoadCommand &LC : Obj.LoadCommands) { - if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB) { + for (LoadCommand &LC : Obj.LoadCommands) { + switch (LC.MachOLoadCommand.load_command_data.cmd) { + case MachO::LC_ID_DYLIB: + if (Config.SharedLibId) { + StringRef Id = Config.SharedLibId.getValue(); + if (Id.empty()) + return createStringError(errc::invalid_argument, + "cannot specify an empty id"); updateLoadCommandPayloadString(LC, Id); - break; + } + break; + + // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB + // here once llvm-objcopy supports them. + case MachO::LC_LOAD_DYLIB: + case MachO::LC_LOAD_WEAK_DYLIB: + StringRef Old, New; + StringRef CurrentInstallName = getPayloadString(LC); + for (const auto &InstallNamePair : Config.InstallNamesToUpdate) { + std::tie(Old, New) = InstallNamePair; + if (CurrentInstallName == Old) { + updateLoadCommandPayloadString(LC, New); + break; + } } } }