FPPS4/sys/kern/subr_dynlib.pas

2538 lines
52 KiB
Plaintext

unit subr_dynlib;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
sysutils,
mqueue,
elf64,
kern_thr,
kern_rtld,
kern_named_id,
kern_sx;
type
p_rel_data=^t_rel_data;
t_rel_data=record
//entry:TAILQ_ENTRY;
//vm_obj:vm_object_t;
//refs:Integer;
//full_size:QWORD;
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;
//void * sce_dynlib_data;
//void * elf_hdr;
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;
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;
//t_rtld_bits rtld_flags;
mainprog :Byte;
tls_done :Byte;
init_scanned :Byte;
init_done :Byte;
on_fini_list :Byte;
not_get_proc :Byte;
textrel :Byte;
init_plt :Byte;
is_system :Byte;
dag_inited :Byte;
jmpslots_done:Byte;
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;
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_Lib_Entry=^Lib_Entry;
Lib_Entry=record
link :TAILQ_ENTRY;
dval :TLibraryValue;
attr :WORD;
import:WORD;
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;
procedure dynlibs_lock;
procedure dynlibs_unlock;
function obj_new():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(obj:p_lib_info;id:Word):pchar;
function get_lib_name(obj:p_lib_info;id:Word):pchar;
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 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 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 dynlib_load_relocate():Integer;
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 dynlib_load_needed_shared_objects():Integer;
var
dynlibs_info:t_dynlibs_info;
implementation
uses
errno,
systm,
vm,
vmparam,
vm_map,
vm_mmap,
vm_object,
vuio,
vstat,
vfcntl,
vnode,
vmount,
vnamei,
vfs_lookup,
vfs_subr,
vnode_if,
kern_reloc,
kern_namedobj;
procedure dynlibs_lock;
begin
sx_xlock(@dynlibs_info.lock);
end;
procedure dynlibs_unlock;
begin
sx_xunlock(@dynlibs_info.lock);
end;
function obj_new():p_lib_info;
begin
Result:=AllocMem(SizeOf(t_lib_info));
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 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
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);
end;
function allocate_tls_offset(obj:p_lib_info):Boolean;
var
off:Int64;
begin
if (obj^.tls_done<>0) then
begin
Exit(True);
end;
off:=obj^.tls_size;
if (off=0) then
begin
obj^.tls_done:=1;
Exit(True);
end;
if (obj^.tls_index=1) then
begin
off:=off + -1;
end else
begin
off:=off + -1 + dynlibs_info.tls_last_offset;
end;
off:=(off + obj^.tls_align) 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;
dynlibs_info.tls_last_offset:=off;
dynlibs_info.tls_last_size :=obj^.tls_size;
Result:=True;
end;
procedure free_tls_offset(obj:p_lib_info);
begin
if (obj^.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;
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);
FreeMem(obj);
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 Lib_Entry_free(lib:p_Lib_Entry);
begin
//
FreeMem(lib);
end;
function get_mod_name(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(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;
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^.init_scanned<>0) or (obj^.init_done<>0) then Exit;
obj^.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^.on_fini_list=0) then
begin
objlist_push_tail(fini_proc_list,obj);
obj^.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^.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^.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 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));
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;
begin
if (obj^.relo_bits=nil) then Exit(False);
Result:=((obj^.relo_bits[i shr 3] shr (i and 7)) and 1)<>0;
end;
procedure set_relo_bits(obj:p_lib_info;i:Integer);
begin
if (obj^.relo_bits=nil) then Exit;
obj^.relo_bits[i shr 3]:=obj^.relo_bits[i shr 3] or (1 shl (i and 7))
end;
procedure reset_relo_bits(obj:p_lib_info;i:Integer);
begin
if (obj^.relo_bits=nil) then Exit;
obj^.relo_bits[i shr 3]:=obj^.relo_bits[i shr 3] 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):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 (budget_ptype_caller=0) then
begin
_2mb_mode:=((g_mode_2mb or 1)=3);
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,budget_ptype_caller);
end;
Result:=self_load_section(imgp,
i,
p_vaddr,
p_offset,
p_memsz,
p_filesz,
p_flags,
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,budget_ptype_caller);
end;
Result:=self_load_section(imgp,
i,
p_vaddr,
p_offset,
p_memsz,
p_filesz,
p_flags,
used_mode_2m,
fname,
cache);
end;
if (Result<>0) then
begin
FreeMem(cache);
Exit;
end;
addr:=(p_vaddr and QWORD($ffffffffffffc000));
size:=((p_vaddr and $3fff) + $3fff + phdr^.p_memsz) and QWORD($ffffffffffffc000);
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(@g_vmspace.vm_map,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;
function self_load_shared_object(path:pchar;new:p_lib_info;wire:Integer):Integer;
label
_fail_dealloc;
var
nd:t_nameidata;
error:Integer;
budget: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;
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;
budget:=budget_ptype_caller;
if is_system_path(path) then
begin
if not is_libc_or_fios(path) then
begin
budget:=2;
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;
rtld_load_auth(imgp);
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=2) then
begin
addr:=ET_DYN_LOAD_ADDR_SYS;
end;
error:=rtld_mmap(@addr,imgp^.max_addr-imgp^.min_addr);
if (error<>0) then
begin
Writeln(StdErr,'self_load_shared_object:','failed to allocate VA for ',imgp^.execpath);
goto _fail_dealloc;
end;
Writeln(' rtld_mmap:0x',HexStr(addr,12),'..0x',HexStr(addr+(imgp^.max_addr-imgp^.min_addr),12),':',imgp^.execpath);
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_object_allocate(OBJT_DEFAULT,OFF_TO_IDX(imgp^.max_addr-imgp^.min_addr));
error:=dynlib_load_sections(imgp,new,phdr,hdr^.e_phnum,delta);
if (error<>0) then
begin
goto _fail_dealloc;
end;
if (budget=2) then
begin
new^.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);
Exit(error);
end;
function change_relro_protection(obj:p_lib_info;prot:Integer):Integer;
var
map:vm_map_t;
addr:Pointer;
size:QWORD;
begin
map:=@g_vmspace.vm_map;
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
map:vm_map_t;
begin
if (obj^.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
map:=@g_vmspace.vm_map;
//
vm_map_lock(map);
//
Result:=change_relro_protection(obj,VM_PROT_RW);
if (Result<>0) then Exit;
Result:=copyout(src,dst,size);
change_relro_protection(obj,VM_PROT_READ);
//
vm_map_unlock(map);
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^.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^.dag_inited:=1;
end;
procedure ref_dag(root:p_lib_info);
var
elm:p_Objlist_Entry;
begin
Assert(root^.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^.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;
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^.init_plt<>0) then Exit;
Result:=change_relro_protection(obj,VM_PROT_RW);
if (Result<>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
For i:=0 to count-1 do
begin
kaddr:=i or QWORD($effffffe00000000);
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);
obj^.init_plt:=1;
end;
function do_load_object(path:pchar;flags:DWORD;var err:Integer):p_lib_info;
label
_inc_max,
_error;
var
fname:pchar;
new:p_lib_info;
obj:p_lib_info;
i:Integer;
tls_max:Integer;
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);
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;
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^.textrel<>0) then
begin
Writeln(StdErr,'do_load_object:',new^.lib_path,' has impure text');
err:=EINVAL;
goto _error;
end;
init_relo_bits(new);
dynlibs_add_obj(new);
new^.loaded:=1;
Exit(new);
_error:
rtld_munmap(new^.map_base,new^.map_size);
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);
//
rtld_munmap(obj^.map_base, obj^.map_size);
TAILQ_REMOVE(@dynlibs_info.obj_list,obj,@obj^.link);
Dec(dynlibs_info.obj_count);
//dynlib_notify_event(td,lib->id,0x80);
obj_free(obj);
//
obj:=next;
end;
end;
function preload_prx_modules(path:pchar;flags:DWORD;var err:Integer):p_lib_info;
label
_do_load;
var
obj:p_lib_info;
pbase:pchar;
fname:RawByteString;
begin
Result:=nil;
err:=0;
pbase:=dynlib_basename(path);
obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
while (obj<>nil) do
begin
if object_match_name(obj,pbase) then
begin
Exit(obj);
end;
obj:=TAILQ_NEXT(obj,@obj^.link);
end;
//try original
fname:=path;
if rtld_file_exists(pchar(fname)) then goto _do_load;
fname:=ChangeFileExt(fname,'.sprx');
if rtld_file_exists(pchar(fname)) then goto _do_load;
fname:=ChangeFileExt(fname,'.prx');
if rtld_file_exists(pchar(fname)) then goto _do_load;
//try /system/*
fname:=pbase;
if (fname[1]<>'/') then
begin
fname:='/'+fname;
end;
fname:='/'+p_proc.p_randomized_path+fname;
if rtld_file_exists(pchar(fname)) then goto _do_load;
fname:=ChangeFileExt(fname,'.sprx');
if rtld_file_exists(pchar(fname)) then goto _do_load;
fname:=ChangeFileExt(fname,'.prx');
if rtld_file_exists(pchar(fname)) then goto _do_load;
Writeln(StdErr,' prx_module_not_found:',dynlib_basename(path));
err:=ENOENT;
Exit(nil);
_do_load:
Result:=do_load_object(pchar(fname),flags,err);
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,ord(root^.jmpslots_done=0));
if (Result=0) then
begin
obj:=dynlibs_info.libprogram;
while (obj<>nil) do
begin
if (obj<>root) then
begin
Result:=relocate_one_object(obj,ord(root^.jmpslots_done=0));
end;
obj:=TAILQ_NEXT(obj,@obj^.link);
end;
end;
change_relro_protection_all(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
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^.jmpslots_done:=1;
end;
if ((flags and $40000)<>0) then //set not_get_proc?
begin
obj^.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
modname:pchar;
begin
if (obj^.ref_count=0) then
begin
Assert(False,'Invalid shared object handle');
Exit(EINVAL);
end;
unref_dag(obj);
if (obj^.ref_count<>1) then
begin
Exit(0);
end;
modname:=get_mod_name(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^.id>0) then Exit(True);
obj^.desc.free:=nil;
obj^.objt:=NAMED_DYNL;
obj^.name:='';
key:=-1;
if id_name_new(@named_table,obj,@key) then
begin
obj^.id:=(key+1);
id_release(obj);
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
key:=id-1;
Result:=id_name_get(@named_table,key,NAMED_DYNL);
if (Result=nil) then Exit;
id_release(Result);
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 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;
end.