"""Q23: callers of the manager setter FUN_18011d780 -> the manager's vtable.

CONTROL: the vtable found must have real functions at slots 0xa08 and 0xa40.
"""
import traceback

try:
    for frm, typ, fn, ent in xrefs_to(0x18011D780):
        print("caller %s(%#x) via %#x %s" % (fn, ent, frm, typ))
    ents = {ent for _f, _t, _n, ent in xrefs_to(0x18011D780) if ent}
    for ent in ents:
        src = dec(ent)
        print("=" * 70)
        print("FUN_%x len=%d" % (ent, len(src)))
        print(src if len(src) < 7000 else src[:7000] + "\n...[cut]")
        f = func(ent)
        cands = set()
        for ad in f.getBody().getAddresses(True):
            ins = listing.getInstructionAt(ad)
            if ins is None:
                continue
            for r in ins.getReferencesFrom():
                t = int(r.getToAddress().getOffset())
                if 0x1801E5000 <= t <= 0x1802891FF:
                    cands.add(t)
        print("--- .rdata refs, checked for vtable shape ---")
        for t in sorted(cands):
            try:
                v0, v1 = qword(t), qword(t + 8)
                s90, s98 = qword(t + 0x490), qword(t + 0x498)
                a08, a40 = qword(t + 0xA08), qword(t + 0xA40)
            except Exception:
                continue
            ok = all(fm.getFunctionAt(addr(x)) is not None
                     for x in (v0, v1) if 0x180000000 <= x < 0x181000000)
            print("  %#x v0=%s v1=%s | +0x490=%s +0x498=%s +0xa08=%s +0xa40=%s%s" %
                  (t, fname(v0), fname(v1), fname(s90), fname(s98),
                   fname(a08), fname(a40), "  <== VTABLE?" if ok else ""))
except Exception:
    traceback.print_exc()
