source: trunk/src/peldr/pe.cpp@ 9556

Last change on this file since 9556 was 9556, checked in by sandervl, 23 years ago

Change BEGINLIBPATH before loading any dlls

File size: 16.3 KB
Line 
1/* $Id: pe.cpp,v 1.36 2002-12-28 13:40:10 sandervl Exp $ */
2
3/*
4 * PELDR main exe loader code
5 *
6 * Copyright 1998-2001 Sander van Leeuwen (sandervl@xs4all.nl)
7 *
8 * Command line options:
9 * /OPT:[x1=y,x2=z,..]
10 * x = CURDIR -> set current directory to y
11 * (not other options available at this time)
12 *
13 * Project Odin Software License can be found in LICENSE.TXT
14 *
15 */
16#define INCL_DOSFILEMGR /* File Manager values */
17#define INCL_DOSERRORS /* DOS Error values */
18#define INCL_DOSPROCESS /* DOS Process values */
19#define INCL_DOSMISC /* DOS Miscellanous values */
20#define INCL_DOSMODULEMGR
21#define INCL_DOSSESMGR
22#define INCL_WIN
23#include <os2.h>
24#include <bseord.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <string.h>
29#include <assert.h>
30#include <win32type.h>
31#include <misc.h>
32#include <wprocess.h>
33#include <win\peexe.h>
34#include <odinpe.h>
35#include "pe.h"
36
37char INFO_BANNER[] = "Usage: PE winexe commandline";
38char szErrorTitle[] = "Odin";
39char szLoadErrorMsg[] = "Can't load executable %s";
40char szFileNotFound[] = "File not found.";
41char szFileErrorMsg[] = "File IO error";
42char szPEErrorMsg[] = "Not a valid win32 exe. (perhaps 16 bits windows)";
43char szCPUErrorMsg[] = "%s doesn't run on x86 machines";
44char szExeErrorMsg[] = "%s isn't an executable";
45char szInteralErrorMsg[]= "Internal Error while loading %s";
46char szInteralErrorMsg1[]= "Internal Error";
47char szNoKernel32Msg[] = "Can't load/find kernel32.dll (rc=%d, module %s)";
48char szDosInfoBlocks[] = "DosInfoBlocks failed!";
49char szErrorExports[] = "Unable to process exports of %s";
50char szErrorMemory[] = "Memory allocation failure while loading %s";
51char szErrorImports[] = "Failure to load \"%s\" due to bad or missing %s";
52
53char szErrDosStartSession[] = "Failed to start win16 session (rc=%d)";
54
55char fullpath[CCHMAXPATH];
56
57typedef HAB (* APIENTRY WININITIALIZEPROC)(ULONG flOptions);
58typedef BOOL (* APIENTRY WINTERMINATEPROC)(HAB hab);
59typedef HMQ (* APIENTRY WINCREATEMSGQUEUEPROC) (HAB hab, LONG cmsg);
60typedef BOOL (* APIENTRY WINDESTROYMSGQUEUEPROC) (HMQ hmq);
61typedef ULONG (* APIENTRY WINMESSAGEBOXPROC) (HWND hwndParent,
62 HWND hwndOwner,
63 PCSZ pszText,
64 PCSZ pszCaption,
65 ULONG idWindow,
66 ULONG flStyle);
67typedef void (* KRNL32EXCEPTPROC) (void *exceptframe);
68
69WININITIALIZEPROC MyWinInitialize = 0;
70WINTERMINATEPROC MyWinTerminate = 0;
71WINCREATEMSGQUEUEPROC MyWinCreateMsgQueue = 0;
72WINDESTROYMSGQUEUEPROC MyWinDestroyMsgQueue = 0;
73WINMESSAGEBOXPROC MyWinMessageBox = 0;
74KRNL32EXCEPTPROC Krnl32SetExceptionHandler = 0;
75KRNL32EXCEPTPROC Krnl32UnsetExceptionHandler = 0;
76
77
78WIN32CTOR CreateWin32Exe = 0;
79ULONG reservedMemory = 0;
80BOOL fConsoleApp = FALSE;
81
82BOOL AllocateExeMem(char *filename, BOOL *fNEExe);
83
84//******************************************************************************
85//******************************************************************************
86int main(int argc, char *argv[])
87{
88 HAB hab = 0; /* PM anchor block handle */
89 HMQ hmq = 0; /* Message queue handle */
90 char exeName[CCHMAXPATH];
91 char fullpath[CCHMAXPATH];
92 char errorMod[CCHMAXPATH];
93 char *pszErrorMsg = NULL;
94 APIRET rc;
95 HMODULE hmodPMWin = 0, hmodKernel32 = 0;
96 PTIB ptib;
97 PPIB ppib;
98 char *cmdline, *win32cmdline, *peoptions, *newcmdline;
99 BOOL fQuote = FALSE, fVioConsole, fIsNEExe, fEndOfCmdLine = FALSE;
100 int nrTries = 1;
101
102 if(DosGetInfoBlocks(&ptib, &ppib) == 0)
103 {
104 char *pszTemp;
105
106 fullpath[0] = 0;
107 DosQueryModuleName(ppib->pib_hmte, sizeof(fullpath), fullpath);
108
109 strupr(fullpath);
110#ifdef COMMAND_LINE_VERSION
111 pszTemp = strstr(fullpath, "PEC.EXE");
112#else
113 pszTemp = strstr(fullpath, "PE.EXE");
114#endif
115 if(pszTemp == NULL) {
116 pszErrorMsg = szInteralErrorMsg1;
117 goto failerror;
118 }
119 pszTemp--; //to go trailing backslash
120 *pszTemp = 0;
121 strcat(fullpath, ";%BeginLIBPATH%");
122 DosSetExtLIBPATH(fullpath, BEGIN_LIBPATH);
123 }
124
125 if(argc >= 2) {
126 if(DosGetInfoBlocks(&ptib, &ppib) == 0) {
127tryagain:
128 cmdline = ppib->pib_pchcmd;
129 cmdline += strlen(cmdline)+1; //skip pe.exe
130 peoptions = strstr(cmdline, "/OPT:[");
131 if(peoptions) {
132 newcmdline = strchr(peoptions, ']');
133 if(newcmdline) {
134 cmdline = newcmdline+1;
135 }
136#ifdef DEBUG
137 else _interrupt(3); //should not happen!
138#endif
139 }
140 while(*cmdline == ' ') cmdline++; //skip leading space
141 if(*cmdline == '"') {
142 cmdline++;
143 fQuote = TRUE;
144 }
145 win32cmdline = cmdline;
146
147 strncpy(exeName, cmdline, sizeof(exeName)-1);
148 exeName[sizeof(exeName)-1] = 0;
149 char *p = exeName;
150 if(fQuote) {
151 while(*p != '"' && *p != 0) p++;
152 }
153 else {
154 for(int i=0;i<nrTries;i++) {
155 while(*p != ' ' && *p != 0) p++;
156 if(*p == 0) break;
157 if(i != nrTries-1) {
158 while(*p == ' ' && *p != 0) p++;
159 }
160 }
161 if(nrTries > 1 && *p == 0) {
162 fEndOfCmdLine = TRUE;
163 }
164 }
165 *p = 0;
166 strupr(exeName);
167 cmdline = strstr(exeName, ".EXE");
168 if(cmdline) {
169 cmdline[4] = 0;
170 win32cmdline += ((ULONG)cmdline - (ULONG)exeName) + 4;
171 }
172 else {
173 win32cmdline += strlen(exeName);
174 if(strstr(exeName, ".") == NULL) {
175 strcat(exeName, ".EXE");
176 }
177 }
178 if(fQuote) win32cmdline++;
179 while(*win32cmdline == ' ') win32cmdline++; //skip spaces
180
181 cmdline = exeName + strlen(exeName) - 1;
182 while(*cmdline == ' ') cmdline--;
183 cmdline[1] = 0;
184
185 char drive[_MAX_DRIVE];
186 char dir[_MAX_DIR];
187 char fname[_MAX_FNAME];
188 char ext[_MAX_EXT];
189 char exeShortName[_MAX_FNAME+_MAX_EXT];
190 _splitpath(exeName, drive, dir, fname, ext);
191
192 strcpy(fullpath, drive);
193 strcat(fullpath, dir);
194
195 strcpy(exeShortName, fname);
196 strcat(exeShortName, ext);
197
198 if ( strlen(fullpath) == 0 )
199 {
200 char newExeName[CCHMAXPATH];
201
202 if(DosSearchPath( SEARCH_CUR_DIRECTORY | SEARCH_ENVIRONMENT | SEARCH_IGNORENETERRS
203 , "WINDOWSPATH" /* environment value */
204 , exeShortName /* Name of file to look for */
205 , newExeName /* Result of the search */
206 , sizeof(newExeName) /* Length of search buffer */
207 ) == NO_ERROR)
208 {
209 strcpy(exeName, newExeName);
210 }
211 }
212
213 FILESTATUS3 fstat3;
214 if(DosQueryPathInfo(exeName, FIL_STANDARD, (PVOID)&fstat3, sizeof(fstat3)))
215 {
216 nrTries++;
217 if(fEndOfCmdLine) {
218 pszErrorMsg = szFileNotFound;
219 goto failerror;
220 }
221
222 if(*win32cmdline != NULL && !fQuote) {
223 goto tryagain;
224 }
225 }
226 }
227 else {//should never happen!
228 pszErrorMsg = szDosInfoBlocks;
229 goto failerror;
230 }
231 AllocateExeMem(exeName, &fIsNEExe);
232 if(fIsNEExe) {
233 STARTDATA sdata = {0};
234 ULONG idSession;
235 PID pid;
236
237 sdata.Length = sizeof(sdata);
238 sdata.PgmName = "w16odin.exe";
239 strcpy(fullpath, exeName);
240 strcat(fullpath, " ");
241 strcat(fullpath, win32cmdline);
242 sdata.PgmInputs = fullpath;
243 sdata.FgBg = SSF_FGBG_FORE;
244 sdata.SessionType = SSF_TYPE_WINDOWEDVDM;
245 rc = DosStartSession(&sdata, &idSession, &pid);
246 if(rc) {
247 sprintf(fullpath, szErrDosStartSession, rc);
248 pszErrorMsg = fullpath;
249 goto failerror;
250 }
251 return 0;
252 }
253 }
254
255#ifdef COMMAND_LINE_VERSION
256 if(DosGetInfoBlocks(&ptib, &ppib) == 0) {
257 //switch process type to PM so the command line app can create PM
258 //windows
259 ppib->pib_ultype = 3;
260 }
261#endif
262
263 rc = DosLoadModule(exeName, sizeof(exeName), "PMWIN.DLL", &hmodPMWin);
264 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32INITIALIZE, NULL, (PFN *)&MyWinInitialize);
265 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32TERMINATE, NULL, (PFN *)&MyWinTerminate);
266 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32CREATEMSGQUEUE, NULL, (PFN *)&MyWinCreateMsgQueue);
267 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32DESTROYMSGQUEUE, NULL, (PFN *)&MyWinDestroyMsgQueue);
268 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32MESSAGEBOX, NULL, (PFN *)&MyWinMessageBox);
269
270 if ((hab = MyWinInitialize(0)) == 0L) /* Initialize PM */
271 goto fail;
272
273 hmq = MyWinCreateMsgQueue(hab, 0);
274
275 if(argc < 2) {
276 MyWinMessageBox(HWND_DESKTOP, NULL, INFO_BANNER, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
277 goto fail;
278 }
279
280 errorMod[0] = 0;
281 rc = DosLoadModule(errorMod, sizeof(errorMod), "KERNEL32.DLL", &hmodKernel32);
282 if(rc) {
283 sprintf(fullpath, szNoKernel32Msg, rc, errorMod);
284 MyWinMessageBox(HWND_DESKTOP, NULL, fullpath, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
285 goto fail;
286 }
287 rc = DosQueryProcAddr(hmodKernel32, 0, "_CreateWin32PeLdrExe@36", (PFN *)&CreateWin32Exe);
288
289#ifdef COMMAND_LINE_VERSION
290 fVioConsole = TRUE;
291#else
292 fVioConsole = FALSE;
293#endif
294 rc = CreateWin32Exe(exeName, win32cmdline, peoptions, reservedMemory, 0,
295 fConsoleApp, fVioConsole, errorMod, sizeof(errorMod));
296 if(rc != LDRERROR_SUCCESS)
297 {
298 char szErrorMsg[512];
299
300 switch(rc) {
301 case LDRERROR_INVALID_MODULE:
302 sprintf(szErrorMsg, szLoadErrorMsg, exeName);
303 break;
304 case LDRERROR_INVALID_CPU:
305 sprintf(pszErrorMsg, szCPUErrorMsg, exeName);
306 break;
307 case LDRERROR_FILE_SYSTEM:
308 sprintf(szErrorMsg, szExeErrorMsg, exeName);
309 break;
310 case LDRERROR_MEMORY:
311 sprintf(szErrorMsg, szErrorMemory, exeName);
312 break;
313 case LDRERROR_EXPORTS:
314 sprintf(szErrorMsg, szErrorExports, exeName);
315 break;
316 case LDRERROR_IMPORTS:
317 sprintf(szErrorMsg, szErrorImports, exeName, errorMod);
318 break;
319 case LDRERROR_INVALID_SECTION:
320 default:
321 sprintf(szErrorMsg, szInteralErrorMsg, exeName);
322 break;
323 }
324
325 WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
326 goto fail;
327 }
328
329 if(hmq) MyWinDestroyMsgQueue( hmq ); /* Tidy up... */
330 MyWinTerminate( hab ); /* Terminate the application */
331
332 DosFreeModule(hmodPMWin);
333 DosFreeModule(hmodKernel32);
334 return 0;
335
336fail:
337 if(hmq) MyWinDestroyMsgQueue( hmq ); /* Tidy up... */
338 if(hab) MyWinTerminate( hab ); /* Terminate the application */
339
340 if(hmodPMWin) DosFreeModule(hmodPMWin);
341 if(hmodKernel32) DosFreeModule(hmodKernel32);
342 return(1);
343
344failerror:
345 rc = DosLoadModule(exeName, sizeof(exeName), "PMWIN.DLL", &hmodPMWin);
346 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32INITIALIZE, NULL, (PFN *)&MyWinInitialize);
347 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32TERMINATE, NULL, (PFN *)&MyWinTerminate);
348 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32CREATEMSGQUEUE, NULL, (PFN *)&MyWinCreateMsgQueue);
349 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32DESTROYMSGQUEUE, NULL, (PFN *)&MyWinDestroyMsgQueue);
350 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32MESSAGEBOX, NULL, (PFN *)&MyWinMessageBox);
351 if(rc == 0) {
352 if ((hab = MyWinInitialize(0)) == 0L) /* Initialize PM */
353 goto fail;
354
355 hmq = MyWinCreateMsgQueue(hab, 0);
356
357 MyWinMessageBox(HWND_DESKTOP, NULL, pszErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
358 }
359 if(hmq) MyWinDestroyMsgQueue( hmq ); /* Tidy up... */
360 if(hab) MyWinTerminate( hab ); /* Terminate the application */
361 if(hmodPMWin) DosFreeModule(hmodPMWin);
362 return 1;
363}
364//******************************************************************************
365//SvL: Reserve memory for win32 exes without fixups
366// This is done before any Odin or PMWIN dll is loaded, so we'll get
367// a very low virtual address. (which is exactly what we want)
368//******************************************************************************
369BOOL AllocateExeMem(char *filename, BOOL *fNEExe)
370{
371 HFILE dllfile = 0;
372 char szFileName[CCHMAXPATH], *tmp;
373 char szResult[CCHMAXPATH];
374 ULONG action, ulRead, signature;
375 APIRET rc;
376 IMAGE_DOS_HEADER doshdr;
377 IMAGE_OPTIONAL_HEADER oh;
378 IMAGE_FILE_HEADER fh;
379 ULONG address = 0;
380 ULONG *memallocs;
381 ULONG alloccnt = 0;
382 ULONG diff, i, baseAddress;
383 ULONG ulSysinfo, flAllocMem = 0;
384 BOOL ret = FALSE;
385
386 *fNEExe = FALSE;
387 strcpy(szFileName, filename);
388
389 rc = DosOpen(szFileName, &dllfile, &action, 0, FILE_READONLY, OPEN_ACTION_OPEN_IF_EXISTS|OPEN_ACTION_FAIL_IF_NEW, OPEN_SHARE_DENYNONE|OPEN_ACCESS_READONLY, NULL);
390 if(rc != 0) {
391 if(!strstr(szFileName, ".EXE")) {
392 strcat(szFileName,".EXE");
393 }
394 }
395 else DosClose(dllfile);
396
397 rc = DosOpen(szFileName, &dllfile, &action, 0, FILE_READONLY, OPEN_ACTION_OPEN_IF_EXISTS|OPEN_ACTION_FAIL_IF_NEW, OPEN_SHARE_DENYNONE|OPEN_ACCESS_READONLY, NULL);
398 if(rc) {
399 if(DosSearchPath(SEARCH_IGNORENETERRS|SEARCH_ENVIRONMENT, "PATH",
400 szFileName, szResult, sizeof(szResult)) != 0) {
401 goto end; //oops
402 }
403 rc = DosOpen(szResult, &dllfile, &action, 0, FILE_READONLY, OPEN_ACTION_OPEN_IF_EXISTS|OPEN_ACTION_FAIL_IF_NEW, OPEN_SHARE_DENYNONE|OPEN_ACCESS_READONLY, NULL);
404 if(rc) {
405 goto end; //oops
406 }
407 }
408
409 //read dos header
410 if(DosRead(dllfile, (LPVOID)&doshdr, sizeof(doshdr), &ulRead)) {
411 goto end;
412 }
413 if(DosSetFilePtr(dllfile, doshdr.e_lfanew, FILE_BEGIN, &ulRead)) {
414 goto end;
415 }
416 //read signature dword
417 if(DosRead(dllfile, (LPVOID)&signature, sizeof(signature), &ulRead)) {
418 goto end;
419 }
420 //read pe header
421 if(DosRead(dllfile, (LPVOID)&fh, sizeof(fh), &ulRead)) {
422 goto end;
423 }
424 //read optional header
425 if(DosRead(dllfile, (LPVOID)&oh, sizeof(oh), &ulRead)) {
426 goto end;
427 }
428 if(doshdr.e_magic != IMAGE_DOS_SIGNATURE || signature != IMAGE_NT_SIGNATURE) {
429 if(LOWORD(signature) == IMAGE_OS2_SIGNATURE) {
430 *fNEExe = TRUE;
431 }
432 goto end;
433 }
434 fConsoleApp = (oh.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
435
436 // check for high memory support
437 rc = DosQuerySysInfo(QSV_VIRTUALADDRESSLIMIT, QSV_VIRTUALADDRESSLIMIT, &ulSysinfo, sizeof(ulSysinfo));
438 if (rc == 0 && ulSysinfo > 512) //VirtualAddresslimit is in MB
439 {
440 flAllocMem = PAG_ANY; // high memory support. Let's use it!
441 }
442
443 //Reserve enough space to store 4096 pointers to 1MB memory chunks
444 memallocs = (ULONG *)alloca(4096*sizeof(ULONG *));
445 if(memallocs == NULL) {
446 goto end; //oops
447 }
448
449 if(oh.ImageBase < 512*1024*1024) {
450 flAllocMem = 0;
451 }
452 else {
453 if(flAllocMem == 0) {
454 goto end; //no support for > 512 MB
455 }
456 }
457 while(TRUE) {
458 rc = DosAllocMem((PPVOID)&address, FALLOC_SIZE, PAG_READ | flAllocMem);
459 if(rc) break;
460
461 if(address + FALLOC_SIZE >= oh.ImageBase) {
462 if(address > oh.ImageBase) {//we've passed it!
463 DosFreeMem((PVOID)address);
464 break;
465 }
466 //found the right address
467 DosFreeMem((PVOID)address);
468
469 diff = oh.ImageBase - address;
470 if(diff) {
471 rc = DosAllocMem((PPVOID)&address, diff, PAG_READ | flAllocMem);
472 if(rc) break;
473 }
474 rc = DosAllocMem((PPVOID)&baseAddress, oh.SizeOfImage, PAG_READ | PAG_WRITE | flAllocMem);
475 if(rc) break;
476
477 if(diff) DosFreeMem((PVOID)address);
478
479 reservedMemory = baseAddress;
480 break;
481 }
482 memallocs[alloccnt++] = address;
483 }
484 for(i=0;i<alloccnt;i++) {
485 DosFreeMem((PVOID)memallocs[i]);
486 }
487 ret = TRUE;
488end:
489 if(dllfile) DosClose(dllfile);
490 return ret;
491}
492//******************************************************************************
493//******************************************************************************
Note: See TracBrowser for help on using the repository browser.