FPPS4/sys/kern/kern_dlsym.pas

521 lines
9.9 KiB
Plaintext

unit kern_dlsym;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
sysutils,
mqueue,
elf64,
subr_dynlib;
const
SYMLOOK_BASE64 =$001;
SYMLOOK_IN_PLT =$002;
SYMLOOK_NOT_DBG =$008;
SYMLOOK_DLSYM =$00A;
SYMLOOK_MANGLED =$100;
type
p_SymLook=^t_SymLook;
t_SymLook=record
name :pchar;
libname :pchar;
modname :pchar;
symbol :pchar;
hash :QWORD;
flags :DWORD;
obj :p_lib_info;
defobj_out:p_lib_info;
sym_out :p_elf64_sym;
end;
function do_dlsym(obj:p_lib_info;symbol,libname:pchar;flags:DWORD):Pointer;
function find_symdef(symnum:QWORD;refobj:p_lib_info;var defobj_out:p_lib_info;flags:DWORD;cache:p_SymCache):p_elf64_sym;
implementation
uses
errno,
elf_nid_utils,
kern_stub;
function convert_raw_symbol_str_to_base64(symbol:pchar):RawByteString;
var
nid:QWORD;
begin
nid:=ps4_nid_hash(symbol);
Result:=EncodeValue64(nid);
end;
function symlook_obj(req:p_SymLook;obj:p_lib_info):Integer;
label
_next;
var
i:Integer;
symnum:Integer;
symp:p_elf64_sym;
strp:pchar;
ST_TYPE:Integer;
begin
Result:=0;
//linear search
For i:=0 to obj^.rel_data^.dynsymcount-1 do
begin
symnum:=i;
//symnum:=obj^.rel_data^.chains[i];
//if (symnum<>0) then
begin
symp:=obj^.rel_data^.symtab_addr+symnum;
strp:=obj^.rel_data^.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) then
begin
if (symp^.st_shndx<>SHN_UNDEF) or
((ST_TYPE=STT_FUN) and
((req^.flags and SYMLOOK_IN_PLT)=0)
) then
begin
if (StrLComp(req^.name,strp,strlen(req^.name))=0) then
begin
goto _next;
end;
end;
end;
end;
STT_TLS:
begin
if (symp^.st_shndx<>SHN_UNDEF) or
((ST_TYPE=STT_FUN) and
((req^.flags and SYMLOOK_IN_PLT)=0)
) then
begin
if (StrLComp(req^.name,strp,strlen(req^.name))=0) then
begin
goto _next;
end;
end;
end;
else;
end;
end;
end;
Exit(ESRCH);
_next:
req^.sym_out:=symp;
req^.defobj_out:=obj;
//needed_filtees/needed_aux_filtees->symlook_needed not used
Result:=0;
end;
function symlook_list(req:p_SymLook;var objlist:TAILQ_HEAD;var dlp:t_DoneList):Integer;
label
_symlook_obj;
var
modname:pchar;
req1:t_SymLook;
elm:p_Objlist_Entry;
def:p_elf64_sym;
defobj:p_lib_info;
str:pchar;
begin
Result:=0;
if ((req^.flags and SYMLOOK_MANGLED)=0) then
begin
modname:=req^.modname;
end else
if (req^.symbol=nil) then
begin
modname:=nil;
end else
begin
modname:=strrscan(req^.symbol,'#');
if (modname<>nil) then
begin
modname:=modname+1;
end;
end;
def :=nil;
defobj:=nil;
elm:=TAILQ_FIRST(@objlist);
while (elm<>nil) do
begin
if not donelist_check(dlp,elm^.obj) then
begin
if (modname=nil) then //any module?
begin
_symlook_obj:
req1:=req^;
Result:=symlook_obj(@req1,elm^.obj);
if (Result=0) then
begin
if (def=nil) or (ELF64_ST_BIND(req1.sym_out^.st_info)<>STB_WEAK) then
begin
def :=req1.sym_out;
defobj:=req1.defobj_out;
if (ELF64_ST_BIND(def^.st_info)<>STB_WEAK) then Break;
end;
end;
end else
begin
str:=get_mod_name(elm^.obj,0); //export=0
if (StrComp(str,modname)=0) then
begin
goto _symlook_obj;
end;
end;
end;
elm:=TAILQ_NEXT(elm,@elm^.link);
end;
if (def<>nil) then
begin
req^.sym_out :=def;
req^.defobj_out:=defobj;
Exit(0);
end;
Exit(ESRCH);
end;
function symlook_global(req:p_SymLook;var donelist:t_DoneList):Integer;
var
req1:t_SymLook;
elm:p_Objlist_Entry;
begin
req1:=req^;
//Search all objects loaded at program start up.
if (req^.defobj_out=nil) or
(ELF64_ST_BIND(req^.sym_out^.st_info)=STB_WEAK) then
begin
Result:=symlook_list(@req1, dynlibs_info.list_main, donelist);
if (Result=0) then
begin
if (req^.defobj_out=nil) or
(ELF64_ST_BIND(req1.sym_out^.st_info)<>STB_WEAK) then
begin
req^.sym_out :=req1.sym_out;
req^.defobj_out:=req1.defobj_out;
Assert(req^.defobj_out<>nil,'req->defobj_out is NULL #1');
end;
end;
end;
//Search all DAGs whose roots are RTLD_GLOBAL objects.
elm:=TAILQ_FIRST(@dynlibs_info.list_global);
while (elm<>nil) do
begin
if (req^.defobj_out<>nil) and
(ELF64_ST_BIND(req^.sym_out^.st_info)<>STB_WEAK) then
begin
Break;
end;
Result:=symlook_list(@req1,elm^.obj^.dagmembers,donelist);
if (Result=0) then
begin
if (req^.defobj_out=nil) or
(ELF64_ST_BIND(req1.sym_out^.st_info)<>STB_WEAK) then
begin
req^.sym_out :=req1.sym_out;
req^.defobj_out:=req1.defobj_out;
Assert(req^.defobj_out<>nil,'req->defobj_out is NULL #2');
end;
end;
//
elm:=TAILQ_NEXT(elm,@elm^.link);
end;
if (req^.sym_out<>nil) then
Exit(0)
else
Exit(ESRCH);
end;
function symlook_default(req:p_SymLook;refobj:p_lib_info):Integer;
var
donelist:t_DoneList;
elm:p_Objlist_Entry;
req1:t_SymLook;
begin
donelist:=Default(t_DoneList);
donelist_init(donelist);
req1:=req^;
symlook_global(req,donelist);
elm:=TAILQ_FIRST(@refobj^.dagmembers);
while (elm<>nil) do
begin
if (req^.sym_out<>nil) then
begin
if (ELF64_ST_BIND(req^.sym_out^.st_info)<>STB_WEAK) then
begin
Break;
end;
end;
Result:=symlook_list(@req1,elm^.obj^.dagmembers,donelist);
if (Result=0) then
begin
if (req^.sym_out=nil) or
(ELF64_ST_BIND(req1.sym_out^.st_info)<>STB_WEAK) then
begin
req^.sym_out :=req1.sym_out;
req^.defobj_out:=req1.defobj_out;
Assert(req^.defobj_out<>nil,'req->defobj_out is NULL #2');
end;
end;
//
elm:=TAILQ_NEXT(elm,@elm^.link);
end;
if (req^.sym_out<>nil) then
Exit(0)
else
Exit(ESRCH);
end;
function do_dlsym(obj:p_lib_info;symbol,libname:pchar;flags:DWORD):Pointer;
var
req:t_SymLook;
base64:RawByteString;
donelist:t_DoneList;
err:Integer;
begin
Result:=nil;
req:=Default(t_SymLook);
req.modname:=get_mod_name(obj,0); //export=0
req.flags:=flags or SYMLOOK_DLSYM;
if ((flags and SYMLOOK_BASE64)=0) then
begin
req.libname:=libname;
if (libname=nil) then
begin
req.libname:=req.modname;
end;
base64:=convert_raw_symbol_str_to_base64(symbol);
symbol:=pchar(base64);
end else
begin
req.libname:=nil;
req.modname:=nil;
end;
req.name :=symbol;
//req.hash :=elf_hash(@req);
req.obj :=obj;
donelist:=Default(t_DoneList);
donelist_init(donelist);
err:=0;
if (obj^.mainprog=0) then
begin
err:=symlook_list(@req,obj^.dagmembers,donelist);
end else
begin
err:=symlook_global(@req,donelist);
end;
if (err<>0) then
begin
req.defobj_out:=nil;
req.sym_out :=nil;
end;
if (req.sym_out=nil) then
begin
Result:=nil;
end else
begin
Result:=req.defobj_out^.relocbase + req.sym_out^.st_value;
end;
end;
//48 8D 3D 00 00 00 00 lea rdi,[rip+$00000000] lea (%rip),%rdi
type
p_jmpq64_trampoline=^t_jmpq64_trampoline;
t_jmpq64_trampoline=packed record
lea:array[0..6] of Byte;
//
inst :Word; //FF 25
offset :DWORD; //00
addr :QWORD;
str :PChar;
libname:PChar;
end;
const
c_jmpq64_trampoline:t_jmpq64_trampoline=(lea:($48,$8D,$3D,$F9,$FF,$FF,$FF);inst:$25FF;offset:0;addr:0);
procedure _unresolve_symbol(data:p_jmpq64_trampoline);
begin
Writeln('_unresolve_symbol:',data^.str,':',data^.libname);
readln;
end;
function get_unresolve_ptr(str,libname:PChar):Pointer;
var
stub:p_stub_chunk;
begin
stub:=p_alloc(nil,SizeOf(t_jmpq64_trampoline));
p_jmpq64_trampoline(@stub^.body)^:=c_jmpq64_trampoline;
p_jmpq64_trampoline(@stub^.body)^.addr:=QWORD(@_unresolve_symbol);
p_jmpq64_trampoline(@stub^.body)^.str:=str;
p_jmpq64_trampoline(@stub^.body)^.libname:=libname;
Result:=@stub^.body;
end;
function find_symdef(symnum:QWORD;refobj:p_lib_info;var defobj_out:p_lib_info;flags:DWORD;cache:p_SymCache):p_elf64_sym;
var
req:t_SymLook;
def:p_elf64_sym;
ref:p_elf64_sym;
defobj:p_lib_info;
str:pchar;
count:Integer;
err:Integer;
ST_BIND:Integer;
nModuleId,nLibraryId:WORD;
nNid:QWORD;
fname:RawByteString;
begin
Result:=nil;
if (refobj^.rel_data^.dynsymcount<=symnum) then Exit(nil);
if (cache<>nil) then
begin
if (cache[symnum].sym<>nil) then
begin
defobj_out:=cache[symnum].obj;
Exit(cache[symnum].sym);
end;
end;
def:=nil;
defobj_out:=nil;
count:=refobj^.rel_data^.symtab_size div SizeOf(elf64_sym);
if (symnum>=count) then Exit(nil);
ref:=refobj^.rel_data^.symtab_addr + symnum;
str:=obj_get_str(refobj,ref^.st_name);
ST_BIND:=ELF64_ST_BIND(ref^.st_info);
if (ST_BIND=STB_LOCAL) then
begin
def :=ref;
defobj:=refobj;
end else
begin
if (ST_BIND=STT_SECTION) then
begin
Writeln(StdErr,'find_symdef:',refobj^.lib_path,': Bogus symbol table entry ',symnum);
end;
req:=Default(t_SymLook);
req.symbol:=str;
req.flags :=(flags{ or SYMLOOK_MANGLED});
req.obj :=refobj;
if DecodeEncName(str,nModuleId,nLibraryId,nNid) then
begin
req.modname:=get_mod_name(refobj,nModuleId);
req.libname:=get_lib_name(refobj,nLibraryId);
fname:=BaseEncName(str);
req.name:=pchar(fname);
end;
//convert_mangled_name_to_long
//req.libname
//req.name
err:=symlook_default(@req, refobj);
if (err=0) then
begin
def :=req.sym_out;
defobj:=req.defobj_out;
end;
end;
if (def=nil) then
begin
if (ELF64_ST_BIND(ref^.st_info)=STB_WEAK) then
begin
def :=@dynlibs_info.sym_zero;
defobj:=dynlibs_info.libprogram;
end else
begin
dynlibs_info.sym_nops.st_info :=(STB_GLOBAL shl 4) or STT_NOTYPE;
dynlibs_info.sym_nops.st_shndx:=SHN_UNDEF;
dynlibs_info.sym_nops.st_value:=-Int64(dynlibs_info.libprogram^.relocbase)+Int64(get_unresolve_ptr(str,req.libname));
def :=@dynlibs_info.sym_nops;
defobj:=dynlibs_info.libprogram;
end;
end;
if (def<>nil) then
begin
defobj_out:=defobj;
if (cache<>nil) then
begin
cache[symnum].sym:=def;
cache[symnum].obj:=defobj;
end;
end;
Exit(def);
end;
end.