mirror of https://github.com/red-prig/fpPS4.git
1265 lines
28 KiB
Plaintext
1265 lines
28 KiB
Plaintext
unit kern_rtld;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
sysutils,
|
|
kern_param,
|
|
kern_thr,
|
|
vnode,
|
|
sys_vm_object,
|
|
vuio,
|
|
elf64,
|
|
kern_authinfo;
|
|
|
|
const
|
|
AT_NULL = 0; // Terminates the vector.
|
|
AT_IGNORE = 1; // Ignored entry.
|
|
AT_EXECFD = 2; // File descriptor of program to load.
|
|
AT_PHDR = 3; // Program header of program already loaded.
|
|
AT_PHENT = 4; // Size of each program header entry.
|
|
AT_PHNUM = 5; // Number of program header entries.
|
|
AT_PAGESZ = 6; // Page size in bytes.
|
|
AT_BASE = 7; // Interpreter's base address.
|
|
AT_FLAGS = 8; // Flags (unused for i386).
|
|
AT_ENTRY = 9; // Where interpreter should transfer control.
|
|
AT_NOTELF =10; // Program is not ELF ??
|
|
AT_UID =11; // Real uid.
|
|
AT_EUID =12; // Effective uid.
|
|
AT_GID =13; // Real gid.
|
|
AT_EGID =14; // Effective gid.
|
|
AT_EXECPATH =15; // Path to the executable.
|
|
AT_CANARY =16; // Canary for SSP
|
|
AT_CANARYLEN =17; // Length of the canary.
|
|
AT_OSRELDATE =18; // OSRELDATE.
|
|
AT_NCPUS =19; // Number of CPUs.
|
|
AT_PAGESIZES =20; // Pagesizes.
|
|
AT_PAGESIZESLEN=21; // Number of pagesizes.
|
|
AT_TIMEKEEP =22; // Pointer to timehands.
|
|
AT_STACKPROT =23; // Initial stack protection.
|
|
|
|
AT_COUNT =24; // Count of defined aux entry types.
|
|
|
|
ARG_MAX=262144; // max bytes for an exec function
|
|
|
|
ps_arg_cache_limit=$400;
|
|
|
|
type
|
|
p_ps_strings=^t_ps_strings;
|
|
t_ps_strings=packed record
|
|
ps_argvstr :ppchar; //first of 0 or more argument string
|
|
ps_nargvstr:Integer; //the number of argument strings
|
|
_align1 :Integer; //
|
|
ps_envstr :ppchar; //first of 0 or more environment strings
|
|
ps_nenvstr :Integer; //the number of environment strings
|
|
_align2 :Integer; //
|
|
end;
|
|
{$IF sizeof(t_ps_strings)<>32}{$STOP sizeof(t_ps_strings)<>32}{$ENDIF}
|
|
|
|
p_image_args=^t_image_args;
|
|
t_image_args=packed record
|
|
buf :PChar; //pointer to string buffer
|
|
begin_argv :PChar; //beginning of argv in buf
|
|
begin_envv :PChar; //beginning of envv in buf
|
|
endp :PChar; //current `end' pointer of arg & env strings
|
|
fname :PChar; //pointer to filename of executable (system space)
|
|
fname_buf :PChar; //pointer to optional malloc(M_TEMP) buffer
|
|
stringspace:Integer; //space left in arg & env buffer
|
|
argc :Integer; //count of argument strings
|
|
envc :Integer; //count of environment strings
|
|
fd :Integer; //file descriptor of the executable
|
|
end;
|
|
|
|
p_elf64_auxargs=^t_elf64_auxargs;
|
|
t_elf64_auxargs=packed record
|
|
execfd:Int64;
|
|
phdr :QWORD;
|
|
phent :QWORD;
|
|
phnum :QWORD;
|
|
pagesz:QWORD;
|
|
base :QWORD;
|
|
flags :QWORD;
|
|
entry :QWORD;
|
|
end;
|
|
|
|
p_image_params=^t_image_params;
|
|
t_image_params=packed record
|
|
vp :p_vnode;
|
|
obj :vm_object_t;
|
|
attr :p_vattr;
|
|
image_self :p_self_header;
|
|
image_header:p_elf64_hdr;
|
|
entry_addr :Pointer;
|
|
reloc_base :Pointer;
|
|
opened :Integer;
|
|
elf_size :Integer;
|
|
auxargs :p_elf64_auxargs;
|
|
auxarg_size :QWORD;
|
|
args :p_image_args;
|
|
execpath :PChar;
|
|
execpathp :Pointer;
|
|
freepath :PChar;
|
|
canary :Pointer;
|
|
pagesizes :Pointer;
|
|
canarylen :Integer;
|
|
pagesizeslen:Integer;
|
|
|
|
dyn_vaddr:p_elf64_dyn;
|
|
|
|
tls_size :QWORD;
|
|
tls_align :QWORD;
|
|
tls_init_size:QWORD;
|
|
tls_init_addr:Pointer;
|
|
|
|
eh_frame_hdr_addr:Pointer;
|
|
eh_frame_hdr_size:QWORD;
|
|
|
|
authinfo:t_authinfo;
|
|
|
|
proc_param_addr:pSceProcParam;
|
|
proc_param_size:QWORD;
|
|
|
|
module_param_addr:psceModuleParam;
|
|
module_param_size:QWORD;
|
|
|
|
dyn_id :Integer;
|
|
sce_dynlib_data_id:Integer;
|
|
sce_comment_id :Integer;
|
|
dyn_exist :Integer;
|
|
|
|
dyn_offset :QWORD;
|
|
dyn_filesz :QWORD;
|
|
|
|
sce_dynlib_data_addr:QWORD;
|
|
sce_dynlib_data_size:QWORD;
|
|
|
|
sce_comment_offset :QWORD;
|
|
sce_comment_filesz :QWORD;
|
|
|
|
min_addr:QWORD;
|
|
max_addr:QWORD;
|
|
|
|
relro_addr:Pointer;
|
|
relro_size:QWORD;
|
|
|
|
hdr_e_type:Integer;
|
|
end;
|
|
|
|
function maxInt64(a,b:Int64):Int64; inline;
|
|
function minInt64(a,b:Int64):Int64; inline;
|
|
|
|
function get_elf_phdr(elf_hdr:p_elf64_hdr):p_elf64_phdr;
|
|
|
|
procedure rtld_free_self(imgp:p_image_params);
|
|
function rtld_load_self(imgp:p_image_params):Integer;
|
|
procedure rtld_load_auth(imgp:p_image_params);
|
|
|
|
function is_used_mode_2mb(phdr:p_elf64_phdr;is_dynlib,budget_ptype:Integer):Boolean;
|
|
|
|
function rtld_dirname(path,bname:pchar):Integer;
|
|
function rtld_file_exists(path:pchar):Boolean;
|
|
|
|
function convert_prot(flags:Elf64_Word):Byte;
|
|
|
|
function rtld_mmap (paddr:PQWORD;size:QWORD;hint:PChar):Integer;
|
|
procedure rtld_munmap(base:Pointer;size:QWORD;hint:PChar);
|
|
|
|
function scan_phdr(imgp:p_image_params;phdr:p_elf64_phdr;count:Integer):Integer;
|
|
|
|
function elf64_get_eh_frame_info(hdr:p_eh_frame_hdr;
|
|
hdr_size :QWORD;
|
|
hdr_vaddr:QWORD;
|
|
data_size:QWORD;
|
|
eh_frame_addr:PPointer;
|
|
eh_frame_size:PQWORD):Integer;
|
|
|
|
|
|
function scan_dyn_offset(imgp:p_image_params;phdr:p_elf64_phdr;count:Integer):Integer;
|
|
|
|
function self_load_section(imgp:p_image_params;
|
|
id,vaddr,offset,memsz,filesz:QWORD;
|
|
prot:Byte;
|
|
wire:Byte;
|
|
use_mode_2mb:Boolean;
|
|
name:pchar;
|
|
var cache:Pointer):Integer;
|
|
|
|
function is_system_path(path:pchar):Boolean;
|
|
function is_libc_or_fios_sprx(path:pchar):Boolean;
|
|
function dynlib_basename(path:pchar):pchar;
|
|
|
|
implementation
|
|
|
|
uses
|
|
errno,
|
|
systm,
|
|
vnamei,
|
|
vfs_lookup,
|
|
vfs_subr,
|
|
vnode_if,
|
|
vm,
|
|
vmparam,
|
|
vm_map,
|
|
vm_mmap,
|
|
vm_pmap,
|
|
md_map,
|
|
kern_proc,
|
|
kern_budget,
|
|
kern_patcher;
|
|
|
|
function maxInt64(a,b:Int64):Int64; inline;
|
|
begin
|
|
if (a>b) then Result:=a else Result:=b;
|
|
end;
|
|
|
|
function minInt64(a,b:Int64):Int64; inline;
|
|
begin
|
|
if (a<b) then Result:=a else Result:=b;
|
|
end;
|
|
|
|
function get_elf_phdr(elf_hdr:p_elf64_hdr):p_elf64_phdr; inline;
|
|
begin
|
|
Result:=Pointer(elf_hdr+1);
|
|
end;
|
|
|
|
function get_elf_phdr_offset(elf_hdr:p_elf64_hdr):Int64; inline;
|
|
begin
|
|
Result:=SizeOf(elf64_hdr);
|
|
end;
|
|
|
|
procedure fixup_offset_size(var offset,size:Int64;max:Int64);
|
|
var
|
|
s,e:Int64;
|
|
begin
|
|
s:=offset;
|
|
e:=s+size;
|
|
|
|
s:=MinInt64(s,max);
|
|
e:=MinInt64(e,max);
|
|
|
|
offset:=s;
|
|
size :=(e-s);
|
|
end;
|
|
|
|
function kread(vp:p_vnode;buf:Pointer;nbyte,offset:Integer):Integer;
|
|
var
|
|
uio:t_uio;
|
|
aio:iovec;
|
|
begin
|
|
uio:=Default(t_uio);
|
|
aio:=Default(iovec);
|
|
//
|
|
aio.iov_base :=buf;
|
|
aio.iov_len :=nbyte;
|
|
//
|
|
uio.uio_iov :=@aio;
|
|
uio.uio_iovcnt:=1;
|
|
uio.uio_offset:=offset;
|
|
uio.uio_segflg:=UIO_SYSSPACE;
|
|
uio.uio_rw :=UIO_READ;
|
|
uio.uio_resid :=nbyte;
|
|
uio.uio_td :=curkthread;
|
|
//
|
|
Result:=VOP_READ(vp,@uio,0);
|
|
|
|
if (Result=0) then
|
|
begin
|
|
if (uio.uio_resid>0) then
|
|
begin
|
|
Result:=ENOEXEC;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure rtld_free_self(imgp:p_image_params);
|
|
begin
|
|
FreeMem(imgp^.image_header);
|
|
FreeMem(imgp^.image_self);
|
|
imgp^.image_header:=nil;
|
|
imgp^.image_self:=nil;
|
|
imgp^.elf_size:=0;
|
|
end;
|
|
|
|
function rtld_load_self(imgp:p_image_params):Integer;
|
|
Var
|
|
vp:p_vnode;
|
|
obj_size:Int64;
|
|
n,s:Int64;
|
|
Magic:DWORD;
|
|
i,count:Integer;
|
|
self_hdr :p_self_header;
|
|
self_segs:p_self_segment;
|
|
elf_hdr :p_elf64_hdr;
|
|
elf_phdr :p_elf64_phdr;
|
|
MinSeg :Int64;
|
|
MaxSeg :Int64;
|
|
src_ofs :Int64;
|
|
dst_ofs :Int64;
|
|
mem_size :Int64;
|
|
begin
|
|
Result:=0;
|
|
|
|
if (imgp=nil) then Exit(EINVAL);
|
|
|
|
vp:=imgp^.vp;
|
|
obj_size:=imgp^.attr^.va_size;
|
|
|
|
if (obj_size=0) then Exit(ENOEXEC);
|
|
|
|
Result:=kread(vp,@Magic,SizeOf(DWORD),0);
|
|
if (Result<>0) then Exit;
|
|
|
|
case Magic of
|
|
ELFMAG: //elf64
|
|
begin
|
|
elf_hdr:=AllocMem(obj_size);
|
|
|
|
Result:=kread(vp,elf_hdr,obj_size,0);
|
|
if (Result<>0) then
|
|
begin
|
|
FreeMem(elf_hdr);
|
|
Exit;
|
|
end;
|
|
|
|
imgp^.image_header:=elf_hdr;
|
|
imgp^.image_self :=nil;
|
|
imgp^.elf_size :=obj_size;
|
|
end;
|
|
SELF_MAGIC: //self
|
|
begin
|
|
self_hdr:=AllocMem(obj_size);
|
|
|
|
Result:=kread(vp,self_hdr,obj_size,0);
|
|
if (Result<>0) then
|
|
begin
|
|
FreeMem(self_hdr);
|
|
Exit;
|
|
end;
|
|
|
|
if (self_hdr^.File_size>obj_size) then
|
|
begin
|
|
//resize
|
|
self_hdr:=ReAllocMem(self_hdr,self_hdr^.File_size);
|
|
//fill zeros
|
|
FillChar(PBYTE(self_hdr)[obj_size],self_hdr^.File_size-obj_size,0);
|
|
//new size
|
|
obj_size:=self_hdr^.File_size;
|
|
|
|
//FreeMem(self_hdr);
|
|
//Exit(EFAULT);
|
|
end;
|
|
|
|
count:=self_hdr^.Num_Segments;
|
|
|
|
if (count=0) then
|
|
begin
|
|
FreeMem(self_hdr);
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
self_segs:=Pointer(self_hdr+1);
|
|
|
|
For i:=0 to count-1 do
|
|
if ((self_segs[i].flags and (SELF_PROPS_ENCRYPTED or SELF_PROPS_COMPRESSED))<>0) then
|
|
begin
|
|
Writeln(StdErr,'exec_load_self:',imgp^.execpath,' is encrypted!');
|
|
FreeMem(self_hdr);
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
elf_hdr:=Pointer(self_segs)+(count*SizeOf(t_self_segment));
|
|
|
|
elf_phdr:=get_elf_phdr(elf_hdr);
|
|
|
|
MinSeg:=High(Int64);
|
|
MaxSeg:=0;
|
|
|
|
count:=self_hdr^.Num_Segments;
|
|
|
|
For i:=0 to count-1 do
|
|
if ((self_segs[i].flags and SELF_PROPS_BLOCKED)<>0) then
|
|
begin
|
|
s:=SELF_SEGMENT_INDEX(self_segs[i].flags);
|
|
s:=elf_phdr[s].p_offset;
|
|
MinSeg:=MinInt64(s,MinSeg);
|
|
s:=s+minInt64(self_segs[i].filesz,self_segs[i].memsz);
|
|
MaxSeg:=MaxInt64(s,MaxSeg);
|
|
end;
|
|
|
|
if (MinSeg>MaxSeg) then
|
|
begin
|
|
FreeMem(self_hdr);
|
|
Exit(EFAULT);
|
|
end;
|
|
|
|
imgp^.image_header:=AllocMem(MaxSeg);
|
|
imgp^.elf_size :=MaxSeg;
|
|
|
|
//elf_hdr part
|
|
n:=ptruint(elf_hdr)-ptruint(self_hdr); //offset to hdr
|
|
s:=self_hdr^.Header_Size+self_hdr^.Meta_size; //offset to end
|
|
s:=MinInt64(obj_size,s); //min size
|
|
s:=MaxInt64(s,n)-n; //get size
|
|
|
|
//first page
|
|
Move(elf_hdr^,imgp^.image_header^,s);
|
|
|
|
count:=self_hdr^.Num_Segments;
|
|
|
|
For i:=0 to count-1 do
|
|
if ((self_segs[i].flags and SELF_PROPS_BLOCKED)<>0) then
|
|
begin
|
|
s:=SELF_SEGMENT_INDEX(self_segs[i].flags);
|
|
|
|
mem_size:=minInt64(self_segs[i].filesz,self_segs[i].memsz);
|
|
|
|
src_ofs:=self_segs[i].offset; //start offset
|
|
dst_ofs:=elf_phdr[s].p_offset; //start offset
|
|
|
|
fixup_offset_size(src_ofs,mem_size,obj_size);
|
|
fixup_offset_size(dst_ofs,mem_size,MaxSeg);
|
|
|
|
if (src_ofs>=obj_size) then
|
|
begin
|
|
Assert(false,'src_ofs>=obj_size');
|
|
end;
|
|
|
|
if ((src_ofs+mem_size)>obj_size) then
|
|
begin
|
|
Assert(false,'(src_ofs+mem_size)>obj_size');
|
|
end;
|
|
|
|
if (dst_ofs>=MaxSeg) then
|
|
begin
|
|
Assert(false,'dst_ofs>=MaxSeg');
|
|
end;
|
|
|
|
if ((dst_ofs+mem_size)>MaxSeg) then
|
|
begin
|
|
Assert(false,'(dst_ofs+mem_size)>MaxSeg');
|
|
end;
|
|
|
|
Move( (Pointer(self_hdr) +src_ofs)^, //src
|
|
(Pointer(imgp^.image_header)+dst_ofs)^, //dst
|
|
mem_size); //size
|
|
end;
|
|
|
|
imgp^.image_self:=self_hdr;
|
|
end;
|
|
else
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure rtld_load_auth(imgp:p_image_params);
|
|
var
|
|
hdr:p_elf64_hdr;
|
|
authinfo:p_self_authinfo;
|
|
s:ptruint;
|
|
begin
|
|
if (imgp=nil) then Exit;
|
|
|
|
imgp^.authinfo:=Default(t_authinfo);
|
|
|
|
imgp^.authinfo.app_caps [0]:=QWORD($2000038000000000); //IsGame
|
|
imgp^.authinfo.app_caps [1]:=QWORD($000000000000FF00);
|
|
|
|
imgp^.authinfo.app_attrs[0]:=QWORD($4000400040000000); //dmem map flags
|
|
imgp^.authinfo.app_attrs[1]:=QWORD($4000000000000000);
|
|
imgp^.authinfo.app_attrs[2]:=QWORD($0080000000000002);
|
|
imgp^.authinfo.app_attrs[3]:=QWORD($F0000000FFFF4000);
|
|
|
|
if (imgp^.image_header=nil) or
|
|
(imgp^.image_self =nil) then
|
|
begin
|
|
case ExtractFileExt(imgp^.execpath) of
|
|
'.sprx','.prx' :imgp^.authinfo.app_type:=QWORD($3900000000000002);
|
|
'.sdll','.sexe':imgp^.authinfo.app_type:=QWORD($3901000000000001);
|
|
else
|
|
imgp^.authinfo.app_type:=QWORD($3100000000000001);
|
|
end;
|
|
Exit;
|
|
end;
|
|
|
|
hdr:=imgp^.image_header;
|
|
s:=SizeOf(t_self_header);
|
|
s:=s+(imgp^.image_self^.Num_Segments*SizeOf(t_self_segment));
|
|
s:=s+get_elf_phdr_offset(hdr);
|
|
s:=s+(hdr^.e_phnum*SizeOf(elf64_phdr));
|
|
s:=AlignUp(s,SELF_SEGMENT_BLOCK_ALIGNMENT);
|
|
|
|
authinfo:=Pointer(Pointer(imgp^.image_self)+s);
|
|
|
|
imgp^.authinfo.app_type:=authinfo^.AuthorityID;
|
|
end;
|
|
|
|
function is_used_mode_2mb(phdr:p_elf64_phdr;is_dynlib,budget_ptype:Integer):Boolean;
|
|
var
|
|
flag_write:Integer;
|
|
begin
|
|
Result:=False;
|
|
|
|
if (budget_ptype=PTYPE_BIG_APP) then
|
|
begin
|
|
flag_write:=2;
|
|
if (phdr^.p_type<>PT_SCE_RELRO) then
|
|
begin
|
|
flag_write:=phdr^.p_flags and 2;
|
|
end;
|
|
|
|
case g_mode_2mb of
|
|
M2MB_DEFAULT :Result:=(is_dynlib=0) and (g_self_loading<>0);
|
|
M2MB_READONLY:Result:=(flag_write=0);
|
|
M2MB_ENABLE :Result:=True;
|
|
else;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
function get_char_sep(path:pchar):char; inline;
|
|
const
|
|
c_host='/host/';
|
|
begin
|
|
if (StrLComp(path,c_host,Length(c_host))=0) then
|
|
begin
|
|
Result:='\';
|
|
end else
|
|
begin
|
|
Result:='/';
|
|
end;
|
|
end;
|
|
|
|
function rtld_dirname(path,bname:pchar):Integer;
|
|
var
|
|
endp:pchar;
|
|
chr:char;
|
|
begin
|
|
Result:=0;
|
|
|
|
{ Empty or NULL string gets treated as "." }
|
|
if (path=nil) or (path^=#0) then
|
|
begin
|
|
bname[0]:='.';
|
|
bname[1]:=#0;
|
|
Exit(0);
|
|
end;
|
|
|
|
chr:=get_char_sep(path);
|
|
|
|
{ Strip trailing slashes }
|
|
endp:=path + strlen(path) - 1;
|
|
while (endp > path) and (endp^=chr) do Dec(endp);
|
|
|
|
{ Find the start of the dir }
|
|
while (endp > path) and (endp^<>chr) do Dec(endp);
|
|
|
|
{ Either the dir is "/" or there are no slashes }
|
|
if (endp=path) then
|
|
begin
|
|
if (endp^=chr) then
|
|
begin
|
|
bname[0]:='/';
|
|
end else
|
|
begin
|
|
bname[0]:='.';
|
|
end;
|
|
bname[1]:=#0;
|
|
Exit(0);
|
|
end else
|
|
begin
|
|
repeat
|
|
Dec(endp);
|
|
until not ((endp > path) and (endp^=chr));
|
|
end;
|
|
|
|
if ((endp - path + 2) > PATH_MAX) then
|
|
begin
|
|
Writeln(StdErr,'Filename is too long:',path);
|
|
Exit(-1);
|
|
end;
|
|
|
|
Move(path^, bname^, endp - path + 1);
|
|
bname[endp - path + 1]:=#0;
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
function rtld_file_exists(path:pchar):Boolean;
|
|
var
|
|
nd:t_nameidata;
|
|
error:Integer;
|
|
begin
|
|
Result:=False;
|
|
if (path=nil) then Exit;
|
|
|
|
NDINIT(@nd,LOOKUP,LOCKLEAF or FOLLOW or SAVENAME or MPSAFE, UIO_SYSSPACE, path, curkthread);
|
|
|
|
error:=nd_namei(@nd);
|
|
|
|
if (error=0) then
|
|
begin
|
|
NDFREE(@nd, NDF_ONLY_PNBUF);
|
|
vput(nd.ni_vp);
|
|
Exit(True);
|
|
end;
|
|
|
|
NDFREE(@nd, NDF_ONLY_PNBUF);
|
|
end;
|
|
|
|
function convert_prot(flags:Elf64_Word):Byte;
|
|
begin
|
|
Result:=0;
|
|
if ((flags and PF_X)<>0) then Result:=Result or VM_PROT_EXECUTE;
|
|
if ((flags and PF_W)<>0) then Result:=Result or VM_PROT_WRITE;
|
|
if ((flags and PF_R)<>0) then Result:=Result or VM_PROT_READ;
|
|
end;
|
|
|
|
function rtld_mmap(paddr:PQWORD;size:QWORD;hint:PChar):Integer;
|
|
var
|
|
map:vm_map_t;
|
|
addr:QWORD;
|
|
begin
|
|
map:=p_proc.p_vmspace;
|
|
|
|
addr:=paddr^;
|
|
|
|
if (addr=0) and ((g_appinfo.mmap_flags and 2)<>0) then
|
|
begin
|
|
addr:=SCE_REPLAY_EXEC_START;
|
|
end;
|
|
|
|
Result:=vm_mmap2(map,@addr,size,0,0,MAP_ANON or MAP_PRIVATE or (21 shl MAP_ALIGNMENT_BIT),OBJT_DEFAULT,nil,0,nil);
|
|
|
|
if (Result=0) then
|
|
begin
|
|
paddr^:=addr;
|
|
end else
|
|
begin
|
|
paddr^:=0;
|
|
end;
|
|
|
|
Writeln(' rtld_mmap:0x',HexStr(addr,12),'..0x',HexStr(addr+size,12),':',Result,':',hint);
|
|
end;
|
|
|
|
procedure rtld_munmap(base:Pointer;size:QWORD;hint:PChar);
|
|
var
|
|
map:vm_map_t;
|
|
begin
|
|
Writeln(' rtld_munmap:0x',HexStr(QWORD(base),12),'..0x',HexStr(QWORD(base)+size,12),':',hint);
|
|
|
|
if (base<>nil) and (size<>0) then
|
|
begin
|
|
map:=p_proc.p_vmspace;
|
|
//
|
|
vm_map_remove(map,QWORD(base),QWORD(base) + size);
|
|
end;
|
|
end;
|
|
|
|
function scan_phdr(imgp:p_image_params;phdr:p_elf64_phdr;count:Integer):Integer;
|
|
var
|
|
i:Integer;
|
|
text_id :Integer;
|
|
data_id :Integer;
|
|
sce_relro_id:Integer;
|
|
vaddr:QWORD;
|
|
memsz:QWORD;
|
|
begin
|
|
if (imgp=nil) then Exit(EINVAL);
|
|
if (phdr=nil) then Exit(EINVAL);
|
|
if (count=0) then Exit(EINVAL);
|
|
|
|
imgp^.min_addr:=High(Int64);
|
|
imgp^.max_addr:=0;
|
|
|
|
text_id :=-1;
|
|
data_id :=-1;
|
|
sce_relro_id:=-1;
|
|
imgp^.dyn_id:=-1;
|
|
|
|
if (count<>0) then
|
|
For i:=0 to count-1 do
|
|
begin
|
|
|
|
case phdr[i].p_type of
|
|
PT_LOAD,
|
|
PT_SCE_RELRO:
|
|
begin
|
|
vaddr:=phdr[i].p_vaddr;
|
|
|
|
if ((phdr[i].p_align and PAGE_MASK)<>0) or
|
|
((vaddr and PAGE_MASK)<>0) or
|
|
((phdr[i].p_offset and PAGE_MASK)<>0) then
|
|
begin
|
|
Writeln(StdErr,'scan_phdr:',imgp^.execpath,' segment #',i,' is not page aligned');
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
memsz:=phdr[i].p_memsz;
|
|
|
|
if (memsz<=phdr[i].p_filesz) and (phdr[i].p_filesz<>memsz) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if (memsz > $7fffffff) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if ((phdr[i].p_offset shr $20)<>0) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
imgp^.min_addr:=MinInt64(imgp^.min_addr,vaddr);
|
|
|
|
vaddr:=(vaddr+memsz+PAGE_MASK) and QWORD(not PAGE_MASK);
|
|
|
|
imgp^.max_addr:=MaxInt64(imgp^.max_addr,vaddr);
|
|
|
|
if (phdr[i].p_type=PT_SCE_RELRO) then
|
|
begin
|
|
sce_relro_id:=i;
|
|
end else
|
|
if ((phdr[i].p_flags and PF_X)=0) then
|
|
begin
|
|
if (data_id=-1) then data_id:=i;
|
|
end else
|
|
begin
|
|
text_id:=i;
|
|
end;
|
|
end;
|
|
|
|
PT_DYNAMIC:
|
|
begin
|
|
imgp^.dyn_exist :=1;
|
|
imgp^.dyn_id :=i;
|
|
imgp^.dyn_vaddr :=Pointer(phdr[i].p_vaddr);
|
|
imgp^.dyn_offset:=phdr[i].p_offset;
|
|
imgp^.dyn_filesz:=phdr[i].p_filesz;
|
|
|
|
memsz:=phdr[i].p_memsz;
|
|
|
|
if (memsz<=phdr[i].p_filesz) and (phdr[i].p_filesz<>memsz) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if (memsz > $7fffffff) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if ((phdr[i].p_offset shr $20)<>0) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
end;
|
|
|
|
PT_TLS:
|
|
begin
|
|
imgp^.tls_size :=phdr[i].p_memsz;
|
|
imgp^.tls_align :=phdr[i].p_align;
|
|
imgp^.tls_init_size:=phdr[i].p_filesz;
|
|
imgp^.tls_init_addr:=Pointer(phdr[i].p_vaddr);
|
|
|
|
memsz:=phdr[i].p_memsz;
|
|
|
|
if (memsz<=phdr[i].p_filesz) and (phdr[i].p_filesz<>memsz) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if (memsz > $7fffffff) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if ((phdr[i].p_offset shr $20)<>0) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if (phdr[i].p_align > 32) then
|
|
begin
|
|
Writeln(StdErr,'scan_phdr:',imgp^.execpath,' alignment of segment #',i,' it must be less than 32.');
|
|
Exit(ENOEXEC);
|
|
end;
|
|
end;
|
|
|
|
PT_SCE_DYNLIBDATA:
|
|
begin
|
|
imgp^.sce_dynlib_data_id :=i;
|
|
imgp^.sce_dynlib_data_addr:=phdr[i].p_offset;
|
|
imgp^.sce_dynlib_data_size:=phdr[i].p_filesz;
|
|
|
|
if (phdr[i].p_memsz<>0) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if (phdr[i].p_filesz > $7fffffff) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if ((phdr[i].p_offset shr $20)<>0) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
end;
|
|
|
|
PT_SCE_PROCPARAM:
|
|
begin
|
|
imgp^.proc_param_addr:=Pointer(phdr[i].p_vaddr);
|
|
imgp^.proc_param_size:=phdr[i].p_filesz;
|
|
end;
|
|
|
|
PT_SCE_MODULE_PARAM:
|
|
begin
|
|
imgp^.module_param_addr:=Pointer(phdr[i].p_vaddr);
|
|
imgp^.module_param_size:=phdr[i].p_filesz;
|
|
end;
|
|
|
|
PT_GNU_EH_FRAME:
|
|
begin
|
|
imgp^.eh_frame_hdr_addr:=Pointer(phdr[i].p_vaddr);
|
|
imgp^.eh_frame_hdr_size:=phdr[i].p_memsz;
|
|
|
|
memsz:=phdr[i].p_memsz;
|
|
|
|
if (memsz<=phdr[i].p_filesz) and (phdr[i].p_filesz<>memsz) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if (memsz > $7fffffff) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if ((phdr[i].p_offset shr $20)<>0) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
end;
|
|
|
|
PT_SCE_COMMENT:
|
|
begin
|
|
imgp^.sce_comment_id :=i;
|
|
imgp^.sce_comment_offset:=phdr[i].p_offset;
|
|
imgp^.sce_comment_filesz:=phdr[i].p_filesz;
|
|
|
|
if (phdr[i].p_memsz<>0) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if (phdr[i].p_filesz > $7fffffff) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if ((phdr[i].p_offset shr $20)<>0) then
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if (imgp^.min_addr=High(Int64)) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
if (imgp^.max_addr=0) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
if (imgp^.dyn_exist<>0) then
|
|
begin
|
|
if (imgp^.sce_dynlib_data_size=0) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
if (imgp^.dyn_filesz=0) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
end;
|
|
|
|
if (sce_relro_id<>-1) then
|
|
begin
|
|
vaddr:=phdr[sce_relro_id].p_vaddr;
|
|
|
|
if (vaddr=0) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
memsz:=phdr[sce_relro_id].p_memsz;
|
|
|
|
if (memsz=0) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
if (((phdr[text_id].p_vaddr+phdr[text_id].p_memsz+PAGE_2MB_MASK) and QWORD(not PAGE_2MB_MASK))<>vaddr) and
|
|
(((phdr[text_id].p_vaddr+phdr[text_id].p_memsz+PAGE_MASK ) and QWORD(not PAGE_MASK ))<>vaddr) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
if (((vaddr+memsz+PAGE_2MB_MASK) and QWORD(not PAGE_2MB_MASK))<>phdr[data_id].p_vaddr) and
|
|
(((vaddr+memsz+PAGE_MASK ) and QWORD(not PAGE_MASK ))<>phdr[data_id].p_vaddr) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
end;
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
function elf64_get_eh_frame_info(hdr:p_eh_frame_hdr;
|
|
hdr_size :QWORD;
|
|
hdr_vaddr:QWORD;
|
|
data_size:QWORD;
|
|
eh_frame_addr:PPointer;
|
|
eh_frame_size:PQWORD):Integer;
|
|
label
|
|
__result;
|
|
var
|
|
ret1:Integer;
|
|
h,res,pos:PByte;
|
|
enc:Byte;
|
|
offset:QWORD;
|
|
size:QWORD;
|
|
fde_count:DWORD;
|
|
_end:DWORD;
|
|
begin
|
|
enc:=0;
|
|
ret1:=copyin(@hdr^.eh_frame_ptr_enc,@enc,1);
|
|
if (ret1<>0) then Exit(-1);
|
|
|
|
h:=Pointer(hdr + 1);
|
|
|
|
offset:=0;
|
|
res:=nil;
|
|
case enc of
|
|
DW_EH_PE_udata4:
|
|
begin
|
|
ret1:=copyin(h,@offset,4);
|
|
if (ret1<>0) then Exit(-1);
|
|
|
|
res:=Pointer(Integer(offset) + hdr_vaddr);
|
|
end;
|
|
DW_EH_PE_pcrel or DW_EH_PE_sdata4:
|
|
begin
|
|
ret1:=copyin(h,@offset,4);
|
|
if (ret1<>0) then Exit(-1);
|
|
|
|
res:=h + Integer(offset);
|
|
end;
|
|
else
|
|
Exit(-1)
|
|
end;
|
|
|
|
size:=0;
|
|
if (res=nil) then
|
|
begin
|
|
__result:
|
|
eh_frame_addr^:=res;
|
|
eh_frame_size^:=size;
|
|
Exit(0);
|
|
end;
|
|
|
|
fde_count:=0;
|
|
ret1:=copyin(res,@fde_count,4);
|
|
if (ret1<>0) then Exit(-1);
|
|
|
|
pos:=res;
|
|
size:=0;
|
|
|
|
repeat
|
|
|
|
offset:=fde_count;
|
|
if (offset=$ffffffff) then
|
|
begin
|
|
ret1:=copyin(pos + 4,@offset,8);
|
|
if (ret1<>0) then break;
|
|
offset:=offset + 12;
|
|
end else
|
|
begin
|
|
if (fde_count=0) then
|
|
begin
|
|
size:=size + 4;
|
|
goto __result;
|
|
end;
|
|
offset:=offset + 4;
|
|
end;
|
|
|
|
_end:=offset + size;
|
|
|
|
if (data_size <= (QWORD(res) + _end)) then goto __result;
|
|
pos:=pos + offset;
|
|
|
|
ret1:=copyin(pos,@fde_count,4);
|
|
size:=_end;
|
|
|
|
until (ret1<>0);
|
|
|
|
Result:=-1;
|
|
end;
|
|
|
|
function scan_dyn_offset(imgp:p_image_params;phdr:p_elf64_phdr;count:Integer):Integer;
|
|
var
|
|
p_offset:QWORD;
|
|
p_filesz:QWORD;
|
|
i:Integer;
|
|
begin
|
|
Result:=0;
|
|
|
|
if (imgp^.dyn_id=-1) then Exit;
|
|
|
|
p_offset:=phdr[imgp^.dyn_id].p_offset;
|
|
p_filesz:=phdr[imgp^.dyn_id].p_filesz;
|
|
|
|
if (count<>0) then
|
|
For i:=0 to count-1 do
|
|
begin
|
|
|
|
if (phdr[i].p_offset <= p_offset) and
|
|
(imgp^.dyn_id<>i) and
|
|
( (p_filesz + p_offset) <= (phdr[i].p_offset + phdr[i].p_filesz)) then
|
|
begin
|
|
if (i<>-1) then
|
|
begin
|
|
imgp^.dyn_id :=i;
|
|
imgp^.dyn_offset:=imgp^.dyn_offset - phdr[i].p_offset;
|
|
Exit(0);
|
|
end;
|
|
break;
|
|
end;
|
|
|
|
end;
|
|
|
|
Result:=EINVAL;
|
|
end;
|
|
|
|
function _rwxs(prot:Byte):RawByteString; inline;
|
|
var
|
|
_R:array[0..1] of AnsiChar='_R';
|
|
_W:array[0..1] of AnsiChar='_W';
|
|
_X:array[0..1] of AnsiChar='_X';
|
|
begin
|
|
Result:=_R[ord((prot and VM_PROT_READ )<>0)]+
|
|
_W[ord((prot and VM_PROT_WRITE )<>0)]+
|
|
_X[ord((prot and VM_PROT_EXECUTE)<>0)];
|
|
end;
|
|
|
|
function self_load_section(imgp:p_image_params;
|
|
id,vaddr,offset,memsz,filesz:QWORD;
|
|
prot:Byte;
|
|
wire:Byte;
|
|
use_mode_2mb:Boolean;
|
|
name:pchar;
|
|
var cache:Pointer):Integer;
|
|
var
|
|
map:vm_map_t;
|
|
vaddr_lo:QWORD;
|
|
vaddr_hi:QWORD;
|
|
size :QWORD;
|
|
base :Pointer;
|
|
begin
|
|
Result:=0;
|
|
|
|
if (memsz<filesz) then
|
|
begin
|
|
Writeln(StdErr,'[KERNEL] self_load_section: memsz',HexStr(memsz,8),') < filesz(',HexStr(filesz,8),') at segment ',id);
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if ((prot and 6)=6) then
|
|
begin
|
|
Writeln(StdErr,'[KERNEL] self_load_section: writeable text segment ',id,', ',HexStr(vaddr,8));
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if ((vaddr and PAGE_MASK)<>0) then
|
|
begin
|
|
Writeln(StdErr,'[KERNEL] self_load_section: non-aligned segment ',id,', ',HexStr(vaddr,8));
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
if (filesz < memsz) then
|
|
begin
|
|
if (use_mode_2mb) then
|
|
begin
|
|
size:=QWORD(not PAGE_2MB_MASK);
|
|
end else
|
|
begin
|
|
size:=QWORD(not PAGE_MASK);
|
|
end;
|
|
size:=size and filesz;
|
|
end else
|
|
begin
|
|
size:=(filesz + PAGE_MASK) and QWORD(not PAGE_MASK);
|
|
end;
|
|
|
|
if (size>memsz) then
|
|
begin
|
|
memsz:=size;
|
|
end;
|
|
|
|
vaddr_lo:=vaddr and QWORD(not PAGE_MASK);
|
|
vaddr_hi:=(vaddr + memsz + PAGE_MASK) and QWORD(not PAGE_MASK);
|
|
|
|
base:=Pointer(imgp^.image_header)+offset;
|
|
|
|
map:=p_proc.p_vmspace;
|
|
|
|
vm_map_lock(map);
|
|
|
|
//remove prev if exist
|
|
vm_map_delete(map,vaddr_lo,vaddr_hi,True);
|
|
|
|
vm_object_reference(imgp^.obj);
|
|
|
|
Result:=vm_map_insert(map,
|
|
imgp^.obj,
|
|
offset,
|
|
vaddr_lo,vaddr_hi,
|
|
VM_PROT_RW, VM_PROT_ALL,
|
|
0,
|
|
nil,
|
|
false);
|
|
if (Result<>0) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
//
|
|
vm_object_deallocate(imgp^.obj);
|
|
//
|
|
Writeln(StdErr,'[',HexStr(vaddr_lo,8),'..',HexStr(vaddr_hi,8),']');
|
|
Writeln(StdErr,'[KERNEL] self_load_section: vm_map_insert failed ',id,', ',HexStr(vaddr,8),' (',Result,')');
|
|
Exit(vm_mmap_to_errno(Result));
|
|
end;
|
|
|
|
vm_map_set_name_locked(map,vaddr_lo,vaddr_hi,name);
|
|
|
|
memsz:=vaddr_hi-vaddr_lo;
|
|
cache:=ReAllocMem(cache,memsz);
|
|
|
|
FillChar(cache^,memsz,0);
|
|
|
|
Move(base^,cache^,filesz);
|
|
|
|
//if ((prot and VM_PROT_EXECUTE)<>0) then
|
|
begin
|
|
Writeln(' ',_rwxs(prot),':vaddr=0x',HexStr(vaddr,12),' offset=0x',HexStr(offset,6),' memsz=0x',HexStr(memsz,6),' filesz=0x',HexStr(filesz,6));
|
|
patcher_process_section(imgp,cache,Pointer(vaddr_lo),filesz,memsz,prot);
|
|
end;
|
|
|
|
Result:=copyout(cache,Pointer(vaddr_lo),memsz);
|
|
if (Result<>0) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
//
|
|
Writeln(StdErr,'[KERNEL] self_load_section: copyout failed ',
|
|
id,', ',HexStr(base),'->',HexStr(vaddr_lo,8),':',HexStr(memsz,8),' (',Result,')');
|
|
Assert(false,'self_load_section');
|
|
Exit;
|
|
end;
|
|
|
|
Result:=vm_map_protect(map,vaddr_lo,vaddr_hi,prot,False);
|
|
if (Result<>0) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
//
|
|
Writeln(StdErr,'[KERNEL] self_load_section: vm_map_protect failed ',id,', ',HexStr(vaddr,8),' (',Result,')');
|
|
Exit(vm_mmap_to_errno(Result));
|
|
end;
|
|
|
|
if ((prot and VM_PROT_EXECUTE)<>0) then
|
|
begin
|
|
md_cacheflush(uplift(Pointer(vaddr_lo)),memsz,ICACHE);
|
|
end;
|
|
|
|
vm_map_unlock(map);
|
|
|
|
if (wire<>0) then
|
|
begin
|
|
Result:=vm_map_wire(map,vaddr_lo,vaddr_hi,VM_MAP_WIRE_USER or 8);
|
|
|
|
if (Result<>0) then
|
|
begin
|
|
vm_object_deallocate(imgp^.obj);
|
|
//
|
|
Writeln(StdErr,'[',HexStr(vaddr_lo,8),'..',HexStr(vaddr_hi,8),']');
|
|
Writeln(StdErr,'[KERNEL] self_load_section: vm_map_wire failed ',id,', ',HexStr(vaddr,8),' (',Result,')');
|
|
Exit(vm_mmap_to_errno(Result));
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
function is_system_path(path:pchar):Boolean;
|
|
begin
|
|
if (path=nil) then Exit(False);
|
|
if (path[0]<>'/') then Exit(False);
|
|
Result:=StrLComp(p_proc.p_randomized_path,@path[1],strlen(p_proc.p_randomized_path))=0;
|
|
end;
|
|
|
|
function dynlib_basename(path:pchar):pchar;
|
|
var
|
|
idx:pchar;
|
|
chr:char;
|
|
begin
|
|
if (path=nil) then Exit(nil);
|
|
|
|
chr:=get_char_sep(path);
|
|
|
|
idx:=strrscan(path,chr);
|
|
if (idx=nil) then
|
|
begin
|
|
Result:=path;
|
|
end else
|
|
begin
|
|
Result:=idx+1;
|
|
end;
|
|
end;
|
|
|
|
function is_libc_or_fios_sprx(path:pchar):Boolean;
|
|
const
|
|
c_libc='libc.sprx';
|
|
c_libSceFios2='libSceFios2.sprx';
|
|
var
|
|
f:pchar;
|
|
begin
|
|
f:=dynlib_basename(path);
|
|
|
|
if (StrLComp(f,c_libc,Length(c_libc))=0) or
|
|
(StrLComp(f,c_libSceFios2,Length(c_libSceFios2))=0) then
|
|
begin
|
|
Result:=True;
|
|
end else
|
|
begin
|
|
Result:=False;
|
|
end;
|
|
end;
|
|
|
|
|
|
end.
|
|
|