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 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref, | 79 void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref, |
80 Handle<Object> new_ref) { | 80 Handle<Object> new_ref) { |
81 for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done(); | 81 for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done(); |
82 it.next()) { | 82 it.next()) { |
83 if (it.rinfo()->target_object() == *old_ref) { | 83 if (it.rinfo()->target_object() == *old_ref) { |
84 it.rinfo()->set_target_object(*new_ref); | 84 it.rinfo()->set_target_object(*new_ref); |
85 } | 85 } |
86 } | 86 } |
87 } | 87 } |
88 | 88 |
89 Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) { | 89 #if V8_HOST_ARCH_64_BIT |
90 if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) { | 90 static void MemoryFinalizer(const v8::WeakCallbackInfo<void>& data) { |
91 // TODO(titzer): lift restriction on maximum memory allocated here. | 91 JSArrayBuffer** p = reinterpret_cast<JSArrayBuffer**>(data.GetParameter()); |
92 return Handle<JSArrayBuffer>::null(); | 92 JSArrayBuffer* buffer = *p; |
93 } | |
94 void* memory = isolate->array_buffer_allocator()->Allocate(size); | |
95 if (memory == nullptr) { | |
96 return Handle<JSArrayBuffer>::null(); | |
97 } | |
98 | 93 |
99 #if DEBUG | 94 void* memory = buffer->backing_store(); |
100 // Double check the API allocator actually zero-initialized the memory. | 95 base::OS::Free(memory, kWasmMaxHeapOffset); |
101 const byte* bytes = reinterpret_cast<const byte*>(memory); | 96 |
102 for (size_t i = 0; i < size; ++i) { | 97 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory( |
103 DCHECK_EQ(0, bytes[i]); | 98 -buffer->byte_length()->Number()); |
104 } | 99 |
| 100 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); |
| 101 } |
105 #endif | 102 #endif |
106 | 103 |
107 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); | 104 void* AllocateBackingStore(Isolate* isolate, size_t size, bool guard) { |
108 JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size)); | 105 if (guard) { |
109 buffer->set_is_neuterable(false); | 106 #if V8_HOST_ARCH_64_BIT |
110 return buffer; | 107 void* memory; |
| 108 // TODO(eholk): On Windows we want to make sure we don't commit the guard |
| 109 // pages yet. |
| 110 |
| 111 // We always allocate the largest possible offset into the heap, so the |
| 112 // addressable memory after the guard page can be make inaccessible. |
| 113 const size_t alloc_size = kWasmMaxHeapOffset; |
| 114 DCHECK(size % base::OS::CommitPageSize() == 0); |
| 115 |
| 116 size_t allocated_size = 0; |
| 117 const bool is_executable = false; |
| 118 memory = base::OS::Allocate(alloc_size, &allocated_size, is_executable); |
| 119 if (allocated_size < alloc_size) { |
| 120 base::OS::Free(memory, allocated_size); |
| 121 return nullptr; |
| 122 } |
| 123 |
| 124 if (memory == nullptr) { |
| 125 return nullptr; |
| 126 } |
| 127 |
| 128 byte* bytes = reinterpret_cast<byte*>(memory); |
| 129 base::OS::Guard(bytes + size, alloc_size - size); |
| 130 |
| 131 reinterpret_cast<v8::Isolate*>(isolate) |
| 132 ->AdjustAmountOfExternalAllocatedMemory(size); |
| 133 |
| 134 return memory; |
| 135 #else |
| 136 DCHECK(false && "Guard pages are not supported on 32-bit systems"); |
| 137 #endif |
| 138 } else { |
| 139 void* memory = isolate->array_buffer_allocator()->Allocate(size); |
| 140 return memory; |
| 141 } |
111 } | 142 } |
112 | 143 |
113 void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table, | 144 void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table, |
114 Address old_start, Address start, | 145 Address old_start, Address start, |
115 uint32_t prev_size, uint32_t new_size) { | 146 uint32_t prev_size, uint32_t new_size) { |
116 for (int i = 0; i < code_table->length(); ++i) { | 147 for (int i = 0; i < code_table->length(); ++i) { |
117 DCHECK(code_table->get(i)->IsCode()); | 148 DCHECK(code_table->get(i)->IsCode()); |
118 Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i))); | 149 Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i))); |
119 AllowDeferredHandleDereference embedding_raw_address; | 150 AllowDeferredHandleDereference embedding_raw_address; |
120 int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) | | 151 int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) | |
(...skipping 474 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
595 TRACE_CHAIN(WasmCompiledModule::cast(wasm_module->GetInternalField(0))); | 626 TRACE_CHAIN(WasmCompiledModule::cast(wasm_module->GetInternalField(0))); |
596 TRACE("}\n"); | 627 TRACE("}\n"); |
597 } | 628 } |
598 compiled_module->reset_weak_owning_instance(); | 629 compiled_module->reset_weak_owning_instance(); |
599 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); | 630 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); |
600 TRACE("}\n"); | 631 TRACE("}\n"); |
601 } | 632 } |
602 | 633 |
603 } // namespace | 634 } // namespace |
604 | 635 |
| 636 Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, |
| 637 bool guard) { |
| 638 if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) { |
| 639 // TODO(titzer): lift restriction on maximum memory allocated here. |
| 640 return Handle<JSArrayBuffer>::null(); |
| 641 } |
| 642 |
| 643 void* memory = AllocateBackingStore(isolate, size, guard); |
| 644 bool is_external = guard; |
| 645 |
| 646 if (memory == nullptr) { |
| 647 return Handle<JSArrayBuffer>::null(); |
| 648 } |
| 649 |
| 650 #if DEBUG |
| 651 // Double check the API allocator actually zero-initialized the memory. |
| 652 const byte* bytes = reinterpret_cast<const byte*>(memory); |
| 653 for (size_t i = 0; i < size; ++i) { |
| 654 DCHECK_EQ(0, bytes[i]); |
| 655 } |
| 656 #endif |
| 657 |
| 658 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); |
| 659 JSArrayBuffer::Setup(buffer, isolate, is_external, memory, |
| 660 static_cast<int>(size)); |
| 661 buffer->set_is_neuterable(false); |
| 662 buffer->set_has_guard_region(guard); |
| 663 |
| 664 #if V8_HOST_ARCH_64_BIT |
| 665 if (is_external) { |
| 666 // We mark the buffer as external if we allocated it here with guard |
| 667 // pages. That means we need to arrange for it to be freed. |
| 668 |
| 669 // TODO(eholk): Finalizers may not run when the main thread is shutting |
| 670 // down, which means we may leak memory here. |
| 671 Handle<Object> global_handle = isolate->global_handles()->Create(*buffer); |
| 672 GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(), |
| 673 &MemoryFinalizer, v8::WeakCallbackType::kFinalizer); |
| 674 } |
| 675 #endif |
| 676 |
| 677 return buffer; |
| 678 } |
| 679 |
605 const char* wasm::SectionName(WasmSectionCode code) { | 680 const char* wasm::SectionName(WasmSectionCode code) { |
606 switch (code) { | 681 switch (code) { |
607 case kUnknownSectionCode: | 682 case kUnknownSectionCode: |
608 return "Unknown"; | 683 return "Unknown"; |
609 case kTypeSectionCode: | 684 case kTypeSectionCode: |
610 return "Type"; | 685 return "Type"; |
611 case kImportSectionCode: | 686 case kImportSectionCode: |
612 return "Import"; | 687 return "Import"; |
613 case kFunctionSectionCode: | 688 case kFunctionSectionCode: |
614 return "Function"; | 689 return "Function"; |
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
946 instance->SetInternalField(kWasmMemObject, | 1021 instance->SetInternalField(kWasmMemObject, |
947 isolate_->heap()->undefined_value()); | 1022 isolate_->heap()->undefined_value()); |
948 | 1023 |
949 //-------------------------------------------------------------------------- | 1024 //-------------------------------------------------------------------------- |
950 // Set up the globals for the new instance. | 1025 // Set up the globals for the new instance. |
951 //-------------------------------------------------------------------------- | 1026 //-------------------------------------------------------------------------- |
952 MaybeHandle<JSArrayBuffer> old_globals; | 1027 MaybeHandle<JSArrayBuffer> old_globals; |
953 MaybeHandle<JSArrayBuffer> globals; | 1028 MaybeHandle<JSArrayBuffer> globals; |
954 uint32_t globals_size = module_->globals_size; | 1029 uint32_t globals_size = module_->globals_size; |
955 if (globals_size > 0) { | 1030 if (globals_size > 0) { |
| 1031 const bool guard = false; |
956 Handle<JSArrayBuffer> global_buffer = | 1032 Handle<JSArrayBuffer> global_buffer = |
957 NewArrayBuffer(isolate_, globals_size); | 1033 NewArrayBuffer(isolate_, globals_size, guard); |
958 globals = global_buffer; | 1034 globals = global_buffer; |
959 if (globals.is_null()) { | 1035 if (globals.is_null()) { |
960 thrower_->RangeError("Out of memory: wasm globals"); | 1036 thrower_->RangeError("Out of memory: wasm globals"); |
961 return nothing; | 1037 return nothing; |
962 } | 1038 } |
963 Address old_address = owner.is_null() | 1039 Address old_address = owner.is_null() |
964 ? nullptr | 1040 ? nullptr |
965 : GetGlobalStartAddressFromCodeTemplate( | 1041 : GetGlobalStartAddressFromCodeTemplate( |
966 isolate_->heap()->undefined_value(), | 1042 isolate_->heap()->undefined_value(), |
967 JSObject::cast(*owner.ToHandleChecked())); | 1043 JSObject::cast(*owner.ToHandleChecked())); |
(...skipping 18 matching lines...) Expand all Loading... |
986 //-------------------------------------------------------------------------- | 1062 //-------------------------------------------------------------------------- |
987 MaybeHandle<JSArrayBuffer> old_memory; | 1063 MaybeHandle<JSArrayBuffer> old_memory; |
988 | 1064 |
989 uint32_t min_mem_pages = module_->min_mem_pages; | 1065 uint32_t min_mem_pages = module_->min_mem_pages; |
990 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); | 1066 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); |
991 // TODO(wasm): re-enable counter for max_mem_pages when we use that field. | 1067 // TODO(wasm): re-enable counter for max_mem_pages when we use that field. |
992 | 1068 |
993 if (!memory_.is_null()) { | 1069 if (!memory_.is_null()) { |
994 // Set externally passed ArrayBuffer non neuterable. | 1070 // Set externally passed ArrayBuffer non neuterable. |
995 memory_->set_is_neuterable(false); | 1071 memory_->set_is_neuterable(false); |
| 1072 |
| 1073 DCHECK_IMPLIES(FLAG_wasm_guard_pages, module_->origin == kAsmJsOrigin || |
| 1074 memory_->has_guard_region()); |
996 } else if (min_mem_pages > 0) { | 1075 } else if (min_mem_pages > 0) { |
997 memory_ = AllocateMemory(min_mem_pages); | 1076 memory_ = AllocateMemory(min_mem_pages); |
998 if (memory_.is_null()) return nothing; // failed to allocate memory | 1077 if (memory_.is_null()) return nothing; // failed to allocate memory |
999 } | 1078 } |
1000 | 1079 |
1001 if (!memory_.is_null()) { | 1080 if (!memory_.is_null()) { |
1002 instance->SetInternalField(kWasmMemArrayBuffer, *memory_); | 1081 instance->SetInternalField(kWasmMemArrayBuffer, *memory_); |
1003 Address mem_start = static_cast<Address>(memory_->backing_store()); | 1082 Address mem_start = static_cast<Address>(memory_->backing_store()); |
1004 uint32_t mem_size = | 1083 uint32_t mem_size = |
1005 static_cast<uint32_t>(memory_->byte_length()->Number()); | 1084 static_cast<uint32_t>(memory_->byte_length()->Number()); |
(...skipping 494 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1500 } | 1579 } |
1501 } | 1580 } |
1502 } | 1581 } |
1503 | 1582 |
1504 // Allocate memory for a module instance as a new JSArrayBuffer. | 1583 // Allocate memory for a module instance as a new JSArrayBuffer. |
1505 Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { | 1584 Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { |
1506 if (min_mem_pages > WasmModule::kMaxMemPages) { | 1585 if (min_mem_pages > WasmModule::kMaxMemPages) { |
1507 thrower_->RangeError("Out of memory: wasm memory too large"); | 1586 thrower_->RangeError("Out of memory: wasm memory too large"); |
1508 return Handle<JSArrayBuffer>::null(); | 1587 return Handle<JSArrayBuffer>::null(); |
1509 } | 1588 } |
| 1589 const bool guard = FLAG_wasm_guard_pages; |
1510 Handle<JSArrayBuffer> mem_buffer = | 1590 Handle<JSArrayBuffer> mem_buffer = |
1511 NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize); | 1591 NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize, guard); |
1512 | 1592 |
1513 if (mem_buffer.is_null()) { | 1593 if (mem_buffer.is_null()) { |
1514 thrower_->RangeError("Out of memory: wasm memory"); | 1594 thrower_->RangeError("Out of memory: wasm memory"); |
1515 } | 1595 } |
1516 return mem_buffer; | 1596 return mem_buffer; |
1517 } | 1597 } |
1518 | 1598 |
1519 // Process the exports, creating wrappers for functions, tables, memories, | 1599 // Process the exports, creating wrappers for functions, tables, memories, |
1520 // and globals. | 1600 // and globals. |
1521 void ProcessExports(MaybeHandle<JSArrayBuffer> globals, | 1601 void ProcessExports(MaybeHandle<JSArrayBuffer> globals, |
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1992 // "undefined" case above. | 2072 // "undefined" case above. |
1993 DCHECK_NOT_NULL(old_mem_start); | 2073 DCHECK_NOT_NULL(old_mem_start); |
1994 DCHECK(old_size + pages * WasmModule::kPageSize <= | 2074 DCHECK(old_size + pages * WasmModule::kPageSize <= |
1995 std::numeric_limits<uint32_t>::max()); | 2075 std::numeric_limits<uint32_t>::max()); |
1996 new_size = old_size + pages * WasmModule::kPageSize; | 2076 new_size = old_size + pages * WasmModule::kPageSize; |
1997 } | 2077 } |
1998 | 2078 |
1999 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) { | 2079 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) { |
2000 return -1; | 2080 return -1; |
2001 } | 2081 } |
2002 Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size); | 2082 |
2003 if (buffer.is_null()) return -1; | 2083 Handle<JSArrayBuffer> buffer; |
2004 Address new_mem_start = static_cast<Address>(buffer->backing_store()); | 2084 |
2005 if (old_size != 0) { | 2085 if (FLAG_wasm_guard_pages && !old_buffer.is_null()) { |
2006 memcpy(new_mem_start, old_mem_start, old_size); | 2086 base::OS::Unprotect(old_buffer->backing_store(), new_size); |
| 2087 reinterpret_cast<v8::Isolate*>(isolate) |
| 2088 ->AdjustAmountOfExternalAllocatedMemory(pages * WasmModule::kPageSize); |
| 2089 Handle<Object> new_size_object = |
| 2090 isolate->factory()->NewNumberFromSize(new_size); |
| 2091 old_buffer->set_byte_length(*new_size_object); |
| 2092 |
| 2093 SetInstanceMemory(instance, *old_buffer); |
| 2094 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table(); |
| 2095 RelocateMemoryReferencesInCode(code_table, old_mem_start, old_mem_start, |
| 2096 old_size, new_size); |
| 2097 buffer = old_buffer; |
| 2098 } else { |
| 2099 const bool guard = FLAG_wasm_guard_pages; |
| 2100 buffer = NewArrayBuffer(isolate, new_size, guard); |
| 2101 if (buffer.is_null()) return -1; |
| 2102 Address new_mem_start = static_cast<Address>(buffer->backing_store()); |
| 2103 if (old_size != 0) { |
| 2104 memcpy(new_mem_start, old_mem_start, old_size); |
| 2105 } |
| 2106 SetInstanceMemory(instance, *buffer); |
| 2107 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table(); |
| 2108 RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start, |
| 2109 old_size, new_size); |
2007 } | 2110 } |
2008 SetInstanceMemory(instance, *buffer); | 2111 |
2009 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table(); | |
2010 RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start, | |
2011 old_size, new_size); | |
2012 Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject), | 2112 Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject), |
2013 isolate); | 2113 isolate); |
2014 if (!memory_object->IsUndefined(isolate)) { | 2114 if (!memory_object->IsUndefined(isolate)) { |
2015 WasmJs::SetWasmMemoryArrayBuffer(isolate, memory_object, buffer); | 2115 WasmJs::SetWasmMemoryArrayBuffer(isolate, memory_object, buffer); |
2016 } | 2116 } |
2017 | 2117 |
2018 DCHECK(old_size % WasmModule::kPageSize == 0); | 2118 DCHECK(old_size % WasmModule::kPageSize == 0); |
2019 return (old_size / WasmModule::kPageSize); | 2119 return (old_size / WasmModule::kPageSize); |
2020 } | 2120 } |
2021 | 2121 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2090 CHECK_NOT_NULL(result.val); | 2190 CHECK_NOT_NULL(result.val); |
2091 module = const_cast<WasmModule*>(result.val); | 2191 module = const_cast<WasmModule*>(result.val); |
2092 } | 2192 } |
2093 | 2193 |
2094 Handle<WasmModuleWrapper> module_wrapper = | 2194 Handle<WasmModuleWrapper> module_wrapper = |
2095 WasmModuleWrapper::New(isolate, module); | 2195 WasmModuleWrapper::New(isolate, module); |
2096 | 2196 |
2097 compiled_module->set_module_wrapper(module_wrapper); | 2197 compiled_module->set_module_wrapper(module_wrapper); |
2098 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); | 2198 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); |
2099 } | 2199 } |
OLD | NEW |