[llvm] [llvm-objcopy] Support SREC output format (PR #75874)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 16 14:36:04 PST 2024
================
@@ -2813,6 +2813,244 @@ 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(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;
+}
+
+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 SRECSectionWriterBase::writeSection(const SectionBase &S,
+ ArrayRef<uint8_t> Data) {
+ const uint32_t ChunkSize = 16;
+ uint32_t Address = sectionPhysicalAddr(&S);
+ uint32_t EndAddr = Address + S.Size - 1;
+ 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)};
+ 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;
+}
+
+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
+ // name truncated 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(Obj.Entry);
+ 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 (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 SRECWriter::finalize() {
----------------
quic-areg wrote:
I added a new base class for srec and ihex called `ASCIIHexWriter` to factor out common stuff.
Since they share the same code, ihex errors now start with lower case.
https://github.com/llvm/llvm-project/pull/75874
More information about the llvm-commits
mailing list