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
7 #include "mozilla/Bootstrap.h"
8
9 #include "nspr.h"
10 #include "nsDebug.h"
11 #include "nsXPCOMPrivate.h"
12 #include "nsCOMPtr.h"
13 #include <stdlib.h>
14 #include <stdio.h>
15
16 #include "mozilla/FileUtils.h"
17 #include "mozilla/Result.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/UniquePtr.h"
20 #include "mozilla/UniquePtrExtensions.h"
21
22 using namespace mozilla;
23
24 #define XPCOM_DEPENDENT_LIBS_LIST "dependentlibs.list"
25
26 #if defined(XP_WIN)
27 # define READ_TEXTMODE L"rt"
28 #else
29 # define READ_TEXTMODE "r"
30 #endif
31
32 typedef void (*NSFuncPtr)();
33
34 #if defined(XP_WIN)
35 # include <windows.h>
36 using LibHandleType = HMODULE;
37 #else
38 using LibHandleType = void*;
39 #endif
40
41 using LibHandleResult = ::mozilla::Result<LibHandleType, DLErrorType>;
42
43 #if defined(XP_WIN)
44 # include <mbstring.h>
45 # include "mozilla/WindowsVersion.h"
46 # include "mozilla/PreXULSkeletonUI.h"
47
GetLibHandle(pathstr_t aDependentLib)48 static LibHandleResult GetLibHandle(pathstr_t aDependentLib) {
49 LibHandleType libHandle =
50 LoadLibraryExW(aDependentLib, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
51
52 if (!libHandle) {
53 DWORD err = GetLastError();
54 # if defined(DEBUG)
55 LPWSTR lpMsgBuf;
56 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
57 FORMAT_MESSAGE_IGNORE_INSERTS,
58 nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
59 (LPWSTR)&lpMsgBuf, 0, nullptr);
60 wprintf(L"Error loading %ls: %s\n", aDependentLib, lpMsgBuf);
61 LocalFree(lpMsgBuf);
62 # endif // defined(DEBUG)
63 return Err(err);
64 }
65
66 return libHandle;
67 }
68
GetSymbol(LibHandleType aLibHandle,const char * aSymbol)69 static NSFuncPtr GetSymbol(LibHandleType aLibHandle, const char* aSymbol) {
70 return (NSFuncPtr)GetProcAddress(aLibHandle, aSymbol);
71 }
72
CloseLibHandle(LibHandleType aLibHandle)73 static void CloseLibHandle(LibHandleType aLibHandle) {
74 FreeLibrary(aLibHandle);
75 }
76
77 #else
78 # include <dlfcn.h>
79
80 # if defined(MOZ_LINKER)
81 extern "C" {
82 NS_HIDDEN __typeof(dlopen) __wrap_dlopen;
83 NS_HIDDEN __typeof(dlsym) __wrap_dlsym;
84 NS_HIDDEN __typeof(dlclose) __wrap_dlclose;
85 }
86
87 # define dlopen __wrap_dlopen
88 # define dlsym __wrap_dlsym
89 # define dlclose __wrap_dlclose
90 # endif
91
GetLibHandle(pathstr_t aDependentLib)92 static LibHandleResult GetLibHandle(pathstr_t aDependentLib) {
93 LibHandleType libHandle = dlopen(aDependentLib, RTLD_GLOBAL | RTLD_LAZY
94 # ifdef XP_MACOSX
95 | RTLD_FIRST
96 # endif
97 );
98 if (!libHandle) {
99 UniqueFreePtr<char> errMsg(strdup(dlerror()));
100 fprintf(stderr, "XPCOMGlueLoad error for file %s:\n%s\n", aDependentLib,
101 errMsg.get());
102 return Err(std::move(errMsg));
103 }
104 return libHandle;
105 }
106
GetSymbol(LibHandleType aLibHandle,const char * aSymbol)107 static NSFuncPtr GetSymbol(LibHandleType aLibHandle, const char* aSymbol) {
108 return (NSFuncPtr)dlsym(aLibHandle, aSymbol);
109 }
110
111 # if !defined(MOZ_LINKER) && !defined(__ANDROID__)
CloseLibHandle(LibHandleType aLibHandle)112 static void CloseLibHandle(LibHandleType aLibHandle) { dlclose(aLibHandle); }
113 # endif
114 #endif
115
116 struct DependentLib {
117 LibHandleType libHandle;
118 DependentLib* next;
119 };
120
121 static DependentLib* sTop;
122
AppendDependentLib(LibHandleType aLibHandle)123 static void AppendDependentLib(LibHandleType aLibHandle) {
124 auto* d = new DependentLib;
125 if (!d) {
126 return;
127 }
128
129 d->next = sTop;
130 d->libHandle = aLibHandle;
131
132 sTop = d;
133 }
134
135 using ReadDependentCBResult = ::mozilla::Result<::mozilla::Ok, DLErrorType>;
136
ReadDependentCB(pathstr_t aDependentLib,LibLoadingStrategy aLibLoadingStrategy)137 static ReadDependentCBResult ReadDependentCB(
138 pathstr_t aDependentLib, LibLoadingStrategy aLibLoadingStrategy) {
139 #if !defined(MOZ_LINKER) && !defined(__ANDROID__)
140 // Don't bother doing a ReadAhead if we're not in the parent process.
141 // What we need from the library should already be in the system file
142 // cache.
143 if (aLibLoadingStrategy == LibLoadingStrategy::ReadAhead) {
144 ReadAheadLib(aDependentLib);
145 }
146 #endif
147 LibHandleType libHandle;
148 MOZ_TRY_VAR(libHandle, GetLibHandle(aDependentLib));
149
150 AppendDependentLib(libHandle);
151 return Ok();
152 }
153
154 #ifdef XP_WIN
ReadDependentCB(const char * aDependentLib,LibLoadingStrategy aLibLoadingStrategy)155 static ReadDependentCBResult ReadDependentCB(
156 const char* aDependentLib, LibLoadingStrategy aLibLoadingStrategy) {
157 wchar_t wideDependentLib[MAX_PATH];
158 MultiByteToWideChar(CP_UTF8, 0, aDependentLib, -1, wideDependentLib,
159 MAX_PATH);
160 return ReadDependentCB(wideDependentLib, aLibLoadingStrategy);
161 }
162
TS_tfopen(const char * path,const wchar_t * mode)163 inline FILE* TS_tfopen(const char* path, const wchar_t* mode) {
164 wchar_t wPath[MAX_PATH];
165 MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH);
166 return _wfopen(wPath, mode);
167 }
168 #else
TS_tfopen(const char * aPath,const char * aMode)169 inline FILE* TS_tfopen(const char* aPath, const char* aMode) {
170 return fopen(aPath, aMode);
171 }
172 #endif
173
174 /* RAII wrapper for FILE descriptors */
175 struct ScopedCloseFileTraits {
176 typedef FILE* type;
emptyScopedCloseFileTraits177 static type empty() { return nullptr; }
releaseScopedCloseFileTraits178 static void release(type aFile) {
179 if (aFile) {
180 fclose(aFile);
181 }
182 }
183 };
184 typedef Scoped<ScopedCloseFileTraits> ScopedCloseFile;
185
186 #if !defined(MOZ_LINKER) && !defined(__ANDROID__)
XPCOMGlueUnload()187 static void XPCOMGlueUnload() {
188 while (sTop) {
189 CloseLibHandle(sTop->libHandle);
190
191 DependentLib* temp = sTop;
192 sTop = sTop->next;
193
194 delete temp;
195 }
196 }
197 #endif
198
199 #if defined(XP_WIN)
200 // like strpbrk but finds the *last* char, not the first
ns_strrpbrk(const char * string,const char * strCharSet)201 static const char* ns_strrpbrk(const char* string, const char* strCharSet) {
202 const char* found = nullptr;
203 for (; *string; ++string) {
204 for (const char* search = strCharSet; *search; ++search) {
205 if (*search == *string) {
206 found = string;
207 // Since we're looking for the last char, we save "found"
208 // until we're at the end of the string.
209 }
210 }
211 }
212
213 return found;
214 }
215 #endif
216
217 using XPCOMGlueLoadError = BootstrapError;
218 using XPCOMGlueLoadResult =
219 ::mozilla::Result<::mozilla::Ok, XPCOMGlueLoadError>;
220
XPCOMGlueLoad(const char * aXPCOMFile,LibLoadingStrategy aLibLoadingStrategy)221 static XPCOMGlueLoadResult XPCOMGlueLoad(
222 const char* aXPCOMFile, LibLoadingStrategy aLibLoadingStrategy) {
223 #if defined(MOZ_LINKER) || defined(__ANDROID__)
224 ReadDependentCBResult readDependentCBResult =
225 ReadDependentCB(aXPCOMFile, aLibLoadingStrategy);
226 if (readDependentCBResult.isErr()) {
227 return Err(AsVariant(readDependentCBResult.unwrapErr()));
228 }
229 #else
230 char xpcomDir[MAXPATHLEN];
231 # ifdef XP_WIN
232 const char* lastSlash = ns_strrpbrk(aXPCOMFile, "/\\");
233 # elif XP_MACOSX
234 // On OSX, the dependentlibs.list file lives under Contents/Resources.
235 // However, the actual libraries listed in dependentlibs.list live under
236 // Contents/MacOS. We want to read the list from Contents/Resources, then
237 // load the libraries from Contents/MacOS.
238 const char* tempSlash = strrchr(aXPCOMFile, '/');
239 size_t tempLen = size_t(tempSlash - aXPCOMFile);
240 if (tempLen > MAXPATHLEN) {
241 return Err(AsVariant(NS_ERROR_FAILURE));
242 }
243 char tempBuffer[MAXPATHLEN];
244 memcpy(tempBuffer, aXPCOMFile, tempLen);
245 tempBuffer[tempLen] = '\0';
246 const char* slash = strrchr(tempBuffer, '/');
247 tempLen = size_t(slash - tempBuffer);
248 const char* lastSlash = aXPCOMFile + tempLen;
249 # else
250 const char* lastSlash = strrchr(aXPCOMFile, '/');
251 # endif
252 char* cursor;
253 if (lastSlash) {
254 size_t len = size_t(lastSlash - aXPCOMFile);
255
256 if (len > MAXPATHLEN - sizeof(XPCOM_FILE_PATH_SEPARATOR
257 # ifdef XP_MACOSX
258 "Resources" XPCOM_FILE_PATH_SEPARATOR
259 # endif
260 XPCOM_DEPENDENT_LIBS_LIST)) {
261 return Err(AsVariant(NS_ERROR_FAILURE));
262 }
263 memcpy(xpcomDir, aXPCOMFile, len);
264 strcpy(xpcomDir + len, XPCOM_FILE_PATH_SEPARATOR
265 # ifdef XP_MACOSX
266 "Resources" XPCOM_FILE_PATH_SEPARATOR
267 # endif
268 XPCOM_DEPENDENT_LIBS_LIST);
269 cursor = xpcomDir + len + 1;
270 } else {
271 strcpy(xpcomDir, XPCOM_DEPENDENT_LIBS_LIST);
272 cursor = xpcomDir;
273 }
274
275 if (getenv("MOZ_RUN_GTEST")
276 # ifdef FUZZING
277 || getenv("FUZZER")
278 # endif
279 ) {
280 strcat(xpcomDir, ".gtest");
281 }
282
283 ScopedCloseFile flist;
284 flist = TS_tfopen(xpcomDir, READ_TEXTMODE);
285 if (!flist) {
286 return Err(AsVariant(NS_ERROR_FAILURE));
287 }
288
289 # ifdef XP_MACOSX
290 tempLen = size_t(cursor - xpcomDir);
291 if (tempLen > MAXPATHLEN - sizeof("MacOS" XPCOM_FILE_PATH_SEPARATOR) - 1) {
292 return Err(AsVariant(NS_ERROR_FAILURE));
293 }
294 strcpy(cursor, "MacOS" XPCOM_FILE_PATH_SEPARATOR);
295 cursor += strlen(cursor);
296 # endif
297 *cursor = '\0';
298
299 char buffer[MAXPATHLEN];
300
301 while (fgets(buffer, sizeof(buffer), flist)) {
302 int l = strlen(buffer);
303
304 // ignore empty lines and comments
305 if (l == 0 || *buffer == '#') {
306 continue;
307 }
308 # ifdef XP_WIN
309 // There is no point in reading Universal CRT forwarder DLLs ahead on
310 // Windows 10 because they will not be touched later.
311 if (IsWin10OrLater() && !strncmp(buffer, "api-", 4)) {
312 continue;
313 }
314 # endif
315
316 // cut the trailing newline, if present
317 if (buffer[l - 1] == '\n') {
318 buffer[l - 1] = '\0';
319 }
320
321 if (l + size_t(cursor - xpcomDir) > MAXPATHLEN) {
322 return Err(AsVariant(NS_ERROR_FAILURE));
323 }
324
325 strcpy(cursor, buffer);
326 ReadDependentCBResult readDependentCBResult =
327 ReadDependentCB(xpcomDir, aLibLoadingStrategy);
328 if (readDependentCBResult.isErr()) {
329 XPCOMGlueUnload();
330 return Err(AsVariant(readDependentCBResult.unwrapErr()));
331 }
332
333 # ifdef XP_WIN
334 // We call PollPreXULSkeletonUIEvents here in order to not get flagged by
335 // Windows as nonresponsive. In order to not be flagged as such, we seem to
336 // simply need to respond to *a* message every few seconds. The halfway
337 // point on slow systems between process start and nsWindow taking over the
338 // skeleton UI window seems to be XUL being loaded. Accordingly, placing
339 // this call here covers the most ground (as we will call this after
340 // prefetching and loading all of the dlls in dependentlibs.list, which
341 // includes xul.dll.)
342 PollPreXULSkeletonUIEvents();
343 # endif
344 }
345 #endif
346 return Ok();
347 }
348
349 #if defined(MOZ_WIDGET_GTK) && \
350 (defined(MOZ_MEMORY) || defined(__FreeBSD__) || defined(__NetBSD__))
351 # define MOZ_GSLICE_INIT
352 #endif
353
354 #ifdef MOZ_GSLICE_INIT
355 # include <glib.h>
356
357 class GSliceInit {
358 public:
GSliceInit()359 GSliceInit() {
360 mHadGSlice = bool(getenv("G_SLICE"));
361 if (!mHadGSlice) {
362 // Disable the slice allocator, since jemalloc already uses similar layout
363 // algorithms, and using a sub-allocator tends to increase fragmentation.
364 // This must be done before g_thread_init() is called.
365 // glib >= 2.36 initializes g_slice as a side effect of its various static
366 // initializers, so this needs to happen before glib is loaded, which is
367 // this is hooked in XPCOMGlueStartup before libxul is loaded. This
368 // relies on the main executable not depending on glib.
369 setenv("G_SLICE", "always-malloc", 1);
370 }
371 }
372
~GSliceInit()373 ~GSliceInit() {
374 if (!mHadGSlice) {
375 unsetenv("G_SLICE");
376 }
377 }
378
379 private:
380 bool mHadGSlice;
381 };
382 #endif
383
384 namespace mozilla {
385
GetBootstrap(const char * aXPCOMFile,LibLoadingStrategy aLibLoadingStrategy)386 BootstrapResult GetBootstrap(const char* aXPCOMFile,
387 LibLoadingStrategy aLibLoadingStrategy) {
388 #ifdef MOZ_GSLICE_INIT
389 GSliceInit gSliceInit;
390 #endif
391
392 if (!aXPCOMFile) {
393 return Err(AsVariant(NS_ERROR_INVALID_ARG));
394 }
395
396 char* lastSlash =
397 strrchr(const_cast<char*>(aXPCOMFile), XPCOM_FILE_PATH_SEPARATOR[0]);
398 if (!lastSlash) {
399 return Err(AsVariant(NS_ERROR_FILE_INVALID_PATH));
400 }
401
402 size_t base_len = size_t(lastSlash - aXPCOMFile) + 1;
403
404 UniqueFreePtr<char> file(
405 reinterpret_cast<char*>(malloc(base_len + sizeof(XPCOM_DLL))));
406 memcpy(file.get(), aXPCOMFile, base_len);
407 memcpy(file.get() + base_len, XPCOM_DLL, sizeof(XPCOM_DLL));
408
409 MOZ_TRY(XPCOMGlueLoad(file.get(), aLibLoadingStrategy));
410
411 GetBootstrapType func =
412 (GetBootstrapType)GetSymbol(sTop->libHandle, "XRE_GetBootstrap");
413 if (!func) {
414 return Err(AsVariant(NS_ERROR_NOT_AVAILABLE));
415 }
416
417 Bootstrap::UniquePtr b;
418 (*func)(b);
419
420 return b;
421 }
422
423 } // namespace mozilla
424