OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <memory> | 5 #include <memory> |
6 | 6 |
7 #include "src/base/atomic-utils.h" | 7 #include "src/base/atomic-utils.h" |
8 #include "src/code-stubs.h" | 8 #include "src/code-stubs.h" |
9 | 9 |
10 #include "src/macro-assembler.h" | 10 #include "src/macro-assembler.h" |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref, | 63 void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref, |
64 Handle<Object> new_ref) { | 64 Handle<Object> new_ref) { |
65 for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done(); | 65 for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done(); |
66 it.next()) { | 66 it.next()) { |
67 if (it.rinfo()->target_object() == *old_ref) { | 67 if (it.rinfo()->target_object() == *old_ref) { |
68 it.rinfo()->set_target_object(*new_ref); | 68 it.rinfo()->set_target_object(*new_ref); |
69 } | 69 } |
70 } | 70 } |
71 } | 71 } |
72 | 72 |
73 Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) { | 73 static void MemoryFinalizer(const v8::WeakCallbackInfo<void>& data) { |
74 if (size > (WasmModule::kV8MaxPages * WasmModule::kPageSize)) { | 74 JSArrayBuffer** p = reinterpret_cast<JSArrayBuffer**>(data.GetParameter()); |
75 // TODO(titzer): lift restriction on maximum memory allocated here. | 75 JSArrayBuffer* buffer = *p; |
76 return Handle<JSArrayBuffer>::null(); | |
77 } | |
78 void* memory = isolate->array_buffer_allocator()->Allocate(size); | |
79 if (memory == nullptr) { | |
80 return Handle<JSArrayBuffer>::null(); | |
81 } | |
82 | 76 |
83 #if DEBUG | 77 void* memory = buffer->backing_store(); |
84 // Double check the API allocator actually zero-initialized the memory. | 78 base::OS::Free(memory, |
85 const byte* bytes = reinterpret_cast<const byte*>(memory); | 79 RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize())); |
86 for (size_t i = 0; i < size; ++i) { | 80 |
87 DCHECK_EQ(0, bytes[i]); | 81 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory( |
88 } | 82 -buffer->byte_length()->Number()); |
| 83 |
| 84 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); |
| 85 } |
| 86 |
| 87 #if V8_TARGET_ARCH_64_BIT |
| 88 const bool kGuardRegionsSupported = true; |
| 89 #else |
| 90 const bool kGuardRegionsSupported = false; |
89 #endif | 91 #endif |
90 | 92 |
91 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); | 93 bool EnableGuardRegions() { |
92 JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size)); | 94 return FLAG_wasm_guard_pages && kGuardRegionsSupported; |
93 buffer->set_is_neuterable(false); | 95 } |
94 return buffer; | 96 |
| 97 void* TryAllocateBackingStore(Isolate* isolate, size_t size, |
| 98 bool enable_guard_regions, bool& is_external) { |
| 99 is_external = false; |
| 100 // TODO(eholk): Right now enable_guard_regions has no effect on 32-bit |
| 101 // systems. It may be safer to fail instead, given that other code might do |
| 102 // things that would be unsafe if they expected guard pages where there |
| 103 // weren't any. |
| 104 if (enable_guard_regions && kGuardRegionsSupported) { |
| 105 // TODO(eholk): On Windows we want to make sure we don't commit the guard |
| 106 // pages yet. |
| 107 |
| 108 // We always allocate the largest possible offset into the heap, so the |
| 109 // addressable memory after the guard page can be made inaccessible. |
| 110 const size_t alloc_size = |
| 111 RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize()); |
| 112 DCHECK_EQ(0u, size % base::OS::CommitPageSize()); |
| 113 |
| 114 // AllocateGuarded makes the whole region inaccessible by default. |
| 115 void* memory = base::OS::AllocateGuarded(alloc_size); |
| 116 if (memory == nullptr) { |
| 117 return nullptr; |
| 118 } |
| 119 |
| 120 // Make the part we care about accessible. |
| 121 base::OS::Unprotect(memory, size); |
| 122 |
| 123 reinterpret_cast<v8::Isolate*>(isolate) |
| 124 ->AdjustAmountOfExternalAllocatedMemory(size); |
| 125 |
| 126 is_external = true; |
| 127 return memory; |
| 128 } else { |
| 129 void* memory = isolate->array_buffer_allocator()->Allocate(size); |
| 130 return memory; |
| 131 } |
95 } | 132 } |
96 | 133 |
97 void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table, | 134 void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table, |
98 Address old_start, Address start, | 135 Address old_start, Address start, |
99 uint32_t prev_size, uint32_t new_size) { | 136 uint32_t prev_size, uint32_t new_size) { |
100 for (int i = 0; i < code_table->length(); ++i) { | 137 for (int i = 0; i < code_table->length(); ++i) { |
101 DCHECK(code_table->get(i)->IsCode()); | 138 DCHECK(code_table->get(i)->IsCode()); |
102 Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i))); | 139 Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i))); |
103 AllowDeferredHandleDereference embedding_raw_address; | 140 AllowDeferredHandleDereference embedding_raw_address; |
104 int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) | | 141 int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) | |
(...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
601 Handle<WasmCompiledModule> compiled_module, int func_index) { | 638 Handle<WasmCompiledModule> compiled_module, int func_index) { |
602 int offset, length; | 639 int offset, length; |
603 std::tie(offset, length) = | 640 std::tie(offset, length) = |
604 GetFunctionOffsetAndLength(compiled_module, func_index); | 641 GetFunctionOffsetAndLength(compiled_module, func_index); |
605 return Vector<const uint8_t>( | 642 return Vector<const uint8_t>( |
606 compiled_module->module_bytes()->GetChars() + offset, length); | 643 compiled_module->module_bytes()->GetChars() + offset, length); |
607 } | 644 } |
608 | 645 |
609 } // namespace | 646 } // namespace |
610 | 647 |
| 648 Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, |
| 649 bool enable_guard_regions) { |
| 650 if (size > (WasmModule::kV8MaxPages * WasmModule::kPageSize)) { |
| 651 // TODO(titzer): lift restriction on maximum memory allocated here. |
| 652 return Handle<JSArrayBuffer>::null(); |
| 653 } |
| 654 |
| 655 enable_guard_regions = enable_guard_regions && kGuardRegionsSupported; |
| 656 |
| 657 bool is_external; // Set by TryAllocateBackingStore |
| 658 void* memory = |
| 659 TryAllocateBackingStore(isolate, size, enable_guard_regions, is_external); |
| 660 |
| 661 if (memory == nullptr) { |
| 662 return Handle<JSArrayBuffer>::null(); |
| 663 } |
| 664 |
| 665 #if DEBUG |
| 666 // Double check the API allocator actually zero-initialized the memory. |
| 667 const byte* bytes = reinterpret_cast<const byte*>(memory); |
| 668 for (size_t i = 0; i < size; ++i) { |
| 669 DCHECK_EQ(0, bytes[i]); |
| 670 } |
| 671 #endif |
| 672 |
| 673 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); |
| 674 JSArrayBuffer::Setup(buffer, isolate, is_external, memory, |
| 675 static_cast<int>(size)); |
| 676 buffer->set_is_neuterable(false); |
| 677 buffer->set_has_guard_region(enable_guard_regions); |
| 678 |
| 679 if (is_external) { |
| 680 // We mark the buffer as external if we allocated it here with guard |
| 681 // pages. That means we need to arrange for it to be freed. |
| 682 |
| 683 // TODO(eholk): Finalizers may not run when the main thread is shutting |
| 684 // down, which means we may leak memory here. |
| 685 Handle<Object> global_handle = isolate->global_handles()->Create(*buffer); |
| 686 GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(), |
| 687 &MemoryFinalizer, v8::WeakCallbackType::kFinalizer); |
| 688 } |
| 689 |
| 690 return buffer; |
| 691 } |
| 692 |
611 const char* wasm::SectionName(WasmSectionCode code) { | 693 const char* wasm::SectionName(WasmSectionCode code) { |
612 switch (code) { | 694 switch (code) { |
613 case kUnknownSectionCode: | 695 case kUnknownSectionCode: |
614 return "Unknown"; | 696 return "Unknown"; |
615 case kTypeSectionCode: | 697 case kTypeSectionCode: |
616 return "Type"; | 698 return "Type"; |
617 case kImportSectionCode: | 699 case kImportSectionCode: |
618 return "Import"; | 700 return "Import"; |
619 case kFunctionSectionCode: | 701 case kFunctionSectionCode: |
620 return "Function"; | 702 return "Function"; |
(...skipping 431 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1052 //-------------------------------------------------------------------------- | 1134 //-------------------------------------------------------------------------- |
1053 Handle<WasmInstanceObject> instance = | 1135 Handle<WasmInstanceObject> instance = |
1054 WasmInstanceObject::New(isolate_, compiled_module_); | 1136 WasmInstanceObject::New(isolate_, compiled_module_); |
1055 | 1137 |
1056 //-------------------------------------------------------------------------- | 1138 //-------------------------------------------------------------------------- |
1057 // Set up the globals for the new instance. | 1139 // Set up the globals for the new instance. |
1058 //-------------------------------------------------------------------------- | 1140 //-------------------------------------------------------------------------- |
1059 MaybeHandle<JSArrayBuffer> old_globals; | 1141 MaybeHandle<JSArrayBuffer> old_globals; |
1060 uint32_t globals_size = module_->globals_size; | 1142 uint32_t globals_size = module_->globals_size; |
1061 if (globals_size > 0) { | 1143 if (globals_size > 0) { |
| 1144 const bool enable_guard_regions = false; |
1062 Handle<JSArrayBuffer> global_buffer = | 1145 Handle<JSArrayBuffer> global_buffer = |
1063 NewArrayBuffer(isolate_, globals_size); | 1146 NewArrayBuffer(isolate_, globals_size, enable_guard_regions); |
1064 globals_ = global_buffer; | 1147 globals_ = global_buffer; |
1065 if (globals_.is_null()) { | 1148 if (globals_.is_null()) { |
1066 thrower_->RangeError("Out of memory: wasm globals"); | 1149 thrower_->RangeError("Out of memory: wasm globals"); |
1067 return nothing; | 1150 return nothing; |
1068 } | 1151 } |
1069 Address old_address = | 1152 Address old_address = |
1070 owner.is_null() ? nullptr : GetGlobalStartAddressFromCodeTemplate( | 1153 owner.is_null() ? nullptr : GetGlobalStartAddressFromCodeTemplate( |
1071 isolate_->heap()->undefined_value(), | 1154 isolate_->heap()->undefined_value(), |
1072 *owner.ToHandleChecked()); | 1155 *owner.ToHandleChecked()); |
1073 RelocateGlobals(code_table, old_address, | 1156 RelocateGlobals(code_table, old_address, |
(...skipping 28 matching lines...) Expand all Loading... |
1102 // Set up the memory for the new instance. | 1185 // Set up the memory for the new instance. |
1103 //-------------------------------------------------------------------------- | 1186 //-------------------------------------------------------------------------- |
1104 MaybeHandle<JSArrayBuffer> old_memory; | 1187 MaybeHandle<JSArrayBuffer> old_memory; |
1105 | 1188 |
1106 uint32_t min_mem_pages = module_->min_mem_pages; | 1189 uint32_t min_mem_pages = module_->min_mem_pages; |
1107 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); | 1190 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); |
1108 | 1191 |
1109 if (!memory_.is_null()) { | 1192 if (!memory_.is_null()) { |
1110 // Set externally passed ArrayBuffer non neuterable. | 1193 // Set externally passed ArrayBuffer non neuterable. |
1111 memory_->set_is_neuterable(false); | 1194 memory_->set_is_neuterable(false); |
| 1195 |
| 1196 DCHECK_IMPLIES(EnableGuardRegions(), module_->origin == kAsmJsOrigin || |
| 1197 memory_->has_guard_region()); |
1112 } else if (min_mem_pages > 0) { | 1198 } else if (min_mem_pages > 0) { |
1113 memory_ = AllocateMemory(min_mem_pages); | 1199 memory_ = AllocateMemory(min_mem_pages); |
1114 if (memory_.is_null()) return nothing; // failed to allocate memory | 1200 if (memory_.is_null()) return nothing; // failed to allocate memory |
1115 } | 1201 } |
1116 | 1202 |
1117 if (!memory_.is_null()) { | 1203 if (!memory_.is_null()) { |
1118 instance->set_memory_buffer(*memory_); | 1204 instance->set_memory_buffer(*memory_); |
1119 Address mem_start = static_cast<Address>(memory_->backing_store()); | 1205 Address mem_start = static_cast<Address>(memory_->backing_store()); |
1120 uint32_t mem_size = | 1206 uint32_t mem_size = |
1121 static_cast<uint32_t>(memory_->byte_length()->Number()); | 1207 static_cast<uint32_t>(memory_->byte_length()->Number()); |
(...skipping 452 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1574 } | 1660 } |
1575 } | 1661 } |
1576 } | 1662 } |
1577 | 1663 |
1578 // Allocate memory for a module instance as a new JSArrayBuffer. | 1664 // Allocate memory for a module instance as a new JSArrayBuffer. |
1579 Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { | 1665 Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { |
1580 if (min_mem_pages > WasmModule::kV8MaxPages) { | 1666 if (min_mem_pages > WasmModule::kV8MaxPages) { |
1581 thrower_->RangeError("Out of memory: wasm memory too large"); | 1667 thrower_->RangeError("Out of memory: wasm memory too large"); |
1582 return Handle<JSArrayBuffer>::null(); | 1668 return Handle<JSArrayBuffer>::null(); |
1583 } | 1669 } |
1584 Handle<JSArrayBuffer> mem_buffer = | 1670 const bool enable_guard_regions = EnableGuardRegions(); |
1585 NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize); | 1671 Handle<JSArrayBuffer> mem_buffer = NewArrayBuffer( |
| 1672 isolate_, min_mem_pages * WasmModule::kPageSize, enable_guard_regions); |
1586 | 1673 |
1587 if (mem_buffer.is_null()) { | 1674 if (mem_buffer.is_null()) { |
1588 thrower_->RangeError("Out of memory: wasm memory"); | 1675 thrower_->RangeError("Out of memory: wasm memory"); |
1589 } | 1676 } |
1590 return mem_buffer; | 1677 return mem_buffer; |
1591 } | 1678 } |
1592 | 1679 |
1593 // Process the exports, creating wrappers for functions, tables, memories, | 1680 // Process the exports, creating wrappers for functions, tables, memories, |
1594 // and globals. | 1681 // and globals. |
1595 void ProcessExports(Handle<FixedArray> code_table, | 1682 void ProcessExports(Handle<FixedArray> code_table, |
(...skipping 494 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2090 DCHECK_NOT_NULL(old_mem_start); | 2177 DCHECK_NOT_NULL(old_mem_start); |
2091 DCHECK(old_size + pages * WasmModule::kPageSize <= | 2178 DCHECK(old_size + pages * WasmModule::kPageSize <= |
2092 std::numeric_limits<uint32_t>::max()); | 2179 std::numeric_limits<uint32_t>::max()); |
2093 new_size = old_size + pages * WasmModule::kPageSize; | 2180 new_size = old_size + pages * WasmModule::kPageSize; |
2094 } | 2181 } |
2095 | 2182 |
2096 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size || | 2183 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size || |
2097 WasmModule::kV8MaxPages * WasmModule::kPageSize < new_size) { | 2184 WasmModule::kV8MaxPages * WasmModule::kPageSize < new_size) { |
2098 return -1; | 2185 return -1; |
2099 } | 2186 } |
2100 Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size); | 2187 |
2101 if (buffer.is_null()) return -1; | 2188 Handle<JSArrayBuffer> buffer; |
2102 Address new_mem_start = static_cast<Address>(buffer->backing_store()); | 2189 |
2103 if (old_size != 0) { | 2190 if (!old_buffer.is_null() && old_buffer->has_guard_region()) { |
2104 memcpy(new_mem_start, old_mem_start, old_size); | 2191 // We don't move the backing store, we simply change the protection to make |
| 2192 // more of it accessible. |
| 2193 base::OS::Unprotect(old_buffer->backing_store(), new_size); |
| 2194 reinterpret_cast<v8::Isolate*>(isolate) |
| 2195 ->AdjustAmountOfExternalAllocatedMemory(pages * WasmModule::kPageSize); |
| 2196 Handle<Object> new_size_object = |
| 2197 isolate->factory()->NewNumberFromSize(new_size); |
| 2198 old_buffer->set_byte_length(*new_size_object); |
| 2199 |
| 2200 SetInstanceMemory(instance, *old_buffer); |
| 2201 Handle<FixedArray> code_table = |
| 2202 instance->get_compiled_module()->code_table(); |
| 2203 RelocateMemoryReferencesInCode(code_table, old_mem_start, old_mem_start, |
| 2204 old_size, new_size); |
| 2205 buffer = old_buffer; |
| 2206 } else { |
| 2207 const bool enable_guard_regions = false; |
| 2208 buffer = NewArrayBuffer(isolate, new_size, enable_guard_regions); |
| 2209 if (buffer.is_null()) return -1; |
| 2210 Address new_mem_start = static_cast<Address>(buffer->backing_store()); |
| 2211 if (old_size != 0) { |
| 2212 memcpy(new_mem_start, old_mem_start, old_size); |
| 2213 } |
| 2214 SetInstanceMemory(instance, *buffer); |
| 2215 Handle<FixedArray> code_table = |
| 2216 instance->get_compiled_module()->code_table(); |
| 2217 RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start, |
| 2218 old_size, new_size); |
2105 } | 2219 } |
| 2220 |
2106 SetInstanceMemory(instance, *buffer); | 2221 SetInstanceMemory(instance, *buffer); |
2107 Handle<FixedArray> code_table = instance->get_compiled_module()->code_table(); | |
2108 RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start, | |
2109 old_size, new_size); | |
2110 if (instance->has_memory_object()) { | 2222 if (instance->has_memory_object()) { |
2111 instance->get_memory_object()->set_buffer(*buffer); | 2223 instance->get_memory_object()->set_buffer(*buffer); |
2112 } | 2224 } |
2113 | 2225 |
2114 DCHECK(old_size % WasmModule::kPageSize == 0); | 2226 DCHECK(old_size % WasmModule::kPageSize == 0); |
2115 return (old_size / WasmModule::kPageSize); | 2227 return (old_size / WasmModule::kPageSize); |
2116 } | 2228 } |
2117 | 2229 |
2118 void testing::ValidateInstancesChain(Isolate* isolate, | 2230 void testing::ValidateInstancesChain(Isolate* isolate, |
2119 Handle<WasmModuleObject> module_obj, | 2231 Handle<WasmModuleObject> module_obj, |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2193 MaybeHandle<String> WasmCompiledModule::GetFunctionName( | 2305 MaybeHandle<String> WasmCompiledModule::GetFunctionName( |
2194 Handle<WasmCompiledModule> compiled_module, uint32_t func_index) { | 2306 Handle<WasmCompiledModule> compiled_module, uint32_t func_index) { |
2195 DCHECK_LT(func_index, compiled_module->module()->functions.size()); | 2307 DCHECK_LT(func_index, compiled_module->module()->functions.size()); |
2196 WasmFunction& function = compiled_module->module()->functions[func_index]; | 2308 WasmFunction& function = compiled_module->module()->functions[func_index]; |
2197 Isolate* isolate = compiled_module->GetIsolate(); | 2309 Isolate* isolate = compiled_module->GetIsolate(); |
2198 MaybeHandle<String> string = ExtractStringFromModuleBytes( | 2310 MaybeHandle<String> string = ExtractStringFromModuleBytes( |
2199 isolate, compiled_module, function.name_offset, function.name_length); | 2311 isolate, compiled_module, function.name_offset, function.name_length); |
2200 if (!string.is_null()) return string.ToHandleChecked(); | 2312 if (!string.is_null()) return string.ToHandleChecked(); |
2201 return {}; | 2313 return {}; |
2202 } | 2314 } |
OLD | NEW |