mirror of https://github.com/red-prig/fpPS4.git
1903 lines
40 KiB
Plaintext
1903 lines
40 KiB
Plaintext
unit kern_exec;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
sysutils,
|
|
mqueue,
|
|
kern_param,
|
|
kern_thr,
|
|
vnode,
|
|
vuio,
|
|
vcapability,
|
|
elf64,
|
|
kern_rtld,
|
|
subr_dynlib;
|
|
|
|
function exec_alloc_args(args:p_image_args):Integer;
|
|
procedure exec_free_args(args:p_image_args);
|
|
|
|
function exec_copyin_args(args:p_image_args;
|
|
fname:pchar;
|
|
segflg:uio_seg;
|
|
argv:ppchar;
|
|
envv:ppchar):Integer;
|
|
|
|
function kern_execve(td:p_kthread;args:p_image_args;mode:Integer=0):Integer;
|
|
function main_execve(fname:pchar;argv,envv:ppchar):Integer;
|
|
procedure main_switch_context;
|
|
function sys_execve(fname:pchar;argv,envv:ppchar):Integer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
systm,
|
|
md_systm,
|
|
errno,
|
|
kern_proc,
|
|
kern_mtx,
|
|
vm,
|
|
vmparam,
|
|
vm_map,
|
|
vm_mmap,
|
|
sys_vm_object,
|
|
vm_pager,
|
|
vnamei,
|
|
vfs_lookup,
|
|
vmount,
|
|
vfile,
|
|
vstat,
|
|
vfcntl,
|
|
vfs_vnops,
|
|
vfs_subr,
|
|
kern_thread,
|
|
kern_budget,
|
|
kern_descrip,
|
|
kern_exit,
|
|
vfs_cache,
|
|
vnode_if,
|
|
sys_resource,
|
|
kern_resource,
|
|
sys_event,
|
|
machdep,
|
|
kern_dlsym,
|
|
kern_authinfo,
|
|
vfs_syscalls,
|
|
signal,
|
|
trap,
|
|
md_context,
|
|
subr_backtrace;
|
|
|
|
function exec_alloc_args(args:p_image_args):Integer;
|
|
begin
|
|
args^.buf:=AllocMem(PATH_MAX + ARG_MAX);
|
|
if (args^.buf=nil) then Exit(ENOMEM);
|
|
Result:=0;
|
|
end;
|
|
|
|
procedure exec_free_args(args:p_image_args);
|
|
begin
|
|
if (args^.buf<>nil) then
|
|
begin
|
|
FreeMem(args^.buf);
|
|
args^.buf:=nil;
|
|
end;
|
|
if (args^.fname_buf<>nil) then
|
|
begin
|
|
FreeMem(args^.fname_buf);
|
|
args^.fname_buf:=nil;
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* Copy out argument and environment strings from the old process address
|
|
* space into the temporary string buffer.
|
|
}
|
|
function exec_copyin_args(args :p_image_args;
|
|
fname :pchar;
|
|
segflg:uio_seg;
|
|
argv :ppchar;
|
|
envv :ppchar):Integer;
|
|
label
|
|
err_exit;
|
|
var
|
|
argp,envp:pchar;
|
|
error:Integer;
|
|
length:int64;
|
|
|
|
function f_fuword_argv:Boolean; inline;
|
|
begin
|
|
if (segflg=UIO_SYSSPACE) then
|
|
begin
|
|
argp:=md_fuword(argv^);
|
|
end else
|
|
begin
|
|
argp:=fuword(argv^);
|
|
end;
|
|
Inc(argv);
|
|
Result:=(argp<>nil);
|
|
end;
|
|
|
|
function f_fuword_envv:Boolean; inline;
|
|
begin
|
|
if (segflg=UIO_SYSSPACE) then
|
|
begin
|
|
envp:=md_fuword(envv^);
|
|
end else
|
|
begin
|
|
envp:=fuword(envv^);
|
|
end;
|
|
Inc(envv);
|
|
Result:=(envp<>nil);
|
|
end;
|
|
|
|
begin
|
|
args^:=Default(t_image_args);
|
|
|
|
if (argv=nil) then
|
|
begin
|
|
Exit(EFAULT);
|
|
end;
|
|
|
|
{
|
|
* Allocate demand-paged memory for the file name, argument, and
|
|
* environment strings.
|
|
}
|
|
error:=exec_alloc_args(args);
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
{
|
|
* Copy the file name.
|
|
}
|
|
if (fname<>nil) then
|
|
begin
|
|
args^.fname:=args^.buf;
|
|
|
|
if (segflg=UIO_SYSSPACE) then
|
|
begin
|
|
error:=copystr (fname, args^.fname, PATH_MAX, @length);
|
|
end else
|
|
begin
|
|
error:=copyinstr(fname, args^.fname, PATH_MAX, @length);
|
|
end;
|
|
|
|
if (error<>0) then goto err_exit;
|
|
end else
|
|
begin
|
|
length:=0;
|
|
end;
|
|
|
|
args^.begin_argv:=args^.buf + length;
|
|
args^.endp:=args^.begin_argv;
|
|
args^.stringspace:=ARG_MAX;
|
|
|
|
{
|
|
* extract arguments first
|
|
}
|
|
while (f_fuword_argv) do
|
|
begin
|
|
if (argp=Pointer(-1)) then
|
|
begin
|
|
error:=EFAULT;
|
|
goto err_exit;
|
|
end;
|
|
|
|
if (segflg=UIO_SYSSPACE) then
|
|
begin
|
|
error:=copystr (argp, args^.endp, args^.stringspace, @length);
|
|
end else
|
|
begin
|
|
error:=copyinstr(argp, args^.endp, args^.stringspace, @length);
|
|
end;
|
|
|
|
if (error<>0) then
|
|
begin
|
|
if (error=ENAMETOOLONG) then
|
|
begin
|
|
error:=E2BIG;
|
|
end;
|
|
goto err_exit;
|
|
end;
|
|
Dec(args^.stringspace,length);
|
|
Inc(args^.endp ,length);
|
|
Inc(args^.argc);
|
|
end;
|
|
|
|
args^.begin_envv:=args^.endp;
|
|
|
|
{
|
|
* extract environment strings
|
|
}
|
|
if (envv<>nil) then
|
|
begin
|
|
while (f_fuword_envv) do
|
|
begin
|
|
if (envp=Pointer(-1)) then
|
|
begin
|
|
error:=EFAULT;
|
|
goto err_exit;
|
|
end;
|
|
|
|
if (segflg=UIO_SYSSPACE) then
|
|
begin
|
|
error:=copystr (envp, args^.endp, args^.stringspace, @length);
|
|
end else
|
|
begin
|
|
error:=copyinstr(envp, args^.endp, args^.stringspace, @length);
|
|
end;
|
|
|
|
if (error<>0) then
|
|
begin
|
|
if (error=ENAMETOOLONG) then
|
|
begin
|
|
error:=E2BIG;
|
|
end;
|
|
goto err_exit;
|
|
end;
|
|
Dec(args^.stringspace,length);
|
|
Inc(args^.endp ,length);
|
|
Inc(args^.envc);
|
|
end;
|
|
end;
|
|
|
|
Exit(0);
|
|
|
|
err_exit:
|
|
exec_free_args(args);
|
|
Exit(error);
|
|
end;
|
|
|
|
|
|
{
|
|
* Destroy old address space, and allocate a new stack
|
|
* The new stack is only SGROWSIZ large because it is grown
|
|
* automatically in trap.c.
|
|
}
|
|
function exec_new_vmspace(imgp:p_image_params):Integer;
|
|
var
|
|
error:Integer;
|
|
vmspace:p_vmspace;
|
|
map:vm_map_t;
|
|
//obj:vm_object_t;
|
|
shared_page_base:Pointer;
|
|
shared_page_len :QWORD;
|
|
sv_minuser:QWORD;
|
|
sv_maxuser:QWORD;
|
|
stack_addr:QWORD;
|
|
ssiz:QWORD;
|
|
limit:QWORD;
|
|
is_diag:Boolean;
|
|
begin
|
|
vmspace:=p_proc.p_vmspace;
|
|
|
|
{ May be called with Giant held }
|
|
//EVENTHANDLER_INVOKE(process_exec, p, imgp);
|
|
|
|
{
|
|
* Blow away entire process VM, if address space not shared,
|
|
* otherwise, create a new VM space so that other threads are
|
|
* not disrupted
|
|
}
|
|
map:=@vmspace^.vm_map;
|
|
|
|
sv_minuser:=VM_MINUSER_ADDRESS;
|
|
sv_maxuser:=VM_MAXUSER_ADDRESS;
|
|
|
|
if (vm_map_min(map)=sv_minuser) and
|
|
(vm_map_max(map)=sv_maxuser) then
|
|
begin
|
|
//shmexit(vmspace);
|
|
//pmap_remove_pages(vmspace_pmap(vmspace));
|
|
vm_map_remove(map, vm_map_min(map), vm_map_max(map));
|
|
end else
|
|
begin
|
|
error:=vmspace_exec(sv_minuser, sv_maxuser);
|
|
if (error<>0) then Exit(error);
|
|
end;
|
|
|
|
if (DWORD(p_proc.p_budget_ptype) < 2) then
|
|
begin
|
|
//(vmspace->vm_map).needs_wakeup:=1;
|
|
//dmem_start_app_process(proc);
|
|
end else
|
|
begin
|
|
//cred = __crget();
|
|
|
|
//copy authinfo
|
|
g_authinfo:=imgp^.authinfo;
|
|
|
|
is_diag:=sceSblACMgrIsDiagProcess(@g_authinfo);
|
|
//crfree(cred);
|
|
//(vmspace->vm_map).needs_wakeup:=is_diag<>0;
|
|
if (is_diag) then
|
|
begin
|
|
//dmem_start_app_process(proc);
|
|
end;
|
|
end;
|
|
|
|
//budget
|
|
ssiz:=MAXSSIZ;
|
|
|
|
if (g_self_loading<>0) and
|
|
(p_proc.p_budget_ptype=PTYPE_BIG_APP) and
|
|
((g_appinfo.mmap_flags and 1)<>0) then
|
|
begin
|
|
limit:=game_fmem_size + ssiz;
|
|
if (bigapp_max_fmem_size < limit) then
|
|
begin
|
|
limit:=bigapp_max_fmem_size;
|
|
end;
|
|
set_bigapp_limits(limit,0);
|
|
end;
|
|
//
|
|
|
|
//calc shared page addres
|
|
vmspace^.sv_usrstack:=Pointer(USRSTACK {- (aslr_offset and $ffc000)});
|
|
|
|
{ Map a shared page }
|
|
|
|
shared_page_base:=vmspace^.sv_usrstack;
|
|
shared_page_len :=p_proc.p_sysent^.sv_shared_page_len;
|
|
|
|
//mapping shared page (sv_usrstack_len=0x4000)
|
|
error:=vm_map_fixed(map,nil,0,
|
|
QWORD(shared_page_base), shared_page_len,
|
|
VM_PROT_RW,
|
|
VM_PROT_RW or VM_PROT_EXECUTE,
|
|
MAP_INHERIT_SHARE or
|
|
MAP_ACC_NO_CHARGE,
|
|
MAP_COW_NO_BUDGET,
|
|
nil);
|
|
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
//copy sigcode
|
|
with p_proc.p_sysent^ do
|
|
if (sv_sigcode<>nil) and
|
|
(sv_szsigcode<>nil) then
|
|
begin
|
|
copyout(sv_sigcode,shared_page_base,sv_szsigcode^);
|
|
end;
|
|
|
|
//set prot
|
|
vm_map_protect(map,
|
|
QWORD(shared_page_base),
|
|
QWORD(shared_page_base)+shared_page_len,
|
|
VM_PROT_READ or VM_PROT_EXECUTE,
|
|
False);
|
|
|
|
{
|
|
obj:=sv^.sv_shared_page_obj;
|
|
if (obj<>nil) then
|
|
begin
|
|
vm_object_reference(obj);
|
|
error:=vm_map_fixed(map, obj, 0,
|
|
sv^.sv_shared_page_base, sv^.sv_shared_page_len,
|
|
VM_PROT_READ or VM_PROT_EXECUTE,
|
|
VM_PROT_READ or VM_PROT_EXECUTE,
|
|
MAP_INHERIT_SHARE or MAP_ACC_NO_CHARGE);
|
|
|
|
if (error<>0) then
|
|
begin
|
|
vm_object_deallocate(obj);
|
|
Exit(error);
|
|
end;
|
|
end;
|
|
}
|
|
|
|
ssiz:=MAXSSIZ;
|
|
|
|
stack_addr:=QWORD(vmspace^.sv_usrstack) - ssiz;
|
|
|
|
Writeln('vm_map_stack:0x',HexStr(stack_addr,11),'..0x',HexStr(stack_addr+ssiz,11));
|
|
|
|
error:=vm_map_stack(map,
|
|
stack_addr,ssiz,
|
|
VM_PROT_RW,VM_PROT_ALL,
|
|
MAP_STACK_GROWS_DOWN,
|
|
nil);
|
|
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
if (p_proc.p_vm_container=1) then
|
|
begin
|
|
error:=vm_map_wire(map,stack_addr,QWORD(vmspace^.sv_usrstack),VM_MAP_WIRE_USER or 8);
|
|
if (error<>0) then Exit;
|
|
end;
|
|
|
|
vm_map_set_name(map,stack_addr,QWORD(vmspace^.sv_usrstack),'main stack');
|
|
|
|
{ vm_ssize and vm_maxsaddr are somewhat antiquated concepts in the
|
|
* VM_STACK case, but they are still used to monitor the size of the
|
|
* process stack so we can check the stack rlimit.
|
|
}
|
|
vmspace^.vm_ssize :=sgrowsiz shr PAGE_SHIFT;
|
|
vmspace^.vm_maxsaddr:=vmspace^.sv_usrstack - ssiz;
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
|
|
function exec_copyout_strings(imgp:p_image_params):PQWORD;
|
|
var
|
|
vms:p_vmspace;
|
|
argc,envc:Integer;
|
|
vectp:ppchar;
|
|
stringp:pchar;
|
|
destp:Pointer;
|
|
arginfo:p_ps_strings;
|
|
stack_base:PQWORD;
|
|
execpath_len:QWORD;
|
|
canary:array[0..7] of QWORD;
|
|
begin
|
|
{
|
|
* Calculate string base and vector table pointers.
|
|
* Also deal with signal trampoline code for this exec type.
|
|
}
|
|
if (imgp^.execpath<>nil) and (imgp^.auxargs<>nil) then
|
|
execpath_len:=strlen(imgp^.execpath) + 1
|
|
else
|
|
execpath_len:=0;
|
|
|
|
vms:=p_proc.p_vmspace;
|
|
|
|
vms^.ps_strings:=(vms^.sv_usrstack-SizeOf(t_ps_strings));
|
|
arginfo:=vms^.ps_strings;
|
|
destp:=Pointer(arginfo);
|
|
|
|
{
|
|
* Copy the image path for the rtld.
|
|
}
|
|
if (execpath_len<>0) then
|
|
begin
|
|
Dec(destp,execpath_len);
|
|
imgp^.execpathp:=destp;
|
|
copyout(imgp^.execpath,destp,execpath_len);
|
|
end;
|
|
|
|
{
|
|
* Prepare the canary for SSP.
|
|
}
|
|
//arc4rand(canary, sizeof(canary), 0);
|
|
|
|
canary[0]:=QWORD($FEEDBABEFEEDBABE);
|
|
canary[1]:=QWORD($FEEDBABEFEEDBABE);
|
|
canary[2]:=QWORD($FEEDBABEFEEDBABE);
|
|
canary[3]:=QWORD($FEEDBABEFEEDBABE);
|
|
canary[4]:=QWORD($FEEDBABEFEEDBABE);
|
|
canary[5]:=QWORD($FEEDBABEFEEDBABE);
|
|
canary[6]:=QWORD($FEEDBABEFEEDBABE);
|
|
canary[7]:=QWORD($FEEDBABEFEEDBABE);
|
|
|
|
Dec(destp,sizeof(canary));
|
|
imgp^.canary:=destp;
|
|
copyout(@canary, destp, sizeof(canary));
|
|
imgp^.canarylen:=sizeof(canary);
|
|
|
|
{
|
|
* Prepare the pagesizes array.
|
|
}
|
|
Dec(destp,sizeof(pagesizes));
|
|
destp:=AlignUp(destp,8);
|
|
|
|
imgp^.pagesizes:=destp;
|
|
copyout(@pagesizes, destp, sizeof(pagesizes));
|
|
imgp^.pagesizeslen:=Length(pagesizes);
|
|
|
|
Dec(destp,ARG_MAX-imgp^.args^.stringspace);
|
|
destp:=AlignUp(destp,8);
|
|
|
|
{
|
|
* If we have a valid auxargs ptr, prepare some room
|
|
* on the stack.
|
|
}
|
|
if (imgp^.auxargs<>nil) then
|
|
begin
|
|
{
|
|
* 'AT_COUNT*2' is size for the ELF Auxargs data. This is for
|
|
* lower compatibility.
|
|
}
|
|
if (imgp^.auxarg_size<>0) then
|
|
begin
|
|
imgp^.auxarg_size:=imgp^.auxarg_size;
|
|
end else
|
|
begin
|
|
imgp^.auxarg_size:=(AT_COUNT * 2);
|
|
end;
|
|
{
|
|
* The '+ 2' is for the nil pointers at the end of each of
|
|
* the arg and env vector sets,and imgp^.auxarg_size is room
|
|
* for argument of Runtime loader.
|
|
}
|
|
vectp:=(destp - (imgp^.args^.argc + imgp^.args^.envc + 2 + imgp^.auxarg_size) * sizeof(Pointer));
|
|
end else
|
|
begin
|
|
{
|
|
* The '+ 2' is for the nil pointers at the end of each of
|
|
* the arg and env vector sets
|
|
}
|
|
vectp:=(destp - (imgp^.args^.argc + imgp^.args^.envc + 2) * sizeof(Pointer));
|
|
end;
|
|
|
|
{
|
|
* vectp also becomes our initial stack base
|
|
}
|
|
stack_base:=Pointer(vectp);
|
|
|
|
stringp:=imgp^.args^.begin_argv;
|
|
argc:=imgp^.args^.argc;
|
|
envc:=imgp^.args^.envc;
|
|
|
|
{
|
|
* Copy out strings - arguments and environment.
|
|
}
|
|
copyout(stringp, destp, ARG_MAX - imgp^.args^.stringspace);
|
|
|
|
{
|
|
* Fill in "ps_strings" struct for ps, w, etc.
|
|
}
|
|
suword(arginfo^.ps_argvstr, vectp);
|
|
suword32(PDWORD(@arginfo^.ps_nargvstr)^, argc);
|
|
|
|
{
|
|
* Fill in argument portion of vector table.
|
|
}
|
|
while (argc>0) do
|
|
begin
|
|
suword(vectp^, destp);
|
|
Inc(vectp);
|
|
|
|
while (stringp^<>#0) do
|
|
begin
|
|
Inc(stringp);
|
|
Inc(destp);
|
|
end;
|
|
Inc(stringp);
|
|
|
|
Inc(destp);
|
|
Dec(argc);
|
|
end;
|
|
|
|
{ a nil vector table pointer separates the argp's from the envp's }
|
|
suword(vectp^, nil);
|
|
Inc(vectp);
|
|
|
|
suword(arginfo^.ps_envstr, vectp);
|
|
suword32(PDWORD(@arginfo^.ps_nenvstr)^, envc);
|
|
|
|
{
|
|
* Fill in environment portion of vector table.
|
|
}
|
|
while (envc>0) do
|
|
begin
|
|
suword(vectp^, destp);
|
|
Inc(vectp);
|
|
|
|
while (stringp^<>#0) do
|
|
begin
|
|
Inc(stringp);
|
|
Inc(destp);
|
|
end;
|
|
Inc(stringp);
|
|
|
|
Inc(destp);
|
|
Dec(envc);
|
|
end;
|
|
|
|
{ end of vector table is a nil pointer }
|
|
suword(vectp^, nil);
|
|
|
|
Exit(stack_base);
|
|
end;
|
|
|
|
{
|
|
* Check permissions of file to execute.
|
|
* Called with imgp^.vp locked.
|
|
* Return 0 for success or error code on failure.
|
|
}
|
|
function exec_check_permissions(imgp:p_image_params):Integer;
|
|
var
|
|
vp:p_vnode;
|
|
attr:p_vattr;
|
|
error:Integer;
|
|
begin
|
|
vp:=imgp^.vp;
|
|
attr:=imgp^.attr;
|
|
|
|
{ Get file attributes }
|
|
error:=VOP_GETATTR(vp, attr);
|
|
if (error<>0) then Exit(error);
|
|
|
|
{
|
|
* 1) Check if file execution is disabled for the filesystem that
|
|
* this file resides on.
|
|
* 2) Ensure that at least one execute bit is on. Otherwise, a
|
|
* privileged user will always succeed, and we don't want this
|
|
* to happen unless the file really is executable.
|
|
* 3) Ensure that the file is a regular file.
|
|
}
|
|
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
|
|
Exit(EACCES);
|
|
end;
|
|
|
|
{
|
|
* Zero length files can't be exec'd
|
|
}
|
|
if (attr^.va_size=0) then Exit(ENOEXEC);
|
|
|
|
{
|
|
* Check for execute permission to file based on current credentials.
|
|
}
|
|
error:=VOP_ACCESS(vp, VEXEC);
|
|
if (error<>0) then Exit(error);
|
|
|
|
{
|
|
* Check number of open-for-writes on the file and deny execution
|
|
* if there are any.
|
|
}
|
|
if (vp^.v_writecount<>0) then Exit(ETXTBSY);
|
|
|
|
{
|
|
* Call filesystem specific open routine (which does nothing in the
|
|
* general case).
|
|
}
|
|
error:=VOP_OPEN(vp, FREAD, nil);
|
|
if (error=0) then imgp^.opened:=1;
|
|
|
|
Exit(error);
|
|
end;
|
|
|
|
function scan_load_sections(imgp:p_image_params;phdr:p_elf64_phdr;count:Integer):Integer;
|
|
var
|
|
i:Integer;
|
|
|
|
hdr:p_elf64_hdr;
|
|
|
|
total_size:QWORD;
|
|
text_addr :QWORD;
|
|
text_size :QWORD;
|
|
data_addr :QWORD;
|
|
data_size :QWORD;
|
|
relro_addr:QWORD;
|
|
relro_size:QWORD;
|
|
max_size1 :QWORD;
|
|
max_size2 :QWORD;
|
|
|
|
p_memsz :QWORD;
|
|
p_vaddr :QWORD;
|
|
p_filesz :QWORD;
|
|
p_offset :QWORD;
|
|
|
|
addr:QWORD;
|
|
size:QWORD;
|
|
|
|
used :QWORD;
|
|
limit:QWORD;
|
|
|
|
p_type :Elf64_Word;
|
|
p_flags :Byte;
|
|
wire :Byte;
|
|
_2mb_mode:Boolean;
|
|
used_mode_2m:Boolean;
|
|
|
|
auxargs:p_elf64_auxargs;
|
|
|
|
vms:p_vmspace;
|
|
|
|
cache:Pointer;
|
|
begin
|
|
Result:=0;
|
|
|
|
total_size:=0;
|
|
text_addr :=0;
|
|
text_size :=0;
|
|
data_addr :=0;
|
|
data_size :=0;
|
|
relro_addr:=0;
|
|
relro_size:=0;
|
|
|
|
hdr:=imgp^.image_header;
|
|
|
|
if (p_proc.p_budget_ptype=PTYPE_BIG_APP) then
|
|
begin
|
|
|
|
_2mb_mode:=((g_mode_2mb or 1)=3) or //M2MB_READONLY,M2MB_ENABLE
|
|
((g_self_loading<>0) and (g_mode_2mb=M2MB_DEFAULT));
|
|
end else
|
|
begin
|
|
_2mb_mode:=False;
|
|
end;
|
|
|
|
cache:=nil;
|
|
|
|
if (count<>0) then
|
|
begin
|
|
max_size1:=0;
|
|
max_size2:=0;
|
|
|
|
scan_max_size(imgp,phdr,count,max_size1,max_size2);
|
|
|
|
if ((g_appinfo.mmap_flags and 1)<>0) and
|
|
(g_self_loading<>0) then
|
|
begin
|
|
size:=g_mode_2mb_size;
|
|
if (max_size2 < g_mode_2mb_size) then
|
|
begin
|
|
size:=max_size2;
|
|
end;
|
|
|
|
p_offset:=0;
|
|
if ((g_mode_2mb or 1)=3) then //M2MB_READONLY,M2MB_ENABLE
|
|
begin
|
|
p_offset:=size;
|
|
end;
|
|
|
|
p_memsz:=game_fmem_size + max_size1;
|
|
|
|
if (bigapp_max_fmem_size < (p_memsz - p_offset)) then
|
|
begin
|
|
Writeln(stderr,'vm_budget ENOMEM');
|
|
Exit(ENOMEM);
|
|
end;
|
|
|
|
if ((DWORD(g_mode_2mb) - 2) < 2) then
|
|
begin
|
|
p_memsz:=p_memsz - size;
|
|
size:=0;
|
|
end else
|
|
begin
|
|
if (g_mode_2mb=M2MB_DISABLE) then
|
|
begin
|
|
size:=0;
|
|
end else
|
|
begin
|
|
size:=max_size2;
|
|
if (g_mode_2mb<>M2MB_DEFAULT) then
|
|
begin
|
|
Writeln(stderr,'unknown 2mb mode');
|
|
Assert(false,'unknown 2mb mode');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if (bigapp_max_fmem_size < p_memsz) then
|
|
begin
|
|
p_memsz:=bigapp_max_fmem_size;
|
|
end;
|
|
|
|
set_bigapp_limits(p_memsz,size);
|
|
end;
|
|
|
|
if ((g_mode_2mb and $fffffffe)=2) then //M2MB_READONLY,M2MB_ENABLE
|
|
begin
|
|
size:=g_mode_2mb_rsrv;
|
|
if (size<=max_size2) then
|
|
begin
|
|
max_size2:=g_mode_2mb_rsrv;
|
|
end;
|
|
|
|
used :=vm_budget_used (PTYPE_BIG_APP,field_mlock);
|
|
limit:=vm_budget_limit(PTYPE_BIG_APP,field_mlock);
|
|
|
|
if (limit < (used + (max_size1 - max_size2))) then
|
|
begin
|
|
Writeln(stderr,'vm_budget ENOMEM');
|
|
Exit(ENOMEM);
|
|
end;
|
|
end;
|
|
|
|
wire:=ord(p_proc.p_budget_ptype=PTYPE_BIG_APP);
|
|
|
|
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:=phdr^.p_vaddr;
|
|
|
|
if (hdr^.e_type=ET_SCE_DYNEXEC) then
|
|
begin
|
|
p_vaddr:=p_vaddr + QWORD(imgp^.reloc_base);
|
|
end;
|
|
|
|
p_filesz:=phdr^.p_filesz;
|
|
p_offset:=phdr^.p_offset;
|
|
|
|
if (p_type=PT_SCE_RELRO) and (p_proc.p_budget_ptype=PTYPE_BIG_APP) then
|
|
begin
|
|
|
|
if (_2mb_mode=false) then
|
|
begin
|
|
used_mode_2m:=false;
|
|
end else
|
|
begin
|
|
used_mode_2m:=is_used_mode_2mb(phdr,0,0);
|
|
end;
|
|
|
|
Result:=self_load_section(imgp,
|
|
i,
|
|
p_vaddr,
|
|
p_offset,
|
|
p_memsz,
|
|
p_filesz,
|
|
p_flags,
|
|
wire,
|
|
used_mode_2m,
|
|
'executable',
|
|
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,0,p_proc.p_budget_ptype);
|
|
end;
|
|
|
|
Result:=self_load_section(imgp,
|
|
i,
|
|
p_vaddr,
|
|
p_offset,
|
|
p_memsz,
|
|
p_filesz,
|
|
p_flags,
|
|
wire,
|
|
used_mode_2m,
|
|
'executable',
|
|
cache);
|
|
end;
|
|
if (Result<>0) then
|
|
begin
|
|
FreeMem(cache);
|
|
Exit;
|
|
end;
|
|
|
|
addr:=(p_vaddr and QWORD(not PAGE_MASK));
|
|
size:=((p_vaddr and PAGE_MASK) + PAGE_MASK + p_memsz) and QWORD(not PAGE_MASK);
|
|
|
|
if (p_type=PT_SCE_RELRO) then
|
|
begin
|
|
relro_addr:=addr;
|
|
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;
|
|
end;
|
|
|
|
FreeMem(cache);
|
|
|
|
if (data_addr=0) and (data_size=0) then
|
|
begin
|
|
data_addr:=text_addr;
|
|
data_size:=text_size;
|
|
end;
|
|
|
|
if (data_size > lim_cur(RLIMIT_DATA)) or
|
|
(text_size > maxtsiz) or
|
|
(total_size > lim_cur(RLIMIT_VMEM)) then
|
|
begin
|
|
Exit(ENOMEM);
|
|
end;
|
|
|
|
Writeln(' text addr=0x',HexStr(text_addr ,16),'..',HexStr(text_addr +text_size ,16));
|
|
Writeln(' data addr=0x',HexStr(data_addr ,16),'..',HexStr(data_addr +data_size ,16));
|
|
Writeln(' relro addr=0x',HexStr(relro_addr,16),'..',HexStr(relro_addr+relro_size,16));
|
|
|
|
vms:=p_proc.p_vmspace;
|
|
|
|
vms^.vm_tsize:=text_size shr PAGE_SHIFT;
|
|
vms^.vm_taddr:=Pointer(text_addr);
|
|
vms^.vm_dsize:=data_size shr PAGE_SHIFT;
|
|
vms^.vm_daddr:=Pointer(data_addr);
|
|
|
|
imgp^.relro_addr:=Pointer(relro_addr);
|
|
imgp^.relro_size:=relro_size;
|
|
|
|
addr:=0;
|
|
if (imgp^.hdr_e_type<>hdr^.e_type) then
|
|
begin
|
|
//ET_SCE_EXEC hack
|
|
addr:=QWORD(imgp^.reloc_base);
|
|
end else
|
|
if (hdr^.e_type=ET_SCE_DYNEXEC) then
|
|
begin
|
|
addr:=text_addr;
|
|
end;
|
|
|
|
imgp^.entry_addr:=Pointer(addr + hdr^.e_entry);
|
|
|
|
Writeln(' entry_addr=0x',HexStr(imgp^.entry_addr));
|
|
|
|
//Do not update if the ET_SCE_EXEC hack is used
|
|
if (imgp^.hdr_e_type=hdr^.e_type) then
|
|
begin
|
|
imgp^.reloc_base:=Pointer(addr);
|
|
end;
|
|
|
|
auxargs:=AllocMem(SizeOf(t_elf64_auxargs));
|
|
|
|
auxargs^.execfd:=-1;
|
|
auxargs^.phdr :=0;
|
|
auxargs^.phent :=hdr^.e_phentsize;
|
|
auxargs^.phnum :=hdr^.e_phnum;
|
|
auxargs^.pagesz:=PAGE_SIZE;
|
|
auxargs^.base :=(QWORD(vms^.vm_daddr) + PAGE_MASK + lim_max(RLIMIT_DATA)) and QWORD(not PAGE_MASK);
|
|
auxargs^.flags :=0;
|
|
auxargs^.entry :=QWORD(imgp^.entry_addr);
|
|
|
|
p_proc.p_osrel:=$dbbcc;
|
|
|
|
imgp^.auxargs:=auxargs;
|
|
|
|
MoveChar0(imgp^.execpath^,p_proc.p_prog_name,1024);
|
|
|
|
if (relro_addr<>0) and (relro_size<>0) then
|
|
begin
|
|
Result:=vm_map_protect(@vms^.vm_map,relro_addr,relro_addr+relro_size,VM_PROT_READ,False);
|
|
Result:=vm_mmap_to_errno(Result);
|
|
end;
|
|
|
|
end;
|
|
|
|
function dynlib_proc_initialize_step1(imgp:p_image_params):Integer;
|
|
var
|
|
vms:p_vmspace;
|
|
obj:p_lib_info;
|
|
text_addr:Pointer;
|
|
eh_frame_addr:Pointer;
|
|
eh_frame_size:QWORD;
|
|
begin
|
|
Result:=0;
|
|
|
|
TAILQ_INIT(@dynlibs_info.list_global);
|
|
TAILQ_INIT(@dynlibs_info.list_main);
|
|
TAILQ_INIT(@dynlibs_info.init_list);
|
|
TAILQ_INIT(@dynlibs_info.fini_list);
|
|
TAILQ_INIT(@dynlibs_info.obj_list);
|
|
|
|
dynlibs_info.obj_count :=0;
|
|
dynlibs_info.tls_last_offset :=0;
|
|
dynlibs_info.tls_last_size :=0;
|
|
dynlibs_info.tls_static_space:=0;
|
|
dynlibs_info.tls_count :=1;
|
|
dynlibs_info.tls_max :=1;
|
|
|
|
obj:=obj_new();
|
|
|
|
obj^.relocbase:=imgp^.reloc_base;
|
|
|
|
vms:=p_proc.p_vmspace;
|
|
|
|
text_addr:=vms^.vm_taddr;
|
|
|
|
obj^.map_base:=text_addr;
|
|
|
|
obj^.text_size:=vms^.vm_tsize * PAGE_SIZE;
|
|
obj^.data_addr:=vms^.vm_daddr;
|
|
obj^.data_size:=vms^.vm_dsize * PAGE_SIZE;
|
|
|
|
obj^.map_size :=((QWORD(obj^.data_addr) + obj^.data_size + PAGE_MASK) and QWORD(not PAGE_MASK)) - QWORD(text_addr);
|
|
|
|
obj^.relro_addr:=imgp^.relro_addr;
|
|
obj^.relro_size:=imgp^.relro_size;
|
|
|
|
obj^.tls_index :=1;
|
|
obj^.tls_size :=imgp^.tls_size;
|
|
obj^.tls_align :=imgp^.tls_align;
|
|
obj^.tls_init_size:=imgp^.tls_init_size;
|
|
obj^.tls_init_addr:=imgp^.tls_init_addr;
|
|
|
|
eh_frame_addr:=nil;
|
|
eh_frame_size:=0;
|
|
|
|
if (elf64_get_eh_frame_info(imgp^.eh_frame_hdr_addr,
|
|
imgp^.eh_frame_hdr_size,
|
|
0,
|
|
obj^.text_size + QWORD(text_addr),
|
|
@eh_frame_addr,
|
|
@eh_frame_size)<>0) then
|
|
begin
|
|
eh_frame_addr:=nil;
|
|
eh_frame_size:=0;
|
|
end;
|
|
|
|
obj^.eh_frame_hdr_addr:=imgp^.eh_frame_hdr_addr;
|
|
obj^.eh_frame_hdr_size:=imgp^.eh_frame_hdr_size;
|
|
obj^.eh_frame_addr :=eh_frame_addr;
|
|
obj^.eh_frame_size :=eh_frame_size;
|
|
|
|
obj^.entry_addr :=imgp^.entry_addr;
|
|
|
|
obj_set_lib_path(obj,imgp^.execpath);
|
|
|
|
obj^.rtld_flags.mainprog:=1;
|
|
obj^.loaded:=4;
|
|
|
|
dynlibs_info.libprogram:=obj;
|
|
|
|
if (imgp^.dyn_exist=0) then
|
|
begin
|
|
dynlibs_info.dyn_non_exist:=1;
|
|
obj^.rel_data:=nil;
|
|
//
|
|
end else
|
|
begin
|
|
dynlibs_info.dyn_non_exist:=0;
|
|
|
|
Result:=acquire_per_file_info_obj(imgp,obj);
|
|
|
|
if (Result<>0) then
|
|
begin
|
|
obj_free(dynlibs_info.libprogram);
|
|
dynlibs_info.libprogram:=nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function dynlib_proc_initialize_step2(imgp:p_image_params):Integer;
|
|
var
|
|
obj,tail:p_lib_info;
|
|
init_proc_addr:Pointer;
|
|
fini_proc_addr:Pointer;
|
|
begin
|
|
Result:=0;
|
|
|
|
dynlibs_info.proc_param_addr:=imgp^.proc_param_addr;
|
|
dynlibs_info.proc_param_size:=imgp^.proc_param_size;
|
|
|
|
obj:=dynlibs_info.libprogram;
|
|
|
|
if (imgp^.dyn_exist=0) then
|
|
begin
|
|
dynlibs_add_obj(obj);
|
|
Exit;
|
|
end;
|
|
|
|
Result:=digest_dynamic(obj);
|
|
if (Result<>0) then
|
|
begin
|
|
Writeln(StdErr,'dynlib_proc_initialize_step2:','digest_dynamic()=',Result);
|
|
Exit;
|
|
end;
|
|
|
|
init_relo_bits(obj);
|
|
dynlibs_add_obj(obj);
|
|
|
|
dynlibs_info.sym_zero.st_info :=(STB_GLOBAL shl 4) or STT_NOTYPE;
|
|
dynlibs_info.sym_zero.st_shndx:=SHN_UNDEF;
|
|
dynlibs_info.sym_zero.st_value:=-Int64(obj^.relocbase);
|
|
|
|
init_proc_addr:=obj^.fini_proc_addr;
|
|
fini_proc_addr:=obj^.init_proc_addr;
|
|
|
|
obj^.fini_proc_addr:=nil;
|
|
obj^.init_proc_addr:=nil;
|
|
|
|
tail:=TAILQ_LAST(@dynlibs_info.obj_list);
|
|
if (tail=nil) then
|
|
begin
|
|
tail:=dynlibs_info.obj_list.tqh_first;
|
|
end;
|
|
|
|
initlist_add_objects(dynlibs_info.fini_proc_list,
|
|
dynlibs_info.obj_list.tqh_first,
|
|
tail,
|
|
dynlibs_info.init_proc_list);
|
|
|
|
obj^.init_proc_addr:=init_proc_addr;
|
|
obj^.fini_proc_addr:=fini_proc_addr;
|
|
|
|
///
|
|
end;
|
|
|
|
function dynlib_copy_executable_sdk_version():Integer;
|
|
var
|
|
proc_param:pSceProcParam;
|
|
begin
|
|
proc_param:=dynlibs_info.proc_param_addr;
|
|
//
|
|
if (proc_param=nil) then
|
|
begin
|
|
p_proc.p_sdk_version:=0;
|
|
Result:=0;
|
|
end else
|
|
begin
|
|
Result:=copyin(@proc_param^.SDK_version,@p_proc.p_sdk_version,SizeOf(Integer));
|
|
end;
|
|
end;
|
|
|
|
procedure dynlib_proc_initialize_step3(imgp:p_image_params);
|
|
label
|
|
_dyn_not_exist;
|
|
var
|
|
obj:p_lib_info;
|
|
str:RawByteString;
|
|
err:Integer;
|
|
flags:DWORD;
|
|
begin
|
|
err:=0;
|
|
|
|
obj:=nil;
|
|
|
|
//if (td_proc->sdk_version < 0x5000000) {
|
|
// *(byte *)&dynlibs_info->bits = *(byte *)&dynlibs_info->bits | 0x20; (find_symdef mode)
|
|
//}
|
|
|
|
if (imgp^.dyn_exist=0) then goto _dyn_not_exist;
|
|
|
|
flags:=$40; //priv libs?
|
|
if (p_proc.p_budget_ptype=PTYPE_BIG_APP) then flags:=flags or $20; //vm_map_wire
|
|
|
|
pick_obj(dynlibs_info.libprogram);
|
|
|
|
str:='/libkernel.sprx';
|
|
obj:=preload_prx_modules(pchar(str),flags,err);
|
|
|
|
dynlibs_info.libkernel:=obj;
|
|
|
|
if (obj=nil) then
|
|
begin
|
|
Writeln(StdErr,'preload_prx_modules:',str,' not loaded');
|
|
end;
|
|
|
|
str:='/libSceLibcInternal.sprx';
|
|
obj:=preload_prx_modules(pchar(str),flags,err);
|
|
|
|
if (obj=nil) then
|
|
begin
|
|
Writeln(StdErr,'preload_prx_modules:',str,' not loaded');
|
|
end;
|
|
|
|
obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
|
|
while (obj<>nil) do
|
|
begin
|
|
objlist_push_tail(dynlibs_info.list_main,obj);
|
|
|
|
Inc(obj^.ref_count);
|
|
|
|
objlist_push_tail(obj^.dagmembers,obj);
|
|
objlist_push_tail(obj^.dldags ,obj);
|
|
|
|
//
|
|
obj:=TAILQ_NEXT(obj,@obj^.link);
|
|
end;
|
|
|
|
dynlibs_info.rep_unpf:=do_dlsym(dynlibs_info.libkernel,'sceKernelReportUnpatchedFunctionCall',nil,0);
|
|
dynlibs_info.__freeze:=do_dlsym(dynlibs_info.libkernel,'__freeze','libkernel_sysc_se', 0);
|
|
dynlibs_info.sysc_s00:=do_dlsym(dynlibs_info.libkernel,'sysc_s00','libkernel_sysc_se', 0);
|
|
dynlibs_info.sysc_e00:=do_dlsym(dynlibs_info.libkernel,'sysc_e00','libkernel_sysc_se', 0);
|
|
|
|
obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
|
|
while (obj<>nil) do
|
|
begin
|
|
dynlib_initialize_pltgot_each(obj);
|
|
//
|
|
obj:=TAILQ_NEXT(obj,@obj^.link);
|
|
end;
|
|
|
|
if (dynlibs_info.libkernel=nil) then
|
|
begin
|
|
//error
|
|
Exit;
|
|
end;
|
|
|
|
imgp^.entry_addr:=dynlibs_info.libkernel^.entry_addr;
|
|
|
|
p_proc.p_libkernel_start_addr:=dynlibs_info.libkernel^.map_base;
|
|
p_proc.p_libkernel___end_addr:=dynlibs_info.libkernel^.map_base + dynlibs_info.libkernel^.text_size;
|
|
|
|
_dyn_not_exist:
|
|
|
|
obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
|
|
while (obj<>nil) do
|
|
begin
|
|
if not alloc_obj_id(obj) then
|
|
begin
|
|
Assert(False,'ID for PRX cannot be assigned.');
|
|
end;
|
|
//
|
|
obj:=TAILQ_NEXT(obj,@obj^.link);
|
|
end;
|
|
|
|
end;
|
|
|
|
function exec_self_imgact(imgp:p_image_params):Integer;
|
|
var
|
|
hdr :p_elf64_hdr;
|
|
phdr:p_elf64_phdr;
|
|
reloc_base:Pointer;
|
|
begin
|
|
Result:=0;
|
|
|
|
if (imgp=nil) then Exit(EINVAL);
|
|
|
|
hdr:=imgp^.image_header;
|
|
|
|
if (hdr=nil) then Exit(EINVAL);
|
|
|
|
Case hdr^.e_type of
|
|
ET_SCE_EXEC ,
|
|
ET_SCE_REPLAY_EXEC,
|
|
ET_SCE_DYNEXEC :
|
|
else
|
|
begin
|
|
Writeln(StdErr,'exec_self_imgact:',imgp^.execpath,' unspported e_type:0x',HexStr(hdr^.e_type,4));
|
|
Exit(ENOEXEC);
|
|
end;
|
|
end;
|
|
|
|
if (hdr^.e_machine<>EM_X86_64) then
|
|
begin
|
|
Writeln(StdErr,'exec_self_imgact:',imgp^.execpath,' unspported e_machine:',hdr^.e_machine);
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
imgp^.hdr_e_type:=hdr^.e_type;
|
|
|
|
phdr:=get_elf_phdr(hdr);
|
|
|
|
Result:=scan_phdr(imgp,phdr,hdr^.e_phnum);
|
|
if (Result<>0) then
|
|
begin
|
|
Writeln(StdErr,'exec_self_imgact:','found illegal segment header in ',imgp^.execpath);
|
|
Exit;
|
|
end;
|
|
|
|
if (imgp^.dyn_exist=0) and (hdr^.e_type=ET_SCE_DYNEXEC) then
|
|
begin
|
|
Writeln(StdErr,'exec_self_imgact:','illegal ELF file image',imgp^.execpath);
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
rtld_load_auth(imgp);
|
|
|
|
case hdr^.e_type of
|
|
ET_EXEC:
|
|
begin
|
|
Exit(ENOEXEC);
|
|
end;
|
|
|
|
ET_SCE_REPLAY_EXEC:
|
|
begin
|
|
g_appinfo.mmap_flags:=g_appinfo.mmap_flags or 2; //is_system ???
|
|
end;
|
|
|
|
ET_SCE_DYNEXEC:
|
|
begin
|
|
reloc_base:=Pointer(PROC_IMAGE_AREA_START);
|
|
//
|
|
imgp^.reloc_base :=reloc_base;
|
|
imgp^.dyn_vaddr :=reloc_base+QWORD(imgp^.dyn_vaddr );
|
|
imgp^.tls_init_addr :=reloc_base+QWORD(imgp^.tls_init_addr );
|
|
imgp^.eh_frame_hdr_addr:=reloc_base+QWORD(imgp^.eh_frame_hdr_addr);
|
|
imgp^.proc_param_addr :=reloc_base+QWORD(imgp^.proc_param_addr );
|
|
end;
|
|
|
|
ET_SCE_EXEC:
|
|
if (PROC_IMAGE_AREA_START=_PROC_AREA_START_1) then
|
|
begin
|
|
//hack
|
|
hdr^.e_type:=ET_SCE_DYNEXEC;
|
|
//
|
|
reloc_base:=Pointer(_PROC_AREA_START_1);
|
|
//
|
|
imgp^.reloc_base :=reloc_base;
|
|
imgp^.dyn_vaddr :=reloc_base+QWORD(imgp^.dyn_vaddr );
|
|
imgp^.tls_init_addr :=reloc_base+QWORD(imgp^.tls_init_addr );
|
|
imgp^.eh_frame_hdr_addr:=reloc_base+QWORD(imgp^.eh_frame_hdr_addr);
|
|
imgp^.proc_param_addr :=reloc_base+QWORD(imgp^.proc_param_addr );
|
|
end;
|
|
|
|
else;
|
|
end;
|
|
|
|
if (imgp^.dyn_exist=0) then
|
|
begin
|
|
//
|
|
end else
|
|
begin
|
|
Result:=scan_dyn_offset(imgp,phdr,hdr^.e_phnum);
|
|
if (Result<>0) then Exit;
|
|
end;
|
|
|
|
VOP_UNLOCK(imgp^.vp, 0);
|
|
|
|
Result:=exec_new_vmspace(imgp);
|
|
|
|
vn_lock(imgp^.vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
if (Result<>0) then Exit;
|
|
|
|
imgp^.obj:=vm_pager_allocate(OBJT_SELF,
|
|
imgp^.vp,
|
|
imgp^.max_addr-imgp^.min_addr,
|
|
1,
|
|
0);
|
|
|
|
vm_object_set_budget(imgp^.obj,p_proc.p_budget_ptype);
|
|
|
|
Result:=scan_load_sections(imgp,phdr,hdr^.e_phnum);
|
|
if (Result<>0) then
|
|
begin
|
|
Exit;
|
|
end;
|
|
|
|
Result:=dynlib_proc_initialize_step1(imgp);
|
|
if (Result<>0) then
|
|
begin
|
|
Writeln(StdErr,'exec_self_imgact:',imgp^.execpath,' dynlib_proc_initialize_step1:',Result);
|
|
Exit;
|
|
end;
|
|
|
|
Result:=dynlib_proc_initialize_step2(imgp);
|
|
if (Result<>0) then
|
|
begin
|
|
Writeln(StdErr,'exec_self_imgact:',imgp^.execpath,' dynlib_proc_initialize_step1:',Result);
|
|
Exit;
|
|
end;
|
|
|
|
Result:=dynlib_copy_executable_sdk_version();
|
|
if (Result<>0) then
|
|
begin
|
|
Writeln(StdErr,'exec_self_imgact:','sdk version is not found in ',imgp^.execpath);
|
|
Exit;
|
|
end;
|
|
|
|
if (p_proc.p_vm_container=1) then
|
|
begin
|
|
vm_map_lock (p_proc.p_vmspace);
|
|
vm_map_modflags(p_proc.p_vmspace,MAP_WIREFUTURE or 4,0);
|
|
vm_map_unlock (p_proc.p_vmspace);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure AUXARGS_ENTRY(var pos:PPointer;id,val:QWORD); inline;
|
|
begin
|
|
suword(pos^, Pointer(id )); Inc(pos);
|
|
suword(pos^, Pointer(val)); Inc(pos);
|
|
end;
|
|
|
|
procedure AUXARGS_ENTRY(var pos:PPointer;id:QWORD;val:Pointer); inline;
|
|
begin
|
|
suword(pos^, Pointer(id )); Inc(pos);
|
|
suword(pos^, Pointer(val)); Inc(pos);
|
|
end;
|
|
|
|
function self_orbis_fixup(stack_base:PPointer;imgp:p_image_params):Integer;
|
|
const
|
|
osreldate=$000DBBA0;
|
|
mp_ncpus=8;
|
|
var
|
|
args:p_elf64_auxargs;
|
|
base:PPointer;
|
|
pos :PPointer;
|
|
begin
|
|
args:=imgp^.auxargs;
|
|
|
|
base:=stack_base^;
|
|
pos:=base + (imgp^.args^.argc + imgp^.args^.envc + 2);
|
|
|
|
if (args^.execfd<>-1) then
|
|
begin
|
|
AUXARGS_ENTRY(pos, AT_EXECFD, args^.execfd);
|
|
end;
|
|
|
|
AUXARGS_ENTRY(pos, AT_PHDR , args^.phdr );
|
|
AUXARGS_ENTRY(pos, AT_PHENT , args^.phent );
|
|
AUXARGS_ENTRY(pos, AT_PHNUM , args^.phnum );
|
|
AUXARGS_ENTRY(pos, AT_PAGESZ, args^.pagesz);
|
|
AUXARGS_ENTRY(pos, AT_FLAGS , args^.flags );
|
|
AUXARGS_ENTRY(pos, AT_ENTRY , args^.entry );
|
|
AUXARGS_ENTRY(pos, AT_BASE , args^.base );
|
|
|
|
if (imgp^.execpathp<>nil) then
|
|
begin
|
|
AUXARGS_ENTRY(pos, AT_EXECPATH, imgp^.execpathp);
|
|
end;
|
|
|
|
AUXARGS_ENTRY(pos, AT_OSRELDATE, osreldate);
|
|
|
|
if (imgp^.canary<>nil) then
|
|
begin
|
|
AUXARGS_ENTRY(pos, AT_CANARY , imgp^.canary );
|
|
AUXARGS_ENTRY(pos, AT_CANARYLEN, imgp^.canarylen);
|
|
end;
|
|
|
|
AUXARGS_ENTRY(pos, AT_NCPUS, mp_ncpus);
|
|
|
|
if (imgp^.pagesizes<>nil) then
|
|
begin
|
|
AUXARGS_ENTRY(pos, AT_PAGESIZES , imgp^.pagesizes );
|
|
AUXARGS_ENTRY(pos, AT_PAGESIZESLEN, imgp^.pagesizeslen);
|
|
end;
|
|
|
|
AUXARGS_ENTRY(pos, AT_STACKPROT, VM_PROT_RW);
|
|
|
|
AUXARGS_ENTRY(pos, AT_NULL, 0);
|
|
|
|
FreeMem(imgp^.auxargs);
|
|
imgp^.auxargs:=nil;
|
|
|
|
Dec(base);
|
|
suword(base^, Pointer(QWORD(imgp^.args^.argc)));
|
|
|
|
stack_base^:=base;
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
procedure init_tty; //TODO before execve
|
|
begin
|
|
kern_openat(STDIN_FILENO ,'/dev/deci_stdin' ,UIO_SYSSPACE,O_RDWR,0);
|
|
kern_openat(STDOUT_FILENO,'/dev/deci_stdout',UIO_SYSSPACE,O_RDWR,0);
|
|
kern_openat(STDERR_FILENO,'/dev/deci_stderr',UIO_SYSSPACE,O_RDWR,0);
|
|
kern_openat(3 ,'/dev/null' ,UIO_SYSSPACE,O_RDWR,0); //sys_rdup(pid,fd)
|
|
end;
|
|
|
|
const
|
|
fexecv_proc_title='(fexecv)';
|
|
|
|
function do_execve(td:p_kthread;args:p_image_args;mode:Integer):Integer;
|
|
label
|
|
_fullpath,
|
|
exec_fail,
|
|
exec_fail_dealloc,
|
|
done2;
|
|
var
|
|
nd:t_nameidata;
|
|
stack_base:Pointer;
|
|
i,error:Integer;
|
|
image_params:t_image_params;
|
|
imgp:p_image_params;
|
|
attr:t_vattr;
|
|
|
|
textvp,binvp:p_vnode;
|
|
|
|
vfslocked:Integer;
|
|
tvfslocked:Integer;
|
|
|
|
newargs,oldargs:p_pargs;
|
|
|
|
vms:p_vmspace;
|
|
begin
|
|
|
|
textvp:=nil;
|
|
binvp :=nil;
|
|
|
|
vfslocked:=0;
|
|
imgp:=@image_params;
|
|
image_params:=Default(t_image_params);
|
|
|
|
if (p_proc.p_budget_ptype=PTYPE_BIG_APP) then
|
|
if ((g_appinfo.mmap_flags and 1)<>0) then
|
|
begin
|
|
if (g_self_loading<>0) then
|
|
begin
|
|
init_bigapp_limits;
|
|
end;
|
|
end;
|
|
|
|
p_proc.p_flag:=p_proc.p_flag or P_INEXEC;
|
|
|
|
{
|
|
* Initialize part of the common data
|
|
}
|
|
attr:=Default(t_vattr);
|
|
imgp^.attr:=@attr;
|
|
imgp^.args:=args;
|
|
|
|
{
|
|
* Translate the file name. namei() returns a vnode pointer
|
|
* in ni_vp amoung other things.
|
|
*
|
|
* XXXAUDIT: It would be desirable to also audit the name of the
|
|
* interpreter if this is an interpreted binary.
|
|
}
|
|
if (args^.fname<>nil) then
|
|
begin
|
|
NDINIT(@nd, LOOKUP, ISOPEN or LOCKLEAF or FOLLOW or SAVENAME or MPSAFE or AUDITVNODE1, UIO_SYSSPACE, args^.fname, td);
|
|
end;
|
|
|
|
if (args^.fname<>nil) then
|
|
begin
|
|
|
|
error:=nd_namei(@nd);
|
|
if (error<>0) then goto exec_fail;
|
|
|
|
vfslocked:=NDHASGIANT(@nd);
|
|
binvp:=nd.ni_vp;
|
|
imgp^.vp:=binvp;
|
|
end else
|
|
begin
|
|
{
|
|
* Some might argue that CAP_READ and/or CAP_MMAP should also
|
|
* be required here; such arguments will be entertained.
|
|
*
|
|
* Descriptors opened only with O_EXEC or O_RDONLY are allowed.
|
|
}
|
|
error:=fgetvp_exec(args^.fd, CAP_FEXECVE, @binvp);
|
|
if (error<>0) then goto exec_fail;
|
|
|
|
vfslocked:=VFS_LOCK_GIANT(binvp^.v_mount);
|
|
vn_lock(binvp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
imgp^.vp:=binvp;
|
|
end;
|
|
|
|
{
|
|
* Check file permissions (also 'opens' file)
|
|
}
|
|
error:=exec_check_permissions(imgp);
|
|
if (error<>0) then goto exec_fail_dealloc;
|
|
|
|
//imgp^.obj:=imgp^.vp^.v_object;
|
|
//if (imgp^.obj<>nil) then
|
|
//begin
|
|
// vm_object_reference(imgp^.obj);
|
|
//end;
|
|
|
|
error:=rtld_load_self(imgp);
|
|
if (error<>0) then goto exec_fail_dealloc;
|
|
|
|
p_proc.p_osrel:=0;
|
|
|
|
if (args^.fname=nil) then
|
|
begin
|
|
_fullpath:
|
|
|
|
VOP_UNLOCK(imgp^.vp, 0);
|
|
|
|
i:=vn_fullpath(imgp^.vp,@imgp^.execpath,@imgp^.freepath);
|
|
if (i<>0) then
|
|
begin
|
|
imgp^.execpath:=args^.fname;
|
|
end;
|
|
|
|
vn_lock(imgp^.vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
end else
|
|
if (args^.fname^<>'/') then
|
|
begin
|
|
goto _fullpath;
|
|
end else
|
|
begin
|
|
imgp^.execpath:=args^.fname;
|
|
end;
|
|
|
|
error:=exec_self_imgact(imgp);
|
|
if (error<>0) then goto exec_fail_dealloc;
|
|
|
|
{
|
|
* NB: We unlock the vnode here because it is believed that none
|
|
* of the sv_copyout_strings/sv_fixup operations require the vnode.
|
|
}
|
|
VOP_UNLOCK(imgp^.vp, 0);
|
|
|
|
{
|
|
* Do the best to calculate the full path to the image file.
|
|
}
|
|
if (imgp^.auxargs<>nil) and
|
|
(
|
|
((args^.fname<>nil) and (args^.fname[0]='/')) or
|
|
(vn_fullpath(imgp^.vp, @imgp^.execpath, @imgp^.freepath)<>0)
|
|
) then
|
|
begin
|
|
imgp^.execpath:=args^.fname;
|
|
end;
|
|
|
|
{
|
|
* Copy out strings (args and env) and initialize stack base
|
|
}
|
|
stack_base:=exec_copyout_strings(imgp);
|
|
|
|
{
|
|
* If custom stack fixup routine present for this process
|
|
* let it do the stack setup.
|
|
* Else stuff argument count as first item on stack
|
|
}
|
|
|
|
self_orbis_fixup(@stack_base,imgp);
|
|
|
|
{
|
|
* Malloc things before we need locks.
|
|
}
|
|
i:=imgp^.args^.begin_envv - imgp^.args^.begin_argv;
|
|
{ Cache arguments if they fit inside our allowance }
|
|
if (ps_arg_cache_limit >= (i + sizeof(t_pargs))) then
|
|
begin
|
|
newargs:=pargs_alloc(i);
|
|
Move(imgp^.args^.begin_argv^,newargs^.ar_args,i);
|
|
end;
|
|
|
|
{ close files on exec }
|
|
fdcloseexec();
|
|
|
|
vn_lock(imgp^.vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
{ Get a reference to the vnode prior to locking the proc }
|
|
VREF(binvp);
|
|
|
|
{
|
|
* For security and other reasons, signal handlers cannot
|
|
* be shared after an exec. The new process gets a copy of the old
|
|
* handlers. In execsigs(), the new process will have its signals
|
|
* reset.
|
|
}
|
|
PROC_LOCK();
|
|
|
|
if (args^.fname<>nil) then
|
|
begin
|
|
Move(nd.ni_cnd.cn_nameptr^, p_proc.p_comm, maxInt64(nd.ni_cnd.cn_namelen, MAXCOMLEN));
|
|
end else
|
|
begin
|
|
Move(fexecv_proc_title, p_proc.p_comm, sizeof(fexecv_proc_title));
|
|
end;
|
|
|
|
Move(p_proc.p_comm, td^.td_name, sizeof(td^.td_name));
|
|
KernSetThreadDebugName(td,'ps4:');
|
|
|
|
PROC_UNLOCK();
|
|
|
|
{
|
|
* Free any previous argument cache and replace it with
|
|
* the new argument cache, if any.
|
|
}
|
|
oldargs:=p_proc.p_args;
|
|
p_proc.p_args:=newargs;
|
|
newargs:=nil;
|
|
|
|
dynlib_proc_initialize_step3(imgp);
|
|
|
|
if (dynlibs_info.libkernel=nil) then
|
|
begin
|
|
error:=ENOENT;
|
|
goto exec_fail_dealloc;
|
|
end;
|
|
|
|
PROC_LOCK();
|
|
|
|
{
|
|
* mark as execed, wakeup the process that vforked (if any) and tell
|
|
* it that it now has its own resources back
|
|
}
|
|
p_proc.p_flag:=p_proc.p_flag or P_EXEC;
|
|
|
|
{
|
|
* Notify others that we exec'd, and clear the P_INEXEC flag
|
|
* as we're now a bona fide freshly-execed process.
|
|
}
|
|
KNOTE_LOCKED(@p_proc.p_klist, NOTE_EXEC);
|
|
p_proc.p_flag:=p_proc.p_flag and (not P_INEXEC);
|
|
|
|
{ clear 'fork but no exec' flag, as we _are_ execing }
|
|
//p^.p_acflag:= and ~AFORK;
|
|
|
|
PROC_UNLOCK();
|
|
|
|
vms:=p_proc.p_vmspace;
|
|
|
|
{ Set values passed into the program in registers. }
|
|
exec_setregs(td, QWORD(imgp^.entry_addr), QWORD(stack_base), QWORD(vms^.sv_usrstack));
|
|
|
|
vfs_mark_atime(imgp^.vp);
|
|
|
|
//copy authinfo
|
|
g_authinfo:=imgp^.authinfo;
|
|
|
|
//copy appinfo (TODO before execve)
|
|
g_appinfo.AppId:=$60000100;
|
|
|
|
g_appinfo.mmap_flags:=g_appinfo.mmap_flags or 1; //is_big_app ???
|
|
|
|
if (p_proc.p_budget_ptype=PTYPE_BIG_APP) then
|
|
if ((g_appinfo.mmap_flags and 1)<>0) then
|
|
begin
|
|
set_bigapp_cred_limits;
|
|
end;
|
|
|
|
//init std tty (TODO before execve)
|
|
init_tty;
|
|
|
|
{
|
|
* Free any resources malloc'd earlier that we didn't use.
|
|
}
|
|
|
|
VOP_UNLOCK(imgp^.vp, 0);
|
|
|
|
{
|
|
* Handle deferred decrement of ref counts.
|
|
}
|
|
if (textvp<>nil) then
|
|
begin
|
|
tvfslocked:=VFS_LOCK_GIANT(textvp^.v_mount);
|
|
vrele(textvp);
|
|
VFS_UNLOCK_GIANT(tvfslocked);
|
|
end;
|
|
if (binvp<>nil) and (error<>0) then
|
|
begin
|
|
vrele(binvp);
|
|
end;
|
|
|
|
vn_lock(imgp^.vp, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
pargs_drop(oldargs);
|
|
pargs_drop(newargs);
|
|
|
|
exec_fail_dealloc:
|
|
|
|
{
|
|
* free various allocated resources
|
|
}
|
|
rtld_free_self(imgp);
|
|
|
|
if (imgp^.vp<>nil) then
|
|
begin
|
|
if (args^.fname<>nil) then
|
|
begin
|
|
NDFREE(@nd, NDF_ONLY_PNBUF);
|
|
end;
|
|
|
|
if (imgp^.opened<>0) then
|
|
begin
|
|
VOP_CLOSE(imgp^.vp, FREAD);
|
|
end;
|
|
|
|
vput(imgp^.vp);
|
|
end;
|
|
|
|
vm_object_deallocate(imgp^.obj);
|
|
|
|
FreeMem(imgp^.freepath);
|
|
|
|
if (error=0) then
|
|
begin
|
|
//PROC_LOCK();
|
|
//td^.td_dbgflags:= or TDB_EXEC;
|
|
//PROC_UNLOCK();
|
|
|
|
{
|
|
* Stop the process here if its stop event mask has
|
|
* the S_EXEC bit set.
|
|
}
|
|
//STOPEVENT(p, S_EXEC, 0);
|
|
goto done2;
|
|
end;
|
|
|
|
exec_fail:
|
|
{ we're done here, clear P_INEXEC }
|
|
PROC_LOCK();
|
|
p_proc.p_flag:=p_proc.p_flag and (not P_INEXEC);
|
|
PROC_UNLOCK();
|
|
|
|
done2:
|
|
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
|
|
|
if (error<>0) and (mode=0) then
|
|
begin
|
|
print_error_td('error execve "'+args^.fname+'" code='+IntToStr(error),True);
|
|
|
|
exec_free_args(args);
|
|
|
|
// sorry, no more process anymore. exit gracefully
|
|
exit1(W_EXITCODE(0, SIGABRT));
|
|
// NOT REACHED
|
|
end;
|
|
|
|
exec_free_args(args);
|
|
|
|
Exit(error);
|
|
end;
|
|
|
|
function kern_execve(td:p_kthread;args:p_image_args;mode:Integer=0):Integer;
|
|
var
|
|
error:Integer;
|
|
begin
|
|
if (td=nil) then Exit(-1);
|
|
|
|
Assert((td^.td_pflags and TDP_EXECVMSPC)=0,'nested execve');
|
|
|
|
error:=do_execve(td, args, mode);
|
|
|
|
if ((td^.td_pflags and TDP_EXECVMSPC)<>0) then
|
|
begin
|
|
td^.td_pflags:=td^.td_pflags and (not TDP_EXECVMSPC);
|
|
end;
|
|
|
|
Exit(error);
|
|
end;
|
|
|
|
function main_execve(fname:pchar;argv,envv:ppchar):Integer;
|
|
var
|
|
error:Integer;
|
|
args:t_image_args;
|
|
td:p_kthread;
|
|
begin
|
|
error:=exec_copyin_args(@args, fname, UIO_SYSSPACE, argv, envv);
|
|
|
|
if (error=0) then
|
|
begin
|
|
error:=kern_execve(curkthread, @args, 1);
|
|
end;
|
|
|
|
Result:=(error);
|
|
|
|
if (Result=0) then
|
|
begin
|
|
//unset sys mark
|
|
td:=curkthread;
|
|
td^.td_pflags:=td^.td_pflags and (not TDP_KTHREAD);
|
|
//unset sys mark
|
|
|
|
//force JIT mode
|
|
set_pcb_flags(curkthread,PCB_IS_JIT);
|
|
//force JIT mode
|
|
|
|
//---> main_switch_context;
|
|
end;
|
|
end;
|
|
|
|
procedure main_switch_context;
|
|
begin
|
|
ipi_sigreturn;
|
|
Writeln(stderr,'I''m a teapot!');
|
|
end;
|
|
|
|
function sys_execve(fname:pchar;argv,envv:ppchar):Integer;
|
|
var
|
|
error:Integer;
|
|
args:t_image_args;
|
|
begin
|
|
error:=exec_copyin_args(@args, fname, UIO_USERSPACE, argv, envv);
|
|
|
|
if (error=0) then
|
|
begin
|
|
error:=kern_execve(curkthread, @args);
|
|
end;
|
|
|
|
Result:=(error);
|
|
end;
|
|
|
|
end.
|
|
|