[Memory/CPU] UWP: Support separate code execution and write memory, FromApp functions + other Windows memory fixes

This commit is contained in:
Triang3l
2020-11-24 22:18:50 +03:00
parent cabd28b9bb
commit a73592c2ef
15 changed files with 348 additions and 113 deletions

View File

@@ -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*>),