FPPS4/sys/jit/kern_jit.pas

2644 lines
50 KiB
Plaintext

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<fin) do
begin
adec.Disassemble(ptr,ACodeBytes,ACode);
case adec.Instr.OpCode.Opcode of
OPX_Invalid..OPX_GroupP:
begin
Result:=False;
Break;
end;
else;
end;
if (adec.Instr.Flags * [ifOnly32, ifOnly64, ifOnlyVex] <> []) 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)<QWORD(sw_next))
) then
begin
rel:=0;
if (copyin(sw_data,@rel,SizeOf(Integer))<>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;
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;
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!');
Writeln('original------------------------':32,' ','0x',HexStr(ctx.ptr_curr));
print_disassemble(ctx.code,dis.CodeIdx);
Writeln('original------------------------':32,' ','0x',HexStr(ctx.ptr_next));
Writeln('Unhandled jit:',
ctx.din.OpCode.Prefix,',',
ctx.din.OpCode.Opcode,',',
ctx.din.OpCode.Suffix,' ',
ctx.din.Operand[1].Size,' ',
ctx.din.Operand[2].Size);
Writeln('opcode=$',HexStr(ctx.dis.opcode,8),' ',
'MIndex=',ctx.dis.ModRM.Index,' ',
'SimdOp=',ctx.dis.SimdOpcode,':',SCODES[ctx.dis.SimdOpcode],' ',
'mm=',ctx.dis.mm,':',MCODES[ctx.dis.mm and 3]);
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.