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,36 @@ +# 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 + +# CHECK: Format: ELF64-x86-64 +# CHECK-NEXT: Arch: x86_64 +# CHECK-NEXT: AddressSize: 64bit +# CHECK-NEXT: LoadName: +# CHECK-NEXT: ElfHeader { +# CHECK-NEXT: Ident { +# CHECK-NEXT: Magic: (7F 45 4C 46) +# CHECK-NEXT: Class: 64-bit (0x2) +# CHECK-NEXT: DataEncoding: LittleEndian (0x1) +# CHECK-NEXT: FileVersion: 1 +# CHECK-NEXT: OS/ABI: SystemV (0x0) +# CHECK-NEXT: ABIVersion: 0 +# CHECK-NEXT: Unused: (00 00 00 00 00 00 00) +# CHECK-NEXT: } +# CHECK-NEXT: Type: Relocatable (0x1) +# CHECK-NEXT: Machine: EM_X86_64 (0x3E) +# CHECK-NEXT: Version: 1 +# CHECK-NEXT: Entry: +# CHECK-NEXT: ProgramHeaderOffset: +# CHECK-NEXT: SectionHeaderOffset: +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: HeaderSize: +# CHECK-NEXT: ProgramHeaderEntrySize: +# CHECK-NEXT: ProgramHeaderCount: +# CHECK-NEXT: SectionHeaderEntrySize: 64 +# CHECK-NEXT: SectionHeaderCount: +# CHECK-NEXT: StringTableSectionIndex: +# CHECK-NEXT: } Index: test/tools/llvm-objcopy/ELF/cross-arch-bad-format.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/ELF/cross-arch-bad-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 format: 'xyz'. Index: test/tools/llvm-objcopy/ELF/cross-arch-headers.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/ELF/cross-arch-headers.test @@ -0,0 +1,89 @@ +# RUN: yaml2obj %s > %t.o + +# RUN: llvm-objcopy %t.o -Oelf32-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 -Oelf32-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 -Oelf32-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 -Oelf64-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 -Oelf64-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 + +# CHECK: ElfHeader { +# CHECK-NEXT: Ident { +# CHECK-NEXT: Magic: (7F 45 4C 46) +# 32-NEXT: Class: 32-bit (0x1) +# 64-NEXT: Class: 64-bit (0x2) +# CHECK-NEXT: DataEncoding: LittleEndian (0x1) +# CHECK-NEXT: FileVersion: 1 +# CHECK-NEXT: OS/ABI: SystemV (0x0) +# CHECK-NEXT: ABIVersion: 0 +# CHECK-NEXT: Unused: (00 00 00 00 00 00 00) +# CHECK-NEXT: } +# CHECK-NEXT: Type: Executable (0x2) +# I386-NEXT: Machine: EM_386 (0x3) +# PPC-NEXT: Machine: EM_PPC (0x14) +# PPC64-NEXT: Machine: EM_PPC64 (0x15) +# X86-64-NEXT: Machine: EM_X86_64 (0x3E) +# CHECK-NEXT: Version: 1 +# CHECK-NEXT: Entry: 0x0 +# CHECK-NEXT: ProgramHeaderOffset: +# CHECK-NEXT: SectionHeaderOffset: +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# 32-NEXT: HeaderSize: 52 +# 64-NEXT: HeaderSize: 64 +# 32-NEXT: ProgramHeaderEntrySize: 0 +# 64-NEXT: ProgramHeaderEntrySize: 0 +# CHECK-NEXT: ProgramHeaderCount: 0 +# 32-NEXT: SectionHeaderEntrySize: 40 +# 64-NEXT: SectionHeaderEntrySize: 64 +# CHECK-NEXT: SectionHeaderCount: 6 +# CHECK-NEXT: StringTableSectionIndex: +# CHECK-NEXT: } 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,149 @@ +# 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 -Oelf64-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 ] + - 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: 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: 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: 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: 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: 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: 0 +# 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: 0xABCD +# CHECK-NEXT: Size: 0 +# 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 @@ -48,6 +48,8 @@ // 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 FormatMap{ + // 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 &getFormatMachineInfo(StringRef Format) { + auto Iter = FormatMap.find(Format); + if (Iter == std::end(FormatMap)) + error("Invalid 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 = getFormatMachineInfo(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 if from the input. + const ElfType OutputElfType = + Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) + : getOutputElfType(In); ArrayRef BuildIdBytes; if (!Config.BuildIdLinkDir.empty()) {