FPPS4/sys/jit/kern_jit.pas

1866 lines
36 KiB
Plaintext

unit kern_jit;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
mqueue,
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);
procedure pick_locked_internal(var ctx:t_jit_context2);
procedure pick_locked(var ctx:t_jit_context2);
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;
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
if (cb<>nil) then
begin
cb(ctx);
end;
jmp(r14);
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
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;
begin
ctx.label_flags:=ctx.label_flags or LF_JMP;
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);
if (link<>nil_link) then
begin
ctx.builder.jmp(link);
ctx.add_forward_point(fpCall,dst);
end else
begin
id:=ctx.builder.jmp(nil_link);
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;
//
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);
//
op_pop_rip_part0(ctx,imm); //out:r14
//
ctx.imm:=imm;
op_jmp_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);
if (link<>nil_link) then
begin
ctx.builder.jmp(link);
ctx.add_forward_point(fpCall,dst);
end else
begin
id:=ctx.builder.jmp(nil_link);
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._label:=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);
id2._label:=ctx.builder.get_curr_label.after;
}
end;
end;
procedure op_loop(var ctx:t_jit_context2);
var
id1,id2,id3: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;
id1:=ctx.builder.loop(ctx.din.OpCode.Suffix,nil_link,ctx.dis.AddressSize);
if ctx.is_text_addr(QWORD(dst)) and
(not exist_entry(dst)) then
begin
//near
link:=ctx.get_link(dst);
id2:=ctx.builder.jmp(nil_link,os8);
id1._label:=ctx.builder.get_curr_label.after;
id3:=ctx.builder.jmp(nil_link);
id2._label:=ctx.builder.get_curr_label.after;
if (link<>nil_link) then
begin
ctx.add_forward_point(fpCall,dst);
end else
begin
ctx.add_forward_point(fpCall,id3,dst);
end;
end else
begin
//far
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_jcxz(var ctx:t_jit_context2);
var
id1,id2,id3: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;
id1:=ctx.builder.jcxz(nil_link,ctx.dis.AddressSize);
if ctx.is_text_addr(QWORD(dst)) and
(not exist_entry(dst)) then
begin
//near
link:=ctx.get_link(dst);
id2:=ctx.builder.jmp(nil_link,os8);
id1._label:=ctx.builder.get_curr_label.after;
id3:=ctx.builder.jmp(nil_link);
id2._label:=ctx.builder.get_curr_label.after;
if (link<>nil_link) then
begin
ctx.add_forward_point(fpCall,dst);
end else
begin
ctx.add_forward_point(fpCall,id3,dst);
end;
end else
begin
//far
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;
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_nop(var ctx:t_jit_context2);
begin
//align?
end;
procedure op_invalid(var ctx:t_jit_context2);
begin
ctx.builder.int3;
ctx.builder.ud2;
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._label:=ctx.builder.get_curr_label.after;
end;
//debug
end;
procedure op_jit2native(var ctx:t_jit_context2;pcb:Boolean);
const
and_desc:t_op_type=(op:$80;index:4);
begin
with ctx.builder do
begin
//reset PCB_IS_JIT
if pcb then
begin
_MI8(and_desc,[r13-jit_frame_offset+Integer(@p_kthread(nil)^.pcb_flags),os8],not Byte(PCB_IS_JIT));
end;
//save internal stack
movq([r13-jit_frame_offset+Integer(@p_kthread(nil)^.td_jctx.rsp)],rsp);
movq([r13-jit_frame_offset+Integer(@p_kthread(nil)^.td_jctx.rbp)],rbp);
//load guest stack
movq(r14,[r13-jit_frame_offset+Integer(@p_kthread(nil)^.td_ustack.stack)]);
movq(r15,[r13-jit_frame_offset+Integer(@p_kthread(nil)^.td_ustack.sttop)]);
//set teb
movq([GS+teb_stack],r14);
movq([GS+teb_sttop],r15);
//load rsp,rbp,r14,r15,r13
movq(rsp,[r13+Integer(@p_jit_frame(nil)^.tf_rsp)]);
movq(rbp,[r13+Integer(@p_jit_frame(nil)^.tf_rbp)]);
movq(r14,[r13+Integer(@p_jit_frame(nil)^.tf_r14)]);
movq(r15,[r13+Integer(@p_jit_frame(nil)^.tf_r15)]);
movq(r13,[r13+Integer(@p_jit_frame(nil)^.tf_r13)]);
end;
end;
procedure op_native2jit(var ctx:t_jit_context2;pcb:Boolean);
const
or_desc:t_op_type=(op:$80;index:1);
begin
with ctx.builder do
begin
//save r13
movq([GS+Integer(teb_jitcall)],r13);
//load curkthread,jit ctx
movq(r13,[GS+Integer(teb_thread)]);
leaq(r13,[r13+jit_frame_offset]);
//load r14,r15
movq([r13+Integer(@p_jit_frame(nil)^.tf_r14)],r14);
movq([r13+Integer(@p_jit_frame(nil)^.tf_r15)],r15);
//load r13
movq(r14,[GS+Integer(teb_jitcall)]);
movq([r13+Integer(@p_jit_frame(nil)^.tf_r13)],r14);
//load rsp,rbp
movq([r13+Integer(@p_jit_frame(nil)^.tf_rsp)],rsp);
movq([r13+Integer(@p_jit_frame(nil)^.tf_rbp)],rbp);
//load host stack
movq(r14,[r13-jit_frame_offset+Integer(@p_kthread(nil)^.td_kstack.stack)]);
movq(r15,[r13-jit_frame_offset+Integer(@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+Integer(@p_kthread(nil)^.td_jctx.rsp)]);
movq(rbp,[r13-jit_frame_offset+Integer(@p_kthread(nil)^.td_jctx.rbp)]);
//set PCB_IS_JIT
if pcb then
begin
_MI8(or_desc,[r13-jit_frame_offset+Integer(@p_kthread(nil)^.pcb_flags),os8],Byte(PCB_IS_JIT));
end;
end;
end;
function op_lazy_jit(var ctx:t_jit_context2):Boolean;
begin
Result:=False;
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,
OPenter,
OPleave,
OPpopf,
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_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,false);
add_orig(ctx);
op_native2jit(ctx,false);
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,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;
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
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;
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;
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?)
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);
//_pmap_prot_int(map^.pmap,lock_start,lock___end,PAGE_PROT_READ);
if (cmInternal in ctx.modes) then
begin
pick_locked_internal(ctx);
end else
begin
pick_locked(ctx);
end;
//restore non tracked (mirrors?)
//_pmap_prot_fix(map^.pmap,lock_start,lock___end,TRACK_PROT or REMAP_PROT);
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 pick_locked_internal(var ctx:t_jit_context2);
var
node:t_jit_context2.p_export_point;
link_curr,link_next:t_jit_i_link;
begin
node:=ctx.export_list;
if (node=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);
while (node<>nil) do
begin
ctx.ptr_curr:=ctx.ptr_next;
ctx.ptr_next:=ctx.ptr_curr+16;
if (ctx.ptr_curr>=Pointer(ctx.text___end)) then
begin
Assert(false,'pick_locked_internal');
end;
link_curr:=ctx.builder.get_curr_label.after;
//
op_jit2native(ctx,true);
ctx.builder.call_far(node^.native);
op_native2jit(ctx,true);
//
op_pop_rip_part0(ctx,0); //out:r14
ctx.imm:=0;
op_jmp_dispatcher(ctx,@op_pop_rip_part1);
//
if (node^.dst<>nil) then
begin
node^.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:=node^.next;
end;
ctx.end_chunk(ctx.ptr_next);
build(ctx);
ctx.Free;
end;
var
_print_stat:Integer=0;
procedure pick_locked(var ctx:t_jit_context2);
label
_next,
_build,
_invalid;
var
addr:Pointer;
ptr:Pointer;
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;
begin
if (cmDontScanRipRel in ctx.modes) then
begin
//dont scan rip relative
ctx.max_rel:=0;
end else
begin
ctx.max_rel:=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_rel :0x',HexStr(ctx.max_rel,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
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;
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;
goto _invalid;
end;
ctx.label_flags:=0;
ctx.ptr_curr:=ptr;
//guest->host ptr
ctx.code:=uplift(ptr);
ptr:=ctx.code;
dis.Disassemble(dm64,ptr,din);
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);
ctx.builder.int3;
ctx.builder.int3;
ctx.builder.ud2;
link_curr:=ctx.builder.get_curr_label.before;
link_next:=ctx.builder.get_curr_label.after;
cb:=@op_invalid;
ctx.trim:=True;
goto _next; //trim
end;
else;
end;
{
if (qword(ctx.ptr_curr) and $FFFFF) = $427F5 then
begin
print_asm:=true;
ctx.builder.int3;
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+Integer(teb_jitcall)],r14);
}
{
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;
{
if (node_curr<>node_next) and
(node_curr<>nil) then
begin
node:=TAILQ_NEXT(node_curr,@node_curr^.link);
while (node<>nil) do
begin
if not test_disassemble(@node^.AData,node^.ASize) then
begin
print_asm:=True;
Break;
end;
node:=TAILQ_NEXT(node,@node^.link);
end;
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;
}
//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^.ASize);
node:=TAILQ_NEXT(node,@node^.entry);
end;
Writeln('recompiled----------------------':32,' ','');
end;
//print_asm:=False;
{
if (qword(ctx.ptr_curr) and $FFFFF) = $2f1f6 then
begin
//print_asm:=true;
ctx.builder.int3;
end;
}
_next:
//debug
if (cb<>@op_invalid) then
begin
op_debug_info(ctx);
end;
//debug
//resolve forward links
if (links.root<>nil) then
begin
links.Resolve(link_curr);
links.root:=nil;
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
ctx.trim:=False;
//close chunk
ctx.end_chunk(ctx.ptr_next);
repeat
if not ctx.fetch_forward_point(links,addr) then
begin
goto _build;
end;
link_new:=ctx.get_link(addr);
if (link_new=nil_link) then
begin
//Writeln('not found:0x',HexStr(addr));
Break;
end else
begin
links.Resolve(link_new);
links.root:=nil;
//
ctx.add_entry_point(addr,link_new);
end;
until false;
entry_link:=addr;
ctx.new_chunk(links.ptype,entry_link);
ptr:=addr;
end;
end;
_build:
//build blob
ctx.builder.int3;
ctx.builder.int3;
ctx.builder.int3;
ctx.builder.ud2;
build(ctx);
ctx.Free;
end;
initialization
init_cbs;
end.