[llvm-install-name-tool] Add support for -rpath option
This diff implements -rpath option for llvm-install-name-tool
which replaces the rpath value in the specified Mach-O binary.
Patch by Sameer Arora!
Test plan: make check-all
Differential revision: https://reviews.llvm.org/D82051
diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp
index 749df08..b483116 100644
--- a/llvm/tools/llvm-objcopy/CopyConfig.cpp
+++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp
@@ -827,6 +827,13 @@
llvm::opt::InputArgList InputArgs =
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+ if (MissingArgumentCount)
+ return createStringError(
+ errc::invalid_argument,
+ "missing argument to " +
+ StringRef(InputArgs.getArgString(MissingArgumentIndex)) +
+ " option");
+
if (InputArgs.size() == 0) {
printHelp(T, errs(), ToolType::InstallNameTool);
exit(1);
@@ -860,6 +867,43 @@
Config.RPathsToRemove.insert(RPath);
}
+ for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_rpath)) {
+ StringRef Old = Arg->getValue(0);
+ StringRef New = Arg->getValue(1);
+
+ auto Match = [=](StringRef RPath) { return RPath == Old || RPath == New; };
+
+ // Cannot specify duplicate -rpath entries
+ auto It1 = find_if(Config.RPathsToUpdate,
+ [&Match](const std::pair<StringRef, StringRef> &OldNew) {
+ return Match(OldNew.first) || Match(OldNew.second);
+ });
+ if (It1 != Config.RPathsToUpdate.end())
+ return createStringError(
+ errc::invalid_argument,
+ "cannot specify both -rpath %s %s and -rpath %s %s",
+ It1->first.str().c_str(), It1->second.str().c_str(),
+ Old.str().c_str(), New.str().c_str());
+
+ // Cannot specify the same rpath under both -delete_rpath and -rpath
+ auto It2 = find_if(Config.RPathsToRemove, Match);
+ if (It2 != Config.RPathsToRemove.end())
+ return createStringError(
+ errc::invalid_argument,
+ "cannot specify both -delete_rpath %s and -rpath %s %s",
+ It2->str().c_str(), Old.str().c_str(), New.str().c_str());
+
+ // Cannot specify the same rpath under both -add_rpath and -rpath
+ auto It3 = find_if(Config.RPathToAdd, Match);
+ if (It3 != Config.RPathToAdd.end())
+ return createStringError(
+ errc::invalid_argument,
+ "cannot specify both -add_rpath %s and -rpath %s %s",
+ It3->str().c_str(), Old.str().c_str(), New.str().c_str());
+
+ Config.RPathsToUpdate.emplace_back(Old, New);
+ }
+
SmallVector<StringRef, 2> 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/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h
index 427016a..a492e46 100644
--- a/llvm/tools/llvm-objcopy/CopyConfig.h
+++ b/llvm/tools/llvm-objcopy/CopyConfig.h
@@ -178,6 +178,7 @@
std::vector<StringRef> DumpSection;
std::vector<StringRef> SymbolsToAdd;
std::vector<StringRef> RPathToAdd;
+ std::vector<std::pair<StringRef, StringRef>> RPathsToUpdate;
DenseSet<StringRef> RPathsToRemove;
// Section matchers
diff --git a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td
index b91aba0..cbdb878 100644
--- a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td
+++ b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td
@@ -21,5 +21,8 @@
def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>,
HelpText<"Delete specified rpath">;
+def rpath: MultiArg<["-", "--"], "rpath", 2>,
+ HelpText<"Change rpath path 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
index 6a6f5e7..2586ef2 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
@@ -22,16 +22,31 @@
using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>;
using LoadCommandPred = std::function<bool(const LoadCommand &LC)>;
+static bool isLoadCommandWithPayloadString(const LoadCommand &LC) {
+ // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and
+ // LC_LAZY_LOAD_DYLIB
+ return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB;
+}
+
+static StringRef getPayloadString(const LoadCommand &LC) {
+ assert(isLoadCommandWithPayloadString(LC) &&
+ "unsupported load command encountered");
+
+ return StringRef(reinterpret_cast<const char *>(LC.Payload.data()),
+ LC.Payload.size())
+ .rtrim('\0');
+}
+
static Error removeLoadCommands(const CopyConfig &Config, Object &Obj) {
DenseSet<StringRef> 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<const char *>(LC.Payload.data()),
- LC.Payload.size())
- .rtrim('\0');
+ StringRef RPath = getPayloadString(LC);
if (RPathsToRemove.count(RPath)) {
RPathsToRemove.erase(RPath);
return true;
@@ -116,6 +131,18 @@
Obj.SymTable.removeSymbols(RemovePred);
}
+template <typename LCType>
+static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) {
+ assert(isLoadCommandWithPayloadString(LC) &&
+ "unsupported load command encountered");
+
+ uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8);
+
+ LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize;
+ LC.Payload.assign(NewCmdsize - sizeof(LCType), 0);
+ std::copy(S.begin(), S.end(), LC.Payload.begin());
+}
+
static LoadCommand buildRPathLoadCommand(StringRef Path) {
LoadCommand LC;
MachO::rpath_command RPathLC;
@@ -257,12 +284,35 @@
if (Error E = removeLoadCommands(Config, Obj))
return E;
+ StringRef Old, New;
+ for (const auto &OldNew : Config.RPathsToUpdate) {
+ std::tie(Old, New) = OldNew;
+
+ auto FindRPathLC = [&Obj](StringRef RPath) {
+ return find_if(Obj.LoadCommands, [=](const LoadCommand &LC) {
+ return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH &&
+ getPayloadString(LC) == RPath;
+ });
+ };
+
+ auto NewIt = FindRPathLC(New);
+ if (NewIt != Obj.LoadCommands.end())
+ return createStringError(errc::invalid_argument,
+ "rpath " + New +
+ " would create a duplicate load command");
+
+ auto OldIt = FindRPathLC(Old);
+ if (OldIt == Obj.LoadCommands.end())
+ return createStringError(errc::invalid_argument,
+ "no LC_RPATH load command with path: " + Old);
+
+ updateLoadCommandPayloadString<MachO::rpath_command>(*OldIt, New);
+ }
+
for (StringRef RPath : Config.RPathToAdd) {
for (LoadCommand &LC : Obj.LoadCommands) {
if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH &&
- RPath == StringRef(reinterpret_cast<char *>(LC.Payload.data()),
- LC.Payload.size())
- .trim(0)) {
+ RPath == getPayloadString(LC)) {
return createStringError(errc::invalid_argument,
"rpath " + RPath +
" would create a duplicate load command");