[Memory/CPU] UWP: Support separate code execution and write memory, FromApp functions + other Windows memory fixes
This commit is contained in:
@@ -41,8 +41,15 @@ X64CodeCache::~X64CodeCache() {
|
||||
|
||||
// Unmap all views and close mapping.
|
||||
if (mapping_ != xe::memory::kFileMappingHandleInvalid) {
|
||||
xe::memory::UnmapFileView(mapping_, generated_code_base_,
|
||||
kGeneratedCodeSize);
|
||||
if (generated_code_write_base_ &&
|
||||
generated_code_write_base_ != generated_code_execute_base_) {
|
||||
xe::memory::UnmapFileView(mapping_, generated_code_write_base_,
|
||||
kGeneratedCodeSize);
|
||||
}
|
||||
if (generated_code_execute_base_) {
|
||||
xe::memory::UnmapFileView(mapping_, generated_code_execute_base_,
|
||||
kGeneratedCodeSize);
|
||||
}
|
||||
xe::memory::CloseFileMappingHandle(mapping_, file_name_);
|
||||
mapping_ = xe::memory::kFileMappingHandleInvalid;
|
||||
}
|
||||
@@ -73,17 +80,41 @@ bool X64CodeCache::Initialize() {
|
||||
}
|
||||
|
||||
// Map generated code region into the file. Pages are committed as required.
|
||||
generated_code_base_ = reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
|
||||
mapping_, reinterpret_cast<void*>(kGeneratedCodeBase), kGeneratedCodeSize,
|
||||
xe::memory::PageAccess::kExecuteReadWrite, 0));
|
||||
if (!generated_code_base_) {
|
||||
XELOGE("Unable to allocate code cache generated code storage");
|
||||
XELOGE(
|
||||
"This is likely because the {:X}-{:X} range is in use by some other "
|
||||
"system DLL",
|
||||
static_cast<uint64_t>(kGeneratedCodeBase),
|
||||
kGeneratedCodeBase + kGeneratedCodeSize);
|
||||
return false;
|
||||
if (xe::memory::IsWritableExecutableMemoryPreferred()) {
|
||||
generated_code_execute_base_ =
|
||||
reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
|
||||
mapping_, reinterpret_cast<void*>(kGeneratedCodeExecuteBase),
|
||||
kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite, 0));
|
||||
generated_code_write_base_ = generated_code_execute_base_;
|
||||
if (!generated_code_execute_base_ || !generated_code_write_base_) {
|
||||
XELOGE("Unable to allocate code cache generated code storage");
|
||||
XELOGE(
|
||||
"This is likely because the {:X}-{:X} range is in use by some other "
|
||||
"system DLL",
|
||||
uint64_t(kGeneratedCodeExecuteBase),
|
||||
uint64_t(kGeneratedCodeExecuteBase + kGeneratedCodeSize));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
generated_code_execute_base_ =
|
||||
reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
|
||||
mapping_, reinterpret_cast<void*>(kGeneratedCodeExecuteBase),
|
||||
kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadOnly, 0));
|
||||
generated_code_write_base_ =
|
||||
reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
|
||||
mapping_, reinterpret_cast<void*>(kGeneratedCodeWriteBase),
|
||||
kGeneratedCodeSize, xe::memory::PageAccess::kReadWrite, 0));
|
||||
if (!generated_code_execute_base_ || !generated_code_write_base_) {
|
||||
XELOGE("Unable to allocate code cache generated code storage");
|
||||
XELOGE(
|
||||
"This is likely because the {:X}-{:X} and {:X}-{:X} ranges are in "
|
||||
"use by some other system DLL",
|
||||
uint64_t(kGeneratedCodeExecuteBase),
|
||||
uint64_t(kGeneratedCodeExecuteBase + kGeneratedCodeSize),
|
||||
uint64_t(kGeneratedCodeWriteBase),
|
||||
uint64_t(kGeneratedCodeWriteBase + kGeneratedCodeSize));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Preallocate the function map to a large, reasonable size.
|
||||
@@ -117,7 +148,7 @@ void X64CodeCache::CommitExecutableRange(uint32_t guest_low,
|
||||
xe::memory::AllocFixed(
|
||||
indirection_table_base_ + (guest_low - kIndirectionTableBase),
|
||||
guest_high - guest_low, xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadWrite);
|
||||
xe::memory::PageAccess::kReadWrite);
|
||||
|
||||
// Fill memory with the default value.
|
||||
uint32_t* p = reinterpret_cast<uint32_t*>(indirection_table_base_);
|
||||
@@ -126,21 +157,26 @@ void X64CodeCache::CommitExecutableRange(uint32_t guest_low,
|
||||
}
|
||||
}
|
||||
|
||||
void* X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
|
||||
const EmitFunctionInfo& func_info) {
|
||||
void X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
|
||||
const EmitFunctionInfo& func_info,
|
||||
void*& code_execute_address_out,
|
||||
void*& code_write_address_out) {
|
||||
// Same for now. We may use different pools or whatnot later on, like when
|
||||
// we only want to place guest code in a serialized cache on disk.
|
||||
return PlaceGuestCode(guest_address, machine_code, func_info, nullptr);
|
||||
PlaceGuestCode(guest_address, machine_code, func_info, nullptr,
|
||||
code_execute_address_out, code_write_address_out);
|
||||
}
|
||||
|
||||
void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
const EmitFunctionInfo& func_info,
|
||||
GuestFunction* function_info) {
|
||||
void X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
const EmitFunctionInfo& func_info,
|
||||
GuestFunction* function_info,
|
||||
void*& code_execute_address_out,
|
||||
void*& code_write_address_out) {
|
||||
// Hold a lock while we bump the pointers up. This is important as the
|
||||
// unwind table requires entries AND code to be sorted in order.
|
||||
size_t low_mark;
|
||||
size_t high_mark;
|
||||
uint8_t* code_address;
|
||||
uint8_t* code_execute_address;
|
||||
UnwindReservation unwind_reservation;
|
||||
{
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
@@ -149,26 +185,33 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
|
||||
// Reserve code.
|
||||
// Always move the code to land on 16b alignment.
|
||||
code_address = generated_code_base_ + generated_code_offset_;
|
||||
code_execute_address =
|
||||
generated_code_execute_base_ + generated_code_offset_;
|
||||
code_execute_address_out = code_execute_address;
|
||||
uint8_t* code_write_address =
|
||||
generated_code_write_base_ + generated_code_offset_;
|
||||
code_write_address_out = code_write_address;
|
||||
generated_code_offset_ += xe::round_up(func_info.code_size.total, 16);
|
||||
|
||||
auto tail_address = generated_code_base_ + generated_code_offset_;
|
||||
auto tail_write_address =
|
||||
generated_code_write_base_ + generated_code_offset_;
|
||||
|
||||
// Reserve unwind info.
|
||||
// We go on the high size of the unwind info as we don't know how big we
|
||||
// need it, and a few extra bytes of padding isn't the worst thing.
|
||||
unwind_reservation =
|
||||
RequestUnwindReservation(generated_code_base_ + generated_code_offset_);
|
||||
unwind_reservation = RequestUnwindReservation(generated_code_write_base_ +
|
||||
generated_code_offset_);
|
||||
generated_code_offset_ += xe::round_up(unwind_reservation.data_size, 16);
|
||||
|
||||
auto end_address = generated_code_base_ + generated_code_offset_;
|
||||
auto end_write_address =
|
||||
generated_code_write_base_ + generated_code_offset_;
|
||||
|
||||
high_mark = generated_code_offset_;
|
||||
|
||||
// Store in map. It is maintained in sorted order of host PC dependent on
|
||||
// us also being append-only.
|
||||
generated_code_map_.emplace_back(
|
||||
(uint64_t(code_address - generated_code_base_) << 32) |
|
||||
(uint64_t(code_execute_address - generated_code_execute_base_) << 32) |
|
||||
generated_code_offset_,
|
||||
function_info);
|
||||
|
||||
@@ -185,21 +228,30 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
if (high_mark <= old_commit_mark) break;
|
||||
|
||||
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
||||
xe::memory::AllocFixed(generated_code_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadWrite);
|
||||
if (generated_code_execute_base_ == generated_code_write_base_) {
|
||||
xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadWrite);
|
||||
} else {
|
||||
xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadOnly);
|
||||
xe::memory::AllocFixed(generated_code_write_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kReadWrite);
|
||||
}
|
||||
} while (generated_code_commit_mark_.compare_exchange_weak(
|
||||
old_commit_mark, new_commit_mark));
|
||||
|
||||
// Copy code.
|
||||
std::memcpy(code_address, machine_code, func_info.code_size.total);
|
||||
std::memcpy(code_write_address, machine_code, func_info.code_size.total);
|
||||
|
||||
// Fill unused slots with 0xCC
|
||||
std::memset(tail_address, 0xCC,
|
||||
static_cast<size_t>(end_address - tail_address));
|
||||
std::memset(tail_write_address, 0xCC,
|
||||
static_cast<size_t>(end_write_address - tail_write_address));
|
||||
|
||||
// Notify subclasses of placed code.
|
||||
PlaceCode(guest_address, machine_code, func_info, code_address,
|
||||
PlaceCode(guest_address, machine_code, func_info, code_execute_address,
|
||||
unwind_reservation);
|
||||
}
|
||||
|
||||
@@ -214,7 +266,7 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
|
||||
iJIT_Method_Load_V2 method = {0};
|
||||
method.method_id = iJIT_GetNewMethodID();
|
||||
method.method_load_address = code_address;
|
||||
method.method_load_address = code_execute_address;
|
||||
method.method_size = uint32_t(code_size);
|
||||
method.method_name = const_cast<char*>(method_name.data());
|
||||
method.module_name = function_info
|
||||
@@ -230,10 +282,9 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
if (guest_address && indirection_table_base_) {
|
||||
uint32_t* indirection_slot = reinterpret_cast<uint32_t*>(
|
||||
indirection_table_base_ + (guest_address - kIndirectionTableBase));
|
||||
*indirection_slot = uint32_t(reinterpret_cast<uint64_t>(code_address));
|
||||
*indirection_slot =
|
||||
uint32_t(reinterpret_cast<uint64_t>(code_execute_address));
|
||||
}
|
||||
|
||||
return code_address;
|
||||
}
|
||||
|
||||
uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||
@@ -245,7 +296,7 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||
|
||||
// Reserve code.
|
||||
// Always move the code to land on 16b alignment.
|
||||
data_address = generated_code_base_ + generated_code_offset_;
|
||||
data_address = generated_code_write_base_ + generated_code_offset_;
|
||||
generated_code_offset_ += xe::round_up(length, 16);
|
||||
|
||||
high_mark = generated_code_offset_;
|
||||
@@ -260,9 +311,18 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||
if (high_mark <= old_commit_mark) break;
|
||||
|
||||
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
||||
xe::memory::AllocFixed(generated_code_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadWrite);
|
||||
if (generated_code_execute_base_ == generated_code_write_base_) {
|
||||
xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadWrite);
|
||||
} else {
|
||||
xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadOnly);
|
||||
xe::memory::AllocFixed(generated_code_write_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kReadWrite);
|
||||
}
|
||||
} while (generated_code_commit_mark_.compare_exchange_weak(old_commit_mark,
|
||||
new_commit_mark));
|
||||
|
||||
@@ -273,7 +333,7 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||
}
|
||||
|
||||
GuestFunction* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
||||
uint32_t key = uint32_t(host_pc - kGeneratedCodeBase);
|
||||
uint32_t key = uint32_t(host_pc - kGeneratedCodeExecuteBase);
|
||||
void* fn_entry = std::bsearch(
|
||||
&key, generated_code_map_.data(), generated_code_map_.size() + 1,
|
||||
sizeof(std::pair<uint32_t, Function*>),
|
||||
|
||||
Reference in New Issue
Block a user