diff options
Diffstat (limited to '')
-rw-r--r-- | src/libs/xpcom18a4/xpcom/components/xcDll.cpp | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/components/xcDll.cpp b/src/libs/xpcom18a4/xpcom/components/xcDll.cpp new file mode 100644 index 00000000..494cf174 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/components/xcDll.cpp @@ -0,0 +1,457 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* nsDll + * + * Abstraction of a Dll. Stores modifiedTime and size for easy detection of + * change in dll. + * + * dp Suresh <dp@netscape.com> + */ + +#include "xcDll.h" +#include "nsDebug.h" +#include "nsIComponentManager.h" +#include "nsIComponentLoaderManager.h" +#include "nsIModule.h" +#include "nsILocalFile.h" +#include "nsDirectoryServiceDefs.h" +#include "nsDirectoryServiceUtils.h" +#include "nsCOMPtr.h" +#include "nsCRT.h" +#include "nsString.h" +#include "nsITimelineService.h" +#include "nsModule.h" +#ifdef DEBUG +#if defined(VMS) +#include <lib$routines.h> +#include <ssdef.h> +#elif defined(XP_MACOSX) +#include <signal.h> +#endif +#endif /* defined(DEBUG) */ + +#include "nsTraceRefcntImpl.h" + +#define UNLOAD_DEPENDENT_LIBS +#ifdef HPUX +#undef UNLOAD_DEPENDENT_LIBS +#endif + +#include "nsNativeComponentLoader.h" +#ifdef VBOX_USE_IPRT_IN_XPCOM +# include "nsMemory.h" +#endif + +nsDll::nsDll(nsIFile *dllSpec, nsNativeComponentLoader *loader) + : m_dllSpec(do_QueryInterface(dllSpec)), + m_instance(NULL), + m_moduleObject(NULL), + m_loader(loader), + m_markForUnload(PR_FALSE) +{ + NS_ASSERTION(loader, "Null loader when creating a nsDLL"); +} + +nsDll::~nsDll(void) +{ + //#if DEBUG_dougt + // The dll gets deleted when the dllStore is destroyed. This happens on + // app shutdown. At that point, unloading dlls can cause crashes if we have + // - dll dependencies + // - callbacks + // - static dtors + // Hence turn it back on after all the above have been removed. + //Unload(); + //#endif +} + +void +nsDll::GetDisplayPath(nsACString& aLeafName) +{ + m_dllSpec->GetNativeLeafName(aLeafName); + + if (aLeafName.IsEmpty()) + aLeafName.AssignLiteral("unknown!"); +} + +PRBool +nsDll::HasChanged() +{ + nsCOMPtr<nsIComponentLoaderManager> manager = do_QueryInterface(m_loader->mCompMgr); + if (!manager) + return PR_TRUE; + + // If mod date has changed, then dll has changed + PRInt64 currentDate; + nsresult rv = m_dllSpec->GetLastModifiedTime(¤tDate); + if (NS_FAILED(rv)) + return PR_TRUE; + PRBool changed = PR_TRUE; + manager->HasFileChanged(m_dllSpec, nsnull, currentDate, &changed); + return changed; +} + +PRBool nsDll::Load(void) +{ + if (m_instance != NULL) + { + // Already loaded + return (PR_TRUE); + } + + if (m_dllSpec) + { +#ifdef NS_BUILD_REFCNT_LOGGING + nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE); +#endif + + // Load any library dependencies + // The Component Loader Manager may hold onto some extra data + // set by either the native component loader or the native + // component. We assume that this data is a space delimited + // listing of dependent libraries which are required to be + // loaded prior to us loading the given component. Once, the + // component is loaded into memory, we can release our hold + // on the dependent libraries with the assumption that the + // component library holds a reference via the OS so loader. + +#if defined(XP_UNIX) + nsCOMPtr<nsIComponentLoaderManager> manager = do_QueryInterface(m_loader->mCompMgr); + if (!manager) + return PR_TRUE; + + nsXPIDLCString extraData; + manager->GetOptionalData(m_dllSpec, nsnull, getter_Copies(extraData)); + +#ifdef UNLOAD_DEPENDENT_LIBS + nsVoidArray dependentLibArray; +#endif + + // if there was any extra data, treat it as a listing of dependent libs + if (extraData != nsnull) + { + // all dependent libraries are suppose to be in the "gre" directory. + // note that the gre directory is the same as the "bin" directory, + // when there isn't a real "gre" found. + + nsXPIDLCString path; + nsCOMPtr<nsIFile> file; + NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(file)); + + if (!file) + return NS_ERROR_FAILURE; + + // we are talking about a file in the GRE dir. Lets append something + // stupid right now, so that later we can just set the leaf name. + file->AppendNative(NS_LITERAL_CSTRING("dummy")); + +# ifdef VBOX_USE_IPRT_IN_XPCOM + char *buffer = (char *)nsMemory::Clone(extraData, strlen(extraData) + 1); +# else + char *buffer = strdup(extraData); +# endif + if (!buffer) + return NS_ERROR_OUT_OF_MEMORY; + + char* newStr; + char *token = nsCRT::strtok(buffer, " ", &newStr); + while (token!=nsnull) + { + nsCStringKey key(token); + if (m_loader->mLoadedDependentLibs.Get(&key)) { + token = nsCRT::strtok(newStr, " ", &newStr); + continue; + } + + m_loader->mLoadedDependentLibs.Put(&key, (void*)1); + + nsXPIDLCString libpath; + file->SetNativeLeafName(nsDependentCString(token)); + file->GetNativePath(path); + if (!path) + return NS_ERROR_FAILURE; + + // Load this dependent library with the global flag and stash + // the result for later so that we can unload it. + PRLibSpec libSpec; + libSpec.type = PR_LibSpec_Pathname; + + // if the depend library path starts with a / we are + // going to assume that it is a full path and should + // be loaded without prepending the gre diretory + // location. We could have short circuited the + // SetNativeLeafName above, but this is clearer and + // the common case is a relative path. + + if (token[0] == '/') + libSpec.value.pathname = token; + else + libSpec.value.pathname = path; + + PRLibrary* lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_LAZY|PR_LD_GLOBAL); + // if we couldn't load the dependent library. We did the best we + // can. Now just let us fail later if this really was a required + // dependency. +#ifdef UNLOAD_DEPENDENT_LIBS + if (lib) + dependentLibArray.AppendElement((void*)lib); +#endif + + token = nsCRT::strtok(newStr, " ", &newStr); + } +# ifdef VBOX_USE_IPRT_IN_XPCOM + nsMemory::Free(buffer); +# else + free(buffer); +# endif + } +#endif + + // load the component + nsCOMPtr<nsILocalFile> lf(do_QueryInterface(m_dllSpec)); + NS_ASSERTION(lf, "nsIFile here must implement a nsILocalFile"); + lf->Load(&m_instance); + +#if defined(XP_UNIX) + // Unload any of library dependencies we loaded earlier. The assumption + // here is that the component will have a "internal" reference count to + // the dependency library we just loaded. + // XXX should we unload later - or even at all? + +#ifdef UNLOAD_DEPENDENT_LIBS + if (extraData != nsnull) + { + PRInt32 arrayCount = dependentLibArray.Count(); + for (PRInt32 index = 0; index < arrayCount; index++) + PR_UnloadLibrary((PRLibrary*)dependentLibArray.ElementAt(index)); + } +#endif +#endif + +#ifdef NS_BUILD_REFCNT_LOGGING + nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE); + if (m_instance) { + // Inform refcnt tracer of new library so that calls through the + // new library can be traced. + nsXPIDLCString displayPath; + GetDisplayPath(displayPath); + nsTraceRefcntImpl::LoadLibrarySymbols(displayPath.get(), m_instance); + } +#endif + } + +#ifdef SHOULD_IMPLEMENT_BREAKAFTERLOAD + // Debugging help for components. Component dlls need to have their + // symbols loaded before we can put a breakpoint in the debugger. + // This will help figureing out the point when the dll was loaded. + nsXPIDLCString displayPath; + GetDisplayPath(displayPath); + BreakAfterLoad(displayPath.get()); +#endif + + return ((m_instance == NULL) ? PR_FALSE : PR_TRUE); +} + +PRBool nsDll::Unload(void) +{ + if (m_instance == NULL) + return (PR_FALSE); + + // Shutdown the dll + Shutdown(); + +#ifdef NS_BUILD_REFCNT_LOGGING + nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE); +#endif + PRStatus ret = PR_UnloadLibrary(m_instance); +#ifdef NS_BUILD_REFCNT_LOGGING + nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE); +#endif + + if (ret == PR_SUCCESS) + { + m_instance = NULL; + return (PR_TRUE); + } + else + return (PR_FALSE); +} + +void * nsDll::FindSymbol(const char *symbol) +{ + if (symbol == NULL) + return (NULL); + + // If not already loaded, load it now. + if (Load() != PR_TRUE) + return (NULL); + + return(PR_FindSymbol(m_instance, symbol)); +} + + +// Component dll specific functions +nsresult nsDll::GetDllSpec(nsIFile **fsobj) +{ + NS_ASSERTION(m_dllSpec, "m_dllSpec NULL"); + NS_ASSERTION(fsobj, "xcDll::GetModule : Null argument" ); + + *fsobj = m_dllSpec; + NS_ADDREF(*fsobj); + return NS_OK; +} + +nsresult nsDll::GetModule(nsISupports *servMgr, nsIModule **cobj) +{ + // using the backpointer of the loader. + nsIComponentManager* compMgr = m_loader->mCompMgr; + NS_ASSERTION(compMgr, "Global Component Manager is null" ); + if (!compMgr) return NS_ERROR_UNEXPECTED; + + NS_ASSERTION(cobj, "xcDll::GetModule : Null argument" ); + + if (m_moduleObject) + { + NS_ADDREF(m_moduleObject); + *cobj = m_moduleObject; + return NS_OK; + } + + // If not already loaded, load it now. + if (Load() != PR_TRUE) return NS_ERROR_FAILURE; + + // We need a nsIFile for location + if (!m_dllSpec) + { + return NS_ERROR_FAILURE; + } + + nsGetModuleProc proc = + (nsGetModuleProc) FindSymbol(NS_GET_MODULE_SYMBOL); + + if (proc == NULL) + return NS_ERROR_FACTORY_NOT_LOADED; + + nsresult rv = (*proc) (compMgr, m_dllSpec, &m_moduleObject); + if (NS_SUCCEEDED(rv)) + { + NS_ADDREF(m_moduleObject); + *cobj = m_moduleObject; + } + return rv; +} + + +// These are used by BreakAfterLoad, below. +#ifdef SHOULD_IMPLEMENT_BREAKAFTERLOAD +static nsCString *sBreakList[16]; +static int sBreakListCount = 0; +#endif + +nsresult nsDll::Shutdown(void) +{ + // Release the module object if we got one + nsrefcnt refcnt; + if (m_moduleObject) + { + NS_RELEASE2(m_moduleObject, refcnt); + NS_ASSERTION(refcnt == 0, "Dll moduleObject refcount non zero"); + } +#ifdef SHOULD_IMPLEMENT_BREAKAFTERLOAD + for (int i = 0; i < sBreakListCount; i++) + { + delete sBreakList[i]; + sBreakList[i] = nsnull; + } + sBreakListCount = 0; +#endif + return NS_OK; + +} +#ifdef SHOULD_IMPLEMENT_BREAKAFTERLOAD +void nsDll::BreakAfterLoad(const char *nsprPath) +{ + static PRBool firstTime = PR_TRUE; + + // return if invalid input + if (!nsprPath || !*nsprPath) return; + + // return if nothing to break on + if (!firstTime && sBreakListCount == 0) return; + + if (firstTime) + { + firstTime = PR_FALSE; + // Form the list of dlls to break on load + nsCAutoString envList(getenv("XPCOM_BREAK_ON_LOAD")); + if (envList.IsEmpty()) return; + PRInt32 ofset = 0; + PRInt32 start = 0; + do + { + ofset = envList.FindChar(':', start); + sBreakList[sBreakListCount] = new nsCString(); + envList.Mid(*(sBreakList[sBreakListCount]), start, ofset); + sBreakListCount++; + start = ofset + 1; + } + while (ofset != -1 && 16 > sBreakListCount); // avoiding vc6.0 compiler issue. count < thinks it is starting a template + } + + // Find the dllname part of the string + nsCString currentPath(nsprPath); + PRInt32 lastSep = currentPath.RFindCharInSet(":\\/"); + + for (int i=0; i<sBreakListCount; i++) + if (currentPath.Find(*(sBreakList[i]), PR_TRUE, lastSep) > 0) + { + // Loading a dll that we want to break on + // Put your breakpoint here + fprintf(stderr, "...Loading module %s\n", nsprPath); + // Break in the debugger here. +#if defined(__i386) && defined(__GNUC__) + asm("int $3"); +#elif defined(VMS) + lib$signal(SS$_DEBUG); +#elif defined(XP_MACOSX) + raise(SIGTRAP); +#endif + } + return; +} +#endif /* SHOULD_IMPLEMENT_BREAKAFTERLOAD */ |