From 60ad610fdd95d9a58037979831b2a6116aa226bc Mon Sep 17 00:00:00 2001 From: Dmitry Mottl Date: Wed, 18 Mar 2026 21:39:53 +0300 Subject: [PATCH] Fix: added UnionSyntheticProvider for LLDB --- src/etc/lldb_lookup.py | 2 ++ src/etc/lldb_providers.py | 61 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index 2b90d4022f7fb..80c3c9f5cd2e0 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -106,6 +106,8 @@ def synthetic_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object: ) return ClangEncodedEnumProvider(valobj, _dict) + if rust_type == RustType.REGULAR_UNION: + return UnionSyntheticProvider(valobj, _dict) if rust_type == RustType.STD_VEC: return StdVecSyntheticProvider(valobj, _dict) if rust_type == RustType.STD_VEC_DEQUE: diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 88d210691d00e..3fc26d2016ce8 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -10,6 +10,7 @@ eBasicTypeUnsignedChar, eFormatChar, eTypeIsInteger, + LLDB_INVALID_ADDRESS, ) from rust_types import is_tuple_fields @@ -407,6 +408,66 @@ def has_children(self) -> bool: return True +def _child_is_valid(child: SBValue) -> bool: + """ + Return True only if `child` is a non-None, valid SBValue whose load + address satisfies the alignment required by its own type. + + This protects against LLDB crashing when it tries to materialise a union + variant that is incompatible with the object's actual memory address — + which happens with #[repr(C, align(N))] types nested inside unions that + also contain zero-sized / lower-aligned variants (e.g. ManuallyDrop). + """ + if child is None or not child.IsValid(): + return False + + ty = child.GetType() + if not ty.IsValid(): + return False + + byte_align = ty.GetByteAlign() # SBType.GetByteAlign() → alignment in bytes + if byte_align <= 1: + return True # trivially satisfied + + addr = child.GetLoadAddress() + if addr == LLDB_INVALID_ADDRESS: + return True # can't check — let LLDB decide + + return (addr % byte_align) == 0 + + +class UnionSyntheticProvider: + """Pretty-printer for unions""" + + def __init__(self, valobj: SBValue, _dict: LLDBOpaque): + self.valobj = valobj + self._children = [] + + def num_children(self) -> int: + return len(self._children) + + def get_child_index(self, name: str) -> int: + return self.valobj.GetIndexOfChildWithName(name) + + def get_child_at_index(self, index: int) -> SBValue: + if index < 0 or index >= len(self._children): + return SBValue() # nothing found + return self._children[index] + + def update(self): + # Count only children whose alignment is actually satisfiable so that + # LLDB never asks us for an index that would crash on materialisation. + raw_count = self.valobj.GetNumChildren() + self._children = [ + self.valobj.GetChildAtIndex(i) + for i in range(raw_count) + if _child_is_valid(self.valobj.GetChildAtIndex(i)) + ] + + def has_children(self) -> bool: + return self.valobj.MightHaveChildren() + + class StdStringSyntheticProvider: def __init__(self, valobj: SBValue, _dict: LLDBOpaque): self.valobj = valobj