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

Last change on this file since 22029 was 22029, checked in by dmik, 13 years ago

pe: Fix memory allocation error for some Win32 EXEs.

This error could be displayed if the size of the EXE was too small.

File size: 17.5 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 szBadKernel32Msg[] = "Invalid or outdated kernel32.dll";
45char szErrorExports[] = "Unable to process exports of %s";
46char szErrorMemory[] = "Memory allocation failure while loading %s";
47char szErrorImports[] = "Failure to load \"%s\" due to bad or missing %s";
48
49char szErrDosStartSession[] = "Failed to start win16 (%s) for %s (rc=%d)";
50
51char fullpath[CCHMAXPATH];
52
53typedef HAB (* APIENTRY WININITIALIZEPROC)(ULONG flOptions);
54typedef BOOL (* APIENTRY WINTERMINATEPROC)(HAB hab);
55typedef HMQ (* APIENTRY WINCREATEMSGQUEUEPROC) (HAB hab, LONG cmsg);
56typedef BOOL (* APIENTRY WINDESTROYMSGQUEUEPROC) (HMQ hmq);
57typedef ULONG (* APIENTRY WINMESSAGEBOXPROC) (HWND hwndParent,
58 HWND hwndOwner,
59 PCSZ pszText,
60 PCSZ pszCaption,
61 ULONG idWindow,
62 ULONG flStyle);
63typedef void (* KRNL32EXCEPTPROC) (void *exceptframe);
64
65WININITIALIZEPROC MyWinInitialize = 0;
66WINTERMINATEPROC MyWinTerminate = 0;
67WINCREATEMSGQUEUEPROC MyWinCreateMsgQueue = 0;
68WINDESTROYMSGQUEUEPROC MyWinDestroyMsgQueue = 0;
69WINMESSAGEBOXPROC MyWinMessageBox = 0;
70KRNL32EXCEPTPROC Krnl32SetExceptionHandler = 0;
71KRNL32EXCEPTPROC Krnl32UnsetExceptionHandler = 0;
72
73
74WIN32CTOR CreateWin32Exe = 0;
75ULONG reservedMemory = 0;
76BOOL fConsoleApp = FALSE;
77
78BOOL AllocateExeMem(char *filename, BOOL *fNEExe);
79
80#ifdef DEBUG
81void print(const char *fmt, ...)
82{
83 static char buf[1024];
84 char *s, *e;
85 ULONG dummy;
86 int len;
87 va_list args;
88 va_start(args, fmt);
89 len = vsnprintf(buf, sizeof(buf), fmt, args);
90 va_end(args);
91 s = buf;
92 while (*s)
93 {
94 e = (char *)str_find_char(s, '\n');
95 DosWrite((HFILE)1, s, e - s, &dummy);
96 if (*e == '\n')
97 {
98 DosWrite((HFILE)1, "\n\r", 2, &dummy);
99 e++;
100 }
101 s = e;
102 }
103}
104#define DBG(a) do { print a; } while (0)
105#else
106#define DBG(a) do {} while (0)
107#endif
108
109//******************************************************************************
110//******************************************************************************
111int simple_main()
112{
113 DBG(("PE: BEGIN (DEBUG mode)\n"));
114
115 HAB hab = 0; /* PM anchor block handle */
116 HMQ hmq = 0; /* Message queue handle */
117 char exeName[CCHMAXPATH];
118 char fullpath[CCHMAXPATH * 2];
119 char errorMod[CCHMAXPATH];
120 char szErrorMsg[512];
121 char *pszErrorMsg = NULL;
122 APIRET rc;
123 HMODULE hmodPMWin = 0, hmodKernel32 = 0;
124 PTIB ptib;
125 PPIB ppib;
126 char *cmdline, *win32cmdline, *peoptions;
127 BOOL fVioConsole, fIsNEExe;
128
129 char *pszTemp, *pszTemp2;
130
131 DosGetInfoBlocks(&ptib, &ppib);
132
133#ifdef COMMAND_LINE_VERSION
134 if(DosGetInfoBlocks(&ptib, &ppib) == 0)
135 {
136 //switch process type to PM so the command line app can create PM
137 //windows
138 ppib->pib_ultype = 3;
139 }
140#endif
141
142 fullpath[0] = 0;
143 DosQueryModuleName(ppib->pib_hmte, sizeof(fullpath), fullpath);
144
145 pszTemp = fullpath + strlen(fullpath) - 1;
146 while (pszTemp >= fullpath && (*pszTemp != '\\' && *pszTemp != '/'))
147 --pszTemp;
148 if (pszTemp >= fullpath)
149 {
150 *pszTemp = 0;
151 strcat(fullpath, ";%BeginLIBPATH%");
152 DosSetExtLIBPATH(fullpath, BEGIN_LIBPATH);
153 DBG(("PE: Added '%s' to BEGINLIBPATH\n", fullpath));
154 }
155
156 cmdline = ppib->pib_pchcmd;
157 if (cmdline)
158 {
159 cmdline += strlen(cmdline) + 1; // skip executable name
160 if (!*cmdline)
161 cmdline = NULL;
162 }
163
164 if (cmdline)
165 {
166 DBG(("PE: Full command line: '%s'\n", cmdline));
167
168 pszTemp = (char *)str_skip_char(cmdline, ' ');
169
170 // get PE options
171 if (str_starts_with(pszTemp, "/OPT:["))
172 {
173 const char *end = str_find_char(pszTemp , ']');
174 if (*end)
175 pszTemp = (char *)str_skip_char(end + 1, ' ');
176 else
177 peoptions = NULL;
178 }
179
180 // get Win32 executable name
181 int delim = ' ';
182 if (*pszTemp == '"')
183 {
184 pszTemp++;
185 delim = '"';
186 }
187 pszTemp2 = exeName;
188 while (*pszTemp && *pszTemp != delim &&
189 (pszTemp2 - exeName) < CCHMAXPATH - 1)
190 {
191 *pszTemp2++ = *pszTemp++;
192 }
193 *pszTemp2 = '\0';
194 if (delim == '"')
195 pszTemp++;
196 pszTemp = (char *)str_skip_char(pszTemp, ' ');
197
198 // get Win32 command line
199 win32cmdline = pszTemp;
200
201 // add the .EXE extension if missing (only if there's no extension already)
202 if (*str_find_last_char(exeName, '.') == '\0')
203 {
204 int i = strlen(exeName);
205 if (i + 4 < CCHMAXPATH)
206 strcat(exeName, ".exe");
207 }
208
209 // try to locate the executable
210 pszTemp = exeName + strlen(exeName) - 1;
211 while (pszTemp >= exeName && (*pszTemp != '\\' && *pszTemp != '/'))
212 --pszTemp;
213 if (pszTemp < exeName)
214 {
215 // no path information, perform a search
216
217 char newExeName[CCHMAXPATH];
218
219 if(DosSearchPath( SEARCH_CUR_DIRECTORY | SEARCH_ENVIRONMENT | SEARCH_IGNORENETERRS
220 , "WINDOWSPATH" /* environment value */
221 , exeName /* Name of file to look for */
222 , newExeName /* Result of the search */
223 , sizeof(newExeName) /* Length of search buffer */
224 ) == NO_ERROR)
225 {
226 strcpy(exeName, newExeName);
227 }
228 }
229
230 DBG(("PE: Win32 EXE: '%s'\n", exeName));
231 DBG(("PE: Win32 command line: '%s'\n", win32cmdline));
232
233 FILESTATUS3 fstat3;
234 if (DosQueryPathInfo(exeName, FIL_STANDARD, (PVOID)&fstat3, sizeof(fstat3)) != NO_ERROR)
235 {
236 snprintf(szErrorMsg, sizeof(szErrorMsg), szFileNotFound, exeName);
237 pszErrorMsg = szErrorMsg;
238 }
239 }
240 else
241 {
242 // No command line, show the usage message
243 pszErrorMsg = INFO_BANNER;
244 }
245
246 if (!pszErrorMsg)
247 {
248 if (AllocateExeMem(exeName, &fIsNEExe))
249 {
250 if (fIsNEExe)
251 {
252 STARTDATA sdata = {0};
253 ULONG idSession;
254 PID pid;
255
256 sdata.Length = sizeof(sdata);
257 sdata.PgmName = (PSZ)"w16odin.exe";
258 strcpy(fullpath, exeName);
259 strcat(fullpath, " ");
260 strcat(fullpath, win32cmdline);
261 sdata.PgmInputs = fullpath;
262 sdata.FgBg = SSF_FGBG_FORE;
263 sdata.SessionType = SSF_TYPE_WINDOWEDVDM;
264 rc = DosStartSession(&sdata, &idSession, &pid);
265 if (rc)
266 {
267 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrDosStartSession, sdata.PgmName, exeName, rc);
268 pszErrorMsg = szErrorMsg;
269 }
270 else
271 {
272 // we don't need to do anything else, return shortly
273 DBG(("PE: END (returning 0)\n"));
274 return 0;
275 }
276 }
277 }
278 else
279 {
280 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorMemory, exeName);
281 pszErrorMsg = szErrorMsg;
282 }
283 }
284
285 // Resolve PM functions (needed to show the pszErrorMsg box too)
286 rc = DosLoadModule(exeName, sizeof(exeName), "PMWIN", &hmodPMWin);
287 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32INITIALIZE, NULL, (PFN *)&MyWinInitialize);
288 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32TERMINATE, NULL, (PFN *)&MyWinTerminate);
289 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32CREATEMSGQUEUE, NULL, (PFN *)&MyWinCreateMsgQueue);
290 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32DESTROYMSGQUEUE, NULL, (PFN *)&MyWinDestroyMsgQueue);
291 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32MESSAGEBOX, NULL, (PFN *)&MyWinMessageBox);
292
293 if ((hab = MyWinInitialize(0)) &&
294 (hmq = MyWinCreateMsgQueue(hab, 0)))
295 {
296 if (!pszErrorMsg)
297 {
298 errorMod[0] = 0;
299 rc = DosLoadModule(errorMod, sizeof(errorMod), "KERNEL32", &hmodKernel32);
300 if (rc)
301 {
302 snprintf(szErrorMsg, sizeof(szErrorMsg), szNoKernel32Msg, rc, errorMod);
303 pszErrorMsg = szErrorMsg;
304 }
305 else
306 {
307 rc = DosQueryProcAddr(hmodKernel32, 0, "_CreateWin32PeLdrExe@36", (PFN *)&CreateWin32Exe);
308 if (rc)
309 {
310 snprintf(szErrorMsg, sizeof(szErrorMsg), szBadKernel32Msg);
311 pszErrorMsg = szErrorMsg;
312 }
313 }
314
315 if (!pszErrorMsg)
316 {
317#ifdef COMMAND_LINE_VERSION
318 fVioConsole = TRUE;
319#else
320 fVioConsole = FALSE;
321#endif
322 DBG(("PE: fVioConsole: %d\n", fVioConsole));
323
324 rc = CreateWin32Exe(exeName, win32cmdline, peoptions, reservedMemory, 0,
325 fConsoleApp, fVioConsole, errorMod, sizeof(errorMod));
326 if (rc != LDRERROR_SUCCESS)
327 {
328 switch (rc)
329 {
330 case LDRERROR_INVALID_MODULE:
331 snprintf(szErrorMsg, sizeof(szErrorMsg), szLoadErrorMsg, exeName);
332 break;
333 case LDRERROR_INVALID_CPU:
334 snprintf(szErrorMsg, sizeof(szErrorMsg), szCPUErrorMsg, exeName);
335 break;
336 case LDRERROR_FILE_SYSTEM:
337 snprintf(szErrorMsg, sizeof(szErrorMsg), szExeErrorMsg, exeName);
338 break;
339 case LDRERROR_MEMORY:
340 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorMemory, exeName);
341 break;
342 case LDRERROR_EXPORTS:
343 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorExports, exeName);
344 break;
345 case LDRERROR_IMPORTS:
346 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorImports, exeName, errorMod);
347 break;
348 case LDRERROR_INVALID_SECTION:
349 default:
350 snprintf(szErrorMsg, sizeof(szErrorMsg), szInteralErrorMsg, exeName);
351 break;
352 }
353
354 pszErrorMsg = szErrorMsg;
355 }
356 }
357 }
358
359 if (pszErrorMsg)
360 MyWinMessageBox(HWND_DESKTOP, NULLHANDLE, pszErrorMsg, szErrorTitle,
361 0, MB_OK | MB_ERROR | MB_MOVEABLE);
362 }
363 else
364 {
365 DBG(("PE: WinInitialize/WinCreateMsgQueue failed!\n"));
366 }
367
368 if(hmodKernel32)
369 DosFreeModule(hmodKernel32);
370
371 if (hmq)
372 MyWinDestroyMsgQueue(hmq);
373 if (hab)
374 MyWinTerminate(hab);
375
376 if(hmodPMWin)
377 DosFreeModule(hmodPMWin);
378
379#ifdef DEBUG
380 if (pszErrorMsg)
381 DBG(("PE: Error: '%s'\n", pszErrorMsg));
382#endif
383
384 DBG(("PE: END (returning %d)\n", pszErrorMsg ? 1 : 0));
385 return pszErrorMsg ? 1 : 0;
386}
387
388//******************************************************************************
389//SvL: Reserve memory for win32 exes without fixups
390// This is done before any Odin or PMWIN dll is loaded, so we'll get
391// a very low virtual address. (which is exactly what we want)
392//******************************************************************************
393BOOL AllocateExeMem(char *filename, BOOL *fNEExe)
394{
395 HFILE dllfile = 0;
396 char *tmp;
397 ULONG action, ulRead, signature;
398 APIRET rc;
399 IMAGE_DOS_HEADER doshdr;
400 IMAGE_OPTIONAL_HEADER oh;
401 IMAGE_FILE_HEADER fh;
402 ULONG address = 0;
403 ULONG alloccnt = 0;
404 ULONG diff, i, baseAddress;
405 ULONG ulSysinfo, flAllocMem = 0;
406 BOOL ret = FALSE;
407 ULONG allocSize = FALLOC_SIZE;
408
409 // Reserve enough space to store 4096 pointers to 1MB memory chunks +
410 // 16 pointers to 64K memory chunks (1MB) for the extra loop (see below)
411 static ULONG memallocs[4096 + 16];
412
413 *fNEExe = FALSE;
414
415 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);
416 if(rc) {
417 goto end; //oops
418 }
419
420 //read dos header
421 if(DosRead(dllfile, (LPVOID)&doshdr, sizeof(doshdr), &ulRead)) {
422 goto end;
423 }
424 if(DosSetFilePtr(dllfile, doshdr.e_lfanew, FILE_BEGIN, &ulRead)) {
425 goto end;
426 }
427 //read signature dword
428 if(DosRead(dllfile, (LPVOID)&signature, sizeof(signature), &ulRead)) {
429 goto end;
430 }
431 //read pe header
432 if(DosRead(dllfile, (LPVOID)&fh, sizeof(fh), &ulRead)) {
433 goto end;
434 }
435 //read optional header
436 if(DosRead(dllfile, (LPVOID)&oh, sizeof(oh), &ulRead)) {
437 goto end;
438 }
439 if(doshdr.e_magic != IMAGE_DOS_SIGNATURE || signature != IMAGE_NT_SIGNATURE) {
440 if(LOWORD(signature) == IMAGE_OS2_SIGNATURE) {
441 *fNEExe = TRUE;
442 }
443 goto end;
444 }
445 fConsoleApp = (oh.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
446
447 DBG(("PE: AllocateExeMem: oh.Subsystem: %d\n", oh.Subsystem));
448 DBG(("PE: AllocateExeMem: oh.ImageBase: 0x%08X\n", oh.ImageBase));
449 DBG(("PE: AllocateExeMem: oh.SizeOfImage: 0x%08X\n", oh.SizeOfImage));
450
451 // check for high memory support
452 rc = DosQuerySysInfo(QSV_VIRTUALADDRESSLIMIT, QSV_VIRTUALADDRESSLIMIT, &ulSysinfo, sizeof(ulSysinfo));
453 if (rc == 0 && ulSysinfo > 512) //VirtualAddresslimit is in MB
454 {
455 flAllocMem = PAG_ANY; // high memory support. Let's use it!
456 }
457
458 DBG(("PE: AllocateExeMem: VIRTUALADDRESSLIMIT: %d (rc %d)\n", ulSysinfo, rc));
459
460 if(oh.ImageBase < 512*1024*1024) {
461 flAllocMem = 0;
462 }
463 else {
464 if(flAllocMem == 0) {
465 goto end; //no support for > 512 MB
466 }
467 }
468 while(TRUE) {
469 rc = DosAllocMem((PPVOID)&address, allocSize, PAG_READ | flAllocMem);
470 if(rc) break;
471
472 if(address + allocSize >= oh.ImageBase) {
473 if(address > oh.ImageBase) {//we've passed it!
474 DosFreeMem((PVOID)address);
475 break;
476 }
477 //found the right address
478 DosFreeMem((PVOID)address);
479
480 diff = oh.ImageBase - address;
481 if(diff) {
482 rc = DosAllocMem((PPVOID)&address, diff, PAG_READ | flAllocMem);
483 if(rc) break;
484 }
485 rc = DosAllocMem((PPVOID)&baseAddress, oh.SizeOfImage, PAG_READ | PAG_WRITE | flAllocMem);
486 if(rc) break;
487
488 // Sometimes it's possible that a smaller block of memory enough to
489 // fit SizeOfImage is available below the target base address which
490 // will be skipped by the loop allocating memory in FALLOC_SIZE
491 // chunks when FALLOC_SIZE is greater than SizeOfImage. Continue
492 // allocation in smaller chunks in this case to get a perfect match.
493 if (baseAddress != oh.ImageBase) {
494 // save already allocated blocks for further release
495 memallocs[alloccnt++] = diff;
496 memallocs[alloccnt++] = baseAddress;
497 // set the exact chunk size
498 allocSize = oh.SizeOfImage;
499 continue;
500 }
501
502 if(diff) DosFreeMem((PVOID)address);
503
504 reservedMemory = baseAddress;
505 break;
506 }
507 memallocs[alloccnt++] = address;
508 }
509 for(i=0;i<alloccnt;i++) {
510 DosFreeMem((PVOID)memallocs[i]);
511 }
512 ret = rc == 0;
513end:
514 if(dllfile) DosClose(dllfile);
515 DBG(("PE: AllocateExeMem: reservedMemory: 0x%08X\n", reservedMemory));
516 DBG(("PE: AllocateExeMem: returning %d\n", ret));
517 return ret;
518}
519//******************************************************************************
520//******************************************************************************
Note: See TracBrowser for help on using the repository browser.