VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/components/xcDll.cpp@ 66250

Last change on this file since 66250 was 31259, checked in by vboxsync, 14 years ago

xpcom: Use RTMem* for memory alloc so that we can more easily wrap direct it to the electric fence heap.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38/* nsDll
39 *
40 * Abstraction of a Dll. Stores modifiedTime and size for easy detection of
41 * change in dll.
42 *
43 * dp Suresh <dp@netscape.com>
44 */
45
46#include "xcDll.h"
47#include "nsDebug.h"
48#include "nsIComponentManager.h"
49#include "nsIComponentLoaderManager.h"
50#include "nsIModule.h"
51#include "nsILocalFile.h"
52#include "nsDirectoryServiceDefs.h"
53#include "nsDirectoryServiceUtils.h"
54#include "nsCOMPtr.h"
55#include "nsCRT.h"
56#include "nsString.h"
57#include "nsITimelineService.h"
58#include "nsModule.h"
59#ifdef DEBUG
60#if defined(VMS)
61#include <lib$routines.h>
62#include <ssdef.h>
63#elif defined(XP_MACOSX)
64#include <signal.h>
65#endif
66#endif /* defined(DEBUG) */
67
68#include "nsTraceRefcntImpl.h"
69
70#define UNLOAD_DEPENDENT_LIBS
71#ifdef HPUX
72#undef UNLOAD_DEPENDENT_LIBS
73#endif
74
75#include "nsNativeComponentLoader.h"
76#ifdef VBOX_USE_IPRT_IN_XPCOM
77# include "nsMemory.h"
78#endif
79
80nsDll::nsDll(nsIFile *dllSpec, nsNativeComponentLoader *loader)
81 : m_dllSpec(do_QueryInterface(dllSpec)),
82 m_instance(NULL),
83 m_moduleObject(NULL),
84 m_loader(loader),
85 m_markForUnload(PR_FALSE)
86{
87 NS_ASSERTION(loader, "Null loader when creating a nsDLL");
88}
89
90nsDll::~nsDll(void)
91{
92 //#if DEBUG_dougt
93 // The dll gets deleted when the dllStore is destroyed. This happens on
94 // app shutdown. At that point, unloading dlls can cause crashes if we have
95 // - dll dependencies
96 // - callbacks
97 // - static dtors
98 // Hence turn it back on after all the above have been removed.
99 //Unload();
100 //#endif
101}
102
103void
104nsDll::GetDisplayPath(nsACString& aLeafName)
105{
106 m_dllSpec->GetNativeLeafName(aLeafName);
107
108 if (aLeafName.IsEmpty())
109 aLeafName.AssignLiteral("unknown!");
110}
111
112PRBool
113nsDll::HasChanged()
114{
115 nsCOMPtr<nsIComponentLoaderManager> manager = do_QueryInterface(m_loader->mCompMgr);
116 if (!manager)
117 return PR_TRUE;
118
119 // If mod date has changed, then dll has changed
120 PRInt64 currentDate;
121 nsresult rv = m_dllSpec->GetLastModifiedTime(&currentDate);
122 if (NS_FAILED(rv))
123 return PR_TRUE;
124 PRBool changed = PR_TRUE;
125 manager->HasFileChanged(m_dllSpec, nsnull, currentDate, &changed);
126 return changed;
127}
128
129PRBool nsDll::Load(void)
130{
131 if (m_instance != NULL)
132 {
133 // Already loaded
134 return (PR_TRUE);
135 }
136
137 if (m_dllSpec)
138 {
139#ifdef NS_BUILD_REFCNT_LOGGING
140 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
141#endif
142
143 // Load any library dependencies
144 // The Component Loader Manager may hold onto some extra data
145 // set by either the native component loader or the native
146 // component. We assume that this data is a space delimited
147 // listing of dependent libraries which are required to be
148 // loaded prior to us loading the given component. Once, the
149 // component is loaded into memory, we can release our hold
150 // on the dependent libraries with the assumption that the
151 // component library holds a reference via the OS so loader.
152
153#if defined(XP_UNIX)
154 nsCOMPtr<nsIComponentLoaderManager> manager = do_QueryInterface(m_loader->mCompMgr);
155 if (!manager)
156 return PR_TRUE;
157
158 nsXPIDLCString extraData;
159 manager->GetOptionalData(m_dllSpec, nsnull, getter_Copies(extraData));
160
161#ifdef UNLOAD_DEPENDENT_LIBS
162 nsVoidArray dependentLibArray;
163#endif
164
165 // if there was any extra data, treat it as a listing of dependent libs
166 if (extraData != nsnull)
167 {
168 // all dependent libraries are suppose to be in the "gre" directory.
169 // note that the gre directory is the same as the "bin" directory,
170 // when there isn't a real "gre" found.
171
172 nsXPIDLCString path;
173 nsCOMPtr<nsIFile> file;
174 NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(file));
175
176 if (!file)
177 return NS_ERROR_FAILURE;
178
179 // we are talking about a file in the GRE dir. Lets append something
180 // stupid right now, so that later we can just set the leaf name.
181 file->AppendNative(NS_LITERAL_CSTRING("dummy"));
182
183# ifdef VBOX_USE_IPRT_IN_XPCOM
184 char *buffer = (char *)nsMemory::Clone(extraData, strlen(extraData) + 1);
185# else
186 char *buffer = strdup(extraData);
187# endif
188 if (!buffer)
189 return NS_ERROR_OUT_OF_MEMORY;
190
191 char* newStr;
192 char *token = nsCRT::strtok(buffer, " ", &newStr);
193 while (token!=nsnull)
194 {
195 nsCStringKey key(token);
196 if (m_loader->mLoadedDependentLibs.Get(&key)) {
197 token = nsCRT::strtok(newStr, " ", &newStr);
198 continue;
199 }
200
201 m_loader->mLoadedDependentLibs.Put(&key, (void*)1);
202
203 nsXPIDLCString libpath;
204 file->SetNativeLeafName(nsDependentCString(token));
205 file->GetNativePath(path);
206 if (!path)
207 return NS_ERROR_FAILURE;
208
209 // Load this dependent library with the global flag and stash
210 // the result for later so that we can unload it.
211 PRLibSpec libSpec;
212 libSpec.type = PR_LibSpec_Pathname;
213
214 // if the depend library path starts with a / we are
215 // going to assume that it is a full path and should
216 // be loaded without prepending the gre diretory
217 // location. We could have short circuited the
218 // SetNativeLeafName above, but this is clearer and
219 // the common case is a relative path.
220
221 if (token[0] == '/')
222 libSpec.value.pathname = token;
223 else
224 libSpec.value.pathname = path;
225
226 PRLibrary* lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_LAZY|PR_LD_GLOBAL);
227 // if we couldn't load the dependent library. We did the best we
228 // can. Now just let us fail later if this really was a required
229 // dependency.
230#ifdef UNLOAD_DEPENDENT_LIBS
231 if (lib)
232 dependentLibArray.AppendElement((void*)lib);
233#endif
234
235 token = nsCRT::strtok(newStr, " ", &newStr);
236 }
237# ifdef VBOX_USE_IPRT_IN_XPCOM
238 nsMemory::Free(buffer);
239# else
240 free(buffer);
241# endif
242 }
243#endif
244
245 // load the component
246 nsCOMPtr<nsILocalFile> lf(do_QueryInterface(m_dllSpec));
247 NS_ASSERTION(lf, "nsIFile here must implement a nsILocalFile");
248 lf->Load(&m_instance);
249
250#if defined(XP_UNIX)
251 // Unload any of library dependencies we loaded earlier. The assumption
252 // here is that the component will have a "internal" reference count to
253 // the dependency library we just loaded.
254 // XXX should we unload later - or even at all?
255
256#ifdef UNLOAD_DEPENDENT_LIBS
257 if (extraData != nsnull)
258 {
259 PRInt32 arrayCount = dependentLibArray.Count();
260 for (PRInt32 index = 0; index < arrayCount; index++)
261 PR_UnloadLibrary((PRLibrary*)dependentLibArray.ElementAt(index));
262 }
263#endif
264#endif
265
266#ifdef NS_BUILD_REFCNT_LOGGING
267 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
268 if (m_instance) {
269 // Inform refcnt tracer of new library so that calls through the
270 // new library can be traced.
271 nsXPIDLCString displayPath;
272 GetDisplayPath(displayPath);
273 nsTraceRefcntImpl::LoadLibrarySymbols(displayPath.get(), m_instance);
274 }
275#endif
276 }
277
278#ifdef SHOULD_IMPLEMENT_BREAKAFTERLOAD
279 // Debugging help for components. Component dlls need to have their
280 // symbols loaded before we can put a breakpoint in the debugger.
281 // This will help figureing out the point when the dll was loaded.
282 nsXPIDLCString displayPath;
283 GetDisplayPath(displayPath);
284 BreakAfterLoad(displayPath.get());
285#endif
286
287 return ((m_instance == NULL) ? PR_FALSE : PR_TRUE);
288}
289
290PRBool nsDll::Unload(void)
291{
292 if (m_instance == NULL)
293 return (PR_FALSE);
294
295 // Shutdown the dll
296 Shutdown();
297
298#ifdef NS_BUILD_REFCNT_LOGGING
299 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
300#endif
301 PRStatus ret = PR_UnloadLibrary(m_instance);
302#ifdef NS_BUILD_REFCNT_LOGGING
303 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
304#endif
305
306 if (ret == PR_SUCCESS)
307 {
308 m_instance = NULL;
309 return (PR_TRUE);
310 }
311 else
312 return (PR_FALSE);
313}
314
315void * nsDll::FindSymbol(const char *symbol)
316{
317 if (symbol == NULL)
318 return (NULL);
319
320 // If not already loaded, load it now.
321 if (Load() != PR_TRUE)
322 return (NULL);
323
324 return(PR_FindSymbol(m_instance, symbol));
325}
326
327
328// Component dll specific functions
329nsresult nsDll::GetDllSpec(nsIFile **fsobj)
330{
331 NS_ASSERTION(m_dllSpec, "m_dllSpec NULL");
332 NS_ASSERTION(fsobj, "xcDll::GetModule : Null argument" );
333
334 *fsobj = m_dllSpec;
335 NS_ADDREF(*fsobj);
336 return NS_OK;
337}
338
339nsresult nsDll::GetModule(nsISupports *servMgr, nsIModule **cobj)
340{
341 // using the backpointer of the loader.
342 nsIComponentManager* compMgr = m_loader->mCompMgr;
343 NS_ASSERTION(compMgr, "Global Component Manager is null" );
344 if (!compMgr) return NS_ERROR_UNEXPECTED;
345
346 NS_ASSERTION(cobj, "xcDll::GetModule : Null argument" );
347
348 if (m_moduleObject)
349 {
350 NS_ADDREF(m_moduleObject);
351 *cobj = m_moduleObject;
352 return NS_OK;
353 }
354
355 // If not already loaded, load it now.
356 if (Load() != PR_TRUE) return NS_ERROR_FAILURE;
357
358 // We need a nsIFile for location
359 if (!m_dllSpec)
360 {
361 return NS_ERROR_FAILURE;
362 }
363
364 nsGetModuleProc proc =
365 (nsGetModuleProc) FindSymbol(NS_GET_MODULE_SYMBOL);
366
367 if (proc == NULL)
368 return NS_ERROR_FACTORY_NOT_LOADED;
369
370 nsresult rv = (*proc) (compMgr, m_dllSpec, &m_moduleObject);
371 if (NS_SUCCEEDED(rv))
372 {
373 NS_ADDREF(m_moduleObject);
374 *cobj = m_moduleObject;
375 }
376 return rv;
377}
378
379
380// These are used by BreakAfterLoad, below.
381#ifdef SHOULD_IMPLEMENT_BREAKAFTERLOAD
382static nsCString *sBreakList[16];
383static int sBreakListCount = 0;
384#endif
385
386nsresult nsDll::Shutdown(void)
387{
388 // Release the module object if we got one
389 nsrefcnt refcnt;
390 if (m_moduleObject)
391 {
392 NS_RELEASE2(m_moduleObject, refcnt);
393 NS_ASSERTION(refcnt == 0, "Dll moduleObject refcount non zero");
394 }
395#ifdef SHOULD_IMPLEMENT_BREAKAFTERLOAD
396 for (int i = 0; i < sBreakListCount; i++)
397 {
398 delete sBreakList[i];
399 sBreakList[i] = nsnull;
400 }
401 sBreakListCount = 0;
402#endif
403 return NS_OK;
404
405}
406#ifdef SHOULD_IMPLEMENT_BREAKAFTERLOAD
407void nsDll::BreakAfterLoad(const char *nsprPath)
408{
409 static PRBool firstTime = PR_TRUE;
410
411 // return if invalid input
412 if (!nsprPath || !*nsprPath) return;
413
414 // return if nothing to break on
415 if (!firstTime && sBreakListCount == 0) return;
416
417 if (firstTime)
418 {
419 firstTime = PR_FALSE;
420 // Form the list of dlls to break on load
421 nsCAutoString envList(getenv("XPCOM_BREAK_ON_LOAD"));
422 if (envList.IsEmpty()) return;
423 PRInt32 ofset = 0;
424 PRInt32 start = 0;
425 do
426 {
427 ofset = envList.FindChar(':', start);
428 sBreakList[sBreakListCount] = new nsCString();
429 envList.Mid(*(sBreakList[sBreakListCount]), start, ofset);
430 sBreakListCount++;
431 start = ofset + 1;
432 }
433 while (ofset != -1 && 16 > sBreakListCount); // avoiding vc6.0 compiler issue. count < thinks it is starting a template
434 }
435
436 // Find the dllname part of the string
437 nsCString currentPath(nsprPath);
438 PRInt32 lastSep = currentPath.RFindCharInSet(":\\/");
439
440 for (int i=0; i<sBreakListCount; i++)
441 if (currentPath.Find(*(sBreakList[i]), PR_TRUE, lastSep) > 0)
442 {
443 // Loading a dll that we want to break on
444 // Put your breakpoint here
445 fprintf(stderr, "...Loading module %s\n", nsprPath);
446 // Break in the debugger here.
447#if defined(__i386) && defined(__GNUC__)
448 asm("int $3");
449#elif defined(VMS)
450 lib$signal(SS$_DEBUG);
451#elif defined(XP_MACOSX)
452 raise(SIGTRAP);
453#endif
454 }
455 return;
456}
457#endif /* SHOULD_IMPLEMENT_BREAKAFTERLOAD */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette