unit kern_jit; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses ps4libdoc, mqueue, systm, x86_fpdbgdisas, x86_jit, kern_jit_ctx; var print_asm :Boolean=False; debug_info:Boolean=False; procedure pick(var ctx:t_jit_context2;preload:Pointer); implementation uses sysutils, time, vm, vmparam, vm_pmap_prot, vm_pmap, vm_map, vm_tracking_map, sys_bootparam, kern_proc, kern_jit_ops, kern_jit_ops_sse, kern_jit_ops_avx, kern_jit_dynamic, kern_jit_test, kern_jit_asm, kern_thr, subr_backtrace; procedure jit_assert(tf_rip:QWORD); var td:p_kthread; begin td:=curkthread; jit_save_to_sys_save(td); td^.td_frame.tf_rip:=tf_rip; print_error_td('Assert in guest code!'); Assert(false); end; procedure _jit_assert; assembler; nostackframe; asm call jit_save_ctx mov %r14,%rdi jmp jit_assert end; procedure jit_system_error(tf_rip:QWORD); var td:p_kthread; begin td:=curkthread; jit_save_to_sys_save(td); td^.td_frame.tf_rip:=tf_rip; print_error_td('System error in guest code!'); Assert(false); end; procedure _jit_system_error; assembler; nostackframe; asm call jit_save_ctx mov %r14,%rdi jmp jit_system_error end; procedure jit_unknow_int; begin Assert(False,'jit_unknow_int'); end; procedure jit_exit_proc(tf_rip:QWORD); var td:p_kthread; begin td:=curkthread; jit_save_to_sys_save(td); td^.td_frame.tf_rip:=tf_rip; print_error_td('TODO:jit_exit_proc'); Assert(False); end; procedure _jit_exit_proc; assembler; nostackframe; asm call jit_save_ctx mov %r14,%rdi jmp jit_exit_proc end; //in:r14(addr) out:r14(addr) procedure op_jmp_plt(var ctx:t_jit_context2); var plt :t_jit_i_link; link_jcxz:t_jit_i_link; link_jmp :t_jit_i_link; //link_jne :t_jit_i_link; begin with ctx.builder do begin movq(r13,rcx); //save rcx (break jit_frame) plt:=leap(r15); movq(r15,[r15]); //plt^ movq(rcx,[r15+(@p_jplt_cache_asm(nil)^.neg)]); //plt^.neg leaq(rcx,[rcx+r14]); link_jcxz:=jcxz(nil_link,as64,os8); //plt cache fail movq(rcx,r13); //restore rcx //restore jit_frame in jit_jmp_dispatch //reload plt link leap(r15,plt); call_far(@jit_jmp_dispatch); //input:r14,r15 out:r14 //exit: link_jmp:=jmp(nil_link,os8); //jmp _exit //plt cache succes link_jcxz.target:=ctx.builder.get_curr_label.after; movq(rcx,r13); //restore rcx //restore jit_frame movq(r13,[GS +teb_thread]); leaq(r13,[r13+jit_frame_offset]); movq(r14,[r15+(@p_jplt_cache_asm(nil)^.dst)]); //plt^.dst //exit link_jmp.target:=ctx.builder.get_curr_label.after; ///////////////////////////////////////// { plt:=leap(r15); movq(r15,[r15]); //plt^ pushfq(os64); cmpq(r14,[r15+(@p_jplt_cache_asm(nil)^.src)]); //next instr.target:=get_curr_label.after; link_jne:=jcc(OPSc_nz,nil_link,os8); //jne _non_cache popfq(os64); //get blk movq(r14,[r15+(@p_jplt_cache_asm(nil)^.blk)]); //save current block movq([r13+ ( -(@p_kthread(nil)^.td_frame.tf_r13) +(@p_kthread(nil)^.td_jctx.block) ) ],r14); //get dst movq(r14,[r15+(@p_jplt_cache_asm(nil)^.dst)]); //interrupt //jmp %gs:teb.jit_trp link_jmp:=jmp(nil_link,os8); //jmp _exit //_non_cache: link_jne.target:=ctx.builder.get_curr_label.after; popfq(os64); leap(r15,plt); call_far(@jit_jmp_dispatch); //input:r14,r15 out:r14 //_exit: link_jmp.target:=ctx.builder.get_curr_label.after; } end; end; procedure op_jmp_dispatcher(var ctx:t_jit_context2;cb:t_jit_cb); begin with ctx.builder do begin //leap(r15); //call_far(@jit_jmp_plt_cache); //input:r14,r15 out:r14 op_jmp_plt(ctx); if (cb<>nil) then begin cb(ctx); end; jmp(r14); end; end; function op_add_local_cache(var ctx:t_jit_context2):t_jit_i_link; begin with ctx.builder do begin movq(r15,[r13-jit_frame_offset+(@p_kthread(nil)^.td_jctx.call_ret_cache)]); movi(r15w,Word(QWORD(ctx.ptr_next)*16)); Result:=leaj(r14,[rip+$7FFFFFFF],nil_link); //set deferred movq([r15+8,os64],r14); //dst op_set_mem_imm(ctx,[r15+0,os64],-Int64(ctx.ptr_next),[not_use_r_tmp1]); //-src end; end; procedure op_call_dispatcher(var ctx:t_jit_context2;cb:t_jit_cb); begin with ctx.builder do begin //leap(r15); //call_far(@jit_jmp_plt_cache); //input:r14,r15 out:r14 op_jmp_plt(ctx); if (cb<>nil) then begin cb(ctx); end; jmp(r14); end; end; //r14 procedure op_ret_dispatcher(var ctx:t_jit_context2;cb:t_jit_cb); var link_jcxz:t_jit_i_link; link_jmp :t_jit_i_link; begin with ctx.builder do begin movq(r15,[r13-jit_frame_offset+(@p_kthread(nil)^.td_jctx.call_ret_cache)]); movq(r13,rcx); //save rcx xorq(ecx,ecx); leaq(rcx,[r14*8+rcx]); leaq(r15w,[rcx+rcx]); //Word(r14*16) movq(rcx,[r15]); //-src leaq(rcx,[r14+rcx]); //r14+(-src) link_jcxz:=jcxz(nil_link,as64,os8); //r14+(-src)=0 //plt cache fail movq(rcx,r13); //restore rcx //restore jit_frame in jit_jmp_dispatch //stub plt link xorq(r15d,r15d); call_far(@jit_jmp_dispatch); //input:r14,r15 out:r14 //exit: link_jmp:=jmp(nil_link,os8); //jmp _exit //plt cache succes link_jcxz.target:=ctx.builder.get_curr_label.after; movq(rcx,r13); //restore rcx //restore jit_frame movq(r13,[GS +teb_thread]); leaq(r13,[r13+jit_frame_offset]); movq(r14,[r15+8]); //dst //exit link_jmp.target:=ctx.builder.get_curr_label.after; //leap(r15); //call_far(@jit_jmp_plt_cache); //input:r14,r15 out:r14 //op_jmp_plt(ctx); if (cb<>nil) then begin cb(ctx); end; jmp(r14); end; end; procedure trim_flow(var ctx:t_jit_context2); begin ctx.trim:=True; end; procedure op_push_rip_part0(var ctx:t_jit_context2); var stack:TRegValue; imm:Int64; begin //lea rsp,[rsp-8] //mov [rsp],r14 with ctx.builder do begin stack:=r_tmp0; op_load_rsp(ctx,stack); leaq(stack,[stack-8]); op_uplift(ctx,stack,os64); //in/out:r14 imm:=Int64(ctx.ptr_next); if (classif_offset_se64(imm)=os64) then begin if (classif_offset_u64(imm)=os64) then begin //64bit imm movi64(r_tmp1,imm); movq([stack],r_tmp1); end else begin //32bit zero extend movi(new_reg_size(r_tmp1,os32),imm); movq([stack],r_tmp1); end; end else begin //32bit sign extend movi([stack,os64],imm); end; //For transactionality, //first we move the memory, //then we update the register //[op_uplift] op_load_rsp(ctx,stack); //[op_uplift] leaq(stack,[stack-8]); //op_save_rsp(ctx,stack); end; end; procedure op_push_rip_part1(var ctx:t_jit_context2); var stack:TRegValue; begin //lea rsp,[rsp-8] //mov [rsp],r14 with ctx.builder do begin stack:=r_tmp1; //For transactionality, //first we move the memory, //then we update the register op_load_rsp(ctx,stack); leaq(stack,[stack-8]); op_save_rsp(ctx,stack); end; end; procedure op_pop_rip_part0(var ctx:t_jit_context2;imm:Word); //out:r14 var stack:TRegValue; begin //mov r14,[rsp] //lea rsp,[rsp+8+imm] with ctx.builder do begin stack:=r_tmp0; op_load_rsp(ctx,stack); op_uplift(ctx,stack,os64); //in/out:r14 //load to tmp movq(r_tmp0,[stack]); //For transactionality, //first we move the memory, //then we update the register //[op_uplift] op_load_rsp(ctx,stack); //leaq(stack,[stack+8+imm]); //op_save_rsp(ctx,stack); //out:r14 end; end; procedure op_pop_rip_part1(var ctx:t_jit_context2); var stack:TRegValue; begin //mov r14,[rsp] //lea rsp,[rsp+8+imm] with ctx.builder do begin stack:=r_tmp1; //For transactionality, //first we move the memory, //then we update the register op_load_rsp(ctx,stack); leaq(stack,[stack+8+ctx.imm]); op_save_rsp(ctx,stack); end; end; procedure op_call(var ctx:t_jit_context2); var id:t_jit_i_link; ofs:Int64; dst:Pointer; new1,new2:TRegValue; link:t_jit_i_link; ret_dst:t_jit_i_link; begin ctx.label_flags:=ctx.label_flags or LF_JMP; ret_dst:=op_add_local_cache(ctx); op_push_rip_part0(ctx); if (ctx.din.Operand[1].RegValue[0].AType=regNone) then begin //imm offset ofs:=0; GetTargetOfs(ctx.din,ctx.code,1,ofs); dst:=ctx.ptr_next+ofs; if ctx.is_text_addr(QWORD(dst)) and (not exist_entry(dst)) then begin //near op_push_rip_part1(ctx); link:=ctx.get_link(dst); id:=ctx.builder.jmp(link); if (link<>nil_link) then begin ctx.add_forward_point(fpCall,dst); end else begin ctx.add_forward_point(fpCall,id,dst); end; end else begin op_set_r14_imm(ctx,Int64(dst)); // op_call_dispatcher(ctx,@op_push_rip_part1); end; end else if is_memory(ctx.din) then begin new1:=new_reg_size(r_tmp0,ctx.din.Operand[1]); // build_lea(ctx,1,new1,[{inc8_rsp,}code_ref]); // op_uplift(ctx,new1,os64); //in/out:r14 // ctx.builder.movq(new1,[new1]); // op_call_dispatcher(ctx,@op_push_rip_part1); end else if is_preserved(ctx.din) then begin new1:=new_reg_size(r_tmp0,ctx.din.Operand[1]); // op_load(ctx,new1,1); // if is_rsp(ctx.din.Operand[1].RegValue[0]) then begin ctx.builder.leaq(new1,[new1+8]); end; // op_call_dispatcher(ctx,@op_push_rip_part1); end else begin new1:=new_reg_size(r_tmp0,ctx.din.Operand[1]); new2:=new_reg(ctx.din.Operand[1]); // ctx.builder.movq(new1,new2); // op_call_dispatcher(ctx,@op_push_rip_part1); end; if (ret_dst<>nil_link) then begin ret_dst.target:=ctx.builder.get_curr_label.after; end; // ctx.add_forward_point(fpCall,ctx.ptr_next); end; procedure op_ret(var ctx:t_jit_context2); var imm:Int64; begin ctx.label_flags:=ctx.label_flags or LF_JMP; imm:=0; GetTargetOfs(ctx.din,ctx.code,1,imm); // ctx.imm:=imm; // op_pop_rip_part0(ctx,imm); //out:r14 // op_ret_dispatcher(ctx,@op_pop_rip_part1); // trim_flow(ctx); end; procedure op_jmp(var ctx:t_jit_context2); var id:t_jit_i_link; ofs:Int64; dst:Pointer; new1,new2:TRegValue; link:t_jit_i_link; begin ctx.label_flags:=ctx.label_flags or LF_JMP; if (ctx.din.Operand[1].RegValue[0].AType=regNone) then begin //imm offset ofs:=0; GetTargetOfs(ctx.din,ctx.code,1,ofs); dst:=ctx.ptr_next+ofs; if ctx.is_text_addr(QWORD(dst)) and (not exist_entry(dst)) then begin //near link:=ctx.get_link(dst); id:=ctx.builder.jmp(link); if (link<>nil_link) then begin ctx.add_forward_point(fpCall,dst); end else begin ctx.add_forward_point(fpCall,id,dst); end; end else begin op_set_r14_imm(ctx,Int64(dst)); // op_jmp_dispatcher(ctx,nil); end; end else if is_memory(ctx.din) then begin new1:=new_reg_size(r_tmp0,ctx.din.Operand[1]); // build_lea(ctx,1,new1,[code_ref]); // op_uplift(ctx,new1,os64); //in/out:r14 // ctx.builder.movq(new1,[new1]); // op_jmp_dispatcher(ctx,nil); end else if is_preserved(ctx.din) then begin new1:=new_reg_size(r_tmp0,ctx.din.Operand[1]); // op_load(ctx,new1,1); // op_jmp_dispatcher(ctx,nil); end else begin new1:=new_reg_size(r_tmp0,ctx.din.Operand[1]); new2:=new_reg(ctx.din.Operand[1]); // ctx.builder.movq(new1,new2); // op_jmp_dispatcher(ctx,nil); end; // trim_flow(ctx); end; function invert_cond(s:TOpCodeSuffix):TOpCodeSuffix; begin case s of OPSc_o :Result:=OPSc_no; OPSc_no :Result:=OPSc_o; OPSc_b :Result:=OPSc_nb; OPSc_nb :Result:=OPSc_b; OPSc_z :Result:=OPSc_nz; OPSc_nz :Result:=OPSc_z; OPSc_be :Result:=OPSc_nbe; OPSc_nbe:Result:=OPSc_be; OPSc_s :Result:=OPSc_ns; OPSc_ns :Result:=OPSc_s; OPSc_p :Result:=OPSc_np; OPSc_np :Result:=OPSc_p; OPSc_l :Result:=OPSc_nl; OPSc_nl :Result:=OPSc_l; OPSc_le :Result:=OPSc_nle; OPSc_nle:Result:=OPSc_le; OPSc_e :Result:=OPSc_ne; OPSc_ne :Result:=OPSc_e; OPSc_u :Result:=OPSc_nu; OPSc_nu :Result:=OPSc_u; else; Assert(false,'invert_cond'); end; end; procedure op_jcc(var ctx:t_jit_context2); var id1:t_jit_i_link; //id2:t_jit_i_link; ofs:Int64; dst:Pointer; link:t_jit_i_link; begin ctx.label_flags:=ctx.label_flags or LF_JMP; ofs:=0; GetTargetOfs(ctx.din,ctx.code,1,ofs); dst:=ctx.ptr_next+ofs; if ctx.is_text_addr(QWORD(dst)) and (not exist_entry(dst)) then begin //near link:=ctx.get_link(dst); id1:=ctx.builder.jcc(ctx.din.OpCode.Suffix,link); if (link<>nil_link) then begin ctx.add_forward_point(fpCall,dst); end else begin ctx.add_forward_point(fpCall,id1,dst); end; end else begin //far //invert cond jump id1:=ctx.builder.jcc(invert_cond(ctx.din.OpCode.Suffix),nil_link,os8); op_set_r14_imm(ctx,Int64(dst)); op_jmp_dispatcher(ctx,nil); id1.target:=ctx.builder.get_curr_label.after; { id1:=ctx.builder.jcc(ctx.din.OpCode.Suffix,nil_link,os8); id2:=ctx.builder.jmp(nil_link,os8); id1._label:=ctx.builder.get_curr_label.after; op_set_r14_imm(ctx,Int64(dst)); op_jmp_dispatcher(ctx,nil); id2._label:=ctx.builder.get_curr_label.after; } end; end; procedure op_loop(var ctx:t_jit_context2); var id1,id2:t_jit_i_link; ofs:Int64; dst:Pointer; link:t_jit_i_link; begin ctx.label_flags:=ctx.label_flags or LF_JMP; ofs:=0; GetTargetOfs(ctx.din,ctx.code,1,ofs); dst:=ctx.ptr_next+ofs; if ctx.is_text_addr(QWORD(dst)) and (not exist_entry(dst)) then begin //near link:=ctx.get_link(dst); //case handling in micro core id1:=ctx.builder.loop(ctx.din.OpCode.Suffix,link,ctx.dis.AddressSize); if (link<>nil_link) then begin ctx.add_forward_point(fpCall,dst); end else begin ctx.add_forward_point(fpCall,id1,dst); end; end else begin //far id1:=ctx.builder.loop(ctx.din.OpCode.Suffix,nil_link,ctx.dis.AddressSize,os8); id2:=ctx.builder.jmp(nil_link,os8); id1.target:=ctx.builder.get_curr_label.after; op_set_r14_imm(ctx,Int64(dst)); op_jmp_dispatcher(ctx,nil); id2.target:=ctx.builder.get_curr_label.after; end; end; procedure op_jcxz(var ctx:t_jit_context2); var id1,id2:t_jit_i_link; ofs:Int64; dst:Pointer; link:t_jit_i_link; begin ctx.label_flags:=ctx.label_flags or LF_JMP; ofs:=0; GetTargetOfs(ctx.din,ctx.code,1,ofs); dst:=ctx.ptr_next+ofs; if ctx.is_text_addr(QWORD(dst)) and (not exist_entry(dst)) then begin //near link:=ctx.get_link(dst); //case handling in micro core id1:=ctx.builder.jcxz(link,ctx.dis.AddressSize); if (link<>nil_link) then begin ctx.add_forward_point(fpCall,dst); end else begin ctx.add_forward_point(fpCall,id1,dst); end; end else begin //far id1:=ctx.builder.jcxz(nil_link,ctx.dis.AddressSize,os8); id2:=ctx.builder.jmp(nil_link,os8); id1.target:=ctx.builder.get_curr_label.after; op_set_r14_imm(ctx,Int64(dst)); op_jmp_dispatcher(ctx,nil); id2.target:=ctx.builder.get_curr_label.after; end; end; const movsx8_desc:t_op_type=(op:$0FBE); movsxd_desc:t_op_type=(op:$63); procedure op_push(var ctx:t_jit_context2); var imm:Int64; stack,new:TRegValue; begin //lea rsp,[rsp-len] //mov [rsp],reg with ctx.builder do begin stack:=r_tmp0; if is_memory(ctx.din) then begin build_lea(ctx,1,r_tmp0); op_uplift(ctx,r_tmp0,os64); //in/out:r14 new:=new_reg_size(r_tmp1,ctx.din.Operand[1]); movq(new,[r_tmp0]); end else if (ctx.din.Operand[1].ByteCount<>0) then begin imm:=0; GetTargetOfs(ctx.din,ctx.code,1,imm); new:=new_reg_size(r_tmp1,ctx.din.Operand[1].Size); movi(new,imm); end else if is_preserved(ctx.din) then begin new:=new_reg_size(r_tmp1,ctx.din.Operand[1]); op_load(ctx,new,1); end else begin new:=new_reg(ctx.din.Operand[1]); end; //sign extend case new.ASize of os8: begin ctx.builder._RR(movsx8_desc,new,new,os64); new:=new_reg_size(new,os64); end; os32: begin ctx.builder._RR(movsxd_desc,new,new,os64); new:=new_reg_size(new,os64); end else; end; op_load_rsp(ctx,stack); leaq(stack,[stack-OPERAND_BYTES[new.ASize]]); if (new.AIndex=r_tmp1.AIndex) then begin op_uplift(ctx,stack,new.ASize,[not_use_r_tmp1]); //in/out:r14 end else begin op_uplift(ctx,stack,new.ASize); //in/out:r14 end; movq([stack],new); //For transactionality, //first we move the memory, //then we update the register //[op_uplift] op_load_rsp(ctx,stack); //[op_uplift] leaq(stack,[stack-OPERAND_BYTES[new.ASize]]); op_save_rsp(ctx,stack); end; end; procedure op_pushf(var ctx:t_jit_context2); var mem_size:TOperandSize; stack,new:TRegValue; begin //lea rsp,[rsp-len] //mov [rsp],rflags with ctx.builder do begin stack:=r_tmp0; new:=new_reg_size(r_tmp1,ctx.din.Operand[1]); mem_size:=ctx.din.Operand[1].Size; op_load_rsp(ctx,stack); leaq(stack,[stack-OPERAND_BYTES[mem_size]]); op_uplift(ctx,stack,mem_size); //in/out:r14 //get all flags pushfq(mem_size); pop(new); movq([stack],new); //For transactionality, //first we move the memory, //then we update the register //[op_uplift] op_load_rsp(ctx,stack); //[op_uplift] leaq(stack,[stack-OPERAND_BYTES[mem_size]]); op_save_rsp(ctx,stack); end; end; procedure op_leave(var ctx:t_jit_context2); var new,stack:TRegValue; begin //mov rsp,rbp //mov rbp,[rsp] //lea rsp,[rsp+len] with ctx.builder do begin stack:=r_tmp0; new :=r_tmp1; op_load_rbp(ctx,stack); op_uplift(ctx,stack,os64); //in/out:r14 movq(new,[stack]); //For transactionality, //first we move the memory, //then we update the register //[op_uplift] op_load_rbp(ctx,stack); //[op_uplift] op_save_rsp(ctx,stack); op_save_rbp(ctx,new); //[op_uplift] op_load_rsp(ctx,stack); leaq(stack,[stack+OPERAND_BYTES[ctx.dis.OperandSize]]); op_save_rsp(ctx,stack); end; end; procedure op_popf(var ctx:t_jit_context2); var mem_size:TOperandSize; new,stack:TRegValue; begin //mov rflags,[rsp] //lea rsp,[rsp+len] with ctx.builder do begin stack:=r_tmp0; new:=new_reg_size(r_tmp1,ctx.din.Operand[1]); mem_size:=ctx.din.Operand[1].Size; op_load_rsp(ctx,stack); op_uplift(ctx,stack,mem_size); //in/out:r14 movq(new,[stack]); push(new); popfq(mem_size); //For transactionality, //first we move the memory, //then we update the register //[op_uplift] op_load_rsp(ctx,stack); leaq(stack,[stack+OPERAND_BYTES[new.ASize]]); op_save_rsp(ctx,stack); end; end; procedure op_pop(var ctx:t_jit_context2); var new,stack:TRegValue; reload_rsp:Boolean; begin //mov reg,[rsp] //lea rsp,[rsp+len] with ctx.builder do begin stack:=r_tmp0; op_load_rsp(ctx,stack); op_uplift(ctx,stack,os64); //in/out:r14 reload_rsp:=False; if is_memory(ctx.din) then begin new:=new_reg_size(r_tmp1,ctx.din.Operand[1]); movq(new,[stack]); build_lea(ctx,1,stack,[not_use_r_tmp1]); op_uplift(ctx,stack,os64,[not_use_r_tmp1]); //in/out:r14 movq([stack],new); reload_rsp:=True; end else if is_preserved(ctx.din) then begin new:=new_reg_size(r_tmp1,ctx.din.Operand[1]); movq(new,[stack]); op_save(ctx,1,fix_size(new)); end else begin new:=new_reg(ctx.din.Operand[1]); movq(new,[stack]); end; //For transactionality, //first we move the memory, //then we update the register if reload_rsp then begin op_load_rsp(ctx,stack); end; leaq(stack,[stack+OPERAND_BYTES[new.ASize]]); op_save_rsp(ctx,stack); end; end; procedure op_syscall(var ctx:t_jit_context2); begin ctx.label_flags:=ctx.label_flags or LF_JMP; ctx.add_forward_point(fpCall,ctx.ptr_curr); ctx.add_forward_point(fpCall,ctx.ptr_next); // op_set_rip_imm(ctx,Int64(ctx.ptr_next)); // ctx.builder.call_far(@jit_syscall); //syscall dispatcher end; procedure op_int(var ctx:t_jit_context2); var i:Integer; id:Byte; begin i:=ctx.din.Operand[1].ByteCount; Assert(i=1); id:=PByte(ctx.code)[i]; case id of 1,3: begin add_orig(ctx); end; $41: //assert? begin // op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); ctx.builder.call_far(@_jit_assert); trim_flow(ctx); end; $44: //system error? begin // op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); ctx.builder.call_far(@_jit_system_error); trim_flow(ctx); end; else begin ctx.builder.call_far(@jit_unknow_int); trim_flow(ctx); end; end; end; procedure op_ud2(var ctx:t_jit_context2); begin //exit proc? ctx.builder.int3; op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); ctx.builder.call_far(@_jit_exit_proc); //TODO exit dispatcher trim_flow(ctx); end; procedure op_iretq(var ctx:t_jit_context2); begin //exit proc? ctx.builder.int3; op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); ctx.builder.call_far(@_jit_exit_proc); //TODO exit dispatcher trim_flow(ctx); end; procedure op_hlt(var ctx:t_jit_context2); begin //stop thread? ctx.builder.int3; op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); ctx.builder.call_far(@_jit_exit_proc); //TODO exit dispatcher end; procedure op_cpuid(var ctx:t_jit_context2); begin op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); ctx.builder.call_far(@jit_cpuid); end; procedure op_rdtsc(var ctx:t_jit_context2); begin if time.strict_ps4_freq then begin ctx.builder.call_far(@strict_ps4_rdtsc_jit); end else begin add_orig(ctx); end; end; procedure op_rdtscp(var ctx:t_jit_context2); begin if time.strict_ps4_freq then begin ctx.builder.call_far(@strict_ps4_rdtscp_jit); end else with ctx.builder do begin //rdx //result0 //rax //result1 //rcx //result3 //rbx //backup -> CPUID_LOCAL_APIC_ID 0xff000000 0..7 movq(r_tmp0,rbx); //save rbx movi(eax,1); _O($0FA2); //cpuid //load flags to al,ah laxf; shri8 (ebx,6); //cpu_id andi8se(ebx,7); //0..7 movi (ecx,7); subq (ecx,ebx); //7-cpu_id //store flags from al,ah saxf; movq(rbx,r_tmp0); //restore rbx _O($0FAEE8); //lfence _O($0F31); //rdtsc _O($0FAEE8); //lfence end; end; procedure op_nop(var ctx:t_jit_context2); begin //align? end; procedure op_invalid(var ctx:t_jit_context2); begin op_ud2(ctx); end; { //load flags to al,ah seto(al); lahf; //store flags from al,ah addi(al,127); sahf; } // procedure op_debug_info(var ctx:t_jit_context2); var link_jmp:t_jit_i_link; begin //debug if debug_info then begin link_jmp:=ctx.builder.jmp(nil_link,os8); // //ctx.builder.int3; ctx.builder.cli; //op_set_r14_imm(ctx,$FACEADD7); op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); add_orig(ctx); op_set_r14_imm(ctx,Int64(ctx.ptr_next)); //op_set_r14_imm(ctx,$FACEADDE); ctx.builder.sti; // link_jmp.target:=ctx.builder.get_curr_label.after; end; //debug end; type t_convert_mode=(mInstruction,mExport,mImport); procedure op_jit2native(var ctx:t_jit_context2;mode:t_convert_mode); var i:Integer; begin with ctx.builder do begin case mode of mExport, mImport: begin //set PCB_IS_HLE ori([r13-jit_frame_offset+(@p_kthread(nil)^.pcb_flags),os8],Byte(PCB_IS_HLE)); end; else; end; case mode of mInstruction: begin //save internal stack movq([r13-jit_frame_offset+(@p_kthread(nil)^.td_jctx.rsp)],rsp); movq([r13-jit_frame_offset+(@p_kthread(nil)^.td_jctx.rbp)],rbp); //load guest stack movq(r14,[r13-jit_frame_offset+(@p_kthread(nil)^.td_ustack.stack)]); movq(r15,[r13-jit_frame_offset+(@p_kthread(nil)^.td_ustack.sttop)]); //set teb movq([GS+teb_stack],r14); movq([GS+teb_sttop],r15); //load rsp,rbp movq(rsp,[r13+(@p_jit_frame(nil)^.tf_rsp)]); movq(rbp,[r13+(@p_jit_frame(nil)^.tf_rbp)]); // end; mExport: begin //load guest stack //pushq %rbp //////////push([r13+(@p_jit_frame(nil)^.tf_rbp),os64]); //movq %rsp,%rbp movq(r14,[r13+(@p_jit_frame(nil)^.tf_rsp)]); //<-rsp //////////movq([r13+(@p_jit_frame(nil)^.tf_rbp)],r14); //->rbp //prolog (debugger) push(rbp); movq(rbp,rsp); leaq(rsp,[rsp-$8]); //shift guard //alloc host stack leaq(rsp,[rsp-$50]); //preload stack argc //$50 = 10*8 For i:=0 to 7 do begin movq(r15,[r14+i*8+(8)]); //call(8) movq([rsp+i*8],r15); end; // end; mImport: begin //restore guest/host stack //movq %rbp,%rsp movq(r14,[r13+(@p_jit_frame(nil)^.tf_rbp)]); //<-rbp movq([r13+(@p_jit_frame(nil)^.tf_rsp)],r14); //->rsp //popq %rbp pop([r13+(@p_jit_frame(nil)^.tf_rbp),os64]); // end; else; end; //load r14,r15,r13 movq(r14,[r13+(@p_jit_frame(nil)^.tf_r14)]); movq(r15,[r13+(@p_jit_frame(nil)^.tf_r15)]); movq(r13,[r13+(@p_jit_frame(nil)^.tf_r13)]); end; end; procedure op_native2jit(var ctx:t_jit_context2;mode:t_convert_mode); var i:Integer; begin with ctx.builder do begin //save r13 movq([GS+teb_jitcall],r13); //load curkthread,jit_ctx movq(r13,[GS +teb_thread]); leaq(r13,[r13+jit_frame_offset ]); //load r14,r15 movq([r13+(@p_jit_frame(nil)^.tf_r14)],r14); movq([r13+(@p_jit_frame(nil)^.tf_r15)],r15); //load r13 movq(r14,[GS+teb_jitcall]); movq([r13+(@p_jit_frame(nil)^.tf_r13)],r14); case mode of mInstruction: begin //load rsp,rbp movq([r13+(@p_jit_frame(nil)^.tf_rsp)],rsp); movq([r13+(@p_jit_frame(nil)^.tf_rbp)],rbp); //load host stack movq(r14,[r13-jit_frame_offset+(@p_kthread(nil)^.td_kstack.stack)]); movq(r15,[r13-jit_frame_offset+(@p_kthread(nil)^.td_kstack.sttop)]); //set teb movq([GS+teb_stack],r14); movq([GS+teb_sttop],r15); //load internal stack movq(rsp,[r13-jit_frame_offset+(@p_kthread(nil)^.td_jctx.rsp)]); movq(rbp,[r13-jit_frame_offset+(@p_kthread(nil)^.td_jctx.rbp)]); // end; mExport: begin //free host stack //epilog (debugger) movq(rsp,rbp); pop (rbp); //restore guest/host stack //movq %rbp,%rsp //////////movq(r14,[r13+(@p_jit_frame(nil)^.tf_rbp)]); //<-rbp //////////movq([r13+(@p_jit_frame(nil)^.tf_rsp)],r14); //->rsp //popq %rbp //////////pop([r13+(@p_jit_frame(nil)^.tf_rbp),os64]); // end; mImport: begin //load guest stack //pushq %rbp push([r13+(@p_jit_frame(nil)^.tf_rbp),os64]); //movq %rsp,%rbp movq(r14,[r13+(@p_jit_frame(nil)^.tf_rsp)]); //<-rsp movq([r13+(@p_jit_frame(nil)^.tf_rbp)],r14); //->rbp leaq(r14,[r14-$8]); //shift guard //alloc guest rsp leaq(r14,[r14-$50]); movq([r13+(@p_jit_frame(nil)^.tf_rsp)],r14); //rsp //preload stack argc //$50 = 10*8 For i:=0 to 7 do begin movq(r15,[rsp+i*8+(8+8)]); //call(8) + push(8) movq([r14+i*8],r15); end; // end; else; end; case mode of mExport, mImport: begin //reset PCB_IS_HLE andi([r13-jit_frame_offset+(@p_kthread(nil)^.pcb_flags),os8],not Byte(PCB_IS_HLE)); // end; else; end; end; end; //rdi -> addr //rsi -> rdi //rdx -> rsi //rcx -> rdx //r8 -> rcx //r9 -> r8 // EXECUTE_MAGIC_ADDR $FFFFFFFFFCAFEDAD // $0xFCAFEDAD = -55579219 procedure ExecuteGuest; SysV_ABI_CDecl; assembler; nostackframe; public; asm //save r13 movq %r13,%gs:teb.jitcall //load curkthread,jit_ctx movq %gs:teb.thread ,%r13 //curkthread leaq kthread.td_frame.tf_r13(%r13),%r13 //jit_frame //load r14,r15 movq %r14,jit_frame.tf_r14(%r13) movq %r15,jit_frame.tf_r15(%r13) //load r13 movq %gs:teb.jitcall,%r14 movq %r14,jit_frame.tf_r13(%r13) //alloc rbp,rsp //load addr movq %rdi,%r14 //move params movq %rsi,%rdi movq %rdx,%rsi movq %rcx,%rdx movq %r8 ,%rcx movq %r9 ,%r8 //call xorq %r15,%r15 call jit_jmp_dispatch //in:r14(addr) r15(plt) out:r14(addr) movq jit_frame.tf_rsp(%r13), %r15 //mov r15,rsp leaq -8(%r15), %r15 //lea r15,[r15-8] movq $-55579219,(%r15) //mov [r15],magic (sign extend) movq %r15, jit_frame.tf_rsp(%r13) //mov rsp,r15 call %r14 //restore rbp,rsp //load r14,r15,r13 movq jit_frame.tf_r14(%r13),%r14 movq jit_frame.tf_r15(%r13),%r15 movq jit_frame.tf_r13(%r13),%r13 end; function is_push_op(Opcode:TOpcode):Boolean; inline; begin case Opcode of OPpush, OPpop, OPpushf, OPpopf: Result:=True; else Result:=False; end; end; const use_lazy_jit=False; function op_lazy_jit(var ctx:t_jit_context2):Boolean; begin Result:=False; if not use_lazy_jit then begin Exit; end; if (jit_cbs[ctx.din.OpCode.Prefix,ctx.din.OpCode.Opcode,ctx.din.OpCode.Suffix]=@op_invalid) then begin Exit; end; case ctx.din.OpCode.Opcode of OPcall, OPjmp, OPret, OPretf, OPj__, OPloop, OPjcxz, OPjecxz, OPjrcxz, //OPpush, //OPpop, //OPpushf, //OPpopf, OPenter, OPleave, OPsyscall, OPint, OPint1, OPint3, OPud1, OPud2, OPiret, OPhlt, OPcpuid, OPrdtsc, OPnop :Exit; else; end; if is_rep_prefix(ctx.din) then begin Exit; end; if is_segment(ctx.din) then begin Exit; end; if is_push_op(ctx.din.OpCode.Opcode) or is_preserved(ctx.din) then begin if is_rip(ctx.din) then begin Exit; end; end else begin add_orig(ctx); Exit(True); end; op_jit2native(ctx,mInstruction); add_orig(ctx); op_native2jit(ctx,mInstruction); Result:=True; end; procedure init_cbs; begin // jit_rep_cbs[repOPins ]:=@op_invalid; jit_rep_cbs[repOPouts]:=@op_invalid; jit_rep_cbs[repOPret ]:=@op_ret; // jit_cbs[OPPnone,OPcall,OPSnone]:=@op_call; jit_cbs[OPPnone,OPjmp ,OPSnone]:=@op_jmp; jit_cbs[OPPnone,OPret ,OPSnone]:=@op_ret; jit_cbs[OPPnone,OPretf,OPSnone]:=@op_ret; jit_cbs[OPPnone,OPj__,OPSc_o ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_no ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_b ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_nb ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_z ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_nz ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_be ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_nbe]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_s ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_ns ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_p ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_np ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_l ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_nl ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_le ]:=@op_jcc; jit_cbs[OPPnone,OPj__,OPSc_nle]:=@op_jcc; jit_cbs[OPPnone,OPloop,OPSnone]:=@op_loop; jit_cbs[OPPnone,OPloop,OPSc_ne]:=@op_loop; jit_cbs[OPPnone,OPloop,OPSc_e ]:=@op_loop; jit_cbs[OPPnone,OPjcxz ,OPSnone]:=@op_jcxz; jit_cbs[OPPnone,OPjecxz,OPSnone]:=@op_jcxz; jit_cbs[OPPnone,OPjrcxz,OPSnone]:=@op_jcxz; jit_cbs[OPPnone,OPpush,OPSnone]:=@op_push; jit_cbs[OPPnone,OPpop ,OPSnone]:=@op_pop; jit_cbs[OPPnone,OPpushf ,OPSnone]:=@op_pushf; jit_cbs[OPPnone,OPpushf ,OPSx_q ]:=@op_pushf; jit_cbs[OPPnone,OPenter ,OPSnone]:=@op_invalid; //TODO jit_cbs[OPPnone,OPleave ,OPSnone]:=@op_leave; jit_cbs[OPPnone,OPpopf ,OPSnone]:=@op_popf; jit_cbs[OPPnone,OPpopf ,OPSx_q ]:=@op_popf; jit_cbs[OPPnone,OPsyscall,OPSnone]:=@op_syscall; jit_cbs[OPPnone,OPint ,OPSnone]:=@op_int; jit_cbs[OPPnone,OPint1 ,OPSnone]:=@add_orig; jit_cbs[OPPnone,OPint3 ,OPSnone]:=@add_orig; jit_cbs[OPPnone,OPud1 ,OPSnone]:=@add_orig; jit_cbs[OPPnone,OPud2 ,OPSnone]:=@op_ud2; jit_cbs[OPPnone,OPiret,OPSnone]:=@op_iretq; jit_cbs[OPPnone,OPiret,OPSx_d ]:=@op_iretq; jit_cbs[OPPnone,OPiret,OPSx_q ]:=@op_iretq; jit_cbs[OPPnone,OPhlt ,OPSnone]:=@op_hlt; jit_cbs[OPPnone,OPcpuid,OPSnone]:=@op_cpuid; jit_cbs[OPPnone,OPrdtsc,OPSnone]:=@op_rdtsc; jit_cbs[OPPnone,OPrdtsc,OPSx_p ]:=@op_rdtscp; jit_cbs[OPPnone,OPnop,OPSnone]:=@op_nop; jit_cbs[OPPnone,OPin ,OPSnone]:=@op_invalid; jit_cbs[OPPnone,OPins ,OPSx_b ]:=@op_invalid; jit_cbs[OPPnone,OPins ,OPSx_w ]:=@op_invalid; jit_cbs[OPPnone,OPins ,OPSx_d ]:=@op_invalid; jit_cbs[OPPnone,OPout ,OPSnone]:=@op_invalid; jit_cbs[OPPnone,OPouts,OPSx_b ]:=@op_invalid; jit_cbs[OPPnone,OPouts,OPSx_w ]:=@op_invalid; jit_cbs[OPPnone,OPouts,OPSx_d ]:=@op_invalid; jit_cbs[OPPnone,OPrdmsr,OPSnone]:=@op_invalid; jit_cbs[OPPnone,OPwrmsr,OPSnone]:=@op_invalid; jit_cbs[OPPnone,OPsldt,OPSnone]:=@op_invalid; jit_cbs[OPPnone,OPlldt,OPSnone]:=@op_invalid; jit_cbs[OPPnone,OPxbegin,OPSnone]:=@op_invalid; jit_cbs[OPPnone,OPxend ,OPSnone]:=@op_invalid; jit_cbs[OPPnone,OPwbinvd,OPSnone]:=@op_invalid; end; function test_disassemble(addr:Pointer;vsize:Integer):Boolean; var proc:TDbgProcess; adec:TX86AsmDecoder; ptr,fin:Pointer; ACodeBytes,ACode:RawByteString; begin Result:=True; ptr:=addr; fin:=addr+vsize; proc:=TDbgProcess.Create(dm64); adec:=TX86AsmDecoder.Create(proc); while (ptr []) or (adec.Instr.ParseFlags * [preF3,preF2] <> []) or is_invalid(adec.Instr) then begin Result:=False; Break; end; end; adec.Free; proc.Free; end; function pick_on_destroy(handle:Pointer):Integer; begin Result:=DO_NOTHING; Assert(false,'TODO: destroy in code analize'); end; function pick_on_trigger(handle:Pointer;mode:T_TRIGGER_MODE):Integer; begin case mode of M_CPU_WRITE :; M_DMEM_WRITE:; else Exit; end; Result:=DO_NOTHING; Assert(false,'TODO: trigger in code analize'); end; function pick_locked_internal(var ctx:t_jit_context2):p_jit_dynamic_blob; forward; function pick_locked_normal (var ctx:t_jit_context2):p_jit_dynamic_blob; forward; procedure pick(var ctx:t_jit_context2;preload:Pointer); [public, alias:'kern_jit_pick']; label _exit; var map:vm_map_t; lock:Pointer; node:p_jit_entry_point; lock_start:QWORD; lock___end:QWORD; tobj:p_vm_track_object; blob:p_jit_dynamic_blob; begin map:=p_proc.p_vmspace; lock_start:=ctx.text_start; lock___end:=ctx.text___end; lock:=pmap_wlock(map^.pmap,lock_start,lock___end); if (preload<>nil) then begin //recheck node:=preload_entry(preload); if (node<>nil) then begin node^.dec_ref('preload_entry'); goto _exit; end; end; //lock pageflt read-only (mirrors?) //TODO: Works slowly, needs optimization { tobj:=vm_track_object_allocate(node,lock_start,lock___end,H_ZERO,PAGE_TRACK_W); tobj^.on_destroy:=@pick_on_destroy; tobj^.on_trigger:=@pick_on_trigger; vm_map_track_insert(p_proc.p_vmspace,tobj); } if (cmInternal in ctx.modes) then begin blob:=pick_locked_internal(ctx); end else begin blob:=pick_locked_normal(ctx); end; if (blob<>nil) then begin blob^.attach; //blob.attach-> blob_track-> vm_map_track_insert end; //restore non tracked (mirrors?) //TODO: Works slowly, needs optimization { vm_map_track_remove(p_proc.p_vmspace,tobj); tobj^.on_destroy:=nil; tobj^.on_trigger:=nil; vm_track_object_deallocate(tobj); //<-vm_track_object_allocate } _exit: pmap_unlock(map^.pmap,lock); end; procedure op_debug_info_addr(var ctx:t_jit_context2;addr:Pointer); var link_jmp:t_jit_i_link; begin //debug if debug_info then begin link_jmp:=ctx.builder.jmp(nil_link,os8); // ctx.builder.cli; op_set_r14_imm(ctx,Int64(addr)); ctx.builder.sti; // link_jmp.target:=ctx.builder.get_curr_label.after; end; //debug end; const trace_hle_call=False; procedure print_beg_hle(nid:QWORD); SysV_ABI_CDecl; var str:shortstring; begin str:=ps4libdoc.GetFunctName(nid); if (str='Unknow') then begin str:='0x'+HexStr(nid,16); end; if Pos('sceAudioOut',str)<>0 then Exit; case str of 'sceNpCheckCallback' :Exit; 'sceNetCtlCheckCallback' :Exit; 'sceUserServiceGetEvent' :Exit; 'sceNpCheckCallbackForLib' :Exit; 'sceNetCtlCheckCallbackForNpToolkit':Exit; 'sceSystemServiceGetStatus' :Exit; end; Writeln(str,'->'); end; procedure print_end_hle(nid:QWORD); SysV_ABI_CDecl; var str:shortstring; begin str:=ps4libdoc.GetFunctName(nid); if (str='Unknow') then begin str:='0x'+HexStr(nid,16); end; if Pos('sceAudioOut',str)<>0 then Exit; case str of 'sceNpCheckCallback' :Exit; 'sceNetCtlCheckCallback' :Exit; 'sceUserServiceGetEvent' :Exit; 'sceNpCheckCallbackForLib' :Exit; 'sceNetCtlCheckCallbackForNpToolkit':Exit; 'sceSystemServiceGetStatus' :Exit; end; Writeln(str,'<-'); end; function pick_locked_internal(var ctx:t_jit_context2):p_jit_dynamic_blob; var node_export:t_jit_context2.p_export_point; node_import:t_jit_context2.p_import_point; link_curr:t_jit_i_link; link_next:t_jit_i_link; begin Result:=nil; node_export:=ctx.export_list; node_import:=ctx.import_list; if (node_export=nil) and (node_import=nil) then begin ctx.Free; Exit; end; ctx.ptr_curr:=Pointer(ctx.text_start); ctx.ptr_next:=ctx.ptr_curr; ctx.new_chunk(fpCall,ctx.ptr_curr); //export while (node_export<>nil) do begin ctx.ptr_curr:=ctx.ptr_next; ctx.ptr_next:=ctx.ptr_curr+8; if (ctx.ptr_curr>=Pointer(ctx.text___end)) then begin Assert(false,'pick_locked_internal'); end; // link_curr:=ctx.builder.get_curr_label.after; // //beg if trace_hle_call then with ctx.builder do begin movi64(r14,node_export^.nid); movi64(r15,QWORD(@print_beg_hle)); call_far(@jit_hle_trace); end; op_jit2native(ctx,mExport); //[JIT->HLE] ctx.builder.call_far(node_export^.native); op_debug_info_addr(ctx,node_export^.native); //[HLE->JIT] op_native2jit(ctx,mExport); //TODO: [HLE->JIT] combine with [ret] //end if trace_hle_call then with ctx.builder do begin movi64(r14,node_export^.nid); movi64(r15,QWORD(@print_end_hle)); call_far(@jit_hle_trace); end; //save last call { if debug_info then with ctx.builder do begin ctx.builder.movi64(r14,QWORD(node_export^.native)); ctx.builder.movq ([GS+$100],r14); end; } // op_pop_rip_part0(ctx,0); //out:r14 ctx.imm:=0; op_jmp_dispatcher(ctx,@op_pop_rip_part1); // // if (node_export^.dst<>nil) then begin node_export^.dst^:=ctx.ptr_curr; end; // // link_next:=ctx.builder.get_curr_label.after; ctx.add_label(ctx.ptr_curr, ctx.ptr_next, link_curr, link_next, LF_JMP); // ctx.add_entry_point(ctx.ptr_curr,link_curr); // // node_export:=node_export^.next; end; //export //import while (node_import<>nil) do begin ctx.ptr_curr:=ctx.ptr_next; ctx.ptr_next:=ctx.ptr_curr+8; if (ctx.ptr_curr>=Pointer(ctx.text___end)) then begin Assert(false,'pick_locked_internal'); end; // link_curr:=ctx.builder.get_curr_label.after; // //[HLE->JIT] op_native2jit(ctx,mImport); with ctx.builder do begin op_push_rip_part0(ctx); op_set_r14_imm(ctx,Int64(node_import^.guest)); movq(r14,[r14]); op_call_dispatcher(ctx,@op_push_rip_part1); end; //---RETURN ENTRY POINT---- // link_next:=ctx.builder.get_curr_label.after; // ctx.add_label(ctx.ptr_curr, ctx.ptr_next, link_curr, link_next, LF_JMP); // ctx.add_entry_point(ctx.ptr_curr,link_curr); // // if (node_import^.dst<>nil) then begin node_import^.dst^:=ctx.ptr_curr; end; // // ctx.ptr_curr:=ctx.ptr_next; ctx.ptr_next:=ctx.ptr_curr+8; // // link_curr:=ctx.builder.get_curr_label.after; // //--EPILOG-- op_jit2native(ctx,mImport); //[JIT->HLE] // ctx.builder.reta; // // link_next:=ctx.builder.get_curr_label.after; ctx.add_label(ctx.ptr_curr, ctx.ptr_next, link_curr, link_next, LF_JMP); // ctx.add_entry_point(ctx.ptr_curr,link_curr); // node_import:=node_import^.next; end; //import ctx.end_chunk(ctx.ptr_next); Result:=build(ctx); ctx.Free; end; function scan_step_switchtable(var ctx:t_jit_context2; sw_table:t_jit_context2.p_switchtable_point; sw_next :PInteger):Boolean; var sw_data:PInteger; ofs:Int64; rel:Integer; begin Result:=False; Assert(sw_table^.curr<>nil); // sw_data:=sw_table^.curr; // if print_asm then begin Writeln('switchtable:0x',HexStr(QWORD(sw_table^.table),11),'..0x',HexStr(QWORD(sw_next),11)); end; // if ctx.is_text_addr(QWORD(sw_data)) and ( (sw_next=nil) or (QWORD(sw_data)0) then begin Exit; end; if (DWORD(rel) and $FFFF0000)=$FFFF0000 then begin ofs:=Int64(sw_table^.table)+rel; if ctx.is_text_addr(ofs) and (ofs<=ctx.max_reloc) then begin if print_asm then begin Writeln(' [0x',HexStr(QWORD(sw_data),11),']->0x',HexStr(ofs,11)); end; // ctx.add_forward_point(fpCall,Pointer(ofs)); //step up sw_table^.curr:=sw_data+1; //SizeOf(Integer) // Result:=True; end else begin //Terminate sw_table^.curr:=nil; end; end else begin //Terminate sw_table^.curr:=nil; end; end; end; procedure on_builder_error(const Msg:RawByteString;userdata:Pointer); register; var ctx:p_jit_context2; begin ctx:=userdata; with ctx^ do begin Writeln('original------------------------':32,' ','0x',HexStr(ptr_curr)); print_disassemble(code,dis.CodeIdx); Writeln('original------------------------':32,' ','0x',HexStr(ptr_next)); Writeln('builder error:', din.OpCode.Prefix,',', din.OpCode.Opcode,',', din.OpCode.Suffix,' ', din.Operand[1].Size,' ', din.Operand[2].Size); Writeln('opcode=$',HexStr(dis.opcode,8),' ', 'MIndex=',dis.ModRM.Index,' ', 'SimdOp=',dis.SimdOpcode,':',SCODES[dis.SimdOpcode],' ', 'mm=',MCODES[dis.mm and 3],':',dis.mm); end; end; var _print_stat:Integer=0; function pick_locked_normal(var ctx:t_jit_context2):p_jit_dynamic_blob; label _next, _next_forward, _switchtables, _invalid; var addr:Pointer; ptr :Pointer; sw_table:t_jit_context2.p_switchtable_point; sw_next :PInteger; links:t_jit_context2.t_forward_links; entry_link:Pointer; dis:TX86Disassembler; din:TInstruction; cb:t_jit_cb; link_new :t_jit_i_link; link_curr:t_jit_i_link; link_next:t_jit_i_link; node,node_curr,node_next:p_jit_instruction; i:Integer; begin Result:=nil; //init error cbs with ctx.builder do begin on_error:=@on_builder_error; on_udata:=@ctx; end; if (cmDontScanRipRel in ctx.modes) then begin //dont scan rip relative ctx.max_reloc:=0; end else begin ctx.max_reloc:=QWORD(ctx.max_forward_point); end; if (p_print_jit_preload) then begin Writeln(' ctx.text_start:0x',HexStr(ctx.text_start,16)); Writeln(' ctx.max_reloc :0x',HexStr(ctx.max_reloc ,16)); Writeln(' ctx.text___end:0x',HexStr(ctx.text___end,16)); Writeln(' ctx.map____end:0x',HexStr(ctx.map____end,16)); end; if System.InterlockedExchange(_print_stat,1)=0 then begin print_test_jit_cbs(False,True); end; links:=Default(t_jit_context2.t_forward_links); addr:=nil; if not ctx.fetch_forward_point(links,addr) then begin //No entry points? early exit ctx.Free; Exit; end; ctx.trim:=False; entry_link:=addr; ctx.new_chunk(links.ptype,entry_link); ptr:=addr; dis:=Default(TX86Disassembler); din:=Default(TInstruction); while True do begin if not ctx.is_text_addr(QWORD(ptr)) then begin if (p_print_jit_preload) then begin writeln('not excec:0x',HexStr(ptr)); end; ctx.ptr_curr:=ptr; goto _invalid; end; if ((ppmap_get_prot(QWORD(ptr)) and PAGE_PROT_EXECUTE)=0) then begin if (p_print_jit_preload) then begin writeln('not excec:0x',HexStr(ptr)); end; ctx.ptr_curr:=ptr; goto _invalid; end; ctx.label_flags:=0; ctx.ptr_curr:=ptr; //pre check if exist_entry(ptr) then begin if (entry_link=ptr) then begin entry_link:=nil; //clear end; link_curr:=ctx.builder.get_curr_label.after; node_curr:=link_curr._node; op_set_r14_imm(ctx,Int64(ptr)); // op_jmp_dispatcher(ctx,nil); link_next:=ctx.builder.get_curr_label.after; node_next:=link_next._node; cb:=@op_invalid; ctx.trim:=True; goto _next; //trim end; //guest->host ptr ctx.code:=uplift(ptr); ptr:=ctx.code; dis.Disassemble(dm64,ptr,din); apply_din_stat(din,(ptr-ctx.code)); ctx.ptr_next:=ctx.ptr_curr+(ptr-ctx.code); case din.OpCode.Opcode of OPX_Invalid..OPX_GroupP: begin //invalid if (p_print_jit_preload) then begin writeln('invalid1:0x',HexStr(ctx.ptr_curr)); end; _invalid: if (p_print_jit_preload) then begin print_frame(stdout,ctx.ptr_curr); Writeln('original------------------------':32,' ','0x',HexStr(ctx.ptr_curr)); print_disassemble(ctx.code,dis.CodeIdx); Writeln('original------------------------':32,' ','0x',HexStr(ctx.ptr_next)); end; ctx.mark_chunk(fpInvalid); link_curr:=ctx.builder.get_curr_label.after; node_curr:=link_curr._node; ctx.builder.int3; ctx.builder.int3; ctx.builder.ud2; if debug_info then begin op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); end; link_next:=ctx.builder.get_curr_label.after; node_next:=link_next._node; cb:=@op_invalid; ctx.trim:=True; goto _next; //trim end; else; end; if (din.Flags * [ifOnly32, ifOnly64, ifOnlyVex] <> []) or (din.ParseFlags * [preF3,preF2] <> []) or is_invalid(din) then begin if (p_print_jit_preload) then begin writeln('invalid2:0x',HexStr(ctx.ptr_curr)); end; goto _invalid; end; if print_asm then begin Writeln('original------------------------':32,' ','0x',HexStr(ctx.ptr_curr)); print_disassemble(ctx.code,dis.CodeIdx); Writeln('original------------------------':32,' ','0x',HexStr(ctx.ptr_next)); end; ctx.dis:=dis; ctx.din:=din; if is_rep_prefix(ctx.din) then begin cb:=@op_invalid; if (ctx.din.OpCode.Prefix=OPPnone) then begin case ctx.din.OpCode.Opcode of OPins :cb:=jit_rep_cbs[repOPins ]; OPouts:cb:=jit_rep_cbs[repOPouts]; OPmovs:cb:=jit_rep_cbs[repOPmovs]; OPlods:cb:=jit_rep_cbs[repOPlods]; OPstos:cb:=jit_rep_cbs[repOPstos]; OPcmps:cb:=jit_rep_cbs[repOPcmps]; OPscas:cb:=jit_rep_cbs[repOPscas]; OPret :cb:=jit_rep_cbs[repOPret ]; else; end; end; end else begin cb:=jit_cbs[ctx.din.OpCode.Prefix,ctx.din.OpCode.Opcode,ctx.din.OpCode.Suffix]; end; if (cb=@op_invalid) then begin case ctx.get_chunk_ptype of fpData, fpInvalid: begin writeln('skip:0x',HexStr(ctx.ptr_curr)); goto _invalid; end else; end; end; if (cb=nil) then begin print_error_td('Unhandled jit opcode!'); with ctx do begin Writeln('original------------------------':32,' ','0x',HexStr(ptr_curr)); print_disassemble(code,dis.CodeIdx); Writeln('original------------------------':32,' ','0x',HexStr(ptr_next)); Writeln('Unhandled jit:', din.OpCode.Prefix,',', din.OpCode.Opcode,',', din.OpCode.Suffix,' ', din.Operand[1].Size,' ', din.Operand[2].Size); Writeln('opcode=$',HexStr(dis.opcode,8),' ', 'MIndex=',dis.ModRM.Index,' ', 'SimdOp=',dis.SimdOpcode,':',SCODES[dis.SimdOpcode],' ', 'mm=',MCODES[dis.mm and 3],':',dis.mm); end; Assert(false); end; link_curr:=ctx.builder.get_curr_label.after; node_curr:=link_curr._node; { op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); with ctx.builder do movq([GS+teb_jitcall],r14); } { if (qword(ctx.ptr_curr) and $FFFFFF) = $2f662e then begin //print_asm:=true; ctx.builder.int3; end; } if op_lazy_jit(ctx) then begin // end else begin cb(ctx); end; { The main idea of interrupting JIT code: If the LF_JMP flag is set for the label, then it is enough to set the value to %gs:teb.jit_trp. Otherwise, for each thread, generate an individual lacuna with JIT code, containing: instructions to the end of the current guest instruction (start...rip...t_jinstr_len.recompil) then jmp_dispatcher to return to the code. If necessary, you can generate a page lock recovery after the current command: current instruction then jmp_dispatcher } //op_jit_interrupt(ctx); //cb(ctx); link_next:=ctx.builder.get_curr_label.after; node_next:=link_next._node; ////// i:=0; if (node_curr<>node_next) and (node_curr<>nil) then begin node:=TAILQ_NEXT(node_curr,@node_curr^.entry); while (node<>nil) do begin i:=i+node^.AInstructionSize; { if not test_disassemble(@node^.AData,node^.ASize) then begin print_asm:=True; Break; end; } node:=TAILQ_NEXT(node,@node^.entry); end; end; apply_jit_stat(i); ////// { if print_asm then begin Writeln('original------------------------':32,' ','0x',HexStr(ctx.ptr_curr)); print_disassemble(ctx.code,dis.CodeIdx); Writeln('original------------------------':32,' ','0x',HexStr(ctx.ptr_next)); end; } //debug print if print_asm then if (node_curr<>node_next) and (node_curr<>nil) then begin node:=TAILQ_NEXT(node_curr,@node_curr^.entry); Writeln('recompiled----------------------':32,' ',''); while (node<>nil) do begin print_disassemble(@node^.AData,node^.AInstructionSize); node:=TAILQ_NEXT(node,@node^.entry); end; Writeln('recompiled----------------------':32,' ',''); end; //print_asm:=False; _next: //debug if (cb<>@op_invalid) then begin op_debug_info(ctx); end; //debug //resolve forward links if (links.root<>nil) then begin ctx.Resolve_forwards(links,link_curr); end; //add new entry point if (entry_link<>nil) then begin ctx.add_entry_point(entry_link,link_curr); entry_link:=nil; end; //label exist in current blob if not ctx.trim then begin link_new:=ctx.get_link(ctx.ptr_next); if (link_new<>nil_link) then begin ctx.builder.jmp(link_new); //Writeln('jmp next:0x',HexStr(ptr)); ctx.trim:=True; end; end; //entry exist in another blob if not ctx.trim then if exist_entry(ctx.ptr_next) then begin op_set_r14_imm(ctx,Int64(ctx.ptr_next)); // op_jmp_dispatcher(ctx,nil); // ctx.trim:=True; end; //add new label [link_curr..link_next] begin //update link_next link_next:=ctx.builder.get_curr_label.after; ctx.add_label(ctx.ptr_curr, ctx.ptr_next, link_curr, link_next, ctx.label_flags); ctx.label_flags:=0; end; if ctx.trim then begin //close chunk ctx.end_chunk(ctx.ptr_next); _next_forward: ctx.trim:=False; repeat if not ctx.fetch_forward_point(links,addr) then begin goto _switchtables; end; link_new:=ctx.get_link(addr); if (link_new=nil_link) then begin //Writeln('not found:0x',HexStr(addr)); Break; end else begin ctx.Resolve_forwards(links,link_new); // ctx.add_entry_point(addr,link_new); end; until false; entry_link:=addr; ctx.new_chunk(links.ptype,entry_link); ptr:=addr; end; end; _switchtables: //scan switchtables sw_table:=nil; sw_next :=nil; if ctx.fetch_switchtable(sw_table,sw_next) then begin // repeat if scan_step_switchtable(ctx,sw_table,sw_next) then begin goto _next_forward; end; until not ctx.fetch_switchtable(sw_table,sw_next); // end; //build blob ctx.builder.int3; ctx.builder.int3; ctx.builder.int3; ctx.builder.ud2; if debug_info then begin op_set_r14_imm(ctx,Int64(ctx.ptr_curr)); end; Result:=build(ctx); ctx.Free; //print_din_stats; end; initialization init_cbs; end.