[llvm] [llvm-readobj][COFF] Dump .modmeta section (PR #201695)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 06:31:01 PDT 2026


https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/201695

>From 4c2c3072e47abb8e3d13d72ccdb2c38e4fb49aae Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Thu, 4 Jun 2026 18:31:18 +0200
Subject: [PATCH 1/5] [llvm-readobj][COFF] Dump .modmeta section

---
 .../llvm/Object/COFFCxxModuleMetadata.h       | 117 ++++++++
 llvm/lib/Object/CMakeLists.txt                |   1 +
 llvm/lib/Object/COFFCxxModuleMetadata.cpp     | 131 +++++++++
 .../COFF/cxx-modmeta-headerunits.yaml         | 253 ++++++++++++++++++
 .../COFF/cxx-modmeta-many-modules.yaml        | 150 +++++++++++
 .../COFF/cxx-modmeta-private-syms.yaml        | 220 +++++++++++++++
 llvm/tools/llvm-readobj/COFFDumper.cpp        |  74 +++++
 llvm/tools/llvm-readobj/ObjDumper.h           |   1 +
 llvm/tools/llvm-readobj/Opts.td               |   1 +
 llvm/tools/llvm-readobj/llvm-readobj.cpp      |   4 +
 10 files changed, 952 insertions(+)
 create mode 100644 llvm/include/llvm/Object/COFFCxxModuleMetadata.h
 create mode 100644 llvm/lib/Object/COFFCxxModuleMetadata.cpp
 create mode 100644 llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-headerunits.yaml
 create mode 100644 llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-many-modules.yaml
 create mode 100644 llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-private-syms.yaml

diff --git a/llvm/include/llvm/Object/COFFCxxModuleMetadata.h b/llvm/include/llvm/Object/COFFCxxModuleMetadata.h
new file mode 100644
index 0000000000000..3cabb415d3bc5
--- /dev/null
+++ b/llvm/include/llvm/Object/COFFCxxModuleMetadata.h
@@ -0,0 +1,117 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Windows-specific.
+// Definitions and a parser for the C++ 20 ".modmeta" section.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJECT_COFFMODULEMAP_H
+#define LLVM_OBJECT_COFFMODULEMAP_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm::object {
+
+struct COFFCxxModuleMetadataHeader {
+  uint8_t Version;
+  uint8_t Reserved;
+  /// Number of bytes used to encode module IDs.
+  uint8_t ModuleIndexWidth;
+  /// Number of bytes used to encode symbol IDs.
+  uint8_t SymbolIndexWidth;
+  /// Size of this header and the module lists.
+  support::ulittle32_t ModuleDataSize;
+};
+
+struct COFFCxxModuleMetadata {
+  uint8_t Version;
+  uint8_t Reserved;
+  /// Number of bytes used to encode module IDs.
+  uint8_t ModuleIndexWidth;
+  /// Number of bytes used to encode symbol IDs.
+  uint8_t SymbolIndexWidth;
+
+  /// Data for modules.
+  ///
+  /// Starts with a list of module IDs that are header units.
+  /// This is followed by a list of modules, which is terminated by the maximum
+  /// module ID (e.g. 0xff for Width=1). Each module starts with the ID followed
+  /// by a list of modules it depends on, a list of non-exported symbols, and a
+  /// list of exported symbols.
+  StringRef ModuleData;
+
+  /// List of null-terminated module names.
+  ///
+  /// The names are present in the order of the modules in \p ModuleData. Note
+  /// that the module with ID 0 does not have a name.
+  StringRef NamesData;
+};
+
+struct LLVM_ABI COFFCxxModuleMetadataReader {
+  COFFCxxModuleMetadataReader(const COFFCxxModuleMetadata &Map);
+
+  StringRef ModuleData;
+  StringRef NamesData;
+
+  uint8_t ModuleIndexWidth;
+  uint8_t SymbolIndexWidth;
+
+  bool hasModuleData() const;
+
+  Expected<uint32_t> readModuleID();
+  Expected<StringRef> readModuleName();
+
+  /// Read a list of modules.
+  ///
+  /// \param Visitor A callback that accepts an `ArrayRef` of `uint8_t`,
+  /// `ulittle16_t`, or `ulittle32_t` depending on the index width.
+  template <typename T> Error readModuleList(T &&Visitor) {
+    return readList(std::forward<T>(Visitor), ModuleIndexWidth);
+  }
+
+  /// Read a list of symbols.
+  ///
+  /// \param Visitor A callback that accepts an `ArrayRef` of `uint8_t`,
+  /// `ulittle16_t`, or `ulittle32_t` depending on the index width.
+  template <typename T> Error readSymbolList(T &&Visitor) {
+    return readList(std::forward<T>(Visitor), SymbolIndexWidth);
+  }
+
+private:
+  template <typename T> Error readList(T &&Visitor, uint8_t Width) {
+    Expected<ArrayRef<uint8_t>> List = readListImpl(Width);
+    if (!List)
+      return List.takeError();
+
+    if (Width == 1)
+      Visitor(*List);
+    else if (Width == 2)
+      Visitor(ArrayRef<support::ulittle16_t>(
+          reinterpret_cast<const support::ulittle16_t *>(List->data()),
+          List->size() / sizeof(support::ulittle16_t)));
+    else
+      Visitor(ArrayRef<support::ulittle32_t>(
+          reinterpret_cast<const support::ulittle32_t *>(List->data()),
+          List->size() / sizeof(support::ulittle32_t)));
+
+    return Error::success();
+  }
+
+  Expected<ArrayRef<uint8_t>> readListImpl(uint8_t Width);
+};
+
+LLVM_ABI Expected<COFFCxxModuleMetadata>
+parseCOFFCxxModuleMetadata(StringRef SectionData);
+
+} // namespace llvm::object
+
+#endif
diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt
index 77a50f0d631d3..3635b4d6b8b6d 100644
--- a/llvm/lib/Object/CMakeLists.txt
+++ b/llvm/lib/Object/CMakeLists.txt
@@ -4,6 +4,7 @@ add_llvm_component_library(LLVMObject
   BBAddrMap.cpp
   Binary.cpp
   BuildID.cpp
+  COFFCxxModuleMetadata.cpp
   COFFImportFile.cpp
   COFFModuleDefinition.cpp
   COFFObjectFile.cpp
diff --git a/llvm/lib/Object/COFFCxxModuleMetadata.cpp b/llvm/lib/Object/COFFCxxModuleMetadata.cpp
new file mode 100644
index 0000000000000..0cffd8f836e37
--- /dev/null
+++ b/llvm/lib/Object/COFFCxxModuleMetadata.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/COFFCxxModuleMetadata.h"
+#include "llvm/Support/ErrorExtras.h"
+
+namespace llvm::object {
+
+COFFCxxModuleMetadataReader::COFFCxxModuleMetadataReader(
+    const COFFCxxModuleMetadata &Map)
+    : ModuleData(Map.ModuleData), NamesData(Map.NamesData),
+      ModuleIndexWidth(Map.ModuleIndexWidth),
+      SymbolIndexWidth(Map.SymbolIndexWidth) {}
+
+bool COFFCxxModuleMetadataReader::hasModuleData() const {
+  return !ModuleData.empty();
+}
+
+Expected<uint32_t> COFFCxxModuleMetadataReader::readModuleID() {
+  switch (ModuleIndexWidth) {
+  case 1: {
+    if (ModuleData.size() < 1)
+      return createStringError("Not enough data");
+    uint8_t ID = static_cast<uint8_t>(ModuleData[0]);
+    ModuleData = ModuleData.slice(1, StringRef::npos);
+    if (ID == std::numeric_limits<uint8_t>::max())
+      return std::numeric_limits<uint32_t>::max();
+    return ID;
+  }
+  case 2: {
+    if (ModuleData.size() < 2)
+      return createStringError("Not enough data");
+    uint16_t ID =
+        support::endian::read<uint16_t>(ModuleData.data(), endianness::little);
+    ModuleData = ModuleData.slice(2, StringRef::npos);
+    if (ID == std::numeric_limits<uint16_t>::max())
+      return std::numeric_limits<uint32_t>::max();
+    return ID;
+  }
+  case 4: {
+    if (ModuleData.size() < 4)
+      return createStringError("Not enough data");
+    uint32_t ID =
+        support::endian::read<uint32_t>(ModuleData.data(), endianness::little);
+    ModuleData = ModuleData.slice(4, StringRef::npos);
+    return ID;
+  }
+  default:
+    return createStringErrorV("Unsupported index width: {0}", ModuleIndexWidth);
+  }
+}
+
+Expected<StringRef> COFFCxxModuleMetadataReader::readModuleName() {
+  size_t End = NamesData.find('\0');
+  if (End == StringRef::npos)
+    return createStringError("Missing null terminator");
+  StringRef Str = NamesData.slice(0, End);
+  NamesData = NamesData.drop_front(End + 1);
+  return Str;
+}
+
+Expected<ArrayRef<uint8_t>>
+COFFCxxModuleMetadataReader::readListImpl(uint8_t Width) {
+  StringRef Sentinel;
+  switch (Width) {
+  case 1:
+    Sentinel = "\xff";
+    break;
+  case 2:
+    Sentinel = "\xff\xff";
+    break;
+  case 4:
+    Sentinel = "\xff\xff\xff\xff";
+    break;
+  default:
+    return createStringErrorV("Unsupported index width: {0}", Width);
+  }
+  size_t Last = ModuleData.find(Sentinel);
+  if (Last == StringRef::npos)
+    return createStringError("Missing end sentinel");
+
+  ArrayRef<uint8_t> Data(ModuleData.bytes_begin(),
+                         ModuleData.bytes_begin() + Last);
+  ModuleData = ModuleData.drop_front(Last + Width);
+  return Data;
+}
+
+Expected<COFFCxxModuleMetadata>
+parseCOFFCxxModuleMetadata(StringRef SectionData) {
+  if (SectionData.size() <= sizeof(COFFCxxModuleMetadataHeader))
+    return createStringError("Insufficient size");
+
+  const auto *Header =
+      reinterpret_cast<const COFFCxxModuleMetadataHeader *>(SectionData.data());
+  if (Header->Version != 1)
+    return createStringError("Unsupported version");
+
+  auto IsSupportedIndexWidth = [](uint8_t Width) {
+    return Width == 1 || Width == 2 || Width == 4;
+  };
+
+  if (!IsSupportedIndexWidth(Header->ModuleIndexWidth))
+    return createStringErrorV("Unsupported module index width: {0}",
+                              Header->ModuleIndexWidth);
+  if (!IsSupportedIndexWidth(Header->SymbolIndexWidth))
+    return createStringErrorV("Unsupported symbol index width: {0}",
+                              Header->SymbolIndexWidth);
+
+  size_t ModuleDataSize = Header->ModuleDataSize.value();
+  if (ModuleDataSize <= sizeof(COFFCxxModuleMetadataHeader) ||
+      ModuleDataSize + 1 >= SectionData.size())
+    return createStringErrorV("Invalid module data size: {0}", ModuleDataSize);
+
+  COFFCxxModuleMetadata Map;
+  Map.Version = Header->Version;
+  Map.Reserved = Header->Reserved;
+  Map.ModuleIndexWidth = Header->ModuleIndexWidth;
+  Map.SymbolIndexWidth = Header->SymbolIndexWidth;
+  Map.ModuleData =
+      SectionData.slice(sizeof(COFFCxxModuleMetadataHeader), ModuleDataSize);
+  // Skip one reserved(?) byte.
+  Map.NamesData = SectionData.slice(ModuleDataSize + 1, StringRef::npos);
+  return Map;
+}
+
+} // namespace llvm::object
diff --git a/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-headerunits.yaml b/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-headerunits.yaml
new file mode 100644
index 0000000000000..4e2d4cec67dde
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-headerunits.yaml
@@ -0,0 +1,253 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-readobj --coff-cxx-module-metadata %t | FileCheck %s
+
+# CHECK:      CxxModuleMetadata {
+# CHECK-NEXT:   Version: 1
+# CHECK-NEXT:   Reserved: 0
+# CHECK-NEXT:   ModuleIndexWidth: 1
+# CHECK-NEXT:   SymbolIndexWidth: 1
+# CHECK-NEXT:   Modules [
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x0
+# CHECK-NEXT:       Name: 
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: [0x1, 0x3, 0x25]
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: []
+# CHECK-NEXT:     }
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x1
+# CHECK-NEXT:       Name: o
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: []
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: [?getO@@YAHXZ::<!o> (16)]
+# CHECK-NEXT:     }
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x3
+# CHECK-NEXT:       Name: {{.*}}/unit.hpp
+# CHECK-NEXT:       IsHeaderUnit: Yes
+# CHECK-NEXT:       Dependents: []
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: [?diff@@YAHHH at Z (14)]
+# CHECK-NEXT:     }
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x25
+# CHECK-NEXT:       Name: {{.*}}/unit2.hpp
+# CHECK-NEXT:       IsHeaderUnit: Yes
+# CHECK-NEXT:       Dependents: []
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: [?sum@@YAHHH at Z (15)]
+# CHECK-NEXT:     }
+# CHECK-NEXT:   ]
+# CHECK-NEXT: }
+
+# This was created with the following commands and inputs:
+
+# cl unit.hpp /exportHeader /Founit.hpp.obj /std:c++20 /c
+# cl unit2.hpp /exportHeader /Founit2.hpp.obj /std:c++20 /c
+# cl o.ixx /std:c++20 /c                                   
+# cl /headerUnit unit.hpp=unit.hpp.ifc /headerUnit unit2.hpp=unit2.hpp.ifc main.cpp /std:c++20 /c
+
+# unit.hpp:
+# inline int diff(int a, int b) { return a - b; }
+
+# unit2.hpp:
+# inline int sum(int a, int b) { return a + b; }
+
+# o.ixx:
+# export module o;
+# int foo = 42;
+# export int getO() { return foo; }
+
+# main.cpp:
+# import "unit.hpp";
+# import "unit2.hpp";
+# import o;
+# int main() {
+#   int d = diff(2, 3);
+#   return sum(1, d) + getO();
+# }
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            .modmeta
+    Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+    Alignment:       1
+    SectionData:     01000101220000000325FF00010325FFFFFF01FFFF10FF03FFFF0EFF25FFFF0FFFFF006F00463A2F4465762F64756D6D792F686561646572756E6974732F756E69742E68707000463A2F4465762F64756D6D792F686561646572756E6974732F756E6974322E68707000
+    SizeOfRawData:   106
+symbols:
+  - Name:            '@comp.id'
+    Value:           17140626
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '@feat.00'
+    Value:           2147549584
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '@vol.md'
+    Value:           3
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .drectve
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          47
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            '.debug$S'
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          116
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            '.text$mn'
+    Value:           0
+    SectionNumber:   3
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          59
+      NumberOfRelocations: 3
+      NumberOfLinenumbers: 0
+      CheckSum:        3944583797
+      Number:          0
+  - Name:            '.text$mn'
+    Value:           0
+    SectionNumber:   4
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          21
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        2168134607
+      Number:          0
+      Selection:       IMAGE_COMDAT_SELECT_ANY
+  - Name:            '.text$mn'
+    Value:           0
+    SectionNumber:   5
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          21
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        1888124682
+      Number:          0
+      Selection:       IMAGE_COMDAT_SELECT_ANY
+  - Name:            main
+    Value:           0
+    SectionNumber:   3
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '?diff@@YAHHH at Z'
+    Value:           0
+    SectionNumber:   4
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '?sum@@YAHHH at Z'
+    Value:           0
+    SectionNumber:   5
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '?getO@@YAHXZ::<!o>'
+    Value:           0
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '$LN3'
+    Value:           0
+    SectionNumber:   3
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_LABEL
+  - Name:            .xdata
+    Value:           0
+    SectionNumber:   6
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          8
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        931692337
+      Number:          0
+  - Name:            '$unwind$main'
+    Value:           0
+    SectionNumber:   6
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .pdata
+    Value:           0
+    SectionNumber:   7
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          12
+      NumberOfRelocations: 3
+      NumberOfLinenumbers: 0
+      CheckSum:        3634843435
+      Number:          0
+  - Name:            '$pdata$main'
+    Value:           0
+    SectionNumber:   7
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .modmeta
+    Value:           0
+    SectionNumber:   8
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          106
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            .chks64
+    Value:           0
+    SectionNumber:   9
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          72
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-many-modules.yaml b/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-many-modules.yaml
new file mode 100644
index 0000000000000..a968050c34c2e
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-many-modules.yaml
@@ -0,0 +1,150 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-readobj --coff-cxx-module-metadata %t | FileCheck %s
+
+# CHECK:      CxxModuleMetadata {
+# CHECK-NEXT:   Version: 1
+# CHECK-NEXT:   Reserved: 0
+# CHECK-NEXT:   ModuleIndexWidth: 2
+# CHECK-NEXT:   SymbolIndexWidth: 1
+# CHECK-NEXT:   Modules [
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x0
+# CHECK-NEXT:       Name: 
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: [0x1, 0x7, 0xE, 0x13, 0x1A, 0x21, {{.*}} 0x685, 0x68C]
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: []
+# CHECK-NEXT:     }
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x1
+# CHECK-NEXT:       Name: mod45
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: []
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: []
+# CHECK-NEXT:     }
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x7
+# CHECK-NEXT:       Name: mod249
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: []
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: []
+# CHECK-NEXT:     }
+
+# CHECK:            ID: 0x685
+# CHECK-NEXT:       Name: mod177
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: []
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: []
+# CHECK-NEXT:     }
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x68C
+# CHECK-NEXT:       Name: mod132
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: []
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: []
+# CHECK-NEXT:     }
+# CHECK-NEXT:   ]
+# CHECK-NEXT: }
+
+# This was created by compiling a module with 256 dependencies named `mod{i}`.
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            .modmeta
+    Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+    Alignment:       1
+    SectionData:     0100020112080000FFFF0000010007000E0013001A00210028002F0036003D0043004A00500056005C0063006A00710078007E0084008B0092009900A000A700AC00B200B800BE00C500CC00D300D900DF00E600ED00F400FB00010107010C0113011A01210128012F0136013C01430149014F0156015C0163016A01710177017D0184018B0192019901A001A501AA01B001B601BD01C401CB01D201D801DE01E501EC01F301FA01000206020B0212021902200227022E0235023B02420248024E0254025A02610268026F0275027C0283028A02910298029E02A302A902B002B602BD02C302CA02D102D802DE02E502EB02F202F902000307030D03130319031F0326032D0334033B03420349034F0356035C03620368036F0376037D03820388038F0396039D03A403AB03B203B903BF03C603CC03D203D803DF03E503EC03F303FA03000406040D0414041B04220428042E0434043B04410448044F0456045D0464046A04710477047D0483048904900497049C04A204A904B004B704BE04C504CC04D304D904E004E604EC04F304F904000507050E0514051A05200527052E0535053C05420548054E0555055C0563056A05710577057D0584058A05900597059E05A305AA05B105B805BF05C605CD05D405DA05E105E705ED05F305FA05010608060F0615061B0622062906300637063E0644064A06500656065D0664066B06710677067E0685068C06FFFFFFFF0100FFFFFFFF0700FFFFFFFF0E00FFFFFFFF1300FFFFFFFF1A00FFFFFFFF2100FFFFFFFF2800FFFFFFFF2F00FFFFFFFF3600FFFFFFFF3D00FFFFFFFF4300FFFFFFFF4A00FFFFFFFF5000FFFFFFFF5600FFFFFFFF5C00FFFFFFFF6300FFFFFFFF6A00FFFFFFFF7100FFFFFFFF7800FFFFFFFF7E00FFFFFFFF8400FFFFFFFF8B00FFFFFFFF9200FFFFFFFF9900FFFFFFFFA000FFFFFFFFA700FFFFFFFFAC00FFFFFFFFB200FFFFFFFFB800FFFFFFFFBE00FFFFFFFFC500FFFFFFFFCC00FFFFFFFFD300FFFFFFFFD900FFFFFFFFDF00FFFFFFFFE600FFFFFFFFED00FFFFFFFFF400FFFFFFFFFB00FFFFFFFF0101FFFFFFFF0701FFFFFFFF0C01FFFFFFFF1301FFFFFFFF1A01FFFFFFFF2101FFFFFFFF2801FFFFFFFF2F01FFFFFFFF3601FFFFFFFF3C01FFFFFFFF4301FFFFFFFF4901FFFFFFFF4F01FFFFFFFF5601FFFFFFFF5C01FFFFFFFF6301FFFFFFFF6A01FFFFFFFF7101FFFFFFFF7701FFFFFFFF7D01FFFFFFFF8401FFFFFFFF8B01FFFFFFFF9201FFFFFFFF9901FFFFFFFFA001FFFFFFFFA501FFFFFFFFAA01FFFFFFFFB001FFFFFFFFB601FFFFFFFFBD01FFFFFFFFC401FFFFFFFFCB01FFFFFFFFD201FFFFFFFFD801FFFFFFFFDE01FFFFFFFFE501FFFFFFFFEC01FFFFFFFFF301FFFFFFFFFA01FFFFFFFF0002FFFFFFFF0602FFFFFFFF0B02FFFFFFFF1202FFFFFFFF1902FFFFFFFF2002FFFFFFFF2702FFFFFFFF2E02FFFFFFFF3502FFFFFFFF3B02FFFFFFFF4202FFFFFFFF4802FFFFFFFF4E02FFFFFFFF5402FFFFFFFF5A02FFFFFFFF6102FFFFFFFF6802FFFFFFFF6F02FFFFFFFF7502FFFFFFFF7C02FFFFFFFF8302FFFFFFFF8A02FFFFFFFF9102FFFFFFFF9802FFFFFFFF9E02FFFFFFFFA302FFFFFFFFA902FFFFFFFFB002FFFFFFFFB602FFFFFFFFBD02FFFFFFFFC302FFFFFFFFCA02FFFFFFFFD102FFFFFFFFD802FFFFFFFFDE02FFFFFFFFE502FFFFFFFFEB02FFFFFFFFF202FFFFFFFFF902FFFFFFFF0003FFFFFFFF0703FFFFFFFF0D03FFFFFFFF1303FFFFFFFF1903FFFFFFFF1F03FFFFFFFF2603FFFFFFFF2D03FFFFFFFF3403FFFFFFFF3B03FFFFFFFF4203FFFFFFFF4903FFFFFFFF4F03FFFFFFFF5603FFFFFFFF5C03FFFFFFFF6203FFFFFFFF6803FFFFFFFF6F03FFFFFFFF7603FFFFFFFF7D03FFFFFFFF8203FFFFFFFF8803FFFFFFFF8F03FFFFFFFF9603FFFFFFFF9D03FFFFFFFFA403FFFFFFFFAB03FFFFFFFFB203FFFFFFFFB903FFFFFFFFBF03FFFFFFFFC603FFFFFFFFCC03FFFFFFFFD203FFFFFFFFD803FFFFFFFFDF03FFFFFFFFE503FFFFFFFFEC03FFFFFFFFF303FFFFFFFFFA03FFFFFFFF0004FFFFFFFF0604FFFFFFFF0D04FFFFFFFF1404FFFFFFFF1B04FFFFFFFF2204FFFFFFFF2804FFFFFFFF2E04FFFFFFFF3404FFFFFFFF3B04FFFFFFFF4104FFFFFFFF4804FFFFFFFF4F04FFFFFFFF5604FFFFFFFF5D04FFFFFFFF6404FFFFFFFF6A04FFFFFFFF7104FFFFFFFF7704FFFFFFFF7D04FFFFFFFF8304FFFFFFFF8904FFFFFFFF9004FFFFFFFF9704FFFFFFFF9C04FFFFFFFFA204FFFFFFFFA904FFFFFFFFB004FFFFFFFFB704FFFFFFFFBE04FFFFFFFFC504FFFFFFFFCC04FFFFFFFFD304FFFFFFFFD904FFFFFFFFE004FFFFFFFFE604FFFFFFFFEC04FFFFFFFFF304FFFFFFFFF904FFFFFFFF0005FFFFFFFF0705FFFFFFFF0E05FFFFFFFF1405FFFFFFFF1A05FFFFFFFF2005FFFFFFFF2705FFFFFFFF2E05FFFFFFFF3505FFFFFFFF3C05FFFFFFFF4205FFFFFFFF4805FFFFFFFF4E05FFFFFFFF5505FFFFFFFF5C05FFFFFFFF6305FFFFFFFF6A05FFFFFFFF7105FFFFFFFF7705FFFFFFFF7D05FFFFFFFF8405FFFFFFFF8A05FFFFFFFF9005FFFFFFFF9705FFFFFFFF9E05FFFFFFFFA305FFFFFFFFAA05FFFFFFFFB105FFFFFFFFB805FFFFFFFFBF05FFFFFFFFC605FFFFFFFFCD05FFFFFFFFD405FFFFFFFFDA05FFFFFFFFE105FFFFFFFFE705FFFFFFFFED05FFFFFFFFF305FFFFFFFFFA05FFFFFFFF0106FFFFFFFF0806FFFFFFFF0F06FFFFFFFF1506FFFFFFFF1B06FFFFFFFF2206FFFFFFFF2906FFFFFFFF3006FFFFFFFF3706FFFFFFFF3E06FFFFFFFF4406FFFFFFFF4A06FFFFFFFF5006FFFFFFFF5606FFFFFFFF5D06FFFFFFFF6406FFFFFFFF6B06FFFFFFFF7106FFFFFFFF7706FFFFFFFF7E06FFFFFFFF8506FFFFFFFF8C06FFFFFFFFFFFF006D6F643435006D6F64323439006D6F6430006D6F64313539006D6F64313836006D6F64313937006D6F64323437006D6F64323136006D6F64323332006D6F643237006D6F64323530006D6F643534006D6F643635006D6F643834006D6F64313130006D6F64313135006D6F64313533006D6F64313430006D6F643231006D6F643734006D6F64313335006D6F64313330006D6F64323035006D6F64313630006D6F64323430006D6F6433006D6F643431006D6F643238006D6F643937006D6F64313136006D6F64313837006D6F64323134006D6F643837006D6F643832006D6F64313433006D6F64313036006D6F64313831006D6F64313336006D6F643131006D6F643439006D6F6434006D6F64323231006D6F64313633006D6F64313538006D6F64323031006D6F64323139006D6F64313838006D6F643331006D6F64323232006D6F643538006D6F643639006D6F64313139006D6F643838006D6F64313134006D6F64313235006D6F64313434006D6F643235006D6F643132006D6F64313731006D6F64313334006D6F64323039006D6F64313634006D6F64323434006D6F6437006D6F6432006D6F643737006D6F643332006D6F64313031006D6F64313230006D6F64313931006D6F64323138006D6F643539006D6F643836006D6F64313437006D6F64313432006D6F64313835006D6F64313732006D6F643135006D6F643533006D6F6438006D6F64323235006D6F64313039006D6F64313637006D6F64313632006D6F64323233006D6F64313932006D6F643335006D6F64323236006D6F643330006D6F643733006D6F643931006D6F643630006D6F64313138006D6F64313239006D6F64313438006D6F643136006D6F64313735006D6F64313338006D6F64323133006D6F64313638006D6F64323438006D6F643433006D6F6436006D6F643831006D6F64323533006D6F643336006D6F64313035006D6F643932006D6F64313935006D6F64313930006D6F64323531006D6F643633006D6F64323534006D6F643930006D6F64313531006D6F64313436006D6F64313537006D6F64313736006D6F643139006D6F643134006D6F643537006D6F643434006D6F64323239006D6F64313133006D6F64323033006D6F64313636006D6F64323237006D6F64313936006D6F643339006D6F64323330006D6F643334006D6F643935006D6F643634006D6F64313232006D6F64313333006D6F64313532006D6F6431006D6F643230006D6F64323337006D6F64313739006D6F64313734006D6F64323137006D6F64323034006D6F64323230006D6F64323335006D6F643437006D6F64323338006D6F643130006D6F643835006D6F643430006D6F64313431006D6F643936006D6F64313939006D6F64313934006D6F64323535006D6F643637006D6F643632006D6F64313233006D6F64313530006D6F64313631006D6F64313830006D6F643233006D6F643138006D6F643239006D6F64323333006D6F643438006D6F64313137006D6F64323037006D6F64313730006D6F64323331006D6F64323030006D6F643735006D6F64323334006D6F643338006D6F643939006D6F643638006D6F643934006D6F64313337006D6F64313234006D6F6435006D6F643234006D6F64323431006D6F64313833006D6F64313738006D6F64313839006D6F64323038006D6F64323234006D6F64323339006D6F643531006D6F64323432006D6F643436006D6F643839006D6F64313037006D6F643736006D6F64313435006D6F64313030006D6F64313938006D6F643133006D6F643731006D6F643636006D6F64313237006D6F64313534006D6F64313635006D6F64313834006D6F643232006D6F643333006D6F643532006D6F64313231006D6F64313038006D6F64323131006D6F64323036006D6F64323532006D6F643739006D6F643432006D6F64313033006D6F643732006D6F643938006D6F64313733006D6F64313238006D6F6439006D6F64323435006D6F64313535006D6F64313832006D6F64313933006D6F64323433006D6F64323132006D6F64323238006D6F643535006D6F64323436006D6F643530006D6F643631006D6F643830006D6F64313131006D6F64313439006D6F64313034006D6F64323032006D6F643137006D6F643730006D6F64313331006D6F64313236006D6F64313639006D6F64313536006D6F64323336006D6F643236006D6F643337006D6F643536006D6F643933006D6F64313132006D6F64323135006D6F64323130006D6F643833006D6F643738006D6F64313339006D6F64313032006D6F64313737006D6F6431333200
+    SizeOfRawData:   3749
+symbols:
+  - Name:            '@comp.id'
+    Value:           17140626
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '@feat.00'
+    Value:           2147549584
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '@vol.md'
+    Value:           3
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .drectve
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          47
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            '.debug$S'
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          116
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            '.text$mn'
+    Value:           0
+    SectionNumber:   3
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          6
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        1049675166
+      Number:          0
+  - Name:            main
+    Value:           0
+    SectionNumber:   3
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            .modmeta
+    Value:           0
+    SectionNumber:   4
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          3749
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            .chks64
+    Value:           0
+    SectionNumber:   5
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          40
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-private-syms.yaml b/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-private-syms.yaml
new file mode 100644
index 0000000000000..3588cc6885e45
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/cxx-modmeta-private-syms.yaml
@@ -0,0 +1,220 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-readobj --coff-cxx-module-metadata %t | FileCheck %s
+
+# CHECK:      CxxModuleMetadata {
+# CHECK-NEXT:   Version: 1
+# CHECK-NEXT:   Reserved: 0
+# CHECK-NEXT:   ModuleIndexWidth: 1
+# CHECK-NEXT:   SymbolIndexWidth: 1
+# CHECK-NEXT:   Modules [
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x1
+# CHECK-NEXT:       Name: m
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: [0x3]
+# CHECK-NEXT:       Symbols: [?nonExported2@@3HA::<!m> (9), ?notExported@@YAHXZ::<!m> (12)]
+# CHECK-NEXT:       Exports: [?munge@@YAHHH at Z::<!m> (13), ?anotherFunction@@YAHXZ::<!m> (15)]
+# CHECK-NEXT:     }
+# CHECK-NEXT:     CxxModule {
+# CHECK-NEXT:       ID: 0x3
+# CHECK-NEXT:       Name: o
+# CHECK-NEXT:       IsHeaderUnit: No
+# CHECK-NEXT:       Dependents: []
+# CHECK-NEXT:       Symbols: []
+# CHECK-NEXT:       Exports: [?getO@@YAHXZ::<!o> (14)]
+# CHECK-NEXT:     }
+# CHECK-NEXT:   ]
+# CHECK-NEXT: }
+
+# This was created with the following commands and inputs:
+
+# cl o.ixx /std:c++20 /c
+# cl m.ixx /std:c++20 /c
+
+# o.ixx:
+# export module o;
+# int nonExportedO() { return 1; }
+# export int getO() { return 42; }
+
+# m.ixx:
+# export module m;
+# import o;
+# int notExported() { return 42; }
+# int nonExported2 = 43;
+# export int munge(int a, int b) { return a + b + getO(); }
+# export int anotherFunction() { return 0; }
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            .modmeta
+    Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+    Alignment:       1
+    SectionData:     0100010118000000FF0103FF090CFF0D0FFF03FFFF0EFFFF006D006F00
+    SizeOfRawData:   29
+symbols:
+  - Name:            '@comp.id'
+    Value:           17140626
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '@feat.00'
+    Value:           2147549584
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '@vol.md'
+    Value:           3
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .drectve
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          47
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            '.debug$S'
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          108
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            .data
+    Value:           0
+    SectionNumber:   3
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          4
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        1999841343
+      Number:          0
+  - Name:            '?nonExported2@@3HA::<!m>'
+    Value:           0
+    SectionNumber:   3
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '.text$mn'
+    Value:           0
+    SectionNumber:   4
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          83
+      NumberOfRelocations: 1
+      NumberOfLinenumbers: 0
+      CheckSum:        3042698026
+      Number:          0
+  - Name:            '?notExported@@YAHXZ::<!m>'
+    Value:           0
+    SectionNumber:   4
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '?munge@@YAHHH at Z::<!m>'
+    Value:           16
+    SectionNumber:   4
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '?getO@@YAHXZ::<!o>'
+    Value:           0
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '?anotherFunction@@YAHXZ::<!m>'
+    Value:           80
+    SectionNumber:   4
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '$LN3'
+    Value:           16
+    SectionNumber:   4
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_LABEL
+  - Name:            .xdata
+    Value:           0
+    SectionNumber:   5
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          8
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        3251837369
+      Number:          0
+  - Name:            '$unwind$?munge@@YAHHH at Z::<!m>'
+    Value:           0
+    SectionNumber:   5
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .pdata
+    Value:           0
+    SectionNumber:   6
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          12
+      NumberOfRelocations: 3
+      NumberOfLinenumbers: 0
+      CheckSum:        3975966820
+      Number:          0
+  - Name:            '$pdata$?munge@@YAHHH at Z::<!m>'
+    Value:           0
+    SectionNumber:   6
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .modmeta
+    Value:           0
+    SectionNumber:   7
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          29
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            .chks64
+    Value:           0
+    SectionNumber:   8
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          64
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+...
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index 55e106d8ea6ac..8422599a02695 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -17,6 +17,7 @@
 #include "Win64EHDumper.h"
 #include "llvm-readobj.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/BinaryFormat/COFF.h"
@@ -42,6 +43,7 @@
 #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
 #include "llvm/DebugInfo/CodeView/TypeTableCollection.h"
 #include "llvm/Object/COFF.h"
+#include "llvm/Object/COFFCxxModuleMetadata.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Object/WindowsResource.h"
 #include "llvm/Support/BinaryStreamReader.h"
@@ -100,6 +102,7 @@ class COFFDumper : public ObjDumper {
   void printCOFFTLSDirectory() override;
   void printCOFFResources() override;
   void printCOFFLoadConfig() override;
+  void printCOFFCxxModuleMetadata() override;
   void printCodeViewDebugInfo() override;
   void mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs,
                           llvm::codeview::MergingTypeTableBuilder &CVTypes,
@@ -2558,3 +2561,74 @@ void COFFDumper::printCOFFTLSDirectory(
                ArrayRef(ImageSectionCharacteristics),
                COFF::SectionCharacteristics(COFF::IMAGE_SCN_ALIGN_MASK));
 }
+
+void COFFDumper::printCOFFCxxModuleMetadata() {
+  SectionRef Sect;
+  for (const SectionRef &S : Obj->sections()) {
+    StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName());
+    if (SectionName == ".modmeta") {
+      Sect = S;
+      break;
+    }
+  }
+  if (Sect == SectionRef())
+    return;
+
+  StringRef Contents = unwrapOrError(Obj->getFileName(), Sect.getContents());
+  COFFCxxModuleMetadata ModMap =
+      unwrapOrError(Obj->getFileName(), parseCOFFCxxModuleMetadata(Contents));
+
+  DictScope D(W, "CxxModuleMetadata");
+  W.printNumber("Version", ModMap.Version);
+  W.printNumber("Reserved", ModMap.Reserved);
+  W.printNumber("ModuleIndexWidth", ModMap.ModuleIndexWidth);
+  W.printNumber("SymbolIndexWidth", ModMap.SymbolIndexWidth);
+
+  COFFCxxModuleMetadataReader Reader(ModMap);
+
+  SmallSet<uint32_t, 8> HeaderUnits;
+  Error Err = Reader.readModuleList(makeVisitor([&](auto A) {
+    for (auto V : A)
+      HeaderUnits.insert(V);
+  }));
+  if (Err)
+    reportError(std::move(Err), Obj->getFileName());
+
+  ListScope L(W, "Modules");
+  while (Reader.hasModuleData()) {
+    uint32_t ModuleID =
+        unwrapOrError(Obj->getFileName(), Reader.readModuleID());
+    if (ModuleID == std::numeric_limits<uint32_t>::max())
+      break;
+
+    DictScope D(W, "CxxModule");
+    StringRef Name;
+    if (ModuleID != 0)
+      Name = unwrapOrError(Obj->getFileName(), Reader.readModuleName());
+    W.printHex("ID", ModuleID);
+    W.printString("Name", Name);
+    W.printBoolean("IsHeaderUnit", HeaderUnits.contains(ModuleID));
+
+    Err = Reader.readModuleList([&](auto A) {
+      W.printList("Dependents", A, [&](auto &OS, auto V) { OS << W.hex(V); });
+    });
+    if (Err)
+      reportError(std::move(Err), Obj->getFileName());
+
+    Err = Reader.readSymbolList([&](auto A) {
+      W.printList("Symbols", A, [&](auto &OS, auto V) {
+        OS << getSymbolName(V) << " (" << to_string<uint32_t>(V) << ')';
+      });
+    });
+    if (Err)
+      reportError(std::move(Err), Obj->getFileName());
+
+    Err = Reader.readSymbolList([&](auto A) {
+      W.printList("Exports", A, [&](auto &OS, auto V) {
+        OS << getSymbolName(V) << " (" << to_string<uint32_t>(V) << ')';
+      });
+    });
+    if (Err)
+      reportError(std::move(Err), Obj->getFileName());
+  }
+}
diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index 0dba8252fd466..10f81486e7859 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -153,6 +153,7 @@ class ObjDumper {
   virtual void printCOFFTLSDirectory() {}
   virtual void printCOFFResources() {}
   virtual void printCOFFLoadConfig() { }
+  virtual void printCOFFCxxModuleMetadata() {}
   virtual void printCodeViewDebugInfo() { }
   virtual void
   mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs,
diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td
index 7aafdb03973e8..a97722550d3bb 100644
--- a/llvm/tools/llvm-readobj/Opts.td
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -96,6 +96,7 @@ def coff_pseudoreloc
       Group<grp_coff>;
 def coff_resources : FF<"coff-resources", "Display .rsrc section">, Group<grp_coff>;
 def coff_tls_directory : FF<"coff-tls-directory", "Display TLS directory">, Group<grp_coff>;
+def coff_cxx_modmeta : FF<"coff-cxx-module-metadata", "Display C++ module metadata">, Group<grp_coff>;
 
 // XCOFF specific options.
 def grp_xcoff : OptionGroup<"kind">, HelpText<"OPTIONS (XCOFF specific)">;
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index fa56e3e48e58c..699c97b23d10d 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -165,6 +165,7 @@ static bool COFFImports;
 static bool COFFLoadConfig;
 static bool COFFResources;
 static bool COFFTLSDirectory;
+static bool COFFCxxModuleMetadata;
 
 // XCOFF specific options.
 static bool XCOFFAuxiliaryHeader;
@@ -320,6 +321,7 @@ static void parseOptions(const opt::InputArgList &Args) {
   opts::COFFLoadConfig = Args.hasArg(OPT_coff_load_config);
   opts::COFFResources = Args.hasArg(OPT_coff_resources);
   opts::COFFTLSDirectory = Args.hasArg(OPT_coff_tls_directory);
+  opts::COFFCxxModuleMetadata = Args.hasArg(OPT_coff_cxx_modmeta);
 
   // XCOFF specific options.
   opts::XCOFFAuxiliaryHeader = Args.hasArg(OPT_auxiliary_header);
@@ -516,6 +518,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
       Dumper->printCOFFLoadConfig();
     if (opts::CGProfile)
       Dumper->printCGProfile();
+    if (opts::COFFCxxModuleMetadata)
+      Dumper->printCOFFCxxModuleMetadata();
     if (opts::Addrsig)
       Dumper->printAddrsig();
     if (opts::CodeView)

>From 752eb192f297b98ec74a25194eb5e19187037603 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 9 Jun 2026 15:20:16 +0200
Subject: [PATCH 2/5] Address review comments

---
 llvm/docs/CommandGuide/llvm-readobj.rst       |   4 +
 .../llvm/Object/COFFCxxModuleMetadata.h       |   6 +-
 llvm/lib/Object/COFFCxxModuleMetadata.cpp     | 104 +++++++++---------
 llvm/tools/llvm-readobj/COFFDumper.cpp        |  70 +++++++-----
 4 files changed, 103 insertions(+), 81 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-readobj.rst b/llvm/docs/CommandGuide/llvm-readobj.rst
index bb79438177bf8..aa2298a1904b0 100644
--- a/llvm/docs/CommandGuide/llvm-readobj.rst
+++ b/llvm/docs/CommandGuide/llvm-readobj.rst
@@ -329,6 +329,10 @@ The following options are implemented only for the PE/COFF file format.
 
  Display the .reloc section.
 
+.. option:: --coff-cxx-module-metadata
+
+ Display the .modmeta section.
+
 .. option:: --coff-debug-directory
 
  Display the debug directory.
diff --git a/llvm/include/llvm/Object/COFFCxxModuleMetadata.h b/llvm/include/llvm/Object/COFFCxxModuleMetadata.h
index 3cabb415d3bc5..d1a73e18bdb92 100644
--- a/llvm/include/llvm/Object/COFFCxxModuleMetadata.h
+++ b/llvm/include/llvm/Object/COFFCxxModuleMetadata.h
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Windows-specific.
+// MSVC-specific.
 // Definitions and a parser for the C++ 20 ".modmeta" section.
 //
 //===----------------------------------------------------------------------===//
@@ -98,10 +98,12 @@ struct LLVM_ABI COFFCxxModuleMetadataReader {
       Visitor(ArrayRef<support::ulittle16_t>(
           reinterpret_cast<const support::ulittle16_t *>(List->data()),
           List->size() / sizeof(support::ulittle16_t)));
-    else
+    else if (Width == 4)
       Visitor(ArrayRef<support::ulittle32_t>(
           reinterpret_cast<const support::ulittle32_t *>(List->data()),
           List->size() / sizeof(support::ulittle32_t)));
+    else
+      return createStringError("Unsupported width: %d", Width);
 
     return Error::success();
   }
diff --git a/llvm/lib/Object/COFFCxxModuleMetadata.cpp b/llvm/lib/Object/COFFCxxModuleMetadata.cpp
index 0cffd8f836e37..4e9a8fd5b3568 100644
--- a/llvm/lib/Object/COFFCxxModuleMetadata.cpp
+++ b/llvm/lib/Object/COFFCxxModuleMetadata.cpp
@@ -22,43 +22,38 @@ bool COFFCxxModuleMetadataReader::hasModuleData() const {
 }
 
 Expected<uint32_t> COFFCxxModuleMetadataReader::readModuleID() {
+  if (ModuleIndexWidth != 1 && ModuleIndexWidth != 2 && ModuleIndexWidth != 4)
+    return createStringError("unsupported index width: %d", ModuleIndexWidth);
+
+  if (ModuleData.size() < ModuleIndexWidth)
+    return createStringError("not enough data");
+
+  uint32_t ID = std::numeric_limits<uint32_t>::max();
   switch (ModuleIndexWidth) {
   case 1: {
-    if (ModuleData.size() < 1)
-      return createStringError("Not enough data");
-    uint8_t ID = static_cast<uint8_t>(ModuleData[0]);
-    ModuleData = ModuleData.slice(1, StringRef::npos);
-    if (ID == std::numeric_limits<uint8_t>::max())
-      return std::numeric_limits<uint32_t>::max();
-    return ID;
-  }
+    uint8_t V = static_cast<uint8_t>(ModuleData[0]);
+    if (V != std::numeric_limits<uint8_t>::max())
+      ID = V;
+  } break;
   case 2: {
-    if (ModuleData.size() < 2)
-      return createStringError("Not enough data");
-    uint16_t ID =
+    uint16_t V =
         support::endian::read<uint16_t>(ModuleData.data(), endianness::little);
-    ModuleData = ModuleData.slice(2, StringRef::npos);
-    if (ID == std::numeric_limits<uint16_t>::max())
-      return std::numeric_limits<uint32_t>::max();
-    return ID;
-  }
+    if (V != std::numeric_limits<uint16_t>::max())
+      ID = V;
+  } break;
   case 4: {
-    if (ModuleData.size() < 4)
-      return createStringError("Not enough data");
-    uint32_t ID =
-        support::endian::read<uint32_t>(ModuleData.data(), endianness::little);
-    ModuleData = ModuleData.slice(4, StringRef::npos);
-    return ID;
-  }
-  default:
-    return createStringErrorV("Unsupported index width: {0}", ModuleIndexWidth);
+    ID = support::endian::read<uint32_t>(ModuleData.data(), endianness::little);
+  } break;
   }
+
+  ModuleData = ModuleData.slice(ModuleIndexWidth, StringRef::npos);
+  return ID;
 }
 
 Expected<StringRef> COFFCxxModuleMetadataReader::readModuleName() {
   size_t End = NamesData.find('\0');
   if (End == StringRef::npos)
-    return createStringError("Missing null terminator");
+    return createStringError("missing null terminator");
   StringRef Str = NamesData.slice(0, End);
   NamesData = NamesData.drop_front(End + 1);
   return Str;
@@ -66,55 +61,56 @@ Expected<StringRef> COFFCxxModuleMetadataReader::readModuleName() {
 
 Expected<ArrayRef<uint8_t>>
 COFFCxxModuleMetadataReader::readListImpl(uint8_t Width) {
-  StringRef Sentinel;
-  switch (Width) {
-  case 1:
-    Sentinel = "\xff";
-    break;
-  case 2:
-    Sentinel = "\xff\xff";
-    break;
-  case 4:
-    Sentinel = "\xff\xff\xff\xff";
-    break;
-  default:
-    return createStringErrorV("Unsupported index width: {0}", Width);
+  if (Width != 1 && Width != 2 && Width != 4)
+    return createStringError("unsupported index width: %d", Width);
+
+  StringRef Sentinel("\xff\xff\xff\xff", Width);
+  StringRef Rest = ModuleData;
+  while (Rest.size() >= Width) {
+    if (Rest.consume_front(Sentinel)) {
+      ArrayRef<uint8_t> Data(ModuleData.bytes_begin(),
+                             Rest.bytes_begin() - Width);
+      ModuleData = Rest;
+      return Data;
+    }
+    Rest = Rest.drop_front(Width);
   }
-  size_t Last = ModuleData.find(Sentinel);
-  if (Last == StringRef::npos)
-    return createStringError("Missing end sentinel");
-
-  ArrayRef<uint8_t> Data(ModuleData.bytes_begin(),
-                         ModuleData.bytes_begin() + Last);
-  ModuleData = ModuleData.drop_front(Last + Width);
-  return Data;
+
+  return createStringError("missing end sentinel");
 }
 
 Expected<COFFCxxModuleMetadata>
 parseCOFFCxxModuleMetadata(StringRef SectionData) {
   if (SectionData.size() <= sizeof(COFFCxxModuleMetadataHeader))
-    return createStringError("Insufficient size");
+    return createStringError("insufficient size: got %d, expected more than %d",
+                             SectionData.size(),
+                             sizeof(COFFCxxModuleMetadataHeader));
 
   const auto *Header =
       reinterpret_cast<const COFFCxxModuleMetadataHeader *>(SectionData.data());
   if (Header->Version != 1)
-    return createStringError("Unsupported version");
+    return createStringError("unsupported version: %d", Header->Version);
 
   auto IsSupportedIndexWidth = [](uint8_t Width) {
     return Width == 1 || Width == 2 || Width == 4;
   };
 
   if (!IsSupportedIndexWidth(Header->ModuleIndexWidth))
-    return createStringErrorV("Unsupported module index width: {0}",
+    return createStringErrorV("unsupported module index width: {0}",
                               Header->ModuleIndexWidth);
   if (!IsSupportedIndexWidth(Header->SymbolIndexWidth))
-    return createStringErrorV("Unsupported symbol index width: {0}",
+    return createStringErrorV("unsupported symbol index width: {0}",
                               Header->SymbolIndexWidth);
 
   size_t ModuleDataSize = Header->ModuleDataSize.value();
-  if (ModuleDataSize <= sizeof(COFFCxxModuleMetadataHeader) ||
-      ModuleDataSize + 1 >= SectionData.size())
-    return createStringErrorV("Invalid module data size: {0}", ModuleDataSize);
+  if (ModuleDataSize <= sizeof(COFFCxxModuleMetadataHeader))
+    return createStringError(
+        "module data size too small: got %d, expected more than %d",
+        ModuleDataSize, sizeof(COFFCxxModuleMetadataHeader));
+  if (ModuleDataSize + 1 >= SectionData.size())
+    return createStringErrorV(
+        "module data size too big: got %d, section size is %d", ModuleDataSize,
+        SectionData.size());
 
   COFFCxxModuleMetadata Map;
   Map.Version = Header->Version;
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index 8422599a02695..de578d3cb76d8 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -2564,9 +2564,9 @@ void COFFDumper::printCOFFTLSDirectory(
 
 void COFFDumper::printCOFFCxxModuleMetadata() {
   SectionRef Sect;
-  for (const SectionRef &S : Obj->sections()) {
-    StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName());
-    if (SectionName == ".modmeta") {
+  for (SectionRef S : Obj->sections()) {
+    Expected<StringRef> SectionName = S.getName();
+    if (SectionName && *SectionName == ".modmeta") {
       Sect = S;
       break;
     }
@@ -2574,61 +2574,81 @@ void COFFDumper::printCOFFCxxModuleMetadata() {
   if (Sect == SectionRef())
     return;
 
-  StringRef Contents = unwrapOrError(Obj->getFileName(), Sect.getContents());
-  COFFCxxModuleMetadata ModMap =
-      unwrapOrError(Obj->getFileName(), parseCOFFCxxModuleMetadata(Contents));
+  Expected<StringRef> Contents = Sect.getContents();
+  if (!Contents) {
+    reportWarning(Contents.takeError(), Obj->getFileName());
+    return;
+  }
+  Expected<COFFCxxModuleMetadata> ModMap =
+      parseCOFFCxxModuleMetadata(*Contents);
+  if (!ModMap) {
+    reportWarning(ModMap.takeError(), Obj->getFileName());
+    return;
+  }
 
   DictScope D(W, "CxxModuleMetadata");
-  W.printNumber("Version", ModMap.Version);
-  W.printNumber("Reserved", ModMap.Reserved);
-  W.printNumber("ModuleIndexWidth", ModMap.ModuleIndexWidth);
-  W.printNumber("SymbolIndexWidth", ModMap.SymbolIndexWidth);
+  W.printNumber("Version", ModMap->Version);
+  W.printNumber("Reserved", ModMap->Reserved);
+  W.printNumber("ModuleIndexWidth", ModMap->ModuleIndexWidth);
+  W.printNumber("SymbolIndexWidth", ModMap->SymbolIndexWidth);
 
-  COFFCxxModuleMetadataReader Reader(ModMap);
+  COFFCxxModuleMetadataReader Reader(*ModMap);
 
   SmallSet<uint32_t, 8> HeaderUnits;
   Error Err = Reader.readModuleList(makeVisitor([&](auto A) {
     for (auto V : A)
       HeaderUnits.insert(V);
   }));
-  if (Err)
-    reportError(std::move(Err), Obj->getFileName());
+  if (Err) {
+    reportWarning(std::move(Err), Obj->getFileName());
+    return;
+  }
 
   ListScope L(W, "Modules");
   while (Reader.hasModuleData()) {
-    uint32_t ModuleID =
-        unwrapOrError(Obj->getFileName(), Reader.readModuleID());
-    if (ModuleID == std::numeric_limits<uint32_t>::max())
+    Expected<uint32_t> ModuleID = Reader.readModuleID();
+    if (!ModuleID) {
+      reportWarning(ModuleID.takeError(), Obj->getFileName());
+      return;
+    }
+
+    if (*ModuleID == std::numeric_limits<uint32_t>::max())
       break;
 
     DictScope D(W, "CxxModule");
     StringRef Name;
-    if (ModuleID != 0)
+    if (*ModuleID != 0)
       Name = unwrapOrError(Obj->getFileName(), Reader.readModuleName());
-    W.printHex("ID", ModuleID);
+    W.printHex("ID", *ModuleID);
     W.printString("Name", Name);
-    W.printBoolean("IsHeaderUnit", HeaderUnits.contains(ModuleID));
+    W.printBoolean("IsHeaderUnit", HeaderUnits.contains(*ModuleID));
 
     Err = Reader.readModuleList([&](auto A) {
       W.printList("Dependents", A, [&](auto &OS, auto V) { OS << W.hex(V); });
     });
-    if (Err)
-      reportError(std::move(Err), Obj->getFileName());
+    if (Err) {
+      reportWarning(std::move(Err), Obj->getFileName());
+      return;
+    }
 
     Err = Reader.readSymbolList([&](auto A) {
       W.printList("Symbols", A, [&](auto &OS, auto V) {
         OS << getSymbolName(V) << " (" << to_string<uint32_t>(V) << ')';
       });
     });
-    if (Err)
-      reportError(std::move(Err), Obj->getFileName());
+    if (Err) {
+      reportWarning(std::move(Err), Obj->getFileName());
+      return;
+    }
 
     Err = Reader.readSymbolList([&](auto A) {
       W.printList("Exports", A, [&](auto &OS, auto V) {
         OS << getSymbolName(V) << " (" << to_string<uint32_t>(V) << ')';
       });
     });
-    if (Err)
-      reportError(std::move(Err), Obj->getFileName());
+    if (Err) {
+      reportWarning(std::move(Err), Obj->getFileName());
+      return;
+    }
   }
 }

>From f8197cb4fbeef5e7c67699f341bb53a4adb42bc8 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 9 Jun 2026 15:20:58 +0200
Subject: [PATCH 3/5] Use assertion in readList()

---
 llvm/include/llvm/Object/COFFCxxModuleMetadata.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Object/COFFCxxModuleMetadata.h b/llvm/include/llvm/Object/COFFCxxModuleMetadata.h
index d1a73e18bdb92..ebf2c554aaef1 100644
--- a/llvm/include/llvm/Object/COFFCxxModuleMetadata.h
+++ b/llvm/include/llvm/Object/COFFCxxModuleMetadata.h
@@ -103,7 +103,7 @@ struct LLVM_ABI COFFCxxModuleMetadataReader {
           reinterpret_cast<const support::ulittle32_t *>(List->data()),
           List->size() / sizeof(support::ulittle32_t)));
     else
-      return createStringError("Unsupported width: %d", Width);
+      assert(false && "unexpected list width");
 
     return Error::success();
   }

>From b0452f8fb455676d53393386b226b32891e05356 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 9 Jun 2026 15:27:21 +0200
Subject: [PATCH 4/5] Convert all createStringErrorV

---
 llvm/lib/Object/COFFCxxModuleMetadata.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Object/COFFCxxModuleMetadata.cpp b/llvm/lib/Object/COFFCxxModuleMetadata.cpp
index 4e9a8fd5b3568..9051c6ca54848 100644
--- a/llvm/lib/Object/COFFCxxModuleMetadata.cpp
+++ b/llvm/lib/Object/COFFCxxModuleMetadata.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Object/COFFCxxModuleMetadata.h"
-#include "llvm/Support/ErrorExtras.h"
 
 namespace llvm::object {
 
@@ -96,11 +95,11 @@ parseCOFFCxxModuleMetadata(StringRef SectionData) {
   };
 
   if (!IsSupportedIndexWidth(Header->ModuleIndexWidth))
-    return createStringErrorV("unsupported module index width: {0}",
-                              Header->ModuleIndexWidth);
+    return createStringError("unsupported module index width: %d",
+                             Header->ModuleIndexWidth);
   if (!IsSupportedIndexWidth(Header->SymbolIndexWidth))
-    return createStringErrorV("unsupported symbol index width: {0}",
-                              Header->SymbolIndexWidth);
+    return createStringError("unsupported symbol index width: %d",
+                             Header->SymbolIndexWidth);
 
   size_t ModuleDataSize = Header->ModuleDataSize.value();
   if (ModuleDataSize <= sizeof(COFFCxxModuleMetadataHeader))
@@ -108,7 +107,7 @@ parseCOFFCxxModuleMetadata(StringRef SectionData) {
         "module data size too small: got %d, expected more than %d",
         ModuleDataSize, sizeof(COFFCxxModuleMetadataHeader));
   if (ModuleDataSize + 1 >= SectionData.size())
-    return createStringErrorV(
+    return createStringError(
         "module data size too big: got %d, section size is %d", ModuleDataSize,
         SectionData.size());
 

>From 633f5cb2ca3e1144fcb438126f1ac2de10b6a3d8 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 9 Jun 2026 15:30:46 +0200
Subject: [PATCH 5/5] Convert one last unwrap

---
 llvm/tools/llvm-readobj/COFFDumper.cpp | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index de578d3cb76d8..e80c8449b3476 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -2617,8 +2617,14 @@ void COFFDumper::printCOFFCxxModuleMetadata() {
 
     DictScope D(W, "CxxModule");
     StringRef Name;
-    if (*ModuleID != 0)
-      Name = unwrapOrError(Obj->getFileName(), Reader.readModuleName());
+    if (*ModuleID != 0) {
+      Expected<StringRef> ExpName = Reader.readModuleName();
+      if (!ExpName) {
+        reportWarning(ExpName.takeError(), Obj->getFileName());
+        return;
+      }
+      Name = *ExpName;
+    }
     W.printHex("ID", *ModuleID);
     W.printString("Name", Name);
     W.printBoolean("IsHeaderUnit", HeaderUnits.contains(*ModuleID));



More information about the llvm-commits mailing list