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

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

Remove evil goto and code duplication in PE/PEC.

This finalizes the major code cleanup started in r21942.

File size: 16.7 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
202 int i = strlen(exeName);
203 if (i < 4 || !((exeName[i-1] == 'E' || exeName[i-1] == 'e') &&
204 (exeName[i-2] == 'X' || exeName[i-2] == 'x') &&
205 (exeName[i-3] == 'E' || exeName[i-3] == 'e') &&
206 exeName[i-4] == '.'))
207 {
208 if (i + 4 < CCHMAXPATH)
209 strcat(exeName, ".exe");
210 }
211
212 // try to locate the executable
213 pszTemp = exeName + strlen(exeName) - 1;
214 while (pszTemp >= exeName && (*pszTemp != '\\' && *pszTemp != '/'))
215 --pszTemp;
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
411 //Reserve enough space to store 4096 pointers to 1MB memory chunks
412 static ULONG memallocs[4096];
413
414 *fNEExe = FALSE;
415
416 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);
417 if(rc) {
418 goto end; //oops
419 }
420
421 //read dos header
422 if(DosRead(dllfile, (LPVOID)&doshdr, sizeof(doshdr), &ulRead)) {
423 goto end;
424 }
425 if(DosSetFilePtr(dllfile, doshdr.e_lfanew, FILE_BEGIN, &ulRead)) {
426 goto end;
427 }
428 //read signature dword
429 if(DosRead(dllfile, (LPVOID)&signature, sizeof(signature), &ulRead)) {
430 goto end;
431 }
432 //read pe header
433 if(DosRead(dllfile, (LPVOID)&fh, sizeof(fh), &ulRead)) {
434 goto end;
435 }
436 //read optional header
437 if(DosRead(dllfile, (LPVOID)&oh, sizeof(oh), &ulRead)) {
438 goto end;
439 }
440 if(doshdr.e_magic != IMAGE_DOS_SIGNATURE || signature != IMAGE_NT_SIGNATURE) {
441 if(LOWORD(signature) == IMAGE_OS2_SIGNATURE) {
442 *fNEExe = TRUE;
443 }
444 goto end;
445 }
446 fConsoleApp = (oh.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
447
448 DBG(("PE: AllocateExeMem: oh.Subsystem: %d\n", oh.Subsystem));
449 DBG(("PE: AllocateExeMem: oh.ImageBase: 0x%08X\n", oh.ImageBase));
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, FALLOC_SIZE, PAG_READ | flAllocMem);
470 if(rc) break;
471
472 if(address + FALLOC_SIZE >= 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 if(diff) DosFreeMem((PVOID)address);
489
490 reservedMemory = baseAddress;
491 break;
492 }
493 memallocs[alloccnt++] = address;
494 }
495 for(i=0;i<alloccnt;i++) {
496 DosFreeMem((PVOID)memallocs[i]);
497 }
498 DBG(("PE: AllocateExeMem: reservedMemory: 0x%08X\n", reservedMemory));
499 ret = TRUE;
500end:
501 if(dllfile) DosClose(dllfile);
502 DBG(("PE: AllocateExeMem: returning %d\n", ret));
503 return ret;
504}
505//******************************************************************************
506//******************************************************************************
Note: See TracBrowser for help on using the repository browser.