Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : *
4 : * Copyright 2016 Mozilla Foundation
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "wasm/WasmCode.h"
20 :
21 : #include "mozilla/BinarySearch.h"
22 : #include "mozilla/EnumeratedRange.h"
23 :
24 : #include "jit/ExecutableAllocator.h"
25 : #ifdef JS_ION_PERF
26 : # include "jit/PerfSpewer.h"
27 : #endif
28 : #include "vtune/VTuneWrapper.h"
29 : #include "wasm/WasmModule.h"
30 : #include "wasm/WasmSerialize.h"
31 :
32 : #include "jit/MacroAssembler-inl.h"
33 :
34 : using namespace js;
35 : using namespace js::jit;
36 : using namespace js::wasm;
37 : using mozilla::BinarySearch;
38 : using mozilla::MakeEnumeratedRange;
39 : using JS::GenericNaN;
40 :
41 : static uint32_t
42 0 : RoundupCodeLength(uint32_t codeLength)
43 : {
44 : // codeLength is a multiple of the system's page size, but not necessarily
45 : // a multiple of ExecutableCodePageSize.
46 0 : MOZ_ASSERT(codeLength % gc::SystemPageSize() == 0);
47 0 : return JS_ROUNDUP(codeLength, ExecutableCodePageSize);
48 : }
49 :
50 : /* static */ CodeSegment::UniqueCodeBytes
51 0 : CodeSegment::AllocateCodeBytes(uint32_t codeLength)
52 : {
53 0 : codeLength = RoundupCodeLength(codeLength);
54 :
55 0 : void* p = AllocateExecutableMemory(codeLength, ProtectionSetting::Writable);
56 :
57 : // If the allocation failed and the embedding gives us a last-ditch attempt
58 : // to purge all memory (which, in gecko, does a purging GC/CC/GC), do that
59 : // then retry the allocation.
60 0 : if (!p) {
61 0 : if (OnLargeAllocationFailure) {
62 0 : OnLargeAllocationFailure();
63 0 : p = AllocateExecutableMemory(codeLength, ProtectionSetting::Writable);
64 : }
65 : }
66 :
67 0 : if (!p)
68 0 : return nullptr;
69 :
70 : // We account for the bytes allocated in WasmModuleObject::create, where we
71 : // have the necessary JSContext.
72 :
73 0 : return UniqueCodeBytes((uint8_t*)p, FreeCode(codeLength));
74 : }
75 :
76 : void
77 0 : CodeSegment::FreeCode::operator()(uint8_t* bytes)
78 : {
79 0 : MOZ_ASSERT(codeLength);
80 0 : MOZ_ASSERT(codeLength == RoundupCodeLength(codeLength));
81 :
82 : #ifdef MOZ_VTUNE
83 0 : vtune::UnmarkBytes(bytes, codeLength);
84 : #endif
85 0 : DeallocateExecutableMemory(bytes, codeLength);
86 0 : }
87 :
88 : static bool
89 0 : StaticallyLink(const CodeSegment& cs, const LinkDataTier& linkData)
90 : {
91 0 : for (LinkDataTier::InternalLink link : linkData.internalLinks) {
92 0 : uint8_t* patchAt = cs.base() + link.patchAtOffset;
93 0 : void* target = cs.base() + link.targetOffset;
94 0 : if (link.isRawPointerPatch())
95 0 : *(void**)(patchAt) = target;
96 : else
97 0 : Assembler::PatchInstructionImmediate(patchAt, PatchedImmPtr(target));
98 : }
99 :
100 0 : if (!EnsureBuiltinThunksInitialized())
101 0 : return false;
102 :
103 0 : for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
104 0 : const Uint32Vector& offsets = linkData.symbolicLinks[imm];
105 0 : if (offsets.empty())
106 0 : continue;
107 :
108 0 : void* target = SymbolicAddressTarget(imm);
109 0 : for (uint32_t offset : offsets) {
110 0 : uint8_t* patchAt = cs.base() + offset;
111 0 : Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
112 : PatchedImmPtr(target),
113 0 : PatchedImmPtr((void*)-1));
114 : }
115 : }
116 :
117 0 : return true;
118 : }
119 :
120 : static void
121 0 : StaticallyUnlink(uint8_t* base, const LinkDataTier& linkData)
122 : {
123 0 : for (LinkDataTier::InternalLink link : linkData.internalLinks) {
124 0 : uint8_t* patchAt = base + link.patchAtOffset;
125 0 : void* target = 0;
126 0 : if (link.isRawPointerPatch())
127 0 : *(void**)(patchAt) = target;
128 : else
129 0 : Assembler::PatchInstructionImmediate(patchAt, PatchedImmPtr(target));
130 : }
131 :
132 0 : for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
133 0 : const Uint32Vector& offsets = linkData.symbolicLinks[imm];
134 0 : if (offsets.empty())
135 0 : continue;
136 :
137 0 : void* target = SymbolicAddressTarget(imm);
138 0 : for (uint32_t offset : offsets) {
139 0 : uint8_t* patchAt = base + offset;
140 0 : Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
141 : PatchedImmPtr((void*)-1),
142 0 : PatchedImmPtr(target));
143 : }
144 : }
145 0 : }
146 :
147 : static void
148 0 : SendCodeRangesToProfiler(const CodeSegment& cs, const Bytes& bytecode, const Metadata& metadata)
149 : {
150 0 : bool enabled = false;
151 : #ifdef JS_ION_PERF
152 : enabled |= PerfFuncEnabled();
153 : #endif
154 : #ifdef MOZ_VTUNE
155 0 : enabled |= vtune::IsProfilingActive();
156 : #endif
157 0 : if (!enabled)
158 0 : return;
159 :
160 0 : for (const CodeRange& codeRange : metadata.metadata(cs.tier()).codeRanges) {
161 0 : if (!codeRange.isFunction())
162 0 : continue;
163 :
164 0 : uintptr_t start = uintptr_t(cs.base() + codeRange.begin());
165 0 : uintptr_t end = uintptr_t(cs.base() + codeRange.end());
166 0 : uintptr_t size = end - start;
167 :
168 0 : UTF8Bytes name;
169 0 : if (!metadata.getFuncName(&bytecode, codeRange.funcIndex(), &name))
170 0 : return;
171 0 : if (!name.append('\0'))
172 0 : return;
173 :
174 : // Avoid "unused" warnings
175 : (void)start;
176 : (void)size;
177 :
178 : #ifdef JS_ION_PERF
179 : if (PerfFuncEnabled()) {
180 : const char* file = metadata.filename.get();
181 : unsigned line = codeRange.funcLineOrBytecode();
182 : unsigned column = 0;
183 : writePerfSpewerWasmFunctionMap(start, size, file, line, column, name.begin());
184 : }
185 : #endif
186 : #ifdef MOZ_VTUNE
187 0 : if (vtune::IsProfilingActive())
188 0 : vtune::MarkWasm(vtune::GenerateUniqueMethodID(), name.begin(), (void*)start, size);
189 : #endif
190 : }
191 :
192 0 : return;
193 : }
194 :
195 : /* static */ UniqueConstCodeSegment
196 0 : CodeSegment::create(Tier tier,
197 : MacroAssembler& masm,
198 : const ShareableBytes& bytecode,
199 : const LinkDataTier& linkData,
200 : const Metadata& metadata)
201 : {
202 : // Round up the code size to page size since this is eventually required by
203 : // the executable-code allocator and for setting memory protection.
204 0 : uint32_t bytesNeeded = masm.bytesNeeded();
205 0 : uint32_t padding = ComputeByteAlignment(bytesNeeded, gc::SystemPageSize());
206 0 : uint32_t codeLength = bytesNeeded + padding;
207 :
208 0 : MOZ_ASSERT(linkData.functionCodeLength < codeLength);
209 :
210 0 : UniqueCodeBytes codeBytes = AllocateCodeBytes(codeLength);
211 0 : if (!codeBytes)
212 0 : return nullptr;
213 :
214 : // We'll flush the icache after static linking, in initialize().
215 0 : masm.executableCopy(codeBytes.get(), /* flushICache = */ false);
216 :
217 : // Zero the padding.
218 0 : memset(codeBytes.get() + bytesNeeded, 0, padding);
219 :
220 0 : return create(tier, Move(codeBytes), codeLength, bytecode, linkData, metadata);
221 : }
222 :
223 : /* static */ UniqueConstCodeSegment
224 0 : CodeSegment::create(Tier tier,
225 : const Bytes& unlinkedBytes,
226 : const ShareableBytes& bytecode,
227 : const LinkDataTier& linkData,
228 : const Metadata& metadata)
229 : {
230 : // The unlinked bytes are a snapshot of the MacroAssembler's contents so
231 : // round up just like in the MacroAssembler overload above.
232 0 : uint32_t padding = ComputeByteAlignment(unlinkedBytes.length(), gc::SystemPageSize());
233 0 : uint32_t codeLength = unlinkedBytes.length() + padding;
234 :
235 0 : UniqueCodeBytes codeBytes = AllocateCodeBytes(codeLength);
236 0 : if (!codeBytes)
237 0 : return nullptr;
238 :
239 0 : memcpy(codeBytes.get(), unlinkedBytes.begin(), unlinkedBytes.length());
240 0 : memset(codeBytes.get() + unlinkedBytes.length(), 0, padding);
241 :
242 0 : return create(tier, Move(codeBytes), codeLength, bytecode, linkData, metadata);
243 : }
244 :
245 : /* static */ UniqueConstCodeSegment
246 0 : CodeSegment::create(Tier tier,
247 : UniqueCodeBytes codeBytes,
248 : uint32_t codeLength,
249 : const ShareableBytes& bytecode,
250 : const LinkDataTier& linkData,
251 : const Metadata& metadata)
252 : {
253 : // These should always exist and should never be first in the code segment.
254 0 : MOZ_ASSERT(linkData.interruptOffset != 0);
255 0 : MOZ_ASSERT(linkData.outOfBoundsOffset != 0);
256 0 : MOZ_ASSERT(linkData.unalignedAccessOffset != 0);
257 :
258 0 : auto cs = js::MakeUnique<CodeSegment>();
259 0 : if (!cs)
260 0 : return nullptr;
261 :
262 0 : if (!cs->initialize(tier, Move(codeBytes), codeLength, bytecode, linkData, metadata))
263 0 : return nullptr;
264 :
265 0 : return UniqueConstCodeSegment(cs.release());
266 : }
267 :
268 : bool
269 0 : CodeSegment::initialize(Tier tier,
270 : UniqueCodeBytes codeBytes,
271 : uint32_t codeLength,
272 : const ShareableBytes& bytecode,
273 : const LinkDataTier& linkData,
274 : const Metadata& metadata)
275 : {
276 0 : MOZ_ASSERT(bytes_ == nullptr);
277 0 : MOZ_ASSERT(tier == Tier::Baseline || tier == Tier::Ion);
278 :
279 0 : tier_ = tier;
280 0 : bytes_ = Move(codeBytes);
281 0 : functionLength_ = linkData.functionCodeLength;
282 0 : length_ = codeLength;
283 0 : interruptCode_ = bytes_.get() + linkData.interruptOffset;
284 0 : outOfBoundsCode_ = bytes_.get() + linkData.outOfBoundsOffset;
285 0 : unalignedAccessCode_ = bytes_.get() + linkData.unalignedAccessOffset;
286 :
287 0 : if (!StaticallyLink(*this, linkData))
288 0 : return false;
289 :
290 0 : ExecutableAllocator::cacheFlush(bytes_.get(), RoundupCodeLength(codeLength));
291 :
292 : // Reprotect the whole region to avoid having separate RW and RX mappings.
293 0 : if (!ExecutableAllocator::makeExecutable(bytes_.get(), RoundupCodeLength(codeLength)))
294 0 : return false;
295 :
296 0 : SendCodeRangesToProfiler(*this, bytecode.bytes, metadata);
297 :
298 0 : return true;
299 : }
300 :
301 : size_t
302 0 : CodeSegment::serializedSize() const
303 : {
304 0 : return sizeof(uint32_t) + length_;
305 : }
306 :
307 : void
308 0 : CodeSegment::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const
309 : {
310 0 : *data += mallocSizeOf(this);
311 0 : *code += RoundupCodeLength(length_);
312 0 : }
313 :
314 : uint8_t*
315 0 : CodeSegment::serialize(uint8_t* cursor, const LinkDataTier& linkData) const
316 : {
317 0 : MOZ_ASSERT(tier() == Tier::Ion);
318 :
319 0 : cursor = WriteScalar<uint32_t>(cursor, length_);
320 0 : uint8_t* base = cursor;
321 0 : cursor = WriteBytes(cursor, bytes_.get(), length_);
322 0 : StaticallyUnlink(base, linkData);
323 0 : return cursor;
324 : }
325 :
326 : const uint8_t*
327 0 : CodeSegment::deserialize(const uint8_t* cursor, const ShareableBytes& bytecode,
328 : const LinkDataTier& linkData, const Metadata& metadata)
329 : {
330 : uint32_t length;
331 0 : cursor = ReadScalar<uint32_t>(cursor, &length);
332 0 : if (!cursor)
333 0 : return nullptr;
334 :
335 0 : MOZ_ASSERT(length_ % gc::SystemPageSize() == 0);
336 0 : UniqueCodeBytes bytes = AllocateCodeBytes(length);
337 0 : if (!bytes)
338 0 : return nullptr;
339 :
340 0 : cursor = ReadBytes(cursor, bytes.get(), length);
341 0 : if (!cursor)
342 0 : return nullptr;
343 :
344 0 : if (!initialize(Tier::Ion, Move(bytes), length, bytecode, linkData, metadata))
345 0 : return nullptr;
346 :
347 0 : return cursor;
348 : }
349 :
350 : size_t
351 0 : FuncExport::serializedSize() const
352 : {
353 0 : return sig_.serializedSize() +
354 0 : sizeof(pod);
355 : }
356 :
357 : uint8_t*
358 0 : FuncExport::serialize(uint8_t* cursor) const
359 : {
360 0 : cursor = sig_.serialize(cursor);
361 0 : cursor = WriteBytes(cursor, &pod, sizeof(pod));
362 0 : return cursor;
363 : }
364 :
365 : const uint8_t*
366 0 : FuncExport::deserialize(const uint8_t* cursor)
367 : {
368 0 : (cursor = sig_.deserialize(cursor)) &&
369 0 : (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
370 0 : return cursor;
371 : }
372 :
373 : size_t
374 0 : FuncExport::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
375 : {
376 0 : return sig_.sizeOfExcludingThis(mallocSizeOf);
377 : }
378 :
379 : size_t
380 0 : FuncImport::serializedSize() const
381 : {
382 0 : return sig_.serializedSize() +
383 0 : sizeof(pod);
384 : }
385 :
386 : uint8_t*
387 0 : FuncImport::serialize(uint8_t* cursor) const
388 : {
389 0 : cursor = sig_.serialize(cursor);
390 0 : cursor = WriteBytes(cursor, &pod, sizeof(pod));
391 0 : return cursor;
392 : }
393 :
394 : const uint8_t*
395 0 : FuncImport::deserialize(const uint8_t* cursor)
396 : {
397 0 : (cursor = sig_.deserialize(cursor)) &&
398 0 : (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
399 0 : return cursor;
400 : }
401 :
402 : size_t
403 0 : FuncImport::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
404 : {
405 0 : return sig_.sizeOfExcludingThis(mallocSizeOf);
406 : }
407 :
408 : static size_t
409 0 : StringLengthWithNullChar(const char* chars)
410 : {
411 0 : return chars ? strlen(chars) + 1 : 0;
412 : }
413 :
414 : size_t
415 0 : CacheableChars::serializedSize() const
416 : {
417 0 : return sizeof(uint32_t) + StringLengthWithNullChar(get());
418 : }
419 :
420 : uint8_t*
421 0 : CacheableChars::serialize(uint8_t* cursor) const
422 : {
423 0 : uint32_t lengthWithNullChar = StringLengthWithNullChar(get());
424 0 : cursor = WriteScalar<uint32_t>(cursor, lengthWithNullChar);
425 0 : cursor = WriteBytes(cursor, get(), lengthWithNullChar);
426 0 : return cursor;
427 : }
428 :
429 : const uint8_t*
430 0 : CacheableChars::deserialize(const uint8_t* cursor)
431 : {
432 : uint32_t lengthWithNullChar;
433 0 : cursor = ReadBytes(cursor, &lengthWithNullChar, sizeof(uint32_t));
434 :
435 0 : if (lengthWithNullChar) {
436 0 : reset(js_pod_malloc<char>(lengthWithNullChar));
437 0 : if (!get())
438 0 : return nullptr;
439 :
440 0 : cursor = ReadBytes(cursor, get(), lengthWithNullChar);
441 : } else {
442 0 : MOZ_ASSERT(!get());
443 : }
444 :
445 0 : return cursor;
446 : }
447 :
448 : size_t
449 0 : CacheableChars::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
450 : {
451 0 : return mallocSizeOf(get());
452 : }
453 :
454 : size_t
455 0 : MetadataTier::serializedSize() const
456 : {
457 0 : return SerializedPodVectorSize(memoryAccesses) +
458 0 : SerializedPodVectorSize(codeRanges) +
459 0 : SerializedPodVectorSize(callSites) +
460 0 : SerializedVectorSize(funcImports) +
461 0 : SerializedVectorSize(funcExports);
462 : }
463 :
464 : size_t
465 0 : MetadataTier::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
466 : {
467 0 : return memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
468 0 : codeRanges.sizeOfExcludingThis(mallocSizeOf) +
469 0 : callSites.sizeOfExcludingThis(mallocSizeOf) +
470 0 : SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
471 0 : SizeOfVectorExcludingThis(funcExports, mallocSizeOf);
472 : }
473 :
474 : uint8_t*
475 0 : MetadataTier::serialize(uint8_t* cursor) const
476 : {
477 0 : MOZ_ASSERT(debugTrapFarJumpOffsets.empty() && debugFuncToCodeRange.empty());
478 0 : cursor = SerializePodVector(cursor, memoryAccesses);
479 0 : cursor = SerializePodVector(cursor, codeRanges);
480 0 : cursor = SerializePodVector(cursor, callSites);
481 0 : cursor = SerializeVector(cursor, funcImports);
482 0 : cursor = SerializeVector(cursor, funcExports);
483 0 : return cursor;
484 : }
485 :
486 : /* static */ const uint8_t*
487 0 : MetadataTier::deserialize(const uint8_t* cursor)
488 : {
489 0 : (cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
490 0 : (cursor = DeserializePodVector(cursor, &codeRanges)) &&
491 0 : (cursor = DeserializePodVector(cursor, &callSites)) &&
492 0 : (cursor = DeserializeVector(cursor, &funcImports)) &&
493 0 : (cursor = DeserializeVector(cursor, &funcExports));
494 0 : debugTrapFarJumpOffsets.clear();
495 0 : debugFuncToCodeRange.clear();
496 0 : return cursor;
497 : }
498 :
499 : Tiers
500 0 : Metadata::tiers() const
501 : {
502 0 : return Tiers(tier_->tier);
503 : }
504 :
505 : const MetadataTier&
506 0 : Metadata::metadata(Tier t) const
507 : {
508 0 : switch (t) {
509 : case Tier::Debug:
510 : case Tier::Baseline:
511 0 : MOZ_RELEASE_ASSERT(tier_->tier == Tier::Baseline);
512 0 : return *tier_;
513 : case Tier::Ion:
514 0 : MOZ_RELEASE_ASSERT(tier_->tier == Tier::Ion);
515 0 : return *tier_;
516 : case Tier::TBD:
517 0 : return *tier_;
518 : default:
519 0 : MOZ_CRASH();
520 : }
521 : }
522 :
523 : MetadataTier&
524 0 : Metadata::metadata(Tier t)
525 : {
526 0 : switch (t) {
527 : case Tier::Debug:
528 : case Tier::Baseline:
529 0 : MOZ_RELEASE_ASSERT(tier_->tier == Tier::Baseline);
530 0 : return *tier_;
531 : case Tier::Ion:
532 0 : MOZ_RELEASE_ASSERT(tier_->tier == Tier::Ion);
533 0 : return *tier_;
534 : case Tier::TBD:
535 0 : return *tier_;
536 : default:
537 0 : MOZ_CRASH();
538 : }
539 : }
540 :
541 : size_t
542 0 : Metadata::serializedSize() const
543 : {
544 : return sizeof(pod()) +
545 0 : metadata(Tier::Ion).serializedSize() +
546 0 : SerializedVectorSize(sigIds) +
547 0 : SerializedPodVectorSize(globals) +
548 0 : SerializedPodVectorSize(tables) +
549 0 : SerializedPodVectorSize(funcNames) +
550 0 : SerializedPodVectorSize(customSections) +
551 0 : filename.serializedSize() +
552 0 : sizeof(hash);
553 : }
554 :
555 : size_t
556 0 : Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
557 : {
558 0 : size_t sum = 0;
559 :
560 0 : for (auto t : tiers())
561 0 : sum += metadata(t).sizeOfExcludingThis(mallocSizeOf);
562 :
563 0 : return sum +
564 0 : SizeOfVectorExcludingThis(sigIds, mallocSizeOf) +
565 0 : globals.sizeOfExcludingThis(mallocSizeOf) +
566 0 : tables.sizeOfExcludingThis(mallocSizeOf) +
567 0 : funcNames.sizeOfExcludingThis(mallocSizeOf) +
568 0 : customSections.sizeOfExcludingThis(mallocSizeOf) +
569 0 : filename.sizeOfExcludingThis(mallocSizeOf);
570 : }
571 :
572 : uint8_t*
573 0 : Metadata::serialize(uint8_t* cursor) const
574 : {
575 0 : MOZ_ASSERT(!debugEnabled && debugFuncArgTypes.empty() && debugFuncReturnTypes.empty());
576 0 : cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
577 0 : cursor = metadata(Tier::Ion).serialize(cursor);
578 0 : cursor = SerializeVector(cursor, sigIds);
579 0 : cursor = SerializePodVector(cursor, globals);
580 0 : cursor = SerializePodVector(cursor, tables);
581 0 : cursor = SerializePodVector(cursor, funcNames);
582 0 : cursor = SerializePodVector(cursor, customSections);
583 0 : cursor = filename.serialize(cursor);
584 0 : cursor = WriteBytes(cursor, hash, sizeof(hash));
585 0 : return cursor;
586 : }
587 :
588 : /* static */ const uint8_t*
589 0 : Metadata::deserialize(const uint8_t* cursor)
590 : {
591 0 : (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
592 0 : (cursor = metadata(Tier::Ion).deserialize(cursor)) &&
593 0 : (cursor = DeserializeVector(cursor, &sigIds)) &&
594 0 : (cursor = DeserializePodVector(cursor, &globals)) &&
595 0 : (cursor = DeserializePodVector(cursor, &tables)) &&
596 0 : (cursor = DeserializePodVector(cursor, &funcNames)) &&
597 0 : (cursor = DeserializePodVector(cursor, &customSections)) &&
598 0 : (cursor = filename.deserialize(cursor)) &&
599 0 : (cursor = ReadBytes(cursor, hash, sizeof(hash)));
600 0 : debugEnabled = false;
601 0 : debugFuncArgTypes.clear();
602 0 : debugFuncReturnTypes.clear();
603 0 : return cursor;
604 : }
605 :
606 : struct ProjectFuncIndex
607 : {
608 : const FuncExportVector& funcExports;
609 :
610 0 : explicit ProjectFuncIndex(const FuncExportVector& funcExports)
611 0 : : funcExports(funcExports)
612 0 : {}
613 0 : uint32_t operator[](size_t index) const {
614 0 : return funcExports[index].funcIndex();
615 : }
616 : };
617 :
618 : const FuncExport&
619 0 : MetadataTier::lookupFuncExport(uint32_t funcIndex) const
620 : {
621 : size_t match;
622 0 : if (!BinarySearch(ProjectFuncIndex(funcExports), 0, funcExports.length(), funcIndex, &match))
623 0 : MOZ_CRASH("missing function export");
624 :
625 0 : return funcExports[match];
626 : }
627 :
628 : bool
629 0 : Metadata::getFuncName(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const
630 : {
631 0 : if (funcIndex < funcNames.length()) {
632 0 : MOZ_ASSERT(maybeBytecode, "NameInBytecode requires preserved bytecode");
633 :
634 0 : const NameInBytecode& n = funcNames[funcIndex];
635 0 : MOZ_ASSERT(n.offset + n.length < maybeBytecode->length());
636 :
637 0 : if (n.length != 0)
638 0 : return name->append((const char*)maybeBytecode->begin() + n.offset, n.length);
639 : }
640 :
641 : // For names that are out of range or invalid, synthesize a name.
642 :
643 0 : const char beforeFuncIndex[] = "wasm-function[";
644 0 : const char afterFuncIndex[] = "]";
645 :
646 0 : ToCStringBuf cbuf;
647 0 : const char* funcIndexStr = NumberToCString(nullptr, &cbuf, funcIndex);
648 0 : MOZ_ASSERT(funcIndexStr);
649 :
650 0 : return name->append(beforeFuncIndex, strlen(beforeFuncIndex)) &&
651 0 : name->append(funcIndexStr, strlen(funcIndexStr)) &&
652 0 : name->append(afterFuncIndex, strlen(afterFuncIndex));
653 : }
654 :
655 0 : Code::Code(UniqueConstCodeSegment tier, const Metadata& metadata)
656 0 : : tier_(Move(tier)),
657 : metadata_(&metadata),
658 0 : profilingLabels_(mutexid::WasmCodeProfilingLabels, CacheableCharsVector())
659 : {
660 0 : }
661 :
662 0 : Code::Code()
663 0 : : profilingLabels_(mutexid::WasmCodeProfilingLabels, CacheableCharsVector())
664 : {
665 0 : }
666 :
667 : Tier
668 0 : Code::anyTier() const
669 : {
670 0 : return tier_->tier();
671 : }
672 :
673 : Tiers
674 0 : Code::tiers() const
675 : {
676 0 : return Tiers(tier_->tier());
677 : }
678 :
679 : const CodeSegment&
680 0 : Code::segment(Tier tier) const
681 : {
682 0 : switch (tier) {
683 : case Tier::Debug:
684 : case Tier::Baseline:
685 0 : MOZ_RELEASE_ASSERT(tier_->tier() == Tier::Baseline);
686 0 : return *tier_;
687 : case Tier::Ion:
688 0 : MOZ_RELEASE_ASSERT(tier_->tier() == Tier::Ion);
689 0 : return *tier_;
690 : case Tier::TBD:
691 0 : return *tier_;
692 : default:
693 0 : MOZ_CRASH();
694 : }
695 : }
696 :
697 : bool
698 0 : Code::containsFunctionPC(const void* pc, const CodeSegment** segmentp) const
699 : {
700 0 : for (auto t : tiers()) {
701 0 : const CodeSegment& cs = segment(t);
702 0 : if (cs.containsFunctionPC(pc)) {
703 0 : if (segmentp)
704 0 : *segmentp = &cs;
705 0 : return true;
706 : }
707 : }
708 0 : return false;
709 : }
710 :
711 : bool
712 0 : Code::containsCodePC(const void* pc, const CodeSegment** segmentp) const
713 : {
714 0 : for (auto t : tiers()) {
715 0 : const CodeSegment& cs = segment(t);
716 0 : if (cs.containsCodePC(pc)) {
717 0 : if (segmentp)
718 0 : *segmentp = &cs;
719 0 : return true;
720 : }
721 : }
722 0 : return false;
723 : }
724 :
725 : struct CallSiteRetAddrOffset
726 : {
727 : const CallSiteVector& callSites;
728 0 : explicit CallSiteRetAddrOffset(const CallSiteVector& callSites) : callSites(callSites) {}
729 0 : uint32_t operator[](size_t index) const {
730 0 : return callSites[index].returnAddressOffset();
731 : }
732 : };
733 :
734 : size_t
735 0 : Code::serializedSize() const
736 : {
737 0 : return metadata().serializedSize() +
738 0 : segment(Tier::Ion).serializedSize();
739 : }
740 :
741 : uint8_t*
742 0 : Code::serialize(uint8_t* cursor, const LinkData& linkData) const
743 : {
744 0 : MOZ_RELEASE_ASSERT(!metadata().debugEnabled);
745 :
746 0 : cursor = metadata().serialize(cursor);
747 0 : cursor = segment(Tier::Ion).serialize(cursor, linkData.linkData(Tier::Ion));
748 0 : return cursor;
749 : }
750 :
751 : const uint8_t*
752 0 : Code::deserialize(const uint8_t* cursor, const SharedBytes& bytecode, const LinkData& linkData,
753 : Metadata* maybeMetadata)
754 : {
755 0 : MutableMetadata metadata;
756 0 : if (maybeMetadata) {
757 0 : metadata = maybeMetadata;
758 : } else {
759 0 : auto tier = js::MakeUnique<MetadataTier>(Tier::Ion);
760 0 : if (!tier)
761 0 : return nullptr;
762 :
763 0 : metadata = js_new<Metadata>(Move(tier));
764 0 : if (!metadata)
765 0 : return nullptr;
766 : }
767 :
768 0 : cursor = metadata->deserialize(cursor);
769 0 : if (!cursor)
770 0 : return nullptr;
771 :
772 0 : UniqueCodeSegment codeSegment = js::MakeUnique<CodeSegment>();
773 0 : if (!codeSegment)
774 0 : return nullptr;
775 :
776 0 : cursor = codeSegment->deserialize(cursor, *bytecode, linkData.linkData(Tier::Ion), *metadata);
777 0 : if (!cursor)
778 0 : return nullptr;
779 :
780 0 : tier_ = UniqueConstCodeSegment(codeSegment.release());
781 0 : metadata_ = metadata;
782 :
783 0 : return cursor;
784 : }
785 :
786 : const CallSite*
787 0 : Code::lookupCallSite(void* returnAddress, const CodeSegment** segmentp) const
788 : {
789 0 : for (auto t : tiers()) {
790 0 : uint32_t target = ((uint8_t*)returnAddress) - segment(t).base();
791 0 : size_t lowerBound = 0;
792 0 : size_t upperBound = metadata(t).callSites.length();
793 :
794 : size_t match;
795 0 : if (BinarySearch(CallSiteRetAddrOffset(metadata(t).callSites), lowerBound, upperBound,
796 : target, &match))
797 : {
798 0 : if (segmentp)
799 0 : *segmentp = &segment(t);
800 0 : return &metadata(t).callSites[match];
801 : }
802 : }
803 :
804 0 : return nullptr;
805 : }
806 :
807 : const CodeRange*
808 0 : Code::lookupRange(void* pc, const CodeSegment** segmentp) const
809 : {
810 0 : for (auto t : tiers()) {
811 0 : CodeRange::OffsetInCode target((uint8_t*)pc - segment(t).base());
812 0 : const CodeRange* result = LookupInSorted(metadata(t).codeRanges, target);
813 0 : if (result) {
814 0 : if (segmentp)
815 0 : *segmentp = &segment(t);
816 0 : return result;
817 : }
818 : }
819 :
820 0 : return nullptr;
821 : }
822 :
823 : struct MemoryAccessOffset
824 : {
825 : const MemoryAccessVector& accesses;
826 0 : explicit MemoryAccessOffset(const MemoryAccessVector& accesses) : accesses(accesses) {}
827 0 : uintptr_t operator[](size_t index) const {
828 0 : return accesses[index].insnOffset();
829 : }
830 : };
831 :
832 : const MemoryAccess*
833 0 : Code::lookupMemoryAccess(void* pc, const CodeSegment** segmentp) const
834 : {
835 0 : for (auto t : tiers()) {
836 0 : MOZ_ASSERT(segment(t).containsFunctionPC(pc));
837 :
838 0 : const MemoryAccessVector& memoryAccesses = metadata(t).memoryAccesses;
839 :
840 0 : uint32_t target = ((uint8_t*)pc) - segment(t).base();
841 0 : size_t lowerBound = 0;
842 0 : size_t upperBound = memoryAccesses.length();
843 :
844 : size_t match;
845 0 : if (BinarySearch(MemoryAccessOffset(memoryAccesses), lowerBound, upperBound, target,
846 : &match))
847 : {
848 0 : if (segmentp)
849 0 : *segmentp = &segment(t);
850 0 : return &memoryAccesses[match];
851 : }
852 : }
853 0 : return nullptr;
854 : }
855 :
856 : // When enabled, generate profiling labels for every name in funcNames_ that is
857 : // the name of some Function CodeRange. This involves malloc() so do it now
858 : // since, once we start sampling, we'll be in a signal-handing context where we
859 : // cannot malloc.
860 : void
861 0 : Code::ensureProfilingLabels(const Bytes* maybeBytecode, bool profilingEnabled) const
862 : {
863 0 : auto labels = profilingLabels_.lock();
864 :
865 0 : if (!profilingEnabled) {
866 0 : labels->clear();
867 0 : return;
868 : }
869 :
870 0 : if (!labels->empty())
871 0 : return;
872 :
873 : // Any tier will do, we only need tier-invariant data that are incidentally
874 : // stored with the code ranges.
875 :
876 0 : for (const CodeRange& codeRange : metadata(anyTier()).codeRanges) {
877 0 : if (!codeRange.isFunction())
878 0 : continue;
879 :
880 0 : ToCStringBuf cbuf;
881 0 : const char* bytecodeStr = NumberToCString(nullptr, &cbuf, codeRange.funcLineOrBytecode());
882 0 : MOZ_ASSERT(bytecodeStr);
883 :
884 0 : UTF8Bytes name;
885 0 : if (!metadata().getFuncName(maybeBytecode, codeRange.funcIndex(), &name))
886 0 : return;
887 0 : if (!name.append(" (", 2))
888 0 : return;
889 :
890 0 : if (const char* filename = metadata().filename.get()) {
891 0 : if (!name.append(filename, strlen(filename)))
892 0 : return;
893 : } else {
894 0 : if (!name.append('?'))
895 0 : return;
896 : }
897 :
898 0 : if (!name.append(':') ||
899 0 : !name.append(bytecodeStr, strlen(bytecodeStr)) ||
900 0 : !name.append(")\0", 2))
901 : {
902 0 : return;
903 : }
904 :
905 0 : UniqueChars label(name.extractOrCopyRawBuffer());
906 0 : if (!label)
907 0 : return;
908 :
909 0 : if (codeRange.funcIndex() >= labels->length()) {
910 0 : if (!labels->resize(codeRange.funcIndex() + 1))
911 0 : return;
912 : }
913 :
914 0 : ((CacheableCharsVector&)labels)[codeRange.funcIndex()] = Move(label);
915 : }
916 : }
917 :
918 : const char*
919 0 : Code::profilingLabel(uint32_t funcIndex) const
920 : {
921 0 : auto labels = profilingLabels_.lock();
922 :
923 0 : if (funcIndex >= labels->length() || !((CacheableCharsVector&)labels)[funcIndex])
924 0 : return "?";
925 0 : return ((CacheableCharsVector&)labels)[funcIndex].get();
926 : }
927 :
928 : void
929 0 : Code::addSizeOfMiscIfNotSeen(MallocSizeOf mallocSizeOf,
930 : Metadata::SeenSet* seenMetadata,
931 : Code::SeenSet* seenCode,
932 : size_t* code,
933 : size_t* data) const
934 : {
935 0 : auto p = seenCode->lookupForAdd(this);
936 0 : if (p)
937 0 : return;
938 0 : bool ok = seenCode->add(p, this);
939 : (void)ok; // oh well
940 :
941 0 : *data += mallocSizeOf(this) +
942 0 : metadata().sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) +
943 0 : profilingLabels_.lock()->sizeOfExcludingThis(mallocSizeOf);
944 :
945 0 : for (auto t : tiers())
946 0 : segment(t).addSizeOfMisc(mallocSizeOf, code, data);
947 : }
|