ship one record per function: merge the release set, gen reads it, descriptions as doc comments, gates for what was only claimed; v3.0
Some checks failed
CI / fuzz (push) Successful in 2m2s
CI / lint (push) Successful in 15s
CI / test (push) Failing after 18s

This commit is contained in:
Kamal Tufekcic 2026-08-02 22:01:36 +03:00
commit 3410a79b6a
28 changed files with 30596 additions and 955 deletions

View file

@ -18,11 +18,9 @@ pub struct VTable {
/// One vtable discovered by the whole-binary sweep — the class inventory row.
pub struct ClassVtable {
pub mangled: String, // the raw `_ZTS` type name, e.g. "11CBaseEntity"
pub name: String, // demangled, e.g. "CBaseEntity"
pub vtable_va: u64, // vaddr of slot index 0
pub offset_to_top: i64, // 0 for the primary (complete-object) vtable; <0 for sub-object tables
pub typeinfo: u64, // vaddr of the Itanium typeinfo struct
pub slots: Vec<u64>, // method vaddrs; a method's gamedata offset == its index here
pub bases: Vec<BaseClass>, // direct base classes (the is-a graph edges)
}
@ -143,10 +141,10 @@ fn demangle_type(mangled: &str) -> String {
.unwrap_or_else(|| mangled.to_string())
}
/// If `ti` addresses a valid Itanium typeinfo, return its `(mangled, demangled)` class name.
/// If `ti` addresses a valid Itanium typeinfo, return its DEMANGLED class name.
/// A typeinfo is `[kind_vtable_ptr][name_ptr][ base-class data … ]`: `+0` points at one of the
/// C++ runtime's type_info-kind vtables, `+8` at the `_ZTS` name string.
fn typeinfo_name(img: &CodeImage, ti: u64, kinds: &RttiKinds) -> Option<(String, String)> {
fn typeinfo_name(img: &CodeImage, ti: u64, kinds: &RttiKinds) -> Option<String> {
// +0 must be one of the three kind vtables. Prefer the symbol-name-derived tag (the only signal that
// survives a DYNAMICALLY-linked C++ runtime, where the three kinds all resolve to the same offline
// value); else fall back to the in-image value check (statically-linked / stripped builds).
@ -163,7 +161,7 @@ fn typeinfo_name(img: &CodeImage, ti: u64, kinds: &RttiKinds) -> Option<(String,
if !(c0.is_ascii_digit() || matches!(c0, b'N' | b'I' | b'P' | b'K' | b'S')) {
return None;
}
Some((mangled.clone(), demangle_type(&mangled)))
Some(demangle_type(&mangled))
}
/// Direct base classes of the typeinfo at `ti`, dispatched on its exact Itanium kind.
@ -187,7 +185,7 @@ fn typeinfo_bases(img: &CodeImage, ti: u64, kinds: &RttiKinds) -> Vec<BaseClass>
Some(KindTag::Si) => {
// __si_class_type_info: one public, non-virtual base at offset 0; its typeinfo ptr at +16.
if let Some(bp) = img.read_ptr(ti.wrapping_add(16))
&& let Some((_, name)) = typeinfo_name(img, bp, kinds)
&& let Some(name) = typeinfo_name(img, bp, kinds)
{
return vec![BaseClass {
name,
@ -199,7 +197,7 @@ fn typeinfo_bases(img: &CodeImage, ti: u64, kinds: &RttiKinds) -> Vec<BaseClass>
}
Some(KindTag::Vmi) => {
// __vmi_class_type_info: flags@+16, base_count@+20, then 16-byte {typeinfo_ptr, offset_flags}.
let Some(count) = img.read_u32(ti + 20) else {
let Some(count) = img.read_u32(ti.wrapping_add(20)) else {
return Vec::new();
};
if count == 0 || count > 128 {
@ -211,7 +209,7 @@ fn typeinfo_bases(img: &CodeImage, ti: u64, kinds: &RttiKinds) -> Vec<BaseClass>
let Some(bp) = img.read_ptr(e) else {
break;
};
if let Some((_, name)) = typeinfo_name(img, bp, kinds) {
if let Some(name) = typeinfo_name(img, bp, kinds) {
let of = img.read_i64(e.wrapping_add(8)).unwrap_or(0);
bases.push(BaseClass {
name,
@ -239,7 +237,7 @@ pub fn enumerate_vtables(img: &CodeImage, max_slots: usize) -> Vec<ClassVtable>
if slot < 8 {
continue;
}
let Some((mangled, name)) = typeinfo_name(img, val, &kinds) else {
let Some(name) = typeinfo_name(img, val, &kinds) else {
continue;
};
// No de-dup guard: `reloc_slots` iterates a map KEYED by slot vaddr, so every slot — and hence
@ -259,11 +257,9 @@ pub fn enumerate_vtables(img: &CodeImage, max_slots: usize) -> Vec<ClassVtable>
}
let bases = typeinfo_bases(img, val, &kinds);
out.push(ClassVtable {
mangled,
name,
vtable_va,
offset_to_top: ott,
typeinfo: val,
slots,
bases,
});