File: | var/lib/jenkins/workspace/firefox-scan-build/browser/app/nsBrowserApp.cpp |
Warning: | line 180, column 15 Potential memory leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||||
2 | /* This Source Code Form is subject to the terms of the Mozilla Public | ||||
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||
5 | |||||
6 | #include "nsXULAppAPI.h" | ||||
7 | #include "mozilla/XREAppData.h" | ||||
8 | #include "XREChildData.h" | ||||
9 | #include "XREShellData.h" | ||||
10 | #include "application.ini.h" | ||||
11 | #include "mozilla/Bootstrap.h" | ||||
12 | #include "mozilla/ProcessType.h" | ||||
13 | #include "mozilla/RuntimeExceptionModule.h" | ||||
14 | #include "mozilla/ScopeExit.h" | ||||
15 | #include "BrowserDefines.h" | ||||
16 | #if defined(XP_WIN) | ||||
17 | # include <windows.h> | ||||
18 | # include <stdlib.h> | ||||
19 | #elif defined(XP_UNIX1) | ||||
20 | # include <sys/resource.h> | ||||
21 | # include <unistd.h> | ||||
22 | # include <fcntl.h> | ||||
23 | #endif | ||||
24 | |||||
25 | #include <stdio.h> | ||||
26 | #include <stdarg.h> | ||||
27 | #include <time.h> | ||||
28 | |||||
29 | #include "nsCOMPtr.h" | ||||
30 | |||||
31 | #ifdef XP_WIN | ||||
32 | # include "mozilla/PreXULSkeletonUI.h" | ||||
33 | # include "freestanding/SharedSection.h" | ||||
34 | # include "LauncherProcessWin.h" | ||||
35 | # include "mozilla/GeckoArgs.h" | ||||
36 | # include "mozilla/mscom/ProcessRuntime.h" | ||||
37 | # include "mozilla/WindowsDllBlocklist.h" | ||||
38 | # include "mozilla/WindowsDpiInitialization.h" | ||||
39 | # include "mozilla/WindowsProcessMitigations.h" | ||||
40 | |||||
41 | # define XRE_WANT_ENVIRON | ||||
42 | # include "nsWindowsWMain.cpp" | ||||
43 | |||||
44 | # define strcasecmp _stricmp | ||||
45 | # ifdef MOZ_SANDBOX1 | ||||
46 | # include "mozilla/sandboxing/SandboxInitialization.h" | ||||
47 | # include "mozilla/sandboxing/sandboxLogging.h" | ||||
48 | # endif | ||||
49 | #endif | ||||
50 | #include "BinaryPath.h" | ||||
51 | |||||
52 | #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL | ||||
53 | |||||
54 | #include "mozilla/Sprintf.h" | ||||
55 | #include "mozilla/StartupTimeline.h" | ||||
56 | #include "BaseProfiler.h" | ||||
57 | |||||
58 | #ifdef LIBFUZZER | ||||
59 | # include "FuzzerDefs.h" | ||||
60 | #endif | ||||
61 | |||||
62 | #ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR | ||||
63 | # include <cpuid.h> | ||||
64 | # include "mozilla/Unused.h" | ||||
65 | |||||
66 | static bool IsSSE2Available() { | ||||
67 | // The rest of the app has been compiled to assume that SSE2 is present | ||||
68 | // unconditionally, so we can't use the normal copy of SSE.cpp here. | ||||
69 | // Since SSE.cpp caches the results and we need them only transiently, | ||||
70 | // instead of #including SSE.cpp here, let's just inline the specific check | ||||
71 | // that's needed. | ||||
72 | unsigned int level = 1u; | ||||
73 | unsigned int eax, ebx, ecx, edx; | ||||
74 | unsigned int bits = (1u << 26); | ||||
75 | unsigned int max = __get_cpuid_max(0, nullptr); | ||||
76 | if (level > max) { | ||||
77 | return false; | ||||
78 | } | ||||
79 | __cpuid_count(level, 0, eax, ebx, ecx, edx); | ||||
80 | return (edx & bits) == bits; | ||||
81 | } | ||||
82 | |||||
83 | static const char sSSE2Message[] = | ||||
84 | "This browser version requires a processor with the SSE2 instruction " | ||||
85 | "set extension.\nYou may be able to obtain a version that does not " | ||||
86 | "require SSE2 from your Linux distribution.\n"; | ||||
87 | |||||
88 | __attribute__((constructor)) static void SSE2Check() { | ||||
89 | if (IsSSE2Available()) { | ||||
90 | return; | ||||
91 | } | ||||
92 | // Using write() in order to avoid jemalloc-based buffering. Ignoring return | ||||
93 | // values, since there isn't much we could do on failure and there is no | ||||
94 | // point in trying to recover from errors. | ||||
95 | MOZ_UNUSED(write(STDERR_FILENO, sSSE2Message, std::size(sSSE2Message) - 1))do { if (write(2, sSSE2Message, std::size(sSSE2Message) - 1)) { (void)0; } } while (0); | ||||
96 | // _exit() instead of exit() to avoid running the usual "at exit" code. | ||||
97 | _exit(255); | ||||
98 | } | ||||
99 | #endif | ||||
100 | |||||
101 | #if !defined(MOZ_WIDGET_COCOA) && !defined(MOZ_WIDGET_ANDROID) | ||||
102 | # define MOZ_BROWSER_CAN_BE_CONTENTPROC | ||||
103 | #endif | ||||
104 | |||||
105 | using namespace mozilla; | ||||
106 | |||||
107 | #ifdef XP_MACOSX | ||||
108 | # define kOSXResourcesFolder "Resources" | ||||
109 | #endif | ||||
110 | #define kDesktopFolder"browser" "browser" | ||||
111 | |||||
112 | static MOZ_FORMAT_PRINTF(1, 2)__attribute__((format(printf, 1, 2))) void Output(const char* fmt, ...) { | ||||
113 | va_list ap; | ||||
114 | va_start(ap, fmt)__builtin_va_start(ap, fmt); | ||||
115 | |||||
116 | #ifndef XP_WIN | ||||
117 | vfprintf(stderrstderr, fmt, ap); | ||||
118 | #else | ||||
119 | char msg[2048]; | ||||
120 | vsnprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap); | ||||
121 | |||||
122 | wchar_t wide_msg[2048]; | ||||
123 | MultiByteToWideChar(CP_UTF8, 0, msg, -1, wide_msg, _countof(wide_msg)); | ||||
124 | # if MOZ_WINCONSOLE | ||||
125 | fwprintf_s(stderrstderr, wide_msg); | ||||
126 | # else | ||||
127 | // Linking user32 at load-time interferes with the DLL blocklist (bug 932100). | ||||
128 | // This is a rare codepath, so we can load user32 at run-time instead. | ||||
129 | HMODULE user32 = LoadLibraryW(L"user32.dll"); | ||||
130 | if (user32) { | ||||
131 | decltype(MessageBoxW)* messageBoxW = | ||||
132 | (decltype(MessageBoxW)*)GetProcAddress(user32, "MessageBoxW"); | ||||
133 | if (messageBoxW) { | ||||
134 | messageBoxW(nullptr, wide_msg, L"Firefox", | ||||
135 | MB_OK | MB_ICONERROR | MB_SETFOREGROUND); | ||||
136 | } | ||||
137 | FreeLibrary(user32); | ||||
138 | } | ||||
139 | # endif | ||||
140 | #endif | ||||
141 | |||||
142 | va_end(ap)__builtin_va_end(ap); | ||||
143 | } | ||||
144 | |||||
145 | /** | ||||
146 | * Return true if |arg| matches the given argument name. | ||||
147 | */ | ||||
148 | static bool IsArg(const char* arg, const char* s) { | ||||
149 | if (*arg == '-') { | ||||
150 | if (*++arg == '-') ++arg; | ||||
151 | return !strcasecmp(arg, s); | ||||
152 | } | ||||
153 | |||||
154 | #if defined(XP_WIN) | ||||
155 | if (*arg == '/') return !strcasecmp(++arg, s); | ||||
156 | #endif | ||||
157 | |||||
158 | return false; | ||||
159 | } | ||||
160 | |||||
161 | MOZ_RUNINIT Bootstrap::UniquePtr gBootstrap; | ||||
162 | |||||
163 | static int do_main(int argc, char* argv[], char* envp[]) { | ||||
164 | // Allow firefox.exe to launch XULRunner apps via -app <application.ini> | ||||
165 | // Note that -app must be the *first* argument. | ||||
166 | const char* appDataFile = getenv("XUL_APP_FILE"); | ||||
167 | if ((!appDataFile
| ||||
168 | if (argc == 2) { | ||||
169 | Output("Incorrect number of arguments passed to -app"); | ||||
170 | return 255; | ||||
171 | } | ||||
172 | appDataFile = argv[2]; | ||||
173 | |||||
174 | char appEnv[MAXPATHLEN4096]; | ||||
175 | SprintfLiteral(appEnv, "XUL_APP_FILE=%s", argv[2]); | ||||
176 | if (putenv(strdup(appEnv))) { | ||||
177 | Output("Couldn't set %s.\n", appEnv); | ||||
178 | return 255; | ||||
179 | } | ||||
180 | argv[2] = argv[0]; | ||||
| |||||
181 | argv += 2; | ||||
182 | argc -= 2; | ||||
183 | } else if (argc > 1 && IsArg(argv[1], "xpcshell")) { | ||||
184 | for (int i = 1; i < argc; i++) { | ||||
185 | argv[i] = argv[i + 1]; | ||||
186 | } | ||||
187 | |||||
188 | XREShellData shellData; | ||||
189 | #if defined(XP_WIN) && defined(MOZ_SANDBOX1) | ||||
190 | shellData.sandboxBrokerServices = | ||||
191 | sandboxing::GetInitializedBrokerServices(); | ||||
192 | #endif | ||||
193 | |||||
194 | #ifdef LIBFUZZER | ||||
195 | shellData.fuzzerDriver = fuzzer::FuzzerDriver; | ||||
196 | #endif | ||||
197 | #ifdef AFLFUZZ | ||||
198 | shellData.fuzzerDriver = afl_interface_raw; | ||||
199 | #endif | ||||
200 | |||||
201 | return gBootstrap->XRE_XPCShellMain(--argc, argv, envp, &shellData); | ||||
202 | } | ||||
203 | |||||
204 | BootstrapConfig config; | ||||
205 | |||||
206 | if (appDataFile && *appDataFile) { | ||||
207 | config.appData = nullptr; | ||||
208 | config.appDataPath = appDataFile; | ||||
209 | } else { | ||||
210 | // no -app flag so we use the compiled-in app data | ||||
211 | config.appData = &sAppData; | ||||
212 | config.appDataPath = kDesktopFolder"browser"; | ||||
213 | } | ||||
214 | |||||
215 | #if defined(XP_WIN) && defined(MOZ_SANDBOX1) | ||||
216 | sandbox::BrokerServices* brokerServices = | ||||
217 | sandboxing::GetInitializedBrokerServices(); | ||||
218 | if (!brokerServices) { | ||||
219 | Output("Couldn't initialize the broker services.\n"); | ||||
220 | return 255; | ||||
221 | } | ||||
222 | config.sandboxBrokerServices = brokerServices; | ||||
223 | #endif | ||||
224 | |||||
225 | #ifdef LIBFUZZER | ||||
226 | if (getenv("FUZZER")) | ||||
227 | gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver); | ||||
228 | #endif | ||||
229 | |||||
230 | EnsureBrowserCommandlineSafe(argc, argv); | ||||
231 | |||||
232 | return gBootstrap->XRE_main(argc, argv, config); | ||||
233 | } | ||||
234 | |||||
235 | static nsresult InitXPCOMGlue(LibLoadingStrategy aLibLoadingStrategy) { | ||||
236 | if (gBootstrap) { | ||||
237 | return NS_OK; | ||||
238 | } | ||||
239 | |||||
240 | UniqueFreePtr<char> exePath = BinaryPath::Get(); | ||||
241 | if (!exePath) { | ||||
242 | Output("Couldn't find the application directory.\n"); | ||||
243 | return NS_ERROR_FAILURE; | ||||
244 | } | ||||
245 | |||||
246 | auto bootstrapResult = | ||||
247 | mozilla::GetBootstrap(exePath.get(), aLibLoadingStrategy); | ||||
248 | if (bootstrapResult.isErr()) { | ||||
249 | Output("Couldn't load XPCOM.\n"); | ||||
250 | return NS_ERROR_FAILURE; | ||||
251 | } | ||||
252 | |||||
253 | gBootstrap = bootstrapResult.unwrap(); | ||||
254 | |||||
255 | // This will set this thread as the main thread. | ||||
256 | gBootstrap->NS_LogInit(); | ||||
257 | |||||
258 | return NS_OK; | ||||
259 | } | ||||
260 | |||||
261 | #ifdef HAS_DLL_BLOCKLIST | ||||
262 | // NB: This must be extern, as this value is checked elsewhere | ||||
263 | uint32_t gBlocklistInitFlags = eDllBlocklistInitFlagDefault; | ||||
264 | #endif | ||||
265 | |||||
266 | #if defined(XP_UNIX1) | ||||
267 | static void ReserveDefaultFileDescriptors() { | ||||
268 | // Reserve the lower positions of the file descriptors to make sure | ||||
269 | // we don't reuse stdin/stdout/stderr in case they we closed | ||||
270 | // before launch. | ||||
271 | // Otherwise code explicitly writing to fd 1 or 2 might accidentally | ||||
272 | // write to something else, like in bug 1820896 where FD 1 is | ||||
273 | // reused for the X server display connection. | ||||
274 | int fd = open("/dev/null", O_RDONLY00); | ||||
275 | for (int i = 0; i < 2; i++) { | ||||
276 | mozilla::Unused << dup(fd); | ||||
277 | } | ||||
278 | } | ||||
279 | #endif | ||||
280 | |||||
281 | int main(int argc, char* argv[], char* envp[]) { | ||||
282 | #if defined(XP_UNIX1) | ||||
283 | ReserveDefaultFileDescriptors(); | ||||
284 | #endif | ||||
285 | |||||
286 | #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC | ||||
287 | if (argc > 1 && IsArg(argv[1], "contentproc")) { | ||||
| |||||
288 | // Set the process type and gecko child id. | ||||
289 | SetGeckoProcessType(argv[--argc]); | ||||
290 | SetGeckoChildID(argv[--argc]); | ||||
291 | |||||
292 | # if defined(MOZ_ENABLE_FORKSERVER1) | ||||
293 | if (GetGeckoProcessType() == GeckoProcessType_ForkServer) { | ||||
294 | nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead); | ||||
295 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { | ||||
296 | return 255; | ||||
297 | } | ||||
298 | |||||
299 | // Run a fork server in this process, single thread. When it returns, it | ||||
300 | // means the fork server have been stopped or a new child process is | ||||
301 | // created. | ||||
302 | // | ||||
303 | // For the latter case, XRE_ForkServer() will return false, running in a | ||||
304 | // child process just forked from the fork server process. argc & argv | ||||
305 | // will be updated with the values passing from the chrome process, as | ||||
306 | // will GeckoProcessType and GeckoChildID. With the new values, this | ||||
307 | // function continues the reset of the code acting as a child process. | ||||
308 | if (gBootstrap->XRE_ForkServer(&argc, &argv)) { | ||||
309 | // Return from the fork server in the fork server process. | ||||
310 | // Stop the fork server. | ||||
311 | // InitXPCOMGlue calls NS_LogInit, so we need to balance it here. | ||||
312 | gBootstrap->NS_LogTerm(); | ||||
313 | return 0; | ||||
314 | } | ||||
315 | } | ||||
316 | # endif | ||||
317 | } | ||||
318 | #endif | ||||
319 | |||||
320 | mozilla::TimeStamp start = mozilla::TimeStamp::Now(); | ||||
321 | |||||
322 | AUTO_BASE_PROFILER_INIT::mozilla::baseprofiler::AutoProfilerInit raiiObject322; | ||||
323 | AUTO_BASE_PROFILER_LABEL("nsBrowserApp main", OTHER)::mozilla::baseprofiler::AutoProfilerLabel raiiObject323( "nsBrowserApp main" , nullptr, ::mozilla::baseprofiler::ProfilingCategoryPair::OTHER ); | ||||
324 | |||||
325 | // Register an external module to report on otherwise uncatchable exceptions. | ||||
326 | // Note that in child processes this must be called after Gecko process type | ||||
327 | // has been set. | ||||
328 | CrashReporter::RegisterRuntimeExceptionModule(); | ||||
329 | |||||
330 | // Make sure we unregister the runtime exception module before returning. | ||||
331 | auto unregisterRuntimeExceptionModule = | ||||
332 | MakeScopeExit([] { CrashReporter::UnregisterRuntimeExceptionModule(); }); | ||||
333 | |||||
334 | #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC | ||||
335 | // We are launching as a content process, delegate to the appropriate | ||||
336 | // main | ||||
337 | if (GetGeckoProcessType() != GeckoProcessType_Default) { | ||||
338 | # if defined(XP_WIN) && defined(MOZ_SANDBOX1) | ||||
339 | // We need to set whether our process is supposed to have win32k locked down | ||||
340 | // from the command line setting before DllBlocklist_Initialize, | ||||
341 | // GetInitializedTargetServices and WindowsDpiInitialization. | ||||
342 | Maybe<bool> win32kLockedDown = | ||||
343 | mozilla::geckoargs::sWin32kLockedDown.Get(argc, argv); | ||||
344 | if (win32kLockedDown.isSome() && *win32kLockedDown) { | ||||
345 | mozilla::SetWin32kLockedDownInPolicy(); | ||||
346 | } | ||||
347 | # endif | ||||
348 | |||||
349 | # ifdef HAS_DLL_BLOCKLIST | ||||
350 | uint32_t initFlags = | ||||
351 | gBlocklistInitFlags | eDllBlocklistInitFlagIsChildProcess; | ||||
352 | SetDllBlocklistProcessTypeFlags(initFlags, GetGeckoProcessType()); | ||||
353 | DllBlocklist_Initialize(initFlags); | ||||
354 | # endif // HAS_DLL_BLOCKLIST | ||||
355 | |||||
356 | # if defined(XP_WIN) && defined(MOZ_SANDBOX1) | ||||
357 | // We need to initialize the sandbox TargetServices before InitXPCOMGlue | ||||
358 | // because we might need the sandbox broker to give access to some files. | ||||
359 | if (IsSandboxedProcess() && !sandboxing::GetInitializedTargetServices()) { | ||||
360 | Output("Failed to initialize the sandbox target services."); | ||||
361 | return 255; | ||||
362 | } | ||||
363 | # endif | ||||
364 | # if defined(XP_WIN) | ||||
365 | // Ideally, we would be able to set our DPI awareness in | ||||
366 | // firefox.exe.manifest Unfortunately, that would cause Win32k calls when | ||||
367 | // user32.dll gets loaded, which would be incompatible with Win32k Lockdown | ||||
368 | // | ||||
369 | // MSDN says that it's allowed-but-not-recommended to initialize DPI | ||||
370 | // programatically, as long as it's done before any HWNDs are created. | ||||
371 | // Thus, we do it almost as soon as we possibly can | ||||
372 | { | ||||
373 | auto result = mozilla::WindowsDpiInitialization(); | ||||
374 | (void)result; // Ignore errors since some tools block DPI calls | ||||
375 | } | ||||
376 | # endif | ||||
377 | |||||
378 | nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead); | ||||
379 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { | ||||
380 | return 255; | ||||
381 | } | ||||
382 | |||||
383 | XREChildData childData; | ||||
384 | |||||
385 | # if defined(XP_WIN) && defined(MOZ_SANDBOX1) | ||||
386 | if (IsSandboxedProcess()) { | ||||
387 | childData.sandboxTargetServices = | ||||
388 | mozilla::sandboxing::GetInitializedTargetServices(); | ||||
389 | if (!childData.sandboxTargetServices) { | ||||
390 | return 1; | ||||
391 | } | ||||
392 | |||||
393 | childData.ProvideLogFunction = mozilla::sandboxing::ProvideLogFunction; | ||||
394 | } | ||||
395 | # endif | ||||
396 | |||||
397 | rv = gBootstrap->XRE_InitChildProcess(argc, argv, &childData); | ||||
398 | |||||
399 | # if defined(DEBUG1) && defined(HAS_DLL_BLOCKLIST) | ||||
400 | DllBlocklist_Shutdown(); | ||||
401 | # endif | ||||
402 | |||||
403 | // InitXPCOMGlue calls NS_LogInit, so we need to balance it here. | ||||
404 | gBootstrap->NS_LogTerm(); | ||||
405 | |||||
406 | return NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) ? 1 : 0; | ||||
407 | } | ||||
408 | #endif | ||||
409 | |||||
410 | #ifdef HAS_DLL_BLOCKLIST | ||||
411 | DllBlocklist_Initialize(gBlocklistInitFlags); | ||||
412 | #endif | ||||
413 | |||||
414 | // We will likely only ever support this as a command line argument on Windows | ||||
415 | // and OSX, so we're ifdefing here just to not create any expectations. | ||||
416 | #if defined(XP_WIN) || defined(XP_MACOSX) | ||||
417 | if (argc > 1 && IsArg(argv[1], "silentmode")) { | ||||
418 | ::putenv(const_cast<char*>("MOZ_APP_SILENT_START=1")); | ||||
419 | # if defined(XP_WIN) | ||||
420 | // On windows We also want to set a separate variable, which we want to | ||||
421 | // persist across restarts, which will let us keep the process alive | ||||
422 | // even if the last window is closed. | ||||
423 | ::putenv(const_cast<char*>("MOZ_APP_ALLOW_WINDOWLESS=1")); | ||||
424 | # endif | ||||
425 | # if defined(XP_MACOSX) | ||||
426 | ::putenv(const_cast<char*>("MOZ_APP_NO_DOCK=1")); | ||||
427 | # endif | ||||
428 | } | ||||
429 | #endif | ||||
430 | |||||
431 | #if defined(XP_WIN) | ||||
432 | |||||
433 | // Ideally, we would be able to set our DPI awareness in firefox.exe.manifest | ||||
434 | // Unfortunately, that would cause Win32k calls when user32.dll gets loaded, | ||||
435 | // which would be incompatible with Win32k Lockdown | ||||
436 | // | ||||
437 | // MSDN says that it's allowed-but-not-recommended to initialize DPI | ||||
438 | // programatically, as long as it's done before any HWNDs are created. | ||||
439 | // Thus, we do it almost as soon as we possibly can | ||||
440 | { | ||||
441 | auto result = mozilla::WindowsDpiInitialization(); | ||||
442 | (void)result; // Ignore errors since some tools block DPI calls | ||||
443 | } | ||||
444 | |||||
445 | // Once the browser process hits the main function, we no longer need | ||||
446 | // a writable section handle because all dependent modules have been | ||||
447 | // loaded. | ||||
448 | mozilla::freestanding::gSharedSection.ConvertToReadOnly(); | ||||
449 | |||||
450 | mozilla::CreateAndStorePreXULSkeletonUI(GetModuleHandle(nullptr), argc, argv); | ||||
451 | #endif | ||||
452 | |||||
453 | nsresult rv = InitXPCOMGlue(LibLoadingStrategy::ReadAhead); | ||||
454 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { | ||||
455 | return 255; | ||||
456 | } | ||||
457 | |||||
458 | gBootstrap->XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start); | ||||
459 | |||||
460 | #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC | ||||
461 | gBootstrap->XRE_EnableSameExecutableForContentProc(); | ||||
462 | #endif | ||||
463 | |||||
464 | int result = do_main(argc, argv, envp); | ||||
465 | |||||
466 | #if defined(XP_WIN) | ||||
467 | CleanupProcessRuntime(); | ||||
468 | #endif | ||||
469 | |||||
470 | gBootstrap->NS_LogTerm(); | ||||
471 | |||||
472 | #if defined(DEBUG1) && defined(HAS_DLL_BLOCKLIST) | ||||
473 | DllBlocklist_Shutdown(); | ||||
474 | #endif | ||||
475 | |||||
476 | #ifdef XP_MACOSX | ||||
477 | // Allow writes again. While we would like to catch writes from static | ||||
478 | // destructors to allow early exits to use _exit, we know that there is | ||||
479 | // at least one such write that we don't control (see bug 826029). For | ||||
480 | // now we enable writes again and early exits will have to use exit instead | ||||
481 | // of _exit. | ||||
482 | gBootstrap->XRE_StopLateWriteChecks(); | ||||
483 | #endif | ||||
484 | |||||
485 | gBootstrap.reset(); | ||||
486 | |||||
487 | return result; | ||||
488 | } |