Index: test/tools/llvm-objcopy/binary-input-with-arch.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/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/binary-input.test =================================================================== --- test/tools/llvm-objcopy/binary-input.test +++ test/tools/llvm-objcopy/binary-input.test @@ -45,7 +45,7 @@ # CHECK-NEXT: Size: # CHECK-NEXT: Link: 1 # CHECK-NEXT: Info: 1 -# CHECK-NEXT: AddressAlignment: 1 +# CHECK-NEXT: AddressAlignment: 8 # CHECK-NEXT: EntrySize: 24 # CHECK-NEXT: } # CHECK-NEXT: Section { Index: test/tools/llvm-objcopy/cross-arch-bad-output-format.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/cross-arch-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 format: 'xyz'. Index: test/tools/llvm-objcopy/cross-arch-headers.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/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/cross-arch-sections-symbols.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/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) + MachineInfo 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; @@ -265,6 +281,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/Object.h =================================================================== --- tools/llvm-objcopy/Object.h +++ tools/llvm-objcopy/Object.h @@ -238,6 +238,10 @@ virtual void removeSymbols(function_ref ToRemove); virtual void accept(SectionVisitor &Visitor) const = 0; virtual void markSymbols(); + // Specific section subclasses may implement this if they have any + // arch-dependent fields that need to be changed, e.g. symbol table entry + // size. + virtual void setArch(const MachineInfo &MI); }; class Segment { @@ -479,6 +483,7 @@ void finalize() override; void accept(SectionVisitor &Visitor) const override; void removeSymbols(function_ref ToRemove) override; + void setArch(const MachineInfo &MI) override; static bool classof(const SectionBase *S) { return S->Type == ELF::SHT_SYMTAB; @@ -541,6 +546,7 @@ void accept(SectionVisitor &Visitor) const override; void removeSymbols(function_ref ToRemove) override; void markSymbols() override; + void setArch(const MachineInfo &MI) override; static bool classof(const SectionBase *S) { if (S->Flags & ELF::SHF_ALLOC) @@ -648,7 +654,7 @@ template class BinaryELFBuilder { using Elf_Sym = typename ELFT::Sym; - uint16_t EMachine; + MachineInfo MInfo; MemoryBuffer *MemBuf; std::unique_ptr Obj; @@ -660,8 +666,8 @@ void initSections(); public: - BinaryELFBuilder(uint16_t EM, MemoryBuffer *MB) - : EMachine(EM), MemBuf(MB), Obj(llvm::make_unique()) {} + BinaryELFBuilder(MachineInfo MI, MemoryBuffer *MB) + : MInfo(MI), MemBuf(MB), Obj(llvm::make_unique()) {} std::unique_ptr build(); }; @@ -744,6 +750,8 @@ SymbolTableSection *SymbolTable = nullptr; SectionIndexSection *SectionIndexTable = nullptr; + void setArch(const MachineInfo &MI); + void sortSections(); SectionTableRef sections() { return SectionTableRef(Sections); } ConstRange sections() const { Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -54,6 +54,7 @@ void SectionBase::initialize(SectionTableRef SecTable) {} void SectionBase::finalize() {} void SectionBase::markSymbols() {} +void SectionBase::setArch(const MachineInfo &) {} template void ELFWriter::writeShdr(const SectionBase &Sec) { uint8_t *B = Buf.getBufferStart(); @@ -393,6 +394,13 @@ assignIndices(); } +void SymbolTableSection::setArch(const MachineInfo &MI) { + EntrySize = MI.Is64Bit ? sizeof(ELF64LE::Sym) : sizeof(ELF32LE::Sym); + Size = Symbols.size() * EntrySize; + Align = MI.Is64Bit ? sizeof(ELF64LE::Shdr::sh_addralign) + : sizeof(ELF32LE::Shdr::sh_addralign); +} + void SymbolTableSection::initialize(SectionTableRef SecTable) { Size = 0; setStrTab(SecTable.getSectionOfType( @@ -551,6 +559,16 @@ Reloc.RelocSymbol->Referenced = true; } +void RelocationSection::setArch(const MachineInfo &MI) { + if (Type == SHT_REL) + EntrySize = MI.Is64Bit ? sizeof(ELF64LE::Rel) : sizeof(ELF32LE::Rel); + else + EntrySize = MI.Is64Bit ? sizeof(ELF64LE::Rela) : sizeof(ELF32LE::Rela); + Size = Relocations.size() * EntrySize; + Align = MI.Is64Bit ? sizeof(ELF64LE::Shdr::sh_addralign) + : sizeof(ELF32LE::Shdr::sh_addralign); +} + void SectionWriter::visit(const DynamicRelocationSection &Sec) { std::copy(std::begin(Sec.Contents), std::end(Sec.Contents), Out.getBufferStart() + Sec.Offset); @@ -702,7 +720,7 @@ Obj->Flags = 0x0; Obj->Type = ET_REL; Obj->Entry = 0x0; - Obj->Machine = EMachine; + Obj->Machine = MInfo.EMachine; Obj->Version = 1; } @@ -772,6 +790,7 @@ SymbolTableSection *SymTab = addSymTab(StrTab); initSections(); addData(SymTab); + Obj->setArch(MInfo); return std::move(Obj); } @@ -1118,12 +1137,12 @@ std::unique_ptr BinaryReader::create() const { if (MInfo.Is64Bit) return MInfo.IsLittleEndian - ? BinaryELFBuilder(MInfo.EMachine, MemBuf).build() - : BinaryELFBuilder(MInfo.EMachine, MemBuf).build(); + ? BinaryELFBuilder(MInfo, MemBuf).build() + : BinaryELFBuilder(MInfo, MemBuf).build(); else return MInfo.IsLittleEndian - ? BinaryELFBuilder(MInfo.EMachine, MemBuf).build() - : BinaryELFBuilder(MInfo.EMachine, MemBuf).build(); + ? BinaryELFBuilder(MInfo, MemBuf).build() + : BinaryELFBuilder(MInfo, MemBuf).build(); } std::unique_ptr ELFReader::create() const { @@ -1245,6 +1264,12 @@ Sec.accept(*SecWriter); } +void Object::setArch(const MachineInfo &MI) { + Machine = MI.EMachine; + for (auto &Sec : sections()) + Sec.setArch(MI); +} + void Object::removeSections(std::function ToRemove) { auto Iter = std::stable_partition( Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -113,7 +113,18 @@ return !isDWOSection(Sec); } -static ElfType getOutputElfType(const Binary &Bin) { +static ElfType getOutputElfType(const MachineInfo &MI) { + // Infer output ELF type from the binary arch specified + if (MI.Is64Bit) + return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE; + else + return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; +} + +static ElfType getOutputElfType(const CopyConfig &Config, const Binary &Bin) { + if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") + return getOutputElfType(Config.OutputArch); + // Infer output ELF type from the input ELF object if (isa>(Bin)) return ELFT_ELF32LE; @@ -126,14 +137,6 @@ llvm_unreachable("Invalid ELFType"); } -static ElfType getOutputElfType(const MachineInfo &MI) { - // Infer output ELF type from the binary arch specified - if (MI.Is64Bit) - return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE; - else - return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; -} - static std::unique_ptr createWriter(const CopyConfig &Config, Object &Obj, Buffer &Buf, ElfType OutputElfType) { @@ -252,6 +255,9 @@ splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); } + if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") + Obj.setArch(Config.OutputArch); + // TODO: update or remove symbols only if there is an option that affects // them. if (Obj.SymbolTable) { @@ -521,7 +527,11 @@ BinaryReader Reader(Config.BinaryArch, &In); std::unique_ptr Obj = Reader.create(); - const ElfType OutputElfType = getOutputElfType(Config.BinaryArch); + const MachineInfo &OutputArch = + !Config.OutputFormat.empty() && Config.OutputFormat != "binary" + ? Config.OutputArch + : Config.BinaryArch; + const ElfType OutputElfType = getOutputElfType(OutputArch); handleArgs(Config, *Obj, Reader, OutputElfType); std::unique_ptr Writer = createWriter(Config, *Obj, Out, OutputElfType); @@ -533,7 +543,7 @@ object::ELFObjectFileBase &In, Buffer &Out) { ELFReader Reader(&In); std::unique_ptr Obj = Reader.create(); - const ElfType OutputElfType = getOutputElfType(In); + const ElfType OutputElfType = getOutputElfType(Config, In); handleArgs(Config, *Obj, Reader, OutputElfType); std::unique_ptr Writer = createWriter(Config, *Obj, Out, OutputElfType);