[llvm] r361949 - [llvm-objcopy] Implement IHEX writer

Eugene Leviant via llvm-commits llvm-commits at lists.llvm.org
Wed May 29 04:37:16 PDT 2019


Author: evgeny777
Date: Wed May 29 04:37:16 2019
New Revision: 361949

URL: http://llvm.org/viewvc/llvm-project?rev=361949&view=rev
Log:
[llvm-objcopy] Implement IHEX writer

Differential revision: https://reviews.llvm.org/D60270

Added:
    llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
    llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
    llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
    llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
    llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test
Modified:
    llvm/trunk/include/llvm/Support/Error.h
    llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp
    llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
    llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp
    llvm/trunk/tools/llvm-objcopy/ELF/Object.h

Modified: llvm/trunk/include/llvm/Support/Error.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Error.h?rev=361949&r1=361948&r2=361949&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/Error.h (original)
+++ llvm/trunk/include/llvm/Support/Error.h Wed May 29 04:37:16 2019
@@ -1177,11 +1177,14 @@ Error createStringError(std::error_code
 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);
   }
 
@@ -1193,26 +1196,36 @@ public:
   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 
@@ -1221,6 +1234,12 @@ inline Error createFileError(const Twine
   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.

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml?rev=361949&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml Wed May 29 04:37:16 2019
@@ -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

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml?rev=361949&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml Wed May 29 04:37:16 2019
@@ -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

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml?rev=361949&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml Wed May 29 04:37:16 2019
@@ -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"

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml?rev=361949&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml Wed May 29 04:37:16 2019
@@ -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

Added: llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test?rev=361949&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/ELF/ihex-writer.test Wed May 29 04:37:16 2019
@@ -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

Modified: llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp?rev=361949&r1=361948&r2=361949&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp Wed May 29 04:37:16 2019
@@ -458,7 +458,8 @@ Expected<DriverConfig> parseObjcopyOptio
       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();

Modified: llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp?rev=361949&r1=361948&r2=361949&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp Wed May 29 04:37:16 2019
@@ -130,12 +130,9 @@ static ElfType getOutputElfType(const Ma
     return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE;
 }
 
-static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
-                                            Object &Obj, Buffer &Buf,
-                                            ElfType OutputElfType) {
-  if (Config.OutputFormat == "binary") {
-    return llvm::make_unique<BinaryWriter>(Obj, Buf);
-  }
+static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config,
+                                               Object &Obj, Buffer &Buf,
+                                               ElfType OutputElfType) {
   // Depending on the initial ELFT and OutputFormat we need a different Writer.
   switch (OutputElfType) {
   case ELFT_ELF32LE:
@@ -154,6 +151,17 @@ static std::unique_ptr<Writer> createWri
   llvm_unreachable("Invalid output format");
 }
 
+static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
+                                            Object &Obj, Buffer &Buf,
+                                            ElfType OutputElfType) {
+  using Functor = std::function<std::unique_ptr<Writer>()>;
+  return StringSwitch<Functor>(Config.OutputFormat)
+      .Case("binary", [&] { return llvm::make_unique<BinaryWriter>(Obj, Buf); })
+      .Case("ihex", [&] { return llvm::make_unique<IHexWriter>(Obj, Buf); })
+      .Default(
+          [&] { return createELFWriter(Config, Obj, Buf, OutputElfType); })();
+}
+
 template <class ELFT>
 static Expected<ArrayRef<uint8_t>>
 findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) {
@@ -714,6 +722,15 @@ static Error handleArgs(const CopyConfig
   return Error::success();
 }
 
+static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out,
+                         ElfType OutputElfType) {
+  std::unique_ptr<Writer> Writer =
+      createWriter(Config, Obj, Out, OutputElfType);
+  if (Error E = Writer->finalize())
+    return E;
+  return Writer->write();
+}
+
 Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
                                 Buffer &Out) {
   BinaryReader Reader(Config.BinaryArch, &In);
@@ -721,15 +738,11 @@ Error executeObjcopyOnRawBinary(const Co
 
   // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch
   // (-B<arch>).
-  const ElfType OutputElfType = getOutputElfType(
-      Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch);
+  const ElfType OutputElfType =
+      getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch));
   if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType))
     return E;
-  std::unique_ptr<Writer> Writer =
-      createWriter(Config, *Obj, Out, OutputElfType);
-  if (Error E = Writer->finalize())
-    return E;
-  return Writer->write();
+  return writeOutput(Config, *Obj, Out, OutputElfType);
 }
 
 Error executeObjcopyOnBinary(const CopyConfig &Config,
@@ -764,12 +777,8 @@ Error executeObjcopyOnBinary(const CopyC
   if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType))
     return createFileError(Config.InputFilename, std::move(E));
 
-  std::unique_ptr<Writer> Writer =
-      createWriter(Config, *Obj, Out, OutputElfType);
-  if (Error E = Writer->finalize())
+  if (Error E = writeOutput(Config, *Obj, Out, OutputElfType))
     return createFileError(Config.InputFilename, std::move(E));
-  if (Error E = Writer->write())
-    return E;
   if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput)
     if (Error E =
             linkToBuildIdDir(Config, Config.OutputFilename,

Modified: llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp?rev=361949&r1=361948&r2=361949&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/ELF/Object.cpp Wed May 29 04:37:16 2019
@@ -17,7 +17,7 @@
 #include "llvm/MC/MCTargetOptions.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Support/Compression.h"
-#include "llvm/Support/Errc.h"
+#include "llvm/Support/Endian.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileOutputBuffer.h"
 #include "llvm/Support/Path.h"
@@ -147,6 +147,156 @@ void SectionWriter::visit(const Section
     llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
 }
 
+static bool addressOverflows32bit(uint64_t Addr) {
+  // Sign extended 32 bit addresses (e.g 0xFFFFFFFF80000000) are ok
+  return Addr > UINT32_MAX && Addr + 0x80000000 > UINT32_MAX;
+}
+
+template <class T> static T checkedGetHex(StringRef S) {
+  T Value;
+  bool Fail = S.getAsInteger(16, Value);
+  assert(!Fail);
+  (void)Fail;
+  return Value;
+}
+
+// Fills exactly Len bytes of buffer with hexadecimal characters
+// representing value 'X'
+template <class T, class Iterator>
+static Iterator utohexstr(T X, Iterator It, size_t Len) {
+  // Fill range with '0'
+  std::fill(It, It + Len, '0');
+
+  for (long I = Len - 1; I >= 0; --I) {
+    unsigned char Mod = static_cast<unsigned char>(X) & 15;
+    *(It + I) = hexdigit(Mod, false);
+    X >>= 4;
+  }
+  assert(X == 0);
+  return It + Len;
+}
+
+uint8_t IHexRecord::getChecksum(StringRef S) {
+  assert((S.size() & 1) == 0);
+  uint8_t Checksum = 0;
+  while (!S.empty()) {
+    Checksum += checkedGetHex<uint8_t>(S.take_front(2));
+    S = S.drop_front(2);
+  }
+  return -Checksum;
+}
+
+IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr,
+                                 ArrayRef<uint8_t> Data) {
+  IHexLineData Line(getLineLength(Data.size()));
+  assert(Line.size());
+  auto Iter = Line.begin();
+  *Iter++ = ':';
+  Iter = utohexstr(Data.size(), Iter, 2);
+  Iter = utohexstr(Addr, Iter, 4);
+  Iter = utohexstr(Type, Iter, 2);
+  for (uint8_t X : Data)
+    Iter = utohexstr(X, Iter, 2);
+  StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter));
+  Iter = utohexstr(getChecksum(S), Iter, 2);
+  *Iter++ = '\r';
+  *Iter++ = '\n';
+  assert(Iter == Line.end());
+  return Line;
+}
+
+static uint64_t sectionPhysicalAddr(const SectionBase *Sec) {
+  Segment *Seg = Sec->ParentSegment;
+  if (Seg && Seg->Type != ELF::PT_LOAD)
+    Seg = nullptr;
+  return Seg ? Seg->PAddr + Sec->OriginalOffset - Seg->OriginalOffset
+             : Sec->Addr;
+}
+
+void IHexSectionWriterBase::writeSection(const SectionBase *Sec,
+                                         ArrayRef<uint8_t> Data) {
+  assert(Data.size() == Sec->Size);
+  const uint32_t ChunkSize = 16;
+  uint32_t Addr = sectionPhysicalAddr(Sec) & 0xFFFFFFFFU;
+  while (!Data.empty()) {
+    uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
+    if (Addr > SegmentAddr + BaseAddr + 0xFFFFU) {
+      if (Addr > 0xFFFFFU) {
+        // Write extended address record, zeroing segment address
+        // if needed.
+        if (SegmentAddr != 0)
+          SegmentAddr = writeSegmentAddr(0U);
+        BaseAddr = writeBaseAddr(Addr);
+      } else {
+        // We can still remain 16-bit
+        SegmentAddr = writeSegmentAddr(Addr);
+      }
+    }
+    uint64_t SegOffset = Addr - BaseAddr - SegmentAddr;
+    assert(SegOffset <= 0xFFFFU);
+    DataSize = std::min(DataSize, 0x10000U - SegOffset);
+    writeData(0, SegOffset, Data.take_front(DataSize));
+    Addr += DataSize;
+    Data = Data.drop_front(DataSize);
+  }
+}
+
+uint64_t IHexSectionWriterBase::writeSegmentAddr(uint64_t Addr) {
+  assert(Addr <= 0xFFFFFU);
+  uint8_t Data[] = {static_cast<uint8_t>((Addr & 0xF0000U) >> 12), 0};
+  writeData(2, 0, Data);
+  return Addr & 0xF0000U;
+}
+
+uint64_t IHexSectionWriterBase::writeBaseAddr(uint64_t Addr) {
+  assert(Addr <= 0xFFFFFFFFU);
+  uint64_t Base = Addr & 0xFFFF0000U;
+  uint8_t Data[] = {static_cast<uint8_t>(Base >> 24),
+                    static_cast<uint8_t>((Base >> 16) & 0xFF)};
+  writeData(4, 0, Data);
+  return Base;
+}
+
+void IHexSectionWriterBase::writeData(uint8_t Type, uint16_t Addr,
+                                      ArrayRef<uint8_t> Data) {
+  Offset += IHexRecord::getLineLength(Data.size());
+}
+
+void IHexSectionWriterBase::visit(const Section &Sec) {
+  writeSection(&Sec, Sec.Contents);
+}
+
+void IHexSectionWriterBase::visit(const OwnedDataSection &Sec) {
+  writeSection(&Sec, Sec.Data);
+}
+
+void IHexSectionWriterBase::visit(const StringTableSection &Sec) {
+  // Check that sizer has already done its work
+  assert(Sec.Size == Sec.StrTabBuilder.getSize());
+  // We are free to pass an invalid pointer to writeSection as long
+  // as we don't actually write any data. The real writer class has
+  // to override this method .
+  writeSection(&Sec, {nullptr, Sec.Size});
+}
+
+void IHexSectionWriterBase::visit(const DynamicRelocationSection &Sec) {
+  writeSection(&Sec, Sec.Contents);
+}
+
+void IHexSectionWriter::writeData(uint8_t Type, uint16_t Addr,
+                                  ArrayRef<uint8_t> Data) {
+  IHexLineData HexData = IHexRecord::getLine(Type, Addr, Data);
+  memcpy(Out.getBufferStart() + Offset, HexData.data(), HexData.size());
+  Offset += HexData.size();
+}
+
+void IHexSectionWriter::visit(const StringTableSection &Sec) {
+  assert(Sec.Size == Sec.StrTabBuilder.getSize());
+  std::vector<uint8_t> Data(Sec.Size);
+  Sec.StrTabBuilder.write(Data.data());
+  writeSection(&Sec, Data);
+}
+
 void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); }
 
 void Section::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); }
@@ -217,6 +367,15 @@ void OwnedDataSection::accept(MutableSec
   Visitor.visit(*this);
 }
 
+void OwnedDataSection::appendHexData(StringRef HexData) {
+  assert((HexData.size() & 1) == 0);
+  while (!HexData.empty()) {
+    Data.push_back(checkedGetHex<uint8_t>(HexData.take_front(2)));
+    HexData = HexData.drop_front(2);
+  }
+  Size = Data.size();
+}
+
 void BinarySectionWriter::visit(const CompressedSection &Sec) {
   error("cannot write compressed section '" + Sec.Name + "' ");
 }
@@ -1807,6 +1966,109 @@ Error BinaryWriter::finalize() {
   return Error::success();
 }
 
+bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs,
+                                            const SectionBase *Rhs) const {
+  return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) <
+         (sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU);
+}
+
+uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) {
+  IHexLineData HexData;
+  uint8_t Data[4] = {};
+  // We don't write entry point record if entry is zero.
+  if (Obj.Entry == 0)
+    return 0;
+
+  if (Obj.Entry <= 0xFFFFFU) {
+    Data[0] = ((Obj.Entry & 0xF0000U) >> 12) & 0xFF;
+    support::endian::write(&Data[2], static_cast<uint16_t>(Obj.Entry),
+                           support::big);
+    HexData = IHexRecord::getLine(IHexRecord::StartAddr80x86, 0, Data);
+  } else {
+    support::endian::write(Data, static_cast<uint32_t>(Obj.Entry),
+                           support::big);
+    HexData = IHexRecord::getLine(IHexRecord::StartAddr, 0, Data);
+  }
+  memcpy(Buf, HexData.data(), HexData.size());
+  return HexData.size();
+}
+
+uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) {
+  IHexLineData HexData = IHexRecord::getLine(IHexRecord::EndOfFile, 0, {});
+  memcpy(Buf, HexData.data(), HexData.size());
+  return HexData.size();
+}
+
+Error IHexWriter::write() {
+  IHexSectionWriter Writer(Buf);
+  // Write sections.
+  for (const SectionBase *Sec : Sections)
+    Sec->accept(Writer);
+
+  uint64_t Offset = Writer.getBufferOffset();
+  // Write entry point address.
+  Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset);
+  // Write EOF.
+  Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset);
+  assert(Offset == TotalSize);
+  return Buf.commit();
+}
+
+Error IHexWriter::checkSection(const SectionBase &Sec) {
+  uint64_t Addr = sectionPhysicalAddr(&Sec);
+  if (addressOverflows32bit(Addr) || addressOverflows32bit(Addr + Sec.Size - 1))
+    return createStringError(
+        errc::invalid_argument,
+        "Section '%s' address range [%p, %p] is not 32 bit", Sec.Name.c_str(),
+        Addr, Addr + Sec.Size - 1);
+  return Error::success();
+}
+
+Error IHexWriter::finalize() {
+  bool UseSegments = false;
+  auto ShouldWrite = [](const SectionBase &Sec) {
+    return (Sec.Flags & ELF::SHF_ALLOC) && (Sec.Type != ELF::SHT_NOBITS);
+  };
+  auto IsInPtLoad = [](const SectionBase &Sec) {
+    return Sec.ParentSegment && Sec.ParentSegment->Type == ELF::PT_LOAD;
+  };
+
+  // We can't write 64-bit addresses.
+  if (addressOverflows32bit(Obj.Entry))
+    return createStringError(errc::invalid_argument,
+                             "Entry point address %p overflows 32 bits.",
+                             Obj.Entry);
+
+  // If any section we're to write has segment then we
+  // switch to using physical addresses. Otherwise we
+  // use section virtual address.
+  for (auto &Section : Obj.sections())
+    if (ShouldWrite(Section) && IsInPtLoad(Section)) {
+      UseSegments = true;
+      break;
+    }
+
+  for (auto &Section : Obj.sections())
+    if (ShouldWrite(Section) && (!UseSegments || IsInPtLoad(Section))) {
+      if (Error E = checkSection(Section))
+        return E;
+      Sections.insert(&Section);
+    }
+
+  IHexSectionWriterBase LengthCalc(Buf);
+  for (const SectionBase *Sec : Sections)
+    Sec->accept(LengthCalc);
+
+  // We need space to write section records + StartAddress record
+  // (if start adress is not zero) + EndOfFile record.
+  TotalSize = LengthCalc.getBufferOffset() +
+              (Obj.Entry ? IHexRecord::getLineLength(4) : 0) +
+              IHexRecord::getLineLength(0);
+  if (Error E = Buf.allocate(TotalSize))
+    return E;
+  return Error::success();
+}
+
 template class ELFBuilder<ELF64LE>;
 template class ELFBuilder<ELF64BE>;
 template class ELFBuilder<ELF32LE>;

Modified: llvm/trunk/tools/llvm-objcopy/ELF/Object.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/ELF/Object.h?rev=361949&r1=361948&r2=361949&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/ELF/Object.h (original)
+++ llvm/trunk/tools/llvm-objcopy/ELF/Object.h Wed May 29 04:37:16 2019
@@ -17,6 +17,7 @@
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/MC/StringTableBuilder.h"
 #include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/Errc.h"
 #include "llvm/Support/FileOutputBuffer.h"
 #include <cstddef>
 #include <cstdint>
@@ -168,6 +169,8 @@ public:
 
 #define MAKE_SEC_WRITER_FRIEND                                                 \
   friend class SectionWriter;                                                  \
+  friend class IHexSectionWriterBase;                                          \
+  friend class IHexSectionWriter;                                              \
   template <class ELFT> friend class ELFSectionWriter;                         \
   template <class ELFT> friend class ELFSectionSizer;
 
@@ -186,6 +189,114 @@ public:
   explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
 };
 
+using IHexLineData = SmallVector<char, 64>;
+
+struct IHexRecord {
+  // Memory address of the record.
+  uint16_t Addr;
+  // Record type (see below).
+  uint16_t Type;
+  // Record data in hexadecimal form.
+  StringRef HexData;
+
+  // Helper method to get file length of the record
+  // including newline character
+  static size_t getLength(size_t DataSize) {
+    // :LLAAAATT[DD...DD]CC'
+    return DataSize * 2 + 11;
+  }
+
+  // Gets length of line in a file (getLength + CRLF).
+  static size_t getLineLength(size_t DataSize) {
+    return getLength(DataSize) + 2;
+  }
+
+  // Given type, address and data returns line which can
+  // be written to output file.
+  static IHexLineData getLine(uint8_t Type, uint16_t Addr,
+                              ArrayRef<uint8_t> Data);
+
+  // Calculates checksum of stringified record representation
+  // S must NOT contain leading ':' and trailing whitespace
+  // characters
+  static uint8_t getChecksum(StringRef S);
+
+  enum Type {
+    // Contains data and a 16-bit starting address for the data.
+    // The byte count specifies number of data bytes in the record.
+    Data = 0,
+    // Must occur exactly once per file in the last line of the file.
+    // The data field is empty (thus byte count is 00) and the address
+    // field is typically 0000.
+    EndOfFile = 1,
+    // The data field contains a 16-bit segment base address (thus byte
+    // count is always 02) compatible with 80x86 real mode addressing.
+    // The address field (typically 0000) is ignored. The segment address
+    // from the most recent 02 record is multiplied by 16 and added to each
+    // subsequent data record address to form the physical starting address
+    // for the data. This allows addressing up to one megabyte of address
+    // space.
+    SegmentAddr = 2,
+    // or 80x86 processors, specifies the initial content of the CS:IP
+    // registers. The address field is 0000, the byte count is always 04,
+    // the first two data bytes are the CS value, the latter two are the
+    // IP value.
+    StartAddr80x86 = 3,
+    // Allows for 32 bit addressing (up to 4GiB). The record's address field
+    // is ignored (typically 0000) and its byte count is always 02. The two
+    // data bytes (big endian) specify the upper 16 bits of the 32 bit
+    // absolute address for all subsequent type 00 records
+    ExtendedAddr = 4,
+    // The address field is 0000 (not used) and the byte count is always 04.
+    // The four data bytes represent a 32-bit address value. In the case of
+    // 80386 and higher CPUs, this address is loaded into the EIP register.
+    StartAddr = 5,
+    // We have no other valid types
+    InvalidType = 6
+  };
+};
+
+// Base class for IHexSectionWriter. This class implements writing algorithm,
+// but doesn't actually write records. It is used for output buffer size
+// calculation in IHexWriter::finalize.
+class IHexSectionWriterBase : public BinarySectionWriter {
+  // 20-bit segment address
+  uint32_t SegmentAddr = 0;
+  // Extended linear address
+  uint32_t BaseAddr = 0;
+
+  // Write segment address corresponding to 'Addr'
+  uint64_t writeSegmentAddr(uint64_t Addr);
+  // Write extended linear (base) address corresponding to 'Addr'
+  uint64_t writeBaseAddr(uint64_t Addr);
+
+protected:
+  // Offset in the output buffer
+  uint64_t Offset = 0;
+
+  void writeSection(const SectionBase *Sec, ArrayRef<uint8_t> Data);
+  virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data);
+
+public:
+  explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {}
+
+  uint64_t getBufferOffset() const { return Offset; }
+  void visit(const Section &Sec) final;
+  void visit(const OwnedDataSection &Sec) final;
+  void visit(const StringTableSection &Sec) override;
+  void visit(const DynamicRelocationSection &Sec) final;
+  using BinarySectionWriter::visit;
+};
+
+// Real IHEX section writer
+class IHexSectionWriter : public IHexSectionWriterBase {
+public:
+  IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {}
+
+  void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override;
+  void visit(const StringTableSection &Sec) override;
+};
+
 class Writer {
 protected:
   Object &Obj;
@@ -245,6 +356,25 @@ public:
   BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
 };
 
+class IHexWriter : public Writer {
+  struct SectionCompare {
+    bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const;
+  };
+
+  std::set<const SectionBase *, SectionCompare> Sections;
+  size_t TotalSize;
+
+  Error checkSection(const SectionBase &Sec);
+  uint64_t writeEntryPointRecord(uint8_t *Buf);
+  uint64_t writeEndOfFileRecord(uint8_t *Buf);
+
+public:
+  ~IHexWriter() {}
+  Error finalize() override;
+  Error write() override;
+  IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
+};
+
 class SectionBase {
 public:
   std::string Name;
@@ -361,6 +491,16 @@ public:
     OriginalOffset = std::numeric_limits<uint64_t>::max();
   }
 
+  OwnedDataSection(const Twine &SecName, uint64_t SecAddr, uint64_t SecFlags,
+                   uint64_t SecOff) {
+    Name = SecName.str();
+    Type = ELF::SHT_PROGBITS;
+    Addr = SecAddr;
+    Flags = SecFlags;
+    OriginalOffset = SecOff;
+  }
+
+  void appendHexData(StringRef HexData);
   void accept(SectionVisitor &Sec) const override;
   void accept(MutableSectionVisitor &Visitor) override;
 };




More information about the llvm-commits mailing list