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.