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. | |
Mircea Trofin
2016/10/28 22:16:29
can be made
Eric Holk
2016/10/29 00:04:30
Done.
| |
113 const size_t alloc_size = kWasmMaxHeapOffset; | |
114 DCHECK(size % base::OS::CommitPageSize() == 0); | |
Mircea Trofin
2016/10/28 22:16:29
DCHECK_EQ(0, size % etc)
Eric Holk
2016/10/29 00:04:30
Done.
| |
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; | |
Mircea Trofin
2016/10/28 22:16:29
because it may fail, perhaps name the API TryAlloc
Eric Holk
2016/10/29 00:04:30
Done.
| |
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 475 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
596 TRACE_CHAIN(WasmCompiledModule::cast(wasm_module->GetInternalField(0))); | 627 TRACE_CHAIN(WasmCompiledModule::cast(wasm_module->GetInternalField(0))); |
597 TRACE("}\n"); | 628 TRACE("}\n"); |
598 } | 629 } |
599 compiled_module->reset_weak_owning_instance(); | 630 compiled_module->reset_weak_owning_instance(); |
600 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); | 631 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); |
601 TRACE("}\n"); | 632 TRACE("}\n"); |
602 } | 633 } |
603 | 634 |
604 } // namespace | 635 } // namespace |
605 | 636 |
637 Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, | |
638 bool guard) { | |
Mircea Trofin
2016/10/28 22:16:29
guard->enable_guard_regions
also, if this flag is
Eric Holk
2016/10/29 00:04:30
Done.
Yes (at least on 64-bit platforms)
| |
639 if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) { | |
640 // TODO(titzer): lift restriction on maximum memory allocated here. | |
641 return Handle<JSArrayBuffer>::null(); | |
642 } | |
643 | |
644 void* memory = AllocateBackingStore(isolate, size, guard); | |
645 bool is_external = guard; | |
646 | |
647 if (memory == nullptr) { | |
648 return Handle<JSArrayBuffer>::null(); | |
649 } | |
650 | |
651 #if DEBUG | |
652 // Double check the API allocator actually zero-initialized the memory. | |
653 const byte* bytes = reinterpret_cast<const byte*>(memory); | |
654 for (size_t i = 0; i < size; ++i) { | |
655 DCHECK_EQ(0, bytes[i]); | |
656 } | |
657 #endif | |
658 | |
659 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); | |
660 JSArrayBuffer::Setup(buffer, isolate, is_external, memory, | |
661 static_cast<int>(size)); | |
662 buffer->set_is_neuterable(false); | |
663 buffer->set_has_guard_region(guard); | |
664 | |
665 #if V8_HOST_ARCH_64_BIT | |
666 if (is_external) { | |
667 // We mark the buffer as external if we allocated it here with guard | |
668 // pages. That means we need to arrange for it to be freed. | |
669 | |
670 // TODO(eholk): Finalizers may not run when the main thread is shutting | |
671 // down, which means we may leak memory here. | |
672 Handle<Object> global_handle = isolate->global_handles()->Create(*buffer); | |
673 GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(), | |
674 &MemoryFinalizer, v8::WeakCallbackType::kFinalizer); | |
675 } | |
676 #endif | |
677 | |
678 return buffer; | |
679 } | |
680 | |
606 const char* wasm::SectionName(WasmSectionCode code) { | 681 const char* wasm::SectionName(WasmSectionCode code) { |
607 switch (code) { | 682 switch (code) { |
608 case kUnknownSectionCode: | 683 case kUnknownSectionCode: |
609 return "Unknown"; | 684 return "Unknown"; |
610 case kTypeSectionCode: | 685 case kTypeSectionCode: |
611 return "Type"; | 686 return "Type"; |
612 case kImportSectionCode: | 687 case kImportSectionCode: |
613 return "Import"; | 688 return "Import"; |
614 case kFunctionSectionCode: | 689 case kFunctionSectionCode: |
615 return "Function"; | 690 return "Function"; |
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
947 instance->SetInternalField(kWasmMemObject, | 1022 instance->SetInternalField(kWasmMemObject, |
948 isolate_->heap()->undefined_value()); | 1023 isolate_->heap()->undefined_value()); |
949 | 1024 |
950 //-------------------------------------------------------------------------- | 1025 //-------------------------------------------------------------------------- |
951 // Set up the globals for the new instance. | 1026 // Set up the globals for the new instance. |
952 //-------------------------------------------------------------------------- | 1027 //-------------------------------------------------------------------------- |
953 MaybeHandle<JSArrayBuffer> old_globals; | 1028 MaybeHandle<JSArrayBuffer> old_globals; |
954 MaybeHandle<JSArrayBuffer> globals; | 1029 MaybeHandle<JSArrayBuffer> globals; |
955 uint32_t globals_size = module_->globals_size; | 1030 uint32_t globals_size = module_->globals_size; |
956 if (globals_size > 0) { | 1031 if (globals_size > 0) { |
1032 const bool guard = false; | |
957 Handle<JSArrayBuffer> global_buffer = | 1033 Handle<JSArrayBuffer> global_buffer = |
958 NewArrayBuffer(isolate_, globals_size); | 1034 NewArrayBuffer(isolate_, globals_size, guard); |
959 globals = global_buffer; | 1035 globals = global_buffer; |
960 if (globals.is_null()) { | 1036 if (globals.is_null()) { |
961 thrower_->RangeError("Out of memory: wasm globals"); | 1037 thrower_->RangeError("Out of memory: wasm globals"); |
962 return nothing; | 1038 return nothing; |
963 } | 1039 } |
964 Address old_address = owner.is_null() | 1040 Address old_address = owner.is_null() |
965 ? nullptr | 1041 ? nullptr |
966 : GetGlobalStartAddressFromCodeTemplate( | 1042 : GetGlobalStartAddressFromCodeTemplate( |
967 isolate_->heap()->undefined_value(), | 1043 isolate_->heap()->undefined_value(), |
968 JSObject::cast(*owner.ToHandleChecked())); | 1044 JSObject::cast(*owner.ToHandleChecked())); |
(...skipping 18 matching lines...) Expand all Loading... | |
987 //-------------------------------------------------------------------------- | 1063 //-------------------------------------------------------------------------- |
988 MaybeHandle<JSArrayBuffer> old_memory; | 1064 MaybeHandle<JSArrayBuffer> old_memory; |
989 | 1065 |
990 uint32_t min_mem_pages = module_->min_mem_pages; | 1066 uint32_t min_mem_pages = module_->min_mem_pages; |
991 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); | 1067 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); |
992 // TODO(wasm): re-enable counter for max_mem_pages when we use that field. | 1068 // TODO(wasm): re-enable counter for max_mem_pages when we use that field. |
993 | 1069 |
994 if (!memory_.is_null()) { | 1070 if (!memory_.is_null()) { |
995 // Set externally passed ArrayBuffer non neuterable. | 1071 // Set externally passed ArrayBuffer non neuterable. |
996 memory_->set_is_neuterable(false); | 1072 memory_->set_is_neuterable(false); |
1073 | |
1074 DCHECK_IMPLIES(FLAG_wasm_guard_pages, module_->origin == kAsmJsOrigin || | |
Mircea Trofin
2016/10/28 22:16:29
asmjs, not wasm?
Eric Holk
2016/10/29 00:04:30
Yes. For AsmJs origin we still do the bounds check
| |
1075 memory_->has_guard_region()); | |
997 } else if (min_mem_pages > 0) { | 1076 } else if (min_mem_pages > 0) { |
998 memory_ = AllocateMemory(min_mem_pages); | 1077 memory_ = AllocateMemory(min_mem_pages); |
999 if (memory_.is_null()) return nothing; // failed to allocate memory | 1078 if (memory_.is_null()) return nothing; // failed to allocate memory |
1000 } | 1079 } |
1001 | 1080 |
1002 if (!memory_.is_null()) { | 1081 if (!memory_.is_null()) { |
1003 instance->SetInternalField(kWasmMemArrayBuffer, *memory_); | 1082 instance->SetInternalField(kWasmMemArrayBuffer, *memory_); |
1004 Address mem_start = static_cast<Address>(memory_->backing_store()); | 1083 Address mem_start = static_cast<Address>(memory_->backing_store()); |
1005 uint32_t mem_size = | 1084 uint32_t mem_size = |
1006 static_cast<uint32_t>(memory_->byte_length()->Number()); | 1085 static_cast<uint32_t>(memory_->byte_length()->Number()); |
(...skipping 494 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1501 } | 1580 } |
1502 } | 1581 } |
1503 } | 1582 } |
1504 | 1583 |
1505 // Allocate memory for a module instance as a new JSArrayBuffer. | 1584 // Allocate memory for a module instance as a new JSArrayBuffer. |
1506 Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { | 1585 Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { |
1507 if (min_mem_pages > WasmModule::kMaxMemPages) { | 1586 if (min_mem_pages > WasmModule::kMaxMemPages) { |
1508 thrower_->RangeError("Out of memory: wasm memory too large"); | 1587 thrower_->RangeError("Out of memory: wasm memory too large"); |
1509 return Handle<JSArrayBuffer>::null(); | 1588 return Handle<JSArrayBuffer>::null(); |
1510 } | 1589 } |
1590 const bool guard = FLAG_wasm_guard_pages; | |
1511 Handle<JSArrayBuffer> mem_buffer = | 1591 Handle<JSArrayBuffer> mem_buffer = |
1512 NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize); | 1592 NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize, guard); |
1513 | 1593 |
1514 if (mem_buffer.is_null()) { | 1594 if (mem_buffer.is_null()) { |
1515 thrower_->RangeError("Out of memory: wasm memory"); | 1595 thrower_->RangeError("Out of memory: wasm memory"); |
1516 } | 1596 } |
1517 return mem_buffer; | 1597 return mem_buffer; |
1518 } | 1598 } |
1519 | 1599 |
1520 // Process the exports, creating wrappers for functions, tables, memories, | 1600 // Process the exports, creating wrappers for functions, tables, memories, |
1521 // and globals. | 1601 // and globals. |
1522 void ProcessExports(MaybeHandle<JSArrayBuffer> globals, | 1602 void ProcessExports(MaybeHandle<JSArrayBuffer> globals, |
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1993 // "undefined" case above. | 2073 // "undefined" case above. |
1994 DCHECK_NOT_NULL(old_mem_start); | 2074 DCHECK_NOT_NULL(old_mem_start); |
1995 DCHECK(old_size + pages * WasmModule::kPageSize <= | 2075 DCHECK(old_size + pages * WasmModule::kPageSize <= |
1996 std::numeric_limits<uint32_t>::max()); | 2076 std::numeric_limits<uint32_t>::max()); |
1997 new_size = old_size + pages * WasmModule::kPageSize; | 2077 new_size = old_size + pages * WasmModule::kPageSize; |
1998 } | 2078 } |
1999 | 2079 |
2000 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) { | 2080 if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size) { |
2001 return -1; | 2081 return -1; |
2002 } | 2082 } |
2003 Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size); | 2083 |
2004 if (buffer.is_null()) return -1; | 2084 Handle<JSArrayBuffer> buffer; |
2005 Address new_mem_start = static_cast<Address>(buffer->backing_store()); | 2085 |
2006 if (old_size != 0) { | 2086 if (FLAG_wasm_guard_pages && !old_buffer.is_null()) { |
2007 memcpy(new_mem_start, old_mem_start, old_size); | 2087 base::OS::Unprotect(old_buffer->backing_store(), new_size); |
2088 reinterpret_cast<v8::Isolate*>(isolate) | |
2089 ->AdjustAmountOfExternalAllocatedMemory(pages * WasmModule::kPageSize); | |
Mircea Trofin
2016/10/28 22:16:29
would it make sense to add this API to the interna
Eric Holk
2016/10/29 00:04:30
Perhaps, but I'd rather leave it how it is. Adjust
Michael Lippautz
2016/10/29 07:39:19
+1 for leaving now. This API is used by several co
| |
2090 Handle<Object> new_size_object = | |
2091 isolate->factory()->NewNumberFromSize(new_size); | |
2092 old_buffer->set_byte_length(*new_size_object); | |
2093 | |
2094 SetInstanceMemory(instance, *old_buffer); | |
2095 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table(); | |
2096 RelocateMemoryReferencesInCode(code_table, old_mem_start, old_mem_start, | |
2097 old_size, new_size); | |
2098 buffer = old_buffer; | |
2099 } else { | |
2100 const bool guard = FLAG_wasm_guard_pages; | |
2101 buffer = NewArrayBuffer(isolate, new_size, guard); | |
2102 if (buffer.is_null()) return -1; | |
2103 Address new_mem_start = static_cast<Address>(buffer->backing_store()); | |
2104 if (old_size != 0) { | |
2105 memcpy(new_mem_start, old_mem_start, old_size); | |
2106 } | |
2107 SetInstanceMemory(instance, *buffer); | |
2108 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table(); | |
2109 RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start, | |
2110 old_size, new_size); | |
2008 } | 2111 } |
2009 SetInstanceMemory(instance, *buffer); | 2112 |
2010 Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table(); | |
2011 RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start, | |
2012 old_size, new_size); | |
2013 Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject), | 2113 Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject), |
2014 isolate); | 2114 isolate); |
2015 if (!memory_object->IsUndefined(isolate)) { | 2115 if (!memory_object->IsUndefined(isolate)) { |
2016 WasmJs::SetWasmMemoryArrayBuffer(isolate, memory_object, buffer); | 2116 WasmJs::SetWasmMemoryArrayBuffer(isolate, memory_object, buffer); |
2017 } | 2117 } |
2018 | 2118 |
2019 DCHECK(old_size % WasmModule::kPageSize == 0); | 2119 DCHECK(old_size % WasmModule::kPageSize == 0); |
2020 return (old_size / WasmModule::kPageSize); | 2120 return (old_size / WasmModule::kPageSize); |
2021 } | 2121 } |
2022 | 2122 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2091 CHECK_NOT_NULL(result.val); | 2191 CHECK_NOT_NULL(result.val); |
2092 module = const_cast<WasmModule*>(result.val); | 2192 module = const_cast<WasmModule*>(result.val); |
2093 } | 2193 } |
2094 | 2194 |
2095 Handle<WasmModuleWrapper> module_wrapper = | 2195 Handle<WasmModuleWrapper> module_wrapper = |
2096 WasmModuleWrapper::New(isolate, module); | 2196 WasmModuleWrapper::New(isolate, module); |
2097 | 2197 |
2098 compiled_module->set_module_wrapper(module_wrapper); | 2198 compiled_module->set_module_wrapper(module_wrapper); |
2099 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); | 2199 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); |
2100 } | 2200 } |
OLD | NEW |