unit kern_dynlib;

{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}

interface

uses
 sysutils,
 mqueue,
 kern_rtld,
 subr_dynlib,
 kern_dlsym,
 kern_reloc,
 kern_named_id;

const
 SCE_DBG_MAX_NAME_LENGTH = 256;
 SCE_DBG_MAX_SEGMENTS    = 4;
 SCE_DBG_NUM_FINGERPRINT = 20;

type
 SceKernelModuleSegmentInfo=packed record
  addr:Pointer;
  size:DWORD;
  prot:Integer;  //PF_
 end;

 SceDynlibName=array[0..SCE_DBG_MAX_NAME_LENGTH-1] of AnsiChar;

 pSceKernelModuleInfo=^SceKernelModuleInfo;
 SceKernelModuleInfo=packed record
  size        :QWORD;  //Size of this structure
  name        :SceDynlibName; //name.prx
  segmentInfo :array[0..SCE_DBG_MAX_SEGMENTS-1] of SceKernelModuleSegmentInfo;
  segmentCount:DWORD;
  fingerprint :array[0..SCE_DBG_NUM_FINGERPRINT-1] of Byte;
 end;
 {$IF sizeof(SceKernelModuleInfo)<>352}{$STOP sizeof(SceKernelModuleInfo)<>352}{$ENDIF}

 pSceKernelModuleInfoEx=^SceKernelModuleInfoEx;
 SceKernelModuleInfoEx=packed record
  st_size          :QWORD; //424
  name             :SceDynlibName; //name.prx
  id               :Integer;
  tls_index        :DWORD;
  tls_init_addr    :Pointer;
  tls_init_size    :DWORD;
  tls_size         :DWORD;
  tls_offset       :DWORD;
  tls_align        :DWORD;
  init_proc_addr   :Pointer;
  fini_proc_addr   :Pointer;
  reserved1        :QWORD;
  reserved2        :QWORD;
  eh_frame_hdr_addr:Pointer;
  eh_frame_addr    :Pointer;
  eh_frame_hdr_size:DWORD;
  eh_frame_size    :DWORD;
  segments         :array[0..SCE_DBG_MAX_SEGMENTS-1] of SceKernelModuleSegmentInfo;
  segment_count    :DWORD;
  ref_count        :DWORD;
 end;
 {$IF sizeof(SceKernelModuleInfoEx)<>424}{$STOP sizeof(SceKernelModuleInfoEx)<>424}{$ENDIF}

 pSceModuleInfoForUnwind=^SceModuleInfoForUnwind;
 SceModuleInfoForUnwind=packed record
  st_size          :qword; //304
  name             :SceDynlibName; //name.prx
  eh_frame_hdr_addr:Pointer;
  eh_frame_addr    :Pointer;
  eh_frame_size    :qword;
  seg0_addr        :Pointer;
  seg0_size        :qword;
 end;

function sys_dynlib_dlsym(handle:Integer;symbol:pchar;addrp:ppointer):Integer;
function sys_dynlib_process_needed_and_relocate():Integer;
function sys_dynlib_do_copy_relocations():Integer;
function sys_dynlib_load_prx(moduleFileName:pchar;flags:DWORD;pRes:PInteger;unused:Pointer):Integer;
function sys_dynlib_unload_prx(handle:Integer;args:QWORD;argp:Pointer):Integer;
function sys_dynlib_get_info(handle:Integer;info:Pointer):Integer;
function sys_dynlib_get_info2(handle:Integer;info:Pointer):Integer;
function sys_dynlib_get_info_ex(handle,flags:Integer;info:Pointer):Integer;
function sys_dynlib_get_info_for_libdbg(handle:Integer;info:Pointer):Integer;
function sys_dynlib_get_list(pArray:PInteger;numArray:QWORD;pActualNum:PQWORD):Integer;
function sys_dynlib_get_list2(pArray:PInteger;numArray:QWORD;pActualNum:PQWORD):Integer;
function sys_dynlib_get_list_for_libdbg(pArray:PInteger;numArray:QWORD;pActualNum:PQWORD):Integer;
function sys_dynlib_get_obj_member(handle:Integer;num:Byte;pout:PPointer):Integer;
function sys_dynlib_get_proc_param(pout:PPointer;psize:PQWORD):Integer;
function sys_dl_get_info(pid,handle:Integer;pout:PPointer):Integer;
function sys_dl_get_list(pid:Integer;pArray:PInteger;numArray:Integer;pActualNum:PInteger):Integer;
function sys_dl_get_metadata(pid,handle:Integer;pout:Pointer;size:Integer;pactual_size:PInteger):Integer;

implementation

uses
 errno,
 systm,
 vm,
 kern_proc,
 kern_budget;

function not_dynamic:Boolean; inline;
begin
 Result:=True;
 if (dynlibs_info.libprogram=nil) then Exit;
 if (dynlibs_info.libprogram^.rel_data=nil) then Exit;
 Result:=False;
end;

function sys_dynlib_dlsym(handle:Integer;symbol:pchar;addrp:ppointer):Integer;
label
 _exit;
var
 obj:p_lib_info;
 flags:Integer;
 ptr:Pointer;

 fsym:array[0..2560-1] of char;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_dlsym:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 Result:=copyinstr(symbol,@fsym,sizeof(fsym),nil);
 if (Result<>0) then Exit;

 Writeln('sys_dynlib_dlsym:',fsym);

 dynlibs_lock;

 obj:=find_obj_by_handle(handle);
 if (obj=nil) then
 begin
  Result:=ESRCH;
  goto _exit;
 end;

 flags:=0;
 if (StrLComp(@fsym,'BaOKcng8g88',Length('BaOKcng8g88'))=0) or
    (StrLComp(@fsym,'KpDMrPHvt3Q',Length('KpDMrPHvt3Q'))=0) then
 begin
  flags:=SYMLOOK_BASE64;
 end;

 ptr:=do_dlsym(obj,@fsym,nil,flags);

 if (ptr=nil) then
 begin
  Result:=ESRCH;
  goto _exit;
 end;

 Result:=copyout(@ptr,addrp,SizeOf(Pointer));

 _exit:
  dynlibs_unlock;
end;

function sys_dynlib_process_needed_and_relocate():Integer;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_process_needed_and_relocate:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 dynlibs_lock;

 Result:=dynlib_load_needed_shared_objects();
 if (Result=0) then
 begin
  Result:=dynlib_load_relocate();
 end;

 if (Result=0) then
 begin
  Result:=dmem_process_relocated();
 end;

 dynlibs_unlock;
end;

function sys_dynlib_do_copy_relocations():Integer;
begin
 dynlibs_lock;

 Result:=check_copy_relocations(dynlibs_info.libprogram);

 dynlibs_unlock;
end;

function sys_dynlib_load_prx(moduleFileName:pchar;flags:DWORD;pRes:PInteger;unused:Pointer):Integer;
label
 _exit;
var
 len:ptruint;
 fname:array[0..1024-1] of char;

 obj:p_lib_info;
 key:Integer;
 allocs:Boolean;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_load_prx:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 //0x10000 //priv libs?
 //0x20000 //set jmpslots_done?
 //0x40000 //set not_get_proc?
 if ((flags and $fff8ffff)<>0) then Exit(EINVAL);

 len:=0;
 Result:=copyinstr(moduleFileName,@fname,sizeof(fname),@len);
 if (Result<>0) then Exit;

 Writeln('sys_dynlib_load_prx("',fname,'",0x',HexStr(flags,6),')');

 dynlibs_lock;

 obj:=nil;
 Result:=load_prx(@fname,flags or ord(p_proc.p_budget_ptype=PTYPE_BIG_APP),obj);
 if (Result=0) then
 begin
  allocs:=(obj^.id<=0);

  if (obj^.ref_count < 2) then
  begin
   if not alloc_obj_id(obj) then
   begin
    unload_prx(obj);
    Result:=EAGAIN;
    goto _exit;
   end;
  end;
  key:=obj^.id;

  Result:=copyout(@key,pRes,SizeOf(Integer));
  if (Result<>0) then
  begin
   if allocs then
   begin
    free_obj_id(obj^.id);
    obj^.id:=0;
   end;
   unload_prx(obj);
   Result:=EFAULT;
  end;
 end;

 _exit:
  dynlibs_unlock;

 //dynlib_notify_event(td,resid,0x40);
end;

function sys_dynlib_unload_prx(handle:Integer;args:QWORD;argp:Pointer):Integer;
var
 obj:p_lib_info;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_unload_prx:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 //Writeln('sys_dynlib_unload_prx:',handle);

 dynlibs_lock;

 obj:=find_obj_id(handle);
 if (obj=nil) then
 begin
  Result:=ESRCH;
 end else
 begin
  id_acqure(obj);
  //
  Result:=unload_prx(obj);
  //
  id_release(obj); //<-id_acqure
 end;

 dynlibs_unlock;
end;

function kern_dynlib_get_info(handle,flags:Integer;dst:pSceKernelModuleInfo):Integer;
var
 obj:p_lib_info;
 lib_path:pchar;
 lib_name:pchar;
begin
 Result:=ESRCH;

 dynlibs_lock;

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  if (obj^.id=handle) then
  begin
   dst^:=Default(SceKernelModuleInfo);

   if ((flags and 1)=0) and (obj^.rtld_flags.is_system<>0) then
   begin
    Result:=EPERM;
    Break;
   end;

   lib_path:=obj^.lib_path;
   lib_name:=dynlib_basename(lib_path);

   strlcopy(dst^.name,lib_name,SCE_DBG_MAX_NAME_LENGTH);

   //if not is_webkit then
   begin
    dst^.segmentCount:=2;
    dst^.segmentInfo[0].addr:=obj^.map_base;
    dst^.segmentInfo[0].size:=obj^.text_size;
    dst^.segmentInfo[0].prot:=VM_PROT_READ or VM_PROT_EXECUTE;
    dst^.segmentInfo[1].addr:=obj^.data_addr;
    dst^.segmentInfo[1].size:=obj^.data_size;
    dst^.segmentInfo[1].prot:=VM_PROT_RW;
    if (obj^.relro_addr<>nil) and (obj^.relro_size<>0) then
    begin
     dst^.segmentInfo[2].addr:=obj^.relro_addr;
     dst^.segmentInfo[2].size:=obj^.relro_size;
     dst^.segmentInfo[2].prot:=VM_PROT_READ;
     dst^.segmentCount:=3;
    end;
   end;

   Move(obj^.fingerprint,dst^.fingerprint,SCE_DBG_NUM_FINGERPRINT);

   Result:=0;
   Break;
  end;
  //
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;

 dynlibs_unlock;
end;

function sys_dynlib_get_info(handle:Integer;info:Pointer):Integer;
var
 size:QWORD;
 dst:SceKernelModuleInfo;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_get_info:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 size:=0;
 Result:=copyin(info,@size,SizeOf(QWORD));
 if (Result<>0) then
 begin
  Exit(EFAULT);
 end;

 if (size<>SizeOf(SceKernelModuleInfo)) then
 begin
  Exit(EINVAL);
 end;

 Result:=kern_dynlib_get_info(handle,1,@dst);
 if (Result<>0) then Exit;

 Result:=copyout(@dst,info,SizeOf(SceKernelModuleInfo));
end;

function sys_dynlib_get_info2(handle:Integer;info:Pointer):Integer;
var
 size:QWORD;
 dst:SceKernelModuleInfo;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_get_info2:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 size:=0;
 Result:=copyin(info,@size,SizeOf(QWORD));
 if (Result<>0) then
 begin
  Exit(EFAULT);
 end;

 if (size<>SizeOf(SceKernelModuleInfo)) then
 begin
  Exit(EINVAL);
 end;

 Result:=kern_dynlib_get_info(handle,0,@dst);
 if (Result<>0) then Exit;

 Result:=copyout(@dst,info,SizeOf(SceKernelModuleInfo));
end;

function kern_dynlib_get_info_ex(handle,flags:Integer;dst:pSceKernelModuleInfoEx):Integer;
var
 obj:p_lib_info;
 lib_path:pchar;
 lib_name:pchar;
 tls_index:Integer;
begin
 Result:=ESRCH;

 dynlibs_lock;

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  if (obj^.id=handle) then
  begin
   dst^:=Default(SceKernelModuleInfoEx);

   lib_path:=obj^.lib_path;
   lib_name:=dynlib_basename(lib_path);

   strlcopy(dst^.name,lib_name,SCE_DBG_MAX_NAME_LENGTH);

   dst^.id:=obj^.id;

   tls_index:=WORD(obj^.tls_index);
   dst^.tls_index:=tls_index;

   if ((flags and 1)<>0) then
   begin
    tls_index:=(
                (obj^.rtld_flags.is_system or (obj^.rtld_flags.mainprog shl 1)) shl 16
               ) or tls_index;
    dst^.tls_index:=tls_index;
   end;

   Writeln(' get_info_ex  :',obj^.lib_path);
   Writeln(' obj.id       :',obj^.id);
   Writeln(' tls_flags    :0x',HexStr(tls_index shr 16,4));
   Writeln(' tls_index    :0x',HexStr(tls_index,4));
   Writeln(' tls_init_addr:0x',HexStr(QWORD(obj^.tls_init_addr),11));
   Writeln(' tls_init_size:0x',HexStr(obj^.tls_init_size,8));
   Writeln(' tls_size     :0x',HexStr(obj^.tls_size     ,8));
   Writeln(' tls_offset   :0x',HexStr(obj^.tls_offset   ,8));
   Writeln(' tls_align    :0x',HexStr(obj^.tls_align    ,8));

   if ((flags and 2)<>0) then
   begin
    if (obj^.rtld_flags.is_system<>0) {or (is_webkit)} then
    begin
     FillChar(dst^.name,SCE_DBG_MAX_NAME_LENGTH,0);
    end;
   end;

   dst^.tls_init_addr    :=obj^.tls_init_addr;
   dst^.tls_init_size    :=DWORD(obj^.tls_init_size);
   dst^.tls_size         :=DWORD(obj^.tls_size);
   dst^.tls_offset       :=DWORD(obj^.tls_offset);
   dst^.tls_align        :=DWORD(obj^.tls_align);
   dst^.reserved1        :=0;
   dst^.reserved2        :=0;
   dst^.eh_frame_hdr_addr:=obj^.eh_frame_hdr_addr;
   dst^.eh_frame_addr    :=obj^.eh_frame_addr;
   dst^.eh_frame_hdr_size:=DWORD(obj^.eh_frame_hdr_size);
   dst^.eh_frame_size    :=DWORD(obj^.eh_frame_size);
   dst^.ref_count        :=obj^.ref_count;

   //if not webkit then
   begin
    if (obj^.rtld_flags.not_get_proc=0) then
    begin
     dst^.init_proc_addr:=obj^.init_proc_addr;
     dst^.fini_proc_addr:=obj^.fini_proc_addr;
    end;

    dst^.segments[0].addr:=obj^.map_base;
    dst^.segments[0].size:=obj^.text_size;
    dst^.segments[0].prot:=VM_PROT_READ or VM_PROT_EXECUTE;
    dst^.segments[1].addr:=obj^.data_addr;
    dst^.segments[1].size:=obj^.data_size;
    dst^.segments[1].prot:=VM_PROT_RW;
    dst^.segment_count:=2;
   end;

   Result:=0;
   Break;
  end;
  //
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;

 dynlibs_unlock;
end;

function sys_dynlib_get_info_ex(handle,flags:Integer;info:Pointer):Integer;
var
 size:QWORD;
 dst:SceKernelModuleInfoEx;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_get_info_ex:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 size:=0;
 Result:=copyin(info,@size,SizeOf(QWORD));
 if (Result<>0) then
 begin
  Exit(EFAULT);
 end;

 if (size<>SizeOf(SceKernelModuleInfoEx)) then
 begin
  Exit(EINVAL);
 end;

 Result:=kern_dynlib_get_info_ex(handle,flags,@dst);
 if (Result<>0) then Exit;

 Result:=copyout(@dst,info,SizeOf(SceKernelModuleInfoEx));
end;

function sys_dynlib_get_info_for_libdbg(handle:Integer;info:Pointer):Integer;
begin
 Exit(EPERM);  //sceKernelIsDevelopmentMode
end;

function copyout_module_handle_list(pArray:PInteger;numArray:QWORD;flags:DWORD;pActualNum:PQWORD):Integer;
var
 i,w,count:QWORD;
 src:PInteger;
 obj:p_lib_info;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'copyout_module_handle_list:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 dynlibs_lock;

 i:=0;
 w:=0;
 count:=dynlibs_info.obj_count;

 if (((flags and 1)<>0) and (count > numArray)) then
 begin
  dynlibs_unlock;
  Exit(ENOMEM);
 end;

 src:=AllocMem(count*SizeOf(Integer));

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) and (i<count) do
 begin
  if ((flags and 1)<>0) or (obj^.rtld_flags.is_system=0) then
  begin
   if (w>=numArray) then
   begin
    dynlibs_unlock;
    FreeMem(src);
    Exit(ENOMEM);
   end;
   src[w]:=obj^.id;
   Inc(w);
  end;
  //
  Inc(i);
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;

 dynlibs_unlock;

 if (i<>count) and ((flags and 1)<>0) then
 begin
  Writeln(StdErr,'copyout_module_handle_list:','WARNING: num<>dp^.obj_count');
 end;

 Result:=copyout(src,pArray,w*SizeOf(Integer));

 if (Result=0) then
 begin
  Result:=copyout(@w,pActualNum,8);
 end;

 FreeMem(src);
end;

function sys_dynlib_get_list(pArray:PInteger;numArray:QWORD;pActualNum:PQWORD):Integer;
begin
 Result:=copyout_module_handle_list(pArray,numArray,1,pActualNum);
end;

function sys_dynlib_get_list2(pArray:PInteger;numArray:QWORD;pActualNum:PQWORD):Integer;
begin
 Result:=copyout_module_handle_list(pArray,numArray,0,pActualNum);
end;

function sys_dynlib_get_list_for_libdbg(pArray:PInteger;numArray:QWORD;pActualNum:PQWORD):Integer;
begin
 Exit(EPERM);  //sceKernelIsDevelopmentMode
end;

function sys_dynlib_get_obj_member(handle:Integer;num:Byte;pout:PPointer):Integer;
var
 obj:p_lib_info;
 dst:Pointer;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_get_obj_member:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 dynlibs_lock;

 Result:=ESRCH;

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  if (obj^.id=handle) then
  begin
   dst:=nil;

   case num of
    1:if (obj^.rtld_flags.not_get_proc=0) then
      begin
       dst:=obj^.init_proc_addr;
      end;
    2:if (obj^.rtld_flags.not_get_proc=0) then
      begin
       dst:=obj^.fini_proc_addr;
      end;
    3:begin
       dst:=obj^.eh_frame_hdr_addr;
      end;
    4:begin
       dst:=obj^.eh_frame_addr;
      end;
    7:begin
       dst:=obj^.tls_init_addr;
      end;
    8:begin
       dst:=obj^.module_param;
      end;
    else
      begin
       dynlibs_unlock;
       Exit(EINVAL);
      end;
   end;

   Result:=copyout(@dst,pout,SizeOf(Pointer));

   Break;
  end;
  //
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;

 dynlibs_unlock;
end;

function sys_dynlib_get_proc_param(pout:PPointer;psize:PQWORD):Integer;
begin
 if not_dynamic then
 begin
  Writeln(StdErr,'sys_dynlib_get_proc_param:','this is not dynamic linked program.');
  Exit(EPERM);
 end;

 Result:=copyout(@dynlibs_info.proc_param_addr,pout,SizeOf(Pointer));
 if (Result=0) then
 begin
  Result:=copyout(@dynlibs_info.proc_param_size,psize,SizeOf(QWORD));
 end;
end;

function sys_dl_get_info(pid,handle:Integer;pout:PPointer):Integer;
begin
 Exit(EPERM);  //sceSblACMgrIsDebuggerProcess || sceSblACMgrIsCoredumpProcess || sceSblACMgrIsSyscoreProcess
end;

function sys_dl_get_list(pid:Integer;pArray:PInteger;numArray:Integer;pActualNum:PInteger):Integer;
begin
 Exit(EPERM);  //sceSblACMgrIsDebuggerProcess || sceSblACMgrIsCoredumpProcess || sceSblACMgrIsSyscoreProcess
end;

function sys_dl_get_metadata(pid,handle:Integer;pout:Pointer;size:Integer;pactual_size:PInteger):Integer;
begin
 //sce_comment_addr
 //sce_comment_size
 Exit(EPERM);  //sceSblACMgrIsDebuggerProcess || sceSblACMgrIsCoredumpProcess || sceSblACMgrIsSyscoreProcess
end;


end.





