[llvm] [llvm-objcopy] Support SREC output format (PR #75874)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 8 15:37:00 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 01/19] [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 02/19] 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 03/19] 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 04/19] 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 05/19] 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 06/19] 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 07/19] 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
>From 51c28c982ea5d89937dceacf72dfa983bdb09d2f Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Mon, 15 Jan 2024 09:28:04 -0800
Subject: [PATCH 08/19] Minor fixes
- comment style
- simplify test files (remove EXECINSTR, AddressAligns)
- add comments to asserts
- use enum values instead of raw literals
- fix error messages to conform to LLVM standards
---
llvm/lib/ObjCopy/ELF/ELFObject.cpp | 36 +++++----
llvm/lib/ObjCopy/ELF/ELFObject.h | 77 ++++++++++---------
.../ELF/Inputs/srec-elf-sections-err.yaml | 8 +-
.../ELF/Inputs/srec-elf-sections.yaml | 18 ++---
.../ELF/Inputs/srec-elf-segments.yaml | 8 +-
.../tools/llvm-objcopy/ELF/srec-writer.test | 10 +--
6 files changed, 76 insertions(+), 81 deletions(-)
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index 590cb799285882..5d6cfe51960a67 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -2814,9 +2814,11 @@ Error IHexWriter::finalize() {
}
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.
+ // Check that the sizer has already done its work.
+ assert(Sec.Size == Sec.StrTabBuilder.getSize() &&
+ "Expected section size to have been finalized");
+ // We don't need to write anything here because the real writer has already
+ // done it.
return Error::success();
}
@@ -2853,7 +2855,7 @@ void SRECSectionWriter::writeRecords(uint32_t Entry) {
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
+ // seen that does not fit the current record Type.
Type = std::max(Type, SRecord::getType(Entry));
uint64_t Off = HeaderSize;
for (SRecord &Record : Records) {
@@ -2879,7 +2881,8 @@ void SRECSectionWriterBase::writeSection(const SectionBase &S,
}
Error SRECSectionWriter::visit(const StringTableSection &Sec) {
- assert(Sec.Size == Sec.StrTabBuilder.getSize());
+ assert(Sec.Size == Sec.StrTabBuilder.getSize() &&
+ "Section size does not match the section's string table builder size");
std::vector<uint8_t> Data(Sec.Size);
Sec.StrTabBuilder.write(Data.data());
writeSection(Sec, Data);
@@ -2891,14 +2894,14 @@ SRecLineData SRecord::toString() const {
auto *Iter = Line.begin();
*Iter++ = 'S';
*Iter++ = '0' + Type;
- // write 1 byte (2 hex characters) record count
+ // Write 1 byte (2 hex characters) record count.
Iter = toHexStr(getCount(), Iter, 2);
- // write the address field with length depending on record type
+ // Write the address field with length depending on record type.
Iter = toHexStr(Address, Iter, getAddressSize());
- // write data byte by byte
+ // Write data byte by byte.
for (uint8_t X : Data)
Iter = toHexStr(X, Iter, 2);
- // write the 1 byte checksum
+ // Write the 1 byte checksum.
Iter = toHexStr(getChecksum(), Iter, 2);
*Iter++ = '\r';
*Iter++ = '\n';
@@ -2918,7 +2921,7 @@ uint8_t SRecord::getChecksum() const {
}
size_t SRecord::getSize() const {
- // 2 characters for type, count, checksum, CRLF
+ // Type, Count, Checksum, and CRLF are two characters each.
return 2 + 2 + getAddressSize() + Data.size() * 2 + 2 + 2;
}
@@ -2954,7 +2957,7 @@ uint8_t SRecord::getType(uint32_t Address) {
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
- // name truncated to 40 characters.
+ // name truncated to 40 characters to match the behavior of GNU objcopy.
StringRef HeaderContents = FileName.slice(0, 40);
ArrayRef<uint8_t> Data(
reinterpret_cast<const uint8_t *>(HeaderContents.data()),
@@ -2969,7 +2972,8 @@ size_t SRECWriter::writeHeader(uint8_t *Buf) {
}
size_t SRECWriter::writeTerminator(uint8_t *Buf, uint8_t Type) {
- assert(Type >= 7 && Type <= 9);
+ assert(Type >= SRecord::S7 && Type <= SRecord::S9 &&
+ "Invalid record type for terminator");
uint32_t Entry = Obj.Entry;
SRecLineData Data = SRecord{Type, Entry, {}}.toString();
memcpy(Buf, Data.data(), Data.size());
@@ -2986,7 +2990,8 @@ Error SRECWriter::write() {
}
Writer.writeRecords(Obj.Entry);
uint64_t Offset = Writer.getBufferOffset();
- // S1 terminates with S9, S2 with S8, S3 with S7
+
+ // An S1 record terminates with an S9 record, S2 with S8, and S3 with S7.
uint8_t TerminatorType = 10 - Writer.getType();
Offset += writeTerminator(
reinterpret_cast<uint8_t *>(Buf->getBufferStart() + Offset),
@@ -3000,16 +3005,17 @@ Error SRECWriter::checkSection(const SectionBase &S) const {
if (addressOverflows32bit(S.Addr + S.Size - 1))
return createStringError(
errc::invalid_argument,
- "Section '%s' address range [0x%llx, 0x%llx] is not 32 bit",
+ "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();
}
+/// FIXME: refactor to share code with ihex
Error SRECWriter::finalize() {
// We can't write 64-bit addresses.
if (addressOverflows32bit(Obj.Entry))
return createStringError(errc::invalid_argument,
- "Entry point address 0x%llx overflows 32 bits",
+ "entry point address 0x%llx overflows 32 bits",
Obj.Entry);
for (const SectionBase &S : Obj.sections()) {
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 38ae1e408f7780..a3ebb6b75d0471 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -399,38 +399,45 @@ struct SRecord {
ArrayRef<uint8_t> Data;
SRecLineData toString() const;
uint8_t getCount() const;
- // get address size in characters
+ // Get address size in characters.
uint8_t getAddressSize() const;
uint8_t getChecksum() const;
size_t getSize() const;
static SRecord getHeader(StringRef FileName);
static uint8_t getType(uint32_t Address);
+
enum Type : uint8_t {
- // Vendor specific text comment
+ // Vendor specific text comment.
S0 = 0,
- // Data that starts at a 16 bit address
+ // Data that starts at a 16 bit address.
S1 = 1,
- // Data that starts at a 24 bit address
+ // Data that starts at a 24 bit address.
S2 = 2,
- // Data that starts at a 32 bit address
+ // Data that starts at a 32 bit address.
S3 = 3,
- // Reserved
+ // Reserved.
S4 = 4,
- // 16 bit count of S1/S2/S3 records (optional)
+ // 16 bit count of S1/S2/S3 records (optional).
S5 = 5,
- // 32 bit count of S1/S2/S3 records (optional)
+ // 32 bit count of S1/S2/S3 records (optional).
S6 = 6,
- // Terminates a series of S3 records
+ // Terminates a series of S3 records.
S7 = 7,
- // Terminates a series of S2 records
+ // Terminates a series of S2 records.
S8 = 8,
- // Terminates a series of S1 records
+ // Terminates a series of S1 records.
S9 = 9
};
};
-/// The real writer
class SRECWriter : public Writer {
+public:
+ SRECWriter(Object &Obj, raw_ostream &OS, StringRef OutputFile)
+ : Writer(Obj, OS), OutputFileName(OutputFile) {}
+ Error finalize() override;
+ Error write() override;
+
+private:
StringRef OutputFileName;
size_t TotalSize = 0;
std::vector<const SectionBase *> Sections;
@@ -438,56 +445,52 @@ class SRECWriter : public Writer {
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
+ using BinarySectionWriter::visit;
+
+ // Once the type of all records is known, write the records.
virtual void writeRecords(uint32_t Entry);
uint64_t getBufferOffset() const { return Offset; }
- Error visit(const Section &S) final;
- Error visit(const OwnedDataSection &S) final;
+ Error visit(const Section &S) override;
+ Error visit(const OwnedDataSection &S) override;
Error visit(const StringTableSection &S) override;
- Error visit(const DynamicRelocationSection &S) final;
+ Error visit(const DynamicRelocationSection &S) override;
uint8_t getType() const { return Type; };
void writeRecord(SRecord &Record);
- using BinarySectionWriter::visit;
+
+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);
};
-// Real SREC section writer
+// 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(uint32_t Entry) override;
+
+private:
+ void writeRecord(SRecord &Record, uint64_t Off);
};
class SectionBase {
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
index ecd031c1760e75..db0c01763142d9 100644
--- 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
@@ -9,14 +9,12 @@ Sections:
# Part of section data is in 32-bit address range
# and part isn't.
Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Flags: [ SHF_ALLOC]
Address: 0xFFFFFFF8
- AddressAlign: 0x8
Content: "000102030405060708"
- Name: .text2
- # Entire secion is outside of 32-bit range
+ # Entire secion is outside of 32-bit range.
Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Flags: [ SHF_ALLOC ]
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 331419bc3438bc..838ea68420680f 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
@@ -6,48 +6,42 @@ FileHeader:
Machine: EM_X86_64
Sections:
- Name: .data1
-# Records for this section should come last
+# 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
+# its record type to be S3.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "3031323334353637383940"
Address: 0xFFFFF8
AddressAlign: 0x8
-# Sign-extended addresses are OK
+# 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
# should appear before records for the previous section.
Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Flags: [ SHF_ALLOC ]
Address: 0x1000
- AddressAlign: 0x8
Content: "000102030405060708090A0B0C0D0E0F1011121314"
- Name: .bss
-# NOBITS sections are not written
+# 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
+# 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
index 3d3fcc7d74edce..a18aac593a4e20 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
@@ -12,40 +12,34 @@ FileHeader:
Sections:
- Name: .text
Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Flags: [ SHF_ALLOC ]
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: [ SHF_ALLOC ]
Address: 0x20FFF8
Size: 3
- AddressAlign: 0x8
- Name: .nonalloc
Type: SHT_PROGBITS
Flags: [ ]
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
index 95652006ae45ff..e9f427a0466973 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -33,7 +33,7 @@
# 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
+## 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
@@ -54,8 +54,8 @@
# 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
+# 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]}}
@@ -65,6 +65,6 @@
# ENTRY: S00400002DCE
# ENTRY-NEXT: S705F00000000A
-# BAD_START: Entry point address 0xf00000000 overflows 32 bits
+# BAD_START: entry point address 0xf00000000 overflows 32 bits
-# BAD_EXTENDED_START: Entry point address 0xffffffff0f000000 overflows 32 bits
+# BAD_EXTENDED_START: entry point address 0xffffffff0f000000 overflows 32 bits
>From b0fd4f310172946f4fc61ab794a41899cd790c3d Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Tue, 16 Jan 2024 08:56:34 -0800
Subject: [PATCH 09/19] inline tests
---
.../ELF/Inputs/srec-elf-sections-err.yaml | 20 --
.../ELF/Inputs/srec-elf-sections.yaml | 47 -----
.../ELF/Inputs/srec-elf-segments.yaml | 54 ------
.../tools/llvm-objcopy/ELF/srec-writer.test | 181 +++++++++++++++---
4 files changed, 152 insertions(+), 150 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-sections.yaml
delete mode 100644 llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
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 db0c01763142d9..00000000000000
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections-err.yaml
+++ /dev/null
@@ -1,20 +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]
- Address: 0xFFFFFFF8
- Content: "000102030405060708"
- - Name: .text2
- # Entire secion is outside of 32-bit range.
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- Address: 0xFFFFFFFF0
- 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
deleted file mode 100644
index 838ea68420680f..00000000000000
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-sections.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-!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
- - 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
-# Sign-extended addresses are OK.
- - Name: .data3
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- Address: 0xFFFFFFFF80001000
- 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
-# should appear before records for the previous section.
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- Address: 0x1000
- Content: "000102030405060708090A0B0C0D0E0F1011121314"
- - Name: .bss
-# NOBITS sections are not written.
- Type: SHT_NOBITS
- Flags: [ SHF_ALLOC ]
- Address: 0x10100
- Size: 0x1000
- - Name: .dummy
-# Non-allocatable sections are not written.
- Type: SHT_PROGBITS
- Flags: [ ]
- Address: 0x20FFF8
- Size: 65536
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 a18aac593a4e20..00000000000000
--- a/llvm/test/tools/llvm-objcopy/ELF/Inputs/srec-elf-segments.yaml
+++ /dev/null
@@ -1,54 +0,0 @@
-# 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: 0x100000
-Sections:
- - Name: .text
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- Address: 0x0
- Content: "000102030405060708090A0B0C0D0E0F1011121314"
- - Name: .data1
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- Content: "3031323334353637383940"
- Address: 0xFFF8
- - Name: .data2
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- Content: "40414243"
- Address: 0x10100
- - Name: .data3
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- Content: "5051525354555657585960"
- Address: 0x10FFF8
- - Name: .bss
- Type: SHT_NOBITS
- Flags: [ SHF_ALLOC ]
- Address: 0x10100
- Size: 0x1000
- - Name: .dummy
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- Address: 0x20FFF8
- Size: 3
- - Name: .nonalloc
- Type: SHT_PROGBITS
- 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 e9f427a0466973..0d6224cfa1619d 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -1,18 +1,6 @@
-# RUN: yaml2obj %p/Inputs/srec-elf-sections.yaml -o %t
+# RUN: yaml2obj %s --docnum=1 -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
-
## 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
@@ -29,10 +17,6 @@
# 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
-
## 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,
@@ -45,14 +29,71 @@
# CHECK-NEXT: S30A8000100000010203045B
# CHECK-NEXT: S70500000000FA
-# PADDR: S00400002DCE
-# PADDR-NEXT: S214100000000102030405060708090A0B0C0D0E0F63
-# PADDR-NEXT: S20910001010111213147C
-# PADDR-NEXT: S20F10001830313233343536373839407B
-# PADDR-NEXT: S20810002840414243B9
-# PADDR-NEXT: S20F100030505152535455565758596003
-# PADDR-NEXT: S20720FFF8000000E1
-# PADDR-NEXT: S804100000EB
+# ENTRY: S00400002DCE
+# ENTRY-NEXT: S705F00000000A
+
+# BAD_START: entry point address 0xf00000000 overflows 32 bits
+
+# BAD_EXTENDED_START: entry point address 0xffffffff0f000000 overflows 32 bits
+
+--- !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
+ - 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
+# Sign-extended addresses are OK.
+ - Name: .data3
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0xFFFFFFFF80001000
+ 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
+# should appear before records for the previous section.
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0x1000
+ Content: "000102030405060708090A0B0C0D0E0F1011121314"
+ - Name: .bss
+# NOBITS sections are not written.
+ Type: SHT_NOBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0x10100
+ Size: 0x1000
+ - Name: .dummy
+# Non-allocatable sections are not written.
+ Type: SHT_PROGBITS
+ Flags: [ ]
+ Address: 0x20FFF8
+ Size: 65536
+
+## Check that section address range overlapping 32 bit range
+## triggers an error
+# RUN: yaml2obj %s --docnum=2 -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
# BAD-ADDR: section '.text1' address range [0xfffffff8, 0x100000000] is not 32 bit
# BAD-ADDR2: section '.text2' address range [0xffffffff0, 0xffffffff4] is not 32 bit
@@ -62,9 +103,91 @@
# ZERO_SIZE_SEC: S00400002DCE
# ZERO_SIZE_SEC-NEXT: S9030000FC
-# ENTRY: S00400002DCE
-# ENTRY-NEXT: S705F00000000A
+--- !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]
+ Address: 0xFFFFFFF8
+ Content: "000102030405060708"
+ - Name: .text2
+ # Entire secion is outside of 32-bit range.
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0xFFFFFFFF0
+ Content: "0001020304"
-# BAD_START: entry point address 0xf00000000 overflows 32 bits
+## Records should use segment physical addresses
+# RUN: yaml2obj %s --docnum=3 -o %t.seg
+# RUN: llvm-objcopy -O srec %t.seg - | FileCheck %s --check-prefix=PADDR
-# BAD_EXTENDED_START: entry point address 0xffffffff0f000000 overflows 32 bits
+# PADDR: S00400002DCE
+# PADDR-NEXT: S214100000000102030405060708090A0B0C0D0E0F63
+# PADDR-NEXT: S20910001010111213147C
+# PADDR-NEXT: S20F10001530313233343536373839407E
+# PADDR-NEXT: S20810002040414243C1
+# PADDR-NEXT: S20F10002450515253545556575859600F
+# PADDR-NEXT: S20720FFF8000000E1
+# PADDR-NEXT: S804100000EB
+
+--- !ELF
+# 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.
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+ Entry: 0x100000
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0x0
+ Content: "000102030405060708090A0B0C0D0E0F1011121314"
+ - Name: .data1
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Content: "3031323334353637383940"
+ Address: 0xFFF8
+ - Name: .data2
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Content: "40414243"
+ Address: 0x10100
+ - Name: .data3
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Content: "5051525354555657585960"
+ Address: 0x10FFF8
+ - Name: .bss
+ Type: SHT_NOBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0x10100
+ Size: 0x1000
+ - Name: .dummy
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0x20FFF8
+ Size: 3
+ - Name: .nonalloc
+ Type: SHT_PROGBITS
+ Flags: [ ]
+ Address: 0x300000
+ Size: 1
+ProgramHeaders:
+ - Type: PT_LOAD
+ Flags: [ PF_X, PF_R ]
+ VAddr: 0xF00000000
+ PAddr: 0x100000
+ FirstSec: .text
+ LastSec: .bss
>From 5789109e9e82e553c2960c3d4f5732ee8e74d83e Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:11:30 -0800
Subject: [PATCH 10/19] fix test comment
---
llvm/test/tools/llvm-objcopy/ELF/srec-writer.test | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
index 0d6224cfa1619d..382e8c56fcdf51 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -138,10 +138,9 @@ Sections:
# PADDR-NEXT: S804100000EB
--- !ELF
-# 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.
+# This file has a non-contiguous section layout with large gaps.
+# These sections are all tightly packed into one PT_LOAD segment
+# starting at physical address 0x100000. Records should use physical addresses.
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
>From 2dfc065fc7fc8ba2d6aafc0de35fc6549b7c4678 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Tue, 16 Jan 2024 14:03:10 -0800
Subject: [PATCH 11/19] refactor to share code with ihex
---
llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp | 2 +-
llvm/lib/ObjCopy/ELF/ELFObject.cpp | 186 +++++++-----------
llvm/lib/ObjCopy/ELF/ELFObject.h | 59 +++---
.../tools/llvm-objcopy/ELF/ihex-writer.test | 6 +-
4 files changed, 112 insertions(+), 141 deletions(-)
diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index 772c19610d8c4e..387ef737b4df87 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -182,7 +182,7 @@ static std::unique_ptr<Writer> createWriter(const CommonConfig &Config,
case FileFormat::Binary:
return std::make_unique<BinaryWriter>(Obj, Out, Config);
case FileFormat::IHex:
- return std::make_unique<IHexWriter>(Obj, Out);
+ return std::make_unique<IHexWriter>(Obj, Out, Config.OutputFilename);
case FileFormat::SREC:
return std::make_unique<SRECWriter>(Obj, Out, Config.OutputFilename);
default:
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index 5d6cfe51960a67..2f541866189435 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -2707,10 +2707,52 @@ Error BinaryWriter::finalize() {
return Error::success();
}
-bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs,
- const SectionBase *Rhs) const {
- return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) <
- (sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU);
+Error ASCIIHexWriter::checkSection(const SectionBase &S) const {
+ if (addressOverflows32bit(S.Addr) ||
+ addressOverflows32bit(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 ASCIIHexWriter::finalize() {
+ // We can't write 64-bit addresses.
+ if (addressOverflows32bit(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 sectionPhysicalAddr(A) < sectionPhysicalAddr(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");
+
+ Expected<size_t> ExpTotalSize = getTotalSize(*EmptyBuffer);
+ if (!ExpTotalSize)
+ return ExpTotalSize.takeError();
+ TotalSize = *ExpTotalSize;
+
+ 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();
}
uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) {
@@ -2740,6 +2782,20 @@ uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) {
return HexData.size();
}
+Expected<size_t>
+IHexWriter::getTotalSize(WritableMemoryBuffer &EmptyBuffer) const {
+ IHexSectionWriterBase LengthCalc(EmptyBuffer);
+ for (const SectionBase *Sec : Sections)
+ if (Error Err = Sec->accept(LengthCalc))
+ return Err;
+
+ // We need space to write section records + StartAddress record
+ // (if start adress is not zero) + EndOfFile record.
+ return LengthCalc.getBufferOffset() +
+ (Obj.Entry ? IHexRecord::getLineLength(4) : 0) +
+ IHexRecord::getLineLength(0);
+}
+
Error IHexWriter::write() {
IHexSectionWriter Writer(*Buf);
// Write sections.
@@ -2762,57 +2818,6 @@ Error IHexWriter::write() {
return Error::success();
}
-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 [0x%llx, 0x%llx] is not 32 bit",
- Sec.Name.c_str(), Addr, Addr + Sec.Size - 1);
- return Error::success();
-}
-
-Error IHexWriter::finalize() {
- // We can't write 64-bit addresses.
- if (addressOverflows32bit(Obj.Entry))
- return createStringError(errc::invalid_argument,
- "Entry point address 0x%llx overflows 32 bits",
- Obj.Entry);
-
- for (const SectionBase &Sec : Obj.sections())
- if ((Sec.Flags & ELF::SHF_ALLOC) && Sec.Type != ELF::SHT_NOBITS &&
- Sec.Size > 0) {
- if (Error E = checkSection(Sec))
- return E;
- Sections.insert(&Sec);
- }
-
- std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
- WritableMemoryBuffer::getNewMemBuffer(0);
- if (!EmptyBuffer)
- return createStringError(errc::not_enough_memory,
- "failed to allocate memory buffer of 0 bytes");
-
- IHexSectionWriterBase LengthCalc(*EmptyBuffer);
- for (const SectionBase *Sec : Sections)
- if (Error Err = Sec->accept(LengthCalc))
- return Err;
-
- // 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);
-
- Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
- if (!Buf)
- return createStringError(errc::not_enough_memory,
- "failed to allocate memory buffer of " +
- Twine::utohexstr(TotalSize) + " bytes");
-
- return Error::success();
-}
-
Error SRECSectionWriterBase::visit(const StringTableSection &Sec) {
// Check that the sizer has already done its work.
assert(Sec.Size == Sec.StrTabBuilder.getSize() &&
@@ -2980,6 +2985,21 @@ size_t SRECWriter::writeTerminator(uint8_t *Buf, uint8_t Type) {
return Data.size();
}
+Expected<size_t>
+SRECWriter::getTotalSize(WritableMemoryBuffer &EmptyBuffer) const {
+ SRECSectionWriterBase LengthCalc(EmptyBuffer, 0);
+ for (const SectionBase *Sec : Sections)
+ if (Error Err = Sec->accept(LengthCalc))
+ return Err;
+
+ 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();
+ SRecord Terminator = {TerminatorType, static_cast<uint32_t>(Obj.Entry), {}};
+ return Header.getSize() + LengthCalc.getBufferOffset() + Terminator.getSize();
+}
+
Error SRECWriter::write() {
uint32_t HeaderSize =
writeHeader(reinterpret_cast<uint8_t *>(Buf->getBufferStart()));
@@ -3001,62 +3021,6 @@ Error SRECWriter::write() {
return Error::success();
}
-Error SRECWriter::checkSection(const SectionBase &S) const {
- if (addressOverflows32bit(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();
-}
-
-/// FIXME: refactor to share code with ihex
-Error SRECWriter::finalize() {
- // We can't write 64-bit addresses.
- if (addressOverflows32bit(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 sectionPhysicalAddr(A) < sectionPhysicalAddr(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(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();
- 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 a3ebb6b75d0471..4ebaecf23cb47c 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -373,23 +373,47 @@ class BinaryWriter : public Writer {
: Writer(Obj, Out), GapFill(Config.GapFill), PadTo(Config.PadTo) {}
};
-class IHexWriter : public Writer {
- struct SectionCompare {
- bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const;
- };
+// A base class for writing ascii hex formats such as srec and ihex.
+class ASCIIHexWriter : public Writer {
+public:
+ ASCIIHexWriter(Object &Obj, raw_ostream &OS, StringRef OutputFile)
+ : Writer(Obj, OS), OutputFileName(OutputFile) {}
+ Error finalize() override;
- std::set<const SectionBase *, SectionCompare> Sections;
+protected:
+ StringRef OutputFileName;
size_t TotalSize = 0;
+ std::vector<const SectionBase *> Sections;
+
+ Error checkSection(const SectionBase &S) const;
+ virtual Expected<size_t>
+ getTotalSize(WritableMemoryBuffer &EmptyBuffer) const = 0;
+};
+
+class IHexWriter : public ASCIIHexWriter {
+public:
+ Error write() override;
+ IHexWriter(Object &Obj, raw_ostream &Out, StringRef OutputFile)
+ : ASCIIHexWriter(Obj, Out, OutputFile) {}
- Error checkSection(const SectionBase &Sec);
+private:
uint64_t writeEntryPointRecord(uint8_t *Buf);
uint64_t writeEndOfFileRecord(uint8_t *Buf);
+ Expected<size_t>
+ getTotalSize(WritableMemoryBuffer &EmptyBuffer) const override;
+};
+class SRECWriter : public ASCIIHexWriter {
public:
- ~IHexWriter() {}
- Error finalize() override;
+ SRECWriter(Object &Obj, raw_ostream &OS, StringRef OutputFile)
+ : ASCIIHexWriter(Obj, OS, OutputFile) {}
Error write() override;
- IHexWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {}
+
+private:
+ size_t writeHeader(uint8_t *Buf);
+ size_t writeTerminator(uint8_t *Buf, uint8_t Type);
+ Expected<size_t>
+ getTotalSize(WritableMemoryBuffer &EmptyBuffer) const override;
};
using SRecLineData = SmallVector<char, 64>;
@@ -430,23 +454,6 @@ struct SRecord {
};
};
-class SRECWriter : public Writer {
-public:
- SRECWriter(Object &Obj, raw_ostream &OS, StringRef OutputFile)
- : Writer(Obj, OS), OutputFileName(OutputFile) {}
- Error finalize() override;
- Error write() override;
-
-private:
- 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;
-};
-
/// Base class for SRecSectionWriter
/// This class does not actually write anything. It is only used for size
/// calculation.
diff --git a/llvm/test/tools/llvm-objcopy/ELF/ihex-writer.test b/llvm/test/tools/llvm-objcopy/ELF/ihex-writer.test
index 09ff8ae951d493..6c07f9f88bda57 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/ihex-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/ihex-writer.test
@@ -70,8 +70,8 @@
# SIGN_EXTENDED-NEXT: :051000000001020304E1
# SIGN_EXTENDED-NEXT: :00000001FF
-# BAD-ADDR: error: {{.*}}: Section '.text2' address range [0x{{.*}}, 0x{{.*}}] is not 32 bit
-# BAD-ADDR2: error: {{.*}}: Section '.text3' address range [0x{{.*}}, 0x{{.*}}] is not 32 bit
+# BAD-ADDR: error: {{.*}}: section '.text2' address range [0x{{.*}}, 0x{{.*}}] is not 32 bit
+# BAD-ADDR2: error: {{.*}}: section '.text3' address range [0x{{.*}}, 0x{{.*}}] is not 32 bit
# There shouldn't be 'ExtendedAddr' nor 'Data' records
# ZERO_SIZE_SEC-NOT: :02000004
@@ -81,4 +81,4 @@
# START1: :040000030000FFFFFB
# START2: :0400000500100000E7
# START3: :040000058000100067
-# BAD-START: error: {{.*}}: Entry point address 0x{{.*}} overflows 32 bits
+# BAD-START: error: {{.*}}: entry point address 0x{{.*}} overflows 32 bits
>From e45d11834091ac6e848a9a8fd5910b57ac0702d7 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Fri, 19 Jan 2024 09:02:39 -0800
Subject: [PATCH 12/19] remove unneeded comment
---
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 4ebaecf23cb47c..6b3150ef191a2d 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -466,7 +466,6 @@ class SRECSectionWriterBase : public BinarySectionWriter {
using BinarySectionWriter::visit;
- // Once the type of all records is known, write the records.
virtual void writeRecords(uint32_t Entry);
uint64_t getBufferOffset() const { return Offset; }
Error visit(const Section &S) override;
>From 4b374219339af18091718cd404996303e7c4f465 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Fri, 19 Jan 2024 09:02:59 -0800
Subject: [PATCH 13/19] test improvements
- comment style
- style change for line continuations
- adding implicit-check-not, match-full-lines, strict-whitespace
- explicit test prefixes
---
.../tools/llvm-objcopy/ELF/srec-writer.test | 134 ++++++++++--------
1 file changed, 71 insertions(+), 63 deletions(-)
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
index 382e8c56fcdf51..b4b48591b7c19b 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -1,36 +1,40 @@
+## Check for basic functionality using an input file with
+## various section types, adresses, data, and no segments.
+
# RUN: yaml2obj %s --docnum=1 -o %t
-# RUN: llvm-objcopy -O srec %t - | FileCheck %s
+# RUN: llvm-objcopy -O srec %t - | \
+# RUN: FileCheck --match-full-lines --strict-whitespace %s --check-prefix=SREC
-## 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
+## Terminator should contain the entry point.
+# RUN: llvm-objcopy -O srec --set-start=0xF0000000 %t --only-section=.dummy - 2>&1 | \
+# RUN: FileCheck --match-full-lines --strict-whitespace %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
+## Sign-extended entry point is OK.
+# RUN: llvm-objcopy -O srec --set-start=0xFFFFFFFFF0000000 %t --only-section=.dummy - 2>&1 | \
+# RUN: FileCheck --match-full-lines --strict-whitespace %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
+## 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
+## 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
## 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"
+## 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: S30A8000100000010203045B
-# CHECK-NEXT: S70500000000FA
-
-# ENTRY: S00400002DCE
-# ENTRY-NEXT: S705F00000000A
+## 1 byte for output file and 1 byte for checksum.
+ # SREC:S00400002DCE
+# SREC-NEXT:S31500001000000102030405060708090A0B0C0D0E0F62
+# SREC-NEXT:S30A0000101010111213147B
+# SREC-NEXT:S30F00EFFFFF1111111111111111111159
+# SREC-NEXT:S31000FFFFF83031323334353637383940AC
+# SREC-NEXT:S30A8000100000010203045B
+# SREC-NEXT:S70500000000FA
+
+ # ENTRY:S00400002DCE
+# ENTRY-NEXT:S705F00000000A
# BAD_START: entry point address 0xf00000000 overflows 32 bits
@@ -44,64 +48,65 @@ FileHeader:
Machine: EM_X86_64
Sections:
- Name: .data1
-# Records for this section should come last.
+## Records for this section should come last.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "11111111111111111111"
Address: 0xEFFFFF
- Name: .data2
-# This section overlaps 24-bit address boundary, so we expect
-# its record type to be S3.
+## 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
-# Sign-extended addresses are OK.
+## Sign-extended addresses are OK.
- Name: .data3
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0xFFFFFFFF80001000
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
-# should appear before records for the previous section.
+## 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 ]
Address: 0x1000
Content: "000102030405060708090A0B0C0D0E0F1011121314"
- Name: .bss
-# NOBITS sections are not written.
+## NOBITS sections are not written.
Type: SHT_NOBITS
Flags: [ SHF_ALLOC ]
Address: 0x10100
Size: 0x1000
- Name: .dummy
-# Non-allocatable sections are not written.
+## Non-allocatable sections are not written.
Type: SHT_PROGBITS
Flags: [ ]
Address: 0x20FFF8
Size: 65536
+## Check for various error cases.
+
## Check that section address range overlapping 32 bit range
-## triggers an error
+## triggers an error.
# RUN: yaml2obj %s --docnum=2 -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
+# 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
+## Check that zero length section is not written.
+# RUN: llvm-objcopy -O srec --only-section=.text %t.err - | \
+# RUN: FileCheck --match-full-lines --strict-whitespace --implicit-check-not={{.}} %s --check-prefix=ZERO_SIZE_SEC
# 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
+## There should be no records besides header and terminator.
+# ZERO_SIZE_SEC:S00400002DCE
+# ZERO_SIZE_SEC-NEXT:S9030000FC
--- !ELF
FileHeader:
@@ -111,36 +116,39 @@ FileHeader:
Machine: EM_X86_64
Sections:
- Name: .text1
-# Part of section data is in 32-bit address range
-# and part isn't.
+## Part of section data is in 32-bit address range
+## and part isn't.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC]
Address: 0xFFFFFFF8
Content: "000102030405060708"
- Name: .text2
- # Entire secion is outside of 32-bit range.
+ ## Entire secion is outside of 32-bit range.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0xFFFFFFFF0
Content: "0001020304"
-## Records should use segment physical addresses
-# RUN: yaml2obj %s --docnum=3 -o %t.seg
-# RUN: llvm-objcopy -O srec %t.seg - | FileCheck %s --check-prefix=PADDR
+## This tests an input file with segments and expects
+## physical addresses instead of virtual addresses.
-# PADDR: S00400002DCE
-# PADDR-NEXT: S214100000000102030405060708090A0B0C0D0E0F63
-# PADDR-NEXT: S20910001010111213147C
-# PADDR-NEXT: S20F10001530313233343536373839407E
-# PADDR-NEXT: S20810002040414243C1
-# PADDR-NEXT: S20F10002450515253545556575859600F
-# PADDR-NEXT: S20720FFF8000000E1
-# PADDR-NEXT: S804100000EB
+# RUN: yaml2obj %s --docnum=3 -o %t.seg
+# RUN: llvm-objcopy -O srec %t.seg - | \
+# RUN: FileCheck --match-full-lines --strict-whitespace %s --check-prefix=PADDR
+
+ # PADDR:S00400002DCE
+# PADDR-NEXT:S214100000000102030405060708090A0B0C0D0E0F63
+# PADDR-NEXT:S20910001010111213147C
+# PADDR-NEXT:S20F10001530313233343536373839407E
+# PADDR-NEXT:S20810002040414243C1
+# PADDR-NEXT:S20F10002450515253545556575859600F
+# PADDR-NEXT:S20720FFF8000000E1
+# PADDR-NEXT:S804100000EB
--- !ELF
-# This file has a non-contiguous section layout with large gaps.
-# These sections are all tightly packed into one PT_LOAD segment
-# starting at physical address 0x100000. Records should use physical addresses.
+## This file has a non-contiguous section layout with large gaps.
+## These sections are all tightly packed into one PT_LOAD segment
+## starting at physical address 0x100000. Records should use physical addresses.
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
>From 34ac1fc736796618035cb809f1cd60f6b1cb9968 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Fri, 19 Jan 2024 09:43:11 -0800
Subject: [PATCH 14/19] remove unused member function
---
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 6b3150ef191a2d..f0daad92087e60 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -473,7 +473,6 @@ class SRECSectionWriterBase : public BinarySectionWriter {
Error visit(const StringTableSection &S) override;
Error visit(const DynamicRelocationSection &S) override;
uint8_t getType() const { return Type; };
- void writeRecord(SRecord &Record);
protected:
// Offset in the output buffer.
>From fbb56a93ca3709a291000c0e9f74eb6753e76002 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Fri, 19 Jan 2024 11:42:23 -0800
Subject: [PATCH 15/19] refactor SRECSectionWriter
SRECSectionWriterBase is now an abstract base class
that is implemented by SRECSectionWriter and SRECSizeCalculator.
---
llvm/lib/ObjCopy/ELF/ELFObject.cpp | 12 ++++++------
llvm/lib/ObjCopy/ELF/ELFObject.h | 18 ++++++++++++------
2 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index 2f541866189435..dfdce07a6ce1d8 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -2847,24 +2847,24 @@ void SRECSectionWriter::writeRecord(SRecord &Record, uint64_t Off) {
memcpy(Out.getBufferStart() + Off, Data.data(), Data.size());
}
-void SRECSectionWriter::writeRecords(uint32_t Entry) {
+void SRECSizeCalculator::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;
- writeRecord(Record, Off);
Off += Record.getSize();
}
Offset = Off;
}
-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.
+void SRECSectionWriter::writeRecords(uint32_t Entry) {
Type = std::max(Type, SRecord::getType(Entry));
uint64_t Off = HeaderSize;
for (SRecord &Record : Records) {
Record.Type = Type;
+ writeRecord(Record, Off);
Off += Record.getSize();
}
Offset = Off;
@@ -2987,7 +2987,7 @@ size_t SRECWriter::writeTerminator(uint8_t *Buf, uint8_t Type) {
Expected<size_t>
SRECWriter::getTotalSize(WritableMemoryBuffer &EmptyBuffer) const {
- SRECSectionWriterBase LengthCalc(EmptyBuffer, 0);
+ SRECSizeCalculator LengthCalc(EmptyBuffer, 0);
for (const SectionBase *Sec : Sections)
if (Error Err = Sec->accept(LengthCalc))
return Err;
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index f0daad92087e60..f792c4f527aaa0 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -172,8 +172,9 @@ template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor {
friend class SectionWriter; \
friend class IHexSectionWriterBase; \
friend class IHexSectionWriter; \
- friend class SRECSectionWriterBase; \
friend class SRECSectionWriter; \
+ friend class SRECSectionWriterBase; \
+ friend class SRECSizeCalculator; \
template <class ELFT> friend class ELFSectionWriter; \
template <class ELFT> friend class ELFSectionSizer;
@@ -454,9 +455,6 @@ struct SRecord {
};
};
-/// Base class for SRecSectionWriter
-/// This class does not actually write anything. It is only used for size
-/// calculation.
class SRECSectionWriterBase : public BinarySectionWriter {
public:
explicit SRECSectionWriterBase(WritableMemoryBuffer &Buf,
@@ -466,7 +464,7 @@ class SRECSectionWriterBase : public BinarySectionWriter {
using BinarySectionWriter::visit;
- virtual void writeRecords(uint32_t Entry);
+ virtual void writeRecords(uint32_t Entry) = 0;
uint64_t getBufferOffset() const { return Offset; }
Error visit(const Section &S) override;
Error visit(const OwnedDataSection &S) override;
@@ -486,7 +484,15 @@ class SRECSectionWriterBase : public BinarySectionWriter {
void writeSection(const SectionBase &S, ArrayRef<uint8_t> Data);
};
-// Real SREC section writer.
+// An SRECSectionWriterBase that visits sections but does not write anything.
+// This class is only used to calculate the size of the output file.
+class SRECSizeCalculator : public SRECSectionWriterBase {
+public:
+ SRECSizeCalculator(WritableMemoryBuffer &EmptyBuffer, uint64_t Offset)
+ : SRECSectionWriterBase(EmptyBuffer, Offset) {}
+ void writeRecords(uint32_t Entry) override;
+};
+
class SRECSectionWriter : public SRECSectionWriterBase {
public:
SRECSectionWriter(WritableMemoryBuffer &Buf, uint64_t Offset)
>From 4135baaf58f153a0443755fff1479511715ebe1f Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Mon, 29 Jan 2024 10:50:52 -0800
Subject: [PATCH 16/19] length->size
---
llvm/lib/ObjCopy/ELF/ELFObject.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index dfdce07a6ce1d8..df770c53b538c7 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -2987,17 +2987,17 @@ size_t SRECWriter::writeTerminator(uint8_t *Buf, uint8_t Type) {
Expected<size_t>
SRECWriter::getTotalSize(WritableMemoryBuffer &EmptyBuffer) const {
- SRECSizeCalculator LengthCalc(EmptyBuffer, 0);
+ SRECSizeCalculator SizeCalc(EmptyBuffer, 0);
for (const SectionBase *Sec : Sections)
- if (Error Err = Sec->accept(LengthCalc))
+ if (Error Err = Sec->accept(SizeCalc))
return Err;
- LengthCalc.writeRecords(Obj.Entry);
+ SizeCalc.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();
+ uint8_t TerminatorType = 10 - SizeCalc.getType();
SRecord Terminator = {TerminatorType, static_cast<uint32_t>(Obj.Entry), {}};
- return Header.getSize() + LengthCalc.getBufferOffset() + Terminator.getSize();
+ return Header.getSize() + SizeCalc.getBufferOffset() + Terminator.getSize();
}
Error SRECWriter::write() {
>From 7f6e4338440d6cc934b1f3bb8113ff329698a6a2 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Mon, 29 Jan 2024 11:26:05 -0800
Subject: [PATCH 17/19] test improvements
---
.../tools/llvm-objcopy/ELF/srec-writer.test | 44 +++++++++----------
1 file changed, 20 insertions(+), 24 deletions(-)
diff --git a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
index b4b48591b7c19b..e96b87b8592439 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/srec-writer.test
@@ -1,26 +1,9 @@
## Check for basic functionality using an input file with
## various section types, adresses, data, and no segments.
-
# RUN: yaml2obj %s --docnum=1 -o %t
# RUN: llvm-objcopy -O srec %t - | \
# RUN: FileCheck --match-full-lines --strict-whitespace %s --check-prefix=SREC
-## Terminator should contain the entry point.
-# RUN: llvm-objcopy -O srec --set-start=0xF0000000 %t --only-section=.dummy - 2>&1 | \
-# RUN: FileCheck --match-full-lines --strict-whitespace %s --check-prefix=ENTRY
-
-## Sign-extended entry point is OK.
-# RUN: llvm-objcopy -O srec --set-start=0xFFFFFFFFF0000000 %t --only-section=.dummy - 2>&1 | \
-# RUN: FileCheck --match-full-lines --strict-whitespace %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
-
## 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,
@@ -33,11 +16,26 @@
# SREC-NEXT:S30A8000100000010203045B
# SREC-NEXT:S70500000000FA
+## Terminator should contain the entry point.
+# RUN: llvm-objcopy -O srec --set-start=0xF0000000 %t --only-section=.dummy - 2>&1 | \
+# RUN: FileCheck --match-full-lines --strict-whitespace %s --check-prefix=ENTRY
+## Sign-extended entry point is OK.
+# RUN: llvm-objcopy -O srec --set-start=0xFFFFFFFFF0000000 %t --only-section=.dummy - 2>&1 | \
+# RUN: FileCheck --match-full-lines --strict-whitespace %s --check-prefix=ENTRY
+
# ENTRY:S00400002DCE
# ENTRY-NEXT:S705F00000000A
+## 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
+
# BAD_START: entry point address 0xf00000000 overflows 32 bits
+## 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
+
# BAD_EXTENDED_START: entry point address 0xffffffff0f000000 overflows 32 bits
--- !ELF
@@ -67,7 +65,7 @@ Sections:
Address: 0xFFFFFFFF80001000
Content: "0001020304"
- Name: .text
-## This section's contents exceeds default line length of 16 bytes
+## This section's contents exceed 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
@@ -97,13 +95,13 @@ Sections:
# RUN: not llvm-objcopy -O srec --only-section=.text2 %t.err - 2>&1 | \
# RUN: FileCheck %s --check-prefix=BAD-ADDR2
+# BAD-ADDR: section '.text1' address range [0xfffffff8, 0x100000000] is not 32 bit
+# BAD-ADDR2: section '.text2' address range [0xffffffff0, 0xffffffff4] is not 32 bit
+
## Check that zero length section is not written.
# RUN: llvm-objcopy -O srec --only-section=.text %t.err - | \
# RUN: FileCheck --match-full-lines --strict-whitespace --implicit-check-not={{.}} %s --check-prefix=ZERO_SIZE_SEC
-# 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:S00400002DCE
# ZERO_SIZE_SEC-NEXT:S9030000FC
@@ -116,8 +114,7 @@ FileHeader:
Machine: EM_X86_64
Sections:
- Name: .text1
-## Part of section data is in 32-bit address range
-## and part isn't.
+## Part of section data is in 32-bit address range and part isn't.
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC]
Address: 0xFFFFFFF8
@@ -131,7 +128,6 @@ Sections:
## This tests an input file with segments and expects
## physical addresses instead of virtual addresses.
-
# RUN: yaml2obj %s --docnum=3 -o %t.seg
# RUN: llvm-objcopy -O srec %t.seg - | \
# RUN: FileCheck --match-full-lines --strict-whitespace %s --check-prefix=PADDR
>From 28a7cd51405ab840fb54b3df527168205ed17e6f Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Mon, 29 Jan 2024 11:26:58 -0800
Subject: [PATCH 18/19] move common code to base
---
llvm/lib/ObjCopy/ELF/ELFObject.cpp | 12 +-----------
llvm/lib/ObjCopy/ELF/ELFObject.h | 12 +++++++-----
2 files changed, 8 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index df770c53b538c7..715d47fd904307 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -2847,19 +2847,9 @@ void SRECSectionWriter::writeRecord(SRecord &Record, uint64_t Off) {
memcpy(Out.getBufferStart() + Off, Data.data(), Data.size());
}
-void SRECSizeCalculator::writeRecords(uint32_t Entry) {
+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;
- Off += Record.getSize();
- }
- Offset = Off;
-}
-
-void SRECSectionWriter::writeRecords(uint32_t Entry) {
Type = std::max(Type, SRecord::getType(Entry));
uint64_t Off = HeaderSize;
for (SRecord &Record : Records) {
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index f792c4f527aaa0..896793e7e79709 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -464,7 +464,7 @@ class SRECSectionWriterBase : public BinarySectionWriter {
using BinarySectionWriter::visit;
- virtual void writeRecords(uint32_t Entry) = 0;
+ virtual void writeRecords(uint32_t Entry);
uint64_t getBufferOffset() const { return Offset; }
Error visit(const Section &S) override;
Error visit(const OwnedDataSection &S) override;
@@ -482,6 +482,7 @@ class SRECSectionWriterBase : public BinarySectionWriter {
std::vector<SRecord> Records;
void writeSection(const SectionBase &S, ArrayRef<uint8_t> Data);
+ virtual void writeRecord(SRecord &Record, uint64_t Off) = 0;
};
// An SRECSectionWriterBase that visits sections but does not write anything.
@@ -490,7 +491,9 @@ class SRECSizeCalculator : public SRECSectionWriterBase {
public:
SRECSizeCalculator(WritableMemoryBuffer &EmptyBuffer, uint64_t Offset)
: SRECSectionWriterBase(EmptyBuffer, Offset) {}
- void writeRecords(uint32_t Entry) override;
+
+protected:
+ void writeRecord(SRecord &Record, uint64_t Off) override {}
};
class SRECSectionWriter : public SRECSectionWriterBase {
@@ -498,10 +501,9 @@ class SRECSectionWriter : public SRECSectionWriterBase {
SRECSectionWriter(WritableMemoryBuffer &Buf, uint64_t Offset)
: SRECSectionWriterBase(Buf, Offset) {}
Error visit(const StringTableSection &Sec) override;
- void writeRecords(uint32_t Entry) override;
-private:
- void writeRecord(SRecord &Record, uint64_t Off);
+protected:
+ void writeRecord(SRecord &Record, uint64_t Off) override;
};
class SectionBase {
>From bb1dcde1313e07dd45a35e89287fdb4be2374cc8 Mon Sep 17 00:00:00 2001
From: quic-areg <154252969+quic-areg at users.noreply.github.com>
Date: Thu, 8 Feb 2024 15:33:32 -0800
Subject: [PATCH 19/19] remove unnecessary 'virtual'
---
llvm/lib/ObjCopy/ELF/ELFObject.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 896793e7e79709..7a2e20d82d1150 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -464,7 +464,7 @@ class SRECSectionWriterBase : public BinarySectionWriter {
using BinarySectionWriter::visit;
- virtual void writeRecords(uint32_t Entry);
+ void writeRecords(uint32_t Entry);
uint64_t getBufferOffset() const { return Offset; }
Error visit(const Section &S) override;
Error visit(const OwnedDataSection &S) override;
More information about the llvm-commits
mailing list