[llvm-branch-commits] [llvm] [llvm-objcopy] Stream ELF output to reduce peak memory usage (PR #217706)
James Henderson via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 13 02:08:33 PDT 2026
================
@@ -33,10 +36,87 @@ using namespace llvm::objcopy::elf;
using namespace llvm::object;
using namespace llvm::support;
-template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) {
- uint8_t *B = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
- Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr);
- Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B);
+namespace {
+
+template <class T> void writeObject(raw_ostream &Out, const T &Value) {
+ Out.write(reinterpret_cast<const char *>(&Value), sizeof(Value));
+}
+
+} // namespace
+
+Error ELFWriterOutput::reserve(uint64_t Size) {
+ auto *FD = dyn_cast<raw_fd_stream>(&Dest);
+ if (FD && FD->supportsSeeking() && FD->isRegularFile()) {
+ Start = FD->tell();
+ std::optional<uint64_t> Last = checkedAddUnsigned(Start, Size);
+ if (!Last)
+ return createStringError(errc::file_too_large,
+ "output exceeds the addressable file range");
+ // Discard existing contents after the current position, then extend the
+ // file so unwritten ranges read as zero without materializing them.
+ if (Error E = FD->resize(Start))
+ return E;
+ if (Error E = FD->resize(*Last))
+ return E;
+ Stream = FD;
+ End = *Last;
+ return Error::success();
+ }
+
+ Buf = WritableMemoryBuffer::getNewMemBuffer(Size);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(Size) + " bytes");
+ BufStream.reset({Buf->getBufferStart(), Buf->getBufferSize()});
+ return Error::success();
+}
+
+raw_ostream &ELFWriterOutput::streamAt(uint64_t Offset) {
+ if (!Stream) {
+ BufStream.seek(Offset);
+ return BufStream;
+ }
+ if (!Stream->has_error())
+ Stream->seek(Start + Offset);
+ return *Stream;
+}
+
+void ELFWriterOutput::write(ArrayRef<uint8_t> Data, uint64_t Offset) {
+ if (!Data.empty())
+ streamAt(Offset).write(reinterpret_cast<const char *>(Data.data()),
+ Data.size());
+}
+
+void ELFWriterOutput::writeZeros(uint64_t Offset, uint64_t Size) {
+ if (!Size)
+ return;
+ // raw_ostream::write_zeros writes in 16-byte units, which is too small for
+ // section-sized runs. Fill from a scratch block instead.
+ SmallVector<char, 0> Zeros(std::min<uint64_t>(Size, 64 * 1024));
+ raw_ostream &Out = streamAt(Offset);
+ while (Size) {
+ size_t Chunk = std::min<uint64_t>(Size, Zeros.size());
+ Out.write(Zeros.data(), Chunk);
+ Size -= Chunk;
+ }
+}
+
+Error ELFWriterOutput::finalize() {
+ if (!Stream) {
+ Dest.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
+ }
+ // Leave the stream positioned past the object, as a writer that appended to
+ // it would.
+ if (!Stream->has_error())
+ Stream->seek(End);
+ return Stream->takeError();
+}
+
+template <class ELFT>
+void ELFWriter<ELFT>::writePhdr(raw_ostream &Out, const Segment &Seg) {
+ Elf_Phdr Phdr = {};
----------------
jh7370 wrote:
There's no need for the explicit instantiation of the Elf_Phdr: the struct members are all written to below.
https://github.com/llvm/llvm-project/pull/217706
More information about the llvm-branch-commits
mailing list