13
13
#include < sys/mman.h>
14
14
#include < unistd.h>
15
15
#include < cstddef>
16
+ #include < fstream>
17
+ #include < mutex>
18
+ #include < sstream>
16
19
17
20
#include " xenia/base/math.h"
18
21
#include " xenia/base/platform.h"
@@ -79,14 +82,53 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
79
82
}
80
83
}
81
84
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
+
82
102
bool IsWritableExecutableMemorySupported () { return true ; }
83
103
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
+
84
112
void * AllocFixed (void * base_address, size_t length,
85
113
AllocationType allocation_type, PageAccess access) {
86
114
// mmap does not support reserve / commit, so ignore allocation_type.
87
115
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
+
90
132
if (result == MAP_FAILED) {
91
133
return nullptr ;
92
134
} else {
@@ -96,20 +138,97 @@ void* AllocFixed(void* base_address, size_t length,
96
138
97
139
bool DeallocFixed (void * base_address, size_t length,
98
140
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
+ }
100
169
}
101
170
102
171
bool Protect (void * base_address, size_t length, PageAccess access,
103
172
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
+ }
106
177
107
178
uint32_t prot = ToPosixProtectFlags (access);
108
179
return mprotect (base_address, length, prot) == 0 ;
109
180
}
110
181
111
182
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
112
230
return false ;
231
+ #endif
113
232
}
114
233
115
234
FileMappingHandle CreateFileMappingHandle (const std::filesystem::path& path,
@@ -178,12 +297,40 @@ void CloseFileMappingHandle(FileMappingHandle handle,
178
297
void * MapFileView (FileMappingHandle handle, void * base_address, size_t length,
179
298
PageAccess access, size_t file_offset) {
180
299
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,
182
307
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
+ }
183
317
}
184
318
185
319
bool UnmapFileView (FileMappingHandle handle, void * base_address,
186
320
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." );
187
334
return munmap (base_address, length) == 0 ;
188
335
}
189
336
0 commit comments