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