Skip to content

Commit

Permalink
[llvm-objcopy][NFC] Refactor output target parsing
Browse files Browse the repository at this point in the history
Summary:
Use an enum instead of string to hold the output file format in Config.InputFormat and Config.OutputFormat. It's essential to support other output file formats other than ELF.

Reviewers: espindola, alexshap, rupprecht, jhenderson

Reviewed By: rupprecht, jhenderson

Subscribers: jyknight, compnerd, emaste, arichardson, fedor.sergeev, jakehehrlich, MaskRay, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D63239

llvm-svn: 364254
  • Loading branch information
nuta committed Jun 25, 2019
1 parent f895e1b commit 545f001
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 36 deletions.
67 changes: 48 additions & 19 deletions llvm/tools/llvm-objcopy/CopyConfig.cpp
Expand Up @@ -277,8 +277,13 @@ static Expected<const MachineInfo &> getMachineInfo(StringRef Arch) {
return Iter->getValue();
}

struct TargetInfo {
FileFormat Format;
MachineInfo Machine;
};

// FIXME: consolidate with the bfd parsing used by lld.
static const StringMap<MachineInfo> OutputFormatMap{
static const StringMap<MachineInfo> TargetMap{
// Name, {EMachine, 64bit, LittleEndian}
// x86
{"elf32-i386", {ELF::EM_386, false, true}},
Expand Down Expand Up @@ -312,18 +317,28 @@ static const StringMap<MachineInfo> OutputFormatMap{
{"elf32-sparcel", {ELF::EM_SPARC, false, true}},
};

static Expected<MachineInfo> getOutputFormatMachineInfo(StringRef Format) {
StringRef OriginalFormat = Format;
bool IsFreeBSD = Format.consume_back("-freebsd");
auto Iter = OutputFormatMap.find(Format);
if (Iter == std::end(OutputFormatMap))
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'",
OriginalFormat.str().c_str());
OriginalTargetName.str().c_str());
MachineInfo MI = Iter->getValue();
if (IsFreeBSD)
MI.OSABI = ELF::ELFOSABI_FREEBSD;
return {MI};

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(std::vector<NameOrRegex> &Symbols,
Expand Down Expand Up @@ -445,14 +460,23 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
"--target cannot be used with --input-target or --output-target");

bool UseRegex = InputArgs.hasArg(OBJCOPY_regex);
StringRef InputFormat, OutputFormat;
if (InputArgs.hasArg(OBJCOPY_target)) {
Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
InputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
} else {
Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target);
Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target);
InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target);
OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target);
}
if (Config.InputFormat == "binary") {

// 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 (Config.InputFormat == FileFormat::Binary) {
auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture);
if (BinaryArch.empty())
return createStringError(
Expand All @@ -463,12 +487,17 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
return MI.takeError();
Config.BinaryArch = *MI;
}
if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary" &&
Config.OutputFormat != "ihex") {
Expected<MachineInfo> MI = getOutputFormatMachineInfo(Config.OutputFormat);
if (!MI)
return MI.takeError();
Config.OutputArch = *MI;

Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
.Case("binary", FileFormat::Binary)
.Case("ihex", FileFormat::IHex)
.Default(FileFormat::Unspecified);
if (Config.OutputFormat == FileFormat::Unspecified && !OutputFormat.empty()) {
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,
Expand Down
11 changes: 9 additions & 2 deletions llvm/tools/llvm-objcopy/CopyConfig.h
Expand Up @@ -26,6 +26,13 @@
namespace llvm {
namespace objcopy {

enum class FileFormat {
Unspecified,
ELF,
Binary,
IHex,
};

// This type keeps track of the machine info for various architectures. This
// lets us map architecture names to ELF types and the e_machine value of the
// ELF file.
Expand Down Expand Up @@ -104,9 +111,9 @@ struct NewSymbolInfo {
struct CopyConfig {
// Main input/output options
StringRef InputFilename;
StringRef InputFormat;
FileFormat InputFormat;
StringRef OutputFilename;
StringRef OutputFormat;
FileFormat OutputFormat;

// Only applicable for --input-format=binary
MachineInfo BinaryArch;
Expand Down
14 changes: 8 additions & 6 deletions llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
Expand Up @@ -154,12 +154,14 @@ static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config,
static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
Object &Obj, Buffer &Buf,
ElfType OutputElfType) {
using Functor = std::function<std::unique_ptr<Writer>()>;
return StringSwitch<Functor>(Config.OutputFormat)
.Case("binary", [&] { return llvm::make_unique<BinaryWriter>(Obj, Buf); })
.Case("ihex", [&] { return llvm::make_unique<IHexWriter>(Obj, Buf); })
.Default(
[&] { return createELFWriter(Config, Obj, Buf, OutputElfType); })();
switch (Config.OutputFormat) {
case FileFormat::Binary:
return llvm::make_unique<BinaryWriter>(Obj, Buf);
case FileFormat::IHex:
return llvm::make_unique<IHexWriter>(Obj, Buf);
default:
return createELFWriter(Config, Obj, Buf, OutputElfType);
}
}

template <class ELFT>
Expand Down
30 changes: 21 additions & 9 deletions llvm/tools/llvm-objcopy/llvm-objcopy.cpp
Expand Up @@ -140,11 +140,16 @@ static Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In,
/// of the output specified by the command line options.
static Error executeObjcopyOnRawBinary(const CopyConfig &Config,
MemoryBuffer &In, Buffer &Out) {
// TODO: llvm-objcopy should parse CopyConfig.OutputFormat to recognize
// formats other than ELF / "binary" and invoke
// elf::executeObjcopyOnRawBinary, macho::executeObjcopyOnRawBinary or
// coff::executeObjcopyOnRawBinary accordingly.
return elf::executeObjcopyOnRawBinary(Config, In, Out);
switch (Config.OutputFormat) {
case FileFormat::ELF:
// FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the
// output format is binary/ihex or it's not given. This behavior differs from
// GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details.
case FileFormat::Binary:
case FileFormat::IHex:
case FileFormat::Unspecified:
return elf::executeObjcopyOnRawBinary(Config, In, Out);
}
}

/// The function executeObjcopyOnBinary does the dispatch based on the format
Expand Down Expand Up @@ -224,10 +229,17 @@ static Error executeObjcopy(const CopyConfig &Config) {
return createFileError(Config.InputFilename, EC);

typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &);
auto ProcessRaw = StringSwitch<ProcessRawFn>(Config.InputFormat)
.Case("binary", executeObjcopyOnRawBinary)
.Case("ihex", executeObjcopyOnIHex)
.Default(nullptr);
ProcessRawFn ProcessRaw;
switch (Config.InputFormat) {
case FileFormat::Binary:
ProcessRaw = executeObjcopyOnRawBinary;
break;
case FileFormat::IHex:
ProcessRaw = executeObjcopyOnIHex;
break;
default:
ProcessRaw = nullptr;
}

if (ProcessRaw) {
auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename);
Expand Down

0 comments on commit 545f001

Please sign in to comment.