Index: test/tools/llvm-objcopy/ELF/bad-output-format.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/ELF/bad-output-format.test @@ -0,0 +1,13 @@ +# RUN: yaml2obj %s > %t.o + +# RUN: not llvm-objcopy -O xyz %t.o %t.2.o 2>&1 \ +# RUN: | FileCheck %s --check-prefix=BAD-OUTPUT-FORMAT + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 + +# BAD-OUTPUT-FORMAT: Invalid output format: 'xyz'. Index: test/tools/llvm-objcopy/ELF/binary-input-with-arch.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/ELF/binary-input-with-arch.test @@ -0,0 +1,20 @@ +# 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 -O elf64-x86-64 %t.x-txt %t.o +# RUN: llvm-readobj --file-headers %t.o | FileCheck %s +# RUN: cmp %t.x-txt %t-copy.txt + +# Many uses of objcopy use no spaces in the flags, make sure that also works. +# RUN: llvm-objcopy -Ibinary -Bi386 -Oelf64-x86-64 %t.x-txt %t-no-spaces.o +# RUN: cmp %t.o %t-no-spaces.o + +# CHECK: Format: ELF64-x86-64 +# CHECK-NEXT: Arch: x86_64 +# CHECK-NEXT: AddressSize: 64bit + +# CHECK: Class: 64-bit +# CHECK: DataEncoding: LittleEndian +# CHECK: Machine: EM_X86_64 +# CHECK: HeaderSize: 64 +# CHECK: SectionHeaderEntrySize: 64 Index: test/tools/llvm-objcopy/ELF/cross-arch-headers.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/ELF/cross-arch-headers.test @@ -0,0 +1,71 @@ +# RUN: yaml2obj %s > %t.o + +# RUN: llvm-objcopy %t.o -O elf32-i386 %t.elf32_i386.o +# RUN: llvm-readobj --file-headers %t.elf32_i386.o | FileCheck %s --check-prefixes=CHECK,I386,32 + +# RUN: llvm-objcopy %t.o -O elf32-powerpcle %t.elf32_ppcle.o +# RUN: llvm-readobj --file-headers %t.elf32_ppcle.o | FileCheck %s --check-prefixes=CHECK,PPC,32 + +# RUN: llvm-objcopy %t.o -O elf32-x86-64 %t.elf32_x86_64.o +# RUN: llvm-readobj --file-headers %t.elf32_x86_64.o | FileCheck %s --check-prefixes=CHECK,X86-64,32 + +# RUN: llvm-objcopy %t.o -O elf64-powerpcle %t.elf64_ppcle.o +# RUN: llvm-readobj --file-headers %t.elf64_ppcle.o | FileCheck %s --check-prefixes=CHECK,PPC64,64 + +# RUN: llvm-objcopy %t.o -O elf64-x86-64 %t.elf64_x86_64.o +# RUN: llvm-readobj --file-headers %t.elf64_x86_64.o | FileCheck %s --check-prefixes=CHECK,X86-64,64 + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] +Symbols: + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x1234 + - Name: bar + Type: STT_OBJECT + Section: .data + Value: 0xabcd + +# CHECK: Format: +# 32-SAME: ELF32- +# 64-SAME: ELF64- +# I386-SAME: i386 +# PPC-SAME: ppc +# PPC64-SAME: ppc64 +# X86-64-SAME: x86-64 + +# I386-NEXT: Arch: i386 +# PPC-NEXT: Arch: powerpc +# PPC64-NEXT: Arch: powerpc64le +# X86-64-NEXT: Arch: x86_64 + +# 32-NEXT: AddressSize: 32bit +# 64-NEXT: AddressSize: 64bit + +# 32: Class: 32-bit +# 64: Class: 64-bit +# CHECK: DataEncoding: LittleEndian + +# I386: Machine: EM_386 +# PPC: Machine: EM_PPC +# PPC64: Machine: EM_PPC64 +# X86-64: Machine: EM_X86_64 + +# 32: HeaderSize: 52 +# 64: HeaderSize: 64 + +# 32: SectionHeaderEntrySize: 40 +# 64: SectionHeaderEntrySize: 64 Index: test/tools/llvm-objcopy/ELF/cross-arch-sections-symbols.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/ELF/cross-arch-sections-symbols.test @@ -0,0 +1,153 @@ +# RUN: yaml2obj %s > %t.o +# Preserve input to verify it is not modified. +# RUN: cp %t.o %t-copy.o +# RUN: llvm-objcopy %t.o -O elf64-x86-64 %t.2.o +# RUN: llvm-readobj --sections --symbols %t.2.o | FileCheck %s +# RUN: cmp %t.o %t-copy.o + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Size: 32 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: DEADBEEF + Size: 16 +Symbols: + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x1234 + Size: 8 + - Name: bar + Type: STT_OBJECT + Section: .data + Size: 16 + +# CHECK: Sections [ +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: Name: (0) +# CHECK-NEXT: Type: SHT_NULL (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 0 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: Name: .text +# CHECK-NEXT: Type: SHT_PROGBITS (0x1) +# CHECK-NEXT: Flags [ (0x6) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_EXECINSTR (0x4) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: 32 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 0 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 2 +# CHECK-NEXT: Name: .data +# CHECK-NEXT: Type: SHT_PROGBITS (0x1) +# CHECK-NEXT: Flags [ (0x2) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: 16 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 0 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 3 +# CHECK-NEXT: Name: .symtab +# CHECK-NEXT: Type: SHT_SYMTAB (0x2) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: 72 +# CHECK-NEXT: Link: 4 +# CHECK-NEXT: Info: 1 +# CHECK-NEXT: AddressAlignment: 8 +# CHECK-NEXT: EntrySize: 24 +# CHECK-NEXT: } +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 4 +# CHECK-NEXT: Name: .strtab +# CHECK-NEXT: Type: SHT_STRTAB (0x3) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: 10 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 1 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 5 +# CHECK-NEXT: Name: .shstrtab +# CHECK-NEXT: Type: SHT_STRTAB (0x3) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: 39 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 1 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: foo +# CHECK-NEXT: Value: 0x1234 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: bar +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 16 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .data +# CHECK-NEXT: } +# CHECK-NEXT: ] Index: tools/llvm-objcopy/CopyConfig.h =================================================================== --- tools/llvm-objcopy/CopyConfig.h +++ tools/llvm-objcopy/CopyConfig.h @@ -46,8 +46,10 @@ StringRef OutputFilename; StringRef OutputFormat; - // Only applicable for --input-format=Binary + // Only applicable for --input-format=binary MachineInfo BinaryArch; + // Only applicable when --output-format!=binary (e.g. elf64-x86-64). + Optional OutputArch; // Advanced options StringRef AddGnuDebugLink; Index: tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- tools/llvm-objcopy/CopyConfig.cpp +++ tools/llvm-objcopy/CopyConfig.cpp @@ -189,6 +189,22 @@ return Iter->getValue(); } +static const StringMap OutputFormatMap{ + // Name, {EMachine, 64bit, LittleEndian} + {"elf32-i386", {ELF::EM_386, false, true}}, + {"elf32-powerpcle", {ELF::EM_PPC, false, true}}, + {"elf32-x86-64", {ELF::EM_X86_64, false, true}}, + {"elf64-powerpcle", {ELF::EM_PPC64, true, true}}, + {"elf64-x86-64", {ELF::EM_X86_64, true, true}}, +}; + +static const MachineInfo &getOutputFormatMachineInfo(StringRef Format) { + auto Iter = OutputFormatMap.find(Format); + if (Iter == std::end(OutputFormatMap)) + error("Invalid output format: '" + Format + "'"); + return Iter->getValue(); +} + static void addGlobalSymbolsFromFile(std::vector &Symbols, StringRef Filename) { SmallVector Lines; @@ -266,6 +282,8 @@ error("Specified binary input without specifiying an architecture"); Config.BinaryArch = getMachineInfo(BinaryArch); } + if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") + Config.OutputArch = getOutputFormatMachineInfo(Config.OutputFormat); if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, OBJCOPY_compress_debug_sections_eq)) { Index: tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -173,6 +173,8 @@ auto DWOFile = Reader.create(); DWOFile->removeSections( [&](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); }); + if (Config.OutputArch) + DWOFile->Machine = Config.OutputArch.getValue().EMachine; FileBuffer FB(File); auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType); Writer->finalize(); @@ -261,6 +263,8 @@ if (!Config.SplitDWO.empty()) { splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); } + if (Config.OutputArch) + Obj.Machine = Config.OutputArch.getValue().EMachine; // TODO: update or remove symbols only if there is an option that affects // them. @@ -528,7 +532,10 @@ BinaryReader Reader(Config.BinaryArch, &In); std::unique_ptr Obj = Reader.create(); - const ElfType OutputElfType = getOutputElfType(Config.BinaryArch); + // Prefer OutputArch (-O) if set, otherwise fallback to BinaryArch + // (-B). + const ElfType OutputElfType = getOutputElfType( + Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch); handleArgs(Config, *Obj, Reader, OutputElfType); std::unique_ptr Writer = createWriter(Config, *Obj, Out, OutputElfType); @@ -540,7 +547,10 @@ object::ELFObjectFileBase &In, Buffer &Out) { ELFReader Reader(&In); std::unique_ptr Obj = Reader.create(); - const ElfType OutputElfType = getOutputElfType(In); + // Prefer OutputArch (-O) if set, otherwise infer it from the input. + const ElfType OutputElfType = + Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) + : getOutputElfType(In); ArrayRef BuildIdBytes; if (!Config.BuildIdLinkDir.empty()) {