
Apologies for the large change, I looked for ways to break this up and all of the ones I saw added real complexity. This change focuses on the option's prefixed names and the array of prefixes. These are present in every option and the dominant source of dynamic relocations for PIE or PIC users of LLVM and Clang tooling. In some cases, 100s or 1000s of them for the Clang driver which has a huge number of options. This PR addresses this by building a string table and a prefixes table that can be referenced with indices rather than pointers that require dynamic relocations. This removes almost 7k dynmaic relocations from the `clang` binary, roughly 8% of the remaining dynmaic relocations outside of vtables. For busy-boxing use cases where many different option tables are linked into the same binary, the savings add up a bit more. The string table is a straightforward mechanism, but the prefixes required some subtlety. They are encoded in a Pascal-string fashion with a size followed by a sequence of offsets. This works relatively well for the small realistic prefixes arrays in use. Lots of code has to change in order to land this though: both all the option library code has to be updated to use the string table and prefixes table, and all the users of the options library have to be updated to correctly instantiate the objects. Some follow-up patches in the works to provide an abstraction for this style of code, and to start using the same technique for some of the other strings here now that the infrastructure is in place.
635 lines
22 KiB
C++
635 lines
22 KiB
C++
//===- llvm-ifs.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 "ErrorCollector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/BinaryFormat/ELF.h"
|
|
#include "llvm/InterfaceStub/ELFObjHandler.h"
|
|
#include "llvm/InterfaceStub/IFSHandler.h"
|
|
#include "llvm/InterfaceStub/IFSStub.h"
|
|
#include "llvm/ObjectYAML/yaml2obj.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/Option/Option.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FileOutputBuffer.h"
|
|
#include "llvm/Support/LLVMDriver.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/VersionTuple.h"
|
|
#include "llvm/Support/WithColor.h"
|
|
#include "llvm/Support/YAMLTraits.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
#include "llvm/TextAPI/InterfaceFile.h"
|
|
#include "llvm/TextAPI/TextAPIReader.h"
|
|
#include "llvm/TextAPI/TextAPIWriter.h"
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::yaml;
|
|
using namespace llvm::MachO;
|
|
using namespace llvm::ifs;
|
|
|
|
#define DEBUG_TYPE "llvm-ifs"
|
|
|
|
namespace {
|
|
const VersionTuple IfsVersionCurrent(3, 0);
|
|
|
|
enum class FileFormat { IFS, ELF, TBD };
|
|
} // end anonymous namespace
|
|
|
|
using namespace llvm::opt;
|
|
enum ID {
|
|
OPT_INVALID = 0, // This is not an option ID.
|
|
#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
|
|
#include "Opts.inc"
|
|
#undef OPTION
|
|
};
|
|
|
|
#define OPTTABLE_STR_TABLE_CODE
|
|
#include "Opts.inc"
|
|
#undef OPTTABLE_STR_TABLE_CODE
|
|
|
|
#define OPTTABLE_PREFIXES_TABLE_CODE
|
|
#include "Opts.inc"
|
|
#undef OPTTABLE_PREFIXES_TABLE_CODE
|
|
|
|
static constexpr opt::OptTable::Info InfoTable[] = {
|
|
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
|
|
#include "Opts.inc"
|
|
#undef OPTION
|
|
};
|
|
|
|
class IFSOptTable : public opt::GenericOptTable {
|
|
public:
|
|
IFSOptTable()
|
|
: opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
|
|
setGroupedShortOptions(true);
|
|
}
|
|
};
|
|
|
|
struct DriverConfig {
|
|
std::vector<std::string> InputFilePaths;
|
|
|
|
std::optional<FileFormat> InputFormat;
|
|
std::optional<FileFormat> OutputFormat;
|
|
|
|
std::optional<std::string> HintIfsTarget;
|
|
std::optional<std::string> OptTargetTriple;
|
|
std::optional<IFSArch> OverrideArch;
|
|
std::optional<IFSBitWidthType> OverrideBitWidth;
|
|
std::optional<IFSEndiannessType> OverrideEndianness;
|
|
|
|
bool StripIfsArch = false;
|
|
bool StripIfsBitwidth = false;
|
|
bool StripIfsEndianness = false;
|
|
bool StripIfsTarget = false;
|
|
bool StripNeeded = false;
|
|
bool StripSize = false;
|
|
bool StripUndefined = false;
|
|
|
|
std::vector<std::string> Exclude;
|
|
|
|
std::optional<std::string> SoName;
|
|
|
|
std::optional<std::string> Output;
|
|
std::optional<std::string> OutputElf;
|
|
std::optional<std::string> OutputIfs;
|
|
std::optional<std::string> OutputTbd;
|
|
|
|
bool WriteIfChanged = false;
|
|
};
|
|
|
|
static std::string getTypeName(IFSSymbolType Type) {
|
|
switch (Type) {
|
|
case IFSSymbolType::NoType:
|
|
return "NoType";
|
|
case IFSSymbolType::Func:
|
|
return "Func";
|
|
case IFSSymbolType::Object:
|
|
return "Object";
|
|
case IFSSymbolType::TLS:
|
|
return "TLS";
|
|
case IFSSymbolType::Unknown:
|
|
return "Unknown";
|
|
}
|
|
llvm_unreachable("Unexpected ifs symbol type.");
|
|
}
|
|
|
|
static Expected<std::unique_ptr<IFSStub>>
|
|
readInputFile(std::optional<FileFormat> &InputFormat, StringRef FilePath) {
|
|
// Read in file.
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
|
|
MemoryBuffer::getFileOrSTDIN(FilePath, /*IsText=*/true);
|
|
if (!BufOrError)
|
|
return createStringError(BufOrError.getError(), "Could not open `%s`",
|
|
FilePath.data());
|
|
|
|
std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError);
|
|
ErrorCollector EC(/*UseFatalErrors=*/false);
|
|
|
|
// First try to read as a binary (fails fast if not binary).
|
|
if (!InputFormat || *InputFormat == FileFormat::ELF) {
|
|
Expected<std::unique_ptr<IFSStub>> StubFromELF =
|
|
readELFFile(FileReadBuffer->getMemBufferRef());
|
|
if (StubFromELF) {
|
|
InputFormat = FileFormat::ELF;
|
|
(*StubFromELF)->IfsVersion = IfsVersionCurrent;
|
|
return std::move(*StubFromELF);
|
|
}
|
|
EC.addError(StubFromELF.takeError(), "BinaryRead");
|
|
}
|
|
|
|
// Fall back to reading as a ifs.
|
|
if (!InputFormat || *InputFormat == FileFormat::IFS) {
|
|
Expected<std::unique_ptr<IFSStub>> StubFromIFS =
|
|
readIFSFromBuffer(FileReadBuffer->getBuffer());
|
|
if (StubFromIFS) {
|
|
InputFormat = FileFormat::IFS;
|
|
if ((*StubFromIFS)->IfsVersion > IfsVersionCurrent)
|
|
EC.addError(
|
|
createStringError(errc::not_supported,
|
|
"IFS version " +
|
|
(*StubFromIFS)->IfsVersion.getAsString() +
|
|
" is unsupported."),
|
|
"ReadInputFile");
|
|
else
|
|
return std::move(*StubFromIFS);
|
|
} else {
|
|
EC.addError(StubFromIFS.takeError(), "YamlParse");
|
|
}
|
|
}
|
|
|
|
// If both readers fail, build a new error that includes all information.
|
|
EC.addError(createStringError(errc::not_supported,
|
|
"No file readers succeeded reading `%s` "
|
|
"(unsupported/malformed file?)",
|
|
FilePath.data()),
|
|
"ReadInputFile");
|
|
EC.escalateToFatal();
|
|
return EC.makeError();
|
|
}
|
|
|
|
static int writeTbdStub(const Triple &T, const std::vector<IFSSymbol> &Symbols,
|
|
const StringRef Format, raw_ostream &Out) {
|
|
|
|
auto PlatformTypeOrError =
|
|
[](const llvm::Triple &T) -> llvm::Expected<llvm::MachO::PlatformType> {
|
|
if (T.isMacOSX())
|
|
return llvm::MachO::PLATFORM_MACOS;
|
|
if (T.isTvOS())
|
|
return llvm::MachO::PLATFORM_TVOS;
|
|
if (T.isWatchOS())
|
|
return llvm::MachO::PLATFORM_WATCHOS;
|
|
// Note: put isiOS last because tvOS and watchOS are also iOS according
|
|
// to the Triple.
|
|
if (T.isiOS())
|
|
return llvm::MachO::PLATFORM_IOS;
|
|
|
|
return createStringError(errc::not_supported, "Invalid Platform.\n");
|
|
}(T);
|
|
|
|
if (!PlatformTypeOrError)
|
|
return -1;
|
|
|
|
PlatformType Plat = PlatformTypeOrError.get();
|
|
TargetList Targets({Target(llvm::MachO::mapToArchitecture(T), Plat)});
|
|
|
|
InterfaceFile File;
|
|
File.setFileType(FileType::TBD_V3); // Only supporting v3 for now.
|
|
File.addTargets(Targets);
|
|
|
|
for (const auto &Symbol : Symbols) {
|
|
auto Name = Symbol.Name;
|
|
auto Kind = EncodeKind::GlobalSymbol;
|
|
switch (Symbol.Type) {
|
|
default:
|
|
case IFSSymbolType::NoType:
|
|
Kind = EncodeKind::GlobalSymbol;
|
|
break;
|
|
case IFSSymbolType::Object:
|
|
Kind = EncodeKind::GlobalSymbol;
|
|
break;
|
|
case IFSSymbolType::Func:
|
|
Kind = EncodeKind::GlobalSymbol;
|
|
break;
|
|
}
|
|
if (Symbol.Weak)
|
|
File.addSymbol(Kind, Name, Targets, SymbolFlags::WeakDefined);
|
|
else
|
|
File.addSymbol(Kind, Name, Targets);
|
|
}
|
|
|
|
SmallString<4096> Buffer;
|
|
raw_svector_ostream OS(Buffer);
|
|
if (Error Result = TextAPIWriter::writeToStream(OS, File))
|
|
return -1;
|
|
Out << OS.str();
|
|
return 0;
|
|
}
|
|
|
|
static void fatalError(Error Err) {
|
|
WithColor::defaultErrorHandler(std::move(Err));
|
|
exit(1);
|
|
}
|
|
|
|
static void fatalError(Twine T) {
|
|
WithColor::error() << T.str() << '\n';
|
|
exit(1);
|
|
}
|
|
|
|
/// writeIFS() writes a Text-Based ELF stub to a file using the latest version
|
|
/// of the YAML parser.
|
|
static Error writeIFS(StringRef FilePath, IFSStub &Stub, bool WriteIfChanged) {
|
|
// Write IFS to memory first.
|
|
std::string IFSStr;
|
|
raw_string_ostream OutStr(IFSStr);
|
|
Error YAMLErr = writeIFSToOutputStream(OutStr, Stub);
|
|
if (YAMLErr)
|
|
return YAMLErr;
|
|
OutStr.flush();
|
|
|
|
if (WriteIfChanged) {
|
|
if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
|
|
MemoryBuffer::getFile(FilePath)) {
|
|
// Compare IFS output with the existing IFS file. If unchanged, avoid
|
|
// changing the file.
|
|
if ((*BufOrError)->getBuffer() == IFSStr)
|
|
return Error::success();
|
|
}
|
|
}
|
|
// Open IFS file for writing.
|
|
std::error_code SysErr;
|
|
raw_fd_ostream Out(FilePath, SysErr);
|
|
if (SysErr)
|
|
return createStringError(SysErr, "Couldn't open `%s` for writing",
|
|
FilePath.data());
|
|
Out << IFSStr;
|
|
return Error::success();
|
|
}
|
|
|
|
static DriverConfig parseArgs(int argc, char *const *argv) {
|
|
BumpPtrAllocator A;
|
|
StringSaver Saver(A);
|
|
IFSOptTable Tbl;
|
|
StringRef ToolName = argv[0];
|
|
llvm::opt::InputArgList Args = Tbl.parseArgs(
|
|
argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { fatalError(Msg); });
|
|
if (Args.hasArg(OPT_help)) {
|
|
Tbl.printHelp(llvm::outs(),
|
|
(Twine(ToolName) + " <input_file> <output_file> [options]")
|
|
.str()
|
|
.c_str(),
|
|
"shared object stubbing tool");
|
|
std::exit(0);
|
|
}
|
|
if (Args.hasArg(OPT_version)) {
|
|
llvm::outs() << ToolName << '\n';
|
|
cl::PrintVersionMessage();
|
|
std::exit(0);
|
|
}
|
|
|
|
DriverConfig Config;
|
|
for (const opt::Arg *A : Args.filtered(OPT_INPUT))
|
|
Config.InputFilePaths.push_back(A->getValue());
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_input_format_EQ)) {
|
|
Config.InputFormat = StringSwitch<std::optional<FileFormat>>(A->getValue())
|
|
.Case("IFS", FileFormat::IFS)
|
|
.Case("ELF", FileFormat::ELF)
|
|
.Default(std::nullopt);
|
|
if (!Config.InputFormat)
|
|
fatalError(Twine("invalid argument '") + A->getValue());
|
|
}
|
|
|
|
auto OptionNotFound = [ToolName](StringRef FlagName, StringRef OptionName) {
|
|
fatalError(Twine(ToolName) + ": for the " + FlagName +
|
|
" option: Cannot find option named '" + OptionName + "'!");
|
|
};
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_output_format_EQ)) {
|
|
Config.OutputFormat = StringSwitch<std::optional<FileFormat>>(A->getValue())
|
|
.Case("IFS", FileFormat::IFS)
|
|
.Case("ELF", FileFormat::ELF)
|
|
.Case("TBD", FileFormat::TBD)
|
|
.Default(std::nullopt);
|
|
if (!Config.OutputFormat)
|
|
OptionNotFound("--output-format", A->getValue());
|
|
}
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_arch_EQ)) {
|
|
uint16_t eMachine = ELF::convertArchNameToEMachine(A->getValue());
|
|
if (eMachine == ELF::EM_NONE) {
|
|
fatalError(Twine("unknown arch '") + A->getValue() + "'");
|
|
}
|
|
Config.OverrideArch = eMachine;
|
|
}
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_bitwidth_EQ)) {
|
|
size_t Width;
|
|
llvm::StringRef S(A->getValue());
|
|
if (!S.getAsInteger<size_t>(10, Width) || Width == 64 || Width == 32)
|
|
Config.OverrideBitWidth =
|
|
Width == 64 ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32;
|
|
else
|
|
OptionNotFound("--bitwidth", A->getValue());
|
|
}
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_endianness_EQ)) {
|
|
Config.OverrideEndianness =
|
|
StringSwitch<std::optional<IFSEndiannessType>>(A->getValue())
|
|
.Case("little", IFSEndiannessType::Little)
|
|
.Case("big", IFSEndiannessType::Big)
|
|
.Default(std::nullopt);
|
|
if (!Config.OverrideEndianness)
|
|
OptionNotFound("--endianness", A->getValue());
|
|
}
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_target_EQ))
|
|
Config.OptTargetTriple = A->getValue();
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_hint_ifs_target_EQ))
|
|
Config.HintIfsTarget = A->getValue();
|
|
|
|
Config.StripIfsArch = Args.hasArg(OPT_strip_ifs_arch);
|
|
Config.StripIfsBitwidth = Args.hasArg(OPT_strip_ifs_bitwidth);
|
|
Config.StripIfsEndianness = Args.hasArg(OPT_strip_ifs_endianness);
|
|
Config.StripIfsTarget = Args.hasArg(OPT_strip_ifs_target);
|
|
Config.StripUndefined = Args.hasArg(OPT_strip_undefined);
|
|
Config.StripNeeded = Args.hasArg(OPT_strip_needed);
|
|
Config.StripSize = Args.hasArg(OPT_strip_size);
|
|
|
|
for (const opt::Arg *A : Args.filtered(OPT_exclude_EQ))
|
|
Config.Exclude.push_back(A->getValue());
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_soname_EQ))
|
|
Config.SoName = A->getValue();
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_output_EQ))
|
|
Config.Output = A->getValue();
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_output_elf_EQ))
|
|
Config.OutputElf = A->getValue();
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_output_ifs_EQ))
|
|
Config.OutputIfs = A->getValue();
|
|
if (const opt::Arg *A = Args.getLastArg(OPT_output_tbd_EQ))
|
|
Config.OutputTbd = A->getValue();
|
|
Config.WriteIfChanged = Args.hasArg(OPT_write_if_changed);
|
|
return Config;
|
|
}
|
|
|
|
int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {
|
|
DriverConfig Config = parseArgs(argc, argv);
|
|
|
|
if (Config.InputFilePaths.empty())
|
|
Config.InputFilePaths.push_back("-");
|
|
|
|
// If input files are more than one, they can only be IFS files.
|
|
if (Config.InputFilePaths.size() > 1)
|
|
Config.InputFormat = FileFormat::IFS;
|
|
|
|
// Attempt to merge input.
|
|
IFSStub Stub;
|
|
std::map<std::string, IFSSymbol> SymbolMap;
|
|
std::string PreviousInputFilePath;
|
|
for (const std::string &InputFilePath : Config.InputFilePaths) {
|
|
Expected<std::unique_ptr<IFSStub>> StubOrErr =
|
|
readInputFile(Config.InputFormat, InputFilePath);
|
|
if (!StubOrErr)
|
|
fatalError(StubOrErr.takeError());
|
|
|
|
std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get());
|
|
if (PreviousInputFilePath.empty()) {
|
|
Stub.IfsVersion = TargetStub->IfsVersion;
|
|
Stub.Target = TargetStub->Target;
|
|
Stub.SoName = TargetStub->SoName;
|
|
Stub.NeededLibs = TargetStub->NeededLibs;
|
|
} else {
|
|
if (Stub.IfsVersion != TargetStub->IfsVersion) {
|
|
if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
|
|
WithColor::error()
|
|
<< "Interface Stub: IfsVersion Mismatch."
|
|
<< "\nFilenames: " << PreviousInputFilePath << " "
|
|
<< InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion
|
|
<< " " << TargetStub->IfsVersion << "\n";
|
|
return -1;
|
|
}
|
|
if (TargetStub->IfsVersion > Stub.IfsVersion)
|
|
Stub.IfsVersion = TargetStub->IfsVersion;
|
|
}
|
|
if (Stub.Target != TargetStub->Target && !TargetStub->Target.empty()) {
|
|
WithColor::error() << "Interface Stub: Target Mismatch."
|
|
<< "\nFilenames: " << PreviousInputFilePath << " "
|
|
<< InputFilePath;
|
|
return -1;
|
|
}
|
|
if (Stub.SoName != TargetStub->SoName) {
|
|
WithColor::error() << "Interface Stub: SoName Mismatch."
|
|
<< "\nFilenames: " << PreviousInputFilePath << " "
|
|
<< InputFilePath
|
|
<< "\nSoName Values: " << Stub.SoName << " "
|
|
<< TargetStub->SoName << "\n";
|
|
return -1;
|
|
}
|
|
if (Stub.NeededLibs != TargetStub->NeededLibs) {
|
|
WithColor::error() << "Interface Stub: NeededLibs Mismatch."
|
|
<< "\nFilenames: " << PreviousInputFilePath << " "
|
|
<< InputFilePath << "\n";
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (auto Symbol : TargetStub->Symbols) {
|
|
auto [SI, Inserted] = SymbolMap.try_emplace(Symbol.Name, Symbol);
|
|
if (Inserted)
|
|
continue;
|
|
|
|
assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match.");
|
|
|
|
// Check conflicts:
|
|
if (Symbol.Type != SI->second.Type) {
|
|
WithColor::error() << "Interface Stub: Type Mismatch for "
|
|
<< Symbol.Name << ".\nFilename: " << InputFilePath
|
|
<< "\nType Values: " << getTypeName(SI->second.Type)
|
|
<< " " << getTypeName(Symbol.Type) << "\n";
|
|
|
|
return -1;
|
|
}
|
|
if (Symbol.Size != SI->second.Size) {
|
|
WithColor::error() << "Interface Stub: Size Mismatch for "
|
|
<< Symbol.Name << ".\nFilename: " << InputFilePath
|
|
<< "\nSize Values: " << SI->second.Size << " "
|
|
<< Symbol.Size << "\n";
|
|
|
|
return -1;
|
|
}
|
|
if (Symbol.Weak != SI->second.Weak) {
|
|
Symbol.Weak = false;
|
|
continue;
|
|
}
|
|
// TODO: Not checking Warning. Will be dropped.
|
|
}
|
|
|
|
PreviousInputFilePath = InputFilePath;
|
|
}
|
|
|
|
if (Stub.IfsVersion != IfsVersionCurrent)
|
|
if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
|
|
WithColor::error() << "Interface Stub: Bad IfsVersion: "
|
|
<< Stub.IfsVersion << ", llvm-ifs supported version: "
|
|
<< IfsVersionCurrent << ".\n";
|
|
return -1;
|
|
}
|
|
|
|
for (auto &Entry : SymbolMap)
|
|
Stub.Symbols.push_back(Entry.second);
|
|
|
|
// Change SoName before emitting stubs.
|
|
if (Config.SoName)
|
|
Stub.SoName = *Config.SoName;
|
|
|
|
Error OverrideError =
|
|
overrideIFSTarget(Stub, Config.OverrideArch, Config.OverrideEndianness,
|
|
Config.OverrideBitWidth, Config.OptTargetTriple);
|
|
if (OverrideError)
|
|
fatalError(std::move(OverrideError));
|
|
|
|
if (Config.StripNeeded)
|
|
Stub.NeededLibs.clear();
|
|
|
|
if (Error E = filterIFSSyms(Stub, Config.StripUndefined, Config.Exclude))
|
|
fatalError(std::move(E));
|
|
|
|
if (Config.StripSize)
|
|
for (IFSSymbol &Sym : Stub.Symbols)
|
|
Sym.Size.reset();
|
|
|
|
if (!Config.OutputElf && !Config.OutputIfs && !Config.OutputTbd) {
|
|
if (!Config.OutputFormat) {
|
|
WithColor::error() << "at least one output should be specified.";
|
|
return -1;
|
|
}
|
|
} else if (Config.OutputFormat) {
|
|
WithColor::error() << "'--output-format' cannot be used with "
|
|
"'--output-{FILE_FORMAT}' options at the same time";
|
|
return -1;
|
|
}
|
|
if (Config.OutputFormat) {
|
|
// TODO: Remove OutputFormat flag in the next revision.
|
|
WithColor::warning() << "--output-format option is deprecated, please use "
|
|
"--output-{FILE_FORMAT} options instead\n";
|
|
switch (*Config.OutputFormat) {
|
|
case FileFormat::TBD: {
|
|
std::error_code SysErr;
|
|
raw_fd_ostream Out(*Config.Output, SysErr);
|
|
if (SysErr) {
|
|
WithColor::error() << "Couldn't open " << *Config.Output
|
|
<< " for writing.\n";
|
|
return -1;
|
|
}
|
|
if (!Stub.Target.Triple) {
|
|
WithColor::error()
|
|
<< "Triple should be defined when output format is TBD";
|
|
return -1;
|
|
}
|
|
return writeTbdStub(llvm::Triple(*Stub.Target.Triple), Stub.Symbols,
|
|
"TBD", Out);
|
|
}
|
|
case FileFormat::IFS: {
|
|
Stub.IfsVersion = IfsVersionCurrent;
|
|
if (*Config.InputFormat == FileFormat::ELF && Config.HintIfsTarget) {
|
|
std::error_code HintEC(1, std::generic_category());
|
|
IFSTarget HintTarget = parseTriple(*Config.HintIfsTarget);
|
|
if (*Stub.Target.Arch != *HintTarget.Arch)
|
|
fatalError(make_error<StringError>(
|
|
"Triple hint does not match the actual architecture", HintEC));
|
|
if (*Stub.Target.Endianness != *HintTarget.Endianness)
|
|
fatalError(make_error<StringError>(
|
|
"Triple hint does not match the actual endianness", HintEC));
|
|
if (*Stub.Target.BitWidth != *HintTarget.BitWidth)
|
|
fatalError(make_error<StringError>(
|
|
"Triple hint does not match the actual bit width", HintEC));
|
|
|
|
stripIFSTarget(Stub, true, false, false, false);
|
|
Stub.Target.Triple = *Config.HintIfsTarget;
|
|
} else {
|
|
stripIFSTarget(Stub, Config.StripIfsTarget, Config.StripIfsArch,
|
|
Config.StripIfsEndianness, Config.StripIfsBitwidth);
|
|
}
|
|
Error IFSWriteError =
|
|
writeIFS(*Config.Output, Stub, Config.WriteIfChanged);
|
|
if (IFSWriteError)
|
|
fatalError(std::move(IFSWriteError));
|
|
break;
|
|
}
|
|
case FileFormat::ELF: {
|
|
Error TargetError = validateIFSTarget(Stub, true);
|
|
if (TargetError)
|
|
fatalError(std::move(TargetError));
|
|
Error BinaryWriteError =
|
|
writeBinaryStub(*Config.Output, Stub, Config.WriteIfChanged);
|
|
if (BinaryWriteError)
|
|
fatalError(std::move(BinaryWriteError));
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// Check if output path for individual format.
|
|
if (Config.OutputElf) {
|
|
Error TargetError = validateIFSTarget(Stub, true);
|
|
if (TargetError)
|
|
fatalError(std::move(TargetError));
|
|
Error BinaryWriteError =
|
|
writeBinaryStub(*Config.OutputElf, Stub, Config.WriteIfChanged);
|
|
if (BinaryWriteError)
|
|
fatalError(std::move(BinaryWriteError));
|
|
}
|
|
if (Config.OutputIfs) {
|
|
Stub.IfsVersion = IfsVersionCurrent;
|
|
if (*Config.InputFormat == FileFormat::ELF && Config.HintIfsTarget) {
|
|
std::error_code HintEC(1, std::generic_category());
|
|
IFSTarget HintTarget = parseTriple(*Config.HintIfsTarget);
|
|
if (*Stub.Target.Arch != *HintTarget.Arch)
|
|
fatalError(make_error<StringError>(
|
|
"Triple hint does not match the actual architecture", HintEC));
|
|
if (*Stub.Target.Endianness != *HintTarget.Endianness)
|
|
fatalError(make_error<StringError>(
|
|
"Triple hint does not match the actual endianness", HintEC));
|
|
if (*Stub.Target.BitWidth != *HintTarget.BitWidth)
|
|
fatalError(make_error<StringError>(
|
|
"Triple hint does not match the actual bit width", HintEC));
|
|
|
|
stripIFSTarget(Stub, true, false, false, false);
|
|
Stub.Target.Triple = *Config.HintIfsTarget;
|
|
} else {
|
|
stripIFSTarget(Stub, Config.StripIfsTarget, Config.StripIfsArch,
|
|
Config.StripIfsEndianness, Config.StripIfsBitwidth);
|
|
}
|
|
Error IFSWriteError =
|
|
writeIFS(*Config.OutputIfs, Stub, Config.WriteIfChanged);
|
|
if (IFSWriteError)
|
|
fatalError(std::move(IFSWriteError));
|
|
}
|
|
if (Config.OutputTbd) {
|
|
std::error_code SysErr;
|
|
raw_fd_ostream Out(*Config.OutputTbd, SysErr);
|
|
if (SysErr) {
|
|
WithColor::error() << "Couldn't open " << *Config.OutputTbd
|
|
<< " for writing.\n";
|
|
return -1;
|
|
}
|
|
if (!Stub.Target.Triple) {
|
|
WithColor::error()
|
|
<< "Triple should be defined when output format is TBD";
|
|
return -1;
|
|
}
|
|
return writeTbdStub(llvm::Triple(*Stub.Target.Triple), Stub.Symbols,
|
|
"TBD", Out);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|