4 * Copyright (C) 2013 JP Ikaheimonen <jp_ikaheimonen@mentor.com>
5 * 2016 Michael Jeanson <mjeanson@efficios.com>
7 * These sources are based on ftp://g.oswego.edu/pub/misc/malloc.c
8 * file by Doug Lea, released to the public domain.
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 #define BT_LOG_OUTPUT_LEVEL (mapping->log_level)
30 #define BT_LOG_TAG "COMPAT/MMAN"
31 #include "logging/log.h"
33 #include "common/macros.h"
37 * On macOS, we need a dummy symbol so that the linker won't
38 * complain of an empty table of contents.
41 int bt_mman_dummy_symbol
;
42 #endif /* __APPLE__ */
53 #include "compat/mman.h"
58 /* The duplicated handle. */
60 /* Handle returned by CreateFileMapping. */
65 GHashTable
*mmap_mappings
= NULL
;
68 * This mutex protects the hashtable of memory mappings.
70 static pthread_mutex_t mmap_mutex
= PTHREAD_MUTEX_INITIALIZER
;
73 struct mmap_mapping
*mapping_create(int log_level
)
75 struct mmap_mapping
*mapping
;
77 mapping
= malloc(sizeof(struct mmap_mapping
));
78 if (mapping
!= NULL
) {
79 mapping
->file_handle
= NULL
;
80 mapping
->map_handle
= NULL
;
81 mapping
->log_level
= log_level
;
88 void mapping_clean(struct mmap_mapping
*mapping
)
91 if (!CloseHandle(mapping
->map_handle
)) {
92 BT_LOGF_STR("Failed to close mmap map_handle.");
95 if (!CloseHandle(mapping
->file_handle
)) {
96 BT_LOGF_STR("Failed to close mmap file_handle.");
105 void addr_clean(void *addr
)
107 /* Cleanup of handles should never fail. */
108 if (!UnmapViewOfFile(addr
)) {
110 * FIXME: We don't have access to the mapping's log
111 * level here, so force a FATAL level.
113 BT_LOG_WRITE_CUR_LVL(BT_LOG_FATAL
, BT_LOG_FATAL
, BT_LOG_TAG
,
114 "Failed to unmap mmap mapping.");
120 void mmap_lock(int log_level
)
122 if (pthread_mutex_lock(&mmap_mutex
)) {
123 BT_LOG_WRITE_CUR_LVL(BT_LOG_FATAL
, log_level
, BT_LOG_TAG
, "Failed to acquire mmap_mutex.");
129 void mmap_unlock(int log_level
)
131 if (pthread_mutex_unlock(&mmap_mutex
)) {
132 BT_LOG_WRITE_CUR_LVL(BT_LOG_FATAL
, log_level
, BT_LOG_TAG
, "Failed to release mmap_mutex.");
138 * Convert mmap memory protection flags to CreateFileMapping page protection
139 * flag and MapViewOfFile desired access flag.
142 DWORD
map_prot_flags(int prot
, DWORD
*dwDesiredAccess
)
144 if (prot
& PROT_READ
) {
145 if (prot
& PROT_WRITE
) {
146 *dwDesiredAccess
= FILE_MAP_WRITE
;
147 if (prot
& PROT_EXEC
) {
148 return PAGE_EXECUTE_READWRITE
;
150 return PAGE_READWRITE
;
152 if (prot
& PROT_EXEC
) {
153 *dwDesiredAccess
= FILE_MAP_EXECUTE
;
154 return PAGE_EXECUTE_READ
;
156 *dwDesiredAccess
= FILE_MAP_READ
;
157 return PAGE_READONLY
;
159 if (prot
& PROT_WRITE
) {
160 *dwDesiredAccess
= FILE_MAP_COPY
;
161 return PAGE_WRITECOPY
;
163 if (prot
& PROT_EXEC
) {
164 *dwDesiredAccess
= FILE_MAP_EXECUTE
;
165 return PAGE_EXECUTE_READ
;
168 /* Mapping failed. */
169 *dwDesiredAccess
= 0;
174 void *bt_mmap(void *addr
, size_t length
, int prot
, int flags
, int fd
,
175 off_t offset
, int log_level
)
177 struct mmap_mapping
*mapping
= NULL
;
179 DWORD dwDesiredAccess
;
183 /* Check for a valid fd. */
189 /* We don't support this at the moment. */
190 if (flags
== MAP_FIXED
) {
195 /* Map mmap flags to those of the Windows API. */
196 flProtect
= map_prot_flags(prot
, &dwDesiredAccess
);
197 if (flProtect
== 0) {
202 /* Allocate the mapping struct. */
203 mapping
= mapping_create(log_level
);
205 BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR
, log_level
, BT_LOG_TAG
,
206 "Failed to allocate mmap mapping.");
211 /* Get a handle from the fd. */
212 handle
= (HANDLE
) _get_osfhandle(fd
);
214 /* Duplicate the handle and store it in 'mapping.file_handle'. */
215 if (!DuplicateHandle(GetCurrentProcess(), handle
, GetCurrentProcess(),
216 &mapping
->file_handle
, 0, FALSE
,
217 DUPLICATE_SAME_ACCESS
)) {
223 * Create a file mapping object with a maximum size
224 * of 'offset' + 'length'.
226 mapping
->map_handle
= CreateFileMapping(mapping
->file_handle
, NULL
,
227 flProtect
, 0, offset
+ length
, NULL
);
228 if (mapping
->map_handle
== 0) {
233 /* Map the requested block starting at 'offset' for 'length' bytes. */
234 mapping_addr
= MapViewOfFile(mapping
->map_handle
, dwDesiredAccess
, 0,
236 if (mapping_addr
== 0) {
237 DWORD dwLastErr
= GetLastError();
238 if (dwLastErr
== ERROR_MAPPED_ALIGNMENT
) {
246 mmap_lock(log_level
);
248 /* If we have never done any mappings, allocate the hashtable. */
249 if (!mmap_mappings
) {
250 mmap_mappings
= g_hash_table_new_full(g_direct_hash
,
251 g_direct_equal
, (GDestroyNotify
) addr_clean
,
252 (GDestroyNotify
) mapping_clean
);
253 if (!mmap_mappings
) {
254 BT_LOGE_STR("Failed to allocate mmap hashtable.");
256 goto error_mutex_unlock
;
260 /* Add the new mapping to the hashtable. */
261 g_hash_table_insert(mmap_mappings
, mapping_addr
, mapping
);
263 mmap_unlock(log_level
);
268 mmap_unlock(log_level
);
270 mapping_clean(mapping
);
275 int bt_munmap(void *addr
, size_t length
)
278 struct mmap_mapping
*mapping
= addr
;
282 log_level
= mapping
->log_level
;
283 mmap_lock(log_level
);
285 /* Check if the mapping exists in the hashtable. */
286 if (g_hash_table_lookup(mmap_mappings
, addr
) == NULL
) {
293 if (!g_hash_table_remove(mmap_mappings
, addr
)) {
294 BT_LOGF_STR("Failed to remove mapping from hashtable.");
299 mmap_unlock(log_level
);