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

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

pe: Fix r22028.

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 // get the filename component
202 pszTemp = exeName + strlen(exeName) - 1;
203 while (pszTemp >= exeName && (*pszTemp != '\\' && *pszTemp != '/'))
204 --pszTemp;
205 ++pszTemp;
206
207 // add the .EXE extension if missing (only if there's no extension already)
208 if (*str_find_char(pszTemp, '.') == '\0')
209 {
210 int i = strlen(exeName);
211 if (i + 4 < CCHMAXPATH)
212 strcat(exeName, ".exe");
213 }
214
215 // try to locate the executable
216 if (pszTemp == exeName)
217 {
218 // no path information, perform a search
219
220 char newExeName[CCHMAXPATH];
221
222 if(DosSearchPath( SEARCH_CUR_DIRECTORY | SEARCH_ENVIRONMENT | SEARCH_IGNORENETERRS
223 , "WINDOWSPATH" /* environment value */
224 , exeName /* Name of file to look for */
225 , newExeName /* Result of the search */
226 , sizeof(newExeName) /* Length of search buffer */
227 ) == NO_ERROR)
228 {
229 strcpy(exeName, newExeName);
230 }
231 }
232
233 DBG(("PE: Win32 EXE: '%s'\n", exeName));
234 DBG(("PE: Win32 command line: '%s'\n", win32cmdline));
235
236 FILESTATUS3 fstat3;
237 if (DosQueryPathInfo(exeName, FIL_STANDARD, (PVOID)&fstat3, sizeof(fstat3)) != NO_ERROR)
238 {
239 snprintf(szErrorMsg, sizeof(szErrorMsg), szFileNotFound, exeName);
240 pszErrorMsg = szErrorMsg;
241 }
242 }
243 else
244 {
245 // No command line, show the usage message
246 pszErrorMsg = INFO_BANNER;
247 }
248
249 if (!pszErrorMsg)
250 {
251 if (AllocateExeMem(exeName, &fIsNEExe))
252 {
253 if (fIsNEExe)
254 {
255 STARTDATA sdata = {0};
256 ULONG idSession;
257 PID pid;
258
259 sdata.Length = sizeof(sdata);
260 sdata.PgmName = (PSZ)"w16odin.exe";
261 strcpy(fullpath, exeName);
262 strcat(fullpath, " ");
263 strcat(fullpath, win32cmdline);
264 sdata.PgmInputs = fullpath;
265 sdata.FgBg = SSF_FGBG_FORE;
266 sdata.SessionType = SSF_TYPE_WINDOWEDVDM;
267 rc = DosStartSession(&sdata, &idSession, &pid);
268 if (rc)
269 {
270 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrDosStartSession, sdata.PgmName, exeName, rc);
271 pszErrorMsg = szErrorMsg;
272 }
273 else
274 {
275 // we don't need to do anything else, return shortly
276 DBG(("PE: END (returning 0)\n"));
277 return 0;
278 }
279 }
280 }
281 else
282 {
283 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorMemory, exeName);
284 pszErrorMsg = szErrorMsg;
285 }
286 }
287
288 // Resolve PM functions (needed to show the pszErrorMsg box too)
289 rc = DosLoadModule(exeName, sizeof(exeName), "PMWIN", &hmodPMWin);
290 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32INITIALIZE, NULL, (PFN *)&MyWinInitialize);
291 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32TERMINATE, NULL, (PFN *)&MyWinTerminate);
292 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32CREATEMSGQUEUE, NULL, (PFN *)&MyWinCreateMsgQueue);
293 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32DESTROYMSGQUEUE, NULL, (PFN *)&MyWinDestroyMsgQueue);
294 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32MESSAGEBOX, NULL, (PFN *)&MyWinMessageBox);
295
296 if ((hab = MyWinInitialize(0)) &&
297 (hmq = MyWinCreateMsgQueue(hab, 0)))
298 {
299 if (!pszErrorMsg)
300 {
301 errorMod[0] = 0;
302 rc = DosLoadModule(errorMod, sizeof(errorMod), "KERNEL32", &hmodKernel32);
303 if (rc)
304 {
305 snprintf(szErrorMsg, sizeof(szErrorMsg), szNoKernel32Msg, rc, errorMod);
306 pszErrorMsg = szErrorMsg;
307 }
308 else
309 {
310 rc = DosQueryProcAddr(hmodKernel32, 0, "_CreateWin32PeLdrExe@36", (PFN *)&CreateWin32Exe);
311 if (rc)
312 {
313 snprintf(szErrorMsg, sizeof(szErrorMsg), szBadKernel32Msg);
314 pszErrorMsg = szErrorMsg;
315 }
316 }
317
318 if (!pszErrorMsg)
319 {
320#ifdef COMMAND_LINE_VERSION
321 fVioConsole = TRUE;
322#else
323 fVioConsole = FALSE;
324#endif
325 DBG(("PE: fVioConsole: %d\n", fVioConsole));
326
327 rc = CreateWin32Exe(exeName, win32cmdline, peoptions, reservedMemory, 0,
328 fConsoleApp, fVioConsole, errorMod, sizeof(errorMod));
329 if (rc != LDRERROR_SUCCESS)
330 {
331 switch (rc)
332 {
333 case LDRERROR_INVALID_MODULE:
334 snprintf(szErrorMsg, sizeof(szErrorMsg), szLoadErrorMsg, exeName);
335 break;
336 case LDRERROR_INVALID_CPU:
337 snprintf(szErrorMsg, sizeof(szErrorMsg), szCPUErrorMsg, exeName);
338 break;
339 case LDRERROR_FILE_SYSTEM:
340 snprintf(szErrorMsg, sizeof(szErrorMsg), szExeErrorMsg, exeName);
341 break;
342 case LDRERROR_MEMORY:
343 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorMemory, exeName);
344 break;
345 case LDRERROR_EXPORTS:
346 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorExports, exeName);
347 break;
348 case LDRERROR_IMPORTS:
349 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorImports, exeName, errorMod);
350 break;
351 case LDRERROR_INVALID_SECTION:
352 default:
353 snprintf(szErrorMsg, sizeof(szErrorMsg), szInteralErrorMsg, exeName);
354 break;
355 }
356
357 pszErrorMsg = szErrorMsg;
358 }
359 }
360 }
361
362 if (pszErrorMsg)
363 MyWinMessageBox(HWND_DESKTOP, NULLHANDLE, pszErrorMsg, szErrorTitle,
364 0, MB_OK | MB_ERROR | MB_MOVEABLE);
365 }
366 else
367 {
368 DBG(("PE: WinInitialize/WinCreateMsgQueue failed!\n"));
369 }
370
371 if(hmodKernel32)
372 DosFreeModule(hmodKernel32);
373
374 if (hmq)
375 MyWinDestroyMsgQueue(hmq);
376 if (hab)
377 MyWinTerminate(hab);
378
379 if(hmodPMWin)
380 DosFreeModule(hmodPMWin);
381
382#ifdef DEBUG
383 if (pszErrorMsg)
384 DBG(("PE: Error: '%s'\n", pszErrorMsg));
385#endif
386
387 DBG(("PE: END (returning %d)\n", pszErrorMsg ? 1 : 0));
388 return pszErrorMsg ? 1 : 0;
389}
390
391//******************************************************************************
392//SvL: Reserve memory for win32 exes without fixups
393// This is done before any Odin or PMWIN dll is loaded, so we'll get
394// a very low virtual address. (which is exactly what we want)
395//******************************************************************************
396BOOL AllocateExeMem(char *filename, BOOL *fNEExe)
397{
398 HFILE dllfile = 0;
399 char *tmp;
400 ULONG action, ulRead, signature;
401 APIRET rc;
402 IMAGE_DOS_HEADER doshdr;
403 IMAGE_OPTIONAL_HEADER oh;
404 IMAGE_FILE_HEADER fh;
405 ULONG address = 0;
406 ULONG alloccnt = 0;
407 ULONG diff, i, baseAddress;
408 ULONG ulSysinfo, flAllocMem = 0;
409 BOOL ret = FALSE;
410 ULONG allocSize = FALLOC_SIZE;
411
412 // Reserve enough space to store 4096 pointers to 1MB memory chunks +
413 // 16 pointers to 64K memory chunks (1MB) for the extra loop (see below)
414 static ULONG memallocs[4096 + 16];
415
416 *fNEExe = FALSE;
417
418 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);
419 if(rc) {
420 goto end; //oops
421 }
422
423 //read dos header
424 if(DosRead(dllfile, (LPVOID)&doshdr, sizeof(doshdr), &ulRead)) {
425 goto end;
426 }
427 if(DosSetFilePtr(dllfile, doshdr.e_lfanew, FILE_BEGIN, &ulRead)) {
428 goto end;
429 }
430 //read signature dword
431 if(DosRead(dllfile, (LPVOID)&signature, sizeof(signature), &ulRead)) {
432 goto end;
433 }
434 //read pe header
435 if(DosRead(dllfile, (LPVOID)&fh, sizeof(fh), &ulRead)) {
436 goto end;
437 }
438 //read optional header
439 if(DosRead(dllfile, (LPVOID)&oh, sizeof(oh), &ulRead)) {
440 goto end;
441 }
442 if(doshdr.e_magic != IMAGE_DOS_SIGNATURE || signature != IMAGE_NT_SIGNATURE) {
443 if(LOWORD(signature) == IMAGE_OS2_SIGNATURE) {
444 *fNEExe = TRUE;
445 }
446 goto end;
447 }
448 fConsoleApp = (oh.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
449
450 DBG(("PE: AllocateExeMem: oh.Subsystem: %d\n", oh.Subsystem));
451 DBG(("PE: AllocateExeMem: oh.ImageBase: 0x%08X\n", oh.ImageBase));
452 DBG(("PE: AllocateExeMem: oh.SizeOfImage: 0x%08X\n", oh.SizeOfImage));
453
454 // check for high memory support
455 rc = DosQuerySysInfo(QSV_VIRTUALADDRESSLIMIT, QSV_VIRTUALADDRESSLIMIT, &ulSysinfo, sizeof(ulSysinfo));
456 if (rc == 0 && ulSysinfo > 512) //VirtualAddresslimit is in MB
457 {
458 flAllocMem = PAG_ANY; // high memory support. Let's use it!
459 }
460
461 DBG(("PE: AllocateExeMem: VIRTUALADDRESSLIMIT: %d (rc %d)\n", ulSysinfo, rc));
462
463 if(oh.ImageBase < 512*1024*1024) {
464 flAllocMem = 0;
465 }
466 else {
467 if(flAllocMem == 0) {
468 goto end; //no support for > 512 MB
469 }
470 }
471 while(TRUE) {
472 rc = DosAllocMem((PPVOID)&address, allocSize, PAG_READ | flAllocMem);
473 if(rc) break;
474
475 if(address + allocSize >= oh.ImageBase) {
476 if(address > oh.ImageBase) {//we've passed it!
477 DosFreeMem((PVOID)address);
478 break;
479 }
480 //found the right address
481 DosFreeMem((PVOID)address);
482
483 diff = oh.ImageBase - address;
484 if(diff) {
485 rc = DosAllocMem((PPVOID)&address, diff, PAG_READ | flAllocMem);
486 if(rc) break;
487 }
488 rc = DosAllocMem((PPVOID)&baseAddress, oh.SizeOfImage, PAG_READ | PAG_WRITE | flAllocMem);
489 if(rc) break;
490
491 // Sometimes it's possible that a smaller block of memory enough to
492 // fit SizeOfImage is available below the target base address which
493 // will be skipped by the loop allocating memory in FALLOC_SIZE
494 // chunks when FALLOC_SIZE is greater than SizeOfImage. Continue
495 // allocation in smaller chunks in this case to get a perfect match.
496 if (baseAddress != oh.ImageBase) {
497 // save already allocated blocks for further release
498 memallocs[alloccnt++] = diff;
499 memallocs[alloccnt++] = baseAddress;
500 // set the exact chunk size
501 allocSize = oh.SizeOfImage;
502 continue;
503 }
504
505 if(diff) DosFreeMem((PVOID)address);
506
507 reservedMemory = baseAddress;
508 break;
509 }
510 memallocs[alloccnt++] = address;
511 }
512 for(i=0;i<alloccnt;i++) {
513 DosFreeMem((PVOID)memallocs[i]);
514 }
515 ret = rc == 0;
516end:
517 if(dllfile) DosClose(dllfile);
518 DBG(("PE: AllocateExeMem: reservedMemory: 0x%08X\n", reservedMemory));
519 DBG(("PE: AllocateExeMem: returning %d\n", ret));
520 return ret;
521}
522//******************************************************************************
523//******************************************************************************
Note: See TracBrowser for help on using the repository browser.