#include #include #include #include #include #include #include #include "code-manager.h" #include "list.h" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif typedef struct file_t file_t; typedef struct block_t block_t; /* List of *files*; * List of *segments*, each of which has a name and a pointer into a file */ #define FILENAME_SIZE 128 #define FUNCTION_NAME_SIZE 128 struct file_t { link_t link; char name[FILENAME_SIZE]; size_t map_size; uint8_t * writable; uint8_t * executable; size_t position; }; static bool_t get_tmp_file (char *template, size_t size, uint8_t **writable, uint8_t **executable) { uint8_t *w, *x; int fd; w = NULL; x = NULL; if ((fd = mkstemp (template)) == -1) goto out_error; unlink (template); if (ftruncate (fd, size) == -1) goto out_error; w = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (!w) goto out_error; x = mmap (NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0); if (!x) goto out_error; *writable = w; *executable = x; close (fd); return TRUE; out_error: if (w) munmap (w, size); if (x) munmap (x, size); if (fd != -1) close (fd); return FALSE; } static file_t * file_new (const char *prefix, size_t n_bytes) { char names[2][FILENAME_SIZE]; file_t *file = malloc (sizeof *file); size_t page_mask; int i; if (!file) return NULL; snprintf (names[0], FILENAME_SIZE - 1, "/tmp/%s-XXXXXX", prefix); snprintf (names[1], FILENAME_SIZE - 1, "/var/tmp/%s-XXXXXX", prefix); page_mask = getpagesize() - 1; n_bytes = (n_bytes + page_mask) & ~page_mask; for (i = 0; i < sizeof (names) / sizeof (names[0]); ++i) { uint8_t *writable = NULL; uint8_t *executable = NULL; if (get_tmp_file (names[i], n_bytes, &writable, &executable)) { strcpy (file->name, names[i]); file->writable = writable; file->executable = executable; file->map_size = n_bytes; file->position = 0; return file; } } free (file); return NULL; } static bool_t file_alloc (file_t *file, int n_bytes, size_t align_bits, size_t align_mask, uint8_t **writable, uint8_t **executable) { file->position += (align_bits - file->position) & align_mask; if (file->position + n_bytes > file->map_size) return FALSE; *writable = file->writable + file->position; *executable = file->executable + file->position; file->position += n_bytes; return TRUE; } static void file_free (file_t *file) { munmap (file->writable, file->map_size); munmap (file->executable, file->map_size); free (file); } struct code_manager_t { char prefix[32]; list_t files; }; code_manager_t * code_manager_new (const char *prefix) { code_manager_t *man; if (!(man = malloc (sizeof *man))) return NULL; memset (man->prefix, 0, sizeof man->prefix); strncpy (man->prefix, prefix, sizeof man->prefix - 1); list_init (&man->files); return man; } #define MIN_FILE_SIZE (1024 * 1024) bool_t code_manager_alloc (code_manager_t *manager, const char *name, size_t n_bytes, size_t align_bits, size_t align_mask, uint8_t **writable, uint8_t **executable) { file_t *file = NULL; if (!list_is_empty (&manager->files)) file = CONTAINER_OF (file_t, link, manager->files.head); while (!file || !file_alloc ( file, n_bytes, align_bits, align_mask, writable, executable)) { size_t fsize = (n_bytes > MIN_FILE_SIZE)? n_bytes : MIN_FILE_SIZE; if (!(file = file_new (manager->prefix, fsize))) return FALSE; list_prepend (&manager->files, &file->link); } return TRUE; } void code_manager_free (code_manager_t *manager) { link_t *link; LIST_FOREACH (&manager->files, link) { file_t *file = CONTAINER_OF (file_t, link, link); file_free (file); } free (manager); }