source: trunk/src/kernel32/windlllx.cpp@ 9828

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

Added custombuild API for registering a callback for LX Dll loading thru LoadLibrary*()

File size: 15.1 KB
Line 
1/* $Id: windlllx.cpp,v 1.27 2003-02-20 09:47:01 sandervl Exp $ */
2
3/*
4 * Win32 LX Dll class (compiled in OS/2 using Odin32 api)
5 *
6 * Copyright 1999-2000 Sander van Leeuwen (sandervl@xs4all.nl)
7 *
8 * TODO: Unloading of dlls probably needs to be fixed due to OS/2 bug
9 * (wrong unload order of dlls)
10 *
11 * Project Odin Software License can be found in LICENSE.TXT
12 *
13 */
14#define INCL_DOSFILEMGR /* File Manager values */
15#define INCL_DOSERRORS /* DOS Error values */
16#define INCL_DOSPROCESS /* DOS Process values */
17#define INCL_DOSMODULEMGR
18#define INCL_DOSMISC /* DOS Miscellanous values */
19#define INCL_WIN
20#include <os2wrap.h> //Odin32 OS/2 api wrappers
21#include <os2newapi.h>
22#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25#include <iostream.h>
26#include <fstream.h>
27#include <misc.h>
28#include <win32type.h>
29#include <windllbase.h>
30#include <windlllx.h>
31#include "winexepe2lx.h"
32#include "winexepeldr.h"
33#include <odinlx.h>
34#include <wprocess.h>
35#include "oslibmisc.h"
36#include <custombuild.h>
37
38#include <exe386.h>
39
40#define DBG_LOCALLOG DBG_windlllx
41#include "dbglocal.h"
42
43/*******************************************************************************
44* Global Variables *
45*******************************************************************************/
46char *lpszCustomDllName = NULL;
47char *lpszCustomExportPrefix = NULL;
48ULONG dwOrdinalBase = 0;
49
50/**
51 * FreeLibrary callback for LX Dlls, it's call only when the library is actually
52 * being unloaded.
53 * Maintained by ODIN_SetLxDllUnLoadCallback().
54 */
55PFNLXDLLUNLOAD pfnLxDllUnLoadCallback = NULL;
56
57//******************************************************************************
58//******************************************************************************
59void WIN32API SetCustomBuildName(char *lpszName, DWORD ordinalbase,
60 char *lpszExportPrefix)
61{
62 lpszCustomDllName = lpszName;
63 dwOrdinalBase = ordinalbase;
64 lpszCustomExportPrefix = lpszExportPrefix;
65}
66//******************************************************************************
67//Create LX Dll object and send process attach message
68//System dlls set EntryPoint to 0
69//Parameters:
70// HINSTANCE hInstance - OS/2 module handle
71// WIN32DLLENTRY EntryPoint - Win32 dll entrypoint address
72// PVOID pResData - pointer to win32 resource data
73// DWORD MajorImageVersion - major image/os version (for fake win32 header)
74// DWORD MinorImageVersion - minor image/os version (for fake win32 header)
75// DWORD Subsystem - subsystem type (for fake win32 header)
76// (IMAGE_SUBSYSTEM_WINDOWS_CUI/IMAGE_SUBSYSTEM_WINDOWS_GUI)
77//
78//Returns: Odin32 module handle
79//******************************************************************************
80DWORD WIN32API RegisterLxDll(HINSTANCE hInstance, WIN32DLLENTRY EntryPoint,
81 PVOID pResData,
82 DWORD MajorImageVersion,
83 DWORD MinorImageVersion,
84 DWORD Subsystem)
85{
86 APIRET rc;
87 Win32LxDll *windll;
88 Win32DllBase *windlldep;
89 char szFileName[CCHMAXPATH], szErrName[CCHMAXPATH];
90
91 if(!lpszCustomDllName) {
92 if(OSLibGetDllName(hInstance, szFileName, sizeof(szFileName)) == FALSE) {
93 dprintf(("ERROR: RegisterLxDll: OSLibGetDllName %x failed!!", hInstance));
94 return 0;
95 }
96 dprintf(("RegisterLxDll %x %s", hInstance, szFileName));
97 //Make sure DosLoadModule is called at least once for a dll (to make sure
98 //OS/2 doesn't unload the dll when it's still needed)
99 rc = DosLoadModule(szErrName, sizeof(szErrName), szFileName, &hInstance);
100 if(rc != 0) {
101 dprintf(("ERROR: RegisterLxDll: DosLoadModule %s failed (rc=%d)!!", szFileName, rc));
102 return 0;
103 }
104 }
105 windll = new Win32LxDll(hInstance, EntryPoint, pResData, MajorImageVersion,
106 MinorImageVersion, Subsystem);
107 if(windll == NULL) {
108 dprintf(("RegisterLxDll: windll == NULL!!!"));
109 return 0;
110 }
111 //clear name override in case dll init loads another dll
112 lpszCustomDllName = NULL;
113
114 if(!fPeLoader) {
115 windll->AddRef();
116
117 if(windll->attachProcess() == 0)
118 return 0;
119
120 return windll->getInstanceHandle();
121 }
122 IMAGE_DOS_HEADER doshdr;
123 struct e32_exe lxhdr;
124 ULONG offset;
125 char modulename[CCHMAXPATH];
126 char modsize;
127 int i;
128
129 //SvL: This code reads the import name table of the dll to get the dependencies
130 // on other dlls.
131 //DosQueryHeaderInfo is an undocumented api, but works very well.
132 //(no need to save FS here as we'll return to OS/2 immediately)
133 rc = DosQueryHeaderInfo(hInstance, 0, &doshdr, sizeof(IMAGE_DOS_HEADER), QHINF_READFILE);
134 if(rc) {
135 goto hdrerror;
136 }
137 rc = DosQueryHeaderInfo(hInstance, doshdr.e_lfanew, &lxhdr, sizeof(e32_exe), QHINF_READFILE);
138 if(rc) {
139 goto hdrerror;
140 }
141 offset = doshdr.e_lfanew + lxhdr.e32_impmod;
142 for(i=0;i<lxhdr.e32_impmodcnt;i++) {
143 rc = DosQueryHeaderInfo(hInstance, offset, &modsize, 1, QHINF_READFILE);
144 if(rc) {
145 goto hdrerror;
146 }
147 rc = DosQueryHeaderInfo(hInstance, offset+1, &modulename, min(modsize, sizeof(modulename)), QHINF_READFILE);
148 if(rc) {
149 goto hdrerror;
150 }
151 modulename[modsize] = 0;
152 windlldep = Win32DllBase::findModule(modulename, TRUE);
153 if(windlldep && strcmp(windlldep->getModuleName(), windll->getModuleName())) {
154 dprintf(("RegisterLxDll: Add dependency %s -> %s", windll->getModuleName(), modulename));
155 windll->addDependency(windlldep);
156 }
157 else dprintf(("HARMLESS WARNING: Can't find dll %s referenced by %s", modulename, windll->getModuleName()));
158
159 offset += modsize + 1;
160 }
161#ifdef HACK_NEVER_UNLOAD_LX_DLLS
162 //HACK ALERT!!
163 //This makes sure the LX dll never gets unloaded.
164 //Necessary since unloading doesn't work due to dependencies on dlls
165 //with exitlist handlers.
166 windll->AddRef();
167#endif
168 return windll->getInstanceHandle();
169
170hdrerror:
171 dprintf(("DosQueryHeaderInfo returned %d", rc));
172 return windll->getInstanceHandle();
173}
174//******************************************************************************
175//Destroy LX Dll object
176//******************************************************************************
177BOOL WIN32API UnregisterLxDll(HINSTANCE hInstance)
178{
179 Win32DllBase *windll;
180
181 //Don't proceed for pe2lx/win32k (os/2 dll unload dependency bug)
182 //Don't do it either after ExitProcess has been called
183 if(!fPeLoader || WinExe == NULL)
184 return TRUE;
185
186 windll = Win32DllBase::findModule(hInstance);
187 if(!windll) {
188 dprintf(("UnregisterLxDll: Can't find dll with handle %x (already deleted)", hInstance));
189 return TRUE; //already deleted by Win32LxDll::Release
190 }
191 dprintf(("UnregisterLxDll %s", windll->getModuleName()));
192 //This can only happen for LX dependencies (i.e. wininet loads wsock32)
193 delete windll;
194 return TRUE;
195}
196//******************************************************************************
197//******************************************************************************
198Win32LxDll::Win32LxDll(HINSTANCE hInstance, WIN32DLLENTRY EntryPoint, PVOID pResData,
199 DWORD MajorImageVersion, DWORD MinorImageVersion,
200 DWORD Subsystem)
201 : Win32ImageBase(hInstance),
202 Win32LxImage(hInstance, pResData),
203 Win32DllBase(hInstance, EntryPoint)
204{
205 this->MajorImageVersion = MajorImageVersion;
206 this->MinorImageVersion = MinorImageVersion;
207 this->Subsystem = Subsystem;
208
209 if(EntryPoint == NULL) {
210 fSkipThreadEntryCalls = TRUE;
211 fAttachedToProcess = TRUE;
212 }
213 hinstanceOS2 = hInstance;
214 //new win32 instance handle must be pointer to PE header
215 hinstance = (HINSTANCE)buildHeader(MajorImageVersion, MinorImageVersion,
216 Subsystem);
217 dprintf(("Fake PE header %x for dll %s", hinstance, getModuleName()));
218}
219//******************************************************************************
220//******************************************************************************
221Win32LxDll::~Win32LxDll()
222{
223}
224//******************************************************************************
225//Returns reference count or -1 if load failed (PE loader only!)
226//******************************************************************************
227#ifdef DEBUG
228ULONG Win32LxDll::AddRef(char *parentname)
229#else
230ULONG Win32LxDll::AddRef()
231#endif
232{
233 Win32DllBase *dll;
234 QueueItem *item;
235 ULONG ret;
236
237#ifdef DEBUG
238 ret = Win32DllBase::AddRef(parentname);
239#else
240 ret = Win32DllBase::AddRef();
241#endif
242
243 if(!fPeLoader)
244 return ret;
245
246 if(referenced == 1)
247 {
248 item = loadedDlls.Head();
249 while(item) {
250 dll = (Win32DllBase *)loadedDlls.getItem(item);
251 if(dll == NULL) {
252 dprintf(("ERROR: Win32DllBase::AddRef: dll item == NULL!!"));
253 DebugInt3();
254 return -1;
255 }
256#ifdef DEBUG
257 dll->AddRef(getModuleName());
258#else
259 dll->AddRef();
260#endif
261 item = loadedDlls.getNext(item);
262 }
263 if(attachProcess() == 0) {
264 dprintf(("WARNING: Dll %s refused to be loaded; aborting", getName()));
265 return -1;
266 }
267 }
268 return ret;
269}
270//******************************************************************************
271//******************************************************************************
272ULONG Win32LxDll::Release()
273{
274 HINSTANCE hinst = hinstanceOS2;
275 HINSTANCE hinstOdin = hinstance;
276 WIN32DLLENTRY EntryPointTmp = dllEntryPoint;
277 PVOID pResDataTmp = (PVOID)pResRootDir;
278 DWORD MajorImageVersionTmp = MajorImageVersion;
279 DWORD MinorImageVersionTmp = MinorImageVersion;
280 DWORD SubsystemTmp = Subsystem;
281 BOOL fNoUnload = fDisableUnload; //only set for kernel32.dll
282 BOOL fDynLoaded = isDynamicLib();
283 ULONG ret;
284 APIRET rc;
285
286 ret = Win32DllBase::Release();
287
288 /** @sketch
289 * If this module is not unreference and it was loaded using LoadLibrary() then
290 * call the custombuild callback if present.
291 * The callback should call UnRegisterLxDll().
292 * Endif
293 *
294 * @remark
295 * #ifdef HACK_NEVER_UNLOAD_LX_DLLS
296 * This will never be called!
297 * #endif
298 *
299 * @todo: call pfnLxDllLoadCallback if DosFreeModule failes.
300 * It's not implemented because it's complex and at the moment we will
301 * never actually get here.
302 * So, this callback is here just as a reminder eventhough it's working..
303 */
304 if (!ret && fDynLoaded && pfnLxDllUnLoadCallback)
305 {
306 pfnLxDllUnLoadCallback(hinstanceOS2, hinstance);
307 }
308
309 if (ret == 0 && !fNoUnload) {//only set for kernel32.dll (fDisableUnload)
310 //DosFreeModule sends a termination message to the dll.
311 //The LX dll informs us when it's removed (UnregisterDll call)
312 rc = DosFreeModule(hinst);
313 if(rc) {
314 dprintf(("Win32LxDll::Release: DosFreeModule %x returned %d", hinst, rc));
315 if(rc == ERROR_INVALID_ACCESS && !fExitProcess)
316 {
317 #ifndef HACK_NEVER_UNLOAD_LX_DLLS
318 #error @todo: call pfnLxDllLoadCallback and let it do the registering if it did that on the initial load..
319 #endif
320 //Dll refused to unload because it has an active exitlist handler
321 //or depends on a dll that registered an exitlist handler
322 //In this case the handle remains valid and the entrypoint of
323 //the dll is NOT called for DLL_PROCESS_DETACH
324 //WORKAROUND: Re-register the dll so future LoadLibrary calls
325 // don't fail!
326 dprintf(("WORKAROUND: Re-register the dll so future LoadLibrary calls don't fail!"));
327 RegisterLxDll(hinst, EntryPointTmp, pResDataTmp,
328 MajorImageVersionTmp,
329 MinorImageVersionTmp,
330 SubsystemTmp);
331
332 /* OS/2 dll, system dll, converted dll or win32k took care of it. */
333 Win32LxDll *pModule = Win32LxDll::findModuleByOS2Handle(hinst);
334 if(pModule)
335 {
336 pModule->setDllHandleOS2(hinst);
337 if(fPeLoader)
338 {
339 if(pModule->AddRef() == -1) {//-1 -> load failed (attachProcess)
340 dprintf(("ERROR Dll refused to be loaded; aborting"));
341 DebugInt3();
342 delete pModule;
343 return 0;
344 }
345 }
346 pModule->incDynamicLib();
347 }
348 else DebugInt3();
349 }
350 }
351 }
352 return(ret);
353}
354//******************************************************************************
355//******************************************************************************
356BOOL Win32LxDll::isPe2LxDll() const
357{
358 return FALSE;
359}
360//******************************************************************************
361//******************************************************************************
362BOOL Win32LxDll::isLxDll() const
363{
364 return TRUE;
365}
366//******************************************************************************
367//******************************************************************************
368void Win32LxDll::setDllHandleOS2(HINSTANCE hInstanceOS2)
369{
370 //Loaded with LoadLibrary(Ex); no need for a 2nd DosLoadModule
371 //Dlls that are indirectly loaded (i.e. GDI32->KERNEL32 dependancy) need
372 //this additional DosLoadModule (and setDllHandleOS2 isn't called for those)
373 if(this->hinstanceOS2) {
374 DosFreeModule(this->hinstanceOS2);
375 }
376 this->hinstanceOS2 = hInstanceOS2;
377}
378//******************************************************************************
379//******************************************************************************
380Win32LxDll *Win32LxDll::findModuleByOS2Handle(HINSTANCE hinstance)
381{
382 dlllistmutex.enter();
383
384 Win32DllBase *mod = Win32DllBase::getFirst();
385 while (mod != NULL)
386 {
387 if (mod->isLxDll())
388 {
389 Win32LxDll *lxdll = (Win32LxDll *)mod;
390 if (lxdll->hinstanceOS2 == hinstance)
391 {
392 dlllistmutex.leave();
393 return(lxdll);
394 }
395 }
396 mod = mod->getNext();
397 }
398 dlllistmutex.leave();
399 return(NULL);
400}
401//******************************************************************************
402//******************************************************************************
403
404/**
405 * Custombuild API for registering a callback for LX Dll loading thru LoadLibrary*().
406 * @returns Success indicator.
407 * @param pfn Pointer to callback.
408 * NULL if callback is deregistered.
409 */
410BOOL WIN32API ODIN_SetLxDllUnLoadCallback(PFNLXDLLUNLOAD pfn)
411{
412 pfnLxDllUnLoadCallback = pfn;
413 return TRUE;
414}
415
Note: See TracBrowser for help on using the repository browser.