[llvm-install-name-tool] Add -change option
authorSameer Arora <sameerarora101@fb.com>
Tue, 30 Jun 2020 18:01:51 +0000 (11:01 -0700)
committerShoaib Meenai <smeenai@fb.com>
Tue, 30 Jun 2020 18:28:53 +0000 (11:28 -0700)
Implement `-change` option for install-name-tool. The behavior exactly
matches that of cctools. Depends on D82410.

Reviewed By: jhenderson, smeenai

Differential Revision: https://reviews.llvm.org/D82613

llvm/test/tools/llvm-objcopy/MachO/install-name-tool-change.test [new file with mode: 0644]
llvm/tools/llvm-objcopy/CopyConfig.cpp
llvm/tools/llvm-objcopy/CopyConfig.h
llvm/tools/llvm-objcopy/InstallNameToolOpts.td
llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp

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 (file)
index 0000000..12e1dff
--- /dev/null
@@ -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'
index 20a7627..f93406f 100644 (file)
@@ -907,6 +907,11 @@ parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
   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<StringRef, 2> Positional;
   for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN))
     return createStringError(errc::invalid_argument, "unknown argument '%s'",
index 19fcc2e..ce119de 100644 (file)
@@ -179,6 +179,7 @@ struct CopyConfig {
   std::vector<StringRef> SymbolsToAdd;
   std::vector<StringRef> RPathToAdd;
   std::vector<std::pair<StringRef, StringRef>> RPathsToUpdate;
+  std::vector<std::pair<StringRef, StringRef>> InstallNamesToUpdate;
   DenseSet<StringRef> RPathsToRemove;
 
   // install-name-tool's id option
index 12e27b9..04ffe62 100644 (file)
@@ -27,5 +27,8 @@ def rpath: MultiArg<["-", "--"], "rpath", 2>,
 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.">;
index d0bd602..3844b6f 100644 (file)
@@ -273,15 +273,30 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
       for (std::unique_ptr<Section> &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<MachO::dylib_command>(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<MachO::dylib_command>(LC, New);
+          break;
+        }
       }
     }
   }