"""Read-only PE unwind chain lookup; cold fragments are not independent function entries."""
import argparse,bisect,json,struct
from pathlib import Path
import pefile

p=argparse.ArgumentParser(description=__doc__)
p.add_argument('image',type=Path)
p.add_argument('rvas',nargs='+',type=lambda value:int(value,0))
a=p.parse_args()
pe=pefile.PE(str(a.image),fast_load=True)
pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXCEPTION']])
rows=sorted((e.struct.BeginAddress,e.struct.EndAddress,e.struct.UnwindData) for e in pe.DIRECTORY_ENTRY_EXCEPTION)
starts=[r[0] for r in rows]
for rva in a.rvas:
    i=bisect.bisect_right(starts,rva)-1
    if i<0 or not rows[i][0]<=rva<rows[i][1]:raise ValueError('No exception entry at '+hex(rva))
    entry=rows[i];chain=[]
    for _ in range(16):
        begin,end,unwind=entry;chain.append({'begin':hex(begin),'end':hex(end),'unwind':hex(unwind)})
        header=pe.get_data(unwind,4);flags=header[0]>>3;codes=header[2]
        if not flags&4:break
        entry=struct.unpack('<III',pe.get_data(unwind+4+((codes+1)&~1)*2,12))
    else:raise ValueError('Unwind chain exceeded bound')
    print(json.dumps({'rva':hex(rva),'parent':hex(entry[0]),'chain':chain}))
