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,19 +82,50 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
79
82
}
80
83
}
81
84
85
+ PageAccess ToXeniaProtectFlags (const char * protection) {
86
+ if (protection[0 ] == ' r' && protection[1 ] == ' w' && protection[2 ] == ' x' ) {
87
+ return PageAccess::kExecuteReadWrite ;
88
+ }
89
+ if (protection[0 ] == ' r' && protection[1 ] == ' -' && protection[2 ] == ' x' ) {
90
+ return PageAccess::kExecuteReadOnly ;
91
+ }
92
+ if (protection[0 ] == ' r' && protection[1 ] == ' w' && protection[2 ] == ' -' ) {
93
+ return PageAccess::kReadWrite ;
94
+ }
95
+ if (protection[0 ] == ' r' && protection[1 ] == ' -' && protection[2 ] == ' -' ) {
96
+ return PageAccess::kReadOnly ;
97
+ }
98
+ return PageAccess::kNoAccess ;
99
+ }
100
+
82
101
bool IsWritableExecutableMemorySupported () { return true ; }
83
102
103
+ struct MappedFileRange {
104
+ uintptr_t region_begin;
105
+ uintptr_t region_end;
106
+ };
107
+
108
+ std::vector<MappedFileRange> mapped_file_ranges;
109
+ std::mutex g_mapped_file_ranges_mutex;
110
+
84
111
void * AllocFixed (void * base_address, size_t length,
85
112
AllocationType allocation_type, PageAccess access) {
86
113
// mmap does not support reserve / commit, so ignore allocation_type.
87
114
uint32_t prot = ToPosixProtectFlags (access);
88
- int flags = 0 ;
115
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
116
+
89
117
if (base_address != nullptr ) {
90
- flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
91
- } else {
92
- flags = MAP_PRIVATE | MAP_ANONYMOUS;
118
+ if (allocation_type == AllocationType::kCommit ) {
119
+ if (Protect (base_address, length, access)) {
120
+ return base_address;
121
+ }
122
+ return nullptr ;
123
+ }
124
+ flags |= MAP_FIXED_NOREPLACE;
93
125
}
126
+
94
127
void * result = mmap (base_address, length, prot, flags, -1 , 0 );
128
+
95
129
if (result != MAP_FAILED) {
96
130
return result;
97
131
}
@@ -100,19 +134,90 @@ void* AllocFixed(void* base_address, size_t length,
100
134
101
135
bool DeallocFixed (void * base_address, size_t length,
102
136
DeallocationType deallocation_type) {
103
- return munmap (base_address, length) == 0 ;
137
+ const auto region_begin = reinterpret_cast <uintptr_t >(base_address);
138
+ const uintptr_t region_end =
139
+ reinterpret_cast <uintptr_t >(base_address) + length;
140
+
141
+ std::lock_guard guard (g_mapped_file_ranges_mutex);
142
+ for (const auto & mapped_range : mapped_file_ranges) {
143
+ if (region_begin >= mapped_range.region_begin &&
144
+ region_end <= mapped_range.region_end ) {
145
+ switch (deallocation_type) {
146
+ case DeallocationType::kDecommit :
147
+ return Protect (base_address, length, PageAccess::kNoAccess );
148
+ case DeallocationType::kRelease :
149
+ assert_always (" Error: Tried to release mapped memory!" );
150
+ default :
151
+ assert_unhandled_case (deallocation_type);
152
+ }
153
+ }
154
+ }
155
+
156
+ switch (deallocation_type) {
157
+ case DeallocationType::kDecommit :
158
+ return Protect (base_address, length, PageAccess::kNoAccess );
159
+ case DeallocationType::kRelease :
160
+ return munmap (base_address, length) == 0 ;
161
+ default :
162
+ assert_unhandled_case (deallocation_type);
163
+ }
104
164
}
105
165
106
166
bool Protect (void * base_address, size_t length, PageAccess access,
107
167
PageAccess* out_old_access) {
108
- // Linux does not have a syscall to query memory permissions.
109
- assert_null (out_old_access);
168
+ if (out_old_access) {
169
+ size_t length_copy = length;
170
+ QueryProtect (base_address, length_copy, *out_old_access);
171
+ }
110
172
111
173
uint32_t prot = ToPosixProtectFlags (access);
112
174
return mprotect (base_address, length, prot) == 0 ;
113
175
}
114
176
115
177
bool QueryProtect (void * base_address, size_t & length, PageAccess& access_out) {
178
+ // No generic POSIX solution exists. The Linux solution should work on all
179
+ // Linux kernel based OS, including Android.
180
+ std::ifstream memory_maps;
181
+ memory_maps.open (" /proc/self/maps" , std::ios_base::in);
182
+ std::string maps_entry_string;
183
+
184
+ while (std::getline (memory_maps, maps_entry_string)) {
185
+ std::stringstream entry_stream (maps_entry_string);
186
+ uintptr_t map_region_begin, map_region_end;
187
+ char separator, protection[4 ];
188
+
189
+ entry_stream >> std::hex >> map_region_begin >> separator >>
190
+ map_region_end >> protection;
191
+
192
+ if (map_region_begin <= reinterpret_cast <uintptr_t >(base_address) &&
193
+ map_region_end > reinterpret_cast <uintptr_t >(base_address)) {
194
+ length = map_region_end - reinterpret_cast <uintptr_t >(base_address);
195
+
196
+ access_out = ToXeniaProtectFlags (protection);
197
+
198
+ // Look at the next consecutive mappings
199
+ while (std::getline (memory_maps, maps_entry_string)) {
200
+ std::stringstream next_entry_stream (maps_entry_string);
201
+ uintptr_t next_map_region_begin, next_map_region_end;
202
+ char next_protection[4 ];
203
+
204
+ next_entry_stream >> std::hex >> next_map_region_begin >> separator >>
205
+ next_map_region_end >> next_protection;
206
+ if (map_region_end == next_map_region_begin &&
207
+ access_out == ToXeniaProtectFlags (next_protection)) {
208
+ length =
209
+ next_map_region_end - reinterpret_cast <uintptr_t >(base_address);
210
+ continue ;
211
+ }
212
+ break ;
213
+ }
214
+
215
+ memory_maps.close ();
216
+ return true ;
217
+ }
218
+ }
219
+
220
+ memory_maps.close ();
116
221
return false ;
117
222
}
118
223
@@ -182,12 +287,41 @@ void CloseFileMappingHandle(FileMappingHandle handle,
182
287
void * MapFileView (FileMappingHandle handle, void * base_address, size_t length,
183
288
PageAccess access, size_t file_offset) {
184
289
uint32_t prot = ToPosixProtectFlags (access);
185
- return mmap64 (base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, handle,
186
- file_offset);
290
+
291
+ int flags = MAP_SHARED;
292
+ if (base_address != nullptr ) {
293
+ flags |= MAP_FIXED_NOREPLACE;
294
+ }
295
+
296
+ void * result = mmap (base_address, length, prot, flags, handle, file_offset);
297
+
298
+ if (result != MAP_FAILED) {
299
+ std::lock_guard guard (g_mapped_file_ranges_mutex);
300
+ mapped_file_ranges.push_back (
301
+ {reinterpret_cast <uintptr_t >(result),
302
+ reinterpret_cast <uintptr_t >(result) + length});
303
+ return result;
304
+ }
305
+
306
+ return nullptr ;
187
307
}
188
308
189
309
bool UnmapFileView (FileMappingHandle handle, void * base_address,
190
310
size_t length) {
311
+ std::lock_guard guard (g_mapped_file_ranges_mutex);
312
+ for (auto mapped_range = mapped_file_ranges.begin ();
313
+ mapped_range != mapped_file_ranges.end ();) {
314
+ if (mapped_range->region_begin ==
315
+ reinterpret_cast <uintptr_t >(base_address) &&
316
+ mapped_range->region_end ==
317
+ reinterpret_cast <uintptr_t >(base_address) + length) {
318
+ mapped_file_ranges.erase (mapped_range);
319
+ return munmap (base_address, length) == 0 ;
320
+ }
321
+ ++mapped_range;
322
+ }
323
+ // TODO: Implement partial file unmapping.
324
+ assert_always (" Error: Partial unmapping of files not yet supported." );
191
325
return munmap (base_address, length) == 0 ;
192
326
}
193
327
0 commit comments