Index: llvm/test/tools/llvm-objcopy/ELF/binary-input-and-output.test =================================================================== --- llvm/test/tools/llvm-objcopy/ELF/binary-input-and-output.test +++ llvm/test/tools/llvm-objcopy/ELF/binary-input-and-output.test @@ -9,7 +9,7 @@ # RUN: cmp %t.txt %t-copy.txt # -I binary -O binary preserves payload through an intermediate object file -# RUN: llvm-objcopy -I binary -B i386:x86-64 %t.txt %t.o +# RUN: llvm-objcopy -I binary -B x86-64 -O elf64-x86-64 %t.txt %t.o # RUN: llvm-objcopy -O binary %t.o %t.3.txt # RUN: cmp %t.txt %t.3.txt # RUN: cmp %t.txt %t-copy.txt Index: llvm/test/tools/llvm-objcopy/ELF/binary-input-arch.test =================================================================== --- llvm/test/tools/llvm-objcopy/ELF/binary-input-arch.test +++ llvm/test/tools/llvm-objcopy/ELF/binary-input-arch.test @@ -1,33 +1,33 @@ # RUN: echo abcd > %t.txt -# RUN: llvm-objcopy -I binary -B aarch64 %t.txt %t.aarch64.o +# RUN: llvm-objcopy -I binary -B aarch64 -O elf64-aarch64 %t.txt %t.aarch64.o # RUN: llvm-readobj --file-headers %t.aarch64.o | FileCheck %s --check-prefixes=CHECK,LE,AARCH64,64 -# RUN: llvm-objcopy -I binary -B arm %t.txt %t.arm.o +# RUN: llvm-objcopy -I binary -B arm -O elf32-littlearm %t.txt %t.arm.o # RUN: llvm-readobj --file-headers %t.arm.o | FileCheck %s --check-prefixes=CHECK,LE,ARM,32 -# RUN: llvm-objcopy -I binary -B i386 %t.txt %t.i386.o +# RUN: llvm-objcopy -I binary -B i386 -O elf32-i386 %t.txt %t.i386.o # RUN: llvm-readobj --file-headers %t.i386.o | FileCheck %s --check-prefixes=CHECK,LE,I386,32 -# RUN: llvm-objcopy -I binary -B i386:x86-64 %t.txt %t.i386_x86-64.o +# RUN: llvm-objcopy -I binary -B i386:x86-64 -O elf64-x86-64 %t.txt %t.i386_x86-64.o # RUN: llvm-readobj --file-headers %t.i386_x86-64.o | FileCheck %s --check-prefixes=CHECK,LE,X86-64,64 -# RUN: llvm-objcopy -I binary -B mips %t.txt %t.mips.o +# RUN: llvm-objcopy -I binary -B mips -O elf32-bigmips %t.txt %t.mips.o # RUN: llvm-readobj --file-headers %t.mips.o | FileCheck %s --check-prefixes=CHECK,BE,MIPS,32 -# RUN: llvm-objcopy -I binary -B powerpc:common64 %t.txt %t.powerpc_common64.o +# RUN: llvm-objcopy -I binary -B powerpc:common64 -O elf64-powerpcle %t.txt %t.powerpc_common64.o # RUN: llvm-readobj --file-headers %t.powerpc_common64.o | FileCheck %s --check-prefixes=CHECK,LE,PPC,64 -# RUN: llvm-objcopy -I binary -B riscv:rv32 %t.txt %t.rv32.o +# RUN: llvm-objcopy -I binary -B riscv:rv32 -O elf32-littleriscv %t.txt %t.rv32.o # RUN: llvm-readobj --file-headers %t.rv32.o | FileCheck %s --check-prefixes=CHECK,LE,RISCV32,32 -# RUN: llvm-objcopy -I binary -B riscv:rv64 %t.txt %t.rv64.o +# RUN: llvm-objcopy -I binary -B riscv:rv64 -O elf64-littleriscv %t.txt %t.rv64.o # RUN: llvm-readobj --file-headers %t.rv64.o | FileCheck %s --check-prefixes=CHECK,LE,RISCV64,64 -# RUN: llvm-objcopy -I binary -B sparc %t.txt %t.sparc.o +# RUN: llvm-objcopy -I binary -B sparc -O elf32-sparc %t.txt %t.sparc.o # RUN: llvm-readobj --file-headers %t.sparc.o | FileCheck %s --check-prefixes=CHECK,LE,SPARC,32 -# RUN: llvm-objcopy -I binary -B x86-64 %t.txt %t.x86-64.o +# RUN: llvm-objcopy -I binary -B x86-64 -O elf64-x86-64 %t.txt %t.x86-64.o # RUN: llvm-readobj --file-headers %t.x86-64.o | FileCheck %s --check-prefixes=CHECK,LE,X86-64,64 # CHECK: Format: Index: llvm/test/tools/llvm-objcopy/ELF/binary-input-error.test =================================================================== --- llvm/test/tools/llvm-objcopy/ELF/binary-input-error.test +++ llvm/test/tools/llvm-objcopy/ELF/binary-input-error.test @@ -6,5 +6,10 @@ # RUN: not llvm-objcopy -I binary -B xyz %t.txt %t.o 2>&1 \ # RUN: | FileCheck %s --check-prefix=BAD-BINARY-ARCH +# RUN: not llvm-objcopy -I binary -B i386 %t.txt %t.o 2>&1 \ +# RUN: | FileCheck %s --check-prefix=MISSING-OUTPUT-FORMAT + # MISSING-BINARY-ARCH: Specified binary input without specifiying an architecture # BAD-BINARY-ARCH: Invalid architecture: 'xyz' + +# MISSING-OUTPUT-FORMAT: specified binary input without specifying the output file format \ No newline at end of file Index: llvm/test/tools/llvm-objcopy/ELF/binary-input.test =================================================================== --- llvm/test/tools/llvm-objcopy/ELF/binary-input.test +++ llvm/test/tools/llvm-objcopy/ELF/binary-input.test @@ -1,7 +1,7 @@ # RUN: echo -n abcd > %t.x-txt # Preserve input to verify it is not modified # RUN: cp %t.x-txt %t-copy.txt -# RUN: llvm-objcopy -I binary -B i386:x86-64 %t.x-txt %t.o +# RUN: llvm-objcopy -I binary -B i386:x86-64 -O elf64-x86-64 %t.x-txt %t.o # RUN: llvm-readobj --sections --symbols %t.o | FileCheck %s # RUN: cmp %t.x-txt %t-copy.txt Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -26,17 +26,22 @@ 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. struct MachineInfo { MachineInfo(uint16_t EM, uint8_t ABI, bool Is64, bool IsLittle) : EMachine(EM), OSABI(ABI), Is64Bit(Is64), IsLittleEndian(IsLittle) {} - // Alternative constructor that defaults to NONE for OSABI. - MachineInfo(uint16_t EM, bool Is64, bool IsLittle) - : MachineInfo(EM, ELF::ELFOSABI_NONE, Is64, IsLittle) {} // Default constructor for unset fields. MachineInfo() : MachineInfo(0, 0, false, false) {} + uint16_t EMachine; uint8_t OSABI; bool Is64Bit; @@ -104,9 +109,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 @@ -254,20 +254,21 @@ } static const StringMap ArchMap{ - // Name, {EMachine, 64bit, LittleEndian} - {"aarch64", {ELF::EM_AARCH64, true, true}}, - {"arm", {ELF::EM_ARM, false, true}}, - {"i386", {ELF::EM_386, false, true}}, - {"i386:x86-64", {ELF::EM_X86_64, true, true}}, - {"mips", {ELF::EM_MIPS, false, false}}, - {"powerpc:common64", {ELF::EM_PPC64, true, true}}, - {"riscv:rv32", {ELF::EM_RISCV, false, true}}, - {"riscv:rv64", {ELF::EM_RISCV, true, true}}, - {"sparc", {ELF::EM_SPARC, false, true}}, - {"x86-64", {ELF::EM_X86_64, true, true}}, + // Name, {EMachine, OSABI, MachOCPUType, MachOCPUSubType, 64bit, + // LittleEndian} + {"aarch64", {ELF::EM_AARCH64, ELF::ELFOSABI_NONE, true, true}}, + {"arm", {ELF::EM_ARM, ELF::ELFOSABI_NONE, false, true}}, + {"i386", {ELF::EM_386, ELF::ELFOSABI_NONE, false, true}}, + {"i386:x86-64", {ELF::EM_X86_64, ELF::ELFOSABI_NONE, true, true}}, + {"mips", {ELF::EM_MIPS, ELF::ELFOSABI_NONE, false, false}}, + {"powerpc:common64", {ELF::EM_PPC64, ELF::ELFOSABI_NONE, true, true}}, + {"riscv:rv32", {ELF::EM_RISCV, ELF::ELFOSABI_NONE, false, true}}, + {"riscv:rv64", {ELF::EM_RISCV, ELF::ELFOSABI_NONE, true, true}}, + {"sparc", {ELF::EM_SPARC, ELF::ELFOSABI_NONE, false, true}}, + {"x86-64", {ELF::EM_X86_64, ELF::ELFOSABI_NONE, true, true}}, }; -static Expected getMachineInfo(StringRef Arch) { +static Expected getMachineInfoByArchName(StringRef Arch) { auto Iter = ArchMap.find(Arch); if (Iter == std::end(ArchMap)) return createStringError(errc::invalid_argument, @@ -276,49 +277,74 @@ } // FIXME: consolidate with the bfd parsing used by lld. -static const StringMap OutputFormatMap{ - // Name, {EMachine, 64bit, LittleEndian} +static const StringMap> FileFormatMap{ + // Name, {FileFormat, {EMachine, OSABI, MachOCPUType, MachOCPUSubType, + // 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, ELF::ELFOSABI_NONE, false, true}}}, + {"elf32-x86-64", + {FileFormat::ELF, {ELF::EM_X86_64, ELF::ELFOSABI_NONE, false, true}}}, + {"elf64-x86-64", + {FileFormat::ELF, {ELF::EM_X86_64, ELF::ELFOSABI_NONE, true, true}}}, // Intel MCU - {"elf32-iamcu", {ELF::EM_IAMCU, false, true}}, + {"elf32-iamcu", + {FileFormat::ELF, {ELF::EM_IAMCU, ELF::ELFOSABI_NONE, false, true}}}, // ARM - {"elf32-littlearm", {ELF::EM_ARM, false, true}}, + {"elf32-littlearm", + {FileFormat::ELF, {ELF::EM_ARM, ELF::ELFOSABI_NONE, 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, ELF::ELFOSABI_NONE, true, true}}}, + {"elf64-littleaarch64", + {FileFormat::ELF, {ELF::EM_AARCH64, ELF::ELFOSABI_NONE, 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, ELF::ELFOSABI_NONE, false, true}}}, + {"elf64-littleriscv", + {FileFormat::ELF, {ELF::EM_RISCV, ELF::ELFOSABI_NONE, 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, ELF::ELFOSABI_NONE, false, false}}}, + {"elf32-powerpcle", + {FileFormat::ELF, {ELF::EM_PPC, ELF::ELFOSABI_NONE, false, true}}}, + {"elf64-powerpc", + {FileFormat::ELF, {ELF::EM_PPC64, ELF::ELFOSABI_NONE, true, false}}}, + {"elf64-powerpcle", + {FileFormat::ELF, {ELF::EM_PPC64, ELF::ELFOSABI_NONE, 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, ELF::ELFOSABI_NONE, false, false}}}, + {"elf32-ntradbigmips", + {FileFormat::ELF, {ELF::EM_MIPS, ELF::ELFOSABI_NONE, false, false}}}, + {"elf32-ntradlittlemips", + {FileFormat::ELF, {ELF::EM_MIPS, ELF::ELFOSABI_NONE, false, true}}}, + {"elf32-tradbigmips", + {FileFormat::ELF, {ELF::EM_MIPS, ELF::ELFOSABI_NONE, false, false}}}, + {"elf32-tradlittlemips", + {FileFormat::ELF, {ELF::EM_MIPS, ELF::ELFOSABI_NONE, false, true}}}, + {"elf64-tradbigmips", + {FileFormat::ELF, {ELF::EM_MIPS, ELF::ELFOSABI_NONE, true, false}}}, + {"elf64-tradlittlemips", + {FileFormat::ELF, {ELF::EM_MIPS, ELF::ELFOSABI_NONE, true, true}}}, + // SPARC + {"elf32-sparc", + {FileFormat::ELF, {ELF::EM_SPARC, ELF::ELFOSABI_NONE, false, true}}}, }; -static Expected getOutputFormatMachineInfo(StringRef Format) { +static Expected> +getFileFormatInfoByFormatName(StringRef Format) { StringRef OriginalFormat = Format; bool IsFreeBSD = Format.consume_back("-freebsd"); - auto Iter = OutputFormatMap.find(Format); - if (Iter == std::end(OutputFormatMap)) + auto Iter = FileFormatMap.find(Format); + if (Iter == std::end(FileFormatMap)) return createStringError(errc::invalid_argument, "Invalid output format: '%s'", OriginalFormat.str().c_str()); - MachineInfo MI = Iter->getValue(); + auto Pair = Iter->getValue(); if (IsFreeBSD) - MI.OSABI = ELF::ELFOSABI_FREEBSD; - return {MI}; + Pair.second.OSABI = ELF::ELFOSABI_FREEBSD; + return {Pair}; } static Error addSymbolsFromFile(std::vector &Symbols, @@ -440,30 +466,45 @@ "--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 == "binary") { + Config.InputFormat = FileFormat::Binary; auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); if (BinaryArch.empty()) return createStringError( errc::invalid_argument, "Specified binary input without specifiying an architecture"); - Expected MI = getMachineInfo(BinaryArch); + Expected MI = getMachineInfoByArchName(BinaryArch); 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> Pair = + getFileFormatInfoByFormatName(OutputFormat); + if (!Pair) + return Pair.takeError(); + Config.OutputFormat = Pair->first; + Config.OutputArch = Pair->second; } 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 @@ -29,6 +29,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" @@ -127,11 +128,19 @@ /// 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 use BinaryWriter/IHexWriter in the ELF implementation + // to convert a binary input into a binary/ihex output. Therefore, unlike GNU + // objcopy, it requires explicitly specifying the architecture by the -B + // option. + case FileFormat::Binary: + case FileFormat::IHex: + return elf::executeObjcopyOnRawBinary(Config, In, Out); + case FileFormat::Unspecified: + return createStringError(llvm::errc::invalid_argument, + "specified binary input without specifying the output file format"); + } } /// The function executeObjcopyOnBinary does the dispatch based on the format @@ -210,7 +219,7 @@ if (auto EC = sys::fs::status(Config.InputFilename, Stat)) return createFileError(Config.InputFilename, EC); - if (Config.InputFormat == "binary") { + if (Config.InputFormat == FileFormat::Binary) { auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename); if (!BufOrErr) return createFileError(Config.InputFilename, BufOrErr.getError());