unit kern_jit_dynamic; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses mqueue, hamt, //g23tree, g_node_splay, murmurhash, x86_jit, kern_jit_ctx, kern_jit_asm, kern_rwlock, kern_thr, kern_stub; { entry_point -> +----------+ +---------+ |code_chunk| -> |code_blob| entry_point -> | | | | +----------+ | | entry_point -> +----------+ | | |code_chunk| -> | | entry_point -> | | | | +----------+ +---------+ } type p_jit_dynamic_blob=^t_jit_dynamic_blob; p_jit_entry_point=^t_jit_entry_point; t_jit_entry_point=object next:p_jit_entry_point; blob:p_jit_dynamic_blob; src :Pointer; //<-guest dst :Pointer; //<-host entry_public:Integer; procedure inc_ref(name:pchar); procedure dec_ref(name:pchar); end; p_jinstr_len=^t_jinstr_len; t_jinstr_len=packed record original:0..31; //5 LF_JMP :0..1; bit6 :0..1; recompil:0..511; //9 end; p_jcode_chunk=^t_jcode_chunk; t_jcode_chunk=object entry:TAILQ_ENTRY; //entry_chunk_list next :p_jcode_chunk; //t_jit_dynamic_blob.chunk_list //pLeft :p_jcode_chunk; //pRight:p_jcode_chunk; blob :p_jit_dynamic_blob; tobj :Pointer; //p_vm_track_object start :QWORD; //<-guest __end :QWORD; //<-guest dest :QWORD; //<-host d_end :QWORD; //<-host hash :QWORD; //MurmurHash64A(Pointer(start),__end-start,$010CA1C0DE); count :QWORD; table :record end; //p_jinstr_len[] function c(n1,n2:p_jcode_chunk):Integer; static; procedure inc_ref(name:pchar); procedure dec_ref(name:pchar); function is_mark_del:Boolean; function find_host_by_guest(addr:QWORD):QWORD; function find_guest_by_host(addr:QWORD):QWORD; function cross_guest(c_start,c___end:QWORD):Boolean; function cross_host (c_start,c___end:QWORD):Boolean; end; //t_jcode_chunk_set=specialize T23treeSet; p_jplt_cache=^t_jplt_cache; t_jplt_cache=object(t_jplt_cache_asm) // pLeft :p_jplt_cache; //jpltc_curr pRight:p_jplt_cache; //jpltc_curr // entry:TAILQ_ENTRY; //jpltc_attc // self_block:Pointer; dest_block:Pointer; // function c(n1,n2:p_jplt_cache):Integer; static; end; t_jplt_cache_set=specialize TNodeSplay; t_jit_dynamic_blob=object var entry_list:p_jit_entry_point; chunk_list:p_jcode_chunk; jpltc_curr:t_jplt_cache_set; jpltc_attc:TAILQ_HEAD; mchunk:p_stub_chunk; base:Pointer; size:ptruint; plta:p_jit_plt; pltc:ptruint; plt_stub:t_jplt_cache; lock:Pointer; refs:Integer; attach_count:Integer; procedure inc_ref(name:pchar); procedure dec_ref(name:pchar); procedure inc_attach_count; function dec_attach_count:Boolean; function find_guest_by_host(addr:QWORD):QWORD; function cross_host(c_start,c___end:QWORD):Boolean; procedure Free; function add_entry_point(src,dst:Pointer):p_jit_entry_point; procedure free_entry_point(node:p_jit_entry_point); procedure init_plt; procedure attach_plt_cache(uplock:p_jit_dynamic_blob;node:p_jplt_cache); procedure detach_plt_cache(uplock:p_jit_dynamic_blob;node:p_jplt_cache); procedure detach_all_attc; procedure detach_all_curr; procedure detach_threads; function add_plt_cache(plt:p_jit_plt;src,dst:Pointer;dest_block:p_jit_dynamic_blob):p_jplt_cache; function new_chunk(count:QWORD):p_jcode_chunk; procedure alloc_base(_size:ptruint); procedure free_base; procedure attach_entry(node:p_jit_entry_point); procedure attach_all_entry; procedure attach_all_chunk; procedure attach; function detach_entry(node:p_jit_entry_point):Boolean; procedure detach_all_entry; procedure detach_entry(c_start,c___end:QWORD); procedure detach_chunk(node:p_jcode_chunk); procedure detach_all_chunk; procedure detach; end; function new_blob(_size:ptruint):p_jit_dynamic_blob; var entry_hamt_lock:Pointer=nil; entry_hamt:TSTUB_HAMT64; entry_chunk_lock:Pointer=nil; entry_chunk_list:TAILQ_HEAD=(tqh_first:nil;tqh_last:@entry_chunk_list.tqh_first); //entry_chunk:t_jcode_chunk_set; function fetch_entry(src:Pointer):p_jit_entry_point; function exist_entry(src:Pointer):Boolean; function fetch_chunk_by_guest(src:Pointer):p_jcode_chunk; function fetch_blob_by_host(src:Pointer):p_jit_dynamic_blob; function exist_jit_host(src:Pointer;tf_tip:PQWORD):Boolean; function next_chunk(node:p_jcode_chunk;src:Pointer):p_jcode_chunk; //procedure unmap_jit_cache(start,__end:QWORD); function preload_entry(addr:Pointer):p_jit_entry_point; procedure jit_ctx_free(td:p_kthread); procedure switch_to_jit(td:p_kthread); function jmp_dispatcher(addr:Pointer;plt:p_jit_plt;from:Pointer):Pointer; procedure blob_track(blob:p_jit_dynamic_blob); function build(var ctx:t_jit_context2):p_jit_dynamic_blob; procedure preload(addr:Pointer); implementation uses sysutils, vmparam, sys_bootparam, kern_proc, vm, vm_map, vm_pmap_prot, vm_pmap, md_map, vm_tracking_map, subr_dynlib, kern_dlsym; // procedure pick(var ctx:t_jit_context2;preload:Pointer); external name 'kern_jit_pick'; // function scan_up_exc(addr:QWORD):QWORD; begin addr:=(addr+PAGE_MASK) and QWORD(not PAGE_MASK); while is_guest_addr(addr) do begin Result:=addr; if ((ppmap_get_prot(QWORD(addr)) and PAGE_PROT_EXECUTE)=0) then begin Break; end; addr:=addr+PAGE_SIZE; end; end; function scan_dw_exc(addr:QWORD):QWORD; begin addr:=addr and QWORD(not PAGE_MASK); while is_guest_addr(addr) do begin Result:=addr; if ((ppmap_get_prot(QWORD(addr)) and PAGE_PROT_EXECUTE)=0) then begin Break; end; addr:=addr-PAGE_SIZE; end; end; procedure preload(addr:Pointer); var node:p_jit_entry_point; ctx:t_jit_context2; begin if (p_print_jit_preload) then begin Writeln('unk addr:0x',HexStr(addr)); end; node:=preload_entry(addr); if (node=nil) then begin ctx:=Default(t_jit_context2); ctx.text_start:=scan_dw_exc(QWORD(addr)); ctx.text___end:=scan_up_exc(QWORD(addr)); ctx.map____end:=ctx.text___end; ctx.modes :=[cmDontScanRipRel]; //dont scan rip relative ctx.add_forward_point(fpCall,addr); pick(ctx,addr); end else begin node^.dec_ref('preload_entry'); end; end; procedure jit_ctx_free(td:p_kthread); public; begin //td^.td_jctx.block:=nil; kmem_free(td^.td_jctx.call_ret_cache,64*1024); td^.td_jctx.call_ret_cache:=nil; end; procedure switch_to_jit(td:p_kthread); public; label _host, _start; var node:p_jit_entry_point; jctx:p_td_jctx; frame:p_jit_frame; begin if (td=nil) then Exit; if ((td^.pcb_flags and PCB_IS_HLE)<>0) then begin //hle mode goto _host; end else if ((td^.pcb_flags and PCB_IS_JIT)<>0) then begin //jit mode if is_guest_addr(td^.td_frame.tf_rip) then begin //jit->jit end else begin //jit->host if ((td^.td_pflags and TDP_KTHREAD)<>0) then begin //system thread //clear jit flag td^.pcb_flags:=td^.pcb_flags and (not PCB_IS_JIT); Exit; //internal? end else begin //forbidden Assert(false,'forbidden jump to 0x'+HexStr(td^.td_frame.tf_rip,16)); end; end; end else begin //host mode _host: if is_guest_addr(td^.td_frame.tf_rip) then begin //host->jit set_pcb_flags(td,PCB_IS_JIT); end else begin //host->host Exit; //internal? end; end; _start: node:=fetch_entry(Pointer(td^.td_frame.tf_rip)); if (node=nil) then begin preload(Pointer(td^.td_frame.tf_rip)); goto _start; end; jctx:=@td^.td_jctx; frame:=@td^.td_frame.tf_r13; //jctx^.block:=node^.blob; if (jctx^.rsp=nil) then begin jctx^.rsp:=td^.td_kstack.stack; end; if (jctx^.rbp=nil) then begin jctx^.rbp:=td^.td_kstack.stack; end; if (jctx^.call_ret_cache=nil) then begin jctx^.call_ret_cache:=kmem_alloc(64*1024,VM_RW); end; //tf_r14 not need to move //tf_r15 not need to move frame^.tf_r13:=td^.td_frame.tf_r13; frame^.tf_rsp:=td^.td_frame.tf_rsp; frame^.tf_rbp:=td^.td_frame.tf_rbp; td^.td_frame.tf_rsp:=QWORD(td^.td_kstack.stack); td^.td_frame.tf_rbp:=QWORD(td^.td_kstack.stack); td^.td_frame.tf_rip:=QWORD(node^.dst); td^.td_frame.tf_r13:=QWORD(frame); set_pcb_flags(td,PCB_FULL_IRET or PCB_IS_JIT); if (td^.td_teb^.jit_trp=nil) then begin td^.td_teb^.jit_trp:=@jit_interrupt_nop; end; //teb stack td^.td_teb^.sttop:=td^.td_kstack.sttop; td^.td_teb^.stack:=td^.td_kstack.stack; //teb stack // node^.dec_ref('fetch_entry') end; function fetch_chunk_by_guest(src:Pointer):p_jcode_chunk; var //i:t_jcode_chunk_set.Iterator; //node:t_jcode_chunk; obj:p_vm_track_object; begin Result:=nil; obj:=vm_map_track_next(p_proc.p_vmspace, vm_offset_t(src), nil, H_JIT_CHUNK); if (obj<>nil) then begin Result:=obj^.handle; if (Result<>nil) then begin Result^.inc_ref('fetch_chunk_by_guest'); end; vm_track_object_deallocate(obj); //<-vm_track_map_next_object end; { node:=Default(t_jcode_chunk); node.start:=QWORD(src); rw_rlock(entry_chunk_lock); i:=entry_chunk.Find_le(@node); if (i.Item<>nil) then begin Result:=i.Item^; end; if (Result<>nil) then begin Result^.inc_ref; end; rw_runlock(entry_chunk_lock); } end; function next_chunk(node:p_jcode_chunk;src:Pointer):p_jcode_chunk; var //i:t_jcode_chunk_set.Iterator; obj:p_vm_track_object; begin Result:=nil; // obj:=vm_map_track_next(p_proc.p_vmspace, vm_offset_t(src), node^.tobj, H_JIT_CHUNK); if (obj<>nil) then begin Result:=obj^.handle; if (Result<>nil) then begin Result^.inc_ref('next_chunk'); end; vm_track_object_deallocate(obj); //<-vm_track_map_next_object end; { rw_rlock(entry_chunk_lock); i:=entry_chunk.find_be(node); if (i.Item<>nil) then begin i.Next; end; if (i.Item<>nil) then begin Result:=i.Item^; end; if (Result<>nil) then begin Result^.inc_ref; end; rw_runlock(entry_chunk_lock); } end; function fetch_blob_by_host(src:Pointer):p_jit_dynamic_blob; var //i:t_jcode_chunk_set.Iterator; node:p_jcode_chunk; prev:p_jit_dynamic_blob; blob:p_jit_dynamic_blob; begin Result:=nil; prev:=nil; // rw_rlock(entry_chunk_lock); node:=TAILQ_FIRST(@entry_chunk_list); //i:=entry_chunk.cbegin; //while (i.Item<>nil) do while (node<>nil) do begin //node:=i.Item^; //if (node<>nil) then if not node^.is_mark_del then begin blob:=node^.blob; if (blob<>nil) and (blob<>prev) then begin if blob^.cross_host(QWORD(src),QWORD(src)+1) then begin blob^.inc_ref('fetch_blob_by_host'); rw_runlock(entry_chunk_lock); Exit(blob); end; prev:=blob; end; end; //i.Next; node:=TAILQ_NEXT(node,@node^.entry); end; rw_runlock(entry_chunk_lock); end; function exist_jit_host(src:Pointer;tf_tip:PQWORD):Boolean; public; var blob:p_jit_dynamic_blob; begin blob:=fetch_blob_by_host(src); if (blob<>nil) then begin if (tf_tip<>nil) then begin rw_rlock(blob^.lock); tf_tip^:=blob^.find_guest_by_host(QWORD(src)); rw_runlock(blob^.lock); end; blob^.dec_ref('fetch_blob_by_host'); Result:=True; end else begin Result:=False; end; end; { procedure unmap_jit_cache(start,__end:QWORD); [public, alias:'kern_unmap_jit_cache']; var curr,next:p_jcode_chunk; blob:p_jit_dynamic_blob; begin curr:=fetch_chunk_by_guest(Pointer(start)); while (curr<>nil) do begin if (curr^.start>=__end) then Break; if curr^.cross_guest(start,__end) then begin blob:=curr^.blob; rw_wlock(blob^.lock); blob^.detach_chunk(curr); blob^.detach_entry(start,__end); rw_wunlock(blob^.lock); end; next:=next_chunk(curr); curr^.dec_ref; curr:=next; end; if (curr<>nil) then begin curr^.dec_ref; end; end; } function preload_entry(addr:Pointer):p_jit_entry_point; var curr,next:p_jcode_chunk; blob:p_jit_dynamic_blob; dest:QWORD; begin Result:=nil; curr:=fetch_chunk_by_guest(addr); while (curr<>nil) do begin //Writeln(HexStr(addr),':',HexStr(curr^.start,16),'..',HexStr(curr^.__end,16)); //if (QWORD(addr)0) then begin if (p_print_jit_preload) then begin Writeln('cache:',HexStr(addr),'->',HexStr(dest,16)); end; blob:=curr^.blob; rw_wlock(blob^.lock); if not curr^.is_mark_del then begin Result:=blob^.add_entry_point(addr,Pointer(dest)); end; rw_wunlock(blob^.lock); blob^.attach_entry(Result); Exit; end; next:=next_chunk(curr,addr); curr^.dec_ref('preload_entry'); curr:=next; end; if (curr<>nil) then begin curr^.dec_ref('preload_entry'); end; end; function hash_addr(addr:Pointer):Byte; inline; begin Result:=Byte(QWORD(addr) shr 4) xor Byte(QWORD(addr) shr 12); end; procedure ExecuteStop; assembler; nostackframe; public; asm //Return JIT->HLE end; const EXECUTE_MAGIC_ADDR:QWORD=QWORD($FFFFFFFFFCAFEDAD); function jmp_dispatcher(addr:Pointer;plt:p_jit_plt;from:Pointer):Pointer; public; label _start; var td :p_kthread; node :p_jit_entry_point; jctx :p_td_jctx; curr :p_jit_dynamic_blob; cache:p_jplt_cache; begin td:=curkthread; if (td=nil) then Exit(nil); //jit_state:=((td^.pcb_flags and PCB_IS_JIT)<>0); if not is_guest_addr(QWORD(addr)) then begin //switch to internal if (QWORD(addr)=EXECUTE_MAGIC_ADDR) then begin Exit(@ExecuteStop); end else if ((QWORD(addr) and UNRESOLVE_MAGIC_MASK)=UNRESOLVE_MAGIC_ADDR) then begin if exist_jit_host(from,@td^.td_frame.tf_rip) then begin test_unresolve_symbol(td,addr); end; end; writeln('not guest addr:0x',HexStr(addr)); Assert(False,'TODO'); //td^.td_teb^.jitcall:=addr; //Exit(@jit_jmp_internal); end; _start: if ((ppmap_get_prot(QWORD(addr)) and PAGE_PROT_EXECUTE)=0) then begin writeln('not excec:0x',HexStr(addr)); Assert(False,'TODO'); end; jctx:=@td^.td_jctx; cache:=jctx^.local_cache[hash_addr(addr)]; if (cache<>nil) then begin if (cache^.src=addr) then begin //jctx^.block:=cache^.blk; Result:=cache^.dst; if (InterlockedExchangeAdd64(QWORD(cache^.dest_block),0)=0) then begin //reset all cache:=nil; Result:=nil; jctx^.local_cache[hash_addr(addr)]:=nil; end else begin Exit; end; end else begin cache:=nil; end; end; node:=fetch_entry(addr); if (node=nil) then begin preload(addr); goto _start; end; //jctx:=@td^.td_jctx; //curr:=jctx^.block; //curr:=fetch_blob_by_host(plt); //curr:=plt^.block; if (plt<>nil) then begin cache:=plt^.cache; curr:=cache^.self_block; end else begin curr:=nil; end; if (curr=nil) or (plt=nil) then begin //jctx^.block:=node^.blob; end else begin cache:=curr^.add_plt_cache(plt,node^.src,node^.dst,node^.blob); jctx^.local_cache[hash_addr(addr)]:=cache; //jctx^.block:=node^.blob; Assert(cache<>nil); Assert(cache^.src<>nil); Assert(cache^.dst<>nil); //one element plt cache System.InterlockedExchange(plt^.cache,cache); end; Result:=node^.dst; // node^.dec_ref('fetch_entry'); end; function t_jcode_chunk.c(n1,n2:p_jcode_chunk):Integer; begin Result:=Integer(n1^.start>n2^.start)-Integer(n1^.start0) then Exit; Result:=Integer(n1^.hash>n2^.hash)-Integer(n1^.hashn2^.plt)-Integer(n1^.plt0) then Exit; Result:=Integer(n1^.src>n2^.src)-Integer(n1^.srcnil_link) then begin if (link_prev.offset<>link_curr.offset) then begin Writeln('oaddr:',HexStr(curr),'..',HexStr(next),' prev:',HexStr(prev)); Writeln('table:',HexStr(blob^.base+link_prev.offset),'<>',HexStr(blob^.base+link_curr.offset)); print_disassemble(blob^.base+link_prev.offset,link_next.offset-link_prev.offset); Assert(False); end; end; original:=QWORD(next)-QWORD(curr); recompil:=link_next.offset-link_curr.offset; if (original>16) or (recompil>512) then begin Writeln('0x',HexStr(curr)); Writeln(original,':',recompil); Assert(False); end; table[i].original:=Byte(original); table[i].recompil:=Byte(recompil); table[i].LF_JMP :=ord((clabel^.flags and LF_JMP)<>0); { writeln('|0x',HexStr(curr),'..',HexStr(next), ':0x',HexStr(link_curr.offset,8),'..',HexStr(link_next.offset,8), ':',i); } prev:=curr; link_prev:=link_next; Inc(i); curr:=next; end; //get last addr //jcode^.d_end:=QWORD(blob^.base)+clabel^.link_curr.offset; recompil:=0; for i:=0 to count-1 do begin recompil:=recompil+table[i].recompil; end; jcode^.d_end:=QWORD(jcode^.dest)+recompil; if (p_print_jit_preload) then begin Writeln('build_chunk:0x',HexStr(jcode^.dest,16),'..',HexStr(jcode^.d_end,16),':',count); end; //writeln('[0x',HexStr(start,16),':0x',HexStr(__end,16),':',count); end; procedure build_blob(var ctx:t_jit_context2; blob:p_jit_dynamic_blob; start,__end:QWORD); var count:QWORD; clabel:t_jit_context2.p_label; link_prev:t_jit_i_link; link_curr:t_jit_i_link; link_next:t_jit_i_link; prev:QWORD; curr:QWORD; next:QWORD; begin count:=0; curr:=start; prev:=0; link_prev:=nil_link; //get count while (QWORD(curr)<__end) do begin clabel:=ctx.get_label(Pointer(curr)); if (clabel=nil) then begin Writeln('(clabel=nil) 0x',HexStr(curr,16)); Assert(false); end; //Writeln('clabel:0x',HexStr(QWORD(blob^.base)+clabel^.link_curr.offset,16)); next:=QWORD(clabel^.next); link_curr:=clabel^.link_curr; link_next:=clabel^.link_next; if (link_prev<>nil_link) then begin if (link_prev.offset<>link_curr.offset) then begin //devide chunk build_chunk(ctx,blob,start,curr,count); start:=curr; count:=0; end; end; prev:=curr; link_prev:=link_next; Inc(count); curr:=next; end; build_chunk(ctx,blob,start,__end,count); end; function build(var ctx:t_jit_context2):p_jit_dynamic_blob; var addr:Pointer; blob:p_jit_dynamic_blob; entry_point:t_jit_context2.p_entry_point; chunk:p_jit_code_chunk; start:QWORD; __end:QWORD; //F:THandle; begin Result:=nil; if (ctx.builder.GetMemSize=0) then Exit; blob:=new_blob(ctx.builder.GetMemSize); ctx.builder.SaveTo(blob^.base,ctx.builder.GetMemSize); blob^.plta:=blob^.base+ctx.builder.GetPltStart; blob^.pltc:=ctx.builder.APltCount; blob^.init_plt; if (p_print_jit_preload) then begin Writeln('build:0x',HexStr(ctx.text_start,16),'->0x',HexStr(blob^.base),'..',HexStr(blob^.base+blob^.size)); end; //F:=FileCreate('recompile.bin'); //FileWrite(F,blob^.base^,ctx.builder.GetMemSize); //FileClose(F); //copy entrys entry_point:=ctx.entry_list; while (entry_point<>nil) do begin addr:=blob^.base+entry_point^.instruction.offset; // blob^.add_entry_point(entry_point^.src,addr); // entry_point:=entry_point^.next; end; start:=0; __end:=0; //copy chunks chunk:=TAILQ_FIRST(@ctx.builder.ACodeChunkList); while (chunk<>nil) do begin if (chunk^.start=chunk^.__end) then begin //no instr end else begin // {if (t_point_type(chunk^.data)=fpInvalid) then begin //skip end else } if (__end=chunk^.start) then begin //expand __end:=chunk^.__end; end else begin //save if (start<>0) then begin //build prev saved build_blob(ctx,blob,start,__end); end; //new start:=chunk^.start; __end:=chunk^.__end; end; end; // chunk:=TAILQ_NEXT(chunk,@chunk^.entry); end; if (start<>0) then begin //build prev saved build_blob(ctx,blob,start,__end); end; //blob^.attach; Result:=blob; end; function fetch_entry(src:Pointer):p_jit_entry_point; var data:PPointer; begin Result:=nil; rw_rlock(entry_hamt_lock); data:=HAMT_search64(@entry_hamt,QWORD(src)); if (data<>nil) then begin Result:=data^; end; if (Result<>nil) then begin Result^.inc_ref('fetch_entry'); end; rw_runlock(entry_hamt_lock); end; function exist_entry(src:Pointer):Boolean; var entry:p_jit_entry_point; begin entry:=fetch_entry(src); if (entry<>nil) then begin entry^.dec_ref('fetch_entry'); Result:=True; end else begin Result:=False; end; end; // function new_blob(_size:ptruint):p_jit_dynamic_blob; begin Result:=AllocMem(SizeOf(t_jit_dynamic_blob)); Result^.alloc_base(_size); TAILQ_INIT(@Result^.jpltc_attc); end; // procedure t_jit_entry_point.inc_ref(name:pchar); begin blob^.inc_ref(name); end; procedure t_jit_entry_point.dec_ref(name:pchar); begin blob^.dec_ref(name); end; // procedure t_jcode_chunk.inc_ref(name:pchar); begin blob^.inc_ref(name); end; procedure t_jcode_chunk.dec_ref(name:pchar); begin blob^.dec_ref(name); end; function t_jcode_chunk.is_mark_del:Boolean; begin Result:=(tobj=nil); end; // procedure t_jit_dynamic_blob.inc_ref(name:pchar); begin //Writeln('inc_ref:0x',HexStr(@self),' ',name); System.InterlockedIncrement(refs); end; procedure t_jit_dynamic_blob.dec_ref(name:pchar); begin //Writeln('dec_ref:0x',HexStr(@self),' ',name); if (System.InterlockedDecrement(refs)=0) then begin //Writeln('free:',HexStr(@self),' ',name); Free; end; end; procedure t_jit_dynamic_blob.inc_attach_count; begin System.InterlockedIncrement(attach_count); end; function t_jit_dynamic_blob.dec_attach_count:Boolean; begin Result:=(System.InterlockedDecrement(attach_count)=0); end; function t_jit_dynamic_blob.find_guest_by_host(addr:QWORD):QWORD; var node:p_jcode_chunk; begin //Writeln('_ind_guest_by_host:0x',HexStr(base),' 0x',HexStr(base+size),' 0x',HexStr(addr,16)); Result:=0; node:=chunk_list; while (node<>nil) do begin Result:=node^.find_guest_by_host(addr); if (Result<>0) then Exit; node:=node^.next; end; end; function t_jit_dynamic_blob.cross_host(c_start,c___end:QWORD):Boolean; begin Result:=(c___end>QWORD(base)) and (c_start<(QWORD(base)+size)); end; // procedure t_jit_dynamic_blob.Free; var node,next:p_jit_entry_point; begin node:=entry_list; while (node<>nil) do begin next:=node^.next; free_entry_point(node); node:=next; end; free_base; //TODO: GC FREE FreeMem(@Self); end; function t_jit_dynamic_blob.add_entry_point(src,dst:Pointer):p_jit_entry_point; begin if (src=nil) or (dst=nil) then Exit; Result:=AllocMem(Sizeof(t_jit_entry_point)); Result^.next:=entry_list; Result^.blob:=@Self; Result^.src :=src; Result^.dst :=dst; // entry_list:=Result; end; procedure t_jit_dynamic_blob.free_entry_point(node:p_jit_entry_point); begin FreeMem(node); end; procedure t_jit_dynamic_blob.init_plt; var i:Integer; begin if (pltc<>0) then begin plt_stub.self_block:=@Self; plt_stub.dest_block:=@Self; For i:=0 to pltc-1 do begin plta[i].cache:=@plt_stub; //plta[i].block:=@Self; end; end; end; procedure t_jit_dynamic_blob.attach_plt_cache(uplock:p_jit_dynamic_blob;node:p_jplt_cache); begin Self.inc_ref('attach_plt_cache'); if (uplock<>@Self) then begin rw_wlock(lock); end; TAILQ_INSERT_TAIL(@jpltc_attc,node,@node^.entry); if (uplock<>@Self) then begin rw_wunlock(lock); end; end; procedure t_jit_dynamic_blob.detach_plt_cache(uplock:p_jit_dynamic_blob;node:p_jplt_cache); begin if (node^.entry.tqe_prev<>nil) then begin if (uplock<>@Self) then begin rw_wlock(lock); end; TAILQ_REMOVE(@jpltc_attc,node,@node^.entry); node^.entry:=Default(TAILQ_ENTRY); if (uplock<>@Self) then begin rw_wunlock(lock); end; Self.dec_ref('attach_plt_cache'); end; end; procedure reset_plt(node:p_jplt_cache); var blk:p_jit_dynamic_blob; plt:p_jit_plt; begin blk:=node^.self_block; plt:=node^.plt; if (plt<>nil) and (blk<>nil) then begin //one element plt reset System.InterlockedCompareExchange(plt^.cache,@blk^.plt_stub,node); end; end; procedure t_jit_dynamic_blob.detach_all_attc; var node,next:p_jplt_cache; begin node:=TAILQ_FIRST(@jpltc_attc); while (node<>nil) do begin next:=TAILQ_NEXT(node,@node^.entry); TAILQ_REMOVE(@jpltc_attc,node,@node^.entry); node^.entry:=Default(TAILQ_ENTRY); reset_plt(node); //force deref if (System.InterlockedCompareExchange(node^.dest_block,nil,@Self)=@Self) then begin Self.dec_ref('add_plt_cache'); end; node:=next; end; if TAILQ_FIRST(@jpltc_attc)<>nil then begin Assert(false); end; end; procedure t_jit_dynamic_blob.detach_all_curr; var node:p_jplt_cache; blk :p_jit_dynamic_blob; begin node:=jpltc_curr.Min; while (node<>nil) do begin jpltc_curr.Delete(node); reset_plt(node); blk:=System.InterlockedExchange(node^.dest_block,nil); if (blk<>nil) then begin blk^.detach_plt_cache(@Self,node); end; //TODO: GC FREE FreeMem(node); node:=jpltc_curr.Min; end; end; procedure t_jit_dynamic_blob.detach_threads; var ttd:p_kthread; call_ret_cache:PQWORD; bend:QWORD; src:QWORD; i:Integer; begin bend:=QWORD(base)+size; threads_lock; ttd:=TAILQ_FIRST(get_p_threads); while (ttd<>nil) do begin // call_ret_cache:=ttd^.td_jctx.call_ret_cache; if (call_ret_cache<>nil) then For i:=0 to (64*1024 div 16)-1 do begin src:=-call_ret_cache[i*2]; //-(-src) if (src>=QWORD(base)) and (src=QWORD(base)) and (srcnil); Assert(dest_block<>nil); node.plt:=plt; //key node.src:=src; //key node.neg:=Pointer(-QWORD(src)); repeat old_blk:=nil; //high threads concurrency rw_wlock(lock); Result:=jpltc_curr.Find(@node); if (Result<>nil) then begin //update Result^.dst:=dst; // old_blk:=System.InterlockedExchange(Result^.dest_block,dest_block); if (old_blk<>dest_block) then begin if (old_blk<>nil) and (old_blk=@Self) then begin //detach immediately old_blk^.detach_plt_cache(@Self,Result); end; if (dest_block=@Self) then begin //attach immediately dest_block^.attach_plt_cache(@Self,Result); end; end; end; rw_wunlock(lock); if (Result<>nil) then begin if (old_blk<>dest_block) then begin if (old_blk<>nil) and (old_blk<>@Self) then begin //detach deferred old_blk^.detach_plt_cache(@Self,Result); end; // if (dest_block<>@Self) then begin //attach deferred dest_block^.attach_plt_cache(@Self,Result); end; end; // Break; end else begin Result:=AllocMem(Sizeof(t_jplt_cache)); Result^.plt:=plt; //key Result^.src:=src; //key Result^.neg:=Pointer(-QWORD(src)); Result^.dst:=dst; Result^.self_block:=@Self; Result^.dest_block:=dest_block; // rw_wlock(lock); _insert:=jpltc_curr.Insert(Result); if _insert and (dest_block=@Self) then begin //attach immediately dest_block^.attach_plt_cache(@Self,Result); end; rw_wunlock(lock); // if _insert then begin //attach deferred if (dest_block<>@Self) then begin dest_block^.attach_plt_cache(@Self,Result); end; // Break; end else begin FreeMem(Result); Result:=nil; end; end; until false; end; function t_jit_dynamic_blob.new_chunk(count:QWORD):p_jcode_chunk; begin Result:=AllocMem(SizeOf(t_jcode_chunk)+SizeOf(t_jinstr_len)*count); Result^.count:=count; Result^.blob :=@Self; // Result^.next:=chunk_list; chunk_list:=Result; end; procedure t_jit_dynamic_blob.alloc_base(_size:ptruint); begin base:=nil; size:=_size; ///md_mmap(base,size,VM_RWX); mchunk:=p_alloc(nil,_size,False); base:=@mchunk^.body; end; procedure t_jit_dynamic_blob.free_base; begin p_free(mchunk); ////md_unmap(base,size); base:=nil; size:=0; end; // function t_jcode_chunk.find_host_by_guest(addr:QWORD):QWORD; var i,src,dst:QWORD; _table:p_jinstr_len; begin Result:=0; if (addr>=start) and (addr<__end) then if (count<>0) then begin src:=start; dst:=dest; _table:=@table; For i:=0 to count-1 do begin if (src=addr) then begin Exit(dst); end else if (src>addr) then begin Exit(0); end; src:=src+_table[i].original; dst:=dst+_table[i].recompil; end; end; end; function t_jcode_chunk.find_guest_by_host(addr:QWORD):QWORD; var i,src,dst:QWORD; _table:p_jinstr_len; begin Result:=0; //Writeln('find_guest_by_host:0x',HexStr(dest,16),' 0x',HexStr(d_end,16),' 0x',HexStr(addr,16)); if (addr>=dest) and (addr<=d_end) then if (count<>0) then begin src:=start; dst:=dest; _table:=@table; For i:=0 to count-1 do begin if (addr>=dst) then begin Result:=src; end else if (dst>addr) then begin Exit; end; src:=src+_table[i].original; dst:=dst+_table[i].recompil; end; end; end; function t_jcode_chunk.cross_guest(c_start,c___end:QWORD):Boolean; begin Result:=(c___end>start) and (c_start<__end); end; function t_jcode_chunk.cross_host(c_start,c___end:QWORD):Boolean; begin Result:=(c___end>dest) and (c_startnil); if (data^<>node) then begin old:=data^; data^:=node; end; node^.entry_public:=1; rw_wunlock(entry_hamt_lock); end; procedure t_jit_dynamic_blob.attach_all_entry; var node,next:p_jit_entry_point; begin node:=entry_list; while (node<>nil) do begin next:=node^.next; attach_entry(node); node:=next; end; end; procedure t_jit_dynamic_blob.attach_all_chunk; //var // node,next:p_jcode_chunk; begin blob_track(@self); { node:=chunk_list; while (node<>nil) do begin next:=node^.next; node^.inc_ref; rw_wlock(entry_chunk_lock); entry_chunk.Insert(node); rw_wunlock(entry_chunk_lock); node:=next; end; } end; procedure t_jit_dynamic_blob.attach; begin attach_all_entry; attach_all_chunk; end; function t_jit_dynamic_blob.detach_entry(node:p_jit_entry_point):Boolean; var old:p_jit_entry_point; begin if (node^.entry_public=0) then Exit; old:=nil; rw_wlock(entry_hamt_lock); HAMT_delete64(@entry_hamt,QWORD(node^.src),@old); rw_wunlock(entry_hamt_lock); if (old=node) then begin if (System.InterlockedExchange(node^.entry_public,0)<>0) then begin Result:=self.dec_attach_count; node^.dec_ref('attach_entry'); end; end; end; procedure t_jit_dynamic_blob.detach_all_entry; var node,next:p_jit_entry_point; begin node:=entry_list; while (node<>nil) do begin next:=node^.next; detach_entry(node); node:=next; end; end; procedure t_jit_dynamic_blob.detach_entry(c_start,c___end:QWORD); var node,next:p_jit_entry_point; s:QWORD; begin node:=entry_list; while (node<>nil) do begin next:=node^.next; s:=QWORD(node^.src); if (s>=c_start) and (snil) then begin _vm_map_track_delete_deferred(p_proc.p_vmspace,tobj); end; removed:=False; if (node^.entry.tqe_next<>nil) and (node^.entry.tqe_prev<>nil) then begin rw_wlock(entry_chunk_lock); if (node^.entry.tqe_next<>nil) and (node^.entry.tqe_prev<>nil) then begin TAILQ_REMOVE(@entry_chunk_list,node,@node^.entry); node^.entry:=Default(TAILQ_ENTRY); removed:=True; end; rw_wunlock(entry_chunk_lock); end; if removed then begin node^.dec_ref('blob_track'); end; end; procedure t_jit_dynamic_blob.detach_all_chunk; var node,next:p_jcode_chunk; begin node:=chunk_list; while (node<>nil) do begin next:=node^.next; detach_chunk(node); node:=next; end; end; procedure t_jit_dynamic_blob.detach; begin inc_ref('detach'); rw_wlock(lock); detach_all_entry; detach_all_attc; detach_all_curr; detach_threads; detach_all_chunk; rw_wunlock(lock); dec_ref('detach'); end; procedure delete_jit_cache(chunk:p_jcode_chunk); var blob:p_jit_dynamic_blob; begin if (chunk=nil) then Exit; //delete full in blob blob:=chunk^.blob; blob^.detach; chunk^.dec_ref('tobj'); end; function on_destroy(handle:Pointer):Integer; var tobj:Pointer; begin //Writeln('on_destroy:',HexStr(handle)); tobj:=System.InterlockedExchange(p_jcode_chunk(handle)^.tobj,nil); //mark delete if (tobj=nil) then begin Exit(DO_DELETE); end; delete_jit_cache(p_jcode_chunk(handle)); Result:=DO_DELETE; end; function on_trigger(handle:Pointer;mode:T_TRIGGER_MODE):Integer; begin Result:=DO_NOTHING; case mode of M_CPU_WRITE :; M_DMEM_WRITE:; else Exit; end; //TODO Result:=DO_INCREMENT; end; procedure blob_track(blob:p_jit_dynamic_blob); var node:p_jcode_chunk; tobj:p_vm_track_object; begin node:=blob^.chunk_list; while (node<>nil) do begin if (node^.start<>node^.__end) then begin node^.inc_ref('blob_track'); rw_wlock(entry_chunk_lock); TAILQ_INSERT_TAIL(@entry_chunk_list,node,@node^.entry); rw_wunlock(entry_chunk_lock); node^.inc_ref('tobj'); tobj:=vm_track_object_allocate(node,node^.start,node^.__end,H_JIT_CHUNK,PAGE_TRACK_W); tobj^.on_destroy:=@on_destroy; tobj^.on_trigger:=@on_trigger; node^.tobj:=tobj; vm_map_track_insert(p_proc.p_vmspace,tobj); vm_track_object_deallocate(tobj); //<-vm_track_object_allocate end; // node:=node^.next; end; end; end.