Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -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. @@ -104,9 +111,9 @@ 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; Index: llvm/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -276,50 +276,55 @@ return Iter->getValue(); } +struct TargetInfo { + FileFormat Format; + MachineInfo Machine; +}; + // FIXME: consolidate with the bfd parsing used by lld. -static const StringMap OutputFormatMap{ - // Name, {EMachine, 64bit, LittleEndian} +static const StringMap TargetMap{ + // Name, {FileFormat, {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}}, + {"elf32-i386", {FileFormat::ELF, {ELF::EM_386, false, true}}}, + {"elf32-x86-64", {FileFormat::ELF, {ELF::EM_X86_64, false, true}}}, + {"elf64-x86-64", {FileFormat::ELF, {ELF::EM_X86_64, true, true}}}, // Intel MCU - {"elf32-iamcu", {ELF::EM_IAMCU, false, true}}, + {"elf32-iamcu", {FileFormat::ELF, {ELF::EM_IAMCU, false, true}}}, // ARM - {"elf32-littlearm", {ELF::EM_ARM, false, true}}, + {"elf32-littlearm", {FileFormat::ELF, {ELF::EM_ARM, false, true}}}, // ARM AArch64 - {"elf64-aarch64", {ELF::EM_AARCH64, true, true}}, - {"elf64-littleaarch64", {ELF::EM_AARCH64, true, true}}, + {"elf64-aarch64", {FileFormat::ELF, {ELF::EM_AARCH64, true, true}}}, + {"elf64-littleaarch64", {FileFormat::ELF, {ELF::EM_AARCH64, true, true}}}, // RISC-V - {"elf32-littleriscv", {ELF::EM_RISCV, false, true}}, - {"elf64-littleriscv", {ELF::EM_RISCV, true, true}}, + {"elf32-littleriscv", {FileFormat::ELF, {ELF::EM_RISCV, false, true}}}, + {"elf64-littleriscv", {FileFormat::ELF, {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}}, + {"elf32-powerpc", {FileFormat::ELF, {ELF::EM_PPC, false, false}}}, + {"elf32-powerpcle", {FileFormat::ELF, {ELF::EM_PPC, false, true}}}, + {"elf64-powerpc", {FileFormat::ELF, {ELF::EM_PPC64, true, false}}}, + {"elf64-powerpcle", {FileFormat::ELF, {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}}, + {"elf32-bigmips", {FileFormat::ELF, {ELF::EM_MIPS, false, false}}}, + {"elf32-ntradbigmips", {FileFormat::ELF, {ELF::EM_MIPS, false, false}}}, + {"elf32-ntradlittlemips", {FileFormat::ELF, {ELF::EM_MIPS, false, true}}}, + {"elf32-tradbigmips", {FileFormat::ELF, {ELF::EM_MIPS, false, false}}}, + {"elf32-tradlittlemips", {FileFormat::ELF, {ELF::EM_MIPS, false, true}}}, + {"elf64-tradbigmips", {FileFormat::ELF, {ELF::EM_MIPS, true, false}}}, + {"elf64-tradlittlemips", {FileFormat::ELF, {ELF::EM_MIPS, true, true}}}, }; -static Expected getOutputFormatMachineInfo(StringRef Format) { +static Expected getTargetInfoByTargetName(StringRef Format) { StringRef OriginalFormat = Format; bool IsFreeBSD = Format.consume_back("-freebsd"); - auto Iter = OutputFormatMap.find(Format); - if (Iter == std::end(OutputFormatMap)) + auto Iter = TargetMap.find(Format); + if (Iter == std::end(TargetMap)) return createStringError(errc::invalid_argument, "Invalid output format: '%s'", OriginalFormat.str().c_str()); - MachineInfo MI = Iter->getValue(); + TargetInfo Target = Iter->getValue(); if (IsFreeBSD) - MI.OSABI = ELF::ELFOSABI_FREEBSD; - return {MI}; + Target.Machine.OSABI = ELF::ELFOSABI_FREEBSD; + return {Target}; } static Error addSymbolsFromFile(std::vector &Symbols, @@ -441,14 +446,20 @@ "--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") { + if (InputFormat.empty()) + Config.InputFormat = FileFormat::Unspecified; + else if (InputFormat == "ihex") + Config.InputFormat = FileFormat::IHex; + else if (InputFormat == "binary") { + Config.InputFormat = FileFormat::Binary; auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); if (BinaryArch.empty()) return createStringError( @@ -458,13 +469,23 @@ if (!MI) return MI.takeError(); Config.BinaryArch = *MI; - } - if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary" && - Config.OutputFormat != "ihex") { - Expected MI = getOutputFormatMachineInfo(Config.OutputFormat); - if (!MI) - return MI.takeError(); - Config.OutputArch = *MI; + } else + return createStringError(errc::invalid_argument, + "unsupported input format: '%s'", + InputFormat.str().c_str()); + + if (OutputFormat.empty()) + Config.OutputFormat = FileFormat::Unspecified; + else if (OutputFormat == "binary") + Config.OutputFormat = FileFormat::Binary; + else if (OutputFormat == "ihex") + Config.OutputFormat = FileFormat::IHex; + else { + Expected Target = getTargetInfoByTargetName(OutputFormat); + if (!Target) + return Target.takeError(); + Config.OutputFormat = Target->Format; + Config.OutputArch = Target->Machine; } if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -154,12 +154,14 @@ static std::unique_ptr createWriter(const CopyConfig &Config, Object &Obj, Buffer &Buf, ElfType OutputElfType) { - using Functor = std::function()>; - return StringSwitch(Config.OutputFormat) - .Case("binary", [&] { return llvm::make_unique(Obj, Buf); }) - .Case("ihex", [&] { return llvm::make_unique(Obj, Buf); }) - .Default( - [&] { return createELFWriter(Config, Obj, Buf, OutputElfType); })(); + switch (Config.OutputFormat) { + case FileFormat::Binary: + return llvm::make_unique(Obj, Buf); + case FileFormat::IHex: + return llvm::make_unique(Obj, Buf); + default: + return createELFWriter(Config, Obj, Buf, OutputElfType); + } } template Index: llvm/tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -135,11 +135,16 @@ /// 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 @@ -219,10 +224,17 @@ return createFileError(Config.InputFilename, EC); typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &); - auto ProcessRaw = StringSwitch(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);