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

Last change on this file since 22132 was 22132, checked in by dmik, 8 years ago

pe: Support newer LIBC forcing safe Dos wrappers which we don't need.

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