This is another attempt at D59351 which attempted to add --update-section, but with some heuristics for adjusting segment/section offsets/sizes in the event the data copied into the section is larger than the original size of the section. We are opting to not support this case. GNU's objcopy was able to do this because the linker and objcopy are tightly coupled enough that segment reformatting was simpler. This is not the case with llvm-objcopy and lld where they like to be separated. This will attempt to copy data into the section without changing any other properties of the parent segment (if the section is part of one). Differential Revision: https://reviews.llvm.org/D112116
1391 lines
57 KiB
C++
1391 lines
57 KiB
C++
//===- ConfigManager.cpp --------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ConfigManager.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/Support/CRC.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Compression.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/StringSaver.h"
|
|
#include <memory>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::objcopy;
|
|
|
|
namespace {
|
|
enum ObjcopyID {
|
|
OBJCOPY_INVALID = 0, // This is not an option ID.
|
|
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
|
|
HELPTEXT, METAVAR, VALUES) \
|
|
OBJCOPY_##ID,
|
|
#include "ObjcopyOpts.inc"
|
|
#undef OPTION
|
|
};
|
|
|
|
#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE;
|
|
#include "ObjcopyOpts.inc"
|
|
#undef PREFIX
|
|
|
|
const opt::OptTable::Info ObjcopyInfoTable[] = {
|
|
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
|
|
HELPTEXT, METAVAR, VALUES) \
|
|
{OBJCOPY_##PREFIX, \
|
|
NAME, \
|
|
HELPTEXT, \
|
|
METAVAR, \
|
|
OBJCOPY_##ID, \
|
|
opt::Option::KIND##Class, \
|
|
PARAM, \
|
|
FLAGS, \
|
|
OBJCOPY_##GROUP, \
|
|
OBJCOPY_##ALIAS, \
|
|
ALIASARGS, \
|
|
VALUES},
|
|
#include "ObjcopyOpts.inc"
|
|
#undef OPTION
|
|
};
|
|
|
|
class ObjcopyOptTable : public opt::OptTable {
|
|
public:
|
|
ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {
|
|
setGroupedShortOptions(true);
|
|
}
|
|
};
|
|
|
|
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
|
|
|
|
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 BitcodeStripID {
|
|
BITCODE_STRIP_INVALID = 0, // This is not an option ID.
|
|
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
|
|
HELPTEXT, METAVAR, VALUES) \
|
|
BITCODE_STRIP_##ID,
|
|
#include "BitcodeStripOpts.inc"
|
|
#undef OPTION
|
|
};
|
|
|
|
#define PREFIX(NAME, VALUE) const char *const BITCODE_STRIP_##NAME[] = VALUE;
|
|
#include "BitcodeStripOpts.inc"
|
|
#undef PREFIX
|
|
|
|
const opt::OptTable::Info BitcodeStripInfoTable[] = {
|
|
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
|
|
HELPTEXT, METAVAR, VALUES) \
|
|
{BITCODE_STRIP_##PREFIX, \
|
|
NAME, \
|
|
HELPTEXT, \
|
|
METAVAR, \
|
|
BITCODE_STRIP_##ID, \
|
|
opt::Option::KIND##Class, \
|
|
PARAM, \
|
|
FLAGS, \
|
|
BITCODE_STRIP_##GROUP, \
|
|
BITCODE_STRIP_##ALIAS, \
|
|
ALIASARGS, \
|
|
VALUES},
|
|
#include "BitcodeStripOpts.inc"
|
|
#undef OPTION
|
|
};
|
|
|
|
class BitcodeStripOptTable : public opt::OptTable {
|
|
public:
|
|
BitcodeStripOptTable() : OptTable(BitcodeStripInfoTable) {}
|
|
};
|
|
|
|
enum StripID {
|
|
STRIP_INVALID = 0, // This is not an option ID.
|
|
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
|
|
HELPTEXT, METAVAR, VALUES) \
|
|
STRIP_##ID,
|
|
#include "StripOpts.inc"
|
|
#undef OPTION
|
|
};
|
|
|
|
#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE;
|
|
#include "StripOpts.inc"
|
|
#undef PREFIX
|
|
|
|
const opt::OptTable::Info StripInfoTable[] = {
|
|
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
|
|
HELPTEXT, METAVAR, VALUES) \
|
|
{STRIP_##PREFIX, NAME, HELPTEXT, \
|
|
METAVAR, STRIP_##ID, opt::Option::KIND##Class, \
|
|
PARAM, FLAGS, STRIP_##GROUP, \
|
|
STRIP_##ALIAS, ALIASARGS, VALUES},
|
|
#include "StripOpts.inc"
|
|
#undef OPTION
|
|
};
|
|
|
|
class StripOptTable : public opt::OptTable {
|
|
public:
|
|
StripOptTable() : OptTable(StripInfoTable) { setGroupedShortOptions(true); }
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static SectionFlag parseSectionRenameFlag(StringRef SectionName) {
|
|
return llvm::StringSwitch<SectionFlag>(SectionName)
|
|
.CaseLower("alloc", SectionFlag::SecAlloc)
|
|
.CaseLower("load", SectionFlag::SecLoad)
|
|
.CaseLower("noload", SectionFlag::SecNoload)
|
|
.CaseLower("readonly", SectionFlag::SecReadonly)
|
|
.CaseLower("debug", SectionFlag::SecDebug)
|
|
.CaseLower("code", SectionFlag::SecCode)
|
|
.CaseLower("data", SectionFlag::SecData)
|
|
.CaseLower("rom", SectionFlag::SecRom)
|
|
.CaseLower("merge", SectionFlag::SecMerge)
|
|
.CaseLower("strings", SectionFlag::SecStrings)
|
|
.CaseLower("contents", SectionFlag::SecContents)
|
|
.CaseLower("share", SectionFlag::SecShare)
|
|
.CaseLower("exclude", SectionFlag::SecExclude)
|
|
.Default(SectionFlag::SecNone);
|
|
}
|
|
|
|
static Expected<SectionFlag>
|
|
parseSectionFlagSet(ArrayRef<StringRef> SectionFlags) {
|
|
SectionFlag ParsedFlags = SectionFlag::SecNone;
|
|
for (StringRef Flag : SectionFlags) {
|
|
SectionFlag ParsedFlag = parseSectionRenameFlag(Flag);
|
|
if (ParsedFlag == SectionFlag::SecNone)
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"unrecognized section flag '%s'. Flags supported for GNU "
|
|
"compatibility: alloc, load, noload, readonly, exclude, debug, "
|
|
"code, data, rom, share, contents, merge, strings",
|
|
Flag.str().c_str());
|
|
ParsedFlags |= ParsedFlag;
|
|
}
|
|
|
|
return ParsedFlags;
|
|
}
|
|
|
|
static Expected<SectionRename> parseRenameSectionValue(StringRef FlagValue) {
|
|
if (!FlagValue.contains('='))
|
|
return createStringError(errc::invalid_argument,
|
|
"bad format for --rename-section: missing '='");
|
|
|
|
// Initial split: ".foo" = ".bar,f1,f2,..."
|
|
auto Old2New = FlagValue.split('=');
|
|
SectionRename SR;
|
|
SR.OriginalName = Old2New.first;
|
|
|
|
// Flags split: ".bar" "f1" "f2" ...
|
|
SmallVector<StringRef, 6> NameAndFlags;
|
|
Old2New.second.split(NameAndFlags, ',');
|
|
SR.NewName = NameAndFlags[0];
|
|
|
|
if (NameAndFlags.size() > 1) {
|
|
Expected<SectionFlag> ParsedFlagSet =
|
|
parseSectionFlagSet(makeArrayRef(NameAndFlags).drop_front());
|
|
if (!ParsedFlagSet)
|
|
return ParsedFlagSet.takeError();
|
|
SR.NewFlags = *ParsedFlagSet;
|
|
}
|
|
|
|
return SR;
|
|
}
|
|
|
|
static Expected<std::pair<StringRef, uint64_t>>
|
|
parseSetSectionAlignment(StringRef FlagValue) {
|
|
if (!FlagValue.contains('='))
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"bad format for --set-section-alignment: missing '='");
|
|
auto Split = StringRef(FlagValue).split('=');
|
|
if (Split.first.empty())
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"bad format for --set-section-alignment: missing section name");
|
|
uint64_t NewAlign;
|
|
if (Split.second.getAsInteger(0, NewAlign))
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"invalid alignment for --set-section-alignment: '%s'",
|
|
Split.second.str().c_str());
|
|
return std::make_pair(Split.first, NewAlign);
|
|
}
|
|
|
|
static Expected<SectionFlagsUpdate>
|
|
parseSetSectionFlagValue(StringRef FlagValue) {
|
|
if (!StringRef(FlagValue).contains('='))
|
|
return createStringError(errc::invalid_argument,
|
|
"bad format for --set-section-flags: missing '='");
|
|
|
|
// Initial split: ".foo" = "f1,f2,..."
|
|
auto Section2Flags = StringRef(FlagValue).split('=');
|
|
SectionFlagsUpdate SFU;
|
|
SFU.Name = Section2Flags.first;
|
|
|
|
// Flags split: "f1" "f2" ...
|
|
SmallVector<StringRef, 6> SectionFlags;
|
|
Section2Flags.second.split(SectionFlags, ',');
|
|
Expected<SectionFlag> ParsedFlagSet = parseSectionFlagSet(SectionFlags);
|
|
if (!ParsedFlagSet)
|
|
return ParsedFlagSet.takeError();
|
|
SFU.NewFlags = *ParsedFlagSet;
|
|
|
|
return SFU;
|
|
}
|
|
|
|
namespace {
|
|
struct TargetInfo {
|
|
FileFormat Format;
|
|
MachineInfo Machine;
|
|
};
|
|
} // namespace
|
|
|
|
// FIXME: consolidate with the bfd parsing used by lld.
|
|
static const StringMap<MachineInfo> TargetMap{
|
|
// Name, {EMachine, 64bit, LittleEndian}
|
|
// x86
|
|
{"elf32-i386", {ELF::EM_386, false, true}},
|
|
{"elf32-x86-64", {ELF::EM_X86_64, false, true}},
|
|
{"elf64-x86-64", {ELF::EM_X86_64, true, true}},
|
|
// Intel MCU
|
|
{"elf32-iamcu", {ELF::EM_IAMCU, false, true}},
|
|
// ARM
|
|
{"elf32-littlearm", {ELF::EM_ARM, false, true}},
|
|
// ARM AArch64
|
|
{"elf64-aarch64", {ELF::EM_AARCH64, true, true}},
|
|
{"elf64-littleaarch64", {ELF::EM_AARCH64, true, true}},
|
|
// RISC-V
|
|
{"elf32-littleriscv", {ELF::EM_RISCV, false, true}},
|
|
{"elf64-littleriscv", {ELF::EM_RISCV, true, true}},
|
|
// PowerPC
|
|
{"elf32-powerpc", {ELF::EM_PPC, false, false}},
|
|
{"elf32-powerpcle", {ELF::EM_PPC, false, true}},
|
|
{"elf64-powerpc", {ELF::EM_PPC64, true, false}},
|
|
{"elf64-powerpcle", {ELF::EM_PPC64, true, true}},
|
|
// MIPS
|
|
{"elf32-bigmips", {ELF::EM_MIPS, false, false}},
|
|
{"elf32-ntradbigmips", {ELF::EM_MIPS, false, false}},
|
|
{"elf32-ntradlittlemips", {ELF::EM_MIPS, false, true}},
|
|
{"elf32-tradbigmips", {ELF::EM_MIPS, false, false}},
|
|
{"elf32-tradlittlemips", {ELF::EM_MIPS, false, true}},
|
|
{"elf64-tradbigmips", {ELF::EM_MIPS, true, false}},
|
|
{"elf64-tradlittlemips", {ELF::EM_MIPS, true, true}},
|
|
// SPARC
|
|
{"elf32-sparc", {ELF::EM_SPARC, false, false}},
|
|
{"elf32-sparcel", {ELF::EM_SPARC, false, true}},
|
|
{"elf32-hexagon", {ELF::EM_HEXAGON, false, true}},
|
|
};
|
|
|
|
static Expected<TargetInfo>
|
|
getOutputTargetInfoByTargetName(StringRef TargetName) {
|
|
StringRef OriginalTargetName = TargetName;
|
|
bool IsFreeBSD = TargetName.consume_back("-freebsd");
|
|
auto Iter = TargetMap.find(TargetName);
|
|
if (Iter == std::end(TargetMap))
|
|
return createStringError(errc::invalid_argument,
|
|
"invalid output format: '%s'",
|
|
OriginalTargetName.str().c_str());
|
|
MachineInfo MI = Iter->getValue();
|
|
if (IsFreeBSD)
|
|
MI.OSABI = ELF::ELFOSABI_FREEBSD;
|
|
|
|
FileFormat Format;
|
|
if (TargetName.startswith("elf"))
|
|
Format = FileFormat::ELF;
|
|
else
|
|
// This should never happen because `TargetName` is valid (it certainly
|
|
// exists in the TargetMap).
|
|
llvm_unreachable("unknown target prefix");
|
|
|
|
return {TargetInfo{Format, MI}};
|
|
}
|
|
|
|
static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc,
|
|
StringRef Filename, MatchStyle MS,
|
|
function_ref<Error(Error)> ErrorCallback) {
|
|
StringSaver Saver(Alloc);
|
|
SmallVector<StringRef, 16> Lines;
|
|
auto BufOrErr = MemoryBuffer::getFile(Filename);
|
|
if (!BufOrErr)
|
|
return createFileError(Filename, BufOrErr.getError());
|
|
|
|
BufOrErr.get()->getBuffer().split(Lines, '\n');
|
|
for (StringRef Line : Lines) {
|
|
// Ignore everything after '#', trim whitespace, and only add the symbol if
|
|
// it's not empty.
|
|
auto TrimmedLine = Line.split('#').first.trim();
|
|
if (!TrimmedLine.empty())
|
|
if (Error E = Symbols.addMatcher(NameOrPattern::create(
|
|
Saver.save(TrimmedLine), MS, ErrorCallback)))
|
|
return E;
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Expected<NameOrPattern>
|
|
NameOrPattern::create(StringRef Pattern, MatchStyle MS,
|
|
function_ref<Error(Error)> ErrorCallback) {
|
|
switch (MS) {
|
|
case MatchStyle::Literal:
|
|
return NameOrPattern(Pattern);
|
|
case MatchStyle::Wildcard: {
|
|
SmallVector<char, 32> Data;
|
|
bool IsPositiveMatch = true;
|
|
if (Pattern[0] == '!') {
|
|
IsPositiveMatch = false;
|
|
Pattern = Pattern.drop_front();
|
|
}
|
|
Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern);
|
|
|
|
// If we couldn't create it as a glob, report the error, but try again with
|
|
// a literal if the error reporting is non-fatal.
|
|
if (!GlobOrErr) {
|
|
if (Error E = ErrorCallback(GlobOrErr.takeError()))
|
|
return std::move(E);
|
|
return create(Pattern, MatchStyle::Literal, ErrorCallback);
|
|
}
|
|
|
|
return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr),
|
|
IsPositiveMatch);
|
|
}
|
|
case MatchStyle::Regex: {
|
|
SmallVector<char, 32> Data;
|
|
return NameOrPattern(std::make_shared<Regex>(
|
|
("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)));
|
|
}
|
|
}
|
|
llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum");
|
|
}
|
|
|
|
static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename,
|
|
BumpPtrAllocator &Alloc,
|
|
StringRef Filename) {
|
|
StringSaver Saver(Alloc);
|
|
SmallVector<StringRef, 16> Lines;
|
|
auto BufOrErr = MemoryBuffer::getFile(Filename);
|
|
if (!BufOrErr)
|
|
return createFileError(Filename, BufOrErr.getError());
|
|
|
|
BufOrErr.get()->getBuffer().split(Lines, '\n');
|
|
size_t NumLines = Lines.size();
|
|
for (size_t LineNo = 0; LineNo < NumLines; ++LineNo) {
|
|
StringRef TrimmedLine = Lines[LineNo].split('#').first.trim();
|
|
if (TrimmedLine.empty())
|
|
continue;
|
|
|
|
std::pair<StringRef, StringRef> Pair = Saver.save(TrimmedLine).split(' ');
|
|
StringRef NewName = Pair.second.trim();
|
|
if (NewName.empty())
|
|
return createStringError(errc::invalid_argument,
|
|
"%s:%zu: missing new symbol name",
|
|
Filename.str().c_str(), LineNo + 1);
|
|
SymbolsToRename.insert({Pair.first, NewName});
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
template <class T> static ErrorOr<T> getAsInteger(StringRef Val) {
|
|
T Result;
|
|
if (Val.getAsInteger(0, Result))
|
|
return errc::invalid_argument;
|
|
return Result;
|
|
}
|
|
|
|
namespace {
|
|
|
|
enum class ToolType { Objcopy, Strip, InstallNameTool, BitcodeStrip };
|
|
|
|
} // anonymous namespace
|
|
|
|
static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS,
|
|
ToolType Tool) {
|
|
StringRef HelpText, ToolName;
|
|
switch (Tool) {
|
|
case ToolType::Objcopy:
|
|
ToolName = "llvm-objcopy";
|
|
HelpText = " [options] input [output]";
|
|
break;
|
|
case ToolType::Strip:
|
|
ToolName = "llvm-strip";
|
|
HelpText = " [options] inputs...";
|
|
break;
|
|
case ToolType::InstallNameTool:
|
|
ToolName = "llvm-install-name-tool";
|
|
HelpText = " [options] input";
|
|
break;
|
|
case ToolType::BitcodeStrip:
|
|
ToolName = "llvm-bitcode-strip";
|
|
HelpText = " [options] input";
|
|
break;
|
|
}
|
|
OptTable.printHelp(OS, (ToolName + HelpText).str().c_str(),
|
|
(ToolName + " tool").str().c_str());
|
|
// TODO: Replace this with libOption call once it adds extrahelp support.
|
|
// The CommandLine library has a cl::extrahelp class to support this,
|
|
// but libOption does not have that yet.
|
|
OS << "\nPass @FILE as argument to read options from FILE.\n";
|
|
}
|
|
|
|
static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) {
|
|
// Parse value given with --add-symbol option and create the
|
|
// new symbol if possible. The value format for --add-symbol is:
|
|
//
|
|
// <name>=[<section>:]<value>[,<flags>]
|
|
//
|
|
// where:
|
|
// <name> - symbol name, can be empty string
|
|
// <section> - optional section name. If not given ABS symbol is created
|
|
// <value> - symbol value, can be decimal or hexadecimal number prefixed
|
|
// with 0x.
|
|
// <flags> - optional flags affecting symbol type, binding or visibility.
|
|
NewSymbolInfo SI;
|
|
StringRef Value;
|
|
std::tie(SI.SymbolName, Value) = FlagValue.split('=');
|
|
if (Value.empty())
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"bad format for --add-symbol, missing '=' after '%s'",
|
|
SI.SymbolName.str().c_str());
|
|
|
|
if (Value.contains(':')) {
|
|
std::tie(SI.SectionName, Value) = Value.split(':');
|
|
if (SI.SectionName.empty() || Value.empty())
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"bad format for --add-symbol, missing section name or symbol value");
|
|
}
|
|
|
|
SmallVector<StringRef, 6> Flags;
|
|
Value.split(Flags, ',');
|
|
if (Flags[0].getAsInteger(0, SI.Value))
|
|
return createStringError(errc::invalid_argument, "bad symbol value: '%s'",
|
|
Flags[0].str().c_str());
|
|
|
|
using Functor = std::function<void()>;
|
|
SmallVector<StringRef, 6> UnsupportedFlags;
|
|
for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I)
|
|
static_cast<Functor>(
|
|
StringSwitch<Functor>(Flags[I])
|
|
.CaseLower("global",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Global); })
|
|
.CaseLower("local", [&] { SI.Flags.push_back(SymbolFlag::Local); })
|
|
.CaseLower("weak", [&] { SI.Flags.push_back(SymbolFlag::Weak); })
|
|
.CaseLower("default",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Default); })
|
|
.CaseLower("hidden",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Hidden); })
|
|
.CaseLower("protected",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Protected); })
|
|
.CaseLower("file", [&] { SI.Flags.push_back(SymbolFlag::File); })
|
|
.CaseLower("section",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Section); })
|
|
.CaseLower("object",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Object); })
|
|
.CaseLower("function",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Function); })
|
|
.CaseLower(
|
|
"indirect-function",
|
|
[&] { SI.Flags.push_back(SymbolFlag::IndirectFunction); })
|
|
.CaseLower("debug", [&] { SI.Flags.push_back(SymbolFlag::Debug); })
|
|
.CaseLower("constructor",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Constructor); })
|
|
.CaseLower("warning",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Warning); })
|
|
.CaseLower("indirect",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Indirect); })
|
|
.CaseLower("synthetic",
|
|
[&] { SI.Flags.push_back(SymbolFlag::Synthetic); })
|
|
.CaseLower("unique-object",
|
|
[&] { SI.Flags.push_back(SymbolFlag::UniqueObject); })
|
|
.StartsWithLower("before=",
|
|
[&] {
|
|
StringRef SymNamePart =
|
|
Flags[I].split('=').second;
|
|
|
|
if (!SymNamePart.empty())
|
|
SI.BeforeSyms.push_back(SymNamePart);
|
|
})
|
|
.Default([&] { UnsupportedFlags.push_back(Flags[I]); }))();
|
|
if (!UnsupportedFlags.empty())
|
|
return createStringError(errc::invalid_argument,
|
|
"unsupported flag%s for --add-symbol: '%s'",
|
|
UnsupportedFlags.size() > 1 ? "s" : "",
|
|
join(UnsupportedFlags, "', '").c_str());
|
|
|
|
return SI;
|
|
}
|
|
|
|
Expected<const ELFConfig &> ConfigManager::getELFConfig() const {
|
|
return ELF;
|
|
}
|
|
|
|
Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const {
|
|
if (!Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
|
|
!Common.AllocSectionsPrefix.empty() || !Common.DumpSection.empty() ||
|
|
!Common.KeepSection.empty() || !Common.SymbolsToGlobalize.empty() ||
|
|
!Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() ||
|
|
!Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() ||
|
|
!Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
|
|
Common.ExtractDWO || Common.PreserveDates || Common.StripDWO ||
|
|
Common.StripNonAlloc || Common.StripSections || Common.Weaken ||
|
|
Common.DecompressDebugSections ||
|
|
Common.DiscardMode == DiscardType::Locals ||
|
|
!Common.SymbolsToAdd.empty()) {
|
|
return createStringError(llvm::errc::invalid_argument,
|
|
"option not supported by llvm-objcopy for COFF");
|
|
}
|
|
|
|
return COFF;
|
|
}
|
|
|
|
Expected<const MachOConfig &> ConfigManager::getMachOConfig() const {
|
|
if (!Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
|
|
!Common.AllocSectionsPrefix.empty() || !Common.KeepSection.empty() ||
|
|
!Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() ||
|
|
!Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() ||
|
|
!Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() ||
|
|
!Common.UnneededSymbolsToRemove.empty() ||
|
|
!Common.SetSectionAlignment.empty() || !Common.SetSectionFlags.empty() ||
|
|
Common.ExtractDWO || Common.PreserveDates || Common.StripAllGNU ||
|
|
Common.StripDWO || Common.StripNonAlloc || Common.StripSections ||
|
|
Common.Weaken || Common.DecompressDebugSections || Common.StripUnneeded ||
|
|
Common.DiscardMode == DiscardType::Locals ||
|
|
!Common.SymbolsToAdd.empty()) {
|
|
return createStringError(llvm::errc::invalid_argument,
|
|
"option not supported by llvm-objcopy for MachO");
|
|
}
|
|
|
|
return MachO;
|
|
}
|
|
|
|
Expected<const WasmConfig &> ConfigManager::getWasmConfig() const {
|
|
if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition ||
|
|
!Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
|
|
!Common.AllocSectionsPrefix.empty() ||
|
|
Common.DiscardMode != DiscardType::None || !Common.SymbolsToAdd.empty() ||
|
|
!Common.SymbolsToGlobalize.empty() || !Common.SymbolsToLocalize.empty() ||
|
|
!Common.SymbolsToKeep.empty() || !Common.SymbolsToRemove.empty() ||
|
|
!Common.UnneededSymbolsToRemove.empty() ||
|
|
!Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() ||
|
|
!Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
|
|
!Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty()) {
|
|
return createStringError(
|
|
llvm::errc::invalid_argument,
|
|
"only flags for section dumping, removal, and addition are supported");
|
|
}
|
|
|
|
return Wasm;
|
|
}
|
|
|
|
// ParseObjcopyOptions returns the config and sets the input arguments. If a
|
|
// help flag is set then ParseObjcopyOptions will print the help messege and
|
|
// exit.
|
|
Expected<DriverConfig>
|
|
objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
|
|
function_ref<Error(Error)> ErrorCallback) {
|
|
DriverConfig DC;
|
|
ObjcopyOptTable T;
|
|
|
|
const char *const *DashDash =
|
|
std::find_if(RawArgsArr.begin(), RawArgsArr.end(),
|
|
[](StringRef Str) { return Str == "--"; });
|
|
ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash);
|
|
if (DashDash != RawArgsArr.end())
|
|
DashDash = std::next(DashDash);
|
|
|
|
unsigned MissingArgumentIndex, MissingArgumentCount;
|
|
llvm::opt::InputArgList InputArgs =
|
|
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
|
|
|
|
if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) {
|
|
printHelp(T, errs(), ToolType::Objcopy);
|
|
exit(1);
|
|
}
|
|
|
|
if (InputArgs.hasArg(OBJCOPY_help)) {
|
|
printHelp(T, outs(), ToolType::Objcopy);
|
|
exit(0);
|
|
}
|
|
|
|
if (InputArgs.hasArg(OBJCOPY_version)) {
|
|
outs() << "llvm-objcopy, compatible with GNU objcopy\n";
|
|
cl::PrintVersionMessage();
|
|
exit(0);
|
|
}
|
|
|
|
SmallVector<const char *, 2> Positional;
|
|
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN))
|
|
return createStringError(errc::invalid_argument, "unknown argument '%s'",
|
|
Arg->getAsString(InputArgs).c_str());
|
|
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT))
|
|
Positional.push_back(Arg->getValue());
|
|
std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional));
|
|
|
|
if (Positional.empty())
|
|
return createStringError(errc::invalid_argument, "no input file specified");
|
|
|
|
if (Positional.size() > 2)
|
|
return createStringError(errc::invalid_argument,
|
|
"too many positional arguments");
|
|
|
|
ConfigManager ConfigMgr;
|
|
CommonConfig &Config = ConfigMgr.Common;
|
|
ELFConfig &ELFConfig = ConfigMgr.ELF;
|
|
MachOConfig &MachOConfig = ConfigMgr.MachO;
|
|
Config.InputFilename = Positional[0];
|
|
Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1];
|
|
if (InputArgs.hasArg(OBJCOPY_target) &&
|
|
(InputArgs.hasArg(OBJCOPY_input_target) ||
|
|
InputArgs.hasArg(OBJCOPY_output_target)))
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"--target cannot be used with --input-target or --output-target");
|
|
|
|
if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard))
|
|
return createStringError(errc::invalid_argument,
|
|
"--regex and --wildcard are incompatible");
|
|
|
|
MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex)
|
|
? MatchStyle::Regex
|
|
: MatchStyle::Wildcard;
|
|
MatchStyle SymbolMatchStyle = InputArgs.hasArg(OBJCOPY_regex)
|
|
? MatchStyle::Regex
|
|
: InputArgs.hasArg(OBJCOPY_wildcard)
|
|
? MatchStyle::Wildcard
|
|
: MatchStyle::Literal;
|
|
StringRef InputFormat, OutputFormat;
|
|
if (InputArgs.hasArg(OBJCOPY_target)) {
|
|
InputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
|
|
OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
|
|
} else {
|
|
InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target);
|
|
OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target);
|
|
}
|
|
|
|
// FIXME: Currently, we ignore the target for non-binary/ihex formats
|
|
// explicitly specified by -I option (e.g. -Ielf32-x86-64) and guess the
|
|
// format by llvm::object::createBinary regardless of the option value.
|
|
Config.InputFormat = StringSwitch<FileFormat>(InputFormat)
|
|
.Case("binary", FileFormat::Binary)
|
|
.Case("ihex", FileFormat::IHex)
|
|
.Default(FileFormat::Unspecified);
|
|
|
|
if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) {
|
|
const uint8_t Invalid = 0xff;
|
|
StringRef VisibilityStr =
|
|
InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility);
|
|
|
|
ELFConfig.NewSymbolVisibility = StringSwitch<uint8_t>(VisibilityStr)
|
|
.Case("default", ELF::STV_DEFAULT)
|
|
.Case("hidden", ELF::STV_HIDDEN)
|
|
.Case("internal", ELF::STV_INTERNAL)
|
|
.Case("protected", ELF::STV_PROTECTED)
|
|
.Default(Invalid);
|
|
|
|
if (ELFConfig.NewSymbolVisibility == Invalid)
|
|
return createStringError(errc::invalid_argument,
|
|
"'%s' is not a valid symbol visibility",
|
|
VisibilityStr.str().c_str());
|
|
}
|
|
|
|
Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
|
|
.Case("binary", FileFormat::Binary)
|
|
.Case("ihex", FileFormat::IHex)
|
|
.Default(FileFormat::Unspecified);
|
|
if (Config.OutputFormat == FileFormat::Unspecified) {
|
|
if (OutputFormat.empty()) {
|
|
Config.OutputFormat = Config.InputFormat;
|
|
} else {
|
|
Expected<TargetInfo> Target =
|
|
getOutputTargetInfoByTargetName(OutputFormat);
|
|
if (!Target)
|
|
return Target.takeError();
|
|
Config.OutputFormat = Target->Format;
|
|
Config.OutputArch = Target->Machine;
|
|
}
|
|
}
|
|
|
|
if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections,
|
|
OBJCOPY_compress_debug_sections_eq)) {
|
|
Config.CompressionType = DebugCompressionType::Z;
|
|
|
|
if (Arg->getOption().getID() == OBJCOPY_compress_debug_sections_eq) {
|
|
Config.CompressionType =
|
|
StringSwitch<DebugCompressionType>(
|
|
InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq))
|
|
.Case("zlib-gnu", DebugCompressionType::GNU)
|
|
.Case("zlib", DebugCompressionType::Z)
|
|
.Default(DebugCompressionType::None);
|
|
if (Config.CompressionType == DebugCompressionType::None)
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"invalid or unsupported --compress-debug-sections format: %s",
|
|
InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)
|
|
.str()
|
|
.c_str());
|
|
}
|
|
if (!zlib::isAvailable())
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress");
|
|
}
|
|
|
|
Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink);
|
|
// The gnu_debuglink's target is expected to not change or else its CRC would
|
|
// become invalidated and get rejected. We can avoid recalculating the
|
|
// checksum for every target file inside an archive by precomputing the CRC
|
|
// here. This prevents a significant amount of I/O.
|
|
if (!Config.AddGnuDebugLink.empty()) {
|
|
auto DebugOrErr = MemoryBuffer::getFile(Config.AddGnuDebugLink);
|
|
if (!DebugOrErr)
|
|
return createFileError(Config.AddGnuDebugLink, DebugOrErr.getError());
|
|
auto Debug = std::move(*DebugOrErr);
|
|
Config.GnuDebugLinkCRC32 =
|
|
llvm::crc32(arrayRefFromStringRef(Debug->getBuffer()));
|
|
}
|
|
Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
|
|
Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols);
|
|
Config.AllocSectionsPrefix =
|
|
InputArgs.getLastArgValue(OBJCOPY_prefix_alloc_sections);
|
|
if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition))
|
|
Config.ExtractPartition = Arg->getValue();
|
|
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {
|
|
if (!StringRef(Arg->getValue()).contains('='))
|
|
return createStringError(errc::invalid_argument,
|
|
"bad format for --redefine-sym");
|
|
auto Old2New = StringRef(Arg->getValue()).split('=');
|
|
if (!Config.SymbolsToRename.insert(Old2New).second)
|
|
return createStringError(errc::invalid_argument,
|
|
"multiple redefinition of symbol '%s'",
|
|
Old2New.first.str().c_str());
|
|
}
|
|
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbols))
|
|
if (Error E = addSymbolsToRenameFromFile(Config.SymbolsToRename, DC.Alloc,
|
|
Arg->getValue()))
|
|
return std::move(E);
|
|
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) {
|
|
Expected<SectionRename> SR =
|
|
parseRenameSectionValue(StringRef(Arg->getValue()));
|
|
if (!SR)
|
|
return SR.takeError();
|
|
if (!Config.SectionsToRename.try_emplace(SR->OriginalName, *SR).second)
|
|
return createStringError(errc::invalid_argument,
|
|
"multiple renames of section '%s'",
|
|
SR->OriginalName.str().c_str());
|
|
}
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) {
|
|
Expected<std::pair<StringRef, uint64_t>> NameAndAlign =
|
|
parseSetSectionAlignment(Arg->getValue());
|
|
if (!NameAndAlign)
|
|
return NameAndAlign.takeError();
|
|
Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second;
|
|
}
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) {
|
|
Expected<SectionFlagsUpdate> SFU =
|
|
parseSetSectionFlagValue(Arg->getValue());
|
|
if (!SFU)
|
|
return SFU.takeError();
|
|
if (!Config.SetSectionFlags.try_emplace(SFU->Name, *SFU).second)
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"--set-section-flags set multiple times for section '%s'",
|
|
SFU->Name.str().c_str());
|
|
}
|
|
// Prohibit combinations of --set-section-flags when the section name is used
|
|
// by --rename-section, either as a source or a destination.
|
|
for (const auto &E : Config.SectionsToRename) {
|
|
const SectionRename &SR = E.second;
|
|
if (Config.SetSectionFlags.count(SR.OriginalName))
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"--set-section-flags=%s conflicts with --rename-section=%s=%s",
|
|
SR.OriginalName.str().c_str(), SR.OriginalName.str().c_str(),
|
|
SR.NewName.str().c_str());
|
|
if (Config.SetSectionFlags.count(SR.NewName))
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"--set-section-flags=%s conflicts with --rename-section=%s=%s",
|
|
SR.NewName.str().c_str(), SR.OriginalName.str().c_str(),
|
|
SR.NewName.str().c_str());
|
|
}
|
|
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section))
|
|
if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SectionMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section))
|
|
if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SectionMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_only_section))
|
|
if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SectionMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) {
|
|
StringRef ArgValue(Arg->getValue());
|
|
if (!ArgValue.contains('='))
|
|
return createStringError(errc::invalid_argument,
|
|
"bad format for --add-section: missing '='");
|
|
if (ArgValue.split("=").second.empty())
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"bad format for --add-section: missing file name");
|
|
Config.AddSection.push_back(ArgValue);
|
|
}
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_update_section)) {
|
|
StringRef ArgValue(Arg->getValue());
|
|
if (!ArgValue.contains('='))
|
|
return createStringError(errc::invalid_argument,
|
|
"bad format for --update-section: missing '='");
|
|
if (ArgValue.split("=").second.empty())
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"bad format for --update-section: missing file name");
|
|
Config.UpdateSection.push_back(ArgValue);
|
|
}
|
|
for (auto *Arg : InputArgs.filtered(OBJCOPY_dump_section)) {
|
|
StringRef Value(Arg->getValue());
|
|
if (Value.split('=').second.empty())
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"bad format for --dump-section, expected section=file");
|
|
Config.DumpSection.push_back(Value);
|
|
}
|
|
Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all);
|
|
Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu);
|
|
Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug);
|
|
Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo);
|
|
Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections);
|
|
Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc);
|
|
Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded);
|
|
Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo);
|
|
Config.ExtractMainPartition =
|
|
InputArgs.hasArg(OBJCOPY_extract_main_partition);
|
|
ELFConfig.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
|
|
Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
|
|
if (InputArgs.hasArg(OBJCOPY_discard_all, OBJCOPY_discard_locals))
|
|
Config.DiscardMode =
|
|
InputArgs.hasFlag(OBJCOPY_discard_all, OBJCOPY_discard_locals)
|
|
? DiscardType::All
|
|
: DiscardType::Locals;
|
|
Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug);
|
|
ELFConfig.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
|
|
MachOConfig.KeepUndefined = InputArgs.hasArg(OBJCOPY_keep_undefined);
|
|
Config.DecompressDebugSections =
|
|
InputArgs.hasArg(OBJCOPY_decompress_debug_sections);
|
|
if (Config.DiscardMode == DiscardType::All) {
|
|
Config.StripDebug = true;
|
|
ELFConfig.KeepFileSymbols = true;
|
|
}
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol))
|
|
if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols))
|
|
if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc,
|
|
Arg->getValue(), SymbolMatchStyle,
|
|
ErrorCallback))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol))
|
|
if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols))
|
|
if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc,
|
|
Arg->getValue(), SymbolMatchStyle,
|
|
ErrorCallback))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol))
|
|
if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols))
|
|
if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc,
|
|
Arg->getValue(), SymbolMatchStyle,
|
|
ErrorCallback))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol))
|
|
if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols))
|
|
if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc,
|
|
Arg->getValue(), SymbolMatchStyle,
|
|
ErrorCallback))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol))
|
|
if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols))
|
|
if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc,
|
|
Arg->getValue(), SymbolMatchStyle,
|
|
ErrorCallback))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol))
|
|
if (Error E =
|
|
Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols))
|
|
if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc,
|
|
Arg->getValue(), SymbolMatchStyle,
|
|
ErrorCallback))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol))
|
|
if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbols))
|
|
if (Error E =
|
|
addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(),
|
|
SymbolMatchStyle, ErrorCallback))
|
|
return std::move(E);
|
|
for (auto *Arg : InputArgs.filtered(OBJCOPY_add_symbol)) {
|
|
Expected<NewSymbolInfo> SymInfo = parseNewSymbolInfo(Arg->getValue());
|
|
if (!SymInfo)
|
|
return SymInfo.takeError();
|
|
|
|
Config.SymbolsToAdd.push_back(*SymInfo);
|
|
}
|
|
|
|
ELFConfig.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links);
|
|
|
|
Config.DeterministicArchives = InputArgs.hasFlag(
|
|
OBJCOPY_enable_deterministic_archives,
|
|
OBJCOPY_disable_deterministic_archives, /*default=*/true);
|
|
|
|
Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates);
|
|
|
|
if (Config.PreserveDates &&
|
|
(Config.OutputFilename == "-" || Config.InputFilename == "-"))
|
|
return createStringError(errc::invalid_argument,
|
|
"--preserve-dates requires a file");
|
|
|
|
for (auto Arg : InputArgs)
|
|
if (Arg->getOption().matches(OBJCOPY_set_start)) {
|
|
auto EAddr = getAsInteger<uint64_t>(Arg->getValue());
|
|
if (!EAddr)
|
|
return createStringError(
|
|
EAddr.getError(), "bad entry point address: '%s'", Arg->getValue());
|
|
|
|
ELFConfig.EntryExpr = [EAddr](uint64_t) { return *EAddr; };
|
|
} else if (Arg->getOption().matches(OBJCOPY_change_start)) {
|
|
auto EIncr = getAsInteger<int64_t>(Arg->getValue());
|
|
if (!EIncr)
|
|
return createStringError(EIncr.getError(),
|
|
"bad entry point increment: '%s'",
|
|
Arg->getValue());
|
|
auto Expr = ELFConfig.EntryExpr ? std::move(ELFConfig.EntryExpr)
|
|
: [](uint64_t A) { return A; };
|
|
ELFConfig.EntryExpr = [Expr, EIncr](uint64_t EAddr) {
|
|
return Expr(EAddr) + *EIncr;
|
|
};
|
|
}
|
|
|
|
if (Config.DecompressDebugSections &&
|
|
Config.CompressionType != DebugCompressionType::None) {
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"cannot specify both --compress-debug-sections and "
|
|
"--decompress-debug-sections");
|
|
}
|
|
|
|
if (Config.DecompressDebugSections && !zlib::isAvailable())
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress");
|
|
|
|
if (Config.ExtractPartition && Config.ExtractMainPartition)
|
|
return createStringError(errc::invalid_argument,
|
|
"cannot specify --extract-partition together with "
|
|
"--extract-main-partition");
|
|
|
|
DC.CopyConfigs.push_back(std::move(ConfigMgr));
|
|
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>
|
|
objcopy::parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
|
|
DriverConfig DC;
|
|
ConfigManager ConfigMgr;
|
|
CommonConfig &Config = ConfigMgr.Common;
|
|
MachOConfig &MachOConfig = ConfigMgr.MachO;
|
|
InstallNameToolOptTable T;
|
|
unsigned MissingArgumentIndex, MissingArgumentCount;
|
|
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);
|
|
}
|
|
|
|
if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) {
|
|
printHelp(T, outs(), ToolType::InstallNameTool);
|
|
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))
|
|
MachOConfig.RPathToAdd.push_back(Arg->getValue());
|
|
|
|
for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_prepend_rpath))
|
|
MachOConfig.RPathToPrepend.push_back(Arg->getValue());
|
|
|
|
for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) {
|
|
StringRef RPath = Arg->getValue();
|
|
|
|
// Cannot add and delete the same rpath at the same time.
|
|
if (is_contained(MachOConfig.RPathToAdd, RPath))
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"cannot specify both -add_rpath '%s' and -delete_rpath '%s'",
|
|
RPath.str().c_str(), RPath.str().c_str());
|
|
if (is_contained(MachOConfig.RPathToPrepend, RPath))
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"cannot specify both -prepend_rpath '%s' and -delete_rpath '%s'",
|
|
RPath.str().c_str(), RPath.str().c_str());
|
|
|
|
MachOConfig.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(
|
|
MachOConfig.RPathsToUpdate,
|
|
[&Match](const DenseMap<StringRef, StringRef>::value_type &OldNew) {
|
|
return Match(OldNew.getFirst()) || Match(OldNew.getSecond());
|
|
});
|
|
if (It1 != MachOConfig.RPathsToUpdate.end())
|
|
return createStringError(errc::invalid_argument,
|
|
"cannot specify both -rpath '" +
|
|
It1->getFirst() + "' '" + It1->getSecond() +
|
|
"' and -rpath '" + Old + "' '" + New + "'");
|
|
|
|
// Cannot specify the same rpath under both -delete_rpath and -rpath
|
|
auto It2 = find_if(MachOConfig.RPathsToRemove, Match);
|
|
if (It2 != MachOConfig.RPathsToRemove.end())
|
|
return createStringError(errc::invalid_argument,
|
|
"cannot specify both -delete_rpath '" + *It2 +
|
|
"' and -rpath '" + Old + "' '" + New + "'");
|
|
|
|
// Cannot specify the same rpath under both -add_rpath and -rpath
|
|
auto It3 = find_if(MachOConfig.RPathToAdd, Match);
|
|
if (It3 != MachOConfig.RPathToAdd.end())
|
|
return createStringError(errc::invalid_argument,
|
|
"cannot specify both -add_rpath '" + *It3 +
|
|
"' and -rpath '" + Old + "' '" + New + "'");
|
|
|
|
// Cannot specify the same rpath under both -prepend_rpath and -rpath.
|
|
auto It4 = find_if(MachOConfig.RPathToPrepend, Match);
|
|
if (It4 != MachOConfig.RPathToPrepend.end())
|
|
return createStringError(errc::invalid_argument,
|
|
"cannot specify both -prepend_rpath '" + *It4 +
|
|
"' and -rpath '" + Old + "' '" + New + "'");
|
|
|
|
MachOConfig.RPathsToUpdate.insert({Old, New});
|
|
}
|
|
|
|
if (auto *Arg = InputArgs.getLastArg(INSTALL_NAME_TOOL_id)) {
|
|
MachOConfig.SharedLibId = Arg->getValue();
|
|
if (MachOConfig.SharedLibId->empty())
|
|
return createStringError(errc::invalid_argument,
|
|
"cannot specify an empty id");
|
|
}
|
|
|
|
for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_change))
|
|
MachOConfig.InstallNamesToUpdate.insert(
|
|
{Arg->getValue(0), Arg->getValue(1)});
|
|
|
|
MachOConfig.RemoveAllRpaths =
|
|
InputArgs.hasArg(INSTALL_NAME_TOOL_delete_all_rpaths);
|
|
|
|
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(ConfigMgr));
|
|
return std::move(DC);
|
|
}
|
|
|
|
Expected<DriverConfig>
|
|
objcopy::parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) {
|
|
DriverConfig DC;
|
|
ConfigManager ConfigMgr;
|
|
CommonConfig &Config = ConfigMgr.Common;
|
|
BitcodeStripOptTable T;
|
|
unsigned MissingArgumentIndex, MissingArgumentCount;
|
|
opt::InputArgList InputArgs =
|
|
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
|
|
|
|
if (InputArgs.size() == 0) {
|
|
printHelp(T, errs(), ToolType::BitcodeStrip);
|
|
exit(1);
|
|
}
|
|
|
|
if (InputArgs.hasArg(BITCODE_STRIP_help)) {
|
|
printHelp(T, outs(), ToolType::BitcodeStrip);
|
|
exit(0);
|
|
}
|
|
|
|
if (InputArgs.hasArg(BITCODE_STRIP_version)) {
|
|
outs() << "llvm-bitcode-strip, compatible with cctools "
|
|
"bitcode_strip\n";
|
|
cl::PrintVersionMessage();
|
|
exit(0);
|
|
}
|
|
|
|
for (auto *Arg : InputArgs.filtered(BITCODE_STRIP_UNKNOWN))
|
|
return createStringError(errc::invalid_argument, "unknown argument '%s'",
|
|
Arg->getAsString(InputArgs).c_str());
|
|
|
|
SmallVector<StringRef, 2> Positional;
|
|
for (auto *Arg : InputArgs.filtered(BITCODE_STRIP_INPUT))
|
|
Positional.push_back(Arg->getValue());
|
|
if (Positional.size() > 1)
|
|
return createStringError(errc::invalid_argument,
|
|
"llvm-bitcode-strip expects a single input file");
|
|
assert(!Positional.empty());
|
|
Config.InputFilename = Positional[0];
|
|
Config.OutputFilename = Positional[0];
|
|
|
|
DC.CopyConfigs.push_back(std::move(ConfigMgr));
|
|
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.
|
|
Expected<DriverConfig>
|
|
objcopy::parseStripOptions(ArrayRef<const char *> RawArgsArr,
|
|
function_ref<Error(Error)> ErrorCallback) {
|
|
const char *const *DashDash =
|
|
std::find_if(RawArgsArr.begin(), RawArgsArr.end(),
|
|
[](StringRef Str) { return Str == "--"; });
|
|
ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash);
|
|
if (DashDash != RawArgsArr.end())
|
|
DashDash = std::next(DashDash);
|
|
|
|
StripOptTable T;
|
|
unsigned MissingArgumentIndex, MissingArgumentCount;
|
|
llvm::opt::InputArgList InputArgs =
|
|
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
|
|
|
|
if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) {
|
|
printHelp(T, errs(), ToolType::Strip);
|
|
exit(1);
|
|
}
|
|
|
|
if (InputArgs.hasArg(STRIP_help)) {
|
|
printHelp(T, outs(), ToolType::Strip);
|
|
exit(0);
|
|
}
|
|
|
|
if (InputArgs.hasArg(STRIP_version)) {
|
|
outs() << "llvm-strip, compatible with GNU strip\n";
|
|
cl::PrintVersionMessage();
|
|
exit(0);
|
|
}
|
|
|
|
SmallVector<StringRef, 2> Positional;
|
|
for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN))
|
|
return createStringError(errc::invalid_argument, "unknown argument '%s'",
|
|
Arg->getAsString(InputArgs).c_str());
|
|
for (auto Arg : InputArgs.filtered(STRIP_INPUT))
|
|
Positional.push_back(Arg->getValue());
|
|
std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional));
|
|
|
|
if (Positional.empty())
|
|
return createStringError(errc::invalid_argument, "no input file specified");
|
|
|
|
if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output))
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"multiple input files cannot be used in combination with -o");
|
|
|
|
ConfigManager ConfigMgr;
|
|
CommonConfig &Config = ConfigMgr.Common;
|
|
ELFConfig &ELFConfig = ConfigMgr.ELF;
|
|
MachOConfig &MachOConfig = ConfigMgr.MachO;
|
|
|
|
if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard))
|
|
return createStringError(errc::invalid_argument,
|
|
"--regex and --wildcard are incompatible");
|
|
MatchStyle SectionMatchStyle =
|
|
InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard;
|
|
MatchStyle SymbolMatchStyle = InputArgs.hasArg(STRIP_regex)
|
|
? MatchStyle::Regex
|
|
: InputArgs.hasArg(STRIP_wildcard)
|
|
? MatchStyle::Wildcard
|
|
: MatchStyle::Literal;
|
|
ELFConfig.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links);
|
|
Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug);
|
|
|
|
if (InputArgs.hasArg(STRIP_discard_all, STRIP_discard_locals))
|
|
Config.DiscardMode =
|
|
InputArgs.hasFlag(STRIP_discard_all, STRIP_discard_locals)
|
|
? DiscardType::All
|
|
: DiscardType::Locals;
|
|
Config.StripSections = InputArgs.hasArg(STRIP_strip_sections);
|
|
Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded);
|
|
if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all))
|
|
Config.StripAll = Arg->getOption().getID() == STRIP_strip_all;
|
|
Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu);
|
|
MachOConfig.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols);
|
|
Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug);
|
|
ELFConfig.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols);
|
|
MachOConfig.KeepUndefined = InputArgs.hasArg(STRIP_keep_undefined);
|
|
|
|
for (auto Arg : InputArgs.filtered(STRIP_keep_section))
|
|
if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SectionMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
|
|
for (auto Arg : InputArgs.filtered(STRIP_remove_section))
|
|
if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SectionMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
|
|
for (auto Arg : InputArgs.filtered(STRIP_strip_symbol))
|
|
if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
|
|
for (auto Arg : InputArgs.filtered(STRIP_keep_symbol))
|
|
if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create(
|
|
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
|
|
return std::move(E);
|
|
|
|
if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug &&
|
|
!Config.StripUnneeded && Config.DiscardMode == DiscardType::None &&
|
|
!Config.StripAllGNU && Config.SymbolsToRemove.empty())
|
|
Config.StripAll = true;
|
|
|
|
if (Config.DiscardMode == DiscardType::All) {
|
|
Config.StripDebug = true;
|
|
ELFConfig.KeepFileSymbols = true;
|
|
}
|
|
|
|
Config.DeterministicArchives =
|
|
InputArgs.hasFlag(STRIP_enable_deterministic_archives,
|
|
STRIP_disable_deterministic_archives, /*default=*/true);
|
|
|
|
Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates);
|
|
Config.InputFormat = FileFormat::Unspecified;
|
|
Config.OutputFormat = FileFormat::Unspecified;
|
|
|
|
DriverConfig DC;
|
|
if (Positional.size() == 1) {
|
|
Config.InputFilename = Positional[0];
|
|
Config.OutputFilename =
|
|
InputArgs.getLastArgValue(STRIP_output, Positional[0]);
|
|
DC.CopyConfigs.push_back(std::move(ConfigMgr));
|
|
} else {
|
|
StringMap<unsigned> InputFiles;
|
|
for (StringRef Filename : Positional) {
|
|
if (InputFiles[Filename]++ == 1) {
|
|
if (Filename == "-")
|
|
return createStringError(
|
|
errc::invalid_argument,
|
|
"cannot specify '-' as an input file more than once");
|
|
if (Error E = ErrorCallback(createStringError(
|
|
errc::invalid_argument, "'%s' was already specified",
|
|
Filename.str().c_str())))
|
|
return std::move(E);
|
|
}
|
|
Config.InputFilename = Filename;
|
|
Config.OutputFilename = Filename;
|
|
DC.CopyConfigs.push_back(ConfigMgr);
|
|
}
|
|
}
|
|
|
|
if (Config.PreserveDates && (is_contained(Positional, "-") ||
|
|
InputArgs.getLastArgValue(STRIP_output) == "-"))
|
|
return createStringError(errc::invalid_argument,
|
|
"--preserve-dates requires a file");
|
|
|
|
return std::move(DC);
|
|
}
|