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+
82102bool 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+
84112void * 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
97139bool 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
102171bool 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
111182bool 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
115234FileMappingHandle CreateFileMappingHandle (const std::filesystem::path& path,
@@ -178,12 +297,40 @@ void CloseFileMappingHandle(FileMappingHandle handle,
178297void * 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
185319bool 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