1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6  * This Original Code has been modified by IBM Corporation.
7  * Modifications made by IBM described herein are
8  * Copyright (c) International Business Machines
9  * Corporation, 2000
10  *
11  * Modifications to Mozilla code or documentation
12  * identified per MPL Section 3.3
13  *
14  * Date             Modified by     Description of modification
15  * 04/20/2000       IBM Corp.      Added PR_CALLBACK for Optlink use in OS2
16  */
17 
18 #include "nsNativeModuleLoader.h"
19 
20 #include "mozilla/Logging.h"
21 #include "prinit.h"
22 #include "prerror.h"
23 
24 #include "nsComponentManager.h"
25 #include "ManifestParser.h" // for LogMessage
26 #include "nsCRTGlue.h"
27 #include "nsThreadUtils.h"
28 #include "nsTraceRefcnt.h"
29 
30 #include "nsIFile.h"
31 #include "mozilla/WindowsDllBlocklist.h"
32 
33 #ifdef XP_WIN
34 #include <windows.h>
35 #endif
36 
37 #ifdef XP_MACOSX
38 #include <signal.h>
39 #endif
40 
41 #ifdef DEBUG
42 #define IMPLEMENT_BREAK_AFTER_LOAD
43 #endif
44 
45 using namespace mozilla;
46 
47 static LazyLogModule sNativeModuleLoaderLog("nsNativeModuleLoader");
48 #define LOG(level, args) MOZ_LOG(sNativeModuleLoaderLog, level, args)
49 
50 nsresult
Init()51 nsNativeModuleLoader::Init()
52 {
53   MOZ_ASSERT(NS_IsMainThread(), "Startup not on main thread?");
54   LOG(LogLevel::Debug, ("nsNativeModuleLoader::Init()"));
55   return NS_OK;
56 }
57 
58 class LoadModuleMainThreadRunnable : public Runnable
59 {
60 public:
LoadModuleMainThreadRunnable(nsNativeModuleLoader * aLoader,FileLocation & aFile)61   LoadModuleMainThreadRunnable(nsNativeModuleLoader* aLoader,
62                                FileLocation& aFile)
63     : mManager(nsComponentManagerImpl::gComponentManager)
64     , mLoader(aLoader)
65     , mFile(aFile)
66     , mResult(nullptr)
67   {
68   }
69 
Run()70   NS_IMETHOD Run() override
71   {
72     mResult = mLoader->LoadModule(mFile);
73     return NS_OK;
74   }
75 
76   RefPtr<nsComponentManagerImpl> mManager;
77   nsNativeModuleLoader* mLoader;
78   FileLocation mFile;
79   const mozilla::Module* mResult;
80 };
81 
82 const mozilla::Module*
LoadModule(FileLocation & aFile)83 nsNativeModuleLoader::LoadModule(FileLocation& aFile)
84 {
85   if (aFile.IsZip()) {
86     NS_ERROR("Binary components cannot be loaded from JARs");
87     return nullptr;
88   }
89   nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
90   nsresult rv;
91 
92   if (!NS_IsMainThread()) {
93     // If this call is off the main thread, synchronously proxy it
94     // to the main thread.
95     RefPtr<LoadModuleMainThreadRunnable> r =
96       new LoadModuleMainThreadRunnable(this, aFile);
97     NS_DispatchToMainThread(r, NS_DISPATCH_SYNC);
98     return r->mResult;
99   }
100 
101   nsCOMPtr<nsIHashable> hashedFile(do_QueryInterface(file));
102   if (!hashedFile) {
103     NS_ERROR("nsIFile is not nsIHashable");
104     return nullptr;
105   }
106 
107   nsAutoCString filePath;
108   file->GetNativePath(filePath);
109 
110   NativeLoadData data;
111 
112   if (mLibraries.Get(hashedFile, &data)) {
113     NS_ASSERTION(data.mModule, "Corrupt mLibraries hash");
114     LOG(LogLevel::Debug,
115         ("nsNativeModuleLoader::LoadModule(\"%s\") - found in cache",
116          filePath.get()));
117     return data.mModule;
118   }
119 
120   // We haven't loaded this module before
121   {
122 #ifdef HAS_DLL_BLOCKLIST
123     AutoSetXPCOMLoadOnMainThread guard;
124 #endif
125     rv = file->Load(&data.mLibrary);
126   }
127 
128   if (NS_FAILED(rv)) {
129     char errorMsg[1024] = "<unknown; can't get error from NSPR>";
130 
131     if (PR_GetErrorTextLength() < (int)sizeof(errorMsg)) {
132       PR_GetErrorText(errorMsg);
133     }
134 
135     LogMessage("Failed to load native module at path '%s': (%lx) %s",
136                filePath.get(), rv, errorMsg);
137 
138     return nullptr;
139   }
140 
141 #ifdef IMPLEMENT_BREAK_AFTER_LOAD
142   nsAutoCString leafName;
143   file->GetNativeLeafName(leafName);
144 
145   char* env = getenv("XPCOM_BREAK_ON_LOAD");
146   char* blist;
147   if (env && *env && (blist = strdup(env))) {
148     char* nextTok = blist;
149     while (char* token = NS_strtok(":", &nextTok)) {
150       if (leafName.Find(token, true) != kNotFound) {
151         NS_BREAK();
152       }
153     }
154 
155     free(blist);
156   }
157 #endif
158 
159   void* module = PR_FindSymbol(data.mLibrary, "NSModule");
160   if (!module) {
161     LogMessage("Native module at path '%s' doesn't export symbol `NSModule`.",
162                filePath.get());
163     PR_UnloadLibrary(data.mLibrary);
164     return nullptr;
165   }
166 
167   data.mModule = *(mozilla::Module const* const*)module;
168   if (mozilla::Module::kVersion != data.mModule->mVersion) {
169     LogMessage("Native module at path '%s' is incompatible with this version of Firefox, has version %i, expected %i.",
170                filePath.get(), data.mModule->mVersion,
171                mozilla::Module::kVersion);
172     PR_UnloadLibrary(data.mLibrary);
173     return nullptr;
174   }
175 
176   mLibraries.Put(hashedFile, data); // infallible
177   return data.mModule;
178 }
179 
180 void
UnloadLibraries()181 nsNativeModuleLoader::UnloadLibraries()
182 {
183   MOZ_ASSERT(NS_IsMainThread(), "Shutdown not on main thread?");
184 
185   for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) {
186     NativeLoadData& loadData = iter.Data();
187     loadData.mModule = nullptr;
188   }
189 
190   for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) {
191     if (MOZ_LOG_TEST(sNativeModuleLoaderLog, LogLevel::Debug)) {
192       nsIHashable* hashedFile = iter.Key();
193       nsCOMPtr<nsIFile> file(do_QueryInterface(hashedFile));
194 
195       nsAutoCString filePath;
196       file->GetNativePath(filePath);
197 
198       LOG(LogLevel::Debug,
199           ("nsNativeModuleLoader::UnloaderFunc(\"%s\")", filePath.get()));
200     }
201 
202 #ifdef NS_BUILD_REFCNT_LOGGING
203     nsTraceRefcnt::SetActivityIsLegal(false);
204 #endif
205 
206 #if 0
207     // XXXbsmedberg: do this as soon as the static-destructor crash(es)
208     // are fixed
209     NativeLoadData& loadData = iter.Data();
210     PRStatus ret = PR_UnloadLibrary(loadData.mLibrary);
211     NS_ASSERTION(ret == PR_SUCCESS, "Failed to unload library");
212 #endif
213 
214 #ifdef NS_BUILD_REFCNT_LOGGING
215     nsTraceRefcnt::SetActivityIsLegal(true);
216 #endif
217 
218     iter.Remove();
219   }
220 }
221