1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 19:12:56 +02:00

Introduce llvm-install-name-tool

This diff adds a new "driver" for llvm-objcopy
which is supposed to emulate the behavior of install-name-tool.
This is a recommit of b5913e6d2 with ubsan issues fixed.

Differential revision: https://reviews.llvm.org/D69146

Test plan: make check-all
This commit is contained in:
Alexander Shaposhnikov 2019-11-18 15:44:58 -08:00
parent 1cdbb3b74e
commit 42ca846668
15 changed files with 398 additions and 20 deletions

View File

@ -0,0 +1,88 @@
--- !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
- ''
...

View File

@ -0,0 +1,89 @@
--- !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
- ''
...

View File

@ -0,0 +1,23 @@
## 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

View File

@ -0,0 +1,10 @@
# 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'

View File

@ -0,0 +1,2 @@
# RUN: llvm-install-name-tool --version | FileCheck %s
# CHECK: {{ version }}

View File

@ -9,6 +9,10 @@ set(LLVM_TARGET_DEFINITIONS ObjcopyOpts.td)
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)
@ -31,9 +35,11 @@ add_llvm_tool(llvm-objcopy
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)

View File

@ -63,6 +63,44 @@ public:
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, \
@ -752,6 +790,57 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
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.

View File

@ -150,9 +150,9 @@ struct CopyConfig {
// 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;
@ -175,6 +175,7 @@ struct CopyConfig {
std::vector<StringRef> AddSection;
std::vector<StringRef> DumpSection;
std::vector<StringRef> SymbolsToAdd;
std::vector<StringRef> RPathToAdd;
// Section matchers
NameMatcher KeepSection;
@ -251,6 +252,12 @@ Expected<DriverConfig>
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
@ -258,7 +265,6 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
Expected<DriverConfig>
parseStripOptions(ArrayRef<const char *> ArgsArr,
llvm::function_ref<Error(Error)> ErrorCallback);
} // namespace objcopy
} // namespace llvm

View File

@ -0,0 +1,22 @@
//===-- 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.">;

View File

@ -72,6 +72,18 @@ static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
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 ||
@ -94,7 +106,6 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
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.
@ -108,6 +119,19 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
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();
}

View File

@ -150,10 +150,11 @@ void MachOReader::readLoadCommands(Object &O) const {
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) {
@ -162,10 +163,11 @@ void MachOReader::readLoadCommands(Object &O) const {
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"
}

View File

@ -180,7 +180,8 @@ void MachOWriter::writeLoadCommands() {
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;
@ -193,7 +194,8 @@ void MachOWriter::writeLoadCommands() {
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"

View File

@ -29,6 +29,10 @@ void Object::removeSections(function_ref<bool(const Section &)> ToRemove) {
std::end(LC.Sections));
}
void Object::addLoadCommand(LoadCommand LC) {
LoadCommands.push_back(std::move(LC));
}
} // end namespace macho
} // end namespace objcopy
} // end namespace llvm

View File

@ -74,7 +74,7 @@ struct LoadCommand {
// 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
@ -276,6 +276,7 @@ struct Object {
Optional<size_t> FunctionStartsCommandIndex;
void removeSections(function_ref<bool(const Section &)> ToRemove);
void addLoadCommand(LoadCommand LC);
};
} // end namespace macho

View File

@ -313,11 +313,20 @@ static Error executeObjcopy(CopyConfig &Config) {
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
@ -332,10 +341,11 @@ int main(int argc, char **argv) {
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));