Skip to content

Commit 7ddc320

Browse files
authored
[llvm-objcopy] Support SREC output format (#75874)
Adds a new output target "srec" to write SREC files from ELF inputs. https://en.wikipedia.org/wiki/SREC_(file_format)
1 parent 2095655 commit 7ddc320

File tree

9 files changed

+576
-62
lines changed

9 files changed

+576
-62
lines changed

llvm/docs/CommandGuide/llvm-objcopy.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,13 @@ options. For GNU :program:`objcopy` compatibility, the values are all bfdnames.
544544
- `elf32-sparc`
545545
- `elf32-sparcel`
546546

547-
Additionally, all targets except `binary` and `ihex` can have `-freebsd` as a
548-
suffix.
547+
The following formats are suppoprted by :program:`llvm-objcopy` for the
548+
:option:`--output-target` only:
549+
550+
- `srec`
551+
552+
Additionally, all targets except `binary`, `ihex`, and `srec` can have
553+
`-freebsd` as a suffix.
549554

550555
BINARY INPUT AND OUTPUT
551556
-----------------------

llvm/include/llvm/ObjCopy/CommonConfig.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,7 @@
2727
namespace llvm {
2828
namespace objcopy {
2929

30-
enum class FileFormat {
31-
Unspecified,
32-
ELF,
33-
Binary,
34-
IHex,
35-
};
30+
enum class FileFormat { Unspecified, ELF, Binary, IHex, SREC };
3631

3732
// This type keeps track of the machine info for various architectures. This
3833
// lets us map architecture names to ELF types and the e_machine value of the

llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,9 @@ static std::unique_ptr<Writer> createWriter(const CommonConfig &Config,
182182
case FileFormat::Binary:
183183
return std::make_unique<BinaryWriter>(Obj, Out, Config);
184184
case FileFormat::IHex:
185-
return std::make_unique<IHexWriter>(Obj, Out);
185+
return std::make_unique<IHexWriter>(Obj, Out, Config.OutputFilename);
186+
case FileFormat::SREC:
187+
return std::make_unique<SRECWriter>(Obj, Out, Config.OutputFilename);
186188
default:
187189
return createELFWriter(Config, Obj, Out, OutputElfType);
188190
}

llvm/lib/ObjCopy/ELF/ELFObject.cpp

Lines changed: 239 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,10 +2707,52 @@ Error BinaryWriter::finalize() {
27072707
return Error::success();
27082708
}
27092709

2710-
bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs,
2711-
const SectionBase *Rhs) const {
2712-
return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) <
2713-
(sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU);
2710+
Error ASCIIHexWriter::checkSection(const SectionBase &S) const {
2711+
if (addressOverflows32bit(S.Addr) ||
2712+
addressOverflows32bit(S.Addr + S.Size - 1))
2713+
return createStringError(
2714+
errc::invalid_argument,
2715+
"section '%s' address range [0x%llx, 0x%llx] is not 32 bit",
2716+
S.Name.c_str(), S.Addr, S.Addr + S.Size - 1);
2717+
return Error::success();
2718+
}
2719+
2720+
Error ASCIIHexWriter::finalize() {
2721+
// We can't write 64-bit addresses.
2722+
if (addressOverflows32bit(Obj.Entry))
2723+
return createStringError(errc::invalid_argument,
2724+
"entry point address 0x%llx overflows 32 bits",
2725+
Obj.Entry);
2726+
2727+
for (const SectionBase &S : Obj.sections()) {
2728+
if ((S.Flags & ELF::SHF_ALLOC) && S.Type != ELF::SHT_NOBITS && S.Size > 0) {
2729+
if (Error E = checkSection(S))
2730+
return E;
2731+
Sections.push_back(&S);
2732+
}
2733+
}
2734+
2735+
llvm::sort(Sections, [](const SectionBase *A, const SectionBase *B) {
2736+
return sectionPhysicalAddr(A) < sectionPhysicalAddr(B);
2737+
});
2738+
2739+
std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
2740+
WritableMemoryBuffer::getNewMemBuffer(0);
2741+
if (!EmptyBuffer)
2742+
return createStringError(errc::not_enough_memory,
2743+
"failed to allocate memory buffer of 0 bytes");
2744+
2745+
Expected<size_t> ExpTotalSize = getTotalSize(*EmptyBuffer);
2746+
if (!ExpTotalSize)
2747+
return ExpTotalSize.takeError();
2748+
TotalSize = *ExpTotalSize;
2749+
2750+
Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
2751+
if (!Buf)
2752+
return createStringError(errc::not_enough_memory,
2753+
"failed to allocate memory buffer of 0x" +
2754+
Twine::utohexstr(TotalSize) + " bytes");
2755+
return Error::success();
27142756
}
27152757

27162758
uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) {
@@ -2740,6 +2782,20 @@ uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) {
27402782
return HexData.size();
27412783
}
27422784

2785+
Expected<size_t>
2786+
IHexWriter::getTotalSize(WritableMemoryBuffer &EmptyBuffer) const {
2787+
IHexSectionWriterBase LengthCalc(EmptyBuffer);
2788+
for (const SectionBase *Sec : Sections)
2789+
if (Error Err = Sec->accept(LengthCalc))
2790+
return Err;
2791+
2792+
// We need space to write section records + StartAddress record
2793+
// (if start adress is not zero) + EndOfFile record.
2794+
return LengthCalc.getBufferOffset() +
2795+
(Obj.Entry ? IHexRecord::getLineLength(4) : 0) +
2796+
IHexRecord::getLineLength(0);
2797+
}
2798+
27432799
Error IHexWriter::write() {
27442800
IHexSectionWriter Writer(*Buf);
27452801
// Write sections.
@@ -2762,54 +2818,196 @@ Error IHexWriter::write() {
27622818
return Error::success();
27632819
}
27642820

2765-
Error IHexWriter::checkSection(const SectionBase &Sec) {
2766-
uint64_t Addr = sectionPhysicalAddr(&Sec);
2767-
if (addressOverflows32bit(Addr) || addressOverflows32bit(Addr + Sec.Size - 1))
2768-
return createStringError(
2769-
errc::invalid_argument,
2770-
"Section '%s' address range [0x%llx, 0x%llx] is not 32 bit",
2771-
Sec.Name.c_str(), Addr, Addr + Sec.Size - 1);
2821+
Error SRECSectionWriterBase::visit(const StringTableSection &Sec) {
2822+
// Check that the sizer has already done its work.
2823+
assert(Sec.Size == Sec.StrTabBuilder.getSize() &&
2824+
"Expected section size to have been finalized");
2825+
// We don't need to write anything here because the real writer has already
2826+
// done it.
27722827
return Error::success();
27732828
}
27742829

2775-
Error IHexWriter::finalize() {
2776-
// We can't write 64-bit addresses.
2777-
if (addressOverflows32bit(Obj.Entry))
2778-
return createStringError(errc::invalid_argument,
2779-
"Entry point address 0x%llx overflows 32 bits",
2780-
Obj.Entry);
2830+
Error SRECSectionWriterBase::visit(const Section &Sec) {
2831+
writeSection(Sec, Sec.Contents);
2832+
return Error::success();
2833+
}
27812834

2782-
for (const SectionBase &Sec : Obj.sections())
2783-
if ((Sec.Flags & ELF::SHF_ALLOC) && Sec.Type != ELF::SHT_NOBITS &&
2784-
Sec.Size > 0) {
2785-
if (Error E = checkSection(Sec))
2786-
return E;
2787-
Sections.insert(&Sec);
2788-
}
2835+
Error SRECSectionWriterBase::visit(const OwnedDataSection &Sec) {
2836+
writeSection(Sec, Sec.Data);
2837+
return Error::success();
2838+
}
27892839

2790-
std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
2791-
WritableMemoryBuffer::getNewMemBuffer(0);
2792-
if (!EmptyBuffer)
2793-
return createStringError(errc::not_enough_memory,
2794-
"failed to allocate memory buffer of 0 bytes");
2840+
Error SRECSectionWriterBase::visit(const DynamicRelocationSection &Sec) {
2841+
writeSection(Sec, Sec.Contents);
2842+
return Error::success();
2843+
}
2844+
2845+
void SRECSectionWriter::writeRecord(SRecord &Record, uint64_t Off) {
2846+
SRecLineData Data = Record.toString();
2847+
memcpy(Out.getBufferStart() + Off, Data.data(), Data.size());
2848+
}
27952849

2796-
IHexSectionWriterBase LengthCalc(*EmptyBuffer);
2850+
void SRECSectionWriterBase::writeRecords(uint32_t Entry) {
2851+
// The ELF header could contain an entry point outside of the sections we have
2852+
// seen that does not fit the current record Type.
2853+
Type = std::max(Type, SRecord::getType(Entry));
2854+
uint64_t Off = HeaderSize;
2855+
for (SRecord &Record : Records) {
2856+
Record.Type = Type;
2857+
writeRecord(Record, Off);
2858+
Off += Record.getSize();
2859+
}
2860+
Offset = Off;
2861+
}
2862+
2863+
void SRECSectionWriterBase::writeSection(const SectionBase &S,
2864+
ArrayRef<uint8_t> Data) {
2865+
const uint32_t ChunkSize = 16;
2866+
uint32_t Address = sectionPhysicalAddr(&S);
2867+
uint32_t EndAddr = Address + S.Size - 1;
2868+
Type = std::max(SRecord::getType(EndAddr), Type);
2869+
while (!Data.empty()) {
2870+
uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
2871+
SRecord Record{Type, Address, Data.take_front(DataSize)};
2872+
Records.push_back(Record);
2873+
Data = Data.drop_front(DataSize);
2874+
Address += DataSize;
2875+
}
2876+
}
2877+
2878+
Error SRECSectionWriter::visit(const StringTableSection &Sec) {
2879+
assert(Sec.Size == Sec.StrTabBuilder.getSize() &&
2880+
"Section size does not match the section's string table builder size");
2881+
std::vector<uint8_t> Data(Sec.Size);
2882+
Sec.StrTabBuilder.write(Data.data());
2883+
writeSection(Sec, Data);
2884+
return Error::success();
2885+
}
2886+
2887+
SRecLineData SRecord::toString() const {
2888+
SRecLineData Line(getSize());
2889+
auto *Iter = Line.begin();
2890+
*Iter++ = 'S';
2891+
*Iter++ = '0' + Type;
2892+
// Write 1 byte (2 hex characters) record count.
2893+
Iter = toHexStr(getCount(), Iter, 2);
2894+
// Write the address field with length depending on record type.
2895+
Iter = toHexStr(Address, Iter, getAddressSize());
2896+
// Write data byte by byte.
2897+
for (uint8_t X : Data)
2898+
Iter = toHexStr(X, Iter, 2);
2899+
// Write the 1 byte checksum.
2900+
Iter = toHexStr(getChecksum(), Iter, 2);
2901+
*Iter++ = '\r';
2902+
*Iter++ = '\n';
2903+
assert(Iter == Line.end());
2904+
return Line;
2905+
}
2906+
2907+
uint8_t SRecord::getChecksum() const {
2908+
uint32_t Sum = getCount();
2909+
Sum += (Address >> 24) & 0xFF;
2910+
Sum += (Address >> 16) & 0xFF;
2911+
Sum += (Address >> 8) & 0xFF;
2912+
Sum += Address & 0xFF;
2913+
for (uint8_t Byte : Data)
2914+
Sum += Byte;
2915+
return 0xFF - (Sum & 0xFF);
2916+
}
2917+
2918+
size_t SRecord::getSize() const {
2919+
// Type, Count, Checksum, and CRLF are two characters each.
2920+
return 2 + 2 + getAddressSize() + Data.size() * 2 + 2 + 2;
2921+
}
2922+
2923+
uint8_t SRecord::getAddressSize() const {
2924+
switch (Type) {
2925+
case Type::S2:
2926+
return 6;
2927+
case Type::S3:
2928+
return 8;
2929+
case Type::S7:
2930+
return 8;
2931+
case Type::S8:
2932+
return 6;
2933+
default:
2934+
return 4;
2935+
}
2936+
}
2937+
2938+
uint8_t SRecord::getCount() const {
2939+
uint8_t DataSize = Data.size();
2940+
uint8_t ChecksumSize = 1;
2941+
return getAddressSize() / 2 + DataSize + ChecksumSize;
2942+
}
2943+
2944+
uint8_t SRecord::getType(uint32_t Address) {
2945+
if (isUInt<16>(Address))
2946+
return SRecord::S1;
2947+
if (isUInt<24>(Address))
2948+
return SRecord::S2;
2949+
return SRecord::S3;
2950+
}
2951+
2952+
SRecord SRecord::getHeader(StringRef FileName) {
2953+
// Header is a record with Type S0, Address 0, and Data that is a
2954+
// vendor-specific text comment. For the comment we will use the output file
2955+
// name truncated to 40 characters to match the behavior of GNU objcopy.
2956+
StringRef HeaderContents = FileName.slice(0, 40);
2957+
ArrayRef<uint8_t> Data(
2958+
reinterpret_cast<const uint8_t *>(HeaderContents.data()),
2959+
HeaderContents.size());
2960+
return {SRecord::S0, 0, Data};
2961+
}
2962+
2963+
size_t SRECWriter::writeHeader(uint8_t *Buf) {
2964+
SRecLineData Record = SRecord::getHeader(OutputFileName).toString();
2965+
memcpy(Buf, Record.data(), Record.size());
2966+
return Record.size();
2967+
}
2968+
2969+
size_t SRECWriter::writeTerminator(uint8_t *Buf, uint8_t Type) {
2970+
assert(Type >= SRecord::S7 && Type <= SRecord::S9 &&
2971+
"Invalid record type for terminator");
2972+
uint32_t Entry = Obj.Entry;
2973+
SRecLineData Data = SRecord{Type, Entry, {}}.toString();
2974+
memcpy(Buf, Data.data(), Data.size());
2975+
return Data.size();
2976+
}
2977+
2978+
Expected<size_t>
2979+
SRECWriter::getTotalSize(WritableMemoryBuffer &EmptyBuffer) const {
2980+
SRECSizeCalculator SizeCalc(EmptyBuffer, 0);
27972981
for (const SectionBase *Sec : Sections)
2798-
if (Error Err = Sec->accept(LengthCalc))
2982+
if (Error Err = Sec->accept(SizeCalc))
27992983
return Err;
28002984

2801-
// We need space to write section records + StartAddress record
2802-
// (if start adress is not zero) + EndOfFile record.
2803-
TotalSize = LengthCalc.getBufferOffset() +
2804-
(Obj.Entry ? IHexRecord::getLineLength(4) : 0) +
2805-
IHexRecord::getLineLength(0);
2985+
SizeCalc.writeRecords(Obj.Entry);
2986+
// We need to add the size of the Header and Terminator records.
2987+
SRecord Header = SRecord::getHeader(OutputFileName);
2988+
uint8_t TerminatorType = 10 - SizeCalc.getType();
2989+
SRecord Terminator = {TerminatorType, static_cast<uint32_t>(Obj.Entry), {}};
2990+
return Header.getSize() + SizeCalc.getBufferOffset() + Terminator.getSize();
2991+
}
28062992

2807-
Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
2808-
if (!Buf)
2809-
return createStringError(errc::not_enough_memory,
2810-
"failed to allocate memory buffer of " +
2811-
Twine::utohexstr(TotalSize) + " bytes");
2993+
Error SRECWriter::write() {
2994+
uint32_t HeaderSize =
2995+
writeHeader(reinterpret_cast<uint8_t *>(Buf->getBufferStart()));
2996+
SRECSectionWriter Writer(*Buf, HeaderSize);
2997+
for (const SectionBase *S : Sections) {
2998+
if (Error E = S->accept(Writer))
2999+
return E;
3000+
}
3001+
Writer.writeRecords(Obj.Entry);
3002+
uint64_t Offset = Writer.getBufferOffset();
28123003

3004+
// An S1 record terminates with an S9 record, S2 with S8, and S3 with S7.
3005+
uint8_t TerminatorType = 10 - Writer.getType();
3006+
Offset += writeTerminator(
3007+
reinterpret_cast<uint8_t *>(Buf->getBufferStart() + Offset),
3008+
TerminatorType);
3009+
assert(Offset == TotalSize);
3010+
Out.write(Buf->getBufferStart(), Buf->getBufferSize());
28133011
return Error::success();
28143012
}
28153013

0 commit comments

Comments
 (0)