1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
/*
* This file is from CPython's Modules/_ctypes/malloc_closure.c
* and has received some edits.
*/
#include <ffi.h>
#ifdef MS_WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
# define MAP_ANONYMOUS MAP_ANON
# endif
#endif
/* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC.
This is, apparently, an undocumented change to ffi_prep_closure():
depending on the Linux kernel we're running on, we must give it a
mmap that is either PROT_READ|PROT_WRITE|PROT_EXEC or only
PROT_READ|PROT_WRITE. In the latter case, just trying to obtain a
mmap with PROT_READ|PROT_WRITE|PROT_EXEC would kill our process(!),
but in that situation libffi is fine with only PROT_READ|PROT_WRITE.
There is nothing in the libffi API to know that, though, so we have
to guess by parsing /proc/self/status. "Meh."
*/
#ifdef __linux__
#include <stdlib.h>
static int emutramp_enabled = -1;
static int
emutramp_enabled_check (void)
{
char *buf = NULL;
size_t len = 0;
FILE *f;
int ret;
f = fopen ("/proc/self/status", "r");
if (f == NULL)
return 0;
ret = 0;
while (getline (&buf, &len, f) != -1)
if (!strncmp (buf, "PaX:", 4))
{
char emutramp;
if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
ret = (emutramp == 'E');
break;
}
free (buf);
fclose (f);
return ret;
}
#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
: (emutramp_enabled = emutramp_enabled_check ()))
#else
#define is_emutramp_enabled() 0
#endif
/* 'allocate_num_pages' is dynamically adjusted starting from one
page. It grows by a factor of PAGE_ALLOCATION_GROWTH_RATE. This is
meant to handle both the common case of not needing a lot of pages,
and the rare case of needing many of them. Systems in general have a
limit of how many mmap'd blocks can be open.
*/
#define PAGE_ALLOCATION_GROWTH_RATE 1.3
static Py_ssize_t allocate_num_pages = 0;
/* #define MALLOC_CLOSURE_DEBUG */ /* enable for some debugging output */
/******************************************************************/
union mmaped_block {
ffi_closure closure;
union mmaped_block *next;
};
static union mmaped_block *free_list = 0;
static Py_ssize_t _pagesize = 0;
static void more_core(void)
{
union mmaped_block *item;
Py_ssize_t count, i;
/* determine the pagesize */
#ifdef MS_WIN32
if (!_pagesize) {
SYSTEM_INFO systeminfo;
GetSystemInfo(&systeminfo);
_pagesize = systeminfo.dwPageSize;
}
#else
if (!_pagesize) {
#ifdef _SC_PAGESIZE
_pagesize = sysconf(_SC_PAGESIZE);
#else
_pagesize = getpagesize();
#endif
}
#endif
if (_pagesize <= 0)
_pagesize = 4096;
/* bump 'allocate_num_pages' */
allocate_num_pages = 1 + (
(Py_ssize_t)(allocate_num_pages * PAGE_ALLOCATION_GROWTH_RATE));
/* calculate the number of mmaped_blocks to allocate */
count = (allocate_num_pages * _pagesize) / sizeof(union mmaped_block);
/* allocate a memory block */
#ifdef MS_WIN32
item = (union mmaped_block *)VirtualAlloc(NULL,
count * sizeof(union mmaped_block),
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (item == NULL)
return;
#else
{
int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
if (is_emutramp_enabled ())
prot &= ~PROT_EXEC;
item = (union mmaped_block *)mmap(NULL,
allocate_num_pages * _pagesize,
prot,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
if (item == (void *)MAP_FAILED)
return;
}
#endif
#ifdef MALLOC_CLOSURE_DEBUG
printf("block at %p allocated (%ld bytes), %ld mmaped_blocks\n",
item, (long)(allocate_num_pages * _pagesize), (long)count);
#endif
/* put them into the free list */
for (i = 0; i < count; ++i) {
item->next = free_list;
free_list = item;
++item;
}
}
/******************************************************************/
/* put the item back into the free list */
static void cffi_closure_free(ffi_closure *p)
{
union mmaped_block *item = (union mmaped_block *)p;
item->next = free_list;
free_list = item;
}
/* return one item from the free list, allocating more if needed */
static ffi_closure *cffi_closure_alloc(void)
{
union mmaped_block *item;
if (!free_list)
more_core();
if (!free_list)
return NULL;
item = free_list;
free_list = item->next;
return &item->closure;
}
|