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 /* SpiderMonkey initialization and shutdown code. */
8 
9 #include "js/Initialization.h"
10 
11 #include "mozilla/Assertions.h"
12 #if JS_HAS_INTL_API
13 #  include "mozilla/intl/ICU4CLibrary.h"
14 #endif
15 #include "mozilla/TextUtils.h"
16 
17 #include "jstypes.h"
18 
19 #include "builtin/AtomicsObject.h"
20 #include "builtin/TestingFunctions.h"
21 #include "ds/MemoryProtectionExceptionHandler.h"
22 #include "gc/Statistics.h"
23 #include "jit/Assembler.h"
24 #include "jit/AtomicOperations.h"
25 #include "jit/Ion.h"
26 #include "jit/JitCommon.h"
27 #include "jit/JitOptions.h"
28 #include "jit/ProcessExecutableMemory.h"
29 #include "js/Utility.h"
30 #include "threading/ProtectedData.h"  // js::AutoNoteSingleThreadedRegion
31 #include "util/Poison.h"
32 #include "vm/ArrayBufferObject.h"
33 #include "vm/BigIntType.h"
34 #include "vm/DateTime.h"
35 #include "vm/HelperThreads.h"
36 #include "vm/Runtime.h"
37 #include "vm/Time.h"
38 #include "vm/TraceLogging.h"
39 #ifdef MOZ_VTUNE
40 #  include "vtune/VTuneWrapper.h"
41 #endif
42 #include "wasm/WasmProcess.h"
43 
44 using js::FutexThread;
45 using JS::detail::InitState;
46 using JS::detail::libraryInitState;
47 
48 InitState JS::detail::libraryInitState;
49 
50 #ifdef DEBUG
MessageParameterCount(const char * format)51 static unsigned MessageParameterCount(const char* format) {
52   unsigned numfmtspecs = 0;
53   for (const char* fmt = format; *fmt != '\0'; fmt++) {
54     if (*fmt == '{' && mozilla::IsAsciiDigit(fmt[1])) {
55       ++numfmtspecs;
56     }
57   }
58   return numfmtspecs;
59 }
60 
CheckMessageParameterCounts()61 static void CheckMessageParameterCounts() {
62   // Assert that each message format has the correct number of braced
63   // parameters.
64 #  define MSG_DEF(name, count, exception, format) \
65     MOZ_ASSERT(MessageParameterCount(format) == count);
66 #  include "js/friend/ErrorNumbers.msg"
67 #  undef MSG_DEF
68 }
69 #endif /* DEBUG */
70 
71 #if defined(JS_RUNTIME_CANONICAL_NAN)
72 namespace JS::detail {
73 uint64_t CanonicalizedNaNBits;
74 }  // namespace JS::detail
75 #endif
76 
SetupCanonicalNaN()77 static void SetupCanonicalNaN() {
78   // Compute the standard NaN value that the hardware generates.
79   volatile double infinity = mozilla::PositiveInfinity<double>();
80   volatile double hardwareNaN = infinity - infinity;
81   uint64_t hardwareNaNBits = mozilla::BitwiseCast<uint64_t>(hardwareNaN);
82   hardwareNaNBits &= ~mozilla::FloatingPoint<double>::kSignBit;
83 
84 #if defined(JS_NONCANONICAL_HARDWARE_NAN)
85   // If the NaN generated by hardware operations is not compatible
86   // with our canonical NaN, we must canonicalize every double. This
87   // is implemented for C++ code in Value::bitsFromDouble, but is not
88   // implemented for JIT code.
89 #  if !defined(JS_CODEGEN_NONE)
90 #    error "No JIT support for non-canonical hardware NaN"
91 #  endif
92 
93   (void)hardwareNaNBits;
94 #elif defined(JS_RUNTIME_CANONICAL_NAN)
95   // Determine canonical NaN at startup. It must still match the ValueIsDouble
96   // requirements.
97   MOZ_RELEASE_ASSERT(JS::detail::ValueIsDouble(hardwareNaNBits));
98   JS::detail::CanonicalizedNaNBits = hardwareNaNBits;
99 #else
100   // Assert that the NaN generated by hardware operations is
101   // compatible with the canonical NaN we use for JS::Value. This is
102   // true for all of our supported platforms, but not for SPARC.
103   MOZ_RELEASE_ASSERT(hardwareNaNBits == JS::detail::CanonicalizedNaNBits,
104                      "Unexpected default hardware NaN value");
105 #endif
106 }
107 
108 #define RETURN_IF_FAIL(code)           \
109   do {                                 \
110     if (!code) return #code " failed"; \
111   } while (0)
112 
113 extern "C" void install_rust_panic_hook();
114 extern "C" void install_rust_oom_hook();
115 
InitWithFailureDiagnostic(bool isDebugBuild)116 JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic(
117     bool isDebugBuild) {
118   // Verify that our DEBUG setting matches the caller's.
119 #ifdef DEBUG
120   MOZ_RELEASE_ASSERT(isDebugBuild);
121 #else
122   MOZ_RELEASE_ASSERT(!isDebugBuild);
123 #endif
124 
125   MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
126              "must call JS_Init once before any JSAPI operation except "
127              "JS_SetICUMemoryFunctions");
128   MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
129              "how do we have live runtimes before JS_Init?");
130 
131   libraryInitState = InitState::Initializing;
132 
133 #ifdef JS_STANDALONE
134   // The rust hooks are initialized by Gecko on non-standalone builds.
135   install_rust_panic_hook();
136   install_rust_oom_hook();
137 #endif
138 
139   PRMJ_NowInit();
140 
141   // The first invocation of `ProcessCreation` creates a temporary thread
142   // and crashes if that fails, i.e. because we're out of memory. To prevent
143   // that from happening at some later time, get it out of the way during
144   // startup.
145   mozilla::TimeStamp::ProcessCreation();
146 
147 #ifdef DEBUG
148   CheckMessageParameterCounts();
149 #endif
150 
151   SetupCanonicalNaN();
152 
153   RETURN_IF_FAIL(js::TlsContext.init());
154 
155 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
156   RETURN_IF_FAIL(js::oom::InitThreadType());
157 #endif
158 
159 #if defined(FUZZING)
160   js::oom::InitLargeAllocLimit();
161 #endif
162 
163 #if defined(JS_GC_ALLOW_EXTRA_POISONING)
164   if (getenv("JSGC_EXTRA_POISONING")) {
165     js::gExtraPoisoningEnabled = true;
166   }
167 #endif
168 
169   js::InitMallocAllocator();
170 
171   RETURN_IF_FAIL(js::Mutex::Init());
172 
173   js::gc::InitMemorySubsystem();  // Ensure gc::SystemPageSize() works.
174 
175   RETURN_IF_FAIL(js::wasm::Init());
176 
177   js::coverage::InitLCov();
178 
179   RETURN_IF_FAIL(js::MemoryProtectionExceptionHandler::install());
180 
181   RETURN_IF_FAIL(js::jit::InitializeJit());
182 
183   RETURN_IF_FAIL(js::InitDateTimeState());
184 
185 #ifdef MOZ_VTUNE
186   RETURN_IF_FAIL(js::vtune::Initialize());
187 #endif
188 
189 #if JS_HAS_INTL_API
190   if (mozilla::intl::ICU4CLibrary::Initialize().isErr()) {
191     return "ICU4CLibrary::Initialize() failed";
192   }
193 #endif  // JS_HAS_INTL_API
194 
195   RETURN_IF_FAIL(js::CreateHelperThreadsState());
196   RETURN_IF_FAIL(FutexThread::initialize());
197   RETURN_IF_FAIL(js::gcstats::Statistics::initialize());
198   RETURN_IF_FAIL(js::InitTestingFunctions());
199 
200 #ifdef JS_SIMULATOR
201   RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
202 #endif
203 
204 #ifdef JS_TRACE_LOGGING
205   RETURN_IF_FAIL(JS::InitTraceLogger());
206 #endif
207 
208 #ifndef JS_CODEGEN_NONE
209   // This is forced by InitializeJit.
210   MOZ_ASSERT(js::jit::CPUFlagsHaveBeenComputed());
211 #endif
212 
213   libraryInitState = InitState::Running;
214   return nullptr;
215 }
216 
217 #undef RETURN_IF_FAIL
218 
InitSelfHostedCode(JSContext * cx,SelfHostedCache cache,SelfHostedWriter writer)219 JS_PUBLIC_API bool JS::InitSelfHostedCode(JSContext* cx, SelfHostedCache cache,
220                                           SelfHostedWriter writer) {
221   MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
222                      "JS::InitSelfHostedCode() called more than once");
223 
224   js::AutoNoteSingleThreadedRegion anstr;
225 
226   JSRuntime* rt = cx->runtime();
227 
228   if (!rt->initializeParserAtoms(cx)) {
229     return false;
230   }
231 
232   if (!rt->initSelfHostingStencil(cx, cache, writer)) {
233     return false;
234   }
235 
236   if (!rt->initializeAtoms(cx)) {
237     return false;
238   }
239 
240   if (!rt->initSelfHostingFromStencil(cx)) {
241     return false;
242   }
243 
244   if (js::jit::HasJitBackend()) {
245     if (!rt->createJitRuntime(cx)) {
246       return false;
247     }
248   }
249 
250   return true;
251 }
252 
JS_ShutDown(void)253 JS_PUBLIC_API void JS_ShutDown(void) {
254   MOZ_ASSERT(
255       libraryInitState == InitState::Running,
256       "JS_ShutDown must only be called after JS_Init and can't race with it");
257 #ifdef DEBUG
258   if (JSRuntime::hasLiveRuntimes()) {
259     // Gecko is too buggy to assert this just yet.
260     fprintf(stderr,
261             "WARNING: YOU ARE LEAKING THE WORLD (at least one JSRuntime "
262             "and everything alive inside it, that is) AT JS_ShutDown "
263             "TIME.  FIX THIS!\n");
264   }
265 #endif
266 
267   FutexThread::destroy();
268 
269   js::DestroyHelperThreadsState();
270 
271 #ifdef JS_SIMULATOR
272   js::jit::SimulatorProcess::destroy();
273 #endif
274 
275 #ifdef JS_TRACE_LOGGING
276   js::DestroyTraceLoggerThreadState();
277   js::DestroyTraceLoggerGraphState();
278 #endif
279 
280   js::MemoryProtectionExceptionHandler::uninstall();
281 
282   js::wasm::ShutDown();
283 
284   // The only difficult-to-address reason for the restriction that you can't
285   // call JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ
286   // NowInit initialization code, which uses PR_CallOnce to initialize the
287   // PRMJ_Now subsystem.  (For reinitialization to be permitted, we'd need to
288   // "reset" the called-once status -- doable, but more trouble than it's
289   // worth now.)  Initializing that subsystem from JS_Init eliminates the
290   // problem, but initialization can take a comparatively long time (15ms or
291   // so), so we really don't want to do it in JS_Init, and we really do want
292   // to do it only when PRMJ_Now is eventually called.
293   PRMJ_NowShutdown();
294 
295 #if JS_HAS_INTL_API
296   mozilla::intl::ICU4CLibrary::Cleanup();
297 #endif  // JS_HAS_INTL_API
298 
299 #ifdef MOZ_VTUNE
300   js::vtune::Shutdown();
301 #endif  // MOZ_VTUNE
302 
303   js::FinishDateTimeState();
304 
305   js::jit::ShutdownJit();
306 
307   MOZ_ASSERT_IF(!JSRuntime::hasLiveRuntimes(), !js::LiveMappedBufferCount());
308 
309   js::ShutDownMallocAllocator();
310 
311   libraryInitState = InitState::ShutDown;
312 }
313 
JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,JS_ICUReallocFn reallocFn,JS_ICUFreeFn freeFn)314 JS_PUBLIC_API bool JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
315                                             JS_ICUReallocFn reallocFn,
316                                             JS_ICUFreeFn freeFn) {
317   MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
318              "must call JS_SetICUMemoryFunctions before any other JSAPI "
319              "operation (including JS_Init)");
320 
321 #if JS_HAS_INTL_API
322   return mozilla::intl::ICU4CLibrary::SetMemoryFunctions(
323              {allocFn, reallocFn, freeFn})
324       .isOk();
325 #else
326   return true;
327 #endif
328 }
329 
330 #if defined(ENABLE_WASM_SIMD) && \
331     (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86))
SetAVXEnabled()332 void JS::SetAVXEnabled() { js::jit::CPUInfo::SetAVXEnabled(); }
333 #endif
334 
DisableJitBackend()335 JS_PUBLIC_API void JS::DisableJitBackend() {
336   MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
337              "DisableJitBackend must be called before JS_Init");
338   MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
339              "DisableJitBackend must be called before creating a JSContext");
340   js::jit::JitOptions.disableJitBackend = true;
341 }
342