From 3a128336e5659cfb6bd282a54939ebf971f2a467 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Fri, 13 Nov 2026 10:18:36 +0900 Subject: [PATCH] Print vtables, VTTs and thunks as GNU and LLVM do We found discrepancies in demangling while working on the mold linker. The linker lets users assign symbol versions by matching patterns against demangled symbol names. Because the names differ from GNU's, some symbols end up not versioned at all. Specifically, this patch changes `{vtable(A)}` to `vtable for A` and `{vtt(A)}` to `VTT for A`, prints thunks as `virtual thunk to A::f()` and so on, and fixes the order of the two classes in a construction vtable's name. --- CHANGELOG.md | 3 ++- src/ast.rs | 40 +++++++++++++--------------------------- tests/tests.rs | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09548ed..effaa16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ Released YYYY/MM/DD ## Changed -* TODO (or remove section if none) +* Vtables, VTTs and thunks are now printed as GNU and LLVM do, e.g. + `vtable for A` instead of `{vtable(A)}`. ## Deprecated diff --git a/src/ast.rs b/src/ast.rs index b5c7fe4..dcfc95d 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -8130,18 +8130,15 @@ where match *self { SpecialName::VirtualTable(ref ty) => { - write!(ctx, "{{vtable(")?; + write!(ctx, "vtable for ")?; ctx.push_demangle_node(DemangleNodeType::VirtualTable); ty.demangle(ctx, scope)?; ctx.pop_demangle_node(); - write!(ctx, ")}}")?; Ok(()) } SpecialName::Vtt(ref ty) => { - write!(ctx, "{{vtt(")?; - ty.demangle(ctx, scope)?; - write!(ctx, ")}}")?; - Ok(()) + write!(ctx, "VTT for ")?; + ty.demangle(ctx, scope) } SpecialName::Typeinfo(ref ty) => { write!(ctx, "typeinfo for ")?; @@ -8152,26 +8149,15 @@ where ty.demangle(ctx, scope) } SpecialName::VirtualOverrideThunk(ref offset, ref encoding) => { - write!(ctx, "{{virtual override thunk(")?; - offset.demangle(ctx, scope)?; - write!(ctx, ", ")?; - encoding.demangle(ctx, scope)?; - write!(ctx, ")}}")?; - Ok(()) + match *offset { + CallOffset::NonVirtual(..) => write!(ctx, "non-virtual thunk to ")?, + CallOffset::Virtual(..) => write!(ctx, "virtual thunk to ")?, + } + encoding.demangle(ctx, scope) } - SpecialName::VirtualOverrideThunkCovariant( - ref this_offset, - ref result_offset, - ref encoding, - ) => { - write!(ctx, "{{virtual override thunk(")?; - this_offset.demangle(ctx, scope)?; - write!(ctx, ", ")?; - result_offset.demangle(ctx, scope)?; - write!(ctx, ", ")?; - encoding.demangle(ctx, scope)?; - write!(ctx, ")}}")?; - Ok(()) + SpecialName::VirtualOverrideThunkCovariant(_, _, ref encoding) => { + write!(ctx, "covariant return thunk to ")?; + encoding.demangle(ctx, scope) } SpecialName::Guard(ref name) => { write!(ctx, "guard variable for ")?; @@ -8183,9 +8169,9 @@ where } SpecialName::ConstructionVtable(ref ty1, _, ref ty2) => { write!(ctx, "construction vtable for ")?; - ty1.demangle(ctx, scope)?; + ty2.demangle(ctx, scope)?; write!(ctx, "-in-")?; - ty2.demangle(ctx, scope) + ty1.demangle(ctx, scope) } SpecialName::TypeinfoFunction(ref ty) => { write!(ctx, "typeinfo fn for ")?; diff --git a/tests/tests.rs b/tests/tests.rs index e782d2c..d0e8e30 100755 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -297,6 +297,26 @@ demangles!( "TLS wrapper function for js::TlsContext" ); +demangles!(_ZTVN2ns1AE, "vtable for ns::A"); +demangles!(_ZTVN2ns2T1ImEE, "vtable for ns::T1"); +demangles!(_ZTTN2ns1DE, "VTT for ns::D"); +demangles!( + _ZTCN2ns1DE16_NS_1WE, + "construction vtable for ns::W-in-ns::D" +); +demangles!(_ZThn16_N2ns1C1gEv, "non-virtual thunk to ns::C::g()"); +demangles!(_ZThn16_N2ns1CD1Ev, "non-virtual thunk to ns::C::~C()"); +demangles!(_ZTv0_n32_N2ns1D1fEv, "virtual thunk to ns::D::f()"); +demangles!(_ZTv0_n24_N2ns1DD0Ev, "virtual thunk to ns::D::~D()"); +demangles!( + _ZTch0_h16_N2ns1C5cloneEv, + "covariant return thunk to ns::C::clone()" +); +demangles!( + _ZTchn16_h16_N2ns1C5cloneEv, + "covariant return thunk to ns::C::clone()" +); + demangles!(_Z3fooILb0EEvi, "void foo(int)"); demangles!(_Z3fooILb1EEvi, "void foo(int)"); demangles!(_Z3fooILb2EEvi, "void foo<(bool)2>(int)");