Move to kernel style SPDX license identifiers
[babeltrace.git] / src / compat / mman.c
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright (C) 2013 JP Ikaheimonen <jp_ikaheimonen@mentor.com>
5 * Copyright (C) 2016 Michael Jeanson <mjeanson@efficios.com>
6 *
7 * These sources are based on ftp://g.oswego.edu/pub/misc/malloc.c
8 * file by Doug Lea, released to the public domain.
9 */
10
11 #define BT_LOG_OUTPUT_LEVEL (mapping->log_level)
12 #define BT_LOG_TAG "COMPAT/MMAN"
13 #include "logging/log.h"
14
15 #include "common/macros.h"
16 #include "common/common.h"
17
18 #ifdef __APPLE__
19 /*
20 * On macOS, we need a dummy symbol so that the linker won't
21 * complain of an empty table of contents.
22 */
23 BT_HIDDEN
24 int bt_mman_dummy_symbol;
25 #endif /* __APPLE__ */
26
27 #ifdef __MINGW32__
28
29 #include <errno.h>
30 #include <glib.h>
31 #include <io.h>
32 #include <pthread.h>
33 #include <stdlib.h>
34 #include <windows.h>
35
36 #include "compat/mman.h"
37
38 struct mmap_mapping {
39 int log_level;
40
41 /* The duplicated handle. */
42 HANDLE file_handle;
43 /* Handle returned by CreateFileMapping. */
44 HANDLE map_handle;
45 };
46
47 static
48 GHashTable *mmap_mappings = NULL;
49
50 /*
51 * This mutex protects the hashtable of memory mappings.
52 */
53 static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
54
55 static
56 struct mmap_mapping *mapping_create(int log_level)
57 {
58 struct mmap_mapping *mapping;
59
60 mapping = malloc(sizeof(struct mmap_mapping));
61 if (mapping) {
62 mapping->file_handle = NULL;
63 mapping->map_handle = NULL;
64 mapping->log_level = log_level;
65 }
66
67 return mapping;
68 }
69
70 static
71 void mapping_clean(struct mmap_mapping *mapping)
72 {
73 if (mapping) {
74 if (!CloseHandle(mapping->map_handle)) {
75 BT_LOGF_STR("Failed to close mmap map_handle.");
76 bt_common_abort();
77 }
78 if (!CloseHandle(mapping->file_handle)) {
79 BT_LOGF_STR("Failed to close mmap file_handle.");
80 bt_common_abort();
81 }
82 free(mapping);
83 mapping = NULL;
84 }
85 }
86
87 static
88 void addr_clean(void *addr)
89 {
90 /* Cleanup of handles should never fail. */
91 if (!UnmapViewOfFile(addr)) {
92 /*
93 * FIXME: We don't have access to the mapping's log
94 * level here, so force a FATAL level.
95 */
96 BT_LOG_WRITE_CUR_LVL(BT_LOG_FATAL, BT_LOG_FATAL, BT_LOG_TAG,
97 "Failed to unmap mmap mapping.");
98 bt_common_abort();
99 }
100 }
101
102 static
103 void mmap_lock(int log_level)
104 {
105 if (pthread_mutex_lock(&mmap_mutex)) {
106 BT_LOG_WRITE_CUR_LVL(BT_LOG_FATAL, log_level, BT_LOG_TAG, "Failed to acquire mmap_mutex.");
107 bt_common_abort();
108 }
109 }
110
111 static
112 void mmap_unlock(int log_level)
113 {
114 if (pthread_mutex_unlock(&mmap_mutex)) {
115 BT_LOG_WRITE_CUR_LVL(BT_LOG_FATAL, log_level, BT_LOG_TAG, "Failed to release mmap_mutex.");
116 bt_common_abort();
117 }
118 }
119
120 /*
121 * Convert mmap memory protection flags to CreateFileMapping page protection
122 * flag and MapViewOfFile desired access flag.
123 */
124 static
125 DWORD map_prot_flags(int prot, DWORD *dwDesiredAccess)
126 {
127 if (prot & PROT_READ) {
128 if (prot & PROT_WRITE) {
129 *dwDesiredAccess = FILE_MAP_WRITE;
130 if (prot & PROT_EXEC) {
131 return PAGE_EXECUTE_READWRITE;
132 }
133 return PAGE_READWRITE;
134 }
135 if (prot & PROT_EXEC) {
136 *dwDesiredAccess = FILE_MAP_EXECUTE;
137 return PAGE_EXECUTE_READ;
138 }
139 *dwDesiredAccess = FILE_MAP_READ;
140 return PAGE_READONLY;
141 }
142 if (prot & PROT_WRITE) {
143 *dwDesiredAccess = FILE_MAP_COPY;
144 return PAGE_WRITECOPY;
145 }
146 if (prot & PROT_EXEC) {
147 *dwDesiredAccess = FILE_MAP_EXECUTE;
148 return PAGE_EXECUTE_READ;
149 }
150
151 /* Mapping failed. */
152 *dwDesiredAccess = 0;
153 return 0;
154 }
155
156 BT_HIDDEN
157 void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd,
158 off_t offset, int log_level)
159 {
160 struct mmap_mapping *mapping = NULL;
161 void *mapping_addr;
162 DWORD dwDesiredAccess;
163 DWORD flProtect;
164 HANDLE handle;
165
166 /* Check for a valid fd. */
167 if (fd == -1) {
168 _set_errno(EBADF);
169 goto error;
170 }
171
172 /* We don't support this at the moment. */
173 if (flags == MAP_FIXED) {
174 _set_errno(ENOTSUP);
175 goto error;
176 }
177
178 /* Map mmap flags to those of the Windows API. */
179 flProtect = map_prot_flags(prot, &dwDesiredAccess);
180 if (flProtect == 0) {
181 _set_errno(EINVAL);
182 goto error;
183 }
184
185 /* Allocate the mapping struct. */
186 mapping = mapping_create(log_level);
187 if (!mapping) {
188 BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
189 "Failed to allocate mmap mapping.");
190 _set_errno(ENOMEM);
191 goto error;
192 }
193
194 /* Get a handle from the fd. */
195 handle = (HANDLE) _get_osfhandle(fd);
196
197 /* Duplicate the handle and store it in 'mapping.file_handle'. */
198 if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(),
199 &mapping->file_handle, 0, FALSE,
200 DUPLICATE_SAME_ACCESS)) {
201 _set_errno(ENOMEM);
202 goto error;
203 }
204
205 /*
206 * Create a file mapping object with a maximum size
207 * of 'offset' + 'length'.
208 */
209 mapping->map_handle = CreateFileMapping(mapping->file_handle, NULL,
210 flProtect, 0, offset + length, NULL);
211 if (mapping->map_handle == 0) {
212 _set_errno(EACCES);
213 goto error;
214 }
215
216 /* Map the requested block starting at 'offset' for 'length' bytes. */
217 mapping_addr = MapViewOfFile(mapping->map_handle, dwDesiredAccess, 0,
218 offset, length);
219 if (mapping_addr == 0) {
220 DWORD dwLastErr = GetLastError();
221 if (dwLastErr == ERROR_MAPPED_ALIGNMENT) {
222 _set_errno(EINVAL);
223 } else {
224 _set_errno(EACCES);
225 }
226 goto error;
227 }
228
229 mmap_lock(log_level);
230
231 /* If we have never done any mappings, allocate the hashtable. */
232 if (!mmap_mappings) {
233 mmap_mappings = g_hash_table_new_full(g_direct_hash,
234 g_direct_equal, (GDestroyNotify) addr_clean,
235 (GDestroyNotify) mapping_clean);
236 if (!mmap_mappings) {
237 BT_LOGE_STR("Failed to allocate mmap hashtable.");
238 _set_errno(ENOMEM);
239 goto error_mutex_unlock;
240 }
241 }
242
243 /* Add the new mapping to the hashtable. */
244 g_hash_table_insert(mmap_mappings, mapping_addr, mapping);
245
246 mmap_unlock(log_level);
247
248 return mapping_addr;
249
250 error_mutex_unlock:
251 mmap_unlock(log_level);
252 error:
253 mapping_clean(mapping);
254 return MAP_FAILED;
255 }
256
257 BT_HIDDEN
258 int bt_munmap(void *addr, size_t length)
259 {
260 int ret = 0;
261 struct mmap_mapping *mapping = addr;
262 int log_level;
263
264 BT_ASSERT(mapping);
265 log_level = mapping->log_level;
266 mmap_lock(log_level);
267
268 /* Check if the mapping exists in the hashtable. */
269 if (!g_hash_table_lookup(mmap_mappings, addr)) {
270 _set_errno(EINVAL);
271 ret = -1;
272 goto end;
273 }
274
275 /* Remove it. */
276 if (!g_hash_table_remove(mmap_mappings, addr)) {
277 BT_LOGF_STR("Failed to remove mapping from hashtable.");
278 bt_common_abort();
279 }
280
281 end:
282 mmap_unlock(log_level);
283 return ret;
284 }
285
286 BT_HIDDEN
287 size_t bt_mmap_get_offset_align_size(int log_level)
288 {
289 SYSTEM_INFO sysinfo;
290
291 GetNativeSystemInfo(&sysinfo);
292 BT_LOG_WRITE_CUR_LVL(BT_LOG_DEBUG, log_level, BT_LOG_TAG,
293 "Allocator granularity is %lu.",
294 sysinfo.dwAllocationGranularity);
295
296 return sysinfo.dwAllocationGranularity;
297 }
298
299 #endif
This page took 0.034848 seconds and 4 git commands to generate.