source: trunk/src/peldr/pe.c@ 21942

Last change on this file since 21942 was 21942, checked in by dmik, 14 years ago

Use custom startup code for PE loader.

This allows to grab the lowest memory address before any DLL
(including the C library) kicks in and reserves memory for its own
purposes. This fixes #54.

File size: 16.6 KB
Line 
1/*
2 * PELDR main exe loader code
3 *
4 * Copyright 1998-2001 Sander van Leeuwen (sandervl@xs4all.nl)
5 * Copyright 2012 Dmitriy Kuminov
6 *
7 * Command line options:
8 * /OPT:[x1=y,x2=z,..]
9 * x = CURDIR -> set current directory to y
10 * (not other options available at this time)
11 *
12 * Project Odin Software License can be found in LICENSE.TXT
13 */
14
15#define INCL_DOSFILEMGR /* File Manager values */
16#define INCL_DOSERRORS /* DOS Error values */
17#define INCL_DOSPROCESS /* DOS Process values */
18#define INCL_DOSMISC /* DOS Miscellanous values */
19#define INCL_DOSMODULEMGR
20#define INCL_DOSSESMGR
21#define INCL_WIN
22#include <os2.h>
23#include <bseord.h>
24#include <win32type.h>
25#include <misc.h>
26#include <wprocess.h>
27#include <win/peexe.h>
28#include <odinpe.h>
29#include "pe.h"
30
31#include "helpers.h"
32
33char INFO_BANNER[] = "Usage: PE winexe commandline";
34char szErrorTitle[] = "Odin";
35char szLoadErrorMsg[] = "Can't load executable %s";
36char szFileNotFound[] = "File not found: %s";
37char szFileErrorMsg[] = "File IO error";
38char szPEErrorMsg[] = "Not a valid win32 exe. (perhaps 16 bits windows)";
39char szCPUErrorMsg[] = "%s doesn't run on x86 machines";
40char szExeErrorMsg[] = "%s isn't an executable";
41char szInteralErrorMsg[]= "Internal Error while loading %s";
42char szInteralErrorMsg1[]= "Internal Error";
43char szNoKernel32Msg[] = "Can't load/find kernel32.dll (rc=%d, module %s)";
44char szErrorExports[] = "Unable to process exports of %s";
45char szErrorMemory[] = "Memory allocation failure while loading %s";
46char szErrorImports[] = "Failure to load \"%s\" due to bad or missing %s";
47
48char szErrDosStartSession[] = "Failed to start win16 (%s) for %s (rc=%d)";
49
50char fullpath[CCHMAXPATH];
51
52typedef HAB (* APIENTRY WININITIALIZEPROC)(ULONG flOptions);
53typedef BOOL (* APIENTRY WINTERMINATEPROC)(HAB hab);
54typedef HMQ (* APIENTRY WINCREATEMSGQUEUEPROC) (HAB hab, LONG cmsg);
55typedef BOOL (* APIENTRY WINDESTROYMSGQUEUEPROC) (HMQ hmq);
56typedef ULONG (* APIENTRY WINMESSAGEBOXPROC) (HWND hwndParent,
57 HWND hwndOwner,
58 PCSZ pszText,
59 PCSZ pszCaption,
60 ULONG idWindow,
61 ULONG flStyle);
62typedef void (* KRNL32EXCEPTPROC) (void *exceptframe);
63
64WININITIALIZEPROC MyWinInitialize = 0;
65WINTERMINATEPROC MyWinTerminate = 0;
66WINCREATEMSGQUEUEPROC MyWinCreateMsgQueue = 0;
67WINDESTROYMSGQUEUEPROC MyWinDestroyMsgQueue = 0;
68WINMESSAGEBOXPROC MyWinMessageBox = 0;
69KRNL32EXCEPTPROC Krnl32SetExceptionHandler = 0;
70KRNL32EXCEPTPROC Krnl32UnsetExceptionHandler = 0;
71
72
73WIN32CTOR CreateWin32Exe = 0;
74ULONG reservedMemory = 0;
75BOOL fConsoleApp = FALSE;
76
77BOOL AllocateExeMem(char *filename, BOOL *fNEExe);
78
79#ifdef DEBUG
80void print(const char *fmt, ...)
81{
82 static char buf[1024];
83 char *s, *e;
84 ULONG dummy;
85 int len;
86 va_list args;
87 va_start(args, fmt);
88 len = vsnprintf(buf, sizeof(buf), fmt, args);
89 va_end(args);
90 s = buf;
91 while (*s)
92 {
93 e = (char *)str_find_char(s, '\n');
94 DosWrite((HFILE)1, s, e - s, &dummy);
95 if (*e == '\n')
96 {
97 DosWrite((HFILE)1, "\n\r", 2, &dummy);
98 e++;
99 }
100 s = e;
101 }
102}
103#define DBG(a) do { print a; } while (0)
104#else
105#define DBG(a) do {} while (0)
106#endif
107
108//******************************************************************************
109//******************************************************************************
110int simple_main()
111{
112 DBG(("PE: BEGIN (DEBUG mode)\n"));
113
114 HAB hab = 0; /* PM anchor block handle */
115 HMQ hmq = 0; /* Message queue handle */
116 char exeName[CCHMAXPATH];
117 char fullpath[CCHMAXPATH * 2];
118 char errorMod[CCHMAXPATH];
119 char szErrorMsg[512];
120 char *pszErrorMsg = NULL;
121 APIRET rc;
122 HMODULE hmodPMWin = 0, hmodKernel32 = 0;
123 PTIB ptib;
124 PPIB ppib;
125 char *cmdline, *win32cmdline, *peoptions;
126 BOOL fVioConsole, fIsNEExe;
127
128 char *pszTemp, *pszTemp2;
129
130 DosGetInfoBlocks(&ptib, &ppib);
131
132#ifdef COMMAND_LINE_VERSION
133 if(DosGetInfoBlocks(&ptib, &ppib) == 0)
134 {
135 //switch process type to PM so the command line app can create PM
136 //windows
137 ppib->pib_ultype = 3;
138 }
139#endif
140
141 fullpath[0] = 0;
142 DosQueryModuleName(ppib->pib_hmte, sizeof(fullpath), fullpath);
143
144 pszTemp = fullpath + strlen(fullpath) - 1;
145 while (pszTemp >= fullpath && (*pszTemp != '\\' && *pszTemp != '/'))
146 --pszTemp;
147 if (pszTemp >= fullpath)
148 {
149 *pszTemp = 0;
150 strcat(fullpath, ";%BeginLIBPATH%");
151 DosSetExtLIBPATH(fullpath, BEGIN_LIBPATH);
152 DBG(("PE: Added '%s' to BEGINLIBPATH\n", fullpath));
153 }
154
155 cmdline = ppib->pib_pchcmd;
156 if (cmdline)
157 {
158 cmdline += strlen(cmdline) + 1; // skip executable name
159 if (!*cmdline)
160 cmdline = NULL;
161 }
162
163 if (cmdline)
164 {
165 DBG(("PE: Full command line: '%s'\n", cmdline));
166
167 pszTemp = (char *)str_skip_char(cmdline, ' ');
168
169 // get PE options
170 if (str_starts_with(pszTemp, "/OPT:["))
171 {
172 const char *end = str_find_char(pszTemp , ']');
173 if (*end)
174 pszTemp = (char *)str_skip_char(end + 1, ' ');
175 else
176 peoptions = NULL;
177 }
178
179 // get Win32 executable name
180 int delim = ' ';
181 if (*pszTemp == '"')
182 {
183 pszTemp++;
184 delim = '"';
185 }
186 pszTemp2 = exeName;
187 while (*pszTemp && *pszTemp != delim &&
188 (pszTemp2 - exeName) < CCHMAXPATH - 1)
189 {
190 *pszTemp2++ = *pszTemp++;
191 }
192 *pszTemp2 = '\0';
193 if (delim == '"')
194 pszTemp++;
195 pszTemp = (char *)str_skip_char(pszTemp, ' ');
196
197 // get Win32 command line
198 win32cmdline = pszTemp;
199
200 // add the .EXE extension if missing
201 int i = strlen(exeName);
202 if (i < 4 || !((exeName[i-1] == 'E' || exeName[i-1] == 'e') &&
203 (exeName[i-2] == 'X' || exeName[i-2] == 'x') &&
204 (exeName[i-3] == 'E' || exeName[i-3] == 'e') &&
205 exeName[i-4] == '.'))
206 {
207 if (i + 4 < CCHMAXPATH)
208 strcat(exeName, ".exe");
209 }
210
211 // try to locate the executable
212 pszTemp = exeName + strlen(exeName) - 1;
213 while (pszTemp >= exeName && (*pszTemp != '\\' && *pszTemp != '/'))
214 --pszTemp;
215 if (pszTemp < exeName)
216 {
217 // no path information, perform a search
218
219 char newExeName[CCHMAXPATH];
220
221 if(DosSearchPath( SEARCH_CUR_DIRECTORY | SEARCH_ENVIRONMENT | SEARCH_IGNORENETERRS
222 , "WINDOWSPATH" /* environment value */
223 , exeName /* Name of file to look for */
224 , newExeName /* Result of the search */
225 , sizeof(newExeName) /* Length of search buffer */
226 ) == NO_ERROR)
227 {
228 strcpy(exeName, newExeName);
229 }
230 }
231
232 DBG(("PE: Win32 EXE: '%s'\n", exeName));
233 DBG(("PE: Win32 command line: '%s'\n", win32cmdline));
234
235 FILESTATUS3 fstat3;
236 if (DosQueryPathInfo(exeName, FIL_STANDARD, (PVOID)&fstat3, sizeof(fstat3)) != NO_ERROR)
237 {
238 snprintf(szErrorMsg, sizeof(szErrorMsg), szFileNotFound, exeName);
239 pszErrorMsg = szErrorMsg;
240 goto failerror;
241 }
242
243 AllocateExeMem(exeName, &fIsNEExe);
244
245 if (fIsNEExe)
246 {
247 STARTDATA sdata = {0};
248 ULONG idSession;
249 PID pid;
250
251 sdata.Length = sizeof(sdata);
252 sdata.PgmName = (PSZ)"w16odin.exe";
253 strcpy(fullpath, exeName);
254 strcat(fullpath, " ");
255 strcat(fullpath, win32cmdline);
256 sdata.PgmInputs = fullpath;
257 sdata.FgBg = SSF_FGBG_FORE;
258 sdata.SessionType = SSF_TYPE_WINDOWEDVDM;
259 rc = DosStartSession(&sdata, &idSession, &pid);
260 if (rc)
261 {
262 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrDosStartSession, sdata.PgmName, exeName, rc);
263 pszErrorMsg = szErrorMsg;
264 goto failerror;
265 }
266 return 0;
267 }
268 }
269
270 rc = DosLoadModule(exeName, sizeof(exeName), "PMWIN", &hmodPMWin);
271 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32INITIALIZE, NULL, (PFN *)&MyWinInitialize);
272 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32TERMINATE, NULL, (PFN *)&MyWinTerminate);
273 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32CREATEMSGQUEUE, NULL, (PFN *)&MyWinCreateMsgQueue);
274 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32DESTROYMSGQUEUE, NULL, (PFN *)&MyWinDestroyMsgQueue);
275 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32MESSAGEBOX, NULL, (PFN *)&MyWinMessageBox);
276
277 if ((hab = MyWinInitialize(0)) == 0L) /* Initialize PM */
278 goto fail;
279
280 hmq = MyWinCreateMsgQueue(hab, 0);
281
282 if (!cmdline) {
283 MyWinMessageBox(HWND_DESKTOP, NULLHANDLE, INFO_BANNER, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
284 goto fail;
285 }
286
287 errorMod[0] = 0;
288 rc = DosLoadModule(errorMod, sizeof(errorMod), "KERNEL32", &hmodKernel32);
289 if(rc) {
290 snprintf(fullpath, CCHMAXPATH, szNoKernel32Msg, rc, errorMod);
291 MyWinMessageBox(HWND_DESKTOP, NULLHANDLE, fullpath, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
292 goto fail;
293 }
294 rc = DosQueryProcAddr(hmodKernel32, 0, "_CreateWin32PeLdrExe@36", (PFN *)&CreateWin32Exe);
295
296#ifdef COMMAND_LINE_VERSION
297 fVioConsole = TRUE;
298#else
299 fVioConsole = FALSE;
300#endif
301 rc = CreateWin32Exe(exeName, win32cmdline, peoptions, reservedMemory, 0,
302 fConsoleApp, fVioConsole, errorMod, sizeof(errorMod));
303 if(rc != LDRERROR_SUCCESS)
304 {
305 switch(rc) {
306 case LDRERROR_INVALID_MODULE:
307 snprintf(szErrorMsg, sizeof(szErrorMsg), szLoadErrorMsg, exeName);
308 break;
309 case LDRERROR_INVALID_CPU:
310 snprintf(szErrorMsg, sizeof(szErrorMsg), szCPUErrorMsg, exeName);
311 break;
312 case LDRERROR_FILE_SYSTEM:
313 snprintf(szErrorMsg, sizeof(szErrorMsg), szExeErrorMsg, exeName);
314 break;
315 case LDRERROR_MEMORY:
316 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorMemory, exeName);
317 break;
318 case LDRERROR_EXPORTS:
319 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorExports, exeName);
320 break;
321 case LDRERROR_IMPORTS:
322 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorImports, exeName, errorMod);
323 break;
324 case LDRERROR_INVALID_SECTION:
325 default:
326 snprintf(szErrorMsg, sizeof(szErrorMsg), szInteralErrorMsg, exeName);
327 break;
328 }
329
330 MyWinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
331 goto fail;
332 }
333
334 if(hmq) MyWinDestroyMsgQueue( hmq ); /* Tidy up... */
335 MyWinTerminate( hab ); /* Terminate the application */
336
337 DosFreeModule(hmodPMWin);
338 DosFreeModule(hmodKernel32);
339
340 DBG(("PE: END (returning 0)\n"));
341 return 0;
342
343fail:
344 if(hmq) MyWinDestroyMsgQueue( hmq ); /* Tidy up... */
345 if(hab) MyWinTerminate( hab ); /* Terminate the application */
346
347 if(hmodPMWin) DosFreeModule(hmodPMWin);
348 if(hmodKernel32) DosFreeModule(hmodKernel32);
349
350 DBG(("PE: END (returning 1)\n"));
351 return(1);
352
353failerror:
354 rc = DosLoadModule(exeName, sizeof(exeName), "PMWIN", &hmodPMWin);
355 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32INITIALIZE, NULL, (PFN *)&MyWinInitialize);
356 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32TERMINATE, NULL, (PFN *)&MyWinTerminate);
357 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32CREATEMSGQUEUE, NULL, (PFN *)&MyWinCreateMsgQueue);
358 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32DESTROYMSGQUEUE, NULL, (PFN *)&MyWinDestroyMsgQueue);
359 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32MESSAGEBOX, NULL, (PFN *)&MyWinMessageBox);
360 if(rc == 0) {
361 if ((hab = MyWinInitialize(0)) == 0L) /* Initialize PM */
362 goto fail;
363
364 hmq = MyWinCreateMsgQueue(hab, 0);
365
366 MyWinMessageBox(HWND_DESKTOP, NULLHANDLE, pszErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
367 }
368 if(hmq) MyWinDestroyMsgQueue( hmq ); /* Tidy up... */
369 if(hab) MyWinTerminate( hab ); /* Terminate the application */
370 if(hmodPMWin) DosFreeModule(hmodPMWin);
371
372 DBG(("PE: END (returning 1)\n"));
373 return 1;
374}
375//******************************************************************************
376//SvL: Reserve memory for win32 exes without fixups
377// This is done before any Odin or PMWIN dll is loaded, so we'll get
378// a very low virtual address. (which is exactly what we want)
379//******************************************************************************
380BOOL AllocateExeMem(char *filename, BOOL *fNEExe)
381{
382 HFILE dllfile = 0;
383 char *tmp;
384 ULONG action, ulRead, signature;
385 APIRET rc;
386 IMAGE_DOS_HEADER doshdr;
387 IMAGE_OPTIONAL_HEADER oh;
388 IMAGE_FILE_HEADER fh;
389 ULONG address = 0;
390 ULONG alloccnt = 0;
391 ULONG diff, i, baseAddress;
392 ULONG ulSysinfo, flAllocMem = 0;
393 BOOL ret = FALSE;
394
395 //Reserve enough space to store 4096 pointers to 1MB memory chunks
396 static ULONG memallocs[4096];
397
398 *fNEExe = FALSE;
399
400 rc = DosOpen(filename, &dllfile, &action, 0, FILE_READONLY, OPEN_ACTION_OPEN_IF_EXISTS|OPEN_ACTION_FAIL_IF_NEW, OPEN_SHARE_DENYNONE|OPEN_ACCESS_READONLY, NULL);
401 if(rc) {
402 goto end; //oops
403 }
404
405 //read dos header
406 if(DosRead(dllfile, (LPVOID)&doshdr, sizeof(doshdr), &ulRead)) {
407 goto end;
408 }
409 if(DosSetFilePtr(dllfile, doshdr.e_lfanew, FILE_BEGIN, &ulRead)) {
410 goto end;
411 }
412 //read signature dword
413 if(DosRead(dllfile, (LPVOID)&signature, sizeof(signature), &ulRead)) {
414 goto end;
415 }
416 //read pe header
417 if(DosRead(dllfile, (LPVOID)&fh, sizeof(fh), &ulRead)) {
418 goto end;
419 }
420 //read optional header
421 if(DosRead(dllfile, (LPVOID)&oh, sizeof(oh), &ulRead)) {
422 goto end;
423 }
424 if(doshdr.e_magic != IMAGE_DOS_SIGNATURE || signature != IMAGE_NT_SIGNATURE) {
425 if(LOWORD(signature) == IMAGE_OS2_SIGNATURE) {
426 *fNEExe = TRUE;
427 }
428 goto end;
429 }
430 fConsoleApp = (oh.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
431
432 DBG(("PE: AllocateExeMem: oh.Subsystem: %d\n", oh.Subsystem));
433 DBG(("PE: AllocateExeMem: oh.ImageBase: 0x%08X\n", oh.ImageBase));
434
435 // check for high memory support
436 rc = DosQuerySysInfo(QSV_VIRTUALADDRESSLIMIT, QSV_VIRTUALADDRESSLIMIT, &ulSysinfo, sizeof(ulSysinfo));
437 if (rc == 0 && ulSysinfo > 512) //VirtualAddresslimit is in MB
438 {
439 flAllocMem = PAG_ANY; // high memory support. Let's use it!
440 }
441
442 DBG(("PE: AllocateExeMem: VIRTUALADDRESSLIMIT: %d (rc %d)\n", ulSysinfo, rc));
443
444 if(oh.ImageBase < 512*1024*1024) {
445 flAllocMem = 0;
446 }
447 else {
448 if(flAllocMem == 0) {
449 goto end; //no support for > 512 MB
450 }
451 }
452 while(TRUE) {
453 rc = DosAllocMem((PPVOID)&address, FALLOC_SIZE, PAG_READ | flAllocMem);
454 if(rc) break;
455
456 if(address + FALLOC_SIZE >= oh.ImageBase) {
457 if(address > oh.ImageBase) {//we've passed it!
458 DosFreeMem((PVOID)address);
459 break;
460 }
461 //found the right address
462 DosFreeMem((PVOID)address);
463
464 diff = oh.ImageBase - address;
465 if(diff) {
466 rc = DosAllocMem((PPVOID)&address, diff, PAG_READ | flAllocMem);
467 if(rc) break;
468 }
469 rc = DosAllocMem((PPVOID)&baseAddress, oh.SizeOfImage, PAG_READ | PAG_WRITE | flAllocMem);
470 if(rc) break;
471
472 if(diff) DosFreeMem((PVOID)address);
473
474 reservedMemory = baseAddress;
475 break;
476 }
477 memallocs[alloccnt++] = address;
478 }
479 for(i=0;i<alloccnt;i++) {
480 DosFreeMem((PVOID)memallocs[i]);
481 }
482 DBG(("PE: AllocateExeMem: reservedMemory: 0x%08X\n", reservedMemory));
483 ret = TRUE;
484end:
485 if(dllfile) DosClose(dllfile);
486 DBG(("PE: AllocateExeMem: returning %d\n", ret));
487 return ret;
488}
489//******************************************************************************
490//******************************************************************************
Note: See TracBrowser for help on using the repository browser.