1 /* $Id: spawn.c,v 1.4 2002/03/11 20:46:38 hyperion Exp $
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS POSIX+ Subsystem
6 * FILE: subsys/psx/lib/psxdll/misc/spawn.c
7 * PURPOSE: Create the first POSIX+ process
8 * PROGRAMMER: KJK::Hyperion <noog@libero.it>
14 * NOTE by KJK::Hyperion:
15 * The __PdxSpawnPosixProcess() call solves the chicken-egg dilemma of
16 * creating the first POSIX+ process in a group without the ability to
17 * fork+exec (for example from inside a Win32 process). Processes created by
18 * __PdxSpawnPosixProcess() will *not* inherit anything from the parent, not
19 * even handles: all creation parameters have to be specified explicitely
22 #include <ddk/ntddk.h>
23 #include <ntdll/base.h>
24 #include <napi/i386/segment.h>
25 #include <ntdll/rtl.h>
26 #include <ntdll/ldr.h>
31 #include <psx/debug.h>
32 #include <psx/pdata.h>
33 #include <psx/spawn.h>
34 #include <psx/stdlib.h>
36 NTSTATUS STDCALL __PdxSpawnPosixProcess
38 OUT PHANDLE ProcessHandle
,
39 OUT PHANDLE ThreadHandle
,
40 IN POBJECT_ATTRIBUTES FileObjectAttributes
,
41 IN POBJECT_ATTRIBUTES ProcessObjectAttributes
,
42 IN HANDLE InheritFromProcessHandle
,
43 IN __PPDX_PDATA ProcessData
46 __PPDX_SERIALIZED_PDATA pspdProcessData
;
47 IO_STATUS_BLOCK isbStatus
;
48 PROCESS_BASIC_INFORMATION pbiProcessInfo
;
49 ANSI_STRING strStartEntry
;
51 INITIAL_TEB itInitialTeb
;
52 PRTL_USER_PROCESS_PARAMETERS pppProcessParameters
;
53 CONTEXT ctxThreadContext
;
60 PVOID pPdataBuffer
= 0;
61 PVOID pParamsBuffer
= 0;
62 ULONG nDestBufferSize
;
63 ULONG nCurFilDesOffset
;
70 /* STEP 1: map executable image in memory */
71 /* 1.1: open the file for execution */
75 SYNCHRONIZE
| FILE_EXECUTE
,
78 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
79 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
83 if(!NT_SUCCESS(nErrCode
))
85 ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode
);
89 /* 1.2: create an image section for the file */
90 nErrCode
= NtCreateSection
101 /* close file handle (not needed anymore) */
105 if(!NT_SUCCESS(nErrCode
))
107 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode
);
111 /* STEP 2: create process */
112 nErrCode
= NtCreateProcess
116 ProcessObjectAttributes
,
117 InheritFromProcessHandle
,
124 /* close image handle (not needed anymore) */
128 if(!NT_SUCCESS(nErrCode
))
130 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode
);
134 /* STEP 3: write process environment and process parameters */
135 /* 3.1: convert process data into process parameters */
136 __PdxProcessDataToProcessParameters
138 &pppProcessParameters
,
140 FileObjectAttributes
->ObjectName
143 /* 3.2: serialize the process data for transfer */
144 /* FIXME: the serialized data can be allocated and written directly in the
145 destination process */
146 __PdxSerializeProcessData(ProcessData
, &pspdProcessData
);
148 /* 3.2.1: adjust some fields */
149 pspdProcessData
->ProcessData
.Spawned
= TRUE
;
151 /* 3.3: allocate memory in the destination process */
152 /* 3.3.1: process data */
153 nDestBufferSize
= pspdProcessData
->AllocSize
;
155 nErrCode
= NtAllocateVirtualMemory
166 if(!NT_SUCCESS(nErrCode
))
168 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
172 /* 3.3.2: process parameters */
173 nDestBufferSize
= pppProcessParameters
->Length
;
175 nErrCode
= NtAllocateVirtualMemory
186 if(!NT_SUCCESS(nErrCode
))
188 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
192 /* 3.4: get pointer to the PEB */
193 nErrCode
= NtQueryInformationProcess
196 ProcessBasicInformation
,
198 sizeof(pbiProcessInfo
),
203 if(!NT_SUCCESS(nErrCode
))
205 ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode
);
209 /* 3.5: write pointers in the PEB */
210 /* 3.5.1: process data */
211 nErrCode
= NtWriteVirtualMemory
214 (PVOID
)((ULONG
)pbiProcessInfo
.PebBaseAddress
+ offsetof(PEB
, SubSystemData
)),
221 if(!NT_SUCCESS(nErrCode
))
223 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode
);
227 /* 3.5.2: process parameters */
228 nErrCode
= NtWriteVirtualMemory
231 (PVOID
)((ULONG
)pbiProcessInfo
.PebBaseAddress
+ offsetof(PEB
, ProcessParameters
)),
238 if(!NT_SUCCESS(nErrCode
))
240 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode
);
244 /* 3.6: write the process data */
245 nErrCode
= NtWriteVirtualMemory
250 pspdProcessData
->AllocSize
,
255 /* deallocate the temporary data block in the current process */
259 (PVOID
*)&pspdProcessData
,
264 /* destroy process parameters */
265 RtlDestroyProcessParameters(pppProcessParameters
);
268 if(!NT_SUCCESS(nErrCode
))
271 /* STEP 4: duplicate handles */
272 /* 4.1: handles in the structure itself */
273 /* 4.1.1: root directory */
274 nErrCode
= NtDuplicateObject
277 ProcessData
->RootHandle
,
279 (PHANDLE
)((ULONG
)pPdataBuffer
+ offsetof(__PDX_PDATA
, RootHandle
)),
282 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
286 if(!NT_SUCCESS(nErrCode
))
288 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
292 /* 4.2: file descriptors table */
295 /* pspdProcessData->ProcessData.FdTable.Descriptors contains the offset to
296 the descriptors array inside pspdProcessData->Buffer[], that is to the
297 first element of the array */
299 nCurFilDesOffset
= (ULONG
)pspdProcessData
->ProcessData
.FdTable
.Descriptors
;
300 /* iterate through all allocated descriptors */
301 i
< ProcessData
->FdTable
.AllocatedDescriptors
;
302 /* at every step, go on to next input descriptor, and increase the offset to
303 the next output descriptor */
304 i
++, nCurFilDesOffset
+= sizeof(__fildes_t
)
306 /* FIXME? check the table's bitmap instead? */
307 if(ProcessData
->FdTable
.Descriptors
[i
].FileHandle
!= NULL
)
309 /* duplicate the source handle, ProcessData->FdTable.Descriptors[i], from
310 the current process into the process identified by hProcess, at an
311 address calculated by adding to the serialized data block base address:
312 - the offset to the current descriptor
313 - the offset to the handle field of the descriptor */
314 nErrCode
= NtDuplicateObject
317 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
320 (ULONG
)pPdataBuffer
+ nCurFilDesOffset
+ offsetof(__fildes_t
, FileHandle
)
324 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
328 if(!NT_SUCCESS(nErrCode
))
330 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
334 /* duplicate standard handles */
336 if(i
== STDIN_FILENO
)
338 nErrCode
= NtDuplicateObject
341 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
343 (PHANDLE
)((ULONG
)pParamsBuffer
+ offsetof(RTL_USER_PROCESS_PARAMETERS
, InputHandle
)),
346 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
350 if(!NT_SUCCESS(nErrCode
))
352 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
356 /* standard output */
357 else if(i
== STDOUT_FILENO
)
359 nErrCode
= NtDuplicateObject
362 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
364 (PHANDLE
)((ULONG
)pParamsBuffer
+ offsetof(RTL_USER_PROCESS_PARAMETERS
, OutputHandle
)),
367 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
371 if(!NT_SUCCESS(nErrCode
))
373 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
378 else if(i
== STDERR_FILENO
)
380 nErrCode
= NtDuplicateObject
383 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
385 (PHANDLE
)((ULONG
)pParamsBuffer
+ offsetof(RTL_USER_PROCESS_PARAMETERS
, ErrorHandle
)),
388 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
392 if(!NT_SUCCESS(nErrCode
))
394 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
400 /* STEP 5: create first thread */
401 /* 5.1: get thunk routine's address */
402 RtlInitAnsiString(&strStartEntry
, "LdrInitializeThunk");
405 nErrCode
= LdrGetProcedureAddress
407 (PVOID
)0x78460000, /* NTDLL_BASE */
414 if(!NT_SUCCESS(nErrCode
))
416 ERR("LdrGetProcedureAddress() failed with status 0x%08X\n", nErrCode
);
420 pStartAddress
= LdrGetProcedureAddress
;
423 /* 5.2: set up the stack */
424 itInitialTeb
.StackAllocate
= NULL
;
425 nVirtualSize
= 0x100000;
426 nCommitSize
= 0x100000 - PAGESIZE
;
428 /* 5.2.1: reserve the stack */
429 nErrCode
= NtAllocateVirtualMemory
432 &itInitialTeb
.StackAllocate
,
440 if(!NT_SUCCESS(nErrCode
))
442 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
446 itInitialTeb
.StackBase
=
447 (PVOID
)((ULONG
)itInitialTeb
.StackAllocate
+ nVirtualSize
);
449 itInitialTeb
.StackLimit
=
450 (PVOID
)((ULONG
)itInitialTeb
.StackBase
- nCommitSize
);
452 /* 5.2.2: commit the stack */
453 nVirtualSize
= nCommitSize
+ PAGESIZE
;
455 (PVOID
)((ULONG
)itInitialTeb
.StackBase
- nVirtualSize
);
457 nErrCode
= NtAllocateVirtualMemory
468 if(!NT_SUCCESS(nErrCode
))
470 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
474 /* 5.2.3: set up the guard page */
475 nVirtualSize
= PAGESIZE
;
477 nErrCode
= NtProtectVirtualMemory
482 PAGE_GUARD
| PAGE_READWRITE
,
487 if(!NT_SUCCESS(nErrCode
))
489 ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode
);
493 /* 5.3: initialize the thread context */
494 memset(&ctxThreadContext
, 0, sizeof(ctxThreadContext
));
496 ctxThreadContext
.Eip
= (ULONG
)pStartAddress
;
497 ctxThreadContext
.SegGs
= USER_DS
;
498 ctxThreadContext
.SegFs
= USER_DS
;
499 ctxThreadContext
.SegEs
= USER_DS
;
500 ctxThreadContext
.SegDs
= USER_DS
;
501 ctxThreadContext
.SegCs
= USER_CS
;
502 ctxThreadContext
.SegSs
= USER_DS
;
503 /* skip five doublewords (four - unknown - parameters for LdrInitializeThunk,
504 and the return address) */
505 ctxThreadContext
.Esp
= (ULONG
)itInitialTeb
.StackBase
- 5 * 4;
506 ctxThreadContext
.EFlags
= (1 << 1) + (1 << 9);
508 /* 5.4: create the thread object */
509 nErrCode
= NtCreateThread
522 if(!NT_SUCCESS(nErrCode
))
524 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode
);
529 return (STATUS_SUCCESS
);