[llvm] [llvm-objcopy] Stream ELF output to reduce peak memory usage (PR #217706)

Farid Zakaria via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 10:31:57 PDT 2026


https://github.com/fzakaria created https://github.com/llvm/llvm-project/pull/217706

## Motivation

At Meta, we use `llvm-objcopy` after we build our software with [buck2](https://buck2.build/) to stamp build information (i.e. `build-id` , date, author etc..) We do this post-build rather than during the build to make sure out build artifacts are nearly all bit-reproducible .

We cave some pretty massive binaries and we noticed that the memory requirements for `llvm-objcopy` are 1:1 with the filesize.
We would like to improve upon the memory use of `llvm-objcopy` to save a lot of memory on the stamping steps, which have the really unfortunate property of sometimes running massively parallel, swamping host memory use and causing OOMs.

The original change here is was done by @jtbraun and I am helping him upstream this.

## Change

llvm-objcopy currently materializes the entire ELF output in a memory buffer before writing it to disk. For very large
binaries, this adds memory usage approximately proportional to the output size. When many stamping operations run
concurrently, the resulting memory pressure can cause host OOMs.

This change writes ELF output directly to seekable regular files using offset writes. Buffered output remains in use for stdout and other non-seekable streams.

## Benchmark

I tested a no-op copy of a synthetic ELF containing a 1 GiB payload. The results below are the medians of three warm-cache runs measured with GNU time: 

| | Before | After |
|---|---|---|
| **Peak RSS** | 2,102,916 KiB | 1,053,844 KiB |
| **Wall time** | 1.06 s | 0.77 s |

This reduces peak RSS by 49.9%. The before and after outputs were byte-identical.

I also tested a real 1.2 GiB ELF build artifact:

| | Before | After |
|---|---|---|
| **Peak RSS** | 3.12 GiB | 2.01 GiB |
| **Wall time** | 2.74 s | 2.36 s |

Those outputs were also byte-identical.


**Note to reviewers**:
This PR is organized as three commits so the preparatory refactoring and error propagation can be reviewed separately from
the streaming implementation.  This change was developed with assistance from OpenAI Codex. Each commit includes an Assisted-by: OpenAI Codex trailer following the AI policy.





>From 454e6e154e29723601c90136e044cd71dbf13716 Mon Sep 17 00:00:00 2001
From: Farid Zakaria <fmzakari at fb.com>
Date: Thu, 20 Aug 2026 09:59:50 -0700
Subject: [PATCH 1/3] [llvm-objcopy] Route section copies through
 writeSectionContents

Centralize section byte copies behind a virtual helper so ELF output can later target either a memory buffer or a seekable stream. Materialize SHT_SYMTAB_SHNDX entries using the target ELF word type to preserve output endianness.

Co-authored-by: Jeremy Braun <jtbraun at meta.com>

Assisted-by: OpenAI Codex
---
 llvm/lib/ObjCopy/ELF/ELFObject.cpp            | 41 ++++++++++++-------
 llvm/lib/ObjCopy/ELF/ELFObject.h              |  1 +
 .../tools/llvm-objcopy/ELF/many-sections.test |  2 +
 3 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index ac818343f3f8f..dace67fdca44d 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -183,9 +183,14 @@ Error BinarySectionWriter::visit(const GroupSection &Sec) {
                            "cannot write '" + Sec.Name + "' out to binary");
 }
 
+void SectionWriter::writeSectionContents(ArrayRef<uint8_t> Data,
+                                         uint64_t Offset) {
+  llvm::copy(Data, Out.getBufferStart() + Offset);
+}
+
 Error SectionWriter::visit(const Section &Sec) {
   if (Sec.Type != SHT_NOBITS)
-    llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
+    writeSectionContents(Sec.Contents, Sec.Offset);
 
   return Error::success();
 }
@@ -453,7 +458,7 @@ void Section::restoreSymTabLink(SymbolTableSection &SymTab) {
 }
 
 Error SectionWriter::visit(const OwnedDataSection &Sec) {
-  llvm::copy(Sec.Data, Out.getBufferStart() + Sec.Offset);
+  writeSectionContents(Sec.Data, Sec.Offset);
   return Error::success();
 }
 
@@ -487,9 +492,7 @@ Error ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) {
                              "failed to decompress section '" + Sec.Name +
                                  "': " + toString(std::move(E)));
 
-  uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
-  llvm::copy(Decompressed, Buf);
-
+  writeSectionContents(Decompressed, Sec.Offset);
   return Error::success();
 }
 
@@ -532,11 +535,10 @@ Error BinarySectionWriter::visit(const CompressedSection &Sec) {
 
 template <class ELFT>
 Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
-  uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
   Elf_Chdr_Impl<ELFT> Chdr = {};
   switch (Sec.CompressionType) {
   case DebugCompressionType::None:
-    llvm::copy(Sec.OriginalData, Buf);
+    writeSectionContents(Sec.OriginalData, Sec.Offset);
     return Error::success();
   case DebugCompressionType::Zlib:
     Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB;
@@ -547,10 +549,10 @@ Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
   }
   Chdr.ch_size = Sec.DecompressedSize;
   Chdr.ch_addralign = Sec.DecompressedAlign;
-  memcpy(Buf, &Chdr, sizeof(Chdr));
-  Buf += sizeof(Chdr);
-
-  llvm::copy(Sec.CompressedData, Buf);
+  writeSectionContents(
+      ArrayRef(reinterpret_cast<const uint8_t *>(&Chdr), sizeof(Chdr)),
+      Sec.Offset);
+  writeSectionContents(Sec.CompressedData, Sec.Offset + sizeof(Chdr));
   return Error::success();
 }
 
@@ -613,8 +615,14 @@ Error StringTableSection::accept(MutableSectionVisitor &Visitor) {
 
 template <class ELFT>
 Error ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) {
-  uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
-  llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf));
+  SmallVector<Elf_Word, 0> Indexes;
+  Indexes.reserve(Sec.Indexes.size());
+  for (uint32_t Index : Sec.Indexes)
+    Indexes.emplace_back(Index);
+  writeSectionContents(
+      ArrayRef(reinterpret_cast<const uint8_t *>(Indexes.data()),
+               Indexes.size() * sizeof(Elf_Word)),
+      Sec.Offset);
   return Error::success();
 }
 
@@ -1029,7 +1037,7 @@ void RelocationSection::replaceSectionReferences(
 }
 
 Error SectionWriter::visit(const DynamicRelocationSection &Sec) {
-  llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
+  writeSectionContents(Sec.Contents, Sec.Offset);
   return Error::success();
 }
 
@@ -1182,7 +1190,10 @@ Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) {
   Elf_Word *CRC =
       reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word));
   *CRC = Sec.CRC32;
-  llvm::copy(Sec.FileName, Buf);
+  writeSectionContents(
+      ArrayRef(reinterpret_cast<const uint8_t *>(Sec.FileName.data()),
+               Sec.FileName.size()),
+      Sec.Offset);
   return Error::success();
 }
 
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 2783ef27ac9de..9df62d8642917 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -107,6 +107,7 @@ class MutableSectionVisitor {
 class SectionWriter : public SectionVisitor {
 protected:
   WritableMemoryBuffer &Out;
+  virtual void writeSectionContents(ArrayRef<uint8_t> Data, uint64_t Offset);
 
 public:
   ~SectionWriter() override = default;
diff --git a/llvm/test/tools/llvm-objcopy/ELF/many-sections.test b/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
index 6622db237026f..5a93fbaf649f3 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
@@ -4,6 +4,8 @@ RUN: %python %p/../Inputs/ungzip.py %p/Inputs/many-sections.o.gz > %t
 RUN: llvm-objcopy %t %t2
 RUN: llvm-readobj --file-headers --sections --symbols %t2 | FileCheck %s
 RUN: llvm-readelf --symbols %t2 | FileCheck --check-prefix=SYMS %s
+RUN: llvm-objcopy --output-target=elf64-s390 %t %t.be
+RUN: llvm-readobj --file-headers --sections --symbols %t.be | FileCheck %s
 
 ## The ELF header should have e_shnum == 0 and e_shstrndx == SHN_XINDEX.
 # CHECK:        SectionHeaderCount: 0

>From 9d6006702a1d7bb63aae65901111f239331c64a1 Mon Sep 17 00:00:00 2001
From: Farid Zakaria <fmzakari at fb.com>
Date: Thu, 20 Aug 2026 10:00:51 -0700
Subject: [PATCH 2/3] [llvm-objcopy] Propagate section write errors

Return Error from SectionWriter::writeSectionContents and propagate failures through all visitor call sites. This lets a later streaming writer report seek and write failures without fatal destructor-time handling.

Co-authored-by: Jeremy Braun <jtbraun at meta.com>

Assisted-by: OpenAI Codex
---
 llvm/lib/ObjCopy/ELF/ELFObject.cpp | 35 +++++++++++++-----------------
 llvm/lib/ObjCopy/ELF/ELFObject.h   |  2 +-
 2 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index dace67fdca44d..0714f2df107c1 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -183,14 +183,15 @@ Error BinarySectionWriter::visit(const GroupSection &Sec) {
                            "cannot write '" + Sec.Name + "' out to binary");
 }
 
-void SectionWriter::writeSectionContents(ArrayRef<uint8_t> Data,
-                                         uint64_t Offset) {
+Error SectionWriter::writeSectionContents(ArrayRef<uint8_t> Data,
+                                          uint64_t Offset) {
   llvm::copy(Data, Out.getBufferStart() + Offset);
+  return Error::success();
 }
 
 Error SectionWriter::visit(const Section &Sec) {
   if (Sec.Type != SHT_NOBITS)
-    writeSectionContents(Sec.Contents, Sec.Offset);
+    return writeSectionContents(Sec.Contents, Sec.Offset);
 
   return Error::success();
 }
@@ -458,8 +459,7 @@ void Section::restoreSymTabLink(SymbolTableSection &SymTab) {
 }
 
 Error SectionWriter::visit(const OwnedDataSection &Sec) {
-  writeSectionContents(Sec.Data, Sec.Offset);
-  return Error::success();
+  return writeSectionContents(Sec.Data, Sec.Offset);
 }
 
 template <class ELFT>
@@ -492,8 +492,7 @@ Error ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) {
                              "failed to decompress section '" + Sec.Name +
                                  "': " + toString(std::move(E)));
 
-  writeSectionContents(Decompressed, Sec.Offset);
-  return Error::success();
+  return writeSectionContents(Decompressed, Sec.Offset);
 }
 
 Error BinarySectionWriter::visit(const DecompressedSection &Sec) {
@@ -538,8 +537,7 @@ Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
   Elf_Chdr_Impl<ELFT> Chdr = {};
   switch (Sec.CompressionType) {
   case DebugCompressionType::None:
-    writeSectionContents(Sec.OriginalData, Sec.Offset);
-    return Error::success();
+    return writeSectionContents(Sec.OriginalData, Sec.Offset);
   case DebugCompressionType::Zlib:
     Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB;
     break;
@@ -549,11 +547,11 @@ Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
   }
   Chdr.ch_size = Sec.DecompressedSize;
   Chdr.ch_addralign = Sec.DecompressedAlign;
-  writeSectionContents(
-      ArrayRef(reinterpret_cast<const uint8_t *>(&Chdr), sizeof(Chdr)),
-      Sec.Offset);
-  writeSectionContents(Sec.CompressedData, Sec.Offset + sizeof(Chdr));
-  return Error::success();
+  if (Error E = writeSectionContents(
+          ArrayRef(reinterpret_cast<const uint8_t *>(&Chdr), sizeof(Chdr)),
+          Sec.Offset))
+    return E;
+  return writeSectionContents(Sec.CompressedData, Sec.Offset + sizeof(Chdr));
 }
 
 CompressedSection::CompressedSection(const SectionBase &Sec,
@@ -619,11 +617,10 @@ Error ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) {
   Indexes.reserve(Sec.Indexes.size());
   for (uint32_t Index : Sec.Indexes)
     Indexes.emplace_back(Index);
-  writeSectionContents(
+  return writeSectionContents(
       ArrayRef(reinterpret_cast<const uint8_t *>(Indexes.data()),
                Indexes.size() * sizeof(Elf_Word)),
       Sec.Offset);
-  return Error::success();
 }
 
 Error SectionIndexSection::initialize(SectionTableRef SecTable) {
@@ -1037,8 +1034,7 @@ void RelocationSection::replaceSectionReferences(
 }
 
 Error SectionWriter::visit(const DynamicRelocationSection &Sec) {
-  writeSectionContents(Sec.Contents, Sec.Offset);
-  return Error::success();
+  return writeSectionContents(Sec.Contents, Sec.Offset);
 }
 
 Error DynamicRelocationSection::accept(SectionVisitor &Visitor) const {
@@ -1190,11 +1186,10 @@ Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) {
   Elf_Word *CRC =
       reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word));
   *CRC = Sec.CRC32;
-  writeSectionContents(
+  return writeSectionContents(
       ArrayRef(reinterpret_cast<const uint8_t *>(Sec.FileName.data()),
                Sec.FileName.size()),
       Sec.Offset);
-  return Error::success();
 }
 
 Error GnuDebugLinkSection::accept(SectionVisitor &Visitor) const {
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 9df62d8642917..bef59ca4148c5 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -107,7 +107,7 @@ class MutableSectionVisitor {
 class SectionWriter : public SectionVisitor {
 protected:
   WritableMemoryBuffer &Out;
-  virtual void writeSectionContents(ArrayRef<uint8_t> Data, uint64_t Offset);
+  virtual Error writeSectionContents(ArrayRef<uint8_t> Data, uint64_t Offset);
 
 public:
   ~SectionWriter() override = default;

>From 68378863d7e103b5b0d1176f9432e75c81c1baec Mon Sep 17 00:00:00 2001
From: Farid Zakaria <fmzakari at fb.com>
Date: Thu, 20 Aug 2026 10:11:13 -0700
Subject: [PATCH 3/3] [llvm-objcopy] Stream ELF output to regular files

Write ELF output directly to seekable regular files instead of first allocating a buffer for the entire output. Keep buffered output for stdout and other non-seekable streams, and materialize regenerated metadata one section at a time.

Track written ranges so unwritten gaps are zero-filled and the final file is resized exactly. Surface file stream failures as Error values so temporary outputs are discarded instead of producing destructor-time fatal errors.

On a synthetic stamping-style ELF containing a 1 GiB payload, three warm-cache no-op copies had median peak RSS of 2,102,916 KiB before and 1,053,844 KiB after (49.9% lower). Median wall time improved from 1.06 s to 0.77 s, and the outputs were byte-identical.

Co-authored-by: Jeremy Braun <jtbraun at meta.com>

Assisted-by: OpenAI Codex
---
 llvm/include/llvm/Support/raw_ostream.h     |   7 +
 llvm/lib/ObjCopy/ELF/ELFObject.cpp          | 298 ++++++++++++++------
 llvm/lib/ObjCopy/ELF/ELFObject.h            |  51 +++-
 llvm/lib/Support/raw_ostream.cpp            |  50 +++-
 llvm/unittests/ObjCopy/ObjCopyTest.cpp      |  62 ++++
 llvm/unittests/Support/raw_ostream_test.cpp |  66 +++++
 6 files changed, 436 insertions(+), 98 deletions(-)

diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
index 841fe90515608..a2853b35f3d9d 100644
--- a/llvm/include/llvm/Support/raw_ostream.h
+++ b/llvm/include/llvm/Support/raw_ostream.h
@@ -31,6 +31,7 @@
 namespace llvm {
 
 class Duration;
+class Error;
 class formatv_object_base;
 class FormattedString;
 class FormattedNumber;
@@ -575,6 +576,9 @@ class LLVM_ABI raw_fd_ostream : public raw_pwrite_stream {
   ///
   void clear_error() { EC = std::error_code(); }
 
+  /// Return and clear any output error recorded by this stream.
+  Error takeError();
+
   /// Locks the underlying file.
   ///
   /// @returns RAII object that releases the lock upon leaving the scope, if the
@@ -648,6 +652,9 @@ class raw_fd_stream : public raw_fd_ostream {
   /// error code.
   LLVM_ABI ssize_t read(char *Ptr, size_t Size);
 
+  /// Resize the underlying file to \p Size bytes.
+  LLVM_ABI Error resize(uint64_t Size);
+
   /// Check if \p OS is a pointer of type raw_fd_stream*.
   LLVM_ABI static bool classof(const raw_ostream *OS);
 };
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index 0714f2df107c1..d62a656b661c9 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -16,10 +16,12 @@
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/MC/MCELFExtras.h"
 #include "llvm/MC/MCTargetOptions.h"
+#include "llvm/Support/CheckedArithmetic.h"
 #include "llvm/Support/Compression.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cstddef>
 #include <cstdint>
@@ -33,10 +35,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);
+Error ELFWriterOutput::write(ArrayRef<uint8_t> Data, uint64_t Offset) {
+  if (Data.empty())
+    return Error::success();
+  if (Buffer) {
+    llvm::copy(Data, Buffer->getBufferStart() + Offset);
+    return Error::success();
+  }
+  Stream->pwrite(reinterpret_cast<const char *>(Data.data()), Data.size(),
+                 StartOffset + Offset);
+  if (Error E = Stream->takeError())
+    return E;
+  WrittenRanges.insert({Offset, Offset + Data.size()});
+  return Error::success();
+}
+
+Error ELFWriterOutput::writeZerosImpl(uint64_t Offset, uint64_t Size) {
+  if (!Size)
+    return Error::success();
+
+  const uint64_t Pos = Stream->tell();
+  Stream->seek(StartOffset + Offset);
+  if (Error E = Stream->takeError())
+    return E;
+  static constexpr uint8_t Zeros[64 * 1024] = {};
+  while (Size) {
+    size_t ChunkSize = std::min<uint64_t>(Size, sizeof(Zeros));
+    Stream->write(reinterpret_cast<const char *>(Zeros), ChunkSize);
+    if (Error E = Stream->takeError())
+      return E;
+    Size -= ChunkSize;
+  }
+  Stream->flush();
+  if (Error E = Stream->takeError())
+    return E;
+  Stream->seek(Pos);
+  return Stream->takeError();
+}
+
+Error ELFWriterOutput::writeZeros(uint64_t Offset, uint64_t Size) {
+  if (Buffer) {
+    std::memset(Buffer->getBufferStart() + Offset, 0, Size);
+    return Error::success();
+  }
+
+  if (Error E = writeZerosImpl(Offset, Size))
+    return E;
+  if (Size)
+    WrittenRanges.insert({Offset, Offset + Size});
+  return Error::success();
+}
+
+Error ELFWriterOutput::finalize(uint64_t Size) {
+  if (Buffer)
+    return Error::success();
+
+  uint64_t End = 0;
+  for (const AddressRange &Range : WrittenRanges) {
+    assert(Range.end() <= Size && "write exceeds output size");
+    if (Range.start() > End)
+      if (Error E = writeZerosImpl(End, Range.start() - End))
+        return E;
+    End = Range.end();
+  }
+  if (End < Size)
+    if (Error E = writeZerosImpl(End, Size - End))
+      return E;
+
+  std::optional<uint64_t> EndOffset = checkedAddUnsigned(StartOffset, Size);
+  if (!EndOffset)
+    return createStringError(errc::file_too_large,
+                             "output exceeds the addressable file range");
+  if (Error E = Stream->resize(*EndOffset))
+    return E;
+  Stream->seek(*EndOffset);
+  if (Error E = Stream->takeError())
+    return E;
+  return Error::success();
+}
+
+template <class ELFT> Error ELFWriter<ELFT>::writePhdr(const Segment &Seg) {
+  Elf_Phdr Phdr = {};
   Phdr.p_type = Seg.Type;
   Phdr.p_flags = Seg.Flags;
   Phdr.p_offset = Seg.Offset;
@@ -45,6 +124,9 @@ template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) {
   Phdr.p_filesz = Seg.FileSize;
   Phdr.p_memsz = Seg.MemSize;
   Phdr.p_align = Seg.Align;
+  return Output->write(
+      ArrayRef(reinterpret_cast<const uint8_t *>(&Phdr), sizeof(Phdr)),
+      Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr));
 }
 
 Error SectionBase::removeSectionReferences(
@@ -63,10 +145,8 @@ void SectionBase::replaceSectionReferences(
     const DenseMap<SectionBase *, SectionBase *> &) {}
 void SectionBase::onRemove() {}
 
-template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
-  uint8_t *B =
-      reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Sec.HeaderOffset;
-  Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B);
+template <class ELFT> Error ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
+  Elf_Shdr Shdr = {};
   Shdr.sh_name = Sec.NameIndex;
   Shdr.sh_type = Sec.Type;
   Shdr.sh_flags = Sec.Flags;
@@ -77,6 +157,9 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
   Shdr.sh_info = Sec.Info;
   Shdr.sh_addralign = Sec.Align;
   Shdr.sh_entsize = Sec.EntrySize;
+  return Output->write(
+      ArrayRef(reinterpret_cast<const uint8_t *>(&Shdr), sizeof(Shdr)),
+      Sec.HeaderOffset);
 }
 
 template <class ELFT> Error ELFSectionSizer<ELFT>::visit(Section &) {
@@ -183,12 +266,18 @@ Error BinarySectionWriter::visit(const GroupSection &Sec) {
                            "cannot write '" + Sec.Name + "' out to binary");
 }
 
-Error SectionWriter::writeSectionContents(ArrayRef<uint8_t> Data,
-                                          uint64_t Offset) {
+Error BinarySectionWriter::writeSectionContents(ArrayRef<uint8_t> Data,
+                                                uint64_t Offset) {
   llvm::copy(Data, Out.getBufferStart() + Offset);
   return Error::success();
 }
 
+template <class ELFT>
+Error ELFSectionWriter<ELFT>::writeSectionContents(ArrayRef<uint8_t> Data,
+                                                   uint64_t Offset) {
+  return Out.write(Data, Offset);
+}
+
 Error SectionWriter::visit(const Section &Sec) {
   if (Sec.Type != SHT_NOBITS)
     return writeSectionContents(Sec.Contents, Sec.Offset);
@@ -598,9 +687,9 @@ void StringTableSection::prepareForLayout() {
 }
 
 Error SectionWriter::visit(const StringTableSection &Sec) {
-  Sec.StrTabBuilder.write(reinterpret_cast<uint8_t *>(Out.getBufferStart()) +
-                          Sec.Offset);
-  return Error::success();
+  SmallVector<uint8_t, 0> Data(Sec.Size);
+  Sec.StrTabBuilder.write(Data.data());
+  return writeSectionContents(Data, Sec.Offset);
 }
 
 Error StringTableSection::accept(SectionVisitor &Visitor) const {
@@ -865,7 +954,8 @@ Expected<Symbol *> SymbolTableSection::getSymbolByIndex(uint32_t Index) {
 
 template <class ELFT>
 Error ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) {
-  Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Out.getBufferStart() + Sec.Offset);
+  SmallVector<Elf_Sym, 0> Symbols(Sec.Symbols.size());
+  Elf_Sym *Sym = Symbols.data();
   // Loop though symbols setting each entry of the symbol table.
   for (const std::unique_ptr<Symbol> &Symbol : Sec.Symbols) {
     Sym->st_name = Symbol->NameIndex;
@@ -877,7 +967,10 @@ Error ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) {
     Sym->st_shndx = Symbol->getShndx();
     ++Sym;
   }
-  return Error::success();
+  return writeSectionContents(
+      ArrayRef(reinterpret_cast<const uint8_t *>(Symbols.data()),
+               Symbols.size() * sizeof(Elf_Sym)),
+      Sec.Offset);
 }
 
 Error SymbolTableSection::accept(SectionVisitor &Visitor) const {
@@ -987,18 +1080,27 @@ static void writeRel(const RelRange &Relocations, T *Buf, bool IsMips64EL) {
 
 template <class ELFT>
 Error ELFSectionWriter<ELFT>::visit(const RelocationSection &Sec) {
-  uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
   if (Sec.Type == SHT_CREL) {
     auto Content = encodeCrel<ELFT::Is64Bits>(Sec.Relocations);
-    memcpy(Buf, Content.data(), Content.size());
-  } else if (Sec.Type == SHT_REL) {
-    writeRel(Sec.Relocations, reinterpret_cast<Elf_Rel *>(Buf),
-             Sec.getObject().IsMips64EL);
-  } else {
-    writeRel(Sec.Relocations, reinterpret_cast<Elf_Rela *>(Buf),
-             Sec.getObject().IsMips64EL);
-  }
-  return Error::success();
+    return writeSectionContents(
+        ArrayRef(reinterpret_cast<const uint8_t *>(Content.data()),
+                 Content.size()),
+        Sec.Offset);
+  }
+  if (Sec.Type == SHT_REL) {
+    SmallVector<Elf_Rel, 0> Relocations(Sec.Relocations.size());
+    writeRel(Sec.Relocations, Relocations.data(), Sec.getObject().IsMips64EL);
+    return writeSectionContents(
+        ArrayRef(reinterpret_cast<const uint8_t *>(Relocations.data()),
+                 Relocations.size() * sizeof(Elf_Rel)),
+        Sec.Offset);
+  }
+  SmallVector<Elf_Rela, 0> Relocations(Sec.Relocations.size());
+  writeRel(Sec.Relocations, Relocations.data(), Sec.getObject().IsMips64EL);
+  return writeSectionContents(
+      ArrayRef(reinterpret_cast<const uint8_t *>(Relocations.data()),
+               Relocations.size() * sizeof(Elf_Rela)),
+      Sec.Offset);
 }
 
 Error RelocationSection::accept(SectionVisitor &Visitor) const {
@@ -1181,15 +1283,16 @@ GnuDebugLinkSection::GnuDebugLinkSection(StringRef File,
 
 template <class ELFT>
 Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) {
-  unsigned char *Buf =
-      reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
-  Elf_Word *CRC =
-      reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word));
-  *CRC = Sec.CRC32;
+  Elf_Word CRC;
+  CRC = Sec.CRC32;
+  if (Error E = writeSectionContents(
+          ArrayRef(reinterpret_cast<const uint8_t *>(Sec.FileName.data()),
+                   Sec.FileName.size()),
+          Sec.Offset))
+    return E;
   return writeSectionContents(
-      ArrayRef(reinterpret_cast<const uint8_t *>(Sec.FileName.data()),
-               Sec.FileName.size()),
-      Sec.Offset);
+      ArrayRef(reinterpret_cast<const uint8_t *>(&CRC), sizeof(CRC)),
+      Sec.Offset + Sec.Size - sizeof(CRC));
 }
 
 Error GnuDebugLinkSection::accept(SectionVisitor &Visitor) const {
@@ -1202,12 +1305,15 @@ Error GnuDebugLinkSection::accept(MutableSectionVisitor &Visitor) {
 
 template <class ELFT>
 Error ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) {
-  ELF::Elf32_Word *Buf =
-      reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset);
-  endian::write32<ELFT::Endianness>(Buf++, Sec.FlagWord);
-  for (SectionBase *S : Sec.GroupMembers)
-    endian::write32<ELFT::Endianness>(Buf++, S->Index);
-  return Error::success();
+  SmallVector<uint8_t, 0> Data(Sec.Size);
+  uint8_t *Buf = Data.data();
+  endian::write32<ELFT::Endianness>(Buf, Sec.FlagWord);
+  Buf += sizeof(ELF::Elf32_Word);
+  for (SectionBase *S : Sec.GroupMembers) {
+    endian::write32<ELFT::Endianness>(Buf, S->Index);
+    Buf += sizeof(ELF::Elf32_Word);
+  }
+  return writeSectionContents(Data, Sec.Offset);
 }
 
 Error GroupSection::accept(SectionVisitor &Visitor) const {
@@ -2025,9 +2131,8 @@ Expected<std::unique_ptr<Object>> ELFReader::create(bool EnsureSymtab) const {
   return createStringError(errc::invalid_argument, "invalid file type");
 }
 
-template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
-  Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf->getBufferStart());
-  std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0);
+template <class ELFT> Error ELFWriter<ELFT>::writeEhdr() {
+  Elf_Ehdr Ehdr = {};
   Ehdr.e_ident[EI_MAG0] = 0x7f;
   Ehdr.e_ident[EI_MAG1] = 'E';
   Ehdr.e_ident[EI_MAG2] = 'L';
@@ -2080,40 +2185,38 @@ template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
     Ehdr.e_shnum = 0;
     Ehdr.e_shstrndx = 0;
   }
+  return Output->write(
+      ArrayRef(reinterpret_cast<const uint8_t *>(&Ehdr), sizeof(Ehdr)), 0);
 }
 
-template <class ELFT> void ELFWriter<ELFT>::writePhdrs() {
-  for (auto &Seg : Obj.segments())
-    writePhdr(Seg);
+template <class ELFT> Error ELFWriter<ELFT>::writePhdrs() {
+  for (auto &Seg : Obj.segments()) {
+    if (Error E = writePhdr(Seg))
+      return E;
+  }
+  return Error::success();
 }
 
-template <class ELFT> void ELFWriter<ELFT>::writeShdrs() {
-  // This reference serves to write the dummy section header at the begining
-  // of the file. It is not used for anything else
-  Elf_Shdr &Shdr =
-      *reinterpret_cast<Elf_Shdr *>(Buf->getBufferStart() + Obj.SHOff);
-  Shdr.sh_name = 0;
+template <class ELFT> Error ELFWriter<ELFT>::writeShdrs() {
+  Elf_Shdr Shdr = {};
   Shdr.sh_type = SHT_NULL;
-  Shdr.sh_flags = 0;
-  Shdr.sh_addr = 0;
-  Shdr.sh_offset = 0;
   // See writeEhdr for why we do this.
   uint64_t Shnum = Obj.sections().size() + 1;
   if (Shnum >= SHN_LORESERVE)
     Shdr.sh_size = Shnum;
-  else
-    Shdr.sh_size = 0;
   // See writeEhdr for why we do this.
   if (Obj.SectionNames != nullptr && Obj.SectionNames->Index >= SHN_LORESERVE)
     Shdr.sh_link = Obj.SectionNames->Index;
-  else
-    Shdr.sh_link = 0;
-  Shdr.sh_info = 0;
-  Shdr.sh_addralign = 0;
-  Shdr.sh_entsize = 0;
+  if (Error E = Output->write(
+          ArrayRef(reinterpret_cast<const uint8_t *>(&Shdr), sizeof(Shdr)),
+          Obj.SHOff))
+    return E;
 
-  for (SectionBase &Sec : Obj.sections())
-    writeShdr(Sec);
+  for (SectionBase &Sec : Obj.sections()) {
+    if (Error E = writeShdr(Sec))
+      return E;
+  }
+  return Error::success();
 }
 
 template <class ELFT> Error ELFWriter<ELFT>::writeSectionData() {
@@ -2128,11 +2231,11 @@ template <class ELFT> Error ELFWriter<ELFT>::writeSectionData() {
   return Error::success();
 }
 
-template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
+template <class ELFT> Error ELFWriter<ELFT>::writeSegmentData() {
   for (Segment &Seg : Obj.segments()) {
     size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size());
-    std::memcpy(Buf->getBufferStart() + Seg.Offset, Seg.getContents().data(),
-                Size);
+    if (Error E = Output->write(Seg.getContents().take_front(Size), Seg.Offset))
+      return E;
   }
 
   for (const auto &it : Obj.getUpdatedSections()) {
@@ -2143,7 +2246,8 @@ template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
     assert(Parent && "This section should've been part of a segment.");
     uint64_t Offset =
         Sec->OriginalOffset - Parent->OriginalOffset + Parent->Offset;
-    llvm::copy(Data, Buf->getBufferStart() + Offset);
+    if (Error E = Output->write(Data, Offset))
+      return E;
   }
 
   // Iterate over removed sections and overwrite their old data with zeroes.
@@ -2153,8 +2257,10 @@ template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
       continue;
     uint64_t Offset =
         Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset;
-    std::memset(Buf->getBufferStart() + Offset, 0, Sec.Size);
+    if (Error E = Output->writeZeros(Offset, Sec.Size))
+      return E;
   }
+  return Error::success();
 }
 
 template <class ELFT>
@@ -2556,17 +2662,24 @@ template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const {
 template <class ELFT> Error ELFWriter<ELFT>::write() {
   // Segment data must be written first, so that the ELF header and program
   // header tables can overwrite it, if covered by a segment.
-  writeSegmentData();
-  writeEhdr();
-  writePhdrs();
+  if (Error E = writeSegmentData())
+    return E;
+  if (Error E = writeEhdr())
+    return E;
+  if (Error E = writePhdrs())
+    return E;
   if (Error E = writeSectionData())
     return E;
-  if (WriteSectionHeaders)
-    writeShdrs();
+  if (WriteSectionHeaders) {
+    if (Error E = writeShdrs())
+      return E;
+  }
 
-  // TODO: Implement direct writing to the output stream (without intermediate
-  // memory buffer Buf).
-  Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+  if (Error E = Output->finalize(totalSize()))
+    return E;
+
+  if (Buf)
+    Out.write(Buf->getBufferStart(), Buf->getBufferSize());
   return Error::success();
 }
 
@@ -2695,13 +2808,34 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() {
   }
 
   size_t TotalSize = totalSize();
-  Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
-  if (!Buf)
-    return createStringError(errc::not_enough_memory,
-                             "failed to allocate memory buffer of " +
-                                 Twine::utohexstr(TotalSize) + " bytes");
+  if (auto *Stream = dyn_cast<raw_fd_stream>(&Out);
+      Stream && Stream->supportsSeeking() && Stream->isRegularFile()) {
+    uint64_t StartOffset = Stream->tell();
+    std::optional<uint64_t> EndOffset =
+        checkedAddUnsigned(StartOffset, static_cast<uint64_t>(TotalSize));
+    if (!EndOffset)
+      return createStringError(errc::file_too_large,
+                               "output exceeds the addressable file range");
+    if (TotalSize) {
+      Stream->seek(*EndOffset - 1);
+      if (Error E = Stream->takeError())
+        return E;
+      *Stream << '\0';
+      Stream->flush();
+      if (Error E = Stream->takeError())
+        return E;
+    }
+    Output.emplace(*Stream, StartOffset);
+  } else {
+    Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+    if (!Buf)
+      return createStringError(errc::not_enough_memory,
+                               "failed to allocate memory buffer of " +
+                                   Twine::utohexstr(TotalSize) + " bytes");
+    Output.emplace(*Buf);
+  }
 
-  SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(*Buf);
+  SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(*Output);
   return Error::success();
 }
 
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index bef59ca4148c5..c3f965e4930b3 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIB_OBJCOPY_ELF_ELFOBJECT_H
 #define LLVM_LIB_OBJCOPY_ELF_ELFOBJECT_H
 
+#include "llvm/ADT/AddressRanges.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
@@ -28,6 +29,7 @@
 
 namespace llvm {
 enum class DebugCompressionType;
+class raw_fd_stream;
 namespace objcopy {
 namespace elf {
 
@@ -47,6 +49,24 @@ class Segment;
 class Object;
 struct Symbol;
 
+class ELFWriterOutput {
+  WritableMemoryBuffer *Buffer = nullptr;
+  raw_fd_stream *Stream = nullptr;
+  uint64_t StartOffset = 0;
+  AddressRanges WrittenRanges;
+
+  Error writeZerosImpl(uint64_t Offset, uint64_t Size);
+
+public:
+  explicit ELFWriterOutput(WritableMemoryBuffer &Buffer) : Buffer(&Buffer) {}
+  ELFWriterOutput(raw_fd_stream &Stream, uint64_t StartOffset)
+      : Stream(&Stream), StartOffset(StartOffset) {}
+
+  Error write(ArrayRef<uint8_t> Data, uint64_t Offset);
+  Error writeZeros(uint64_t Offset, uint64_t Size);
+  Error finalize(uint64_t Size);
+};
+
 class SectionTableRef {
   ArrayRef<std::unique_ptr<SectionBase>> Sections;
 
@@ -106,8 +126,8 @@ class MutableSectionVisitor {
 
 class SectionWriter : public SectionVisitor {
 protected:
-  WritableMemoryBuffer &Out;
-  virtual Error writeSectionContents(ArrayRef<uint8_t> Data, uint64_t Offset);
+  virtual Error writeSectionContents(ArrayRef<uint8_t> Data,
+                                     uint64_t Offset) = 0;
 
 public:
   ~SectionWriter() override = default;
@@ -123,8 +143,6 @@ class SectionWriter : public SectionVisitor {
   Error visit(const SectionIndexSection &Sec) override = 0;
   Error visit(const CompressedSection &Sec) override = 0;
   Error visit(const DecompressedSection &Sec) override = 0;
-
-  explicit SectionWriter(WritableMemoryBuffer &Buf) : Out(Buf) {}
 };
 
 template <class ELFT> class ELFSectionWriter : public SectionWriter {
@@ -134,6 +152,9 @@ template <class ELFT> class ELFSectionWriter : public SectionWriter {
   using Elf_Rela = typename ELFT::Rela;
   using Elf_Sym = typename ELFT::Sym;
 
+  ELFWriterOutput &Out;
+  Error writeSectionContents(ArrayRef<uint8_t> Data, uint64_t Offset) override;
+
 public:
   ~ELFSectionWriter() override = default;
   Error visit(const SymbolTableSection &Sec) override;
@@ -144,7 +165,7 @@ template <class ELFT> class ELFSectionWriter : public SectionWriter {
   Error visit(const CompressedSection &Sec) override;
   Error visit(const DecompressedSection &Sec) override;
 
-  explicit ELFSectionWriter(WritableMemoryBuffer &Buf) : SectionWriter(Buf) {}
+  explicit ELFSectionWriter(ELFWriterOutput &Out) : Out(Out) {}
 };
 
 template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor {
@@ -180,6 +201,10 @@ template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor {
   template <class ELFT> friend class ELFSectionSizer;
 
 class BinarySectionWriter : public SectionWriter {
+protected:
+  WritableMemoryBuffer &Out;
+  Error writeSectionContents(ArrayRef<uint8_t> Data, uint64_t Offset) override;
+
 public:
   ~BinarySectionWriter() override = default;
 
@@ -191,8 +216,7 @@ class BinarySectionWriter : public SectionWriter {
   Error visit(const CompressedSection &Sec) override;
   Error visit(const DecompressedSection &Sec) override;
 
-  explicit BinarySectionWriter(WritableMemoryBuffer &Buf)
-      : SectionWriter(Buf) {}
+  explicit BinarySectionWriter(WritableMemoryBuffer &Buf) : Out(Buf) {}
 };
 
 using IHexLineData = SmallVector<char, 64>;
@@ -331,17 +355,18 @@ template <class ELFT> class ELFWriter : public Writer {
 
   void initEhdrSegment();
 
-  void writeEhdr();
-  void writePhdr(const Segment &Seg);
-  void writeShdr(const SectionBase &Sec);
+  Error writeEhdr();
+  Error writePhdr(const Segment &Seg);
+  Error writeShdr(const SectionBase &Sec);
 
-  void writePhdrs();
-  void writeShdrs();
+  Error writePhdrs();
+  Error writeShdrs();
   Error writeSectionData();
-  void writeSegmentData();
+  Error writeSegmentData();
 
   void assignOffsets();
 
+  std::optional<ELFWriterOutput> Output;
   std::unique_ptr<ELFSectionWriter<ELFT>> SecWriter;
 
   size_t totalSize() const;
diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index 28401d674479c..331b40341e29b 100644
--- a/llvm/lib/Support/raw_ostream.cpp
+++ b/llvm/lib/Support/raw_ostream.cpp
@@ -62,6 +62,23 @@
 
 using namespace llvm;
 
+namespace {
+class RawOstreamError : public ErrorInfo<RawOstreamError> {
+public:
+  static char ID;
+
+  explicit RawOstreamError(std::error_code EC) : EC(EC) {}
+
+  void log(raw_ostream &OS) const override { OS << EC.message(); }
+  std::error_code convertToErrorCode() const override { return EC; }
+
+private:
+  std::error_code EC;
+};
+} // namespace
+
+char RawOstreamError::ID;
+
 raw_ostream::~raw_ostream() {
   // raw_ostream's subclasses should take care to flush the buffer
   // in their destructors.
@@ -664,6 +681,14 @@ raw_fd_ostream::~raw_fd_ostream() {
                           error().message());
 }
 
+Error raw_fd_ostream::takeError() {
+  if (!has_error())
+    return Error::success();
+  Error E = make_error<RawOstreamError>(error());
+  clear_error();
+  return E;
+}
+
 #if defined(_WIN32)
 // The most reliable way to print unicode in a Windows console is with
 // WriteConsoleW. To use that, first transcode from UTF-8 to UTF-16. This
@@ -933,6 +958,15 @@ ssize_t raw_fd_stream::read(char *Ptr, size_t Size) {
   return Ret;
 }
 
+Error raw_fd_stream::resize(uint64_t Size) {
+  flush();
+  if (has_error())
+    return takeError();
+  if (std::error_code EC = sys::fs::resize_file(get_fd(), Size))
+    return make_error<RawOstreamError>(EC);
+  return Error::success();
+}
+
 bool raw_fd_stream::classof(const raw_ostream *OS) {
   return OS->get_kind() == OStreamKind::OK_FDStream;
 }
@@ -1027,14 +1061,24 @@ Error llvm::writeToOutput(StringRef OutputFileName,
   }
 #endif
 
-  raw_fd_ostream Out(Temp->FD, false);
+  Error E = Error::success();
+  {
+    raw_fd_stream Out(Temp->FD, false);
+    E = Write(Out);
+    Out.flush();
+    E = joinErrors(std::move(E), Out.takeError());
+  }
+
+  E = handleErrors(
+      std::move(E), [&](std::unique_ptr<RawOstreamError> StreamError) {
+        return createFileError(OutputFileName, Error(std::move(StreamError)));
+      });
 
-  if (Error E = Write(Out)) {
+  if (E) {
     if (Error DiscardError = Temp->discard())
       return joinErrors(std::move(E), std::move(DiscardError));
     return E;
   }
-  Out.flush();
 
   return Temp->keep(OutputFileName);
 }
diff --git a/llvm/unittests/ObjCopy/ObjCopyTest.cpp b/llvm/unittests/ObjCopy/ObjCopyTest.cpp
index 4382c73e889e9..7d0114e91734b 100644
--- a/llvm/unittests/ObjCopy/ObjCopyTest.cpp
+++ b/llvm/unittests/ObjCopy/ObjCopyTest.cpp
@@ -11,7 +11,10 @@
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/ObjectYAML/yaml2obj.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
 
@@ -218,6 +221,65 @@ TEST(CopySimpleInMemoryFile, ELF) {
                              [](const Binary &File) { return File.isELF(); });
 }
 
+TEST(CopySimpleInMemoryFile, ELFFileStreamMatchesBufferedOutput) {
+  SmallVector<char> Storage;
+  Expected<std::unique_ptr<ObjectFile>> Obj =
+      createObjectFileFromYamlDescription(
+          SimpleFileELFYAML, Storage,
+          [](const Binary &File) { return File.isELF(); });
+  ASSERT_THAT_EXPECTED(Obj, Succeeded());
+
+  ConfigManager Config;
+  Config.Common.OutputFilename = "a.out";
+  SmallVector<char> ExpectedData;
+  raw_svector_ostream ExpectedOut(ExpectedData);
+  ASSERT_THAT_ERROR(objcopy::executeObjcopyOnBinary(Config, **Obj, ExpectedOut),
+                    Succeeded());
+
+  SmallString<64> Path;
+  int FD;
+  ASSERT_FALSE(sys::fs::createTemporaryFile("objcopy", "elf", FD, Path));
+  FileRemover Cleanup(Path);
+  StringRef Prefix = "prefix";
+  {
+    raw_fd_stream Out(FD, true);
+    Out << Prefix << std::string(ExpectedData.size() + 16, '\xff');
+    Out.seek(Prefix.size());
+    ASSERT_THAT_ERROR(objcopy::executeObjcopyOnBinary(Config, **Obj, Out),
+                      Succeeded());
+  }
+
+  ErrorOr<std::unique_ptr<MemoryBuffer>> Output = MemoryBuffer::getFile(Path);
+  ASSERT_TRUE(Output);
+  std::string Expected(Prefix);
+  Expected.append(ExpectedData.begin(), ExpectedData.end());
+  EXPECT_EQ((*Output)->getBuffer(), Expected);
+}
+
+TEST(CopySimpleInMemoryFile, ELFFileStreamReturnsWriteErrors) {
+  SmallVector<char> Storage;
+  Expected<std::unique_ptr<ObjectFile>> Obj =
+      createObjectFileFromYamlDescription(
+          SimpleFileELFYAML, Storage,
+          [](const Binary &File) { return File.isELF(); });
+  ASSERT_THAT_EXPECTED(Obj, Succeeded());
+
+  SmallString<64> Path;
+  int FD;
+  ASSERT_FALSE(sys::fs::createTemporaryFile("objcopy", "elf", FD, Path));
+  FileRemover Cleanup(Path);
+  {
+    raw_fd_ostream Out(FD, true);
+  }
+  ASSERT_FALSE(sys::fs::openFileForRead(Path, FD));
+
+  ConfigManager Config;
+  Config.Common.OutputFilename = "a.out";
+  raw_fd_stream Out(FD, true);
+  EXPECT_THAT_ERROR(objcopy::executeObjcopyOnBinary(Config, **Obj, Out),
+                    Failed());
+}
+
 TEST(CopySimpleInMemoryFile, MachO) {
   SCOPED_TRACE("CopySimpleInMemoryFileMachO");
 
diff --git a/llvm/unittests/Support/raw_ostream_test.cpp b/llvm/unittests/Support/raw_ostream_test.cpp
index aded02c7b3402..86367bb30412d 100644
--- a/llvm/unittests/Support/raw_ostream_test.cpp
+++ b/llvm/unittests/Support/raw_ostream_test.cpp
@@ -572,6 +572,72 @@ TEST(raw_ostreamTest, writeToOutputFile) {
   checkFileData(Path, "HelloWorld");
 }
 
+TEST(raw_ostreamTest, writeToOutputErrorPreservesDestination) {
+  SmallString<64> Path;
+  int FD;
+  ASSERT_FALSE(sys::fs::createTemporaryFile("foo", "bar", FD, Path));
+  FileRemover Cleanup(Path);
+  {
+    raw_fd_ostream Out(FD, true);
+    Out << "Original";
+  }
+
+  std::string ErrorMessage =
+      toString(createFileError(Path, make_error_code(errc::invalid_argument)));
+  EXPECT_THAT_ERROR(writeToOutput(Path,
+                                  [](raw_ostream &Out) -> Error {
+                                    static_cast<raw_fd_stream &>(Out).seek(
+                                        UINT64_MAX);
+                                    return Error::success();
+                                  }),
+                    FailedWithMessage(ErrorMessage));
+  checkFileData(Path, "Original");
+}
+
+TEST(raw_ostreamTest, writeToOutputReturnedStreamErrorPreservesDestination) {
+  SmallString<64> Path;
+  int FD;
+  ASSERT_FALSE(sys::fs::createTemporaryFile("foo", "bar", FD, Path));
+  FileRemover Cleanup(Path);
+  {
+    raw_fd_ostream Out(FD, true);
+    Out << "Original";
+  }
+
+  std::string ErrorMessage =
+      toString(createFileError(Path, make_error_code(errc::invalid_argument)));
+  EXPECT_THAT_ERROR(writeToOutput(Path,
+                                  [](raw_ostream &Out) -> Error {
+                                    auto &Stream =
+                                        static_cast<raw_fd_stream &>(Out);
+                                    Stream.seek(UINT64_MAX);
+                                    return Stream.takeError();
+                                  }),
+                    FailedWithMessage(ErrorMessage));
+  checkFileData(Path, "Original");
+}
+
+TEST(raw_ostreamTest, writeToOutputCallbackErrorPreservesDestination) {
+  SmallString<64> Path;
+  int FD;
+  ASSERT_FALSE(sys::fs::createTemporaryFile("foo", "bar", FD, Path));
+  FileRemover Cleanup(Path);
+  {
+    raw_fd_ostream Out(FD, true);
+    Out << "Original";
+  }
+
+  EXPECT_THAT_ERROR(writeToOutput(Path,
+                                  [](raw_ostream &Out) -> Error {
+                                    Out << "Replacement";
+                                    return createStringError(
+                                        errc::invalid_argument,
+                                        "callback failed");
+                                  }),
+                    FailedWithMessage("callback failed"));
+  checkFileData(Path, "Original");
+}
+
 #ifdef __MVS__
 TEST(raw_ostreamTest, writeToOutputFileEncoding) {
   // Create the temp file with that has ISO8859-1 encoding



More information about the llvm-commits mailing list