Skip to content

Commit

Permalink
[llvm-objcopy] Implement IHEX writer
Browse files Browse the repository at this point in the history
Differential revision: https://reviews.llvm.org/D60270

llvm-svn: 361949
  • Loading branch information
eleviant committed May 29, 2019
1 parent 377c1cf commit a6fb183
Show file tree
Hide file tree
Showing 10 changed files with 715 additions and 24 deletions.
27 changes: 23 additions & 4 deletions llvm/include/llvm/Support/Error.h
Expand Up @@ -1177,11 +1177,14 @@ Error createStringError(std::error_code EC, char const *Msg);
class FileError final : public ErrorInfo<FileError> {

friend Error createFileError(const Twine &, Error);
friend Error createFileError(const Twine &, size_t, Error);

public:
void log(raw_ostream &OS) const override {
assert(Err && !FileName.empty() && "Trying to log after takeError().");
OS << "'" << FileName << "': ";
if (Line.hasValue())
OS << "line " << Line.getValue() << ": ";
Err->log(OS);
}

Expand All @@ -1193,26 +1196,36 @@ class FileError final : public ErrorInfo<FileError> {
static char ID;

private:
FileError(const Twine &F, std::unique_ptr<ErrorInfoBase> E) {
FileError(const Twine &F, Optional<size_t> LineNum,
std::unique_ptr<ErrorInfoBase> E) {
assert(E && "Cannot create FileError from Error success value.");
assert(!F.isTriviallyEmpty() &&
"The file name provided to FileError must not be empty.");
FileName = F.str();
Err = std::move(E);
Line = std::move(LineNum);
}

static Error build(const Twine &F, Error E) {
return Error(std::unique_ptr<FileError>(new FileError(F, E.takePayload())));
static Error build(const Twine &F, Optional<size_t> Line, Error E) {
return Error(
std::unique_ptr<FileError>(new FileError(F, Line, E.takePayload())));
}

std::string FileName;
Optional<size_t> Line;
std::unique_ptr<ErrorInfoBase> Err;
};

/// Concatenate a source file path and/or name with an Error. The resulting
/// Error is unchecked.
inline Error createFileError(const Twine &F, Error E) {
return FileError::build(F, std::move(E));
return FileError::build(F, Optional<size_t>(), std::move(E));
}

/// Concatenate a source file path and/or name with line number and an Error.
/// The resulting Error is unchecked.
inline Error createFileError(const Twine &F, size_t Line, Error E) {
return FileError::build(F, Optional<size_t>(Line), std::move(E));
}

/// Concatenate a source file path and/or name with a std::error_code
Expand All @@ -1221,6 +1234,12 @@ inline Error createFileError(const Twine &F, std::error_code EC) {
return createFileError(F, errorCodeToError(EC));
}

/// Concatenate a source file path and/or name with line number and
/// std::error_code to form an Error object.
inline Error createFileError(const Twine &F, size_t Line, std::error_code EC) {
return createFileError(F, Line, errorCodeToError(EC));
}

Error createFileError(const Twine &F, ErrorSuccess) = delete;

/// Helper for check-and-exit error handling.
Expand Down
20 changes: 20 additions & 0 deletions llvm/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
@@ -0,0 +1,20 @@
!ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x0
AddressAlign: 0x8
Content: "0001020304"
ProgramHeaders:
- Type: PT_NULL
Flags: [ PF_X, PF_R ]
VAddr: 0xF00000000
PAddr: 0x100000
Sections:
- Section: .text
60 changes: 60 additions & 0 deletions llvm/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
@@ -0,0 +1,60 @@
!ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Sections:
- Name: .text
# This section contents exceeds default IHex line length of 16 bytes
# so we expect two lines created for it.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x0
AddressAlign: 0x8
Content: "000102030405060708090A0B0C0D0E0F1011121314"
- Name: .data
# This section overlap 16-bit segment boundary, so we expect
# additional 'SegmentAddr' record of type '02'
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "3031323334353637383940"
Address: 0xFFF8
AddressAlign: 0x8
- Name: .data2
# Previous section '.data' should have forced creation of
# 'SegmentAddr'(02) record with segment address of 0x10000,
# so this section should have address of 0x100.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "40414243"
Address: 0x10100
AddressAlign: 0x8
- Name: .data3
# The last section not only overlaps segment boundary, but
# also has linear address which doesn't fit 20 bits. The
# following records should be craeted:
# 'SegmentAddr'(02) record with address 0x0
# 'ExtendedAddr'(04) record with address 0x100000
# 'Data'(00) record with 8 bytes of section data
# 'SegmentAddr'(02) record with address 0x10000
# 'Data'(00) record with remaining 3 bytes of data.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "5051525354555657585960"
Address: 0x10FFF8
AddressAlign: 0x8
- Name: .bss
# NOBITS sections are not written to IHex
Type: SHT_NOBITS
Flags: [ SHF_ALLOC ]
Address: 0x10100
Size: 0x1000
AddressAlign: 0x8
- Name: .dummy
# Non-allocatable sections are not written to IHex
Type: SHT_PROGBITS
Flags: [ ]
Address: 0x20FFF8
Size: 65536
AddressAlign: 0x8
39 changes: 39 additions & 0 deletions llvm/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
@@ -0,0 +1,39 @@
!ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Sections:
- Name: .text
# Zero length sections are not exported to IHex
# 'SegmentAddr' and 'ExtendedAddr' records aren't
# created either.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x7FFFFFFF
AddressAlign: 0x8
Size: 0
- Name: .text1
# Section address is sign-extended 32-bit address
# Data fits 32-bit range
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0xFFFFFFFF80001000
AddressAlign: 0x8
Content: "0001020304"
- Name: .text2
# Part of section data is in 32-bit address range
# and part isn't.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0xFFFFFFF8
AddressAlign: 0x8
Content: "000102030405060708"
- Name: .text3
# Entire secion is outside of 32-bit range
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0xFFFFFFFF0
AddressAlign: 0x8
Content: "0001020304"
60 changes: 60 additions & 0 deletions llvm/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
@@ -0,0 +1,60 @@
# Here we use yaml from ihex-elf-sections.yaml, but add single load
# segment containing all exported sections. In such case we should
# use physical address of a section intead of virtual address. Physical
# addresses start from 0x100000, so we create two additional 'ExtenededAddr'
# (03) record in the beginning of IHex file with that physical address
!ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Entry: 0x100000
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x0
AddressAlign: 0x8
Content: "000102030405060708090A0B0C0D0E0F1011121314"
- Name: .data1
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "3031323334353637383940"
Address: 0xFFF8
AddressAlign: 0x8
- Name: .data2
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "40414243"
Address: 0x10100
AddressAlign: 0x8
- Name: .data3
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "5051525354555657585960"
Address: 0x10FFF8
AddressAlign: 0x8
- Name: .bss
Type: SHT_NOBITS
Flags: [ SHF_ALLOC ]
Address: 0x10100
Size: 0x1000
AddressAlign: 0x8
- Name: .dummy
Type: SHT_PROGBITS
Flags: [ ]
Address: 0x20FFF8
Size: 65536
AddressAlign: 0x8
ProgramHeaders:
- Type: PT_LOAD
Flags: [ PF_X, PF_R ]
VAddr: 0xF00000000
PAddr: 0x100000
Sections:
- Section: .text
- Section: .data1
- Section: .data2
- Section: .data3
- Section: .bss
81 changes: 81 additions & 0 deletions llvm/test/tools/llvm-objcopy/ELF/ihex-writer.test
@@ -0,0 +1,81 @@
# RUN: yaml2obj %p/Inputs/ihex-elf-sections.yaml -o %t
# RUN: llvm-objcopy -O ihex %t - | FileCheck %s

# Check ihex output, when we have segments in ELF file
# In such case only sections in PT_LOAD segments will
# be exported and their physical addresses will be used
# RUN: yaml2obj %p/Inputs/ihex-elf-segments.yaml -o %t-segs
# RUN: llvm-objcopy -O ihex %t-segs - | FileCheck %s --check-prefix=SEGMENTS

# Check that non-load segments are ignored:
# RUN: yaml2obj %p/Inputs/ihex-elf-pt-null.yaml -o %t2-segs
# RUN: llvm-objcopy -O ihex %t2-segs - | FileCheck %s --check-prefix=PT_NULL

# Check that sign-extended 32-bit section addresses are processed
# correctly
# RUN: yaml2obj %p/Inputs/ihex-elf-sections2.yaml -o %t-sec2
# RUN: llvm-objcopy -O ihex --only-section=.text1 %t-sec2 - | FileCheck %s --check-prefix=SIGN_EXTENDED

# Check that section address range overlapping 32 bit range
# triggers an error
# RUN: not llvm-objcopy -O ihex --only-section=.text2 %t-sec2 %t-sec2-2.hex 2>&1 | FileCheck %s --check-prefix=BAD-ADDR
# RUN: not llvm-objcopy -O ihex --only-section=.text3 %t-sec2 %t-sec2-3.hex 2>&1 | FileCheck %s --check-prefix=BAD-ADDR2

# Check that zero length section is not written
# RUN: llvm-objcopy -O ihex --only-section=.text %t-sec2 - | FileCheck %s --check-prefix=ZERO_SIZE_SEC

# Check 80x86 start address record. It is created for start
# addresses less than 0x100000
# RUN: llvm-objcopy -O ihex --set-start=0xFFFF %t - | FileCheck %s --check-prefix=START1

# Check i386 start address record (05). It is created for
# start addresses which doesn't fit 20 bits
# RUN: llvm-objcopy -O ihex --set-start=0x100000 %t - | FileCheck %s --check-prefix=START2

# We allow sign extended 32 bit start addresses as well.
# RUN: llvm-objcopy -O ihex --set-start=0xFFFFFFFF80001000 %t - | FileCheck %s --check-prefix=START3

# Start address which exceeds 32 bit range triggers an error
# RUN: not llvm-objcopy -O ihex --set-start=0xF00000000 %t %t6.hex 2>&1 | FileCheck %s --check-prefix=BAD-START

# CHECK: :10000000000102030405060708090A0B0C0D0E0F78
# CHECK-NEXT: :05001000101112131491
# CHECK-NEXT: :08FFF800303132333435363765
# CHECK-NEXT: :020000021000EC
# CHECK-NEXT: :030000003839404C
# CHECK-NEXT: :0401000040414243F5
# CHECK-NEXT: :020000020000FC
# CHECK-NEXT: :020000040010EA
# CHECK-NEXT: :08FFF800505152535455565765
# CHECK-NEXT: :020000040011E9
# CHECK-NEXT: :03000000585960EC
# CHECK-NEXT: :00000001FF

# SEGMENTS: :020000040010EA
# SEGMENTS-NEXT: :10000000000102030405060708090A0B0C0D0E0F78
# SEGMENTS-NEXT: :05001000101112131491
# SEGMENTS-NEXT: :0B001800303132333435363738394090
# SEGMENTS-NEXT: :0400280040414243CE
# SEGMENTS-NEXT: :0B003000505152535455565758596018
# SEGMENTS-NEXT: :0400000500100000E7
# SEGMENTS-NEXT: :00000001FF

# 'ExtendedAddr' (04) record shouldn't be created
# PT_NULL-NOT: :02000004

# SIGN_EXTENDED: :0200000480007A
# SIGN_EXTENDED-NEXT: :051000000001020304E1
# SIGN_EXTENDED-NEXT: :00000001FF

# BAD-ADDR: error: {{.*}}: Section '.text2' address range [0xfffffff8, 0x100000000] is not 32 bit
# BAD-ADDR2: error: {{.*}}: Section '.text3' address range [0xffffffff0, 0xffffffff4] is not 32 bit

# There shouldn't be 'ExtendedAddr' nor 'Data' records
# ZERO_SIZE_SEC-NOT: :02000004
# ZERO_SIZE_SEC-NOT: :00FFFF00
# ZERO_SIZE_SEC: :00000001FF

# START1: :040000030000FFFFFB
# START2: :0400000500100000E7
# START3: :040000058000100067
# BAD-START: error: {{.*}}: Entry point address 0xf00000000 overflows 32 bits
3 changes: 2 additions & 1 deletion llvm/tools/llvm-objcopy/CopyConfig.cpp
Expand Up @@ -458,7 +458,8 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
return MI.takeError();
Config.BinaryArch = *MI;
}
if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") {
if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary" &&
Config.OutputFormat != "ihex") {
Expected<MachineInfo> MI = getOutputFormatMachineInfo(Config.OutputFormat);
if (!MI)
return MI.takeError();
Expand Down

0 comments on commit a6fb183

Please sign in to comment.