unit subr_dynlib;

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

interface

uses
 sysutils,
 mqueue,
 hamt,
 elf64,
 kern_thr,
 kern_rtld,
 kern_named_id,
 kern_sx;

type
 TLIBRARY=packed object
  libptr:Pointer; //p_Lib_Entry
  mod_id:Word;
  function set_symb (nid:QWORD;info:Byte;value:Pointer;size:QWORD):Boolean;
  function set_proc (nid:QWORD;value:Pointer):Boolean;            //publish procedure export:[JIT->HLE] or import:[JIT->value^]
  function set_data (nid:QWORD;value:Pointer;size:QWORD):Boolean; //publish data      export:[JIT->HLE]
  function set_weak (nid:QWORD;value:Pointer):Boolean;
  function add_data (value:PPointer;size:QWORD):Boolean;          //alloc local data [guest->value^]
  function add_func (value:PPointer;code:Pointer):Boolean;        //alloc JIT addr   [guest->value^]
  function get_value(nid:QWORD):Pointer;
 end;

 TMODULE=packed object
  libptr:Pointer; //p_lib_info
  import:Word;
  mod_id:Word;
  function add_lib(lib_name:pchar):TLIBRARY;
 end;

 TGUEST_STACK=object
  p_rsp:PPointer;
  o_rsp:Pointer;
  function  alloca(size:QWORD):Pointer;
  procedure epilog;
 end;

function prolog:TGUEST_STACK;

type
 p_rel_data=^t_rel_data;
 t_rel_data=record
  obj        :Pointer;

  symtab_addr:p_elf64_sym;
  symtab_size:QWORD;

  strtab_addr:pchar;
  strtab_size:QWORD;

  pltrela_addr:p_elf64_rela;
  pltrela_size:QWORD;

  rela_addr:p_elf64_rela;
  rela_size:QWORD;

  hash_addr:Pointer;
  hash_size:QWORD;

  dynamic_addr:p_elf64_dyn;
  dynamic_size:QWORD;

  sce_comment_addr:PByte;
  sce_comment_size:QWORD;

  sce_dynlib_addr:Pointer;
  sce_dynlib_size:QWORD;

  execpath:pchar;

  buckets:PDWORD;
  buckets_size:QWORD;

  chains:PDWORD;
  chains_size:QWORD;

  nbuckets:DWORD;
  dynsymcount:DWORD;

  original_filename:pchar;
 end;

 p_lib_info=^t_lib_info;
 t_lib_info=object(t_id_named_desc)
  link:TAILQ_ENTRY;

  lib_path   :PAnsiChar;
  lib_dirname:PAnsiChar;

  ref_count:Integer;
  id       :Integer;

  map_base :Pointer;
  map_size :QWORD;
  text_size:QWORD;

  data_addr:Pointer;
  data_size:QWORD;

  relro_addr:Pointer;
  relro_size:QWORD;

  hle_import_table:Pointer; //p_hle_import_entry
  hle_import_count:Integer; //hle_import_table count

  relocbase :Pointer;
  entry_addr:Pointer;

  tls_index:QWORD;

  tls_init_addr:Pointer;
  tls_init_size:QWORD;
  tls_size     :QWORD;
  tls_offset   :QWORD;
  tls_align    :QWORD;

  //pltgot:Pointer;

  needed     :TAILQ_HEAD; //Needed_Entry
  lib_table  :TAILQ_HEAD; //Lib_Entry
  mod_table  :TAILQ_HEAD; //Lib_Entry
  names      :TAILQ_HEAD; //Name_Entry

  init_proc_addr:Pointer;
  fini_proc_addr:Pointer;

  eh_frame_hdr_addr:Pointer;
  eh_frame_hdr_size:QWORD;

  eh_frame_addr:Pointer;
  eh_frame_size:QWORD;

  loaded:Integer;

  rtld_flags:bitpacked record
   mainprog     :0..1;
   tls_done     :0..1;
   init_scanned :0..1;
   init_done    :0..1;
   on_fini_list :0..1;
   not_get_proc :0..1;
   textrel      :0..1;
   init_plt     :0..1;
   is_system    :0..1;
   dag_inited   :0..1;
   jmpslots_done:0..1;
   internal     :0..1; //HLE
  end;

  dldags    :TAILQ_HEAD; //Objlist_Entry
  dagmembers:TAILQ_HEAD; //Objlist_Entry

  relo_bits:PByte;

  rel_data:p_rel_data;

  fingerprint:array[0..19] of Byte;

  module_param:pSceModuleParam;

  procedure init_rel_data;
  function  add_str(str:pchar):QWORD;
  function  add_lib(lib_name:pchar;import:Word=0):TLIBRARY;
  function  add_mod(mod_name:pchar;import:Word=0):TMODULE;
 end;

 p_Objlist_Entry=^Objlist_Entry;
 Objlist_Entry=record
  link:TAILQ_ENTRY;
  obj :p_lib_info;
 end;

 p_Needed_Entry=^Needed_Entry;
 Needed_Entry=record
  link :TAILQ_ENTRY;
  obj  :p_lib_info;
  flags:QWORD;
  name :Char;
 end;

 p_Name_Entry=^Name_Entry;
 Name_Entry=record
  link:TAILQ_ENTRY;
  name:Char;
 end;

 p_sym_hash_entry=^t_sym_hash_entry;
 t_sym_hash_entry=record
  link  :TAILQ_ENTRY;
  nid   :QWORD;
  mod_id:WORD;
  lib_id:WORD;
  native:Pointer; //HLE
  sym   :elf64_sym;
 end;

 //sum_entry^.native^ -> native
 //sum_entry^.native^ [-> jit_guest_addr] -> jit_host_addr -> JIT -> import_place_addr^

 p_hle_import_entry=^t_hle_import_entry;
 t_hle_import_entry=record
  h_entry          :p_sym_hash_entry;
  jit_guest_addr   :Pointer;
  jit_host_addr    :Pointer;
  import_place_addr:PPointer;
 end;

 p_Lib_Entry=^Lib_Entry;
 Lib_Entry=packed record
  link  :TAILQ_ENTRY;
  dval  :TLibraryValue;
  attr  :WORD;
  import:WORD;
  count :Integer;
  hamt  :THAMT;      //p_sym_hash_entry
  syms  :TAILQ_HEAD; //p_sym_hash_entry
 end;

 t_DoneList=record
  objs:array of p_lib_info;
  num_used:DWORD;
 end;

 p_SymCache=^t_SymCache;
 t_SymCache=record
  sym:p_elf64_sym;
  obj:p_lib_info;
 end;

 p_dynlibs_info=^t_dynlibs_info;
 t_dynlibs_info=record
  lock       :t_sx;

  obj_list   :TAILQ_HEAD; //p_lib_info
  libprogram :p_lib_info;
  libkernel  :p_lib_info;
  obj_count  :Integer;
  list_global:TAILQ_HEAD; //p_Objlist_Entry
  list_main  :TAILQ_HEAD; //p_Objlist_Entry
  init_list  :TAILQ_HEAD; //p_Objlist_Entry
  fini_list  :TAILQ_HEAD; //p_Objlist_Entry

  init_proc_list:TAILQ_HEAD; //p_Objlist_Entry
  fini_proc_list:TAILQ_HEAD; //p_Objlist_Entry

  tls_last_offset :QWORD;
  tls_last_size   :QWORD;
  tls_static_space:QWORD;

  tls_count   :Integer;
  tls_max     :Integer;

  proc_param_addr:pSceProcParam;
  proc_param_size:QWORD;

  rep_unpf:Pointer;
  __freeze:Pointer;
  sysc_s00:Pointer;
  sysc_e00:Pointer;

  sym_zero:elf64_sym;
  sym_nops:elf64_sym;

  dyn_non_exist:Integer;
 end;

const
 IF_PRELOAD =1;
 IF_POSTLOAD=2;

type
 t_int_load=function(name:pchar):p_lib_info;

 p_int_file=^t_int_file;
 t_int_file=record
  link:SLIST_ENTRY;
  name:pchar;
  icbs:t_int_load;
  flag:ptruint;
 end;

function  dynlibs_locked:Boolean;
procedure dynlibs_lock;
procedure dynlibs_unlock;

function  obj_new():p_lib_info;
function  obj_new_int(mod_name:pchar):p_lib_info;
procedure obj_free(obj:p_lib_info);

procedure objlist_push_tail(var list:TAILQ_HEAD;obj:p_lib_info);
function  objlist_find(var list:TAILQ_HEAD;obj:p_lib_info):p_Objlist_Entry;
procedure objlist_remove(var list:TAILQ_HEAD;obj:p_lib_info);

function  obj_get_str      (obj:p_lib_info;offset:Int64):pchar;
procedure object_add_name  (obj:p_lib_info;name:pchar);
function  object_match_name(obj:p_lib_info;name:pchar):Boolean;
procedure obj_set_lib_path (obj:p_lib_info;path:PAnsiChar);

function  Needed_new(obj:p_lib_info;str:pchar):p_Needed_Entry;

function  Lib_Entry_new(d_val:QWORD;import:Word):p_Lib_Entry;
procedure Lib_Entry_free(lib:p_Lib_Entry);

function  get_mod_name_by_id   (obj:p_lib_info;id:Word):pchar;
function  get_lib_name_by_id   (obj:p_lib_info;id:Word):pchar;

function  get_lib_entry_by_id  (obj:p_lib_info;id:Word):p_Lib_Entry;
function  get_lib_entry_by_name(obj:p_lib_info;name:pchar):p_Lib_Entry;

procedure release_per_file_info_obj(obj:p_lib_info);
function  acquire_per_file_info_obj(imgp:p_image_params;new:p_lib_info):Integer;

function  allocate_tls_offset(obj:p_lib_info):Boolean;
procedure free_tls_offset(obj:p_lib_info);

procedure initlist_add_objects(var fini_proc_list:TAILQ_HEAD;
                               obj :p_lib_info;
                               tail:p_lib_info;
                               var init_proc_list:TAILQ_HEAD);

procedure initlist_add_neededs(var fini_proc_list:TAILQ_HEAD;
                               needed:p_Needed_Entry;
                               var init_proc_list:TAILQ_HEAD);

function  digest_dynamic(obj:p_lib_info):Integer;

procedure dynlibs_add_obj(obj:p_lib_info);

procedure init_relo_bits (obj:p_lib_info);
function  check_relo_bits(obj:p_lib_info;i:Integer):Boolean;
procedure set_relo_bits  (obj:p_lib_info;i:Integer);
procedure reset_relo_bits(obj:p_lib_info;i:Integer);

procedure scan_max_size(imgp:p_image_params;
                        phdr:p_elf64_phdr;count:Integer;
                        var max_size1,max_size2:QWORD);

procedure donelist_init(var dlp:t_DoneList);
function  donelist_check(var dlp:t_DoneList;obj:p_lib_info):Boolean;

function  change_relro_protection(obj:p_lib_info;prot:Integer):Integer;
function  change_relro_protection_all(prot:Integer):Integer;

function  relocate_text_or_data_segment(obj:p_lib_info;src,dst:Pointer;size:QWORD):Integer;

procedure init_dag (root:p_lib_info);
procedure ref_dag  (root:p_lib_info);
procedure unref_dag(root:p_lib_info);

function  dynlib_initialize_pltgot_each(obj:p_lib_info):Integer;

function  DecodeSym(obj:p_lib_info;
                    str:pchar;
                    ST_TYPE:Integer;
                    var mod_id,lib_id:WORD;
                    var nid:QWORD):Boolean;

function  do_load_object(path:pchar;flags:DWORD;var err:Integer):p_lib_info;
procedure unload_object(root:p_lib_info);

function  relocate_object(root:p_lib_info):Integer;
function  relocate_object_export_only(obj:p_lib_info):Integer;
function  dynlib_load_relocate():Integer;

procedure pick_obj(obj:p_lib_info);

function  preload_prx_modules(path:pchar;flags:DWORD;var err:Integer):p_lib_info;
function  load_prx(path:pchar;flags:DWORD;var pobj:p_lib_info):Integer;
function  unload_prx(obj:p_lib_info):Integer;

function  alloc_obj_id(obj:p_lib_info):Boolean;
function  free_obj_id (id:Integer):Boolean;
function  find_obj_id (id:Integer):p_lib_info;

function  find_obj_by_handle(id:Integer):p_lib_info;
function  find_obj_by_name  (name:pchar):p_lib_info;

function  find_obj_by_addr_safe(addr:Pointer):p_lib_info;

function  dynlib_load_needed_shared_objects():Integer;

function  copy_proc_param(pout:pSceProcParam):Integer;
function  copy_libc_param(pout:pSceLibcParam):Integer;

const
 UNRESOLVE_MAGIC_MASK:QWORD=QWORD($FFFFFFFF00000000);
 UNRESOLVE_MAGIC_ADDR:QWORD=QWORD($EFFFFFFE00000000);

var
 dynlibs_info:t_dynlibs_info;

 dynlibs_intf:SLIST_HEAD=(slh_first:nil);

Procedure RegisteredInternalFile(var stub:t_int_file;name:pchar;icbs:t_int_load;flag:ptruint=IF_PRELOAD);

implementation

uses
 errno,
 systm,
 subr_backtrace,
 vm,
 vmparam,
 vm_map,
 vm_mmap,
 sys_vm_object,
 vm_pager,
 vuio,
 vstat,
 vfcntl,
 vnode,
 vmount,
 vnamei,
 vfs_lookup,
 vfs_subr,
 vnode_if,
 kern_proc,
 kern_budget,
 kern_authinfo,
 kern_namedobj,
 elf_nid_utils,
 kern_jit_ctx,
 kern_jit_asm,
 kern_jit_dynamic;

//

function relocate_one_object(obj:p_lib_info;jmpslots,export_only:Boolean):Integer; external;
function dynlib_unlink_imported_symbols_each(root,obj:p_lib_info):Integer; external;

//

function dynlibs_locked:Boolean;
begin
 Result:=sx_xlocked(@dynlibs_info.lock);
end;

procedure dynlibs_lock;
begin
 sx_xlock(@dynlibs_info.lock);
end;

procedure dynlibs_unlock;
begin
 sx_xunlock(@dynlibs_info.lock);
end;

Procedure RegisteredInternalFile(var stub:t_int_file;name:pchar;icbs:t_int_load;flag:ptruint=IF_PRELOAD);
begin
 stub.name:=name;
 stub.icbs:=icbs;
 stub.flag:=flag;
 //
 SLIST_INSERT_HEAD(@dynlibs_intf,@stub,@stub.link);
end;

procedure t_lib_info.init_rel_data;
begin
 if (rel_data=nil) then
 begin
  rel_data:=AllocMem(SizeOf(t_rel_data));
 end;
end;

function t_lib_info.add_str(str:pchar):QWORD;
var
 len,size:QWORD;
begin
 init_rel_data;

 len:=strlen(str)+1;
 size:=rel_data^.strtab_size;

 rel_data^.strtab_addr:=ReAllocMem(rel_data^.strtab_addr,size+len);

 Result:=size;

 Move(str^,rel_data^.strtab_addr[size],len);

 rel_data^.strtab_size:=size+len;
end;

function t_lib_info.add_lib(lib_name:pchar;import:Word=0):TLIBRARY;
label
 rep;
var
 lib_entry:p_Lib_Entry;
 v:TLibraryValue;
begin
 Result:=Default(TLIBRARY);

 v:=Default(TLibraryValue);

 rep:
 lib_entry:=TAILQ_FIRST(@lib_table);
 while (lib_entry<>nil) do
 begin
  if (lib_entry^.dval.id=v.id) then
  begin
   Inc(v.id);
   goto rep;
  end;
  lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
 end;

 lib_entry:=Lib_Entry_new(QWORD(v),import);
 lib_entry^.dval.name_offset:=add_str(lib_name);

 TAILQ_INSERT_TAIL(@lib_table,lib_entry,@lib_entry^.link);

 Result.libptr:=lib_entry;
 Result.mod_id:=0; //export (default)
end;

function t_lib_info.add_mod(mod_name:pchar;import:Word=0):TMODULE;
label
 rep;
var
 mod_entry:p_Lib_Entry;
 v:TLibraryValue;
begin
 v:=Default(TLibraryValue);

 rep:
 mod_entry:=TAILQ_FIRST(@mod_table);
 while (mod_entry<>nil) do
 begin
  if (mod_entry^.dval.id=v.id) then
  begin
   Inc(v.id);
   goto rep;
  end;
  mod_entry:=TAILQ_NEXT(mod_entry,@mod_entry^.link)
 end;

 mod_entry:=Lib_Entry_new(QWORD(v),import);
 mod_entry^.dval.name_offset:=add_str(mod_name);

 TAILQ_INSERT_TAIL(@mod_table,mod_entry,@mod_entry^.link);

 Result.libptr:=@Self;
 Result.import:=import;
 Result.mod_id:=mod_entry^.dval.id;
end;

function TMODULE.add_lib(lib_name:pchar):TLIBRARY;
begin
 Result:=p_lib_info(libptr)^.add_lib(lib_name,import);
 Result.mod_id:=mod_id;
end;

function TLIBRARY.set_symb(nid:QWORD;info:Byte;value:Pointer;size:QWORD):Boolean;
var
 lib_entry:p_Lib_Entry;
 h_entry:p_sym_hash_entry;
 data:PPointer;
begin
 lib_entry:=libptr;
 //
 h_entry:=AllocMem(SizeOf(t_sym_hash_entry));
 //
 h_entry^.nid   :=nid;
 h_entry^.mod_id:=mod_id; //export -> mod_id=0
 h_entry^.lib_id:=lib_entry^.dval.id;
 //
 h_entry^.sym.st_info :=info;
 h_entry^.sym.st_value:=0; //jit build
 h_entry^.sym.st_size :=size;
 //
 h_entry^.native:=value;

 //
 if (Lib_Entry^.hamt=nil) then
 begin
  Lib_Entry^.hamt:=HAMT_create64;
  TAILQ_INIT(@Lib_Entry^.syms);
 end;
 //
 data:=HAMT_insert64(Lib_Entry^.hamt,nid,h_entry);
 if (data=nil) then Exit(False); //NOMEM
 //
 if (data^<>h_entry) then
 begin
   //is another exists
  FreeMem(h_entry);
  Result:=False;
 end else
 begin
  //new
  TAILQ_INSERT_TAIL(@Lib_Entry^.syms,h_entry,@h_entry^.link);
  Inc(Lib_Entry^.count);
  Result:=True;
 end;
end;

function TLIBRARY.set_proc(nid:QWORD;value:Pointer):Boolean;
begin
 Result:=set_symb(nid,(STB_GLOBAL shl 4) or STT_FUN,value,0);
end;

function TLIBRARY.set_data(nid:QWORD;value:Pointer;size:QWORD):Boolean;
begin
 Result:=set_symb(nid,(STB_GLOBAL shl 4) or STT_OBJECT,value,size);
end;

function TLIBRARY.set_weak(nid:QWORD;value:Pointer):Boolean;
begin
 Result:=set_symb(nid,(STB_WEAK shl 4) or STT_FUN,value,0);
end;

function TLIBRARY.add_data(value:PPointer;size:QWORD):Boolean;
var
 lib_entry:p_Lib_Entry;
 h_entry:p_sym_hash_entry;
 data:PPointer;
begin
 lib_entry:=libptr;
 //
 h_entry:=AllocMem(SizeOf(t_sym_hash_entry));
 //
 h_entry^.nid   :=0;
 h_entry^.mod_id:=mod_id; //export -> mod_id=0
 h_entry^.lib_id:=lib_entry^.dval.id;
 //
 h_entry^.sym.st_info :=(STB_LOCAL shl 4) or STT_OBJECT;
 h_entry^.sym.st_value:=0; //preload build
 h_entry^.sym.st_size :=size;
 //
 h_entry^.native:=value;

 TAILQ_INSERT_TAIL(@Lib_Entry^.syms,h_entry,@h_entry^.link);
 Inc(Lib_Entry^.count);
 Result:=True;
end;

function TLIBRARY.add_func(value:PPointer;code:Pointer):Boolean;
var
 lib_entry:p_Lib_Entry;
 h_entry:p_sym_hash_entry;
 data:PPointer;
begin
 lib_entry:=libptr;
 //
 h_entry:=AllocMem(SizeOf(t_sym_hash_entry));
 //
 h_entry^.nid   :=0;
 h_entry^.mod_id:=mod_id; //export -> mod_id=0
 h_entry^.lib_id:=lib_entry^.dval.id;
 //
 h_entry^.sym.st_info :=(STB_LOCAL shl 4) or STT_FUN;
 h_entry^.sym.st_value:=PtrUint(code);
 h_entry^.sym.st_size :=0;
 //
 h_entry^.native:=value;

 TAILQ_INSERT_TAIL(@Lib_Entry^.syms,h_entry,@h_entry^.link);
 Inc(Lib_Entry^.count);
 Result:=True;
end;

function TLIBRARY.get_value(nid:QWORD):Pointer;
var
 lib_entry:p_Lib_Entry;
 h_entry:p_sym_hash_entry;
 data:PPointer;
begin
 lib_entry:=libptr;
 //
 if (Lib_Entry^.hamt=nil) then Exit(nil);
 data:=HAMT_search64(Lib_Entry^.hamt,nid);
 if (data=nil) then Exit(nil);
 h_entry:=data^;
 if (h_entry=nil) then Exit(nil);
 Result:=h_entry^.native;
end;

//
function prolog:TGUEST_STACK;
var
 frame:p_jit_frame;
begin
 frame:=@curkthread^.td_frame.tf_r13;
 Result.p_rsp:=@frame^.tf_rsp;
 Result.o_rsp:=Pointer(frame^.tf_rsp);
end;

function TGUEST_STACK.alloca(size:QWORD):Pointer;
begin
 Result:=p_rsp^-size;
 p_rsp^:=Result;
end;

procedure TGUEST_STACK.epilog;
begin
 p_rsp^:=o_rsp;
end;
//

procedure _free_obj(data:pointer);
begin
 FreeMem(data);
end;

function obj_new():p_lib_info;
begin
 Result:=AllocMem(SizeOf(t_lib_info));
 Result^.desc.free:=@_free_obj;
 id_acqure(Result);

 TAILQ_INIT(@Result^.needed);
 TAILQ_INIT(@Result^.lib_table);
 TAILQ_INIT(@Result^.mod_table);
 TAILQ_INIT(@Result^.names);

 TAILQ_INIT(@Result^.dldags);
 TAILQ_INIT(@Result^.dagmembers);

 //Result^.rel_data:=(t_rel_data *)0x0;

 //puVar1:=&(Result^.rtld_flags).field_0x1;
 //*puVar1:=*puVar1 | 2;
end;

function obj_new_int(mod_name:pchar):p_lib_info;
begin
 Result:=obj_new();
 Result^.rtld_flags.internal:=1;
 Result^.tls_align:=1;
 Result^.add_mod(mod_name);
end;

function preprocess_dt_entries(new:p_lib_info;hdr_e_type:Integer):Integer;
label
 _unsupp;
var
 dt_ent:p_elf64_dyn;
 i,count:Integer;

 SCE_SYMTABSZ         :Boolean;
 SCE_HASHSZ           :Boolean;
 SCE_SYMENT           :Boolean;
 SCE_SYMTAB           :Boolean;
 SCE_STRSZ            :Boolean;
 SCE_STRTAB           :Boolean;
 SCE_RELAENT          :Boolean;
 SCE_PLTREL           :Boolean;
 SCE_RELASZ           :Boolean;
 SCE_RELA             :Boolean;
 SCE_PLTRELSZ         :Boolean;
 SCE_JMPREL           :Boolean;
 SCE_PLTGOT           :Boolean;
 SCE_HASH             :Boolean;
 SCE_MODULE_INFO      :Boolean;
 SCE_ORIGINAL_FILENAME:Boolean;
 SCE_FINGERPRINT      :Boolean;
begin
 Result:=0;

 dt_ent:=new^.rel_data^.dynamic_addr;
 count :=new^.rel_data^.dynamic_size div sizeof(elf64_dyn);

 SCE_SYMTABSZ         :=False;
 SCE_HASHSZ           :=False;
 SCE_SYMENT           :=False;
 SCE_SYMTAB           :=False;
 SCE_STRSZ            :=False;
 SCE_STRTAB           :=False;
 SCE_RELAENT          :=False;
 SCE_PLTREL           :=False;
 SCE_RELASZ           :=False;
 SCE_RELA             :=False;
 SCE_PLTRELSZ         :=False;
 SCE_JMPREL           :=False;
 SCE_PLTGOT           :=False;
 SCE_HASH             :=False;
 SCE_MODULE_INFO      :=False;
 SCE_ORIGINAL_FILENAME:=False;
 SCE_FINGERPRINT      :=False;

 if (count<>0) then
 For i:=0 to count-1 do
 begin
  case dt_ent^.d_tag of
   DT_NULL,
   DT_NEEDED,
   DT_INIT,
   DT_FINI,
   DT_SONAME,
   DT_SYMBOLIC,
   DT_DEBUG,
   DT_TEXTREL,
   DT_INIT_ARRAY,
   DT_FINI_ARRAY,
   DT_INIT_ARRAYSZ,
   DT_FINI_ARRAYSZ,
   DT_FLAGS,
   DT_PREINIT_ARRAY,
   DT_PREINIT_ARRAYSZ,
   DT_SCE_NEEDED_MODULE,
   DT_SCE_MODULE_ATTR,
   DT_SCE_EXPORT_LIB,
   DT_SCE_IMPORT_LIB,
   DT_SCE_EXPORT_LIB_ATTR,
   DT_SCE_IMPORT_LIB_ATTR,
   DT_RELACOUNT,
   DT_FLAGS_1:; //ignore

   DT_PLTRELSZ,
   DT_SCE_PLTRELSZ:
     begin
      SCE_PLTRELSZ:=true;
      new^.rel_data^.pltrela_size:=dt_ent^.d_un.d_val;
     end;

   DT_PLTREL,
   DT_SCE_PLTREL:
     begin
      SCE_PLTREL:=true;
      if (dt_ent^.d_un.d_val<>7) then
      begin
       Writeln(StdErr,'preprocess_dt_entries:','illegal value in DT_PLTREL entry',' found in ',new^.lib_path);
       Exit(EINVAL);
      end;
     end;

   DT_RELASZ,
   DT_SCE_RELASZ:
     begin
      SCE_RELASZ:=true;
      new^.rel_data^.rela_size:=dt_ent^.d_un.d_val;
     end;

   DT_RELAENT,
   DT_SCE_RELAENT:
     begin
      SCE_RELAENT:=true;
      if (dt_ent^.d_un.d_val<>24) then
      begin
       Writeln(StdErr,'preprocess_dt_entries:','illegal value in DT_RELAENT entry',' found in ',new^.lib_path);
       Exit(EINVAL);
      end;
     end;

   DT_STRSZ,
   DT_SCE_STRSZ:
     begin
      SCE_STRSZ:=true;
      new^.rel_data^.strtab_size:=dt_ent^.d_un.d_val;
     end;

   DT_SYMENT,
   DT_SCE_SYMENT:
     begin
      SCE_SYMENT:=true;
      if (dt_ent^.d_un.d_val<>24) then
      begin
       Writeln(StdErr,'preprocess_dt_entries:','illegal value in DT_SYMENT entry',' found in ',new^.lib_path);
       Exit(EINVAL);
      end;
     end;

   DT_SCE_FINGERPRINT:
     begin
      SCE_FINGERPRINT:=true;
     end;

   DT_SCE_ORIGINAL_FILENAME:
     begin
      SCE_ORIGINAL_FILENAME:=true;
     end;

   DT_SCE_MODULE_INFO:
     begin
      SCE_MODULE_INFO:=true;
     end;

   DT_SCE_PLTGOT:
     begin
      SCE_PLTGOT:=true;
     end;

   DT_SCE_HASH:
     begin
      SCE_HASH:=true;
      new^.rel_data^.hash_addr:=Pointer(dt_ent^.d_un.d_val);
     end;

   DT_SCE_JMPREL:
     begin
      SCE_JMPREL:=true;
      new^.rel_data^.pltrela_addr:=Pointer(dt_ent^.d_un.d_val);
     end;

   DT_SCE_RELA:
     begin
      SCE_RELA:=true;
      new^.rel_data^.rela_addr:=Pointer(dt_ent^.d_un.d_val);
     end;

   DT_SCE_STRTAB:
     begin
      SCE_STRTAB:=true;
      new^.rel_data^.strtab_addr:=Pointer(dt_ent^.d_un.d_val);
     end;

   DT_SCE_SYMTAB:
     begin
      SCE_SYMTAB:=true;
      new^.rel_data^.symtab_addr:=Pointer(dt_ent^.d_un.d_val);
     end;

   DT_SCE_HASHSZ:
     begin
      SCE_HASHSZ:=true;
      new^.rel_data^.hash_size:=dt_ent^.d_un.d_val;
     end;

   DT_SCE_SYMTABSZ:
     begin
      SCE_SYMTABSZ:=true;
      new^.rel_data^.symtab_size:=dt_ent^.d_un.d_val;
     end;

   DT_PLTGOT,
   DT_RPATH,
   DT_BIND_NOW,
   DT_RUNPATH,
   DT_ENCODING,
   $61000008,
   $6100000a,
   $6100000b,
   $6100000c,
   $6100000e,
   $61000010,
   $61000012,
   $61000014,
   $61000016,
   $61000018,
   $6100001a,
   $6100001b,
   $6100001c,
   DT_SCE_STUB_MODULE_NAME,
   $6100001e,
   DT_SCE_STUB_MODULE_VERSION,
   $61000020,
   DT_SCE_STUB_LIBRARY_NAME,
   $61000022,
   DT_SCE_STUB_LIBRARY_VERSION,
   $61000024,
   $61000026,
   $61000028,
   $6100002a,
   $6100002c,
   $6100002e,
   $61000030,
   $61000032,
   $61000034,
   $61000036,
   $61000038,
   $6100003a,
   $6100003c,
   $6100003e:
     begin
      _unsupp:
      Writeln(StdErr,'preprocess_dt_entries:','Unsupported DT tag 0x',HexStr(dt_ent^.d_tag,8),' found in ',new^.lib_path);
      Exit(ENOEXEC);
     end;

   DT_HASH,
   DT_STRTAB,
   DT_SYMTAB,
   DT_RELA,
   DT_JMPREL:
     begin
      Writeln(StdErr,'preprocess_dt_entries:','ORBIS object file does not support DT tag 0x',HexStr(dt_ent^.d_tag,8),' found in ',new^.lib_path);
      Exit(EINVAL);
     end;

   else
     goto _unsupp;
  end;

  Inc(dt_ent);
 end;

 if (SCE_HASHSZ) and (SCE_SYMTABSZ) then
 begin
   if  ( (hdr_e_type=ET_SCE_DYNAMIC) and ((not SCE_ORIGINAL_FILENAME) or (not SCE_MODULE_INFO)) ) or
       (not SCE_FINGERPRINT) or
       (not SCE_HASH) or
       (not SCE_PLTGOT) or
       (not SCE_JMPREL) or
       (not SCE_PLTREL) or
       (not SCE_PLTRELSZ) or
       (not SCE_RELA) or
       (not SCE_RELASZ) or
       (not SCE_RELAENT) or
       (not SCE_STRTAB) or
       (not SCE_STRSZ) or
       (not SCE_SYMTAB) or
       (not SCE_SYMENT) then
   begin
    Writeln(StdErr,'preprocess_dt_entries:',new^.lib_path,' does not have required tabs.');
    Exit(EINVAL);
   end;
 end else
 begin
  Writeln(StdErr,'preprocess_dt_entries:',new^.lib_path,' does not have DT_SCE_SYMTABSZ or DT_SCE_HASHSZ tabs.');
  Exit(EINVAL);
 end;

end;

procedure release_per_file_info_obj(obj:p_lib_info);
begin
 if (obj^.rel_data<>nil) then
 begin
  vm_object_deallocate(obj^.rel_data^.obj);
  //
  FreeMem(obj^.rel_data);
  obj^.rel_data:=nil;
 end;
end;

function acquire_per_file_info_obj(imgp:p_image_params;new:p_lib_info):Integer;
var
 full_size:QWORD;
 src,dst:Pointer;
begin
 Result:=0;

 if (imgp^.dyn_exist=0) then Exit(EINVAL);

 full_size:=sizeOf(t_rel_data)+
            AlignUp(imgp^.sce_dynlib_data_size,8)+
            AlignUp(imgp^.sce_comment_filesz  ,8)+
            strlen(imgp^.execpath)+1;

 new^.rel_data:=AllocMem(full_size);

 dst:=Pointer(new^.rel_data+1);

 src:=Pointer(imgp^.image_header)+imgp^.sce_dynlib_data_addr;

 Move(src^,dst^,imgp^.sce_dynlib_data_size);

 new^.rel_data^.sce_dynlib_addr:=dst;
 new^.rel_data^.sce_dynlib_size:=imgp^.sce_dynlib_data_size;

 dst:=dst+AlignUp(imgp^.sce_dynlib_data_size,8);

 if (imgp^.sce_comment_filesz<>0) then
 begin
  src:=Pointer(imgp^.image_header)+imgp^.sce_comment_offset;

  Move(src^,dst^,imgp^.sce_comment_filesz);

  new^.rel_data^.sce_comment_addr:=dst;
  new^.rel_data^.sce_comment_size:=imgp^.sce_comment_filesz;

  dst:=dst+AlignUp(imgp^.sce_comment_filesz,8);
 end;

 Move(imgp^.execpath^,dst^,strlen(imgp^.execpath));

 new^.rel_data^.execpath:=dst;

 src:=new^.rel_data^.sce_dynlib_addr;

 new^.rel_data^.dynamic_addr:=src+imgp^.dyn_offset;
 new^.rel_data^.dynamic_size:=imgp^.dyn_filesz;

 Result:=preprocess_dt_entries(new,imgp^.hdr_e_type);

 if (Result<>0) then
 begin
  FreeMem(new^.rel_data);
  new^.rel_data:=nil;
  Exit;
 end;

 src:=new^.rel_data^.sce_dynlib_addr;

 new^.rel_data^.symtab_addr :=Pointer(QWORD(src)+QWORD(new^.rel_data^.symtab_addr ));
 new^.rel_data^.strtab_addr :=Pointer(QWORD(src)+QWORD(new^.rel_data^.strtab_addr ));
 new^.rel_data^.pltrela_addr:=Pointer(QWORD(src)+QWORD(new^.rel_data^.pltrela_addr));
 new^.rel_data^.rela_addr   :=Pointer(QWORD(src)+QWORD(new^.rel_data^.rela_addr   ));
 new^.rel_data^.hash_addr   :=Pointer(QWORD(src)+QWORD(new^.rel_data^.hash_addr   ));

 src:=new^.rel_data^.hash_addr;

 new^.rel_data^.nbuckets    :=PDWORD(src)^;
 new^.rel_data^.buckets_size:=new^.rel_data^.nbuckets shl 2;

 new^.rel_data^.buckets    :=Pointer(QWORD(src) + 8);
 new^.rel_data^.dynsymcount:=PDWORD (QWORD(src) + 4)^;
 new^.rel_data^.chains_size:=new^.rel_data^.dynsymcount shl 2;

 new^.rel_data^.chains:=Pointer(QWORD(src) + (new^.rel_data^.nbuckets + 2) * 4);

 new^.rel_data^.obj:=imgp^.obj;
 vm_object_reference(imgp^.obj);
end;

function allocate_tls_offset(obj:p_lib_info):Boolean;
var
 off:Int64;
begin
 if (obj^.rtld_flags.tls_done<>0) then
 begin
  Exit(True);
 end;

 off:=obj^.tls_size;
 if (off=0) then
 begin
  obj^.rtld_flags.tls_done:=1;
  Exit(True);
 end;

 if (obj^.tls_index=1) then
 begin
  //
 end else
 begin
  off:=off + dynlibs_info.tls_last_offset;
 end;

 off:=(off + obj^.tls_align - 1) and (-obj^.tls_align);

 if ((dynlibs_info.tls_static_space<>0) and (dynlibs_info.tls_static_space < off)) then
 begin
  Exit(False);
 end;

 obj^.tls_offset:=off;

 Writeln('allocate_tls_offset: tls_index=',obj^.tls_index,' tls_size=',obj^.tls_size,' tls_align=',obj^.tls_align,' tls_offset=',obj^.tls_offset);

 dynlibs_info.tls_last_offset:=off;
 dynlibs_info.tls_last_size  :=obj^.tls_size;

 obj^.rtld_flags.tls_done:=1;

 Result:=True;
end;

procedure free_tls_offset(obj:p_lib_info);
begin
 if (obj^.rtld_flags.tls_done<>0) and (obj^.tls_offset=dynlibs_info.tls_last_offset) then
 begin
  dynlibs_info.tls_last_offset:=obj^.tls_offset - obj^.tls_size;
  dynlibs_info.tls_last_size  :=0;
 end;
end;

procedure obj_free(obj:p_lib_info);
var
 needed:p_Needed_Entry;
 names:p_Name_Entry;
 dag:p_Objlist_Entry;
 Lib_Entry:p_Lib_Entry;
begin
 free_tls_offset(obj);

 needed:=TAILQ_FIRST(@obj^.needed);
 while (needed<>nil) do
 begin
  TAILQ_REMOVE(@obj^.needed,needed,@needed^.link);
  FreeMem(needed);
  needed:=TAILQ_FIRST(@obj^.needed);
 end;

 names:=TAILQ_FIRST(@obj^.names);
 while (names<>nil) do
 begin
  TAILQ_REMOVE(@obj^.names,names,@names^.link);
  FreeMem(names);
  names:=TAILQ_FIRST(@obj^.names);
 end;

 dag:=TAILQ_FIRST(@obj^.dldags);
 while (dag<>nil) do
 begin
  TAILQ_REMOVE(@obj^.dldags,dag,@dag^.link);
  FreeMem(dag);
  dag:=TAILQ_FIRST(@obj^.dldags);
 end;

 dag:=TAILQ_FIRST(@obj^.dagmembers);
 while (dag<>nil) do
 begin
  TAILQ_REMOVE(@obj^.dagmembers,dag,@dag^.link);
  FreeMem(dag);
  dag:=TAILQ_FIRST(@obj^.dagmembers);
 end;

 if (obj^.lib_dirname<>nil) then
 begin
  FreeMem(obj^.lib_dirname);
  obj^.lib_dirname:=nil;
 end;

 if (obj^.lib_path<>nil) then
 begin
  FreeMem(obj^.lib_path);
  obj^.lib_path:=nil;
 end;

 if (obj^.relo_bits<>nil) then
 begin
  FreeMem(obj^.relo_bits);
  obj^.relo_bits:=nil
 end;

 FreeMem(obj^.hle_import_table); //HLE

 Lib_Entry:=TAILQ_FIRST(@obj^.lib_table);
 while (Lib_Entry<>nil) do
 begin
  TAILQ_REMOVE(@obj^.lib_table,Lib_Entry,@Lib_Entry^.link);
  Lib_Entry_free(Lib_Entry);
  Lib_Entry:=TAILQ_FIRST(@obj^.lib_table);
 end;

 Lib_Entry:=TAILQ_FIRST(@obj^.mod_table);
 while (Lib_Entry<>nil) do
 begin
  TAILQ_REMOVE(@obj^.mod_table,Lib_Entry,@Lib_Entry^.link);
  Lib_Entry_free(Lib_Entry);
  Lib_Entry:=TAILQ_FIRST(@obj^.mod_table);
 end;

 release_per_file_info_obj(obj);

 free_obj_id(obj^.id);

 id_release(obj); //-> obj_new
end;

procedure objlist_push_tail(var list:TAILQ_HEAD;obj:p_lib_info);
var
 entry:p_Objlist_Entry;
begin
 entry:=AllocMem(SizeOf(Objlist_Entry));
 entry^.obj:=obj;
 //
 TAILQ_INSERT_TAIL(@list,entry,@entry^.link);
end;

function objlist_find(var list:TAILQ_HEAD;obj:p_lib_info):p_Objlist_Entry;
var
 elm:p_Objlist_Entry;
begin
 elm:=TAILQ_FIRST(@dynlibs_info.list_global);
 while (elm<>nil) do
 begin
  if (elm^.obj=obj) then Exit(elm);
  //
  elm:=TAILQ_NEXT(elm,@elm^.link);
 end;
 Result:=nil;
end;

procedure objlist_remove(var list:TAILQ_HEAD;obj:p_lib_info);
var
 elm:p_Objlist_Entry;
begin
 elm:=objlist_find(list,obj);
 if (elm<>nil) then
 begin
  TAILQ_REMOVE(@list,elm,@elm^.link);
  FreeMem(elm);
 end;
end;

function obj_get_str(obj:p_lib_info;offset:Int64):pchar;
begin
 if (obj^.rel_data^.strtab_size<=offset) then
 begin
  Writeln(StdErr,'obj_get_str:','offset=0x',HexStr(offset,8),' is out of range of string table of ',obj^.lib_path);
  Exit(nil);
 end;

 Result:=obj^.rel_data^.strtab_addr+offset;
end;

procedure object_add_name(obj:p_lib_info;name:pchar);
var
 len:Integer;
 entry:p_Name_Entry;
begin
 len:=strlen(name);
 entry:=AllocMem(SizeOf(Name_Entry)+len);
 Move(name^,entry^.name,len);
 //
 TAILQ_INSERT_TAIL(@obj^.names,entry,@entry^.link);
end;

function object_match_name(obj:p_lib_info;name:pchar):Boolean;
var
 entry:p_Name_Entry;
begin
 entry:=TAILQ_FIRST(@obj^.names);
 while (entry<>nil) do
 begin
  if (StrComp(name,@entry^.name)=0) then
  begin
   Exit(True);
  end;
  entry:=TAILQ_NEXT(entry,@entry^.link);
 end;
 Result:=False;
end;

procedure obj_set_lib_path(obj:p_lib_info;path:PAnsiChar);
var
 size:int64;
begin
 size:=strlen(path);
 obj^.lib_path:=AllocMem(size+1);
 Move(path^,obj^.lib_path^,size);
end;

function Needed_new(obj:p_lib_info;str:pchar):p_Needed_Entry;
var
 len:Integer;
begin
 len:=strlen(str);
 Result:=AllocMem(SizeOf(Needed_Entry)+len);
 Result^.obj :=obj;
 Move(str^,Result^.name,len);
end;

function Lib_Entry_new(d_val:QWORD;import:Word):p_Lib_Entry;
begin
 Result:=AllocMem(SizeOf(Lib_Entry));
 QWORD(Result^.dval):=d_val;
 Result^.import:=import;
end;

procedure free_sym_hash_entry(data,userdata:Pointer); register;
begin
 FreeMem(data);
end;

procedure Lib_Entry_free(lib:p_Lib_Entry);
begin
 if (lib=nil) then Exit;
 if (lib^.hamt<>nil) then
 begin
  HAMT_destroy64(lib^.hamt,@free_sym_hash_entry,nil);
 end;
 //
 FreeMem(lib);
end;

function get_mod_name_by_id(obj:p_lib_info;id:Word):pchar;
var
 lib_entry:p_Lib_Entry;
 offset:QWORD;
begin
 Result:=nil;
 lib_entry:=TAILQ_FIRST(@obj^.mod_table);
 while (lib_entry<>nil) do
 begin
  if (lib_entry^.dval.id=id) then
  begin
   offset:=lib_entry^.dval.name_offset;
   Result:=obj_get_str(obj,offset);
   Exit;
  end;
  lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
 end;
end;

function get_lib_name_by_id(obj:p_lib_info;id:Word):pchar;
var
 lib_entry:p_Lib_Entry;
 offset:QWORD;
begin
 Result:=nil;
 lib_entry:=TAILQ_FIRST(@obj^.lib_table);
 while (lib_entry<>nil) do
 begin
  if (lib_entry^.dval.id=id) then
  begin
   offset:=lib_entry^.dval.name_offset;
   Result:=obj_get_str(obj,offset);
   Exit;
  end;
  lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
 end;
end;

function get_lib_entry_by_id(obj:p_lib_info;id:Word):p_Lib_Entry;
var
 lib_entry:p_Lib_Entry;
begin
 Result:=nil;
 lib_entry:=TAILQ_FIRST(@obj^.lib_table);
 while (lib_entry<>nil) do
 begin
  if (lib_entry^.dval.id=id) then
  begin
   Exit(lib_entry);
  end;
  lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
 end;
end;

function get_lib_entry_by_name(obj:p_lib_info;name:pchar):p_Lib_Entry;
var
 lib_entry:p_Lib_Entry;
 offset:QWORD;
 str:pchar;
begin
 Result:=nil;
 if (name=nil) then Exit;
 lib_entry:=TAILQ_FIRST(@obj^.lib_table);
 while (lib_entry<>nil) do
 begin
  offset:=lib_entry^.dval.name_offset;
  str:=obj_get_str(obj,offset);
  if (StrComp(str,name)=0) then
  begin
   Exit(lib_entry);
  end;
  lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
 end;
end;

procedure initlist_add_objects(var fini_proc_list:TAILQ_HEAD;
                               obj :p_lib_info;
                               tail:p_lib_info;
                               var init_proc_list:TAILQ_HEAD);
begin
 if (obj^.rtld_flags.init_scanned<>0) or
    (obj^.rtld_flags.init_done<>0) then Exit;
 obj^.rtld_flags.init_scanned:=1;

 if (obj<>tail) then
 begin
  initlist_add_objects(fini_proc_list,obj^.link.tqe_next,tail,init_proc_list);
 end;

 if (obj^.needed.tqh_first<>nil) then
 begin
  initlist_add_neededs(fini_proc_list,obj^.needed.tqh_first,init_proc_list);
 end;

 if (obj^.init_proc_addr<>nil) then
 begin
  objlist_push_tail(init_proc_list,obj);
 end;

 if (obj^.fini_proc_addr<>nil) and
    (obj^.rtld_flags.on_fini_list=0) then
 begin
  objlist_push_tail(fini_proc_list,obj);

  obj^.rtld_flags.on_fini_list:=1;
 end;
end;

procedure initlist_add_neededs(var fini_proc_list:TAILQ_HEAD;
                               needed:p_Needed_Entry;
                               var init_proc_list:TAILQ_HEAD);
var
 obj:p_lib_info;
begin
 if (needed^.link.tqe_next<>nil) then
 begin
  initlist_add_neededs(fini_proc_list,needed^.link.tqe_next,init_proc_list);
 end;

 obj:=needed^.obj;
 if (obj<>nil) then
 begin
  initlist_add_objects(fini_proc_list,obj,obj,init_proc_list);
 end;
end;

function digest_dynamic(obj:p_lib_info):Integer;
var
 dt_ent:p_elf64_dyn;
 i,count:Integer;

 str:pchar;

 needed:p_Needed_Entry;
 lib_entry:p_Lib_Entry;

 addr:Pointer;
 dval:QWORD;

 dyn_soname:p_elf64_dyn;
 dt_fingerprint:Int64;
begin
 Result:=0;

 dyn_soname:=nil;
 dt_fingerprint:=-1;

 if (obj^.rel_data<>nil) then
 begin
  dt_ent:=obj^.rel_data^.dynamic_addr;
  count :=obj^.rel_data^.dynamic_size div sizeof(elf64_dyn);

  if (count<>0) then
  For i:=0 to count-1 do
  begin

   case dt_ent^.d_tag of
    DT_NULL,
    DT_PLTRELSZ,
    DT_HASH,
    DT_STRTAB,
    DT_SYMTAB,
    DT_RELA,
    DT_RELASZ,
    DT_RELAENT,
    DT_STRSZ,
    DT_SYMENT,
    DT_PLTREL,
    DT_DEBUG,
    DT_JMPREL,
    DT_INIT_ARRAY,
    DT_FINI_ARRAY,
    DT_INIT_ARRAYSZ,
    DT_FINI_ARRAYSZ,
    DT_PREINIT_ARRAY,
    DT_PREINIT_ARRAYSZ,
    DT_SCE_HASH,
    DT_SCE_JMPREL,
    DT_SCE_PLTREL,
    DT_SCE_PLTRELSZ,
    DT_SCE_RELA,
    DT_SCE_RELASZ,
    DT_SCE_RELAENT,
    DT_SCE_STRTAB,
    DT_SCE_STRSZ,
    DT_SCE_SYMTAB,
    DT_SCE_SYMENT,
    DT_SCE_HASHSZ,
    DT_SCE_SYMTABSZ,
    DT_RELACOUNT:;  //ignore

    DT_SONAME:
     begin
      dyn_soname:=dt_ent;
     end;

    DT_PLTGOT,
    DT_SCE_PLTGOT:
     begin
      //pltgot
     end;

    DT_NEEDED:
      begin
       str:=obj_get_str(obj,dt_ent^.d_un.d_val);

       if (str=nil) then
       begin
        Writeln(StdErr,'digest_dynamic:',{$INCLUDE %LINE%});
        Exit(EINVAL);
       end;

       needed:=Needed_new(obj,str);
       TAILQ_INSERT_TAIL(@obj^.needed,needed,@Needed^.link);
      end;

    DT_INIT:
      begin
       addr:=obj^.relocbase+dt_ent^.d_un.d_val;
       obj^.init_proc_addr:=addr;

       if (obj^.map_base>addr) or ((addr+8)>(obj^.map_base+obj^.text_size)) then
       begin
        Writeln(StdErr,'digest_dynamic:',{$INCLUDE %LINE%});
        Exit(ENOEXEC);
       end;
      end;

    DT_FINI:
      begin
       addr:=obj^.relocbase+dt_ent^.d_un.d_val;
       obj^.fini_proc_addr:=addr;

       if (obj^.map_base>addr) or ((addr+8)>(obj^.map_base+obj^.text_size)) then
       begin
        Writeln(StdErr,'digest_dynamic:',{$INCLUDE %LINE%});
        Exit(ENOEXEC);
       end;
      end;

    DT_SYMBOLIC:
      begin
       Writeln(StdErr,'digest_dynamic:','DT_SYMBOLIC is obsolete.');
       Exit(EINVAL);
      end;

    DT_TEXTREL:
      begin
       obj^.rtld_flags.textrel:=1;
      end;

    DT_FLAGS:
      begin
       dval:=dt_ent^.d_un.d_val;

       if ((dval and DF_SYMBOLIC)<>0) then
       begin
        Writeln(StdErr,'digest_dynamic:','DT_SYMBOLIC is obsolete.');
        Exit(EINVAL);
       end;

       if ((dval and DF_BIND_NOW)<>0) then
       begin
        Writeln(StdErr,'digest_dynamic:','DF_BIND_NOW is obsolete.');
        Exit(EINVAL);
       end;

       if ((dval and DF_TEXTREL)<>0) then
       begin
        obj^.rtld_flags.textrel:=1;
       end;
      end;

    DT_SCE_FINGERPRINT:
      begin
       dt_fingerprint:=dt_ent^.d_un.d_val;

       if (obj^.rel_data=nil) or
          ((dt_fingerprint + 20)>obj^.rel_data^.sce_dynlib_size) then
       begin
        Writeln(StdErr,'digest_dynamic:',{$INCLUDE %LINE%});
        Exit(ENOEXEC);
       end;
      end;

    DT_SCE_ORIGINAL_FILENAME:
      begin
       str:=obj_get_str(obj,dt_ent^.d_un.d_val);

       if (str=nil) then
       begin
        Writeln(StdErr,'digest_dynamic:',{$INCLUDE %LINE%});
        Exit(EINVAL);
       end;

       obj^.rel_data^.original_filename:=str;
      end;

    DT_SCE_MODULE_INFO,
    DT_SCE_NEEDED_MODULE:
      begin
       lib_entry:=Lib_Entry_new(dt_ent^.d_un.d_val,ord(dt_ent^.d_tag=DT_SCE_NEEDED_MODULE));
       TAILQ_INSERT_TAIL(@obj^.mod_table,lib_entry,@lib_entry^.link);
      end;

    DT_SCE_MODULE_ATTR:
      begin
       dval:=dt_ent^.d_un.d_val;

       lib_entry:=TAILQ_FIRST(@obj^.mod_table);
       while (lib_entry<>nil) do
       begin
        if (TLibraryAttr(dval).id=lib_entry^.dval.id) then
        begin
         Break;
        end;
        lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
       end;

       if (lib_entry=nil) then
       begin
        Writeln(StdErr,'digest_dynamic:','unknown ID found in DT_SCE_*_MODULE_ATTR entry ',TLibraryAttr(dval).id);
        Exit(EINVAL);
       end;

       lib_entry^.attr:=TLibraryAttr(dval).attr;
      end;

    DT_SCE_EXPORT_LIB,
    DT_SCE_IMPORT_LIB:
      begin
       lib_entry:=Lib_Entry_new(dt_ent^.d_un.d_val,ord(dt_ent^.d_tag=DT_SCE_IMPORT_LIB));
       TAILQ_INSERT_TAIL(@obj^.lib_table,lib_entry,@lib_entry^.link);
      end;

    DT_SCE_EXPORT_LIB_ATTR,
    DT_SCE_IMPORT_LIB_ATTR:
      begin
       dval:=dt_ent^.d_un.d_val;

       lib_entry:=TAILQ_FIRST(@obj^.lib_table);
       while (lib_entry<>nil) do
       begin
        if (TLibraryAttr(dval).id=lib_entry^.dval.id) then
        begin
         Break;
        end;
        lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
       end;

       if (lib_entry=nil) then
       begin
        Writeln(StdErr,'digest_dynamic:','unknown ID found in DT_SCE_*_LIB_ATTR entry ',TLibraryAttr(dval).id);
        Exit(EINVAL);
       end;

       lib_entry^.attr:=TLibraryAttr(dval).attr;
      end;

    DT_FLAGS_1:
      begin
       dval:=dt_ent^.d_un.d_val;

       if ((dval and DF_1_BIND_NOW)<>0) then
       begin
        Writeln(StdErr,'digest_dynamic:','DF_1_BIND_NOW is obsolete.');
        Exit(EINVAL);
       end;

       if ((dval and DF_1_NODELETE)<>0) then
       begin
        Writeln(StdErr,'digest_dynamic:','DF_1_NODELETE is obsolete.');
        Exit(EINVAL);
       end;

       if ((dval and DF_1_LOADFLTR)<>0) then
       begin
        Writeln(StdErr,'digest_dynamic:','DF_1_LOADFLTR is obsolete.');
        Exit(EINVAL);
       end;

       if ((dval and DF_1_NOOPEN)<>0) then
       begin
        Writeln(StdErr,'digest_dynamic:','DF_1_NOOPEN is obsolete.');
        Exit(EINVAL);
       end;
      end;

    else
      begin
       Writeln(StdErr,'digest_dynamic:','Unsupported DT tag 0x',HexStr(dt_ent^.d_tag,8),' found in ',obj^.lib_path);
       Exit(ENOEXEC);
      end;

   end; //case

   Inc(dt_ent);
  end; //for

 end;

 addr:=obj^.rel_data^.sce_dynlib_addr;

 if (dt_fingerprint=-1) then
 begin
  if (addr<>nil) then
  begin
   Move(addr^,obj^.fingerprint,20);
  end;
 end else
 begin
  if (addr<>nil) then
  begin
   Move((addr+dt_fingerprint)^,obj^.fingerprint,20);
  end;
 end;

 if (obj^.lib_path<>nil) then
 begin
  obj^.lib_dirname:=AllocMem(strlen(obj^.lib_path)+1);
  //
  Result:=rtld_dirname(obj^.lib_path,obj^.lib_dirname);
  if (Result<>0) then
  begin
   Exit(EINVAL);
  end;
 end;

 if (dyn_soname<>nil) then
 begin
  str:=obj_get_str(obj,dyn_soname^.d_un.d_val);

  if (str=nil) then
  begin
   Writeln(StdErr,'digest_dynamic:',{$INCLUDE %LINE%});
   Exit(EINVAL);
  end;

  object_add_name(obj,str);
 end;

end;

procedure dynlibs_add_obj(obj:p_lib_info);
begin
 TAILQ_INSERT_TAIL(@dynlibs_info.obj_list,obj,@obj^.link);
 Inc(dynlibs_info.obj_count);
end;

procedure dynlibs_del_obj(obj:p_lib_info);
begin
 TAILQ_REMOVE(@dynlibs_info.obj_list,obj,@obj^.link);
 Dec(dynlibs_info.obj_count);
end;

procedure init_relo_bits(obj:p_lib_info);
var
 count:Integer;
begin
 if (obj^.rel_data=nil) then
 begin
  count:=0;
 end else
 begin
  count:=(obj^.rel_data^.pltrela_size div sizeof(elf64_rela)) +
         (obj^.rel_data^.rela_size div sizeof(elf64_rela)) +
         (obj^.hle_import_count);
 end;

 if (count=0) then
 begin
  obj^.relo_bits:=nil;
 end else
 begin
  obj^.relo_bits:=AllocMem((count+7) div 8);
 end;

end;

function check_relo_bits(obj:p_lib_info;i:Integer):Boolean;
var
 p:PByte;
begin
 if (obj^.relo_bits=nil) then Exit(False);

 p:=@obj^.relo_bits[i shr 3];

 Result:=((p^ shr (i and 7)) and 1)<>0;
end;

procedure set_relo_bits(obj:p_lib_info;i:Integer);
var
 p:PByte;
begin
 if (obj^.relo_bits=nil) then Exit;

 p:=@obj^.relo_bits[i shr 3];

 p^:=p^ or (1 shl (i and 7));
end;

procedure reset_relo_bits(obj:p_lib_info;i:Integer);
var
 p:PByte;
begin
 if (obj^.relo_bits=nil) then Exit;

 p:=@obj^.relo_bits[i shr 3];

 p^:=p^ and (not (1 shl (i and 7)));
end;

function dynlib_load_sections(imgp:p_image_params;new:p_lib_info;phdr:p_elf64_phdr;count:Integer;delta:QWORD;wire:Integer):Integer;
var
 i:Integer;

 hdr:p_elf64_hdr;

 total_size:QWORD;
 data_size :QWORD;
 data_addr :QWORD;
 text_addr :QWORD;
 text_size :QWORD;

 p_memsz   :QWORD;
 p_vaddr   :QWORD;
 p_filesz  :QWORD;
 p_offset  :QWORD;

 addr:QWORD;
 size:QWORD;

 p_type   :Elf64_Word;
 p_flags  :Byte;
 _2mb_mode:Boolean;
 used_mode_2m:Boolean;

 fname:PChar;

 cache:Pointer;
begin
 Result:=0;

 fname:=dynlib_basename(imgp^.execpath);

 total_size:=0;
 data_size :=0;
 data_addr :=0;
 text_addr :=0;
 text_size :=0;

 if (p_proc.p_budget_ptype=PTYPE_BIG_APP) then
 begin
  _2mb_mode:=((g_mode_2mb or 1)=3); //M2MB_READONLY,M2MB_ENABLE
 end else
 begin
  _2mb_mode:=False;
 end;

 cache:=nil;

 if (count<>0) then
 For i:=0 to count-1 do
 begin
  p_type :=phdr^.p_type;
  p_memsz:=phdr^.p_memsz;

  if ((p_type=PT_SCE_RELRO) or (p_type=PT_LOAD)) and (p_memsz<>0) then
  begin

   p_flags:=VM_PROT_READ or VM_PROT_WRITE;
   if (p_type<>PT_SCE_RELRO) then
   begin
    p_flags:=convert_prot(phdr^.p_flags);
   end;

   p_vaddr:=delta+phdr^.p_vaddr;

   p_filesz:=phdr^.p_filesz;
   p_offset:=phdr^.p_offset;

   if (p_type=PT_SCE_RELRO) then
   begin

    if (_2mb_mode=false) then
    begin
     used_mode_2m:=false;
    end else
    begin
     used_mode_2m:=is_used_mode_2mb(phdr,1,p_proc.p_budget_ptype);
    end;

    Result:=self_load_section(imgp,
                              i,
                              p_vaddr,
                              p_offset,
                              p_memsz,
                              p_filesz,
                              p_flags,
                              wire,
                              used_mode_2m,
                              fname,
                              cache);

   end else
   begin

    if (_2mb_mode=false) then
    begin
     used_mode_2m:=false;
    end else
    begin
     used_mode_2m:=is_used_mode_2mb(phdr,1,p_proc.p_budget_ptype);
    end;

    Result:=self_load_section(imgp,
                              i,
                              p_vaddr,
                              p_offset,
                              p_memsz,
                              p_filesz,
                              p_flags,
                              wire,
                              used_mode_2m,
                              fname,
                              cache);
   end;
   if (Result<>0) then
   begin
    FreeMem(cache);
    Exit;
   end;

   addr:=(p_vaddr and QWORD(not PAGE_MASK));
   size:=((p_vaddr and PAGE_MASK) + PAGE_MASK + phdr^.p_memsz) and QWORD(not PAGE_MASK);

   if (p_type=PT_SCE_RELRO) then
   begin
    imgp^.relro_addr:=Pointer(addr);
    imgp^.relro_size:=size;
   end else
   if ((phdr^.p_flags and PF_X)<>0) and (text_size < size) then
   begin
    text_size:=size;
    text_addr:=addr;
   end else
   begin
    data_size:=size;
    data_addr:=addr;
   end;

   total_size:=total_size+size;
  end;

  Inc(phdr);
 end;

 FreeMem(cache);

 if (data_addr=0) and (data_size=0) then
 begin
  data_addr:=text_addr;
  data_size:=text_size;
 end;

 if (imgp^.relro_addr<>nil) and (imgp^.relro_size<>0) then
 begin
  Result:=vm_map_protect(p_proc.p_vmspace,QWORD(imgp^.relro_addr),QWORD(imgp^.reloc_base)+imgp^.relro_size,VM_PROT_READ,False);
  Result:=vm_mmap_to_errno(Result);
  if (Result<>0) then Exit;
 end;

 addr:=imgp^.min_addr;

 imgp^.dyn_vaddr    :=Pointer(imgp^.dyn_vaddr    )+addr;
 imgp^.entry_addr   :=Pointer(imgp^.entry_addr   )+addr;
 imgp^.tls_init_addr:=Pointer(imgp^.tls_init_addr)+addr;

 if (imgp^.eh_frame_hdr_addr<>nil) then
 begin
  imgp^.eh_frame_hdr_addr:=Pointer(imgp^.eh_frame_hdr_addr)+addr;
 end;

 if (imgp^.module_param_addr<>nil) then
 begin
  imgp^.module_param_addr:=Pointer(imgp^.module_param_addr)+addr;
 end;

 if (elf64_get_eh_frame_info(new^.eh_frame_hdr_addr,
                             new^.eh_frame_hdr_size,
                             delta,
                             text_size + text_addr,
                             @new^.eh_frame_addr,
                             @new^.eh_frame_size)<>0) then
 begin
  new^.eh_frame_addr:=nil;
  new^.eh_frame_size:=0;
 end;

 hdr:=imgp^.image_header;

 new^.map_base    :=Pointer(addr);
 new^.map_size    :=imgp^.max_addr - imgp^.min_addr;
 new^.text_size   :=text_size;
 new^.data_addr   :=Pointer(data_addr);
 new^.data_size   :=data_size;
 new^.relocbase   :=Pointer(addr);
 new^.entry_addr  :=Pointer(delta + hdr^.e_entry);
 new^.module_param:=imgp^.module_param_addr;
 new^.relro_addr  :=imgp^.relro_addr;
 new^.relro_size  :=imgp^.relro_size;
end;

procedure scan_max_size(imgp:p_image_params;
                        phdr:p_elf64_phdr;count:Integer;
                        var max_size1,max_size2:QWORD);
var
 i:Integer;

 hdr:p_elf64_hdr;

 p_memsz:QWORD;
 p_vaddr:QWORD;

 size:QWORD;
 base:QWORD;

 p_type:Elf64_Word;

 used_mode_2m:Boolean;
begin
 max_size1:=0;
 max_size2:=0;

 hdr:=imgp^.image_header;

 if (count<>0) then
 begin
  For i:=0 to count-1 do
  begin
   p_type :=phdr^.p_type;
   p_memsz:=phdr^.p_memsz;

   if ((p_type=PT_SCE_RELRO) or (p_type=PT_LOAD)) and (p_memsz<>0) then
   begin

    p_vaddr:=phdr^.p_vaddr;

    if (hdr^.e_type=ET_SCE_DYNEXEC) then
    begin
     p_vaddr:=p_vaddr + QWORD(imgp^.reloc_base);
    end;

    p_memsz:=p_vaddr and QWORD(not PAGE_MASK);

    p_vaddr:=(phdr^.p_memsz + p_vaddr - p_memsz + PAGE_MASK) and QWORD(not PAGE_MASK);

    max_size1:=max_size1+p_vaddr;

    used_mode_2m:=is_used_mode_2mb(phdr,0,p_proc.p_budget_ptype);

    if (used_mode_2m) then
    begin
     size   :=(p_vaddr + p_memsz      ) and QWORD(not PAGE_2MB_MASK);
     p_vaddr:=(p_memsz + PAGE_2MB_MASK) and QWORD(not PAGE_2MB_MASK);
     base:=0;
     if (p_vaddr<=size) then
     begin
      base:=size-p_vaddr;
     end;
     max_size2:=max_size2+base;
    end;

   end;

   Inc(phdr);
  end;
 end;

end;

function self_load_shared_object(path:pchar;new:p_lib_info;wire:Integer):Integer;
label
 _fail_dealloc;
var
 nd:t_nameidata;
 error:Integer;
 budget_id:Integer;

 image_params:t_image_params;
 imgp:p_image_params;
 attr:t_vattr;
 vp:p_vnode;

 hdr :p_elf64_hdr;
 phdr:p_elf64_phdr;

 addr,delta:QWORD;

 max_size1:QWORD;
 max_size2:QWORD;
 used     :QWORD;
 limit    :QWORD;
begin
 Result:=-1;
 if (path=nil) then Exit;

 error:=0;
 imgp:=@image_params;
 image_params:=Default(t_image_params);

 attr:=Default(t_vattr);
 imgp^.attr:=@attr;
 imgp^.execpath:=path;

 NDINIT(@nd, LOOKUP, ISOPEN or LOCKLEAF or FOLLOW or SAVENAME or MPSAFE or AUDITVNODE1, UIO_SYSSPACE, path, curkthread);

 error:=nd_namei(@nd);
 if (error<>0) then
 begin
  if (error<>EACCES) then Exit(error);
  Writeln(StdErr,'self_load_shared_object:','namei() error (path=',path,')');
  Exit(error);
 end;

 vp:=nd.ni_vp;
 imgp^.vp:=vp;

 { Get file attributes }
 error:=VOP_GETATTR(vp, imgp^.attr);
 if (error<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  Exit(error);
 end;

 if ((p_mount(vp^.v_mount)^.mnt_flag and MNT_NOEXEC)<>0) or
    ((attr.va_mode and (S_IXUSR or S_IXGRP or S_IXOTH))=0) or
    (attr.va_type<>VREG) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  Writeln(StdErr,'self_load_shared_object:','mount flag / attribute error (path=',path,')');
  Exit(EACCES);
 end;

 if (attr.va_size<32) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  Exit(ENOEXEC);
 end;

 error:=VOP_ACCESS(vp, VEXEC);
 if (error<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  Writeln(StdErr,'self_load_shared_object:','VOP_ACCESS() error (path=',path,')');
  Exit(error);
 end;

 if (vp^.v_writecount<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  Exit(ETXTBSY);
 end;

 error:=VOP_OPEN(vp, FREAD, nil);
 if (error<>0) then
 begin
  NDFREE(@nd, NDF_ONLY_PNBUF);
  Writeln(StdErr,'self_load_shared_object:','VOP_OPEN() error (path=',path,')');
  Exit(error);
 end;

 error:=rtld_load_self(imgp);
 if (error<>0) then goto _fail_dealloc;

 hdr:=imgp^.image_header;

 if (hdr=nil) then Exit(EINVAL);

 Case hdr^.e_type of
  ET_SCE_DYNAMIC:
  else
   begin
    Writeln(StdErr,'self_load_shared_object:',imgp^.execpath,' Unsupported ELF e_type:0x',HexStr(hdr^.e_type,4));
    error:=ENOEXEC;
    goto _fail_dealloc;
   end;
 end;

 rtld_load_auth(imgp);

 budget_id:=PTYPE_BIG_APP;

 if ((PByte(@imgp^.authinfo.app_type)[7] and $f) - 4 < 4) then
 begin
  budget_id:=p_proc.p_budget_ptype;
 end else
 begin
  if ((PByte(@imgp^.authinfo.app_type)[7] and $f) = 1) then
  begin

   if is_system_path(path) then
   begin
    budget_id:=PTYPE_SYSTEM;

    if is_libc_or_fios_sprx(path) then
    begin
     budget_id:=p_proc.p_budget_ptype;
    end;

   end else
   begin
    budget_id:=p_proc.p_budget_ptype;
   end;

  end else
  begin
   budget_id:=PTYPE_SYSTEM;

   if is_system_path(path) then
   begin
    budget_id:=PTYPE_SYSTEM;

    if is_libc_or_fios_sprx(path) then
    begin
     budget_id:=p_proc.p_budget_ptype;
    end;

   end;

  end;

 end;

 imgp^.hdr_e_type:=hdr^.e_type;

 phdr:=get_elf_phdr(hdr);

 error:=scan_phdr(imgp,phdr,hdr^.e_phnum);
 if (error<>0) then
 begin
  Writeln(StdErr,'self_load_shared_object:','found illegal segment header in ',imgp^.execpath);
  goto _fail_dealloc;
 end;

 if (imgp^.dyn_exist=0) then
 begin
  Writeln(StdErr,'self_load_shared_object:','illegal ELF file image',imgp^.execpath);
  error:=ENOEXEC;
  goto _fail_dealloc;
 end;

 new^.tls_size         :=imgp^.tls_size;
 new^.tls_align        :=imgp^.tls_align;
 new^.tls_init_size    :=imgp^.tls_init_size;
 new^.tls_init_addr    :=imgp^.tls_init_addr;
 new^.eh_frame_hdr_addr:=imgp^.eh_frame_hdr_addr;
 new^.eh_frame_hdr_size:=imgp^.eh_frame_hdr_size;

 error:=scan_dyn_offset(imgp,phdr,hdr^.e_phnum);
 if (error<>0) then
 begin
  goto _fail_dealloc;
 end;

 addr:=ET_DYN_LOAD_ADDR_USR;
 if (budget_id=PTYPE_SYSTEM) then
 begin
  addr:=ET_DYN_LOAD_ADDR_SYS;
 end;

 if ((g_mode_2mb and DWORD($fffffffe))=2) then //M2MB_READONLY,M2MB_ENABLE
 begin
  max_size1:=0;
  max_size2:=0;
  scan_max_size(imgp,phdr,hdr^.e_phnum,max_size1,max_size2);

  if (max_size2 > g_mode_2mb_rsrv) then
  begin
   max_size2:=g_mode_2mb_rsrv;
  end;

  used :=vm_budget_used (PTYPE_BIG_APP,field_mlock);
  limit:=vm_budget_limit(PTYPE_BIG_APP,field_mlock);

  if ((used + (max_size1 - max_size2)) > limit) then
  begin
   error:=ENOMEM;
   goto _fail_dealloc;
  end;
 end;

 error:=rtld_mmap(@addr,imgp^.max_addr-imgp^.min_addr,imgp^.execpath);

 if (error<>0) then
 begin
  Writeln(StdErr,'self_load_shared_object:','failed to allocate VA for ',imgp^.execpath);
  goto _fail_dealloc;
 end;

 delta:=addr-imgp^.min_addr;
 imgp^.min_addr:=addr;
 imgp^.max_addr:=imgp^.max_addr+delta;

 new^.tls_init_addr    :=new^.tls_init_addr    +delta;
 new^.eh_frame_hdr_addr:=new^.eh_frame_hdr_addr+delta;

 imgp^.obj:=vm_pager_allocate(OBJT_SELF,
                              imgp^.vp,
                              imgp^.max_addr-imgp^.min_addr,
                              1,
                              0);

 vm_object_set_budget(imgp^.obj,budget_id);

 error:=dynlib_load_sections(imgp,new,phdr,hdr^.e_phnum,delta,wire);
 if (error<>0) then
 begin
  goto _fail_dealloc;
 end;

 if (budget_id=PTYPE_SYSTEM) then
 begin
  new^.rtld_flags.is_system:=1;
 end;

 error:=acquire_per_file_info_obj(imgp,new);
 if (error<>0) then
 begin
  Writeln(StdErr,'self_load_shared_object:','acquire_per_file_info_obj()=',error);
  goto _fail_dealloc;
 end;

 _fail_dealloc:
  rtld_free_self(imgp);
  NDFREE(@nd, NDF_ONLY_PNBUF);
  VOP_CLOSE(vp, FREAD);
  vput(vp);

  vm_object_deallocate(imgp^.obj); //<-vm_pager_allocate deref

 Exit(error);
end;

function change_relro_protection(obj:p_lib_info;prot:Integer):Integer;
var
 map:vm_map_t;
 addr:Pointer;
 size:QWORD;
begin
 Result:=0;
 map:=p_proc.p_vmspace;

 addr:=obj^.relro_addr;
 size:=obj^.relro_size;

 if (addr<>nil) and (size<>0) then
 begin
  Result:=vm_map_protect(map,QWORD(addr),QWORD(addr)+size,prot,False);
  if (Result<>0) then
  begin
   Writeln(StdErr,'change_relro_protection:','failed to make RELRO segment writable.');
  end;
 end;
end;

function change_relro_protection_all(prot:Integer):Integer;
var
 obj:p_lib_info;
begin
 Result:=0;
 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  Result:=change_relro_protection(obj,prot);
  if (Result<>0) then Exit;
  //
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;
end;

function relocate_text_or_data_segment(obj:p_lib_info;src,dst:Pointer;size:QWORD):Integer;
var
 offset :Integer;
 p_size :Integer;
 p_start:Pointer;
 p___end:Pointer;
 base   :Pointer;
begin
 if (obj^.rtld_flags.textrel=0) or
    (obj^.map_base > dst) or
    ((obj^.map_base + obj^.text_size) < (dst + size)) then
 begin
  Result:=copyout(src,dst,size);
 end else
 if (p_proc.p_sdk_version < $1700000) then
 begin
  p_start:=Pointer(QWORD(dst) and QWORD(not PAGE_MASK));
  p___end:=Pointer((QWORD(dst)+size+PAGE_MASK) and QWORD(not PAGE_MASK));

  offset:=QWORD(dst) and PAGE_MASK;
  p_size:=QWORD(p___end)-QWORD(p_start);

  base:=mirror_map(p_start,p_size);

  dst:=base+offset;

  Result:=copyout_nofault(src,dst,size);

  mirror_unmap(base,p_size);
 end else
 begin
  Writeln(StdErr,'relocate_text_or_data_segment:','text relocation is prohibited.');
  Exit(ENOEXEC);
 end;
end;

procedure donelist_init(var dlp:t_DoneList);
begin
 SetLength(dlp.objs,dynlibs_info.obj_count);
 dlp.num_used:=0;
end;

function donelist_check(var dlp:t_DoneList;obj:p_lib_info):Boolean;
var
 i:DWORD;
begin
 if (dlp.num_used<>0) then
 For i:=0 to dlp.num_used-1 do
 begin
  if (dlp.objs[i]=obj) then Exit(True);
 end;

 if (dlp.num_used < Length(dlp.objs)) then
 begin
  dlp.objs[dlp.num_used]:=obj;
  Inc(dlp.num_used);
 end;

 Result:=False;
end;

procedure init_dag(root:p_lib_info);
label
 _continue;
var
 needed:p_Needed_Entry;
 elm:p_Objlist_Entry;
 donelist:t_DoneList;
begin
 if (root^.rtld_flags.dag_inited<>0) then Exit;

 donelist:=Default(t_DoneList);
 donelist_init(donelist);

 // Root object belongs to own DAG.
 objlist_push_tail(root^.dldags,     root);
 objlist_push_tail(root^.dagmembers, root);
 donelist_check(donelist, root);

 {
 * Add dependencies of root object to DAG in breadth order
 * by exploiting the fact that each new object get added
 * to the tail of the dagmembers list.
 }
 elm:=TAILQ_FIRST(@root^.dagmembers);
 while (elm<>nil) do
 begin

  needed:=TAILQ_FIRST(@elm^.obj^.needed);
  while (needed<>nil) do
  begin
   if (needed^.obj=nil) then
   begin
    goto _continue;
   end;

   if donelist_check(donelist,needed^.obj) then
   begin
    goto _continue;
   end;

   objlist_push_tail(needed^.obj^.dldags, root);
   objlist_push_tail(root^.dagmembers, needed^.obj);

   _continue:
   needed:=TAILQ_NEXT(needed,@needed^.link);
  end;

  elm:=TAILQ_NEXT(elm,@elm^.link);
 end;

 root^.rtld_flags.dag_inited:=1;
end;

procedure ref_dag(root:p_lib_info);
var
 elm:p_Objlist_Entry;
begin
 Assert(root^.rtld_flags.dag_inited<>0,'DAG is not initialized');

 elm:=TAILQ_FIRST(@root^.dagmembers);
 while (elm<>nil) do
 begin
  Inc(elm^.obj^.ref_count);
  //
  elm:=TAILQ_NEXT(elm,@elm^.link);
 end;
end;

procedure unref_dag(root:p_lib_info);
var
 elm:p_Objlist_Entry;
begin
 Assert(root^.rtld_flags.dag_inited<>0,'DAG is not initialized');

 elm:=TAILQ_FIRST(@root^.dagmembers);
 while (elm<>nil) do
 begin
  Dec(elm^.obj^.ref_count);
  //
  elm:=TAILQ_NEXT(elm,@elm^.link);
 end;
end;

function dynlib_initialize_pltgot_each(obj:p_lib_info):Integer;
var
 addr:Pointer;
 entry:p_elf64_rela;

 kaddr:QWORD;

 i,count,err:Integer;
begin
 Result:=0;

 if (obj^.rtld_flags.init_plt<>0) then Exit;

 entry:=obj^.rel_data^.pltrela_addr;
 count:=obj^.rel_data^.pltrela_size div SizeOf(elf64_rela);

 if (entry<>nil) and (count<>0) then
 begin
  Result:=change_relro_protection(obj,VM_PROT_RW);
  if (Result<>0) then Exit;

  For i:=0 to count-1 do
  begin
   kaddr:=i or UNRESOLVE_MAGIC_ADDR;

   addr:=Pointer(obj^.relocbase) + entry^.r_offset;

   if (
       (addr < obj^.data_addr) or
       ((obj^.data_addr + obj^.data_size) < (addr + 8))
      ) and
      ( (obj^.relro_addr=nil) or
        (obj^.relro_addr>addr) or
        (obj^.relro_size=0) or
        ((obj^.relro_size + obj^.relro_addr) < (addr + 8))
      ) then
   begin
    Result:=ENOEXEC;
    Break;
   end;

   err:=copyout(@kaddr,addr,SizeOf(Pointer));
   if (err<>0) then
   begin
    Writeln(StdErr,'relro:0x',HexStr(obj^.relro_addr),'..0x',HexStr(obj^.relro_addr+obj^.relro_size));
    Writeln(StdErr,'dynlib_initialize_pltgot_each:','ERROR in .pltrela: where=0x',HexStr(addr));
    Result:=ENOEXEC;
    Break;
   end;

   Inc(entry);
  end;

  err:=change_relro_protection(obj,VM_PROT_READ);
 end;

 obj^.rtld_flags.init_plt:=1;
end;

function DecodeSym(obj:p_lib_info;
                   str:pchar;
                   ST_TYPE:Integer;
                   var mod_id,lib_id:WORD;
                   var nid:QWORD):Boolean;
var
 Lib_Entry:p_Lib_Entry;
begin
 Result:=False;

 mod_id:=0; //export=0
 lib_id:=0;
 nid   :=0;

 case ST_TYPE of
  STT_NOTYPE:
   begin
    nid:=ps4_nid_hash(str);

    Lib_Entry:=get_lib_entry_by_name(obj,get_mod_name_by_id(obj,0));
    if (Lib_Entry<>nil) then
    begin
     lib_id:=Lib_Entry^.dval.id;
    end;
   end;
  STT_SCE:
   begin
    Result:=DecodeValue64(str,strlen(str),nid);
    if (not Result) then Exit;

    Lib_Entry:=get_lib_entry_by_name(obj,get_mod_name_by_id(obj,0));
    if (Lib_Entry<>nil) then
    begin
     lib_id:=Lib_Entry^.dval.id;
    end;
   end;
  STT_OBJECT,
  STT_FUN,
  STT_TLS:
   begin
    Result:=DecodeEncName(str,mod_id,lib_id,nid);
   end
  else;
 end;

end;

procedure init_sym_hash(obj:p_lib_info);
var
 i,count:Integer;

 symtab_addr:p_elf64_sym;
 strtab_addr:pchar;
 strtab_size:QWORD;

 symp:p_elf64_sym;
 strp:pchar;

 ST_TYPE:Integer;

 mod_id:WORD;
 lib_id:WORD;
 nid   :QWORD;

 Lib_Entry:p_Lib_Entry;
 h_entry:p_sym_hash_entry;
 data:PPointer;
begin
 count:=obj^.rel_data^.symtab_size div SizeOf(elf64_sym);
 if (count=0) then Exit;

 symtab_addr:=obj^.rel_data^.symtab_addr;
 strtab_addr:=obj^.rel_data^.strtab_addr;
 strtab_size:=obj^.rel_data^.strtab_size;

 For i:=0 to count-1 do
 begin
  symp:=symtab_addr+i;

  if (symp^.st_name>=strtab_size) then
  begin
   Continue; //skip
  end;

  strp:=strtab_addr+symp^.st_name;

  ST_TYPE:=ELF64_ST_TYPE(symp^.st_info);

  Case ST_TYPE of
   STT_SECTION:;
   STT_NOTYPE,
   STT_OBJECT,
   STT_FUN,
   STT_SCE:
     begin
      if (symp^.st_value=0) or
         (symp^.st_shndx=SHN_UNDEF) then
      begin
       Continue; //skip
      end;
     end;
   STT_TLS:
     begin
      if (symp^.st_shndx=SHN_UNDEF) then
      begin
       Continue; //skip
      end;
     end;
   else
    Continue; //skip
  end; //case

  mod_id:=0;
  lib_id:=0;
  nid   :=0;

  if DecodeSym(obj,strp,ST_TYPE,mod_id,lib_id,nid) then
  begin
   Lib_Entry:=get_lib_entry_by_id(obj,lib_id);
   if (Lib_Entry<>nil) then
   if (Lib_Entry^.import=0) then //export
   begin
    h_entry:=AllocMem(SizeOf(t_sym_hash_entry));
    //
    h_entry^.nid   :=nid;
    h_entry^.mod_id:=mod_id;
    h_entry^.lib_id:=lib_id;
    h_entry^.sym   :=symp^;
    //
    if (Lib_Entry^.hamt=nil) then
    begin
     Lib_Entry^.hamt:=HAMT_create64;
     TAILQ_INIT(@Lib_Entry^.syms);
    end;
    //
    data:=HAMT_insert64(Lib_Entry^.hamt,nid,h_entry);
    Assert(data<>nil,'NOMEM');
    //
    if (data^<>h_entry) then
    begin
      //is another exists
     FreeMem(h_entry);
    end else
    begin
     //new
     TAILQ_INSERT_TAIL(@Lib_Entry^.syms,h_entry,@h_entry^.link);
     Inc(Lib_Entry^.count);
    end;
    //
   end;
  end;

 end; //for


end;

procedure alloc_tls(new:p_lib_info);
label
 _inc_max;
var
 obj:p_lib_info;
 i:Integer;
 tls_max:Integer;
begin
 if (new^.tls_size=0) then
 begin
  i:=0;
 end else
 begin
  dynlibs_info.tls_count:=dynlibs_info.tls_count + 1;
  tls_max:=dynlibs_info.tls_max;

  if (tls_max<1) then
  begin
   _inc_max:
   i:=tls_max+1;
   dynlibs_info.tls_max:=i;
  end else
  begin
   i:=1;
   obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
   while (obj<>nil) do
   begin
    while (obj^.tls_index=i) do
    begin
     i:=i+1;
     obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
     if (tls_max < i) then
     begin
      goto _inc_max;
     end;
    end;
    obj:=TAILQ_NEXT(obj,@obj^.link);
   end;
  end;
 end;

 new^.tls_index:=i;
end;

function do_load_object(path:pchar;flags:DWORD;var err:Integer):p_lib_info;
label
 _error;
var
 fname:pchar;
 new:p_lib_info;
begin
 Result:=nil;

 Writeln(' do_load_object:',dynlib_basename(path));

 new:=obj_new();

 err:=self_load_shared_object(path,new,ord((flags and $20)<>0));
 if (err<>0) then
 begin
  goto _error;
 end;

 fname:=dynlib_basename(path);
 object_add_name(new,fname);

 obj_set_lib_path(new,path);

 alloc_tls(new);

 err:=digest_dynamic(new);
 if (err<>0) then
 begin
  Writeln(StdErr,'do_load_object:','digest_dynamic() failed rv=',err);
  goto _error;
 end;

 err:=dynlib_initialize_pltgot_each(new);
 if (err<>0) then
 begin
  Writeln(StdErr,'do_load_object:','dynlib_initialize_pltgot_each() failed rv=',err);
  goto _error;
 end;

 if (new^.rtld_flags.textrel<>0) then
 begin
  Writeln(StdErr,'do_load_object:',new^.lib_path,' has impure text');
  err:=EINVAL;
  goto _error;
 end;

 init_relo_bits(new);
 init_sym_hash(new);
 dynlibs_add_obj(new);

 new^.loaded:=1;
 Exit(new);

 _error:

 rtld_munmap(new^.map_base,new^.map_size,dynlib_basename(new^.lib_path));

 obj_free(new);

 Exit(nil);
end;

procedure unlink_object(root:p_lib_info);
var
 elm,next:p_Objlist_Entry;
begin
 if (root^.ref_count<>0) then Exit;

 //Remove the object from the RTLD_GLOBAL list.
 objlist_remove(dynlibs_info.list_global, root);

 //Remove the object from all objects' DAG lists.
 elm:=TAILQ_FIRST(@root^.dagmembers);
 while (elm<>nil) do
 begin
  next:=TAILQ_NEXT(elm,@elm^.link);
  //
  objlist_remove(elm^.obj^.dldags, root);
  if (elm^.obj<>root) then
  begin
   unlink_object(elm^.obj);
  end;
  //
  elm:=next;
 end;
end;

procedure unload_object(root:p_lib_info);
var
 obj,next:p_lib_info;
begin
 Assert(root^.ref_count=0,'unload_object ref_count');

 Writeln(' unload_object:',dynlib_basename(root^.lib_path));

 {
  * Pass over the DAG removing unreferenced objects from
  * appropriate lists.
 }
 unlink_object(root);

 // Unmap all objects that are no longer referenced.

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  next:=TAILQ_NEXT(obj,@obj^.link);
  //
  if (obj^.ref_count=0) then
  begin
   rtld_munmap(obj^.map_base, obj^.map_size,dynlib_basename(obj^.lib_path));

   dynlibs_del_obj(obj);

   //dynlib_notify_event(td,lib->id,0x80);

   obj_free(obj);
  end;
  //
  obj:=next;
 end;

end;

function map_prx_internal(name:pchar;obj:p_lib_info):Integer;
var
 Lib_Entry:p_Lib_Entry;
 h_entry  :p_sym_hash_entry;
 hle_import_table:p_hle_import_entry;
 hle_import_base :PPointer;
 hle_local_data  :Pointer;
 map:vm_map_t;
 vaddr_lo    :QWORD;
 vaddr_hi    :QWORD;
 data        :Pointer;
 export_count:Integer;
 import_count:Integer;
 local_size  :QWORD;
 s           :QWORD;
 i,error:Integer;
begin
 Result:=0;

 export_count:=0;
 import_count:=0;
 local_size  :=0;

 //get sym count
 Lib_Entry:=TAILQ_FIRST(@obj^.lib_table);
 while (Lib_Entry<>nil) do
 begin
  //
  h_entry:=TAILQ_FIRST(@Lib_Entry^.syms);
  while (h_entry<>nil) do
  begin
   //
   if (ELF64_ST_BIND(h_entry^.sym.st_info)=STB_LOCAL) then
   begin
    //local
    Case ELF64_ST_TYPE(h_entry^.sym.st_info) of
     STT_NOTYPE,
     STT_FUN,
     STT_SCE:
        begin
         export_count:=export_count+1;
        end;
     STT_OBJECT:
        begin
         local_size:=local_size+h_entry^.sym.st_size;
        end;
     else;
    end; //case
    //local
   end else
   if (ELF64_ST_BIND(h_entry^.sym.st_info)=STB_GLOBAL) then
   begin
    //global
    Case ELF64_ST_TYPE(h_entry^.sym.st_info) of
     STT_NOTYPE,
     STT_FUN,
     STT_SCE:
        begin
         if (Lib_Entry^.import=0) then //export
         begin
          export_count:=export_count+1;
         end else
         begin
          import_count:=import_count+1;
         end;
        end;
     else;
    end; //case
    //global
   end;
   //
   h_entry:=TAILQ_NEXT(h_entry,@h_entry^.link)
  end;
  //
  Lib_Entry:=TAILQ_NEXT(Lib_Entry,@Lib_Entry^.link)
 end;

 //jit export space = (export_count*8) + (import_count*2*8)
 obj^.text_size:=AlignUp((export_count*8) + (import_count*2*8),PAGE_SIZE);
 //jit import space = sceModuleParam + local_size + import_count*8
 obj^.data_size:=AlignUp(Sizeof(TsceModuleParam)+local_size+import_count*8,PAGE_SIZE);
 //sum
 obj^.map_size :=obj^.text_size+obj^.data_size;

 //alloc addr
 vaddr_lo:=ET_DYN_LOAD_ADDR_SYS;
 error:=rtld_mmap(@vaddr_lo,obj^.map_size,dynlib_basename(obj^.lib_path));

 if (error<>0) then
 begin
  Writeln(StdErr,'[KERNEL] preload_prx_internal:','failed to allocate VA for ',obj^.lib_path,' (',error,')');
  Exit(error);
 end;

 vaddr_hi:=vaddr_lo+obj^.map_size;
 data:=Pointer(vaddr_lo+obj^.text_size);

 map:=p_proc.p_vmspace;

 //map RW
 vm_map_lock(map);

  vm_map_delete(map,vaddr_lo,vaddr_hi,True);

  error:=vm_map_insert(map,nil,0,
                       vaddr_lo,vaddr_hi,
                       VM_PROT_RW,VM_PROT_RWX,
                       0,
                       nil,false);
  if (error<>0) then
  begin
   vm_map_unlock(map);
   //
   Writeln(StdErr,'[',HexStr(vaddr_lo,8),'..',HexStr(vaddr_hi,8),']');
   Writeln(StdErr,'[KERNEL] preload_prx_internal: vm_map_insert failed ',HexStr(vaddr_lo,8),' (',error,')');
   error:=vm_mmap_to_errno(error);
   Exit(error);
  end;

  vm_map_set_name_locked(map,vaddr_lo,vaddr_hi,name);

 vm_map_unlock(map);

 vm_map_wire(map,vaddr_lo,vaddr_hi,VM_MAP_WIRE_USER or 8);

 //copy module_param
 pSceModuleParam(data)^:=obj^.module_param^;
 obj^.module_param:=data;

 //per lib global data
 hle_local_data :=data+sizeOf(TsceModuleParam); //HLE

 //per lib import table
 hle_import_base:=hle_local_data+local_size; //HLE

 obj^.hle_import_count:=import_count;

 //alloc hle import table / per lib global data
 if (import_count<>0) or (local_size<>0) then
 begin

  if (import_count<>0) then
  begin
   hle_import_table:=AllocMem(import_count*SizeOf(t_hle_import_entry));
   obj^.hle_import_table:=hle_import_table;
  end;

  i:=0;
  s:=0;
  Lib_Entry:=TAILQ_FIRST(@obj^.lib_table);
  while (Lib_Entry<>nil) do
  begin
   //
   h_entry:=TAILQ_FIRST(@Lib_Entry^.syms);
   while (h_entry<>nil) do
   begin
    //
    if (ELF64_ST_BIND(h_entry^.sym.st_info)=STB_LOCAL) then
    begin
     //local
     Case ELF64_ST_TYPE(h_entry^.sym.st_info) of
      STT_OBJECT:
         begin
          PPointer(h_entry^.native)^:=(hle_local_data+s);
          s:=s+h_entry^.sym.st_size;
         end;
      else;
     end; //case
     //local
    end else
    if (ELF64_ST_BIND(h_entry^.sym.st_info)=STB_GLOBAL) then
    begin
     //global
     Case ELF64_ST_TYPE(h_entry^.sym.st_info) of
      STT_NOTYPE,
      STT_FUN,
      STT_SCE:
         begin
          if (Lib_Entry^.import=0) then //export
          begin
           //
          end else
          begin
           //
           hle_import_table[i].h_entry          :=h_entry;
           hle_import_table[i].jit_guest_addr   :=nil; //jit build
           hle_import_table[i].jit_host_addr    :=nil; //jit dynamic cache
           hle_import_table[i].import_place_addr:=@hle_import_base[i];
           Inc(i);
           //
          end;
         end;
      else;
     end; //case
     //global
    end;
    //
    h_entry:=TAILQ_NEXT(h_entry,@h_entry^.link)
   end;
   //
   Lib_Entry:=TAILQ_NEXT(Lib_Entry,@Lib_Entry^.link)
  end;
 end; //(import_count<>0)

 //protect text,data
 vm_map_lock(map);

  //text
  error:=vm_map_protect(map,vaddr_lo,QWORD(data),VM_PROT_RX,False);
  if (error<>0) then
  begin
   vm_map_unlock(map);
   //
   Writeln(StdErr,'[KERNEL] preload_prx_internal: vm_map_protect failed ',HexStr(vaddr_lo,8),' (',error,')');
   error:=vm_mmap_to_errno(error);
   Exit(error);
  end;

  //data
  error:=vm_map_protect(map,QWORD(data),vaddr_hi,VM_PROT_RW,False);
  if (error<>0) then
  begin
   vm_map_unlock(map);
   //
   Writeln(StdErr,'[KERNEL] preload_prx_internal: vm_map_protect failed ',HexStr(QWORD(data),8),' (',error,')');
   error:=vm_mmap_to_errno(error);
   Exit(error);
  end;

 vm_map_unlock(map);

 //
 obj^.map_base     :=Pointer(vaddr_lo);
 obj^.data_addr    :=data;
 obj^.tls_init_addr:=data;
end;

const
 internal_module_param:TsceModuleParam=(
  Size       :SizeOf(TsceModuleParam);
  Magic      :$23C13F4BF;
  SDK_version:$000000001;///$010010001;
 );

function preload_prx_internal(name,orig_path:pchar;flag:ptruint):p_lib_info;
label
 _error;
var
 entry:p_int_file;
 err:Integer;
begin
 Result:=nil;

 entry:=SLIST_FIRST(@dynlibs_intf);
 while (entry<>nil) do
 begin
  if ((entry^.flag and flag)<>0) then
  if (entry^.icbs<>nil) then
  if (StrComp(name,entry^.name)=0) then
  begin
   Result:=entry^.icbs(entry^.name);
   if (Result<>nil) then
   begin

    //fix module_param
    if (Result^.module_param=nil) then
    begin
     Result^.module_param:=@internal_module_param;
    end;

    //set path
    obj_set_lib_path(Result,orig_path);

    object_add_name(Result,dynlib_basename(Result^.lib_path));

    alloc_tls(Result);

    //load fake code mem
    err:=map_prx_internal(name,Result);
    if (err<>0) then
    begin
     _error:
      obj_free(Result);
      Exit(nil);
    end;

    //

    err:=digest_dynamic(Result);
    if (err<>0) then
    begin
     Writeln(StdErr,'preload_prx_internal:','digest_dynamic() failed rv=',err);
     goto _error;
    end;

    init_relo_bits(Result);
    //reg lib
    dynlibs_add_obj(Result);
    //
    Result^.rtld_flags.init_plt :=1;
    Result^.rtld_flags.is_system:=1;
    Result^.rtld_flags.internal :=1;
    Result^.rtld_flags.tls_done :=1;
    Result^.loaded:=1;

    Writeln(' preload_prx_internal:',name);

    //
    Exit;
   end;
  end;
  entry:=SLIST_NEXT(entry,@entry^.link);
 end;

end;

procedure pick(var ctx:t_jit_context2;preload:Pointer); external name 'kern_jit_pick';

procedure pick_obj_internal(obj:p_lib_info);
var
 ctx:t_jit_context2;

 Lib_Entry:p_Lib_Entry;
 h_entry:p_sym_hash_entry;
 i,hle_count:Integer;

 hle_import_table:p_hle_import_entry;

 node:p_jit_entry_point;
begin
 Writeln('pick_obj_internal:',obj^.lib_path);

 ctx:=Default(t_jit_context2);
 ctx.obj:=obj;
 ctx.text_start:=QWORD(obj^.map_base);
 ctx.text___end:=ctx.text_start+obj^.text_size;
 ctx.map____end:=ctx.text_start+obj^.map_size;
 ctx.modes:=[cmInternal];

 //load exports
 lib_entry:=TAILQ_FIRST(@obj^.lib_table);
 while (lib_entry<>nil) do
 begin
  if (Lib_Entry^.import=0) then //export
  begin
   h_entry:=TAILQ_FIRST(@Lib_Entry^.syms);
   while (h_entry<>nil) do
   begin

    if (ELF64_ST_BIND(h_entry^.sym.st_info)=STB_LOCAL) then
    begin
     //local
     Case ELF64_ST_TYPE(h_entry^.sym.st_info) of
      STT_NOTYPE,
      STT_FUN,
      STT_SCE:
         begin
          ctx.add_export_point(h_entry^.nid,Pointer(h_entry^.sym.st_value),h_entry^.native);
         end;
      else;
     end; //case
     //local
    end else
    if (ELF64_ST_BIND(h_entry^.sym.st_info)=STB_GLOBAL) then
    begin
     //global
     Case ELF64_ST_TYPE(h_entry^.sym.st_info) of
      STT_NOTYPE,
      STT_FUN,
      STT_SCE:
         begin
          ctx.add_export_point(h_entry^.nid,h_entry^.native,@h_entry^.sym.st_value);
         end;
      else;
     end; //case
     //global
    end;

    h_entry:=TAILQ_NEXT(h_entry,@h_entry^.link)
   end;
  end;
  lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
 end;
 //load exports

 //load imports
 hle_count:=obj^.hle_import_count;
 hle_import_table:=obj^.hle_import_table;

 if (hle_count<>0) and (hle_import_table<>nil) then
 begin
  For i:=0 to hle_count-1 do
  begin
   h_entry:=hle_import_table[i].h_entry;

   if (ELF64_ST_BIND(h_entry^.sym.st_info)=STB_GLOBAL) then
    Case ELF64_ST_TYPE(h_entry^.sym.st_info) of
     STT_NOTYPE,
     STT_FUN,
     STT_SCE:
        begin
         ctx.add_import_point(hle_import_table[i].import_place_addr,@hle_import_table[i].jit_guest_addr);
        end;
     else;
    end; //case

  end;
 end;
 //load imports

 pick(ctx,nil);

 //load imports dynamic cache
 if (hle_count<>0) and (hle_import_table<>nil) then
 begin
  For i:=0 to hle_count-1 do
  begin
   h_entry:=hle_import_table[i].h_entry;

   if (ELF64_ST_BIND(h_entry^.sym.st_info)=STB_GLOBAL) then
    Case ELF64_ST_TYPE(h_entry^.sym.st_info) of
     STT_NOTYPE,
     STT_FUN,
     STT_SCE:
        begin
         //
         Assert(hle_import_table[i].jit_guest_addr<>nil,'jit_guest_addr');

         node:=fetch_entry(hle_import_table[i].jit_guest_addr);
         Assert(node<>nil,'fetch_entry');
         //
         hle_import_table[i].jit_host_addr:=node^.dst;
         //
         node^.dec_ref('fetch_entry');
         //
        end;
     else;
    end; //case

  end;
 end;
 //load imports dynamic cache

end;

procedure pick_obj(obj:p_lib_info);
var
 ctx:t_jit_context2;

 Lib_Entry:p_Lib_Entry;
 h_entry:p_sym_hash_entry;
 symp:p_elf64_sym;
 addr:Pointer;
 ST_TYPE:Integer;

 i,count:Integer;
 pltrela_addr:p_elf64_rela;
begin
 if (obj=nil) then Exit;

 if (obj^.rtld_flags.internal<>0) then
 begin
  pick_obj_internal(obj);
  Exit;
 end;

 relocate_object_export_only(obj);

 Writeln('pick_obj:',obj^.lib_path);

 ctx:=Default(t_jit_context2);
 ctx.obj:=obj;
 ctx.text_start:=QWORD(obj^.map_base);
 ctx.text___end:=ctx.text_start+obj^.text_size;
 ctx.map____end:=ctx.text_start+obj^.map_size;

 ctx.add_forward_point(fpCall,obj^.entry_addr);

 if (obj^.rtld_flags.mainprog=0) then
 begin
  ctx.add_forward_point(fpCall,obj^.init_proc_addr);
  ctx.add_forward_point(fpCall,obj^.fini_proc_addr);
 end;

 //load export links
 lib_entry:=TAILQ_FIRST(@obj^.lib_table);
 while (lib_entry<>nil) do
 begin
  if (Lib_Entry^.import=0) then //export
  begin
   h_entry:=TAILQ_FIRST(@Lib_Entry^.syms);
   while (h_entry<>nil) do
   begin

    symp:=@h_entry^.sym;

    ST_TYPE:=ELF64_ST_TYPE(symp^.st_info);

    Case ST_TYPE of
     STT_NOTYPE,
     STT_FUN,
     STT_SCE:
        if (symp^.st_value<>0) and
           (symp^.st_shndx<>SHN_UNDEF) and
           (symp^.st_value<obj^.text_size) then
        begin
         addr:=obj^.relocbase + symp^.st_value;

         ctx.add_forward_point(fpCall,addr);
        end;
     else;
    end; //case

    h_entry:=TAILQ_NEXT(h_entry,@h_entry^.link)
   end;
  end;
  lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
 end;

 //load jumpslot links
 count:=obj^.rel_data^.pltrela_size div SizeOf(elf64_rela);
 pltrela_addr:=obj^.rel_data^.pltrela_addr;

 if (pltrela_addr<>nil) and (count<>0) then
 For i:=0 to count-1 do
  begin
   addr:=(obj^.relocbase + pltrela_addr[i].r_offset);

   ctx.add_jumpslot(addr);
  end;

 pick(ctx,nil);
end;

function find_by_ext(const path:RawByteString):RawByteString;
var
 tmp:RawByteString;
begin
 Result:='';
 tmp:=path;

 if rtld_file_exists(pchar(tmp)) then
 begin
  Exit(tmp);
 end;

 tmp:=ChangeFileExt(tmp,'.sprx');
 if rtld_file_exists(pchar(tmp)) then
 begin
  Exit(tmp);
 end;

 tmp:=ChangeFileExt(tmp,'.prx');
 if rtld_file_exists(pchar(tmp)) then
 begin
  Exit(tmp);
 end;

end;

function inc_unix_sep(const name:RawByteString):RawByteString; inline;
begin
 if (name[1]='/') then
 begin
  Result:=name;
 end else
 begin
  Result:='/'+name;
 end;
end;

function preload_prx_modules(path:pchar;flags:DWORD;var err:Integer):p_lib_info;
label
 _do_load,
 _do_pick;
var
 obj:p_lib_info;
 basename:pchar;
 fname:RawByteString;
begin
 Result:=nil;
 err:=0;

 basename:=dynlib_basename(path);

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  if object_match_name(obj,basename) then
  begin
   Exit(obj);
  end;
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;

 //try internal preload
 fname:=ChangeFileExt(basename,'.prx');

 Result:=preload_prx_internal(pchar(fname),path,IF_PRELOAD);
 if (Result<>nil) then goto _do_pick;

 //try original

 if (path[0]='/') then
 begin
  //path is absolute or system relative?

  //try /system/common/lib/*
  fname:=find_by_ext('/'+p_proc.p_randomized_path+'/common/lib'+inc_unix_sep(path));
  if (fname<>'') then goto _do_load;

  //try /system/priv/lib/*
  fname:=find_by_ext('/'+p_proc.p_randomized_path+'/priv/lib'+inc_unix_sep(path));
  if (fname<>'') then goto _do_load;

  //try path
  fname:=find_by_ext(path);
  if (fname<>'') then goto _do_load;

  //try /system/*
  fname:=find_by_ext('/'+p_proc.p_randomized_path+inc_unix_sep(basename));
  if (fname<>'') then goto _do_load;
 end else
 begin
  //path is relative?

  if (p_proc.p_sdk_version > $3ffffff) then
  begin
   if (Pos('web_core.elf',p_proc.p_prog_name)<>0) then
   begin
    //web_core only
    fname:=ChangeFileExt(basename,'');
    case fname of
     'libScePigletv2VSH':;
     'libSceSysCore':;
     'libSceVideoCoreServerInterface':;
     else
      begin
       err:=ENOENT;
       Exit(nil);
      end;
    end;
   end else
   begin
    err:=ENOENT;
    Exit(nil);
   end;
  end;

  if (Pos('/',path)=0) then //path is full relative?
  begin

   //try /libprogram.lib_dirname/*  (/app0/*)
   fname:=find_by_ext(dynlibs_info.libprogram^.lib_dirname+inc_unix_sep(basename));
   if (fname<>'') then goto _do_load;

   if ((flags and $40)<>0) then //priv libs?
   begin
    //try /system/priv/lib/*
    fname:=find_by_ext('/'+p_proc.p_randomized_path+'/priv/lib'+inc_unix_sep(basename));
    if (fname<>'') then goto _do_load;

    //try /system/common/lib/*
    fname:=find_by_ext('/'+p_proc.p_randomized_path+'/common/lib'+inc_unix_sep(basename));
    if (fname<>'') then goto _do_load;
   end;

  end else
  begin
   //try path
   fname:=find_by_ext(path);
   if (fname<>'') then goto _do_load;
  end;

 end;

 //try internal postload
 fname:=ChangeFileExt(basename,'.prx');

 Result:=preload_prx_internal(pchar(fname),path,IF_POSTLOAD);
 if (Result<>nil) then goto _do_pick;

 Writeln(StdErr,' prx_module_not_found:',dynlib_basename(path));
 print_backtrace_td(stderr);

 err:=ENOENT;
 Exit(nil);

 _do_load:

 Result:=do_load_object(pchar(fname),flags,err);

 _do_pick:

 pick_obj(Result);
end;

function relocate_object(root:p_lib_info):Integer;
var
 obj:p_lib_info;
begin
 Result:=change_relro_protection_all(VM_PROT_RW);
 if (Result<>0) then Exit;

 Result:=relocate_one_object(root,(root^.rtld_flags.jmpslots_done=0),false);

 if (Result=0) then
 begin
  obj:=dynlibs_info.libprogram;

  while (obj<>nil) do
  begin
   if (obj<>root) then
   begin
    Result:=relocate_one_object(obj,(root^.rtld_flags.jmpslots_done=0),false);
   end;
   obj:=TAILQ_NEXT(obj,@obj^.link);
  end;
 end;

 change_relro_protection_all(VM_PROT_READ);
end;

function relocate_object_export_only(obj:p_lib_info):Integer;
begin
 Result:=change_relro_protection(obj,VM_PROT_RW);
 if (Result<>0) then Exit;

 Result:=relocate_one_object(obj,(obj^.rtld_flags.jmpslots_done=0),true);

 change_relro_protection(obj,VM_PROT_READ);
end;

function dynlib_load_relocate():Integer;
var
 elm:p_Objlist_Entry;
begin
 elm:=TAILQ_FIRST(@dynlibs_info.list_main);
 while (elm<>nil) do
 begin
  allocate_tls_offset(elm^.obj);
  //
  elm:=TAILQ_NEXT(elm,@elm^.link);
 end;
 //
 Result:=relocate_object(dynlibs_info.libprogram);
end;

function load_prx(path:pchar;flags:DWORD;var pobj:p_lib_info):Integer;
var
 obj:p_lib_info;
 err,pflags:Integer;
begin
 Result:=0;
 err:=0;

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  if (StrLComp(obj^.lib_path,path,$400)=0) then
  begin
   pobj:=obj;
   Exit(0);
  end;
  //
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;

 pflags:=2;
 if ((flags and $00001)<>0) then pflags:=pflags or $20; //vm_map_wire
 if ((flags and $10000)<>0) then pflags:=pflags or $40; //priv libs?

 obj:=preload_prx_modules(path,pflags,err);
 if (obj=nil) then Exit(err);

 if (objlist_find(dynlibs_info.list_global,obj)=nil) then
 begin
  objlist_push_tail(dynlibs_info.list_global,obj);
 end;

 if (obj^.ref_count=0) then
 begin
  if ((flags and $20000)<>0) then //set jmpslots_done?
  begin
   obj^.rtld_flags.jmpslots_done:=1;
  end;

  if ((flags and $40000)<>0) then //set not_get_proc?
  begin
   obj^.rtld_flags.not_get_proc:=1;
  end;

  init_dag(obj);
  ref_dag (obj);

  err:=relocate_object(obj);
  if (err<>0) then
  begin
   unref_dag(obj);
   if (obj^.ref_count=0) then
   begin
    unload_object(obj);
   end;
   Writeln(StdErr,'load_prx:','Fail to relocate ',path);
   Exit(err);
  end;
 end else
 begin
  ref_dag(obj);
 end;

 pobj:=obj;
 Result:=0;
end;

function dynlib_unlink_imported_symbols(root:p_lib_info;modname:pchar):Integer;
var
 obj:p_lib_info;
 lib_entry:p_Lib_Entry;
 offset:QWORD;
 str:pchar;
begin
 Result:=change_relro_protection_all(VM_PROT_RW);
 if (Result<>0) then Exit;

 if (Result=0) then
 begin
  obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
  while (obj<>nil) do
  begin
   if (obj<>root) then
   begin
    lib_entry:=TAILQ_FIRST(@obj^.mod_table);
    while (lib_entry<>nil) do
    begin
     if (lib_entry^.dval.id<>0) then //import
     begin
      offset:=lib_entry^.dval.name_offset;
      str:=obj_get_str(obj,offset);
      //
      if (modname<>nil) then
      if (StrComp(str,modname)=0) then //used module
      begin
       Result:=dynlib_unlink_imported_symbols_each(root,obj);
       //ended
       Break;
      end;
     end;
     //
     lib_entry:=TAILQ_NEXT(lib_entry,@lib_entry^.link)
    end;
   end;
   //
   obj:=TAILQ_NEXT(obj,@obj^.link);
  end;
 end;

 change_relro_protection_all(VM_PROT_READ);
end;

function unload_prx(obj:p_lib_info):Integer;
var
 ref_count:Integer;
 modname:pchar;
begin
 ref_count:=obj^.ref_count;

 if (ref_count=0) then
 begin
  Assert(False,'Invalid shared object handle');
  Exit(EINVAL);
 end;

 unref_dag(obj);

 if (ref_count<>1) then
 begin
  Exit(0);
 end;

 modname:=get_mod_name_by_id(obj,0); //export=0

 if (modname<>nil) then
 begin
  Result:=dynlib_unlink_imported_symbols(obj,modname);
 end;

 unload_object(obj);

 Result:=0;
end;

function alloc_obj_id(obj:p_lib_info):Boolean;
var
 key:Integer;
begin
 Result:=False;

 if (obj=dynlibs_info.libprogram) then
 begin
  //eboot.bin should always be zero
  obj^.id:=0;
  Exit(True);
 end;

 if (obj^.id>0) then Exit(True);

 obj^.objt:=NAMED_DYNL;
 obj^.name:=dynlib_basename(obj^.lib_path);

 key:=-1;
 if id_name_new(@named_table,obj,@key) then
 begin
  obj^.id:=(key+1);
  id_release(obj); //<-id_name_new
  Result:=True;
 end;
end;

function free_obj_id(id:Integer):Boolean;
var
 key:Integer;
begin
 if (id<=0) then Exit(True);

 key:=id-1;
 Result:=id_name_del(@named_table,key,NAMED_DYNL,nil);
end;

function find_obj_id(id:Integer):p_lib_info;
var
 key:Integer;
begin
 if (id=0) then
 begin
  Exit(dynlibs_info.libprogram);
 end;

 key:=id-1;

 Result:=id_name_get(@named_table,key,NAMED_DYNL);
 if (Result=nil) then Exit;

 id_release(Result); //<-id_name_get
end;

function find_obj_by_handle(id:Integer):p_lib_info;
var
 obj:p_lib_info;
begin
 Result:=nil;

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  if (obj^.id=id) then
  begin
   Exit(obj);
  end;
  //
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;
end;

function find_obj_by_name(name:pchar):p_lib_info;
var
 obj:p_lib_info;
 str:pchar;
begin
 Result:=nil;

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  str:=get_mod_name_by_id(obj,0);
  if (StrComp(str,name)=0) then
  begin
   Exit(obj);
  end;
  //
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;
end;

function find_obj_by_addr_safe(addr:Pointer):p_lib_info;
var
 obj:p_lib_info;
begin
 Result:=nil;

 obj:=fuptr(dynlibs_info.obj_list.tqh_first);
 while (obj<>nil) do
 begin
  if (addr>=obj^.map_base) and
     (addr<(obj^.map_base+obj^.map_size)) then
  begin
   Exit(obj);
  end;

  //
  obj:=fuptr(obj^.link.tqe_next);
 end;
end;

function dynlib_load_needed_shared_objects():Integer;
var
 obj:p_lib_info;
 needed:PPointer;
 i:Integer;
begin
 obj:=dynlibs_info.libprogram;

 if (obj<>nil) then
 begin
  i:=0;
  repeat
   needed:=@obj^.needed.tqh_first;
   obj:=TAILQ_NEXT(obj,@obj^.link);
   i:=(i+1)-ord(needed^=nil);
  until (obj=nil);

  if (i<0) then
  begin
   Writeln(StdErr,'dynlib_load_needed_shared_objects:','load_needed_objects() fails');
   Exit(EINVAL);
  end;
 end;

 obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
 while (obj<>nil) do
 begin
  init_dag(obj);
  ref_dag (obj);
  //
  obj:=TAILQ_NEXT(obj,@obj^.link);
 end;

 Result:=0;
end;

//

function copy_proc_param(pout:pSceProcParam):Integer;
var
 proc_param_addr:pSceProcParam;
 proc_param_size:QWORD;
begin
 proc_param_addr:=dynlibs_info.proc_param_addr;
 proc_param_size:=dynlibs_info.proc_param_size;

 if (proc_param_addr=nil) then Exit(ENOENT);

 pout^:=Default(TSceProcParam);

 if (proc_param_size>SizeOf(TSceProcParam)) then
 begin
  proc_param_size:=SizeOf(TSceProcParam);
 end;

 Result:=copyin(proc_param_addr,pout,proc_param_size);

 if (Result=0) then
 begin
  if (pout^.Magic<>$4942524f) then Result:=ENOEXEC;
 end;
end;

function copy_libc_param(pout:pSceLibcParam):Integer;
var
 proc_param:TSceProcParam;
 libc_param_addr:pSceLibcParam;
 libc_param_size:QWORD;
begin
 Result:=copy_proc_param(@proc_param);
 if (Result<>0) then Exit;

 if (proc_param.Entry_count=0) or
    (proc_param.Size <= 63) or
    (proc_param._sceLibcParam=nil) then
 begin
  Exit(ENOEXEC);
 end;

 libc_param_addr:=proc_param._sceLibcParam;

 Result:=copyin(libc_param_addr,@libc_param_size,8);
 if (Result<>0) then Exit;

 if (libc_param_size >= 169) then Exit(EINVAL);

 pout^:=Default(TSceLibcParam);

 Result:=copyin(proc_param._sceLibcParam,pout,libc_param_size);
end;



end.

