source: trunk/src/peldr/pe.c

Last change on this file 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
RevLine 
[4]1/*
2 * PELDR main exe loader code
3 *
[5956]4 * Copyright 1998-2001 Sander van Leeuwen (sandervl@xs4all.nl)
[21942]5 * Copyright 2012 Dmitriy Kuminov
[4]6 *
[4445]7 * Command line options:
[21916]8 * /OPT:[x1=y,x2=z,..]
[4445]9 * x = CURDIR -> set current directory to y
10 * (not other options available at this time)
[506]11 *
12 * Project Odin Software License can be found in LICENSE.TXT
[4]13 */
[21942]14
[22132]15/* Safe wrappers depend on LIBC heap, avoid it since we need -nostdlib */
16#define NO_INCL_SAFE_HIMEM_WRAPPERS
17
[4]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 */
[506]22#define INCL_DOSMODULEMGR
[5956]23#define INCL_DOSSESMGR
[4]24#define INCL_WIN
[506]25#include <os2.h>
26#include <bseord.h>
[131]27#include <win32type.h>
28#include <misc.h>
29#include <wprocess.h>
[21916]30#include <win/peexe.h>
[9536]31#include <odinpe.h>
[506]32#include "pe.h"
[4]33
[21942]34#include "helpers.h"
35
[4]36char INFO_BANNER[] = "Usage: PE winexe commandline";
[34]37char szErrorTitle[] = "Odin";
[9536]38char szLoadErrorMsg[] = "Can't load executable %s";
[21942]39char szFileNotFound[] = "File not found: %s";
[4]40char szFileErrorMsg[] = "File IO error";
41char szPEErrorMsg[] = "Not a valid win32 exe. (perhaps 16 bits windows)";
[9536]42char szCPUErrorMsg[] = "%s doesn't run on x86 machines";
43char szExeErrorMsg[] = "%s isn't an executable";
44char szInteralErrorMsg[]= "Internal Error while loading %s";
[9556]45char szInteralErrorMsg1[]= "Internal Error";
[5494]46char szNoKernel32Msg[] = "Can't load/find kernel32.dll (rc=%d, module %s)";
[21943]47char szBadKernel32Msg[] = "Invalid or outdated kernel32.dll";
[9536]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
[21942]52char szErrDosStartSession[] = "Failed to start win16 (%s) for %s (rc=%d)";
[4]53
54char fullpath[CCHMAXPATH];
55
[506]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,
[21940]61 HWND hwndOwner,
62 PCSZ pszText,
63 PCSZ pszCaption,
64 ULONG idWindow,
65 ULONG flStyle);
[704]66typedef void (* KRNL32EXCEPTPROC) (void *exceptframe);
[4]67
[506]68WININITIALIZEPROC MyWinInitialize = 0;
69WINTERMINATEPROC MyWinTerminate = 0;
70WINCREATEMSGQUEUEPROC MyWinCreateMsgQueue = 0;
71WINDESTROYMSGQUEUEPROC MyWinDestroyMsgQueue = 0;
72WINMESSAGEBOXPROC MyWinMessageBox = 0;
[704]73KRNL32EXCEPTPROC Krnl32SetExceptionHandler = 0;
74KRNL32EXCEPTPROC Krnl32UnsetExceptionHandler = 0;
[506]75
[3058]76
[4441]77WIN32CTOR CreateWin32Exe = 0;
78ULONG reservedMemory = 0;
79BOOL fConsoleApp = FALSE;
[506]80
[5956]81BOOL AllocateExeMem(char *filename, BOOL *fNEExe);
[2572]82
[21942]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
[2572]112//******************************************************************************
113//******************************************************************************
[21942]114int simple_main()
[4]115{
[21942]116 DBG(("PE: BEGIN (DEBUG mode)\n"));
117
[21940]118 HAB hab = 0; /* PM anchor block handle */
119 HMQ hmq = 0; /* Message queue handle */
120 char exeName[CCHMAXPATH];
[21942]121 char fullpath[CCHMAXPATH * 2];
[21940]122 char errorMod[CCHMAXPATH];
[21942]123 char szErrorMsg[512];
[21940]124 char *pszErrorMsg = NULL;
125 APIRET rc;
126 HMODULE hmodPMWin = 0, hmodKernel32 = 0;
127 PTIB ptib;
128 PPIB ppib;
[21942]129 char *cmdline, *win32cmdline, *peoptions;
130 BOOL fVioConsole, fIsNEExe;
[4]131
[21942]132 char *pszTemp, *pszTemp2;
133
134 DosGetInfoBlocks(&ptib, &ppib);
135
136#ifdef COMMAND_LINE_VERSION
[21940]137 if(DosGetInfoBlocks(&ptib, &ppib) == 0)
138 {
[21942]139 //switch process type to PM so the command line app can create PM
140 //windows
141 ppib->pib_ultype = 3;
142 }
143#endif
[9556]144
[21942]145 fullpath[0] = 0;
146 DosQueryModuleName(ppib->pib_hmte, sizeof(fullpath), fullpath);
[9556]147
[21942]148 pszTemp = fullpath + strlen(fullpath) - 1;
149 while (pszTemp >= fullpath && (*pszTemp != '\\' && *pszTemp != '/'))
150 --pszTemp;
151 if (pszTemp >= fullpath)
152 {
[21940]153 *pszTemp = 0;
154 strcat(fullpath, ";%BeginLIBPATH%");
155 DosSetExtLIBPATH(fullpath, BEGIN_LIBPATH);
[21942]156 DBG(("PE: Added '%s' to BEGINLIBPATH\n", fullpath));
[21940]157 }
[9556]158
[21942]159 cmdline = ppib->pib_pchcmd;
160 if (cmdline)
161 {
162 cmdline += strlen(cmdline) + 1; // skip executable name
163 if (!*cmdline)
164 cmdline = NULL;
165 }
[3376]166
[21942]167 if (cmdline)
168 {
169 DBG(("PE: Full command line: '%s'\n", cmdline));
[5015]170
[21942]171 pszTemp = (char *)str_skip_char(cmdline, ' ');
[7970]172
[21942]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 }
[7970]182
[21942]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, ' ');
[7970]200
[21942]201 // get Win32 command line
202 win32cmdline = pszTemp;
[7970]203
[22032]204 // get the filename component
205 pszTemp = exeName + strlen(exeName) - 1;
206 while (pszTemp >= exeName && (*pszTemp != '\\' && *pszTemp != '/'))
207 --pszTemp;
208 ++pszTemp;
209
[22028]210 // add the .EXE extension if missing (only if there's no extension already)
[22032]211 if (*str_find_char(pszTemp, '.') == '\0')
[21942]212 {
[22016]213 int i = strlen(exeName);
[22028]214 if (i + 4 < CCHMAXPATH)
215 strcat(exeName, ".exe");
[21942]216 }
[21940]217
[21942]218 // try to locate the executable
[22032]219 if (pszTemp == exeName)
[21942]220 {
221 // no path information, perform a search
[7991]222
[21942]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)
[21940]231 {
[21942]232 strcpy(exeName, newExeName);
[21940]233 }
234 }
[21942]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;
[21940]244 }
[21943]245 }
246 else
247 {
248 // No command line, show the usage message
249 pszErrorMsg = INFO_BANNER;
250 }
[21942]251
[21943]252 if (!pszErrorMsg)
253 {
254 if (AllocateExeMem(exeName, &fIsNEExe))
[21942]255 {
[21943]256 if (fIsNEExe)
257 {
258 STARTDATA sdata = {0};
259 ULONG idSession;
260 PID pid;
[6185]261
[21943]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 }
[5956]282 }
283 }
[21943]284 else
285 {
286 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorMemory, exeName);
287 pszErrorMsg = szErrorMsg;
288 }
[21940]289 }
[2572]290
[21943]291 // Resolve PM functions (needed to show the pszErrorMsg box too)
[21940]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);
[4]298
[21943]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 }
[506]320
[21943]321 if (!pszErrorMsg)
322 {
[4501]323#ifdef COMMAND_LINE_VERSION
[21943]324 fVioConsole = TRUE;
[4501]325#else
[21943]326 fVioConsole = FALSE;
[4501]327#endif
[21943]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 }
[9536]363 }
364
[21943]365 if (pszErrorMsg)
366 MyWinMessageBox(HWND_DESKTOP, NULLHANDLE, pszErrorMsg, szErrorTitle,
367 0, MB_OK | MB_ERROR | MB_MOVEABLE);
[21940]368 }
[21943]369 else
370 {
371 DBG(("PE: WinInitialize/WinCreateMsgQueue failed!\n"));
372 }
[4]373
[21943]374 if(hmodKernel32)
375 DosFreeModule(hmodKernel32);
[506]376
[21943]377 if (hmq)
378 MyWinDestroyMsgQueue(hmq);
379 if (hab)
380 MyWinTerminate(hab);
[21942]381
[21943]382 if(hmodPMWin)
383 DosFreeModule(hmodPMWin);
[704]384
[21943]385#ifdef DEBUG
386 if (pszErrorMsg)
387 DBG(("PE: Error: '%s'\n", pszErrorMsg));
388#endif
[704]389
[21943]390 DBG(("PE: END (returning %d)\n", pszErrorMsg ? 1 : 0));
391 return pszErrorMsg ? 1 : 0;
392}
[21942]393
[4]394//******************************************************************************
[2572]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)
[4]398//******************************************************************************
[5956]399BOOL AllocateExeMem(char *filename, BOOL *fNEExe)
[2572]400{
[21940]401 HFILE dllfile = 0;
[21942]402 char *tmp;
[21940]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;
[22029]413 ULONG allocSize = FALLOC_SIZE;
[2572]414
[22029]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];
[21942]418
[21940]419 *fNEExe = FALSE;
[2572]420
[21942]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);
[21940]422 if(rc) {
[21942]423 goto end; //oops
[21940]424 }
[2572]425
[21940]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) {
[5956]446 if(LOWORD(signature) == IMAGE_OS2_SIGNATURE) {
447 *fNEExe = TRUE;
448 }
[21940]449 goto end;
450 }
451 fConsoleApp = (oh.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
[2572]452
[21942]453 DBG(("PE: AllocateExeMem: oh.Subsystem: %d\n", oh.Subsystem));
454 DBG(("PE: AllocateExeMem: oh.ImageBase: 0x%08X\n", oh.ImageBase));
[22029]455 DBG(("PE: AllocateExeMem: oh.SizeOfImage: 0x%08X\n", oh.SizeOfImage));
[21942]456
[21940]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 }
[2572]463
[21942]464 DBG(("PE: AllocateExeMem: VIRTUALADDRESSLIMIT: %d (rc %d)\n", ulSysinfo, rc));
[2572]465
[21940]466 if(oh.ImageBase < 512*1024*1024) {
467 flAllocMem = 0;
468 }
469 else {
[5956]470 if(flAllocMem == 0) {
471 goto end; //no support for > 512 MB
[21940]472 }
473 }
474 while(TRUE) {
[22029]475 rc = DosAllocMem((PPVOID)&address, allocSize, PAG_READ | flAllocMem);
[21940]476 if(rc) break;
[2572]477
[22029]478 if(address + allocSize >= oh.ImageBase) {
[21940]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);
[2572]485
[21940]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;
[2572]493
[22029]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
[21940]508 if(diff) DosFreeMem((PVOID)address);
[2572]509
[21940]510 reservedMemory = baseAddress;
511 break;
512 }
513 memallocs[alloccnt++] = address;
514 }
515 for(i=0;i<alloccnt;i++) {
516 DosFreeMem((PVOID)memallocs[i]);
517 }
[22029]518 ret = rc == 0;
[2572]519end:
[21940]520 if(dllfile) DosClose(dllfile);
[22029]521 DBG(("PE: AllocateExeMem: reservedMemory: 0x%08X\n", reservedMemory));
[21942]522 DBG(("PE: AllocateExeMem: returning %d\n", ret));
[21940]523 return ret;
[2572]524}
525//******************************************************************************
526//******************************************************************************
Note: See TracBrowser for help on using the repository browser.