[llvm] [ObjectYAML][NFC] Finalize COFF symbol table offset in writeCOFF (PR #212671)

Haohai Wen via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 13 04:34:37 PDT 2026


https://github.com/HaohaiWen updated https://github.com/llvm/llvm-project/pull/212671

>From af31ad85dbc955ef732e555108825717d91fdf12 Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Tue, 28 Jul 2026 16:47:47 +0800
Subject: [PATCH 1/4] [ObjectYAML][NFC] Finalize COFF header fields in
 writeCOFF

Move NumberOfSections and NumberOfSymbols initialization out of
layoutCOFF. Derive PointerToSymbolTable from the live output offset and
write its final value back to the emitted header.

Add an endian-aware ContiguousBlobAccumulator::updateDataAt overload.

This is a preparatory step toward removing the separate COFF layout
pass.
---
 .../ObjectYAML/ContiguousBlobAccumulator.h    |  7 ++
 llvm/lib/ObjectYAML/COFFEmitter.cpp           | 73 ++++++++++---------
 2 files changed, 47 insertions(+), 33 deletions(-)

diff --git a/llvm/include/llvm/ObjectYAML/ContiguousBlobAccumulator.h b/llvm/include/llvm/ObjectYAML/ContiguousBlobAccumulator.h
index 9730d40a35a62..62c2a243a61ee 100644
--- a/llvm/include/llvm/ObjectYAML/ContiguousBlobAccumulator.h
+++ b/llvm/include/llvm/ObjectYAML/ContiguousBlobAccumulator.h
@@ -89,6 +89,13 @@ class ContiguousBlobAccumulator {
       support::endian::write<T>(OS, Val, E);
   }
 
+  template <typename T>
+  void updateDataAt(uint64_t Pos, T Val, llvm::endianness E) {
+    char Data[sizeof(T)];
+    support::endian::write<T>(Data, Val, E);
+    updateDataAt(Pos, Data, sizeof(Data));
+  }
+
   LLVM_ABI void updateDataAt(uint64_t Pos, const void *Data, size_t Size);
 };
 
diff --git a/llvm/lib/ObjectYAML/COFFEmitter.cpp b/llvm/lib/ObjectYAML/COFFEmitter.cpp
index d2a4a3b1313d2..8e7406042fb87 100644
--- a/llvm/lib/ObjectYAML/COFFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/COFFEmitter.cpp
@@ -264,39 +264,6 @@ static bool layoutCOFF(COFFParser &CP) {
     }
   }
 
-  uint32_t SymbolTableStart = CurrentSectionDataOffset;
-
-  // Calculate number of symbols.
-  uint32_t NumberOfSymbols = 0;
-  for (std::vector<COFFYAML::Symbol>::iterator i = CP.Obj.Symbols.begin(),
-                                               e = CP.Obj.Symbols.end();
-       i != e; ++i) {
-    uint32_t NumberOfAuxSymbols = 0;
-    if (i->FunctionDefinition)
-      NumberOfAuxSymbols += 1;
-    if (i->bfAndefSymbol)
-      NumberOfAuxSymbols += 1;
-    if (i->WeakExternal)
-      NumberOfAuxSymbols += 1;
-    if (!i->File.empty())
-      NumberOfAuxSymbols +=
-          (i->File.size() + CP.getSymbolSize() - 1) / CP.getSymbolSize();
-    if (i->SectionDefinition)
-      NumberOfAuxSymbols += 1;
-    if (i->CLRToken)
-      NumberOfAuxSymbols += 1;
-    i->Header.NumberOfAuxSymbols = NumberOfAuxSymbols;
-    NumberOfSymbols += 1 + NumberOfAuxSymbols;
-  }
-
-  // Store all the allocated start addresses in the header.
-  CP.Obj.Header.NumberOfSections = CP.Obj.Sections.size();
-  CP.Obj.Header.NumberOfSymbols = NumberOfSymbols;
-  if (NumberOfSymbols > 0 || CP.StringTable.size() > 4)
-    CP.Obj.Header.PointerToSymbolTable = SymbolTableStart;
-  else
-    CP.Obj.Header.PointerToSymbolTable = 0;
-
   *reinterpret_cast<support::ulittle32_t *>(CP.StringTable.data()) =
       CP.StringTable.size();
 
@@ -359,6 +326,34 @@ static uint32_t initializeOptionalHeader(COFFParser &CP, uint16_t Magic,
 }
 
 static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
+  // Calculate number of symbols.
+  CP.Obj.Header.NumberOfSymbols = 0;
+  for (std::vector<COFFYAML::Symbol>::iterator i = CP.Obj.Symbols.begin(),
+                                               e = CP.Obj.Symbols.end();
+       i != e; ++i) {
+    uint32_t NumberOfAuxSymbols = 0;
+    if (i->FunctionDefinition)
+      NumberOfAuxSymbols += 1;
+    if (i->bfAndefSymbol)
+      NumberOfAuxSymbols += 1;
+    if (i->WeakExternal)
+      NumberOfAuxSymbols += 1;
+    if (!i->File.empty())
+      NumberOfAuxSymbols +=
+          (i->File.size() + CP.getSymbolSize() - 1) / CP.getSymbolSize();
+    if (i->SectionDefinition)
+      NumberOfAuxSymbols += 1;
+    if (i->CLRToken)
+      NumberOfAuxSymbols += 1;
+    i->Header.NumberOfAuxSymbols = NumberOfAuxSymbols;
+    CP.Obj.Header.NumberOfSymbols += 1 + NumberOfAuxSymbols;
+  }
+
+  CP.Obj.Header.NumberOfSections = CP.Obj.Sections.size();
+
+  // Save field offsets for writing back their final values.
+  uint64_t PointerToSymbolTableOffset = 0;
+
   if (CP.isPE()) {
     // PE files start with a DOS stub.
     object::dos_header DH;
@@ -393,6 +388,8 @@ static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
     CBA.write(COFF::BigObjMagic, sizeof(COFF::BigObjMagic));
     CBA.writeZeros(4 * sizeof(uint32_t));
     CBA.write(CP.Obj.Header.NumberOfSections, LittleEndian);
+    PointerToSymbolTableOffset = CBA.getOffset();
+    // Write the initial value. The final value is written back later.
     CBA.write(CP.Obj.Header.PointerToSymbolTable, LittleEndian);
     CBA.write(CP.Obj.Header.NumberOfSymbols, LittleEndian);
   } else {
@@ -400,6 +397,8 @@ static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
     CBA.write(static_cast<int16_t>(CP.Obj.Header.NumberOfSections),
               LittleEndian);
     CBA.write(CP.Obj.Header.TimeDateStamp, LittleEndian);
+    PointerToSymbolTableOffset = CBA.getOffset();
+    // Write the initial value. The final value is written back later.
     CBA.write(CP.Obj.Header.PointerToSymbolTable, LittleEndian);
     CBA.write(CP.Obj.Header.NumberOfSymbols, LittleEndian);
     CBA.write(CP.Obj.Header.SizeOfOptionalHeader, LittleEndian);
@@ -490,6 +489,14 @@ static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
   }
 
   // Output symbol table.
+  if (CP.Obj.Header.NumberOfSymbols || CP.StringTable.size() > 4)
+    CP.Obj.Header.PointerToSymbolTable = CBA.getOffset();
+  else
+    CP.Obj.Header.PointerToSymbolTable = 0;
+
+  CBA.updateDataAt(PointerToSymbolTableOffset,
+                   CP.Obj.Header.PointerToSymbolTable, LittleEndian);
+
   for (std::vector<COFFYAML::Symbol>::const_iterator i = CP.Obj.Symbols.begin(),
                                                      e = CP.Obj.Symbols.end();
        i != e; ++i) {

>From 2c72511274f4eb7bd2573ef4f53992681e551eff Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Wed, 29 Jul 2026 14:03:09 +0800
Subject: [PATCH 2/4] [ObjectYAML] Handle updates to truncated output ranges

Allow updateDataAt to update ranges written before reaching the output size
limit, while ignoring ranges omitted by truncation. Preserve the assertion for
invalid ranges when no limit error occurred.
---
 llvm/lib/ObjectYAML/ContiguousBlobAccumulator.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/ObjectYAML/ContiguousBlobAccumulator.cpp b/llvm/lib/ObjectYAML/ContiguousBlobAccumulator.cpp
index a2438d3da0bb4..b237d784b41c8 100644
--- a/llvm/lib/ObjectYAML/ContiguousBlobAccumulator.cpp
+++ b/llvm/lib/ObjectYAML/ContiguousBlobAccumulator.cpp
@@ -66,6 +66,13 @@ unsigned ContiguousBlobAccumulator::writeSLEB128(int64_t Val) {
 
 void ContiguousBlobAccumulator::updateDataAt(uint64_t Pos, const void *Data,
                                              size_t Size) {
-  assert(Pos >= InitialOffset && Pos + Size <= getOffset());
+  uint64_t CurrentOffset = getOffset();
+  bool IsValidRange = Pos >= InitialOffset && Pos <= CurrentOffset &&
+                      Size <= CurrentOffset - Pos;
+  if (!IsValidRange) {
+    assert(ReachedLimitErr &&
+           "update range is invalid without reaching the output size limit");
+    return;
+  }
   memcpy(&Buf[Pos - InitialOffset], Data, Size);
 }

>From 64875f0790ab33b216e5988d6132bc8b37fead5c Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Fri, 7 Aug 2026 21:13:38 +0800
Subject: [PATCH 3/4] [ObjectYAML] Add ContiguousBlobAccumulator unit tests

Add coverage for normal writes and updates, including invalid ranges
before and after reaching the output size limit.

Use zero-filled placeholders for COFF header fields finalized later and
simplify symbol iteration.
---
 llvm/lib/ObjectYAML/COFFEmitter.cpp           | 28 +++----
 llvm/unittests/ObjectYAML/CMakeLists.txt      |  1 +
 .../ContiguousBlobAccumulatorTest.cpp         | 84 +++++++++++++++++++
 3 files changed, 97 insertions(+), 16 deletions(-)
 create mode 100644 llvm/unittests/ObjectYAML/ContiguousBlobAccumulatorTest.cpp

diff --git a/llvm/lib/ObjectYAML/COFFEmitter.cpp b/llvm/lib/ObjectYAML/COFFEmitter.cpp
index 8e7406042fb87..93b3140a4f39c 100644
--- a/llvm/lib/ObjectYAML/COFFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/COFFEmitter.cpp
@@ -328,31 +328,29 @@ static uint32_t initializeOptionalHeader(COFFParser &CP, uint16_t Magic,
 static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
   // Calculate number of symbols.
   CP.Obj.Header.NumberOfSymbols = 0;
-  for (std::vector<COFFYAML::Symbol>::iterator i = CP.Obj.Symbols.begin(),
-                                               e = CP.Obj.Symbols.end();
-       i != e; ++i) {
+  for (COFFYAML::Symbol &Sym : CP.Obj.Symbols) {
     uint32_t NumberOfAuxSymbols = 0;
-    if (i->FunctionDefinition)
+    if (Sym.FunctionDefinition)
       NumberOfAuxSymbols += 1;
-    if (i->bfAndefSymbol)
+    if (Sym.bfAndefSymbol)
       NumberOfAuxSymbols += 1;
-    if (i->WeakExternal)
+    if (Sym.WeakExternal)
       NumberOfAuxSymbols += 1;
-    if (!i->File.empty())
+    if (!Sym.File.empty())
       NumberOfAuxSymbols +=
-          (i->File.size() + CP.getSymbolSize() - 1) / CP.getSymbolSize();
-    if (i->SectionDefinition)
+          (Sym.File.size() + CP.getSymbolSize() - 1) / CP.getSymbolSize();
+    if (Sym.SectionDefinition)
       NumberOfAuxSymbols += 1;
-    if (i->CLRToken)
+    if (Sym.CLRToken)
       NumberOfAuxSymbols += 1;
-    i->Header.NumberOfAuxSymbols = NumberOfAuxSymbols;
+    Sym.Header.NumberOfAuxSymbols = NumberOfAuxSymbols;
     CP.Obj.Header.NumberOfSymbols += 1 + NumberOfAuxSymbols;
   }
 
   CP.Obj.Header.NumberOfSections = CP.Obj.Sections.size();
 
   // Save field offsets for writing back their final values.
-  uint64_t PointerToSymbolTableOffset = 0;
+  uint64_t PointerToSymbolTableOffset;
 
   if (CP.isPE()) {
     // PE files start with a DOS stub.
@@ -389,8 +387,7 @@ static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
     CBA.writeZeros(4 * sizeof(uint32_t));
     CBA.write(CP.Obj.Header.NumberOfSections, LittleEndian);
     PointerToSymbolTableOffset = CBA.getOffset();
-    // Write the initial value. The final value is written back later.
-    CBA.write(CP.Obj.Header.PointerToSymbolTable, LittleEndian);
+    CBA.writeZeros(sizeof(CP.Obj.Header.PointerToSymbolTable));
     CBA.write(CP.Obj.Header.NumberOfSymbols, LittleEndian);
   } else {
     CBA.write(CP.Obj.Header.Machine, LittleEndian);
@@ -398,8 +395,7 @@ static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
               LittleEndian);
     CBA.write(CP.Obj.Header.TimeDateStamp, LittleEndian);
     PointerToSymbolTableOffset = CBA.getOffset();
-    // Write the initial value. The final value is written back later.
-    CBA.write(CP.Obj.Header.PointerToSymbolTable, LittleEndian);
+    CBA.writeZeros(sizeof(CP.Obj.Header.PointerToSymbolTable));
     CBA.write(CP.Obj.Header.NumberOfSymbols, LittleEndian);
     CBA.write(CP.Obj.Header.SizeOfOptionalHeader, LittleEndian);
     CBA.write(CP.Obj.Header.Characteristics, LittleEndian);
diff --git a/llvm/unittests/ObjectYAML/CMakeLists.txt b/llvm/unittests/ObjectYAML/CMakeLists.txt
index 17772ce494710..56958702ceb49 100644
--- a/llvm/unittests/ObjectYAML/CMakeLists.txt
+++ b/llvm/unittests/ObjectYAML/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_llvm_unittest(ObjectYAMLTests
+  ContiguousBlobAccumulatorTest.cpp
   DWARFYAMLTest.cpp
   DXContainerYAMLTest.cpp
   ELFYAMLTest.cpp
diff --git a/llvm/unittests/ObjectYAML/ContiguousBlobAccumulatorTest.cpp b/llvm/unittests/ObjectYAML/ContiguousBlobAccumulatorTest.cpp
new file mode 100644
index 0000000000000..37ae684105994
--- /dev/null
+++ b/llvm/unittests/ObjectYAML/ContiguousBlobAccumulatorTest.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains unit tests for ContiguousBlobAccumulator.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ObjectYAML/ContiguousBlobAccumulator.h"
+#include "llvm/ObjectYAML/YAML.h"
+#include "llvm/Testing/Support/Error.h"
+
+using namespace llvm;
+using namespace llvm::yaml;
+
+static std::string getData(const ContiguousBlobAccumulator &CBA) {
+  SmallString<16> Data;
+  raw_svector_ostream OS(Data);
+  CBA.writeBlobToStream(OS);
+  return std::string(OS.str());
+}
+
+TEST(ContiguousBlobAccumulatorTest, Normal) {
+  ContiguousBlobAccumulator CBA(/*BaseOffset=*/4, /*SizeLimit=*/64);
+  EXPECT_EQ(CBA.tell(), 0u);
+  EXPECT_EQ(CBA.getOffset(), 4u);
+
+  raw_ostream *OS = CBA.getRawOS(1);
+  ASSERT_NE(OS, nullptr);
+  *OS << 'a';
+  CBA.write("bc", 2);
+  CBA.write(static_cast<unsigned char>('d'));
+  CBA.writeZeros(2);
+  CBA.write("12", 2);
+  CBA.write(static_cast<uint16_t>(0x1234), llvm::endianness::big);
+  BinaryRef Bin("333435");
+  CBA.writeAsBinary(Bin, 2);
+
+  EXPECT_EQ(CBA.padToAlignment(8), 16u);
+  EXPECT_EQ(CBA.writeULEB128(0x7f), 1u);
+  EXPECT_EQ(CBA.writeSLEB128(-1), 1u);
+
+  const char ExpectedBeforeUpdate[] = {'a', 'b',  'c',  'd',  '\0', '\0', '1',
+                                       '2', 0x12, 0x34, 0x33, 0x34, 0x7f, 0x7f};
+  EXPECT_EQ(getData(CBA),
+            std::string(ExpectedBeforeUpdate, sizeof(ExpectedBeforeUpdate)));
+
+  CBA.updateDataAt(/*Pos=*/4, "ABCD", 4);
+  CBA.updateDataAt(/*Pos=*/10, static_cast<uint16_t>(0x3536),
+                   llvm::endianness::big);
+  CBA.updateDataAt(/*Pos=*/12, static_cast<uint16_t>(0x3837),
+                   llvm::endianness::little);
+
+  const char ExpectedAfterUpdate[] = {'A',  'B',  'C',  'D',  '\0', '\0', 0x35,
+                                      0x36, 0x37, 0x38, 0x33, 0x34, 0x7f, 0x7f};
+  EXPECT_EQ(CBA.tell(), sizeof(ExpectedAfterUpdate));
+  EXPECT_EQ(CBA.getOffset(), 4 + sizeof(ExpectedAfterUpdate));
+  EXPECT_EQ(getData(CBA),
+            std::string(ExpectedAfterUpdate, sizeof(ExpectedAfterUpdate)));
+  EXPECT_THAT_ERROR(CBA.takeLimitError(), Succeeded());
+}
+
+TEST(ContiguousBlobAccumulatorTest, Invalid) {
+  ContiguousBlobAccumulator CBA(/*BaseOffset=*/0, /*SizeLimit=*/4);
+  CBA.write("abcd", 4);
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+  EXPECT_DEATH(CBA.updateDataAt(/*Pos=*/4, "Z", 1),
+               "update range is invalid without reaching the output size "
+               "limit");
+#endif
+
+  CBA.write("e", 1);
+  CBA.updateDataAt(/*Pos=*/1, "XY", 2);
+  CBA.updateDataAt(/*Pos=*/4, "Z", 1);
+  EXPECT_EQ(getData(CBA), "aXYd");
+  EXPECT_THAT_ERROR(CBA.takeLimitError(),
+                    FailedWithMessage("reached the output size limit"));
+}

>From c705ee92f9b8e3c7e3572d1c1a59204e7fe35493 Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Thu, 13 Aug 2026 19:33:35 +0800
Subject: [PATCH 4/4] Address comments

---
 llvm/lib/ObjectYAML/COFFEmitter.cpp           |  2 +
 .../ContiguousBlobAccumulatorTest.cpp         | 60 +++++++++----------
 2 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/llvm/lib/ObjectYAML/COFFEmitter.cpp b/llvm/lib/ObjectYAML/COFFEmitter.cpp
index 93b3140a4f39c..e01e3653ed930 100644
--- a/llvm/lib/ObjectYAML/COFFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/COFFEmitter.cpp
@@ -387,6 +387,7 @@ static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
     CBA.writeZeros(4 * sizeof(uint32_t));
     CBA.write(CP.Obj.Header.NumberOfSections, LittleEndian);
     PointerToSymbolTableOffset = CBA.getOffset();
+    // The final symbol table offset is written after the section data.
     CBA.writeZeros(sizeof(CP.Obj.Header.PointerToSymbolTable));
     CBA.write(CP.Obj.Header.NumberOfSymbols, LittleEndian);
   } else {
@@ -395,6 +396,7 @@ static bool writeCOFF(COFFParser &CP, ContiguousBlobAccumulator &CBA) {
               LittleEndian);
     CBA.write(CP.Obj.Header.TimeDateStamp, LittleEndian);
     PointerToSymbolTableOffset = CBA.getOffset();
+    // The final symbol table offset is written after the section data.
     CBA.writeZeros(sizeof(CP.Obj.Header.PointerToSymbolTable));
     CBA.write(CP.Obj.Header.NumberOfSymbols, LittleEndian);
     CBA.write(CP.Obj.Header.SizeOfOptionalHeader, LittleEndian);
diff --git a/llvm/unittests/ObjectYAML/ContiguousBlobAccumulatorTest.cpp b/llvm/unittests/ObjectYAML/ContiguousBlobAccumulatorTest.cpp
index 37ae684105994..46602f7ece050 100644
--- a/llvm/unittests/ObjectYAML/ContiguousBlobAccumulatorTest.cpp
+++ b/llvm/unittests/ObjectYAML/ContiguousBlobAccumulatorTest.cpp
@@ -26,58 +26,56 @@ static std::string getData(const ContiguousBlobAccumulator &CBA) {
 }
 
 TEST(ContiguousBlobAccumulatorTest, Normal) {
-  ContiguousBlobAccumulator CBA(/*BaseOffset=*/4, /*SizeLimit=*/64);
+  constexpr uint64_t BaseOffset = 4;
+  ContiguousBlobAccumulator CBA(BaseOffset, /*SizeLimit=*/64);
   EXPECT_EQ(CBA.tell(), 0u);
-  EXPECT_EQ(CBA.getOffset(), 4u);
+  EXPECT_EQ(CBA.getOffset(), BaseOffset);
 
   raw_ostream *OS = CBA.getRawOS(1);
   ASSERT_NE(OS, nullptr);
   *OS << 'a';
   CBA.write("bc", 2);
   CBA.write(static_cast<unsigned char>('d'));
-  CBA.writeZeros(2);
-  CBA.write("12", 2);
+  CBA.writeZeros(1);
   CBA.write(static_cast<uint16_t>(0x1234), llvm::endianness::big);
+  CBA.write(static_cast<uint16_t>(0x5678), llvm::endianness::little);
   BinaryRef Bin("333435");
   CBA.writeAsBinary(Bin, 2);
 
+  EXPECT_EQ(CBA.tell(), 11u);
   EXPECT_EQ(CBA.padToAlignment(8), 16u);
+  EXPECT_EQ(CBA.tell(), 12u);
   EXPECT_EQ(CBA.writeULEB128(0x7f), 1u);
-  EXPECT_EQ(CBA.writeSLEB128(-1), 1u);
+  EXPECT_EQ(CBA.writeSLEB128(-2), 1u);
 
-  const char ExpectedBeforeUpdate[] = {'a', 'b',  'c',  'd',  '\0', '\0', '1',
-                                       '2', 0x12, 0x34, 0x33, 0x34, 0x7f, 0x7f};
-  EXPECT_EQ(getData(CBA),
-            std::string(ExpectedBeforeUpdate, sizeof(ExpectedBeforeUpdate)));
+  const std::string ExpectedBeforeUpdate = {'a',  'b',  'c',  'd',  '\0',
+                                            0x12, 0x34, 0x78, 0x56, 0x33,
+                                            0x34, '\0', 0x7f, 0x7e};
+  EXPECT_EQ(getData(CBA), ExpectedBeforeUpdate);
 
-  CBA.updateDataAt(/*Pos=*/4, "ABCD", 4);
-  CBA.updateDataAt(/*Pos=*/10, static_cast<uint16_t>(0x3536),
-                   llvm::endianness::big);
-  CBA.updateDataAt(/*Pos=*/12, static_cast<uint16_t>(0x3837),
+  CBA.updateDataAt(/*Pos=*/BaseOffset, "ABCD", 4);
+  CBA.updateDataAt(/*Pos=*/BaseOffset + 5, static_cast<uint16_t>(0x3536),
                    llvm::endianness::little);
+  CBA.updateDataAt(/*Pos=*/BaseOffset + 7, static_cast<uint16_t>(0x3738),
+                   llvm::endianness::big);
 
-  const char ExpectedAfterUpdate[] = {'A',  'B',  'C',  'D',  '\0', '\0', 0x35,
-                                      0x36, 0x37, 0x38, 0x33, 0x34, 0x7f, 0x7f};
-  EXPECT_EQ(CBA.tell(), sizeof(ExpectedAfterUpdate));
-  EXPECT_EQ(CBA.getOffset(), 4 + sizeof(ExpectedAfterUpdate));
-  EXPECT_EQ(getData(CBA),
-            std::string(ExpectedAfterUpdate, sizeof(ExpectedAfterUpdate)));
+  const std::string ExpectedAfterUpdate = {'A',  'B',  'C',  'D',  '\0',
+                                           0x36, 0x35, 0x37, 0x38, 0x33,
+                                           0x34, '\0', 0x7f, 0x7e};
+  EXPECT_EQ(CBA.getOffset(), BaseOffset + ExpectedAfterUpdate.size());
+  EXPECT_EQ(getData(CBA), ExpectedAfterUpdate);
   EXPECT_THAT_ERROR(CBA.takeLimitError(), Succeeded());
 }
 
-TEST(ContiguousBlobAccumulatorTest, Invalid) {
-  ContiguousBlobAccumulator CBA(/*BaseOffset=*/0, /*SizeLimit=*/4);
-  CBA.write("abcd", 4);
-
-#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
-  EXPECT_DEATH(CBA.updateDataAt(/*Pos=*/4, "Z", 1),
-               "update range is invalid without reaching the output size "
-               "limit");
-#endif
-
+TEST(ContiguousBlobAccumulatorTest, ReachedLimit) {
+  constexpr uint64_t BaseOffset = 0, SizeLimit = 4;
+  ContiguousBlobAccumulator CBA(BaseOffset, SizeLimit);
+  CBA.write("abcd", SizeLimit);
+  EXPECT_EQ(CBA.getOffset(), BaseOffset + SizeLimit);
+  // Trigger the size limit error so the out-of-bounds update below is ignored.
   CBA.write("e", 1);
-  CBA.updateDataAt(/*Pos=*/1, "XY", 2);
-  CBA.updateDataAt(/*Pos=*/4, "Z", 1);
+  CBA.updateDataAt(/*Pos=*/BaseOffset + 1, "XY", 2);
+  CBA.updateDataAt(/*Pos=*/BaseOffset + SizeLimit, "Z", 1);
   EXPECT_EQ(getData(CBA), "aXYd");
   EXPECT_THAT_ERROR(CBA.takeLimitError(),
                     FailedWithMessage("reached the output size limit"));



More information about the llvm-commits mailing list