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

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

pe: A better fix for adding .EXE than r22016.

It will only add .EXE if there is no (any) extention already specified,
regardless of whether the path is present or not. This matches the
behavior of CMD.EXE on both Windows and OS/2.

File size: 16.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#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
408 //Reserve enough space to store 4096 pointers to 1MB memory chunks
409 static ULONG memallocs[4096];
410
411 *fNEExe = FALSE;
412
413 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);
414 if(rc) {
415 goto end; //oops
416 }
417
418 //read dos header
419 if(DosRead(dllfile, (LPVOID)&doshdr, sizeof(doshdr), &ulRead)) {
420 goto end;
421 }
422 if(DosSetFilePtr(dllfile, doshdr.e_lfanew, FILE_BEGIN, &ulRead)) {
423 goto end;
424 }
425 //read signature dword
426 if(DosRead(dllfile, (LPVOID)&signature, sizeof(signature), &ulRead)) {
427 goto end;
428 }
429 //read pe header
430 if(DosRead(dllfile, (LPVOID)&fh, sizeof(fh), &ulRead)) {
431 goto end;
432 }
433 //read optional header
434 if(DosRead(dllfile, (LPVOID)&oh, sizeof(oh), &ulRead)) {
435 goto end;
436 }
437 if(doshdr.e_magic != IMAGE_DOS_SIGNATURE || signature != IMAGE_NT_SIGNATURE) {
438 if(LOWORD(signature) == IMAGE_OS2_SIGNATURE) {
439 *fNEExe = TRUE;
440 }
441 goto end;
442 }
443 fConsoleApp = (oh.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
444
445 DBG(("PE: AllocateExeMem: oh.Subsystem: %d\n", oh.Subsystem));
446 DBG(("PE: AllocateExeMem: oh.ImageBase: 0x%08X\n", oh.ImageBase));
447
448 // check for high memory support
449 rc = DosQuerySysInfo(QSV_VIRTUALADDRESSLIMIT, QSV_VIRTUALADDRESSLIMIT, &ulSysinfo, sizeof(ulSysinfo));
450 if (rc == 0 && ulSysinfo > 512) //VirtualAddresslimit is in MB
451 {
452 flAllocMem = PAG_ANY; // high memory support. Let's use it!
453 }
454
455 DBG(("PE: AllocateExeMem: VIRTUALADDRESSLIMIT: %d (rc %d)\n", ulSysinfo, rc));
456
457 if(oh.ImageBase < 512*1024*1024) {
458 flAllocMem = 0;
459 }
460 else {
461 if(flAllocMem == 0) {
462 goto end; //no support for > 512 MB
463 }
464 }
465 while(TRUE) {
466 rc = DosAllocMem((PPVOID)&address, FALLOC_SIZE, PAG_READ | flAllocMem);
467 if(rc) break;
468
469 if(address + FALLOC_SIZE >= oh.ImageBase) {
470 if(address > oh.ImageBase) {//we've passed it!
471 DosFreeMem((PVOID)address);
472 break;
473 }
474 //found the right address
475 DosFreeMem((PVOID)address);
476
477 diff = oh.ImageBase - address;
478 if(diff) {
479 rc = DosAllocMem((PPVOID)&address, diff, PAG_READ | flAllocMem);
480 if(rc) break;
481 }
482 rc = DosAllocMem((PPVOID)&baseAddress, oh.SizeOfImage, PAG_READ | PAG_WRITE | flAllocMem);
483 if(rc) break;
484
485 if(diff) DosFreeMem((PVOID)address);
486
487 reservedMemory = baseAddress;
488 break;
489 }
490 memallocs[alloccnt++] = address;
491 }
492 for(i=0;i<alloccnt;i++) {
493 DosFreeMem((PVOID)memallocs[i]);
494 }
495 DBG(("PE: AllocateExeMem: reservedMemory: 0x%08X\n", reservedMemory));
496 ret = TRUE;
497end:
498 if(dllfile) DosClose(dllfile);
499 DBG(("PE: AllocateExeMem: returning %d\n", ret));
500 return ret;
501}
502//******************************************************************************
503//******************************************************************************
Note: See TracBrowser for help on using the repository browser.