--- /dev/null
+--- !mach-o
+FileHeader:
+ magic: 0xFEEDFACE
+ cputype: 0x00000007
+ cpusubtype: 0x00000003
+ filetype: 0x00000001
+ ncmds: 4
+ sizeofcmds: 312
+ flags: 0x00002000
+LoadCommands:
+ - cmd: LC_SEGMENT
+ cmdsize: 192
+ segname: ''
+ vmaddr: 0
+ vmsize: 72
+ fileoff: 340
+ filesize: 72
+ maxprot: 7
+ initprot: 7
+ nsects: 2
+ flags: 0
+ Sections:
+ - sectname: __text
+ segname: __TEXT
+ addr: 0x0000000000000000
+ size: 18
+ offset: 0x00000154
+ align: 4
+ reloff: 0x00000000
+ nreloc: 0
+ flags: 0x80000400
+ reserved1: 0x00000000
+ reserved2: 0x00000000
+ reserved3: 0x00000000
+ - sectname: __eh_frame
+ segname: __TEXT
+ addr: 0x0000000000000014
+ size: 52
+ offset: 0x00000168
+ align: 2
+ reloff: 0x00000000
+ nreloc: 0
+ flags: 0x6800000B
+ reserved1: 0x00000000
+ reserved2: 0x00000000
+ reserved3: 0x00000000
+ - cmd: LC_VERSION_MIN_MACOSX
+ cmdsize: 16
+ version: 656384
+ sdk: 0
+ - cmd: LC_SYMTAB
+ cmdsize: 24
+ symoff: 412
+ nsyms: 1
+ stroff: 424
+ strsize: 8
+ - cmd: LC_DYSYMTAB
+ cmdsize: 80
+ ilocalsym: 0
+ nlocalsym: 0
+ iextdefsym: 0
+ nextdefsym: 1
+ iundefsym: 1
+ nundefsym: 0
+ tocoff: 0
+ ntoc: 0
+ modtaboff: 0
+ nmodtab: 0
+ extrefsymoff: 0
+ nextrefsyms: 0
+ indirectsymoff: 0
+ nindirectsyms: 0
+ extreloff: 0
+ nextrel: 0
+ locreloff: 0
+ nlocrel: 0
+LinkEditData:
+ NameList:
+ - n_strx: 1
+ n_type: 0x0F
+ n_sect: 1
+ n_desc: 0
+ n_value: 0
+ StringTable:
+ - ''
+ - _main
+ - ''
+...
--- /dev/null
+--- !mach-o
+FileHeader:
+ magic: 0xFEEDFACF
+ cputype: 0x01000007
+ cpusubtype: 0x00000003
+ filetype: 0x00000001
+ ncmds: 4
+ sizeofcmds: 352
+ flags: 0x00002000
+ reserved: 0x00000000
+LoadCommands:
+ - cmd: LC_SEGMENT_64
+ cmdsize: 232
+ segname: ''
+ vmaddr: 0
+ vmsize: 80
+ fileoff: 384
+ filesize: 80
+ maxprot: 7
+ initprot: 7
+ nsects: 2
+ flags: 0
+ Sections:
+ - sectname: __text
+ segname: __TEXT
+ addr: 0x0000000000000000
+ size: 15
+ offset: 0x00000180
+ align: 4
+ reloff: 0x00000000
+ nreloc: 0
+ flags: 0x80000400
+ reserved1: 0x00000000
+ reserved2: 0x00000000
+ reserved3: 0x00000000
+ - sectname: __eh_frame
+ segname: __TEXT
+ addr: 0x0000000000000010
+ size: 64
+ offset: 0x00000190
+ align: 3
+ reloff: 0x00000000
+ nreloc: 0
+ flags: 0x6800000B
+ reserved1: 0x00000000
+ reserved2: 0x00000000
+ reserved3: 0x00000000
+ - cmd: LC_VERSION_MIN_MACOSX
+ cmdsize: 16
+ version: 656384
+ sdk: 0
+ - cmd: LC_SYMTAB
+ cmdsize: 24
+ symoff: 464
+ nsyms: 1
+ stroff: 480
+ strsize: 8
+ - cmd: LC_DYSYMTAB
+ cmdsize: 80
+ ilocalsym: 0
+ nlocalsym: 0
+ iextdefsym: 0
+ nextdefsym: 1
+ iundefsym: 1
+ nundefsym: 0
+ tocoff: 0
+ ntoc: 0
+ modtaboff: 0
+ nmodtab: 0
+ extrefsymoff: 0
+ nextrefsyms: 0
+ indirectsymoff: 0
+ nindirectsyms: 0
+ extreloff: 0
+ nextrel: 0
+ locreloff: 0
+ nlocrel: 0
+LinkEditData:
+ NameList:
+ - n_strx: 1
+ n_type: 0x0F
+ n_sect: 1
+ n_desc: 0
+ n_value: 0
+ StringTable:
+ - ''
+ - _main
+ - ''
+...
--- /dev/null
+## This test checks adding a new LC_RPATH load command to a MachO binary.
+
+# RUN: yaml2obj %p/Inputs/i386.yaml > %t.i386
+# RUN: llvm-install-name-tool -add_rpath @executable_path/. %t.i386
+# RUN: llvm-objdump -p %t.i386 | FileCheck --check-prefix=NEW-RPATH %s
+
+# RUN: yaml2obj %p/Inputs/x86_64.yaml > %t.x86_64
+# RUN: llvm-install-name-tool -add_rpath @executable_path/. %t.x86_64
+# RUN: llvm-objdump -p %t.x86_64 | FileCheck --check-prefix=NEW-RPATH %s
+
+# NEW-RPATH: cmd LC_RPATH
+# NEW-RPATH-NEXT: cmdsize
+# NEW-RPATH-NEXT: @executable_path/.
+
+# RUN: not llvm-install-name-tool -add_rpath @executable_path/. %t.i386 2>&1 \
+# RUN: | FileCheck --check-prefix=DUPLICATE-RPATH %s
+
+# DUPLICATE-RPATH: duplicate load command
+
+# RUN: not llvm-install-name-tool -add_rpath @executable_path/. 2>&1 \
+# RUN: | FileCheck --check-prefix=NO-INPUT %s
+
+# NO-INPUT: no input file specified
--- /dev/null
+# RUN: llvm-install-name-tool -h | FileCheck --check-prefix=INSTALL-NAME-TOOL-USAGE %s
+# RUN: llvm-install-name-tool --help | FileCheck --check-prefix=INSTALL-NAME-TOOL-USAGE %s
+# RUN: not llvm-install-name-tool 2>&1 | FileCheck --check-prefix=INSTALL-NAME-TOOL-USAGE %s
+# RUN: not llvm-install-name-tool -abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG %s
+# RUN: not llvm-install-name-tool --abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG %s
+
+# INSTALL-NAME-TOOL-USAGE: USAGE: llvm-install-name-tool
+# INSTALL-NAME-TOOL-USAGE: @FILE
+
+# UNKNOWN-ARG: unknown argument '{{-+}}abcabc'
--- /dev/null
+# RUN: llvm-install-name-tool --version | FileCheck %s
+# CHECK: {{ version }}
tablegen(LLVM ObjcopyOpts.inc -gen-opt-parser-defs)
add_public_tablegen_target(ObjcopyOptsTableGen)
+set(LLVM_TARGET_DEFINITIONS InstallNameToolOpts.td)
+tablegen(LLVM InstallNameToolOpts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(InstallNameToolOptsTableGen)
+
set(LLVM_TARGET_DEFINITIONS StripOpts.td)
tablegen(LLVM StripOpts.inc -gen-opt-parser-defs)
add_public_tablegen_target(StripOptsTableGen)
MachO/Object.cpp
DEPENDS
ObjcopyOptsTableGen
+ InstallNameToolOptsTableGen
StripOptsTableGen
)
+add_llvm_tool_symlink(llvm-install-name-tool llvm-objcopy)
add_llvm_tool_symlink(llvm-strip llvm-objcopy)
if(LLVM_INSTALL_BINUTILS_SYMLINKS)
ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {}
};
+enum InstallNameToolID {
+ INSTALL_NAME_TOOL_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ INSTALL_NAME_TOOL_##ID,
+#include "InstallNameToolOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) \
+ const char *const INSTALL_NAME_TOOL_##NAME[] = VALUE;
+#include "InstallNameToolOpts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info InstallNameToolInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {INSTALL_NAME_TOOL_##PREFIX, \
+ NAME, \
+ HELPTEXT, \
+ METAVAR, \
+ INSTALL_NAME_TOOL_##ID, \
+ opt::Option::KIND##Class, \
+ PARAM, \
+ FLAGS, \
+ INSTALL_NAME_TOOL_##GROUP, \
+ INSTALL_NAME_TOOL_##ALIAS, \
+ ALIASARGS, \
+ VALUES},
+#include "InstallNameToolOpts.inc"
+#undef OPTION
+};
+
+class InstallNameToolOptTable : public opt::OptTable {
+public:
+ InstallNameToolOptTable() : OptTable(InstallNameToolInfoTable) {}
+};
+
enum StripID {
STRIP_INVALID = 0, // This is not an option ID.
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
return std::move(DC);
}
+// ParseInstallNameToolOptions returns the config and sets the input arguments.
+// If a help flag is set then ParseInstallNameToolOptions will print the help
+// messege and exit.
+Expected<DriverConfig>
+parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
+ DriverConfig DC;
+ CopyConfig Config;
+ InstallNameToolOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ llvm::opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (InputArgs.size() == 0) {
+ printHelp(T, errs(), "llvm-install-name-tool");
+ exit(1);
+ }
+
+ if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) {
+ printHelp(T, outs(), "llvm-install-name-tool");
+ exit(0);
+ }
+
+ if (InputArgs.hasArg(INSTALL_NAME_TOOL_version)) {
+ outs() << "llvm-install-name-tool, compatible with cctools "
+ "install_name_tool\n";
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath))
+ Config.RPathToAdd.push_back(Arg->getValue());
+
+ SmallVector<StringRef, 2> Positional;
+ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN))
+ return createStringError(errc::invalid_argument, "unknown argument '%s'",
+ Arg->getAsString(InputArgs).c_str());
+ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_INPUT))
+ Positional.push_back(Arg->getValue());
+ if (Positional.empty())
+ return createStringError(errc::invalid_argument, "no input file specified");
+ if (Positional.size() > 1)
+ return createStringError(
+ errc::invalid_argument,
+ "llvm-install-name-tool expects a single input file");
+ Config.InputFilename = Positional[0];
+ Config.OutputFilename = Positional[0];
+
+ DC.CopyConfigs.push_back(std::move(Config));
+ return std::move(DC);
+}
+
// ParseStripOptions returns the config and sets the input arguments. If a
// help flag is set then ParseStripOptions will print the help messege and
// exit.
// Main input/output options
StringRef InputFilename;
- FileFormat InputFormat;
+ FileFormat InputFormat = FileFormat::Unspecified;
StringRef OutputFilename;
- FileFormat OutputFormat;
+ FileFormat OutputFormat = FileFormat::Unspecified;
// Only applicable when --output-format!=binary (e.g. elf64-x86-64).
Optional<MachineInfo> OutputArch;
std::vector<StringRef> AddSection;
std::vector<StringRef> DumpSection;
std::vector<StringRef> SymbolsToAdd;
+ std::vector<StringRef> RPathToAdd;
// Section matchers
NameMatcher KeepSection;
parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
llvm::function_ref<Error(Error)> ErrorCallback);
+// ParseInstallNameToolOptions returns the config and sets the input arguments.
+// If a help flag is set then ParseInstallNameToolOptions will print the help
+// messege and exit.
+Expected<DriverConfig>
+parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr);
+
// ParseStripOptions returns the config and sets the input arguments. If a
// help flag is set then ParseStripOptions will print the help messege and
// exit. ErrorCallback is used to handle recoverable errors. An Error returned
Expected<DriverConfig>
parseStripOptions(ArrayRef<const char *> ArgsArr,
llvm::function_ref<Error(Error)> ErrorCallback);
-
} // namespace objcopy
} // namespace llvm
--- /dev/null
+//===-- InstallNameToolOpts.td - llvm-install-name-tool options --------*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes the command line options of llvm-install-name.
+//
+//===----------------------------------------------------------------------===//
+
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["--"], "help">;
+def h : Flag<["-"], "h">, Alias<help>;
+
+def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>,
+ HelpText<"Add new rpath">;
+
+def version : Flag<["--"], "version">,
+ HelpText<"Print the version and exit.">;
Obj.SymTable.removeSymbols(RemovePred);
}
+static LoadCommand buildRPathLoadCommand(StringRef Path) {
+ LoadCommand LC;
+ MachO::rpath_command RPathLC;
+ RPathLC.cmd = MachO::LC_RPATH;
+ RPathLC.path = sizeof(MachO::rpath_command);
+ RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size(), 8);
+ LC.MachOLoadCommand.rpath_command_data = RPathLC;
+ LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0);
+ std::copy(Path.begin(), Path.end(), LC.Payload.begin());
+ return LC;
+}
+
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() ||
Config.BuildIdLinkInput || Config.BuildIdLinkOutput ||
return createStringError(llvm::errc::invalid_argument,
"option not supported by llvm-objcopy for MachO");
}
-
removeSections(Config, Obj);
// Mark symbols to determine which symbols are still needed.
for (Section &Sec : LC.Sections)
Sec.Relocations.clear();
+ 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)) {
+ return createStringError(errc::invalid_argument,
+ "rpath " + RPath +
+ " would create a duplicate load command");
+ }
+ }
+ Obj.addLoadCommand(buildRPathLoadCommand(RPath));
+ }
return Error::success();
}
sizeof(MachO::LCStruct)); \
if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) \
MachO::swapStruct(LC.MachOLoadCommand.LCStruct##_data); \
- LC.Payload = ArrayRef<uint8_t>( \
- reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \
- sizeof(MachO::LCStruct), \
- LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \
+ if (LoadCmd.C.cmdsize > sizeof(MachO::LCStruct)) \
+ LC.Payload = ArrayRef<uint8_t>( \
+ reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \
+ sizeof(MachO::LCStruct), \
+ LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \
break;
switch (LoadCmd.C.cmd) {
sizeof(MachO::load_command));
if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost)
MachO::swapStruct(LC.MachOLoadCommand.load_command_data);
- LC.Payload = ArrayRef<uint8_t>(
- reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) +
- sizeof(MachO::load_command),
- LoadCmd.C.cmdsize - sizeof(MachO::load_command));
+ if (LoadCmd.C.cmdsize > sizeof(MachO::load_command))
+ LC.Payload = ArrayRef<uint8_t>(
+ reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) +
+ sizeof(MachO::load_command),
+ LoadCmd.C.cmdsize - sizeof(MachO::load_command));
break;
#include "llvm/BinaryFormat/MachO.def"
}
MachO::swapStruct(MLC.LCStruct##_data); \
memcpy(Begin, &MLC.LCStruct##_data, sizeof(MachO::LCStruct)); \
Begin += sizeof(MachO::LCStruct); \
- memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \
+ if (!LC.Payload.empty()) \
+ memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \
Begin += LC.Payload.size(); \
break;
MachO::swapStruct(MLC.load_command_data);
memcpy(Begin, &MLC.load_command_data, sizeof(MachO::load_command));
Begin += sizeof(MachO::load_command);
- memcpy(Begin, LC.Payload.data(), LC.Payload.size());
+ if (!LC.Payload.empty())
+ memcpy(Begin, LC.Payload.data(), LC.Payload.size());
Begin += LC.Payload.size();
break;
#include "llvm/BinaryFormat/MachO.def"
std::end(LC.Sections));
}
+void Object::addLoadCommand(LoadCommand LC) {
+ LoadCommands.push_back(std::move(LC));
+}
+
} // end namespace macho
} // end namespace objcopy
} // end namespace llvm
// The raw content of the payload of the load command (located right after the
// corresponding struct). In some cases it is either empty or can be
// copied-over without digging into its structure.
- ArrayRef<uint8_t> Payload;
+ std::vector<uint8_t> Payload;
// Some load commands can contain (inside the payload) an array of sections,
// though the contents of the sections are stored separately. The struct
Optional<size_t> FunctionStartsCommandIndex;
void removeSections(function_ref<bool(const Section &)> ToRemove);
+ void addLoadCommand(LoadCommand LC);
};
} // end namespace macho
return Error::success();
}
+namespace {
+
+enum class ToolType { Objcopy, Strip, InstallNameTool };
+
+} // anonymous namespace
+
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
ToolName = argv[0];
- bool IsStrip = sys::path::stem(ToolName).contains("strip");
-
+ ToolType Tool = StringSwitch<ToolType>(sys::path::stem(ToolName))
+ .EndsWith("strip", ToolType::Strip)
+ .EndsWith("install-name-tool", ToolType::InstallNameTool)
+ .EndsWith("install_name_tool", ToolType::InstallNameTool)
+ .Default(ToolType::Objcopy);
// Expand response files.
// TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp,
// into a separate function in the CommandLine library and call that function
NewArgv);
auto Args = makeArrayRef(NewArgv).drop_front();
-
Expected<DriverConfig> DriverConfig =
- IsStrip ? parseStripOptions(Args, reportWarning)
- : parseObjcopyOptions(Args, reportWarning);
+ (Tool == ToolType::Strip) ? parseStripOptions(Args, reportWarning)
+ : ((Tool == ToolType::InstallNameTool)
+ ? parseInstallNameToolOptions(Args)
+ : parseObjcopyOptions(Args, reportWarning));
if (!DriverConfig) {
logAllUnhandledErrors(DriverConfig.takeError(),
WithColor::error(errs(), ToolName));