[llvm] e1e52c9 - [win][x64] Updated `llvm-objdump` and `llvm-readobj` to be able to dump Windows x64 Unwind v3 information. (#199120)
via llvm-commits
llvm-commits at lists.llvm.org
Wed May 27 09:04:06 PDT 2026
Author: Daniel Paoliello
Date: 2026-05-27T09:03:59-07:00
New Revision: e1e52c9ccef39f4ccc55e1e89c5d2957ec186a47
URL: https://github.com/llvm/llvm-project/commit/e1e52c9ccef39f4ccc55e1e89c5d2957ec186a47
DIFF: https://github.com/llvm/llvm-project/commit/e1e52c9ccef39f4ccc55e1e89c5d2957ec186a47.diff
LOG: [win][x64] Updated `llvm-objdump` and `llvm-readobj` to be able to dump Windows x64 Unwind v3 information. (#199120)
Public docs:
<https://learn.microsoft.com/en-us/cpp/build/x64-unwind-information-v3?view=msvc-170>
The change adds Windows x64 unwind v3 info decoding and printing support
in LLVM, including new data structures, enums, and decoding functions to
handle the different WOD opcodes and epilog descriptors. It also updates
the dumping utilities (llvm-readobj and llvm-objdump) to correctly
interpret v3 unwind info.
Added:
llvm/lib/Support/Win64EH.cpp
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-all-wods.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-apx.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-inherit.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-opcode.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-push-consecutive-2.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-basic.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-chain.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-distinct-epilog.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog-flags.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog-large.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-handler-odd-payload.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-handler.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-large.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-multi-epilog.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-neg-offset.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-pool-overflow.yaml
llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-reloc.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-all-wods.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-apx.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-bad-inherit.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-bad-opcode.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-basic.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-chain.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-distinct-epilog.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-epilog-flags.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-epilog.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-handler.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-multi-epilog.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-neg-offset.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-pool-overflow.yaml
llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-reloc.yaml
Modified:
llvm/include/llvm/Support/Win64EH.h
llvm/lib/Support/CMakeLists.txt
llvm/tools/llvm-objdump/COFFDump.cpp
llvm/tools/llvm-objdump/ObjdumpOpts.td
llvm/tools/llvm-objdump/llvm-objdump.cpp
llvm/tools/llvm-objdump/llvm-objdump.h
llvm/tools/llvm-readobj/Opts.td
llvm/tools/llvm-readobj/Win64EHDumper.cpp
llvm/tools/llvm-readobj/Win64EHDumper.h
llvm/tools/llvm-readobj/llvm-readobj.cpp
llvm/tools/llvm-readobj/llvm-readobj.h
Removed:
################################################################################
diff --git a/llvm/include/llvm/Support/Win64EH.h b/llvm/include/llvm/Support/Win64EH.h
index ec3413b31ee4a..c42d2ac063b8d 100644
--- a/llvm/include/llvm/Support/Win64EH.h
+++ b/llvm/include/llvm/Support/Win64EH.h
@@ -15,8 +15,12 @@
#ifndef LLVM_SUPPORT_WIN64EH_H
#define LLVM_SUPPORT_WIN64EH_H
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
namespace llvm {
namespace Win64EH {
@@ -110,6 +114,10 @@ enum UnwindOpcodes {
// A custom unspecified opcode, consisting of one or more bytes. This
// allows producing opcodes in the implementation defined/reserved range.
UOP_Custom,
+
+ // V3-only x86_64 opcodes. They are documented at
+ // https://learn.microsoft.com/en-us/cpp/build/x64-unwind-information-v3
+ UOP_Push2, // PUSH2 — two registers in one instruction
};
/// UnwindCode - This union describes a single operation in a function prolog,
@@ -143,7 +151,11 @@ enum {
UNW_TerminateHandler = 0x02,
/// UNW_ChainInfo - Specifies that this UnwindInfo structure is chained to
/// another one.
- UNW_ChainInfo = 0x04
+ UNW_ChainInfo = 0x04,
+ /// UNW_FlagLarge - V3 only. When set, the header is 5 bytes (an extra
+ /// UNWIND_INFO_LARGE_V3 byte follows), SizeOfProlog extends to 16 bits,
+ /// and prolog IP offset entries are 16-bit.
+ UNW_FlagLarge = 0x08
};
/// RuntimeFunction - An entry in the table of functions with unwind info.
@@ -221,6 +233,90 @@ struct UnwindInfo {
}
};
+//===----------------------------------------------------------------------===//
+// V3 Unwind Information
+//===----------------------------------------------------------------------===//
+
+/// V3 Winding Operation Descriptor opcodes.
+enum WODOpcode : uint8_t {
+ WOD_SET_FPREG = 0, // 8-bit opcode, 2 bytes
+ WOD_ALLOC_HUGE = 1, // 8-bit opcode, 5 bytes
+ WOD_ALLOC_LARGE = 2, // 8-bit opcode, 3 bytes
+ WOD_PUSH_CANONICAL_FRAME = 3, // 8-bit opcode, 2 bytes
+ WOD_PUSH = 4, // 3-bit opcode, 1 byte
+ WOD_SAVE_NONVOL_FAR = 5, // 3-bit opcode, 5 bytes
+ WOD_SAVE_NONVOL = 6, // 3-bit opcode, 3 bytes
+ WOD_PUSH_CONSECUTIVE_2 = 7, // 3-bit opcode, 1 byte
+ WOD_ALLOC_SMALL = 8, // 4-bit opcode, 1 byte
+ WOD_SAVE_XMM128_FAR = 9, // 4-bit opcode, 5 bytes
+ WOD_SAVE_XMM128 = 10, // 4-bit opcode, 3 bytes
+ WOD_PUSH2 = 32, // 6-bit opcode, 2 bytes
+};
+
+/// V3 EPILOG_INFO flags.
+enum EpilogInfoFlagsV3 : uint8_t {
+ EPILOG_PARENT_FRAGMENT_TRANSFER = 0x01,
+ /// When set, the extended descriptor uses EPILOG_INFO_LARGE_EX_V3 (16-bit
+ /// IpOffsetOfLastInstruction) and the IP offset array uses 16-bit entries.
+ EPILOG_INFO_LARGE = 0x02,
+};
+
+/// Decoded V3 Winding Operation Descriptor.
+struct DecodedWOD {
+ WODOpcode Opcode;
+ uint8_t Register; // For applicable ops (5-bit for int, 4-bit for XMM)
+ uint8_t Register2; // For WOD_PUSH2
+ // TODO: Define a named enum for WOD_PUSH_CANONICAL_FRAME Type values once
+ // the Windows x64 Unwind V3 spec is finalized. The set of valid values is
+ // defined by the OS (see the Windows SDK headers) but is not yet stable.
+ uint8_t Type; // For WOD_PUSH_CANONICAL_FRAME
+ uint8_t ByteSize; // How many bytes this WOD consumed (max 5)
+ uint32_t Size; // For alloc ops: final computed size
+ uint32_t Displacement; // For save ops: final computed displacement
+};
+
+/// Decoded V3 epilog descriptor.
+struct DecodedEpilogV3 {
+ uint8_t Flags;
+ uint8_t NumberOfOps;
+ uint16_t IpOffsetOfLastInstruction;
+ uint16_t FirstOp;
+ int32_t EpilogOffset; // Resolved absolute offset (accumulated from deltas).
+ SmallVector<uint16_t, 8> IpOffsets;
+
+ /// Whether the EPILOG_INFO_LARGE flag is set.
+ bool isLarge() const { return Flags & EPILOG_INFO_LARGE; }
+};
+
+/// Decoded V3 UNWIND_INFO.
+struct DecodedUnwindInfoV3 {
+ uint8_t Version;
+ uint8_t Flags;
+ uint8_t PayloadWords;
+ uint8_t NumberOfOps;
+ uint8_t NumberOfEpilogs;
+ uint16_t SizeOfProlog;
+ /// Total bytes consumed by header + payload (used to locate handler/chain).
+ uint16_t PayloadSize;
+ SmallVector<uint16_t, 8> PrologIpOffsets;
+ SmallVector<DecodedEpilogV3, 4> Epilogs;
+ ArrayRef<uint8_t> WODPool;
+
+ /// Whether the UNW_FlagLarge flag is set.
+ bool isLarge() const { return Flags & UNW_FlagLarge; }
+};
+
+/// Return the register name for a 5-bit AMD64 integer register number.
+/// Covers 0-15 (RAX-R15) and 16-31 (R16-R31 for APX).
+StringRef getRegisterNameV3(unsigned Reg);
+
+/// Decode one WOD from the pool at the given byte offset.
+/// Returns an error on malformed data.
+Expected<DecodedWOD> decodeWOD(ArrayRef<uint8_t> Pool, unsigned Offset);
+
+/// Parse a V3 UNWIND_INFO from raw bytes.
+/// Returns an error on malformed data.
+Expected<DecodedUnwindInfoV3> decodeUnwindInfoV3(ArrayRef<uint8_t> Data);
} // End of namespace Win64EH
} // End of namespace llvm
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index c3f5961b6584d..99205b8809036 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -282,6 +282,7 @@ add_llvm_component_library(LLVMSupport
VirtualOutputConfig.cpp
VirtualOutputError.cpp
VirtualOutputFile.cpp
+ Win64EH.cpp
WithColor.cpp
YAMLParser.cpp
YAMLTraits.cpp
diff --git a/llvm/lib/Support/Win64EH.cpp b/llvm/lib/Support/Win64EH.cpp
new file mode 100644
index 0000000000000..479da174f608d
--- /dev/null
+++ b/llvm/lib/Support/Win64EH.cpp
@@ -0,0 +1,364 @@
+//===-- Win64EH.cpp - Win64 EH V3 Support -----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements decoding helpers for V3 unwind information on Win64.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Win64EH.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MathExtras.h"
+
+using namespace llvm;
+using namespace llvm::Win64EH;
+
+StringRef Win64EH::getRegisterNameV3(unsigned Reg) {
+ static const char *const Names[] = {
+ "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI",
+ "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
+ "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
+ "R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31",
+ };
+ if (Reg >= std::size(Names))
+ return "<invalid>";
+ return Names[Reg];
+}
+
+Expected<DecodedWOD> Win64EH::decodeWOD(ArrayRef<uint8_t> Pool,
+ unsigned Offset) {
+ if (Offset >= Pool.size())
+ return createStringError("WOD pool overflow at offset %u", Offset);
+
+ uint8_t FirstByte = Pool[Offset];
+ DecodedWOD W = {};
+
+ // Determine opcode from variable-width prefix encoding.
+ // The dispatch order matters: check shorter prefixes first since they
+ // occupy the lowest bits, then fall through to longer prefixes.
+ // 3-bit prefix (bits [2:0] >= 4): opcodes 4-7
+ // 4-bit prefix (bits [3:0] >= 8): opcodes 8-10
+ // 6-bit prefix (bits [5:0] == 0x20): opcode 32 (PUSH2)
+ // 8-bit prefix (full byte 0-3): opcodes 0-3
+ uint8_t Low3 = FirstByte & 0x07;
+
+ // 3-bit opcode: bits [2:0] in {4, 5, 6, 7}
+ if (Low3 >= 4) {
+ switch (Low3) {
+ case WOD_PUSH: {
+ W.Opcode = WOD_PUSH;
+ W.ByteSize = 1;
+ W.Register = (FirstByte >> 3) & 0x1F; // 5-bit register
+ return W;
+ }
+ case WOD_SAVE_NONVOL_FAR: {
+ W.Opcode = WOD_SAVE_NONVOL_FAR;
+ W.ByteSize = 5;
+ if (Offset + 5 > Pool.size())
+ return createStringError("WOD_SAVE_NONVOL_FAR truncated at offset %u",
+ Offset);
+ W.Register = (FirstByte >> 3) & 0x1F;
+ W.Displacement = support::endian::read32le(&Pool[Offset + 1]);
+ return W;
+ }
+ case WOD_SAVE_NONVOL: {
+ W.Opcode = WOD_SAVE_NONVOL;
+ W.ByteSize = 3;
+ if (Offset + 3 > Pool.size())
+ return createStringError("WOD_SAVE_NONVOL truncated at offset %u",
+ Offset);
+ W.Register = (FirstByte >> 3) & 0x1F;
+ W.Displacement =
+ (uint32_t)support::endian::read16le(&Pool[Offset + 1]) * 8;
+ return W;
+ }
+ case WOD_PUSH_CONSECUTIVE_2: {
+ W.Opcode = WOD_PUSH_CONSECUTIVE_2;
+ W.ByteSize = 1;
+ W.Register = (FirstByte >> 3) & 0x1F;
+ if (W.Register > 30)
+ return createStringError(
+ "WOD_PUSH_CONSECUTIVE_2 Register=%u out of range [0,30] at pool "
+ "offset %u",
+ W.Register, Offset);
+ return W;
+ }
+ default:
+ return createStringError("unknown WOD opcode 0x%02X at pool offset %u",
+ FirstByte, Offset);
+ }
+ }
+
+ // 4-bit opcode: bits [3:0] in {8, 9, 10, ...}
+ uint8_t Low4 = FirstByte & 0x0F;
+ if (Low4 >= 8) {
+ switch (Low4) {
+ case WOD_ALLOC_SMALL: {
+ W.Opcode = WOD_ALLOC_SMALL;
+ W.ByteSize = 1;
+ W.Size = (unsigned)(((FirstByte >> 4) & 0x0F) + 1) * 8;
+ return W;
+ }
+ case WOD_SAVE_XMM128_FAR: {
+ W.Opcode = WOD_SAVE_XMM128_FAR;
+ W.ByteSize = 5;
+ if (Offset + 5 > Pool.size())
+ return createStringError("WOD_SAVE_XMM128_FAR truncated at offset %u",
+ Offset);
+ W.Register = (FirstByte >> 4) & 0x0F;
+ W.Displacement = support::endian::read32le(&Pool[Offset + 1]);
+ return W;
+ }
+ case WOD_SAVE_XMM128: {
+ W.Opcode = WOD_SAVE_XMM128;
+ W.ByteSize = 3;
+ if (Offset + 3 > Pool.size())
+ return createStringError("WOD_SAVE_XMM128 truncated at offset %u",
+ Offset);
+ W.Register = (FirstByte >> 4) & 0x0F;
+ W.Displacement =
+ (uint32_t)support::endian::read16le(&Pool[Offset + 1]) * 16;
+ return W;
+ }
+ default:
+ return createStringError("unknown WOD opcode 0x%02X at pool offset %u",
+ FirstByte, Offset);
+ }
+ }
+
+ // 6-bit opcode: bits [5:0] == 0x20 (WOD_PUSH2)
+ uint8_t Low6 = FirstByte & 0x3F;
+ if (Low6 == WOD_PUSH2) {
+ W.Opcode = WOD_PUSH2;
+ W.ByteSize = 2;
+ if (Offset + 2 > Pool.size())
+ return createStringError("WOD_PUSH2 truncated at offset %u", Offset);
+ uint8_t SecondByte = Pool[Offset + 1];
+ // First reg from bits [7:6] of first byte (2 bits) and bits [2:0] of second
+ // (3 bits)
+ W.Register = ((FirstByte >> 6) & 0x03) | ((SecondByte & 0x07) << 2);
+ W.Register2 = (SecondByte >> 3) & 0x1F;
+ return W;
+ }
+
+ // 8-bit opcode: full byte is opcode (values 0-3)
+ switch (FirstByte) {
+ case WOD_SET_FPREG: {
+ W.Opcode = WOD_SET_FPREG;
+ W.ByteSize = 2;
+ if (Offset + 2 > Pool.size())
+ return createStringError("WOD_SET_FPREG truncated at offset %u", Offset);
+ uint8_t SecondByte = Pool[Offset + 1];
+ W.Register = SecondByte & 0x0F; // 4-bit register
+ W.Displacement = (unsigned)((SecondByte >> 4) & 0x0F) * 16;
+ return W;
+ }
+ case WOD_ALLOC_HUGE: {
+ W.Opcode = WOD_ALLOC_HUGE;
+ W.ByteSize = 5;
+ if (Offset + 5 > Pool.size())
+ return createStringError("WOD_ALLOC_HUGE truncated at offset %u", Offset);
+ W.Size = support::endian::read32le(&Pool[Offset + 1]);
+ return W;
+ }
+ case WOD_ALLOC_LARGE: {
+ W.Opcode = WOD_ALLOC_LARGE;
+ W.ByteSize = 3;
+ if (Offset + 3 > Pool.size())
+ return createStringError("WOD_ALLOC_LARGE truncated at offset %u",
+ Offset);
+ W.Size = (uint32_t)support::endian::read16le(&Pool[Offset + 1]) * 8;
+ return W;
+ }
+ case WOD_PUSH_CANONICAL_FRAME: {
+ W.Opcode = WOD_PUSH_CANONICAL_FRAME;
+ W.ByteSize = 2;
+ if (Offset + 2 > Pool.size())
+ return createStringError(
+ "WOD_PUSH_CANONICAL_FRAME truncated at offset %u", Offset);
+ W.Type = Pool[Offset + 1];
+ return W;
+ }
+ default:
+ return createStringError("unknown WOD opcode 0x%02X at pool offset %u",
+ FirstByte, Offset);
+ }
+}
+
+Expected<DecodedUnwindInfoV3>
+Win64EH::decodeUnwindInfoV3(ArrayRef<uint8_t> Data) {
+ if (Data.size() < 4)
+ return createStringError("V3 unwind info too short: %zu bytes",
+ Data.size());
+
+ DecodedUnwindInfoV3 Info;
+ Info.Version = Data[0] & 0x07;
+ Info.Flags = (Data[0] >> 3) & 0x1F;
+ Info.SizeOfProlog = Data[1];
+ Info.PayloadWords = Data[2];
+ Info.NumberOfOps = Data[3] & 0x1F;
+ Info.NumberOfEpilogs = (Data[3] >> 5) & 0x07;
+
+ // The fixed header is always 4 bytes. When UNW_FlagLarge is set, the first
+ // byte of the payload is the UNWIND_INFO_LARGE_V3 extension byte (which
+ // extends SizeOfProlog to 16 bits and widens prolog IP offset entries to
+ // 16 bits). That byte IS counted in PayloadWords.
+ unsigned Offset = 4; // Start of payload
+
+ // Compute the end of the payload area declared by PayloadWords. All
+ // subsequent reads of payload structures (the optional UNWIND_INFO_LARGE_V3
+ // byte, prolog IP offsets, epilog descriptors) must stay within this region;
+ // reading past it would either overflow the buffer or cross into the
+ // trailing handler/chain data, both of which indicate a malformed record.
+ unsigned PayloadEnd = 4 + Info.PayloadWords * 2;
+ if (PayloadEnd > Data.size())
+ return createStringError(
+ "V3 unwind info PayloadWords (%u) extends past end of buffer",
+ Info.PayloadWords);
+
+ bool IsLarge = Info.isLarge();
+ if (IsLarge) {
+ if (Offset >= PayloadEnd)
+ return createStringError(
+ "V3 unwind info with UNW_FlagLarge too short: PayloadWords (%u) "
+ "leaves no room for UNWIND_INFO_LARGE_V3",
+ Info.PayloadWords);
+ Info.SizeOfProlog |= static_cast<uint16_t>(Data[Offset]) << 8;
+ Offset += 1;
+ }
+
+ // Read prolog IP offsets (8-bit each, or 16-bit when LARGE)
+ for (unsigned I = 0; I < Info.NumberOfOps; ++I) {
+ if (IsLarge) {
+ if (Offset + 2 > PayloadEnd)
+ return createStringError(
+ "V3 payload truncated reading prolog IP offset %u", I);
+ Info.PrologIpOffsets.push_back(support::endian::read16le(&Data[Offset]));
+ Offset += 2;
+ } else {
+ if (Offset >= PayloadEnd)
+ return createStringError(
+ "V3 payload truncated reading prolog IP offset %u", I);
+ Info.PrologIpOffsets.push_back(Data[Offset++]);
+ }
+ }
+
+ // Read epilog descriptors
+ int32_t PrevResolvedOffset = 0;
+ for (unsigned I = 0; I < Info.NumberOfEpilogs; ++I) {
+ DecodedEpilogV3 Epi;
+ if (Offset >= PayloadEnd)
+ return createStringError(
+ "V3 payload truncated reading epilog %u FlagsAndNumOps", I);
+ uint8_t FlagsAndNumOps = Data[Offset++];
+ Epi.Flags = FlagsAndNumOps & 0x07;
+ Epi.NumberOfOps = (FlagsAndNumOps >> 3) & 0x1F;
+
+ if (Offset + 2 > PayloadEnd)
+ return createStringError(
+ "V3 payload truncated reading epilog %u EpilogOffset", I);
+ int16_t RawOffset =
+ static_cast<int16_t>(support::endian::read16le(&Data[Offset]));
+ Offset += 2;
+
+ // The first epilog's EpilogOffset is absolute (from fragment start or
+ // tail). Subsequent epilogs store a delta from the previous epilog's
+ // resolved position. Accumulate to resolve all to absolute.
+ if (I == 0)
+ Epi.EpilogOffset = RawOffset;
+ else
+ Epi.EpilogOffset = PrevResolvedOffset + RawOffset;
+ PrevResolvedOffset = Epi.EpilogOffset;
+
+ // Inherited descriptors (NumberOfOps == 0) are only 3 bytes:
+ // FlagsAndNumOps(1) + EpilogOffset(2). They have no FirstOp,
+ // IpOffsetOfLastInstruction, or IP offset fields appended; instead,
+ // the previous epilog's Flags bits 0 and 1, FirstOp,
+ // IpOffsetOfLastInstruction, and IP offset array are inherited.
+ //
+ // If this is the first epilog there is no previous descriptor to
+ // inherit from — the record is malformed. We leave the extended fields
+ // zero-initialized so callers can still see the (broken) header and
+ // EpilogOffset; downstream consumers (e.g. the dumpers) surface a
+ // warning when they encounter NumberOfOps == 0 at index 0.
+ if (Epi.NumberOfOps == 0) {
+ if (!Info.Epilogs.empty()) {
+ const DecodedEpilogV3 &Prev = Info.Epilogs.back();
+ // Flags bits 0 (EPILOG_INFO_PARENT_FRAGMENT_TRANSFER) and 1
+ // (EPILOG_INFO_LARGE) are inherited from the previous epilog; any
+ // bits present in this descriptor's own flags byte at those
+ // positions are ignored. Bit 2 (reserved) keeps its raw read value.
+ Epi.Flags = (Epi.Flags & uint8_t{0xFC}) | (Prev.Flags & uint8_t{0x03});
+ Epi.FirstOp = Prev.FirstOp;
+ Epi.IpOffsetOfLastInstruction = Prev.IpOffsetOfLastInstruction;
+ Epi.IpOffsets = Prev.IpOffsets;
+ } else {
+ Epi.FirstOp = 0;
+ Epi.IpOffsetOfLastInstruction = 0;
+ }
+ Info.Epilogs.push_back(std::move(Epi));
+ continue;
+ }
+
+ bool EpiLarge = Epi.isLarge();
+
+ if (Offset + 2 > PayloadEnd)
+ return createStringError("V3 payload truncated reading epilog %u FirstOp",
+ I);
+ Epi.FirstOp = support::endian::read16le(&Data[Offset]);
+ Offset += 2;
+
+ // IpOffsetOfLastInstruction: 8-bit normally, 16-bit when EPILOG_INFO_LARGE
+ if (EpiLarge) {
+ if (Offset + 2 > PayloadEnd)
+ return createStringError(
+ "V3 payload truncated reading epilog %u IpOffsetOfLastInstruction",
+ I);
+ Epi.IpOffsetOfLastInstruction = support::endian::read16le(&Data[Offset]);
+ Offset += 2;
+ } else {
+ if (Offset >= PayloadEnd)
+ return createStringError(
+ "V3 payload truncated reading epilog %u IpOffsetOfLastInstruction",
+ I);
+ Epi.IpOffsetOfLastInstruction = Data[Offset++];
+ }
+
+ // Read epilog IP offsets (8-bit each, or 16-bit when EPILOG_INFO_LARGE)
+ for (unsigned J = 0; J < Epi.NumberOfOps; ++J) {
+ if (EpiLarge) {
+ if (Offset + 2 > PayloadEnd)
+ return createStringError(
+ "V3 payload truncated reading epilog %u IP offset %u", I, J);
+ Epi.IpOffsets.push_back(support::endian::read16le(&Data[Offset]));
+ Offset += 2;
+ } else {
+ if (Offset >= PayloadEnd)
+ return createStringError(
+ "V3 payload truncated reading epilog %u IP offset %u", I, J);
+ Epi.IpOffsets.push_back(Data[Offset++]);
+ }
+ }
+
+ Info.Epilogs.push_back(std::move(Epi));
+ }
+
+ // Identify WOD pool: everything from current offset until the end of
+ // the payload area declared by PayloadWords.
+ if (Offset < PayloadEnd)
+ Info.WODPool = Data.slice(Offset, PayloadEnd - Offset);
+ else
+ Info.WODPool = ArrayRef<uint8_t>();
+
+ // When PayloadWords is odd, the encoder emits 2 trailing zero bytes inside
+ // the payload region as padding before the handler/chain. Report the
+ // aligned offset so consumers locate the next field correctly.
+ Info.PayloadSize = alignTo(PayloadEnd, 4);
+
+ return Info;
+}
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-all-wods.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-all-wods.yaml
new file mode 100644
index 0000000000000..10a235985ef55
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-all-wods.yaml
@@ -0,0 +1,140 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T4: V3 with all WOD types — comprehensive WOD decode coverage.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x30 (SizeOfProlog=48)
+## Byte 2: 0x17 (PayloadWords=23 words -> 46 bytes payload)
+## Byte 3: 0x0C (NumberOfOps=12, NumberOfEpilogs=0)
+## See readobj test for full hex breakdown of WOD pool.
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1050
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0x30
+# CHECK-NEXT: PayloadWords: 23
+# CHECK-NEXT: NumberOfOps: 12
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [12 ops]:
+# CHECK-NEXT: [0] IP +0x0030: WOD_PUSH Reg=RDI
+# CHECK-NEXT: [1] IP +0x002C: WOD_PUSH2 Reg1=RAX, Reg2=RCX
+# CHECK-NEXT: [2] IP +0x002B: WOD_PUSH_CONSECUTIVE_2 Reg=RBX (+RSP)
+# CHECK-NEXT: [3] IP +0x002A: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [4] IP +0x0028: WOD_ALLOC_LARGE Size=0x1000
+# CHECK-NEXT: [5] IP +0x0024: WOD_ALLOC_HUGE Size=0x12345
+# CHECK-NEXT: [6] IP +0x0020: WOD_SET_FPREG Reg=RBP, Offset=0x30
+# CHECK-NEXT: [7] IP +0x001C: WOD_SAVE_NONVOL Reg=R12, Disp=0x40
+# CHECK-NEXT: [8] IP +0x0018: WOD_SAVE_NONVOL_FAR Reg=R13, Disp=0x100000
+# CHECK-NEXT: [9] IP +0x0014: WOD_SAVE_XMM128 Reg=XMM4, Disp=0x100
+# CHECK-NEXT: [10] IP +0x0010: WOD_SAVE_XMM128_FAR Reg=XMM7, Disp=0x20000
+# CHECK-NEXT: [11] IP +0x0000: WOD_PUSH_CANONICAL_FRAME Type=1
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 80
+ SectionData: '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 50
+ SectionData: '0330170C302C2B2A2824201C181410003C20081F38020002014523010000356608006D000010004A10007900000200030100'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000005010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-apx.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-apx.yaml
new file mode 100644
index 0000000000000..6ff6c90978abd
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-apx.yaml
@@ -0,0 +1,123 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T7: V3 with APX registers (R16-R31).
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0x4
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0004: WOD_PUSH Reg=R16
+# CHECK-NEXT: [1] IP +0x0000: WOD_PUSH Reg=R31
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 8
+ SectionData: '03040202040084FC'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-inherit.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-inherit.yaml
new file mode 100644
index 0000000000000..e08dd4b045281
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-inherit.yaml
@@ -0,0 +1,128 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe 2>%t.err | FileCheck %s
+# RUN: FileCheck %s --check-prefix=WARN < %t.err
+
+## T10: V3 malformed — first epilog has NumberOfOps=0 (invalid inheritance).
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0xB
+# CHECK-NEXT: PayloadWords: 5
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] (Flags=0x00, Offset=+0x14) [inherited]:
+# CHECK-EMPTY:
+# WARN: warning: first epilog cannot inherit (NumberOfOps=0)
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 14
+ SectionData: '030B052307020000140038343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-opcode.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-opcode.yaml
new file mode 100644
index 0000000000000..327ebe681910f
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-opcode.yaml
@@ -0,0 +1,124 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe 2>%t.err | FileCheck %s
+# RUN: FileCheck %s --check-prefix=WARN < %t.err
+
+## T9: V3 malformed — invalid WOD opcode.
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0x2
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-EMPTY:
+# WARN: warning: unknown WOD opcode 0x0B at pool offset 0
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 8
+ SectionData: '0302020202000B3C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-push-consecutive-2.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-push-consecutive-2.yaml
new file mode 100644
index 0000000000000..b4c268ed2608c
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-bad-push-consecutive-2.yaml
@@ -0,0 +1,138 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe 2>%t.err | FileCheck %s
+# RUN: FileCheck %s --check-prefix=WARN < %t.err
+
+## T17: V3 malformed — WOD_PUSH_CONSECUTIVE_2 with Register=31.
+## Per the spec, Register must be in [0,30] because the instruction pushes
+## Register and Register+1; Register=31 would put Register+1 (R32) out of
+## bounds. The decoder must reject this.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x02 (SizeOfProlog=2)
+## Byte 2: 0x02 (PayloadWords=2 words -> 4 bytes payload)
+## Byte 3: 0x01 (NumberOfOps=1, NumberOfEpilogs=0)
+## +0: 02 Prolog IP offset[0]: +2
+## +1: FF WOD pool[0]: WOD_PUSH_CONSECUTIVE_2 Register=31 (INVALID)
+## ((31<<3) | 7) = 0xFF
+## +2: 00 Padding
+## +3: 00 Padding
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0x2
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 1
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [1 ops]:
+# CHECK-EMPTY:
+# WARN: warning: WOD_PUSH_CONSECUTIVE_2 Register=31 out of range [0,30] at pool offset 0
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 8
+ SectionData: '0302020102FF0000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-basic.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-basic.yaml
new file mode 100644
index 0000000000000..01b9908a64c4a
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-basic.yaml
@@ -0,0 +1,133 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T1: V3 basic — prolog only (no epilog).
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x06 (SizeOfProlog=6)
+## Byte 2: 0x02 (PayloadWords=2 words -> 4 bytes payload)
+## Byte 3: 0x02 (NumberOfOps=2, NumberOfEpilogs=0)
+## +0: 06 Prolog IP offset[0]: +6
+## +1: 00 Prolog IP offset[1]: +0
+## +2: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=(3+1)*8=32
+## +3: 3C WOD pool[1]: WOD_PUSH RDI (reg=7: (7<<3)|4=0x3C)
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0x6
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0006: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: WOD_PUSH Reg=RDI
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 8
+ SectionData: '030602020600383C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-chain.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-chain.yaml
new file mode 100644
index 0000000000000..e6666ea954cc2
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-chain.yaml
@@ -0,0 +1,127 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T6: V3 with chain info (UNW_FLAG_CHAININFO).
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x4 UNW_ChainInfo
+# CHECK-NEXT: Size of prolog: 0x6
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0006: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Chained:
+# CHECK-NEXT: Start Address: 0x1100
+# CHECK-NEXT: End Address: 0x1200
+# CHECK-NEXT: Unwind Info Address: 0x2100
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: '230602020600383C001100000012000000210000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-distinct-epilog.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-distinct-epilog.yaml
new file mode 100644
index 0000000000000..9760dfdd6e73d
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-distinct-epilog.yaml
@@ -0,0 +1,125 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T8: V3 with distinct (non-mirror) epilog.
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0x7
+# CHECK-NEXT: PayloadWords: 6
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0007: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] (Flags=0x00, Offset=+0x14, IpOfLast=+0x3) [1 ops, FirstOp=0x2]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_PUSH Reg=RBX
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 16
+ SectionData: '03070622070008140002000300383C1C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog-flags.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog-flags.yaml
new file mode 100644
index 0000000000000..70afac2a635e4
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog-flags.yaml
@@ -0,0 +1,128 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T13: V3 with EPILOG_PARENT_FRAGMENT_TRANSFER flag set in epilog.
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1020
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0xB
+# CHECK-NEXT: PayloadWords: 8
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] (Flags=0x01 EPILOG_PARENT_FRAGMENT_TRANSFER, Offset=+0x14, IpOfLast=+0x6) [3 ops, FirstOp=0x0]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: WOD_PUSH Reg=RDI
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 32
+ SectionData: '0000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: '030B082307020019140000000600040538343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000002010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog-large.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog-large.yaml
new file mode 100644
index 0000000000000..55aab583ebb4d
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog-large.yaml
@@ -0,0 +1,140 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T16: V3 with EPILOG_INFO_LARGE flag on an epilog.
+## The epilog uses 16-bit IpOffsetOfLastInstruction and 16-bit IP offsets.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x06 (SizeOfProlog=6)
+## Byte 2: 0x06 (PayloadWords=6 words -> 12 bytes payload)
+## Byte 3: 0x21 (NumberOfOps=1, NumberOfEpilogs=1)
+## Payload:
+## +0: 04 Prolog IP offset[0]: +4
+## +1: 0A Epilog[0] FlagsAndNumOps: NumOps=1, Flags=0x02 (EPILOG_INFO_LARGE)
+## +2,+3: 14 00 Epilog[0] EpilogOffset: +20
+## +4,+5: 00 00 Epilog[0] FirstOp: 0
+## +6,+7: 30 01 Epilog[0] IpOffsetOfLastInstruction: 0x0130 (304, 16-bit)
+## +8,+9: 00 00 Epilog[0] IP offset[0]: 0x0000 (16-bit)
+## +10: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=32
+## +11: 00 Padding
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1200
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0x6
+# CHECK-NEXT: PayloadWords: 6
+# CHECK-NEXT: NumberOfOps: 1
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [1 ops]:
+# CHECK-NEXT: [0] IP +0x0004: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: Epilog [0] (Flags=0x02 EPILOG_INFO_LARGE, Offset=+0x14, IpOfLast=+0x130) [1 ops, FirstOp=0x0]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_ALLOC_SMALL Size=0x20
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 512
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 16
+ SectionData: '03060621040A14000000300100003800'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000000012000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog.yaml
new file mode 100644
index 0000000000000..6f54408ddcc3c
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-epilog.yaml
@@ -0,0 +1,148 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T2: V3 with a single epilog that mirrors the prolog.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x0B (SizeOfProlog=11)
+## Byte 2: 0x08 (PayloadWords=8 words -> 16 bytes payload)
+## Byte 3: 0x23 (NumberOfOps=3, NumberOfEpilogs=1: (1<<5)|3)
+## +0: 07 Prolog IP offset[0]: +7 (SUB RSP,0x20)
+## +1: 02 Prolog IP offset[1]: +2 (PUSH RSI)
+## +2: 00 Prolog IP offset[2]: +0 (PUSH RDI)
+## +3: 18 Epilog[0] FlagsAndNumOps: flags=0, nops=3 -> (3<<3)|0=0x18
+## +4: 14 00 Epilog[0] EpilogOffset: +20 (0x0014 LE)
+## +6: 00 00 Epilog[0] FirstOp: 0
+## +8: 06 Epilog[0] IpOffsetOfLastInstruction: 6
+## +9: 00 Epilog[0] IP offset[0]: +0 (ADD RSP,0x20)
+## +10: 04 Epilog[0] IP offset[1]: +4 (POP RSI)
+## +11: 05 Epilog[0] IP offset[2]: +5 (POP RDI)
+## +12: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=(3+1)*8=32
+## +13: 34 WOD pool[1]: WOD_PUSH RSI (reg=6: (6<<3)|4=0x34)
+## +14: 3C WOD pool[2]: WOD_PUSH RDI (reg=7: (7<<3)|4=0x3C)
+## +15: 00 Padding
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1020
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0xB
+# CHECK-NEXT: PayloadWords: 8
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] (Flags=0x00, Offset=+0x14, IpOfLast=+0x6) [3 ops, FirstOp=0x0]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: WOD_PUSH Reg=RDI
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 32
+ SectionData: '0000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: '030B082307020018140000000600040538343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000002010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-handler-odd-payload.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-handler-odd-payload.yaml
new file mode 100644
index 0000000000000..cf4cb346cefb4
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-handler-odd-payload.yaml
@@ -0,0 +1,144 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+# T18: V3 with EHANDLER and odd PayloadWords.
+#
+# When PayloadWords is odd, the payload region occupies 4 + odd*2 bytes,
+# which is not a multiple of 4. The V3 spec locates the handler RVA at
+# handler_offset = ALIGN_UP(sizeof(UNWIND_INFO_V3) + PayloadWords * 2, 4)
+# so the encoder emits 2 trailing zero padding bytes between the payload
+# and the handler. The decoder must therefore round up to the next 4-byte
+# boundary when looking for the handler.
+#
+# .xdata hex breakdown (PayloadWords=3, payload=6 bytes, padded to 8):
+# Byte 0: 0x0B (Version=3, Flags=1 (UNW_FLAG_EHANDLER))
+# Byte 1: 0x07 (SizeOfProlog=7)
+# Byte 2: 0x03 (PayloadWords=3 words -> 6 bytes payload)
+# Byte 3: 0x02 (NumberOfOps=2, NumberOfEpilogs=0)
+# +0: 07 Prolog IP offset[0]: +7
+# +1: 00 Prolog IP offset[1]: +0
+# +2: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=32
+# +3: 34 WOD pool[1]: WOD_PUSH RSI
+# +4: 00 00 Unused pool tail (only 2 WODs referenced by NumberOfOps=2)
+# +6: 00 00 Padding: handler_offset = ALIGN_UP(4+6, 4) = 12
+# Handler RVA at file offset 0xC: 00 10 00 00 = 0x1000
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x1 UNW_ExceptionHandler
+# CHECK-NEXT: Size of prolog: 0x7
+# CHECK-NEXT: PayloadWords: 3
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0007: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: WOD_PUSH Reg=RSI
+# CHECK-NEXT: Handler: 0x1000
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 16
+ SectionData: '0B070302070038340000000000100000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-handler.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-handler.yaml
new file mode 100644
index 0000000000000..fc6eae045b9f9
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-handler.yaml
@@ -0,0 +1,124 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T5: V3 with exception handler (UNW_FLAG_EHANDLER).
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x1 UNW_ExceptionHandler
+# CHECK-NEXT: Size of prolog: 0x6
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0006: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Handler: 0x1000
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 12
+ SectionData: '0B0602020600383C00100000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-large.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-large.yaml
new file mode 100644
index 0000000000000..6bc5d8dfd73f4
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-large.yaml
@@ -0,0 +1,139 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T15: V3 with UNW_FLAG_LARGE set.
+## SizeOfProlog is a 16-bit composite (low byte in the header, high byte in
+## the UNWIND_INFO_LARGE_V3 extension which is part of the payload), and
+## prolog IP offsets are 16-bit.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x43 (Version=3, Flags=0x08=UNW_FlagLarge)
+## Byte 1: 0x20 (SizeOfProlog low byte: 0x20)
+## Byte 2: 0x04 (PayloadWords=4 words -> 8 bytes payload, includes LARGE byte)
+## Byte 3: 0x02 (NumberOfOps=2, NumberOfEpilogs=0)
+## Payload (8 bytes):
+## +0: 01 UNWIND_INFO_LARGE_V3: SizeOfPrologHighByte=1 (combined=0x0120=288)
+## +1,+2: 10 01 Prolog IP offset[0]: 0x0110 (16-bit LE)
+## +3,+4: 00 00 Prolog IP offset[1]: 0x0000 (16-bit LE)
+## +5: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=(3+1)*8=32
+## +6: 3C WOD pool[1]: WOD_PUSH RDI (reg=7: (7<<3)|4=0x3C)
+## +7: 00 Padding to PayloadWords*2 boundary
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1200
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x8 UNW_FlagLarge
+# CHECK-NEXT: Size of prolog: 0x120
+# CHECK-NEXT: PayloadWords: 4
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0110: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: WOD_PUSH Reg=RDI
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 512
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 12
+ SectionData: '432004020110010000383C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000000012000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-multi-epilog.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-multi-epilog.yaml
new file mode 100644
index 0000000000000..5cb299d0bc332
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-multi-epilog.yaml
@@ -0,0 +1,164 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+## --unwind-show-wod-pool appends a raw dump of the WOD pool with byte
+## offsets, useful for seeing how the pool is shared between prolog/epilogs.
+# RUN: llvm-objdump --unwind-info --unwind-show-wod-pool %t.exe \
+# RUN: | FileCheck %s --check-prefixes=CHECK,POOL
+
+## T3: V3 with multiple epilogs including inheritance.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x0B (SizeOfProlog=11)
+## Byte 2: 0x0D (PayloadWords=13 words -> 26 bytes payload)
+## Byte 3: 0x63 (NumberOfOps=3, NumberOfEpilogs=3: (3<<5)|3)
+## Payload (26 bytes):
+## +0: 07 Prolog IP offset[0]: +7
+## +1: 02 Prolog IP offset[1]: +2
+## +2: 00 Prolog IP offset[2]: +0
+## +3: 18 Epilog[0] FlagsAndNumOps: flags=0, nops=3
+## +4: 14 00 Epilog[0] EpilogOffset: +20
+## +6: 00 00 Epilog[0] FirstOp: 0
+## +8: 06 Epilog[0] IpOffsetOfLastInstruction: 6
+## +9: 00 04 05 Epilog[0] IP offsets
+## +12: 00 Epilog[1] FlagsAndNumOps: nops=0 (inherited, 3 bytes only)
+## +13: 0A 00 Epilog[1] EpilogOffset: +10 (delta from Epilog[0]; resolves to +30)
+## +15: 10 Epilog[2] FlagsAndNumOps: nops=2
+## +16: 05 00 Epilog[2] EpilogOffset: +5 (delta from Epilog[1]; resolves to +35)
+## +18: 01 00 Epilog[2] FirstOp: 1
+## +20: 03 Epilog[2] IpOffsetOfLastInstruction: 3
+## +21: 00 01 Epilog[2] IP offsets
+## +23: 38 34 3C WOD pool: ALLOC_SMALL(32), PUSH RSI, PUSH RDI
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1040
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0xB
+# CHECK-NEXT: PayloadWords: 13
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 3
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] (Flags=0x00, Offset=+0x14, IpOfLast=+0x6) [3 ops, FirstOp=0x0]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [1] (Flags=0x00, Offset=+0x1E) [inherited]:
+# CHECK-NEXT: (inherits from previous epilog: FirstOp=0x0, IpOfLast=+0x6, 3 ops)
+# CHECK-NEXT: Epilog [2] (Flags=0x00, Offset=+0x23, IpOfLast=+0x3) [2 ops, FirstOp=0x1]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [1] IP +0x0001: WOD_PUSH Reg=RDI
+# POOL-NEXT: WOD pool [3 bytes]:
+# POOL-NEXT: +0x0000: WOD_ALLOC_SMALL Size=0x20
+# POOL-NEXT: +0x0001: WOD_PUSH Reg=RSI
+# POOL-NEXT: +0x0002: WOD_PUSH Reg=RDI
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 64
+ SectionData: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 30
+ SectionData: '030B0D63070200181400000006000405000A00100500010003000138343C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000004010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-neg-offset.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-neg-offset.yaml
new file mode 100644
index 0000000000000..456eb560b2360
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-neg-offset.yaml
@@ -0,0 +1,128 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+## T12: V3 with negative EpilogOffset (offset from fragment tail).
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1020
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0xB
+# CHECK-NEXT: PayloadWords: 8
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] (Flags=0x00, Offset=-0x14, IpOfLast=+0x6) [3 ops, FirstOp=0x0]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: WOD_PUSH Reg=RDI
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 32
+ SectionData: '0000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: '030B082307020018ECFF00000600040538343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000002010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-pool-overflow.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-pool-overflow.yaml
new file mode 100644
index 0000000000000..3a6663cabed7f
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-pool-overflow.yaml
@@ -0,0 +1,127 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe 2>%t.err | FileCheck %s
+# RUN: FileCheck %s --check-prefix=WARN < %t.err
+
+## T11: V3 malformed — WOD pool overflow.
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: 0x1000
+# CHECK-NEXT: End Address: 0x1010
+# CHECK-NEXT: Unwind Info Address: 0x2000
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0x2
+# CHECK-NEXT: PayloadWords: 5
+# CHECK-NEXT: NumberOfOps: 1
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [1 ops]:
+# CHECK-NEXT: [0] IP +0x0002: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] (Flags=0x00, Offset=+0xA, IpOfLast=+0x5) [2 ops, FirstOp=0x0]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-EMPTY:
+# WARN: warning: WOD pool overflow at offset 1
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 14
+ SectionData: '0302052102100A0000000500033C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-reloc.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-reloc.yaml
new file mode 100644
index 0000000000000..0d09a5e90f1d1
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv3-reloc.yaml
@@ -0,0 +1,102 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: llvm-objdump --unwind-info %t.obj | FileCheck %s
+
+## T14: V3 unwind info in a relocatable object file (.obj).
+## This tests the relocation-based resolution path where .pdata fields
+## are zero and relocations point to the actual symbols.
+##
+## Uses the same V3 xdata as T2 (single epilog mirroring prolog):
+## .xdata = 030B0823070200181400000006000405 38343C00
+
+# CHECK-LABEL: Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT: Function Table:
+# CHECK-NEXT: Start Address: func
+# CHECK-NEXT: End Address: func + 0x0020
+# CHECK-NEXT: Unwind Info Address: .xdata
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags: 0x0
+# CHECK-NEXT: Size of prolog: 0xB
+# CHECK-NEXT: PayloadWords: 8
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: WOD_PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] (Flags=0x00, Offset=+0x14, IpOfLast=+0x6) [3 ops, FirstOp=0x0]:
+# CHECK-NEXT: [0] IP +0x0000: WOD_ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: WOD_PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: WOD_PUSH Reg=RDI
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '030B082307020018140000000600040538343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '000000002000000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: func
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: func
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: .xdata
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ 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
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 20
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-all-wods.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-all-wods.yaml
new file mode 100644
index 0000000000000..652efc48d9ff0
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-all-wods.yaml
@@ -0,0 +1,173 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T4: V3 with all WOD types — comprehensive WOD decode coverage.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x30 (SizeOfProlog=48)
+## Byte 2: 0x17 (PayloadWords=23 words -> 46 bytes payload)
+## Byte 3: 0x0C (NumberOfOps=12, NumberOfEpilogs=0)
+## Prolog IP offsets (12 bytes):
+## +0: 30 IP[0]: +0x30
+## +1: 2C IP[1]: +0x2C
+## +2: 2B IP[2]: +0x2B
+## +3: 2A IP[3]: +0x2A
+## +4: 28 IP[4]: +0x28
+## +5: 24 IP[5]: +0x24
+## +6: 20 IP[6]: +0x20
+## +7: 1C IP[7]: +0x1C
+## +8: 18 IP[8]: +0x18
+## +9: 14 IP[9]: +0x14
+## +10: 10 IP[10]: +0x10
+## +11: 00 IP[11]: +0x00
+## WOD pool (34 bytes):
+## +12: 3C WOD_PUSH RDI (reg=7: (7<<3)|4=0x3C)
+## +13: 20 08 WOD_PUSH2 Reg1=RAX(0), Reg2=RCX(1)
+## +15: 1F WOD_PUSH_CONSECUTIVE_2 Reg=RBX(3): (3<<3)|7
+## +16: 38 WOD_ALLOC_SMALL Size=(3+1)*8=32
+## +17: 02 00 02 WOD_ALLOC_LARGE Size=0x200*8=4096
+## +20: 01 45 23 01 00 WOD_ALLOC_HUGE Size=0x12345=74565
+## +25: 00 35 WOD_SET_FPREG Reg=RBP(5), Offset=3*16=48
+## +27: 66 08 00 WOD_SAVE_NONVOL Reg=R12(12), Disp=8*8=64
+## +30: 6D 00 00 10 00 WOD_SAVE_NONVOL_FAR Reg=R13(13), Disp=0x100000
+## +35: 4A 10 00 WOD_SAVE_XMM128 Reg=XMM4, Disp=16*16=256
+## +38: 79 00 00 02 00 WOD_SAVE_XMM128_FAR Reg=XMM7, Disp=0x20000
+## +43: 03 01 WOD_PUSH_CANONICAL_FRAME Type=1
+## +45: 00 Padding
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0x30
+# CHECK-NEXT: PayloadWords: 23
+# CHECK-NEXT: NumberOfOps: 12
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [12 ops]:
+# CHECK-NEXT: [0] IP +0x0030: PUSH Reg=RDI
+# CHECK-NEXT: [1] IP +0x002C: PUSH2 Reg1=RAX, Reg2=RCX
+# CHECK-NEXT: [2] IP +0x002B: PUSH_CONSECUTIVE_2 Reg=RBX (+RSP)
+# CHECK-NEXT: [3] IP +0x002A: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [4] IP +0x0028: ALLOC_LARGE Size=0x1000
+# CHECK-NEXT: [5] IP +0x0024: ALLOC_HUGE Size=0x12345
+# CHECK-NEXT: [6] IP +0x0020: SET_FPREG Reg=RBP, Offset=0x30
+# CHECK-NEXT: [7] IP +0x001C: SAVE_NONVOL Reg=R12, Disp=0x40
+# CHECK-NEXT: [8] IP +0x0018: SAVE_NONVOL_FAR Reg=R13, Disp=0x100000
+# CHECK-NEXT: [9] IP +0x0014: SAVE_XMM128 Reg=XMM4, Disp=0x100
+# CHECK-NEXT: [10] IP +0x0010: SAVE_XMM128_FAR Reg=XMM7, Disp=0x20000
+# CHECK-NEXT: [11] IP +0x0000: PUSH_CANONICAL_FRAME Type=1
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 80
+ SectionData: '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 50
+ SectionData: '0330170C302C2B2A2824201C181410003C20081F38020002014523010000356608006D000010004A10007900000200030100'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000005010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-apx.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-apx.yaml
new file mode 100644
index 0000000000000..5379693a7a332
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-apx.yaml
@@ -0,0 +1,140 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T7: V3 with APX registers (R16-R31).
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x04 (SizeOfProlog=4)
+## Byte 2: 0x02 (PayloadWords=2 words -> 4 bytes payload)
+## Byte 3: 0x02 (NumberOfOps=2, NumberOfEpilogs=0)
+## +0: 04 Prolog IP offset[0]: +4
+## +1: 00 Prolog IP offset[1]: +0
+## +2: 84 WOD pool[0]: WOD_PUSH R16 (reg=0x84>>3=16: (16<<3)|4=0x84)
+## +3: FC WOD pool[1]: WOD_PUSH R31 (reg=0xFC>>3=31: (31<<3)|4=0xFC)
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0x4
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0004: PUSH Reg=R16
+# CHECK-NEXT: [1] IP +0x0000: PUSH Reg=R31
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 8
+ SectionData: '03040202040084FC'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-bad-inherit.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-bad-inherit.yaml
new file mode 100644
index 0000000000000..fb840cf8d3f10
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-bad-inherit.yaml
@@ -0,0 +1,155 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe 2>%t.err | FileCheck %s
+# RUN: FileCheck %s --check-prefix=WARN < %t.err
+
+## T10: V3 malformed — first epilog has NumberOfOps=0 (invalid inheritance).
+## The first epilog descriptor cannot inherit because there is no previous
+## descriptor. The tool should warn.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x0B (SizeOfProlog=11)
+## Byte 2: 0x05 (PayloadWords=5 words -> 10 bytes payload)
+## Byte 3: 0x23 (NumberOfOps=3, NumberOfEpilogs=1: (1<<5)|3)
+## Payload (10 bytes):
+## +0: 07 Prolog IP offset[0]: +7
+## +1: 02 Prolog IP offset[1]: +2
+## +2: 00 Prolog IP offset[2]: +0
+## +3: 00 Epilog[0] FlagsAndNumOps: flags=0, nops=0 (INVALID, 3 bytes only)
+## +4: 14 00 Epilog[0] EpilogOffset: +20
+## +6: 38 34 3C WOD pool: ALLOC_SMALL(32), PUSH RSI, PUSH RDI
+## +9: 00 Padding
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0xB
+# CHECK-NEXT: PayloadWords: 5
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0x14
+# CHECK-NEXT: NumberOfOps: 0
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# WARN: warning: first epilog cannot inherit (NumberOfOps=0)
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 14
+ SectionData: '030B052307020000140038343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-bad-opcode.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-bad-opcode.yaml
new file mode 100644
index 0000000000000..a220e08af2aba
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-bad-opcode.yaml
@@ -0,0 +1,143 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe 2>%t.err | FileCheck %s
+# RUN: FileCheck %s --check-prefix=WARN < %t.err
+
+## T9: V3 malformed — invalid WOD opcode in pool.
+## The WOD pool contains 0x0B which is not a valid opcode.
+## The tool should warn and continue.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x02 (SizeOfProlog=2)
+## Byte 2: 0x02 (PayloadWords=2 words -> 4 bytes payload)
+## Byte 3: 0x02 (NumberOfOps=2, NumberOfEpilogs=0)
+## +0: 02 Prolog IP offset[0]: +2
+## +1: 00 Prolog IP offset[1]: +0
+## +2: 0B WOD pool[0]: INVALID opcode (0x0B)
+## +3: 3C WOD pool[1]: WOD_PUSH RDI (would be valid if reached)
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0x2
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+# WARN: warning: unknown WOD opcode 0x0B at pool offset 0
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 8
+ SectionData: '0302020202000B3C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-basic.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-basic.yaml
new file mode 100644
index 0000000000000..20631cf3b1fd3
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-basic.yaml
@@ -0,0 +1,140 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T1: V3 basic — prolog only (no epilog).
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x06 (SizeOfProlog=6)
+## Byte 2: 0x02 (PayloadWords=2 words -> 4 bytes payload)
+## Byte 3: 0x02 (NumberOfOps=2, NumberOfEpilogs=0)
+## +0: 06 Prolog IP offset[0]: +6
+## +1: 00 Prolog IP offset[1]: +0
+## +2: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=(3+1)*8=32
+## +3: 3C WOD pool[1]: WOD_PUSH RDI (reg=7: (7<<3)|4=0x3C)
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0x6
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0006: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 8
+ SectionData: '030602020600383C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-chain.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-chain.yaml
new file mode 100644
index 0000000000000..b977d3c7bcc74
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-chain.yaml
@@ -0,0 +1,150 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T6: V3 with chain info (UNW_FLAG_CHAININFO).
+##
+## .xdata hex breakdown:
+## Byte 0: 0x23 (Version=3, Flags=4 (UNW_FLAG_CHAININFO))
+## Byte 1: 0x06 (SizeOfProlog=6)
+## Byte 2: 0x02 (PayloadWords=2 words -> 4 bytes payload)
+## Byte 3: 0x02 (NumberOfOps=2, NumberOfEpilogs=0)
+## +0: 06 Prolog IP offset[0]: +6
+## +1: 00 Prolog IP offset[1]: +0
+## +2: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=32
+## +3: 3C WOD pool[1]: WOD_PUSH RDI
+## Chain RUNTIME_FUNCTION at offset 8 (4 + ALIGN_UP(4, 4)):
+## +8: 00 11 00 00 BeginAddress: 0x1100
+## +12: 00 12 00 00 EndAddress: 0x1200
+## +16: 00 21 00 00 UnwindInfoAddress: 0x2100
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x4)
+# CHECK-NEXT: ChainInfo (0x4)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0x6
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0006: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Chained {
+# CHECK-NEXT: StartAddress: (0x1100)
+# CHECK-NEXT: EndAddress: (0x1200)
+# CHECK-NEXT: UnwindInfoAddress: (0x2100)
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: '230602020600383C001100000012000000210000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-distinct-epilog.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-distinct-epilog.yaml
new file mode 100644
index 0000000000000..8cfbecda0348d
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-distinct-epilog.yaml
@@ -0,0 +1,157 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T8: V3 with distinct (non-mirror) epilog. The epilog has
diff erent WODs
+## than the prolog, with FirstOp pointing into a separate region of the pool.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x07 (SizeOfProlog=7)
+## Byte 2: 0x06 (PayloadWords=6 words -> 12 bytes payload)
+## Byte 3: 0x22 (NumberOfOps=2, NumberOfEpilogs=1: (1<<5)|2)
+## Payload (12 bytes):
+## +0: 07 Prolog IP offset[0]: +7
+## +1: 00 Prolog IP offset[1]: +0
+## +2: 08 Epilog[0] FlagsAndNumOps: flags=0, nops=1 -> (1<<3)|0
+## +3: 14 00 Epilog[0] EpilogOffset: +20
+## +5: 02 00 Epilog[0] FirstOp: 2 (byte offset into WOD pool)
+## +7: 03 Epilog[0] IpOffsetOfLastInstruction: 3
+## +8: 00 Epilog[0] IP offset[0]: +0
+## +9: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=32
+## +10: 3C WOD pool[1]: WOD_PUSH RDI
+## +11: 1C WOD pool[2]: WOD_PUSH RBX (reg=0x1C>>3=3)
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0x7
+# CHECK-NEXT: PayloadWords: 6
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0007: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0x14
+# CHECK-NEXT: NumberOfOps: 1
+# CHECK-NEXT: FirstOp: 0x2
+# CHECK-NEXT: IpOffsetOfLastInstruction: 0x3
+# CHECK-NEXT: [0] IP +0x0000: PUSH Reg=RBX
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 16
+ SectionData: '03070622070008140002000300383C1C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-epilog-flags.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-epilog-flags.yaml
new file mode 100644
index 0000000000000..366cc1c3dcb23
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-epilog-flags.yaml
@@ -0,0 +1,158 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T13: V3 with EPILOG_PARENT_FRAGMENT_TRANSFER flag set in epilog.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x0B (SizeOfProlog=11)
+## Byte 2: 0x08 (PayloadWords=8 words -> 16 bytes payload)
+## Byte 3: 0x23 (NumberOfOps=3, NumberOfEpilogs=1: (1<<5)|3)
+## Payload (16 bytes):
+## +0: 07 Prolog IP[0]: +7
+## +1: 02 Prolog IP[1]: +2
+## +2: 00 Prolog IP[2]: +0
+## +3: 19 Epilog[0] FlagsAndNumOps: flags=1 (PARENT_FRAG_XFER), nops=3 -> (3<<3)|1
+## +4,+5: 14 00 Epilog[0] EpilogOffset: +20
+## +6,+7: 00 00 Epilog[0] FirstOp: 0
+## +8: 06 Epilog[0] IpOfLast: 6
+## +9: 00 Epilog[0] IP[0]
+## +10: 04 Epilog[0] IP[1]
+## +11: 05 Epilog[0] IP[2]
+## +12: 38 WOD pool[0]: ALLOC_SMALL 32
+## +13: 34 WOD pool[1]: PUSH RSI
+## +14: 3C WOD pool[2]: PUSH RDI
+## +15: 00 Padding
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0xB
+# CHECK-NEXT: PayloadWords: 8
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] {
+# CHECK-NEXT: Flags [ (0x1)
+# CHECK-NEXT: ParentFragmentTransfer (0x1)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0x14
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: FirstOp: 0x0
+# CHECK-NEXT: IpOffsetOfLastInstruction: 0x6
+# CHECK-NEXT: [0] IP +0x0000: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: PUSH Reg=RDI
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 32
+ SectionData: '0000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: '030b082307020019140000000600040538343c00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000002010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-epilog.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-epilog.yaml
new file mode 100644
index 0000000000000..2d2a3f20859cd
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-epilog.yaml
@@ -0,0 +1,163 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T2: V3 with a single epilog that mirrors the prolog.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x0B (SizeOfProlog=11)
+## Byte 2: 0x08 (PayloadWords=8 words -> 16 bytes payload)
+## Byte 3: 0x23 (NumberOfOps=3, NumberOfEpilogs=1: (1<<5)|3)
+## Payload:
+## +0: 07 Prolog IP offset[0]: +7 (SUB RSP,0x20)
+## +1: 02 Prolog IP offset[1]: +2 (PUSH RSI)
+## +2: 00 Prolog IP offset[2]: +0 (PUSH RDI)
+## +3: 18 Epilog[0] FlagsAndNumOps: flags=0, nops=3 -> (3<<3)|0=0x18
+## +4: 14 00 Epilog[0] EpilogOffset: +20 (0x0014 LE)
+## +6: 00 00 Epilog[0] FirstOp: 0
+## +8: 06 Epilog[0] IpOffsetOfLastInstruction: 6
+## +9: 00 Epilog[0] IP offset[0]: +0 (ADD RSP,0x20)
+## +10: 04 Epilog[0] IP offset[1]: +4 (POP RSI)
+## +11: 05 Epilog[0] IP offset[2]: +5 (POP RDI)
+## +12: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=(3+1)*8=32
+## +13: 34 WOD pool[1]: WOD_PUSH RSI (reg=6: (6<<3)|4=0x34)
+## +14: 3C WOD pool[2]: WOD_PUSH RDI (reg=7: (7<<3)|4=0x3C)
+## +15: 00 Padding
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0xB
+# CHECK-NEXT: PayloadWords: 8
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0x14
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: FirstOp: 0x0
+# CHECK-NEXT: IpOffsetOfLastInstruction: 0x6
+# CHECK-NEXT: [0] IP +0x0000: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: PUSH Reg=RDI
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 32
+ SectionData: '0000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: '030B082307020018140000000600040538343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000002010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-handler.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-handler.yaml
new file mode 100644
index 0000000000000..e6b10e8381a9d
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-handler.yaml
@@ -0,0 +1,144 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T5: V3 with exception handler (UNW_FLAG_EHANDLER).
+##
+## .xdata hex breakdown:
+## Byte 0: 0x0B (Version=3, Flags=1 (UNW_FLAG_EHANDLER))
+## Byte 1: 0x06 (SizeOfProlog=6)
+## Byte 2: 0x02 (PayloadWords=2 words -> 4 bytes payload)
+## Byte 3: 0x02 (NumberOfOps=2, NumberOfEpilogs=0)
+## +0: 06 Prolog IP offset[0]: +6
+## +1: 00 Prolog IP offset[1]: +0
+## +2: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=(3+1)*8=32
+## +3: 3C WOD pool[1]: WOD_PUSH RDI
+## Handler RVA at offset 8 (4 + ALIGN_UP(4, 4)):
+## +8: 00 10 00 00 Handler RVA = 0x1000
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x1)
+# CHECK-NEXT: ExceptionHandler (0x1)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0x6
+# CHECK-NEXT: PayloadWords: 2
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: NumberOfEpilogs: 0
+# CHECK-NEXT: Prolog [2 ops]:
+# CHECK-NEXT: [0] IP +0x0006: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Handler: .text (0x1000)
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 12
+ SectionData: '0B0602020600383C00100000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-multi-epilog.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-multi-epilog.yaml
new file mode 100644
index 0000000000000..d45449b3d5e68
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-multi-epilog.yaml
@@ -0,0 +1,191 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+## --unwind-show-wod-pool appends a raw dump of the WOD pool with byte
+## offsets, useful for seeing how the pool is shared between prolog/epilogs.
+# RUN: llvm-readobj --unwind --unwind-show-wod-pool %t.exe \
+# RUN: | FileCheck %s --check-prefixes=CHECK,POOL
+
+## T3: V3 with multiple epilogs including inheritance.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x0B (SizeOfProlog=11)
+## Byte 2: 0x0D (PayloadWords=13 words -> 26 bytes payload)
+## Byte 3: 0x63 (NumberOfOps=3, NumberOfEpilogs=3: (3<<5)|3=0x63)
+## Payload (26 bytes):
+## +0: 07 Prolog IP offset[0]: +7
+## +1: 02 Prolog IP offset[1]: +2
+## +2: 00 Prolog IP offset[2]: +0
+## +3: 18 Epilog[0] FlagsAndNumOps: flags=0, nops=3 -> (3<<3)|0
+## +4: 14 00 Epilog[0] EpilogOffset: +20
+## +6: 00 00 Epilog[0] FirstOp: 0
+## +8: 06 Epilog[0] IpOffsetOfLastInstruction: 6
+## +9: 00 04 05 Epilog[0] IP offsets
+## +12: 00 Epilog[1] FlagsAndNumOps: nops=0 (inherited, 3 bytes only)
+## +13: 0A 00 Epilog[1] EpilogOffset: +10 (delta from Epilog[0]; resolves to +30)
+## +15: 10 Epilog[2] FlagsAndNumOps: nops=2
+## +16: 05 00 Epilog[2] EpilogOffset: +5 (delta from Epilog[1]; resolves to +35)
+## +18: 01 00 Epilog[2] FirstOp: 1 (starts at WOD pool byte 1)
+## +20: 03 Epilog[2] IpOffsetOfLastInstruction: 3
+## +21: 00 01 Epilog[2] IP offsets
+## +23: 38 34 3C WOD pool: ALLOC_SMALL(32), PUSH RSI, PUSH RDI
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0xB
+# CHECK-NEXT: PayloadWords: 13
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 3
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0x14
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: FirstOp: 0x0
+# CHECK-NEXT: IpOffsetOfLastInstruction: 0x6
+# CHECK-NEXT: [0] IP +0x0000: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: PUSH Reg=RDI
+# CHECK-NEXT: }
+# CHECK-NEXT: Epilog [1] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0x1E
+# CHECK-NEXT: NumberOfOps: 0
+# CHECK-NEXT: (inherits from previous epilog: FirstOp=0x0, IpOffsetOfLastInstruction=0x6, 3 ops)
+# CHECK-NEXT: }
+# CHECK-NEXT: Epilog [2] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0x23
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: FirstOp: 0x1
+# CHECK-NEXT: IpOffsetOfLastInstruction: 0x3
+# CHECK-NEXT: [0] IP +0x0000: PUSH Reg=RSI
+# CHECK-NEXT: [1] IP +0x0001: PUSH Reg=RDI
+# CHECK-NEXT: }
+# POOL-NEXT: WODPool [3 bytes] [
+# POOL-NEXT: +0x0000: ALLOC_SMALL Size=0x20
+# POOL-NEXT: +0x0001: PUSH Reg=RSI
+# POOL-NEXT: +0x0002: PUSH Reg=RDI
+# POOL-NEXT: ]
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 64
+ SectionData: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 30
+ SectionData: '030B0D63070200181400000006000405000A00100500010003000138343C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000004010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-neg-offset.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-neg-offset.yaml
new file mode 100644
index 0000000000000..106c9fcf30ba5
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-neg-offset.yaml
@@ -0,0 +1,163 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe | FileCheck %s
+
+## T12: V3 with negative EpilogOffset (offset from fragment tail).
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x0B (SizeOfProlog=11)
+## Byte 2: 0x08 (PayloadWords=8 words -> 16 bytes payload)
+## Byte 3: 0x23 (NumberOfOps=3, NumberOfEpilogs=1: (1<<5)|3)
+## Payload (16 bytes):
+## +0: 07 Prolog IP offset[0]: +7
+## +1: 02 Prolog IP offset[1]: +2
+## +2: 00 Prolog IP offset[2]: +0
+## +3: 18 Epilog[0] FlagsAndNumOps: flags=0, nops=3 -> (3<<3)|0
+## +4: EC FF Epilog[0] EpilogOffset: -20 (0xFFEC as signed int16)
+## +6: 00 00 Epilog[0] FirstOp: 0
+## +8: 06 Epilog[0] IpOffsetOfLastInstruction: 6
+## +9: 00 Epilog[0] IP offset[0]: +0
+## +10: 04 Epilog[0] IP offset[1]: +4
+## +11: 05 Epilog[0] IP offset[2]: +5
+## +12: 38 WOD pool[0]: WOD_ALLOC_SMALL Size=32
+## +13: 34 WOD pool[1]: WOD_PUSH RSI
+## +14: 3C WOD pool[2]: WOD_PUSH RDI
+## +15: 00 Padding
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0xB
+# CHECK-NEXT: PayloadWords: 8
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: -0x14
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: FirstOp: 0x0
+# CHECK-NEXT: IpOffsetOfLastInstruction: 0x6
+# CHECK-NEXT: [0] IP +0x0000: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: PUSH Reg=RDI
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 32
+ SectionData: '0000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 20
+ SectionData: '030B082307020018ECFF00000600040538343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000002010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-pool-overflow.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-pool-overflow.yaml
new file mode 100644
index 0000000000000..0372dc9368182
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-pool-overflow.yaml
@@ -0,0 +1,155 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-readobj --unwind %t.exe 2>%t.err | FileCheck %s
+# RUN: FileCheck %s --check-prefix=WARN < %t.err
+
+## T11: V3 malformed — WOD pool overflow.
+## Epilog's FirstOp + WODs consumed extends past pool boundary.
+##
+## .xdata hex breakdown:
+## Byte 0: 0x03 (Version=3, Flags=0)
+## Byte 1: 0x02 (SizeOfProlog=2)
+## Byte 2: 0x05 (PayloadWords=5 words -> 10 bytes payload)
+## Byte 3: 0x21 (NumberOfOps=1, NumberOfEpilogs=1: (1<<5)|1)
+## Payload (10 bytes):
+## +0: 02 Prolog IP offset[0]: +2
+## +1: 10 Epilog[0] FlagsAndNumOps: flags=0, nops=2 -> (2<<3)|0
+## +2: 0A 00 Epilog[0] EpilogOffset: +10
+## +4: 00 00 Epilog[0] FirstOp: 0
+## +6: 05 Epilog[0] IpOffsetOfLastInstruction: 5
+## +7: 00 03 Epilog[0] IP offsets
+## +9: 3C WOD pool[0]: WOD_PUSH RDI (only 1 byte, but epilog needs 2)
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0x2
+# CHECK-NEXT: PayloadWords: 5
+# CHECK-NEXT: NumberOfOps: 1
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [1 ops]:
+# CHECK-NEXT: [0] IP +0x0002: PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0xA
+# CHECK-NEXT: NumberOfOps: 2
+# CHECK-NEXT: FirstOp: 0x0
+# CHECK-NEXT: IpOffsetOfLastInstruction: 0x5
+# CHECK-NEXT: [0] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# WARN: warning: WOD pool overflow at offset 1
+
+--- !COFF
+OptionalHeader:
+ AddressOfEntryPoint: 4096
+ ImageBase: 0
+ SectionAlignment: 4096
+ FileAlignment: 512
+ MajorOperatingSystemVersion: 6
+ MinorOperatingSystemVersion: 0
+ MajorImageVersion: 0
+ MinorImageVersion: 0
+ MajorSubsystemVersion: 6
+ MinorSubsystemVersion: 0
+ Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ]
+ SizeOfStackReserve: 1048576
+ SizeOfStackCommit: 4096
+ SizeOfHeapReserve: 1048576
+ SizeOfHeapCommit: 4096
+ ExportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ImportTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ResourceTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ExceptionTable:
+ RelativeVirtualAddress: 12288
+ Size: 12
+ CertificateTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BaseRelocationTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Debug:
+ RelativeVirtualAddress: 0
+ Size: 0
+ Architecture:
+ RelativeVirtualAddress: 0
+ Size: 0
+ GlobalPtr:
+ RelativeVirtualAddress: 0
+ Size: 0
+ TlsTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ LoadConfigTable:
+ RelativeVirtualAddress: 0
+ Size: 0
+ BoundImport:
+ RelativeVirtualAddress: 0
+ Size: 0
+ IAT:
+ RelativeVirtualAddress: 0
+ Size: 0
+ DelayImportDescriptor:
+ RelativeVirtualAddress: 0
+ Size: 0
+ ClrRuntimeHeader:
+ RelativeVirtualAddress: 0
+ Size: 0
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 4096
+ VirtualSize: 16
+ SectionData: '00000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 8192
+ VirtualSize: 14
+ SectionData: '0302052102100A0000000500033C'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ VirtualAddress: 12288
+ VirtualSize: 12
+ SectionData: '001000001010000000200000'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-reloc.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-reloc.yaml
new file mode 100644
index 0000000000000..2cd1ef3f99666
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-v3-reloc.yaml
@@ -0,0 +1,118 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: llvm-readobj --unwind %t.obj | FileCheck %s
+
+## T14: V3 unwind info in a relocatable object file (.obj).
+## This tests the relocation-based resolution path where .pdata fields
+## are zero and relocations point to the actual symbols.
+##
+## Uses the same V3 xdata as T2 (single epilog mirroring prolog):
+## .xdata = 030B0823070200181400000006000405 38343C00
+##
+## .pdata layout (12 bytes, all zeros — resolved via relocations):
+## +0: 00000000 StartAddress → reloc to func
+## +4: 00000000 EndAddress → reloc to func (addend 0x20 in SectionData)
+## +8: 00000000 UnwindInfoOffset → reloc to .xdata
+
+# CHECK-LABEL: UnwindInformation [
+# CHECK-NEXT: RuntimeFunction {
+# CHECK-NEXT: StartAddress: func
+# CHECK: EndAddress: func +0x20
+# CHECK: UnwindInfoAddress: .xdata
+# CHECK: UnwindInfo {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: SizeOfProlog: 0xB
+# CHECK-NEXT: PayloadWords: 8
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: NumberOfEpilogs: 1
+# CHECK-NEXT: Prolog [3 ops]:
+# CHECK-NEXT: [0] IP +0x0007: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0002: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0000: PUSH Reg=RDI
+# CHECK-NEXT: Epilog [0] {
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: EpilogOffset: +0x14
+# CHECK-NEXT: NumberOfOps: 3
+# CHECK-NEXT: FirstOp: 0x0
+# CHECK-NEXT: IpOffsetOfLastInstruction: 0x6
+# CHECK-NEXT: [0] IP +0x0000: ALLOC_SMALL Size=0x20
+# CHECK-NEXT: [1] IP +0x0004: PUSH Reg=RSI
+# CHECK-NEXT: [2] IP +0x0005: PUSH Reg=RDI
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '030B082307020018140000000600040538343C00'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '000000002000000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: func
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: func
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: .xdata
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ 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
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 20
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: func
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/tools/llvm-objdump/COFFDump.cpp b/llvm/tools/llvm-objdump/COFFDump.cpp
index b374bead08fc8..788626b016a77 100644
--- a/llvm/tools/llvm-objdump/COFFDump.cpp
+++ b/llvm/tools/llvm-objdump/COFFDump.cpp
@@ -685,6 +685,235 @@ static void printWin64EHUnwindInfo(const Win64EH::UnwindInfo *UI) {
outs().flush();
}
+static void printDecodedWOD(const DecodedWOD &W) {
+ switch (W.Opcode) {
+ case WOD_PUSH:
+ outs() << "WOD_PUSH Reg=" << getRegisterNameV3(W.Register);
+ break;
+ case WOD_PUSH2:
+ outs() << "WOD_PUSH2 Reg1=" << getRegisterNameV3(W.Register)
+ << ", Reg2=" << getRegisterNameV3(W.Register2);
+ break;
+ case WOD_PUSH_CONSECUTIVE_2:
+ outs() << "WOD_PUSH_CONSECUTIVE_2 Reg=" << getRegisterNameV3(W.Register)
+ << " (+" << getRegisterNameV3(W.Register + 1) << ")";
+ break;
+ case WOD_ALLOC_SMALL:
+ outs() << format("WOD_ALLOC_SMALL Size=0x%X", W.Size);
+ break;
+ case WOD_ALLOC_LARGE:
+ outs() << format("WOD_ALLOC_LARGE Size=0x%X", W.Size);
+ break;
+ case WOD_ALLOC_HUGE:
+ outs() << format("WOD_ALLOC_HUGE Size=0x%X", W.Size);
+ break;
+ case WOD_SET_FPREG:
+ outs() << "WOD_SET_FPREG Reg=" << getRegisterNameV3(W.Register)
+ << format(", Offset=0x%X", W.Displacement);
+ break;
+ case WOD_SAVE_NONVOL:
+ outs() << "WOD_SAVE_NONVOL Reg=" << getRegisterNameV3(W.Register)
+ << format(", Disp=0x%X", W.Displacement);
+ break;
+ case WOD_SAVE_NONVOL_FAR:
+ outs() << "WOD_SAVE_NONVOL_FAR Reg=" << getRegisterNameV3(W.Register)
+ << format(", Disp=0x%X", W.Displacement);
+ break;
+ case WOD_SAVE_XMM128:
+ outs() << "WOD_SAVE_XMM128 Reg=XMM" << static_cast<unsigned>(W.Register)
+ << format(", Disp=0x%X", W.Displacement);
+ break;
+ case WOD_SAVE_XMM128_FAR:
+ outs() << "WOD_SAVE_XMM128_FAR Reg=XMM" << static_cast<unsigned>(W.Register)
+ << format(", Disp=0x%X", W.Displacement);
+ break;
+ case WOD_PUSH_CANONICAL_FRAME:
+ // TODO: When the Windows x64 Unwind V3 spec is finalized, replace this
+ // raw Type value with a descriptive name. Type values are defined by the
+ // OS (see the Windows SDK headers) but the set is not yet stable.
+ outs() << "WOD_PUSH_CANONICAL_FRAME Type=" << static_cast<unsigned>(W.Type);
+ break;
+ }
+}
+
+static void printWODSequence(ArrayRef<uint8_t> WODPool, unsigned PoolOffset,
+ ArrayRef<uint16_t> IpOffsets, unsigned Count,
+ StringRef Indent) {
+ unsigned CurrentOffset = PoolOffset;
+ for (unsigned I = 0; I < Count; ++I) {
+ Expected<DecodedWOD> WOrErr = decodeWOD(WODPool, CurrentOffset);
+ if (!WOrErr) {
+ WithColor::warning(errs()) << toString(WOrErr.takeError()) << "\n";
+ return;
+ }
+ const DecodedWOD &W = *WOrErr;
+ outs() << Indent
+ << format("[%u] IP +0x%04X: ", I,
+ I < IpOffsets.size() ? IpOffsets[I] : 0);
+ printDecodedWOD(W);
+ outs() << "\n";
+ CurrentOffset += W.ByteSize;
+ }
+}
+
+static void printWin64EHUnwindInfoV3(ArrayRef<uint8_t> Data) {
+ Expected<DecodedUnwindInfoV3> InfoOrErr = decodeUnwindInfoV3(Data);
+ if (!InfoOrErr) {
+ WithColor::warning(errs()) << toString(InfoOrErr.takeError()) << "\n";
+ return;
+ }
+ const DecodedUnwindInfoV3 &Info = *InfoOrErr;
+
+ outs() << " Version: " << static_cast<int>(Info.Version) << "\n";
+ outs() << format(" Flags: 0x%X", static_cast<unsigned>(Info.Flags));
+ if (Info.Flags) {
+ if (Info.Flags & UNW_ExceptionHandler)
+ outs() << " UNW_ExceptionHandler";
+ if (Info.Flags & UNW_TerminateHandler)
+ outs() << " UNW_TerminateHandler";
+ if (Info.Flags & UNW_ChainInfo)
+ outs() << " UNW_ChainInfo";
+ if (Info.Flags & UNW_FlagLarge)
+ outs() << " UNW_FlagLarge";
+ }
+ outs() << "\n";
+ outs() << format(" Size of prolog: 0x%X\n",
+ static_cast<unsigned>(Info.SizeOfProlog));
+ outs() << " PayloadWords: " << static_cast<int>(Info.PayloadWords) << "\n";
+ outs() << " NumberOfOps: " << static_cast<int>(Info.NumberOfOps) << "\n";
+ outs() << " NumberOfEpilogs: " << static_cast<int>(Info.NumberOfEpilogs)
+ << "\n";
+
+ // Validation: SizeOfProlog must be >= first (largest) prolog IP offset.
+ if (Info.NumberOfOps > 0 && Info.SizeOfProlog < Info.PrologIpOffsets[0]) {
+ WithColor::warning(errs())
+ << format("SizeOfProlog (%u) is smaller than first prolog IP offset "
+ "(%u)\n",
+ Info.SizeOfProlog, Info.PrologIpOffsets[0]);
+ }
+
+ // Per the V3 spec, Flags bit 4 (0x10) is reserved and must be zero. Warn
+ // (rather than error) so we stay forward-compatible if Microsoft later
+ // defines this bit.
+ if (Info.Flags & 0x10)
+ WithColor::warning(errs())
+ << "V3 unwind info has reserved Flags bit 4 set\n";
+
+ // Prolog ops
+ outs() << format(" Prolog [%u ops]:\n", Info.NumberOfOps);
+ printWODSequence(Info.WODPool, 0, ArrayRef(Info.PrologIpOffsets),
+ Info.NumberOfOps, " ");
+
+ // Epilog descriptors
+ for (unsigned I = 0; I < Info.NumberOfEpilogs; ++I) {
+ const DecodedEpilogV3 &Epi = Info.Epilogs[I];
+
+ // Format the signed EpilogOffset as hex with explicit sign so negative
+ // tail-relative offsets remain readable (e.g. "-0x14" rather than
+ // "0xFFFFFFEC").
+ int32_t SignedOff = static_cast<int32_t>(Epi.EpilogOffset);
+ uint32_t AbsOff =
+ SignedOff < 0 ? static_cast<uint32_t>(-static_cast<int64_t>(SignedOff))
+ : static_cast<uint32_t>(SignedOff);
+ const char *Sign = SignedOff < 0 ? "-" : "+";
+
+ // Render Flags as "0xNN [name1 name2 ...]" so each set bit is visible.
+ std::string FlagsStr;
+ {
+ raw_string_ostream OSS(FlagsStr);
+ OSS << format("0x%02X", Epi.Flags);
+ if (Epi.Flags & EPILOG_PARENT_FRAGMENT_TRANSFER)
+ OSS << " EPILOG_PARENT_FRAGMENT_TRANSFER";
+ if (Epi.Flags & EPILOG_INFO_LARGE)
+ OSS << " EPILOG_INFO_LARGE";
+ }
+
+ if (Epi.NumberOfOps == 0) {
+ outs() << format(
+ " Epilog [%u] (Flags=%s, Offset=%s0x%X) [inherited]:\n", I,
+ FlagsStr.c_str(), Sign, AbsOff);
+ if (I == 0) {
+ WithColor::warning(errs())
+ << "first epilog cannot inherit (NumberOfOps=0)\n";
+ } else {
+ // Surface the values inherited from the previous epilog so a
+ // reader can see what the unwinder will actually execute.
+ outs() << format(" (inherits from previous epilog: FirstOp=0x%X, "
+ "IpOfLast=+0x%X, %u ops)\n",
+ Epi.FirstOp,
+ static_cast<unsigned>(Epi.IpOffsetOfLastInstruction),
+ static_cast<unsigned>(Epi.IpOffsets.size()));
+ }
+ } else {
+ outs() << format(
+ " Epilog [%u] (Flags=%s, Offset=%s0x%X, IpOfLast=+0x%X) "
+ "[%u ops, FirstOp=0x%X]:\n",
+ I, FlagsStr.c_str(), Sign, AbsOff,
+ static_cast<unsigned>(Epi.IpOffsetOfLastInstruction), Epi.NumberOfOps,
+ Epi.FirstOp);
+ printWODSequence(Info.WODPool, Epi.FirstOp, ArrayRef(Epi.IpOffsets),
+ Epi.NumberOfOps, " ");
+ }
+ }
+
+ // Optionally dump the WOD pool with byte offsets. This is useful for
+ // understanding how WODs are shared between the prolog and epilogs but is
+ // normally redundant with the per-prolog / per-epilog decoded output, so
+ // it's gated behind --unwind-show-wod-pool.
+ if (UnwindShowWODPool) {
+ outs() << format(" WOD pool [%zu bytes]:\n", Info.WODPool.size());
+ unsigned PoolOffset = 0;
+ while (PoolOffset < Info.WODPool.size()) {
+ // PayloadWords counts 2-byte words, so the pool may have a single
+ // trailing zero padding byte to round up to a word boundary. A bare
+ // 0x00 byte is never a valid 1-byte WOD (WOD_ALLOC_SMALL requires the
+ // low nibble to be 8), so treat a final zero byte as padding rather
+ // than trying to decode it.
+ if (PoolOffset + 1 == Info.WODPool.size() &&
+ Info.WODPool[PoolOffset] == 0) {
+ outs() << format(" +0x%04X: (padding)\n", PoolOffset);
+ break;
+ }
+ Expected<DecodedWOD> WOrErr = decodeWOD(Info.WODPool, PoolOffset);
+ if (!WOrErr) {
+ WithColor::warning(errs()) << toString(WOrErr.takeError()) << "\n";
+ break;
+ }
+ const DecodedWOD &W = *WOrErr;
+ outs() << format(" +0x%04X: ", PoolOffset);
+ printDecodedWOD(W);
+ outs() << "\n";
+ PoolOffset += W.ByteSize;
+ }
+ }
+
+ // Exception handler RVA or chained RUNTIME_FUNCTION sits immediately after
+ // the payload. Match the readobj output by surfacing it here when the
+ // corresponding flag is set. We don't have symbol-formatting context here
+ // (unlike readobj), so we print the raw RVA / fields.
+ if (Info.Flags & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
+ if (Info.PayloadSize + 4 <= Data.size()) {
+ uint32_t HandlerRVA = support::endian::read32le(&Data[Info.PayloadSize]);
+ outs() << format(" Handler: 0x%X\n", HandlerRVA);
+ }
+ } else if (Info.Flags & UNW_ChainInfo) {
+ if (Info.PayloadSize + sizeof(RuntimeFunction) <= Data.size()) {
+ const auto *Chained =
+ reinterpret_cast<const RuntimeFunction *>(&Data[Info.PayloadSize]);
+ outs() << " Chained:\n"
+ << format(" Start Address: 0x%X\n",
+ static_cast<uint32_t>(Chained->StartAddress))
+ << format(" End Address: 0x%X\n",
+ static_cast<uint32_t>(Chained->EndAddress))
+ << format(" Unwind Info Address: 0x%X\n",
+ static_cast<uint32_t>(Chained->UnwindInfoOffset));
+ }
+ }
+
+ outs() << "\n";
+ outs().flush();
+}
+
/// Prints out the given RuntimeFunction struct for x64, assuming that Obj is
/// pointing to an executable file.
static void printRuntimeFunction(const COFFObjectFile *Obj,
@@ -701,7 +930,53 @@ static void printRuntimeFunction(const COFFObjectFile *Obj,
uintptr_t addr;
if (Obj->getRvaPtr(RF.UnwindInfoOffset, addr))
return;
- printWin64EHUnwindInfo(reinterpret_cast<const Win64EH::UnwindInfo *>(addr));
+
+ // Check version before interpreting through V1/V2 struct.
+ const uint8_t *RawBytes = reinterpret_cast<const uint8_t *>(addr);
+ uint8_t Version = RawBytes[0] & 0x07;
+ if (Version == 3) {
+ // Estimate available size conservatively. V3 layout is a 4-byte header
+ // followed by PayloadWords*2 bytes of payload (the optional
+ // UNWIND_INFO_LARGE_V3 byte is part of the payload), plus possible
+ // handler/chain data.
+ //
+ // Clamp the slice we hand to the V3 decoder to the bytes actually
+ // available in the mapped file, so a malformed or truncated record
+ // cannot drive the decoder past the end of the underlying buffer.
+ StringRef FileData = Obj->getData();
+ const uint8_t *FileBegin =
+ reinterpret_cast<const uint8_t *>(FileData.data());
+ const uint8_t *FileEnd = FileBegin + FileData.size();
+ if (RawBytes < FileBegin || RawBytes >= FileEnd) {
+ WithColor::warning(errs())
+ << "unwind info pointer is outside of file bounds\n";
+ return;
+ }
+ size_t Remaining = static_cast<size_t>(FileEnd - RawBytes);
+ if (Remaining < 4) {
+ WithColor::warning(errs()) << "truncated UNWIND_INFO_V3 header\n";
+ return;
+ }
+ unsigned PayloadWords = RawBytes[2];
+ // When PayloadWords is odd there are 2 bytes of zero padding between
+ // the payload and the trailing handler/chain. Use the aligned size so the
+ // slice we hand the decoder reaches the handler/chain bytes.
+ size_t MinSize = alignTo(4 + static_cast<size_t>(PayloadWords) * 2, 4);
+ // Include enough trailing bytes for the optional handler RVA (4 bytes)
+ // or chained RUNTIME_FUNCTION (12 bytes) that may follow the payload.
+ size_t ExtraBytes = 0;
+ uint8_t Flags = (RawBytes[0] >> 3) & 0x1F;
+ if (Flags & (UNW_ExceptionHandler | UNW_TerminateHandler))
+ ExtraBytes = 4;
+ else if (Flags & UNW_ChainInfo)
+ ExtraBytes = sizeof(RuntimeFunction);
+ size_t ClampedSize = std::min(MinSize + ExtraBytes, Remaining);
+ if (ClampedSize < MinSize)
+ WithColor::warning(errs()) << "truncated UNWIND_INFO_V3 payload\n";
+ printWin64EHUnwindInfoV3(ArrayRef<uint8_t>(RawBytes, ClampedSize));
+ } else {
+ printWin64EHUnwindInfo(reinterpret_cast<const Win64EH::UnwindInfo *>(addr));
+ }
}
/// Prints out the given RuntimeFunction struct for x64, assuming that Obj is
@@ -748,12 +1023,19 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj,
return;
UnwindInfoOffset += RF.UnwindInfoOffset;
- if (UnwindInfoOffset > XContents.size())
+ if (UnwindInfoOffset >= XContents.size())
return;
- auto *UI = reinterpret_cast<const Win64EH::UnwindInfo *>(XContents.data() +
- UnwindInfoOffset);
- printWin64EHUnwindInfo(UI);
+ // Check version before interpreting through V1/V2 struct.
+ uint8_t Version = XContents[UnwindInfoOffset] & 0x07;
+ if (Version == 3) {
+ ArrayRef<uint8_t> RawData = XContents.slice(UnwindInfoOffset);
+ printWin64EHUnwindInfoV3(RawData);
+ } else {
+ auto *UI = reinterpret_cast<const Win64EH::UnwindInfo *>(XContents.data() +
+ UnwindInfoOffset);
+ printWin64EHUnwindInfo(UI);
+ }
}
void objdump::printCOFFUnwindInfo(const COFFObjectFile *Obj) {
diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td
index 146674a6eb714..2df559799739f 100644
--- a/llvm/tools/llvm-objdump/ObjdumpOpts.td
+++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td
@@ -231,6 +231,10 @@ def unwind_info : Flag<["--"], "unwind-info">,
def : Flag<["-"], "u">, Alias<unwind_info>,
HelpText<"Alias for --unwind-info">;
+def unwind_show_wod_pool : Flag<["--"], "unwind-show-wod-pool">,
+ HelpText<"Also display the raw Windows x64 unwind V3 WOD pool with byte offsets "
+ "(requires --unwind-info)">;
+
def wide : Flag<["--"], "wide">,
HelpText<"Ignored for compatibility with GNU objdump">;
def : Flag<["-"], "w">, Alias<wide>;
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index d5d3c9f772809..35296d7123bc5 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -341,6 +341,7 @@ static bool PrettyPGOAnalysisMap;
static bool DynamicSymbolTable;
std::string objdump::TripleName;
bool objdump::UnwindInfo;
+bool objdump::UnwindShowWODPool;
std::string objdump::Prefix;
uint32_t objdump::PrefixStrip;
@@ -3898,6 +3899,7 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) {
DynamicSymbolTable = InputArgs.hasArg(OBJDUMP_dynamic_syms);
TripleName = InputArgs.getLastArgValue(OBJDUMP_triple_EQ).str();
UnwindInfo = InputArgs.hasArg(OBJDUMP_unwind_info);
+ UnwindShowWODPool = InputArgs.hasArg(OBJDUMP_unwind_show_wod_pool);
Prefix = InputArgs.getLastArgValue(OBJDUMP_prefix).str();
parseIntArg(InputArgs, OBJDUMP_prefix_strip, PrefixStrip);
if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_debug_vars_EQ)) {
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h
index 185908b6856c4..208bac8be3b3f 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.h
+++ b/llvm/tools/llvm-objdump/llvm-objdump.h
@@ -79,6 +79,7 @@ extern bool TracebackTable;
extern bool SymbolTable;
extern std::string TripleName;
extern bool UnwindInfo;
+extern bool UnwindShowWODPool;
extern StringSet<> FoundSectionSet;
diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td
index f489e564d3182..7aafdb03973e8 100644
--- a/llvm/tools/llvm-readobj/Opts.td
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -49,6 +49,7 @@ defm string_dump : Eq<"string-dump", "Display the specified section(s) as a list
def string_table : FF<"string-table", "Display the string table (only for XCOFF/COFF now)">;
def symbols : FF<"symbols", "Display the symbol table. Also display the dynamic symbol table when using GNU output style for ELF">;
def unwind : FF<"unwind", "Display unwind information">;
+def unwind_show_wod_pool : FF<"unwind-show-wod-pool", "Also display the raw Windows x64 unwind V3 WOD pool with byte offsets (requires --unwind)">;
// ELF specific options.
def grp_elf : OptionGroup<"kind">, HelpText<"OPTIONS (ELF specific)">;
diff --git a/llvm/tools/llvm-readobj/Win64EHDumper.cpp b/llvm/tools/llvm-readobj/Win64EHDumper.cpp
index e17a035f1d371..db5e2ed32bf09 100644
--- a/llvm/tools/llvm-readobj/Win64EHDumper.cpp
+++ b/llvm/tools/llvm-readobj/Win64EHDumper.cpp
@@ -11,17 +11,27 @@
#include "llvm/Object/COFF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/WithColor.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::Win64EH;
+// clang-format off
const EnumEntry<unsigned> UnwindFlags[] = {
{ "ExceptionHandler", UNW_ExceptionHandler },
{ "TerminateHandler", UNW_TerminateHandler },
- { "ChainInfo" , UNW_ChainInfo }
+ { "ChainInfo" , UNW_ChainInfo },
+ { "Large" , UNW_FlagLarge }
};
+const EnumEntry<unsigned> EpilogFlags[] = {
+ { "ParentFragmentTransfer", EPILOG_PARENT_FRAGMENT_TRANSFER },
+ { "Large" , EPILOG_INFO_LARGE }
+};
+// clang-format on
+
const EnumEntry<unsigned> UnwindOpInfo[] = {
{ "RAX", 0 },
{ "RCX", 1 },
@@ -407,8 +417,228 @@ void Dumper::printRuntimeFunction(const Context &Ctx,
if (Offset > Contents.size())
return;
- const auto UI = reinterpret_cast<const UnwindInfo*>(Contents.data() + Offset);
- printUnwindInfo(Ctx, XData, Offset, *UI);
+ // Check version before casting to UnwindInfo struct.
+ // Only byte 0 (VersionAndFlags) is layout-compatible between V1/V2 and V3.
+ uint8_t VersionByte = Contents[Offset];
+ uint8_t Version = VersionByte & 0x07;
+
+ if (Version == 3) {
+ ArrayRef<uint8_t> RawData = Contents.slice(Offset);
+ printUnwindInfoV3(Ctx, XData, Offset, RawData);
+ } else {
+ const auto UI =
+ reinterpret_cast<const UnwindInfo *>(Contents.data() + Offset);
+ printUnwindInfo(Ctx, XData, Offset, *UI);
+ }
+}
+
+static void printDecodedWOD(ScopedPrinter &SW, raw_ostream &OS,
+ const DecodedWOD &W) {
+ switch (W.Opcode) {
+ case WOD_PUSH:
+ OS << "PUSH Reg=" << getRegisterNameV3(W.Register);
+ break;
+ case WOD_PUSH2:
+ OS << "PUSH2 Reg1=" << getRegisterNameV3(W.Register)
+ << ", Reg2=" << getRegisterNameV3(W.Register2);
+ break;
+ case WOD_PUSH_CONSECUTIVE_2:
+ OS << "PUSH_CONSECUTIVE_2 Reg=" << getRegisterNameV3(W.Register) << " (+"
+ << getRegisterNameV3(W.Register + 1) << ")";
+ break;
+ case WOD_ALLOC_SMALL:
+ OS << format("ALLOC_SMALL Size=0x%X", W.Size);
+ break;
+ case WOD_ALLOC_LARGE:
+ OS << format("ALLOC_LARGE Size=0x%X", W.Size);
+ break;
+ case WOD_ALLOC_HUGE:
+ OS << format("ALLOC_HUGE Size=0x%X", W.Size);
+ break;
+ case WOD_SET_FPREG:
+ OS << "SET_FPREG Reg=" << getRegisterNameV3(W.Register)
+ << format(", Offset=0x%X", W.Displacement);
+ break;
+ case WOD_SAVE_NONVOL:
+ OS << "SAVE_NONVOL Reg=" << getRegisterNameV3(W.Register)
+ << format(", Disp=0x%X", W.Displacement);
+ break;
+ case WOD_SAVE_NONVOL_FAR:
+ OS << "SAVE_NONVOL_FAR Reg=" << getRegisterNameV3(W.Register)
+ << format(", Disp=0x%X", W.Displacement);
+ break;
+ case WOD_SAVE_XMM128:
+ OS << "SAVE_XMM128 Reg=XMM" << static_cast<unsigned>(W.Register)
+ << format(", Disp=0x%X", W.Displacement);
+ break;
+ case WOD_SAVE_XMM128_FAR:
+ OS << "SAVE_XMM128_FAR Reg=XMM" << static_cast<unsigned>(W.Register)
+ << format(", Disp=0x%X", W.Displacement);
+ break;
+ case WOD_PUSH_CANONICAL_FRAME:
+ // TODO: When the Windows x64 Unwind V3 spec is finalized, replace this
+ // raw Type value with a descriptive name. Type values are defined by the
+ // OS (see the Windows SDK headers) but the set is not yet stable.
+ OS << "PUSH_CANONICAL_FRAME Type=" << static_cast<unsigned>(W.Type);
+ break;
+ }
+}
+
+/// Decode and print N WODs from the pool starting at byte offset PoolOffset,
+/// pairing each with the corresponding IP offset from IpOffsets.
+static void printWODSequence(ScopedPrinter &SW, raw_ostream &OS,
+ ArrayRef<uint8_t> WODPool, unsigned PoolOffset,
+ ArrayRef<uint16_t> IpOffsets, unsigned Count) {
+ unsigned CurrentOffset = PoolOffset;
+ for (unsigned I = 0; I < Count; ++I) {
+ Expected<DecodedWOD> WOrErr = decodeWOD(WODPool, CurrentOffset);
+ if (!WOrErr) {
+ WithColor::warning(errs()) << toString(WOrErr.takeError()) << "\n";
+ return;
+ }
+ const DecodedWOD &W = *WOrErr;
+ SW.startLine() << format("[%u] IP +0x%04X: ", I,
+ I < IpOffsets.size() ? IpOffsets[I] : 0);
+ printDecodedWOD(SW, OS, W);
+ OS << "\n";
+ CurrentOffset += W.ByteSize;
+ }
+}
+
+void Dumper::printUnwindInfoV3(const Context &Ctx,
+ const object::coff_section *Section,
+ off_t Offset, ArrayRef<uint8_t> Data) {
+ DictScope UIS(SW, "UnwindInfo");
+
+ Expected<DecodedUnwindInfoV3> InfoOrErr = decodeUnwindInfoV3(Data);
+ if (!InfoOrErr) {
+ WithColor::warning(errs()) << toString(InfoOrErr.takeError()) << "\n";
+ return;
+ }
+ const DecodedUnwindInfoV3 &Info = *InfoOrErr;
+
+ SW.printNumber("Version", Info.Version);
+ SW.printFlags("Flags", Info.Flags, ArrayRef(UnwindFlags));
+ SW.printHex("SizeOfProlog", Info.SizeOfProlog);
+ SW.printNumber("PayloadWords", Info.PayloadWords);
+ SW.printNumber("NumberOfOps", Info.NumberOfOps);
+ SW.printNumber("NumberOfEpilogs", Info.NumberOfEpilogs);
+
+ // Validation: SizeOfProlog must be >= first (largest) prolog IP offset.
+ // SizeOfProlog is the total prolog size in bytes, while the first IP offset
+ // is the start of the last unwind-affecting instruction within the prolog.
+ if (Info.NumberOfOps > 0 && Info.SizeOfProlog < Info.PrologIpOffsets[0]) {
+ WithColor::warning(errs())
+ << format("SizeOfProlog (%u) is smaller than first prolog IP offset "
+ "(%u)\n",
+ Info.SizeOfProlog, Info.PrologIpOffsets[0]);
+ }
+
+ // Per the V3 spec, Flags bit 4 (0x10) is reserved and must be zero. Warn
+ // (rather than error) so we stay forward-compatible if Microsoft later
+ // defines this bit.
+ if (Info.Flags & 0x10)
+ WithColor::warning(errs())
+ << "V3 unwind info has reserved Flags bit 4 set\n";
+
+ // Print prolog ops
+ {
+ SW.startLine() << format("Prolog [%u ops]:\n", Info.NumberOfOps);
+ SW.indent();
+ printWODSequence(SW, OS, Info.WODPool, 0, ArrayRef(Info.PrologIpOffsets),
+ Info.NumberOfOps);
+ SW.unindent();
+ }
+
+ // Print epilog descriptors
+ for (unsigned I = 0; I < Info.NumberOfEpilogs; ++I) {
+ const DecodedEpilogV3 &Epi = Info.Epilogs[I];
+
+ DictScope ES(SW, formatv("Epilog [{0}]", I).str());
+ SW.printFlags("Flags", Epi.Flags, ArrayRef(EpilogFlags));
+ // Format the signed EpilogOffset as hex with explicit sign so negative
+ // tail-relative offsets remain readable (e.g. "-0x14" rather than
+ // "0xFFFFFFEC").
+ {
+ int32_t SignedOff = static_cast<int32_t>(Epi.EpilogOffset);
+ uint32_t AbsOff =
+ SignedOff < 0
+ ? static_cast<uint32_t>(-static_cast<int64_t>(SignedOff))
+ : static_cast<uint32_t>(SignedOff);
+ SW.printString(
+ "EpilogOffset",
+ formatv("{0}0x{1:X-}", SignedOff < 0 ? "-" : "+", AbsOff).str());
+ }
+ SW.printNumber("NumberOfOps", Epi.NumberOfOps);
+
+ if (Epi.NumberOfOps == 0) {
+ if (I == 0) {
+ WithColor::warning(errs())
+ << "first epilog cannot inherit (NumberOfOps=0)\n";
+ } else {
+ // Surface the values inherited from the previous epilog so a
+ // reader can see what the unwinder will actually execute.
+ SW.startLine() << format(
+ "(inherits from previous epilog: FirstOp=0x%X, "
+ "IpOffsetOfLastInstruction=0x%X, %u ops)\n",
+ Epi.FirstOp, static_cast<unsigned>(Epi.IpOffsetOfLastInstruction),
+ static_cast<unsigned>(Epi.IpOffsets.size()));
+ }
+ } else {
+ SW.printHex("FirstOp", Epi.FirstOp);
+ SW.printHex("IpOffsetOfLastInstruction", Epi.IpOffsetOfLastInstruction);
+ printWODSequence(SW, OS, Info.WODPool, Epi.FirstOp,
+ ArrayRef(Epi.IpOffsets), Epi.NumberOfOps);
+ }
+ }
+
+ // Optionally dump the WOD pool with byte offsets. This is useful for
+ // understanding how WODs are shared between the prolog and epilogs but is
+ // normally redundant with the per-prolog / per-epilog decoded output, so
+ // it's gated behind --unwind-show-wod-pool.
+ if (opts::UnwindShowWODPool) {
+ ListScope WS(SW, formatv("WODPool [{0} bytes]", Info.WODPool.size()).str());
+ unsigned PoolOffset = 0;
+ while (PoolOffset < Info.WODPool.size()) {
+ // PayloadWords counts 2-byte words, so the pool may have a single
+ // trailing zero padding byte to round up to a word boundary. A bare
+ // 0x00 byte is never a valid 1-byte WOD (WOD_ALLOC_SMALL requires the
+ // low nibble to be 8), so treat a final zero byte as padding rather
+ // than trying to decode it.
+ if (PoolOffset + 1 == Info.WODPool.size() &&
+ Info.WODPool[PoolOffset] == 0) {
+ SW.startLine() << format("+0x%04X: (padding)\n", PoolOffset);
+ break;
+ }
+ Expected<DecodedWOD> WOrErr = decodeWOD(Info.WODPool, PoolOffset);
+ if (!WOrErr) {
+ WithColor::warning(errs()) << toString(WOrErr.takeError()) << "\n";
+ break;
+ }
+ const DecodedWOD &W = *WOrErr;
+ SW.startLine() << format("+0x%04X: ", PoolOffset);
+ printDecodedWOD(SW, OS, W);
+ OS << "\n";
+ PoolOffset += W.ByteSize;
+ }
+ }
+
+ // Handle exception handler / chain info
+ uint64_t LSDAOffset = Offset + Info.PayloadSize;
+ if (Info.Flags & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
+ if (LSDAOffset + 4 <= Data.size() + Offset) {
+ uint32_t HandlerRVA = support::endian::read32le(&Data[Info.PayloadSize]);
+ SW.printString("Handler",
+ formatSymbol(Ctx, Section, LSDAOffset, HandlerRVA));
+ }
+ } else if (Info.Flags & UNW_ChainInfo) {
+ if (LSDAOffset + sizeof(RuntimeFunction) <= Data.size() + Offset) {
+ const auto *Chained =
+ reinterpret_cast<const RuntimeFunction *>(&Data[Info.PayloadSize]);
+ DictScope CS(SW, "Chained");
+ printRuntimeFunctionEntry(Ctx, Section, LSDAOffset, *Chained);
+ }
+ }
}
void Dumper::printData(const Context &Ctx) {
diff --git a/llvm/tools/llvm-readobj/Win64EHDumper.h b/llvm/tools/llvm-readobj/Win64EHDumper.h
index a23d30be7a113..29479e1b466eb 100644
--- a/llvm/tools/llvm-readobj/Win64EHDumper.h
+++ b/llvm/tools/llvm-readobj/Win64EHDumper.h
@@ -48,6 +48,9 @@ class Dumper {
bool &SeenFirstEpilog);
void printUnwindInfo(const Context &Ctx, const object::coff_section *Section,
off_t Offset, const UnwindInfo &UI);
+ void printUnwindInfoV3(const Context &Ctx,
+ const object::coff_section *Section, off_t Offset,
+ ArrayRef<uint8_t> Data);
void printRuntimeFunction(const Context &Ctx,
const object::coff_section *Section,
uint64_t SectionOffset, const RuntimeFunction &RF);
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 95fb283940c17..fa56e3e48e58c 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -123,6 +123,7 @@ static std::vector<std::string> StringDump;
static bool StringTable;
static bool Symbols;
static bool UnwindInfo;
+bool UnwindShowWODPool;
static cl::boolOrDefault SectionMapping;
static SmallVector<SortSymbolKeyTy> SortKeys;
@@ -253,6 +254,7 @@ static void parseOptions(const opt::InputArgList &Args) {
opts::StringTable = Args.hasArg(OPT_string_table);
opts::Symbols = Args.hasArg(OPT_symbols);
opts::UnwindInfo = Args.hasArg(OPT_unwind);
+ opts::UnwindShowWODPool = Args.hasArg(OPT_unwind_show_wod_pool);
// ELF specific options.
opts::DynamicTable = Args.hasArg(OPT_dynamic_table);
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.h b/llvm/tools/llvm-readobj/llvm-readobj.h
index e4ee2c1396b28..a8047b9136a01 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.h
+++ b/llvm/tools/llvm-readobj/llvm-readobj.h
@@ -40,6 +40,7 @@ extern bool SectionData;
extern bool ExpandRelocs;
extern bool CodeViewSubsectionBytes;
extern bool Demangle;
+extern bool UnwindShowWODPool;
enum OutputStyleTy { LLVM, GNU, JSON, UNKNOWN };
extern OutputStyleTy Output;
} // namespace opts
More information about the llvm-commits
mailing list