diff --git a/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-add-rpath.test b/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-add-rpath.test --- a/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-add-rpath.test +++ b/llvm/test/tools/llvm-objcopy/MachO/install-name-tool-add-rpath.test @@ -22,6 +22,13 @@ # NO-INPUT: no input file specified +## Add same RPATH twice: +# RUN: not llvm-install-name-tool -add_rpath @executable_X \ +# RUN: -add_rpath @executable_X %t.i386 2>&1 \ +# RUN: | FileCheck --check-prefix=DOUBLE %s + +# DOUBLE: duplicate load command + ## Check that cmdsize accounts for NULL terminator. # RUN: yaml2obj %p/Inputs/x86_64.yaml -o %t.x86_64 # RUN: llvm-install-name-tool -add_rpath abcd %t.x86_64 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 @@ -155,6 +155,76 @@ return LC; } +static Error updateLoadCommands(const CopyConfig &Config, Object &Obj) { + DenseSet OriginalRPaths; + auto FindPair = + [](StringRef Old, + const std::vector> &Range) { + return find_if(Range, [=](const std::pair Pair) { + return Pair.first == Old; + }); + }; + + 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; + + case MachO::LC_RPATH: { + StringRef CurrentRPath = getPayloadString(LC); + auto FoundArg = FindPair(CurrentRPath, Config.RPathsToUpdate); + if (FoundArg != Config.RPathsToUpdate.end()) { + updateLoadCommandPayloadString(LC, + FoundArg->second); + OriginalRPaths.insert(FoundArg->first); + } else { + OriginalRPaths.insert(CurrentRPath); + } + break; + } + + case MachO::LC_LOAD_DYLIB: + case MachO::LC_LOAD_WEAK_DYLIB: + StringRef CurrentInstallName = getPayloadString(LC); + auto FoundArg = FindPair(CurrentInstallName, Config.InstallNamesToUpdate); + if (FoundArg != Config.InstallNamesToUpdate.end()) + updateLoadCommandPayloadString(LC, + FoundArg->second); + break; + } + } + + StringRef Old, New; + for (const auto &OldNew : Config.RPathsToUpdate) { + std::tie(Old, New) = OldNew; + if (OriginalRPaths.count(Old) == 0) + return createStringError(errc::invalid_argument, + "no LC_RPATH load command with path: " + Old); + if (OriginalRPaths.count(New) != 0) + return createStringError(errc::invalid_argument, + "rpath " + New + + " would create a duplicate load command"); + } + + for (StringRef RPath : Config.RPathToAdd) { + if (OriginalRPaths.count(RPath) != 0) + return createStringError(errc::invalid_argument, + "rpath " + RPath + + " would create a duplicate load command"); + OriginalRPaths.insert(RPath); + Obj.addLoadCommand(buildRPathLoadCommand(RPath)); + } + + return Error::success(); +} + static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { for (LoadCommand &LC : Obj.LoadCommands) @@ -271,32 +341,6 @@ for (std::unique_ptr
&Sec : LC.Sections) Sec->Relocations.clear(); - 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; - - 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; - } - } - } - } - for (const auto &Flag : Config.AddSection) { std::pair SecPair = Flag.split("="); StringRef SecName = SecPair.first; @@ -310,42 +354,9 @@ 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(*OldIt, New); - } + if (Error E = updateLoadCommands(Config, Obj)) + return E; - for (StringRef RPath : Config.RPathToAdd) { - for (LoadCommand &LC : Obj.LoadCommands) { - if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH && - RPath == getPayloadString(LC)) { - return createStringError(errc::invalid_argument, - "rpath " + RPath + - " would create a duplicate load command"); - } - } - Obj.addLoadCommand(buildRPathLoadCommand(RPath)); - } return Error::success(); }