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):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):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) then
 begin
  print_error_td('error execve "'+args^.fname+'" code='+IntToStr(error));

  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):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);

 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);
 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.

