[llvm] [llvm-objcopy] Support SREC output format (PR #75874)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 10 23:08:38 PST 2024


https://github.com/quic-areg updated https://github.com/llvm/llvm-project/pull/75874

>From 3fb6b1930441cfa59c7835a62bf9fa0d4b40e43d Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Mon, 18 Dec 2023 14:24:24 -0800
Subject: [PATCH 1/7] [llvm-objcopy] Support SREC output format

Adds a new output target "srec" to write SREC files from ELF inputs.

https://en.wikipedia.org/wiki/SREC_(file_format)
---
 llvm/include/llvm/ObjCopy/CommonConfig.h      |   7 +-
 llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp           |   2 +
 llvm/lib/ObjCopy/ELF/ELFObject.cpp            | 237 ++++++++++++++++++
 llvm/lib/ObjCopy/ELF/ELFObject.h              | 102 ++++++++
 .../ELF/Inputs/srec-elf-sections-err.yaml     |  22 ++
 .../ELF/Inputs/srec-elf-sections.yaml         |  46 ++++
 .../ELF/Inputs/srec-elf-segments.yaml         |  45 ++++
 .../tools/llvm-objcopy/ELF/srec-writer.test   |  44 ++++
 llvm/tools/llvm-objcopy/ObjcopyOptions.cpp    |   1 +
 llvm/tools/llvm-objcopy/llvm-objcopy.cpp      |   1 +
 10 files changed, 501 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/srec-writer.test

diff --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h
index 386c20aec184de..893308eff3a42c 100644
--- a/llvm/include/llvm/ObjCopy/CommonConfig.h
+++ b/llvm/include/llvm/ObjCopy/CommonConfig.h
@@ -27,12 +27,7 @@
 namespace llvm {
 namespace objcopy {
 
-enum class FileFormat {
-  Unspecified,
-  ELF,
-  Binary,
-  IHex,
-};
+enum class FileFormat { Unspecified, ELF, Binary, IHex, SREC };
 
 // This type keeps track of the machine info for various architectures. This
 // lets us map architecture names to ELF types and the e_machine value of the
diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index daf03810fd7bff..772c19610d8c4e 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -183,6 +183,8 @@ static std::unique_ptr<Writer> createWriter(const CommonConfig &Config,
     return std::make_unique<BinaryWriter>(Obj, Out, Config);
   case FileFormat::IHex:
     return std::make_unique<IHexWriter>(Obj, Out);
+  case FileFormat::SREC:
+    return std::make_unique<SRECWriter>(Obj, Out, Config.OutputFilename);
   default:
     return createELFWriter(Config, Obj, Out, OutputElfType);
   }
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index 5352736bdcb9b8..5868168700a16a 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -324,6 +324,14 @@ Expected<IHexRecord> IHexRecord::parse(StringRef Line) {
   return Rec;
 }
 
+static uint64_t sectionLMA(const SectionBase *Sec) {
+  Segment *Seg = Sec->ParentSegment;
+  if (Seg && Seg->Type == PT_LOAD && Seg->VAddr <= Sec->Addr &&
+      (Seg->VAddr + Seg->MemSize > Sec->Addr))
+    return Sec->Addr - Seg->VAddr + Seg->PAddr;
+  return Sec->Addr;
+}
+
 static uint64_t sectionPhysicalAddr(const SectionBase *Sec) {
   Segment *Seg = Sec->ParentSegment;
   if (Seg && Seg->Type != ELF::PT_LOAD)
@@ -2813,6 +2821,235 @@ Error IHexWriter::finalize() {
   return Error::success();
 }
 
+Error SRECSectionWriterBase::visit(const StringTableSection &Sec) {
+  // Check that sizer has already done its work
+  assert(Sec.Size == Sec.StrTabBuilder.getSize());
+  // we don't need to write anything here the real writer has already done it.
+  return Error::success();
+}
+
+Error SRECSectionWriterBase::visit(const Section &Sec) {
+  writeSection(Sec, Sec.Contents);
+  return Error::success();
+}
+
+Error SRECSectionWriterBase::visit(const OwnedDataSection &Sec) {
+  writeSection(Sec, Sec.Data);
+  return Error::success();
+}
+
+Error SRECSectionWriterBase::visit(const DynamicRelocationSection &Sec) {
+  writeSection(Sec, Sec.Contents);
+  return Error::success();
+}
+
+void SRECSectionWriter::writeRecord(SRecord &Record, uint64_t Off) {
+  SRecLineData Data = Record.toString();
+  memcpy(Out.getBufferStart() + Off, Data.data(), Data.size());
+}
+
+void SRECSectionWriter::writeRecords() {
+  uint64_t Off = HeaderSize;
+  for (SRecord &Record : Records) {
+    Record.Type = Type;
+    writeRecord(Record, Off);
+    Off += Record.getSize();
+  }
+  Offset = Off;
+}
+
+void SRECSectionWriterBase::writeRecords() {
+  uint64_t Off = HeaderSize;
+  for (SRecord &Record : Records) {
+    Record.Type = Type;
+    Off += Record.getSize();
+  }
+  Offset = Off;
+}
+
+void SRECSectionWriterBase::writeSection(const SectionBase &S,
+                                         ArrayRef<uint8_t> Data) {
+  const uint32_t ChunkSize = 16;
+  uint32_t Address = sectionLMA(&S);
+  uint32_t EndAddr = Address + S.Size - 1;
+  if (isUInt<16>(EndAddr))
+    Type = std::max(static_cast<uint8_t>(SRecord::S1), Type);
+  else if (isUInt<24>(EndAddr))
+    Type = std::max(static_cast<uint8_t>(SRecord::S2), Type);
+  else
+    Type = SRecord::S3;
+  while (!Data.empty()) {
+    uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
+    SRecord Record{Type, Address, Data.take_front(DataSize)};
+    Records.push_back(Record);
+    Data = Data.drop_front(DataSize);
+    Address += DataSize;
+  }
+}
+
+Error SRECSectionWriter::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);
+  return Error::success();
+}
+
+SRecLineData SRecord::toString() const {
+  SRecLineData Line(getSize());
+  auto *Iter = Line.begin();
+  *Iter++ = 'S';
+  *Iter++ = '0' + Type;
+  // write 1 byte (2 hex characters) record count
+  Iter = toHexStr(getCount(), Iter, 2);
+  // write the address field with length depending on record type
+  Iter = toHexStr(Address, Iter, getAddressSize());
+  // write data byte by byte
+  for (uint8_t X : Data)
+    Iter = toHexStr(X, Iter, 2);
+  // write the 1 byte checksum
+  Iter = toHexStr(getChecksum(), Iter, 2);
+  *Iter++ = '\r';
+  *Iter++ = '\n';
+  assert(Iter == Line.end());
+  return Line;
+}
+
+uint8_t SRecord::getChecksum() const {
+  uint32_t Sum = getCount();
+  Sum += (Address >> 24) & 0xFF;
+  Sum += (Address >> 16) & 0xFF;
+  Sum += (Address >> 8) & 0xFF;
+  Sum += Address & 0xFF;
+  for (uint8_t Byte : Data)
+    Sum += Byte;
+  return 0xFF - (Sum & 0xFF);
+}
+
+size_t SRecord::getSize() const {
+  // 2 characters for type, count, checksum, CRLF
+  return 2 + 2 + getAddressSize() + Data.size() * 2 + 2 + 2;
+}
+
+uint8_t SRecord::getAddressSize() const {
+  switch (Type) {
+  case Type::S2:
+    return 6;
+  case Type::S3:
+    return 8;
+  case Type::S7:
+    return 8;
+  case Type::S8:
+    return 6;
+  default:
+    return 4;
+  }
+}
+
+uint8_t SRecord::getCount() const {
+  uint8_t DataSize = Data.size();
+  uint8_t ChecksumSize = 1;
+  return getAddressSize() / 2 + DataSize + ChecksumSize;
+}
+
+SRecord SRecord::getHeader(StringRef FileName) {
+  // limit header to 40 characters
+  StringRef HeaderContents = FileName.slice(0, 40);
+  ArrayRef<uint8_t> Data(
+      reinterpret_cast<const uint8_t *>(HeaderContents.data()),
+      HeaderContents.size());
+  return {SRecord::S0, 0, Data};
+}
+
+size_t SRECWriter::writeHeader(uint8_t *Buf) {
+  SRecLineData Record = SRecord::getHeader(OutputFileName).toString();
+  memcpy(Buf, Record.data(), Record.size());
+  return Record.size();
+}
+
+size_t SRECWriter::writeTerminator(uint8_t *Buf, uint8_t Type) {
+  assert(Type >= 7 && Type <= 9);
+  uint32_t Entry = Obj.Entry;
+  SRecLineData Data = SRecord{Type, Entry, {}}.toString();
+  memcpy(Buf, Data.data(), Data.size());
+  return Data.size();
+}
+
+Error SRECWriter::write() {
+  uint32_t HeaderSize =
+      writeHeader(reinterpret_cast<uint8_t *>(Buf->getBufferStart()));
+  SRECSectionWriter Writer(*Buf, HeaderSize);
+  for (const SectionBase *S : Sections) {
+    if (Error E = S->accept(Writer))
+      return E;
+  }
+  Writer.writeRecords();
+  uint64_t Offset = Writer.getBufferOffset();
+  /// S1 terminates with S9, S2 with S8, S3 with S7
+  uint8_t TerminatorType = 10 - Writer.getType();
+  Offset += writeTerminator(
+      reinterpret_cast<uint8_t *>(Buf->getBufferStart() + Offset),
+      TerminatorType);
+  assert(Offset == TotalSize);
+  Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+  return Error::success();
+}
+
+Error SRECWriter::checkSection(const SectionBase &S) const {
+  if (!isUInt<32>(S.Addr + S.Size - 1))
+    return createStringError(
+        errc::invalid_argument,
+        "Section '%s' address range [0x%llx, 0x%llx] is not 32 bit",
+        S.Name.c_str(), S.Addr, S.Addr + S.Size - 1);
+  return Error::success();
+}
+
+Error SRECWriter::finalize() {
+  // We can't write 64-bit addresses.
+  if (!isUInt<32>(Obj.Entry))
+    return createStringError(errc::invalid_argument,
+                             "Entry point address 0x%llx overflows 32 bits",
+                             Obj.Entry);
+
+  for (const SectionBase &S : Obj.sections()) {
+    if ((S.Flags & ELF::SHF_ALLOC) && S.Type != ELF::SHT_NOBITS && S.Size > 0) {
+      if (Error E = checkSection(S))
+        return E;
+      Sections.push_back(&S);
+    }
+  }
+
+  llvm::sort(Sections, [](const SectionBase *A, const SectionBase *B) {
+    return sectionLMA(A) < sectionLMA(B);
+  });
+
+  std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
+      WritableMemoryBuffer::getNewMemBuffer(0);
+  if (!EmptyBuffer)
+    return createStringError(errc::not_enough_memory,
+                             "failed to allocate memory buffer of 0 bytes");
+
+  SRECSectionWriterBase LengthCalc(*EmptyBuffer, 0);
+  for (const SectionBase *Sec : Sections)
+    if (Error Err = Sec->accept(LengthCalc))
+      return Err;
+
+  LengthCalc.writeRecords();
+
+  // We need to add the size of the Header and Terminator records
+  SRecord Header = SRecord::getHeader(OutputFileName);
+  uint8_t TerminatorType = 10 - LengthCalc.getType();
+  SRecord Terminator = {TerminatorType, static_cast<uint32_t>(Obj.Entry), {}};
+  TotalSize =
+      LengthCalc.getBufferOffset() + Header.getSize() + Terminator.getSize();
+  Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+  if (!Buf)
+    return createStringError(errc::not_enough_memory,
+                             "failed to allocate memory buffer of 0x" +
+                                 Twine::utohexstr(TotalSize) + " bytes");
+  return Error::success();
+}
+
 namespace llvm {
 namespace objcopy {
 namespace elf {
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 95bea0964eaef3..c33d096e32fe35 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -172,6 +172,8 @@ template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor {
   friend class SectionWriter;                                                  \
   friend class IHexSectionWriterBase;                                          \
   friend class IHexSectionWriter;                                              \
+  friend class SRECSectionWriterBase;                                          \
+  friend class SRECSectionWriter;                                              \
   template <class ELFT> friend class ELFSectionWriter;                         \
   template <class ELFT> friend class ELFSectionSizer;
 
@@ -390,6 +392,106 @@ class IHexWriter : public Writer {
   IHexWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {}
 };
 
+using SRecLineData = SmallVector<char, 64>;
+struct SRecord {
+  uint8_t Type;
+  uint32_t Address;
+  ArrayRef<uint8_t> Data;
+  SRecLineData toString() const;
+  uint8_t getCount() const;
+  // get address size in characters
+  uint8_t getAddressSize() const;
+  uint8_t getChecksum() const;
+  size_t getSize() const;
+  static SRecord getHeader(StringRef FileName);
+  static SRecord getTerminator(uint8_t Type, uint32_t Entry);
+  enum Type : uint8_t {
+    // Vendor specific text comment
+    S0 = 0,
+    // Data that starts at a 16 bit address
+    S1 = 1,
+    // Data that starts at a 24 bit address
+    S2 = 2,
+    // Data that starts at a 32 bit address
+    S3 = 3,
+    // Reserved
+    S4 = 4,
+    // 16 bit count of S1/S2/S3 records (optional)
+    S5 = 5,
+    // 32 bit count of S1/S2/S3 records (optional)
+    S6 = 6,
+    // Terminates a series of S3 records
+    S7 = 7,
+    // Terminates a series of S2 records
+    S8 = 8,
+    // Terminates a series of S1 records
+    S9 = 9
+  };
+
+private:
+  uint16_t getCheckSum(StringRef S) const;
+};
+
+/// The real Writer
+class SRECWriter : public Writer {
+  StringRef OutputFileName;
+  size_t TotalSize = 0;
+  std::vector<const SectionBase *> Sections;
+  size_t writeHeader(uint8_t *Buf);
+  size_t writeTerminator(uint8_t *Buf, uint8_t Type);
+  Error checkSection(const SectionBase &S) const;
+
+public:
+  ~SRECWriter() = default;
+  Error finalize() override;
+  Error write() override;
+  SRECWriter(Object &Obj, raw_ostream &OS, StringRef OutputFile)
+      : Writer(Obj, OS), OutputFileName(OutputFile) {}
+};
+
+/// Base class for SRecSectionWriter
+/// This class does not actually write anything. It is only used for size
+/// calculation.
+class SRECSectionWriterBase : public BinarySectionWriter {
+protected:
+  // Offset in the output buffer
+  uint64_t Offset;
+  // Sections start after the header
+  uint64_t HeaderSize;
+  // Type of records to write
+  uint8_t Type = SRecord::S1;
+  std::vector<SRecord> Records;
+  void writeSection(const SectionBase &S, ArrayRef<uint8_t> Data);
+
+public:
+  explicit SRECSectionWriterBase(WritableMemoryBuffer &Buf,
+                                 uint64_t StartOffset)
+      : BinarySectionWriter(Buf), Offset(StartOffset), HeaderSize(StartOffset) {
+  }
+
+  // Once the type of all records is known, write the records
+  virtual void writeRecords();
+  uint64_t getBufferOffset() const { return Offset; }
+  Error visit(const Section &S) final;
+  Error visit(const OwnedDataSection &S) final;
+  Error visit(const StringTableSection &S) override;
+  Error visit(const DynamicRelocationSection &S) final;
+  uint8_t getType() const { return Type; };
+  void writeRecord(SRecord &Record);
+  using BinarySectionWriter::visit;
+};
+
+// Real SREC section writer
+class SRECSectionWriter : public SRECSectionWriterBase {
+  void writeRecord(SRecord &Record, uint64_t Off);
+
+public:
+  SRECSectionWriter(WritableMemoryBuffer &Buf, uint64_t Offset)
+      : SRECSectionWriterBase(Buf, Offset) {}
+  Error visit(const StringTableSection &Sec) override;
+  void writeRecords() override;
+};
+
 class SectionBase {
 public:
   std::string Name;
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
new file mode 100644
index 00000000000000..ecd031c1760e75
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
@@ -0,0 +1,22 @@
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text1
+# 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:            .text2
+  # Entire secion is outside of 32-bit range
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0xFFFFFFFF0
+    AddressAlign:    0x8
+    Content:         "0001020304"
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
new file mode 100644
index 00000000000000..30190283b09c52
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
@@ -0,0 +1,46 @@
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .data1
+# Records for this section should come last
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Content:         "11111111111111111111"
+    Address:         0xEFFFFF
+    AddressAlign:    0x8
+  - Name:            .data2
+# This section overlaps 24-bit address boundary, so we expect
+# its record type to be S3
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Content:         "3031323334353637383940"
+    Address:         0xFFFFF8
+    AddressAlign:    0x8
+  - Name:            .text
+# This section's contents exceeds default line length of 16 bytes
+# so we expect two lines created for it. Records for this section
+# should appear before records for the previous section.
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x8
+    Content:         "000102030405060708090A0B0C0D0E0F1011121314"
+  - Name:            .bss
+# NOBITS sections are not written
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x10100
+    Size:            0x1000
+    AddressAlign:    0x8
+  - Name:            .dummy
+# Non-allocatable sections are not written
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+    Address:         0x20FFF8
+    Size:            65536
+    AddressAlign:    0x8
+
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
new file mode 100644
index 00000000000000..3f4b27ba3ae3ce
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
@@ -0,0 +1,45 @@
+# A test where LMA and VMA are different. Records should use the LMA
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+  Entry:           0x8000
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    FirstSec:        .text
+    LastSec:         .text
+    VAddr:           0x8000
+    Align:           0x1000
+    Offset:          0x1000
+  - Type:            PT_LOAD
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .bss
+    LastSec:         .data
+    VAddr:           0x20000
+    PAddr:           0x8005
+    Align:           0x1000
+    Offset:          0x2000
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x8000
+    AddressAlign:    0x4
+    Offset:          0x1000
+    Content:         '0020048090'
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x20000
+    AddressAlign:    0x1
+    Offset:          0x2000
+    Size:            0x4
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x20004
+    AddressAlign:    0x1
+    Content:         FF00
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
new file mode 100644
index 00000000000000..9624e33807873b
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -0,0 +1,44 @@
+# RUN: yaml2obj %p/Inputs/srec-elf-sections.yaml -o %t
+# RUN: llvm-objcopy -O srec %t - | FileCheck %s
+
+# Check that section address range overlapping 32 bit range
+# triggers an error
+# RUN: yaml2obj %p/Inputs/srec-elf-sections-err.yaml -o %t.err
+# RUN: not llvm-objcopy -O srec --only-section=.text1 %t.err - 2>&1 \
+#   RUN: | FileCheck %s --check-prefix=BAD-ADDR
+# RUN: not llvm-objcopy -O srec --only-section=.text2 %t.err - 2>&1 \
+#   RUN: | FileCheck %s --check-prefix=BAD-ADDR2
+
+# Check that zero length section is not written
+# RUN: llvm-objcopy -O srec --only-section=.text %t.err - \
+#   RUN:| FileCheck %s --check-prefix=ZERO_SIZE_SEC
+
+# Start address which exceeds 32 bit range triggers an error
+# RUN: not llvm-objcopy -O srec --set-start=0xF00000000 %t - 2>&1 \
+#   RUN: | FileCheck %s --check-prefix=BAD_START
+
+# Records should use LMA instead of VMA
+# RUN: yaml2obj %p/Inputs/srec-elf-segments.yaml -o %t.lma
+# RUN: llvm-objcopy -O srec %t.lma - | FileCheck %s --check-prefix=LMA
+
+CHECK: S00400002DCE
+CHECK-NEXT: S31500001000000102030405060708090A0B0C0D0E0F62
+CHECK-NEXT: S30A0000101010111213147B
+CHECK-NEXT: S30F00EFFFFF1111111111111111111159
+CHECK-NEXT: S31000FFFFF83031323334353637383940AC
+CHECK-NEXT: S70500000000FA
+
+LMA: S00400002DCE
+LMA-NEXT: S1088000002004809043
+LMA-NEXT: S1058009FF0072
+LMA-NEXT: S90380007C
+
+# BAD-ADDR: Section '.text1' address range [0xfffffff8, 0x100000000] is not 32 bit
+# BAD-ADDR2: Section '.text2' address range [0xffffffff0, 0xffffffff4] is not 32 bit
+
+# there should be no records besides header and terminator
+# ZERO_SIZE_SEC-NOT: {{S[1-8]}}
+# ZERO_SIZE_SEC: S00400002DCE
+# ZERO_SIZE_SEC-NEXT: S9030000FC
+
+# BAD_START: Entry point address 0xf00000000 overflows 32 bits
\ No newline at end of file
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
index f15307181fad61..3188dde8393d68 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
+++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
@@ -687,6 +687,7 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
   Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
                             .Case("binary", FileFormat::Binary)
                             .Case("ihex", FileFormat::IHex)
+                            .Case("srec", FileFormat::SREC)
                             .Default(FileFormat::Unspecified);
   if (Config.OutputFormat == FileFormat::Unspecified) {
     if (OutputFormat.empty()) {
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index 558359eca3cd72..87683e17207f73 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -122,6 +122,7 @@ static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr,
   case FileFormat::Binary:
   case FileFormat::IHex:
   case FileFormat::Unspecified:
+  case FileFormat::SREC:
     Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig();
     if (!ELFConfig)
       return ELFConfig.takeError();

>From 8a559f4d25a4d48278f6c3cd4f12cb5bf8225ad5 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Tue, 19 Dec 2023 09:36:43 -0800
Subject: [PATCH 2/7] Update documentation

---
 llvm/docs/CommandGuide/llvm-objcopy.rst | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst
index 6e13cd94b92fda..1f084b425ffa8c 100644
--- a/llvm/docs/CommandGuide/llvm-objcopy.rst
+++ b/llvm/docs/CommandGuide/llvm-objcopy.rst
@@ -539,8 +539,13 @@ options. For GNU :program:`objcopy` compatibility, the values are all bfdnames.
 - `elf32-sparc`
 - `elf32-sparcel`
 
-Additionally, all targets except `binary` and `ihex` can have `-freebsd` as a
-suffix.
+The following formats are suppoprted by :program:`llvm-objcopy` for the
+:option:`--output-target` only:
+
+- `srec`
+
+Additionally, all targets except `binary`, `ihex`, and `srec` can have
+`-freebsd` as a suffix.
 
 BINARY INPUT AND OUTPUT
 -----------------------

>From 2f1ba89ed181fc040cf50d1ab26e40a2e4a3045c Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Tue, 19 Dec 2023 11:41:51 -0800
Subject: [PATCH 3/7] simplify patch

Ignores sections and only writes the header to build functionality
incrementally.
---
 llvm/lib/ObjCopy/ELF/ELFObject.cpp            | 151 +-----------------
 llvm/lib/ObjCopy/ELF/ELFObject.h              |  54 +------
 .../ELF/Inputs/srec-elf-sections-err.yaml     |  22 ---
 .../ELF/Inputs/srec-elf-sections.yaml         |   2 +
 .../ELF/Inputs/srec-elf-segments.yaml         |  45 ------
 .../tools/llvm-objcopy/ELF/srec-writer.test   |  45 +-----
 6 files changed, 18 insertions(+), 301 deletions(-)
 delete mode 100644 llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
 delete mode 100644 llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml

diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index 5868168700a16a..199c3db7004f9a 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -324,14 +324,6 @@ Expected<IHexRecord> IHexRecord::parse(StringRef Line) {
   return Rec;
 }
 
-static uint64_t sectionLMA(const SectionBase *Sec) {
-  Segment *Seg = Sec->ParentSegment;
-  if (Seg && Seg->Type == PT_LOAD && Seg->VAddr <= Sec->Addr &&
-      (Seg->VAddr + Seg->MemSize > Sec->Addr))
-    return Sec->Addr - Seg->VAddr + Seg->PAddr;
-  return Sec->Addr;
-}
-
 static uint64_t sectionPhysicalAddr(const SectionBase *Sec) {
   Segment *Seg = Sec->ParentSegment;
   if (Seg && Seg->Type != ELF::PT_LOAD)
@@ -2821,80 +2813,6 @@ Error IHexWriter::finalize() {
   return Error::success();
 }
 
-Error SRECSectionWriterBase::visit(const StringTableSection &Sec) {
-  // Check that sizer has already done its work
-  assert(Sec.Size == Sec.StrTabBuilder.getSize());
-  // we don't need to write anything here the real writer has already done it.
-  return Error::success();
-}
-
-Error SRECSectionWriterBase::visit(const Section &Sec) {
-  writeSection(Sec, Sec.Contents);
-  return Error::success();
-}
-
-Error SRECSectionWriterBase::visit(const OwnedDataSection &Sec) {
-  writeSection(Sec, Sec.Data);
-  return Error::success();
-}
-
-Error SRECSectionWriterBase::visit(const DynamicRelocationSection &Sec) {
-  writeSection(Sec, Sec.Contents);
-  return Error::success();
-}
-
-void SRECSectionWriter::writeRecord(SRecord &Record, uint64_t Off) {
-  SRecLineData Data = Record.toString();
-  memcpy(Out.getBufferStart() + Off, Data.data(), Data.size());
-}
-
-void SRECSectionWriter::writeRecords() {
-  uint64_t Off = HeaderSize;
-  for (SRecord &Record : Records) {
-    Record.Type = Type;
-    writeRecord(Record, Off);
-    Off += Record.getSize();
-  }
-  Offset = Off;
-}
-
-void SRECSectionWriterBase::writeRecords() {
-  uint64_t Off = HeaderSize;
-  for (SRecord &Record : Records) {
-    Record.Type = Type;
-    Off += Record.getSize();
-  }
-  Offset = Off;
-}
-
-void SRECSectionWriterBase::writeSection(const SectionBase &S,
-                                         ArrayRef<uint8_t> Data) {
-  const uint32_t ChunkSize = 16;
-  uint32_t Address = sectionLMA(&S);
-  uint32_t EndAddr = Address + S.Size - 1;
-  if (isUInt<16>(EndAddr))
-    Type = std::max(static_cast<uint8_t>(SRecord::S1), Type);
-  else if (isUInt<24>(EndAddr))
-    Type = std::max(static_cast<uint8_t>(SRecord::S2), Type);
-  else
-    Type = SRecord::S3;
-  while (!Data.empty()) {
-    uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
-    SRecord Record{Type, Address, Data.take_front(DataSize)};
-    Records.push_back(Record);
-    Data = Data.drop_front(DataSize);
-    Address += DataSize;
-  }
-}
-
-Error SRECSectionWriter::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);
-  return Error::success();
-}
-
 SRecLineData SRecord::toString() const {
   SRecLineData Line(getSize());
   auto *Iter = Line.begin();
@@ -2953,7 +2871,9 @@ uint8_t SRecord::getCount() const {
 }
 
 SRecord SRecord::getHeader(StringRef FileName) {
-  // limit header to 40 characters
+  // Header is a record with Type S0, Address 0, and Data that is a
+  // vendor-specific text comment. For the comment we will use the output file
+  // name truncated to 40 characters.
   StringRef HeaderContents = FileName.slice(0, 40);
   ArrayRef<uint8_t> Data(
       reinterpret_cast<const uint8_t *>(HeaderContents.data()),
@@ -2967,81 +2887,26 @@ size_t SRECWriter::writeHeader(uint8_t *Buf) {
   return Record.size();
 }
 
-size_t SRECWriter::writeTerminator(uint8_t *Buf, uint8_t Type) {
-  assert(Type >= 7 && Type <= 9);
-  uint32_t Entry = Obj.Entry;
-  SRecLineData Data = SRecord{Type, Entry, {}}.toString();
-  memcpy(Buf, Data.data(), Data.size());
-  return Data.size();
-}
-
 Error SRECWriter::write() {
   uint32_t HeaderSize =
       writeHeader(reinterpret_cast<uint8_t *>(Buf->getBufferStart()));
-  SRECSectionWriter Writer(*Buf, HeaderSize);
-  for (const SectionBase *S : Sections) {
-    if (Error E = S->accept(Writer))
-      return E;
-  }
-  Writer.writeRecords();
-  uint64_t Offset = Writer.getBufferOffset();
-  /// S1 terminates with S9, S2 with S8, S3 with S7
-  uint8_t TerminatorType = 10 - Writer.getType();
-  Offset += writeTerminator(
-      reinterpret_cast<uint8_t *>(Buf->getBufferStart() + Offset),
-      TerminatorType);
-  assert(Offset == TotalSize);
-  Out.write(Buf->getBufferStart(), Buf->getBufferSize());
-  return Error::success();
-}
 
-Error SRECWriter::checkSection(const SectionBase &S) const {
-  if (!isUInt<32>(S.Addr + S.Size - 1))
-    return createStringError(
-        errc::invalid_argument,
-        "Section '%s' address range [0x%llx, 0x%llx] is not 32 bit",
-        S.Name.c_str(), S.Addr, S.Addr + S.Size - 1);
+  // FIXME: we currently only write the header and nothing else
+  assert(HeaderSize == TotalSize);
+  Out.write(Buf->getBufferStart(), Buf->getBufferSize());
   return Error::success();
 }
 
 Error SRECWriter::finalize() {
-  // We can't write 64-bit addresses.
-  if (!isUInt<32>(Obj.Entry))
-    return createStringError(errc::invalid_argument,
-                             "Entry point address 0x%llx overflows 32 bits",
-                             Obj.Entry);
-
-  for (const SectionBase &S : Obj.sections()) {
-    if ((S.Flags & ELF::SHF_ALLOC) && S.Type != ELF::SHT_NOBITS && S.Size > 0) {
-      if (Error E = checkSection(S))
-        return E;
-      Sections.push_back(&S);
-    }
-  }
-
-  llvm::sort(Sections, [](const SectionBase *A, const SectionBase *B) {
-    return sectionLMA(A) < sectionLMA(B);
-  });
-
   std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
       WritableMemoryBuffer::getNewMemBuffer(0);
   if (!EmptyBuffer)
     return createStringError(errc::not_enough_memory,
                              "failed to allocate memory buffer of 0 bytes");
 
-  SRECSectionWriterBase LengthCalc(*EmptyBuffer, 0);
-  for (const SectionBase *Sec : Sections)
-    if (Error Err = Sec->accept(LengthCalc))
-      return Err;
-
-  LengthCalc.writeRecords();
-
-  // We need to add the size of the Header and Terminator records
   SRecord Header = SRecord::getHeader(OutputFileName);
-  uint8_t TerminatorType = 10 - LengthCalc.getType();
-  SRecord Terminator = {TerminatorType, static_cast<uint32_t>(Obj.Entry), {}};
-  TotalSize =
-      LengthCalc.getBufferOffset() + Header.getSize() + Terminator.getSize();
+  TotalSize = Header.getSize();
+
   Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
   if (!Buf)
     return createStringError(errc::not_enough_memory,
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index c33d096e32fe35..14a567f5f576b6 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -172,8 +172,6 @@ template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor {
   friend class SectionWriter;                                                  \
   friend class IHexSectionWriterBase;                                          \
   friend class IHexSectionWriter;                                              \
-  friend class SRECSectionWriterBase;                                          \
-  friend class SRECSectionWriter;                                              \
   template <class ELFT> friend class ELFSectionWriter;                         \
   template <class ELFT> friend class ELFSectionSizer;
 
@@ -404,7 +402,6 @@ struct SRecord {
   uint8_t getChecksum() const;
   size_t getSize() const;
   static SRecord getHeader(StringRef FileName);
-  static SRecord getTerminator(uint8_t Type, uint32_t Entry);
   enum Type : uint8_t {
     // Vendor specific text comment
     S0 = 0,
@@ -427,19 +424,14 @@ struct SRecord {
     // Terminates a series of S1 records
     S9 = 9
   };
-
-private:
-  uint16_t getCheckSum(StringRef S) const;
 };
 
-/// The real Writer
+/// FIXME: This is currently a contrived writer that only writes the
+/// Header record and nothing else
 class SRECWriter : public Writer {
   StringRef OutputFileName;
   size_t TotalSize = 0;
-  std::vector<const SectionBase *> Sections;
   size_t writeHeader(uint8_t *Buf);
-  size_t writeTerminator(uint8_t *Buf, uint8_t Type);
-  Error checkSection(const SectionBase &S) const;
 
 public:
   ~SRECWriter() = default;
@@ -449,48 +441,6 @@ class SRECWriter : public Writer {
       : Writer(Obj, OS), OutputFileName(OutputFile) {}
 };
 
-/// Base class for SRecSectionWriter
-/// This class does not actually write anything. It is only used for size
-/// calculation.
-class SRECSectionWriterBase : public BinarySectionWriter {
-protected:
-  // Offset in the output buffer
-  uint64_t Offset;
-  // Sections start after the header
-  uint64_t HeaderSize;
-  // Type of records to write
-  uint8_t Type = SRecord::S1;
-  std::vector<SRecord> Records;
-  void writeSection(const SectionBase &S, ArrayRef<uint8_t> Data);
-
-public:
-  explicit SRECSectionWriterBase(WritableMemoryBuffer &Buf,
-                                 uint64_t StartOffset)
-      : BinarySectionWriter(Buf), Offset(StartOffset), HeaderSize(StartOffset) {
-  }
-
-  // Once the type of all records is known, write the records
-  virtual void writeRecords();
-  uint64_t getBufferOffset() const { return Offset; }
-  Error visit(const Section &S) final;
-  Error visit(const OwnedDataSection &S) final;
-  Error visit(const StringTableSection &S) override;
-  Error visit(const DynamicRelocationSection &S) final;
-  uint8_t getType() const { return Type; };
-  void writeRecord(SRecord &Record);
-  using BinarySectionWriter::visit;
-};
-
-// Real SREC section writer
-class SRECSectionWriter : public SRECSectionWriterBase {
-  void writeRecord(SRecord &Record, uint64_t Off);
-
-public:
-  SRECSectionWriter(WritableMemoryBuffer &Buf, uint64_t Offset)
-      : SRECSectionWriterBase(Buf, Offset) {}
-  Error visit(const StringTableSection &Sec) override;
-  void writeRecords() override;
-};
 
 class SectionBase {
 public:
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
deleted file mode 100644
index ecd031c1760e75..00000000000000
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-!ELF
-FileHeader:
-  Class:           ELFCLASS64
-  Data:            ELFDATA2LSB
-  Type:            ET_EXEC
-  Machine:         EM_X86_64
-Sections:
-  - Name:            .text1
-# 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:            .text2
-  # Entire secion is outside of 32-bit range
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0xFFFFFFFF0
-    AddressAlign:    0x8
-    Content:         "0001020304"
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
index 30190283b09c52..1000c285a606d9 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
@@ -1,3 +1,5 @@
+# FIXME: Currently the sections in this file are ignored and we
+# only write the header
 !ELF
 FileHeader:
   Class:           ELFCLASS64
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
deleted file mode 100644
index 3f4b27ba3ae3ce..00000000000000
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
-# A test where LMA and VMA are different. Records should use the LMA
-!ELF
-FileHeader:
-  Class:           ELFCLASS64
-  Data:            ELFDATA2LSB
-  Type:            ET_EXEC
-  Machine:         EM_X86_64
-  Entry:           0x8000
-ProgramHeaders:
-  - Type:            PT_LOAD
-    Flags:           [ PF_X, PF_R ]
-    FirstSec:        .text
-    LastSec:         .text
-    VAddr:           0x8000
-    Align:           0x1000
-    Offset:          0x1000
-  - Type:            PT_LOAD
-    Flags:           [ PF_W, PF_R ]
-    FirstSec:        .bss
-    LastSec:         .data
-    VAddr:           0x20000
-    PAddr:           0x8005
-    Align:           0x1000
-    Offset:          0x2000
-Sections:
-  - Name:            .text
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x8000
-    AddressAlign:    0x4
-    Offset:          0x1000
-    Content:         '0020048090'
-  - Name:            .bss
-    Type:            SHT_NOBITS
-    Flags:           [ SHF_WRITE, SHF_ALLOC ]
-    Address:         0x20000
-    AddressAlign:    0x1
-    Offset:          0x2000
-    Size:            0x4
-  - Name:            .data
-    Type:            SHT_PROGBITS
-    Flags:           [ SHF_WRITE, SHF_ALLOC ]
-    Address:         0x20004
-    AddressAlign:    0x1
-    Content:         FF00
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
index 9624e33807873b..4927adc4c74dfc 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -1,44 +1,11 @@
 # RUN: yaml2obj %p/Inputs/srec-elf-sections.yaml -o %t
 # RUN: llvm-objcopy -O srec %t - | FileCheck %s
 
-# Check that section address range overlapping 32 bit range
-# triggers an error
-# RUN: yaml2obj %p/Inputs/srec-elf-sections-err.yaml -o %t.err
-# RUN: not llvm-objcopy -O srec --only-section=.text1 %t.err - 2>&1 \
-#   RUN: | FileCheck %s --check-prefix=BAD-ADDR
-# RUN: not llvm-objcopy -O srec --only-section=.text2 %t.err - 2>&1 \
-#   RUN: | FileCheck %s --check-prefix=BAD-ADDR2
-
-# Check that zero length section is not written
-# RUN: llvm-objcopy -O srec --only-section=.text %t.err - \
-#   RUN:| FileCheck %s --check-prefix=ZERO_SIZE_SEC
-
-# Start address which exceeds 32 bit range triggers an error
-# RUN: not llvm-objcopy -O srec --set-start=0xF00000000 %t - 2>&1 \
-#   RUN: | FileCheck %s --check-prefix=BAD_START
-
-# Records should use LMA instead of VMA
-# RUN: yaml2obj %p/Inputs/srec-elf-segments.yaml -o %t.lma
-# RUN: llvm-objcopy -O srec %t.lma - | FileCheck %s --check-prefix=LMA
-
+# The record type for the header should be S0 with a 2 byte address
+# of 0. For an output file named "-" the header data field should contain "2D"
+# The byte count field should therefore have a value of 4: 2 bytes for address,
+# 1 byte for output file and 1 byte for checksum
 CHECK: S00400002DCE
-CHECK-NEXT: S31500001000000102030405060708090A0B0C0D0E0F62
-CHECK-NEXT: S30A0000101010111213147B
-CHECK-NEXT: S30F00EFFFFF1111111111111111111159
-CHECK-NEXT: S31000FFFFF83031323334353637383940AC
-CHECK-NEXT: S70500000000FA
-
-LMA: S00400002DCE
-LMA-NEXT: S1088000002004809043
-LMA-NEXT: S1058009FF0072
-LMA-NEXT: S90380007C
-
-# BAD-ADDR: Section '.text1' address range [0xfffffff8, 0x100000000] is not 32 bit
-# BAD-ADDR2: Section '.text2' address range [0xffffffff0, 0xffffffff4] is not 32 bit
-
-# there should be no records besides header and terminator
-# ZERO_SIZE_SEC-NOT: {{S[1-8]}}
-# ZERO_SIZE_SEC: S00400002DCE
-# ZERO_SIZE_SEC-NEXT: S9030000FC
+CHECK-EMPTY:
 
-# BAD_START: Entry point address 0xf00000000 overflows 32 bits
\ No newline at end of file
+# FIXME: write the rest of the records
\ No newline at end of file

>From 3d60a39f3fda4038face72b90bd62db252231f00 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Tue, 19 Dec 2023 11:52:05 -0800
Subject: [PATCH 4/7] fix formatting

---
 llvm/lib/ObjCopy/ELF/ELFObject.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 14a567f5f576b6..498be59308baf6 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -441,7 +441,6 @@ class SRECWriter : public Writer {
       : Writer(Obj, OS), OutputFileName(OutputFile) {}
 };
 
-
 class SectionBase {
 public:
   std::string Name;

>From 79ff44e9d81a54feba54a379cbb99c47ecdc6595 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Tue, 2 Jan 2024 09:59:58 -0800
Subject: [PATCH 5/7] write sections

---
 llvm/lib/ObjCopy/ELF/ELFObject.cpp            | 145 +++++++++++++++++-
 llvm/lib/ObjCopy/ELF/ELFObject.h              |  53 ++++++-
 .../ELF/Inputs/srec-elf-sections-err.yaml     |  22 +++
 .../ELF/Inputs/srec-elf-sections.yaml         |   2 -
 .../ELF/Inputs/srec-elf-segments.yaml         |  45 ++++++
 .../tools/llvm-objcopy/ELF/srec-writer.test   |  53 ++++++-
 6 files changed, 304 insertions(+), 16 deletions(-)
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
 create mode 100644 llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml

diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index 199c3db7004f9a..ce11e43ff86233 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -324,6 +324,14 @@ Expected<IHexRecord> IHexRecord::parse(StringRef Line) {
   return Rec;
 }
 
+static uint64_t sectionLMA(const SectionBase *Sec) {
+  Segment *Seg = Sec->ParentSegment;
+  if (Seg && Seg->Type == PT_LOAD && Seg->VAddr <= Sec->Addr &&
+      (Seg->VAddr + Seg->MemSize > Sec->Addr))
+    return Sec->Addr - Seg->VAddr + Seg->PAddr;
+  return Sec->Addr;
+}
+
 static uint64_t sectionPhysicalAddr(const SectionBase *Sec) {
   Segment *Seg = Sec->ParentSegment;
   if (Seg && Seg->Type != ELF::PT_LOAD)
@@ -2813,6 +2821,80 @@ Error IHexWriter::finalize() {
   return Error::success();
 }
 
+Error SRECSectionWriterBase::visit(const StringTableSection &Sec) {
+  // Check that sizer has already done its work
+  assert(Sec.Size == Sec.StrTabBuilder.getSize());
+  // we don't need to write anything here the real writer has already done it.
+  return Error::success();
+}
+
+Error SRECSectionWriterBase::visit(const Section &Sec) {
+  writeSection(Sec, Sec.Contents);
+  return Error::success();
+}
+
+Error SRECSectionWriterBase::visit(const OwnedDataSection &Sec) {
+  writeSection(Sec, Sec.Data);
+  return Error::success();
+}
+
+Error SRECSectionWriterBase::visit(const DynamicRelocationSection &Sec) {
+  writeSection(Sec, Sec.Contents);
+  return Error::success();
+}
+
+void SRECSectionWriter::writeRecord(SRecord &Record, uint64_t Off) {
+  SRecLineData Data = Record.toString();
+  memcpy(Out.getBufferStart() + Off, Data.data(), Data.size());
+}
+
+void SRECSectionWriter::writeRecords() {
+  uint64_t Off = HeaderSize;
+  for (SRecord &Record : Records) {
+    Record.Type = Type;
+    writeRecord(Record, Off);
+    Off += Record.getSize();
+  }
+  Offset = Off;
+}
+
+void SRECSectionWriterBase::writeRecords() {
+  uint64_t Off = HeaderSize;
+  for (SRecord &Record : Records) {
+    Record.Type = Type;
+    Off += Record.getSize();
+  }
+  Offset = Off;
+}
+
+void SRECSectionWriterBase::writeSection(const SectionBase &S,
+                                         ArrayRef<uint8_t> Data) {
+  const uint32_t ChunkSize = 16;
+  uint32_t Address = sectionLMA(&S);
+  uint32_t EndAddr = Address + S.Size - 1;
+  if (isUInt<16>(EndAddr))
+    Type = std::max(static_cast<uint8_t>(SRecord::S1), Type);
+  else if (isUInt<24>(EndAddr))
+    Type = std::max(static_cast<uint8_t>(SRecord::S2), Type);
+  else
+    Type = SRecord::S3;
+  while (!Data.empty()) {
+    uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
+    SRecord Record{Type, Address, Data.take_front(DataSize)};
+    Records.push_back(Record);
+    Data = Data.drop_front(DataSize);
+    Address += DataSize;
+  }
+}
+
+Error SRECSectionWriter::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);
+  return Error::success();
+}
+
 SRecLineData SRecord::toString() const {
   SRecLineData Line(getSize());
   auto *Iter = Line.begin();
@@ -2887,25 +2969,80 @@ size_t SRECWriter::writeHeader(uint8_t *Buf) {
   return Record.size();
 }
 
+size_t SRECWriter::writeTerminator(uint8_t *Buf, uint8_t Type) {
+  assert(Type >= 7 && Type <= 9);
+  uint32_t Entry = Obj.Entry;
+  SRecLineData Data = SRecord{Type, Entry, {}}.toString();
+  memcpy(Buf, Data.data(), Data.size());
+  return Data.size();
+}
+
 Error SRECWriter::write() {
   uint32_t HeaderSize =
       writeHeader(reinterpret_cast<uint8_t *>(Buf->getBufferStart()));
-
-  // FIXME: we currently only write the header and nothing else
-  assert(HeaderSize == TotalSize);
+  SRECSectionWriter Writer(*Buf, HeaderSize);
+  for (const SectionBase *S : Sections) {
+    if (Error E = S->accept(Writer))
+      return E;
+  }
+  Writer.writeRecords();
+  uint64_t Offset = Writer.getBufferOffset();
+  // S1 terminates with S9, S2 with S8, S3 with S7
+  uint8_t TerminatorType = 10 - Writer.getType();
+  Offset += writeTerminator(
+      reinterpret_cast<uint8_t *>(Buf->getBufferStart() + Offset),
+      TerminatorType);
+  assert(Offset == TotalSize);
   Out.write(Buf->getBufferStart(), Buf->getBufferSize());
   return Error::success();
 }
 
+Error SRECWriter::checkSection(const SectionBase &S) const {
+  if (!isUInt<32>(S.Addr + S.Size - 1))
+    return createStringError(
+        errc::invalid_argument,
+        "Section '%s' address range [0x%llx, 0x%llx] is not 32 bit",
+        S.Name.c_str(), S.Addr, S.Addr + S.Size - 1);
+  return Error::success();
+}
+
 Error SRECWriter::finalize() {
+  // We can't write 64-bit addresses.
+  if (!isUInt<32>(Obj.Entry))
+    return createStringError(errc::invalid_argument,
+                             "Entry point address 0x%llx overflows 32 bits",
+                             Obj.Entry);
+
+  for (const SectionBase &S : Obj.sections()) {
+    if ((S.Flags & ELF::SHF_ALLOC) && S.Type != ELF::SHT_NOBITS && S.Size > 0) {
+      if (Error E = checkSection(S))
+        return E;
+      Sections.push_back(&S);
+    }
+  }
+
+  llvm::sort(Sections, [](const SectionBase *A, const SectionBase *B) {
+    return sectionLMA(A) < sectionLMA(B);
+  });
+
   std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
       WritableMemoryBuffer::getNewMemBuffer(0);
   if (!EmptyBuffer)
     return createStringError(errc::not_enough_memory,
                              "failed to allocate memory buffer of 0 bytes");
 
+  SRECSectionWriterBase LengthCalc(*EmptyBuffer, 0);
+  for (const SectionBase *Sec : Sections)
+    if (Error Err = Sec->accept(LengthCalc))
+      return Err;
+
+  LengthCalc.writeRecords();
+  // We need to add the size of the Header and Terminator records
   SRecord Header = SRecord::getHeader(OutputFileName);
-  TotalSize = Header.getSize();
+  uint8_t TerminatorType = 10 - LengthCalc.getType();
+  SRecord Terminator = {TerminatorType, static_cast<uint32_t>(Obj.Entry), {}};
+  TotalSize =
+      LengthCalc.getBufferOffset() + Header.getSize() + Terminator.getSize();
 
   Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
   if (!Buf)
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 498be59308baf6..bf2604f3b75f81 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -172,6 +172,8 @@ template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor {
   friend class SectionWriter;                                                  \
   friend class IHexSectionWriterBase;                                          \
   friend class IHexSectionWriter;                                              \
+  friend class SRECSectionWriterBase;                                          \
+  friend class SRECSectionWriter;                                              \
   template <class ELFT> friend class ELFSectionWriter;                         \
   template <class ELFT> friend class ELFSectionSizer;
 
@@ -402,6 +404,7 @@ struct SRecord {
   uint8_t getChecksum() const;
   size_t getSize() const;
   static SRecord getHeader(StringRef FileName);
+  static SRecord getTerminator(uint8_t Type, uint32_t Entry);
   enum Type : uint8_t {
     // Vendor specific text comment
     S0 = 0,
@@ -426,12 +429,15 @@ struct SRecord {
   };
 };
 
-/// FIXME: This is currently a contrived writer that only writes the
-/// Header record and nothing else
+/// The real writer
 class SRECWriter : public Writer {
   StringRef OutputFileName;
   size_t TotalSize = 0;
+  std::vector<const SectionBase *> Sections;
+
   size_t writeHeader(uint8_t *Buf);
+  size_t writeTerminator(uint8_t *Buf, uint8_t Type);
+  Error checkSection(const SectionBase &S) const;
 
 public:
   ~SRECWriter() = default;
@@ -441,6 +447,49 @@ class SRECWriter : public Writer {
       : Writer(Obj, OS), OutputFileName(OutputFile) {}
 };
 
+/// Base class for SRecSectionWriter
+/// This class does not actually write anything. It is only used for size
+/// calculation.
+class SRECSectionWriterBase : public BinarySectionWriter {
+protected:
+  // Offset in the output buffer
+  uint64_t Offset;
+  // Sections start after the header
+  uint64_t HeaderSize;
+  // Type of records to write
+  uint8_t Type = SRecord::S1;
+  std::vector<SRecord> Records;
+  void writeSection(const SectionBase &S, ArrayRef<uint8_t> Data);
+
+public:
+  explicit SRECSectionWriterBase(WritableMemoryBuffer &Buf,
+                                 uint64_t StartOffset)
+      : BinarySectionWriter(Buf), Offset(StartOffset), HeaderSize(StartOffset) {
+  }
+
+  // Once the type of all records is known, write the records
+  virtual void writeRecords();
+  uint64_t getBufferOffset() const { return Offset; }
+  Error visit(const Section &S) final;
+  Error visit(const OwnedDataSection &S) final;
+  Error visit(const StringTableSection &S) override;
+  Error visit(const DynamicRelocationSection &S) final;
+  uint8_t getType() const { return Type; };
+  void writeRecord(SRecord &Record);
+  using BinarySectionWriter::visit;
+};
+
+// Real SREC section writer
+class SRECSectionWriter : public SRECSectionWriterBase {
+  void writeRecord(SRecord &Record, uint64_t Off);
+
+public:
+  SRECSectionWriter(WritableMemoryBuffer &Buf, uint64_t Offset)
+      : SRECSectionWriterBase(Buf, Offset) {}
+  Error visit(const StringTableSection &Sec) override;
+  void writeRecords() override;
+};
+
 class SectionBase {
 public:
   std::string Name;
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
new file mode 100644
index 00000000000000..ecd031c1760e75
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
@@ -0,0 +1,22 @@
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text1
+# 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:            .text2
+  # Entire secion is outside of 32-bit range
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0xFFFFFFFF0
+    AddressAlign:    0x8
+    Content:         "0001020304"
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
index 1000c285a606d9..30190283b09c52 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
@@ -1,5 +1,3 @@
-# FIXME: Currently the sections in this file are ignored and we
-# only write the header
 !ELF
 FileHeader:
   Class:           ELFCLASS64
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
new file mode 100644
index 00000000000000..3f4b27ba3ae3ce
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
@@ -0,0 +1,45 @@
+# A test where LMA and VMA are different. Records should use the LMA
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+  Entry:           0x8000
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    FirstSec:        .text
+    LastSec:         .text
+    VAddr:           0x8000
+    Align:           0x1000
+    Offset:          0x1000
+  - Type:            PT_LOAD
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .bss
+    LastSec:         .data
+    VAddr:           0x20000
+    PAddr:           0x8005
+    Align:           0x1000
+    Offset:          0x2000
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x8000
+    AddressAlign:    0x4
+    Offset:          0x1000
+    Content:         '0020048090'
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x20000
+    AddressAlign:    0x1
+    Offset:          0x2000
+    Size:            0x4
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x20004
+    AddressAlign:    0x1
+    Content:         FF00
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
index 4927adc4c74dfc..b3c7813b8c3f71 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -1,11 +1,48 @@
 # RUN: yaml2obj %p/Inputs/srec-elf-sections.yaml -o %t
 # RUN: llvm-objcopy -O srec %t - | FileCheck %s
 
-# The record type for the header should be S0 with a 2 byte address
-# of 0. For an output file named "-" the header data field should contain "2D"
-# The byte count field should therefore have a value of 4: 2 bytes for address,
-# 1 byte for output file and 1 byte for checksum
-CHECK: S00400002DCE
-CHECK-EMPTY:
-
-# FIXME: write the rest of the records
\ No newline at end of file
+## Check that section address range overlapping 32 bit range
+## triggers an error
+# RUN: yaml2obj %p/Inputs/srec-elf-sections-err.yaml -o %t.err
+# RUN: not llvm-objcopy -O srec --only-section=.text1 %t.err - 2>&1 \
+#   RUN: | FileCheck %s --check-prefix=BAD-ADDR
+# RUN: not llvm-objcopy -O srec --only-section=.text2 %t.err - 2>&1 \
+#   RUN: | FileCheck %s --check-prefix=BAD-ADDR2
+
+## Check that zero length section is not written
+# RUN: llvm-objcopy -O srec --only-section=.text %t.err - \
+#   RUN:| FileCheck %s --check-prefix=ZERO_SIZE_SEC
+
+## Start address which exceeds 32 bit range triggers an error
+# RUN: not llvm-objcopy -O srec --set-start=0xF00000000 %t - 2>&1 \
+#   RUN: | FileCheck %s --check-prefix=BAD_START
+
+## Records should use LMA instead of VMA
+# RUN: yaml2obj %p/Inputs/srec-elf-segments.yaml -o %t.lma
+# RUN: llvm-objcopy -O srec %t.lma - | FileCheck %s --check-prefix=LMA
+
+## # The record type for the header should be S0 with a 2 byte address
+## of 0. For an output file named "-" the header data field should contain "2D"
+## The byte count field should therefore have a value of 4: 2 bytes for address,
+## 1 byte for output file and 1 byte for checksum
+# CHECK: S00400002DCE
+# CHECK-NEXT: S31500001000000102030405060708090A0B0C0D0E0F62
+# CHECK-NEXT: S30A0000101010111213147B
+# CHECK-NEXT: S30F00EFFFFF1111111111111111111159
+# CHECK-NEXT: S31000FFFFF83031323334353637383940AC
+# CHECK-NEXT: S70500000000FA
+
+# LMA: S00400002DCE
+# LMA-NEXT: S1088000002004809043
+# LMA-NEXT: S1058009FF0072
+# LMA-NEXT: S90380007C
+
+# BAD-ADDR: Section '.text1' address range [0xfffffff8, 0x100000000] is not 32 bit
+# BAD-ADDR2: Section '.text2' address range [0xffffffff0, 0xffffffff4] is not 32 bit
+
+# there should be no records besides header and terminator
+# ZERO_SIZE_SEC-NOT: {{S[1-8]}}
+# ZERO_SIZE_SEC: S00400002DCE
+# ZERO_SIZE_SEC-NEXT: S9030000FC
+
+# BAD_START: Entry point address 0xf00000000 overflows 32 bits

>From 2fd3ccf1bc5a83bff2b528217c35454f4ed25428 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Tue, 9 Jan 2024 09:43:20 -0800
Subject: [PATCH 6/7] use segment physical address

---
 llvm/lib/ObjCopy/ELF/ELFObject.cpp            | 12 +--
 .../ELF/Inputs/srec-elf-segments.yaml         | 79 +++++++++++--------
 .../tools/llvm-objcopy/ELF/srec-writer.test   | 18 +++--
 3 files changed, 60 insertions(+), 49 deletions(-)

diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index ce11e43ff86233..9805be64eb9a06 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -324,14 +324,6 @@ Expected<IHexRecord> IHexRecord::parse(StringRef Line) {
   return Rec;
 }
 
-static uint64_t sectionLMA(const SectionBase *Sec) {
-  Segment *Seg = Sec->ParentSegment;
-  if (Seg && Seg->Type == PT_LOAD && Seg->VAddr <= Sec->Addr &&
-      (Seg->VAddr + Seg->MemSize > Sec->Addr))
-    return Sec->Addr - Seg->VAddr + Seg->PAddr;
-  return Sec->Addr;
-}
-
 static uint64_t sectionPhysicalAddr(const SectionBase *Sec) {
   Segment *Seg = Sec->ParentSegment;
   if (Seg && Seg->Type != ELF::PT_LOAD)
@@ -2870,7 +2862,7 @@ void SRECSectionWriterBase::writeRecords() {
 void SRECSectionWriterBase::writeSection(const SectionBase &S,
                                          ArrayRef<uint8_t> Data) {
   const uint32_t ChunkSize = 16;
-  uint32_t Address = sectionLMA(&S);
+  uint32_t Address = sectionPhysicalAddr(&S);
   uint32_t EndAddr = Address + S.Size - 1;
   if (isUInt<16>(EndAddr))
     Type = std::max(static_cast<uint8_t>(SRecord::S1), Type);
@@ -3022,7 +3014,7 @@ Error SRECWriter::finalize() {
   }
 
   llvm::sort(Sections, [](const SectionBase *A, const SectionBase *B) {
-    return sectionLMA(A) < sectionLMA(B);
+    return sectionPhysicalAddr(A) < sectionPhysicalAddr(B);
   });
 
   std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
index 3f4b27ba3ae3ce..3d3fcc7d74edce 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
@@ -1,45 +1,60 @@
-# A test where LMA and VMA are different. Records should use the LMA
+# Test copied from ihex-elf-segments.yaml:
+# 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.
 !ELF
 FileHeader:
   Class:           ELFCLASS64
   Data:            ELFDATA2LSB
   Type:            ET_EXEC
   Machine:         EM_X86_64
-  Entry:           0x8000
-ProgramHeaders:
-  - Type:            PT_LOAD
-    Flags:           [ PF_X, PF_R ]
-    FirstSec:        .text
-    LastSec:         .text
-    VAddr:           0x8000
-    Align:           0x1000
-    Offset:          0x1000
-  - Type:            PT_LOAD
-    Flags:           [ PF_W, PF_R ]
-    FirstSec:        .bss
-    LastSec:         .data
-    VAddr:           0x20000
-    PAddr:           0x8005
-    Align:           0x1000
-    Offset:          0x2000
+  Entry:           0x100000
 Sections:
   - Name:            .text
     Type:            SHT_PROGBITS
     Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
-    Address:         0x8000
-    AddressAlign:    0x4
-    Offset:          0x1000
-    Content:         '0020048090'
+    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_WRITE, SHF_ALLOC ]
-    Address:         0x20000
-    AddressAlign:    0x1
-    Offset:          0x2000
-    Size:            0x4
-  - Name:            .data
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x10100
+    Size:            0x1000
+    AddressAlign:    0x8
+  - Name:            .dummy
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x20FFF8
+    Size:            3
+    AddressAlign:    0x8
+  - Name:            .nonalloc
     Type:            SHT_PROGBITS
-    Flags:           [ SHF_WRITE, SHF_ALLOC ]
-    Address:         0x20004
-    AddressAlign:    0x1
-    Content:         FF00
+    Flags:           [ ]
+    Address:         0x300000
+    Size:            1
+ProgramHeaders:
+  - Type:     PT_LOAD
+    Flags:    [ PF_X, PF_R ]
+    VAddr:    0xF00000000
+    PAddr:    0x100000
+    FirstSec: .text
+    LastSec:  .bss
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
index b3c7813b8c3f71..7192097e455b41 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -17,9 +17,9 @@
 # RUN: not llvm-objcopy -O srec --set-start=0xF00000000 %t - 2>&1 \
 #   RUN: | FileCheck %s --check-prefix=BAD_START
 
-## Records should use LMA instead of VMA
-# RUN: yaml2obj %p/Inputs/srec-elf-segments.yaml -o %t.lma
-# RUN: llvm-objcopy -O srec %t.lma - | FileCheck %s --check-prefix=LMA
+## Records should use segment physical addresses
+# RUN: yaml2obj %p/Inputs/ihex-elf-segments.yaml -o %t.seg
+# RUN: llvm-objcopy -O srec %t.seg - | FileCheck %s --check-prefix=PADDR
 
 ## # The record type for the header should be S0 with a 2 byte address
 ## of 0. For an output file named "-" the header data field should contain "2D"
@@ -32,10 +32,14 @@
 # CHECK-NEXT: S31000FFFFF83031323334353637383940AC
 # CHECK-NEXT: S70500000000FA
 
-# LMA: S00400002DCE
-# LMA-NEXT: S1088000002004809043
-# LMA-NEXT: S1058009FF0072
-# LMA-NEXT: S90380007C
+# PADDR: S00400002DCE
+# PADDR-NEXT: S214100000000102030405060708090A0B0C0D0E0F63
+# PADDR-NEXT: S20910001010111213147C
+# PADDR-NEXT: S20F10001830313233343536373839407B
+# PADDR-NEXT: S20810002840414243B9
+# PADDR-NEXT: S20F100030505152535455565758596003
+# PADDR-NEXT: S20720FFF8000000E1
+# PADDR-NEXT: S804100000EB
 
 # BAD-ADDR: Section '.text1' address range [0xfffffff8, 0x100000000] is not 32 bit
 # BAD-ADDR2: Section '.text2' address range [0xffffffff0, 0xffffffff4] is not 32 bit

>From cada7f6999ad2dcfbf0bbab67ec2c2ab7c669b7c Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Wed, 10 Jan 2024 23:08:22 -0800
Subject: [PATCH 7/7] allow sign-extended address

---
 llvm/lib/ObjCopy/ELF/ELFObject.cpp            | 31 ++++++++++++-------
 llvm/lib/ObjCopy/ELF/ELFObject.h              |  6 ++--
 .../ELF/Inputs/srec-elf-sections.yaml         |  7 +++++
 .../tools/llvm-objcopy/ELF/srec-writer.test   | 18 +++++++++++
 4 files changed, 47 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index 9805be64eb9a06..590cb799285882 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -2840,7 +2840,8 @@ void SRECSectionWriter::writeRecord(SRecord &Record, uint64_t Off) {
   memcpy(Out.getBufferStart() + Off, Data.data(), Data.size());
 }
 
-void SRECSectionWriter::writeRecords() {
+void SRECSectionWriter::writeRecords(uint32_t Entry) {
+  Type = std::max(Type, SRecord::getType(Entry));
   uint64_t Off = HeaderSize;
   for (SRecord &Record : Records) {
     Record.Type = Type;
@@ -2850,7 +2851,10 @@ void SRECSectionWriter::writeRecords() {
   Offset = Off;
 }
 
-void SRECSectionWriterBase::writeRecords() {
+void SRECSectionWriterBase::writeRecords(uint32_t Entry) {
+  // The ELF header could contain an entry point outside of the sections we have
+  // seen that does not fit the current record Type
+  Type = std::max(Type, SRecord::getType(Entry));
   uint64_t Off = HeaderSize;
   for (SRecord &Record : Records) {
     Record.Type = Type;
@@ -2864,12 +2868,7 @@ void SRECSectionWriterBase::writeSection(const SectionBase &S,
   const uint32_t ChunkSize = 16;
   uint32_t Address = sectionPhysicalAddr(&S);
   uint32_t EndAddr = Address + S.Size - 1;
-  if (isUInt<16>(EndAddr))
-    Type = std::max(static_cast<uint8_t>(SRecord::S1), Type);
-  else if (isUInt<24>(EndAddr))
-    Type = std::max(static_cast<uint8_t>(SRecord::S2), Type);
-  else
-    Type = SRecord::S3;
+  Type = std::max(SRecord::getType(EndAddr), Type);
   while (!Data.empty()) {
     uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
     SRecord Record{Type, Address, Data.take_front(DataSize)};
@@ -2944,6 +2943,14 @@ uint8_t SRecord::getCount() const {
   return getAddressSize() / 2 + DataSize + ChecksumSize;
 }
 
+uint8_t SRecord::getType(uint32_t Address) {
+  if (isUInt<16>(Address))
+    return SRecord::S1;
+  if (isUInt<24>(Address))
+    return SRecord::S2;
+  return SRecord::S3;
+}
+
 SRecord SRecord::getHeader(StringRef FileName) {
   // Header is a record with Type S0, Address 0, and Data that is a
   // vendor-specific text comment. For the comment we will use the output file
@@ -2977,7 +2984,7 @@ Error SRECWriter::write() {
     if (Error E = S->accept(Writer))
       return E;
   }
-  Writer.writeRecords();
+  Writer.writeRecords(Obj.Entry);
   uint64_t Offset = Writer.getBufferOffset();
   // S1 terminates with S9, S2 with S8, S3 with S7
   uint8_t TerminatorType = 10 - Writer.getType();
@@ -2990,7 +2997,7 @@ Error SRECWriter::write() {
 }
 
 Error SRECWriter::checkSection(const SectionBase &S) const {
-  if (!isUInt<32>(S.Addr + S.Size - 1))
+  if (addressOverflows32bit(S.Addr + S.Size - 1))
     return createStringError(
         errc::invalid_argument,
         "Section '%s' address range [0x%llx, 0x%llx] is not 32 bit",
@@ -3000,7 +3007,7 @@ Error SRECWriter::checkSection(const SectionBase &S) const {
 
 Error SRECWriter::finalize() {
   // We can't write 64-bit addresses.
-  if (!isUInt<32>(Obj.Entry))
+  if (addressOverflows32bit(Obj.Entry))
     return createStringError(errc::invalid_argument,
                              "Entry point address 0x%llx overflows 32 bits",
                              Obj.Entry);
@@ -3028,7 +3035,7 @@ Error SRECWriter::finalize() {
     if (Error Err = Sec->accept(LengthCalc))
       return Err;
 
-  LengthCalc.writeRecords();
+  LengthCalc.writeRecords(Obj.Entry);
   // We need to add the size of the Header and Terminator records
   SRecord Header = SRecord::getHeader(OutputFileName);
   uint8_t TerminatorType = 10 - LengthCalc.getType();
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index bf2604f3b75f81..38ae1e408f7780 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -404,7 +404,7 @@ struct SRecord {
   uint8_t getChecksum() const;
   size_t getSize() const;
   static SRecord getHeader(StringRef FileName);
-  static SRecord getTerminator(uint8_t Type, uint32_t Entry);
+  static uint8_t getType(uint32_t Address);
   enum Type : uint8_t {
     // Vendor specific text comment
     S0 = 0,
@@ -468,7 +468,7 @@ class SRECSectionWriterBase : public BinarySectionWriter {
   }
 
   // Once the type of all records is known, write the records
-  virtual void writeRecords();
+  virtual void writeRecords(uint32_t Entry);
   uint64_t getBufferOffset() const { return Offset; }
   Error visit(const Section &S) final;
   Error visit(const OwnedDataSection &S) final;
@@ -487,7 +487,7 @@ class SRECSectionWriter : public SRECSectionWriterBase {
   SRECSectionWriter(WritableMemoryBuffer &Buf, uint64_t Offset)
       : SRECSectionWriterBase(Buf, Offset) {}
   Error visit(const StringTableSection &Sec) override;
-  void writeRecords() override;
+  void writeRecords(uint32_t Entry) override;
 };
 
 class SectionBase {
diff --git a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
index 30190283b09c52..331419bc3438bc 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
+++ b/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
@@ -20,6 +20,13 @@ Sections:
     Content:         "3031323334353637383940"
     Address:         0xFFFFF8
     AddressAlign:    0x8
+# Sign-extended addresses are OK
+  - Name:            .data3
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0xFFFFFFFF80001000
+    AddressAlign:    0x8
+    Content:         "0001020304"
   - Name:            .text
 # This section's contents exceeds default line length of 16 bytes
 # so we expect two lines created for it. Records for this section
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
index 7192097e455b41..95652006ae45ff 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -13,10 +13,22 @@
 # RUN: llvm-objcopy -O srec --only-section=.text %t.err - \
 #   RUN:| FileCheck %s --check-prefix=ZERO_SIZE_SEC
 
+## Terminator should contain the entry point
+# RUN: llvm-objcopy -O srec --set-start=0xF0000000 %t \
+#   RUN: --only-section=.dummy - 2>&1 | FileCheck %s --check-prefix=ENTRY
+
+## Sign-extended entry point is OK
+# RUN: llvm-objcopy -O srec --set-start=0xFFFFFFFFF0000000 %t \
+#   RUN: --only-section=.dummy - 2>&1 | FileCheck %s --check-prefix=ENTRY
+
 ## Start address which exceeds 32 bit range triggers an error
 # RUN: not llvm-objcopy -O srec --set-start=0xF00000000 %t - 2>&1 \
 #   RUN: | FileCheck %s --check-prefix=BAD_START
 
+## Sign-extended start address which exceeds 32 bit range triggers an error
+# RUN: not llvm-objcopy -O srec --set-start=0xFFFFFFFF0F000000 %t - 2>&1 \
+#   RUN: | FileCheck %s --check-prefix=BAD_EXTENDED_START
+
 ## Records should use segment physical addresses
 # RUN: yaml2obj %p/Inputs/ihex-elf-segments.yaml -o %t.seg
 # RUN: llvm-objcopy -O srec %t.seg - | FileCheck %s --check-prefix=PADDR
@@ -30,6 +42,7 @@
 # CHECK-NEXT: S30A0000101010111213147B
 # CHECK-NEXT: S30F00EFFFFF1111111111111111111159
 # CHECK-NEXT: S31000FFFFF83031323334353637383940AC
+# CHECK-NEXT: S30A8000100000010203045B
 # CHECK-NEXT: S70500000000FA
 
 # PADDR: S00400002DCE
@@ -49,4 +62,9 @@
 # ZERO_SIZE_SEC: S00400002DCE
 # ZERO_SIZE_SEC-NEXT: S9030000FC
 
+# ENTRY: S00400002DCE
+# ENTRY-NEXT: S705F00000000A
+
 # BAD_START: Entry point address 0xf00000000 overflows 32 bits
+
+# BAD_EXTENDED_START: Entry point address 0xffffffff0f000000 overflows 32 bits



More information about the llvm-commits mailing list