FPPS4/sys/vm/vm_patch_link.pas

347 lines
6.3 KiB
Plaintext

unit vm_patch_link;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
mqueue,
kern_stub;
type
t_patch_type=(pt_none,pt_fsbase,pt_gsbase,pt_syscall,pt_unresolve,pt_jit_prolog,pt_jit_frame);
p_patch_info=^t_patch_info;
t_patch_info=record
vaddr:Pointer;
vsize:Integer;
ptype:t_patch_type;
stub :p_stub_chunk;
end;
p_patch_node=^t_patch_node;
t_patch_node=record
link:TAILQ_ENTRY;
page:TAILQ_ENTRY;
info:t_patch_info;
end;
function vm_get_patch_link(vaddr:Pointer;vsize:Integer;ptype:t_patch_type):t_patch_info;
function vm_patch_exist(vaddr:Pointer;vsize:Integer):Boolean;
procedure vm_add_patch_link (_obj,vaddr:Pointer;vsize:Integer;ptype:t_patch_type;stub:p_stub_chunk);
procedure vm_free_patch_link (_obj:Pointer;node:p_patch_node);
procedure vm_object_patch_remove(_obj:Pointer;start,__end:DWORD);
function vm_get_patch_link (_obj,vaddr:Pointer):p_stub_chunk;
procedure vm_rem_patch_link (_obj,vaddr:Pointer);
implementation
uses
hamt,
kern_rwlock,
vmparam,
sys_vm_object;
type
p_patch_page=^t_patch_page;
t_patch_page=TAILQ_HEAD;
var
hamt_lock:Pointer=nil;
hamt_page:TSTUB_HAMT32;
function OFF_TO_IDX(x:Pointer):DWORD; inline;
begin
Result:=QWORD(x) shr PAGE_SHIFT;
end;
procedure hamt_insert_link(node:p_patch_node);
var
data:PPointer;
page:p_patch_page;
begin
rw_wlock(hamt_lock);
data:=HAMT_search32(@hamt_page,OFF_TO_IDX(node^.info.vaddr));
if (data=nil) then
begin
page:=AllocMem(SizeOf(t_patch_page));
TAILQ_INIT(page);
data:=HAMT_insert32(@hamt_page,OFF_TO_IDX(node^.info.vaddr),page);
end;
page:=data^;
TAILQ_INSERT_TAIL(page,node,@node^.page);
rw_wunlock(hamt_lock);
end;
procedure hamt_remove_link(node:p_patch_node);
var
data:PPointer;
page:p_patch_page;
begin
rw_wlock(hamt_lock);
data:=HAMT_search32(@hamt_page,OFF_TO_IDX(node^.info.vaddr));
if (data=nil) then
begin
rw_wunlock(hamt_lock);
Exit;
end;
page:=data^;
TAILQ_REMOVE(page,node,@node^.page);
if TAILQ_EMPTY(page) then
begin
HAMT_delete32(@hamt_page,OFF_TO_IDX(node^.info.vaddr),nil);
FreeMem(page);
end;
rw_wunlock(hamt_lock);
end;
function info_cross(info:p_patch_info;vaddr:Pointer;vsize:Integer):Boolean; inline;
begin
Result:=((vaddr+vsize)>info^.vaddr) and (vaddr<(info^.vaddr+info^.vsize));
end;
function _vm_get_patch_link(page:p_patch_page;vaddr:Pointer;ptype:t_patch_type):t_patch_info;
var
entry,next:p_patch_node;
begin
Result:=Default(t_patch_info);
if (page=nil) then Exit;
entry:=TAILQ_FIRST(page);
while (entry<>nil) do
begin
next:=TAILQ_NEXT(entry,@entry^.link);
//
if (entry^.info.vaddr=vaddr) and
((ptype=pt_none) or (entry^.info.ptype=ptype)) then
begin
Result:=entry^.info;
p_inc_ref(Result.stub);
end;
//
entry:=next;
end;
end;
function _vm_get_patch_link(page:p_patch_page;vaddr:Pointer;vsize:Integer;ptype:t_patch_type):t_patch_info;
var
entry,next:p_patch_node;
begin
Result:=Default(t_patch_info);
if (page=nil) then Exit;
entry:=TAILQ_FIRST(page);
while (entry<>nil) do
begin
next:=TAILQ_NEXT(entry,@entry^.link);
//
if info_cross(@entry^.info,vaddr,vsize) and
((ptype=pt_none) or (entry^.info.ptype=ptype)) then
begin
Result:=entry^.info;
p_inc_ref(Result.stub);
end;
//
entry:=next;
end;
end;
function vm_get_patch_link(vaddr:Pointer;vsize:Integer;ptype:t_patch_type):t_patch_info;
var
off1,off2:DWORD;
data:PPointer;
page:p_patch_page;
begin
Result:=Default(t_patch_info);
off1:=OFF_TO_IDX(vaddr);
rw_wlock(hamt_lock);
data:=HAMT_search32(@hamt_page,off1);
if (data=nil) then
begin
page:=nil;
end else
begin
page:=data^;
end;
if (vsize=0) then
begin
Result:=_vm_get_patch_link(page,vaddr,ptype);
end else
begin
Result:=_vm_get_patch_link(page,vaddr,vsize,ptype);
off2:=OFF_TO_IDX(vaddr+vsize-1);
if (Result.stub=nil) and (off1<>off2) then
begin
data:=HAMT_search32(@hamt_page,off2);
if (data=nil) then
begin
page:=nil;
end else
begin
page:=data^;
end;
Result:=_vm_get_patch_link(page,vaddr,vsize,ptype);
end;
end;
rw_wunlock(hamt_lock);
end;
function vm_patch_exist(vaddr:Pointer;vsize:Integer):Boolean;
var
info:t_patch_info;
begin
info:=vm_get_patch_link(vaddr,vsize,pt_none);
if (info.stub<>nil) then
begin
p_dec_ref(info.stub);
Result:=True;
end else
begin
Result:=False;
end;
end;
procedure vm_add_patch_link(_obj,vaddr:Pointer;vsize:Integer;ptype:t_patch_type;stub:p_stub_chunk);
var
obj:vm_object_t;
node:p_patch_node;
begin
//Writeln('patch:vaddr=0x',HexStr(vaddr),' type:',ptype);
obj:=_obj;
node:=AllocMem(SizeOf(t_patch_node));
node^.info.vaddr:=vaddr;
node^.info.vsize:=vsize;
node^.info.ptype:=ptype;
node^.info.stub :=stub;
p_inc_ref(stub);
VM_OBJECT_LOCK(obj);
TAILQ_INSERT_TAIL(@obj^.patchq,node,@node^.link);
VM_OBJECT_UNLOCK(obj);
hamt_insert_link(node);
end;
procedure vm_free_patch_link(_obj:Pointer;node:p_patch_node);
var
obj:vm_object_t;
begin
obj:=_obj;
TAILQ_REMOVE(@obj^.patchq,node,@node^.link);
hamt_remove_link(node);
p_dec_ref(node^.info.stub);
FreeMem(node);
end;
procedure vm_object_patch_remove(_obj:Pointer;start,__end:DWORD); public;
var
obj:vm_object_t;
entry,next:p_patch_node;
begin
obj:=_obj;
VM_OBJECT_LOCK(obj);
entry:=TAILQ_FIRST(@obj^.patchq);
while (entry<>nil) do
begin
next:=TAILQ_NEXT(entry,@entry^.link);
//
if ((start=0) or (OFF_TO_IDX(entry^.info.vaddr)>=start)) and
((__end=0) or (OFF_TO_IDX(entry^.info.vaddr)< __end)) then
begin
vm_free_patch_link(_obj,entry);
end;
//
entry:=next;
end;
VM_OBJECT_UNLOCK(obj);
end;
function vm_get_patch_link(_obj,vaddr:Pointer):p_stub_chunk;
var
obj:vm_object_t;
entry,next:p_patch_node;
begin
Result:=nil;
obj:=_obj;
VM_OBJECT_LOCK(obj);
entry:=TAILQ_FIRST(@obj^.patchq);
while (entry<>nil) do
begin
next:=TAILQ_NEXT(entry,@entry^.link);
//
if (entry^.info.vaddr=vaddr) then
begin
Result:=entry^.info.stub;
p_inc_ref(Result);
//
VM_OBJECT_UNLOCK(obj);
Exit;
end;
//
entry:=next;
end;
VM_OBJECT_UNLOCK(obj);
end;
procedure vm_rem_patch_link(_obj,vaddr:Pointer);
var
obj:vm_object_t;
entry,next:p_patch_node;
begin
obj:=_obj;
VM_OBJECT_LOCK(obj);
entry:=TAILQ_FIRST(@obj^.patchq);
while (entry<>nil) do
begin
next:=TAILQ_NEXT(entry,@entry^.link);
//
if (entry^.info.vaddr=vaddr) then
begin
vm_free_patch_link(_obj,entry);
end;
//
entry:=next;
end;
VM_OBJECT_UNLOCK(obj);
end;
end.