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,19 +82,50 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
7982 }
8083}
8184
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+
82101bool IsWritableExecutableMemorySupported () { return true ; }
83102
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+
84111void * AllocFixed (void * base_address, size_t length,
85112 AllocationType allocation_type, PageAccess access) {
86113 // mmap does not support reserve / commit, so ignore allocation_type.
87114 uint32_t prot = ToPosixProtectFlags (access);
88- int flags = 0 ;
115+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
116+
89117 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;
93125 }
126+
94127 void * result = mmap (base_address, length, prot, flags, -1 , 0 );
128+
95129 if (result != MAP_FAILED) {
96130 return result;
97131 }
@@ -100,19 +134,90 @@ void* AllocFixed(void* base_address, size_t length,
100134
101135bool DeallocFixed (void * base_address, size_t length,
102136 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+ }
104164}
105165
106166bool Protect (void * base_address, size_t length, PageAccess access,
107167 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+ }
110172
111173 uint32_t prot = ToPosixProtectFlags (access);
112174 return mprotect (base_address, length, prot) == 0 ;
113175}
114176
115177bool 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 ();
116221 return false ;
117222}
118223
@@ -182,12 +287,41 @@ void CloseFileMappingHandle(FileMappingHandle handle,
182287void * MapFileView (FileMappingHandle handle, void * base_address, size_t length,
183288 PageAccess access, size_t file_offset) {
184289 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 ;
187307}
188308
189309bool UnmapFileView (FileMappingHandle handle, void * base_address,
190310 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." );
191325 return munmap (base_address, length) == 0 ;
192326}
193327
0 commit comments