Skip to content

Commit 7f0a8b0

Browse files
author
guccigang420
committed
Fixed mmap usage and (re)-implemented most parts of memory_posix.cc
1 parent f6b5424 commit 7f0a8b0

File tree

1 file changed

+153
-6
lines changed

1 file changed

+153
-6
lines changed

src/xenia/base/memory_posix.cc

Lines changed: 153 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include <sys/mman.h>
1414
#include <unistd.h>
1515
#include <cstddef>
16+
#include <fstream>
17+
#include <mutex>
18+
#include <sstream>
1619

1720
#include "xenia/base/math.h"
1821
#include "xenia/base/platform.h"
@@ -79,14 +82,53 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
7982
}
8083
}
8184

85+
PageAccess ToXeniaProtectFlags(char* protection) {
86+
if (protection[0] == 'r' && protection[1] == 'w' && protection[2] == 'x') {
87+
return PageAccess::kExecuteReadWrite;
88+
} else if (protection[0] == 'r' && protection[1] == '-' &&
89+
protection[2] == 'x') {
90+
return PageAccess::kExecuteReadOnly;
91+
} else if (protection[0] == 'r' && protection[1] == 'w' &&
92+
protection[2] == '-') {
93+
return PageAccess::kReadWrite;
94+
} else if (protection[0] == 'r' && protection[1] == '-' &&
95+
protection[2] == '-') {
96+
return PageAccess::kReadOnly;
97+
} else {
98+
return PageAccess::kNoAccess;
99+
}
100+
}
101+
82102
bool IsWritableExecutableMemorySupported() { return true; }
83103

104+
struct MappedFileRange {
105+
uintptr_t region_begin;
106+
uintptr_t region_end;
107+
};
108+
109+
std::vector<struct MappedFileRange> mapped_file_ranges;
110+
std::mutex g_mapped_file_ranges_mutex;
111+
84112
void* AllocFixed(void* base_address, size_t length,
85113
AllocationType allocation_type, PageAccess access) {
86114
// mmap does not support reserve / commit, so ignore allocation_type.
87115
uint32_t prot = ToPosixProtectFlags(access);
88-
void* result = mmap(base_address, length, prot,
89-
MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
116+
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
117+
118+
if (base_address != nullptr) {
119+
bool should_protect = allocation_type == AllocationType::kCommit;
120+
if (should_protect) {
121+
if (Protect(base_address, length, access)){
122+
return base_address;
123+
} else {
124+
return nullptr;
125+
}
126+
}
127+
flags |= MAP_FIXED_NOREPLACE;
128+
}
129+
130+
void* result = mmap(base_address, length, prot, flags, -1, 0);
131+
90132
if (result == MAP_FAILED) {
91133
return nullptr;
92134
} else {
@@ -96,20 +138,97 @@ void* AllocFixed(void* base_address, size_t length,
96138

97139
bool DeallocFixed(void* base_address, size_t length,
98140
DeallocationType deallocation_type) {
99-
return munmap(base_address, length) == 0;
141+
const uintptr_t region_begin = (uintptr_t)base_address;
142+
const uintptr_t region_end = (uintptr_t)base_address + length;
143+
144+
std::lock_guard<std::mutex> guard(g_mapped_file_ranges_mutex);
145+
for (const auto& mapped_range : mapped_file_ranges) {
146+
if (region_begin >= mapped_range.region_begin &&
147+
region_end <= mapped_range.region_end) {
148+
149+
switch(deallocation_type) {
150+
case DeallocationType::kDecommit:
151+
return Protect(base_address, length, PageAccess::kNoAccess);
152+
case DeallocationType::kRelease:
153+
assert_always("Error: Tried to release mapped memory!");
154+
default:
155+
assert_unhandled_case(deallocation_type);
156+
}
157+
158+
}
159+
}
160+
161+
switch(deallocation_type) {
162+
case DeallocationType::kDecommit:
163+
return Protect(base_address, length, PageAccess::kNoAccess);
164+
case DeallocationType::kRelease:
165+
return munmap(base_address, length) == 0;
166+
default:
167+
assert_unhandled_case(deallocation_type);
168+
}
100169
}
101170

102171
bool Protect(void* base_address, size_t length, PageAccess access,
103172
PageAccess* out_old_access) {
104-
// Linux does not have a syscall to query memory permissions.
105-
assert_null(out_old_access);
173+
if (out_old_access) {
174+
size_t length_copy = length;
175+
QueryProtect(base_address, length_copy, *out_old_access);
176+
}
106177

107178
uint32_t prot = ToPosixProtectFlags(access);
108179
return mprotect(base_address, length, prot) == 0;
109180
}
110181

111182
bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
183+
// No generic POSIX solution exists. The Linux solution should work on all Linux
184+
// kernel based OS, including Android.
185+
#if XE_PLATFORM_LINUX
186+
std::ifstream memory_maps;
187+
memory_maps.open("/proc/self/maps", std::ios_base::in);
188+
std::string maps_entry_string;
189+
190+
while (std::getline(memory_maps, maps_entry_string)) {
191+
std::stringstream entry_stream(maps_entry_string);
192+
uintptr_t map_region_begin, map_region_end;
193+
char separator, protection[4];
194+
195+
entry_stream >> std::hex >> map_region_begin >> separator >>
196+
map_region_end >> protection;
197+
198+
if (map_region_begin <= (uintptr_t)base_address &&
199+
map_region_end > (uintptr_t)base_address) {
200+
length = map_region_end - reinterpret_cast<uintptr_t>(base_address);
201+
202+
access_out = ToXeniaProtectFlags(protection);
203+
204+
// Look at the next consecutive mappings
205+
while (std::getline(memory_maps, maps_entry_string)) {
206+
std::stringstream next_entry_stream(maps_entry_string);
207+
uintptr_t next_map_region_begin, next_map_region_end;
208+
char next_protection[4];
209+
210+
next_entry_stream >> std::hex >> next_map_region_begin >> separator >>
211+
next_map_region_end >> next_protection;
212+
if (map_region_end == next_map_region_begin &&
213+
access_out == ToXeniaProtectFlags(next_protection)) {
214+
length =
215+
next_map_region_end - reinterpret_cast<uintptr_t>(base_address);
216+
continue;
217+
} else {
218+
break;
219+
}
220+
}
221+
222+
memory_maps.close();
223+
return true;
224+
}
225+
}
226+
227+
memory_maps.close();
228+
return false;
229+
#else
112230
return false;
231+
#endif
113232
}
114233

115234
FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
@@ -178,12 +297,40 @@ void CloseFileMappingHandle(FileMappingHandle handle,
178297
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
179298
PageAccess access, size_t file_offset) {
180299
uint32_t prot = ToPosixProtectFlags(access);
181-
return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, handle,
300+
301+
int flags = MAP_SHARED;
302+
if (base_address != nullptr) {
303+
flags |= MAP_FIXED_NOREPLACE;
304+
}
305+
306+
void* result = mmap(base_address, length, prot, flags, handle,
182307
file_offset);
308+
309+
if (result == MAP_FAILED) {
310+
return nullptr;
311+
} else {
312+
std::lock_guard<std::mutex> guard(g_mapped_file_ranges_mutex);
313+
mapped_file_ranges.push_back(
314+
{(uintptr_t)result, (uintptr_t)result + length});
315+
return result;
316+
}
183317
}
184318

185319
bool UnmapFileView(FileMappingHandle handle, void* base_address,
186320
size_t length) {
321+
std::lock_guard<std::mutex> guard(g_mapped_file_ranges_mutex);
322+
for (auto mapped_range = mapped_file_ranges.begin();
323+
mapped_range != mapped_file_ranges.end();) {
324+
if (mapped_range->region_begin == (uintptr_t)base_address &&
325+
mapped_range->region_end == (uintptr_t)base_address + length) {
326+
mapped_file_ranges.erase(mapped_range);
327+
return munmap(base_address, length) == 0;
328+
} else {
329+
mapped_range++;
330+
}
331+
}
332+
// TODO: Implement partial file unmapping.
333+
assert_always("Error: Partial unmapping of files not yet supported.");
187334
return munmap(base_address, length) == 0;
188335
}
189336

0 commit comments

Comments
 (0)