[llvm-objcopy] Handle -O <format> flag.

Summary:
The -O flag is currently being mostly ignored; it's only checked whether or not the output format is "binary". This adds support for a few formats (e.g. elf64-x86-64), so that when specified, the output can change between 32/64 bit and sizes/alignments are updated accordingly.

This fixes PR39135

Reviewers: jakehehrlich, jhenderson, alexshap, espindola

Reviewed By: jhenderson

Subscribers: emaste, arichardson, llvm-commits

Differential Revision: https://reviews.llvm.org/D53667

llvm-svn: 350541
This commit is contained in:
Jordan Rupprecht 2019-01-07 16:59:12 +00:00
parent 9d96c3c7f2
commit 70038e01c8
7 changed files with 290 additions and 3 deletions

View File

@ -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'.

View File

@ -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

View File

@ -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

View File

@ -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: 16
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: 0x10
# 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: ]

View File

@ -189,6 +189,22 @@ static const MachineInfo &getMachineInfo(StringRef Arch) {
return Iter->getValue();
}
static const StringMap<MachineInfo> 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<std::string> &Symbols,
StringRef Filename) {
SmallVector<StringRef, 16> Lines;
@ -266,6 +282,8 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
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)) {

View File

@ -46,8 +46,10 @@ struct CopyConfig {
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<MachineInfo> OutputArch;
// Advanced options
StringRef AddGnuDebugLink;

View File

@ -173,6 +173,8 @@ static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
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 @@ static void handleArgs(const CopyConfig &Config, Object &Obj,
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 @@ void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
BinaryReader Reader(Config.BinaryArch, &In);
std::unique_ptr<Object> Obj = Reader.create();
const ElfType OutputElfType = getOutputElfType(Config.BinaryArch);
// Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch
// (-B<arch>).
const ElfType OutputElfType = getOutputElfType(
Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch);
handleArgs(Config, *Obj, Reader, OutputElfType);
std::unique_ptr<Writer> Writer =
createWriter(Config, *Obj, Out, OutputElfType);
@ -540,7 +547,10 @@ void executeObjcopyOnBinary(const CopyConfig &Config,
object::ELFObjectFileBase &In, Buffer &Out) {
ELFReader Reader(&In);
std::unique_ptr<Object> Obj = Reader.create();
const ElfType OutputElfType = getOutputElfType(In);
// Prefer OutputArch (-O<format>) if set, otherwise infer it from the input.
const ElfType OutputElfType =
Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue())
: getOutputElfType(In);
ArrayRef<uint8_t> BuildIdBytes;
if (!Config.BuildIdLinkDir.empty()) {