name: vtable
description: Dump a class's MWCC vtable from build/SZBE69_B8/obj/*.o. Shows every sub-object table (primary + each base), slot index, byte offset, and the mangled function symbol. Use when objdiff shows a lwz r12, OFF(r12) mismatch and you need to map the offset to a virtual method, or when verifying a header's virtual-function order against the compiled binary.
argument-hint: "CLASS [--unit UNIT] [--obj PATH] [--offset N] [--sub-offset N] [--json] [--demangle]"
allowed-tools: Bash(python3 scripts/dump_vtable.py *), Read, Grep, Glob
Vtable Dump
Show the layout of a class's MWCC vtable as compiled for SZBE69_B8. Reads .rela.data relocations from the .o that defines __vt__<len><class>, splits it into sub-object tables by __RTTI__<len><class> markers, and reports every function-pointer slot with its mangled symbol.
This is the authoritative source for the build we're matching — vtable layouts differ between Wii banks, so the Bank 5 debug ELF (Ghidra's source) is the wrong place to read vtable bytes from.
Arguments
$ARGUMENTS
First positional arg is the class name (unmangled, e.g. Character, RndDrawable, GemTrackDir). Mangled __vt__9Character is also accepted.
Flags:
--unit UNIT— explicit objdiff-style unit (e.g.system/char/Character). Resolves tobuild/SZBE69_B8/obj/<unit>.o.--obj PATH— explicit.ofile path.--offset N— show only the slot at slot index N (or byte offset N ifN >= 100, treated as a live-vptr asm offset).--sub-offset N— sub-object offset for--offsetlookup (default 0 = primary).--json— emit JSON.--demangle— pretty-printClass::method(length-prefix unwrap; not a full demangler).
Auto-detection of the .o file (when --obj/--unit are not given): glob build/SZBE69_B8/obj/**/<Class>.o, try simple prefix strips (Rnd, Ham, Ui), then fall back to scanning every .o for the __vt__ symbol.
Steps
Dump the vtable. Default text mode:
python3 scripts/dump_vtable.py '$0'The output starts with a one-line summary:
Class: RndDrawable Vtable: __vt__11RndDrawable (208 bytes, build/SZBE69_B8/obj/system/rndobj/Draw.o:.data+0x08) Sub-tables: 3followed by one block per sub-object table. Each block lists every slot:
Sub-table 0: offset_to_top=0 (primary), sub-object offset +0, 23 slot(s), header at +0x00 [ 0] +0x08 ClassName__11RndDrawableCFv [ 1] +0x0c SetType__11RndDrawableF6Symbol ... [ 5] +0x1c Copy__11RndDrawableFPCQ23Hmx6ObjectQ33Hmx6Object8CopyType[N]— slot index within this sub-table (0-based)+0xNN— byte offset within the whole vtable symbol (file offset; includes the 8-byte header)offset_to_top— signed delta from this sub-object back to the primary;0= primary, negative = secondary base
Translate from an objdiff mismatch. When you see
lwz r12, OFF(r12)in asm, OFF is a live-vptr byte offset measured from the START of the__vt__symbol, which includes an 8-byte header (RTTI pointer + offset_to_top) before the first function slot. Slot index = (OFF − 8) / 4. For example,lwz r12, 0x14(r12)= (0x14 − 8) / 4 = slot 3 (not slot 5). The old formulaOFF / 4is WRONG — it ignores the 8-byte header.MWCC vtable layout (vptr points to the start of the vt symbol):
vptr + 0x00= RTTI pointer (not a function)vptr + 0x04= offset_to_top (not a function)vptr + 0x08= slot 0vptr + 0x0c= slot 1vptr + 0x10= slot 2vptr + 0x14= slot 3vptr + 0x18= slot 4vptr + 0x1c= slot 5
Look up a single slot by slot index. When you know the 0-based slot index:
python3 scripts/dump_vtable.py '$0' --offset 3Or to pass the raw vptr byte offset from the asm (e.g.
0x14fromlwz r12, 0x14(r12)):python3 scripts/dump_vtable.py '$0' --offset 0x14 --vptr-offsetFor non-primary sub-objects, add
--sub-offset 32(matching the value fromlwz r12, 0x20(r3)style sub-object loads). See/resolve-vcallfor the three-argument form.
When to Use
- objdiff shows mismatched slot offsets on a
r12-based virtual dispatch and you need to know which virtual method each side is calling. - Verifying a header's virtual function order against the compiled binary (especially after editing the class declaration).
- Auditing multi-inheritance layouts before adjusting member or vtable order in a class header.
Tips
- MWCC on Gekko does not ICF — every slot has a unique symbol, no disambiguation needed.
- Thunks have the form
@N@Method__...(single-step) or@N@M@Method__...(two-step); the--demangleoutput strips them and shows the underlying method, but the unstripped symbol is what's actually in the vtable slot. - Vtable bytes are read from Bank 8
.ofiles only. Do not cross-reference with Bank 5 (band_r_wii.elf) vtable bytes — layouts differ. - DWARF inheritance graphs ARE stable across banks, so if you need richer base-class context,
/struct-checkand/ghidra-decompilecan supplement this output.