Skip to content

An attempt to FH4 - #7

Draft
yasster wants to merge 2 commits into
mainfrom
user/mmissoum/FH4
Draft

An attempt to FH4#7
yasster wants to merge 2 commits into
mainfrom
user/mmissoum/FH4

Conversation

@yasster

@yasster yasster commented Jan 8, 2026

Copy link
Copy Markdown
Owner

No description provided.

@yasster yasster changed the title An attempt to FH4 in clang An attempt to FH4 Jan 9, 2026
The FH4 UnwindMap entry offsets were incorrectly calculated using a
hardcoded approximation of \5 * (I + 1)\ bytes per entry. However,
compressed integers in FH4 can be 1-5 bytes depending on value magnitude,
causing incorrect offsets when values require different encoding sizes.

This caused crashes (STATUS_STACK_BUFFER_OVERRUN 0xC0000409) with deeply
nested try-catch blocks where the accumulated offset errors led to the
runtime reading invalid unwind data.

Changes:
- Add getCompressedIntSize() helper to calculate actual byte sizes
- Pre-calculate entry sizes and accumulate actual byte offsets before
  emitting UnwindMap entries
- Fix emitCompressedInt() to properly mask values for multi-byte encodings
- Defer IP2State table emission to MC layer (MCWin64EH) where symbol
  offsets are known after layout
- Add FH4IP2StateEntry struct and related fields to WinEH::FrameInfo
- Fix SelectionDAGBuilder to use isMSVCCXXPersonality() for both
  MSVC_CXX (FH3) and MSVC_CXX4 (FH4) personalities

Fixes exception handling crashes with 6+ levels of nested try-catch blocks
when using __CxxFrameHandler4.
// For FH4, the parent function and catch funclets need a reference to
// the FuncInfo4 structure ($cppxdata$) following the unwind info.
StringRef FuncLinkageName = GlobalValue::dropLLVMManglingEscape(F.getName());
MCSymbol *FuncInfoXData = Asm->OutContext.getOrCreateSymbol(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks identical to the FH3 info above, what am I missing?

}

static void emitCompressedInt(MCStreamer &OS, uint32_t Value) {
if (Value < 128) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer to switch on getCompressedIntSize instead of duplicating the magic value comparisons here?

if (I == 0) {
// First entry: offset to "before" the unwind map (state -1)
// This is the size of the NumEntries field
NextOffset = getCompressedIntSize(FuncInfo.CxxUnwindMap.size());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems odd to be re-calculating this every time, can we store it in AccumulatedOffset instead (and, maybe, collapse this if statement)?

.concat(Twine(I))
.concat("$")
.concat(FuncLinkageName));
HandlerMaps.push_back(HandlerMapXData);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in the if statement? Or is it ok to push null pointers?

emitCompressedInt(OS, TBME.TryLow);
emitCompressedInt(OS, TBME.TryHigh);
emitCompressedInt(OS, TBME.CatchHigh);
OS.emitValue(create32bitRef(HandlerMapXData), 4);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming that create32bitRef knows how to handle nulls?

Comment thread llvm/lib/MC/MCWin64EH.cpp
/// - Values 128-16383: 2 bytes, (value << 2) | 1 (low 2 bits 01)
/// - Values 16384+: 4 bytes, (value << 3) | 3 (low 3 bits 011)
/// This matches Microsoft's encoding in CxxFrameHandler4.
static void emitFH4NetEncodedInt(MCStreamer &Streamer, uint32_t Value) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a common place we can put this?

Comment thread llvm/lib/MC/MCWin64EH.cpp
if (MaybeOffset && *MaybeOffset >= 0) {
Delta = static_cast<uint32_t>(*MaybeOffset);
} else {
Delta = 1; // Fallback for cross-fragment labels

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we emit a fixup to correct this after relaxation?


; CHECK-LABEL: $handlerMap$0$multiple_catch:
; Should have 2 handlers
; CHECK: .byte

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a value you should be checking here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants