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