mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
571 lines
17 KiB
571 lines
17 KiB
#define INCL_OS2V20_TASKING
|
|
#define INCL_OS2V20_MEMORY
|
|
#define INCL_OS2V20_FILESYS
|
|
#define INCL_ERROR_H
|
|
#define INCL_TYPES
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <bseerr.h>
|
|
#include <os2dll.h>
|
|
#include <os2dll16.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <os2defp.h>
|
|
#include <mi.h>
|
|
#include <ldrxport.h>
|
|
#if PMNT
|
|
#define INCL_32BIT
|
|
#include <pmnt.h>
|
|
extern APIRET PMNTMemMap(PSEL);
|
|
extern APIRET PMNTIOMap(void);
|
|
#endif
|
|
|
|
#ifndef MAXPATHLEN
|
|
#define MAXPATHLEN 260
|
|
#endif
|
|
|
|
ULONG GetTickCount(VOID);
|
|
PVOID LDRExecInfo;
|
|
extern ULONG Od2Start16Stack;
|
|
extern ULONG Od2Start16DS;
|
|
extern POD2_SIG_HANDLER_REC pSigHandlerRec;
|
|
extern POD2_VEC_HANDLER_REC pVecHandlerRec;
|
|
USHORT LDRDoscallsSel;
|
|
extern VOID Od2EnableCtrlHandling(VOID);
|
|
extern VOID Od2UpdateLocalInfoAtStart(VOID);
|
|
extern char Od2PgmFullPathBuf[];
|
|
extern PSZ Od2PgmFilePath;
|
|
extern ULONG timing;
|
|
extern BOOLEAN Od2SignalEnabled;
|
|
extern BOOL Od2ExpandOd2LibPathElements(PCHAR ExpandedString, ULONG MaxSize);
|
|
extern PVOID EntryFlat(VOID);
|
|
extern ULONG Od2ExecPgmErrorText;
|
|
extern ULONG Od2ExecPgmErrorTextLength;
|
|
extern ULONG Od2EnvCommandOffset;
|
|
extern POD2_PROCESS Od2Process;
|
|
|
|
|
|
#if PMNT
|
|
// From winbase.h
|
|
|
|
ULONG
|
|
SetThreadAffinityMask(
|
|
HANDLE hThread,
|
|
DWORD dwThreadAffinityMask
|
|
);
|
|
|
|
HANDLE
|
|
GetCurrentThread(
|
|
VOID
|
|
);
|
|
|
|
#endif // PMNT
|
|
|
|
VOID
|
|
Ow2BoundAppLoadPopup(
|
|
IN PSZ AppName
|
|
);
|
|
|
|
ldrlibi_t InitRecords[MAX_INIT_RECORDS];
|
|
|
|
#if DBG
|
|
void LoadStop(
|
|
int id
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(id);
|
|
|
|
DbgPrint("LoadStop\n");
|
|
// DbgUserBreakPoint();
|
|
}
|
|
#else
|
|
#define LoadStop(x)
|
|
#endif
|
|
|
|
int
|
|
Loader_main()
|
|
{
|
|
PUCHAR pname;
|
|
ldrrei_t exec_info;
|
|
ldrrei_t *pexec_info = &exec_info;
|
|
PNT_TIB ptib;
|
|
PIB *pib;
|
|
PVOID MemoryAddress;
|
|
ULONG RegionSize;
|
|
PCHAR pchar;
|
|
ULONG i;
|
|
OD2_SIG_HANDLER_REC *pSig;
|
|
OD2_VEC_HANDLER_REC *pVec;
|
|
ULONG address;
|
|
int rc;
|
|
NTSTATUS Status;
|
|
I386DESCRIPTOR Desc;
|
|
OS2_API_MSG m;
|
|
P_LDRNEWEXE_MSG a = &m.u.LdrNewExe;
|
|
POS2_CAPTURE_HEADER CaptureBuffer;
|
|
ULONG FileFlags;
|
|
ULONG FileType;
|
|
STRING ProcessNameString;
|
|
STRING LibPathNameString;
|
|
STRING FailNameString;
|
|
// Remember that \OS2SS\DRIVES\ is added for each path element !
|
|
// Let's assume the smallest component is 5 chars long (like C:\X;)
|
|
// (although it could be less, like "." or "\")
|
|
// => we need to add 14 * (MAXPATHLEN/5) = MAXPATHLEN*3
|
|
CHAR ExpandedLibPath[MAXPATHLEN*4];
|
|
ULONG NumOfInitRecords;
|
|
|
|
DosGetThreadInfo(&ptib, &pib);
|
|
|
|
pname = Od2PgmFullPathBuf;
|
|
|
|
rc = Od2Canonicalize(pname,
|
|
CANONICALIZE_FILE_OR_DEV,
|
|
&ProcessNameString,
|
|
NULL,
|
|
&FileFlags,
|
|
&FileType
|
|
);
|
|
/*
|
|
* Reserve 64k at BASE_TILE, which corresponds with selector 0
|
|
*/
|
|
MemoryAddress = (PVOID) BASE_TILE;
|
|
RegionSize = _64K;
|
|
if ((rc = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&MemoryAddress,
|
|
0,
|
|
&RegionSize,
|
|
MEM_RESERVE,
|
|
PAGE_NOACCESS)) != NO_ERROR) {
|
|
LoadStop(0);
|
|
goto returnlabel;
|
|
}
|
|
|
|
Od2ExpandOd2LibPathElements(ExpandedLibPath, sizeof(ExpandedLibPath));
|
|
RtlInitString(&LibPathNameString, ExpandedLibPath);
|
|
|
|
FailNameString.Buffer = NULL;
|
|
FailNameString.Length = 0;
|
|
FailNameString.MaximumLength = 50; // same size as ErrorBuffer in client\dllinit.c
|
|
|
|
CaptureBuffer = Od2AllocateCaptureBuffer(
|
|
4,
|
|
0,
|
|
ProcessNameString.MaximumLength +
|
|
LibPathNameString.MaximumLength +
|
|
FailNameString.MaximumLength +
|
|
MAX_INIT_RECORDS * sizeof(ldrlibi_t)
|
|
);
|
|
if (CaptureBuffer == NULL) {
|
|
LoadStop(6);
|
|
goto returnlabel;
|
|
}
|
|
|
|
Od2CaptureMessageString( CaptureBuffer,
|
|
ProcessNameString.Buffer,
|
|
ProcessNameString.Length,
|
|
ProcessNameString.MaximumLength,
|
|
&a->ProcessName
|
|
);
|
|
RtlFreeHeap(Od2Heap, 0, ProcessNameString.Buffer);
|
|
|
|
Od2CaptureMessageString( CaptureBuffer,
|
|
FailNameString.Buffer,
|
|
FailNameString.Length,
|
|
FailNameString.MaximumLength,
|
|
&a->FailName
|
|
);
|
|
|
|
Od2CaptureMessageString( CaptureBuffer,
|
|
LibPathNameString.Buffer,
|
|
LibPathNameString.Length,
|
|
LibPathNameString.MaximumLength,
|
|
&a->LibPathName
|
|
);
|
|
|
|
Od2CaptureMessageString( CaptureBuffer,
|
|
NULL,
|
|
0,
|
|
MAX_INIT_RECORDS * sizeof(ldrlibi_t),
|
|
&a->InitRecords
|
|
);
|
|
|
|
a->EntryFlatAddr = (ULONG)EntryFlat;
|
|
|
|
Od2CallSubsystem( &m, CaptureBuffer, Ol2LdrNewExe, sizeof( *a ) );
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( LOADER ) {
|
|
if (a->BoundApp) {
|
|
KdPrint(("Loader_main: Bound App Detected\n"));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Od2Process->BoundApp = a->BoundApp; // store away the BoundApp flag
|
|
|
|
#if PMNT
|
|
PMFlags = a->PMProcess;
|
|
#endif // PMNT
|
|
|
|
rc = m.ReturnedErrorValue;
|
|
if (rc != NO_ERROR) {
|
|
#if PMNT
|
|
if (rc == ERROR_PMSHELL_NOT_UP || rc == ERROR_2ND_PMSHELL) {
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
Ow2PMShellErrorPopup(Od2PgmFilePath,rc);
|
|
DosExit(EXIT_PROCESS, rc);
|
|
return(rc);
|
|
}
|
|
#endif
|
|
RtlCopyMemory((PVOID)Od2ExecPgmErrorText, a->FailName.Buffer, a->FailName.Length);
|
|
Od2ExecPgmErrorTextLength = a->FailName.Length;
|
|
*((PCHAR)Od2ExecPgmErrorText + Od2ExecPgmErrorTextLength) = '\0';
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
LoadStop(5);
|
|
|
|
if (a->BoundApp) {
|
|
Ow2BoundAppLoadPopup(Od2PgmFilePath);
|
|
}
|
|
|
|
goto returnlabel;
|
|
}
|
|
|
|
#if PMNT
|
|
if (ProcessIsPMProcess() && !ProcessIsPMShell())
|
|
{
|
|
UNICODE_STRING EventString_U;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
NTSTATUS Status;
|
|
HANDLE Od2PMShellEvent;
|
|
|
|
// Whether PMShell or just a PM app, we need to open the PMShell
|
|
// synchronization semaphore
|
|
|
|
RtlInitUnicodeString( &EventString_U, OS2_SS_PMSHELL_EVENT);
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&EventString_U,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Open the global subsystem synchronization Nt semaphore
|
|
//
|
|
Status = NtOpenEvent(&Od2PMShellEvent,
|
|
EVENT_ALL_ACCESS,
|
|
&Obja);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
#if DBG
|
|
DbgPrint("Os2: Loader_main(), failed to open PMShellEvent, Status %x\n", Status);
|
|
#endif // DBG
|
|
}
|
|
else
|
|
{
|
|
// Regular PM app - wait on the PMShell semaphore !
|
|
Status = NtWaitForSingleObject(
|
|
Od2PMShellEvent,
|
|
TRUE, // Alertable
|
|
NULL
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Os2: Loader_main(), failed to NtWaitForSingleObject PMShellEvent, Status %x\n", Status);
|
|
}
|
|
#endif
|
|
}
|
|
} // PM apps (non-PMShell) handling
|
|
|
|
// Force all PM threads to run on processor#1. Otherwise, PM Desktop
|
|
// locks-up
|
|
if (ProcessIsPMProcess())
|
|
{
|
|
DWORD Ret;
|
|
|
|
Ret = SetThreadAffinityMask(
|
|
GetCurrentThread(),
|
|
0x1);
|
|
#if DBG
|
|
if (Ret == 0)
|
|
{
|
|
DbgPrint("OS2: main() - failed to SetThreadAffinityMask\n");
|
|
}
|
|
else if (Ret != 1)
|
|
{
|
|
DbgPrint("OS2: Loader_main() - SetThreadAffinityMask returned %x (now set to 1)\n",
|
|
Ret);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
#endif // PMNT
|
|
|
|
NumOfInitRecords = a->NumOfInitRecords;
|
|
RtlCopyMemory(InitRecords, a->InitRecords.Buffer,
|
|
NumOfInitRecords * sizeof(ldrlibi_t));
|
|
RtlCopyMemory(pexec_info, &a->ExecInfo, sizeof(exec_info));
|
|
LDRDoscallsSel = (USHORT)a->DoscallsSel;
|
|
Od2FreeCaptureBuffer( CaptureBuffer );
|
|
|
|
Desc.Limit = _64K - 1;
|
|
Desc.Type = READ_WRITE_DATA;
|
|
|
|
//
|
|
// Set selectors for the tiled heap area
|
|
//
|
|
for (Desc.BaseAddress = OD2TILEDHEAP_BASE;
|
|
Desc.BaseAddress < (OD2TILEDHEAP_BASE + OD2TILEDHEAP_SIZE);
|
|
(ULONG)(Desc.BaseAddress) += _64K ) {
|
|
|
|
Status = Nt386SetDescriptorLDT (
|
|
NULL,
|
|
FLATTOSEL(Desc.BaseAddress),
|
|
Desc);
|
|
ASSERT (NT_SUCCESS( Status ));
|
|
}
|
|
|
|
Desc.BaseAddress = OD2ENVIRONMENT_BASE;
|
|
Status = Nt386SetDescriptorLDT (
|
|
NULL,
|
|
FLATTOSEL(OD2ENVIRONMENT_BASE),
|
|
Desc);
|
|
ASSERT (NT_SUCCESS( Status ));
|
|
|
|
pexec_info->ei_envsel = FLATTOSEL(Od2Environment);
|
|
pchar = (PCHAR)Od2Environment;
|
|
|
|
/*
|
|
* Start at beginning of environment and find the command line
|
|
* (Env, 0, fullpathname, cmdline)
|
|
*/
|
|
|
|
pchar = (PCHAR)Od2Environment;
|
|
while(*pchar++ != '\0') {
|
|
if (*pchar == '\0') {
|
|
pchar++;
|
|
if (*pchar == '\0')
|
|
break;
|
|
}
|
|
}
|
|
pchar++;
|
|
pchar += strlen(pname);
|
|
pchar++;
|
|
|
|
pexec_info->ei_comoff = (USHORT)((ULONG) pchar & 0xffff);
|
|
Od2EnvCommandOffset = pexec_info->ei_comoff;
|
|
|
|
/*
|
|
* Set LDRExecInfo pointer for use in OS2DLL in DosGetInfoSeg
|
|
*/
|
|
(PVOID) LDRExecInfo = pexec_info;
|
|
|
|
Od2UpdateLocalInfoAtStart();
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG ( LOADER ) {
|
|
DbgPrint("Values in exec_info for program:\n");
|
|
DbgPrint("ei_startaddr=%x\n", pexec_info->ei_startaddr); /* instruction pointer */
|
|
DbgPrint("ei_stackaddr=%x\n", pexec_info->ei_stackaddr); /* instruction pointer */
|
|
DbgPrint("ei_ds=%x\n", pexec_info->ei_ds); /* instruction pointer */
|
|
DbgPrint("ei_dgroupsize=%x\n", pexec_info->ei_dgroupsize); /* instruction pointer */
|
|
DbgPrint("ei_heapsize=%x\n", pexec_info->ei_heapsize); /* instruction pointer */
|
|
DbgPrint("ei_loadtype=%x\n", pexec_info->ei_loadtype); /* instruction pointer */
|
|
DbgPrint("ei_envsel=%x\n", pexec_info->ei_envsel); /* instruction pointer */
|
|
DbgPrint("ei_comoff=%x\n", pexec_info->ei_comoff); /* instruction pointer */
|
|
DbgPrint("ei_stacksize=%x\n", pexec_info->ei_stacksize); /* instruction pointer */
|
|
DbgPrint("ei_hmod=%x\n", pexec_info->ei_hmod); /* instruction pointer */
|
|
}
|
|
#endif
|
|
|
|
#ifdef PMNT
|
|
if (ProcessIsPMApp())
|
|
{
|
|
SEL DummySel;
|
|
|
|
PMNTMemMap(&DummySel);
|
|
PMNTIOMap();
|
|
}
|
|
#endif // PMNT
|
|
|
|
/*
|
|
* Setup Signal handling default values.
|
|
*/
|
|
pSig = (POD2_SIG_HANDLER_REC) pSigHandlerRec;
|
|
address = FLATTOFARPTR((ULONG)(SELTOFLAT(LDRDoscallsSel)));
|
|
pSig->fholdenable = HLDSIG_ENABLE;
|
|
pSig->outstandingsig[0].sighandleraddr =
|
|
pSig->outstandingsig[1].sighandleraddr =
|
|
pSig->outstandingsig[2].sighandleraddr =
|
|
pSig->outstandingsig[3].sighandleraddr =
|
|
pSig->outstandingsig[4].sighandleraddr =
|
|
pSig->outstandingsig[5].sighandleraddr =
|
|
pSig->outstandingsig[6].sighandleraddr = 0;
|
|
pSig->doscallssel = address;
|
|
pSig->sighandler[SIG_BROKENPIPE - 1] = (ULONG)
|
|
(address | ThunkOffsetDosReturn);
|
|
pSig->action[SIG_BROKENPIPE - 1] = SIGA_IGNORE;
|
|
|
|
pSig->sighandler[SIG_CTRLBREAK - 1] = (ULONG)
|
|
(address | ThunkOffsetExitProcessStub);
|
|
pSig->action[SIG_CTRLBREAK - 1] = SIGA_ACCEPT;
|
|
|
|
pSig->sighandler[SIG_CTRLC - 1] = (ULONG)
|
|
(address | ThunkOffsetExitProcessStub);
|
|
pSig->action[SIG_CTRLC - 1] = SIGA_ACCEPT;
|
|
|
|
pSig->sighandler[SIG_KILLPROCESS - 1] = (ULONG)
|
|
(address | ThunkOffsetExitProcessStub);
|
|
pSig->action[SIG_KILLPROCESS - 1] = SIGA_ACCEPT;
|
|
|
|
pSig->sighandler[SIG_PFLG_A - 1] = (ULONG)
|
|
(address | ThunkOffsetDosReturn);
|
|
pSig->action[SIG_PFLG_A - 1] = SIGA_ACCEPT;
|
|
|
|
pSig->sighandler[SIG_PFLG_B - 1] = (ULONG)
|
|
(address | ThunkOffsetDosReturn);
|
|
pSig->action[SIG_PFLG_B - 1] = SIGA_ACCEPT;
|
|
|
|
pSig->sighandler[SIG_PFLG_C - 1] = (ULONG)
|
|
(address | ThunkOffsetDosReturn);
|
|
pSig->action[SIG_PFLG_C - 1] = SIGA_ACCEPT;
|
|
|
|
/*
|
|
* Setup Vector handling default values.
|
|
*/
|
|
pVec = (POD2_VEC_HANDLER_REC) pVecHandlerRec;
|
|
pVec->doscallssel = address;
|
|
pVec->VecHandler[0] = (ULONG) (address | ThunkOffsetDosReturn);
|
|
pVec->VecHandler[1] = (ULONG) (address | ThunkOffsetDosReturn);
|
|
pVec->VecHandler[2] = (ULONG) (address | ThunkOffsetDosReturn);
|
|
pVec->VecHandler[3] = (ULONG) (address | ThunkOffsetDosReturn);
|
|
pVec->VecHandler[4] = (ULONG) (address | ThunkOffsetDosReturn);
|
|
pVec->VecHandler[5] = (ULONG) (address | ThunkOffsetDosReturn);
|
|
|
|
//
|
|
// Go over all DLLs init routines
|
|
//
|
|
for (i = 0; i < NumOfInitRecords; i++) {
|
|
if (InitRecords[i].stackaddr.ptr_sel == 0) {
|
|
#if DBG
|
|
IF_OD2_DEBUG ( LOADER ) {
|
|
DbgPrint("Replacing SS:SP of module\n");
|
|
}
|
|
#endif
|
|
InitRecords[i].stackaddr.ptr_sel = pexec_info->ei_stackaddr.ptr_sel;
|
|
InitRecords[i].stackaddr.ptr_off = pexec_info->ei_stackaddr.ptr_off;
|
|
}
|
|
#if DBG
|
|
IF_OD2_DEBUG ( LOADER ) {
|
|
DbgPrint("=== Calling Init routine %d for app.\n", i);
|
|
DbgPrint("Values of libi:\n");
|
|
DbgPrint("startaddr=%x\n", InitRecords[i].startaddr); /* instruction pointer */
|
|
DbgPrint("stackaddr=%x\n", InitRecords[i].stackaddr); /* instruction pointer */
|
|
DbgPrint("ds=%x\n", InitRecords[i].ds); /* instruction pointer */
|
|
DbgPrint("heapsize=%x\n", InitRecords[i].heapsize); /* instruction pointer */
|
|
DbgPrint("handle=%x\n", InitRecords[i].handle); /* instruction pointer */
|
|
}
|
|
#endif
|
|
rc = ldrLibiInit(&InitRecords[i], pexec_info);
|
|
#if DBG
|
|
IF_OD2_DEBUG ( LOADER ) {
|
|
DbgPrint("=== Init routine returned %d\n", rc);
|
|
}
|
|
#endif
|
|
if (rc == 0) {
|
|
DosExit(EXIT_PROCESS, 255); // same as OS/2 1.x does
|
|
return(rc);
|
|
}
|
|
}
|
|
|
|
#if PMNT
|
|
// If this is PMShell, release waiting PM apps now
|
|
if (ProcessIsPMShell())
|
|
{
|
|
UNICODE_STRING EventString_U;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
NTSTATUS Status;
|
|
HANDLE Od2PMShellEvent;
|
|
|
|
RtlInitUnicodeString( &EventString_U, OS2_SS_PMSHELL_EVENT);
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&EventString_U,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Open the global subsystem synchronization Nt semaphore
|
|
//
|
|
Status = NtOpenEvent(&Od2PMShellEvent,
|
|
EVENT_ALL_ACCESS,
|
|
&Obja);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
#if DBG
|
|
DbgPrint("Os2: Loader_main(), after LdrLibiInit, failed to open PMShellEvent, Status %x\n", Status);
|
|
#endif // DBG
|
|
}
|
|
else
|
|
{
|
|
// For PMShell, release the PMShell synchronization event
|
|
Status = NtSetEvent (
|
|
Od2PMShellEvent,
|
|
NULL);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Os2: Loader_main(), after LdrLibiInit, failed to NtSetEvent PMShellEvent, Status %x\n", Status);
|
|
}
|
|
#endif // DBG
|
|
}
|
|
}
|
|
#endif // PMNT
|
|
|
|
/*
|
|
* Save starting stack address & DS for exit list processing
|
|
*/
|
|
(PULONG) Od2Start16Stack =
|
|
(PULONG)(pexec_info->ei_stackaddr.ptr_flat);
|
|
(PULONG) Od2Start16DS = (PULONG) (pexec_info->ei_ds);
|
|
|
|
pSig->signature = 0xdead;
|
|
Od2EnableCtrlHandling();
|
|
if (timing)
|
|
{
|
|
printf("Os2 time before ldrstart is %d\n", (GetTickCount()) - timing);
|
|
}
|
|
Od2SignalEnabled = TRUE;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG ( LOADER ) {
|
|
DbgPrint("Values in exec_info:\n");
|
|
DbgPrint("ei_startaddr=%x\n", pexec_info->ei_startaddr); /* instruction pointer */
|
|
DbgPrint("ei_stackaddr=%x\n", pexec_info->ei_stackaddr); /* instruction pointer */
|
|
DbgPrint("ei_ds=%x\n", pexec_info->ei_ds); /* instruction pointer */
|
|
DbgPrint("ei_dgroupsize=%x\n", pexec_info->ei_dgroupsize); /* instruction pointer */
|
|
DbgPrint("ei_heapsize=%x\n", pexec_info->ei_heapsize); /* instruction pointer */
|
|
DbgPrint("ei_loadtype=%x\n", pexec_info->ei_loadtype); /* instruction pointer */
|
|
DbgPrint("ei_envsel=%x\n", pexec_info->ei_envsel); /* instruction pointer */
|
|
DbgPrint("ei_comoff=%x\n", pexec_info->ei_comoff); /* instruction pointer */
|
|
DbgPrint("ei_stacksize=%x\n", pexec_info->ei_stacksize); /* instruction pointer */
|
|
DbgPrint("ei_hmod=%x\n", pexec_info->ei_hmod); /* instruction pointer */
|
|
}
|
|
#endif
|
|
|
|
ldrstart(pexec_info);
|
|
|
|
returnlabel:
|
|
|
|
//
|
|
// Set to indicate loader error to os2srv
|
|
//
|
|
|
|
DosExit(EXIT_PROCESS, 0x80000000 | rc);
|
|
return(rc);
|
|
}
|
|
|